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

二维圆弧与非零有限线段最短距离及对应点求解

二维平面圆弧与有限线段最短距离及对应点计算方案

参数定义

先明确两类几何元素的标准化参数,后续计算统一基于该约定:

  • 圆弧:圆心C(cx, cy)、半径r、起始角θ_start、终止角θ_end(逆时针为角度正方向),圆弧上任意点可表示为P_arc(θ) = (cx + r·cosθ, cy + r·sinθ),合法角度范围为θ ∈ [θ_start, θ_end]
  • 有限线段:两个端点A(ax, ay)、B(bx, by),线段上任意点可表示为P_seg(t) = A + t·(B-A),合法参数范围为t ∈ [0, 1]

核心逻辑

两个有界几何元素的最短距离只会出现在有限个候选点对中,计算所有合法候选的距离后取最小值即可,不需要做全局遍历,三类有效候选如下:

  • 圆弧与线段的法线投影点对:即圆心到线段的垂线与圆、线段的交点,仅当两个点分别落在圆弧段、线段段范围内时有效
  • 线段端点到圆弧的最短距离点对:即线段两个端点A、B分别对应圆弧上的最近点,天然满足点在线段上,仅需校验圆弧上点的范围
  • 圆弧端点到线段的最短距离点对:即圆弧两个端点(θ=θ_start、θ=θ_end对应点)分别对应线段上的最近点,天然满足点在圆弧上,仅需校验线段上点的范围

注意:不要遗漏边界端点候选,绝大多数实现出错都是因为仅计算投影点,未考虑投影点落在弧段/线段外的情况——这类场景下最短距离必然出现在元素端点上。

伪代码实现

通用工具函数

const PI = 3.141592653589793
const EPS = 1e-8

// 计算两点欧氏距离
func dist(p1, p2):
    return sqrt( (p1.x - p2.x)^2 + (p1.y - p2.y)^2 )

// 计算点p到有限线段ab的最近点(返回线段上的点)
func closest_point_on_seg(p, a, b):
    ab_x = b.x - a.x
    ab_y = b.y - a.y
    ap_x = p.x - a.x
    ap_y = p.y - a.y
    t = (ap_x*ab_x + ap_y*ab_y) / (ab_x*ab_x + ab_y*ab_y)
    t = max(0, min(1, t)) // 将参数t限制在线段合法范围[0,1]
    return Point(a.x + t*ab_x, a.y + t*ab_y)

// 角度归一化:将theta调整到以ref为起点的[ref, ref+2π)区间
func normalize_angle(theta, ref):
    while theta < ref:
        theta += 2*PI
    while theta >= ref + 2*PI:
        theta -= 2*PI
    return theta

// 计算点p到指定圆弧的最近点(返回圆弧上的点)
func closest_point_on_arc(p, c, r, theta_s, theta_e):
    dx = p.x - c.x
    dy = p.y - c.y
    theta_p = atan2(dy, dx)
    theta_p = normalize_angle(theta_p, theta_s)
    // 若点相对圆心的角落在圆弧范围内,最近点为连线与圆的交点
    if theta_p >= theta_s - EPS and theta_p <= theta_e + EPS:
        return Point(c.x + r*cos(theta_p), c.y + r*sin(theta_p))
    // 否则最近点为两个圆弧端点中距离更近的那个
    p_s = Point(c.x + r*cos(theta_s), c.y + r*sin(theta_s))
    p_e = Point(c.x + r*cos(theta_e), c.y + r*sin(theta_e))
    return p_s if dist(p, p_s) < dist(p, p_e) else p_e

主计算函数

// 主函数:返回最短距离、圆弧上的最近点、线段上的最近点
func arc_seg_min_dist(arc, seg):
    // 解包参数
    c = arc.center, r = arc.r, theta_s = arc.theta_start, theta_e = arc.theta_end
    a = seg.point_a, b = seg.point_b
    candidates = []

    // 1. 加入法线投影候选点对
    p_seg_proj = closest_point_on_seg(c, a, b)
    dir_x = p_seg_proj.x - c.x
    dir_y = p_seg_proj.y - c.y
    dir_len = sqrt(dir_x*dir_x + dir_y*dir_y)
    if dir_len > EPS: // 排除线段过圆心导致方向向量为0的特殊情况
        p_arc_proj = Point(c.x + dir_x/dir_len*r, c.y + dir_y/dir_len*r)
        theta_proj = atan2(p_arc_proj.y - c.y, p_arc_proj.x - c.x)
        theta_proj = normalize_angle(theta_proj, theta_s)
        if theta_proj >= theta_s - EPS and theta_proj <= theta_e + EPS:
            candidates.append( (p_arc_proj, p_seg_proj) )

    // 2. 加入线段两个端点对应圆弧的最近点对
    p_arc_a = closest_point_on_arc(a, c, r, theta_s, theta_e)
    candidates.append( (p_arc_a, a) )
    p_arc_b = closest_point_on_arc(b, c, r, theta_s, theta_e)
    candidates.append( (p_arc_b, b) )

    // 3. 加入圆弧两个端点对应线段的最近点对
    p_arc_s = Point(c.x + r*cos(theta_s), c.y + r*sin(theta_s))
    p_seg_s = closest_point_on_seg(p_arc_s, a, b)
    candidates.append( (p_arc_s, p_seg_s) )
    p_arc_e = Point(c.x + r*cos(theta_e), c.y + r*sin(theta_e))
    p_seg_e = closest_point_on_seg(p_arc_e, a, b)
    candidates.append( (p_arc_e, p_seg_e) )

    // 遍历所有候选找最小值
    min_d = INF
    res_arc_p = null
    res_seg_p = null
    for (pa, ps) in candidates:
        d = dist(pa, ps)
        if d < min_d:
            min_d = d
            res_arc_p = pa
            res_seg_p = ps
    return (min_d, res_arc_p, res_seg_p)

实现注意事项

  • 所有浮点数范围判断都要加小容差(上述代码用1e-8),不要直接做相等判断,避免浮点精度误差导致合法候选被过滤
  • 如果计算的是跨度大于π的优弧,只需调整角度归一化后的区间判断逻辑即可,整体框架不需要改动
  • 线段与圆弧相交、相切的场景会被上述逻辑自动覆盖:相交时候选对距离为0,相切时切点会被投影候选捕获

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 12:48:21