def normalize_points(point1, point2):
    """规范化两个点,确保小的点在前"""
    if point1 > point2:
        return point2, point1
    return point1, point2


def point_on_segment(px, py, x1, y1, x2, y2):
    """判断点是否在线段上"""
    if (x1 <= px <= x2 or x2 <= px <= x1) and (y1 <= py <= y2 or y2 <= py <= y1):
        cross_product = (py - y1) * (x2 - x1) - (px - x1) * (y2 - y1)
        if abs(cross_product) < 1e-10:
            return True
    return False


def perpendicular_line_from_point(point, line):
    # 提取点的坐标
    px, py = point
    # 提取线段的端点
    x1, y1, x2, y2 = line

    # 计算线段的方向向量
    dx = x2 - x1
    dy = y2 - y1

    # 计算线段长度的平方
    len_sq = dx ** 2 + dy ** 2
    if len_sq == 0:
        raise ValueError("线段的两个端点重合,无法计算垂线。")

    # 计算点在直线上的投影点(垂足)
    t = ((px - x1) * dx + (py - y1) * dy) / len_sq
    foot_x = x1 + t * dx
    foot_y = y1 + t * dy
    print(f"foot_x: {foot_x}, foot_y: {foot_y}, t: {t}")

    # 判断点是否在线段上
    if point_on_segment(px, py, x1, y1, x2, y2):
        # 点在线段上,返回点和线段的中心点
        mid_x = (x1 + x2) / 2
        mid_y = (y1 + y2) / 2
        return normalize_points((px, py), (mid_x, mid_y))
    elif 0 <= t <= 1:
        # 点不在线段上,但垂足在线段上,返回点和垂足点
        return normalize_points((px, py), (foot_x, foot_y))
    else:
        # 点在线段的延长线上,返回点和最近的端点
        dist_to_start = (px - x1) ** 2 + (py - y1) ** 2
        dist_to_end = (px - x2) ** 2 + (py - y2) ** 2
        if dist_to_start < dist_to_end:
            return normalize_points((px, py), (x1, y1))
        else:
            return normalize_points((px, py), (x2, y2))


# 示例用法
point = (2, 2)
line = (3, 1, 3, 4)
result = perpendicular_line_from_point(point, line)
print(result)  # 输出应为 ((2, 2), (3, 2))


point = (3, 5)
line = (3, 2, 3, 5)
result = perpendicular_line_from_point(point, line)
print(result)  # 输出应为 ((3, 2), (3, 3.5))
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.

leng

def normalize_points(point1, point2):
    """规范化两个点,确保小的点在前"""
    if point1 > point2:
        return point2, point1
    return point1, point2


def point_on_segment(px, py, x1, y1, x2, y2):
    """判断点是否在线段上"""
    if (x1 <= px <= x2 or x2 <= px <= x1) and (y1 <= py <= y2 or y2 <= py <= y1):
        cross_product = (py - y1) * (x2 - x1) - (px - x1) * (y2 - y1)
        if abs(cross_product) < 1e-10:
            return True
    return False


def perpendicular_line_from_point(point, line, length=16):
    # 提取点的坐标
    px, py = point
    # 提取线段的端点
    x1, y1, x2, y2 = line

    # 计算线段的方向向量
    dx = x2 - x1
    dy = y2 - y1

    # 计算线段长度的平方
    len_sq = dx ** 2 + dy ** 2
    if len_sq == 0:
        raise ValueError("线段的两个端点重合,无法计算垂线。")

    # 计算点在直线上的投影点(垂足)
    t = ((px - x1) * dx + (py - y1) * dy) / len_sq
    foot_x = x1 + t * dx
    foot_y = y1 + t * dy
    print(f"foot_x: {foot_x}, foot_y: {foot_y}, t: {t}")

    # 判断点是否在线段上
    if point_on_segment(px, py, x1, y1, x2, y2):
        # 点在线段上,返回点和线段的中心点
        mid_x = (x1 + x2) / 2
        mid_y = (y1 + y2) / 2
        return normalize_points((px, py), (mid_x, mid_y))
    elif 0 <= t <= 1:
        # 点不在线段上,但垂足在线段上,返回点和垂足点
        return normalize_points((px, py), (foot_x, foot_y))
    else:
        # 点在线段的延长线上,返回点和最近的端点
        if (x1 == x2):  # 垂直线段
            if py < y1:
                return normalize_points((px, py), (x1, y1))
            else:
                return normalize_points((px, py), (x1, y2))
        elif (y1 == y2):  # 水平线段
            if px < x1:
                return normalize_points((px, py), (x1, y1))
            else:
                return normalize_points((px, py), (x2, y2))
        else:
            # 一般情况下,返回点和最近的端点
            dist_to_start = (px - x1) ** 2 + (py - y1) ** 2
            dist_to_end = (px - x2) ** 2 + (py - y2) ** 2
            if dist_to_start < dist_to_end:
                return normalize_points((px, py), (x1, y1))
            else:
                return normalize_points((px, py), (x2, y2))


def point_and_offset_on_segment(point, line, length=16):
    px, py = point
    x1, y1, x2, y2 = line

    if y1 == y2:  # 水平线段
        if px + length <= max(x1, x2):
            return (px, py), (px + length, py)
        else:
            return (max(x1, x2) - length, py), (max(x1, x2), py)
    elif x1 == x2:  # 垂直线段
        if py + length <= max(y1, y2):
            return (px, py), (px, py + length)
        else:
            return (px, max(y1, y2) - length), (px, max(y1, y2))
    else:
        return None


# 示例用法
point = (2, 2)
line = (3, 1, 3, 4)
result = perpendicular_line_from_point(point, line)
print(result)  # 输出应为 ((2, 2), (3, 2))

point = (3, 5)
line = (3, 2, 3, 5)
result = perpendicular_line_from_point(point, line)
print(result)  # 输出应为 ((3, 2), (3, 3.5))

# 新的测试用例
point = (2, 2)
line = (2, 2, 2, 60)
result = point_and_offset_on_segment(point, line, length=16)
print(result)  # 输出应为 ((2, 2), (18, 2))

point = (2, 10)
line = (2, 50, 2, 10)
result = point_and_offset_on_segment(point, line, length=16)
print(result)  # 输出应为 ((34, 2), (50, 2))
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.