You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求纯Python库或算法:用任意线段精确拆分含线段与圆弧的2D多段线

纯Python实现线段拆分混合多段线的方案

核心思路

要实现精确拆分(不近似圆弧),需分三步处理:先实现基础几何计算工具,再逐个拆分多段线元素,最后重组拆分后的片段为完整多段线列表。

1. 基础几何工具函数

先实现核心几何计算逻辑,用于判断位置、求解交点:

  • 点相对拆分线段的位置判断:通过叉积计算,返回点在拆分线某一侧、另一侧或线上的标识
  • 线段与线段的精确交点:用线性方程组求解,返回交点坐标(若存在)
  • 线段与圆弧的精确交点:先求直线与圆的交点,再验证交点是否落在圆弧的角度范围内

2. 多段线元素拆分逻辑

对多段线的每个元素(线段/圆弧)逐一处理:

  • 线段元素:
    • 若完全在拆分线一侧:直接归入对应侧的多段线片段
    • 若跨拆分线:计算交点,拆分为两段线段,分别归入对应侧
  • 圆弧元素:
    • 若完全在拆分线一侧:直接归入对应侧的多段线片段
    • 若跨拆分线:计算圆弧与拆分线的交点,根据交点角度拆分原圆弧为1-2段圆弧,分别归入对应侧

3. 多段线重组

拆分完成后,跟踪片段的首尾连接关系,将连续片段拼接为完整多段线。若拆分后产生独立的不连续片段,分别归入两侧的多段线列表。

纯Python实现示例框架

import math

# 判断点相对于拆分线段的位置:1=一侧,-1=另一侧,0=在线上
def point_side(p, split_line):
    (x1, y1), (x2, y2) = split_line
    cross = (x2 - x1) * (p[1] - y1) - (y2 - y1) * (p[0] - x1)
    if abs(cross) < 1e-9:
        return 0
    return 1 if cross > 0 else -1

# 计算线段与拆分线的交点
def get_segment_split_intersection(seg, split_line):
    (x1, y1), (x2, y2) = seg
    (sx1, sy1), (sx2, sy2) = split_line

    den = (x1 - x2)*(sy1 - sy2) - (y1 - y2)*(sx1 - sx2)
    if abs(den) < 1e-9:
        return None  # 平行无交点

    t_num = (x1 - sx1)*(sy1 - sy2) - (y1 - sy1)*(sx1 - sx2)
    t = t_num / den
    u_num = -((x1 - x2)*(y1 - sy1) - (y1 - y2)*(x1 - sx1))
    u = u_num / den

    if 0 <= t <= 1 and 0 <= u <= 1:
        x = x1 + t*(x2 - x1)
        y = y1 + t*(y2 - y1)
        return (round(x, 9), round(y, 9))
    return None

# 计算圆弧与拆分线的交点列表
def get_arc_split_intersections(arc, split_line):
    (cx, cy), r, start_angle, end_angle = arc
    (sx1, sy1), (sx2, sy2) = split_line

    # 直线的一般式Ax + By + C = 0
    A = sy2 - sy1
    B = sx1 - sx2
    C = sx2*sy1 - sx1*sy2

    # 计算圆心到直线的距离
    dist = abs(A*cx + B*cy + C) / math.hypot(A, B)
    if dist > r + 1e-9:
        return []  # 无交点
    elif abs(dist - r) < 1e-9:
        # 相切,一个交点
        x = cx - A*(A*cx + B*cy + C)/(A**2 + B**2)
        y = cy - B*(A*cx + B*cy + C)/(A**2 + B**2)
        intersect = (round(x,9), round(y,9))
    else:
        # 两个交点
        sqrt_val = math.sqrt(r**2 - dist**2)
        x1 = cx - A*(A*cx + B*cy + C)/(A**2 + B**2) + B*sqrt_val/(A**2 + B**2)
        y1 = cy - B*(A*cx + B*cy + C)/(A**2 + B**2) - A*sqrt_val/(A**2 + B**2)
        x2 = cx - A*(A*cx + B*cy + C)/(A**2 + B**2) - B*sqrt_val/(A**2 + B**2)
        y2 = cy - B*(A*cx + B*cy + C)/(A**2 + B**2) + A*sqrt_val/(A**2 + B**2)
        intersect = [(round(x1,9), round(y1,9)), (round(x2,9), round(y2,9))]

    # 验证交点是否在圆弧角度范围内
    valid_intersects = []
    theta_dir = 1 if end_angle >= start_angle else -1
    start_rad = math.radians(start_angle)
    end_rad = math.radians(end_angle)

    def angle_in_range(rad):
        if theta_dir == 1:
            return start_rad - 1e-9 <= rad <= end_rad + 1e-9
        else:
            return rad >= start_rad - 1e-9 or rad <= end_rad + 1e-9

    for p in (intersect if isinstance(intersect, list) else [intersect]):
        rad = math.atan2(p[1]-cy, p[0]-cx)
        if rad < 0:
            rad += 2*math.pi
        if angle_in_range(rad):
            valid_intersects.append(p)
    return valid_intersects

