Open CASCADE Technology(OCCT)是一个面向对象的 C++ 框架,旨在快速生产复杂的 CAD / CAM / CAE 应用程序。它为 C++ 环境中的原始 2D 和 3D 建模提供了无限的可能性。OCCT 为 2D 和 3D 对象的建模、编辑、可视化和数据互操作性提供了足够的构建块。
通过使用 OCCT,用户可以使用原始代码命令创建他们想要的对象(或编辑现有的对象)。
这篇文章来源于 dev.opencascade.org,使用 Microsoft Edge 翻译,原文 Open CASCADE Technology - Modeling: Bottle Tutorial
本教程将教您如何使用 Open CASCADE Technology 服务对 3D 对象进行建模。本教程的目的不是介绍所有 Open CASCADE Technology 课程,而是帮助您开始思考将 Open CASCADE Technology 作为一种工具。
本教程假定您具有使用和设置 C++ 的经验。从编程的角度来看,Open CASCADE Technology 旨在通过 3D 建模类、方法和函数增强您的 C++ 工具。所有这些资源的组合将允许您创建大量应用程序。
为了说明如何使用 3D 几何建模工具包中提供的类,您将创建一个瓶子,如下所示:
在本教程中,我们将逐步创建一个函数,该函数将对瓶子进行建模,如上所示。您将找到本教程的完整源代码,包括 Open CASCADE Technology 发行版中的 MakeBottle 函数。函数体在文件 samples/qt/Tutorial/src/MakeBottle.cxx
中提供。
我们首先定义瓶子规格如下:
对象参数 | 参数名称 | 参数值 |
---|---|---|
Bottle height | MyHeight | 70mm |
Bottle width | MyWidth | 50mm |
Bottle thickness | MyThickness | 30mm |
此外,我们决定瓶子的轮廓(底部)将以全局笛卡尔坐标系的原点为中心。
此建模需要四个步骤:
要创建瓶子的轮廓,首先要创建特征点及其坐标,如下图 (XOY) 平面所示。这些点将是定义轮廓几何形状的支座。
在 Open CASCADE Technology 中,有两个类可以从 X、Y 和 Z 坐标描述 3D 笛卡尔点:
句柄(handle)是一种智能指针,可提供自动内存管理。要为此应用程序选择最佳类,请考虑以下事项:
gp_Pnt 是由值(value)操纵的。像所有同类对象一样,它的生存期有限。
Geom_CartesianPoint 由句柄(handle)操作,可能具有多个引用和较长的生存期。
由于您将定义的所有点仅用于创建轮廓的曲线,因此具有有限生存期的对象就可以了。选择 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);
实例化对象后,可以使用类提供的方法访问和修改其数据。例如,要获取点的 X 坐标:
Standard_Real xValue1 = aPnt1.X();
借助先前定义的点,您可以计算瓶子轮廓几何形状的一部分。如下图所示,它将由两条线段和一条弧线组成。
若要创建此类实体,需要一个特定的数据结构,用于实现 3D 几何对象。这可以在 Open CASCADE Technology 的 Geom 软件包(package)中找到。在 Open CASCADE Technology 中,软件包(package)是一组提供相关功能的类。这些类的名称以它们所属的包(package)的名称开头。例如,Geom_Line 和 Geom_Circle 类属于 Geom 包。Geom 软件包实现了 3D 几何对象:提供了基本曲线和曲面以及更复杂的曲线和曲面(例如 Bezier 和 BSpline)。但是,Geom 包仅提供几何实体的数据结构。您可以直接实例化属于 Geom 的类,但使用 GC 包可以更轻松地计算基本曲线和曲面。这是因为 GC 提供了两个算法类,它们正是我们轮廓所需的:
这两个类都返回由句柄操作的 Geom_TrimmedCurve。此实体表示一条基本曲线(在我们的例子中为线段或圆),限制在其两个参数值之间。例如,圆 C 在 0 和 2PI 之间参数化。如果需要创建四分之一的圆,则可以在 C 上创建一个限制在 0 和 M_PI/2 之间的 Geom_TrimmedCurve。
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
和 Value
方法。例如:
GC_MakeSegment mkSeg (aPnt1, aPnt2);
Handle(Geom_TrimmedCurve) aSegment1;
if(mkSeg.IsDone()){
aSegment1 = mkSeg.Value();
}
else {
// 处理错误
}
您已经创建了轮廓一部分的支座几何体,但这些曲线是独立的,彼此之间没有关系。为了简化建模,将这三条曲线作为一个实体进行操作是正确的。这可以通过使用 TopoDS 软件包中定义的 Open CASCADE Technology 的拓扑数据结构来实现:它定义了几何实体之间的关系,这些实体可以链接在一起以表示复杂的形状。从 TopoDS_Shape 类继承的 TopoDS 包的每个对象都描述了一个拓扑形状,如下所述:
形状 | Open CASCADE Technology 类 | 描述 |
---|---|---|
Vertex | TopoDS_Vertex | 对应于几何图形中某个点的零维形状。 |
Edge | TopoDS_Edge | 对应于曲线的一维形状,以每端的顶点为界。 |
Wire | TopoDS_Wire | 由顶点连接的边序列。 |
Face | TopoDS_Face | 由闭合线包围的曲面的一部分。 |
Shell | TopoDS_Shell | 由边连接的一组面。 |
Solid | TopoDS_Solid | 以 Shell 为边界的 3D 空间的一部分。 |
CompSolid | TopoDS_CompSolid | 由它们的面连接的一组实体。 |
Compound | TopoDS_Compound | 上述任何其他形状的集合。 |
参考上表,要构建轮廓,您将创建:
但是,TopoDS 包仅提供拓扑实体的数据结构。可用于计算标准拓扑对象的算法类可以在 BRepBuilderAPI 包中找到。要创建一条边,请将 BRepBuilderAPI_MakeEdge 类与先前计算的曲线一起使用:
TopoDS_Edge anEdge1 = BRepBuilderAPI_MakeEdge(aSegment1);
TopoDS_Edge anEdge2 = BRepBuilderAPI_MakeEdge(aArcOfCircle);
TopoDS_Edge anEdge3 = BRepBuilderAPI_MakeEdge(aSegment2);
在 Open CASCADE Technology 中,您可以通过多种方式创建边(edge)。一种可能性是直接从两个点创建一条边,在这种情况下,该边的基础几何是一条线,由从两个输入点自动计算的两个顶点为边界。例如,anEdge1 和 anEdge3 可以采用更简单的方式计算:
TopoDS_Edge anEdge1 = BRepBuilderAPI_MakeEdge(aPnt1, aPnt3);
TopoDS_Edge anEdge3 = BRepBuilderAPI_MakeEdge(aPnt4, aPnt5);
要连接边(edge),您需要使用 BRepBuilderAPI_MakeWire 类创建一条线(wire)。有两种方法可以使用这个类创建一条线:
当从少于四条边构建线时,如本例所示,您可以直接使用构造函数,如下所示:
TopoDS_Wire aWire = BRepBuilderAPI_MakeWire(anEdge1, anEdge2, anEdge3);
创建线的第一部分后,您需要计算完整的轮廓。执行此操作的一种简单方法是:
若要对形状(包括线(wire))应用变换,首先需要使用 gp_Trsf 类定义 3D 几何变换的属性。这种转换可以是平移、旋转、缩放、反射或这些转换的组合。在我们的例子中,我们需要定义相对于全局坐标系的 X 轴的反射。用 gp_Ax1 类定义的轴由一个点构建,并具有一个方向(3D 单位向量)。有两种方法可以定义这个轴。第一种方法是从头开始定义它,使用其几何定义:
gp_Pnt aOrigin(0, 0, 0);
gp_Dir xDir(1, 0, 0);
gp_Ax1 xAxis(aOrigin, xDir);
第二种也是最简单的方法是使用 gp 包中定义的几何常数(全局坐标系的原点、主方向和轴)。要获取 X 轴,只需调用 gp::OX 方法:
gp_Ax1 xAxis = gp::OX();
如前所述,3D 几何变换是使用 gp_Trsf 类定义的。有两种不同的方式来使用这个类:
SetTranslation
、用于反射的 SetMirror
等):来自动计算矩阵。由于最简单的方法总是最好的方法,因此您应该使用以轴为对称中心的 SetMirror
方法。
gp_Trsf aTrsf;
aTrsf.SetMirror(xAxis);
现在,通过指定以下内容,您拥有了使用 BRepBuilderAPI_Transform 类应用转换所需的所有数据:
BRepBuilderAPI_Transform aBRepTrsf(aWire, aTrsf);
BRepBuilderAPI_Transform 不会改变形状的性质:反射线(wire)的结果仍然是线(wire)。但是类似函数的调用或 BRepBuilderAPI_Transform::Shape
方法返回一个 TopoDS_Shape 对象:
TopoDS_Shape aMirroredShape = aBRepTrsf.Shape();
您需要的是一种将生成的反射形状视为线(wire)的方法。TopoDS 全局函数通过将形状转换为其实际类型来提供此类服务。若要转换变换后的线(wire),请使用 TopoDS::Wire 方法。
TopoDS_Wire aMirroredWire = TopoDS::Wire(aMirroredShape);
瓶子的轮廓几乎完成了。您已经创建了两条线:aWire
和 aMirroredWire
。您需要将它们连接起来以计算单个形状。为此,请使用 BRepBuilderAPI_MakeWire 类,如下所示:
Add
方法添加两条线(wire)的所有边。BRepBuilderAPI_MakeWire mkWire;
mkWire.Add(aWire);
mkWire.Add(aMirroredWire);
TopoDS_Wire myWireProfile = mkWire.Wire();
要计算瓶体,您需要创建一个实体形状。最简单的方法是使用先前创建的轮廓并沿一个方向延伸它。Open CASCADE Technology 的 Prism 功能最适合该任务。它接受一个形状和一个方向作为输入,并根据以下规则生成一个新形状:
Shape | Generates |
---|---|
Vertex | Edge |
Edge | Face |
Wire | Shell |
Face | Solid |
Shell | Compound of Solids |
您当前的轮廓是线(wire)。参考“形状/生成”(Shape/Genesets)表,您需要从其线(wire)中计算面(face)以生成实体(solid)。若要创建面(face),请使用 BRepBuilderAPI_MakeFace 类。如前所述,面(face)是以闭合线(wire)为界的曲面的一部分。通常,BRepBuilderAPI_MakeFace 从曲面和一条或多条线(wire)中计算面(face)。当线(wire)位于平面上时,会自动计算曲面。
TopoDS_Face myFaceProfile = BRepBuilderAPI_MakeFace(myWireProfile);
BRepPrimAPI 包提供了用于创建拓扑基元构造的所有类:长方体、圆锥体、圆柱体、球体等。其中包括 BRepPrimAPI_MakePrism 类。如上所述,Prism 定义如下:
您希望实体是有限的,沿 Z 轴扫描,并且是 myHeight
高度。向量在其 X、Y 和 Z 坐标上用 gp_Vec 类定义,为:
gp_Vec aPrismVec(0, 0, myHeight);
创建瓶体的所有必要数据现在都可用。只需应用 BRepPrimAPI_MakePrism 类来计算实体(solid):
TopoDS_Shape myBody = BRepPrimAPI_MakePrism(myFaceProfile, aPrismVec);
瓶身的边缘非常锋利。要将它们替换为圆角面,你可以使用 Open CASCADE Technology 的 Fillet 功能。出于我们的目的,我们将指定圆角必须是:
myThickness / 12
若要在形状的边缘应用圆角,请使用 BRepFilletAPI_MakeFillet 类。该类通常按如下方式使用:
Add
方法添加圆角描述(边和半径)(您可以根据需要添加任意数量的边)。Shape
方法请求生成的圆角形状。BRepFilletAPI_MakeFillet mkFillet(myBody);
要添加圆角描述,您需要知道属于您的形状的边缘。最好的解决方案是探索实体以检索其边缘。TopExp_Explorer 类提供了此类功能,该类可浏览 TopoDS_Shape 中描述的数据结构,并提取您特别需要的子形状。通常,通过提供以下信息来创建此探索器:
TopExp_Explorer anEdgeExplorer(myBody, TopAbs_EDGE);
探索器(explorer)通常使用其三种主要方法在循环中应用:
More()
用来了解是否有更多子形状可供探索。Current()
知道哪个是当前探索的子形状(仅当 More()
方法返回 true
时才使用)。Next()
移动到下一个要探索的子形状。while(anEdgeExplorer.More()){
TopoDS_Edge anEdge = TopoDS::Edge(anEdgeExplorer.Current());
//Add edge to fillet algorithm
...
anEdgeExplorer.Next();
}
在浏览器(explorer)循环中,您已经找到了瓶子形状的所有边缘。然后,必须使用 Add()
方法将每个实例添加到BRepFilletAPI_MakeFillet 实例中。不要忘记指定圆角的半径。
mkFillet.Add(myThickness / 12., anEdge);
完成此操作后,您可以通过请求圆角形状来执行该过程的最后一步。
myBody = mkFillet.Shape();
要在瓶子上添加颈部,您将创建一个圆柱体并将其融合到瓶身上。圆柱体应放置在瓶体的顶面上,半径为 myThickness / 4
,高度为 myHeight / 10
。
要定位圆柱体,您需要定义一个坐标系,其中 gp_Ax2 类定义一个来自一个点和两个方向的右手坐标系 - 主(Z)轴方向和 X 方向(Y 方向由这两个方向计算)。要使颈部与顶面的中心对齐,使其处于全局坐标系 (0, 0, myHeight)
中,其法线位于全局 Z 轴上,可以按如下方式定义局部坐标系:
gp_Pnt neckLocation(0, 0, myHeight);
gp_Dir neckAxis = gp::DZ();
gp_Ax2 neckAx2(neckLocation, neckAxis);
若要创建圆柱体,请使用基元构造包中的另一个类:BRepPrimAPI_MakeCylinder 类。您必须提供的信息是:
Standard_Real myNeckRadius = myThickness / 4.;
Standard_Real myNeckHeight = myHeight / 10;
BRepPrimAPI_MakeCylinder MKCylinder(neckAx2, myNeckRadius, myNeckHeight);
TopoDS_Shape myNeck = MKCylinder.Shape();
你现在有两个独立的部分:一个瓶体和一个需要融合在一起的颈部。BRepAlgoAPI 包提供了在形状之间执行布尔运算的服务,特别是:common(布尔交集)、cut(布尔差集)和 fuse(布尔并集)。使用 BRepAlgoAPI_Fuse 融合两种形状:
myBody = BRepAlgoAPI_Fuse(myBody, myNeck);
由于真正的瓶子用于盛装液体材料,因此您现在应该从瓶子的顶面创建一个空心实体。在 Open CASCADE Technology 中,空心的实体称为 Thick Solid,其内部计算如下:
若要计算 Thick Solid,请通过提供以下信息来创建 BRepOffsetAPI_MakeThickSolid 类的实例:
在这个过程中,具有挑战性的部分是找到要从你的形状中去除的面(face) - 颈部的顶面,它:
要找到具有这些特征的面,您将再次使用一个浏览器(an explorer)遍历瓶子的所有面以找到合适的面(face)。
for(TopExp_Explorer aFaceExplorer(myBody, TopAbs_FACE) ; aFaceExplorer.More() ; aFaceExplorer.Next()){
TopoDS_Face aFace = TopoDS::Face(aFaceExplorer.Current());
}
对于每个检测到的面,您需要访问形状的几何属性:为此使用 BRep_Tool 类。这个类最常用的方法有:
Surface
用于访问面的表面(surface);Curve
用于访问边(edge)的 3D 曲线;Point
用于访问顶点(vertex)的 3D 点。Handle(Geom_Surface) aSurface = BRep_Tool::Surface(aFace);
如您所见,BRep_Tool::Surface
方法返回由 handle
操作的 Geom_Surface 类的实例。但是,Geom_Surface 类不提供有关对象 aSurface
的实际类型的信息,该对象可能是 Geom_Plane、Geom_CylindricalSurface 等的实例。所有由 handle
操作的对象(如 Geom_Surface)都继承自 Standard_Transient 类,该类提供了两种非常有用的有关类型的方法:
DynamicType
用于了解对象的实际类型IsKind
用于了解对象是否继承自一种特定类型DynamicType
返回对象的实际类型,但您需要将其与现有的已知类型进行比较,以确定 aSurface
是平面、圆柱曲面还是其他类型。若要将给定类型与所查找的类型进行比较,请使用 STANDARD_TYPE 宏,该宏返回类的类型:
if(aSurface->DynamicType() == STANDARD_TYPE(Geom_Plane)){
}
如果这个比较结果为 true
,则您知道 aSurface
实际类型为 Geom_Plane。然后,您可以使用继承自 Standard_Transient 的每个类提供的 DownCast()
方法将其从 Geom_Surface 转换为 Geom_Plane。顾名思义,此静态方法用于将对象向下转换为给定类型,语法如下:
Handle(Geom_Plane) aPlane = Handle(Geom_Plane)::DownCast(aSurface);
请记住,所有这些转换的目标是找到位于平面上的瓶子的最高面。假设您有以下两个全局变量:
TopoDS_Face faceToRemove;
Standard_Real zMax = -1;
您可以很容易地找到原点在 Z 中最大的平面,平面的位置是使用 Geom_Plane::Location
方法给出的。例如:
gp_Pnt aPnt = aPlane->Location();
Standard_Real aZ = aPnt.Z();
if(aZ > zMax){
zMax = aZ;
faceToRemove = aFace;
}
你现在已经找到了颈部的顶面。在创建空心实体之前的最后一步是将此面放入列表中。由于可以从初始实体中删除多个面,因此 BRepOffsetAPI_MakeThickSolid 构造函数将面(face)列表作为参数。Open CASCADE Technology 为不同类型的对象提供了许多集合:TColGeom 包提供了 Geom 包中对象的集合,TColgp 包提供了 gp 包中对象的集合等。 形状的集合可以在 TopTools 包中找到。由于 BRepOffsetAPI_MakeThickSolid 需要列表,因此请使用 TopTools_ListOfShape 类。
TopTools_ListOfShape facesToRemove;
facesToRemove.Append(faceToRemove);
现在,所有必需的数据都可用,因此可以通过调用 BRepOffsetAPI_MakeThickSolid::MakeThickSolidByJoin
方法来创建空心实体:
BRepOffsetAPI_MakeThickSolid aSolidMaker;
aSolidMaker.MakeThickSolidByJoin(myBody, facesToRemove, -myThickness / 50, 1.e-3);
myBody = aSolidMaker.Shape();
到目前为止,您已经学习了如何从 3D 曲线创建边。现在,您将学习如何从 2D 曲线和曲面创建边(edge)。要了解 Open CASCADE Technology 的这一方面,您将在圆柱曲面上根据二维曲线构建螺旋形轮廓。该理论比前面的步骤更复杂,但应用它非常简单。第一步,计算这些圆柱曲面。您已经熟悉 Geom 包的曲线。现在,您可以使用以下方法创建圆柱曲面(Geom_CylindricalSurface):
使用用于定位颈部的相同坐标系 neckAx2
,你可以创建两个具有以下半径的圆柱曲面 Geom_CylindricalSurface:
请注意,其中一个圆柱形曲面小于颈部。这是有充分理由的:创建螺纹后,您会将其与颈部融合。因此,我们必须确保这两个形状保持接触。
Handle(Geom_CylindricalSurface) aCyl1 = new Geom_CylindricalSurface(neckAx2, myNeckRadius * 0.99);
Handle(Geom_CylindricalSurface) aCyl2 = new Geom_CylindricalSurface(neckAx2, myNeckRadius * 1.05);
为了创建瓶子的颈部,您制作了一个基于圆柱形表面的实心圆柱体。您将通过在这样的表面上创建 2D 曲线来创建螺纹轮廓。Geom 包中定义的所有几何图形都已参数化。这意味着 Geom 中的每条曲线或曲面都是使用参数化方程计算的。Geom_CylindricalSurface 曲面由以下参数化方程定义:
P(U, V) = O + R * (cos(U) * xDir + sin(U) * yDir) + V * zDir
,其中:
P
是由参数 (U, V)
定义的点。O
、xDir
、yDir
和 zDir
分别是圆柱曲面局部坐标系的原点、X 方向、Y 方向和 Z 方向。R
是圆柱表面的半径。U
范围为 [0, 2PI]
,V
为无穷大。拥有这种参数化几何的优点是,您可以计算曲面的任何 (U, V)
参数:
这些参数方程还有另一个优点:可以将曲面视为使用 (U, V)
坐标系定义的二维参数空间。例如,考虑颈部表面的参数范围:
假设您在此参数化 (U, V)
空间上创建一条 2D 线并计算其 3D 参数曲线。根据线定义,结果如下:
参数值 | 参数方程 | 参数曲线 |
---|---|---|
U = 0 | P(V) = O + V * zDir | 平行于 Z 方向的线 |
V = 0 | P(U) = O + R * (cos(U) * xDir + sin(U) * yDir) | 平行于 (O, X, Y) 平面的圆 |
U != 0 V != 0 | P(U, V) = O + R * (cos(U) * xDir + sin(U) * yDir) + V * zDir | 描述圆柱体高度和角度演变的螺旋曲线 |
螺旋曲线类型正是您所需要的。在颈部表面,这条曲线的演化规律将是:
0
和 myHeighNeck
之间0
和 2PI
之间。但是,由于圆柱曲面是 U 周期曲面,因此您可以决定将此角度演变扩展到 4PI
,如下图所示:在这个 (U, V)
参数空间中,您将创建一个局部 (X, Y)
坐标系来定位要创建的曲线。此坐标系将定义如下:
(2*PI, myNeckHeight / 2)
。(2*PI, myNeckHeight / 4)
向量定义的 X 方向,使曲线占据颈部曲面的一半。要使用 Open CASCADE Technology 的 2D 基元几何类型来定义点和坐标系,您将再次实例化 gp 中的类:
gp_Pnt2d aPnt(2. * M_PI, myNeckHeight / 2.);
gp_Dir2d aDir(2. * M_PI, myNeckHeight / 4.);
gp_Ax2d anAx2d(aPnt, aDir);
现在,您将定义曲线。如前所述,这些螺纹轮廓是在两个圆柱面上计算的。在下图中,左边的曲线定义了基底(在 aCyl1
表面上),右边的曲线定义了螺纹形状的顶部(在 aCyl2
表面上)。
您已经使用 Geom 包来定义 3D 几何图元。对于 2D,您将使用 Geom2d 包。对于 Geom,所有几何图形都已参数化。例如,Geom2d_Ellipse 椭圆的定义如下:
假设:
2*PI
myNeckHeight / 10
你的椭圆定义如下:
Standard_Real aMajor = 2. * M_PI;
Standard_Real aMinor = myNeckHeight / 10;
Handle(Geom2d_Ellipse) anEllipse1 = new Geom2d_Ellipse(anAx2d, aMajor, aMinor);
Handle(Geom2d_Ellipse) anEllipse2 = new Geom2d_Ellipse(anAx2d, aMajor, aMinor / 4);
要描述上面绘制的圆弧的曲线部分,请从创建的椭圆中定义 Geom2d_TrimmedCurve 修剪的曲线和两个参数来限制它们。由于椭圆的参数方程为 P(U) = O + (MajorRadius * cos(U) * XDirection) + (MinorRadius * sin(U) * YDirection)
,因此椭圆需要限制在 0
和 M_PI
之间。
Handle(Geom2d_TrimmedCurve) anArc1 = new Geom2d_TrimmedCurve(anEllipse1, 0, M_PI);
Handle(Geom2d_TrimmedCurve) anArc2 = new Geom2d_TrimmedCurve(anEllipse2, 0, M_PI);
最后一步是定义线段,对于两个轮廓来说,线段是相同的:一条线受其中一个圆弧的第一个点和最后一个点的限制。要访问与曲线或曲面的参数相对应的点,请使用 Value
或 D0
方法(即 0 阶导数),D1
方法用于一阶导数,D2
用于二阶导数。
gp_Pnt2d anEllipsePnt1 = anEllipse1->Value(0);
gp_Pnt2d anEllipsePnt2;
anEllipse1->D0(M_PI, anEllipsePnt2);
在创建瓶子的轮廓时,您使用了 GC 包中的类,提供了创建基本几何图形的算法。在二维几何中,这种算法可以在 GCE2d 软件包中找到。类名和行为与 GC 中的类似。例如,要从两点创建 2D 线段:
Handle(Geom2d_TrimmedCurve) aSegment = GCE2d_MakeSegment(anEllipsePnt1, anEllipsePnt2);
正如您在创建瓶子的基本轮廓时所做的那样,您现在可以:
之前,您已经构建了:
要计算这些曲线的边,请再次使用 BRepBuilderAPI_MakeEdge 类。它的构造函数之一允许您从曲面的 2D 参数空间中描述的曲线中构建边。
TopoDS_Edge anEdge1OnSurf1 = BRepBuilderAPI_MakeEdge(anArc1, aCyl1);
TopoDS_Edge anEdge2OnSurf1 = BRepBuilderAPI_MakeEdge(aSegment, aCyl1);
TopoDS_Edge anEdge1OnSurf2 = BRepBuilderAPI_MakeEdge(anArc2, aCyl2);
TopoDS_Edge anEdge2OnSurf2 = BRepBuilderAPI_MakeEdge(aSegment, aCyl2);
现在,您可以创建位于每个曲面上的螺纹的两个轮廓。
TopoDS_Wire threadingWire1 = BRepBuilderAPI_MakeWire(anEdge1OnSurf1, anEdge2OnSurf1);
TopoDS_Wire threadingWire2 = BRepBuilderAPI_MakeWire(anEdge1OnSurf2, anEdge2OnSurf2);
请记住,这些线是由曲面和 2D 曲线构建的。就这些线而言,缺少一个重要的数据项:没有关于 3D 曲线的信息。幸运的是,您不需要自己计算,这可能是一项艰巨的任务,因为数学可能非常复杂。当一个形状包含除 3D 曲线之外的所有必要信息时,Open CASCADE Technology 提供了一种自动构建它们的工具。在 BRepLib 工具包中,你可以使用 BuildCurves3d 方法计算一个形状所有边的 3D 曲线。
BRepLib::BuildCurves3d(threadingWire1);
BRepLib::BuildCurves3d(threadingWire2);
您已经计算了螺纹的线。螺纹将是一个实体形状,因此您现在必须计算线的面、允许您连接线的面、这些面的壳,然后是实体本身。这可能是一个冗长的操作。定义基本拓扑时,总是有更快的方法来构建实体。您想用两条线创建一个实体。Open CASCADE Technology 提供了一种通过建造阁楼(loft)的快速方法来做到这一点:一个壳(shell)或一个实体(solid)以给定的顺序穿过一组线。loft 函数在 BRepOffsetAPI_ThruSections 类中实现,您可以按如下方式使用该类:
AddWire
方法添加连续的线。CheckCompatibility
方法激活(或停用)用于检查线是否具有相同数量的边的选项。在这种情况下,每条线都有两条边,因此您可以停用此选项。Shape
方法请求生成的放样(loft)形状。BRepOffsetAPI_ThruSections aTool(Standard_True);
aTool.AddWire(threadingWire1);
aTool.AddWire(threadingWire2);
aTool.CheckCompatibility(Standard_False);
TopoDS_Shape myThreading = aTool.Shape();
你几乎完成了瓶子的制作。使用 TopoDS_Compound 和 BRep_Builder 类从 myBody
和 myThreading
生成单个形状:
TopoDS_Compound aRes;
BRep_Builder aBuilder;
aBuilder.MakeCompound (aRes);
aBuilder.Add (aRes, myBody);
aBuilder.Add (aRes, myThreading);
恭喜!你的瓶子是完整的。以下是 Tutorial 应用程序的结果快照:
我们希望本教程能让您感受到 Open CASCADE Technology 的工业实力。如果您想了解更多信息并使用 Open CASCADE Technology 开发重大项目,我们邀请您在我们的网站 https://www.opencascade.com/content/technology-support 上学习我们的培训、支持和咨询服务。我们的专业服务可以最大限度地发挥您的 Open CASCADE Technology 应用程序的功能。
MakeBottle 函数的完整定义(在教程的 samples/qt/tutorial/src/MakeBottle.cxx
文件中定义):
TopoDS_Shape MakeBottle(const Standard_Real myWidth, const Standard_Real myHeight,
const Standard_Real myThickness)
{
// Profile : Define Support Points
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);
// Profile : Define the Geometry
Handle(Geom_TrimmedCurve) anArcOfCircle = GC_MakeArcOfCircle(aPnt2,aPnt3,aPnt4);
Handle(Geom_TrimmedCurve) aSegment1 = GC_MakeSegment(aPnt1, aPnt2);
Handle(Geom_TrimmedCurve) aSegment2 = GC_MakeSegment(aPnt4, aPnt5);
// Profile : Define the Topology
TopoDS_Edge anEdge1 = BRepBuilderAPI_MakeEdge(aSegment1);
TopoDS_Edge anEdge2 = BRepBuilderAPI_MakeEdge(anArcOfCircle);
TopoDS_Edge anEdge3 = BRepBuilderAPI_MakeEdge(aSegment2);
TopoDS_Wire aWire = BRepBuilderAPI_MakeWire(anEdge1, anEdge2, anEdge3);
// Complete Profile
gp_Ax1 xAxis = gp::OX();
gp_Trsf aTrsf;
aTrsf.SetMirror(xAxis);
BRepBuilderAPI_Transform aBRepTrsf(aWire, aTrsf);
TopoDS_Shape aMirroredShape = aBRepTrsf.Shape();
TopoDS_Wire aMirroredWire = TopoDS::Wire(aMirroredShape);
BRepBuilderAPI_MakeWire mkWire;
mkWire.Add(aWire);
mkWire.Add(aMirroredWire);
TopoDS_Wire myWireProfile = mkWire.Wire();
// Body : Prism the Profile
TopoDS_Face myFaceProfile = BRepBuilderAPI_MakeFace(myWireProfile);
gp_Vec aPrismVec(0, 0, myHeight);
TopoDS_Shape myBody = BRepPrimAPI_MakePrism(myFaceProfile, aPrismVec);
// Body : Apply Fillets
BRepFilletAPI_MakeFillet mkFillet(myBody);
TopExp_Explorer anEdgeExplorer(myBody, TopAbs_EDGE);
while(anEdgeExplorer.More()){
TopoDS_Edge anEdge = TopoDS::Edge(anEdgeExplorer.Current());
//Add edge to fillet algorithm
mkFillet.Add(myThickness / 12., anEdge);
anEdgeExplorer.Next();
}
myBody = mkFillet.Shape();
// Body : Add the Neck
gp_Pnt neckLocation(0, 0, myHeight);
gp_Dir neckAxis = gp::DZ();
gp_Ax2 neckAx2(neckLocation, neckAxis);
Standard_Real myNeckRadius = myThickness / 4.;
Standard_Real myNeckHeight = myHeight / 10.;
BRepPrimAPI_MakeCylinder MKCylinder(neckAx2, myNeckRadius, myNeckHeight);
TopoDS_Shape myNeck = MKCylinder.Shape();
myBody = BRepAlgoAPI_Fuse(myBody, myNeck);
// Body : Create a Hollowed Solid
TopoDS_Face faceToRemove;
Standard_Real zMax = -1;
for(TopExp_Explorer aFaceExplorer(myBody, TopAbs_FACE); aFaceExplorer.More(); aFaceExplorer.Next()){
TopoDS_Face aFace = TopoDS::Face(aFaceExplorer.Current());
// Check if <aFace> is the top face of the bottle's neck
Handle(Geom_Surface) aSurface = BRep_Tool::Surface(aFace);
if(aSurface->DynamicType() == STANDARD_TYPE(Geom_Plane)){
Handle(Geom_Plane) aPlane = Handle(Geom_Plane)::DownCast(aSurface);
gp_Pnt aPnt = aPlane->Location();
Standard_Real aZ = aPnt.Z();
if(aZ > zMax){
zMax = aZ;
faceToRemove = aFace;
}
}
}
TopTools_ListOfShape facesToRemove;
facesToRemove.Append(faceToRemove);
BRepOffsetAPI_MakeThickSolid aSolidMaker;
aSolidMaker.MakeThickSolidByJoin(myBody, facesToRemove, -myThickness / 50, 1.e-3);
myBody = aSolidMaker.Shape();
// Threading : Create Surfaces
Handle(Geom_CylindricalSurface) aCyl1 = new Geom_CylindricalSurface(neckAx2, myNeckRadius * 0.99);
Handle(Geom_CylindricalSurface) aCyl2 = new Geom_CylindricalSurface(neckAx2, myNeckRadius * 1.05);
// Threading : Define 2D Curves
gp_Pnt2d aPnt(2. * M_PI, myNeckHeight / 2.);
gp_Dir2d aDir(2. * M_PI, myNeckHeight / 4.);
gp_Ax2d anAx2d(aPnt, aDir);
Standard_Real aMajor = 2. * M_PI;
Standard_Real aMinor = myNeckHeight / 10;
Handle(Geom2d_Ellipse) anEllipse1 = new Geom2d_Ellipse(anAx2d, aMajor, aMinor);
Handle(Geom2d_Ellipse) anEllipse2 = new Geom2d_Ellipse(anAx2d, aMajor, aMinor / 4);
Handle(Geom2d_TrimmedCurve) anArc1 = new Geom2d_TrimmedCurve(anEllipse1, 0, M_PI);
Handle(Geom2d_TrimmedCurve) anArc2 = new Geom2d_TrimmedCurve(anEllipse2, 0, M_PI);
gp_Pnt2d anEllipsePnt1 = anEllipse1->Value(0);
gp_Pnt2d anEllipsePnt2 = anEllipse1->Value(M_PI);
Handle(Geom2d_TrimmedCurve) aSegment = GCE2d_MakeSegment(anEllipsePnt1, anEllipsePnt2);
// Threading : Build Edges and Wires
TopoDS_Edge anEdge1OnSurf1 = BRepBuilderAPI_MakeEdge(anArc1, aCyl1);
TopoDS_Edge anEdge2OnSurf1 = BRepBuilderAPI_MakeEdge(aSegment, aCyl1);
TopoDS_Edge anEdge1OnSurf2 = BRepBuilderAPI_MakeEdge(anArc2, aCyl2);
TopoDS_Edge anEdge2OnSurf2 = BRepBuilderAPI_MakeEdge(aSegment, aCyl2);
TopoDS_Wire threadingWire1 = BRepBuilderAPI_MakeWire(anEdge1OnSurf1, anEdge2OnSurf1);
TopoDS_Wire threadingWire2 = BRepBuilderAPI_MakeWire(anEdge1OnSurf2, anEdge2OnSurf2);
BRepLib::BuildCurves3d(threadingWire1);
BRepLib::BuildCurves3d(threadingWire2);
// Create Threading
BRepOffsetAPI_ThruSections aTool(Standard_True);
aTool.AddWire(threadingWire1);
aTool.AddWire(threadingWire2);
aTool.CheckCompatibility(Standard_False);
TopoDS_Shape myThreading = aTool.Shape();
// Building the Resulting Compound
TopoDS_Compound aRes;
BRep_Builder aBuilder;
aBuilder.MakeCompound (aRes);
aBuilder.Add (aRes, myBody);
aBuilder.Add (aRes, myThreading);
return aRes;
}