编程生成一个瓶子(2)

 

3 创建瓶体

3.1 轮廓体

为了生成瓶子的瓶体。需要生成一个实体图形,最简单的方法是使用前面创建的外形并沿着一个方向进行推移:Open CASCADE 的实体函数非常适合实现它。它接受一个图形和一个方向为输入参数;然后生成一个图形,生成的规则如下:

图形

生成

顶点

网格

复合的实体

现在的外形是一个网格。参考图形/生成表。利用网格面可以生成体;

编程生成一个瓶子(2)_第1张图片

为了生成面,可以使用BRepBuilderAPI_MakeFace类。前面说过,面是由封闭的网格生成的有边界表面。

通常,面可以由一个表面和一个或多个网格通过BRepBuilderAPI_MakeFace来生成。

如果网格位于一个平面内,表面会自动生成:

TopoDS_Face myFaceProfile = BRepBuilderAPI_MakeFace(myWireProfile);

BRepPrimAPI包中提供了一些类,用来生成具有拓朴结构的图元:长方体、园环、园柱、球等。都位于BRepPrimAPI_MakePrism类中,按照上面所说,这个类要由这些来定义:

l         推移所用的基本图形

l         有一定长度的向量或有一定长度的方向,或者无限长;

 

现在需要的是有一定长度的实体,沿着Z轴推移myHeight大小的高度。这个向量,可以由gp_Vec类来定义它的XYZ坐标值:

gp_Vec aPrismVec(0 , 0 , myHeight);

所有生成瓶体的数据现在都准备好了,下面所做的只是用BRepPrimAPI_MakePrism类来生成实体:

TopoDS_Shape myBody = BRepPrimAPI_MakePrism(myFaceProfile , aPrismVec);

3.2 园角

生成的瓶体,边角比较锐利,要用圆滑的面打钝它。可以使用在Open CASCADE中的园角功能;

根据位置的不同,园角实现方法也不同,比如,可以沿着一条直线,或者沿着边缘线来计算。我们的目的是,用简单的方法来生成园角,约定:

l         在图形的所有边上做园角

l         园角的半径值为myThickness/12;

编程生成一个瓶子(2)_第2张图片为了生成一个边的园角,可以使用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();

 

3.3添加瓶颈

要添加瓶颈,需生成一个园柱并把它焊到瓶体上;园柱的位置在瓶体的顶面,半径设定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);

3.4创建镂空实体

实际的瓶子都是用来装液体的,现在要从瓶子顶面生成一个镂空的实体;

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_PlaneGeom_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);

 

 

你可能感兴趣的:(编程,图形,网格,algorithm,数据结构,扩展)