Files
DarkRoom/core/dr-pipeline/ops/exposure.yaml
dtourolle 7421837c8a Let an operation say what it is about, so the panel can group without naming
Tool tabs need a taxonomy, and the taxonomy was the problem: a table in
`ui/` mapping operation to tab breaks FR-DEV-3a, and a `group:` field risks
what `ui-refinement.md` condemned `starts-group` for — the core deciding
where the panel draws things.

`Attribute` threads the needle. It says what an operation *is* — tone,
colour, detail, optics, geometry, effect — which is the same category as
`ParamKind` and squarely on the core's side of ARCH §4.3a's line. What is
drawn, where it sits and whether it is visible stay the frontend's. There is
no attribute for "the third tab", the enum's order is declaration order
rather than screen order, and a frontend may render these as tabs, as
headings, or ignore them.

The payoff is that a tab strip can be *derived*: the groups are the
attributes present in the capability list, so the interface names no
operation and needs no table to keep in step. An operation joins the right
group by declaring what it is, which is the one thing its author is well
placed to say.

Plural, because the tone curve is genuinely both — an RGB curve is tonal and
the per-channel curves are chromatic, and filing it under one would hide it
from half the people looking for it.

Required and non-empty, enforced in `build.rs`, and the failure was checked
by removing the line rather than assumed. An operation with no attribute is
invisible to a panel that groups by them; a build that stops costs ten
seconds, a control nobody can find costs more. The vocabulary is closed for
the same reason: a typo would otherwise invent a category holding exactly one
operation, which looks like a deliberate one until somebody counts.

Six tests over the real chain, including the hand-written operations that
`build.rs` never sees and so cannot check.
2026-08-22 10:10:00 +02:00

59 lines
1.5 KiB
YAML

# TRACES: FR-DEV-3a | FR-DEV-3c
id: exposure
label: op.exposure
order: 20
attributes: [tone]
doc: |
Exposure — a linear gain, expressed in stops.
The simplest operation in the pipeline and the one that most justifies
working in linear light: a stop is a doubling, so exposure is a single
multiply. Applied to gamma-encoded data it would be neither a doubling nor
reversible, which is why this stage sits where it does (ARCH §5.2).
placement: |
A correction to how the scene was captured, so it precedes every operation
that interprets tone.
params:
exposure:
label: param.exposure
kind: stops
min: -5
max: 5
doc: |
±5 stops. Wider than most edits need, but recovering a badly
underexposed frame is a real use and raw data often supports it.
uniforms:
gain:
value: exp2(exposure)
doc: The linear gain for the current setting.
wgsl: |
c = c * gain;
tests:
- name: one_stop_is_a_doubling
why: |
The definition of a stop. If this is wrong, every exposure adjustment is
subtly off and no test of "looks right" would catch it.
set: { exposure: 1 }
expect: { gain: 2.0 }
- name: one_stop_down_is_a_halving
set: { exposure: -1 }
expect: { gain: 0.5 }
- name: stops_compose_additively
why: +2 stops must equal +1 applied twice.
set: { exposure: 2 }
expect: { gain: 4.0 }
- name: neutral_is_unity_gain
why: |
An unedited image must be the image, not an interpretation of it.
expect: { gain: 1.0 }
expect_active: false