B-Spline Basis Functions摘要Abstract:直接根据B样条的Cox-deBoor递推定义写出计算B样条基函数的程序,并将计算结果在OpenSceneGraph中显示。 关键字Key Words:B Spline Basis Functions、OpenSceneGraph 一、概述Overview有很多方法可以用来定义B样条基函数以及证明它的一些重要性质。例如,可以采用截尾幂函数的差商定义,开花定义,以及由de Boor和Cox等人提出的递推公式等来定义。我们这里采用的是递推定义方法,因为这种方法在计算机实现中是最有效的。 令U={u0,u1,…,um}是一个单调不减的实数序列,即ui<=ui+1,i=0,1,…,m-1。其中,ui称为节点,U称为节点矢量,用Ni,p(u)表示第i个p次B样条基函数,其定义为: B样条基有如下性质: a) 递推性; b) 局部支承性; c) 规范性; d) 可微性; 二、程序 Codes直接根据B样条基函数的Cox-deBoor递推定义,写出计算B样条基函数的程序如下: 头文件BSplineBasisFunction.h:
* File : BSplineBasisFunction.h * Author : eryar@163.com * Date : 2013-03-23 22:13 * Version : V1.0 * * Description : Use Cox-deBoor formula to implemente the * B-Spline Basis functions. * */ #ifndef _BSPLINEBASISFUNCTION_H_ #define _BSPLINEBASISFUNCTION_H_ #include <vector> class BSplineBasisFunction { public: BSplineBasisFunction(const std::vector<double>& U); ~BSplineBasisFunction(void); public: /* * @brief Binary search of the knot vector. */ int FindSpan(double u); /* * @brief * @param [in] i: span of the parameter u; * [in] p: degree; * [in] u: parameter; */ double EvalBasis(int i, int p, double u); /* * @breif Get knot vector size. */ int GetKnotVectorSize(void) const; /* * @breif Get the knot value of the given index. */ double GetKnot(int i) const; private: std::vector<double> mKnotVector; }; #endif // _BSPLINEBASISFUNCTION_H_
/*
* Copyright (c) 2013 eryar All Rights Reserved. * * File : BSplineBasisFunction.cpp * Author : eryar@163.com * Date : 2013-03-23 22:14 * Version : V1.0 * * Description : Use Cox-deBoor formula to implemente the * B-Spline Basis functions. * */ #include "BSplineBasisFunction.h" BSplineBasisFunction::BSplineBasisFunction( const std::vector<double>& U ) :mKnotVector(U) { } BSplineBasisFunction::~BSplineBasisFunction(void) { } int BSplineBasisFunction::GetKnotVectorSize( void ) const { return static_cast<int> (mKnotVector.size()); } double BSplineBasisFunction::GetKnot( int i ) const { return mKnotVector[i]; } /* * @brief Binary search of the knot vector. */ int BSplineBasisFunction::FindSpan( double u ) { int iSize = static_cast<int> (mKnotVector.size()); if (u >= mKnotVector[iSize-1]) { return iSize; } int iLow = 0; int iHigh = iSize; int iMiddle = (iLow + iHigh) / 2; while (u < mKnotVector[iMiddle] || u > mKnotVector[iMiddle+1]) { if (u < mKnotVector[iMiddle]) { iHigh = iMiddle; } else { iLow = iMiddle; } iMiddle = (iLow + iHigh) / 2; } return iMiddle; } double BSplineBasisFunction::EvalBasis( int i, int p, double u ) { if ((i+p+1) >= GetKnotVectorSize()) { return 0; } if (0 == p) { if (u >= mKnotVector[i] && u < mKnotVector[i+1]) { return 1; } else { return 0; } } double dLeftUpper = u - mKnotVector[i]; double dLeftLower = mKnotVector[i+p] - mKnotVector[i]; double dLeftValue = 0; double dRightUpper = mKnotVector[i+p+1] - u; double dRightLower = mKnotVector[i+p+1] - mKnotVector[i+1]; double dRightValue = 0; if (dLeftUpper != 0 && dLeftLower != 0) { dLeftValue = (dLeftUpper / dLeftLower) * EvalBasis(i, p-1, u); } if (dRightUpper != 0 && dRightLower != 0) { dRightValue = (dRightUpper / dRightLower) * EvalBasis(i+1, p-1, u); } return (dLeftValue + dRightValue); }
/*
* Copyright (c) 2013 eryar All Rights Reserved. * * File : Main.cpp * Author : eryar@163.com * Date : 2013-03-23 22:11 * Version : V1.0 * * Description : Use Cox-deBoor formula to implemente the * B-Spline Basis functions. * */ #include <osgDB/ReadFile> #include <osgViewer/Viewer> #include <osgGA/StateSetManipulator> #include <osgViewer/ViewerEventHandlers> #include "BSplineBasisFunction.h" #pragma comment(lib, "osgd.lib") #pragma comment(lib, "osgDBd.lib") #pragma comment(lib, "osgGAd.lib") #pragma comment(lib, "osgViewerd.lib") osg::Node* MakeBasisFuncLine(BSplineBasisFunction& bf, int i, int p) { // The property basis functions. int iLen = bf.GetKnotVectorSize(); int iStep = 800; double dStart = bf.GetKnot(0); double dEnd = bf.GetKnot(iLen-1); double dDelta = (dEnd - dStart) / iStep; double u = 0; double v = 0; // Create the Geode (Geometry Node) to contain all our osg::Geometry objects. osg::Geode* geode = new osg::Geode; // Create Geometry object to store all the vertices and lines primitive. osg::ref_ptr<osg::Geometry> linesGeom = new osg::Geometry; // Set the vertex array to the points geometry object. osg::ref_ptr<osg::Vec3Array> pointsVec = new osg::Vec3Array; for (int s = 0; s <= iStep; s++) { u = s * dDelta; v = bf.EvalBasis(i, p, u); if (v != 0) { pointsVec->push_back(osg::Vec3(u, 0, v)); } } linesGeom->setVertexArray(pointsVec); // Set the colors. osg::ref_ptr<osg::Vec4Array> colors = new osg::Vec4Array; colors->push_back(osg::Vec4(1.0f, 1.0f, 0.0f, 0.0f)); linesGeom->setColorArray(colors.get()); linesGeom->setColorBinding(osg::Geometry::BIND_OVERALL); // Set the normal in the same way of color. osg::ref_ptr<osg::Vec3Array> normals = new osg::Vec3Array; normals->push_back(osg::Vec3(0.0f, -1.0f, 0.0f)); linesGeom->setNormalArray(normals.get()); linesGeom->setNormalBinding(osg::Geometry::BIND_OVERALL); // Add the points geometry to the geode. linesGeom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINE_STRIP, 0, pointsVec->size())); geode->addDrawable(linesGeom.get()); return geode; } osg::Node* CreateScene(void) { osg::Group* root = new osg::Group; // Knot vector: U={0,0,0,1,2,3,4,4,5,5,5}. std::vector<double> knotVector; knotVector.push_back(0); knotVector.push_back(0); knotVector.push_back(0); knotVector.push_back(1); knotVector.push_back(2); knotVector.push_back(3); knotVector.push_back(4); knotVector.push_back(4); knotVector.push_back(5); knotVector.push_back(5); knotVector.push_back(5); BSplineBasisFunction basisFunc(knotVector); for (int i = 0; i < basisFunc.GetKnotVectorSize(); i++) { // //root->addChild(MakeBasisFuncLine(basisFunc, i, 1)); // root->addChild(MakeBasisFuncLine(basisFunc, i, 2)); } return root; } int main(int argc, char* argv[]) { osgViewer::Viewer viewer; viewer.setSceneData(CreateScene()); viewer.addEventHandler(new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet())); viewer.addEventHandler(new osgViewer::StatsHandler); viewer.addEventHandler(new osgViewer::WindowSizeHandler); return viewer.run(); }
若想显示出所有次数的B样条基函数,只需要在CreateScene中添加就好了。 以《The NURBS Book》中的例子2.2,节点矢量U={0, 0, 0, 1, 2, 3, 4, 4, 5, 5, 5},次数p=2,分别将程序计算的一次、二次B样条基函数的结果列出,如下图所示: 图1. 一次B样条基函数 图2. 二次B样条基函数 本来还想将不同的B样条基函数以不同的颜色显示,试了几次,都没有成功。若以不同的颜色显示,会更直观。若你有设置颜色的方法,欢迎告诉我,eryar@163.com。 三、结论 Conclusion程序计算结果与书中吻合,效果还不错。 理解了B样条的Cox-deBoor递推定义之后,可以将程序中的递归代码转换为非递归实现,这样就可以深入理解B样条基函数了。
PDF Version and Codes: B-Spline Basis Functions |
|