At the end of 3D turtle graphics, I leave an exercise for you to create your Customizable tree curve.
2D tree curve
Before you can implement a 3D tree curve, you should have the ability to create a 2D tree curve. Before you can do a 2D tree curve, you should be able to draw a “Y”.
// omitted...require 3D turtle graphics 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);
If you want to grow more “Y” from the branches of the original “Y”,
you can invoke the tree
module recursively. The
recursion needs a stop condition, and we use the length of branches
here.
// omitted...require 3D turtle graphics 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);
We have a 2D version of the recursive tree.
However, we draw some branches repeatedly except the endings. That is, the following code repeats branches.
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
);
Deleting the code will lose some ending branches; however, we have clean code.
// omitted...require 3D turtle graphics 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 tree curve
Evolving the above 2D tree into a 3D tree is easy. If we turn
around the z-axis before invoking the tree
module and
one of the angles is 0 degree, what will happen?
// omitted...require 3D turtle graphics 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);
The tree looks weird. It grows toward the upper and the left.
How about changing the second turnZ
to turnX
and the angle2
value to 127?
// omitted...require 3D turtle graphics 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);
Wow, we have a cute 3D tree.
127 is a prime number. That's why I choose it. You can choose any
angle which doesn't divide 360 so that the branches doesn't appear
at the same angle. We can change leng_scale
to leng_scale1
and leng_scale2
; choose suitable values for them; set
appropriate angles for angle1
and angle2
.
After that, we'll have a pretty 3D tree. For clearness, we rename angle1
and angle2
to angleZ
and angleX
respectively.
// omitted...require 3D turtle graphics 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);
To make branches intensive, I also choose appropriate values for leng
and leng_limit
. The tree is below.
If you want, you can give efforts in the width of branch lines, such as a thickest main trunk and thinner branches while growing. You can add more recursions, such as I did in Customizable tree curve. I even make a little random to the branches. It makes the tree curve more like a real tree. Try to realize all the above by yourself.