我去年有一篇帖子,介绍了NURBS曲线生成与显示的实现代码。
https://blog.csdn.net/stonewu/article/details/133387469?spm=1001.2014.3001.5501文章浏览阅读323次,点赞4次,收藏2次。搞3D几何内核算法研究,必须学习NURBS样条曲线曲面。看《非均匀有理B样条 第2版》这本书,学习起来,事半功倍。在《插件化算法研究平台》上,做了一个样条曲线研究功能,可以分析Bezier曲线、BSpline、NURBS曲线的各种性质,有直观的体验,能更好地理解。https://blog.csdn.net/stonewu/article/details/133387469?spm=1001.2014.3001.5501
本贴子介绍如何在OGG几何内核上做个自己NURBS曲线API。
曲线代码框架参考示例如下:(具体实现代码,请参考我去年的帖子自行实现,或联系本人)
namespace stone
{
using namespace std;
class Point3
{
public:
float x, y, z;
Point3(float x = 0, float y = 0, float z = 0) : x(x), y(y), z(z) {}
double norm() { return sqrt(x * x + y * y + z * z); }
Point3 operator*(double d) { return Point3(x * d, y * d, z * d); }
Point3 operator+(const Point3& p) { return Point3(x + p.x, y + p.y, z + p.z); }
};
class BaseCurve
{
public:
virtual ~BaseCurve() {}
BaseCurve(double precision) { m_precision = precision; }
bool finished = false; //完成控制点构成
bool m_mousePressed = false;
int m_currentControlPointIndex = -1;
double m_precision = 0.01; //精度
vector<Point3> controlPoints;
vector<Point3> curvePoints;
//nurbs参数
vector<double> weights; //权重
vector<double> knots; // 结点向量
int degree = 3; // 曲线次数
virtual void clear()
{
//清空
curvePoints.clear();
//清空
controlPoints.clear();
finished = false;
}
virtual void curveInfo() = 0;
virtual void createCurve() = 0;
virtual void appendControlPoint(Point3 controlPoint) { controlPoints.push_back(controlPoint); }
virtual void moveControlPoint(int index, Point3 controlPoint) { controlPoints[index] = controlPoint; }
void initKnots()
{
。。。
}
void initWeights()
{
。。。
}
};
class BezierCurve : public BaseCurve
{
public:
BezierCurve(double precision = 0.01) : BaseCurve(precision){};
public:
void curveInfo() { debugx("BezierCurve!"); }
// 生成N阶贝塞尔曲线点
void createCurve()
{
curveInfo();
if (controlPoints.size() <= 1)
return;
//清空
curvePoints.clear();
auto size = controlPoints.size();
for (double t = 0; t < 1.0000; t += m_precision)
{ //根据精度绘制点
。。。
}
}
};
class BSplineCurve : public BaseCurve
{
public:
void curveInfo() { debugx("BSplineCurve!"); }
BSplineCurve(int aDegree, double precision) : BaseCurve(precision) { degree = aDegree; }
BSplineCurve(vector<Point3> points, int k, double precision) : BaseCurve(precision)
{
controlPoints = points;
degree = k;
// 初始化结点向量, m = n + 1+ k ,m+1节点数量, n+1控制点数量 ,k 次数
。。。
}
// 计算基函数值
double basis(int i, int k, double u)
{
。。。
return a * basis(i, k - 1, u) + b * basis(i + 1, k - 1, u);
}
// 计算样条曲线上的点
virtual Point3 calculatePoint(double u)
{
Point3 result;
。。。
return result;
}
void createCurve()
{
curveInfo();
//清空
curvePoints.clear();
auto size = controlPoints.size();
if (size < 2)
return;
//生成NURBS曲线上所有的点
。。。
}
void moveControlPoint(int index, Point3 controlPoint)
{
controlPoints[index] = controlPoint;
if (controlPoints.size() < 2)
return;
createCurve();
}
};
class NURBSCurve : public BSplineCurve
{
public:
void curveInfo() override { debugx("NURBSCurve!"); }
NURBSCurve(int aDegree=3, double precision = 0.01) : BSplineCurve(aDegree, precision) {}
// 计算样条曲线上的点
Point3 calculatePoint(double u) override
{
。。。
return result;
}
};
} //namespace stone
namespace stone
{
class CurveUtil
{
public:
static BRepBuilderAPI_MakePolygon CreateBezierCurve(TColgp_Array1OfPnt Array1, double precision = 0.01)
{
。。
}
static BRepBuilderAPI_MakePolygon CreateNURBSCurve(TColgp_Array1OfPnt Array1, int aDegree=3, double precision = 0.01)
{
。。。
return makePolygon;
}
};
} //namespace stone MakeAPI
调用代码参考示例:
主要功能Demo:
1、定义5个控制点,并在界面上显示出来。
2、调用自己的曲线算法API,生成BezierCurve,以此线条为路径,生成管道。并显示。
3、调用自己的曲线算法API,生成NURBSCurve,缺省权重为1,degree为3,节点向量自行生成。以此线条为路径,生成管道。并显示。
4、调用 OCCT几何内核的BSpline API。以此线条为路径,生成管道。并显示。
5、调用 OCCT几何内核的BSpline API NUBRS曲线。以此线条为路径,生成管道。并显示。
void myCurveDemo(OccView *view)
{
gp_Ax2 ax2;
ax2.SetLocation(gp_Pnt(0, 0, 0));
TopoDS_Edge circleEdge = BRepBuilderAPI_MakeEdge(gp_Circ(ax2, 0.1));
const int pointCount=5;
TColgp_HArray1OfPnt points(1, pointCount);
points.SetValue(1, gp_Pnt(0.1, 0, 0));
points.SetValue(2, gp_Pnt(1.1, 1, 1));
points.SetValue(3, gp_Pnt(2.1, 2, 0));
points.SetValue(4, gp_Pnt(3.1, 1, -1));
points.SetValue(5, gp_Pnt(5.1, 1, -3));
// points.SetValue(6, gp_Pnt(5.1, 4, 5));
// points.SetValue(7, gp_Pnt(6.1, 5, -3));
// points.SetValue(8, gp_Pnt(8.1, 2, -5));
//显示控制点
for(int i=1;i<=pointCount;i++)
{
auto p=points.Value(i);
showPoint(view,p,QString::number(i).toStdString().c_str(),false);
}
//调用自己的曲线算法API,生成BezierCurve
auto curve=stone::CurveUtil::CreateBezierCurve(points);
if(curve.IsDone())
{
Handle(AIS_ColoredShape) ais = new AIS_ColoredShape(curve.Wire());
view->Display(ais);
}
//调用自己的曲线算法API,生成NURBSCurve,缺省权重为1,degree为3,节点向量自行生成
curve=stone::CurveUtil::CreateNURBSCurve(points);
if(curve.IsDone())
{
auto wire=curve.Wire();
Handle(AIS_ColoredShape) ais = new AIS_ColoredShape(wire);
ais->SetColor(Quantity_NOC_MAROON);
view->Display(ais);
{//扫掠
TopoDS_Shape pipe=BRepOffsetAPI_MakePipe(wire,circleEdge);
Handle(AIS_Shape) aisPipe = new AIS_Shape(pipe);
aisPipe->SetColor(Quantity_NOC_MAROON);
view->Display(aisPipe);
}
}
//调用 OCCT几何内核的BSpline API
{
// Make a BSpline curve from the points array
Handle(Geom_BSplineCurve) aBSplineCurve = GeomAPI_PointsToBSpline(points).Curve();
// Make an edge between two point on the BSpline curve.
gp_Pnt aPntOnCurve1, aPntOnCurve2;
aBSplineCurve->D0(0.75 * aBSplineCurve->FirstParameter()
+ 0.25 * aBSplineCurve->LastParameter(),
aPntOnCurve1);
aBSplineCurve->D0(0.25 * aBSplineCurve->FirstParameter()
+ 0.75 * aBSplineCurve->LastParameter(),
aPntOnCurve2);
TopoDS_Edge anEdgeBSpline = BRepBuilderAPI_MakeEdge(aBSplineCurve);
Handle(AIS_ColoredShape) anAisEdgeBSpline = new AIS_ColoredShape(anEdgeBSpline);
anAisEdgeBSpline->SetColor(Quantity_Color(Quantity_NOC_MAGENTA));
view->Display(anAisEdgeBSpline);
{//扫掠
auto wire=BRepBuilderAPI_MakeWire(anEdgeBSpline).Wire();
TopoDS_Shape pipe=BRepOffsetAPI_MakePipe(wire,circleEdge);
Handle(AIS_Shape) aisPipe = new AIS_Shape(pipe);
aisPipe->SetColor(Quantity_NOC_MAGENTA);
view->Display(aisPipe);
}
}
//调用 OCCT几何内核的BSpline API NUBRS曲线
{
/// 均匀B样条,节点向量中的节点值成等差排布
/// 均匀B样条的基函数呈周期性,即所有的基函数有相同的形状
/// 每个后续基函数仅仅市前面基函数在新位置上的重复
Standard_Integer degree(2);
Standard_Integer KNum = pointCount + degree + 1;
TColStd_Array1OfReal knots(1,KNum);
for(int i=0; i<KNum; ++i)
knots.SetValue(i+1, i);
TColStd_Array1OfInteger mults(1,KNum);
for(int i=0; i<KNum; ++i)
mults.SetValue(i+1, 1);
Handle(Geom_BSplineCurve) curve = new Geom_BSplineCurve(points, knots, mults, degree);
TopoDS_Edge ed1 = BRepBuilderAPI_MakeEdge(curve);
TopoDS_Wire wr1 = BRepBuilderAPI_MakeWire(ed1);
Handle(AIS_ColoredShape) ais = new AIS_ColoredShape(wr1);
ais->SetColor(Quantity_NOC_SALMON);
view->Display(ais);
}
{
/// 准均匀B样条,节点向量中的节点值也是等差排布,但是起点和终点都有k-1的重复度,其中ke为曲线次数。
Standard_Integer degree(2);
Standard_Integer KNum = pointCount-1;
TColStd_Array1OfReal knots(1,KNum);
for(int i=0; i<KNum; ++i)
knots.SetValue(i+1, i);
TColStd_Array1OfInteger mults(1,KNum);
for(int i=0; i<KNum; ++i)
if(i == 0 || i == KNum-1)
mults.SetValue(i+1, degree+1);
else
mults.SetValue(i+1, 1);
Handle(Geom_BSplineCurve) curve = new Geom_BSplineCurve(points, knots, mults, degree);
TopoDS_Edge ed1 = BRepBuilderAPI_MakeEdge(curve);
TopoDS_Wire wr1 = BRepBuilderAPI_MakeWire(ed1);
Handle(AIS_ColoredShape) ais = new AIS_ColoredShape(wr1);
ais->SetColor(Quantity_NOC_SIENNA);
view->Display(ais);
//扫掠圆,BSpline路径 BRepOffsetAPI_MakePipe
{
{
TopoDS_Shape pipe=BRepOffsetAPI_MakePipe(wr1,circleEdge);
Handle(AIS_Shape) aisPipe = new AIS_Shape(pipe);
aisPipe->SetColor(Quantity_NOC_BISQUE);
view->Display(aisPipe);
}
}
}
}
运行效果:
从运行效果上来看,扫掠生成的管道,与曲线形状非常一致。
绿色的管道(粗线)和自定义算法生成的Bezier曲线路径,如下图:
绿色的管道(粗线)和自定义算法生成的NURBS曲线路径,如下图:
绿色的管道(粗线)和OCCT几何内核的NURBS曲线路径,如下图:
以上三个图中,黄色+处,是控制点位置。黄色+的右下边的数字1,2,3,4,5是控制点的顺序号。