Tree Fractal

This program was my attempt to draw fractals on a VGA screen using Prof. Hunter Adam's VGA Library for the RaspberryPi Pico. The resources for the project include the C SDK User Guide, the RP2040 Datasheet and Prof. Hunter's website. I also used the Nature of Code series by Daniel Shiffman as a starting point for studying fractals.


About Fractals

According to Nature of Code's fractal page, the term fractal (from the Latin fractus, meaning “broken”) was coined by the mathematician Benoit Mandelbrot in 1975. In his seminal work “The Fractal Geometry of Nature,” he defines a fractal as “a rough or fragmented geometric shape that can be split into parts, each of which is (at least approximately) a reduced-size copy of the whole.” In short, a fractal is a shape which when divided into various parts, each part can represent the figure as a whole.


Recursion

The repeated application of a rule to successive results is known as recursion. One of the most famous applications for recursion is the calculation of factorical. The factorial of a natural number is defined as:

\begin{align} n! &= n \times (n-1)! \\ 0! &= 1 \end{align}

For instance, solving for 5! looks like: \begin{align} 5! &= 5 \times 4! \\ 5! &= 5 \times 4 \times 3! \\ 5! &= 5 \times 4 \times 3 \times 2! \\ 5! &= 5 \times 4 \times 3 \times 2 \times 1! \\ 5! &= 5 \times 4 \times 3 \times 2 \times 1 \\ 5! &= 120 \end{align}

Note: Recursions must always have a base case and that too at a reasonable depth, else it will cause a stack overflow error.


The complete code

/*
 * Parth Sarthi Sharma (pss242@cornell.edu)
 * Code based on examples from Raspberry Pi Foundation.
 * This code is an implementation of a tree fractal
 * on the Raspberry Pi Pico to draw a pattern. The pattern is developed
 * by recursively calling a function by reducing a parameter until a base condition is met.
 */
#include <stdio.h> //The standard C library
#include <math.h> //The standard math library
#include "pico/stdlib.h" //Standard library for Pico
#include "hardware/pio.h" //The hardware PIO library
#include "hardware/dma.h" //The hardware DMA library
#include "pico/time.h" //The pico time library
#include "hardware/gpio.h" //The hardware GPIO library
#include "pico/multicore.h" //The pico multicore library
#include "vga_graphics.h" //The graphics library

#define HEIGHT 480 //Height of the VGA screen
#define WIDTH 640 //Width of the VGA screen
#define PI 3.14159265358 //The value of PI

#define INC_ANGLE_PIN 2 //GPIO Pin to increase the angle
#define DEC_ANGLE_PIN 3 //GPIO Pin to decrease the angle

volatile float angle = PI / 4; //The default angle
volatile char resetFlag = 1; //Reset flag to reset the image once the angle changes

void drawTree1(float x1, float y1, float x2, float y2, float theta, float thetaOrig){ //Function to draw the right part of the tree
    drawLine(x1, y1, x2, y2, WHITE); //Draw a white line

    float length = sqrt(pow((x2 - x1), 2) + pow((y2 - y1), 2)); //Calculate the length of the line
    length *= 0.66; //Reduce the length of the new lines to be drawn

    if(length > 5){ //If the length is greater than 5
        float perp = length * sin(theta); //Calculate the perpendicular
        float base = length * cos(theta); //Calculate the base
        drawTree1(x2, y2, x2 + perp, y2 - base, theta - thetaOrig, thetaOrig); //Draw the left branches
        drawTree1(x2, y2, x2 + perp, y2 - base, theta + thetaOrig, thetaOrig); //Draw the right branches
    }
}
void drawTree2(float x1, float y1, float x2, float y2, float theta, float thetaOrig){ //Function to draw the left part of the tree
    drawLine(x1, y1, x2, y2, WHITE); //Draw a white line

    float length = sqrt(pow((x2 - x1), 2) + pow((y2 - y1), 2)); //Calculate the length of the line
    length *= 0.66; //Reduce the length of the new lines to be drawn

    if(length > 5){ //If the length is greater than 5
        float perp = length * sin(theta); //Calculate the perpendicular
        float base = length * cos(theta); //Calculate the base
        drawTree2(x2, y2, x2 - perp, y2 - base, theta - thetaOrig, thetaOrig); //Draw the left branches
        drawTree2(x2, y2, x2 - perp, y2 - base, theta + thetaOrig, thetaOrig); //Draw the right branches
    }
}

void changeAngle(uint gpio, uint32_t events) { //The GPIO callback function to change the angle
    if(gpio == INC_ANGLE_PIN){ //If the GPIO pin is INC_ANGLE_PIN
        if(angle < PI){ //If the angle is less than PI
            angle += 0.01; //Increment the angle by 0.01
        }
    }
    else{ //If the GPIO pin is DEC_ANGLE_PIN
        if(angle > 0){ //If the angle is greater than 0
            angle -= 0.01; //Decrement the angle by 0.01
        }
    }
    resetFlag = 1; //Set the reset flag
}

int main(){ //The program running on core 0
    stdio_init_all(); //Initialize all of the present standard stdio types that are linked into the binary
    initVGA(); //Initialize the VGA screen and functions

    gpio_set_irq_enabled_with_callback(DEC_ANGLE_PIN, GPIO_IRQ_EDGE_FALL, true, &changeAngle); //Enable interrupts for the GPIO pin
    gpio_set_irq_enabled_with_callback(INC_ANGLE_PIN, GPIO_IRQ_EDGE_FALL, true, &changeAngle); //Enable interrupts for the GPIO pin

    while(1){ //While eternity
        while(!resetFlag); //Sit here until the reset flag is set
        fillRect(100, 200, 380, 280, BLACK); //Clear the tree
        drawTree1(WIDTH / 2, 480, WIDTH / 2, 380, angle, angle); //Draw the right tree
        drawTree2(WIDTH / 2, 480, WIDTH / 2, 380, angle, angle); //Draw the left tree
        resetFlag = 0; //Clear the reset flag
    }
}


The output

The generated fractals

CMakeLists.txt

cmake_minimum_required(VERSION 3.13)

include(pico_sdk_import.cmake)

project(TreeFractal-project)

pico_sdk_init()

add_executable(TreeFractal)

pico_enable_stdio_usb(TreeFractal 1)
pico_enable_stdio_uart(TreeFractal 1)

pico_generate_pio_header(TreeFractal ${CMAKE_CURRENT_LIST_DIR}/hsync.pio)
pico_generate_pio_header(TreeFractal ${CMAKE_CURRENT_LIST_DIR}/vsync.pio)
pico_generate_pio_header(TreeFractal ${CMAKE_CURRENT_LIST_DIR}/rgb.pio)

target_sources(TreeFractal PRIVATE TreeFractal.c vga_graphics.c)

target_link_libraries(TreeFractal PRIVATE pico_stdlib hardware_pio hardware_dma hardware_adc hardware_irq pico_time pico_multicore)

pico_add_extra_outputs(TreeFractal)