fix: stroke arcs and splines analytically — opaque core, angle-independent weight

LICE_Arc never reaches opacity and ThickFLine's width is minor-axis. One
distance-to-polyline coverage mask, blended once, replaces both.
This commit is contained in:
2026-08-01 13:18:21 -04:00
parent ae9019465e
commit 2e09776342
16 changed files with 1001 additions and 72 deletions
+15
View File
@@ -101,6 +101,7 @@ L7 sub-pass, 2026-07-27):
- `tooltip` — pure tooltip placement + prefix-strip: strips the `ReaSampler:` display prefix from the registered action phrase; width clamped to the client rect.
- `card_drag` — pure drag-gesture precedence + slot hit-test: leave-client → OS drag-out; other-bank → move/copy; same-bank → reorder / Alt-over-occupied → replace.
- `card_meta` — pure card-metadata formatters: bars.beats.subdivisions and seconds.milliseconds; blank when the sample is unstamped.
- `stroke_aa` — analytic antialiased thick-stroke COVERAGE (the shell blends it): `StrokeCanvas`, a reusable mask holding distance-to-polyline coverage MAX-accumulated across segments, plus `strokePolyline` / `strokeBounds` / `appendArc`. An arc is just a flattened polyline, so ONE path serves the knob arcs, the inner dial, the envelope polyline and both spline traces. Coverage is `clamp(halfWidth + 0.5 - distance, 0, 1)`, which makes perpendicular weight exactly `2·halfWidth` at every angle and gives an opaque core for any width above 1 px. Long segments are subdivided before rasterizing — EXACT, not an approximation (min-distance to a partition of a segment is min-distance to the whole), purely to keep each piece's bounding box tight, since one long diagonal's box has area O(len²).
## Gotchas
@@ -122,3 +123,17 @@ L7 sub-pass, 2026-07-27):
- `rect`'s prior role names survive only as `using` aliases at their old call
sites — changing `rect.h` itself ripples across every directory that aliases
it (e.g. `editor_geometry::Rect`); check all alias sites, not just this one.
- **`stroke_aa`'s mask is deliberately NOT cleared on `reset`.** Only
`[rowLo, rowHi)` of each row holds meaningful coverage; everything else is
whatever the reused buffer last held. That is what keeps a stroke's cost
proportional to its ink rather than to its bounding box — but it means any new
reader must respect the row extents, and any new writer must grow them through
`extendRow`, which zero-fills the newly-valid cells INCLUDING the gap when a
stroke revisits a row far from where it left it (a circle touches most rows on
both sides). Reading the raw buffer outside the extents returns garbage by
design, not zero.
- **Neither `LICE_Arc` nor `LICE_ThickFLine` can draw these strokes** — the first
never reaches an opaque core, the second's width is along the minor axis so its
perpendicular weight falls off as `cos θ`. The evidence and the measurements
live in `docs/product/visual-design-language.md` §8; do not "simplify" a stroke
site back onto either primitive.