愛心置中不置中?


在〈布林運算與 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 模組預設 centerfalse,因此只使用 heart(radius) 時,就是不置中,若要置中,可以使用 heart(radius, center = true)heart(radius, true),建議使用前者會比較清楚。

在這最後的範例中,你看到將 height 轉換為 radius,要使用者想像愛心中有個半徑為 radius 的圓,這比較不直覺,你可以讓使用者指定寬或高,然後透過公式轉換為 radius,我在這邊讓使用者可以指定高,也就是 height,指定愛心會有多高,應該是直覺得多了!