精华内容
下载资源
问答
  • 三次B样条曲线

    2016-12-01 15:53:37
    在屏幕上使用鼠标左键绘制数量大于4的任意顶点形成控制多边形,鼠标右键绘制三次B样条曲线,同时在控制多边形的每一个三角形内用虚线表示三次B样条曲线的几何生产原理 编译器VC2010
  • 三次B样条曲线拟合算法

    万次阅读 多人点赞 2017-01-17 22:10:28
    三次B样条曲线方程B样条曲线分为近似拟合和插值拟合,所谓近似拟合就是不过特征点,而插值拟合就是通过特征点,但是插值拟合需要经过反算得到控制点再拟合出过特征点的B样条曲线方程。这里会一次介绍两种拟合算法。...

    1 三次B样条曲线方程

    B样条曲线分为近似拟合和插值拟合,所谓近似拟合就是不过特征点,而插值拟合就是通过特征点,但是插值拟合需要经过反算得到控制点再拟合出过特征点的B样条曲线方程。这里会一次介绍两种拟合算法。首先介绍B样条的曲线方程。
    B样条曲线的总方程为:P(t)=∑i=0nPiFi,k(t)P(t)=\sum_{i=0}^{n} P_{i}F_{i,k}(t)P(t)=i=0nPiFi,k(t) (1)
    其中PiP_iPi是控制曲线的特征点,Fi,k(u)F_{i,k}(u)Fi,k(u)则是K阶B样条基函数。
    1.1 三次B样条曲线方程中基函数为:
    Fi,k(t)=1k!∑m=0k−i(−1)m(mk+1)(t+k−m−j)kF_{i,k}(t)=\frac{1}{k!}\sum_{m=0}^{k-i}(-1)^{m}\binom{m}{k+1}(t+k-m-j)^kFi,k(t)=k!1m=0ki(1)m(k+1m)(t+kmj)k (2)
    其中(mk+1)\binom{m}{k+1}(k+1m)表示阶乘,化成看的明白的式子就是:

    这里写图片描述
    将图片上的基函数代入到方程(1)中,就是:
    P(t)=P0∗F0,3(t)+P1∗F1,3(t)+P2∗F2,3(t)+P3∗F3,3(t)P(t)= P_0*F_{0,3}(t)+P_1*F_{1,3}(t)+P_2*F_{2,3}(t)+P_3*F_{3,3}(t)P(t)=P0F0,3(t)+P1F1,3(t)+P2F2,3(t)+P3F3,3(t) (3)
    方程(3)就是三次B样条曲线方程。


    2019-04-18 更新

    有小伙伴提到上式(2)的j是什么意思,其实j就是控制点的索引值。这里我把书上的公式摘抄下来,可能看起来更为清晰。
    参考书籍:《计算机图形学 第3版》何援军 第13章
    三次B样条曲线计算
    F0,3(t)=13!∑j=03(−1)jC4j)(t+3−0−j)3F_{0,3}(t)=\frac{1}{3!}\sum_{j=0}^{3}(-1)^{j}C^{j}_{4})(t+3-0-j)^3F0,3(t)=3!1j=03(1)jC4j)(t+30j)3

    =16[(−1)0C40(t+3)3+(−1)1C41(t+2)3+(−1)2C42(t+1)3+(−1)3C43t3]=\frac{1}{6}[(-1)^{0}C^{0}_{4}(t+3)^{3}+(-1)^{1}C^{1}_{4}(t+2)^{3}+(-1)^{2} C^{2}_{4}(t+1)^{3}+(-1)^{3}C^{3}_{4}t^{3}]=61[(1)0C40(t+3)3+(1)1C41(t+2)3+(1)2C42(t+1)3+(1)3C43t3]

    =16(−t3+3t2−3t+1)=16(1−t)3=\frac{1}{6}(-t^{3}+3t^{2}-3t+1)=\frac{1}{6}(1-t)^{3}=61(t3+3t23t+1)=61(1t)3

    F1,3(t)=13!∑j=02(−1)jC4j)(t+3−1−j)3F_{1,3}(t)=\frac{1}{3!}\sum_{j=0}^{2}(-1)^{j}C^{j}_{4})(t+3-1-j)^3F1,3(t)=3!1j=02(1)jC4j)(t+31j)3

    =16[(−1)0C40(t+2)3+(−1)1C41(t+1)3+(−1)2C42t3]=\frac{1}{6}[(-1)^{0}C^{0}_{4}(t+2)^{3}+(-1)^{1}C^{1}_{4}(t+1)^{3}+(-1)^{2} C^{2}_{4}t^{3}]=61[(1)0C40(t+2)3+(1)1C41(t+1)3+(1)2C42t3]

    =16(3t3−6t2+4)=\frac{1}{6}(3t^{3}-6t^{2}+4)=61(3t36t2+4)

    F2,3(t)=13!∑j=01(−1)jC4j)(t+3−2−j)3F_{2,3}(t)=\frac{1}{3!}\sum_{j=0}^{1}(-1)^{j}C^{j}_{4})(t+3-2-j)^3F2,3(t)=3!1j=01(1)jC4j)(t+32j)3

    =16[(−1)0C40(t+1)3+(−1)1C41t3=\frac{1}{6}[(-1)^{0}C^{0}_{4}(t+1)^{3}+(-1)^{1}C^{1}_{4}t^{3}=61[(1)0C40(t+1)3+(1)1C41t3

    =16(−3t3+3t2+3t+1)=\frac{1}{6}(-3t^{3}+3t^{2}+3t+1)=61(3t3+3t2+3t+1)

    F3,3(t)=13!∑j=00(−1)jC4j)(t+3−3−j)3F_{3,3}(t)=\frac{1}{3!}\sum_{j=0}^{0}(-1)^{j}C^{j}_{4})(t+3-3-j)^3F3,3(t)=3!1j=00(1)jC4j)(t+33j)3

    =16[(−1)0C40t3=\frac{1}{6}[(-1)^{0}C^{0}_{4}t^{3}=61[(1)0C40t3

    =16t3=\frac{1}{6}t^{3}=61t3


    2 三次B样条曲线近似拟合

    近似拟合很简单。不需要求控制点,求得Fi,k(t)F_{i,k}(t)Fi,k(t),由上述方程(3),代入P0,P1,P2,P3P_0,P_1,P_2,P_3P0,P1,P2,P3就可以得到由这四个点近似拟合的一段三次B样条曲线,起始点在P0P_0P0,终点在P1P_1P1,对于闭合轮廓,最后一段可以取前两点做辅助,拟合实验结果我最后一块给出。这种近似拟合曲线光滑,但是最大不足就是不过特征点,也就是不过PiP_iPi,需要过点需要反求控制点再拟合。

    3 三次B样条插值拟合

    插值拟合较为复杂。其实也不算是很复杂,找资料过程和理解过程是一个复杂的过程。不过有了前面大神做工作,我们只是借用别人的成果写代码就好了。我给大家看一篇论文,大家可以百度或者去知网搜索,闭合 B 样条曲线控制点的快速求解算法及应用。文章讲解了反求控制点的具体步骤,写的非常详细,基本上贴近代码的那种。大家可以根据这篇论文反求控制点,拟合出来的三次B样条曲线是经过PiP_iPi的。代码就不放了,很多,可以根据我给的那篇论文直接编写相应代码,有问题可以私信我,知无不言。
    ##4 拟合结果
    这里写图片描述 原轮廓
    这里写图片描述 近似拟合轮廓。可以看到没过黑色特征点,只是近似拟合
    这里写图片描述 插值拟。可以看到曲线经过黑色特征点,不过有一些不足之处。

    ##5 总结
    三次B样条曲线拟合轮廓效果还是可以,较之Beizer(可以参考我博客三次Beizer曲线拟合算法),B样条将一些细节描述的很好,很多细节之处都贴近原轮廓,但是有一些不足之处,可以看到对直线拟合效果不是很好。两篇博客都是关于闭合轮廓的拟合,对于非闭合或者只是一段曲线拟合,还有一种曲线是很好的,《数值分析》提到过,叫三次样条插值拟合,拟合效果很好,我做过拟合一元三次方程曲线,拟合效果跟原曲线非常贴近,不过过程中需要用到追赶法,而追赶法需要满足一个条件,对于闭合曲线三次样条插值是不满足这个条件的,所以我没去深研究,大家可以去试一试。谢谢大家!

    -------------------------------------------------2018-10-30--------------------------------------------
    真的很感谢大家的支持,这一年都比较忙,找实习and找工作,而且这个东西是研一上学期搞的,现在看都毫无印象,对自己说一句:牛逼。我把代码放在网盘了(能运行,但现在没效果,大家可以检查下,之前是OK的),本来想上传到CSDN,好像要C币,而且要审核,太墨迹了。
    链接: https://pan.baidu.com/s/1mSQMmvL71gwEAqgiT6O9Gg 提取码: xv5f

    展开全文
  • 三次B样条曲线算法 计算机图形学 MFC 孔令德 代码 课件案例 习题答案 第七章 三次B样条曲线算法 计算机图形学 MFC 孔令德 代码 课件案例 习题答案 第七章
  • 在OpenGL的基础上进行编写,绘制三次B样条曲线,可以通过鼠标设置控制点位置、拖动控制点观察曲线动态变化
  • 简单详细的均匀三次B样条曲线插值MATLAB代码,有注释
  • 非均匀三次B样条曲线插值的Jacobi-PIA算法
  • 三次B样条曲线骨架卷积曲面造型,张海林,金小刚,卷积曲面在树、海生物等复杂光滑物体的造型中具有重要的应用价值。本文提出了一种基于B样条曲线降阶的三次B样条曲线骨架卷积曲面�
  • 使用散乱点集重建曲线曲面,在逆向工程和计算机视觉中有着广泛的应用。提出基于SOM网络的三次B样条曲线...用特征点反求三次B样条曲线控制点,利用控制点重建三次B样条曲线。试验结果表明,算法取得的曲线重建效果良好。
  • 三次B样条曲线拟合C++

    千次阅读 2020-08-21 11:20:17
    三次B样条曲线拟合C++ B样条曲线的方程:P=∑i=0nPiFi,k(t)\sum_{i=0}^nP_iF_{i,k}(t)∑i=0n​Pi​Fi,k​(t) 其中Fi,k(t)F_{i,k}(t)Fi,k​(t)为基函数,三次B样条的基函数为: F0,3(t)=16(1−t)3F_{0,3}(t)=\...

    B样条曲线的方程:P=i=0nPiFi,k(t)\sum_{i=0}^nP_iF_{i,k}(t)
    其中Fi,k(t)F_{i,k}(t)为基函数,三次B样条的基函数为:
    F0,3(t)=16(1t)3F_{0,3}(t)=\displaystyle{\frac{1}{6}{(1-t)}^3}

    F1,3(t)=16(3t36t2+4)F_{1,3}(t)=\displaystyle{\frac{1}{6}(3t^3-6t^2+4)}

    F2,3(t)=16(3t3+3t2+3t+1)F_{2,3}(t)=\displaystyle{\frac{1}{6}(-3t^3+3t^2+3t+1)}

    F3,3(t)=16t3F_{3,3}(t)=\displaystyle{\frac{1}{6}t^3}

    所以,三次B样条的方程式为:
    P=P0F0,3(t)+P1F1,3(t)+P2F2,3(t)+P3F3,3(t)P=P_0F_{0,3}(t)+P_1F_{1,3}(t)+P_2F_{2,3}(t)+P_3F_{3,3}(t)
    把基函数代入可以简化为:
    P=w0+w1t+w2t2+w3t3P=w_0+w_1t+w_2t^2+w_3t^3 (0≤t<≤1)
    其中,w0=16(P0+4P1+P2)w_0=\displaystyle{\frac{1}{6}(P_0+4P_1+P_2)}

    w1=12(P0P2)w_1=\displaystyle{-\frac{1}{2}(P_0-P_2)}

    w2=12(P02P1+P2)w_2=\displaystyle{\frac{1}{2}(P_0-2P_1+P_2)}

    w3=16(P03P1+3P2P3)w_3=\displaystyle{-\frac{1}{6}(P_0-3P_1+3P_2-P_3)}

    因此,每四个离散点就可拟合一段曲线,比如P0,P1,P2,P3P_0,P_1,P_2,P_3可以拟合一段光滑曲线,P1,P2,P3,P4P_1,P_2,P_3,P_4可以拟合下一段,相邻两段曲线是平滑过渡的,以此类推N个点可以拟合出N-3段平滑相接的曲线。
    在这里插入图片描述以上是非闭合曲线的拟合,闭合曲线只需离散点集首尾相连,也就是说,还需用PN1,P0,P1,P2P_{N-1},P_0,P_1,P_2拟合一段曲线。
    c++代码如下:

    /*B样条曲线拟合
    @return 返回拟合得到的曲线
    @discretePoints 输入的离散点,至少4个点
    @closed 是否拟合闭合曲线,true表示闭合,false不闭合
    @stride 拟合精度
    */
    vector<Point2f> BSplineFit(vector<Point2f> discretePoints, bool closed, double stride = 0.01) {
    	vector<Point2f> fittingPoints;
    	for (int i = 0; i < (closed ? discretePoints.size() : discretePoints.size() - 1); i++) {
    		Point2f xy[4];
    		xy[0] = (discretePoints[i] + 4 * discretePoints[(i + 1) % discretePoints.size()] + discretePoints[(i + 2) % discretePoints.size()]) / 6;
    		xy[1] = -(discretePoints[i] - discretePoints[(i + 2) % discretePoints.size()]) / 2;
    		xy[2] = (discretePoints[i] - 2 * discretePoints[(i + 1) % discretePoints.size()] + discretePoints[(i + 2) % discretePoints.size()]) / 2;
    		xy[3] = -(discretePoints[i] - 3 * discretePoints[(i + 1) % discretePoints.size()] + 3 * discretePoints[(i + 2) % discretePoints.size()] - discretePoints[(i + 3) % discretePoints.size()]) / 6;
    		for (double t = 0; t <= 1; t += stride) {
    			Point2f totalPoints = Point2f(0, 0);
    			for (int j = 0; j < 4; j++) {
    				totalPoints += xy[j] * pow(t, j);
    			}
    			fittingPoints.push_back(totalPoints);
    		}
    	}
    	return fittingPoints;
    }
    

    非闭合拟合效果:

    在这里插入图片描述
    闭合曲线拟合效果:
    在这里插入图片描述
    在这里插入图片描述

    展开全文
  • 解压密码为:hur.cn 主要采用C++编程实现,过控制点的三次B样条曲线拟合,可以用于各种高级的曲线拟合方面。
  • 本代码为三次B样条曲线拟合10个点的程序,并利用OpenGL对最后拟合的曲线进行绘制,注意:需要安装EIGEN矩阵库和OpenGL才能运行。
  • 代码是基于C++的三次B样条曲线拟合代码,包含插值拟合,近似拟合就不放代码了,较简单,我的博客中有相关论文链接。http://blog.csdn.net/liumangmao1314/article/details/54588155
  • 反求三次B样条曲线控制顶点,陶淑一,,在实际应用中,为对曲线的形状做局部的修改,通常需要通过给定型值点反求曲线的控制顶点,通过个别控制顶点的调整来达到调整曲线
  • 三次B样条曲线 计算机图形学 Turboc 编译器
  • 三次B样条曲线在VB下的实现,运行成功。三次B样条曲线在VB下的实现,运行成功。
  • 在屏幕上使用鼠标左键绘制数量大于4的任意顶点形成控制多边形,单机鼠标右键绘制三次B样条曲线,同时在控制多边形的每一个特征三角形内用虚线显示三次B样条曲线的几何生成原理。 具体要求: (1) 分析原理,写...
  • 二次与三次B样条曲线c++实现

    千次阅读 多人点赞 2019-08-31 21:26:40
    B样条曲线构建一条平滑曲线,接近而不通过控制点(首尾点除外)。如图 B样条曲线从Bezier曲线演变而来,了解B样条曲线首先得了解Bezier曲线。对于平面上的个点P0,P1,P2,其坐标分别是(x0,y0)、(x1,y1)、(x2...

         B样条曲线构建一条平滑曲线,接近而不通过控制点(首尾点除外)。如图

                                                                       

         B样条曲线从Bezier曲线演变而来,了解B样条曲线首先得了解Bezier曲线。对于平面上的三个点P0,P1,P2,其坐标分别是(x0,y0)、(x1,y1)、(x2,y2)。二次Bezier曲线用一条抛物线进行拟合,其参数方程是

                                                 

        二次Bezier曲线有如下要求:(1)t=0时,曲线经过P0,并与P0P1相切;(2)t=1时,曲线经过P2,并与P1P2相切。即有

                                                   

        联立这些条件可以得到曲线参数表达式:

                                                    

       其中,,称为二次Bezier曲线基函数。

       每3个离散点形成一条Bezier曲线,由于每条Bezier都经过端点,导致分段的Bezier曲线在端点处难以平滑过渡。二次B样条曲线克服了这个缺点,把端点移到线段中点(如下图所示),这样就能保证各段曲线在连接处能够一阶导数连续。

                                 

        由此满足条件:

                                                       

        结合条件可以求解得到二次B样条曲线:

                                                       

        二次B样条曲线实现,只需将曲线参数t划分成k等分,t从0开始取值,间隔dt,直至k*dt。如果想让整条曲线两端与起始点P0和终止点Pn重合,只需以P0和Pn为中点,构造新点PP1=2*P0-P1,与PP2=2*Pn-Pn-1,替换掉P0与Pn即可。

        三次B样条曲线通过样条基函数可以得到如下形式:

                                                      

        三次B样条具有以下物理意义,曲线起点S位于三角形P0P1P2的中线P1M1上,距离P1点1/3倍P1M1处;曲线中点E位于三角形P1P2P3的中线P2M2上,距离1/3倍P2M2处;曲线起点切线平行于P0P2,终点切线平行于P1P3.

                                                              

       三次B样条曲线要想让曲线两端与起始端P0与Pn重合,只需构造新点PP1=2*P0-P1,与PP2=2*Pn-Pn-1,分别加到P0之前与Pn之后即可。由此,参与计算点的增加了2个(注意,二次B样条是替换不是增加)。

    编程实现:

    程序实现了二次与三次B样条曲线,封装成了BSpline类

    BSpine.h

    #pragma once
    //#include "position.h"
    
    typedef struct tagPosition
    {
        double  x;
        double  y;
    	tagPosition(double _x,double _y) { x=_x; y=_y;}
    	tagPosition() {};
    	bool operator==(const tagPosition & pt) { return (x==pt.x && y==pt.y);} 
    } CPosition;
    
    class CBSpline
    {
    public:
    	CBSpline(void);
    	~CBSpline(void);
    
    	void TwoOrderBSplineSmooth(CPosition *pt,int Num);
    	void TwoOrderBSplineInterpolatePt(CPosition *&pt,int &Num,int *InsertNum);
    	double F02(double t);
    	double F12(double t);
    	double F22(double t);
    
    	void ThreeOrderBSplineSmooth(CPosition *pt,int Num);
    	void ThreeOrderBSplineInterpolatePt(CPosition *&pt,int &Num,int *InsertNum);
    	double F03(double t);
    	double F13(double t);
    	double F23(double t);
    	double F33(double t);
    };

    BSpine.cpp

    //****************************     BSpline.cpp     *********************************** 
    // 包含功能:二次B样条平滑,三次B样条平滑;二次B样条平滑后节点插值
    //
    // 作者:    蒋锦朋   1034378054@qq.com
    // 单位:    中国地质大学(武汉) 地球物理与空间信息学院
    // 日期:    2014/12/03
    //*************************************************************************************
    #include "StdAfx.h"
    #include "BSpline.h"
    
    
    CBSpline::CBSpline(void)
    {
    }
    
    
    CBSpline::~CBSpline(void)
    {
    }
    //======================================================================
    // 函数功能: 二次B样条平滑,把给定的点,平滑到B样条曲线上,不增加点的数目
    // 输入参数: *pt :给定点序列,执行完成后,会被替换成新的平滑点
    //            Num:点个数
    // 返回值:   无返回值
    
    // 编辑日期:    2014/12/03
    //======================================================================
    void CBSpline::TwoOrderBSplineSmooth(CPosition *pt,int Num)
    {
    	CPosition *temp=new CPosition[Num];
    	for(int i=0;i<Num;i++)
    		temp[i]=pt[i];
    
    	temp[0].x=2*temp[0].x-temp[1].x;                  //  将折线两端点换成延长线上两点
    	temp[0].y=2*temp[0].y-temp[1].y;
    
    	temp[Num-1].x=2*temp[Num-1].x-temp[Num-2].x;
    	temp[Num-1].y=2*temp[Num-1].y-temp[Num-2].y;
    
    	CPosition NodePt1,NodePt2,NodePt3;
    	double t;
        for(int i=0;i<Num-2;i++)
    	{
    		NodePt1=temp[i]; NodePt2=temp[i+1]; NodePt3=temp[i+2];
    		if(i==0)                                     //  第一段取t=0和t=0.5点
    		{   
    			t=0;
    			pt[i].x=F02(t)*NodePt1.x+F12(t)*NodePt2.x+F22(t)*NodePt3.x;
    			pt[i].y=F02(t)*NodePt1.y+F12(t)*NodePt2.y+F22(t)*NodePt3.y;
    			t=0.5;
    			pt[i+1].x=F02(t)*NodePt1.x+F12(t)*NodePt2.x+F22(t)*NodePt3.x;
    			pt[i+1].y=F02(t)*NodePt1.y+F12(t)*NodePt2.y+F22(t)*NodePt3.y;
    		}else if(i==Num-3)                          //  最后一段取t=0.5和t=1点
    		{
    			t=0.5;
    			pt[i+1].x=F02(t)*NodePt1.x+F12(t)*NodePt2.x+F22(t)*NodePt3.x;
    			pt[i+1].y=F02(t)*NodePt1.y+F12(t)*NodePt2.y+F22(t)*NodePt3.y;
    			t=1;
    			pt[i+2].x=F02(t)*NodePt1.x+F12(t)*NodePt2.x+F22(t)*NodePt3.x;
    			pt[i+2].y=F02(t)*NodePt1.y+F12(t)*NodePt2.y+F22(t)*NodePt3.y;
    		}else                                      //  中间段取t=0.5点
    		{
    			t=0.5;
    			pt[i+1].x=F02(t)*NodePt1.x+F12(t)*NodePt2.x+F22(t)*NodePt3.x;
    			pt[i+1].y=F02(t)*NodePt1.y+F12(t)*NodePt2.y+F22(t)*NodePt3.y;
    		}
    	}
    	delete []temp;
    }
    
    //================================================================
    // 函数功能: 二次B样条拟合,在节点之间均匀插入指定个数点
    // 输入参数: *pt :给定点序列,执行完成后,会被替换成新的数据点
    //            Num:节点点个数
    //            *InsertNum: 节点之间需要插入的点个数指针 
    // 返回值:   无返回值
    //
    // 编辑日期:   2014/12/07
    //=================================================================
    void CBSpline::TwoOrderBSplineInterpolatePt(CPosition *&pt,int &Num,int *InsertNum)
    {
    	if(pt==NULL || InsertNum==NULL) return;
    
    	int InsertNumSum=0;                               //  计算需要插入的点总数
    	for(int i=0;i<Num-1;i++)  InsertNumSum+=InsertNum[i];
    
    	CPosition *temp=new CPosition[Num];               //  二次B样条不需要增加点数,需要将首尾点替换掉
    	for(int i=0;i<Num;i++)
    		temp[i]=pt[i];
    
    	temp[0].x=2*temp[0].x-temp[1].x;                  //  将折线两端点换成延长线上两点
    	temp[0].y=2*temp[0].y-temp[1].y;
    
    	temp[Num-1].x=2*temp[Num-1].x-temp[Num-2].x;
    	temp[Num-1].y=2*temp[Num-1].y-temp[Num-2].y;
    
    	delete []pt;                                      //  点数由原来的Num个增加到Num+InsertNumSum个,删除旧的存储空间,开辟新的存储空间
    
    	pt=new CPosition[Num+InsertNumSum];              
    
    	CPosition NodePt1,NodePt2,NodePt3,NodePt4;        //  两节点间均匀插入点,需要相邻两段样条曲线,因此需要四个节点
    
    	double t;
    	int totalnum=0;
    	for(int i=0;i<Num-1;i++)                          //  每条线段均匀插入点
    	{
    		if(i==0)                                      //  第一段只需计算第一条样条曲线,无NodePt1
    		{   
    			NodePt2=temp[i]; NodePt3=temp[i+1]; NodePt4=temp[i+2];     
    
    			double dt=0.5/(InsertNum[i]+1);
    			for(int j=0;j<InsertNum[i]+1;j++)
    			{
    				t=0+dt*j;
    				pt[totalnum].x=F02(t)*NodePt2.x+F12(t)*NodePt3.x+F22(t)*NodePt4.x;
    				pt[totalnum].y=F02(t)*NodePt2.y+F12(t)*NodePt3.y+F22(t)*NodePt4.y;
    				totalnum++;
    			}
    		}else if(i==Num-2)                            //  最后一段只需计算最后一条样条曲线,无NodePt4
    		{
    			NodePt1=temp[i-1]; NodePt2=temp[i]; NodePt3=temp[i+1];
    
    			double dt=0.5/(InsertNum[i]+1);
    			for(int j=0;j<InsertNum[i]+2;j++)
    			{
    				t=0.5+dt*j;
    				pt[totalnum].x=F02(t)*NodePt1.x+F12(t)*NodePt2.x+F22(t)*NodePt3.x;
    				pt[totalnum].y=F02(t)*NodePt1.y+F12(t)*NodePt2.y+F22(t)*NodePt3.y;
    				totalnum++;
    			}
    		}else                                      
    		{
    			NodePt1=temp[i-1],NodePt2=temp[i]; NodePt3=temp[i+1]; NodePt4=temp[i+2];    // NodePt1,2,3计算第一条曲线,NodePt2,3,4计算第二条曲线
    
    			int LeftInsertNum,RightInsertNum;          //  计算线段间左右曲线段上分别需要插入的点数
    			double rightoffset=0;                      //  左边曲线段从t=0.5开始,又边曲线段从t=rightoffset开始
    			double Leftdt=0,Rightdt=0;                 //  左右曲线取点t步长
    			if(InsertNum[i]==0 )
    			{
    				LeftInsertNum=0;
    				RightInsertNum=0;
    			}else if(InsertNum[i]%2==1)                //  插入点数为奇数,左边曲线段插入点个数比右边多一点
    			{
    				RightInsertNum=InsertNum[i]/2;
    				LeftInsertNum=RightInsertNum+1;
    				Leftdt=0.5/(LeftInsertNum);
    				Rightdt=0.5/(RightInsertNum+1);
    				rightoffset=Rightdt;
    			}else                                      //  插入点数为偶数,左右边曲线段插入个数相同
    			{
    				RightInsertNum=InsertNum[i]/2;
    				LeftInsertNum=RightInsertNum;
    				Leftdt=0.5/(LeftInsertNum+0.5);
    				Rightdt=0.5/(RightInsertNum+0.5);
    				rightoffset=Rightdt/2;
    			}
    
    			for(int j=0;j<LeftInsertNum+1;j++)
    			{
    				t=0.5+Leftdt*j;
    				pt[totalnum].x=F02(t)*NodePt1.x+F12(t)*NodePt2.x+F22(t)*NodePt3.x;
    				pt[totalnum].y=F02(t)*NodePt1.y+F12(t)*NodePt2.y+F22(t)*NodePt3.y;
    				totalnum++;
    			}
    
    			for(int j=0;j<RightInsertNum;j++)
    			{				
    				t=rightoffset+Rightdt*j;
    				pt[totalnum].x=F02(t)*NodePt2.x+F12(t)*NodePt3.x+F22(t)*NodePt4.x;
    				pt[totalnum].y=F02(t)*NodePt2.y+F12(t)*NodePt3.y+F22(t)*NodePt4.y;
    				totalnum++;
    			}
    		}
    	}
    	delete []temp;
    	Num=Num+InsertNumSum;
    
    }
    //================================================================
    // 函数功能: 二次样条基函数
    //
    // 编辑日期:    2014/12/03
    //================================================================
    double CBSpline::F02(double t)
    {
    	return 0.5*(t-1)*(t-1);
    }
    double CBSpline::F12(double t)
    {
    	return 0.5*(-2*t*t+2*t+1);
    }
    double CBSpline::F22(double t)
    {
    	return 0.5*t*t;
    }
    //========================================================================
    // 函数功能: 三次B样条平滑,把给定的点,平滑到B样条曲线上,不增加点的数目
    // 输入参数: *pt :给定点序列,执行完成后,会被替换成新的平滑点
    //            Num:点个数
    // 返回值:   无返回值
    //
    // 编辑日期:    2014/12/03
    //========================================================================
    void CBSpline::ThreeOrderBSplineSmooth(CPosition *pt,int Num)
    {
    	CPosition *temp=new CPosition[Num+2];
    	for(int i=0;i<Num;i++)
    		temp[i+1]=pt[i];
    
    	temp[0].x=2*temp[1].x-temp[2].x;                  //  将折线延长线上两点加入作为首点和尾点
    	temp[0].y=2*temp[1].y-temp[2].y;
    
    	temp[Num+1].x=2*temp[Num].x-temp[Num-1].x;
    	temp[Num+1].y=2*temp[Num].y-temp[Num-1].y;
    
    	CPosition NodePt1,NodePt2,NodePt3,NodePt4;
    	double t;
    	for(int i=0;i<Num-1;i++)
    	{
    		NodePt1=temp[i]; NodePt2=temp[i+1]; NodePt3=temp[i+2]; NodePt4=temp[i+3];
    
    		if(i==Num-4)                          //  最后一段取t=0.5和t=1点
    		{
    			t=0;
    			pt[i].x=F03(t)*NodePt1.x+F13(t)*NodePt2.x+F23(t)*NodePt3.x+F33(t)*NodePt4.x;
    			pt[i].y=F03(t)*NodePt1.y+F13(t)*NodePt2.y+F23(t)*NodePt3.y+F33(t)*NodePt4.y;
    			t=1;
    			pt[i+1].x=F03(t)*NodePt1.x+F13(t)*NodePt2.x+F23(t)*NodePt3.x+F33(t)*NodePt4.x;
    			pt[i+1].y=F03(t)*NodePt1.y+F13(t)*NodePt2.y+F23(t)*NodePt3.y+F33(t)*NodePt4.y;
    		}else                                      //  中间段取t=0.5点
    		{
    			t=0;
    			pt[i].x=F03(t)*NodePt1.x+F13(t)*NodePt2.x+F23(t)*NodePt3.x+F33(t)*NodePt4.x;
    			pt[i].y=F03(t)*NodePt1.y+F13(t)*NodePt2.y+F23(t)*NodePt3.y+F33(t)*NodePt4.y;
    		}
    	}
    	delete []temp;
    }
    
    //================================================================
    // 函数功能: 三次B样条拟合,在节点之间均匀插入指定个数点
    // 输入参数: *pt :给定点序列,执行完成后,会被替换成新的数据点
    //            Num:节点点个数
    //            *InsertNum: 节点之间需要插入的点个数指针 
    // 返回值:   无返回值
    //
    // 编辑日期:   2014/12/07
    //=================================================================
    void CBSpline::ThreeOrderBSplineInterpolatePt(CPosition *&pt,int &Num,int *InsertNum)
    {
    	if(pt==NULL || InsertNum==NULL) return;
    
    	int InsertNumSum=0;                               //  计算需要插入的点总数
    	for(int i=0;i<Num-1;i++)  InsertNumSum+=InsertNum[i];
    
    	CPosition *temp=new CPosition[Num+2];
    	for(int i=0;i<Num;i++)
    		temp[i+1]=pt[i];
    
    	temp[0].x=2*temp[1].x-temp[2].x;                  //  将折线延长线上两点加入作为首点和尾点
    	temp[0].y=2*temp[1].y-temp[2].y;
    
    	temp[Num+1].x=2*temp[Num].x-temp[Num-1].x;
    	temp[Num+1].y=2*temp[Num].y-temp[Num-1].y;
    
    	CPosition NodePt1,NodePt2,NodePt3,NodePt4;
    	double t;
    
    	delete []pt;                                      //  点数由原来的Num个增加到Num+InsertNumSum个,删除旧的存储空间,开辟新的存储空间
    
    	pt=new CPosition[Num+InsertNumSum];              
    
    	int totalnum=0;
    	for(int i=0;i<Num-1;i++)                          //  每条线段均匀插入点
    	{
    		NodePt1=temp[i]; NodePt2=temp[i+1]; NodePt3=temp[i+2]; NodePt4=temp[i+3];
    		double dt=1.0/(InsertNum[i]+1);
    
    		for(int j=0;j<InsertNum[i]+1;j++)
    		{
    			t=dt*j;
    			pt[totalnum].x=F03(t)*NodePt1.x+F13(t)*NodePt2.x+F23(t)*NodePt3.x+F33(t)*NodePt4.x;
    			pt[totalnum].y=F03(t)*NodePt1.y+F13(t)*NodePt2.y+F23(t)*NodePt3.y+F33(t)*NodePt4.y;
    			totalnum++;
    		}
    
    		if(i==Num-2){              //  最后一个尾点
    			t=1;
    			pt[totalnum].x=F03(t)*NodePt1.x+F13(t)*NodePt2.x+F23(t)*NodePt3.x+F33(t)*NodePt4.x;
    			pt[totalnum].y=F03(t)*NodePt1.y+F13(t)*NodePt2.y+F23(t)*NodePt3.y+F33(t)*NodePt4.y;
    			totalnum++;
    		}
    	}
    
    	delete []temp;
    	Num=Num+InsertNumSum;
    
    }
    
    //================================================================
    // 函数功能: 三次样条基函数
    //
    // 编辑日期:    2014/12/03
    //================================================================
    double CBSpline::F03(double t)
    {
    	return 1.0/6*(-t*t*t+3*t*t-3*t+1);
    }
    double CBSpline::F13(double t)
    {
    	return 1.0/6*(3*t*t*t-6*t*t+4);
    }
    double CBSpline::F23(double t)
    {
    	return 1.0/6*(-3*t*t*t+3*t*t+3*t+1);
    }
    double CBSpline::F33(double t)
    {
    	return 1.0/6*t*t*t;
    }

    程序调用:

    #include "stdafx.h"
    #include "math.h"
    #include "BSpline.h"
    
    int _tmain(int argc, _TCHAR* argv[])
    {
    	int num=8;
    	double x[8]={9.59,60.81,105.57,161.59,120.5,100.1,50.0,10.0};
    	double y[8]={61.97,107.13,56.56,105.27,120.5,150.0,110.0,180.0};
    
    	CPosition *testpt=new CPosition[num];
    	for(int i=0;i<num;i++) testpt[i]=CPosition(x[i],y[i]);
    
    	int *Intnum=new int[num-1]; 
    	for(int i=0;i<num-1;i++){
    		Intnum[i]=10;                 //  每一个样条曲线内插入10个点
    	}
    
    	int num2=num;
    	CBSpline bspline;
    	bspline.TwoOrderBSplineInterpolatePt(testpt,num2,Intnum);        //  二次B样条曲线
    	//bspline.ThreeOrderBSplineInterpolatePt(testpt,num2,Intnum);    //  三次B样条曲线
    
    	//bspline.TwoOrderBSplineSmooth(testpt,num2);      //  二次B样条平滑
    	//bspline.ThreeOrderBSplineSmooth(testpt,num2);    //  三次B样条平滑
    	delete Intnum;
    
    
    	FILE *fp_m_x = fopen("Bspline_test_x.txt", "wt");
    	FILE *fp_m_y = fopen("Bspline_test_y.txt", "wt");
    	for (int i = 0; i < num2; i++){
    		fprintf(fp_m_x, "%lf\n", testpt[i].x);
    		fprintf(fp_m_y, "%lf\n", testpt[i].y);
    	}
    	fclose(fp_m_x);
    	fclose(fp_m_y);
    
    	return 0;
    }

    程序运行结束后会生成样条曲线坐标文件,分别为横坐标:Bspline_test_x.txt和纵坐标Bspline_test_y.txt.利用matlab绘图可以得到下面结果: 

                                           

    附matlab绘图代码:

    clear all;
    clc;
    
    load Bspline_test_x2.txt;    % 导入二次B样条计算的结果
    load Bspline_test_y2.txt;
    load Bspline_test_x3.txt;    % 导入三次B样条计算的结果
    load Bspline_test_y3.txt;
    
    load Bspline_test_smooth_x2.txt
    load Bspline_test_smooth_y2.txt
    load Bspline_test_smooth_x3.txt
    load Bspline_test_smooth_y3.txt
    
    control_point_x=[9.59,60.81,105.57,161.59,120.5,100.1,50.0,10.0];
    control_point_y=[61.97,107.13,56.56,105.27,120.5,150.0,110.0,180.0];
    
    figure;
    h1=plot(control_point_x,control_point_y,'--ob'); hold on;
    h2=plot(Bspline_test_x2,Bspline_test_y2,'--k','LineWidth',2);
    h3=plot(Bspline_test_x3,Bspline_test_y3,':r','LineWidth',2);
    h4=plot(Bspline_test_smooth_x2,Bspline_test_smooth_y2,'og','LineWidth',1);
    h5=plot(Bspline_test_smooth_x3,Bspline_test_smooth_y3,'^b','LineWidth',1);
    
    legend([h1 h2 h3 h4 h5],'控制点','二次B样条曲线','三次B样条曲线','二次B样条平滑','三次B样条平滑');
    axis([0 200 40 200]);
    set(gca,'FontSize',15); set(gca,'FontWeight','bold'); set(gca,'LineWidth',2);
    set(get(gca,'XLabel'),'Fontsize',15); set(get(gca,'YLabel'),'FontSize',15);

     

    展开全文
  • 三次b样条曲线算法vc++编程 应该可以运行的算法在
  • 如何绘制三次B样条曲线

    万次阅读 2016-10-21 11:24:57
    B样条的定义就不赘述了,同学们可以参考大神的博客:http://www.cs.mtu.edu/~shene/...我要说的是在理解定义之后,我们如何实现它,这里我只讨论准均匀B样条,即三次B样条曲线经过首尾节点(用的最为广泛), 非均匀及Nurb

    B样条的定义就不赘述了,同学们可以参考大神的博客:http://www.cs.mtu.edu/~shene/COURSES/cs3621/NOTES/,中文翻译也有:http://blog.csdn.net/tuqu/article/details/4749586

    我要说的是在理解定义之后,我们如何实现它,这里我只讨论准均匀B样条,即三次B样条曲线经过首尾节点(用的最为广泛), 非均匀及Nurbs曲线等等还没有研究.


    1、首先是节点向量的确定,节点向量的数目 l = 控制点数目 + 次数p(这里是3)+ 1.  为了经过首尾点,要求节点向量的前p+1个节点向量为0,中间的l-2(p+1)个节点向量依次加一,后p+1个节点为最大值,好多文章说前p个节点向量,其实他说的p指的是阶数,不是次数,而阶数=次数+1,我就被误导过,总是画得不对,坑死爹了.附上函数:

    void BSpline::InitKnots() const
    {
    std::size_t count = _vertexs.size();//_vertexs是控制点数组


    for (int i = 0; i < _degree; i++)//degress表示次数,这里为3
    {
    _knots.push_back(0.0); //前p个0
    }


    double dParam = 0.0;
    for (std::size_t i = 0; i < count - _degree + 1; i++)//这里如果是0则,dParm = 0,也就是说前p+1个节点为0
    {
    if (i > 0)
    dParam += 1.0;
    _knots.push_back(dParam);
    }


    for (std::size_t i = 0; i < _degree; i++)//这里是后p个等于最大,实际上倒数第p+1个也是最大
    {
    _knots.push_back(dParam); //后p个 0
    }
    }

    2、基函数的确定

    本文是用deboor算法的递归形式,形式比较简单,实现难度低,但是效率也低,效率高的方式我会在下一篇博客中介绍,deboor对B样条基函数的定义如下:



    基函数的代码:

    double EvalBasis(double u, int i, int degree) 
    {
    if (degree == 0)
    {
    if (u >= _knots[i] && u < _knots[i + 1])
    {
    return 1.0;
    }
    else
    {
    return 0.0;
    }
    }
    double l0 = u - _knots[i];
    double l1 = _knots[i + degree] - _knots[i];
    double l2 = Divide(l0, l1);


    double k0 = _knots[i + degree + 1] - u;
    double k1 = _knots[i + degree + 1] - _knots[i + 1];
    double k2 = Divide(k0, k1);


    double res = l2 * EvalBasis(u, i, degree - 1) + k2 * EvalBasis(u, i + 1, degree - 1);
    return res;
    }

    double Divide(double x, double y) const
    {
    if (y == 0.0)
    {
    return 0.0;
    }
    return x / y;
    }

    3、生成

    根据B样条的定义,生成B样条离散点,这里用到了osg的数据结构,将就看吧


    osg::Vec3dArray* BSpline::Generate() const
     {
    if (_vertexs.size() < 4)
    {
    return NULL;
    }


    InitKnots();


    int iStep = 100;
    int iLen = _knots.size();
    double dStart = _knots[0];
    double dEnd = _knots[iLen - 1];
    double dDelta = (dEnd - dStart) / iStep;


    osg::ref_ptr<osg::Vec3dArray> v3dArray = new osg::Vec3dArray;
    for (int s = 0; s <= iStep; s++)
    {
    double u = s * dDelta;
    osg::Vec3d v;
    double sum = 0.0;


    for (int i = 0; i < _vertexs.size(); i++)
    {
    double t = EvalBasis(u, i, _degree);
    v += _vertexs.at(i) * t;
    sum += t;
    }


    if (v != osg::Vec3d())
    {
    v3dArray->push_back(v);
    }
    }
    return v3dArray.release();
    }

    下载地址:http://download.csdn.net/detail/qingcaichongchong/9697192

    展开全文
  • B样条曲线基函数次数与控制顶点数无关; 2、Bezier曲线的基函数是Beinstein基函数,它是个多项式函数。B样条曲线的基函数是多项式样条。 3、Bezier曲线是一种特殊表示形式的参数多项式曲线。B样条曲线则是一种特殊...
  • 计算机图形学课程设计题目,使用鼠标边点击边边生成曲线,而且还能实现实现绘制出三次B样条的的切线。
  • 功能:根据参数u值和k(大小为阶数值)与节点矢量,计算第i个k次B样条基数输入参数: u—参数值;k—大小值为阶数;i—第i个k次B样条的支撑区间左端节点的下标;aNode为节点向量。输出参数:返回函数值。double ...
  • 三次B样条曲线. c语言

    2008-12-13 10:33:44
    三次B样条曲线的基函数为 G03(t)=16(-t^3+3t^2-3t+1) G13(t)=16(3t^3-6t^2+4) G23(t)=16(-3t^3+3t^2+3t+1) G33(t)=16t^3
  • 我们今天来介绍一下B样条曲线。相比较Beizer曲线来说,B样条有着两个优点:(1)k次B样条曲线... 我们先来看看什么是B样条曲线,如图1,我们以三次B样条曲线为例。由于k次B样条曲线的控制点有k+1个,所以P0P1P2P...
  • 实验内容: (1)掌握三次B样条曲线算法原理; (2)掌握三次B样条曲线边界条件特点; 实现功能: a)实现三次B样条曲线绘制算法 b)调用B样条曲线算法来绘制一段汉语、英文或你喜爱的平面图案;

空空如也

空空如也

1 2 3 4 5 ... 13
收藏数 243
精华内容 97
关键字:

三次b样条曲线