# 拆分多段线主函数
def split_polyline(polyline, split_line):
    left_segments = []
    right_segments = []
    current_left = []
    current_right = []

    for elem in polyline:
        if len(elem) == 2:  # 线段元素
            p1, p2 = elem
            s1 = point_side(p1, split_line)
            s2 = point_side(p2, split_line)
            if s1 == s2:
                if s1 == 1:
                    current_left.append(elem)
                elif s1 == -1:
                    current_right.append(elem)
            else:
                intersect = get_segment_split_intersection(elem, split_line)
                if intersect:
                    if s1 == 1 or s2 == 1:
                        seg_left = [p1 if s1 ==1 else intersect, p2 if s2 ==1 else intersect]
                        current_left.append(seg_left)
                    if s1 == -1 or s2 == -1:
                        seg_right = [p1 if s1 ==-1 else intersect, p2 if s2 ==-1 else intersect]
                        current_right.append(seg_right)
                    # 此处可添加逻辑处理多段线断开的情况
        else:  # 圆弧元素
            (cx, cy), r, start_angle, end_angle = elem
            start_pt = (cx + r*math.cos(math.radians(start_angle)), cy + r*math.sin(math.radians(start_angle)))
            end_pt = (cx + r*math.cos(math.radians(end_angle)), cy + r*math.sin(math.radians(end_angle)))
            s_start = point_side(start_pt, split_line)
            s_end = point_side(end_pt, split_line)

            if s_start == s_end:
                if s_start ==1:
                    current_left.append(elem)
                elif s_start ==-1:
                    current_right.append(elem)
            else:
                intersects = get_arc_split_intersections(elem, split_line)
                for idx, intersect in enumerate(intersects):
                    intersect_angle = math.degrees(math.atan2(intersect[1]-cy, intersect[0]-cx))
                    if intersect_angle <0:
                        intersect_angle +=360
                    # 拆分圆弧
                    arc1 = [(cx, cy), r, start_angle, intersect_angle]
                    arc2 = [(cx, cy), r, intersect_angle, end_angle]
                    # 判断拆分后圆弧的所属侧
                    mid_angle1 = (start_angle + intersect_angle)/2
                    mid_pt1 = (cx + r*math.cos(math.radians(mid_angle1)), cy + r*math.sin(math.radians(mid_angle1)))
                    if point_side(mid_pt1, split_line) ==1:
                        current_left.append(arc1)
                    else:
                        current_right.append(arc1)

                    mid_angle2 = (intersect_angle + end_angle)/2
                    mid_pt2 = (cx + r*math.cos(math.radians(mid_angle2)), cy + r*math.sin(math.radians(mid_angle2)))
                    if point_side(mid_pt2, split_line) ==1:
                        current_left.append(arc2)
                    else:
                        current_right.append(arc2)
    # 整理最终多段线列表
    left_polylines = [current_left] if current_left else []
    right_polylines = [current_right] if current_right else []
    # 实际场景需补充处理拆分后产生的独立多段线
    return left_polylines, right_polylines

关键注意事项

  • 处理浮点数精度:设置合理的误差阈值(如1e-9),避免因计算误差导致的逻辑错误
  • 圆弧方向处理:拆分后的圆弧需保持原有的顺时针/逆时针方向(通过theta的正负性维持)
  • 多段线重组:需额外跟踪片段的首尾连接关系,确保拆分后的多段线连续完整

内容的提问来源于stack exchange,提问作者mmj

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 09:57:47