fix(client): T-1153/T-1152 round 5 — nearest-wrap-image tile draw placement; canonical reset restores the fit zoom and re-fires

The wrapped tile column (Lendel: -6400 canonicalized to 12739) drew at
its canonical column — off-canvas right — leaving the mosaic's left
third black. nearest_wrap_image() re-expresses a tile column as the
wrap-image closest to held_center for DRAWING only (requests/cache keys
stay canonical). The draw-position test asserts overlap FRACTION, not
bare intersects() — the buggy placement still clipped ~2px of viewport
edge at Lendel scale, so intersects() alone would false-pass.

The full-zoom-out reset restored center but not the fit zoom, and its
'already there' guard keyed on a lagging field so it could only ever
fire once. The guard now also matches tile mode and compares _view_zoom
against the freshly computed fit — restoring the FULL canonical
transform (center, offset, fit zoom) and re-firing as a continued
gesture keeps zooming out (Jeroen's hard condition, both live shapes).

+8 revert-verified tests incl. the continued-gesture reset repro; smoke
stub gained get_body_radius_km (crash confirmed real under a real
driver before fixing). Suites 286 green; gdlint clean.
This commit is contained in:
2026-07-22 15:32:04 +02:00
parent 493a7345d3
commit ccedba4f24
6 changed files with 420 additions and 78 deletions
+148
View File
@@ -744,6 +744,154 @@ func test_district_to_canvas_local_zero_held_n_does_not_crash() -> void:
assert_that(result).is_equal(Vector2(5, 5) * CELL_PIXEL_SIZE)
# =============================================================================
# Live round 5: nearest_wrap_image() — the tile-mosaic WRAP half of "the
# mosaic doesn't fully draw" (the left-third-black repro).
# =============================================================================
## Live round 5's OWN repro, pinned exactly: Lendel's wrapped tile
## canonicalizes to column 12739 (`-6400 mod 19139`) — the CORRECT
## request/cache key — but its nearest wrap-image relative to the canonical
## origin (held_center.x = 0) is -6400, the actual visible position
## immediately west of center.
func test_nearest_wrap_image_matches_the_lendel_repro() -> void:
var result: int = AtlasWindowGeometry.nearest_wrap_image(12739, 0, 19139)
assert_int(result).override_failure_message(
"the wrapped tile's nearest wrap-image relative to held_center=0 must be"
+ " -6400 (its actual on-screen position), not 12739 (the correct REQUEST"
+ " key, but the wrong DRAW position)"
).is_equal(-6400)
## The two Lendel tiles that were NEVER wrapped (already close to
## held_center) must round-trip unchanged — the fix must not perturb tiles
## that were already drawing correctly.
func test_nearest_wrap_image_is_a_noop_for_already_nearby_columns() -> void:
var cols := 19139
for col: int in [0, 6400]:
var result: int = AtlasWindowGeometry.nearest_wrap_image(col, 0, cols)
assert_int(result).override_failure_message(
"column %d is already the nearest wrap-image to held_center=0 — must"
+ " be returned unchanged" % col
).is_equal(col)
## The result must always be a LEGAL wrap-image of the canonical column —
## i.e. `result mod cols == canonical_col mod cols` — regardless of which
## image is nearest. This is the correctness invariant the whole function
## exists to preserve: re-expressing a column for DRAWING must never change
## WHICH district it actually refers to.
func test_nearest_wrap_image_preserves_the_canonical_identity() -> void:
var cols := 19139
for held_col: int in [-50000, -1, 0, 1, 9569, 19138, 50000]:
var result: int = AtlasWindowGeometry.nearest_wrap_image(12739, held_col, cols)
assert_int(posmod(result, cols)).override_failure_message(
"nearest_wrap_image(12739, %d, %d) = %d must still canonicalize back"
+ " to 12739 — it may only pick a DIFFERENT wrap-image, never a"
+ " different district" % [held_col, cols, result]
).is_equal(12739)
## The chosen wrap-image must be the CLOSEST one to held_center — never
## farther than half the circumference away (otherwise a different
## wrap-image would have been nearer).
func test_nearest_wrap_image_is_within_half_circumference_of_held_center() -> void:
var cols := 19139
for canonical_col: int in [0, 1, 9569, 12739, 19138]:
for held_col: int in [-30000, -500, 0, 500, 25000]:
var result: int = AtlasWindowGeometry.nearest_wrap_image(canonical_col, held_col, cols)
var distance: int = absi(result - held_col)
assert_int(distance).override_failure_message(
(
"nearest_wrap_image(%d, %d, %d) = %d is %d districts from"
+ " held_center — must never exceed half the circumference"
+ " (%d), or a closer wrap-image exists"
)
% [canonical_col, held_col, cols, result, distance, cols / 2]
).is_less_equal(cols / 2)
## `cols <= 0` (no-radius bodies, which never tile per compute_tile_grid()'s
## own doc) must be a safe no-op passthrough — no periodicity to resolve.
func test_nearest_wrap_image_zero_cols_is_a_passthrough() -> void:
var result: int = AtlasWindowGeometry.nearest_wrap_image(12739, 0, 0)
assert_int(result).is_equal(12739)
## The coordinator's own draw-position counterpart to
## test_compute_tile_grid_tiles_are_all_canonicalized(): the wrapped tile's
## DRAW rect (via district_to_canvas_local(), fed through
## nearest_wrap_image() the way _draw_tile_mosaic() now does) must land
## SUBSTANTIALLY on-canvas when the view covers the whole body — the exact
## Lendel shape (whole-body fit at entry, held_center at the canonical
## origin). A bare `Rect2.intersects()` check is NOT discriminating enough
## here: at Lendel's own whole-body-fit scale, the BUGGY placement (feeding
## the canonical column directly) happens to clip the viewport edge by only
## a couple of px (confirmed by hand-computation — the tile-grid's own
## edge-to-edge tiling means a full-circumference shift lands almost
## exactly one screen-width away, so `intersects()` alone would pass on a
## near-miss that still reads as "the left third is black" visually).
## Asserting a MEANINGFUL overlap FRACTION (at least half the tile's own
## area) is what actually distinguishes "correctly drawn" from "barely
## clipping the edge."
func test_wrapped_tile_draw_rect_lands_substantially_on_canvas_at_whole_body_view() -> void:
var radius_km := 6238.4 # GJ380c (Lendel) — the live-repro body
var extent: Dictionary = AtlasDescendGeometry.district_extent(radius_km)
var cols: int = int(extent["cols"])
var held_center := Vector2i.ZERO
var held_n: int = cols # enter_orbital()'s own whole-body held_n
var tile_n: int = AtlasWindowGeometry.TILE_N
var half_tile: float = float(tile_n) * 0.5
# The whole-body fit zoom/viewport (matching enter_orbital()'s own fit).
var viewport := Vector2(1600.0, 900.0)
var fit: Dictionary = AtlasWindowGeometry.fit_window_view(
viewport, held_n, CELL_PIXEL_SIZE, 0.0001, 64.0
)
var view_zoom: float = fit["zoom"]
var view_offset: Vector2 = fit["offset"]
# The wrapped tile's own canonical center — mirrors compute_tile_grid()'s
# own dedup/canonicalize step for Lendel's westmost tile.
var wrapped_raw_col := -6400
var canonical_col: int = posmod(wrapped_raw_col, cols)
var draw_col: int = AtlasWindowGeometry.nearest_wrap_image(canonical_col, held_center.x, cols)
var tile_top_left := Vector2(float(draw_col) - half_tile, 0.0 - half_tile)
var local_origin: Vector2 = AtlasWindowGeometry.district_to_canvas_local(
tile_top_left, held_center, held_n, CELL_PIXEL_SIZE
)
var extent_px: float = float(tile_n) * CELL_PIXEL_SIZE
# Canvas-local -> screen space: _canvas.position = view_offset,
# _canvas.scale = view_zoom (AtlasWindowViewer._apply_transform()'s own
# transform, mirrored here since this is a pure-geometry test with no
# live Control/Node2D tree).
var screen_top_left: Vector2 = view_offset + local_origin * view_zoom
var screen_extent: Vector2 = Vector2(extent_px, extent_px) * view_zoom
var tile_rect := Rect2(screen_top_left, screen_extent)
var viewport_rect := Rect2(Vector2.ZERO, viewport)
var overlap: Rect2 = viewport_rect.intersection(tile_rect)
var tile_area: float = screen_extent.x * screen_extent.y
var overlap_fraction: float = 0.0
if tile_area > 0.0:
overlap_fraction = (overlap.size.x * overlap.size.y) / tile_area
assert_float(overlap_fraction).override_failure_message(
(
"the wrapped tile's draw rect %s overlaps the viewport %s by only"
+ " %.1f%% of its own area — must be at least 50%% when the view"
+ " covers the whole body. This is live round 5's 'left third of the"
+ " mosaic is black' repro: drawing the CANONICAL column (%d) directly"
+ " (without nearest_wrap_image()) places this tile off-canvas RIGHT"
+ " instead of its true position on the LEFT"
)
% [tile_rect, viewport_rect, overlap_fraction * 100.0, canonical_col]
).is_greater_equal(0.5)
## The core contract this function exists for: recomputing `_view_offset` so
## a KNOWN screen point continues to map to canvas-local
## `new_held_n/2 * cell_px` (the new window's own center) — i.e. feeding the
@@ -90,6 +90,11 @@ class _TileModeViewerStub:
var tiles: Array = []
var held_center: Vector2i = Vector2i.ZERO
var held_n: int = 0
# Live round 5: nearest_wrap_image()'s cols input — 0 here (a no-radius
# passthrough) is fine for these tests, which don't exercise the wrap
# seam itself (that's test_atlas_window_geometry.gd's own coverage);
# this stub only needs to satisfy _draw_tile_mosaic()'s duck-typed call.
var body_radius_km: float = 0.0
func get_district_window() -> Variant:
return null
@@ -112,6 +117,9 @@ class _TileModeViewerStub:
func get_held_n() -> int:
return held_n
func get_body_radius_km() -> float:
return body_radius_km
class _TileSetStub:
var _tiles: Array = []
+133
View File
@@ -13,6 +13,7 @@ extends GdUnitTestSuite
const AtlasWindowRequest := preload("res://ui/implant/apps/atlas/atlas_window_request.gd")
const AtlasDescendGeometry := preload("res://ui/implant/apps/atlas/atlas_descend_geometry.gd")
const AtlasWindowGeometry := preload("res://ui/implant/apps/atlas/atlas_window_geometry.gd")
## Dudley's WINDOW_GRANULARITY_REGION_KEY (server/src/atlas/layer_proxy.rs) —
## `u32::MAX`, a RESERVED KEY-SPACE TAG the real server ALWAYS puts in the
@@ -295,6 +296,138 @@ func test_reset_to_canonical_frame_fires_and_re_centers_when_fully_zoomed_out()
).is_equal("Region")
## Live round 5's OWN repro, end to end: enter a TILING body's canonical
## frame, wheel-zoom IN far enough to cross out of tile mode (leaving
## `_held_granularity_v2` STALE at "Region" — a real, expected lag per
## `_maybe_reselect_rung()`'s own "does NOT touch _held_granularity_v2"
## doc, not a bug in that function), then wheel-zoom back OUT past the
## fully-zoomed-out threshold. The reset must fire and land EXACTLY on
## enter_orbital()'s own fit zoom for this body/viewport — not merely
## re-center while leaving `_view_zoom` wherever continued `_zoom_at()`
## scaling left it. Before the fix, the stale "Region" granularity
## satisfied the guard's OLD (center + granularity only) check forever,
## so the reset never fired again and `_view_zoom` kept shrinking via
## plain multiplication all the way to MIN_ZOOM.
func test_reset_after_crossing_out_and_back_snaps_to_the_canonical_fit_zoom() -> void:
var v: AtlasWindowViewer = auto_free(AtlasWindowViewer.new())
add_child(v)
v.size = Vector2(1600.0, 900.0)
var radius_km := 6238.4 # GJ380c (Lendel) — a tiling body, the live-repro shape
v.enter_orbital({"body_id": "GJ380c", "body_radius_km": radius_km}, {})
assert_bool(v.is_tile_mode()).override_failure_message(
"sanity: Lendel must enter tile mode — this repro needs a TILING body,"
+ " since that's where _held_granularity_v2 can lag is_tile_mode()"
).is_true()
# Zoom IN far enough to cross out of tile mode (matching
# test_zoom_crossing_recomputes_view_offset_so_the_new_window_is_on_screen's
# own gesture shape).
var cursor_pos := Vector2(800.0, 450.0)
for _i in range(60):
v._zoom_at(cursor_pos, 1.15)
if not v.is_tile_mode():
break
assert_bool(v.is_tile_mode()).override_failure_message(
"sanity: this test needs to actually leave tile mode before zooming back out"
).is_false()
assert_str(v._held_granularity_v2).override_failure_message(
"sanity: _held_granularity_v2 must be STALE at Region here (no mock response"
+ " ever adopted a new value) — this is the exact lagging-field condition"
+ " the guard fix targets, not an artificial setup"
).is_equal("Region")
# Zoom back OUT past the fully-zoomed-out threshold — the reset must fire
# (possibly after a few more _zoom_at() ticks, matching a real wheel
# gesture rather than asserting it fires on the very first step back).
for _i in range(200):
v._zoom_at(cursor_pos, 1.0 / 1.05)
if v.is_tile_mode():
break
assert_bool(v.is_tile_mode()).override_failure_message(
"zooming back out past the threshold must re-fire the reset and land back"
+ " in tile mode — the stale-granularity guard bug left this permanently false"
).is_true()
assert_that(v._held_center).is_equal(Vector2i.ZERO)
var extent: Dictionary = AtlasDescendGeometry.district_extent(radius_km)
var n: int = int(extent["cols"])
var expected_fit: Dictionary = AtlasWindowGeometry.fit_window_view(
v.size, n, AtlasWindowViewer.CELL_PIXEL_SIZE, AtlasWindowViewer.MIN_ZOOM, AtlasWindowViewer.MAX_ZOOM
)
assert_float(v._view_zoom).override_failure_message(
(
"post-reset _view_zoom (%.6f) must equal enter_orbital()'s own fit zoom"
+ " (%.6f) for this body/viewport — Jeroen's condition is the ORIGINAL"
+ " frame (center AND offset AND fit zoom), not merely re-centered at"
+ " whatever zoom continued _zoom_at() scaling left behind"
)
% [v._view_zoom, expected_fit["zoom"]]
).is_equal_approx(float(expected_fit["zoom"]), 0.000001)
## Live round 5's OWN live-drive repro, exactly: a REAL wheel gesture does
## NOT stop the instant the reset first fires — the coordinator's own
## tmp_drive_ladder.gd keeps sending wheel-down ticks toward a fixed target
## zoom (0.004, chosen below the fit zoom) regardless of the reset. This
## test reproduces that shape directly: continue zooming out PAST the point
## where the reset first re-enters tile mode, all the way to a target zoom
## BELOW the fit value. Before the second live-round-5 fix, `_view_zoom`
## drifted back down from the fit value on every subsequent `_zoom_at()`
## tick (ordinary multiplicative scaling doesn't care that a reset just
## happened) while the mode/center/granularity guard read "already
## canonical" and silently let it drift, landing on whatever the LOOP's
## target zoom happened to be instead of the fit value.
func test_reset_resnaps_even_after_continued_zoom_out_past_the_first_reset() -> void:
var v: AtlasWindowViewer = auto_free(AtlasWindowViewer.new())
add_child(v)
v.size = Vector2(1600.0, 900.0)
var radius_km := 6238.4 # GJ380c (Lendel) — the live-repro body
v.enter_orbital({"body_id": "GJ380c", "body_radius_km": radius_km}, {})
var extent: Dictionary = AtlasDescendGeometry.district_extent(radius_km)
var n: int = int(extent["cols"])
var expected_fit: Dictionary = AtlasWindowGeometry.fit_window_view(
v.size, n, AtlasWindowViewer.CELL_PIXEL_SIZE, AtlasWindowViewer.MIN_ZOOM, AtlasWindowViewer.MAX_ZOOM
)
var fit_zoom: float = float(expected_fit["zoom"])
# Zoom IN far enough to leave tile mode (same shape as the test above).
var cursor_pos := Vector2(1100.0, 300.0) # matches tmp_drive_ladder.gd's own aim point
for _i in range(60):
v._zoom_at(cursor_pos, 1.15)
if not v.is_tile_mode():
break
assert_bool(v.is_tile_mode()).is_false()
# Zoom back OUT toward a target BELOW the fit zoom — matching
# tmp_drive_ladder.gd's own `_zoom_until(wv, 0.004, false)` exactly
# (Lendel's own fit zoom is ~0.00627, comfortably above this target),
# WITHOUT stopping early the moment tile mode is first regained. A real
# wheel gesture has no way to know when the reset internally fires.
var target_zoom := 0.004
for _i in range(200):
if v._view_zoom <= target_zoom:
break
v._zoom_at(cursor_pos, 1.0 / 1.05)
assert_bool(v.is_tile_mode()).override_failure_message(
"after continued zoom-out past the reset point, the view must settle back"
+ " into tile mode — a genuinely re-snapped canonical frame can't have zoomed"
+ " OUT further than the fit value in the first place"
).is_true()
assert_float(v._view_zoom).override_failure_message(
(
"post-reset _view_zoom (%.6f) must equal the canonical fit zoom (%.6f) even"
+ " though the wheel gesture continued past the point where the reset first"
+ " fired (target was %.6f, BELOW the fit zoom) — the guard must re-fire on"
+ " every subsequent tick where the zoom has drifted away from the fit value,"
+ " not just once"
)
% [v._view_zoom, fit_zoom, target_zoom]
).is_equal_approx(fit_zoom, 0.000001)
## Not fully zoomed out (a normal District-rung view) must NOT trigger the
## reset — only reaching the top of the ladder resets, not every zoom step.
func test_reset_to_canonical_frame_does_not_fire_when_not_fully_zoomed_out() -> void: