为了生成瓶子的瓶体。需要生成一个实体图形,最简单的方法是使用前面创建的外形并沿着一个方向进行推移:Open CASCADE 的实体函数非常适合实现它。它接受一个图形和一个方向为输入参数;然后生成一个图形,生成的规则如下:
图形 |
生成 |
顶点 |
边 |
边 |
面 |
网格 |
壳 |
面 |
体 |
壳 |
复合的实体 |
现在的外形是一个网格。参考图形/生成表。利用网格面可以生成体;
为了生成面,可以使用BRepBuilderAPI_MakeFace类。前面说过,面是由封闭的网格生成的有边界表面。
通常,面可以由一个表面和一个或多个网格通过BRepBuilderAPI_MakeFace来生成。
如果网格位于一个平面内,表面会自动生成:
TopoDS_Face myFaceProfile = BRepBuilderAPI_MakeFace(myWireProfile); |
在BRepPrimAPI包中提供了一些类,用来生成具有拓朴结构的图元:长方体、园环、园柱、球等。都位于BRepPrimAPI_MakePrism类中,按照上面所说,这个类要由这些来定义:
l 推移所用的基本图形
l 有一定长度的向量或有一定长度的方向,或者无限长;
现在需要的是有一定长度的实体,沿着Z轴推移myHeight大小的高度。这个向量,可以由gp_Vec类来定义它的X,Y,Z坐标值:
gp_Vec aPrismVec(0 , 0 , myHeight); |
所有生成瓶体的数据现在都准备好了,下面所做的只是用BRepPrimAPI_MakePrism类来生成实体:
TopoDS_Shape myBody = BRepPrimAPI_MakePrism(myFaceProfile , aPrismVec); |
生成的瓶体,边角比较锐利,要用圆滑的面打钝它。可以使用在Open CASCADE中的园角功能;
根据位置的不同,园角实现方法也不同,比如,可以沿着一条直线,或者沿着边缘线来计算。我们的目的是,用简单的方法来生成园角,约定:
l 在图形的所有边上做园角
l 园角的半径值为myThickness/12;
为了生成一个边的园角,可以使用BRepFilletAPI_MakeFillet类,这个类的一般用法是:
1. 在BRepFilletAPI_MakeFillet 的构造函数中传入需要园角的图形;
2. 应用Add方法,同时加入园角的参数值(要加入的边和半径),在这里,要加入所有需园角的边;
3. 使用Shape方法得到园角后的图形;
BRepFilletAPI_MakeFillet mkFillet(myBody); |
为了添加园角的说明,需要知道图形都有哪些边。最好的解决方法是遍历实体的所有边。可以用TopExp_Explorer类来完成。
要遍历的数据结构定义是在TopoDS_Shape中,可以根据需要扩展子图形;
一般来说,要遍历的话,要提供下面的内容:
l 要遍历的图形
l 要找的子图形的类型。可以用TopAbs_ShapeEnum枚举结构体来定义它。
TopExp_Explorer aEdgeExplorer(myBody , TopAbs_EDGE); |
应用一个遍历循环过程中主要用到三个方法:
l 是否还有子图形要遍历;
l 得到当前要遍历的子图形;
l 得到下一个要遍历的子图形( 如果More()方法返回的结果为真);
while(aEdgeExplorer.More()){ TopoDS_Edge aEdge = TopoDS::Edge(aEdgeExplorer.Current()); //Add edge to fillet algorithm ... aEdgeExplorer.Next(); } |
在遍历循环中,找瓶子图形的所有边。每个边都用Add方法添加到BRepFilletAPI_MakeFillet实例中。记住,要把要园角的半径值也传进去;
mkFillet.Add(myThickness / 12. , aEdge); |
一旦完成,最后一步是得到园角后的图形:
myBody = mkFillet.Shape(); |
要添加瓶颈,需生成一个园柱并把它焊到瓶体上;园柱的位置在瓶体的顶面,半径设定myThickness/4,高度设定myHeight/10。
gp_Pnt neckLocation(0 , 0 , myHeight); gp_Dir neckNormal = gp::DZ(); gp_Ax2 neckAx2(neckLocation , neckNormal); |
为了定位园柱,要用gp_Ax2类来定义一个右手坐标系,这个坐标系可以由一个点和两个方向来定义,这两个方向分别是法线方向和X轴方向,Y轴方向可以由这两个方向计算得出。
瓶子体上面的中心,在世界坐标系中的坐标是(0,0,myHeight),法线是Z轴。所以,局部坐标系可以这们定义:
gp_Pnt neckLocation(0 , 0 , myHeight); gp_Dir neckNormal = gp::DZ(); gp_Ax2 neckAx2(neckLocation , neckNormal); |
为了生成园柱,要用图元包中的另一个类:BRepPrimAPI_MakeCylinder类。需要提供以下内容:
l 园柱所在的坐标系;
l 园柱的半径和高度;
Standard_Real myNeckRadius = myThickness / 4.; Standard_Real myNeckHeight = myHeight / 10; TopoDS_Shape myNeck = BRepPrimAPI_MakeCylinder(neckAx2 , myNeckRadius , myNeckHeight); |
现在有了两部分,瓶子体和要焊上去的瓶颈;
在BRepAlgoAPI包中提供了对两个图形布尔操作的方法:共同(布尔交),切(布尔减),焊(布尔并);
使用BRepAlgoAPI_Fuse可以把两个图形焊在一起:
myBody = BRepAlgoAPI_Fuse(myBody , myNeck); |
实际的瓶子都是用来装液体的,现在要从瓶子顶面生成一个镂空的实体;
在Open CASCADE中,一个镂空的实体称为一个有厚度的体,生成步骤:
1. 从初始的实体中移除一个或多个面得到第一个镂空实体的面W1;
2. 生成一个平行于W1的面W2,两个面之间的距离是D,如果D为正,则W2在初始实体的外面,否则在里面;
3. 通过W1和W2生成实体;
for(TopExp_Explorer aFaceExplorer(myBody , TopAbs_FACE) ; aFaceExplorer.More() ; aFaceExplorer.Next()) { TopoDS_Face aFace = TopoDS::Face(aFaceExplorer.Current()); TopoDS_Face aFace = TopoDS::Face(aFaceExplorer.Current()); } |
为了生成一个有厚度的实体,可以生成一个BRepOffsetAPI_MakeThickSolid类的实例,给它传递以下值:
l 要镂空的图形
l 需要计算的公差
l 两个面W1和W2之间的厚度(距离D)
l 从原始的实体中移除面而生成的第一个面W1;
这一步最不好做的是从图形中找到要移除的面:瓶颈园柱体的顶面;
这个面:
l 在几何体上的一个平面;
l 瓶子上的最高的面(按Z轴来说);
有了这个面的这些特征,可以再次遍历瓶子的所有面并找到这个面:
for(TopExp_Explorer aFaceExplorer(myBody , TopAbs_FACE) ; aFaceExplorer.More() ; aFaceExplorer.Next()){ TopoDS_Face aFace = TopoDS::Face(aFaceExplorer.Current()); TopoDS_Face aFace = TopoDS::Face(aFaceExplorer.Current()); } |
为了检测每个面,需要找到它的表面。有一个工具类可以处理图形的几何属性:BRep_Tool类,这个类常见的用法有:
l 对于表面:可以处理表面上的面;
l 对于曲线:可以处理曲线上的边;
l 对于向量:可以处理向量上的点;
Handle(Geom_Surface) aSurface = BRep_Tool::Surface(aFace); |
可以看出,BRep_Tool::Surface方法返回一个Geom_Surface类的实例,这个实例是通过句柄分配;
但是,Geom_Surface类并不提供aSurface对象的实际类型的信息,这个引用的类型可能是Geom_Plane,Geom_CylindricalSurface等;
类似Geom_Surface,所有的对象是通过句柄分配,这些对象是从Standard_Transient派生出来的。
在这里与类型相关的两个有用的方法是:
l DynamicType方法:可以得出对象的实际类型;
l IsKind方法:可以计算一个对象是否是从某种类型派生出来的;
DynamicType方法返回对象的实际类型,但是要用它和已知类型比较来得出aSurface是一个平面,还是一个园柱侧面或其它类型;
为了用所给定的类型和要找的类型相比较,可以使用STANDARD_TYPE宏。这个宏返回一个类的类型;
if(aSurface->DynamicType() == STANDARD_TYPE(Geom_Plane)){ ... } |
如果比较的结果为真,可以得出aSurface的实际类型是Geom_Plane。
接下来,可以在Standard_Transient中找到另一个有用的方法,把它从Geom_Surface转化为Geom_Plane,这个方法是DownCast方法;正如它的名字一样,这个静态方法用来把一种给定的类型转化为另一种类型:
Handle(Geom_Plane) aPlane = Handle(Geom_Plane)::DownCast(aSurface); |
记住,转化的目的是为了找到瓶子所有表面中最顶部的平面。、
现在假设有了这两个全局变量:
TopoDS_Face faceToRemove; Standard_Real zMax = -1; |
接下来,使用Geom_Plane::Location方法就很容易找到在Z方向上哪个面的原始点最大,比如:
gp_Pnt aPnt = aPlane->Location(); Standard_Real aZ = aPnt.Z(); if(aZ > zMax) { zMax = aZ; faceToRemove = aFace; } |
现在已经找到的瓶颈的顶面。在生成一个镂空的实体前,最后要做的一步是要把这个面放在一个链表中。因为有可能从原始的实体中移除一个或多个面,所以,BRepOffsetAPI_MakeThickSolid构造函数中传进去的参数是一个面的链表;
Open CASCADE为不同的的对象提供了多种集合,Geom包中对象的集合类都在TColGeom包中;gp包中的对象的集合类都在TColgp包中。
图形的集合是在TopTools包中。由于BRepOffsetAPI_MakeThickSolid需要一个列表,因此,使用TopTools_ListOfShape类:
TopTools_ListOfShape facesToRemove; facesToRemove.Append(faceToRemove); |
现在,所有必须的数据都准备好了,现在可以调用BRepOffsetAPI_MakeThickSolid构造函数来生成一个镂空的实体了;
MyBody = BRepOffsetAPI_MakeThickSolid (myBody , facesToRemove , -myThickness / 50 , 1.e-3); |