Skip to main content

Module shape_collage

Module shape_collage 

Source
Expand description

Flat opaque elements painted on their own paper (ADR-0123).

Every other scene in this engine draws light: premultiplied additive colour into a linear-light composite, where nothing is in front of anything (ADR-0018, ADR-0046, ADR-0056). This one draws a graphic. A pixel starts at the paper colour and walks an array of elements in array order, compositing each with over, so a black bar genuinely sits in front of a red one and the array index is the depth. There is no depth buffer, no sort, and no ordering state — the painter’s loop is the whole mechanism.

§Three engine properties this look rests on, and breaks without

Measured in ADR-0123’s Context; named here because each is a thing an unrelated edit could take away.

  • A fullscreen scene emitting alpha 1 holds the backdrop out entirely — not darkened, absent. So a scene covering every pixel owns its own ground.
  • The tonemap is exactly the identity below KNEE = 0.6 (ADR-0046), so an element at or under it leaves the post chain unshaded. Below the knee the pipeline is a no-op — flatness is not argued for against it.
  • Bloom’s threshold sits above that knee, so a canvas living under it gets no halo and hard edges stay hard, at no cost and no parameter.

The authored hex does reach the display, and the knee is why. A palette stop is sRGB and is decoded to light once at the load boundary (ADR-0151), so the load decode and the display encode are inverses and everything between them is the identity below the knee: an element written #494949 presents as #494949. The cap therefore has an authored form — 0.6 of light is sRGB byte 0xcb, the brightest channel any element here may carry. Same curve for every element, no shading and no halo: that is the property the look rests on. Nor does the curve give paper at pure white — f(1.0) = 0.800, and 1.0 is asymptotically unreachable, so both reference grounds are off-white by construction.

§Colour is a palette coordinate

An element stores a coordinate, never an RGB triple, so every palette, custom stop and A/B crossfade in docs/preset-palettes.md applies here on arrival with no special case (ADR-0086, ADR-0102). The paper takes a coordinate too, and deliberately a raw one: color_span and palette_shift move the elements’ colours and must not drag the ground along with them.

§The aspect comes from the render target (ADR-0037)

This scene computes screen-destined geometry from a normalized space, which is exactly the shape of the bug that has shipped three times in this repo. The canvas is built in square units by stretching NDC x by the render target’s aspect, so a circle element is round at every window shape. tests renders at 1280x800 and measures a circle’s own width against its height: 1920x1080 and this box’s 2048x1152 are both exactly 16:9, where no test can tell a target-derived aspect from a grid-derived one, and 16:10 is the case that discriminates.

§The cost, and where its bound lives

The draw is O(elements) per pixel and the bounding-box reject removes the distance evaluation but not the loop step, so a wavefront walks every element regardless. The bound is TierConfig::collage_elements — measured by core/tests/collage_cost.rs, not assumed.

Two things here exist to keep that loop cheap and are worth not tidying away: the rotation arrives as a precomputed cosine and sine pair rather than an angle (no per-pixel-per-element trig, and no dependence on sin’s implementation-defined precision, which ADR-0096 disqualifies elsewhere for the same reason), and the axis-aligned bounding box is computed CPU-side and tight for every kind — a loose box is a silent cost regression that no picture would show.

Structs§

ShapeCollageScene
The flat-graphic canvas: opaque elements over their own paper, composited in one fullscreen distance-field pass.

Constants§

PARAMS
The parameter names this scene consumes — the vocabulary a preset binding is checked against at load (ADR-0020). Keep in sync with set_param below; declared_params_match_set_param in core/tests/preset.rs fails if the two drift.