This project uses turtle graphics to draw spirals: choose your own design. On the way, we meet a spiral that is 200 million years old.
First take your polygon¶
Start the project by making an empty file
Right-click and open it with IDLE.
Type this (or copy most of it from
final.py in the introduction) and save it:
from turtle import * speed("fastest") def polygon(a, n): "n-sided polygon, side a." for i in range(n): forward(a) left(360/n)
The new line is called a documentation string,
a little reminder of what the function does.
It makes no difference to what the code does,
but in IDLE,
when you get as far as typing
IDLE will pop up this string as a reminder.
Never throw code away.
In these projects we explain the program a piece at a time. Mostly you should add each new code fragment to the program you have already, normally at the end. Sometimes the fragments show a change to what you have.
Occasionally, there is a short fragment of code that you only need for a few tests, then you’ll replace it.
If ever you want to delete a lot of code,
first use “File” >> “Save Copy As” and give a name like
so you can go back to your program as it was.
Offset and fill the polygon¶
Your program is going to draw lots of different spirals. Here’s a simple example where you can see what’s going on:
Drawing starts with the smallest square tile, nearest the middle, and continues anti-clockwise, putting down tiles.
Each tile is slightly bigger than the last. Each circuit has the same number of tiles, that you choose, 9 in this case. By the time it gets round once, the tile is twice the original size, and twice as far from the centre. It will go round as many times as you choose.
You will draw each tile with your
To put it in the right place,
suppose the turtle is in the centre, and pointing the right way.
We need to go forward some distance, then draw the polygon.
Then we go back to the centre, ready for next time.
In code, it looks like this:
def tile(r, a, n): "n-sided polygon, side a, at range r (assumes pen is up)." forward(r) pendown() begin_fill() polygon(a, n) end_fill() penup() back(r)
Add that at the end of your program, and then add this to test it:
# Test fillcolor("red") tile(100, 50, 6)
Lines that begin with a
# are comments.
Python ignores them: they’re a hint to the humans.
You should put comments in code you invent,
but you don’t have to copy them in these examples.
Save and run that. It should look like this:
The initial line is not a fault: it is there because we started with the pen down.
Turn and repeat¶
Putting the tiles down in a spiral is still too difficult for us in one leap. Think instead how would we put tiles down in a circle.
That would be rather like drawing a polygon,
except we use the
tile function each time
Add after your last function:
def circ(r, scale, n, tiles): "Circle of n-sided polygons at radius r and size r/scale." # Angle between each tile step = 360 / tiles # Now draw it penup() for i in range(tiles): tile(r, r/scale, n) left(step)
Change your test code at the end to read:
# Test fillcolor("red") circ(100, 10, 4, 9)
Save and run: you should get a circle of 9 little squares.
Grow a little each time¶
How can we turn the circle into a spiral? This means making the distance from home (the radius) and the tile size grow each time a tile is placed.
Here “grow” means that we should multiply the size and the radius by some amount each time we put down a tile. The growth factor should be only a little more than one, or the size will get huge in just a few tiles. Remember what happens to powers of numbers from chapter 1?
We have to work out the starting radius and the growth per tile. Choosing that number, a little bigger than one, to get the final size you want, is the the hardest part. You may not have learned the maths for this yet, but the comments explain what’s happening. Add after your last function:
def spiral(r, n, scale, cycles, m, growth): """Spiral of n-sided polygons out to radius r and size r/scale, in given number of cycles of m steps, growing each cycle. """ # Angle between each tile step = 360 / m # Total number of tiles (made a whole number) p = int(cycles*m) + 1 # Growth between each tile g = growth ** (1/m) # Starting radius (this will grow with each tile placed) r = r / (growth**cycles) # Now draw it penup() for i in range(p): # As the distance from the centre grows, so does the polygon tile(r, r/scale, n) left(step) r = r * g
You can see this is like the
but with extra code to make the size change.
Finally, we’re ready to try this out.
At the end of your program, add:
def example(): fillcolor("red") spiral(100, 4, 4, 2, 9, 2)
The call to
spiral has a lot or arguments.
In order it says that you want:
- a final size of 100.
- squares (4 sides)
- … that are 4 times smaller than the distance from the centre
- to go round twice
- to have 9 tiles per revolution
- to grow by a factor 2 each revolution
Save and run this. Then at the shell prompt type:
You should get the example we showed earlier.
Choose wild numbers¶
Here is a suggestion to add:
def ammonite(): pensize(1) color("peru", "wheat") spiral(100, 6, 3/2, 4.7, 22.5, 2) hideturtle()
And here is another a suggestion to add:
def vortex(): pensize(5) color("navy blue", "royal blue") spiral(500, 6, 3, 15, 4.2, 1.4) hideturtle()
Call them at the shell prompt, like you did with
The function call
reset() will clear the drawing window between tests.
vortex you get a sort of two-way spiral
because the number of tiles per cycle is not a whole number,
but is close to a whole number (4.2).
So what might have been 4 straight lines becomes 4 curved lines.
What number would make them curve the other way?
spiral in your own function with a variety of numbers.
If you find a combination you like, give it a new name.