feat(ui): river source tapering + width-grammar retune — map fluency pass (T-1175) #207

Closed
jpmschweitzer wants to merge 0 commits from atlas-map-polish into main
Owner

Summary

The single large map-reading polish pass Jeroen requested off the T-1170 captures, benchmarked against RimWorld's world-map fluency (docs/design/references/rimworld-world-map-fluency.jpg).

Source tapering — courses draw as per-vertex-width ribbons instead of uniform draw_polyline: width ramps from a hairline at the upstream source to full class width over 15% of the course's arc length (arc-length parameterized so point density doesn't change the read; mitred joins; degenerate zero-length and two-point courses handled), then holds to the mouth. points[0]-is-source confirmed against the server contract and the existing mouth-ring draw.

Width grammar — class table retuned 0.9/1.4/2.2 → 0.6/1.2/2.4: a clean ~2× per-class ladder so a tributary-joins-trunk confluence reads as a join, not a color change on a uniform line. Trunk held at its visually-proven weight; stream thinner per the benchmark's thin/consistent/restrained grammar. Opacity table untouched (Araminta's T-1170 ruling stands); hue left alone (her prior ruling, reasoning documented inline).

Coast/shore gradient — resolved as already-correct, no change: the coastal transition morphology zones + elevation lightness render the shoreline band; capture-verified.

Biome/relief stipple — assess-only per scope: the design write-up (D-255(e)-legal texture-space dithering of already-served vegetation/elev planes, with the three open design questions) is preserved as T-1194.

Verification

Annotation-layer suite 24/24 (8 new taper-geometry tests: source hairline, full-width hold, monotone ramp, degenerate/two-point courses, vertex parity, normal perpendicularity); gdlint clean; full gate green on push. Author self-capture confirms the taper reads at District rung on live data. The formal eyeball on the final tip includes a confluence spot-check (the one frame the author's search budget didn't land) — merge waits on it.

🤖 Generated with Claude Code

