PL. 45 · CURVES / PARAMETRIC / LINEAR CURVATURE
Euler Spiral
Leonhard Euler, 1744 · Alfred Cornu, 1874
OPEN THE LIVE PLATE ▸DEFINITION
κ(s) = sᵏ⁻¹, k = 2 is the Euler spiral x(s) = ∫₀ˢ cos(uᵏ/k) du y(s) = ∫₀ˢ sin(uᵏ/k) du
NOTES
The curve whose curvature grows in exact proportion to the distance travelled along it — which is what a car traces when the driver turns the wheel at a constant rate. Euler found it studying a coiled spring; Cornu rediscovered it in the intensity integrals of diffraction. Railway and motorway engineers use the middle of it to ease straights into circular arcs, and each arm winds forever toward its Fresnel limit point without reaching it.
PROVENANCE
- Origin
- L. Euler, additamentum to "Methodus inveniendi lineas curvas", 1744; A. Cornu, on diffraction, 1874
- Standing
- Public domain — 18th-century analysis
- Constants
- Arc length sets how far each arm winds into its eye
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. 45 · EULER SPIRAL — Leonhard Euler, 1744 · Alfred Cornu, 1874
// κ(s) = sᵏ⁻¹, k = 2 is the Euler spiral
// x(s) = ∫₀ˢ cos(uᵏ/k) du
// y(s) = ∫₀ˢ sin(uᵏ/k) du
// 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=clothoid
float p_len = 4.6 + chf('len_tweak'); // arc length · live 2.5 .. 7
float p_expo = 2 + chf('expo_tweak'); // k — curvature exponent · live 1.2 .. 4
// 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 generalised Fresnel pair integrated with midpoint steps: curvature
// grows as s^(k−1), and k = 2 is the classic Euler spiral. The second arm is
// the first rotated half a turn — exact at k = 2, where the integrand pair
// is odd, and kept as the figure's symmetry elsewhere.
int forma_n = 1100;
float ds = p_len / float(forma_n);
float hx[], hy[];
float x = 0.0, y = 0.0;
for (int i = 0; i <= forma_n; i++){
push(hx, x); push(hy, y);
float s = (float(i) + 0.5) * ds;
x += cos(pow(s, p_expo) / p_expo) * ds;
y += sin(pow(s, p_expo) / p_expo) * ds;
}
// second arm first, reversed, so the polyline runs end to end through zero
int prim = addprim(0, "polyline");
int total = 2 * forma_n + 1;
for (int i = 0; i < total; i++){
int j = i - forma_n; // −n … n
float sgn = j < 0 ? -1.0 : 1.0;
int a = abs(j);
// canvas y runs down; negated so the spiral winds as the plate shows it
int pt = addpoint(0, set(sgn * hx[a], -sgn * hy[a], 0.0));
// colour sweeps the bright lobe of the ramp along the curve
float u = float(i) / float(total - 1);
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. 45 · EULER SPIRAL — Leonhard Euler, 1744 · Alfred Cornu, 1874
// κ(s) = sᵏ⁻¹, k = 2 is the Euler spiral
// x(s) = ∫₀ˢ cos(uᵏ/k) du
// y(s) = ∫₀ˢ sin(uᵏ/k) du
// 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=clothoid
// 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_len = 4.6 + forma_tweak("len_tweak"); // arc length · live 2.5 .. 7
var p_expo = 2 + forma_tweak("expo_tweak"); // k — curvature exponent · live 1.2 .. 4
// 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.5242642718367279; // 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]; }
// The generalised Euler spiral: curvature grows as s^(k−1), so the tangent
// angle is s^k / k, and the curve is the Fresnel pair integrated by midpoint
// steps — 1,100 of them over the arc length len, exactly as the page does. The
// second arm is the first rotated half a turn: exact at k = 2, where the
// integrand pair is odd, and kept as the figure's symmetry elsewhere. Fitted to
// 0.8 of the frame. The page's comet laps the whole curve in 20 s: Trim Paths.
var N = 1100, ds = p_len / N, k = p_expo;
var half = [], x = 0, y = 0;
for (var i = 0; i <= N; i++){
half.push([x, y]);
var s = (i + 0.5) * ds, a = Math.pow(s, k) / k;
x += Math.cos(a) * ds;
y += Math.sin(a) * ds;
}
var raw = [];
for (var j = half.length - 1; j >= 1; j--) raw.push([-half[j][0], -half[j][1]]);
for (var m = 0; m < half.length; m++) raw.push(half[m]);
var minx = 1e9, maxx = -1e9, miny = 1e9, maxy = -1e9;
for (var n = 0; n < raw.length; n++){
var q = raw[n];
if (q[0] < minx) minx = q[0]; if (q[0] > maxx) maxx = q[0];
if (q[1] < miny) miny = q[1]; if (q[1] > maxy) maxy = q[1];
}
var sc = Math.min(forma_W * 0.8 / ((maxx - minx) || 1), forma_H * 0.8 / ((maxy - miny) || 1));
var ox = (forma_W - (maxx - minx) * sc) / 2 - minx * sc;
var oy = (forma_H - (maxy - miny) * sc) / 2 - miny * sc;
var pts = [];
for (var r = 0; r < raw.length; r++) pts.push(forma_pt(ox + raw[r][0] * sc, oy + raw[r][1] * sc));
createPath(pts, [], [], false);