Files
settled-reach/client/tests/test_atlas_zoom_ladder.gd
T
jpmschweitzerandClaude Fable 5 60faf667a5 fix(ui): T-1156 live rounds — zoom-compensated marker sizes; toggle-redraw regression pin
Live round 2 (the real bug): every nature-overlay marker size was a raw
screen-space constant drawn inside _canvas, whose scale IS view_zoom —
at Lendel's orbital fit zoom (0.0063) a 2.2px trunk dot rendered at
~0.014px, invisible; the same code at District's 3.75 zoom produced the
correctly-visible mouth ring, which is why one capture worked and the
headline rung didn't. Fixed via AtlasWindowGeometry.zoom_compensated_
size() (pure, floor-guarded) wired through every radius/line-width;
basin FILL points are positions and correctly stay unscaled. Suspect
tile-mode-rung-detection was ruled out live (granularity_v2=Region
confirmed in tile mode) but pinned with a named regression test anyway.
+8 pure-function tests incl. a numeric pin of the pre-fix magnitude
(<0.02px at orbital zoom); revert-verified by name. Draw-smoke suite
documented as supplementary (the shared SubViewport background harness
can pass vacuously under X11 BadMatch — the pure suite is the gate).

Live round 3 (drive-script bug, no product change): the lead's scratch
drive passed the button LABEL to set_overlay_visible() and the unknown-
id guard silently no-op'd — but the chase banked a real pin:
test_set_overlay_visible_gen_basins_flips_gate_and_redraws_nature_
overlay (draw-counting spy per the cold-start precedent; is_queued_for_
redraw does not exist in this build). Revert-verified.

Basins verified live: 7 Lendel watershed boundaries render at the
ruling's alphas. Suites: viewer 76/76, geometry-nature 42/42, nature-
overlay 22/22, zoom-ladder 50/50, no regressions across the cluster.

Tickets: T-1156

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 08:55:29 +02:00

953 lines
48 KiB
GDScript

