The spirograph
When does a spirograph pattern close, and how many petals does it have? A wheel with \(r\) teeth rolls inside a ring with \(R\) teeth, and a pen in a hole of the wheel, \(d\) from its center, draws a hypotrochoid. As a point in the complex plane, with \(t\) the wheel’s turn around the ring,
Both terms come back together once \(t\) is a whole number of turns and \(k t\) is too, after \(r / \gcd(R, r)\) turns. On the way, the two terms line up \(R / \gcd(R, r)\) times, once for each petal. With the pen on the wheel’s rim, \(d = r\), and \(r\) dividing \(R\), the curve is a hypocycloid with \(R/r\) cusps. For \(R = 3r\) it is the deltoid, and for \(R = 4r\) the astroid.

[1]:
import math
import planex as px
from scipy.special import ellipe
A pattern
spirograph draws the pattern of a ring and a wheel as one parametric curve, its angles in degrees as everywhere in planex2d, for as many turns as it takes to close. The curve crosses itself, so it is drawn in a sketch rather than as a single wire. The sketch starts at the curve’s first point, where the pen is farthest out, at \(R - r + d\):
[3]:
def spirograph(R, r, d, turns=None):
k = (R - r) / r
turns = turns or r // math.gcd(R, r)
curve = (f"{R - r}*cos(t) + {d}*cos({k}*t)", f"{R - r}*sin(t) - {d}*sin({k}*t)")
return px.QuickSketch((R - r + d, 0)).Curve(curve, (0, 360 * turns), id="pattern")
The examples are a ring of 96 teeth with wheels of 72, 80 and 84 teeth, which give 4, 6 and 8 petals:

Petals and turns
After \(r / \gcd(R, r)\) turns, the pen is back at its start, and one turn earlier it is not:
[5]:
for r, d in ((72, 40), (80, 50), (84, 60)):
g = math.gcd(96, r)
closed, short = spirograph(96, r, d), spirograph(96, r, d, turns=r // g - 1)
print("wheel", r, "| petals", 96 // g, "| closes after", r // g, "turns at", closed.pen.position,
"| one turn earlier at", tuple(round(c, 3) for c in short.pen.position))
wheel 72 | petals 4 | closes after 3 turns at (64.0, 0.0) | one turn earlier at (4.0, 34.641)
wheel 80 | petals 6 | closes after 5 turns at (66.0, 0.0) | one turn earlier at (31.451, 47.553)
wheel 84 | petals 8 | closes after 7 turns at (72.0, 0.0) | one turn earlier at (49.409, 46.91)
The length
The length of a pattern has no elementary formula, but an elliptic integral gives it exactly. The pen moves at the speed \(|z'(t)| = \sqrt{A - B\cos((1 + k)t)}\) with \(A = (R - r)^2 + d^2 k^2\) and \(B = 2(R - r) d k\), and each petal takes a full turn of \((1 + k)t\), so with \(N\) petals
with \(E\) the complete elliptic integral of the second kind, SciPy’s ellipe. Measure gives the length of the sketch’s curve:
[6]:
for r, d in ((72, 40), (80, 50), (84, 60)):
k, N = (96 - r) / r, 96 // math.gcd(96, r)
A, B = (96 - r) ** 2 + d**2 * k**2, 2 * (96 - r) * d * k
exact = 4 * N / (1 + k) * math.sqrt(A + B) * ellipe(2 * B / (A + B))
print("wheel", r, "| length", spirograph(96, r, d).Measure("pattern")["length"], "| exact", exact)
wheel 72 | length 488.0286509901441 | exact 488.0286509907997
wheel 80 | length 553.0799383300723 | exact 553.0799383303112
wheel 84 | length 597.6019101606987 | exact 597.6019101607269
The deltoid and the astroid
A hypocycloid does not cross itself, so px.Curve draws it as a closed wire, and its face has an area. The deltoid of a wheel of radius 1 in a ring of 3 has the area \(2\pi r^2\), and the astroid in a ring of 4 the area \(6\pi r^2\). Each arch between two cusps is \(8r(R - r)/R\) long, so the whole curve is \(8r(R - r)\) long, which the print compares (Figure 1):
[7]:
cusped = {}
for R, name, area in ((3, "deltoid", 2 * math.pi), (4, "astroid", 6 * math.pi)):
curve = px.Curve((f"{R - 1}*cos(t) + cos({R - 1}*t)", f"{R - 1}*sin(t) - sin({R - 1}*t)"), (0, 360))
cusped[name] = px.Face([curve])
print(name, "| area", cusped[name].mass, "exact", area, "| length", curve.mass, "exact", 8 * (R - 1))
deltoid | area 6.28318530718128 exact 6.283185307179586 | length 16.000000153461606 exact 16
astroid | area 18.849555921568385 exact 18.84955592153876 | length 23.99999999999363 exact 24
At the cusps the pen stops and turns back, so they are the curve’s hardest points. The fitted spline follows them closely, and the deltoid’s length differs from 16 by \(1.5 \cdot 10^{-7}\), less than \(10^{-8}\) of it.
(a) Deltoid
(b) Astroid