**A dive into geometry, recurring algorithms and trianglesâ€¦ a number of them!**

**Fractals** are infinitely complex patterns which might be self-similar across different scales. For instance, a tree trunk splits into smaller branches. These in turn split into even smaller branches, and so forth.

By generating fractals programmatically, we will turn easy shapes into complicated repeating patterns.

In this text I might be exploring how we will construct impressive fractals in Python using some basic A-Level geometry and a bit of programming know-how.

Fractals play a very important role in data science. For instance, in fractal evaluation the fractal characteristics of datasets are evaluated to assist understand the structure of underlying processes. As well as, the recurring algorithm on the centre of fractal generation might be applied to a big selection of knowledge problems, from the binary search algorithm to recurrent neural networks.

I would like to put in writing a program that may draw an equilateral triangle. On all sides of the triangle it must then find a way to attract a rather smaller outward facing triangle. It should find a way to repeat this process as persistently as I would love, hopefully creating some interesting patterns.

I might be representing a picture as a two dimensional array of pixels. Each cell within the pixel array will represent the color (RGB) of that pixel.

To realize this, we will use the libraries **NumPy** to generate the pixel array and **Pillow** to show it into a picture that we will save.

Now itâ€™s time to get coding!

Firstly, I would like a function that may take two sets of coordinates and draw a line between them.

The code below works by interpolating between two points, adding recent pixels to the pixel array with each step. You may consider this process like colouring in a line pixel by pixel.

*I even have used the continuation character â€˜â€™ in each code snippet to assist fit some longer lines of code in.*

`import numpy as np`

from PIL import Image

import mathdef plot_line(from_coordinates, to_coordinates, thickness, color, pixels):

# Work out the boundaries of our pixel array

max_x_coordinate = len(pixels[0])

max_y_coordinate = len(pixels)

# The distances along the x and y axis between the two points

horizontal_distance = to_coordinates[1] - from_coordinates[1]

vertical_distance = to_coordinates[0] - from_coordinates[0]

# The full distance between the 2 points

distance = math.sqrt((to_coordinates[1] - from_coordinates[1])**2

+ (to_coordinates[0] - from_coordinates[0])**2)

# How far we'll step forwards every time we color in a recent pixel

horizontal_step = horizontal_distance/distance

vertical_step = vertical_distance/distance

# At this point, we enter the loop to attract the road in our pixel array

# Each iteration of the loop will add a recent point along our line

for i in range(round(distance)):

# These 2 coordinates are those at the middle of our line

current_x_coordinate = round(from_coordinates[1] + (horizontal_step*i))

current_y_coordinate = round(from_coordinates[0] + (vertical_step*i))

# Once we've got the coordinates of our point,

# we draw across the coordinates of size 'thickness'

for x in range (-thickness, thickness):

for y in range (-thickness, thickness):

x_value = current_x_coordinate + x

y_value = current_y_coordinate + y

if (x_value > 0 and x_value < max_x_coordinate and

y_value > 0 and y_value < max_y_coordinate):

pixels[y_value][x_value] = color

# Define the dimensions of our image

pixels = np.zeros( (500,500,3), dtype=np.uint8 )

# Draw a line

plot_line([0,0], [499,499], 1, [255,200,0], pixels)

# Turn our pixel array right into a real picture

img = Image.fromarray(pixels)

# Show our picture, and put it aside

img.show()

img.save('Line.png')

Now I even have a function which might draw a line between two points, itâ€™s time to attract the primary equilateral triangle.

Given the centre point and side length of a triangle, we will work out the peak using the handy formula: **h = Â½(âˆš3a)**.

Now using that height, centre point and side length, I can work out where each corner of the triangle needs to be. Using the *plot_line* function I made earlier, I can draw a line between each corner.

`def draw_triangle(center, side_length, thickness, color, pixels):`# The peak of an equilateral triangle is, h = Â½(âˆš3a)

# where 'a' is the side length

triangle_height = round(side_length * math.sqrt(3)/2)

# The highest corner

top = [center[0] - triangle_height/2, center[1]]

# Bottom left corner

bottom_left = [center[0] + triangle_height/2, center[1] - side_length/2]

# Bottom right corner

bottom_right = [center[0] + triangle_height/2, center[1] + side_length/2]

# Draw a line between each corner to finish the triangle

plot_line(top, bottom_left, thickness, color, pixels)

plot_line(top, bottom_right, thickness, color, pixels)

plot_line(bottom_left, bottom_right, thickness, color, pixels)

The stage is about. Almost the whole lot I would like is able to create my first fractal in Python. How exciting!

Nonetheless, this final step is arguably the trickiest. I would like our triangle function to call itself for all sides it has. For this to work, I would like to find a way to calculate the centre point of every of the brand new smaller triangles, and to rotate them appropriately in order that they are pointing perpendicular to the side they’re attached to.

By subtracting the offset of our centre point from the coordinates I want to rotate, after which applying the formula to rotate a pair of coordinates, we will use this function to rotate each corner of the triangle.

`def rotate(coordinate, center_point, degrees):`

# Subtract the purpose we're rotating around from our coordinate

x = (coordinate[0] - center_point[0])

y = (coordinate[1] - center_point[1])# Python's cos and sin functions take radians as a substitute of degrees

radians = math.radians(degrees)

# Calculate our rotated points

new_x = (x * math.cos(radians)) - (y * math.sin(radians))

new_y = (y * math.cos(radians)) + (x * math.sin(radians))

# Add back our offset we subtracted at the start to our rotated points

return [new_x + center_point[0], new_y + center_point[1]]

Now I can rotate a triangle, I have to switch my focus to drawing a recent smaller triangle on all sides of the primary triangle.

To realize this, I prolonged the *draw_triangle* function to calculate, for every edge, the rotation and centre point of a recent triangle with a side length reduced by the parameter *shrink_side_by*.

Once it has calculated the centre point and rotation of the brand new triangle it calls *draw_triangle* (itself) to attract the brand new, smaller triangle out from the centre of the present line. This may then in turn hit the identical block of code that calculates one other set of centre points and rotations for an excellent smaller triangle.

This known as a recurring algorithm, as our *draw_triangle* function will now call itself until it reaches the *max_depth* of triangles we wish to attract. Itâ€™s necessary to have this escape clause, because otherwise the function would theoretically proceed recurring ceaselessly (but in practice the decision stack will get too large, leading to a stack overflow error)!

`def draw_triangle(center, side_length, degrees_rotate, thickness, color, `

pixels, shrink_side_by, iteration, max_depth):# The peak of an equilateral triangle is, h = Â½(âˆš3a)

# where 'a' is the side length

triangle_height = side_length * math.sqrt(3)/2

# The highest corner

top = [center[0] - triangle_height/2, center[1]]

# Bottom left corner

bottom_left = [center[0] + triangle_height/2, center[1] - side_length/2]

# Bottom right corner

bottom_right = [center[0] + triangle_height/2, center[1] + side_length/2]

if (degrees_rotate != 0):

top = rotate(top, center, degrees_rotate)

bottom_left = rotate(bottom_left, center, degrees_rotate)

bottom_right = rotate(bottom_right, center, degrees_rotate)

# Coordinates between each fringe of the triangle

lines = [[top, bottom_left],[top, bottom_right],[bottom_left, bottom_right]]

line_number = 0

# Draw a line between each corner to finish the triangle

for line in lines:

line_number += 1

plot_line(line[0], line[1], thickness, color, pixels)

# If we've not reached max_depth, draw some recent triangles

if (iteration < max_depth and (iteration < 1 or line_number < 3)):

gradient = (line[1][0] - line[0][0]) / (line[1][1] - line[0][1])

new_side_length = side_length*shrink_side_by

# Center of the road of the traingle we're drawing

center_of_line = [(line[0][0] + line[1][0]) / 2,

(line[0][1] + line[1][1]) / 2]

new_center = []

new_rotation = degrees_rotate

# Amount we want to rotate the traingle by

if (line_number == 1):

new_rotation += 60

elif (line_number == 2):

new_rotation -= 60

else:

new_rotation += 180

# In a perfect world this could be gradient == 0,

# but because of floating point division we cannot

# make sure that it will all the time be the case

if (gradient < 0.0001 and gradient > -0.0001):

if (center_of_line[0] - center[0] > 0):

new_center = [center_of_line[0] + triangle_height *

(shrink_side_by/2), center_of_line[1]]

else:

new_center = [center_of_line[0] - triangle_height *

(shrink_side_by/2), center_of_line[1]]

else:

# Calculate the conventional to the gradient of the road

difference_from_center = -1/gradient

# Calculate the space from the middle of the road

# to the middle of our recent traingle

distance_from_center = triangle_height * (shrink_side_by/2)

# Calculate the length within the x direction,

# from the middle of our line to the middle of our recent triangle

x_length = math.sqrt((distance_from_center**2)/

(1 + difference_from_center**2))

# Work out which way across the x direction must go

if (center_of_line[1] < center[1] and x_length > 0):

x_length *= -1

# Now calculate the length within the y direction

y_length = x_length * difference_from_center

# Offset the middle of the road with our recent x and y values

new_center = [center_of_line[0] + y_length,

center_of_line[1] + x_length]

draw_triangle(new_center, new_side_length, new_rotation,

thickness, color, pixels, shrink_side_by,

iteration+1, max_depth)

Below are some examples of various images we will generate by modifying the *shrink_side_by* and *max_depth* values input to our *draw_triangle* function.

Itâ€™s interesting how these large repeating patterns often create more complex shapes, similar to hexagons, but with a mesmerising symmetry.

*All images unless otherwise noted are by the creator.*

Fractals are great fun to mess around with and may create beautiful patterns. Using a number of easy concepts and a splash of creativity, we will generate very impressive structures.

In understanding the core properties of our fractals, and applying the recurring algorithm, weâ€™ve created a solid foundation which can assist us understand more complex fractal problems in data science.

Be at liberty to read and download the total code **here**. Let me know in the event you find ways to enhance or extend it!

*I ponder what you possibly can create with a distinct shape?*