Draw Recursive Tree With Lines and Circles

​You can only get so far into mathematical art without mentioning fractals! As a review, a fractal is an image for which the same structre is evident at any level of resolution.

Perhaps the simplest fractals are created by the process of recursively drawing a particular pattern. Let's examine some of these deceptively simple-looking images. You most likely won't have time in this class to code up your own recursive drawing, but you're encouraged to choose one of the patterns below and try changing a few parameters. Be warned: the greater the recursive depth, the longer the code will take to execute!

The basic structure for each of these programs is very similar, so let's go over that first. You begin with a function that will compute any global variables and compute and any non-iterative components of the drawing. Let's call this mainFunction. In mainFunction, you call a second function- a sub-function. Let's call it drawingFunction and color it blue in the example program below. drawingFunction does the job of drawing your recursive structure, and takes as an input the number of recursions you want. Then, importantly, drawingFunctioncalls itself. The only difference is that it calls itself with one less iteration. Thus, with a simple call to mainFunction you can draw an entire fractal structure!

Note that the 'code' you see here is not really code at all. This is what we call pseudo-code. It shows us the structure of the program we want to write, but will not run because the details haven't been filled in.

function mainFunction (totalNumberOfIterations)
set variables
maybe draw something
drawingFunction(totalNumberOfIterations)

     functiondrawingFunction(remainingIterations)
draw stuff
drawingFunction(remainingIterations-1)
end

end

Fractal Triangle

For this example, we again harness the power of rotation! We want to draw a triangle centered around the origin [0,0]. Since we know that the three points of an equilateral triangle are separated by 120 degrees, we can simply rotate our first point to get our second and third points. Then we simply draw lines between them using the plot command.

Okay, so now let's make this a fractal. We want to rotate the triangle with each iteration, and also shrink it slightly. So our draw subfunction will calculate the three points of the current triangle, based on the current length and angle parameters, and draw the triangle on top of the existing triangles. We should have something that looks like this:

The inputs for fractalTriangle are described in the code below. Take a look, and let me know if you have any questions!

function fractalTriangle(r,angleIncrement,lengthDecrement,transparency)
% Draw a fractal triangle, with the following inputs:
%   r = int; number of iterations
%   angleIncrement = number; amount the triangle rotates each iteration
%   lengthDecrement = number; amount triangle shrinks each iteration
%   transparency = number between 0 and 1; transparency of each triangle

    figure
drawTriangles(1,0,angleIncrement,r,r,lengthDecrement,transparency)
axis equal
axis off

    function drawTriangles(len,angle,angInc,iterations,r,...
lengthDecrement,transparency)
% Draws the triangle for each iteration and calls itself. Inputs:
%   len = number; length of a side of the triangle
%   angle = number; amount the triangle rotates relative to previous
%           iteration
%   angleIncrement = number; amount the triangle rotates each iteration
%   iterations = int; remaining number of iterations
%   r = int; total number of iterations
%   lengthDecrement = number; amount len changes each iteration
%   transparency = number between 0 and 1; transparency of triangle

        pt1x=len*cosd(angle);
pt1y=len*sind(angle);
rot=[cosd(120) -sind(120);sind(120) cosd(120)];
pt2=rot*[pt1x;pt1y];
pt3=rot*pt2;
c=[0 0 0];
p1=plot([pt1x,pt2(1),pt3(1),pt1x],[pt1y,pt2(2),pt3(2),pt1y],'Color',c);
p1.Color(4)=transparency;
hold on

        if iterations-1>0
drawTriangles(len-len*lengthDecrement,angle+angInc,...
angInc,iterations-1,r,lengthDecrement,transparency);
end
end
end

Fractal Spiral

We'll continue with a simple example: drawing a spiral. With each iteration, we will draw a circle, and we'll simply change the angle, position, and size of the circle with each iteration. Here's how we want it to look:

function fractalCircleSimple(r)
% This function generates a fractal spiral with r iterations

    figure

    % call the drawing function
drawCircles([r/2,r/2],r,90,r)

    axis off
axis equal

    function drawCircles(pt,iterations,initAngle,r)
% This function draws the current circle in the spiral and calls itself
% to draw the next circle in the spiral, until all iterations have been
% drawn. The inputs are:
%   pt = [x,y]; the bottom left hand corner of the circle to be drawn
%   iterations = int; remaining iterations
%   initiAngle = number; the current angle of the spiral
%   r = int; the total number of iterations

        len=iterations;

        % use trigonometry to determine the x and y components
