這玩意兒不好列印,不過,還 是有人印得很漂亮呢!我是開了支撐,勉勉強強還算可以:
先從簡單的開始
要讓字繞著球,需要的數學會更多一些,因此,我們先從簡單的開始,首先,你要能讓方塊繞著 z 軸轉 180 度,如果不會的話,建議你先看看之前的文章,我是這麼做的:
radius = 40;
step_angle = 10;
length = 5;
for(a = [0:step_angle:180]) {
rotate([0, 0, a]) translate([radius, 0, 0])
cube([length, length, length], center = true);
}
非常簡單的程式,結果就是:
如果每個方塊除了繞 z 軸轉之外,同時再繞著 y 軸會如何呢?例如,同時繞 y 軸轉 -45 度:
radius = 40;
step_angle = 10;
length = 5;
for(a = [0:step_angle:180]) {
rotate([0, -45, a]) translate([radius, 0, 0])
cube([length, length, length], center = true);
}
%sphere(radius);
結果會是半圓弧的部份跑到半徑與平面夾 45 度的地方,我故意加上個透明球,這樣比較看得出來:
你可以試著將 45 改為 30、60 等其他數字,半圓弧的部份就會是跑到半徑與平面夾 30、60 度的地方。
如果繞 y 軸轉時,也是一邊從 -90 到 90 度遞增地轉動,這時會發現,方塊就是繞著球轉了:
radius = 40;
step_angle = 10;
length = 5;
for(a = [0:step_angle:180]) {
rotate([0, -90 + a, a]) translate([radius, 0, 0])
cube([length, length, length], center = true);
}
%sphere(radius);
如果這時將步進角調小一些會如何呢?
radius = 40;
step_angle = 0.25;
length = 5;
for(a = [0:step_angle:180]) {
rotate([0, -90 + a, a]) translate([radius, 0, 0])
cube([length, length, length], center = true);
}
%sphere(radius);
結果會是:
你可以試著將上面的模型重複個 8 次,每次繞 z 軸再轉 45 度,然後就知道 Spinning picture ornament 怎麼做了:
不過,正如 3D 線段 中提到的,還有更優雅的方式,可以建立這樣的螺線,當時最後的練習也有提供程式碼實作,別忘了回頭去看一下。
讓文字繞著球
於是,現在該是將方塊改為文字了,字的大小就暫時設為 10 好了,看看會如何:
radius = 40;
step_angle = 10;
font_size = 10;
thickness = 1;
for(a = [0:step_angle:180]) {
rotate([0, -90 + a, a]) translate([radius, 0, 0])
rotate([90, 0, 90])
linear_extrude(thickness)
text("A", size = font_size, valign = "center", halign = "center");
}
%sphere(radius);
喔!繞著球的文字出現了:
不過,這離 文字之球 還有點距離,原因是字不夠密,為什麼字不夠密?原因之一是字降得過快,原因之二是字總共繞 z 軸只轉了半圈,實際上,文字之球中的文字是繞 z 軸轉了好幾圈。
因此,這跟剛剛在做 Spinning
picture ornament 時不一樣,不能只改變 step_angle
,因為這只會改變繞
y 軸遞增的速度,實際上繞 z 軸還是只有 180 度。
如果繞 z 軸遞增 180 度,而繞 y 軸只有從 -90 度開始遞增 90 度(也就是 180 / 2)的話會如何呢?
喔?文字繞到 xy 平面就停了,因為只繞 z 軸 180 度嘛!這簡單,只要繞 z 軸 180 兩遍就好了,如果繞 y 軸只有從 -90 度開始遞增 45 度(也就是 180 / 4)的話會如何呢?為了讓文字能繞到球底部,你會需要繞 z 軸 180 四遍,因此,你會發現到這呈反比的關係,於是我們可以寫下以下的程式:
radius = 40;
step_angle = 10;
font_size = 10;
thickness = 1;
total_semi_circles = 15;
for(a = [0:step_angle:180 * total_semi_circles]) {
rotate([0, -90 + a / total_semi_circles, a]) translate([radius, 0, 0])
rotate([90, 0, 90])
linear_extrude(thickness)
text("A", size = font_size, valign = "center", halign = "center");
}
%sphere(radius);
耶!文字繞球出現了:
你可以改變 total_semi_circles
的大小,數字越大繞得越密,反之就越疏,文
字之球 有個開口,這只要調整 a
的起始值就可以了:
radius = 40;
step_angle = 10;
font_size = 10;
thickness = 1;
total_semi_circles = 15;
open_begin = 500;
for(a = [open_begin:step_angle:180 * total_semi_circles]) {
rotate([0, -90 + a / total_semi_circles, a]) translate([radius, 0, 0])
rotate([90, 0, 90])
linear_extrude(thickness)
text("A", size = font_size, valign = "center", halign = "center");
}
%sphere(radius);
結果會是:
因此,只要適當地改變這幾個參數,調整文字大小與疏密度至好看的外觀,就可以做出文字之球了…
精確地控制數字
本來事情可以就這麼結案了,Thingiverse 上的作品也就只是這樣而已,不過,到底什麼是「適當地」改變這幾個參數?啊!就試啊!試到覺得好看為止…
我這邊覺得好看的意思是,文字的開頭必須是不重疊,而且下一層跟上一層正好是一個字級的疏密度,並且可以適當地決定球的開口,難道就沒有個方式 可以知道適當的參數設定嗎?其實是有的,不過當時我懶得再做下去了…
首先來想想第一個需求好了,文字的開頭必須是不重疊,因此,我可以設定一圈要多少個字,且開口必須是 0 到 180 度哪個位置,這樣才能知道文字的開頭處圓周長多少:
如果開口夾角 angle
是 180 的某個比例 open_begin_ratio
,
那麼:
angle = 180 * open_begin_ratio;
此時,圓切面的圓周長就是 2 * PI * (radius * sin(angle))
,如果一圈有 chars_per_circle
個字,那麼每個字的大就會是:
font_size = 2 * PI * radius * sin(angle) / chars_per_circle;
每個字間的夾角就會是:
step_angle = 360 / chars_per_circle;
再來處理開口,還記得剛剛這張圖怎麼來的嗎?
當繞軸 y 是從 -45 度開始時,也就是說,如果 -90 + a / total_semi_circles
時,a / total_semi_circles
若是從 45
開始,就可以從該位置開始畫起,也就是若開口角度為 a / total_semi_circles = angle
,
那麼 a = angle * total_semi_circles
,也就是 a
的開始必須是 angle * total_semi_circles
:
begin_angle = angle * total_semi_circles;
總圈數呢?因為字每轉一圈,才會剛好又回到同一面,我們希望字每轉一圈,正好是在上一層字的下方,因此,2 * PI *
radius / font_size
就是層數,而轉一圈是一層,也就是轉兩個半圈是一層,因此總圈數就是:
total_semi_circles = 2 * PI * radius / font_size;
把上頭所有的關係整理一下,寫入程式中,就會變成:
radius = 40;
thickness = 1;
chars_per_circle = 20;
open_begin_ratio = 0.25;
angle = 180 * open_begin_ratio;
font_size = 2 * PI * radius * sin(angle) / chars_per_circle;
step_angle = 360 / chars_per_circle;
total_semi_circles = 2 * PI * radius / font_size;
begin_angle = angle * total_semi_circles;
for(a = [begin_angle:step_angle:180 * total_semi_circles]) {
rotate([0, -90 + a / total_semi_circles, a]) translate([radius, 0, 0])
rotate([90, 0, 90])
linear_extrude(thickness)
text("A", size = font_size, valign = "center", halign = "center");
}
%sphere(radius);
完成的結果就是:
這麼一來,就只要決定半徑、厚度、每一圈的字數,以及開口比例,剩下的數字,就會自動計算出來,這樣比較合理,而這個過程,是程式建模的挑戰, 然而也是有趣的地方,你得去觀察,然後思考,尋求規則,接著逐一解決,不單是數學需要這個過程,也不單是程式需要這個過程,你玩弄任何的東西都需 要這個過程,這麼一來,你的設計才會被賦予深度,才會有靈魂!