I am having a difficult time understanding why the author of the book used some lines of code:
import math
import turtle
bob = turtle.Turtle()
def polyline(t, n, length, angle):
"""Draws n line segments.
t: Turtle object
n: number of line segments
length: length of each segment
angle: degrees between segments
"""
for i in range(n):
t.fd(length)
t.lt(angle)
def polygon(t, n, length):
"""Draws a polygon with n sides.
t: Turtle
n: number of sides
length: length of each side.
"""
angle = 360.0/n
polyline(t, n, length, angle)
def arc(t, r, angle):
"""Draws an arc with the given radius and angle.
t: Turtle
r: radius
angle: angle subtended by the arc, in degrees
"""
arc_length = 2 * math.pi * r * abs(angle) / 360
n = int(arc_length / 4) + 3
step_length = arc_length / n
step_angle = float(angle) / n
# making a slight left turn before starting reduces
# the error caused by the linear approximation of the arc
t.lt(step_angle/2)
polyline(t, n, step_length, step_angle)
t.rt(step_angle/2)
def circle(t, r):
"""Draws a circle with the given radius.
t: Turtle
r: radius
"""
arc(t, r, 90)
circle(bob, 100)
I put in comments highlighting what I was confused on. The instructions are to make a general function to draw a circle using the turtle in Python. I am using Python 3.7 but I don't really think that makes a difference. Why does the author use the n where I put the first comment? Where did he get that formula from? Also what is the logic behind the rest of the commented code? I understand the rest of the exercises and I would understand this one if I know how he got that equation.
Let's take the arc function line by line:
arc_length = 2 * math.pi * r * abs(angle) / 360
2 * math.pi * r is the formula for the circumference of a circle. abs(angle) / 360 tells you what proportion of the circle's circumference your path will be. Multiplying them together, you get the distance your turtle will need to travel to describe this arc (assuming it can move along a curved path, which we are only approximating).
n = int(arc_length / 4) + 3
This is the number of steps we want the turtle to take while we are approximating the path described by the circumference of this circle. It needn't be this particular value; other implementations use int(arc_length / 3) + 1 and other values. step_length and step_angle are both scaled by it, so increasing it will make each step smaller and give the individual step a smaller rotation. Ignoring issues of floating point math and accrued rounding errors, the total distance traveled and the total rotation will be unchanged.
step_length = arc_length / n
step_angle = float(angle) / n
Our polyline will be making n steps for us. At each step, we want to travel 1/nth of the arc_length and rotate 1/nth of the total angle our arc describes. If we make n a larger value in the line before these two, step_length and step_angle will be scaled down correspondingly.
As for the rest:
t.lt(step_angle/2)
polyline(t, n, step_length, step_angle) # take a lot of little steps
t.rt(step_angle/2)
The half turn here is a small improvement in our approximation of the arc. Think about the path we are describing here for, say, a 90 degree arc along the unit circle starting at (1, 0) and ending at (0, 1) with 3 steps. Below is a table of values for locations on the unit circle itself (ideal x, ideal y, and ideal heading, where ideal heading is the tangent of the circle at that point), raw x, raw y, and raw heading, and the x, y, and heading yielded by making a step_angle/2 left turn before starting the path and then a step_angle/2 right turn afterward to cancel it out:
|ideal x|ideal y|ideal heading|raw x|raw y|raw heading|adj x|adj y|adj heading|
------------------------------------------------------------------------------------
Start|1 |0 |90 |1 |0 |90 |1 |0 |105 |
Step1|0.86 |0.5 |120 |1 |0.52 |120 |0.86 |0.51 |135 |
Step2|0.5 |0.86 |150 |0.74 |0.98 |150 |0.49 |0.88 |165 |
Step3|0 |1 |180 |0.28 |1.24 |180 |-0.01|1.01 |195 |
End |0 |1 |180 |0.28 |1.24 |180 |-0.01|1.01 |180 |
Note that the adjusted values are much closer to the ideal values than the raw ones. This makes intuitive sense when you consider how polyline works: it moves forward in whatever direction it is facing and then makes a left turn. When you start with the unadjusted heading, you move step_length above the arc before you even try to make a turn. You'll always be above and to the right of the arc. By taking a half step_angle turn to the left first, you'll spend some of each step_length under the arc, and some of each step_length above the arc. This will yield a better approximation of the arc than one which is strictly outside it.
EDIT:
As to the problem with your updated circle method, it takes two parameters (the turtle and the radius of the circle) and then calls arc accordingly right now your implementation is:
def circle(t, r):
arc(t, r, 90)
Where arc's third parameter is the angle of the arc you wish to travel. To make a circle, you need 360 degrees though, not 90.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With