x2=len*cosd(initAngle)+pt(1);
y2=len*sind(initAngle)+pt(2);

        % draw the circle
r1=rectangle('Position',[x2,y2, len, len],...
'Curvature',1,'FaceColor',[iterations/r, 1-iterations/r 1],...
'EdgeColor',[0 0 0]);
r1.FaceColor(4)=0.5;

        % draw the next circle if more iterations remain
if iterations-1>0
drawCircles([x2,y2],iterations-1,initAngle+20,r);
end
end
end

Here's what it looks like with many iterations! Try changing the angle and see how that affects the spiral you create!

A note on colors:

You'll notice that the color of the spiral changes as the circles get smaller. This is controlled by the 'FaceColor' feature of the rectangle function. In Matlab, you set color using RGB values that vary from 0 to 1. In other words, [1 1 1] is white and [0 0 0] is black. So, if we set one of the colors to be (current iteration)/(total iterations), we'll get a number that conveniently varies between zero and one!

Also note that both 'FaceColor' and 'EdgeColor' can be set to 'none'. Try setting the 'EdgeColor' equal to 'none' and see how that changes your spiral!

Finally, note that there is a 'hidden' fourth aspect to the color. This is called the alpha value and determines the transparency of what you're plotting. We set this with the line r1.FaceColor(4)=0.5; and, as with the other color parameters, it can vary between 0 and 1.

A note on shapes:

It may seem odd that we're using the rectangle function to generate circles, but that's just the way Matlab does it! You can transition smoothly from a circle to a rectangle by altering the 'Curvature' parameter in rectangle. This varies between 0 and 1, with 0 giving you a rectangle, 1 giving you a circle (or ellipse) and anything in-between giving you a rounded rectangle. Do you like your spiral better with circles or rectangles?

Adding more circles!

Well, spirals are nice, but let's add some complexity to our drawing now! How about we add a second circle coming off of that first circle at a different angle? Then we'll have a drawing that looks something like:

Notice that, as the number of iterations increase, you can start to see some structure emerging!

function fractalCircles(r)
figure
drawCircles([0,0],r,90,r)
axis off
axis equal

    function drawCircles(pt,iterations,initAngle,r)
len=iterations;
r1=rectangle('Position',[pt(1),pt(2) iterations, iterations],...
'Curvature',1,'FaceColor',[0.2, 0, iterations/r],'EdgeColor',...
[1 1 1]);
r1.FaceColor(4)=iterations/r;
x2=len*cosd(initAngle)+pt(1);
y2=len*sind(initAngle)+pt(2);
x3=-len*cosd(initAngle)+pt(1);
y3=-len*sind(initAngle)+pt(2);
if iterations-1>0
drawCircles([x2,y2],iterations-1,initAngle+10,r);
drawCircles([x3,y3],iterations-1,initAngle+30,r);
end
end
end

Basic Fractal Tree

We'll continue with another 'simple' example: a branching tree. The recurring pattern is that every time you reach the end of a 'branch' in the tree, two more branches will fork out at equal angles. We want it to look something like the image below.

You'll want to copy and paste the following code into Matlab and try running it. Play around with the angle, and the number of iterations. Notice that the color is currently defined iteratively; that is, the smallest the branch, the more pink it is. Can you make the branches be green instead? What about other colors?

function fractalTreeBasic(r,angle,fade)
% This function draws a fractal tree with the following inputs:
% r = int; number of iterations
% angle = number; sideways angle of each of the two branches
% fade = 1 or 0; set value to 1 if you want branches to be transparent
totalIterations=r+1;
figure('Position',[10,10,700,700]);

    % Draw the tree trunk
c1=[0 0 0];
len1=3*1.2^totalIterations;
w1=r^0.6;
plot([0 0],[-len1,0],'LineWidth',w1,'Color',c1)
xlim([-2*len1,2*len1]);
ylim([-2*len1,2*len1]);
axis off
hold on

    % Begin the iterative process of drawing branches
drawBranches(90,[0,0],totalIterations-1,angle,totalIterations,fade);

    function drawBranches(initAngle,pt,iterations,angle,totalIterations,fade)
