Skip to the plate
FORMA PUBLIC DOMAIN GENERATIVE ATLAS / ED. 0.28
Plate 03, Epitrochoid: a still of the roulette / rolling circle plate as the atlas renders it, in the curves accent.

PL. 03  ·  CURVES / ROULETTE / ROLLING CIRCLE

Epitrochoid

Albrecht Dürer, 1525 · Ptolemaic tradition

OPEN THE LIVE PLATE ▸

DEFINITION

x(θ) = (R+r)·cos θ − h·cos(((R+r)/r)·θ)
y(θ) = (R+r)·sin θ − h·sin(((R+r)/r)·θ)

NOTES

The path of a point fixed to a circle rolling around the outside of another circle. Dürer drew them by hand in 1525; the toy shop sells the hypotrochoid version as a Spirograph. Set h = r and you get the epicycloid, the curve the Ptolemaic astronomers used to explain retrograde motion.

PROVENANCE

Origin
Described by Dürer (1525); roulettes studied since antiquity
Standing
Public domain
Constants
Whole-number R/r closes the curve; h is the pen offset

HOUDINI · VEX

The same published mathematics as a Detail Wrangle body. Paste it into a Wrangle with Run Over set to Detail; every constant is the published value plus a tweak channel, so Create Spare Parameters gives a slider that starts where the paper does.

// FORMA — PL. 03 · EPITROCHOID — Albrecht Dürer, 1525 · Ptolemaic tradition
//   x(θ) = (R+r)·cos θ − h·cos(((R+r)/r)·θ)
//   y(θ) = (R+r)·sin θ − h·sin(((R+r)/r)·θ)
// Paste into a Detail Wrangle (Run Over: Detail), no inputs needed.
// Written from the published mathematics, not adapted from any code.
// Constants arrive at their published values. Press the node's Create
// Spare Parameters button and every tweak becomes a slider — starting
// at 0, the published figure, and moving in the constant's own units.
// https://forma-gen.com/#plate=epitrochoid

float p_R = 8 + chf('R_tweak');       // R — fixed circle · live 1 .. 14
float p_r = 3 + chf('r_tweak');       // r — rolling circle · live 1 .. 14
float p_h = 5 + chf('h_tweak');       // h — pen offset · live 1 .. 14

// The plate's own colour: FORMA's CURVES accent as a cosine ramp,
// brightest near t = 0 and t = 1, near-black around t = 0.5.
vector forma_ramp(float t){
  return set(
    0.11 + 0.1196 * cos(6.28318530718 * (t + 0)),
    0.46 + 0.5 * cos(6.28318530718 * (t + 0.05)),
    0.2453 + 0.2667 * cos(6.28318530718 * (t + 0.1)));
}

// The pen closes after r full turns of the rolling contact — θ spans 2π·r,
// tracing every loop of the figure once, as the plate does.
int forma_n = 5000;

float TAU = 6.28318530718;
int   rr = int(rint(p_r));
float ratio = (p_R + p_r) / p_r;
float turns = TAU * float(rr);
int prim = addprim(0, "polyline");
for (int i = 0; i <= forma_n; i++){
    float th = float(i) / float(forma_n) * turns;
    float x = (p_R + p_r) * cos(th) - p_h * cos(ratio * th);
    float y = (p_R + p_r) * sin(th) - p_h * sin(ratio * th);
    // canvas y runs down; negated so the figure sits as the plate shows it
    int pt = addpoint(0, set(x, -y, 0.0));
    // colour sweeps the bright lobe of the ramp along the curve
    float u = float(i) / float(forma_n);
    setpointattrib(0, "Cd", pt, forma_ramp(0.8 + 0.3 * u));
    addvertex(0, prim, pt);
}

AFTER EFFECTS · EXPRESSION

The same published mathematics as a Shape Layer path expression. Paste it onto a Path property; every constant is the published value plus a Slider Control named _tweak, so a bare paste already draws the figure and each slider moves one constant in its own units. Trim Paths is the comet.

// FORMA — PL. 03 · EPITROCHOID — Albrecht Dürer, 1525 · Ptolemaic tradition
//   x(θ) = (R+r)·cos θ − h·cos(((R+r)/r)·θ)
//   y(θ) = (R+r)·sin θ − h·sin(((R+r)/r)·θ)
// After Effects port — paste onto a Shape Layer's Path property
// (Contents › Shape › Path). Written from the published mathematics, not
// adapted from any code. Constants arrive at their published values; add a
// Slider Control (Effect › Expression Controls) named <k>_tweak and that
// constant moves in its own units, starting at 0 — the published figure.
// The plate's comet and its reveal are Trim Paths; the stroke colour is
// FORMA's CURVES accent, #3DFF88. Animation runs on time.
// https://forma-gen.com/#plate=epitrochoid

// A missing slider reads 0, so a bare paste already draws the figure.
function forma_tweak(n){ try { return effect(n)("Slider"); } catch (e){ return 0; } }
var p_R = 8 + forma_tweak("R_tweak");     // R — fixed circle · live 1 .. 14
var p_r = 3 + forma_tweak("r_tweak");     // r — rolling circle · live 1 .. 14
var p_h = 5 + forma_tweak("h_tweak");     // h — pen offset · live 1 .. 14

// The frame: the plate's W × H canvas is this comp, with the origin at the
// layer's anchor; canvas y already runs down, as After Effects' does.
var forma_W = thisComp.width, forma_H = thisComp.height, forma_t = time;
var forma_phase = 0.9555596034042537;   // this plate's own fixed phase, as the page has it
function forma_pt(x, y){ return [x - forma_W / 2, y - forma_H / 2]; }

// A pen at distance h from the centre of a circle of radius r rolling outside
// a fixed circle of radius R, over r full turns so the curve closes. Scaled so
// the widest reach, R + r + h, fills 0.46 of the shorter side.
var s = Math.min(forma_W, forma_H) * 0.46 / (p_R + p_r + p_h);
var ratio = (p_R + p_r) / p_r, turns = Math.PI * 2 * p_r, N = 5000;
var pts = [];
for (var i = 0; i <= N; i++){
  var th = i / N * turns;
  pts.push(forma_pt(forma_W / 2 + s * ((p_R + p_r) * Math.cos(th) - p_h * Math.cos(ratio * th)),
                    forma_H / 2 + s * ((p_R + p_r) * Math.sin(th) - p_h * Math.sin(ratio * th))));
}
createPath(pts, [], [], true);