如何对B样条曲线做弧长重参数化并生成按长度参数化的新B样条?
B样条曲线弧长参数化近似实现方案
B样条不存在解析的弧长参数化表达式,可通过数值近似方案实现需求,误差可通过采样密度灵活控制。
实现步骤
- 高密度采样原始曲线,计算各采样点的累计弧长,构建原始参数
t到弧长s的映射表 - 通过线性插值构建逆映射:输入归一化弧长比例(01,对应总长度0%100%),输出对应原始参数
t - 采样足够多的均匀弧长点,拟合新的二次B样条,得到近似弧长参数化的新曲线
完整代码实现
from splipy import Curve, BSplineBasis from splipy.utils import interpolate_curve import numpy as np import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D ORDER = 3 # 二次B样条 cp = np.array([[0,0,0], [0, 1,0], [0,2,0], [0,3,0], [1,3,0],[2,3,0], [3,3,0]]) num_cp = cp.shape[0] # 构建原始曲线 knot = np.array([0. , 0. , 0. , 0.2, 0.4, 0.6, 0.8, 1. , 1. , 1. ]) basis = BSplineBasis(order=ORDER, knots=knot, periodic=-1) curve = Curve(basis=basis, controlpoints=cp, rational=False) # -------------------------- 弧长参数化核心逻辑 -------------------------- # 1. 高密度采样计算弧长映射表,采样点数越高精度越高 SAMPLE_DENSITY = 1000 t_sample = np.linspace(0, 1, SAMPLE_DENSITY) pts_sample = curve(t_sample) # 计算相邻点距离和累计弧长 delta_l = np.linalg.norm(np.diff(pts_sample, axis=0), axis=1) cum_length = np.concatenate([[0], np.cumsum(delta_l)]) total_length = cum_length[-1] # 归一化累计弧长到0~1区间 cum_length_norm = cum_length / total_length # 2. 逆映射函数:输入归一化弧长比例s_norm(0~1),返回对应原始参数t def s_to_t(s_norm): # 找到s_norm所在的区间位置 idx = np.searchsorted(cum_length_norm, s_norm, side='right') - 1 if idx >= len(cum_length_norm) - 1: return t_sample[-1] # 线性插值计算精确t值 alpha = (s_norm - cum_length_norm[idx]) / (cum_length_norm[idx+1] - cum_length_norm[idx]) return t_sample[idx] + alpha * (t_sample[idx+1] - t_sample[idx]) # 3. 拟合新的弧长参数化B样条 # 采样足够多的均匀弧长点作为拟合依据 NUM_FIT_POINTS = 100 s_fit = np.linspace(0, 1, NUM_FIT_POINTS) t_fit = np.vectorize(s_to_t)(s_fit) pts_fit = curve(t_fit) # 拟合二次B样条 arc_param_curve = interpolate_curve(pts_fit, order=ORDER) # -------------------------- 效果验证 -------------------------- # 对新曲线用均匀参数采样,验证点分布均匀 u_new = np.linspace(0, 1, 10) points_10_even = arc_param_curve(u_new) # 绘图对比 fig = plt.figure(figsize=(12,6)) ax1 = fig.add_subplot(121, projection='3d') ax2 = fig.add_subplot(122, projection='3d') # 左图:原始曲线不均匀采样 t_origin = np.linspace(0,1,100) pts_origin = curve(t_origin) x,y,z = pts_origin.T ax1.plot(x,y,z,'b-') x,y,z = curve(np.linspace(0,1,10)).T ax1.plot(x,y,z,'r*') ax1.set_title('原始曲线均匀参数采样') # 右图:新曲线均匀参数采样 t_new = np.linspace(0,1,100) pts_new = arc_param_curve(t_new) x,y,z = pts_new.T ax2.plot(x,y,z,'g-') x,y,z = points_10_even.T ax2.plot(x,y,z,'r*') ax2.set_title('弧长参数化曲线均匀参数采样') plt.show()
效果说明
原始曲线的均匀参数采样点分布不均,弯曲处点更密集:
新生成的arc_param_curve输入均匀t值即可得到沿弧长均匀分布的点,精度可通过调整SAMPLE_DENSITY和NUM_FIT_POINTS参数控制,数值越高近似效果越好。
内容的提问来源于stack exchange,提问作者Will Sni
相关产品推荐
相关产品推荐

