我正在寻求将描述任意曲线的一系列有序(非常密集)的2D点转换为NURBS表示形式,可以将其写入IGES文件中。
我正在使用scipy.interpolate的splprep获取给定系列点的B样条表示,然后我假定NURBS的定义本质上就是这样,并说所有权重都等于1。但是我认为我是根本上会误解splprep的输出,尤其是“ B样条系数”与在某些CAD软件包中手动重新创建样条所需的控制点之间的关系(我使用的是Siemens NX11)。
我尝试了一个简单的例子,从一组稀疏点中逼近函数y = x ^ 3:
import scipy.interpolate as si
import numpy as np
import matplotlib.pyplot as plt
# Sparse points defining cubic
x = np.linspace(-1,1,7)
y = x**3
# Get B-spline representation
tck, u = si.splprep([x,y],s=0.0)
# Get (x,y) coordinates of control points
c_x = tck[1][0]
c_y = tck[1][1]
# Plotting
u_fine = np.linspace(0,1,1000)
x_fine, y_fine = si.splev(u_fine, tck)
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(x, y, 'o', x_fine, y_fine)
ax.axis('equal')
plt.show()
哪个提供以下参数:
>>> t
array([ 0. , 0. , 0. , 0. , 0.39084883,
0.5 , 0.60915117, 1. , 1. , 1. , 1. ])
>>> c_x
array([ -1.00000000e+00, -9.17992269e-01, -6.42403598e-01,
-2.57934892e-16, 6.42403598e-01, 9.17992269e-01,
1.00000000e+00])
>>> c_y
array([ -1.00000000e+00, -7.12577481e-01, -6.82922469e-03,
-1.00363771e-18, 6.82922469e-03, 7.12577481e-01,
1.00000000e+00])
>>> k
3
>>> u
array([ 0. , 0.25341516, 0.39084883, 0.5 , 0.60915117,
0.74658484, 1. ])
>>>
我假设两组系数(c_x,c_y)描述了构造样条曲线所需极点的(x,y)坐标。在NX中手动尝试可得到相似的样条曲线,尽管不尽相同,但区间中的其他点的评估与Python中的不同。当我将此手动样条线导出为IGES格式时,NX将结线更改为下面的值(同时显然保持相同的控制点/极点,并将所有权重设置为1)。
t_nx = np.array([0.0, 0.0, 0.0, 0.0, 0.25, 0.5, 0.75, 1.0, 1.0, 1.0, 1.0])
以另一种方式,将splprep结(t)写入IGES定义(具有所述“控制点”和权重= 1)似乎没有给出有效的样条。 NX和至少一个其他软件包无法对其进行评估,理由是“ B样条曲线的修剪或参数值无效”。
我认为至少有三种可能性:
我通过比较所有权重= 1的scipy B样条(link)和IGES NURBS样条的方程式来注销第一种可能性(link,第14页)。它们看起来完全相同,正是这使我相信splprep系数=控制点。
Any
帮助澄清以上几点,将不胜感激!注意,我想表示闭合曲线的可能性,因此,如果可能,请坚持使用splprep。
编辑:
我认为先使用splrep尝试此过程会更简单,因为输出对我而言似乎更直观。我假设返回的系数是控制点的y值,但不知道它们对应的x位置。因此,我尝试使用this矩阵方法根据样条曲线定义和输入数据来计算它们。 C矩阵只是输入数据。 N矩阵是每个x值的每个基函数的评估,我使用here所示的(略微修改的)递归函数进行了此操作。然后剩下的就是将N取反,并将C乘以C得到控制点。代码和结果如下:import numpy as np import scipy.interpolate as si # Functions to evaluate B-spline basis functions def B(x, k, i, t): if k == 0: return 1.0 if t[i] <= x < t[i+1] else 0.0 if t[i+k] == t[i]: c1 = 0.0 else: c1 = (x - t[i])/(t[i+k] - t[i]) * B(x, k-1, i, t) if t[i+k+1] == t[i+1]: c2 = 0.0 else: c2 = (t[i+k+1] - x)/(t[i+k+1] - t[i+1]) * B(x, k-1, i+1, t) return c1 + c2 def bspline(x, t, c, k): n = len(t) - k - 1 assert (n >= k+1) and (len(c) >= n) cont = [] for i in range(n): res = B(x, k, i, t) cont.append(res) return cont # Input data x = np.linspace(-1,1,7) y = x**3 # B-spline definition t, c, k = si.splrep(x,y) # Number of knots = m + 1 = n + k + 2 m = len(t) - 1 # Number of kth degree basis fcns n = m - k - 1 # Define C and initialise N matrix C_mat = np.column_stack((x,y)) N_mat = np.zeros(((n+1),(n+1))) # Calculate basis functions for each x, store in matrix for i, xs in enumerate(x): row = bspline(xs, t, c, k) N_mat[i,:] = row # Last value must be one... N_mat[-1,-1] = 1.0 # Invert the matrix N_inv = np.linalg.inv(N_mat) # Now calculate control points P = np.dot(N_inv, C_mat)
结果:
>>> P array([[ -1.00000000e+00, -1.00000000e+00], [ -7.77777778e-01, -3.33333333e-01], [ -4.44444444e-01, -3.29597460e-17], [ -3.12250226e-17, 8.67361738e-18], [ 4.44444444e-01, -2.77555756e-17], [ 7.77777778e-01, 3.33333333e-01], [ 1.00000000e+00, 1.00000000e+00]])
我认为是正确的,因为P的y值与splrep,c的系数匹配。有趣的是,x值似乎是结平均值(可以按以下方式单独计算)。对于熟悉数学的人来说,这个结果也许是显而易见的,但对我而言肯定不是。
def knot_average(knots, degree): """ Determines knot average vector from knot vector. :knots: A 1D numpy array describing knots of B-spline. (NB expected from scipy.interpolate.splrep) :degree: Integer describing degree of B-spline basis fcns """ # Chop first and last vals off knots_to_average = knots[1:-1] num_averaged_knots = len(knots_to_average) - degree + 1 knot_averages = np.zeros((num_averaged_knots,)) for i in range(num_averaged_knots): avg = np.average(knots_to_average[i: i + degree]) knot_averages[i] = avg return(knot_averages)
现在,要将它们转换为IGES NURBS,我认为是定义标准化结矢量,将权重都设置为等于1并从上方包括P个控制点的情况。我将其标准化如下,并在其下面包括了IGES文件。
但是,当我尝试将文件导入NX时,它再次在定义中指出无效的修整参数而失败。谁能告诉我这是否是有效的NURBS定义?
或者也许这是NX的一些限制?例如,我注意到在交互式绘制Studio花键时,结矢量被迫(夹紧)统一(如fang所指)。必须具有此约束(且权重全部= 1)才能唯一定义曲线。有趣的是,如果我强迫splrep使用统一的结点矢量(即钳制但否则统一)返回样条曲线表示,则将IGES读入。尽管从NX的角度来看,我也不认为这是必要的-这样做违背了目的首先拥有NURBS。所以这似乎不太可能,我回头想知道我对splrep输出的解释是否正确...有人可以指出我错了吗?
# Original knot vector >>> t array([-1. , -1. , -1. , -1. , -0.33333333, 0. , 0.33333333, 1. , 1. , 1. , 1. ]) mini = min(t) maxi = max(t) r = maxi - mini norm_t = (t-mini)/r # Giving: >>> norm_t array([ 0. , 0. , 0. , 0. , 0.33333333, 0.5 , 0.66666667, 1. , 1. , 1. , 1. ])
IGES定义:
S 1
,,11Hspline_test,13Hsome_path.igs,19HSpline to iges v1.0,4H 0.1,,,,,,, G 1
1.0, 2,2HMM,,,8H 8:58:19,,,,; G 2
126 1 1 1 0 0 0D 1
126 27 4 0 Spline1 1D 2
126,6,3,0,0,1,0,0.0,0.0,0.0,0.0,0.33333,0.5,0.6666666,1.0,1.0,1.0,1.0, 1P 1
1.0,1.0,1.0,1.0,1.0,1.0,1.0,-1.0,-1.0,0.0,-0.7777,-0.33333,0.0, 1P 2
-0.444444,0.0,0.0,0.0,0.0,0.0,0.4444444,0.0,0.0,0.777777777,0.33333, 1P 3
0.0,1.0,1.0,0.0,0.0,1.0,0.0,0.0,0.0,0; 1P 4
S 1G 2D 2P 4 T 1
我正在尝试将描述任意曲线的一系列有序(非常密集)的2D点转换为NURBS表示形式,可以将其写入IGES文件中。我正在使用scipy.interpolate的...
这个小众查询偶尔会帮助其他人,结果证明问题出在IGES中参数数据部分的格式不正确。描述样条线的数据每行不能超过64个字符。 splprep输出的解释是正确的,(c_x,c_y)数组描述了连续极点的(x,y)坐标。等效的NURBS定义只需要指定所有权重=1。