在〈布林運算與 hull 轉換〉談過怎麼製作愛心,要有愛心很簡單,只是你的考量夠不夠多…XD
上頭的愛心有什麼問題呢?沒有問題,只是如果你想要與其他圖案結合,創造更多複雜的模型時,就顯得不方便了,因為你得知道要將愛心移動到哪個位置,就目前而言,座標的原點,究竟是愛心的什麼位置呢?
OpenSCAD 預設提供的模組並不多,只是一些通用的模組,這些模組有個特性,有的模組既可以置中,也可以用座標原點當作模型的左下角。
因為 OpenSCAD 預設的模組並不多,因此,如果你可以在設計模組時多一份考量,將來這些模組就會逐漸成為你設計時的基礎,根據上頭的描述,也許你可以將愛心模組設計成,可讓使用者選擇是要置中,或者以座標原點作為模型的左下角,為此,你就必須清楚愛心的幾何資訊。
置中的愛心
那麼要怎麼置中愛心?先來考量愛心的左右對稱吧!還記得愛心基本上可以用一個圓與方塊來完成嗎?
radius = 10;
module heart(radius) {
rotated_angle = 45;
diameter = radius * 2;
$fn = 48;
rotate(-rotated_angle) union() {
#circle(radius);
translate([0, -radius, 0])
square(diameter);
}
}
heart(radius);
圓的半徑 radius
,方塊的邊長是 2 * radius
,模型轉了 45
度,要怎麼讓方塊下方的直角位於 y
軸上呢?
因此,只要在 x 軸上位移 -radius * cos(45)
就可以了,而且這麼一來,只要做個鏡像,就可以成為愛心了:
radius = 10;
module heart_sub_component(radius) {
rotated_angle = 45;
diameter = radius * 2;
$fn = 48;
translate([-radius * cos(rotated_angle), 0, 0])
rotate(-rotated_angle) union() {
circle(radius);
translate([0, -radius, 0])
square(diameter);
}
}
module heart(radius) {
# heart_sub_component(radius);
# mirror([1, 0, 0]) heart_sub_component(radius);
}
heart(radius);
mirror
是用來做鏡像轉換,想像一面鏡子立著,它的法線方向是 x 軸方向,那麼這面鏡子就是立於 y-z 平面,使用 mirror
時指定的向量,就是指出法線方向,這就是上頭為什麼要使用 mirror([1, 0, 0])
的原因。
在這邊也可以看到,為了避免重複撰寫相同的程式碼,直接抽出了要鏡像的 heart_sub_component
模組,這樣程式碼也清楚許多,一個 heart_sub_component
模組不鏡像,另一個做了鏡像,結果就是:
這樣的愛心置中了嗎?還沒有,我想以愛心上頭的圓頂端與下頭的尖角間距離的一半當作置中,那麼這段距離會是多少?
從上圖中可以知道,愛心的高會是 (4 * radius * sin(45)) + (radius – radius * sin(45))
,也就是 3 * radius * sin(45) + radius
,然而由於目前座標原點已經是在愛心頂端算下來 radius
處,因此,只要再位移 (3 * radius * sin(45) + radius) / 2 - radius
,也就是 1.5 * radius * sin(45) - 0.5 * radius
,就可以置中了:
radius = 10;
module heart_sub_component(radius) {
rotated_angle = 45;
diameter = radius * 2;
$fn = 48;
translate([-radius * cos(rotated_angle), 0, 0])
rotate(-rotated_angle) union() {
circle(radius);
translate([0, -radius, 0])
square(diameter);
}
}
module heart(radius) {
center_offset_y = 1.5 * radius * sin(45) - 0.5 * radius;
translate([0, center_offset_y, 0]) union() {
heart_sub_component(radius);
mirror([1, 0, 0]) heart_sub_component(radius);
}
}
heart(radius);
不置中的愛心
如果不置中而想以座標原點為左下角,那麼根據第三張圖與第五張圖,分別只要讓愛心在 x 軸上位移 radius + radius * cos(45)
,在 y 軸上位移 3 * radius * sin(45)
就可以了:
height = 10;
module heart_sub_component(radius) {
rotated_angle = 45;
diameter = radius * 2;
$fn = 48;
translate([-radius * cos(rotated_angle), 0, 0])
rotate(-rotated_angle) union() {
circle(radius);
translate([0, -radius, 0])
square(diameter);
}
}
module heart(radius, center = false) {
offsetX = center ? 0 : radius + radius * cos(45);
offsetY = center ? 1.5 * radius * sin(45) - 0.5 * radius : 3 * radius * sin(45);
translate([offsetX, offsetY, 0]) union() {
heart_sub_component(radius);
mirror([1, 0, 0]) heart_sub_component(radius);
}
}
heart(height / (3 * sin(45) + 1));
OpenSCAD 的模組或函式,都可以設置預設引數,像上頭 heart
模組預設 center
是 false
,因此只使用 heart(radius)
時,就是不置中,若要置中,可以使用 heart(radius, center = true)
或 heart(radius, true)
,建議使用前者會比較清楚。
在這最後的範例中,你看到將 height
轉換為 radius
,要使用者想像愛心中有個半徑為 radius
的圓,這比較不直覺,你可以讓使用者指定寬或高,然後透過公式轉換為 radius
,我在這邊讓使用者可以指定高,也就是 height
,指定愛心會有多高,應該是直覺得多了!