DEFINITION
four affine maps fᵢ(x,y) = Aᵢ·(x,y)ᵀ + bᵢ applied at random with fixed probabilities
NOTES
An iterated function system: pick one of four affine transforms at random, apply it, plot the point, repeat. The attractor is the fern regardless of where you start. One map draws the stem, one the left fronds, one the right, and one — used 85% of the time — shrinks and rotates everything upward.
PROVENANCE
- Origin
- M. Barnsley, "Fractals Everywhere", 1988
- Standing
- The IFS coefficients are mathematical facts, not expression
- Constants
- The stem map fires 1% of the time
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. 15 · BARNSLEY FERN — Michael Barnsley, 1988
// four affine maps fᵢ(x,y) = Aᵢ·(x,y)ᵀ + bᵢ
// applied at random with fixed probabilities
// 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=fern
float p_lean = 0 + chf('lean_tweak'); // frond angle · live -0.1 .. 0.1
float p_n = 2500 + chf('n_tweak'); // points per shot · live 500 .. 6000
// 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)));
}
// The IFS replayed as a point cloud: one of four affine maps at random each
// step, at Barnsley's own probabilities — 1% the stem, 85% the upward
// shrink-and-rotate, 7% each frond. The plate plots p_n points a shot;
// forma_shots shots is the same exposure fully developed, landing the
// default on the other exposures' ~120k budget. The walk's first landings
// are discarded, as the plate's expose() does. Deterministic: the choice is
// random(counted seed), so every cook is the same fern. The fern's own y
// already runs up — the plate's window flips it onto canvas — so nothing
// is negated here.
int forma_shots = 48;
int forma_skip = 20;
int total = int(rint(p_n)) * forma_shots;
float x = 0.0, y = 0.0;
int rc = 7; // the plate's own seed, counted upward
for (int i = 0; i < total + forma_skip; i++){
float r = random(rc); rc++;
float nx, ny;
if (r < 0.01) { nx = 0.0; ny = 0.16 * y; }
else if (r < 0.86){ nx = 0.85 * x + (0.04 + p_lean) * y; ny = -0.04 * x + 0.85 * y + 1.6; }
else if (r < 0.93){ nx = 0.20 * x - 0.26 * y; ny = 0.23 * x + 0.22 * y + 1.6; }
else { nx = -0.15 * x + 0.28 * y; ny = 0.26 * x + 0.24 * y + 0.44; }
x = nx; y = ny;
if (i < forma_skip) continue;
int pt = addpoint(0, set(x, y, 0.0));
// colour sweeps the bright lobe of the ramp across the exposure
float u = float(i) / float(total + forma_skip);
setpointattrib(0, "Cd", pt, forma_ramp(0.8 + 0.3 * u));
}