Recursivity a Poem on Drawing Trees
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
function drawTriangles(len,angle,angInc,iterations,r,... pt1x=len*cosd(angle); if iterations-1>0 figure
drawTriangles(1,0,angleIncrement,r,r,lengthDecrement,transparency)
axis equal
axis off
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
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
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
% call the drawing function axis off function drawCircles(pt,iterations,initAngle,r) len=iterations; % use trigonometry to determine the x and y components figure
drawCircles([r/2,r/2],r,90,r)
axis equal
% 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
x2=len*cosd(initAngle)+pt(1);
y2=len*sind(initAngle)+pt(2);
% draw the next circle if more iterations remain % 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;
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 drawCircles(pt,iterations,initAngle,r)function fractalCircles(r)
figure
drawCircles([0,0],r,90,r)
axis off
axis equal
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]);
% Begin the iterative process of drawing branches function drawBranches(initAngle,pt,iterations,angle,totalIterations,fade) % The length of the current branch % Set the color based on the current iteration % Make the branches transparent if fade is set to 1 % 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
drawBranches(90,[0,0],totalIterations-1,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
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;
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);
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)
function drawBranch(pt,angle,remainingIterations,r) ang22=ang2-5; figure('Position',[50,50,700,700]);
drawBranch([0,0],90,r,r);
axis equal
axis off
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);
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.Color(4)=0.8; if remainingIterations-1>0 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);
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;
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.
figure('Position',[50,50,700,700]);function fractalTreeCurved(r)
% This function draws a curved fractal tree with the following inputs:
% r = int; number of iterations
drawCurve([0,0],90,r,30,r,r,r);
index=numBends; y2=len1*sind(ang1)+y1; 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
x1=pt(1);
y1=pt(2);
ang1=initAngle;
width=w;
len1=l;
while index>0
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
function drawFern(pt,numBranches,dir,initAng,length1,r) index=numBranches; % This simply stops the drawing if the length of the line figure
drawFern([0,0],r,1,90,10,r)
axis off
% 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
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);
% becomes too small
if len<.05
return
end
% dir controls the direction of curvature of the stem % calculate the endpoint of the current stem segment % determine the color of the current stem segment % draw the current segment of the stem % draw the next frond. Note that the number of branches % 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
if dir==1
ang=ang-2;
else
ang=ang+2;
end
xdraw=len*cosd(ang)+x2;
ydraw=len*sind(ang)+y2;
c=[0, 1-(.7*(index/r)), 0];
plot([x2,xdraw],[y2,ydraw],'Color',c,'LineWidth',len/2);
hold on
x2=xdraw;
y2=ydraw;
% 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
mansoursumakes1960.blogspot.com
Source: http://bricault.mit.edu/recursive-drawing