OpenCasCade官方开发文档翻译(14)–occt建模:瓶子教程

概述

本教程将教您如何使用 Open CASCADE Technology 服务对 3D 对象进行建模。本教程的目的不是描述所有 Open CASCADE Technology 课程,而是帮助您开始将 Open CASCADE Technology 作为一种工具进行思考。

先决条件

本教程假设您有使用和设置 C++ 的经验。从编程的角度来看,Open CASCADE 技术旨在通过 3D 建模类、方法和函数来增强您的 C++ 工具。所有这些资源的组合将允许您创建大量的应用程序。

该模型

为了说明 3D 几何建模工具包中提供的类的使用,您将创建一个瓶子,如下所示:

图片[1]-OpenCasCade官方开发文档翻译(14)–occt建模:瓶子教程-卡核

在本教程中,我们将逐步创建一个对瓶子进行建模的函数,如上所示。您将找到本教程的完整源代码,包括Open CASCADE Technology 分发包中的MakeBottle函数。函数体在文件 samples/qt/Tutorial/src/MakeBottle.cxx 中提供。

型号规格

我们首先定义瓶子规格如下:

对象参数 参数名称 参数值
瓶子高度 我的身高 70mm
瓶宽 我的宽度 50mm
瓶子厚度 我的厚度 30mm

此外,我们决定瓶子的轮廓(底部)将以全局笛卡尔坐标系的原点为中心。

图片[2]-OpenCasCade官方开发文档翻译(14)–occt建模:瓶子教程-卡核

此建模需要四个步骤:

  • 建立瓶子的个人资料
  • 建立瓶子的身体
  • 在瓶子的脖子上建立螺纹
  • 构建结果化合物

构建配置文件

定义支撑点

要创建瓶子的轮廓,您首先需要在 (XOY) 平面中创建特征点及其坐标,如下所示。这些点将是定义轮廓几何形状的支撑。

图片[3]-OpenCasCade官方开发文档翻译(14)–occt建模:瓶子教程-卡核

在 Open CASCADE 技术中,有两个类可以从其 X、Y 和 Z 坐标描述 3D 笛卡尔点:

句柄是一种提供自动内存管理的智能指针。要为此应用选择最佳等级,请考虑以下因素:

  • gp_Pnt由值操纵。像所有同类物体一样,它的寿命有限。
  • 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);

一旦你的对象被实例化,你就可以使用类提供的方法来访问和修改它的数据。例如,要获取一个点的 X 坐标:

Standard_Real xValue1 = aPnt1.X();

配置文件:定义几何

借助先前定义的点,您可以计算出瓶子轮廓几何的一部分。如下图所示,它将由两条线段和一条弧线组成。

图片[4]-OpenCasCade官方开发文档翻译(14)–occt建模:瓶子教程-卡核

要创建这样的实体,您需要一个实现 3D 几何对象的特定数据结构。这可以在 Open CASCADE Technology 的 Geom 包中找到。在 Open CASCADE 技术中,包是一组提供相关功能的类。这些类的名称以它们所属的包的名称开头。例如,Geom_LineGeom_Circle类属于Geom包。Geom包实现 3D 几何对象:提供基本曲线和曲面以及更复杂的(例如BezierBSpline)。然而,几何包只提供几何实体的数据结构。您可以直接实例化属于Geom的类,但使用GC包更容易计算基本曲线和曲面。这是因为GC提供了两个算法类,这正是我们的配置文件所需要的:

  • GC_MakeSegment创建一个段。它的一个构造函数允许您通过两个端点 P1 和 P2 定义一个段
  • GC_MakeArcOfCircle类创建圆弧。一个有用的构造函数从两个端点 P1 和 P3 并通过 P2 创建一个弧。

这两个类都返回一个由句柄操作的Geom_TrimmedCurve。这个实体代表一个基本曲线(在我们的例子中是直线或圆),限制在它的两个参数值之间。例如,圆 C 参数化在 0 和 2PI 之间。如果您需要创建四分之一圆,则在 C 上创建一个Geom_TrimmedCurve,限制在 0 和 M_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类都提供了一种强制转换方法,可以通过类似函数的调用自动获取结果。请注意,如果构造失败,此方法将引发异常。要更明确地Handle可能的错误,您可以使用IsDoneValue方法。例如:

