DEFINITION
r(φ) = ( |cos(mφ/4)/a|^n₂ + |sin(mφ/4)/b|^n₃ )^(−1/n₁)
NOTES
A generalisation of the superellipse that Gielis proposed as a single equation behind starfish, diatoms, flowers and shells. Four constants take it from circle to polygon to bloom. It is the one entry here with a genuine legal history: Gielis patented pattern synthesis using the operator, and the patent was enforced aggressively enough that it shadowed a well-known video game.
PROVENANCE
- Origin
- Johan Gielis, American Journal of Botany 90(3), 2003
- Patent
- EP1177529 / US7620527 — expired 10 May 2020
- Standing
- Free to use. The patent lapsed; formulas are not copyrightable.
- Source
- doi:10.3732/ajb.90.3.333
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. 04 · SUPERFORMULA — Johan Gielis, 2003
// r(φ) = ( |cos(mφ/4)/a|^n₂ + |sin(mφ/4)/b|^n₃ )^(−1/n₁)
// 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=superformula
float p_m = 7 + chf('m_tweak'); // m — rotational symmetry · live 1 .. 20
float p_n1 = 0.6 + chf('n1_tweak'); // n₁ · live 0.2 .. 8
float p_n2 = 1.7 + chf('n2_tweak'); // n₂ · live 0.2 .. 8
float p_n3 = 1.7 + chf('n3_tweak'); // n₃ · live 0.2 .. 8
float p_a = 1 + chf('a_tweak'); // a — first semi-axis · live 0.2 .. 3
float p_b = 1 + chf('b_tweak'); // b — second semi-axis · live 0.2 .. 3
// 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)));
}
// Gielis's operator in polar form, one closed sweep of φ. a and b are the
// semi-axes the published operator divides by; unequal values are where the
// stretched starfish and diatom forms live. A non-finite r (n₁ near zero at
// a cusp) plots as the origin, exactly as the plate treats it.
int forma_n = 2400;
float TAU = 6.28318530718;
int prim = addprim(0, "polyline");
for (int i = 0; i <= forma_n; i++){
float phi = float(i) / float(forma_n) * TAU;
float ta = pow(abs(cos(p_m * phi / 4.0) / p_a), p_n2);
float tb = pow(abs(sin(p_m * phi / 4.0) / p_b), p_n3);
float r = pow(ta + tb, -1.0 / p_n1);
if (!isfinite(r)) r = 0.0;
// canvas y runs down; negated so the figure sits as the plate shows it
int pt = addpoint(0, set(r * cos(phi), -r * sin(phi), 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
// FORMA — PL. 04 · SUPERFORMULA — Johan Gielis, 2003
// r(φ) = ( |cos(mφ/4)/a|^n₂ + |sin(mφ/4)/b|^n₃ )^(−1/n₁)
// 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=superformula
// 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_m = 7 + forma_tweak("m_tweak"); // m — rotational symmetry · live 1 .. 20
var p_n1 = 0.6 + forma_tweak("n1_tweak"); // n₁ · live 0.2 .. 8
var p_n2 = 1.7 + forma_tweak("n2_tweak"); // n₂ · live 0.2 .. 8
var p_n3 = 1.7 + forma_tweak("n3_tweak"); // n₃ · live 0.2 .. 8
var p_a = 1 + forma_tweak("a_tweak"); // a — first semi-axis · live 0.2 .. 3
var p_b = 1 + forma_tweak("b_tweak"); // b — second semi-axis · live 0.2 .. 3
// 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.9705028349999338; // 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]; }
// Gielis's superformula: r(φ) = (|cos(mφ/4)/a|^n₂ + |sin(mφ/4)/b|^n₃)^(−1/n₁).
// Two passes — one to find the largest radius so the figure always fits
// (0.44 of the shorter side), one to place the points; a non-finite r reads 0.
var N = 2400, rs = [], max = 0;
for (var i = 0; i <= N; i++){
var phi = i / N * Math.PI * 2;
var ta = Math.pow(Math.abs(Math.cos(p_m * phi / 4) / p_a), p_n2);
var tb = Math.pow(Math.abs(Math.sin(p_m * phi / 4) / p_b), p_n3);
var r = Math.pow(ta + tb, -1 / p_n1);
if (!isFinite(r)) r = 0;
if (r > max) max = r;
rs.push(r);
}
var s = Math.min(forma_W, forma_H) * 0.44 / (max || 1);
var pts = [];
for (var j = 0; j <= N; j++){
var ph = j / N * Math.PI * 2;
pts.push(forma_pt(forma_W / 2 + s * rs[j] * Math.cos(ph), forma_H / 2 + s * rs[j] * Math.sin(ph)));
}
createPath(pts, [], [], true);