基于OpenCASCADE自制三维建模软件(六)瓶子模型例程


在OpenCASCADE有一个例程,在官方帮助网站中可以找到。程将教你如何使OpenCASCADE服务来进行三维建模。教程的目的不是描述所有的类,而是帮助你开始考虑将OpenCASCADE作为一种工具。

概述

预备知识

教程中需要有一定的C++的经验。从编程的角度来看,Open CASCADE的目的是用三维建模类、方法和函数来增强C++工具。所有这些资源的组合可以让你创建实际的应用程序。

模型

为了演示三维建模工具包中提类的使用,我们将创建一个三维瓶子,如下图所示:
在这里插入图片描述

规格

制定瓶子规格如下:

物件参数 参数名称 参数值
瓶子高度 myHeight 70mm
瓶子宽度 myWidth 50mm
瓶子厚度 myThickness 30mm

此外,瓶子的底座将以笛卡尔座标系的原点为中心。
在这里插入图片描述

通过以下四个步骤对瓶子建模:

  1. 构建瓶子轮廓
  2. 构建瓶子瓶身
  3. 在瓶口上做螺纹
  4. 合并模型

一、构建轮廓

定义支持点

首先创建轮廓的特征点及其座标,如下面的(XOY)平面所示。这些点为几何形状的支持点。
在这里插入图片描述

    // 轮廓:定义支持点
    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);

定义几何图形

在前面定义的点的帮助下,您可以计算出瓶子轮廓几何的一部分。如图所示,它将由两个线段和一个圆弧组成。
在这里插入图片描述

    // 轮廓:定义几何图形
    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);

定义拓扑结构

现在已经创建了轮廓的一个部分的几何曲线,但这些曲线是独立的,彼此之间没有关系。为了简化建模,可以凭借这三条曲线制作为一个实体。这可以通过使用TopoDS包中定义的OCC的拓扑数据结构来实现:它定义了几何实体之间的关系,这些实体可以链接在一起来表示复杂的形状。TopoDS包的每个对象继承自TopoDS_Shape类,描述一个拓扑形状,如下图所示:

形状 Open CASCADE 类型描述
Vertex TopoDS_Vertex 零维空间形状,相当于几何中的一个点
Edge TopoDS_Edge 与曲线相对应的一维形状,在每条曲线的每一个末端上都有一个顶点
Wire TopoDS_Wire 由顶点连接的边的序列。
Face TopoDS_Face 表面的一部分,以闭合的导线为界
Shell TopoDS_Shell 由边连接的面集
Solid TopoDS_Solid 由外壳包围的部分3D 空间
CompSolid TopoDS_CompSolid 由面连接的一组固体
Compound TopoDS_Compound 上述任何其他形状的集合

参照上表,要构建轮廓需要创建:

  • 之前创建的三条边(Edge)
  • 一条有这些边的线(Wire)
    在这里插入图片描述
    然而,TopoDS包只提供拓扑实体的数据结构。可以在BRepBuilderAPI包中找到用于计算标准拓扑对象的算法类。要创建一条边,可以使用BRepBuilderAPI_MakeEdge类来创建前面计算过的曲线:
    // 轮廓:定义拓扑结构
    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();    // 获得X轴
    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);

二、构建瓶身

拉伸轮廓

要计算瓶身,需要创建一个实体形状。最简单的方法是使用前面创建的轮廓,并沿着一个方向进行扫描。Open CASCADE的Prism功能最适合这项任务。它接受一个形状和一个方向作为输入,并根据以下规则生成一个新的形状:

形状 生成
Vertex Edge
Edge Face
Wire Shell
Face Solid
Shell Compound of Solids

在这里插入图片描述

    // 瓶身:拉伸轮廓
    TopoDS_Face myFaceProfile = BRepBuilderAPI_MakeFace(myWireProfile);
    gp_Vec aPrismVec(0, 0, myHeight);
    TopoDS_Shape myBody = BRepPrimAPI_MakePrism(myFaceProfile, aPrismVec);