GC_MakeSegment mkSeg (aPnt1, aPnt2);
if (mkSegment.IsDone()){
aSegment1 = mkSeg.Value();
}
else{
// Handle错误
}

简介:定义拓扑

您已经创建了轮廓一部分的支撑几何体,但这些曲线是独立的,彼此之间没有任何关系。为了简化建模,将这三个曲线作为一个实体进行操作是正确的。这可以通过使用TopoDS包中定义的 Open CASCADE Technology 的拓扑数据结构来完成:它定义了可以链接在一起以表示复杂形状的几何实体之间的关系。从TopoDS_Shape类继承的TopoDS包的每个对象都描述了一个拓扑形状,如下所述:

形状 开设CASCADE技术课 描述
顶点 TopoDS_Vertex 与几何中的点相对应的零维形状。
TopoDS_Edge 一维形状对应于一条曲线,并以每个端点的顶点为界。
线 TopoDS_Wire 由顶点连接的边序列。
TopoDS_Face 由闭合线包围的表面的一部分。
TopoDS_Shell 由边连接的一组面。
实体 TopoDS_Solid 以壳为界的 3D 空间的一部分。
复合固体 TopoDS_CompSolid 一组通过面连接的实体。
化合物 TopoDS_Compound 上述任何其他形状的集合。

参考上表,要构建配置文件,您将创建:

  • 先前计算的曲线中的三个边。
  • 一根带有这些边的线。
图片[5]-OpenCasCade官方开发文档翻译(14)–occt建模:瓶子教程-卡核

但是,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 中,您可以通过多种方式创建边。一种可能性是直接从两个点创建一条边,在这种情况下,这条边的底层几何图形是一条线,由从两个输入点自动计算的两个顶点界定。例如,anEdge1 和 anEdge3 可以用更简单的方式计算:

TopoDS_Edge anEdge1 = BRepBuilderAPI_MakeEdge (aPnt1, aPnt3);
TopoDS_Edge anEdge2 = BRepBuilderAPI_MakeEdge (aPnt4, aPnt5);

要连接边,您需要使用BRepBuilderAPI_MakeWire类创建连线。有两种方法可以用这个类构建一条线:

  • 直接从一到四个边
  • 通过将其他线或边添加到现有线(本教程后面将对此进行说明)

当从少于四个边构建导线时,如本例所示,您可以直接使用构造函数,如下所示:

TopoDS_Wire aWire = BRepBuilderAPI_MakeWire (anEdge1, anEdge2, anEdge3);

个人资料:完成个人资料

创建导线的第一部分后,您需要计算完整的配置文件。一个简单的方法是:

  • 通过反映现有的线来计算新的线。
  • 将反射线添加到初始线。
图片[6]-OpenCasCade官方开发文档翻译(14)–occt建模:瓶子教程-卡核

要对形状(包括线)应用变换,首先需要使用gp_Trsf类定义 3D 几何变换的属性。这种变换可以是平移、旋转、缩放、反射或这些的组合。在我们的例子中,我们需要定义一个相对于全局坐标系 X 轴的反射。使用gp_Ax1类定义的轴由一个点构建并具有一个方向(3D 酉向量)。有两种方法来定义这个轴。第一种方法是从头开始定义它,使用它的几何定义:

  • X 轴位于 (0, 0, 0) – 使用gp_Pnt类。
  • X 轴方向为 (1, 0, 0) – 使用gp_Dir类。gp_Dir实例是根据其 X、Y 和 Z 坐标创建的
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不会修改形状的性质:反射线的结果仍然是线。但是类似函数的调用或BRepBuilderAPI_Transform::Shape方法返回一个TopoDS_Shape对象:

TopoDS_Shape aMirroredShape = aBRepTrsf.Shape();

您需要一种将产生的反射形状视为线的方法。TopoDS全局函数通过将形状转换为真实类型提供这种服务。要投射转换后的线,请使用TopoDS::Wire方法。

TopoDS_Wire aMirroredWire = TopoDS::Wire (aMirroredShape);

