基於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

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