% This sub-function draws the branches, recursively, with inputs:
% initAngle = number; the current angle of the 'trunk'
% pt = [x,y]; the endpoint of the 'trunk'
% iterations = number; number of times to repeat
% angle = number; amount each branch is splayed sideways
% r = int; total number of iterations (from main function)
% fade = 1 or 0; set to 1 to get transparent branches

                 % The length of the current branch
len=1.2^(iterations);
x1=pt(1);
y1=pt(2);
ang1=initAngle+angle; % left branch angle
ang2=initAngle-angle; % right branch angle
% Use trigonometry to find the new branch endpoints
y2=len*sind(ang1)+y1;
y3=len*sind(ang2)+y1;
x2=len*cosd(ang1)+x1;
x3=len*cosd(ang2)+x1;

        % Set the color based on the current iteration
c2=[1-iterations/(totalIterations) 0 1-iterations/(totalIterations)];
% Set the width of the branch based on the current iteration
w=iterations^0.6;
% Draw a line between the trunk and the left branch endpoint
p1=plot([x1,x2],[y1,y2],'LineWidth',w,'Color',c2);
% Draw a line between the trunk and the right branch endpoint
p2=plot([x1,x3],[y1,y3],'LineWidth',w,'Color',c2);

        % Make the branches transparent if fade is set to 1
if fade==1
c4=1-(totalIterations-iterations)/totalIterations;
p1.Color(4)=c4;
p2.Color(4)=c4;
end

        % If you're not done iterating, draw branches at the end of each
% of the two branches you just created.
if iterations-1>0
drawBranches(ang1,[x2,y2],iterations-1,angle,totalIterations,fade);
drawBranches(ang2,[x3,y3],iterations-1,angle,totalIterations,fade);
end
end
end

This code also has the 'fade' option. In other words, if you set fade to 1 then the smaller a branch is, the more transparent it will be. Here are two versions of the final fractal tree, one with the fade option and one without:

Four-Branched Tree

Okay, now let's take the fractal tree one step further. Instead of having two branches at every branch point, let's have 4. Thus, the first few iterations should look like this:

function fractalTreeFourBranches(r)

    figure('Position',[50,50,700,700]);
drawBranch([0,0],90,r,r);
axis equal
axis off

    function drawBranch(pt,angle,remainingIterations,r)
width=5*(remainingIterations/r);
len1=5*remainingIterations;
ang1=angle+15;
ang2=angle+7;
ang3=angle-7;
ang4=angle-15;
y1=len1*sind(ang1)+pt(2);
x1=len1*cosd(ang1)+pt(1);
y2=len1*sind(ang2)+pt(2);
x2=len1*cosd(ang2)+pt(1);
y3=len1*sind(ang3)+pt(2);
x3=len1*cosd(ang3)+pt(1);
y4=len1*sind(ang4)+pt(2);
x4=len1*cosd(ang4)+pt(1);

        ang22=ang2-5;
ang33=ang3+5;
y22=len1/2*sind(ang22)+y2;
x22=len1/2*cosd(ang22)+x2;
y33=len1/2*sind(ang33)+y3;
x33=len1/2*cosd(ang33)+x3;
c=[0 1-(remainingIterations/r) 1-0.5*(remainingIterations/r)];

        p1=plot([pt(1),x1],[pt(2),y1],'LineWidth',width,'Color',c);
hold on
p2=plot([pt(1),x2],[pt(2),y2],'LineWidth',width,'Color',c);
p3=plot([pt(1),x3],[pt(2),y3],'LineWidth',width,'Color',c);
p4=plot([pt(1),x4],[pt(2),y4],'LineWidth',width,'Color',c);
p5=plot([x2,x22],[y2,y22],'LineWidth',width,'Color',c);
p6=plot([x3,x33],[y3,y33],'LineWidth',width,'Color',c);

        p1.Color(4)=0.8;
p2.Color(4)=0.8;
p3.Color(4)=0.8;
p4.Color(4)=0.8;
p5.Color(4)=0.8;
p6.Color(4)=0.8;

        if remainingIterations-1>0
drawBranch([x1,y1],ang1,remainingIterations-1,r);
drawBranch([x22,y22],ang22,remainingIterations-1,r);
drawBranch([x33,y33],ang33,remainingIterations-1,r);
drawBranch([x4,y4],ang4,remainingIterations-1,r);
end
end
end

