基于起点、终点与初始方向的0-90度圆弧绘制实现方法
《铁路大亨3》定端点定初始方向0-90°圆弧实现方案
这个需求是典型的单约束圆弧拟合问题,几何上存在唯一符合要求的解,不需要迭代,直接用解析公式即可算出所有绘制参数。
输入参数约定
- 起点坐标:
P0 = (x0, y0) - 终点坐标:
P1 = (x1, y1) - 起点处切线方向:
dir = (dx, dy),需提前归一化为单位长度向量 - 约束:圆弧圆心角范围为0°~90°,起点切线与
dir完全重合
核心推导逻辑
圆的两个基础性质可以直接定位圆心:
- 过圆上一点的切线的垂线必然经过圆心,即圆心一定在过P0、与
dir垂直的法线上 - 圆心到P0、P1的距离相等,均为圆弧半径R
基于以上性质直接列方程求解:
- 记P0指向P1的向量为
v = (vx, vy) = (x1-x0, y1-y0) - 垂直于
dir的单位法向量有两个方向候选:n1=(-dy, dx)、n2=(dy, -dx),分别对应切线左右两侧 - 圆心可以表示为参数形式:
C = P0 + R * n(n为选中的单位法向量,R为正半径) - 代入
|C-P1|=R的距离约束,利用n是单位向量的特性消去二次项,直接解出半径:
其中R = (vx² + vy²) / (2 * (vx*nx + vy*ny))nx/ny是选中法向量的x/y分量 - 解出R后即可得到圆心坐标,再通过两个半径向量
P0-C、P1-C的点积计算圆心角θ,筛选出θ在0°~90°区间的合法解即可。
边界判定规则:
- 若法向量代入后分母为0,说明P1在P0的初始切线上,对应α=0°场景,圆弧退化为P0到P1的直线段
- 若算出的R为负数,或圆心角θ>90°,说明当前法向量方向错误,换另一个候选法向量重算即可
- α=90°场景下,合法解对应的圆心角恰好为90°,即标准四分之一圆弧
可直接落地的伪代码
import math def build_rail_arc(p0: tuple[float, float], p1: tuple[float, float], dir: tuple[float, float]): x0, y0 = p0 x1, y1 = p1 dx, dy = dir vx = x1 - x0 vy = y1 - y0 EPS = 1e-6 PI_HALF = math.pi / 2 # 两点重合直接返回空 if abs(vx) < EPS and abs(vy) < EPS: return None # 两个候选法向量 n_candidates = [(-dy, dx), (dy, -dx)] selected = None for nx, ny in n_candidates: denom = 2 * (vx * nx + vy * ny) # 分母为0对应直线场景 if abs(denom) < EPS: continue r = (vx**2 + vy**2) / denom if r <= EPS: continue # 计算圆心坐标 cx = x0 + r * nx cy = y0 + r * ny # 计算圆心角 vec0_x = x0 - cx vec0_y = y0 - cy vec1_x = x1 - cx vec1_y = y1 - cy dot = vec0_x * vec1_x + vec0_y * vec1_y cos_theta = dot / (r * r) cos_theta = max(-1.0, min(1.0, cos_theta)) # 修正浮点误差 theta = math.acos(cos_theta) # 筛选0-90度范围内的合法解 if -EPS <= theta <= PI_HALF + EPS: # 计算圆弧起始角、扫掠方向 start_angle = math.atan2(vec0_y, vec0_x) # 叉积判断弯向(扫掠方向) cross = dx * vy - dy * vx sweep_dir = 1 if cross > 0 else -1 selected = { "type": "arc", "center": (cx, cy), "radius": r, "start_angle": start_angle, "sweep_angle": theta * sweep_dir } break # 无合法圆弧则返回直线(α=0场景) if selected is None: selected = { "type": "line", "start": p0, "end": p1 } return selected
绘制适配说明
- 所有浮点计算预留1e-6的精度容差,避免边界场景下计算错误
- 返回的圆弧参数可以直接传入绝大多数2D绘图接口(Canvas、GDI+、Cocos/Unity图形组件等),注意不同接口对角度正负(顺时针/逆时针)的定义差异,调整
sweep_dir的取值即可 - 通用场景、α=0直线场景、α=90直角四分之一圆场景均能通过该逻辑正确生成
内容的提问来源于stack exchange,提问作者PRR S2 6-8-6 turbine engine
相关产品推荐
相关产品推荐