## T-1153 (D-226 T-1143-rulings amendment): tests for the continuous
## cursor-anchored zoom ladder — enter_orbital() (the canonical planetary
## frame), progressive refinement (held composite survives a rung-crossing
## request), the full-zoom-out reset (Jeroen's HARD condition), rung
## reselection on zoom, and E/W wrap + pole-wall clamps at Region
## granularity. Split out of test_atlas_window_viewer.gd (which owns the
## pre-T-1153 window-viewer behavior — entry, cache reuse, WASD/edge-scroll,
## fit-and-center) purely for file-length reasons (gdlint max-file-lines);
## same instantiation/mock-response conventions as that file, not a
## different testing philosophy.
class_name TestAtlasZoomLadder
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
## legacy `granularity` slot for every Region response (never a real
## multiplier — District=1/Quarter=4 are the only legal wire multipliers).
## Do NOT "fix" this to 1 — that would silently un-repro the live-round bug
## this constant exists to guard against (a real server's actual wire byte,
## not a convenient test value). See _echoed_granularity_matches()'s own doc
## (atlas_window_request.gd) for why this value can NEVER equal a client's
## stored `_granularity` (which stays pinned at DISTRICT_GRANULARITY=1 for
## every rung a T-1152-aware client requests) — that mismatch is exactly
## what silently dropped every Region response before the v2-authoritative
## fix.
const SERVER_LEGACY_GRANULARITY_REGION_SENTINEL: int = 4294967295
## Build a hand-authored DistrictWindowLayer dict (n=2 by default) — mirrors
## test_atlas_window_viewer.gd's own _mock_window().
static func _mock_window(center: Vector2i, n: int = 2) -> Dictionary:
return {
"center": [center.x, center.y],
"n": n,
"morphology": PackedByteArray([8, 14, 0, 1]),
"elev_q": PackedByteArray([40, 90, 5, 60]),
"temp_dc": [120, 95, -32768, 60],
"moisture_q": PackedByteArray([50, 30, 90, 20]),
"vegetation": PackedByteArray([2, 1, 6, 3]),
"glaciation": PackedByteArray([0, 0, 1, 2]),
}
static func _mock_response(body_id: String, window: Variant) -> Dictionary:
return {"body_id": body_id, "status": "Ready", "district_window": window}
# =============================================================================
# T-1153: enter_orbital() — the canonical planetary frame, the ladder's TOP
# REST STATE (Jeroen's HARD condition, D-226 T-1143-rulings amendment).
# =============================================================================
## enter_orbital() must center on district (0,0) — "district (0,0) sits at
## lon 0 / the equator" (AtlasDescendGeometry's own doc).
func test_enter_orbital_centers_on_the_canonical_origin() -> void:
var v: AtlasWindowViewer = auto_free(AtlasWindowViewer.new())
add_child(v)
v.enter_orbital({"body_id": "GJ380c", "body_radius_km": 6238.4}, {})
assert_that(v._held_center).is_equal(Vector2i.ZERO)
## enter_orbital() must request at Region granularity — the orbital view IS
## the Region rung at high n, not a separate screen/mode (the ticket's own
## framing).
func test_enter_orbital_requests_region_granularity() -> void:
var v: AtlasWindowViewer = auto_free(AtlasWindowViewer.new())
add_child(v)
v.enter_orbital({"body_id": "GJ380c", "body_radius_km": 6238.4}, {})
assert_str(v._held_granularity_v2).is_equal("Region")
## **Superseded by live round 3's tiling fix — retargeted, not deleted.**
## GJ380c/Lendel (radius 6238.4 km) was the ORIGINAL single-window C1 repro
## (raw cols ~19,139 vs. the 6,400 clamp ceiling) — but that SAME threshold
## (`DISTRICT_WINDOW_MAX_N_REGION * DISTRICT_M` = the coverage ceiling
## `compute_tile_grid()` tiles past) means any body needing the n-clamp ALSO
## needs tiling: there is no real body where enter_orbital() takes the
## single-window path with a raw `n` big enough to require clamping.
## GJ380c now correctly enters TILE mode (test_enter_orbital_n_is_the_clamped_value_not_raw_circumference's
## old assertion on a single clamped `_held_n` no longer applies — see
## test_enter_orbital_tile_mode_held_n_is_the_whole_body_extent below for
## what `_held_n` means in tile mode instead). The single-window clamp-mirror
## fix itself remains covered: `_enter_at_rung()`'s own doc/the clamp
## mirror's unit tests (test_atlas_window_request.gd) pin the formula
## directly, and test_zoom_crossing_fires_request_and_accepts_wire_accurate_refinement
## exercises the SAME clamp-mirror lesson at the reselect (not entry)
## boundary, which single-window mode still reaches on the way DOWN from a
## tile-mode zoom-in.
func test_enter_orbital_tile_mode_held_n_is_the_whole_body_extent() -> void:
var v: AtlasWindowViewer = auto_free(AtlasWindowViewer.new())
add_child(v)
var radius_km := 6238.4 # GJ380c (Lendel)
var extent: Dictionary = AtlasDescendGeometry.district_extent(radius_km)
var raw_cols: int = int(extent["cols"])
v.enter_orbital({"body_id": "GJ380c", "body_radius_km": radius_km}, {})
assert_bool(v.is_tile_mode()).override_failure_message(
"GJ380c/Lendel needs tiling — enter_orbital() must have entered tile mode"
).is_true()
# In TILE mode, _held_n is the WHOLE body's extent (unclamped) — each
# TILE clamps its own request independently inside AtlasWindowTileSet
# (see that file's own tests), so _held_n here is NOT expected to equal
# any single clamped value the way single-window mode's is.
assert_int(v._held_n).is_equal(raw_cols)
## Coordinator live-eyeball dossier (2026-07-23, suspect 2 — DIAGNOSED FALSE
## but pinned as a regression guard anyway per the coordinator's own
## instruction): AtlasWindowNatureOverlay's _draw() reads
## viewer.get_held_granularity_v2() to key its per-rung policy tables
## (RIVER_CLASS_VISIBLE_BY_RUNG etc.) — if that accessor returned anything
## other than the EXACT string "Region" while is_tile_mode() is true (an
## empty string, a stale District default, a different-cased tag...), the
## visibility tables would silently return their empty/default disposition
## and NOTHING would draw, indistinguishable from the live captures'
## "literally zero river dots" symptom. Live drive-script evidence
## (NATURE_DEBUG print, since removed) confirmed this was NOT the actual bug
## — get_held_granularity_v2() already correctly returns "Region" in tile
## mode — but this test makes that fact load-bearing instead of merely
## observed once, so a future refactor of _enter_tile_mode()'s
## _held_granularity_v2 assignment trips a named failure here.
func test_get_held_granularity_v2_is_exactly_region_string_in_tile_mode() -> void:
var v: AtlasWindowViewer = auto_free(AtlasWindowViewer.new())
add_child(v)
var radius_km := 6238.4 # GJ380c (Lendel) — needs tiling
v.enter_orbital({"body_id": "GJ380c", "body_radius_km": radius_km}, {})
assert_bool(v.is_tile_mode()).override_failure_message(
"this test's premise requires tile mode — Lendel must still need tiling"
).is_true()
assert_str(v.get_held_granularity_v2()).override_failure_message(
"AtlasWindowNatureOverlay's _draw() keys its ENTIRE per-rung policy off"
+ " this exact string — anything other than the literal 'Region' silently"
+ " empties every visibility table and draws nothing, indistinguishable"
+ " from the live-capture symptom (zero river dots at the orbital rest state)"
).is_equal("Region")
## **The live-round-3 regression, end to end for TILE mode:** enter_orbital()
## on GJ380c/Lendel followed by delivering ONE tile's wire-accurate response
## (clamped n=6,400, "Region" granularity_v2, the legacy sentinel in the old
## granularity slot — exactly what a real server sends) must be ACCEPTED
## into that tile's own slot — not silently dropped. This exercises BOTH
## live-round fixes (the v2-authoritative precedence AND per-tile clamping)
## through the tile-set path specifically, complementing
## test_atlas_window_tile_set.gd's own more granular orchestration tests.
func test_enter_orbital_tile_mode_accepts_a_wire_accurate_tile_response() -> void:
var v: AtlasWindowViewer = auto_free(AtlasWindowViewer.new())
add_child(v)
var radius_km := 6238.4 # GJ380c (Lendel) — the live-repro body
v.enter_orbital({"body_id": "GJ380c", "body_radius_km": radius_km}, {})
assert_bool(v.is_tile_mode()).is_true()
var tile_set = v.get_tile_set()
var tiles: Array = tile_set.get_tiles()
assert_int(tiles.size()).is_greater(1)
var first_tile_center: Vector2i = tiles[0]["center"]
var tile_window: Dictionary = {
"center": [first_tile_center.x, first_tile_center.y],
"n": AtlasWindowRequest.SERVER_DISTRICT_WINDOW_MAX_N_REGION,
"granularity": SERVER_LEGACY_GRANULARITY_REGION_SENTINEL,
"granularity_v2": "Region",
"morphology": PackedByteArray([8, 14, 0, 1]),
"elev_q": PackedByteArray([40, 90, 5, 60]),
"temp_dc": [120, 95, -32768, 60],
"moisture_q": PackedByteArray([50, 30, 90, 20]),
"vegetation": PackedByteArray([2, 1, 6, 3]),
"glaciation": PackedByteArray([0, 0, 1, 2]),
}
SimBridge.atlas_layers_received.emit(_mock_response("GJ380c", tile_window))
assert_that(tile_set.get_tiles()[0]["window"]).override_failure_message(
"a wire-accurate response (clamped n, Region granularity_v2, the legacy"
+ " sentinel) for the first tile must be ACCEPTED into that tile's slot"
).is_equal(tile_window)
## A no-radius body (tiny test body) has no circumference concept —
## enter_orbital() falls back to the District-rung default window rather
## than crashing or deriving a degenerate n.
func test_enter_orbital_no_radius_body_falls_back_to_district() -> void:
var v: AtlasWindowViewer = auto_free(AtlasWindowViewer.new())
add_child(v)
v.enter_orbital({"body_id": "GJ380c"}, {})
assert_str(v._held_granularity_v2).is_equal("District")
assert_int(v._held_n).is_equal(AtlasWindowRequest.DISTRICT_WINDOW_DEFAULT_N)
# =============================================================================
# T-1153: progressive refinement — the held composite survives until the
# replacement arrives (§6 "no mode flip": never a blank frame, never a
# clear-then-redraw).
# =============================================================================
## The core acceptance test: once a window is held, a request for a
## DIFFERENT rung being in-flight must NOT clear `_window` — the old
## composite stays exactly what get_district_window() returns until the new
## rung's response actually arrives and is adopted.
func test_held_window_survives_while_a_different_rung_request_is_in_flight() -> void:
var v: AtlasWindowViewer = auto_free(AtlasWindowViewer.new())
add_child(v)
v.enter({"body_id": "GJ380c"}, {}, Vector2i(10, 20), 2)
var district_window: Dictionary = _mock_window(Vector2i(10, 20), 2)
SimBridge.atlas_layers_received.emit(_mock_response("GJ380c", district_window))
assert_that(v.get_district_window()).is_equal(district_window)
# Simulate a rung-reselect firing a NEW (Region) request without the
# response having arrived yet — direct call, mirroring what
# _maybe_reselect_rung() does internally.
v._window_request.request_debounced("GJ380c", Vector2i(10, 20), 2, "Region")
assert_that(v.get_district_window()).override_failure_message(
"the OLD composite must survive while a different-rung request is in"
+ " flight — no blank frame, no premature clear"
).is_equal(district_window)
## Once the new rung's response actually arrives (matching the CURRENTLY
## in-flight request's granularity_v2), it swaps in — the composite reference
## changes from the old rung's window to the new one.
func test_new_rung_window_swaps_in_once_it_arrives() -> void:
var v: AtlasWindowViewer = auto_free(AtlasWindowViewer.new())
add_child(v)
v.enter({"body_id": "GJ380c"}, {}, Vector2i(10, 20), 2)
var district_window: Dictionary = _mock_window(Vector2i(10, 20), 2)
SimBridge.atlas_layers_received.emit(_mock_response("GJ380c", district_window))
v._window_request.request_debounced("GJ380c", Vector2i(10, 20), 2, "Region")
var region_window: Dictionary = {
"center": [10, 20], "n": 2, "granularity_v2": "Region",
"morphology": PackedByteArray([1, 2, 3, 4]),
"elev_q": PackedByteArray([10, 20, 30, 40]),
"temp_dc": [0, 0, 0, 0],
"moisture_q": PackedByteArray([0, 0, 0, 0]),
"vegetation": PackedByteArray([0, 0, 0, 0]),
"glaciation": PackedByteArray([0, 0, 0, 0]),
}
SimBridge.atlas_layers_received.emit(_mock_response("GJ380c", region_window))
assert_that(v.get_district_window()).override_failure_message(
"once the new rung's matching response arrives, it must swap in"
).is_equal(region_window)
assert_str(v._held_granularity_v2).is_equal("Region")
## refresh() clear()s via queue_free() (deferred, not synchronous) — a legend
## that has refreshed more than once in the same frame (build-time refresh at
## _ready(), then an entry-time refresh) can have STALE not-yet-freed
## children still parented alongside the new ones. add_component() always
## APPENDS, so the current ImplantHeader is the LAST one in the list, never
## assumed to be [0].
static func _current_legend_header(legend_panel) -> ImplantHeader:
var children: Array = legend_panel.get_implant_children()
for i in range(children.size() - 1, -1, -1):
if children[i] is ImplantHeader:
return children[i]
return null
## PR #192 review (Araminta, BLOCKING): the legend subtitle used to hardcode
## District's own "2.048 km/cell" — a 100x lie whenever the viewer actually
## holds Region (204.8 km/cell). While in the orbital tile-mode rest state
## (Region granularity), the legend must read Region's real spacing, not the
## stale District literal.
func test_legend_subtitle_reflects_region_spacing_in_tile_mode() -> void:
var v: AtlasWindowViewer = auto_free(AtlasWindowViewer.new())
add_child(v)
var radius_km := 6238.4 # GJ380c (Lendel) — needs tiling, so enters at Region
v.enter_orbital({"body_id": "GJ380c", "body_radius_km": radius_km}, {})
assert_bool(v.is_tile_mode()).is_true()
assert_str(v._held_granularity_v2).is_equal("Region")
var header: ImplantHeader = _current_legend_header(v._legend_panel)
assert_str(header._subtitle_label.text).override_failure_message(
"legend subtitle must reflect Region's real 204.800 km/cell spacing while"
+ " the viewer holds Region granularity, not a hardcoded District figure"
).contains("204.800 km/cell")
## Same bug, the other direction: after crossing INTO a single-window District
## rung, the legend must re-render with District's own spacing — proving the
## legend actually refreshes on a rung change rather than being stuck at
## whatever it showed on the FIRST refresh() call (T-1153's _build_legend_panel()
## fires one at _ready() time, before any real rung is held).
func test_legend_subtitle_reflects_district_spacing_after_crossing_in() -> void:
var v: AtlasWindowViewer = auto_free(AtlasWindowViewer.new())
add_child(v)
v.enter({"body_id": "GJ380c"}, {}, Vector2i(10, 20), 2)
assert_str(v._held_granularity_v2).is_equal("District")
var header: ImplantHeader = _current_legend_header(v._legend_panel)
assert_str(header._subtitle_label.text).override_failure_message(
"legend subtitle must re-render at District's own 2.048 km/cell spacing"
+ " once the viewer holds a District-rung window — proving refresh() is"
+ " actually wired to the rung change, not just called once at build time"
).contains("2.048 km/cell")
## A response for a rung OTHER than what's currently requested (e.g. a
## District response arriving after the viewer has already moved on to a
## Region request — a rapid wheel-zoom race) must be discarded as stale, the
## held composite untouched.
func test_stale_rung_response_after_moving_on_is_discarded() -> void:
var v: AtlasWindowViewer = auto_free(AtlasWindowViewer.new())
add_child(v)
v.enter({"body_id": "GJ380c"}, {}, Vector2i(10, 20), 2)
var district_window: Dictionary = _mock_window(Vector2i(10, 20), 2)
SimBridge.atlas_layers_received.emit(_mock_response("GJ380c", district_window))
v._window_request.request_debounced("GJ380c", Vector2i(10, 20), 2, "Region")
# A LATE district-rung response for the same (center, n) arrives after the
# viewer has already moved on to requesting Region — must be dropped.
var late_district_window: Dictionary = _mock_window(Vector2i(10, 20), 2)
late_district_window["morphology"] = PackedByteArray([9, 9, 9, 9]) # distinguishable payload
SimBridge.atlas_layers_received.emit(_mock_response("GJ380c", late_district_window))
assert_that(v.get_district_window()).override_failure_message(
"a stale response for a rung the viewer has since moved on from must be discarded"
).is_equal(district_window)
# =============================================================================
# T-1153: full-zoom-out reset (Jeroen's HARD condition).
# =============================================================================
## Directly at the canonical frame already (center (0,0), Region granularity)
## must be a no-op — never re-fights a player zooming back IN from the top.
func test_reset_to_canonical_frame_is_noop_when_already_there() -> void:
var v: AtlasWindowViewer = auto_free(AtlasWindowViewer.new())
add_child(v)
v.enter_orbital({"body_id": "GJ380c", "body_radius_km": 6238.4}, {})
v._held_center = Vector2i.ZERO
v._held_granularity_v2 = "Region"
var fired: bool = v._maybe_reset_to_canonical_frame()
assert_bool(fired).override_failure_message(
"already at the canonical frame — the reset must not re-fire"
).is_false()
## A no-radius body must never trigger the reset (no circumference concept —
## matches enter_orbital()'s own guard).
func test_reset_to_canonical_frame_never_fires_for_no_radius_body() -> void:
var v: AtlasWindowViewer = auto_free(AtlasWindowViewer.new())
add_child(v)
v.enter({"body_id": "GJ380c"}, {}, Vector2i(5, 5), 2)
var fired: bool = v._maybe_reset_to_canonical_frame()
assert_bool(fired).is_false()
## Away from the canonical frame (a drifted District-rung pan/zoom state)
## with a fully-zoomed-out world extent must reset — the direct wiring test
## for Jeroen's HARD condition: enter() at a far-off center, then force the
## view zoom low enough that the displayed extent covers the whole body.
func test_reset_to_canonical_frame_fires_and_re_centers_when_fully_zoomed_out() -> void:
var v: AtlasWindowViewer = auto_free(AtlasWindowViewer.new())
add_child(v)
var radius_km := 50.0 # tiny synthetic body — small circumference, reachable by a modest zoom-out
v.enter({"body_id": "GJ380c", "body_radius_km": radius_km}, {}, Vector2i(500, 10), 32)
# Force a very low zoom — a huge displayed world extent, comfortably over
# this tiny body's whole circumference.
v._view_zoom = AtlasWindowViewer.MIN_ZOOM
var fired: bool = v._maybe_reset_to_canonical_frame()
assert_bool(fired).override_failure_message(
"a fully-zoomed-out view on a real-radius body must trigger the reset"
).is_true()
assert_that(v._held_center).override_failure_message(
"the reset must re-center on the canonical origin (0,0)"
).is_equal(Vector2i.ZERO)
assert_str(v._held_granularity_v2).override_failure_message(
"the reset must land on the Region rung — the ladder's top rest state"
).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 round-5 fix, `_view_zoom` drifted back
## down from the fit value on every subsequent `_zoom_at()` tick 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. **Live round 6 update:** the MECHANISM that
## now holds this assertion changed — `_zoom_at()`'s own zoom FLOOR (not a
## re-firing reset) is what keeps `_view_zoom` pinned at fit through
## continued zoom-out ticks; see `_maybe_reset_to_canonical_frame()`'s own
## doc for why re-firing on every tick caused a request storm. This test's
## own assertions are unchanged — only the doc below was updated to match.
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) — _zoom_at()'s own zoom floor"
+ " must keep pinning it at fit through every subsequent tick, not just once"
)
% [v._view_zoom, fit_zoom, target_zoom]
).is_equal_approx(fit_zoom, 0.000001)
## Live round 6's ANTI-STORM test — the exact repro the coordinator's live
## drive caught: drive a REAL continued zoom-out gesture (via `_zoom_at()`,
## the same call path the live drive uses — NOT calling
## `_maybe_reset_to_canonical_frame()` directly with unchanged state, which
## trivially can't reproduce the drift the storm depends on) many ticks past
## the point where the reset first fires — asserts ZERO additional tile-set
## entries occur across the WHOLE gesture. Spies on `AtlasWindowTileSet`'s
## own child `AtlasWindowRequest` node INSTANCES (captured right after the
## FIRST reset) — a fresh `enter_orbital()` call tears down (`queue_free()`s)
## every one of them and creates BRAND NEW ones, so "the same node
## instances are still alive and still the tile set's children after 100
## more ticks" is a direct, non-invasive proxy for "the reset never fired
## again" — no new production instrumentation needed. Before the round-6
## fix, `_zoom_at()`'s continued multiplicative zoom-out drifted `_view_zoom`
## below fit on every subsequent tick, the level-triggered guard read "not
## already there" every time, and `enter_orbital()` fired repeatedly:
## tearing down and recreating the tile set (and its 6 request nodes) every
## tick — exactly the "889 of 897 wire responses arrived during one
## zoom-out phase" storm.
func test_reset_evaluated_repeatedly_at_canonical_frame_issues_zero_additional_requests() -> 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()).is_true()
# Zoom IN far enough to leave tile mode, then zoom back OUT past the
# first reset — same shape as the round-5 continued-zoom-out test, but
# this time spying on the tile set across the WHOLE remaining gesture
# instead of only checking the final zoom value.
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()
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(
"sanity: the first reset must have fired before spying on the tile set"
).is_true()
var tile_set = v.get_tile_set()
var original_requests: Array = tile_set.get_children()
assert_int(original_requests.size()).override_failure_message(
"sanity: the first reset must have created real tile-request child nodes to spy on"
).is_greater(0)
# Continue the SAME zoom-out gesture 100 MORE ticks past the first
# reset — a real wheel gesture has no way to stop exactly at the reset
# point, and holding the wheel down (or residual scroll momentum) keeps
# sending ticks. None of these must tear down/recreate the tile set.
for _i in range(100):
v._zoom_at(cursor_pos, 1.0 / 1.05)
var current_requests: Array = tile_set.get_children()
assert_int(current_requests.size()).override_failure_message(
"the tile set's child count must be unchanged after 100 more continued"
+ " zoom-out ticks — a changed count means teardown/recreate happened"
).is_equal(original_requests.size())
for i in range(original_requests.size()):
assert_bool(is_instance_valid(original_requests[i])).override_failure_message(
"original tile-request node #%d must still be alive — a storm would have"
+ " queue_free()'d it and created a fresh one" % i
).is_true()
assert_bool(is_same(original_requests[i], current_requests[i])).override_failure_message(
(
"tile-request node #%d must be the SAME instance as right after the"
+ " first reset — a different object at the same index means the tile"
+ " set was torn down and recreated (a storm), even if the count"
+ " coincidentally matches"
)
% i
).is_true()
## Live round 6's BLACK-ENTRY repro: enter_orbital(), then deliver the six
## wire-accurate tile responses WHILE a REAL continued zoom-out gesture (via
## `_zoom_at()`, matching the live drive's actual input shape — a held
## wheel-down keeps sending ticks concurrently with responses streaming in
## from the server) is in flight — asserts all six are accepted and HELD
## (tile set stable throughout, no teardown between delivery and the final
## assertion). Before the round-6 fix, the level-triggered guard fired on
## every zoom-out tick once `_view_zoom` drifted below fit, tearing down the
## tile set mid-delivery and orphaning responses addressed to now-freed
## request nodes — nothing ever accumulated, and the mosaic stayed black
## even though the server dutifully answered every request.
func test_six_tile_responses_survive_concurrent_reset_evaluation_and_are_held() -> 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) — 6 tiles, the live-repro shape
v.enter_orbital({"body_id": "GJ380c", "body_radius_km": radius_km}, {})
assert_bool(v.is_tile_mode()).is_true()
var tile_set = v.get_tile_set()
var tiles: Array = tile_set.get_tiles()
assert_int(tiles.size()).override_failure_message(
"sanity: Lendel must produce Lendel's own real tile count (6) for this"
+ " repro to be faithful, not a smaller synthetic count"
).is_equal(6)
# Same continued zoom-out gesture as the anti-storm test above — leave
# tile mode, cross back into it (the first reset), then KEEP sending
# zoom-out ticks (a real held wheel has no way to stop exactly at the
# reset point). Responses are delivered interleaved with these ticks,
# exactly matching the live drive's concurrent shape.
var cursor_pos := Vector2(1100.0, 300.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()).is_false()
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(
"sanity: the first reset must have fired before delivering responses"
).is_true()
for i in range(tiles.size()):
var center: Vector2i = tiles[i]["center"]
var tile_window: Dictionary = {
"center": [center.x, center.y],
"n": AtlasWindowRequest.SERVER_DISTRICT_WINDOW_MAX_N_REGION,
"granularity": SERVER_LEGACY_GRANULARITY_REGION_SENTINEL,
"granularity_v2": "Region",
"morphology": PackedByteArray([8, 14, 0, 1]),
"elev_q": PackedByteArray([40, 90, 5, 60]),
"temp_dc": [120, 95, -32768, 60],
"moisture_q": PackedByteArray([50, 30, 90, 20]),
"vegetation": PackedByteArray([2, 1, 6, 3]),
"glaciation": PackedByteArray([0, 0, 1, 2]),
}
SimBridge.atlas_layers_received.emit(_mock_response("GJ380c", tile_window))
# Interleave several MORE continued zoom-out ticks, matching the live
# drive's per-frame cadence — none of these must tear anything down.
for _tick in range(5):
v._zoom_at(cursor_pos, 1.0 / 1.05)
var final_tiles: Array = tile_set.get_tiles()
assert_int(final_tiles.size()).override_failure_message(
"the tile set must still have all 6 tile slots — a storm mid-delivery"
+ " would have torn it down and rebuilt it with fresh (unfulfilled) slots"
).is_equal(6)
for i in range(final_tiles.size()):
assert_that(final_tiles[i]["window"]).override_failure_message(
(
"tile #%d's window must be HELD (non-null) — all six wire-accurate"
+ " responses delivered during a concurrent continued zoom-out gesture"
+ " must survive to be accepted, not be silently dropped by an"
+ " orphaning teardown"
)
% i
).is_not_null()
## 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:
var v: AtlasWindowViewer = auto_free(AtlasWindowViewer.new())
add_child(v)
v.enter({"body_id": "GJ380c", "body_radius_km": 6238.4}, {}, Vector2i(500, 10), 32)
v._view_zoom = 1.0 # a normal, non-extreme zoom — nowhere near full planetary coverage
var fired: bool = v._maybe_reset_to_canonical_frame()
assert_bool(fired).override_failure_message(
"an ordinary District-rung view must not trigger the top-rest-state reset"
).is_false()
# =============================================================================
# T-1153: rung reselection — _zoom_at() crossing a rung threshold fires a
# new request without touching the held composite.
# =============================================================================
## Zooming OUT far enough from a District-rung window (small n, so a modest
## zoom-out already covers a huge world extent) must fire a coarser-rung
## request — the wheel-zoom-driven wiring test for _maybe_reselect_rung().
func test_zoom_out_past_district_threshold_requests_a_coarser_rung() -> void:
var v: AtlasWindowViewer = auto_free(AtlasWindowViewer.new())
add_child(v)
v.size = Vector2(800.0, 600.0)
v.enter({"body_id": "GJ380c"}, {}, Vector2i(0, 0), 2) # n=2 — a tiny window, easy to overshoot
var district_window: Dictionary = _mock_window(Vector2i(0, 0), 2)
SimBridge.atlas_layers_received.emit(_mock_response("GJ380c", district_window))
assert_str(v._window_request.get_granularity_v2()).is_equal("District")
# A big zoom-OUT factor (well under 1.0) from a tiny n=2 window blows the
# displayed world extent WAY past District's threshold.
v._zoom_at(Vector2(400.0, 300.0), 0.01)
assert_str(v._window_request.get_granularity_v2()).override_failure_message(
"zooming out far enough from a small District window must re-request a coarser rung"
).is_not_equal("District")
# The OLD composite must still be what's held — progressive refinement,
# not a block-on-derive clear.
assert_that(v.get_district_window()).is_equal(district_window)
## Zooming IN on a District-rung window (well within its own legal coverage
## band, `(32,768 m, 131,072 m]` per select_rung()'s redesigned per-rung
## ceiling model — viewport-independent since `canvas_px` no longer affects
## selection) must NOT trigger a rung change — this is the "zoom is
## client-side on the already-held composite" case, unchanged for in-rung
## zoom. Sets _view_zoom DIRECTLY to a value inside District's band (rather
## than relying on enter()'s COVER auto-fit, which for a small n can already
## sit right at Quarter's own threshold — a fit's zoom level is a
## display-density choice independent of what rung selection would pick from
## scratch, and this test is specifically about a SINGLE zoom-in STEP not
## crossing a boundary, not about where the auto-fit itself lands). The
## small 100x80 viewport here is incidental (any size works under the new
## viewport-independent model) — kept small only because that's what the
## original version of this test used.
func test_zoom_in_within_district_threshold_does_not_change_rung() -> void:
var v: AtlasWindowViewer = auto_free(AtlasWindowViewer.new())
add_child(v)
v.size = Vector2(100.0, 80.0)
v.enter({"body_id": "GJ380c"}, {}, Vector2i(0, 0), 32)
var district_window: Dictionary = _mock_window(Vector2i(0, 0), 32)
SimBridge.atlas_layers_received.emit(_mock_response("GJ380c", district_window))
v._view_zoom = 0.10666666666666667 # E=120,000m at C=100px — inside District's legal band
v._apply_transform()
v._zoom_at(Vector2(50.0, 40.0), 1.15) # a single ordinary zoom-in step
assert_str(v._window_request.get_granularity_v2()).override_failure_message(
"a single ordinary zoom-in step must not cross a rung threshold"
).is_equal("District")
## **Live round 3 regression, the direct end-to-end fix target:** a real
## wheel-zoom gesture (many `_zoom_at()` ticks, matching the shape a
## continuous mouse-wheel scroll actually produces) crossing from the
## Region rest state down through District into Quarter territory must (i)
## fire a request at the NEW granularity — `_window_request.get_granularity_v2()`
## must have changed by the end of the gesture — and (ii) accept a
## WIRE-ACCURATE response for that request: echoing the REQUEST's own
## (already re-centered, already re-clamped) center/n, which the live round
## found DIFFERS from the ORIGINAL held center (screen-center-anchored
## refinement re-centers on wherever the cursor currently maps to, not
## wherever the player started) — this is the "second latent drop" the
## coordinator specifically flagged: comparing the echo against a STALE
## `_held_center` (frozen at the pre-crossing value) rather than the
## request's own center would silently drop this response too.
## **Live round 3 update:** GJ380c/Lendel now enters TILE mode via
## enter_orbital() (bug B's fix), so this test starts from THERE — zooming
## in far enough crosses Region's coverage ceiling and must LEAVE tile mode
## for the single-window path at the new (finer) rung, exactly the
## `_maybe_reselect_rung()` "leaving_tile_mode" branch this test exercises.
func test_zoom_crossing_fires_request_and_accepts_wire_accurate_refinement() -> 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}, {})
assert_bool(v.is_tile_mode()).override_failure_message(
"GJ380c/Lendel must enter tile mode at the orbital rest state (live round 3)"
).is_true()
# A real wheel-zoom gesture: many ticks, cursor OFF-CENTER (so cursor-
# anchored zoom genuinely drifts the screen-to-district mapping away from
# the canonical origin, not just scaling in place) — matching the live
# drive's actual input shape, not a single synthetic jump. Zooming in far
# enough must cross OUT of Region's coverage ceiling, leaving tile mode.
var cursor_pos := Vector2(1100.0, 300.0) # off-center, biased toward one quadrant
for _i in range(60):
v._zoom_at(cursor_pos, 1.15)
if not v.is_tile_mode():
break
# (i) Tile mode must have been LEFT, and a request must have gone out at
# a NEW (finer) granularity via the single-window path.
assert_bool(v.is_tile_mode()).override_failure_message(
"zooming in far enough must leave tile mode for the single-window path"
).is_false()
var request_granularity: String = v._window_request.get_granularity_v2()
assert_str(request_granularity).override_failure_message(
"leaving tile mode must fire a request at a new (finer) granularity"
).is_not_equal("Region")
# (ii) The request's own center/n — read AFTER leaving tile mode, so this
# is whatever _maybe_reselect_rung() actually computed — is what a
# wire-accurate response must echo to be accepted.
var request_center: Vector2i = v._window_request._center
var request_n: int = v._window_request._n
var refinement_window: Dictionary = {
"center": [request_center.x, request_center.y],
"n": request_n,
"granularity_v2": request_granularity,
"morphology": PackedByteArray([1, 2, 3, 4]),
"elev_q": PackedByteArray([10, 20, 30, 40]),
"temp_dc": [0, 0, 0, 0],
"moisture_q": PackedByteArray([0, 0, 0, 0]),
"vegetation": PackedByteArray([0, 0, 0, 0]),
"glaciation": PackedByteArray([0, 0, 0, 0]),
}
SimBridge.atlas_layers_received.emit(_mock_response("GJ380c", refinement_window))
assert_that(v.get_district_window()).override_failure_message(
"a wire-accurate refinement response (echoing the REQUEST's own center/n/"
+ " granularity after leaving tile mode) must be ACCEPTED — comparing"
+ " against a stale/wrong reference instead of the request's own would"
+ " silently drop this response forever"
).is_equal(refinement_window)
assert_str(v._held_granularity_v2).is_equal(request_granularity)
## Live round 4's SECOND bug, pinned directly: `_maybe_reselect_rung()` must
## recompute `_view_offset` (via AtlasWindowGeometry.
## recompute_offset_for_held_n_change()) the instant `_held_n` changes across
## a rung crossing — leaving it untouched (the round-4 bug) means the single-
## window `Rect2(0,0,extent)` draw call renders at whatever screen position
## the OLD (Region-scale) offset happened to put canvas-local (0,0), which
## for a whole-body `held_n` vs. a 64-district District `held_n` is tens or
## hundreds of thousands of px away from the viewport — the exact "pitch
## black" repro. Asserts the NEW held window's own extent actually overlaps
## the viewport after the crossing, the concrete on-screen consequence a
## stale offset breaks.
func test_zoom_crossing_recomputes_view_offset_so_the_new_window_is_on_screen() -> 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}, {})
assert_bool(v.is_tile_mode()).is_true()
var cursor_pos := Vector2(1100.0, 300.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 cross out of tile mode to exercise"
+ " the held_n change _maybe_reselect_rung() must react to"
).is_false()
# The new (post-crossing) window's screen-space rect, using the SAME
# formula the overlay's single-window _draw() itself uses
# (Rect2(0,0,extent,extent) in canvas-local space, then _canvas's own
# position/scale transform — _view_offset/_view_zoom here mirror that
# exactly, since _apply_transform() is what sets _canvas.position/scale).
var extent_screen: float = float(v._held_n) * v.CELL_PIXEL_SIZE * v._view_zoom
var screen_top_left: Vector2 = v._view_offset
var screen_bottom_right: Vector2 = screen_top_left + Vector2(extent_screen, extent_screen)
var viewport_rect := Rect2(Vector2.ZERO, v.size)
var window_rect := Rect2(screen_top_left, Vector2(extent_screen, extent_screen))
assert_bool(viewport_rect.intersects(window_rect)).override_failure_message(
(
"the new (post-crossing) held window's screen rect %s must overlap the"
+ " viewport %s — a stale _view_offset (never recomputed for the new"
+ " held_n=%d) is exactly live round 4's 'pitch black' bug: the composite"
+ " renders somewhere entirely off-canvas despite request/response/data"
+ " all being individually correct"
)
% [window_rect, viewport_rect, v._held_n]
).is_true()
# =============================================================================
# T-1153: E/W wrap and pole-wall clamps at EVERY rung — both are extent-
# relative (CELL_PIXEL_SIZE-based district-space math, unchanged regardless
# of which rung's data is actually held), so they must keep working
# unmodified at Region granularity, not just District/Quarter.
# =============================================================================
## The pole wall, wired through the real _apply_pan_delta() path, must still
## clamp at Region granularity — same mechanism as the existing District-rung
## test (test_wasd_pan_is_clamped_by_the_pole_wall_when_wired), just entered
## via enter_orbital() instead of enter().
func test_pole_wall_clamps_at_region_granularity_too() -> void:
var v: AtlasWindowViewer = auto_free(AtlasWindowViewer.new())
add_child(v)
v.size = Vector2(800.0, 800.0)
var radius_km := 50.0 # tiny synthetic body — pole wall reachable by an ordinary tick
v.enter_orbital({"body_id": "GJ380c", "body_radius_km": radius_km}, {})
assert_str(v._held_granularity_v2).is_equal("Region")
var unclamped_magnitude: float = 500.0 * AtlasWindowViewer.PAN_SPEED_CANVAS_PX_S * v.get_view_zoom()
v._apply_pan_delta(Vector2(0.0, -1.0), 500.0) # "W"/north held, an absurdly long tick
assert_float(absf(v.get_view_offset().y)).override_failure_message(
"the pole wall must still clamp an extreme pan at Region granularity"
).is_less(unclamped_magnitude * 0.5)
## East-west wrap (canonicalize_district_center()) must still apply to the
## pan-edge refloat's resulting center at Region granularity — a pan that
## carries the screen-center column past the body's circumference must wrap
## into [0, cols), never run away to an out-of-range column, exactly as the
## District-rung wrap tests already pin (T-1142 item 6a). At Region's own
## enormous held_n (a whole circumference), an ORDINARY pan tick's
## canvas-space delta is negligible relative to the window's half-extent
## (confirmed: ~0.08 districts per 5-second tick vs. a ~9,772-district
## half-window) — so this drives _maybe_refloat_window() DIRECTLY off a
## manually-set _view_offset large enough to genuinely cross the held
## window's edge, the same "exercise the actual edge-crossing branch, not
## just its no-op early-return" discipline _maybe_refloat_window()'s own
## inside-check comment describes.
func test_pan_edge_refloat_wraps_columns_at_region_granularity_too() -> void:
var v: AtlasWindowViewer = auto_free(AtlasWindowViewer.new())
add_child(v)
v.size = Vector2(800.0, 800.0)
var radius_km := 6371.0
var extent: Dictionary = AtlasDescendGeometry.district_extent(radius_km)
var cols: int = int(extent["cols"])
v.enter_orbital({"body_id": "GJ380c", "body_radius_km": radius_km}, {})
assert_str(v._held_granularity_v2).is_equal("Region")
# Force the held center to sit one column short of the wrap seam, then
# shift the CANVAS offset by more than half the window's own on-screen
# extent — enough to move the screen-center's mapped column past the
# window's far edge (i.e. past `cols`, crossing the seam) regardless of
# Region's huge held_n.
v._held_center = Vector2i(cols - 1, 0)
var half_window_screen_px: float = float(v._held_n) * v.get_cell_pixel_size() * v.get_view_zoom() * 0.5
v._view_offset = v.get_view_offset() - Vector2(half_window_screen_px * 1.5, 0.0)
v._maybe_refloat_window()
assert_int(v._held_center.x).override_failure_message(
"a pan crossing the antimeridian at Region granularity must wrap the"
+ " resulting center into [0, cols), never run past cols"
).is_less(cols)
assert_int(v._held_center.x).is_greater_equal(0)