Add draw_spirograph with tests

Draws int(360 / size_of_gap) circles, each in a random RGB color, turning the heading
by the gap after each. A gap that is not positive raises ValueError, and the integer
count avoids the TypeError that range raises for a float.

Task: MIL-004#2
Task: MIL-004#3
This commit is contained in:
2026-10-07 23:59:12 +08:00
parent 949d2edb63
commit 2328490945
4 changed files with 170 additions and 3 deletions
+6 -3
View File
@@ -3,10 +3,11 @@
@brief Solutions to the turtle graphics challenges of day 18 of Udemy's
100 Days of Code.
The challenges so far are @ref turtle_challenges.square.draw_square,
The five challenges are @ref turtle_challenges.square.draw_square,
@ref turtle_challenges.dashed_line.draw_dashed_line,
@ref turtle_challenges.shapes.draw_shape and
@ref turtle_challenges.random_walk.random_walk. Each takes the turtle as its
@ref turtle_challenges.shapes.draw_shape,
@ref turtle_challenges.random_walk.random_walk and
@ref turtle_challenges.spirograph.draw_spirograph. Each takes the turtle as its
first argument, so it can be tested without a window.
"""
@@ -14,12 +15,14 @@ from turtle_challenges.colors import random_color, random_palette_color
from turtle_challenges.dashed_line import draw_dashed_line
from turtle_challenges.random_walk import random_walk
from turtle_challenges.shapes import draw_shape, draw_shapes
from turtle_challenges.spirograph import draw_spirograph
from turtle_challenges.square import draw_square
__all__ = [
"draw_dashed_line",
"draw_shape",
"draw_shapes",
"draw_spirograph",
"draw_square",
"random_color",
"random_palette_color",
+6
View File
@@ -77,6 +77,12 @@ WALK_PEN_SIZE = 10
## Challenge 4: headings of a step: east, north, west and south, in degrees.
WALK_HEADINGS: tuple[int, ...] = (0, 90, 180, 270)
## Challenge 5: radius of every circle of the spirograph, in turtle units.
SPIROGRAPH_RADIUS = 100
## Challenge 5: degrees the heading turns after each circle.
SPIROGRAPH_GAP_DEGREES = 5
## Command line name of challenge 1.
CHALLENGE_SQUARE = "square"
+47
View File
@@ -0,0 +1,47 @@
"""!
@file spirograph.py
@brief Turtle Challenge 5: draw a spirograph.
"""
import random
from turtle_challenges.colors import random_color
from turtle_challenges.constants import (
FASTEST_SPEED,
FULL_TURN_DEGREES,
SPIROGRAPH_GAP_DEGREES,
SPIROGRAPH_RADIUS,
)
from turtle_challenges.pen import Pen
def draw_spirograph(
pen: Pen,
size_of_gap: float = SPIROGRAPH_GAP_DEGREES,
radius: float = SPIROGRAPH_RADIUS,
rng: random.Random | None = None,
) -> None:
"""!
@brief Draw overlapping circles, turning a little after each one.
After every circle the heading grows by @p size_of_gap, so the circles
overlap into a spirograph once the heading has made a full turn. The number
of circles is 360 divided by the gap, rounded down: `range` takes only an
integer, and a float such as 72.0 would raise a `TypeError`.
@param pen The turtle that draws.
@param size_of_gap Degrees between two circles; must be positive.
@param radius Radius of every circle, in turtle units.
@param rng Random generator for the colors; a shared one is used if
omitted.
@exception ValueError If @p size_of_gap or @p radius is not positive.
"""
if size_of_gap <= 0:
raise ValueError(f"size_of_gap must be positive, got {size_of_gap}")
if radius <= 0:
raise ValueError(f"radius must be positive, got {radius}")
pen.speed(FASTEST_SPEED)
for _ in range(int(FULL_TURN_DEGREES / size_of_gap)):
pen.color(random_color(rng))
pen.circle(radius)
pen.setheading(pen.heading() + size_of_gap)