瓶子的轮廓几乎完成了。您已经创建了两条线:aWireaMirroredWire。您需要连接它们以计算单个形状。为此,您可以使用BRepBuilderAPI_MakeWire类,如下所示:

mkWire.Add(aWire);
mkWire.Add(aMirroredWire);
TopoDS_Wire myWireProfile = mkWire.Wire();

建立身体

拉伸轮廓

要计算瓶子的主体,您需要创建一个实体形状。最简单的方法是使用之前创建的轮廓并沿一个方向扫过它。Open CASCADE Technology的Prism功能最适合该任务。它接受一个形状和一个方向作为输入,并根据以下规则生成一个新形状:

形状 生成
线
实体
固体化合物
图片[7]-OpenCasCade官方开发文档翻译(14)–occt建模:瓶子教程-卡核

您当前的个人资料是线。参考 Shape/Generates 表,您需要根据其线计算面以生成实体。要创建面,请使用BRepBuilderAPI_MakeFace类。如前所述,面是由闭合线包围的表面的一部分。通常,BRepBuilderAPI_MakeFace从表面和一根或多根线计算出一个面。当导线位于平面上时,将自动计算曲面。

TopoDS_Face myFaceProfile = BRepBuilderAPI_MakeFace (myWireProfile);

BRepPrimAPI包提供了创建拓扑基元构造的所有类:长方体、圆锥体、圆柱体、球体等。其中包括BRepPrimAPI_MakePrism类。如上所述,拉伸定义为:

  • 要扫描的基础形状;
  • 有限拉伸的矢量或有限和无限拉伸的方向。

您希望实体是有限的,沿 Z 轴扫过并且高度为 myHeight。使用gp_Vec类在其 X、Y 和 Z 坐标上定义的向量是:

gp_Vec aPrismVec(0, 0, myHeight);

现在可以使用创建瓶子主体所需的所有数据。只需应用BRepPrimAPI_MakePrism类来计算实体:

TopoDS_Shape myBody = BRepPrimAPI_MakePrism (myFaceProfile, aPrismVec);

应用圆角

瓶身边非常锋利。要使用圆角面替换它们,您可以使用 Open CASCADE 技术的圆角功能。出于我们的目的,我们将指定圆角必须是:

  • 应用于形状的所有边
  • 半径为myThickness / 12
图片[8]-OpenCasCade官方开发文档翻译(14)–occt建模:瓶子教程-卡核

要在形状的边应用圆角,请使用BRepFilletAPI_MakeFillet类。该类通常使用如下:

  • 在BRepFilletAPI_MakeFillet构造函数中指定要圆角的形状。
  • 使用Add方法添加圆角描述(边和半径)(您可以根据需要添加任意数量的边)。
  • 使用Shape方法请求生成的圆角形状。
BRepFilletAPI_MakeFillet mkFillet(myBody);

要添加圆角描述,您需要知道属于您的形状的边。最好的解决方案是探索您的实体以检索其边。这种功能由TopExp_Explorer类提供,该类探索TopoDS_Shape中描述的数据结构并提取您特别需要的子形状。通常,通过提供以下信息来创建此资源管理器:

  • 探索的形状
  • 要找到的子形状的类型。此信息由TopAbs_ShapeEnum枚举提供。
TopExp_Explorer anEdgeExplorer(myBody, TopAbs_EDGE);

资源管理器通常通过使用其三种主要方法在循环中应用:

  • More()以了解是否有更多子形状需要探索。
  • Current()知道哪个是当前探索的子形状(仅在More()方法返回 true 时使用)。
  • Next()移动到下一个要探索的子形状。
while (anEdgeExplorer.More()){
TopoDS_Edge anEdge = TopoDS::Edge (anEdgeExplorer.Current());
//添加边到圆角算法
anEdgeExplorer.Next();
}

在资源管理器循环中,您已经找到了瓶子形状的所有边。然后必须使用Add()方法将每一个添加到BRepFilletAPI_MakeFillet实例中。不要忘记指定圆角的半径。

mkFillet.Add(myThickness / 12., anEdge);

完成此操作后,您可以通过询问圆角形状来执行该过程的最后一步。

