Skip to the plate
FORMA PUBLIC DOMAIN GENERATIVE ATLAS / ED. 0.28
Plate 43, Maurer Rose: a still of the polar / chord walk plate as the atlas renders it, in the curves accent.

PL. 43  ·  CURVES / POLAR / CHORD WALK

Maurer Rose

Peter M. Maurer, 1987

OPEN THE LIVE PLATE ▸

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 _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. 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);