WriteUp — Fantastic times tables… and how to draw them
Welcome back everyone! Today we’ll move away a bit from the usual coding problem challenges to look at something I used to play with when I started to learn to code. It’s a bit of a silly game really, but also a nice opportunity to explore some times tables math, coding and…turtles!
Ready? Let’s begin then.
Times tables
Times tables are probably the first concept everyone learns at elementary school after basic arithmetic. I still remember my teacher’s method: she used to teach us by learning them by memory, and then we had small competitions in class. We were given a piece of paper with a table and some missing results and we had to fill all the times tables learnt up to that point: whoever finished first was given a prize. It was really funny, back in the days.
Besides being a tool for the every-day life and a school exercise though, if we take a closer look to times tables we can discover that there is?a lot more?hidden behind them. For example: what happens if we try to draw them?
Drawing the tables
The idea behind this little program came to me by a video by Prof. Polster, who runs the brilliant YT channel?Mathologer. You can take a look at his content?here: he delves a lot more into the concept, talking about Mandelbrot sets and Riemann Z function and he’s just way too good at explaining those concepts in a fun and easy to understand way.
We will limit our game to the pictures of the times tables tough, specifically writing some code that will draw them for us.
The idea is pretty simple: we draw a circle first, and we enumerate some points on the circumference, leaving the same distance between them. After that we start calculating the results for a given times table and connect the results. For example, for the 6-times table:
and so on. What happens if we connect a greater number of points? Let’s find out.
A quick refresh
Before starting, a quick refresh about the concepts we are going to see here. A?circumference?is the?set of the points equidistant from its center, also called?origin (O). That distance is called?radius (r).
On the plane, assuming we have our origin point O with coordinate?x?and?y,?we can find the coordinates of the point?A?on the circumference with the following equations:
where θ is the angle created by the point A, the origin and the y axis of the plane, adjusted by the?x?and?y?coordinates. For example, this circle has?O?in?(4,4)?and?r = 3. The point?A?is drawn on its circumference, creating an angle a of?30°. We can calculate the coordinates of?A?like
You can verify with the drawing below.
Another important concept is the difference between?degrees?and?radian:
Without going too deep, we can calculate the Rad by the following equation:
Rad = (π*Degree)/180
We’ll also use a bit of?modular arithmetic?to calculate our results on the circle. Simply put,?the modulo operator gives us the remainder of a division: for instance, A%B = C (red as “A modulo B equals C”), will mean that the remainder of A/B equals C.
That’s enough info for our mission today. We’ll make wide use of these concepts below so check them out carefully.
Now for the fun part, let’s start drawing!
Turtle
When I started learning Python, I used to play a lot with this library,?turtle, which is a great tool to draw shapes and geometries. It’s often skipped while learning the language, mainly because it has no real purpose and it’s usage is really limited. However I think it’s a great library, well documented and easy to use, which is really worthy to explore and have fun with.
The library is really simple: we will “spawn” a turtle (which we’ll call Bob, as it’s often named in tutorials around the web), represented by an arrow on the screen, and move it giving directions and turning points. Here’s an example of some basic commands:
import turtle
bob = turtle.Turtle()
bob.fd(50) # Go 50 pts forwars
bob.left(90) # Turn 90° left
bob.back(50) # Go 50 pts backwards
bob.right(90) # Turn 90° right
bob.pensize(2) # Change line size
bob.pencolor("red") # Change line color
bob.fd(50)
bob.left(90)
bob.fd(100)
bob.pensize(3)
bob.pencolor("green")
bob.left(90)
bob.fd(100)
turtle.mainloop()
Let’s start drawing then!
The circle
import turtle
# Draws a circle from the origin with a given radius
def drawCircle ( turtle :turtle.Turtle, radius :int, origin :tuple=(0,0), penSize :int=1, penColor :str="black", writeLabel :bool=False ) :
turtle.pensize(penSize)
turtle.pencolor(penColor)
turtle.up()
turtle.goto(origin[0],origin[1]-radius)
turtle.down()
turtle.circle(radius)
turtle.up()
if writeLabel:
turtle.goto(origin[0],origin[1])
turtle.down()
turtle.dot()
turtle.write("O",font=("CourierNew",8,"italic"))
turtle.up()
turtle.goto(origin[0],origin[1])
turtle.down()
bob = turtle.Turtle()
drawCircle(bob,150)
turtle.mainloop()
First we define the function?drawCircle?which accepts the following parameters:
Notice that turtle starts drawing from the lowest point of the circumference, so the function just moves the turtle down by the radius length, draws the circle and then moves back up by the radius.
Adding the points
We first need to decide how many points to add on the circle. Let’s start simple and try with 12, like on the clock! We need to put 12 points on the circle, with the same distance between one another.
Knowing that the angle of a full circular rotation it’s 360°, we can simply split this rotation in 12 parts and get an angle of 360°/12 = 30° for each point. Also, we’ll be working with?math.sin?and?math.cos?from the Python?math?library, which use radians as measure, so it’s better to convert our angle right now. Let’s write a function to wrap this up:
def calculateAngle (numberOfPoints :int):
angle = 360/numberOfPoints
return angle*math.pi/180
And now let’s put the points on the circle:
领英推荐
coordinates = {
def drawCircumferencePoints (turtle :turtle.Turtle, radius :int,coordinates :dict, numberOfPoints :int=1000, penSize :int=1,penColor :str="black",writeLabel :bool=False):
turtle.pensize(penSize)
turtle.pencolor(penColor)
def singlePointDraw (angle):
turtle.up()
turtle.goto(
radius*math.sin(angle),
radius*math.cos(angle)
)
turtle.down()
turtle.dot()
angle = calculateAngle(numberOfPoints)
for i in range(numberOfPoints):
singlePointDraw(i*angle)
coordinates[i] = turtle.pos(),i*angle
if writeLabel:
turtle.write(i)}
We define the function?drawCircumferencePoints?, that accepts the usual parameters and a new one,?coordinates?, which is a dictionary to collect the points and their coordinates. This way we can avoid recalculating all the points in the next step, which comes really in handy while working with greater numbers of points.
The inner function?singlePointDraw?simply moves the turtle to the coordinates for a given?angle?and places a dot.
With this function, we range over our points and drop a point with?singlePointDraw?: the angle will always be 30°, so for each point in the range we drop a point at?i*angle?: for point 0 we’ll place it at?0*30° = 0°?, for point 1 at?1*30° = 30°?, for point 2 at?2*30° = 60°?and so on.
At this point we also collect the position of our turtle and the angle in the?coordinates?dictionary. It’s not really necessary to collect the angle: I used it for testing and left it there (unintentionally ;D) !
Finally, if the parameter?writeLabel?is set to?True?, we’ll write the point number aside each point.
Calling?drawCircle?and?drawCircumferencePoints?you should now have something like this:
Also note that the points start at 0 and end at 11: this is why modular arithmetic will come in help pretty soon.
Time tables!
At last, let’s put down our times-table! Let’s pick the number 3, for example, and draw it’s time table.
def drawTable(turtle :turtle.Turtle,timesTable :float, coordinates :dict, radius :int, numberOfPoints :int,penSize :int=1,penColor :str="black")
turtle.pensize(penSize)
turtle.pencolor(penColor)
angle = calculateAngle(numberOfPoints)
for n in range(numberOfPoints):
turtle.up()
startingPoint = (
coordinates[n][0][0],
coordinates[n][0][1]
)
result = (n * timesTable)%len(coordinates)
endPoint = (
radius*math.sin(result*angle),
radius*math.cos(result*angle)
)
turtle.goto(startingPoint[0],startingPoint[1])
turtle.down()
turtle.goto(endPoint[0],endPoint[1]):
We define another function to do this,?drawTable?. After that we calculate the base angle to have a reference in this function and store it in the variable?angle?.
Then, we start looping over our points. For each point we define:
Finally, we simply draw a line between the starting dot and the end dot, and start the loop again with the next number.
At this point, you should have something like this:
Wrapping everything up
Let’s wrap up all the code in a single function:
def timeTablesDrawer (
radius :int,
origin :tuple,
numberOfPoints :int,
timesTable :float,
penSize :int = 1,
penColor :str = "blue",
writeLabel :bool = False,
speed :str = "fastest"
):
bob = turtle.Turtle()
bob.speed(speed)
coordinates = {}
drawCircle(bob,radius,origin,penSize,penColor,writeLabel)
drawCircumferencePoints(bob,radius,coordinates,numberOfPoints,penSize,penColor,writeLabel)
drawTable(bob,timesTable,coordinates,radius,numberOfPoints,penSize,penColor)
turtle.mainloop()
This function basically calls the other function created above in the right order, nothing more really. It adds the parameter?speed?to set the speed of the turtle, so we can control how fast the animation is (trying with higher number of points takes a bit of time!). It also holds the variable?coordinates?, which was outside the?drawCircle?function before, and now it’s contained by the outertimesTablesDrawer?function.
Some more points, please?
The image resulting with the number 3 was ok, but not that much of a masterpiece: it was just a bunch of lines inside a circle. That is because there are too few points on the circle. Let’s try adding some more: 200 points for example!
That’s way cuter! How strange is the shape drawn, right?
These types of curves are called?cardioids, for their shape remembering a heart. It’s pretty clear if you look at the one in the cover image, which is the 2-times table cardioid. Generally speaking, these kind of shapes has a number of “bumps” equal to the chosen times-table minus one: in this example, the time table was 3 and in fact the image has 2 round bumpy shapes in it.
These cardioids pop up a little everywhere around math, physics, chemistry and nature. In microphones and apples, for example…and also in your cup of tea!
And there is more! If we try to draw higher times-tables, we can see tons of different shapes coming up: some of them has nothing to do with cardioids, like the 99 or the 66 times-table!
Here some examples with greater numbers:
Give it a try with different number of points and times-tables: I can assure you it’s so much fun to look at the little turtle running around to draw beautiful shapes!
Intermediate results
We can also draw intermediate results, meaning times-tables with decimal places. It’s also really cool to see intermediate results because it gives us an idea about how the image gets created and changes from one result to another. Here’s an animation of the times-tables from 2 to 3 (sorry for the bad quality!) :
You can see how the shape basically keeps rotating and enveloping on itself, almost shifting to the left, to create the next cardioid. How awesome!
Conclusions
That’s it! A long journey (as most of the times here). It’s a bit different from the usual articles about coding problems solving, but I recently saw that video again and it instantly made me want to share my try to it.
I also wrote a simple program to draw these shapes, you can download it from my Github page,?here?(click on ‘Raw’ to download it!): feel free to use it whenever and wherever you like.
I hope you enjoyed it and find it interesting! If you did, please leave a clap or two or share it with someone who could enjoy it too: it would make my day!
As always, thanks for reading.
Nicola