myBody = mkFillet.Shape();

添加颈部

要为瓶子添加颈部,您将创建一个圆柱体并将其融合到身体上。圆柱体将定位在主体的顶面上,半径为myThickness / 4。高度为myHeight / 10。

图片[9]-OpenCasCade官方开发文档翻译(14)–occt建模:瓶子教程-卡核

要定位圆柱体,您需要使用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 技术中,空心实体称为 实体,其内部计算如下:

  • 从初始实体中移除一个或多个面,以获得空心实体的第一壁 W1。
  • 在距离 D 处从 W1 创建平行墙 W2。如果 D 为正,则 W2 将位于初始实体的外部,否则它将位于内部。
  • 从两个墙 W1 和 W2 计算一个实体。
图片[10]-OpenCasCade官方开发文档翻译(14)–occt建模:瓶子教程-卡核

要计算厚实体,您可以通过提供以下信息来创建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());
}

对于每个检测到的面,您需要访问形状的几何属性:为此使用BRep_Tool类。这个类最常用的方法有:

  • Surface访问面的表面;
  • 曲线访问边的 3D 曲线;
  • Point访问顶点的 3D 点。
Handle( Geom_Surface ) aSurface = BRep_Tool::Surface (aFace);

如您所见,BRep_Tool::Surface方法返回一个由句柄操作的Geom_Surface类的实例。但是,Geom_Surface类不提供有关对象aSurface的真实类型的信息,它可能是Geom_PlaneGeom_CylindricalSurface等的实例。所有由句柄操作的对象,如Geom_Surface,都继承自Standard_Transient类,该类提供了两个非常有用的方法关于类型:

  • DynamicType知道对象的真实类型
  • IsKind知道对象是否继承自一种特定类型

DynamicType 返回对象的真实类型,但您需要将其与现有的已知类型进行比较,以确定aSurface是平面、圆柱面还是其他类型。要将给定类型与您寻找的类型进行比较,请使用STANDARD_TYPE宏,该宏返回类的类型:

if (aSurface->DynamicType() == STANDARD_TYPE ( Geom_Plane )){
}

如果此比较为真,则您知道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;

知道平面的位置是用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 Technology 为不同类型的对象提供了许多集合:参见TColGeom包以获取Geom包中的对象集合,TColgp包获取 gp 包中的对象集合等。形状集合可以在TopTools包中找到。由于BRepOffsetAPI_MakeThickSolid需要一个列表,因此请使用TopTools_ListOfShape类。

TopTools_ListOfShape facesToRemove;
facesToRemove.Append(faceToRemove);

现在所有必要的数据都可用了,因此您可以通过调用BRepOffsetAPI_MakeThickSolid MakeThickSolidByJoin 方法来创建空心实体:

aSolidMaker.MakeThickSolidByJoin(myBody, facesToRemove, -myThickness / 50, 1.e-3);
myBody = aSolidMaker.Shape();

构建线程

创建曲面

到目前为止,您已经学习了如何从 3D 曲线中创建边。您现在将学习如何从 2D 曲线和曲面创建边。要了解 Open CASCADE 技术的这一方面,您将使用圆柱表面上的 2D 曲线构建螺旋轮廓。该理论比前面的步骤更复杂,但应用它非常简单。作为第一步,您计算这些圆柱表面。您已经熟悉Geom包的曲线。现在,您可以使用以下方法创建圆柱表面 ( Geom_CylindricalSurface ):

  • 坐标系;
  • 一个半径。

使用用于定位颈部的相同坐标系eckAx2 ,您可以创建两个具有以下半径的圆柱曲面Geom_CylindricalSurface :

图片[11]-OpenCasCade官方开发文档翻译(14)–occt建模:瓶子教程-卡核

请注意,其中一个圆柱面小于颈部。这样做有一个很好的理由:创建线程后,您会将其与颈部融合。所以,我们必须确保这两个形状保持接触。

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、*Dir、yDir、zDir分别为圆柱面局部坐标系的原点、X方向、Y方向、Z方向。
  • R 是圆柱表面的半径。
  • U 范围是 [0, 2PI],V 是无限的。
