Skip to the plate
FORMA PUBLIC DOMAIN GENERATIVE ATLAS / ED. 0.28
Plate 72, Lévy C Curve: a still of the rewriting / symmetric fold plate as the atlas renders it, in the fractals accent.

PL. 72  ·  FRACTALS / REWRITING / SYMMETRIC FOLD

Lévy C Curve

Ernesto Cesàro, 1906 · Georg Faber, 1910 · Paul Lévy, 1938

OPEN THE LIVE PLATE ▸

DEFINITION

F → +F−−F+   (turn θ, 45° is the C)
every segment folds outward each generation
dimension → 2 as the folds close

NOTES

Fold every segment of a line into a right-angle tent, forever, and the C curve appears — Cesàro described it first and Faber analysed the same family, both for its differentiability; Lévy was the one who set out its self-similarity and the geometric construction, and the name went to him. It is the koch construction’s wilder sibling: koch’s bump keeps the curve simple, while the C’s symmetric fold makes it cross itself into a paved, furred braid. The angle walks the Cesàro–Faber family — de Rham curves, with the classical C at 45°.

PROVENANCE

Origin
E. Cesàro, "Fonctions continues sans dérivée", Archiv der Mathematik und Physik (3) 38 (1906), 57–63; G. Faber, 1910, on the same family; P. Lévy, "Les courbes planes ou gauches et les surfaces composées de parties semblables au tout", Journal de l’École Polytechnique, 1938, 227–247 and 249–291, which is where the self-similar construction and the name come from
Standing
Public domain — early 20th-century geometry
Constants
Generations are locked: each doubles the segment count. The fold angle is free — the whole range is alive

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. 72 · LÉVY C CURVE — Ernesto Cesàro, 1906 · Georg Faber, 1910 · Paul Lévy, 1938
//   F → +F−−F+   (turn θ, 45° is the C)
//   every segment folds outward each generation
//   dimension → 2 as the folds close
// 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=levyc

float p_gens = 12 + chf('gens_tweak');      // generations · live 8 .. 13
float p_ang  = 45 + chf('ang_tweak');       // θ — fold (deg) · live 30 .. 58

// The plate's own colour: FORMA's FRACTALS 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.46 + 0.5 * cos(6.28318530718 * (t + 0)),
    0.1389 + 0.151 * cos(6.28318530718 * (t + 0.05)),
    0.1912 + 0.2078 * cos(6.28318530718 * (t + 0.1)));
}

// F → +F−−F+ with turn θ: every segment folds outward into a symmetric
// tent, forever — the classical C at 45°, the wider Cesàro–Faber family
// either side. Tokens are integers (0 F, 1 +, 2 −); the heading accumulates
// exactly as the plate's own turtle does. Steps are unit length — scale to
// taste.
int forma_ruleF[] = {1, 0, 2, 2, 0, 1};

int gens = int(rint(p_gens));
int seq[] = {0};
for (int g = 0; g < gens; g++){
    int next[];
    foreach (int sym; seq){
        if (sym == 0) foreach (int r; forma_ruleF) push(next, r);
        else          push(next, sym);
    }
    seq = next;
}

float turn = p_ang * 3.14159265359 / 180.0;
float ang = 0.0, x = 0.0, y = 0.0;
int steps = 0;
foreach (int sym; seq) if (sym == 0) steps++;
int prim = addprim(0, "polyline");
int pt = addpoint(0, set(0.0, 0.0, 0.0));
setpointattrib(0, "Cd", pt, forma_ramp(0.8));
addvertex(0, prim, pt);
int i = 0;
foreach (int sym; seq){
    if (sym == 1){ ang += turn; continue; }
    if (sym == 2){ ang -= turn; continue; }
    x += cos(ang);  y += sin(ang);  i++;
    // canvas y runs down; negated so the braid sits as the plate shows it
    pt = addpoint(0, set(x, -y, 0.0));
    // colour sweeps the bright lobe of the ramp along the path
    setpointattrib(0, "Cd", pt, forma_ramp(0.8 + 0.3 * float(i) / float(steps)));
    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. 72 · LÉVY C CURVE — Ernesto Cesàro, 1906 · Georg Faber, 1910 · Paul Lévy, 1938
//   F → +F−−F+   (turn θ, 45° is the C)
//   every segment folds outward each generation
//   dimension → 2 as the folds close
// 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 FRACTALS accent, #FF4D6A. Animation runs on time.
// https://forma-gen.com/#plate=levyc

// 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_gens = 12 + forma_tweak("gens_tweak");    // generations · live 8 .. 13
var p_ang  = 45 + forma_tweak("ang_tweak");     // θ — fold (deg) · live 30 .. 58

// 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.23880349309183657;   // 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 Lévy C curve as an L-system: F → +F−−F+, turning by the slider's angle
// (45° is Lévy's; the slider makes it a family), one unit step per F. The
// generation count is 2^gens segments — 4,096 at the published 12. Fitted to
// 0.82 of the frame; the page grows it over 21 s — Trim Paths, End 0 → 100%.
var gens = Math.round(p_gens), turn = p_ang * Math.PI / 180;
var s = 'F';
for (var g = 0; g < gens; g++) s = s.replace(/F/g, '+F--F+');
var ang = 0, x = 0, y = 0;
var raw = [[0, 0]];
for (var i = 0; i < s.length; i++){
  var ch = s.charAt(i);
  if (ch === '+') ang += turn;
  else if (ch === '-') ang -= turn;
  else { x += Math.cos(ang); y += Math.sin(ang); raw.push([x, y]); }
}
var minx = 1e9, maxx = -1e9, miny = 1e9, maxy = -1e9;
for (var j = 0; j < raw.length; j++){
  var q = raw[j];
  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.82 / ((maxx - minx) || 1), forma_H * 0.82 / ((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 m = 0; m < raw.length; m++) pts.push(forma_pt(ox + raw[m][0] * sc, oy + raw[m][1] * sc));
createPath(pts, [], [], false);