扇形與弧形


Heart chain with text(後來還有盤起來的版本 Text heart chain),是還蠻受歡迎的小創作,想必是有許多人印來送給(小)情人吧!

Heart chain with t

每個愛心之間,用了個簡單的小環上下巧妙地交錯,因此可以一次列印,想要建立這簡單的小環前,需要什麼準備工作呢?每個人的想法應該都不太一 樣,我的想法是,我應該要有個能指定角度範圍畫弧形的模組。

扇形

不過,在想要建立弧形之前,應該要先能建立扇形,如果 circle 模組能提供角度的選項,就可以簡單地完成這個功能了,可惜的是,這個 OpenSCAD 也沒有提供,只好自己來了。

圓的組成 中,我們知道圓其實是多個三角形組成,因此嚴格說來,圓也是個多邊形,談到多邊形,就會想到 polygon 模組,如果從原點出發,算出扇形的每個頂點,不就可以使用 polygon 模組建立扇形了嗎?

radius = 10;
angles = [45, 135];
points = [
    for(a = [angles[0]:1:angles[1]]) [radius * cos(a), radius * sin(a)]
];
polygon(concat([[0, 0]], points));

從結果看來,初步的想法沒錯,畫出了 45 度到 135 度的扇形:

扇形與弧形

不過,這邊在計算每個點時,每次是在角度上遞增 1 度,在 圓的組成 中談過,圓其實是多個三角形組成,就 circle 模組來說,這可以由 $fn 參數來決定,就上面的扇形來說,相當於 $fn = 360 的圓,取其中 45 度到 135 度成為扇形。

這引發了我一個考量,我要建立的扇形模組,必須能與 circle 模組一致,提供 fn 參數(或甚至 fafs),讓模組的使用者,可以指定 fn 大小建立一個圓,再從這個圓擷取指定角度範圍內的扇形。

除了與 circle 能一致之外,這麼做的好處是,我可以擷取任意角度範圍內的扇形,像是從 $fn = 12 的圓,擷取 0 到 135 度的扇形:

扇形與弧形

看到了嗎?因為是 $fn = 12 的圓,最左邊的邊長與右邊幾個邊長不一樣,這邊是故意使用 $fn = 12 來突顯這類的情況。

因此,現在換個角度來想,扇形也可以是建立一個圓後,再減去不想要的部份來得到,例如上面這個結果,可以是底下兩個圖的減集:

扇形與弧形

根據這個想法,可以實作出底下的 sector 模組:

radius = 20;
angles = [45, 135];
fn = 12;

module sector(radius, angles, fn = 24) {
    step = -360 / fn;

    points = concat([[0, 0]],
        [for(a = [angles[0] : step : angles[1] - 360]) 
            [radius * cos(a), radius * sin(a)]
        ],
        [[radius * cos(angles[1]), radius * sin(angles[1])]]
    );

    difference() {
        circle(radius, $fn = fn);
        #polygon(points);
    }
}

sector(radius, angles, fn);

不過,結果有點不是我們要的:

扇形與弧形

喔喔!本來建立用來要從圓減去的的部份,並沒有辦法涵蓋整個圓!解決這個問題方式,其實就只要讓紅色部份在建立時的半徑大於黃色部份,這樣就一 定蓋得住,至於要多大呢?可以很簡單地就讓它是 radius 的兩倍大,這樣一定蓋得住,當然,如果你覺得要龜毛一些比較有樂趣,就來算算看吧!

因為在最差的情況下,黃色部份的頂點會是與紅色部份邊長的一半處,也就是…

扇形與弧形

根據上圖,只要讓紅色的部份的半徑可以是 r,就一定蓋得住黃色的了:

a = 180 / fn;
r = radius / cos(a);

因此,修正後的 sector 模組實作就是:

radius = 20;
angles = [45, 135];
fn = 24;

module sector(radius, angles, fn = 24) {
    r = radius / cos(180 / fn);
    step = -360 / fn;

    points = concat([[0, 0]],
        [for(a = [angles[0] : step : angles[1] - 360]) 
            [r * cos(a), r * sin(a)]
        ],
        [[r * cos(angles[1]), r * sin(angles[1])]]
    );

    difference() {
        circle(radius, $fn = fn);
        polygon(points);
    }
}

sector(radius, angles, fn);  

完成的效果如下:

扇形與弧形

弧形

有了 sector 模組,接下來要建立 arc 畫弧就簡單了,一個大扇形減去一個小扇形就可以啦!

radius = 20;
angles = [45, 290];
width = 2;
fn = 24;

module sector(radius, angles, fn = 24) {
    r = radius / cos(180 / fn);
    step = -360 / fn;

    points = concat([[0, 0]],
        [for(a = [angles[0] : step : angles[1] - 360]) 
            [r * cos(a), r * sin(a)]
        ],
        [[r * cos(angles[1]), r * sin(angles[1])]]
    );

    difference() {
        circle(radius, $fn = fn);
        polygon(points);
    }
}

module arc(radius, angles, width = 1, fn = 24) {
    difference() {
        sector(radius + width, angles, fn);
        sector(radius, angles, fn);
    }
} 

linear_extrude(1) arc(radius, angles, width);

完成的效果如下:

扇形與弧形

如果你去看我放在 Thingiverse 上的作品,裏頭若有用到扇形或弧形時,會發現裏頭的程式碼好像不是這麼一回事,這是因為實作時第一次想到的設計或者是考量,並不是那麼完整,而在寫文件記錄這些設計的同 時,我總是會想,還有沒有其他的考量,還有沒有更好的設計。

記錄,不單只是記錄!