图片[12]-OpenCasCade官方开发文档翻译(14)–occt建模:瓶子教程-卡核

具有这种参数化几何形状的优点是您可以计算表面的任何 (U, V) 参数:

  • 3D点;
  • 此时的 1、2 到 N 阶导数向量。

这些参数方程还有另一个优点:您可以将曲面视为使用 (U, V) 坐标系定义的 2D 参数空间。例如,考虑颈部表面的参数范围:

图片[13]-OpenCasCade官方开发文档翻译(14)–occt建模:瓶子教程-卡核

假设您在此参数(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 描述圆柱体高度和角度演变的螺旋曲线

螺旋曲线类型正是您所需要的。在颈部表面,这条曲线的演化规律将是:

  • 在 V 参数中:0 和 myHeighNeck 之间的高度描述
  • 在 U 参数中:角度描述在 0 到 2PI 之间。但是,由于圆柱表面是 U 周期的,您可以决定将此角度演化扩展到 4PI,如下图所示:
图片[14]-OpenCasCade官方开发文档翻译(14)–occt建模:瓶子教程-卡核

在这个 (U, V) 参数空间中,您将创建一个局部 (X, Y) 坐标系来定位要创建的曲线。该坐标系将定义为:

  • 位于颈部圆柱体参数空间中间的中心,位于 U、V 坐标中 (2*PI, myNeckHeight / 2)。
  • AX 方向由 U、V 坐标中的 (2*PI, myNeckHeight/4) 向量定义,以便曲线占据颈部表面的一半。
图片[15]-OpenCasCade官方开发文档翻译(14)–occt建模:瓶子教程-卡核

要使用 Open CASCADE 技术的 2D 原始几何类型来定义点和坐标系,您将再次从 gp 实例化类:

  • 要从其 X 和 Y 坐标定义 2D 点,请使用gp_Pnt2d类。
  • 要从其 X 和 Y 坐标定义 2D 方向(单位向量),请使用gp_Dir2d类。坐标将自动归一化。
  • 要定义 2D 右手坐标系,请使用gp_Ax2d类,该类是根据一个点(坐标系的原点)和一个方向(坐标系的 X 方向)计算得出的。将自动计算 Y 方向。
gp_Pnt2d aPnt(2. * M_PI, myNeckHeight / 2.);
gp_Dir2d aDir(2. * M_PI, myNeckHeight / 4.);
gp_Ax2d anAx2d(aPnt, aDir);

您现在将定义曲线。如前所述,这些螺纹轮廓是在两个圆柱表面上计算的。在下图中,左侧的曲线定义了底面(在aCyl1表面上),右侧的曲线定义了螺纹形状的顶部(在aCyl2表面上)。

图片[16]-OpenCasCade官方开发文档翻译(14)–occt建模:瓶子教程-卡核

您已经使用Geom包定义了 3D 几何实体。对于 2D,您将使用Geom2d包。至于Geom,所有几何图形都是参数化的。例如,一个Geom2d_Ellipse椭圆定义自:

  • 以椭圆中心为原点的坐标系;
  • 由坐标系的 X 方向定义的主轴上的主半径;
  • 由坐标系的 Y 方向定义的短轴上的小半径。

假设:

  • 两个椭圆的主半径相同,均为 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);

建筑边和线

正如您在创建瓶子的基本配置文件时所做的那样,您现在可以:

  • 计算颈部螺纹的边。
  • 从这些边计算两条线。
图片[17]-OpenCasCade官方开发文档翻译(14)–occt建模:瓶子教程-卡核

以前,您已经构建了:

  • 螺纹的两个圆柱面
  • 定义螺纹的基本几何形状的三个 2D 曲线

要计算这些曲线的边,请再次使用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 技术提供了一种通过构建阁楼的快速方法来做到这一点:一个壳或一个实体以给定的顺序穿过一组线。
loft 函数在BRepOffsetAPI_ThruSections类中实现,您可以按如下方式使用该类:

