3. 瓶子主体
3.1. 柱体轮廓
生成瓶子主体,需要创建一个实心的柱体。最简单的方法是用先前创建的底面轮廓沿着一个方向推移:OCC的功能很合适这样的操作。OCC可以根据一个形状和一个方向生成实体:
形状 | 生成 |
Vertex 顶点 | Edge 边 |
Edge 边 | Face 表面 |
Wire 网格 | Shell 壳 |
Face 表面 | Solid 体 |
Shell 壳 | Compound of Solids 组合体 |
当前的轮廓是一个wire,根据上面的表,应该通过wire获得face,再通过face生成solid。
用 BRepBuilderAPI_MakeFace 类创建face。如前面所说, face是一个由闭合wire组成的表面轮廓的一部分。通常,BRepBuilderAPI_MakeFace类可以将一个或多个wire生成face。
如果wire在一个平面上,则surface将自动生成。
TopoDS_Face myFaceProfile = BRepBuilderAPI_MakeFace(myWireProfile); |
BRepPrimAPI 开发包提供了很多类来创建拓扑图元:boxes, cones, cylinders, spheres, 等等。 其中 BRepPrimAPI_MakePrism 类。 上面的柱体可以这个创建:
gp_Vec aPrismVec(0 , 0 , myHeight); |
|
TopoDS_Shape myBody = BRepPrimAPI_MakePrism(myFaceProfile , aPrismVec); |
3.2. 圆角
瓶体现在已经生成,但边棱比较尖锐生硬。现在要用OCC的圆角方面的功能来圆滑它。
圆角的方法有很多,比如沿着一条线,或者棱线,我们这里采用比较简单的方法:
• 圆角所有的边
• 圆角半径 myThickness / 12
在尖锐的边上圆角使用 BRepFilletAPI_MakeFillet 类。使用方法:
• 使用 BRepFilletAPI_MakeFillet 类的构造函数传入需要圆角的图形。
• 使用Add函数添加要圆角的边和半径。
• 用Shape函数返回圆角结果。
BRepFilletAPI_MakeFillet mkFillet(myBody); |
为了添加圆角的参数边,必须知道图形都有哪些边。最好的解决方法是通过遍历获得形体的所有边。这里面用到TopExp_Explorer类提供的功能和TopoDS_Shape类提供的数据结构,下面我们通过程序来看一下通过遍历,将边作为参数传入圆角类的过程:
TopExp_Explorer aEdgeExplorer(myBody , TopAbs_EDGE); |
while(aEdgeExplorer.More()) { TopoDS_Edge aEdge = TopoDS::Edge(aEdgeExplorer.Current()); //Add edge to fillet algorithm ... aEdgeExplorer.Next(); } |
mkFillet.Add(myThickness / 12. , aEdge); |
myBody = mkFillet.Shape(); |
3.3. 瓶颈
要添加瓶颈,需生成一个园柱并把它焊到瓶体上;园柱的位置在瓶体的顶面,半径设定myThickness/4,高度设定myHeight/10。
为了定位园柱,要用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 类。 需要以下信息:
• 圆柱所在的坐标系
• 半径和高度
Standard_Real myNeckRadius = myThickness / 4.; Standard_Real myNeckHeight = myHeight / 10; TopoDS_Shape myNeck = BRepPrimAPI_MakeCylinder(neckAx2 , myNeckRadius , myNeckHeight); |
现在要把瓶体和瓶颈2个部分连接在一起。
BRepAlgoAPI 开发包中提供了图元间的布尔运算。包括交,并,差 三种运算。
用 BRepAlgoAPI_Fuse 可以将2个图元焊接在一起:
myBody = BRepAlgoAPI_Fuse(myBody , myNeck); |
3.4. 将瓶子镂空
一个真正的瓶子是用来装液体的,所以要镂空,下面看看OCC镂空这个瓶子,将其变成有厚度的实体的步骤:
• 从初始的实体中移除一个或多个面得到第一个镂空实体的面W1;
• 生成一个平行于W1的面W2,两个面之间的距离是D,如果D为正,则W2在初始实体的外面,否则在里面;
• 通过W1和W2生成实体;
为了生成一个有厚度的实体,可以生成一个BRepOffsetAPI_MakeThickSolid类的实例,给它传递以下值:
•要镂空的图形
•需要计算的公差
•两个面W1和W2之间的厚度(距离D)
•从原始的实体中移除面而生成的第一个面W1;
这一步最不好做的是从图形中找到要移除的面:瓶颈园柱体的顶面;
•在几何体上的一个平面
•瓶子上的最高的面(按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类,这个类常见的用法有:
•对于表面:可以处理表面上的面;
•对于曲线:可以处理曲线上的边;
•对于向量:可以处理向量上的点;
Handle(Geom_Surface) aSurface = BRep_Tool::Surface(aFace); |
可以看出,BRep_Tool::Surface方法返回一个Geom_Surface类的实例,这个实例是通过句柄分配;
但是,Geom_Surface类并不提供aSurface对象的实际类型的信息,这个引用的类型可能是Geom_Plane,Geom_CylindricalSurface等;
类似Geom_Surface,所有的对象是通过句柄分配,这些对象是从Standard_Transient派生出来的。
在这里与类型相关的两个有用的方法是:
• DynamicType方法:可以得出对象的实际类型
• 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); |