在〈實作 3D 海龜繪圖〉最後談到了,你有辦法畫個 3D 版本的〈樹 木曲線〉嗎?我實作了一個版本 Customizable tree curve:
2D 版樹木曲線
想要能實作 3D 版本的樹木曲線,基本上得先實作出 2D 版本的遞迴樹木曲線吧!想要畫出 2D 版本的遞迴樹木曲線,基本上也得先畫出…嗯…一個 Y 吧!…XD
// 必須用到之前寫的 3D 海龜繪圖程式碼 leng = 50; leng_scale = 0.7; angle = 20; width = 2; fn = 4; t = turtle3D( pt3D(0, 0, 0), [pt3D(1, 0, 0), pt3D(0, 1, 0), pt3D(0, 0, 1)] // 海龜座標向量 ); module tree(t, leng, leng_scale, angle, width, fn) { t2 = moveX(t, leng); polyline3D([getPt(t), getPt(t2)], width,
fn
); t3 = moveX(turnZ(t2, angle), leng * leng_scale); polyline3D([getPt(t2), getPt(t3)], width,
fn
); t4 = moveX(turnZ(t2, -angle), leng * leng_scale); polyline3D([getPt(t2), getPt(t4)], width,
fn
); } tree(t, leng, leng_scale, angle, width, fn);
如果想從 Y 的支幹也長出 Y,基本上,可以重複呼叫 tree
模組,因為遞迴要有終止條件,因此加上個最短長度限制,結果會是:
// 必須用到之前寫的 3D 海龜繪圖程式碼 leng = 50; leng_limit = 5; leng_scale = 0.7; angle = 20; width = 2; fn = 4; t = turtle3D( pt3D(0, 0, 0), [pt3D(1, 0, 0), pt3D(0, 1, 0), pt3D(0, 0, 1)] // 海龜座標向量 ); module tree(t, leng, leng_scale, leng_limit, angle, width, fn) { if(leng > leng_limit) { t2 = moveX(t, leng); polyline3D([getPt(t), getPt(t2)], width,
fn
); t3 = moveX(turnZ(t2, angle), leng * leng_scale); polyline3D([getPt(t2), getPt(t3)], width,
fn
); t4 = moveX(turnZ(t2, -angle), leng * leng_scale); polyline3D([getPt(t2), getPt(t4)], width,
fn
); tree( turnZ(t2, angle), leng * leng_scale, leng_scale, leng_limit, angle, width, fn); tree( turnZ(t2, -angle), leng * leng_scale, leng_scale, leng_limit, angle, width, fn); } } tree(t, leng, leng_scale, leng_limit, angle, width, fn);
2D 版本的遞迴樹就出現了:
不過,實際上,除了最末端的枝幹之外,其他枝幹都重複繪製了,也就是這段重複作了繪製的動作:
t3 = moveX(turnZ(t2, angle), leng * leng_scale); polyline3D([getPt(t2), getPt(t3)], width,
fn
); t4 = moveX(turnZ(t2, -angle), leng * leng_scale); polyline3D([getPt(t2), getPt(t4)], width,
fn
);
刪掉這兩段,會少一次末端枝幹,不過,程式碼可以精簡一些:
// 必須用到之前寫的 3D 海龜繪圖程式碼 leng = 50; leng_limit = 5; leng_scale = 0.7; angle = 20; width = 2; fn = 4; t = turtle3D( pt3D(0, 0, 0), [pt3D(1, 0, 0), pt3D(0, 1, 0), pt3D(0, 0, 1)] // 海龜座標向量 ); module tree(t, leng, leng_scale, leng_limit, angle, width, fn) { if(leng > leng_limit) { t2 = moveX(t, leng); polyline3D([getPt(t), getPt(t2)], width,
fn
); tree( turnZ(t2, angle), leng * leng_scale, leng_scale, leng_limit, angle, width, fn); tree( turnZ(t2, -angle), leng * leng_scale, leng_scale, leng_limit, angle, width, fn); } } tree(t, leng, leng_scale, leng_limit, angle, width, fn);
3D 版樹木曲線
要將上面的 2D 版本調整成 3D 版本非常簡單,如果 tree
可以指定兩個繞 z
軸轉動角度的話,並且將其中一個角設成 0 度會如何呢?
// 必須用到之前寫的 3D 海龜繪圖程式碼 leng = 50; leng_limit = 5; leng_scale = 0.7; angle1 = 20; angle2 = 0; width = 2; fn = 4; t = turtle3D( pt3D(0, 0, 0), [pt3D(1, 0, 0), pt3D(0, 1, 0), pt3D(0, 0, 1)] // 海龜座標向量 ); module tree(t, leng, leng_scale, leng_limit, angle1, angle2, width, fn) { if(leng > leng_limit) { t2 = moveX(t, leng); polyline3D([getPt(t), getPt(t2)], width,
fn
); tree( turnZ(t2, angle1), leng * leng_scale, leng_scale, leng_limit, angle1, angle2, width, fn); tree( turnZ(t2, angle2), leng * leng_scale, leng_scale, leng_limit, angle1, angle2, width, fn); } } tree(t, leng, leng_scale, leng_limit, angle1, angle2, width, fn);
長得有點奇怪,一邊往上長,又一邊往左長:
如果第二次遞迴呼叫 tree
之前,turnZ
改成 turnX
呢?樹的外型不會變,因為 turnX
也只是轉 0 度,如果 angle2
不是轉 0 度而是轉 127 度呢?
// 必須用到之前寫的 3D 海龜繪圖程式碼 leng = 50; leng_limit = 5; leng_scale = 0.7; angle1 = 20; angle2 = 127; width = 2; fn = 4; t = turtle3D( pt3D(0, 0, 0), [pt3D(1, 0, 0), pt3D(0, 1, 0), pt3D(0, 0, 1)] // 海龜座標向量 ); module tree(t, leng, leng_scale, leng_limit, angle1, angle2, width, fn) { if(leng > leng_limit) { t2 = moveX(t, leng); polyline3D([getPt(t), getPt(t2)], width,
fn
); tree( turnZ(t2, angle1), leng * leng_scale, leng_scale, leng_limit, angle1, angle2, width, fn); tree( turnX(t2, angle2), leng * leng_scale, leng_scale, leng_limit, angle1, angle2, width, fn); } } tree(t, leng, leng_scale, leng_limit, angle1, angle2, width, fn);
形成了一顆 3D 立體小樹了:
我選擇 127 這個角度的原因,是因為它是個質數,基本上只要能不整除 360 的數都可以,這麼一來繞 X 軸時,就不會整除 360
度,樹枝在盤旋上昇時就不會正好都位於同一個角度上,實際上,如果讓 leng_scale
也有 leng_scale1
與 leng_scale2
,並各選擇一個適當值,而 angle1
與 angle2
也選個適當角度,也就會有顆漂亮的樹了,為了符合 angle1
與 angle2
實際上就是繞 z 軸與 x 軸,就改名為 angleZ
與 angleX
吧!
// 必須用到之前寫的 3D 海龜繪圖程式碼 leng = 100; leng_limit = 1; leng_scale1 = 0.4; leng_scale2 = 0.9; angleZ = 60; angleX = 135; width = 2; fn = 4; t = turtle3D( pt3D(0, 0, 0), [pt3D(1, 0, 0), pt3D(0, 1, 0), pt3D(0, 0, 1)] // 海龜座標向量 ); module tree(t, leng, leng_scale1, leng_scale2, leng_limit, angleZ, angleX, width, fn) { if(leng > leng_limit) { t2 = moveX(t, leng); polyline3D([getPt(t), getPt(t2)], width,
fn
); tree( turnZ(t2, angleZ), leng * leng_scale1, leng_scale1, leng_scale2, leng_limit, angleZ, angleX, width, fn); tree( turnX(t2, angleX), leng * leng_scale2, leng_scale1, leng_scale2, leng_limit, angleZ, angleX, width, fn); } } tree(t, leng, leng_scale1, leng_scale2, leng_limit, angleZ, angleX, width, fn);
為了讓樹看起來比較濃密一些,我也調整了 leng
與 leng_limit
,完
成的效果如下:
當然,你還可以繼續在樹幹寬度等上頭下功夫,讓一開始的主幹夠粗,而每一次的支幹比主幹細一些,或者是我寫的 Customizable tree curve,增加更多方向的遞迴(當然,電腦就更辛苦一些了),我還在上頭加了點亂數,讓枝幹略呈不規則狀,讓遞迴結果看 起來更像是樹,這部份就留給你自己來嘗試了!