C-5 B样条曲线
N i , 0 ( u ) = { 1 , u i ≤ u < u i + 1 0 , o t h e r s N_{i,0}(u)=\left\{\begin{matrix} 1 , \quad u_i\le u <u_{i+1} \\0 ,\quad others \qquad \quad\end{matrix}\right. Ni,0(u)={1,ui≤u<ui+10,others
N i , p ( u ) = u − u i u i + p − u i ⋅ N i , p − 1 ( u ) + u i + p + 1 − u u i + p + 1 − u i + 1 ⋅ N i + 1 , p − 1 ( u ) N_{i,p}(u)=\frac{u -u_i}{u_{i+p}-u_i} \cdot N_{i,p-1}(u) +\frac{u_{i+p+1} -u}{u_{i+p+1}-u_{i+1}} \cdot N_{i+1,p-1}(u) Ni,p(u)=ui+p−uiu−ui⋅Ni,p−1(u)+ui+p+1−ui+1ui+p+1−u⋅Ni+1,p−1(u)
C ( u ) = ∑ i = 1 n N i , p ( u ) ⋅ C o n t r o l P o i n t ( i ) C(u) = \sum ^n_{i=1} N_{i,p}(u)\cdot ControlPoint_{(i)} C(u)=i=1∑nNi,p(u)⋅ControlPoint(i)
-
C就是我们要求的曲线
-
只需要给定控制点数组和节点向量数组,我们就能根据以上公式得到一条B样条曲线。
-
n是给定控制点数组的长度,p是B样条的阶数,一般就直接设置为3了, u i u_i ui就算节点向量数组,可以从公式中发现,节点向量的长度等于 控制点数组长度+阶数+1。(从递归公式中的 u i + p + 1 u_{i+p+1} ui+p+1可以看出来)
-
前面的参数 N i , p N_{i,p} Ni,p,只需要给定节点向量和阶数就能直接提前求出来,可以写成矩阵的形式。方便后面移动控制点求解。
-
在Step标准中,B样条是如下定义的:
-
#21=B_SPLINE_CURVE_WITH_KNOTS('',3,(#118,#119,#120,#121),.UNSPECIFIED.,.F.,.F.,(4,4),(0.,1.),.UNSPECIFIED.);//相当于输入控制点 (#118,#119,#120,#121),和节点向量(0,0,0,0,1,1,1,1)//有一个重复的节点,那里的连续性就会减一,具体可以见Games102,刘利刚老师的课//3指3阶B样条曲线,第一个F指这个曲线没有闭合,(4,4)是修饰后面的节点向量,这条曲线比较简单,一般是下面#31这样的//1阶B样条曲线的连续性是C1,也就是连连上,导数不连续。而3阶B样条曲线的连续性是C3#31=B_SPLINE_CURVE_WITH_KNOTS('',3,(#88,#89,#90,#91,#92,#93,#94),.UNSPECIFIED.,.T.,.F.,(4,1,1,1,4),(0.,0.190110440975709,0.596109450554148, 0.871779783977488,1.),.UNSPECIFIED.);
-
局部性
- 局部性:我们操控第i个控制点,只能控制有限的一段曲线而不是影响到这条曲线
- 我们发现,控制点i前面的参数是 N i , p N_{i,p} Ni,p.这个参数一递归,最多最多只能影响到 u i u_i ui到 u i + p + 1 u_{i+p+1} ui+p+1这P+2个节点范围内的曲线,因为递归公式中参数的范围就是这个,然后在用阶梯函数吧,一点点线性插值,构成这一段B样条曲线
代码
- 对于上述的#21的样条曲线,可以用如下代码求解,并将其离散化成32个点
- 代码和详细注释如下,代码中的t相当于公式中的u,一般是从0到1的参变量
#include <iostream>
#include <vector>
#include <array> class Point {
public:double x=0;double y=0;
};
class NubrsEdge {
public:bool ISClOSED=0;//B样条曲线的介数,一般为3int P;//控制点std::vector<Point> ControlPoints;//节点向量std::vector<double> u;//利用节点向量计算得到的参数,我们把B样条曲线统一分成32份std::vector<std::array<double, 32>> N_p;//递归计算参数Ndouble N(int i,int p,double t) {if (p == 0) {if (u[i] <= t && t < u[i + 1]) return 1;else return 0;}else{double x1 = 0;//对重复节点的处理!!!!!!!!!!//不然就出现除数等于0的情况了if (u[i + p] != u[i]) x1 = (t - u[i]) / (u[i + p] - u[i]);double x2 = 0;//对重复节点的处理if(u[i + p + 1] != u[i + 1])x2 = (u[i + p + 1] - t) / (u[i + p + 1] - u[i + 1]);return x1 * N(i, p - 1, t) + x2 * N(i + 1, p - 1, t);}}//计算参数,$N_{i,p}(u)$void calculate_N_p() {N_p.resize(ControlPoints.size());for (int i = 0; i < ControlPoints.size(); i++) {for (int t = 0; t < 32; t++) {N_p[i][t] = N(i, P, double(t)/31.00001);}}}//相当于在做矩阵的乘法std::vector<Point> Discretization() {calculate_N_p();std::vector<Point> result(32);for (int t = 0; t < 32; t++) {for(int i=0;i < ControlPoints.size(); i++){result[t].x += ControlPoints[i].x * N_p[i][t];result[t].y += ControlPoints[i].y * N_p[i][t];}}return result;}
};
int main()
{NubrsEdge e;// u.size() = 8 = ControlPoints.size + P +1 =4 + 3 + 1e.ControlPoints= {{-410.738133667533,-34.3141639975569},{-360.696916167804,124.126883382823},{-317.136530803152,125.95962808178},{-280.056977573576,-28.8159299006879}};e.u = { 0, 0, 0, 0, 1,1,1, 1 };e.P = 3;// 离散化曲线std::vector<Point> curvePoints = e.Discretization();// 打印结果for (int i = 0; i < curvePoints.size(); i++) {std::cout << "Point " << i << ": (" << curvePoints[i].x << ", " << curvePoints[i].y << ")" << std::endl;}return 0;
}
- 可以发现起始点和结束点与第一个和最后一个控制点重合