fix(render): shade depth areas from the mariner's contours, not the baked token#20
Merged
Conversation
60aae85 to
3cf809b
Compare
…aked token
The S-101 rules are run ONCE per cell with the fixed bake context (portray.Context
defaults: shallow 2 m, safety/deep 30 m), because a baked tile must serve every
mariner. So a DEPARE's ColorFill token is the BAKE's shading, not this mariner's —
which is why scene.fillArea also hands every depth area its DRVAL1/DRVAL2, and why
the MapLibre style re-runs SEABED01 client-side (mariner.seabedTokenExpr).
The resolving surfaces never did. All three dropped the range on the floor
(`_ = depth`), painted the baked token, and so:
* DEPDW essentially never appeared — the baked deep contour is 30 m, so any
coastal cell whose depths run under that showed no deep water at all;
* DEPMD never appeared — the bake's safety and deep contours are BOTH 30 m,
which collapses the medium-deep band to nothing;
* the mariner's shallow/safety/deep contours and the two/four-shade toggle
changed nothing on screen.
Add resolve.seabedToken (SEABED01, mirroring mariner.seabedTokenExpr band for
band) and swap the fill colour live in the pixel, vector and ascii surfaces when
a fill carries a depth range. The baked fill transparency is preserved: the swap
is of the colour, not the opacity. Non-depth areas keep their baked token.
The other two live-mariner depth swaps in these surfaces (SNDFRM04 soundings vs
safety_depth, DANGER01/02 vs safety_contour) were already wired; this was the
gap.
…render The S-52 colour tables are a pure function of the embedded colour profile — the same bytes for every chart, and read-only once parsed — but every render path re-parsed the whole XML into three hash maps: the per-handle cache only helped the tile path, and a host that opens and purges chart handles as its quilt walks cells (a large chart set makes that constant) paid the parse on every open. The one-shot render paths (PNG/PDF/ascii/compose, and the per-feature explore thumbnails) paid it on every call. Share one lazily-parsed copy behind a CAS one-shot (Zig 0.16 puts mutexes behind an Io the engine deliberately doesn't take; contention is possible only on the very first render, and steady state is one acquire load). Measured on the explore path, which renders one thumbnail per feature — 239 DEPARE features in US5MD1MC: 239 profile parses before, 1 after.
3cf809b to
e54b464
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The bug
Depth areas were shaded against the bake context, not the mariner's. Deep water
never appeared, and the shallow/safety/deep contours and the two/four-shade toggle
had no effect on a rendered chart.
Why
The S-101 rules run once per cell with the fixed bake context (
portray.Contextdefaults: shallow 2 m, safety and deep both 30 m), because a baked tile has to
serve every mariner. So a DEPARE's
ColorFilltoken encodes the bake's bands, notthe viewer's — which is exactly why
scene.fillAreaalso hands every depth area itsDRVAL1/DRVAL2, and why the MapLibre client re-runs SEABED01 live
(
mariner.seabedTokenExpr).The resolving surfaces never ran the second half. All three dropped the range on the
floor (
_ = depth) and painted the baked token:whose depths run shallower than that showed no deep water at all.
30 m, which collapses the medium-deep band to nothing.
between two and four shades, changed no pixels.
Telling detail: the other two live-mariner depth swaps in these same surfaces were
already wired — soundings against
safety_depth(SNDFRM04), and DANGER01/02 againstsafety_contour. The seabed fill was the one that got missed.The fix
Add
resolve.seabedToken— SEABED01, mirroringmariner.seabedTokenExprband forband so the two styling paths can't silently drift — and swap the fill colour live in
the pixel, vector and ascii surfaces whenever a fill carries a depth range.
opacity.
>=on DRVAL1,>on DRVAL2), perspec.
Tests
resolve: the four bands + the drying fallthrough, the straddle rule, and thetwo-shade case, pinned against the style expression they mirror.
pixel: end-to-end through a real surface — the same 12–20 m area paints DEPMDunder the default contours and DEPDW once the mariner pulls the deep contour in to
10 m, which is the shade that could not previously appear. A non-depth fill is
asserted unchanged.
Full suite green.
Also on this branch:
perf(chart)— the S-52 colour tables are now parsed once perprocess instead of once per render. They are a pure function of the embedded profile
and read-only once parsed, but every render path re-parsed the XML into three hash
maps. Measured on the explore path (one thumbnail per feature): 239 parses before,
1 after across the 239 DEPARE features in
US5MD1MC.