From 6c982cd61725b576bbc6bf6d8708111472bacf6d Mon Sep 17 00:00:00 2001 From: daniel-c-harvey Date: Sat, 1 Aug 2026 15:18:59 -0400 Subject: [PATCH] =?UTF-8?q?docs:=20record=20=CE=98-W7,=20the=20arc-and-spl?= =?UTF-8?q?ine=20antialiasing=20fix,=20and=20file=20the=20scaled-fallback?= =?UTF-8?q?=20deferral?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/COMPLETED.md | 56 +++++++++++++++++++++++++++++++++++++++++++++++ docs/PLAN.md | 46 +++++++++++++++++++++++++++++++++----- docs/TODO.md | 32 +++++++++++++++++++++++++++ 3 files changed, 129 insertions(+), 5 deletions(-) diff --git a/docs/COMPLETED.md b/docs/COMPLETED.md index 706ec23..533894f 100644 --- a/docs/COMPLETED.md +++ b/docs/COMPLETED.md @@ -613,3 +613,59 @@ reset semantics unit-testable; `editor_controls.cpp` dropped 469→293 lines. All visual outcomes remain **pending Daniel's by-eye sign-off on `dev`** — sizes, arc weight, and whether the waveform stroke improves or thickens the docked panel. Not recorded as accepted. + +### Θ-W7-T1 — arc-and-spline-aa + +Two defects Daniel found by eye once Θ-W6-T1's antialiasing pass shipped — diagnosing +both corrected the initial reading of each. + +- **Arcs never reached opacity.** `LICE_Arc` rasterizes a whole circle clipped per 90° + chunk and splits ink across two pixels by the fractional part of the radius; + `rOuter = radius - 0.5f` is half-integer, so no pixel in the ring was ever opaque — + measured peak alpha 138/255. The three stacked radii also did not tile: spacing + dilates from 1.0 px to 1.41 px at 45°, leaving partial-coverage holes. It read as + fuzz, but it was a stroke that never fully inked. +- **Splines were fully aliased, not gapped.** The apparent dotting was not missing + ink: `LICE_ThickFLine` steps the major axis and structurally cannot gap. The paint + loop passed **integer** `cx`/`cy`, so LICE had no sub-pixel position to interpolate + — every pixel was full or empty with no AA fringe, and integer `cy` quantized the + slope into an alternating 1/2 px staircase that reads as beading at 100%. +- **The cheap fix was rejected.** Float endpoints plus `LICE_ThickFLine` fixes + opacity and the staircase, but `ThickFLine` lays width along the *minor* axis, so + perpendicular weight is `wid·cosθ` — a measured 42% ripple dipping at every 45° + diagonal. +- **What landed:** one pure analytic thick-stroke rasterizer. Coverage is + distance-to-polyline, accumulated with `max()` into a scratch buffer and blended + **once** — the single blend is what structurally prevents the compositing fringe + build-up behind the first defect. An arc is just a polyline, so one code path + replaces the stacked arcs, both spline traces, and the two needles. Pure coverage + math in a new `core/ui/stroke_aa`; the blend loop in a new + `shell/instrument/editor_stroke`. `shell/panel/draw_kit` was deliberately **not** + touched, keeping the docked bank panel and browse cards entirely out of the blast + radius. +- **Measured, before → after:** arc peak alpha 138/255 → 255/255; arc perpendicular + weight 1.62–3.24 px (67% ripple) → 2.95–3.11 px (5%); spline weight 1.41–2.00 px + (29%) → 1.95–2.01 px (3%). Cost: **+0.09 ms per full editor repaint** (30 arcs + 0.113 → 0.169 ms; 500 px contour 0.013 → 0.047 ms), a knowing regression on an + interaction-driven surface, measured in Release against real LICE in an + uncommitted harness. +- **`velocity_curve` gained `subpixelFromPoint`** — sub-pixel y was unavoidable since + integer `cy` was the root cause. The existing integer map now *rounds* the new + float map rather than forking a second formula, so hit-testing is unchanged. +- **Daniel then ruled that every sub-2 px stroker width be enlarged**, because the + stroker can only guarantee an opaque core at width >= 2 px (an opaque pixel needs + `d <= halfWidth − 0.5`, and the worst-case pixel-centre-to-centreline distance is + 0.5). The knob track arc, the inner-dial needle, and the deck's mini velocity trace + all moved 1.0 → 2.0 px. A test pinning the sub-opaque behaviour at 1 px was kept as + a guard against reintroduction. +- **The audit's method was the root failure, not its output.** + `docs/product/visual-design-language.md` §8 had claimed stacked 1 px `LICE_Arc` + calls "keep every ring antialiased" — false. The Θ-W6 audit verified *which + primitive was called* rather than *what it rasterized*, which is how both surfaces + were signed off clean while never producing an opaque pixel. That sentence is + deleted, the rows are re-dispositioned with measurements, and the methodological + lesson is recorded in §8 as a standing blockquote. + +All visual outcomes remain **pending Daniel's by-eye sign-off on `dev`** — nothing was +verified in a live REAPER window; all measurement was against an offscreen bitmap in a +standalone harness. Not recorded as accepted. diff --git a/docs/PLAN.md b/docs/PLAN.md index 3aab970..788167d 100644 --- a/docs/PLAN.md +++ b/docs/PLAN.md @@ -115,8 +115,10 @@ drag/drop defects that block getting captures into it. **Consolidates items** 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16. -**All six waves have landed — Phase Θ is complete.** W1 through W6 each carry their own -landed note above; see `docs/COMPLETED.md` for every track's full narrative. +**All seven waves have landed — Phase Θ is complete.** W1 through W7 each carry their own +landed note above; see `docs/COMPLETED.md` for every track's full narrative. Θ-W7 was +opened after Θ-W6-T1 shipped, to fix two rendering defects Daniel found by eye; it did not +exist in the plan when this phase was originally scoped. **The organizing constraint.** Three surfaces in the instrument are single-writer by nature and dictate the wave shape: @@ -283,6 +285,33 @@ All visual outcomes remain pending Daniel's by-eye sign-off on `dev`. --- +### Θ-W7 — Arc-and-spline antialiasing fix + +**Depends on W6 for:** a drawn surface to find a defect on — this wave did not exist in +the plan; it was opened after Daniel found two rendering defects by eye once Θ-W6-T1 +shipped, so the audit's own output is what surfaced them. + +**One track.** + +**Θ-W7-T1 has landed** — `arc-and-spline-aa` — see `docs/COMPLETED.md` for the full +narrative. The stacked-`LICE_Arc` knob and dial rings never reached an opaque core (peak +alpha measured 138/255), and the staged/spline envelope and velocity-curve traces were +fully aliased rather than gapped, from integer `cy` quantizing the slope. Both defects, +plus the two needles, now route through one pure analytic thick-stroke rasterizer — +coverage in a new `core/ui/stroke_aa`, the single LICE blend in a new +`shell/instrument/editor_stroke` — replacing `LICE_Arc` and `LICE_ThickFLine` outright. +Measured: arc peak alpha 138/255 → 255/255, arc perpendicular-weight ripple 67% → 5%, +spline weight ripple 29% → 3%, at a cost of +0.09 ms per full editor repaint. Daniel then +ruled every sub-2 px stroker width up to 2 px, since the stroker only guarantees an +opaque core at width ≥ 2 px; the knob track arc, the inner-dial needle, and the deck's +mini velocity trace each moved 1.0 → 2.0 px. `docs/product/visual-design-language.md` §8 +is corrected — a false "keeps every ring antialiased" claim is deleted, the rows are +re-dispositioned with measurements, and the audit's methodological lesson (verifying +which primitive was called is not verifying what it rasterized) is recorded as a standing +blockquote. All visual outcomes remain pending Daniel's by-eye sign-off on `dev`. + +--- + ## Phase Ξ — The resample loop **Ships:** one consolidated, fully robust provenance/usage tracking system, and on top of @@ -300,7 +329,7 @@ independent of the editor chain — which is what makes Ξ-W1 concurrency-safe w bakes — "filtering, pitching, amp all set up nice" is the instrument items 1, 2, 3, and 14 build — and its settled reset scope enumerates the filter parameters, the spline contours, and the loop points by name. Baking a processing chain that does not exist yet is not a -schedule preference; the feature is not expressible. Phase Θ landed as of Θ-W6-T1 (see +schedule preference; the feature is not expressible. Phase Θ landed as of Θ-W7-T1 (see `docs/COMPLETED.md`); this gate is satisfied. --- @@ -616,8 +645,13 @@ work that did not come from the source doc has no row, and inventing one would w proof it exists to give. - **Θ-W3-T1 — `live-parameter-delivery`** (`pth-w3-t1-live-parameter-delivery`). Arose - from Θ-W2-T1's implementation review, not from `TODO-1.0.md`. It is the only such track - in this plan today; if others appear, they belong on this list rather than in the table. + from Θ-W2-T1's implementation review, not from `TODO-1.0.md`. The first such track in + this plan; see Θ-W7-T1 below for the second. +- **Θ-W7-T1 — `arc-and-spline-aa`** (`pth-w7-t1-arc-and-spline-aa`). Opened after + Θ-W6-T1 shipped, when Daniel found two rendering defects by eye in the editor — not + from `TODO-1.0.md`, and not a track this plan originally scoped. The second such track + in this plan today; if others appear, they belong on this list rather than in the + table. ### Deliberate compressions @@ -672,6 +706,8 @@ Phase Θ — ReaSampler 9000: one parameter set, filter, shapeable envelopes, le T1 spline-egs .............................. 3 W6 Editor legibility pass T1 legibility-and-antialiasing ............. 10, 13 + W7 Arc-and-spline antialiasing fix + T1 arc-and-spline-aa ....................... (not one of the seventeen) Phase Ξ — The resample loop (W1 concurrency-safe with Θ from Θ-W2 onward) W1 Consolidated tracking, and the programmed-note model diff --git a/docs/TODO.md b/docs/TODO.md index f071894..3276fdb 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -309,3 +309,35 @@ been observed to be wrong, only unverified. **Done looks like.** Either `IPlugViewContentScaleSupport` is implemented and the AA/uniform-width guarantees are re-verified at a scaled client size, or a decision is recorded that host-side resampling of the rasterized output is an accepted tradeoff. + +## The analytic stroker's scaled fallback path is unexercised + +**Context (what shipped — Θ-W7-T1, arc-and-spline-aa).** `blendCanvas` +(`shell/instrument/editor_stroke.cpp`) guards against `LICE_EXT_GET_SCALING` being +active by falling back to a per-pixel `LICE_PutPixel` path, because the primary raw-bits +path derives its geometry from logical width/height while writing through +`getRowSpan()` — under an active scale that would misplace the stroke or write past the +DIB allocation. + +**The wart.** Nothing calls `SET_SCALING` today, so the fallback path never runs. Under +an active scale it would rasterize the coverage mask at *logical* resolution with each +logical pixel expanded to a scale-sized block — geometrically correct but blocky rather +than resolution-independent. This connects to the already-filed high-DPI host-scaling +deferral above; cross-referenced here rather than duplicated. + +**Intended fix.** Not proposed — same shape as the host-scaling deferral above: +implementing (or verifying) genuine scale-aware rasterization is the shape of a fix, not +yet scoped. + +**The constraint the fix MUST handle.** Not yet known — no design work has started, and +none can usefully start before the host-scaling deferral above is resolved, since that +is what would first exercise this path. + +**Priority / risk.** Low / deferred. Recorded as a gap, not a defect: the fallback is +guarded, correct-but-blocky rather than wrong, and unreached by anything in the tree +today. + +**Done looks like.** Either the fallback path is exercised under a genuinely scaled +bitmap and confirmed to place the stroke correctly, or it is redesigned to rasterize at +physical rather than logical resolution once `IPlugViewContentScaleSupport` (or +equivalent) makes scaling real.