Expand description
The cellular system: a discrete cellular automaton stepped on a ping-pong
grid (ADR-0012), with every rule space it runs as a named family
selected by [cellular] family (ADR-0180 rule 1).
life_like is the birth/survival family: a dead cell with k live
neighbours is born when bit k of birth is set, and a live one survives
when bit k of survive is. Conway’s Life is birth = 8, survive = 12.
larger_than_life widens the neighbourhood to a box of side
2 * radius + 1 and replaces the masks with two intervals over the box’s
filled fraction: a dead cell is born inside birth_lo..=birth_hi, a live one
survives inside survive_lo..=survive_hi. The fractions are turned into
whole counts on the CPU (interval_counts), so the shader compares
integers. The box is summed separated — a row pass, then a column sum in
the step pass — at 2 * (2r + 1) reads a cell rather than (2r + 1)^2, and
radius is capped by the tier on top of that
(TierConfig::cellular_radius).
cyclic is the cyclic automaton: a cell holds one of states colours and
advances to the next round the cycle when at least threshold of its eight
neighbours already hold it. There is no dead state, so every cell is lit and
its palette coordinate is its state index over states, with no remap.
§The grid is content, not a resolution
[cellular] grid is how many cells a side holds, and a pattern is a fixed
number of cells — a glider is five — so the grid decides how large every
pattern looks. It is declared by the preset and never follows the window.
The tier caps it (TierConfig::cellular_grid),
and because the cap changes content rather than density, a grid clamped to
it is announced — returned from configure as a
CapOverflow — never silently reduced. A bound
radius past the tier’s cap is announced the same way, per frame, through
Scene::mirror_overflow.
The present is a plain normalized stretch of the grid over the target and
computes no screen-destined geometry, so there is no aspect for it to take
from the wrong place (ADR-0037).
§Generations, not frames
step_rate is in generations per second. GenerationClock integrates it
over the injected dt and hands render a whole number of generations to
run, so the automaton advances by the same count in the same wall time at
any refresh rate. Because an automaton is path-dependent, two runs at
different rates diverge for good — which is the meaning of the parameter,
not a defect of it.
§Every cell is drawn from the seed
The field is seeded, and every reseed refills a disc of it, from an
integer hash of the cell’s coordinates and a seed derived from the preset’s
pinned salt (ADR-0051) — never from a clock. The hash is u32 arithmetic,
exact on every adapter, and each rule step is integer counting over exact
texel values, so a field is a pure function of its config and the sequence
of bound values and dts it was driven with.
§The age channel
A binary field reads as noise; what reads as structure is its history. So
the state texture’s second channel counts the generations since each cell
last changed state, and the present paints a dead cell by it: it fades out
over trail generations while sliding age_tint of the way along the
palette. trail = 0 is the binary field exactly.
GPU resources are built lazily, on first render, as the reaction-diffusion scene’s are and for its reason: a capture that never activates this scene never builds them, so the WARP software adapter the golden suite captures on never holds them beside another scene’s.
Structs§
- Cellular
Config [cellular]— the structural configuration, fixed while the preset is loaded and delivered throughScene::configure.- Cellular
Scene - A discrete cellular automaton on a ping-pong grid, driven by named preset
parameters and one
[cellular]table.
Enums§
- Cellular
Family - Which rule space the automaton runs (ADR-0180 rule 1).
Constants§
- DEFAULT_
GRID - The grid an absent
[cellular] gridmeans:reaction_diffusion’s own 256, for the same reason that scene pins it — pattern scale is content. - FAMILY_
PARAMS - Every parameter only some families read (ADR-0180 rule 4), with the range
that reads there — so the generated reference prints
birthaslife_like’s and inert elsewhere. A parameter missing from here reads the same on every family. - MAX_
GENERATIONS_ PER_ FRAME - The most generations one frame encodes. A stall would otherwise queue unbounded passes (the accumulator spiral ADR-0012 names); past this the backlog is dropped and the automaton briefly slows rather than racing. At 30 fps it still carries 240 generations a second.
- MAX_
GRID - The largest grid the loader accepts: a million cells, two 8-byte texels each across the ping-pong pair, 16 MB.
- MAX_
RADIUS - The largest
radiusany tier lets alarger_than_lifepreset ask for — the top of its declared range, and the constant the row and column loops run to. - MAX_
RULE - The largest rule bitmask: one bit for each neighbour count
0..=8. - MAX_
STATES - The most colours a
cycliccycle may hold. A half float holds every state index exactly far past this; the bound is where a cycle stops reading as a cycle across a 256-texel palette. - MAX_
STEP_ RATE - The fastest
step_ratethe clock integrates, in generations per second. Well past the declared range’s top, so it bounds a runaway binding rather than an author. - MAX_
THRESHOLD - The most neighbours a
cyclicthreshold can ask for: all eight. - MAX_
TRAIL - The longest
trailthe present reads, in generations: the age channel’s own ceiling, since a wake cannot outlast the count it is read from. - MIN_
GRID - The smallest grid the loader accepts. Below it a glider’s wake meets its own head on a torus before it has read as motion.
- PARAMS
- The parameter names this scene consumes — the vocabulary a preset binding is
checked against at load (ADR-0020). Keep in sync with
set_parambelow;declared_params_match_set_paramincore/tests/suite/preset.rsfails if the two drift.