文字與圓球


挑戰過 文 字之塔,下一步會不會想挑戰 文 字之球 呢?

文字與圓球

這玩意兒不好列印,不過,還 是有人印得很漂亮呢!我是開了支撐,勉勉強強還算可以:

PI Sphere

先從簡單的開始

要讓字繞著球,需要的數學會更多一些,因此,我們先從簡單的開始,首先,你要能讓方塊繞著 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;
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);

完成的結果就是:

文字與圓球

這麼一來,就只要決定半徑、厚度、每一圈的字數,以及開口比例,剩下的數字,就會自動計算出來,這樣比較合理,而這個過程,是程式建模的挑戰, 然而也是有趣的地方,你得去觀察,然後思考,尋求規則,接著逐一解決,不單是數學需要這個過程,也不單是程式需要這個過程,你玩弄任何的東西都需 要這個過程,這麼一來,你的設計才會被賦予深度,才會有靈魂!