3D tree curve (Fractal again)


At the end of 3D turtle graphics, I leave an exercise for you to create your Customizable tree curve.

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);

3D tree curve (Fractal again)

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.

3D tree curve (Fractal again)

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.

3D tree curve (Fractal again)

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.

3D tree curve (Fractal again)

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.

3D tree curve (Fractal again)

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.