6b31111cd215ff0d3cee5cfe9dfd97e69f6630b6
8
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
8eb5cb9ff7 |
feat(ui): the Atlas ladder bottoms out at Quarter (D-255, T-1213)
Quarter becomes the deepest navigable rung. Block and Chunk leave the ladder. The rule, from the amendment: the deepest Atlas rung is the one at which a screen pixel shows one subtile. At the uniform 2x2 px display ratio a 3440x1440 window gives 540 gridunits on the short axis, so Quarter's 512 m extent draws 0.948 m per gridunit -- about one voxel per gridunit and one 0.5 m subtile per pixel. Block (0.237) and Chunk (0.119) magnify beneath the finest datum that can exist, and measured as exactly that on Ferrath: one morphology zone, one vegetation class, an unbroken colour field. They were not missing a feature; there was nothing left to show them. CHUNK ITSELF IS UNTOUCHED. It remains D-243's 64 m stream/derive unit and is where Phase 5 derives first-person walkable content -- D-012's load-around-the-player is expressed in chunks. Block remains the 128 m generator planning unit. Both keep their enum variants, their extent_m answers and their wire vocabulary. What was retired is the claim that a MAP of one is worth looking at. DEEP_RUNGS moved with the floor, and this is the part worth reading twice. It is a mandatory D-255(d) hardening: a per-body retention cap bounding how much ground an exhaustive pan can hold resident at fine spacing. Left as [Block, Chunk] it would have guarded rungs no client can request -- dead code -- while the accumulation gap silently re-opened under Quarter, now the finest navigable rung at ~0.95 m per gridunit. It is now [District, Quarter]. A control that names its targets by rung has to follow the ladder when the ladder moves. Six tests pinned the old floor and were updated rather than deleted, since each was protecting a real property: the clamp tests now clamp at Quarter, and the disk-cache tests use Region for "shallow" (District is capped now) and Quarter for "deep". One new test pins the distinction the change turns on -- the retired rungs are absent from RUNG_LADDER but still present in RUNG_EXTENT_M, because viewability was retired, not vocabulary. Also removes the four capture scenarios for the retired rungs, including the two blank goldens that had been passing against blank captures. 1838 client tests, 0 failed. Server clippy clean, step_canvas suite green. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
144d35d2a5 |
fix(client): size Global's cell count so the display ratio divides exactly
Jeroen: 'still not filling the screen... maybe we should calculate the depth that needs to be invented based on the canvas size somehow.' That is the fix. The old flow picked cells from the viewport, let the SERVER impose Global's 2:1 aspect, then fitted the returned extent into the drawable area with an INTEGER pixels-per-gridunit ratio. When that extent did not divide the area evenly, floor() dropped a whole step — and at ratio 2 the only step below is 1, i.e. half size. Worse, because the client fitted an extent it had not chosen, the result could fill NEITHER axis. Inverted: global_fill_extent() chooses the cell count FROM the ratio, already shaped 2:1, so cells * RATIO is the drawn size by construction and lands exactly on the binding axis. Verified across window shapes — 1920x1080 fills width, 2560x1080 fills height, 2560x1440 width, 3440x1440 height, 1280x720 width. Exactly one axis fills at every shape, which is the most a 2:1 equirectangular canvas can do in an arbitrary viewport; the other letterboxes. Global keeps its 2:1 aspect because it is 360 degrees of longitude by 180 of latitude — the aspect cannot follow the viewport without shearing the map. Tests pin the invariant directly: 2:1 preserved, never overflowing the drawable area, and never leaving slack on BOTH axes. Client suite 1832 / 1806 passed / 0 failed / 26 skipped. Co-Authored-By: Claude <noreply@anthropic.com> |
||
|
|
9c8fcc2f95 |
feat(client): size the Global rung to the viewport, not the region grid
Global took its cell counts from `global_cell_counts()` — one gridunit per region — and discarded the requested extent entirely. On GJ380c that produced a 191x95 canvas built from a heightmap stored at 512x256: roughly seven times the available cells thrown away before anything was drawn. The count also shrank as REGION_M grew, so tuning the scale ladder silently degraded the opener, which is why the top of the ladder got worse rather than better as the ladder itself was refined. Global now fits the largest 2:1 canvas inside the requested extent. It cannot take its ASPECT from the viewport — the canvas is equirectangular whole-body, 360 degrees of longitude by 180 of latitude, and must stay 2:1 or the cells stop being square and the map shears — so the existing letterbox absorbs the remainder. A hostile extent is still clamped; sizing to the request is not trusting the request. Global also joins the deep display ratio, making the band uniform. At 5 px per gridunit a 1920 px window asked for 384 cells across a body whose heightmap holds 512x256 — discarding stored detail to save work already done. At 2 px it asks for 960, which is heightmap-native: nothing thrown away, nothing invented, and the same screen area filled either way. A body with no radius is not a sphere (asteroid belt, oort cloud) and has no equirectangular surface to fit. Those degrade to the region grid — a visibly degenerate 1x1 canvas — rather than a plausible-looking lie at whatever size the viewport happened to ask for. project.yaml 0.4.3 -> 0.4.4 invalidates the persisted step-canvas disk cache. District/Quarter/Block/Chunk kept identical 960x540 cell counts through the extent inversion, so their cache keys are byte-identical while a District canvas now covers 3.6 km of ground instead of 1,966 km — a warm cache would silently serve pre-inversion canvases. Global still rides the orbital derive, so it carries no courses yet; that is the next step and is deliberately separate, being a cost question over the whole body rather than a sizing one. Pair session with Jeroen, 2026-07-26. Co-Authored-By: Claude <noreply@anthropic.com> |
||
|
|
0a0419abcc |
feat(client): invert the Atlas rung relation — rung sets extent, not spacing
A rung used to fix the gridunit SPACING, with the canvas extent falling out of spacing x cell count. That is why the top of the ladder was unusable: at REGION_M spacing a viewport-sized canvas spanned ~251,658 km — six times around a rocky body — so the Region rung capped to the body and redrew the Global picture pixel-for-pixel. "Global and region look the same" was not a rendering bug; it was this relation, stated in metres. Inverted: a rung fixes the EXTENT and the spacing falls out of the canvas size. The shorter viewport axis spans exactly one cell of the rung's level, so a widescreen window shows more ground on the long axis rather than less on the short one. Every rung now shows the ground its name promises — Region 262x466 km, District 4.1x7.3 km — and the canvas cell count is viewport-driven and identical at every rung, so derive cost no longer varies with depth and resize is free. Consequences that fell out of the inversion rather than being chosen: - Region leaves the orbital derive set. It was envelope-only because at 251,658 km nothing finer made sense; at 262 km it is a genuine provincial map and takes the full courses-aware derive. Region having no rivers at all was much of why the top of the ladder read flat. It also joins the deep display ratio for the same reason. - The S2 station-spacing floor is deleted, not retuned. It guarded an O(1/spacing) blowup that the inversion makes structurally impossible (the canvas cell count is now constant across rungs, so stations-per-course is bounded however deep you scroll). Kept, it would do active harm in the opposite direction: a 2,048 m pitch across a 3.6 km District canvas places two stations and draws every river as a straight line. Station placement gets its own generator pass. - cap_extent_to_body is superseded and now a documented no-op. A canvas can no longer over-request a body by construction. The residual question — whether a rung's cell exceeds the whole body — is liveness, not capping, and is_rung_live_on_body() answers it by omitting the rung. Empirically it never fires on inhabited content: all six rungs are live on all 271 populated bodies with a radius. - snap_to_gridunit no longer truncates its multiplier to int. Post-inversion the deep rungs run sub-metre (Chunk ~0.12 m at a 1080 px short axis), where int(spacing) floors to zero and would collapse every request centre onto the origin. Both sides derive spacing from the same three inputs (rung, echoed cell extent, body radius) rather than one telling the other, so there is nothing to keep in sync beyond the constant table itself. Body radius already reaches the viewer via enter(); no wire change. Pair session with Jeroen, 2026-07-26. D-243/D-255 amendments to be backfiled. Co-Authored-By: Claude <noreply@anthropic.com> |
||
|
|
ae03ea2de7 |
perf(ui): Atlas deep rungs draw one gridunit per 2x2 px block
DISPLAY_RATIO_DEEP 1.0 -> 2.0, so a viewport-fit request at District/ Quarter/Block/Chunk asks for ~4x fewer gridunits (1920x1080: ~2.07M cells -> ~518K) and the server-side derive cost falls with it. Texel- exactness is preserved — the ratio stays a whole number of screen px per gridunit, so every source texel still lands on whole pixels; only the block size changes. Non-integer ratios are not an option here: they reintroduce exactly the sub-pixel blur D-255's texel-exactness exists to prevent. Judged live by Jeroen against the 1x1 build (pair session): the deep rungs read as crisp larger pixels rather than blur, and the speedup is substantial. Looks were the gate. Both transport tests that pinned the old literal now assert the RELATIONSHIP instead — deep rungs share the constant, viewport-fit divides by it — plus a new guard that the ratio stays whole, so the value remains tunable without editing tests that are not about it. Co-Authored-By: Claude <noreply@anthropic.com> |
||
|
|
374b195594 |
fix(ui): PR #205 review round — integer px-per-gridunit fit replaces fractional (T-1189, T-1192)
The fractional fit branch is deleted, resolving both review findings at the root: tyre showed its comments cited D-255 for an exception the record does not contain (the language came from the lead's ticket text, not governance), and hoshe showed it returned sub-1x for a canvas exceeding the viewport on one axis. Replacement: fit_scale_ratio() chooses the largest integer pixels-per-gridunit R fitting both legend-reserved axes, floored at 1 (over-viewport draws native and crops like every fixed rung) — the fine-grained integer lattice (GJ1c 1080p -> 9px/gu = 1593x792, ~98% width; 4K -> 20) that makes the fractional hatch unnecessary. center_offset() floors to whole pixels (half-pixel centering would blur the texel grid). Doc comments cite the real sanction (D-255 amendment 2026-07-25, this branch). Legend column constant is now canonical in transport, read directly by the legend (was an independently-typed literal); its test asserts real geometry. New regression test proves _global_body_extent clears on body switch and never caps another body's requests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
c1a97166c5 |
feat(ui): cap rung extents to the body + fit/center the Global canvas (T-1189, T-1192)
The two client fixes for the Atlas frames Jeroen flagged. Region rung no longer requests more planet than exists: cap_extent_to_body() caps the viewport-fit extent at the body's own region grid (the Global canvas extent echo — cols=regions_per_equator, rows=cols/2), matched by gridunit SPACING not rung name, applied before the request/cache key is built; cold-start scroll-before-Global-echo requests uncapped and lets the server clamp (documented). Kills both the side-by-side continent repeat and the past-the-pole stripe smear. Global opener now fit-scales and centers through one shared letterbox mechanism (center_offset/integer_fit_scale/fit_scale, 75%-coverage integer-vs-fractional decision, NEAREST already forced on orbital rungs per D-255's escape hatch) also used for the Region cap's letterbox remainder. Legend column reserved before fitting — pinned to the legend's own width by a cross-constant test, never overlapping the canvas. Also fixes a latent crash: NOTIFICATION_RESIZED fires mid-_ready() before children exist; null guard in the resize path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
d28d24fd26 |
feat(ui): step-canvas map component — RTT terrain + stepped zoom (D-255, T-1182)
The two-layer client rebuild per D-255(a)(b)(e), replacing the _canvas.scale continuous-zoom model with one viewer, one path, all six rungs: - step_canvas_protocol.gd: StepCanvasRequest/Response codec against the T-1181 wire contract — incl. the discovered png_bytes subtlety (rmp_serde without serde_bytes emits a msgpack int-array, not bin; decode repacks via PackedByteArray before load_png_from_buffer) and the extent-echo rule (read the server-clamped extent, never assume the requested one). - step_canvas/ component: transport (six-rung ladder, cursor-anchored scroll steps, edge-scroll/WASD pan with re-request on edge crossing, hard reset-to-Global), RTT terrain layer (Image.set_pixel colorize per the c1 measured ruling, texture.update reuse on step-cross, NEAREST coarse / LINEAR fine per rung), unscaled screen-space annotation sibling (courses + settlement markers at literal px), in-memory LRU cache (Tier 1; T-1183 layers the disk tiers beneath), request lifecycle (pending retry, staleness gate, extent echo). - Full _canvas.scale retirement in the same change: the zoom-scaled canvas model, the _zs compensation family, select_rung / MAX_COVERAGE_M / compute_tile_grid, the orbital-mosaic-vs-window two-path split, _view_zoom/_canonical_fit_zoom — 10 source files deleted; their 14 test suites deleted with them (T-1157 dead-goldens rule; replacement visual-capture coverage is re-scoped T-1157). - Surviving surfaces kept per the ticket: atlas_window_cache.gd's LRU shape (the ticket's named file atlas_window_tile_set.gd was the retiring orchestrator; the real LRU shape lives in atlas_window_cache.gd — cited in step_canvas_cache.gd), overlay colors, legend/overlay-bar chrome, AtlasViewer descend geometry. Determinism boundary per D-255(e): the client interpolates only within the closed server-supplied input set. 7 new gdUnit suites (164 cases) incl. a real extent-echo bug caught by its own test during implementation. Full client suite green (exit 0) with the live-gated suites running against a worktree server build. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |