目前采用OCC来实现造型,需要保证成形的结果和CATIA的结果一致。因此,首先要分析CATIA中形成的曲线具有什么样的参数,才能用OCC来做出一样的参数曲线来。这一次先用B样条来测试。
1. 首先分析IGES的格式,查阅IGES的标准规范。
中国国家标准有对应的原始英文IGES标准的翻译版本:《GBT 14213-2008初始图形交换规范》或者英文好的直接看原版《Initial Graphics Exchange Specification, IGES 5.3》
IGES格式中,K+1代表控制点的个数,M代表基函数的阶数。按照NURBS的定义,设节点向量的个数为A+1,则有A=K+M+1。
在OCC中,定义L代表控制点个数,Degree代表基函数的阶数,控制点为FlatKnots。按照NURBS定义,有FlatKnots = L+Dgree+1。
二者本质是相同的,其中,FlatKnots=A+1,L=K+1。
下面是IGES参数数据段中,对应B样条线(126)的格式定义。
2. 分析CATIA保存的IGES的B样条曲线的参数。
下面以一个B样条曲线为例,来看看CATIA导出的IGES文件的具体内容。
设三个点坐标为A(1.0,0.0,0.0) B(0.0,0.0,0.0) C(0.0,1.0,1.0) ,按照A,B,C顺序插值获取一个样条线。
首先用CATIA生成该插值B样条曲线,两端的切矢分别为V1=(-0.959683,-0.198757,-0.198757), V2=(0.346512,0.663298,0.663298)。
保存为IGES文件作为文本打开,其中样条的参数数据段如下,即7P对应的行。其中红色标记为B样条曲线的类型标记126,K=8,M=5。绿色为节点向量,紫色为权重,蓝色为控制点的坐标,三个一组。课件节点向量1.0对应的重度是3。如果直接插值为3次B样条(2阶连续曲线),该点的重度应该为1。
126,8,5,0,0,1,0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,1.0,1.0,2.414213562, 7P 4
2.414213562,2.414213562,2.414213562,2.414213562,2.414213562,1.0, 7P 5
1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,0.0,0.0,0.7171572875, 7P 6
-0.05857864376,-0.05857864376,0.4757359313,-0.08786796564, 7P 7
-0.08786796564,0.2757359313,-0.08786796564,-0.08786796564, 7P 8
-0.1071067812,-0.01715728753,-0.01715728753,-0.2485281374, 7P 9
0.2242640687,0.2242640687,-0.2485281374,0.4242640687, 7P 10
0.4242640687,-0.1656854249,0.6828427125,0.6828427125, 7P 11
-6.898041511E-016,1.0,1.0,8.523144705E-018,2.414213562,0.0,0.0, 7P 12
0.0,0,0; 7P 13
转换为IGES标准中的定义表格形式:
索引 | 名称 | 数据 | 说明 |
1 | K | 8 | K+1为控制点个数 |
2 | M | 5 | 阶数 |
3 | PROP1 | 0 | 是否平面曲线 |
4 | PROP2 | 0 | 是否闭曲线 |
5 | PROP3 | 1 | 是否多项式 |
6 | PROP4 | 0 | 是否周期 |
7 | T(-M) | 0.0 | 节点向量第一个 |
… | … | … |
|
7+A | T(N+M) | 2.414213562 | 节点向量最后一个 |
8+A | W(0) | 1.0 | 权重第一个 |
… | … | … |
|
8+A+K | W(K) | 1.0 | 权重最后一个 |
9+A+K | X(0) | 1.0 | 控制点第一个X |
10+A+K | Y(0) | 0.0 | 控制点第一个Y |
11+A+K | Z(0) | 0.0 | 控制点第一个Z |
… | … | … |
|
9+A+4*K | X(K) | -6.90E-16 | 控制点最后一个X |
10+A+4*K | Y(K) | 1.0 | 控制点最后一个Y |
11+A+4*K | Z(K) | 1.0 | 控制点最后一个Z |
12+A+4*K | V(0) | 8.52E-18 | 始参数值 |
13+A+4*K | V(1) | 2.414213562 | 终参数值 |
14+A+4*K | XNORM | 0.0 | 平面曲线的法向 |
15+A+4*K | YNORM | 0.0 |
|
16+A+4*K | ZNORM | 0.0 |
|
那么这样可以看出CATIA采用n=3个点插值出来的B样条曲线默认采用M=5阶的基函数,控制点的个数为3*n。那么n=3个点产生9个控制点。相应的节点向量数量为15。于是对应导出的IGES文件中K=8,M=5,A=14。
这个与NURBS BOOK中的插值算法有所不同,主要是参数u的初始化方法有所区别。NURBS BOOK中设置初始的u可以采用的主要有三种:
a. 等间距equally space:
b. 弦长chord length:然后
c. 向心centripetal method: 然后
再仔细的观察,发现CATIA导出的曲线本质上是采用了第二种弦长法,其方法是在每一个控制点处重度提高到3(重度提高1阶,则连续性降低一阶),并升阶到5阶B样条。结合整体5阶的基函数,其实本质上相当于等效是采用3阶基函数B样条曲线(2阶连续)。将基函数从3阶升到5阶,从而曲线从2阶连续提高到4阶连续。然后各个控制点重度从1增加到3,使控制点处阶数降低2,曲线还是2阶连续。但是为什么CATIA需要这种方式来保存CAD数据,还有待思考。
3. 采用OCC实现相同参数的曲线并保存为IGES。
OCC可以采用GeomAPI提供的插值API:GeomAPI_Interpolate。也可以用BSplCLib提供的B样条底层算法来实现。建议使用API,会简单许多。 OCC中的GeomAPI_Interpolate插值出来的NURBS曲线为3阶,也就是2阶连续曲线,本质上和CATIA是相同的,只是CATIA升阶了。 GeomAPI插值的流程如下,每一步都不能跳过。
a. 确定插值点,确定切矢约束
b. GeomAPI_Interpolate()
c. 用Load()添加约束
d. 用Perform()进行插值
e. 用IsDone()检查计算是否完成
f. 用Curve()获得样条曲线对象
另外GeomAPI_Interpolate函数的参数中有两个要注意的地方:
一是插值点数组TColgp_HArray1OfPnt是一个可以自定义下标范围的类型,而在GeomAPI_Interpolate中要求这个参数的下标从1开始,而不是从0开始。(这个问题找的我吐血,看完插值源代码才明白)
二是Load()函数要求用两种方式给出切矢约束:每个点的切矢及其标致或者两端点的切矢。如果不需要切矢约束,可以采用前者,随意设置每一个点的切矢,之后设置所有点的标志为false。注意不能跨过Load()函数直接进行Perform()。
这样直接生成的B样条曲线为3阶(2阶连续),控制点5个。生成的IGES文件参数数据段如下:
126,4,3,1,0,1,0,0.E+000,0.E+000,0.E+000,0.E+000,1.,2.414213562, 0000001P0000001
2.414213562,2.414213562,2.414213562,1.,1.,1.,1.,1.,1.,0.E+000, 0000001P0000002
0.E+000,0.528595479,-9.763107294E-002,-9.763107294E-002, 0000001P0000003
-0.276142375,-9.763107294E-002,-9.763107294E-002,-0.276142375, 0000001P0000004
0.471404521,0.471404521,0.E+000,1.,1.,0.E+000,2.414213562, 0000001P0000005
-0.E+000,-0.707106781,0.707106781; 0000001P0000006
S 1G 4D 2P 6 T0000001
颜色标记与之前的相同,红色标记为B样条曲线的类型标记126,K=4,M=3。绿色为节点向量,紫色为权重,蓝色为控制点的坐标。
此处发现了一个有趣的地方,这三个点插值获得曲线应该是平面曲线,但是CATIA没有将其标记为平面曲线(见IGES格式参数数据段索引编号为3的数据标记),并且最后一个控制的坐标没有精确为0.0,而是-6.898041511E-016。
为了进一步检验二者是否一致,将OCC生成的IGES导入CATIA,用CATIA的精确测量检查二者最大距离,最大距离结果当然是精确为0。
这样一来,就可以保证在曲线上,用OCC获取的曲线和CATIA获取的曲线精确一致了。但是等等,觉得还是不够?需要导出的阶数也完全一样?好吧,那么只需要进行升阶操作即可,以后再研究。
代码如下:
Handle(Geom_Curve) curve_test1() {Handle(TColgp_HArray1OfPnt) pts = new TColgp_HArray1OfPnt(1,3);pts->SetValue(1,gp_Pnt(1.0,0.0,0.0));pts->SetValue(2,gp_Pnt(0.0,0.0,0.0));pts->SetValue(3,gp_Pnt(0.0,1.0,1.0));//define points to be interpolatedGeomAPI_Interpolate interp(pts,Standard_False,1e-6);gp_Vec v(0.0,0.0,0.0);TColgp_Array1OfVec vtan(v,1,3);Handle(TColStd_HArray1OfBoolean) flags = new TColStd_HArray1OfBoolean(1,3,false);// define tangents and flags for every pole, any data is ok, not used. interp.Load(vtan,flags,Standard_False);interp.Perform();if ( interp.IsDone() ){// return curve with default degree// if you want to increase degree to 5, use this// (interp.Curve())->IncreaseDegree(5);return interp.Curve();}else return NULL; }void iges_write_test() {IGESControl_Controller::Init();IGESControl_Writer ICW ("MM", 0);//creates a writer object for writing in Face mode with millimeters//Handle(Geom_Surface) surf = surface_test();Handle(Geom_Curve) curve = curve_test1();ICW.AddGeom (curve);//adds shape sh to IGES model ICW.ComputeModel();Standard_Boolean OK = ICW.Write ("MyCurveFile.igs");//writes a model to the file MyFile.igs }
这样导出来以后获得的IGES文件内容就和CATIA导出的一模一样了
126,8,5,1,0,1,0,0.E+000,0.E+000,0.E+000,0.E+000,0.E+000,0.E+000, 0000001P0000001
1.,1.,1.,2.414213562,2.414213562,2.414213562,2.414213562, 0000001P0000002
2.414213562,2.414213562,1.,1.,1.,1.,1.,1.,1.,1.,1.,1.,0.E+000, 0000001P0000003
0.E+000,0.717157288,-5.857864376E-002,-5.857864376E-002, 0000001P0000004
0.475735931,-8.786796564E-002,-8.786796564E-002,0.275735931, 0000001P0000005
-8.786796564E-002,-8.786796564E-002,-0.107106781, 0000001P0000006
-1.715728753E-002,-1.715728753E-002,-0.248528137,0.224264069, 0000001P0000007
0.224264069,-0.248528137,0.424264069,0.424264069,-0.165685425, 0000001P0000008
0.682842712,0.682842712,0.E+000,1.,1.,0.E+000,2.414213562, 0000001P0000009
-0.E+000,-0.707106781,0.707106781; 0000001P0000010
S 1G 4D 2P 10 T0000001
和CATIA导出的IGES文件数据一样,绿色为节点向量,紫色为权重,蓝色为控制点的坐标,三个一组,节点向量1.0对应的重度是3。K=8,M=5,A=14。