倒角

瓶身的边缘很锋利。要用圆脸替换它们,可以使用Open CASCADE的圆角功能。根据我们的需求,指定倒角必须:

  • 应用在形状的所有边缘
  • 半径是myThickness / 12

在这里插入图片描述

    // 瓶身:倒角
    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();

添加瓶颈

要在瓶子上加一个颈,需要做一个圆柱体,然后把它和瓶身融合在一起。圆柱体安装在瓶身的顶部,半径为myThickness / 4,高度为和myHeight / 10
在这里插入图片描述

    // 瓶身:添加瓶颈
    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);

创造中空的实体

因为一个真正的瓶子是用来装液体材料的,所以应该从瓶子的顶部创建一个中空的实体。在OpenCASCADE中,被掏空的固体称为Thick Solid,其内部计算如下:

  • 从初始实体中移除一个或多个面,以获得被掏空实体的第一面墙W1。
  • 在距离D处,从W1创建一个平行的墙W2。如果D是正的,W2将在初始实体的外面,否则它将在里面。
  • 从W1和W2两面墙计算一个实体。
    在这里插入图片描述
    // 瓶身:创造中空的实体
    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 BodyMaker;
    BodyMaker.MakeThickSolidByJoin(myBody, facesToRemove, -myThickness / 50, 1.e-3);
    myBody = BodyMaker.Shape();

三、构建螺纹

创建表面

到目前为止,已经学习了如何从3D曲线创建边缘。现在将学习如何创建一个二维曲线和曲面的边缘。为学习这方面的,在圆柱表面创建螺旋形轮廓。这个理论比前面的步骤更复杂,但是应用起来非常简单。第一步,计算这些圆柱面。现在已经熟悉了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,2π],且V是无穷大的

在这里插入图片描述
有这样参数化几何的好处是,可以计算任何(U, V)的表面参数:

  • 三维点
  • 该点1,2到N阶的导数向量
    这些参数方程的另一个好处是,可以把一个曲面看作是一个二维参数空间,它由(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 描述圆柱高度和角度变化的螺旋曲线
    // 螺纹:创建边和线
    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);

创建边和线

创建瓶子的基础轮廓,需要:

  • 计算颈部螺纹的边缘
  • 从这些边中计算出两条线

在这里插入图片描述

    // 螺纹:创建边和线
    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);

构建螺纹

螺纹将是一个实体形状,所以现在必须计算这些线的面,这些面允许您连接这些线,这些面的外壳,然后是实体本身。这可能是一个漫长的操作。在定义基本拓扑时,总是有更快的方法来构建实体。现在可以通过用两条线创造一个实体,OpenCASCADE提供了一种快速的方法来实现这一点,通过建立loft:一个外壳或固体在给定的序列中穿越一组线。loft函数是在BRepOffsetAPI_ThruSections类中实现的,使用方法如下:

  • 通过创建类的实例初始化算法。如果要创建实体,必须指定此构造函数的第一个参数。默认情况下,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);
    return aRes;

五、实际效果

创建一个头文件,添加以下函数,并将瓶子构建内容加入到函数中:

TopoDS_Shape MakeBottle(const Standard_Real myWidth, 
                        const Standard_Real myHeight,
                        const Standard_Real myThickness)
                        {
                        ...
                        }

编写三维显示界面,实现显示瓶子:

    // 创建一个立方体作测试
    TopoDS_Shape t_topo_bottle = MakeBottle(70.0, 50.0, 30.0);
    Handle(AIS_Shape) t_ais_bottle = new AIS_Shape(t_topo_bottle);
    m_context->Display(t_ais_bottle, Standard_True);
    m_view->FitAll();

显示效果:
在这里插入图片描述

项目仓库

https://github.com/Jelatine/JellyCAD

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章