## Summary The single large map-reading polish pass Jeroen requested off the T-1170 captures, benchmarked against RimWorld's world-map fluency (docs/design/references/rimworld-world-map-fluency.jpg). **Source tapering** — courses draw as per-vertex-width ribbons instead of uniform draw_polyline: width ramps from a hairline at the upstream source to full class width over 15% of the course's arc length (arc-length parameterized so point density doesn't change the read; mitred joins; degenerate zero-length and two-point courses handled), then holds to the mouth. `points[0]`-is-source confirmed against the server contract and the existing mouth-ring draw. **Width grammar** — class table retuned 0.9/1.4/2.2 → 0.6/1.2/2.4: a clean ~2× per-class ladder so a tributary-joins-trunk confluence reads as a join, not a color change on a uniform line. Trunk held at its visually-proven weight; stream thinner per the benchmark's thin/consistent/restrained grammar. Opacity table untouched (Araminta's T-1170 ruling stands); hue left alone (her prior ruling, reasoning documented inline). **Coast/shore gradient** — resolved as already-correct, no change: the coastal transition morphology zones + elevation lightness render the shoreline band; capture-verified. **Biome/relief stipple** — assess-only per scope: the design write-up (D-255(e)-legal texture-space dithering of already-served vegetation/elev planes, with the three open design questions) is preserved as **T-1194**. ## Verification Annotation-layer suite 24/24 (8 new taper-geometry tests: source hairline, full-width hold, monotone ramp, degenerate/two-point courses, vertex parity, normal perpendicularity); gdlint clean; full gate green on push. Author self-capture confirms the taper reads at District rung on live data. The formal eyeball on the final tip includes a confluence spot-check (the one frame the author's search budget didn't land) — merge waits on it. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
jpmschweitzer added 2 commits 2026-07-25 15:28:26 +02:00
The single large polish pass Jeroen requested off the T-1170 captures,
benchmarked against RimWorld's world-map fluency. Courses now draw as
source-tapered ribbons: per-vertex width ramps from a hairline at the
upstream source to full class width over 15% of the course's arc length
(arc-length parameterized, not vertex-indexed, so point density doesn't
change the read), built as one draw_polygon ribbon with mitred joins.
Class width table retuned 0.9/1.4/2.2 -> 0.6/1.2/2.4: a clean ~2x
per-class ladder so a tributary-joins-trunk confluence reads as a join,
trunk held at its visually-proven weight, stream thinner per the
benchmark's thin/consistent/restrained grammar. Opacity untouched
(Araminta's T-1170 ruling stands). Coast-gradient item resolved as
already-correct (the coastal transition zones + elevation lightness
render the shoreline band; capture-verified) — no wire change. Stipple
assessment filed as T-1194. Eight new taper-geometry tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
jpmschweitzer added 1 commit 2026-07-25 16:07:39 +02:00
Five findings from the araminta+hoshe round. Tapering now fires only on
a TRUE upstream source: the course's first raw world point is tested
against the canvas's own world bounds (conservative 1m epsilon) —
exact detection because the server crop keeps one point beyond the
window (layer_proxy crop_course_to_window lo = first_in-1, contract
documented), so crop passthroughs draw the old flat full-width cut and
never a false headwater. The averaged-normal joint is replaced by a
real mitre (half_w/cos(theta/2) recovered trig-free via the bisector
normal), clamped by a 2x mitre limit AND 0.45x the shorter adjacent
segment — restoring true perpendicular width at bends (the 29% pinch at
confluences is gone) and preventing the hairpin bowtie; the winding doc
now states the actual bounded guarantee. Antialiasing restored via the
hybrid: only the varying-width taper head draws as a ribbon; the
constant-width ~85% of every course keeps the original antialiased
draw_polyline (byte-identical for untapered courses), split at an
interpolated arc-length point sharing position and width — junction
capture evidence in .cache/screenshots/t1175-fix-round/. Flat-fill
single-element color array. Suite 24 -> 48 tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Author
Owner

Review: atlas-map-polish -> main (type: visual)

Reviewers: Hoshe (QA) + Araminta (art direction). Both REQUEST_CHANGES; all five findings addressed in 686021bee.

Araminta (art direction): REQUEST_CHANGES

# Issue Resolution
1 False headwaters at window-crop edges — taper fired unconditionally on points[0], but the server crops the upstream end silently (no source-side CourseTerminus analog); a mid-river fragment entering the window would draw a fake spring at the crop line Fixed, no wire change needed — detection is exact client-side: the crop keeps one point beyond the window (lo = first_in - 1), so a cropped course's first raw world point lies outside the canvas bounds while a true source lies inside. Taper gates on that test (canvas-space, conservative 1 m epsilon); crop passthroughs draw the old flat full-width cut — her stated preferred default. The one-point-overhang contract this relies on is documented against the server crop so a future change there knows it breaks the gate
2 AA loss (shared with Hoshe #3) See below

Her confirmations on record: width ladder 0.6/1.2/2.4 correct; 15% taper length right when legitimately firing; opacity/hue rulings stand; declined opacity-along-run ramp seconded.

Hoshe (QA): REQUEST_CHANGES

# Issue Resolution
1 "Always simple polygon" claim false — tight hairpin (turn radius < half-width) self-intersects the ribbon, verified numerically Fixed — offset now clamped by 0.45× the shorter adjacent segment (the clamp that actually prevents the bowtie) on top of the 2× mitre limit; doc rewritten to the real bounded guarantee
2 Mitre under-widens by cos(θ/2) at bends — 29% pinch at 90°, right at the confluence moment the retune sells; bend case untested Fixed by the same change — true mitre length half_w/cos(θ/2) (recovered trig-free via the bisector normal, near-fold guard), restoring exact perpendicular width; new bend tests assert the projection onto each adjacent segment's normal equals half_w
3 Silent AA regression — draw_polyline(antialiased=true) → draw_polygon (no AA parameter) Fixed via the hybrid — only the varying-width taper head draws as a ribbon; the constant-width ~85% of every course keeps the original antialiased draw_polyline (byte-identical for untapered courses). Split at an interpolated arc-length point (not index-snapped — station spacing can exceed the taper window at deep rungs) sharing exact position and width: butt joint, no gap/overlap. Junction verified in a live capture at 7× zoom — clean join, smooth AA diagonals: .cache/screenshots/t1175-fix-round/district_overview.png (on disk this time)
4 N-element PackedColorArray for a flat fill Fixed — single-element array

Verification

Annotation suite 24 → 48 tests (crop gate, head/tail split, mitred offsets, hairpin clamp), all green; gdlint clean; full gate green on push. The eyeball on this tip carries the two visual proofs still owed: a confluence in frame (width-grammar) and a crop-edge course drawing flat.

Verdict: CHANGES REQUESTED -> addressed in 686021bee; merge follows the eyeball.

# Review: atlas-map-polish -> main (type: visual) Reviewers: Hoshe (QA) + Araminta (art direction). Both REQUEST_CHANGES; all five findings addressed in `686021bee`. ### Araminta (art direction): REQUEST_CHANGES | # | Issue | Resolution | |---|-------|------------| | 1 | **False headwaters at window-crop edges** — taper fired unconditionally on points[0], but the server crops the upstream end silently (no source-side CourseTerminus analog); a mid-river fragment entering the window would draw a fake spring at the crop line | **Fixed, no wire change needed** — detection is exact client-side: the crop keeps one point *beyond* the window (`lo = first_in - 1`), so a cropped course's first raw world point lies outside the canvas bounds while a true source lies inside. Taper gates on that test (canvas-space, conservative 1 m epsilon); crop passthroughs draw the old flat full-width cut — her stated preferred default. The one-point-overhang contract this relies on is documented against the server crop so a future change there knows it breaks the gate | | 2 | AA loss (shared with Hoshe #3) | See below | Her confirmations on record: width ladder 0.6/1.2/2.4 correct; 15% taper length right *when legitimately firing*; opacity/hue rulings stand; declined opacity-along-run ramp seconded. ### Hoshe (QA): REQUEST_CHANGES | # | Issue | Resolution | |---|-------|------------| | 1 | "Always simple polygon" claim false — tight hairpin (turn radius < half-width) self-intersects the ribbon, verified numerically | **Fixed** — offset now clamped by 0.45× the shorter adjacent segment (the clamp that actually prevents the bowtie) on top of the 2× mitre limit; doc rewritten to the real bounded guarantee | | 2 | Mitre under-widens by cos(θ/2) at bends — 29% pinch at 90°, right at the confluence moment the retune sells; bend case untested | **Fixed by the same change** — true mitre length `half_w/cos(θ/2)` (recovered trig-free via the bisector normal, near-fold guard), restoring exact perpendicular width; new bend tests assert the projection onto *each* adjacent segment's normal equals half_w | | 3 | Silent AA regression — draw_polyline(antialiased=true) → draw_polygon (no AA parameter) | **Fixed via the hybrid** — only the varying-width taper head draws as a ribbon; the constant-width ~85% of every course keeps the original antialiased draw_polyline (byte-identical for untapered courses). Split at an *interpolated* arc-length point (not index-snapped — station spacing can exceed the taper window at deep rungs) sharing exact position and width: butt joint, no gap/overlap. Junction verified in a live capture at 7× zoom — clean join, smooth AA diagonals: `.cache/screenshots/t1175-fix-round/district_overview.png` (on disk this time) | | 4 | N-element PackedColorArray for a flat fill | **Fixed** — single-element array | ### Verification Annotation suite 24 → 48 tests (crop gate, head/tail split, mitred offsets, hairpin clamp), all green; gdlint clean; full gate green on push. The eyeball on this tip carries the two visual proofs still owed: a confluence in frame (width-grammar) and a crop-edge course drawing flat. ### Verdict: CHANGES REQUESTED -> addressed in `686021bee`; merge follows the eyeball.
Author
Owner

Eyeball — PASS on all three owed judgments (merge gate cleared)

Live runs on 686021bee, GJ251c, targets constructed from the real drainage arrays (a scratch probe walking confluences/river_class/fdir, cross-checked against the server's own inflow computation) rather than blind panning.

  • Confluence (width grammar + mitre): at a genuine class-transition join, column-scan measurement shows a flat 2px stream run jumping to a flat 4px trunk run immediately post-join — the exact 2× ladder — with no pinch at the bend (never dips below pre-join width). confluence.png / -zoom.png.
  • Crop-edge flat cut (araminta's finding): a window constructed to bisect a known course; the cropped course's overhang point confirmed ~306 m outside the window bound (the one-station overhang the gate relies on); pixel-flat 2px width from the crop line — no false headwater. crop-edge.png.
  • True-source taper + AA junction: a genuine zero-inflow headwater (104 found on the body) centered in-window tapers in cleanly (renders as a coverage ramp at this class's 2px width — correct); the ribbon-to-polyline junction is invisible at 1×; diagonal spans show clean AA, no stairstepping. taper-source.png, diagonal-aa-zoom.png.
  • Zero errors across 12 driver runs + server logs; process table confirmed clean afterward; worktree source untouched.

Captures: .cache/screenshots/t1175-eyeball/. Merging.

# Eyeball — PASS on all three owed judgments (merge gate cleared) Live runs on `686021bee`, GJ251c, targets *constructed* from the real drainage arrays (a scratch probe walking `confluences`/`river_class`/`fdir`, cross-checked against the server's own inflow computation) rather than blind panning. - **Confluence (width grammar + mitre):** at a genuine class-transition join, column-scan measurement shows a flat 2px stream run jumping to a flat 4px trunk run immediately post-join — the exact 2× ladder — with **no pinch** at the bend (never dips below pre-join width). `confluence.png` / `-zoom.png`. - **Crop-edge flat cut (araminta's finding):** a window constructed to bisect a known course; the cropped course's overhang point confirmed ~306 m outside the window bound (the one-station overhang the gate relies on); pixel-flat 2px width from the crop line — **no false headwater**. `crop-edge.png`. - **True-source taper + AA junction:** a genuine zero-inflow headwater (104 found on the body) centered in-window tapers in cleanly (renders as a coverage ramp at this class's 2px width — correct); the ribbon-to-polyline junction is invisible at 1×; diagonal spans show clean AA, no stairstepping. `taper-source.png`, `diagonal-aa-zoom.png`. - Zero errors across 12 driver runs + server logs; process table confirmed clean afterward; worktree source untouched. Captures: `.cache/screenshots/t1175-eyeball/`. Merging.
jpmschweitzer closed this pull request 2026-07-25 16:43:55 +02:00

Pull request closed

This pull request cannot be reopened because the branch was deleted.
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: jpmschweitzer/settled-reach#207