3. 空间圆弧插补
任务目标
给定三维空间中不共线的三个点,确定其所在平面上的圆弧,并按指定步长在圆弧上插补输出一系列三维坐标点。本案例实现完整的几何求解与插补计算,不依赖第三方几何库。
实现原理
三点确定一个空间圆:首先计算三点所在平面的法向量,进而求出圆心坐标与半径。建立以圆心为原点、法向量为z轴的局部坐标系,将空间圆弧映射为二维平面圆弧,在局部坐标系中按角度步长插补后,再通过旋转矩阵变换回世界坐标系。
关键完整代码
import numpy as np
def space_circle_arc(p1, p2, p3, step_angle_deg=1.0):
"""
三点确定空间圆弧,返回插补点列表(含起点和终点)
p1, p2, p3: 三维坐标,np.array 或 list,不共线
step_angle_deg: 插补步长(度)
"""
p1 = np.array(p1, dtype=float)
p2 = np.array(p2, dtype=float)
p3 = np.array(p3, dtype=float)
# 1. 计算平面法向量
v1 = p2 - p1
v2 = p3 - p1
n = np.cross(v1, v2)
norm_n = np.linalg.norm(n)
if norm_n < 1e-12:
raise ValueError("三点共线,无法确定圆弧")
n = n / norm_n
# 2. 计算圆心(利用垂直平分面交线)
# 圆心满足 |c-p1|^2 = |c-p2|^2 和 |c-p1|^2 = |c-p3|^2
# 即 2*(p2-p1)·c = |p2|^2 - |p1|^2
A = np.array([
2 * (p2 - p1),
2 * (p3 - p1),
n
])
b = np.array([
np.dot(p2, p2) - np.dot(p1, p1),
np.dot(p3, p3) - np.dot(p1, p1),
np.dot(n, p1) # 圆心在平面内: n·c = n·p1
])
center = np.linalg.solve(A, b)
# 3. 半径与局部坐标系基向量
r = np.linalg.norm(p1 - center)
# u: 从圆心指向p1的单位向量
u = (p1 - center) / r
# w: 法向量, v = w × u 构成右手系
w = n
v = np.cross(w, u)
# 4. 计算p2、p3在局部坐标系中的角度
def local_angle(pt):
d = pt - center
x = np.dot(d, u)
y = np.dot(d, v)
return np.arctan2(y, x)
theta1 = 0.0
theta2 = local_angle(p2)
theta3 = local_angle(p3)
# 5. 确定圆弧方向(从p1经p2到p3,保持方向一致)
# 将theta2、theta3调整到相对theta1的[0, 2π)区间
def norm_angle(th):
th = th - theta1
while th < 0:
th += 2 * np.pi
while th >= 2 * np.pi:
th -= 2 * np.pi
return th
a2 = norm_angle(theta2)
a3 = norm_angle(theta3)
# 若a3 < a2,说明跨过2π边界,a3加2π保证连续递增
if a3 < a2:
a3 += 2 * np.pi
# 6. 按步长插补
step = np.deg2rad(step_angle_deg)
n_steps = max(1, int(np.ceil(a3 / step)))
angles = np.linspace(0, a3, n_steps + 1)
# 7. 局部坐标转世界坐标
points = []
for th in angles:
local = np.array([r * np.cos(th), r * np.sin(th), 0.0])
world = center + local[0] * u + local[1] * v + local[2] * w
points.append(world)
return np.array(points)
if __name__ == "__main__":
# 示例:三点位于z=1平面上的一个半圆
p1 = [2.0, 0.0, 1.0]
p2 = [0.0, 2.0, 1.0]
p3 = [-2.0, 0.0, 1.0]
pts = space_circle_arc(p1, p2, p3, step_angle_deg=30.0)
print("插补点数:", len(pts))
for pt in pts:
print(f"({pt[0]:.4f}, {pt[1]:.4f}, {pt[2]:.4f})")
运行与验证
运行方式:保存为 arc_interp.py,执行 python arc_interp.py。需要NumPy环境(pip install numpy)。
验证方法:
- 示例输入三点在z=1平面且共圆,输出点应满足z坐标恒为1.0,且x²+y²≈4(半径2)。
- 插补点从(2,0,1)开始,经(0,2,1)到(-2,0,1),共7个点(步长30°,覆盖180°)。
- 可自行修改三点坐标验证任意平面圆弧,检查输出点是否共面且到圆心距离恒等于半径。
预期输出(示例输入)
插补点数: 7
(2.0000, 0.0000, 1.0000)
(1.7321, 1.0000, 1.0000)
(1.0000, 1.7321, 1.0000)
(0.0000, 2.0000, 1.0000)
(-1.0000, 1.7321, 1.0000)
(-1.7321, 1.0000, 1.0000)
(-2.0000, 0.0000, 1.0000)