Skip to content

geomotif.motifs.roulettes

Roulettes: what one circle draws while rolling around another.

Four classical curves, the toy that made them famous, and the generalization that swallows all five.

:class:Epicycles is the one to reach for when nothing else fits. Stack any number of rotating arms, each with its own radius, frequency and phase, and plot the tip: that is a hypotrochoid with two arms, an epitrochoid with the middle one reversed, a planetary system with three, and a Fourier series with as many as you like. Every other class in this module is a friendlier name for a particular pair of arms.

These curves only close if the two radii are commensurate, so outer and inner are whole numbers here. Their ratio in lowest terms is what decides how many revolutions it takes -- inner // gcd(outer, inner) of them -- and the classes work that out for themselves.

Classes:

Name Description
Hypotrochoid

A pen fixed to a wheel rolling around the inside of a ring.

Epitrochoid

A pen fixed to a wheel rolling around the outside of a ring.

Hypocycloid

A point on the rim of a wheel rolling inside a ring: a cusped star.

Epicycloid

A point on the rim of a wheel rolling outside a ring: a ring of petals.

Spirograph

The toy, in the toy's own terms: a ring, a wheel and a hole.

Epicycles

Rotating arms stacked tip to tail; the path of the last tip.

Hypotrochoid dataclass

Hypotrochoid(outer: int = 100, inner: int = 30, offset: float = 45.0, center: Point = (0.0, 0.0), *, resolution: int | None = None)

Bases: ParametricMotif

A pen fixed to a wheel rolling around the inside of a ring.

The Spirograph curve, in its mathematical clothes -- see :class:Spirograph for the version that takes tooth counts. offset is free to exceed inner, which puts the pen outside the wheel's rim and is not something the physical toy can do.

Parameters:

Name Type Description Default
outer int

Radius of the fixed ring.

100
inner int

Radius of the rolling wheel. Must be less than outer.

30
offset float

Distance from the wheel's center to the pen. Equal to inner gives the :class:Hypocycloid; zero gives a circle.

45.0
center (float, float)

Center of the fixed ring.

(0.0, 0.0)

Epitrochoid dataclass

Epitrochoid(outer: int = 100, inner: int = 30, offset: float = 45.0, center: Point = (0.0, 0.0), *, resolution: int | None = None)

Bases: ParametricMotif

A pen fixed to a wheel rolling around the outside of a ring.

The same construction as :class:Hypotrochoid with the wheel on the far side, which turns the scalloped rosette inside out into a ring of petals. Layered with phase offsets, this is the curve underneath every guilloche pattern on a banknote.

Parameters:

Name Type Description Default
outer int

Radius of the fixed ring.

100
inner int

Radius of the rolling wheel.

30
offset float

Distance from the wheel's center to the pen. Equal to inner gives the :class:Epicycloid; zero gives a circle.

45.0
center (float, float)

Center of the fixed ring.

(0.0, 0.0)

Hypocycloid dataclass

Hypocycloid(outer: int = 100, inner: int = 30, center: Point = (0.0, 0.0), *, resolution: int | None = None)

Bases: ParametricMotif

A point on the rim of a wheel rolling inside a ring: a cusped star.

:class:Hypotrochoid with the pen exactly on the rim, which is what puts a cusp wherever the rim touches the ring. Three cusps is the :class:~geomotif.motifs.curves.Deltoid, four is the :class:~geomotif.motifs.curves.Astroid, and both have their own class with a scale you can set directly.

Parameters:

Name Type Description Default
outer int

Radius of the fixed ring.

100
inner int

Radius of the rolling wheel. outer / inner in lowest terms gives the cusp count as its numerator.

30
center (float, float)

Center of the fixed ring.

(0.0, 0.0)

Epicycloid dataclass

Epicycloid(outer: int = 100, inner: int = 30, center: Point = (0.0, 0.0), *, resolution: int | None = None)

Bases: ParametricMotif

A point on the rim of a wheel rolling outside a ring: a ring of petals.

One cusp is the :class:~geomotif.motifs.curves.Cardioid, two is the :class:~geomotif.motifs.curves.Nephroid, and the general case is the flower shape a gear leaves when it rolls around another one.

Parameters:

Name Type Description Default
outer int

Radius of the fixed ring.

100
inner int

Radius of the rolling wheel. outer / inner in lowest terms gives the petal count as its numerator.

30
center (float, float)

Center of the fixed ring.

(0.0, 0.0)

Spirograph dataclass

Spirograph(ring_teeth: int = 96, wheel_teeth: int = 36, hole: float = 0.7, ring_radius: float = 150.0, center: Point = (0.0, 0.0), *, resolution: int | None = None)

Bases: ParametricMotif

The toy, in the toy's own terms: a ring, a wheel and a hole.

Exactly a :class:Hypotrochoid, parameterized the way the box is. Tooth counts are what actually determine the pattern -- the ring and wheel that come in the tin have 96 and 36 teeth, and their ratio is why that particular rosette is the one everyone remembers drawing.

Parameters:

Name Type Description Default
ring_teeth int

Teeth on the fixed ring.

96
wheel_teeth int

Teeth on the rolling wheel. Fewer than the ring's.

36
hole float

Which hole the pen goes in, as a fraction of the wheel's radius from its center. 1 is the rim, 0 is the middle and draws a circle.

0.7
ring_radius float

Physical size of the ring, which sets the size of the drawing.

150.0
center (float, float)

Center of the ring.

(0.0, 0.0)

Methods:

Name Description
wheel_radius

Return the rolling wheel's radius, scaled from the tooth counts.

wheel_radius

wheel_radius() -> float

Return the rolling wheel's radius, scaled from the tooth counts.

Source code in src/geomotif/motifs/roulettes.py
def wheel_radius(self) -> float:
    """Return the rolling wheel's radius, scaled from the tooth counts."""
    return self.ring_radius * self.wheel_teeth / self.ring_teeth

Epicycles dataclass

Epicycles(arms: tuple[tuple[float, float, float], ...] = ((120.0, 1.0, 0.0), (30.0, 7.0, 0.0), (12.0, 13.0, 0.0)), turns: float = 1.0, center: Point = (0.0, 0.0), *, resolution: int | None = None)

Bases: ParametricMotif

Rotating arms stacked tip to tail; the path of the last tip.

Each arm is (radius, frequency, phase): how long it is, how many revolutions it makes per turn of the whole system, and where it starts. Negative frequencies turn the other way.

This is the general case the rest of this module is made of. Two arms give every trochoid; a few more give the Ptolemaic orbit of a moon of a moon; several dozen give a Fourier series, which is to say any closed curve at all::

Epicycles(arms=((100.0, 1.0, 0.0), (40.0, 5.0, 0.0), (18.0, -9.0, 0.0)))

Parameters:

Name Type Description Default
arms tuple of (float, float, float)

The arms, outermost effect last. At least one.

((120.0, 1.0, 0.0), (30.0, 7.0, 0.0), (12.0, 13.0, 0.0))
turns float

Revolutions of the slowest hand, in effect: the parameter sweeps turns full cycles. Only worth changing when the frequencies are not whole numbers, since whole ones already close in one turn.

1.0
center (float, float)

Where the first arm is anchored.

(0.0, 0.0)