├── Bspline ├── U_quasi_uniform.m ├── U_piecewise_Bezier.m ├── BaseFunction.m ├── DrawSpline.m └── main.m └── README.md /Bspline/U_quasi_uniform.m: -------------------------------------------------------------------------------- 1 | % U_quasi_uniform.m文件 2 | function NodeVector = U_quasi_uniform(n, k) 3 | % 准均匀B样条的节点向量计算,共n+1个控制顶点,k次B样条 4 | NodeVector = zeros(1, n+k+2); 5 | piecewise = n - k + 1; % 曲线的段数 6 | if piecewise == 1 % 只有一段曲线时,n = k 7 | for i = n+2 : n+k+2 8 | NodeVector(1, i) = 1; 9 | end 10 | else 11 | flag = 1; % 不止一段曲线时 12 | while flag ~= piecewise 13 | NodeVector(1, k+1+flag) = NodeVector(1, k + flag) + 1/piecewise; 14 | flag = flag + 1; 15 | end 16 | NodeVector(1, n+2 : n+k+2) = 1; 17 | end -------------------------------------------------------------------------------- /Bspline/U_piecewise_Bezier.m: -------------------------------------------------------------------------------- 1 | % U_piecewise_Bezier.m文件 2 | function NodeVector = U_piecewise_Bezier(n, k) 3 | % 分段Bezier曲线的节点向量计算,共n+1个控制顶点,k次B样条 4 | % 分段Bezier端节点重复度为k+1,内间节点重复度为k,且满足n/k为正整数 5 | 6 | if ~mod(n, k) && (~mod(k, 1) && k>=1) % 满足n是k的整数倍且k为正整数 7 | NodeVector = zeros(1, n+k+2); % 节点矢量长度为n+k+2 8 | NodeVector(1, n+2 : n+k+2) = ones(1, k+1); % 右端节点置1 9 | 10 | piecewise = n / k; % 设定内节点的值 11 | Flg = 0; 12 | if piecewise > 1 13 | for i = 2 : piecewise 14 | for j = 1 : k 15 | NodeVector(1, k+1 + Flg*k+j) = (i-1)/piecewise; 16 | end 17 | Flg = Flg + 1; 18 | end 19 | end 20 | 21 | else 22 | fprintf('error!\n'); 23 | end -------------------------------------------------------------------------------- /Bspline/BaseFunction.m: -------------------------------------------------------------------------------- 1 | % BaseFunction.m文件 2 | function Nik_u = BaseFunction(i, k , u, NodeVector) 3 | % 计算基函数Ni,k(u),NodeVector为节点向量 4 | 5 | if k == 0 % 0次B样条 6 | if (u >= NodeVector(i+1)) && (u < NodeVector(i+2)) 7 | Nik_u = 1.0; 8 | else 9 | Nik_u = 0.0; 10 | end 11 | else 12 | Length1 = NodeVector(i+k+1) - NodeVector(i+1); 13 | Length2 = NodeVector(i+k+2) - NodeVector(i+2); % 支撑区间的长度 14 | if Length1 == 0.0 % 规定0/0 = 0 15 | Length1 = 1.0; 16 | end 17 | if Length2 == 0.0 18 | Length2 = 1.0; 19 | end 20 | Nik_u = (u - NodeVector(i+1)) / Length1 * BaseFunction(i, k-1, u, NodeVector) ... 21 | + (NodeVector(i+k+2) - u) / Length2 * BaseFunction(i+1, k-1, u, NodeVector); 22 | end -------------------------------------------------------------------------------- /Bspline/DrawSpline.m: -------------------------------------------------------------------------------- 1 | % DrawSpline.m文件 2 | function DrawSpline(n, k, P, NodeVector) 3 | % B样条的绘图函数 4 | % 已知n+1个控制顶点P(i), k次B样条,P是2*(n+1)矩阵存控制顶点坐标, 节点向量NodeVector 5 | plot(P(1, 1:n+1), P(2, 1:n+1),... 6 | 'o','LineWidth',1,... 7 | 'MarkerEdgeColor','k',... 8 | 'MarkerFaceColor','g',... 9 | 'MarkerSize',6); 10 | line(P(1, 1:n+1), P(2, 1:n+1)); 11 | Nik = zeros(n+1, 1); 12 | for u = 0 : 0.005 : 1-0.005 13 | for i = 0 : 1 : n 14 | Nik(i+1, 1) = BaseFunction(i, k , u, NodeVector); 15 | end 16 | p_u = P * Nik; 17 | if u == 0 18 | tempx = p_u(1,1); 19 | tempy = p_u(2,1); 20 | line([tempx p_u(1,1)], [tempy p_u(2,1)],... 21 | 'Marker','.','LineStyle','-', 'Color',[.3 .6 .9], 'LineWidth',3); 22 | else 23 | line([tempx p_u(1,1)], [tempy p_u(2,1)],... 24 | 'Marker','.','LineStyle','-', 'Color',[.3 .6 .9], 'LineWidth',3); 25 | tempx = p_u(1,1); 26 | tempy = p_u(2,1); 27 | end 28 | end -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 本程序实现了 均匀B样条曲线、准均匀B样条曲线以及分段Bezier曲线的绘制。 2 | 3 | 本程序为参考[MATLAB绘制B样条曲线_NobodyZhou-CSDN博客](https://blog.csdn.net/mr_grit/article/details/45603627)进行修改注释学习,仅供笔者及同学参考,侵权删。 4 | 5 | 参考资料: 6 | 7 | B样条法内容不多,但是设计程序时细节很影响最后的实验结果,而网络上许多讲解都有着或多或少的错误。以下提供的参考资料,笔者仅提供笔者发现的错误,建议读者仔细检查。 8 | 9 | [深入理解B样条曲线(上) ](https://zhuanlan.zhihu.com/p/144042470) 10 | 11 | 本文详细地介绍了均匀周期性 B 样条曲线的生成过程。 12 | 13 | 错误:文章最后说道:“然后将 t 从 0 取至 6,获得的点集就是我们想要的均匀周期性 B 样条曲线。”,t的取值区间出错,必须是所有基函数都不为0的支撑区间。同时计算多项式递推公式出错。正确公式如下。 14 | 15 | ![正确基函数](https://img-blog.csdn.net/20180328202647357) 16 | 17 | [计算机图形学 学习笔记(十一):曲线曲面(三):B样条 曲线与曲面_学愈进而愈惘-CSDN博客](https://blog.csdn.net/jurbo/article/details/75125663) 18 | 19 | 本文对应的课程[计算机图形学bezier曲线曲面B样条曲线曲面_哔哩哔哩_bilibili](https://www.bilibili.com/video/BV1Dt411f7Qj?p=23)讲解出错,首先是递推公式出错,正确公式如上。其次是如下的**B样条曲线的数字表达式**第一行的的后面的方括号应该为圆括号),不能包括此值。 20 | 21 | ![这里写图片描述](https://img-blog.csdn.net/20170714163343497?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvSnVyYm8=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 22 | 23 | 学习时也要注意B样条曲线的凸包性。 24 | 25 | ![凸包性](https://img-blog.csdn.net/20170714164859351?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvSnVyYm8=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 26 | -------------------------------------------------------------------------------- /Bspline/main.m: -------------------------------------------------------------------------------- 1 | % 绘制三种类型的B样条曲线 2 | clear all; 3 | %控制顶点 4 | P = [9.036145, 21.084337, 37.607573, 51.893287, 61.187608; 5 | 51.779661, 70.084746, 50.254237, 69.745763, 49.576271]; 6 | 7 | n = 4; 8 | %这里的n不为控制顶点P的数量,n其实为控制顶点P的数量-1,这个值要注意要与P对应 9 | k = 2; 10 | %B样条曲线的次数,B样条阶数=k+1,这个值注意越大算法复杂度越大 11 | %k=5时,电脑16g内存受不了了 12 | 13 | flag = 2; 14 | % flag = 1,绘制均匀B样条曲线,不经过起点和终点 15 | % flag = 2, 绘制准均匀B样条曲线,经过起点和终点 16 | % flag = 3, 绘制分段Bezier曲线,经过起点和终点 17 | 18 | switch flag 19 | case 1 20 | NodeVector = linspace(0, 1, n+k+2); 21 | % 均匀B样条的节点矢量数目等于控制顶点个数n+1与B样条阶数k+1之和 22 | %这里把节点取值归一化,但事实上不影响最后的曲线点计算结果 23 | %因为最后关于节点取值的运算是节点之间的除法运算 24 | 25 | % 绘制样条曲线 26 | plot(P(1, 1:n+1), P(2, 1:n+1),... 27 | 'o','LineWidth',1,... 28 | 'MarkerEdgeColor','k',... 29 | 'MarkerFaceColor','g',... 30 | 'MarkerSize',6);%画出控制顶点的位置 31 | line(P(1, 1:n+1), P(2, 1:n+1));%连接控制顶点 32 | Nik = zeros(n+1, 1); 33 | %不同控制点对应的基函数计算结果(权值),其长度与控制点长度一致 34 | for u = k/(n+k+1) : 0.001 : (n+1)/(n+k+1) 35 | %设置u取样值,对应归一化后的[k(B样条曲线的次数),n+1(控制顶点P的数量)] 36 | %相当于取值为[k(B样条曲线的次数),n+2(控制顶点P的数量)) 37 | % for u = 0 : 0.005 : 1 这个是原程序测试 38 | for i = 0 : 1 : n %与控制顶点对应 39 | Nik(i+1, 1) = BaseFunction(i, k , u, NodeVector); 40 | %计算每个控制定点对应的基函数取值 41 | end 42 | p_u = P * Nik; 43 | %控制定点对应的基函数取值(权重)与控制定点左边相乘得到B样条轨迹坐标 44 | line(p_u(1,1), p_u(2,1), 'Marker','.','LineStyle','-', 'Color',[.3 .6 .9]); 45 | %画出设置u取样值对应的曲线中的点,点全部汇合在一起就绘制出曲线 46 | end 47 | case 2 48 | NodeVector = U_quasi_uniform(n, k); % 准均匀B样条的节点矢量 49 | DrawSpline(n, k, P, NodeVector); 50 | case 3 51 | NodeVector = U_piecewise_Bezier(n, k); % 分段Bezier曲线的节点矢量 52 | DrawSpline(n, k, P, NodeVector); 53 | otherwise 54 | fprintf('error!\n'); 55 | end --------------------------------------------------------------------------------