图片[18]-OpenCasCade官方开发文档翻译(14)–occt建模:瓶子教程-卡核
  • 通过创建类的实例来初始化算法。如果要创建实体,则必须指定此构造函数的第一个参数。默认情况下,BRepOffsetAPI_ThruSections构建一个外壳。
  • 使用 AddWire 方法添加连续的连线。
  • 使用CheckCompatibility方法激活(或停用)检查线是否具有相同数量的边的选项。在这种情况下,每条线都有两条边,因此您可以禁用此选项。
  • 使用 Shape 方法请求生成的放样形状。
BRepOffsetAPI_ThruSections aTool(Standard_True);
aTool.AddWire(threadingWire1); aTool.AddWire(threadingWire2);
aTool.CheckCompatibility(Standard_False);
TopoDS_Shape myThreading = aTool.Shape();

构建生成的化合物

你几乎完成了瓶子的制作。使用TopoDS_CompoundBRep_Builder类从myBodymyThreading构建单个形状:

BRep_Builder aBuilder;
aBuilder.MakeCompound (aRes);
aBuilder.Add (aRes, myBody);
aBuilder.Add (aRes, myThreading);

恭喜!你的瓶子是完整的。这是教程应用程序的结果快照:

图片[19]-OpenCasCade官方开发文档翻译(14)–occt建模:瓶子教程-卡核

我们希望本教程能让您感受到 Open CASCADE 技术的工业实力。如果您想了解更多信息并使用 Open CASCADE 技术开发重大项目,我们邀请您在我们的网站https://www.opencascade.com/content/technology-support上学习我们的培训、支持和咨询服务。我们的专业服务可以最大限度地发挥您的 Open CASCADE 技术应用程序的功能。

附录

MakeBottle 函数的完整定义(在教程的 src/MakeBottle.cxx 文件中定义):

TopoDS_Shape MakeBottle( const Standard_Real myWidth , const Standard_Real myHeight,
const Standard_Real myThickness)
{
// Profile : 定义支撑点
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 : 定义几何
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 : 定义拓扑
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);
// 完整的配置文件
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);
mkWire.Add(aWire);
mkWire.Add(aMirroredWire);
TopoDS_Wire myWireProfile = mkWire.Wire();
//正文:拉伸轮廓
TopoDS_Face myFaceProfile = BRepBuilderAPI_MakeFace (myWireProfile);
gp_Vec aPrismVec(0, 0, myHeight);
TopoDS_Shape myBody = BRepPrimAPI_MakePrism (myFaceProfile, aPrismVec);
// 正文:应用圆角
BRepFilletAPI_MakeFillet mkFillet(myBody);
TopExp_Explorer anEdgeExplorer(myBody, TopAbs_EDGE);
while (anEdgeExplorer.More()){
TopoDS_Edge anEdge = TopoDS::Edge (anEdgeExplorer.Current());
//添加边到圆角算法
mkFillet.Add(myThickness / 12., anEdge);
anEdgeExplorer.Next();
}
myBody = mkFillet.Shape();
// Body : 添加颈部
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 : 创建一个空心实体
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());
// 检查 <aFace> 是否是瓶颈的顶面
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);
aSolidMaker.MakeThickSolidByJoin(myBody, facesToRemove, -myThickness / 50, 1.e-3);
myBody = aSolidMaker.Shape();
// 线程:创建表面
Handle( Geom_CylindricalSurface ) aCyl1 = new Geom_CylindricalSurface (neckAx2, myNeckRadius * 0.99);
Handle( Geom_CylindricalSurface ) aCyl2 = new Geom_CylindricalSurface (neckAx2, myNeckRadius * 1.05);
// 线程:定义 2D 曲线
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);
// 线程:构建边和连线
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);
// 创建线程
BRepOffsetAPI_ThruSections aTool(Standard_True);
aTool.AddWire(threadingWire1);
aTool.AddWire(threadingWire2);
aTool.CheckCompatibility(Standard_False);
TopoDS_Shape myThreading = aTool.Shape();
// 构建生成的化合物
BRep_Builder aBuilder;
aBuilder.MakeCompound (aRes);
aBuilder.Add (aRes, myBody);
aBuilder.Add (aRes, myThreading);
return aRes;
}
© 版权声明
THE END
喜欢就支持一下吧
点赞1.2W+ 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容