操作 BREP 實例


在 CadQuery 要建立模型,Workplane 是方便的起點,因為它是個容器,用來管理頂點、邊、線、面、殼、實體等,也提供了許多便於建立模型的高階方法;然而,並不是任何任務都可以透過 Workplane 完成,有些操作也不適合使用 Workplane 管理,在這個時候,親手操作點、邊、線、面、殼、實體,就會是必要的。

認識 BREP〉中談到點、邊、線、面、殼、實體,CadQuery 官方文件〈CadQuery Class Summary〉中列出的類別中,有著 cadquery.Vertexcadquery.Edgecadquery.Wirecadquery.Facecadquery.Shellcadquery.Solid 等類別與之對應。

嚴格來說,cadquery.Vertexcadquery.Edgecadquery.Wirecadquery.Facecadquery.Shellcadquery.Solid 並不是類別,而是 cadquerycadquery.occ_impl.shapes 中對應的類別匯入之名稱。

cadquery.occ_impl.shapes 中對應的類別,是將 PythonOCC 進行封裝,而 PythonOCC 是 Open Cascade 的 Python 封裝;為了說明方便起見,以下直接以 VertexEdgeWireFaceShellSolid 來表示 cadquery.occ_impl.shapes 中的類別

Workplane 管理的點、邊、線、面、殼、實體等,就是 VertexEdgeWireFaceShellSolid 等建構出來的實例,更具體地說,就是 objects 或內部 pending 區保存之物件,也就是說,若能認識這些實例如何操作,就可以直接使用這些實例,完成 Workplane 不能或不適合建構的模型。

(CadQuery 的 Workplane 相對於這些實作物件的關係,就類似於 jQuery 的 jQuery 相對於 DOM 物件間的關係,如果你使用過 jQuery 就會知道,有些需求還是得直接操作 DOM 物件。)

VertexEdgeWireFaceShellSolid 等類別,提供了一些工廠方法來建構對應的 BREP 實例,首先從 Vertex 開始:

from cadquery import Vertex

vertices = [
    Vertex.makeVertex(10, -10, 0),
    Vertex.makeVertex(10, 10, 0),
    Vertex.makeVertex(-10, 10, 0),
    Vertex.makeVertex(-10, -10, 0)
]

for v in vertices:
    show_object(v)

這會建立四個頂點,每個 Vertex 實例,都會有 XYZ 特性,執行後在 CQ-editor 中會看到四個 + 圖案,代表四個頂點的位置,Vertex 實例有個 Center 方法,可以傳回向量物件,為 cadquery.occ_impl.geom.Vector 的實例,cadquery.Vector 實際上是 cadquery 匯入了 cadquery.occ_impl.geom.Vector 名稱。

如果要建立 Edge 實例,可以透過 makeXXX 等方法,最基本的就是透過 makeLine 建立一條線作為邊,這個方法需要的就是兩個 Vector,邊的起點與終點:

from cadquery import Vertex, Edge

vertices = [
    Vertex.makeVertex(10, -10, 0),
    Vertex.makeVertex(10, 10, 0),
    Vertex.makeVertex(-10, 10, 0),
    Vertex.makeVertex(-10, -10, 0)
]

for i in range(4):
    vector1 = vertices[i].Center()
    vector2 = vertices[(i + 1) % 4].Center()
    edge = Edge.makeLine(vector1, vector2)
    show_object(edge)

這會顯示以下的圖形:

操作 BREP 實例

若要進一步地,將 Edge 實例組成 Wire,可以透過 WireassembleEdges

from cadquery import Vertex, Edge, Wire

vertices = [
    Vertex.makeVertex(10, -10, 0),
    Vertex.makeVertex(10, 10, 0),
    Vertex.makeVertex(-10, 10, 0),
    Vertex.makeVertex(-10, -10, 0)
]

edges = [
            Edge.makeLine(
                vertices[i].Center(), 
                vertices[(i + 1) % 4].Center()
            ) 
            for i in range(4)
        ]

wire = Wire.assembleEdges(edges)
show_object(wire)

EdgeWire 在 CQ-editor 顯示出來是相同的圖案,因此執行結果同上圖。

目前已經有 Wire 了,若要擠出,可以透過 SolidextrudeLinear 等高階操作,例如:

from cadquery import Vertex, Edge, Wire, Solid, Vector

vertices = [
    Vertex.makeVertex(10, -10, 0),
    Vertex.makeVertex(10, 10, 0),
    Vertex.makeVertex(-10, 10, 0),
    Vertex.makeVertex(-10, -10, 0)
]

edges = [
            Edge.makeLine(
                vertices[i].Center(), 
                vertices[(i + 1) % 4].Center()
            ) 
            for i in range(4)
        ]

wire = Wire.assembleEdges(edges)
solid = Solid.extrudeLinear(wire, [], Vector(0, 0, 10))
show_object(solid)

extrudeLinear 接受的引數是外部 Wire、內部 Wire 以及擠出時的方向及距離,目前沒有內部 Wire,指定 [] 就可以了,Vector 實例就包含了方向及距離(向量的長度),執行結果如下:

操作 BREP 實例

如果想進一步從邊到建立實體呢?這可以透過 Face.makeFromWires 建立 Face,接著使用 Shell.makeShell 建立 Shell,最後以 Solid.makeSolid 建立 Solid

from cadquery import Vector, Vertex, Edge, Wire, Solid, Shell, Face

# 建立頂點
vertices = [
    Vertex.makeVertex(10, -10, 0),
    Vertex.makeVertex(10, 10, 0),
    Vertex.makeVertex(-10, 10, 0),
    Vertex.makeVertex(-10, -10, 0),
    Vertex.makeVertex(10, -10, 10),
    Vertex.makeVertex(10, 10, 10),
    Vertex.makeVertex(-10, 10, 10),
    Vertex.makeVertex(-10, -10, 10)
]

# 面的頂點索引
faces_indices = [
    [0, 3, 2, 1], 
    [4, 5, 6, 7], 
    [0, 1, 5, 4], 
    [3, 7, 6, 2], 
    [0, 4, 7, 3], 
    [1, 2, 6, 5]
]

faces = []
for face_indices in faces_indices:
    edges = [
        # 建立邊
        Edge.makeLine(
            vertices[face_indices[i]].Center(), 
            vertices[face_indices[(i + 1) % 4]].Center()
        ) 
        for i in range(4)
    ]   
    # 建立線  
    wire = Wire.assembleEdges(edges)
    # 建立面
    face = Face.makeFromWires(wire)
    faces.append(face)

# 建立殼
shell = Shell.makeShell(faces)
# 建立實體
solid = Solid.makeSolid(shell)
show_object(solid)

在建立 Face,線必須共平面,對於方才擠出的方塊而言,每個面的四個邊一定是共平面,因此在這邊可以用來建構 Face,頂點的順序是繞 Face 朝外法向量呈逆時針,執行結果與上圖相同。

視情況而定,你可以搭配 Workplane 來管理自行建構的 BREP 實例,透過 add 方法,可以將 BREP 加到 objects 中管理,透過 toPending,可以將 objects 中的實例放到 pending 區;相對地,你也可以在操作 Workplane 進行高階操作後,取出 objects 中的實例,來做細部的控制,或者加入另一個 Workplane,像這類操作綜合運用,就可以在建構模型更加靈活。

對於其他的多面體,若要求邊一定是共平面,可以使用三角形,因為三角形必然共平面,進一步地,可以將以上範例重構為通用函式,指定頂點與三角面的頂點索引,自動建立多面體,這就之後來談了。