Curved Fractal Tree

This example is conceptually similar, but visually quite different. Here, we utilize a while loop to draw the curving stem. After drawing each segment of the stem, we call our drawing function to draw another stem, at right angles to the existing stem.

function fractalTreeCurved(r)
% This function draws a curved fractal tree with the following inputs:
%     r = int; number of iterations

    figure('Position',[50,50,700,700]);
drawCurve([0,0],90,r,30,r,r,r);

    function drawCurve(pt,initAngle,numBends,angInc,w,l,r)
% Draws the curves of the curved tree with these inputs:
%   pt = [x,y]; starting point of the current curve
%   intitAngle = number; initial angle of the current curve
%   numBends = int; number of segments to draw in the curve
%   angInc = number; incremental angle change with each iteration
%   w = number; width of the first line
%   l = number; length of the first line
%   r = int; total number of iterations

        index=numBends;
x1=pt(1);
y1=pt(2);
ang1=initAngle;
width=w;
len1=l;
while index>0

            y2=len1*sind(ang1)+y1;
x2=len1*cosd(ang1)+x1;
len1=len1-len1/7;
if len1<.05
break
end
c=[0 1-numBends/r 0];
plot([x1,x2],[y1,y2],'LineWidth',width,'Color',c);
axis off
hold on
width=width-width/8;
x1=x2;
y1=y2;
ang1=ang1+angInc;
index=index-1;
drawCurve([x2,y2],ang1-90-angInc,index-2,angInc,width/2,len1/1.5,r)
end
end
end

Fractal Fern

This example is meant to show you one of the really neat, remarkably realistic-looking things you can create with recursive drawing! We probably won't have the time to go over this in class, but of course feel free to play around with the code and let me know if you have any questions!

Also, please note that although it looks extremely similar, this is not a Barnsley Fern. The Barnsley Fern is also a fractal, but it is generated by a very different process. I encourage you to check it out if fractal leaves intrigue you!

Let's start by taking a look at a fern leaf.

Note the fractal structure of the leaf. That is, each frond of the fern itself looks like a fern! Okay, so in our drawing function we want to recursively draw the fern. Sounds simple enough so far, right? .

Well there's a couple of factors that make it more complicated. The orientation of the fronds alternate right and left, and the curvature of those fronds depends on the direction. So we'll have to incorporate a variable that changes direction with every iteration.

function fractalFern(r)
% Draws a fractal fern with r iterations

    figure
drawFern([0,0],r,1,90,10,r)
axis off

    function drawFern(pt,numBranches,dir,initAng,length1,r)
% Draws each frond of the fern, taking inputs:
%   pt = [x,y]; the endpoint of the current stem
%   numBranches = int; number of branches to draw on the current frond
%   dir = 1 or 0; determines the curvature of the frond
%   initAng = number; the initial angle of the stem
%   length1 = number; length of the first segment of the stem
%   r = int; total number of stem segments

        index=numBranches;
ang=initAng;
x2=pt(1);
y2=pt(2);
next=1;
% This while loop draws a new frond after drawing each stem
% segment, alternating left and right
while index>0
len=length1*.9^(numBranches-index);

            % This simply stops the drawing if the length of the line
% becomes too small
if len<.05
return
end

            % next determines the direction of the next frond; this code
% switches it every iteration
if next==1
next=0;
nextAng=ang-50;
else
next=1;
nextAng=ang+50;
end

            % dir controls the direction of curvature of the stem
if dir==1
ang=ang-2;
else
ang=ang+2;
end

            % calculate the endpoint of the current stem segment
xdraw=len*cosd(ang)+x2;
ydraw=len*sind(ang)+y2;

            % determine the color of the current stem segment
c=[0, 1-(.7*(index/r)), 0];

            % draw the current segment of the stem
plot([x2,xdraw],[y2,ydraw],'Color',c,'LineWidth',len/2);
hold on
x2=xdraw;
y2=ydraw;

            % draw the next frond. Note that the number of branches
% to draw on each successive frond decreases by 1
drawFern([x2,y2],index-1,next,nextAng,len/2.5,r);
index=index-1;
end
end
end

chaviraweaught.blogspot.com

Source: http://bricault.mit.edu/recursive-drawing

Belum ada Komentar untuk "Draw Recursive Tree With Lines and Circles"

Posting Komentar

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel