Python Turtle Meets Fractal Art: A Recursive Journey

In this tutorial, we delve into the beauty and complexity of recursion. We use Python's Turtle graphics to create a Sierpinski Triangle, demonstrating recursion's power to replicate nature's intricate patterns in code.

Python Turtle Meets Fractal Art: A Recursive Journey
Stacked potential: Every step in learning Python with us is a step upward. 🐢

Introduction

Welcome to this tutorial on recursion, where the elegance of mathematics collides with the beauty of visual programming.

Recursion is a fundamental concept in mathematics and computer science. It is a technique for solving complex problems by successively reducing them to smaller problems until reaching a size that has a trivial solution. Then, we combine the solutions to those minor problems to find the solutions to the bigger ones.

But it's also a gateway to a world of intricate designs and complex patterns emerging from simple rules. In this article, using Python's Turtle graphics, we explore the fascinating world of fractals, infinite patterns that captivate the eye and stimulate the mind.

Fractals, with their self-similar patterns, are found everywhere in nature, from the branching of trees to the intricate designs of snowflakes. By harnessing the power of recursion, we can replicate the appearance of these natural phenomena in our code. In this article, we will use the Sierpinski Triangle, a classic fractal that demonstrates the elegance and simplicity of recursive programming.

If you are interested in recreating other fractals in Python, please do not hesitate to sign up and comment.

We invite you to experiment with the code, tweak the parameters, and observe how slight changes can lead to dramatically different outcomes.


Understanding Recursion

Recursion occurs when a function calls itself to find the value to return to its caller. Recursion allows us to tackle complex problems by breaking them down into more straightforward, manageable instances of the same problem.

The Core of Recursion

Recursion is built on two main pillars: the base case and the recursive case.

  • Base Case: This is the condition under which the recursion stops. It's the most straightforward instance of the problem, which can be solved directly without further recursion.
  • Recursive Case: This is where the function calls itself, each time with a slightly simpler version of the original problem, gradually moving towards the base case.

Why Use Recursion?

Recursion simplifies the coding of problems that can be divided into similar subproblems. It's particularly adept at handling tasks where the solution involves repeating a process with a diminishing input, such as navigating through directories in a file system or, as we'll explore, drawing fractal patterns.

Recursion in Action

Imagine you're tasked with organizing a stack of books into separate piles. You take one book off the top and organize the smaller remaining stack. This process repeats until you're left with a single book, which is trivial to "organize." Here, your base case is when you have only one book left, and the recursive case is each step where you remove a book and organize the smaller stack.

Recursion is a cornerstone of algorithmic thinking, offering a unique approach to problem-solving where solutions build upon themselves.

In this article, we'll use Python Turtle to bring recursion to life, painting patterns that exemplify the harmony between mathematics and nature.


Calculating Factorials Rcursively

The factorial of a number n, represented as n!, is the product of all positive integers up to n. For instance, the factorial of 5 (5!) is 5 x 4 x 3 x 2 x 1 = 120.

This operation lends itself perfectly to a recursive implementation, where the solution to a problem depends on solutions of the factorial of the predecessor.

The Factorial Function

Here's a simple Python function that computes the factorial of a number using recursion:

Python 3

def factorial(n):
    # Base case: The factorial of 0 is 1
    if n == 0:
        return 1
    # Recursive case: n * factorial of (n-1)
    else:
        return n * factorial(n-1)
        


Breaking Down the Function

  • Base Case: This function defines its base case as n == 0. When n is 0, it returns 1 because the factorial of 0 is defined to be 1. This condition is crucial as it prevents the function from calling itself indefinitely.
  • Recursive Case: The function then handles the recursive case by calling itself with n-1. This step effectively reduces the problem's size with each call, moving closer to the base case.

Understanding Recursion Through Factorials

To compute factorial(5), the function calls itself repeatedly:

  • factorial(5) calls factorial(4)
  • factorial(4) calls factorial(3)
  • ...
  • until factorial(0) returns 1.

At each step, the function multiplies n by the factorial(n-1) result, building up the final result as the calls resolve back up the chain.

This example demonstrates the power of recursion, and if we call it with a big enough number, it will also show us one of its main pitfalls. Since each call adds a stack frame, it will eventually result in a Stack Overflow error.

We will discuss how to avoid this in a follow-up tutorial.


Drawing the Sierpinski Triangle: A Step-by-Step Guide

Drawing a Serpinksky Triangle with Python and Turtle

The Sierpinski Triangle, a classic example of a fractal pattern, is created through a simple recursive process. In this guide, we'll learn how to draw one using Python's Turtle graphics and recursive functions.

Setting the Stage

First, we initialize our drawing canvas and the Turtle object:

Python 3

import turtle

# Set up the drawing canvas
turtle.setup(800, 600)
window = turtle.Screen()
window.bgcolor("white")
window.title("Sierpinski Triangle")

# Creating the Turtle to draw the triangle
sierpinski = turtle.Turtle()
sierpinski.speed('fastest')
        

Defining the Recursive Function

Unlike the straightforward, linear recursion seen in calculating a factorial, drawing the Sierpinski Triangle involves a function that calls itself multiple times to create a complex, self-similar pattern:

Python 3

def get_mid(p1, p2):
    return ((p1[0]+p2[0]) / 2, (p1[1]+p2[1]) / 2)

def draw_triangle(t, vertices, color):
    t.fillcolor(color)
    t.up()
    t.goto(vertices[0][0], vertices[0][1])
    t.down()
    t.begin_fill()
    t.goto(vertices[1][0], vertices[1][1])
    t.goto(vertices[2][0], vertices[2][1])
    t.goto(vertices[0][0], vertices[0][1])
    t.end_fill()

def draw_sierpinski(t, vertices, depth):
    colormap = ['blue', 'red', 'green', 'white', 'yellow', 'violet', 'orange']
    color = depth % len(colormap)
    draw_triangle(t, vertices, colormap[color])
    if depth > 0:
        draw_sierpinski(t, [vertices[0],
                            get_mid(vertices[0], vertices[1]),
                            get_mid(vertices[0], vertices[2])],
                       depth - 1)
        draw_sierpinski(t, [vertices[1],
                            get_mid(vertices[0], vertices[1]),
                            get_mid(vertices[1], vertices[2])],
                       depth - 1)
        draw_sierpinski(t, [vertices[2],
                            get_mid(vertices[2], vertices[1]),
                            get_mid(vertices[0], vertices[2])],
                       depth - 1)

points = [[-200, -100], [0, 200], [200, -100]]  # Define the main triangle points
draw_sierpinski(sierpinski, points, 5)  # Change the depth as needed


turtle.done()          
        

The Recursive Process Explained

  • First Step: The function begins by drawing the external triangle that forms the base of our fractal pattern.
  • Recursive Calls: It then makes three recursive calls, each targeting one of the triangle's corners. These calls draw a smaller triangle within each corner, creating a new pattern layer.
  • Halving the Side: With each recursive call, the side of the triangle being worked on is halved, focusing the function's effort on a smaller area. This halving continues until the specified depth is reached, with each layer adding to the fractal's complexity.

Visualizing the Recursion

Each recursive call to draw_sierpinski reduces the triangle side size in half and shifts the focus to a different part of the triangle. This process creates a self-similar pattern where each part echoes the whole, a hallmark of fractals. The use of color helps illustrate the depth of recursion, with each level of the triangle sporting a different hue from the colormap.

To handle any depth value with a list of only seven colors, we use a common programming trick: the modulo operation maps all numbers to one of the existing colors.

This approach demystifies recursion and showcases its potential to create complex and beautiful designs from simple operations.


Conclusion

This tutorial shows how recursion can unfold simple logic into complex beauty, exemplified by the Sierpinski Triangle. This venture into fractal art with Python demonstrates recursion's potential and opens doors to endless creative exploration.

The key to writing good recursive functions is to reduce the problem's size in each call, moving us closer to the base case with each call.

Keep experimenting, keep learning, and let your curiosity lead you to discover more of what Python and recursion offer.

We hope you enjoyed this journey into the world of recursion and fractal art with Python Turtle. If you're curious about other patterns or have ideas you'd like us to explore, please subscribe and leave a comment below.


Addendum: A Special Note for Our Readers

I decided to delay the introduction of subscriptions, you can read the full story here.

In the meantime, I decided to accept donations.

If you can afford it, please consider donating:

Every donation helps me offset the running costs of the site and an unexpected tax bill. Any amount is greatly appreciated.

Also, if you are looking to buy some Swag, please visit I invite you to visit the TuringTacoTales Store on Redbubble.

Take a look, maybe you can find something you like: