# fractalartmaker
A module for creating fractal art in Python's `turtle` module.
This module is explored in the book "The Recursive Book of Recursion" by Al Sweigart from No Starch Press.
You can purchase this book directly from the publisher at https://nostarch.com/recursive-book-recursion or read it online at https://inventwithpython.com/recursion/
Quickstart
==========
To view some example fractals, run the following from the interactive shell:
>>> import fractalartmaker
>>> fractalartmaker.fast() # draw the fractals quickly
>>> fractalartmaker.example(1) # pass 1 to 9
Making Fractals
===============
The Fractal Art Maker's algorithm has two major components: a shape-drawing function and the recursive `drawFractal()` function.
The shape-drawing function draws a basic shape. The Fractal Art Maker program comes with the two shape-drawing functions, `fractalartmaker.drawFilledSquare()` and `fractalartmaker.drawTriangleOutline()`, but you can also create your own. We pass a shape-drawing function to the `fractalartmaker.drawFractal()` function as an argument.
The `fractalartmaker.drawFractal()` function also has a parameter indicating changes to the size, position, and angle of the shapes between recursive calls to `fractalartmaker.drawFractal()`.
The `fractalartmaker.drawFractal()` function uses a shape-drawing function passed to it to draw the individual parts of the fractal. This is usually a simple shape, such as a square or triangle. The beautiful complexity of the fractals emerges from `fractalartmaker.drawFractal()` recursively calling this function for each individual component of the whole fractal.
Here's the two shape-drawing functions that come in the `fractalartmaker` module:
def drawFilledSquare(size, depth):
size = int(size)
# Move to the top-right corner before drawing:
turtle.penup()
turtle.forward(size // 2)
turtle.left(90)
turtle.forward(size // 2)
turtle.left(180)
turtle.pendown()
# Alternate between white and gray (with black border):
if depth % 2 == 0:
turtle.pencolor('black')
turtle.fillcolor('white')
else:
turtle.pencolor('black')
turtle.fillcolor('gray')
# Draw a square:
turtle.begin_fill()
for i in range(4): # Draw four lines.
turtle.forward(size)
turtle.right(90)
turtle.end_fill()
def drawTriangleOutline(size, depth):
size = int(size)
# Move the turtle to the top of the equilateral triangle:
height = size * math.sqrt(3) / 2
turtle.penup()
turtle.left(90) # Turn to face upwards.
turtle.forward(height * (2/3)) # Move to the top corner.
turtle.right(150) # Turn to face the bottom-right corner.
turtle.pendown()
# Draw the three sides of the triangle:
for i in range(3):
turtle.forward(size)
turtle.right(120)
The shape-drawing functions for the Fractal Art Maker have two parameters: `size` and `depth`. The size parameter is the length of the sides of the square or triangle it draws. The shape-drawing functions should always use arguments to `turtle.forward()` that are based on `size` so that the lengths will be proportionate to size at each level of recursion. Avoid code like `turtle.forward(100)` or `turtle.forward(200)`; instead, use code that is based on the `size` parameter, like `turtle.forward(size)` or `turtle.forward(size * 2)`. In Python's `turtle` module, `turtle.forward(1)` moves the turtle by one unit, which is not necessarily the same as one pixel.
The shape-drawing functions' second parameter is the recursive depth of `fractalartmaker.drawFractal()`. Your shape-drawing function can ignore this argument, but using it can cause interesting variations to the basic shape. For example, the `fractalartmaker.drawFilledSquare()` shape-drawing function uses depth to alternate between drawing white squares and gray squares. Keep this in mind if you'd like to create your own shape-drawing functions for the Fractal Art Maker program, as they must accept a `size` and `depth` argument.
The `fractalartmaker.drawFractal()` function has three required parameters and one optional one: `shapeDrawFunction`, `size`, `specs`, and optionally `maxDepth`. The `shapeDrawFunction` parameter expects a function, like `fractalartmaker.drawFilledSquare` or `fractalartmaker.drawTriangleOutline`. The `size` parameter expects the starting size passed to the drawing function. Often, a value between `100` and `500` is a good starting size, though this depends on the code in your shape-drawing function, and finding the right value may require experimentation.
The `specs` parameter expects a list of dictionaries that specify how the recursive shapes should change their size, position, and angle as `drawFractal()` recursively calls itself. These specifications are described later in this section. To prevent `drawFractal()` from recursing until it causes a stack overflow, the `maxDepth` parameter holds the number of times `drawFractal()` should recursively call itself. By default, `maxDepth` has a value of `8`, but you can provide a different value if you want more recursive shapes or fewer.
The recursive calls to `drawFractal()` are based on the specification in the `specs` list’s dictionaries. For each dictionary, `drawFractal()` makes one recursive call to `drawFractal()`. If specs is a list with one dictionary, every call to `drawFractal()` results in only one recursive call to `drawFractal()`. If specs is a list with three dictionaries, every call to `drawFractal()` results in three recursive calls to `drawFractal()`.
The dictionaries in the specs parameter provide specifications for each recursive call. Each of these dictionaries has the keys `sizeChange`, `xChange`, `yChange`, and `angleChange`. These dictate how the size of the fractal, the position of the turtle, and the heading of the turtle change for a recursive `drawFractal()` call.
* `sizeChange` (default is `1.0`) - The next recursive shape’s size value is the current size multiplied by this value.
* `xChange` (default is `0.0`) - The next recursive shape’s x-coordinate is the current x-coordinate plus the current size multiplied by this value.
* `yChange` (default is `0.0`) - The next recursive shape’s y-coordinate is the current y-coordinate plus the current size multiplied by this value.
* `angleChange` (default is `0.0`) - The next recursive shape’s starting angle is the current starting angle plus this value.
Let’s take a look at the specification dictionary for the Four Corners fractal, which is drawn when you call `fractalartmaker.example(1)`. The call to `drawFractal()` for the Four Corners fractal passes the following list of dictionaries for the `specs` parameter:
fractalartmaker.drawFractal(fractalartmaker.drawFilledSquare, 350,
[{'sizeChange': 0.5, 'xChange': -0.5, 'yChange': 0.5},
{'sizeChange': 0.5, 'xChange': 0.5, 'yChange': 0.5},
{'sizeChange': 0.5, 'xChange': -0.5, 'yChange': -0.5},
{'sizeChange': 0.5, 'xChange': 0.5, 'yChange': -0.5}], 5)
The `specs` list has four dictionaries, so each call to `drawFractal()` that draws a square will, in turn, recursively call `drawFractal()` four more times to draw four more squares.
To determine the size of the next square to be drawn, the value for the `sizeChange` key is multiplied by the current size parameter. The first dictionary in the specs list has a `sizeChange` value of `0.5`, which makes the next recursive call have a size argument of `350 * 0.5`, or `175` units. This makes the next square half the size of the previous square. A `sizeChange` value of `2.0` would, for example, double the size of the next square. If the dictionary has no `sizeChange` key, the value defaults to `1.0` for no change to the size.
If you look at the three other dictionaries in the `specs` list, you’ll notice they all have a `sizeChange` value of `0.5`. The difference between them is that their `xChange` and `yChange` values place them in the other three corners of the current square. As a result, the next four squares are drawn centered on the four corners of the current square.
The dictionaries in the `specs` list for this example don’t have an `angleChange` value, so this value defaults to `0.0` degrees. A positive `angleChange` value indicates a counterclockwise rotation, while a negative value indicates a clockwise rotation.
Take a look at the code in the module's `example()` function for more examples.
The `fractalartmaker` module also has a `fractalartmaker.fast()` function you can call to make the fractals draw quickly, and a `fractalartmaker.clear()` to clear the turtle drawing window.