Record intersection as built in the requirement and the mask plan

FR-DEV-19a said a part is added to the mask or taken out of it, and
mask-editing.md listed Intersect as an M2 item with nothing built. Both
now say what shipped: the requirement names the third join and why it is
a product rather than a minimum, and how old and new sidecars read across
it; the plan marks the blend-table row built, names the tests that hold
the GPU to the definition, and splits M2 into what is done and what is
still outstanding (joining non-painted parts from the panel, per-part
distance fields, folding two layers).
This commit is contained in:
2026-09-24 21:25:48 -04:00
parent 8f3df7b68d
commit e8f68a92f8
2 changed files with 30 additions and 4 deletions
+19 -1
View File
@@ -269,7 +269,7 @@ The set operations are already expressible in fixed-function blending over
|------|-------------------------------|--------|-------|
| Union | `One`, `One`, `Max` | `max(dst, src)` | M1 |
| Subtract | `Zero`, `OneMinusSrc`, `Add` | `dst · (1 − src)` | M1 |
| Intersect | `Zero`, `Src`, `Add` | `dst · src` | M2 |
| Intersect | `Zero`, `Src`, `Add` | `dst · src` | M2 (built 2026-09-24) |
The middle row is `brush_erase`, already constructed in
[`MaskPass::new`](../../core/dr-gpu/src/mask.rs). The other two are the same
@@ -292,6 +292,16 @@ rendering as it did. `an_erase_stroke_holes_its_own_part_and_not_the_mask` in
[`local_adjustments.rs`](../../core/dr-gpu/tests/local_adjustments.rs) is the test
that holds this in place.
Intersection, as built, is exactly the table's row: a third pipeline beside
the other two in `MaskPass::new`, `Join::apply` stating the three on the CPU,
and `a_part_unioned_subtracted_and_intersected_gives_the_three_fields` and
`the_joins_match_their_definition` in `local_adjustments.rs` holding the GPU
to it. It is a product, not a minimum: the two agree wherever either side is
fully in or out, and between two soft edges the product is the softer reading,
which is the right way for an overlap of two partial selections to be wrong.
Its blend is `Add`, not `Max`, so it adds nothing to the Android question
below.
`Max` blending on `r8unorm` is core WGPU and universally supported on the
desktop backends; **verify it on the Android adapter before M2 lands**, since
that is the platform where a blend mode is most likely to be quietly emulated
@@ -689,6 +699,14 @@ nothing, and every report of it came back as "the masks do not work".
per-part distance fields (§5.5), the part list with its join chips, folding
two layers.
Done (2026-09-24): `Intersect` — the join, its blend state, the sidecar word
`intersect`, the part row's chip cycling + / − / ∩, and an "∩ Intersect"
button beside Add and Subtract (§7.1). The part list with its chips and a
per-part eye was already there from M1. Outstanding: joining a model,
gradient or range part from the panel (the buttons still join a painted
part, so an intersection is painted where the mask should survive), per-part
distance fields, and folding two layers.
**M3 — Push, and cling.** The warp pass (§5.3) and edge-aware deposit (§5.6).
Both are refinements of a tool that already works, which is the right place
for the two riskiest pieces.
+11 -3
View File
@@ -544,9 +544,17 @@ decade; a mask that cannot be corrected is the reason an edit leaves this applic
editor.
**FR-DEV-19a — Mask composition.** A layer's mask is an ordered list of parts, each naming a source
and how it joins the mask before it — added to it, or taken out of it. A layer of one part is
exactly the layer that existed before this, and reads and writes the same sidecar. The parts of a
layer merge under FR-NC-9 as the layer does, and each carries its own edge treatment.
and how it joins the mask before it — added to it, taken out of it, or intersected with it, keeping
only where both agree. A layer of one part is exactly the layer that existed before this, and reads
and writes the same sidecar. The parts of a layer merge under FR-NC-9 as the layer does, and each
carries its own edge treatment.
*Intersection added 2026-09-24* (issue #9, `mask-editing.md` M2). Union and subtraction were built
first; the selections that most need composing — the sky that is also bright, the subject that is
also skin — are intersections, and spelling one as a subtraction needs a part that selects the
complement. Intersection is the product of the two coverages, which is the minimum wherever either
side is fully in or out. A sidecar written before it never names it; a build from before it reads
the word as a union, which keeps the part visible rather than dropping it.
**FR-DEV-19b — Hand correction.** A part may be painted, with add and erase strokes, at a radius,
hardness and flow the photographer sets. Strokes are stored as normalised source coordinates and