挖洞與切除


在 3D 模型設計中,為了各種理由,經常會需要挖洞,CadQuery 在這方面提供了許多選擇,例如在 2D 操作後擠出、在 3D 建構切除,或者透過一些高階的 API 來處理。

在〈基本 2D 操作〉就看過了,2D 操作若構成了兩個以上的封閉曲線,若其中一個封閉曲線涵蓋了其他封閉曲線,在擠出時,其他封閉曲線會用來挖洞,其中舉了幾個例子,像是在建立大圓後,透過 center 指定圓中的位置建立方形,或者透過 forConstruction 僅建立幾何資訊,取得頂點位置來建立方形,擠出後的圓中就會有方形的洞。

如果已知一組要挖洞的點,可以透過 pushPoints 加入 Workplane 的堆疊,然後用來建立幾何資訊,例如,在大圓內側沿著小圓挖 24 個洞:

from math import cos, sin, pi
import cadquery as cq

r1 = 10
r2 = 8
r3 = 1
n = 12
thickness = 1

a_step = 2 * pi / n 

points = [(r2 * cos(i * a_step), r2 * sin(i * a_step)) for i in range(n)]

plate = (cq.Workplane()
           .circle(r1)
           .pushPoints(points)
           .circle(r3)
           .extrude(thickness)
        )

這會顯示以下的模型:

挖洞與切除

接下來的示範,建立的模型都會像上圖。

若是圓形的相關位置,也可以透過 polarArray 來指定,以上範例可以簡化如下:

import cadquery as cq

r1 = 10
r2 = 8
r3 = 1
n = 12
thickness = 1

plate = (cq.Workplane()
           .circle(r1)
           .polarArray(r2, 0, 360, n)
           .circle(r3)
           .extrude(thickness)
        )

如果你有使用過其他 CAD 軟體的經驗,也許直覺上,會建立一個 3D 物件,並以減集來達到目的,例如:

import cadquery as cq

r1 = 10
r2 = 8
r3 = 1
n = 12
thickness = 1

plate = (cq.Workplane()
           .circle(r1)
           .extrude(thickness)
        )

sticks = (cq.Workplane()
            .polarArray(r2, 0, 360, n)
            .circle(r3)
            .extrude(thickness)
         )

result = plate - sticks

show_object(result)

不過,CadQuery 可以直接用 2D 幾何資訊來作為切除的依據。例如:

import cadquery as cq

r1 = 10
r2 = 8
r3 = 1
n = 12
thickness = 1

plate = (cq.Workplane()
           .circle(r1)
           .extrude(thickness)
           .faces('>Z')               # 選擇 Z 正方向最遠的面
           .workplane()               # 建立一個工作平面
           .polarArray(r2, 0, 360, n) # 位置資訊
           .circle(r3)                # 建立 Wire
           .cutThruAll()              # 切穿
        )

cutThruAll 會利用尚未被處理的 Wire 來切穿實體,另一個跟切除有關的方式是 cutBlind,它就像是把 Wire 拿來 extrude,擠出後的結果會被用來切除實體,例如:

import cadquery as cq

r1 = 10
r2 = 8
r3 = 1
n = 12
thickness = 1

plate = (cq.Workplane()
           .circle(r1)
           .extrude(thickness)
           .polarArray(r2, 0, 360, n)
           .circle(r3)
           .cutBlind(thickness)
        )

如果 cutBlind(thickness) 改成 cutBlind(thickness / 2),就不會切穿了:

挖洞與切除

單純就挖洞而言,Workplane 可以直接使用 hole 方法:

import cadquery as cq

r1 = 10
r2 = 8
r3 = 1
n = 12
thickness = 1

plate = (cq.Workplane()
           .circle(r1)
           .extrude(thickness)
           .faces('>Z')
           .workplane()
           .polarArray(r2, 0, 360, n)
           .hole(r3 * 2) # 指定直徑
        )

如果不想打穿,hole 有個 depth 可以指定深度。

Workplane 還有兩個挖洞的方法,可以用來埋頭孔,通常作為鎖螺絲後可以藏起螺絲頭的孔,cboreHole 可以建立圓柱形的平底孔,例如,將上例的 hole(r3 * 2) 改為 cboreHole(r3, r3 * 2, thickness / 2),可以看到:

挖洞與切除

cskHole 可以用來建立圓錐形孔,例如將上例的 hole(r3 * 2) 改為 cskHole(r3, r3 * 2, 45, thickness / 42),可以看到:

挖洞與切除

說是打洞,這些方法也不只可以用來打洞,例如:

import cadquery as cq

r1 = 10
r2 = 1
n = 12
thickness = 1

plate = (cq.Workplane()
           .circle(r1)
           .extrude(thickness)
           .faces('>Z')
           .workplane()
           .polarArray(r1, 0, 360, n)
           .hole(r2 * 2)
        )

這會構成以下的效果:

挖洞與切除

CadQuery 因為 API 豐富,一個模型可以有多樣化的建構方式,沒有一定要怎麼做,以清楚易懂為主,例如,以下的程式碼會建立打穿的立方塊模型:

import cadquery as cq

w = 1
half_w = w / 2

box = (cq.Workplane()
         .box(w, w, w)
         .faces('+X')
             .workplane()
             .hole(half_w)
         .faces('+Y')
             .workplane()
             .center(half_w, 0)
             .hole(half_w)
         .faces('+Z')
             .workplane()
             .center(0, -half_w)
             .hole(half_w)
        )

這會顯示以下結果:

挖洞與切除

然而,平面的選擇與中心的處理有點麻煩,不如用以下的程式碼會好懂一些:

import cadquery as cq

w = 1

cylinder = (cq.Workplane()
              .circle(w / 4)
              .extrude(w)
              .translate((0, 0, -w / 2))
           )

cylinders = (cylinder + 
             cylinder.rotate((0, 0, 0), (1, 0, 0), 90) +
             cylinder.rotate((0, 0, 0), (0, 1, 0), 90)
             )

box = cq.Workplane().box(w, w, w) - cylinders

show_object(box)

雖然 CadQuery 鼓勵使用方法鏈,不過別過火了,適當地建立各個部件,或者是將相關模型的建立邏輯,封裝函式甚至類別,讓程式碼容易理解,會是最重要的。