Cascade effects through a chain

renderChain runs a list of stages in order: stage 0 works on the loaded
sources, every later one on the output of the stage before it, so blur
after scanlines softens the scanlines rather than the photographs. The
effects themselves are untouched — none of them knows whether its input
came from disk. All stages share one Rng and run in sequence, so a seed
still reproduces the cascade byte for byte.

The sidebar grows an "Effect chain" list: add a stage from a menu built
from the registry, reorder it, remove it, and edit the selected stage's
params underneath. Each stage keeps its own values. The CLI takes the
same chain from repeated -e, with every flag going to the stage whose
effect declares it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Si
2026-09-18 15:21:29 +02:00
co-authored by Claude Opus 5
parent ee4b1038ac
commit 89985cb989
10 changed files with 409 additions and 75 deletions
+19 -2
View File
@@ -14,7 +14,7 @@ Everything is warning-clean under `-Wall -Wextra -Wpedantic`; keep it that way.
The test suite is a single hand-rolled binary (no framework), so there is no
per-test filter: run `./build/tests/effector_tests` directly to see the section
headings (`rng`, `registry`, `scanlines`, `batch loading`) and the `ok`/`FAIL`
headings (`rng`, `registry`, `scanlines`, `blur`, `chain`, `batch loading`) and the `ok`/`FAIL`
lines. To iterate on one area, comment out the other blocks in
`tests/core_tests.cpp` or add a new `std::cout << "section\n"` block — checks are
plain `check(condition, "description")` calls and `main()` returns the failure
@@ -50,7 +50,24 @@ add a `ParamSpec` instead. Values travel as `ParamValues` (a
Adding an effect: new `src/effects/<name>.{hpp,cpp}` subclassing `Effect`, one
line in the registry list in `src/core/registry.cpp`, one line in the
`effector_core` sources in `CMakeLists.txt`. Nothing else.
`effector_core` sources in `CMakeLists.txt`. Nothing else — it shows up in the
CLI, in the chain menu and as a cascade stage on its own.
### Effects cascade
Effects are never applied on their own: `renderChain` (`src/core/chain.cpp`)
runs a `std::vector<ChainStage>` in order, handing stage 0 the loaded sources
and every later stage the previous stage's output as its *only* source. That is
the whole of the cascade — an effect neither knows nor cares whether its input
came from disk. All stages share one `Rng` and run strictly in sequence, so a
seed still reproduces the cascade byte for byte.
The interface builds the chain in the sidebar ("Effect chain": add from the
menu, reorder, remove, one stage selected at a time with its params below), the
CLI builds it from repeated `-e`: `-e scanlines --strip-height 6 -e blur
--radius 5`. Flags share one namespace across all effects, so `buildChain`
(`src/cli/options.cpp`) hands each flag to the stage whose effect declares it —
two effects must not declare the same param key.
### Two link targets, deliberately