在我的作品中,Character tower generator、Text Sphere 或者是 Spinning picture ornament 都會用到螺線。
後續在討論模型如何結合圓的設計時,會談到如何設計出這些模型,這邊先討論一下,這模型中的螺線怎麼實現,最簡單的作法之一是,在螺線的路徑上,密集地建立小方塊來組成,如果你看看上面一些模型的原始碼,我確實是這麼做的,然而,我也非常地不滿意,我一直在思考,有沒有更優雅的方式來建立這類線段?
如果能計算出螺線的路徑上所有座標點,並有個 polyline3D
之類的模組,這個需求就可以解決了,只是這 polyline3D
要怎麼建立呢?
兩點決定一線
在 線段 中,我們曾經討論過,如何在 XY 平面建立 2D 線段,那時是從兩點決定一線開始實作,如果想要能指定 XYZ 座標的點,建立 3D 線段的話,當然也是要先將兩點決定一線實現出來。
你也許會想,實現 2D 線段時,使用了內建的 polygon
模組,OpenSCAD 還有個內建的 polyhedron
模組,可以指定座標建立 3D 模型,可不可以利用呢?我想,基本上是可以,只不過,要使用 polyhedron
,就必須為 3D 線段建立頂點,這可不是件容易的事,有沒有更簡單的方式?
還記得在 布林運算與 hull 轉換 中談過的 hull
操作嗎?它會像一塊布將含蓋的模型圍起來,例如當時舉的例子是:
radius = 10;
hull() {
circle(radius);
translate([2 * radius, 0, 0])
circle(radius);
}
這會將兩個圓圍起來成為:
如果將兩個圓拉開一點呢?看起來會成為一條線,對吧!如果兩個圓的圓心座標可以指定,而將直徑當成是線段寬度,那是不是也能用來建立線段呢?這也就是 線段 中我談到的,還有其他實作線段的方式之一。
那麼,如果不是個兩個圓,而是個兩個球,球心座標可以指定,並將這兩個球用 hull
的話會如何呢?耶?這似乎是個實作 3D 線段的簡單方法!
module line3D(p1, p2, thickness, fn = 24) {
$fn = fn;
hull() {
translate(p1) sphere(thickness / 2);
translate(p2) sphere(thickness / 2);
}
}
line3D([1, 1, 1], [10, 10, 10], 1, 3);
耶!果然成功了:
由於是由圓來組成線段的兩個端點,這邊特意設計了 fn
參數,指定的 fn
決定了線段的圓滑度,用來創造不同的線段質感,當然,fn
越大,就會需要更多的計算,由於使用了 hull
,當線段多時,fn
越大,繪製模型就會越慢。
polyline3D
可以兩點決定一線了,同樣地,只要兩個點為一組,將全部的點消耗完就可以了:
module line3D(p1, p2, thickness, fn = 24) {
$fn = fn;
hull() {
translate(p1) sphere(thickness / 2);
translate(p2) sphere(thickness / 2);
}
}
module polyline3D(points, thickness, fn) {
module polyline3D_inner(points, index) {
if(index < len(points)) {
line3D(points[index - 1], points[index], thickness, fn);
polyline3D_inner(points, index + 1);
}
}
polyline3D_inner(points, 1);
}
points = [[1, 2, 3], [4, -5, -6], [-1, -3, -5], [0, 0, 0]];
polyline3D(points, 1, 3);
完成的效果不錯:
試著做螺線
接下來就是個練習了,請你試著只實作 Character tower generator 中的螺線,底下列出一種可能的方式:
module line3D(p1, p2, thickness, fn = 24) {
$fn = fn;
hull() {
translate(p1) sphere(thickness / 2);
translate(p2) sphere(thickness / 2);
}
}
module polyline3D(points, thickness, fn) {
module polyline3D_inner(points, index) {
if(index < len(points)) {
line3D(points[index - 1], points[index], thickness, fn);
polyline3D_inner(points, index + 1);
}
}
polyline3D_inner(points, 1);
}
r = 20;
h = 5;
fa = 15;
circles = 10;
points = [
for(a = [0:fa:360 * circles])
[r * cos(a), r * sin(a), h / (360 / fa) * (a / fa)]
];
polyline3D(points, 1, 3);
完成的效果如下:
如果不清楚這程式碼是怎麼一回事,可以看看之後的 文字與圓柱 之說明。
再來一個練習,試著只建立 Spinning picture ornament 中的螺線,底下是一個參考實作:
module line3D(p1, p2, thickness, fn = 24) {
$fn = fn;
hull() {
translate(p1) sphere(thickness / 2);
translate(p2) sphere(thickness / 2);
}
}
module polyline3D(points, thickness, fn) {
module polyline3D_inner(points, index) {
if(index < len(points)) {
line3D(points[index - 1], points[index], thickness, fn);
polyline3D_inner(points, index + 1);
}
}
polyline3D_inner(points, 1);
}
r = 50;
points = [
for(a = [0:180])
[r * cos(-90 + a) * cos(a), r * cos(-90 + a) * sin(a), r * sin(-90 + a)]
];
for(i = [0:7]) {
rotate(45 * i) polyline3D(points, 2, 3);
}
完成的效果如下:
如果不清楚程式碼中的計算是怎麼一回事,可以參考一下 文字與圓球 中的說明。
最後,可以將這些日子以來,一直不滿意的問題給解決掉,不正是一件值得開心的事嗎?! :)