DEFINITION
rose: r = sin(n·θ) walk: θₖ = k·d°, k = 0 … 360 join consecutive walk points with chords
NOTES
Take a rose curve, but instead of drawing it, walk around it in strides of d degrees and join the stops with straight chords. Maurer published it as a classroom exercise in line art; the figure changes completely with d, because the walk is really modular arithmetic on the circle. Prime-ish strides weave dense lace, while divisors of 360 collapse the whole thing to a polygon.
PROVENANCE
- Origin
- P. M. Maurer, "A Rose is a Rose…", The American Mathematical Monthly 94(7), 1987
- Standing
- Public domain — a construction on an 18th-century curve
- Constants
- 361 steps always return to the start, so the walk is a closed loop
- Source
- doi:10.2307/2322215
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. 43 · MAURER ROSE — Peter M. Maurer, 1987
// rose: r = sin(n·θ)
// walk: θₖ = k·d°, k = 0 … 360
// join consecutive walk points with chords
// 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=maurer
float p_n = 6 + chf('n_tweak'); // n — rose harmonic · live 2 .. 8
float p_d = 71 + chf('d_tweak'); // d — stride (deg) · live 1 .. 179
// 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)));
}
// Two layers, as the plate draws them: the rose itself faint underneath, so
// the walk reads as chords of something — then the Maurer walk, 360 strides
// of d° joined point to point. Alpha carries the plate's own layering.
int forma_rose = 720;
float TAU = 6.28318530718;
int n = int(rint(p_n));
// the rose, faint
int under = addprim(0, "polyline");
for (int i = 0; i <= forma_rose; i++){
float th = float(i) / float(forma_rose) * TAU;
float r = sin(float(n) * th);
// canvas y runs down; negated so the petals sit as the plate shows them
int pt = addpoint(0, set(r * cos(th), -r * sin(th), 0.0));
setpointattrib(0, "Cd", pt, forma_ramp(0.95));
setpointattrib(0, "Alpha", pt, 0.14);
addvertex(0, under, pt);
}
// the walk: closed after 360 strides, since 360·d° is a whole number of turns
int walk = addprim(0, "polyline");
for (int k = 0; k <= 360; k++){
float th = float(k) * p_d * TAU / 360.0;
float r = sin(float(n) * th);
int pt = addpoint(0, set(r * cos(th), -r * sin(th), 0.0));
// colour runs along the walk inside the bright lobe of the ramp
float u = float(k) / 360.0;
setpointattrib(0, "Cd", pt, forma_ramp(0.8 + 0.3 * u));
setpointattrib(0, "Alpha", pt, 1.0);
addvertex(0, walk, 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
// FORMA — PL. 43 · MAURER ROSE — Peter M. Maurer, 1987
// rose: r = sin(n·θ)
// walk: θₖ = k·d°, k = 0 … 360
// join consecutive walk points with chords
// 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.
// This plate draws 2 separate paths at its published constants:
// duplicate the group (Contents › Group) that many times and each copy draws
// its own part, read from its position in the layer. A Slider Control named
// "part" on the layer pins one instead.
// https://forma-gen.com/#plate=maurer
// 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_n = 6 + forma_tweak("n_tweak"); // n — rose harmonic · live 2 .. 8
var p_d = 71 + forma_tweak("d_tweak"); // d — stride (deg) · live 1 .. 179
// 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.30121422722004354; // 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]; }
function forma_partIndex(){
try { return Math.round(effect("part")("Slider")); } catch (e){}
try { return thisProperty.propertyGroup(3).propertyIndex - 1; } catch (e){ return 0; }
}
var forma_part = forma_partIndex();
// Part 0 is the Maurer rose: 360 chords of the rose r = R·sin(nθ), each
// stepping the angle by the stride d in degrees, so the polygon closes after a
// full 360 steps. Part 1 is the underlying rose itself, the faint guide the
// page draws beneath — the second copy of the group; lower its opacity.
// parts: 2
var R = Math.min(forma_W, forma_H) * 0.44, cx = forma_W / 2, cy = forma_H / 2;
var pts = [];
if (forma_part === 1){
for (var i = 0; i <= 720; i++){
var th = i / 720 * Math.PI * 2, r = R * Math.sin(p_n * th);
pts.push(forma_pt(cx + r * Math.cos(th), cy + r * Math.sin(th)));
}
} else {
for (var j = 0; j <= 360; j++){
var t2 = j * p_d * Math.PI / 180, r2 = R * Math.sin(p_n * t2);
pts.push(forma_pt(cx + r2 * Math.cos(t2), cy + r2 * Math.sin(t2)));
}
}
createPath(pts, [], [], true);