top of page

Recursive Drawing

Overview

 

You can only get so far into mathematical art without mentioning fractals! As a review, a fractal is an image for which the same structure 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. 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. drawingFunction does the job of drawing your recursive structure, and takes as an input the number of recursions you want. Then, importantly, drawingFunction calls 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)

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

Spinning 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:

triangle2.png
tri2.png

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('Position',[50,50,900,900],'Color',[1,1,1]);
    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,'LineWidth',3);
        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

tri4.png
tri5.png

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.

tree1.png
iterationsTree2.png

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,900,900],'Color',[1,1,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;
    
    % 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
              
        % 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

Fractal Tree with Four Branches

 

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:

fractalTree2_iterations.png
fractalTree2.png

function fractalTreeFourBranches(r)
% This function draws a fractal tree with the following inputs:
    % r = int; number of iterations
    fig1=figure('Position',[50,50,1200,900],'Color',[1 1 1 1]);
    drawBranch([0,0],90,r,r);
    axis equal
    axis off 
    
    function drawBranch(pt,angle,remainingIterations,r)
    % This sub-function draws the branches, recursively, with inputs:
        % angle = number; the current angle of the 'trunk'
        % pt = [x,y]; the endpoint of the 'trunk'
        % remainingIterations = number; number of times to repeat 
        % r = total number of iterations
        
        % Scale the width and length by the number of iterations
        width=5*(remainingIterations/r);
        len1=5*remainingIterations;
        ang1=angle+15;
        ang2=angle+7;
        ang3=angle-7;
        ang4=angle-15;
        
        % Use trig to figure out the new line endpoints
        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;
        
        % Set the RGB color scaled by the current iteration
        c=[0 1-(remainingIterations/r) 1-0.5*(remainingIterations/r)];

        % Plot the four branches
        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);
        
        % Set the branches to be slightly transparent
        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;
        
        % Iterate; draw a new tree at each of the four branches
        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

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!

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.

fernPhoto2.png
ferns1.png

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

    % Initialize the figure
    fig1=figure('Position',[50,50,750,900],'Color',[1 1 1]);
    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

bottom of page