编程生成一个瓶子
本示例取自Open Cascade SDK欢迎从事OCC开发的朋友一同交流, mail:tongabcd@yeah.net;
1 概述
本示例讲述如何使用Open Cascade来创建一个3D模型,目的并不是介绍所有的Open Cascade类,而是对Open Cascade 有个基本了解;
1.1 基本要求
要求有使用C++的经验。
从编程角度来说,Open Cascade提供了高效的模型库、方法和函数,来进行C++开发。利用这些资源,可以开发出稳定的应用程序。
1.2项目
在这里利用3D几何模型工具提供的类创建一个瓶子,如下图所示:
在这里,会将每一步的在一个函数里生成。函数的完整的源代码在程序中,有一个MakeBottle函数。这个函数在Tutorial/src/MakeBottle.cxx文件中。
1.3 项目说明
首先,定义瓶子的一些参数:
内容 | 参数名称 | 参数值 |
瓶高 | MyHeight | 70mm |
瓶宽 | MyWidth | 50mm |
瓶厚 | MyThickness | 30mm |
现在,假定世界笛卡尔直角坐标系的原点位于瓶子底部的中心。
创建这个模型需要四步:
l 创建瓶子的外形;
l 创建瓶子体;
l 创建瓶颈上的螺纹;
l 合并生成瓶子;
2 创建瓶子外形
2.1 定义关键点
为了创建瓶子的外型,第一步是生成特征点,这些点的坐标位于XOY平面内,如下图所示,用这些点来生成瓶子的几何外形:
在Open Cascade中,有两个类来描述3D点的X,Y,Z坐标。
l gp_Pnt类
l 通过句柄分配一个临时的Geom_CartesianPoint类;
句柄就是可以自动分配内存的指针类型。那么到底在应用程序中哪种类合适呢?一般的做法是:
l gp_Pnt是按值传递,和其它对象一样,有固定的生命周期;
l Geom_CartesianPoint是按句柄分配,可以多次引用,有长的生命周期;
由于现在定义的这些点只是在生成瓶子的外形时有用,生命周期有限,因此,选择使用gp_Pnt类。
定义一个gp_Pnt对象,只要定义点在世界坐标系的X,Y,Z坐标值就可以了。
gp_Pnt aPnt1(-myWidth / 2. , 0 , 0); gp_Pnt aPnt2(-myWidth / 2. , -myThickness / 4. , 0); gp_Pnt aPnt3(0 , -myThickness / 2. , 0); gp_Pnt aPnt4(myWidth / 2. , -myThickness / 4. , 0); gp_Pnt aPnt5(myWidth / 2. , 0 , 0); |
如果使用Geom_CartesianPoint类,语法会有点不同,所有对象按句柄分配,需要使用标准C++的new关键字,如下所示:
Handle(Geom_CartesianPoint) aPnt1 = new Geom_CartesianPoint(-myWidth / 2. , 0 , 0); |
一旦分配了对象,就可以使用它们的方法。再重复一下,语法和C++一样,比如,为了得到一个点的X坐标:
gp_Pnt aPnt1(0,0,0); Handle(Geom_CartesianPoint) aPnt2 = new Geom_CartesianPoint(0 , 0 , 0); Standard_Real xValue1 = aPnt1.X(); Standard_Real xValue2 = aPnt2->X(); |
2.2 外形轮廓:定义几何体
利用前面定义的点,可以计算瓶子几何外形的一部分。如下图所示,它由两个线段和一个圆弧组成。
myImageBottle08
为了创建这个实体,需要定义一个数据结构,来实现3D几何对象,可以在Open Cascade 的Geom开发包中找到。
一个Open Cascade开发包是一组类。这一组类完成的功能相同,或者结构类似,Open Cascade类的前面的名称和包的名称一样,比如,Geom_Line类和Geom_Circle类都属于Geom包。Geom包生成3D几何对象:一般的曲线和多个曲线(比如Bezier和BSpline)所组成的平面.
Geom包只提供了几何实体的数据结构。可以直接创建属于Geom包的类。因此,使用GC包中的计算一般曲线和平面的类更容易一些。
这是因为在GC包中提供的两个有关计算的类,它对外形计算来说更合适:
l GC_MakeSegment类用来创建一个线段。其中一个构造函数是用两个点P1和P2来定义一个线段。
l GC_MakeArcOfCircle类创建一个园的一部分(圆弧)。一个非常有用的构造函数是用圆弧的两个端点和所通过的一个点来生成;
这些类通过一个句柄返回一个Geom_TrimmedCurve。这个实体表示一个基本的曲线。通过两个参数来限制。比如,一个圆C的参数是0到2PI,如果需要生成一个四分之一圆,在C上应用Geom_TrimmedCurve方法,将值限制为0到PI/2:
Handle(Geom_TrimmedCurve) aArcOfCircle = GC_MakeArcOfCircle(aPnt2,aPnt3 ,aPnt4); Handle(Geom_TrimmedCurve) aSegment1 = GC_MakeSegment(aPnt1 , aPnt2); Handle(Geom_TrimmedCurve) aSegment2 = GC_MakeSegment(aPnt4 , aPnt5); |
所有的GC类都提供有重载的方法,可以根据不同的函数类型得到相应的结果,可以调用它的IsDone方法,通过返回的值来进行不同处理,这样程序会更安全,比如:
GC_MakeSegment mkSeg (aPnt1 , aPnt2); Handle(Geom_TrimmedCurve) aSegment1; if(mkSegment.IsDone()){ aSegment1 = mkSeg.Value(); ... } |
2.3 外形:定义拓朴结构
现在已经创建了外形的一部分,但是这些曲线还是相互独立的,相互间没有任何关系。
为了建模方便,需要将这三条曲线生成为一个实体对象。
在TopoDS包中,有一个Open Cascade拓朴数据结构,可以使用它来完成:它可以链接一个几何实体到另一个几何实体上,生成复合图形;
这个包中的对象,都是从TopeDS_Shape基类继承而来的,下面是各种拓朴图形的描述:
图形 | Open Cascade类 | 描述 |
顶点 | TopoDS_Vertex | 零维图形,表示几何体上的一个点 |
边 | TopoDS_Edge | 一维图形,表示一个曲线和一个有边界的向量 |
网格 | TopoDS_Wire | 由顶点连起来的一系列边 |
面 | TopoDS_Face | 由闭合的网格组成的边界平面 |
壳 | TopoDS_Shell | 通过边连接起起来一组面 |
体 | TopoDS_Solid | 由壳组成的有边界的三维空间 |
复合体 | TopoDS_CompSolid | 通过面连接的一组体 |
复合对象 | TopoDS_Compound | 由上面各种图形形成的一个集合 |
参考前面的表,可以看出,创建一个外形轮廓,需要生成:
l 用前面生成的曲线构建出三条边
l 用这些边形成一个网格
myImageBottle10
TopoDS包中只提供了拓扑实体的数据结构。在BRepBuilderAPI包中,可以找到计算标准拓朴对象的算法类。
为了创建一个边,通过前面得出曲线,使用BRepBuilderAPI_MakeEdge类来完成:
TopoDS_Edge aEdge1 = BRepBuilderAPI_MakeEdge(aSegment1); TopoDS_Edge aEdge2 = BRepBuilderAPI_MakeEdge(aArcOfCircle); TopoDS_Edge aEdge3 = BRepBuilderAPI_MakeEdge(aSegment2); |
在Open CASCADE中,有几种创建边的方法。可以直接通过两点来创建一个边,在这种情况下所生成的边是直线。输入的两个顶点是直线的两个端点。比如:
TopoDS_Edge aEdge1 = BRepBuilderAPI_MakeEdge(aPnt1 , aPnt3); TopoDS_Edge aEdge2 = BRepBuilderAPI_MakeEdge(aPnt4 , aPnt5); |
可以这样简单地生成边aEdge1和 aEdge3 。
为了将边连接起来,需要用BRepBuilderAPI_MakeWire类来生成一个网格。有两种方法:
l 直接通过一至四个边来生成;
l 在一个现有的网格上添加其它的网格或边(这点在本示例的后面会解释);
用低于四个边来生成网格时,可以直接使用下面的构造函数来直接生成:
TopoDS_Wire aWire = BRepBuilderAPI_MakeWire(aEdge1 , aEdge2 , aEdge3); |
2.4外形轮廓:完成外形
当创建完网格的第一部分后,接下来需要做的是生成整个外形。比较简单的方法是:
1. 通过反射现在的网格生成一个新的网格;
2. 添加反射的网格到原来的网格中;
myImageBottle12
为了在图形上应用变换,需要使用gp_Trsf类来定义一个3D几何变换属性。这种变换可能是平移、旋转、缩放或反射,或者这几种的组合;
现在的情况是,需要定义一个反射,它以世界坐标系下的X轴做为对称轴。轴,可以用gp_Ax1来定义,通过一个点和一个方向(3D单位向量)来生成。
定义这个轴有两种方法:
l 第一种方法是根据草图,使用几何方法定义:
X轴位于(0 , 0 , 0):使用gp_Pnt类
X辆的方向是(1 , 0 , 0):使用gp_Dir类,通过它的X,Y,Z坐标来生成一个gp_Dir实例
gp_Pnt aOrigin(0 , 0 , 0); gp_Dir xDir(1 , 0 , 0); gp_Ax1 xAxis(aOrigin , xDir); |
l 第二种也是最简单的方法是直接使用gp包中的几何常量(原点,常用方向及世界坐标系下的坐标轴),得到X轴,可以调用gp::OX方法:
gp_Ax1 xAxis = gp::OX(); |
前面说过,可以使用gp_Trsf类来定义一个3D几何变换属性。使用这个类有两种不同的方法:
l 根据草图定义一个变换矩阵;
l 利用恰当的方法生成所需的变换(平移用SetTranslation(),镜像用SetMirror()等):变换矩阵自动计算完成;
一般来说,最简单的总是最好的。
使用SetMirror方法,用轴来做为对称中心;
gp_Trsf aTrsf; aTrsf.SetMirror(xAxis); |
使用BRepBuilderAPI_Transform类来应用所定义的变换。 至此,所需的要数据都已准备完成:
l 要应用变换的图形
l 几何变换
BRepBuilderAPI_Transform aBRepTrsf(aWire , aTrsf); |
BRepBuilderAPI_Transform并不修改原来的图形:反射生成的网格仍是一个网格。但是调用类似BRepBuilderAPI_Transform::Shape这样的方法会返回一个TopoDS_Shape对象:
TopoDS_Shape aMirroredShape = aBRepTrsf.Shape(); |
假如需要反射后的结果也是一个网格。TopoDS全局函数提供生成一个图形为它的实际类型的方法。为了生成经过变换的网格,可以使用TopoDS::Wire方法;
TopoDS_Wire aMirroredWire = TopoDS::Wire(aMirroredShape); |
瓶子的外形快完成了。已经创建了两个网格:一个网格和一个反射网格。现在要做的是把它们合成为一个单一的图形。可以使用BRepBuilderAPI_MakeWire类来做这点:
a) 创建一个BRepBuilderAPI_MakeWire实例对象
b) 使用Add方法将两个网格的所有边都添加到此对象上。
BRepBuilderAPI_MakeWire mkWire; mkWire.Add(aWire); mkWire.Add(aMirroredWire); TopoDS_Wire myWireProfile = mkWire.Wire(); |
(未完 待续)