Files
settled-reach/client/tests/test_atlas_window_geometry.gd
T
jpmschweitzerandClaude Fable 5 372ce37bb6 feat(ui): T-1145 round-2 polish — cover-fit, WASD+edge-scroll pan, smoothed composite (Jeroen rulings)
Cover-fit: fit_window_view zooms from the viewport's LARGER dimension,
no margin factor (any factor under 1.0 leaves a long-axis gap — checked
numerically) — the composite fills edge to edge, overhanging the short
axis into pan-space; the refloat center-equality early-return already
prevents refetch churn at rest (proved, not just tested).

Input model (Jeroen: drag breaks click semantics with map objects):
LMB-drag pan REMOVED from the regional window; clicks are
object-reserved. Pan = held WASD/arrows polled in _process (delta- and
zoom-scaled, camera-pans-toward-key convention verified numerically)
plus edge-scroll within 24px of the viewport border; both suppressed
over UI and on OS focus loss; both set _user_adjusted; wheel zoom and
Esc unchanged. Reads RAW physical keycodes deliberately — independent
of the shared D-054 move_* InputMap actions bound to the same keys
(whose occlusion-leak is pre-existing and now ticketed as T-1146).
Pole wall + east-west wrap unchanged, re-driven through the new
inputs; drag tests replaced, not kept.

Smoothed composite (interim pending T-1143): per-cell colors bake into
an n x n Image/ImageTexture (exact existing colorizer incl. overlay +
ice tint) drawn once with LINEAR filtering — GPU bilinear reads as
terrain, the planetary heightmap's own treatment. Crisp per-cell path
preserved behind COMPOSITE_SMOOTH for T-1143 A/B. Rebuild only on
reference-identity change of window/toggle (is_same — verified true
reference equality; value-equal distinct dicts DO rebuild).

Governance: T-1145 amendment paragraph on D-226 T-1124 SS5 (all three
supersessions); pql decisions validate ok.

Suites: window_viewer 74/74, window_geometry 32/32, window_overlay
(new) 16/16; gdlint clean on all six files.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 16:51:24 +02:00

268 lines
13 KiB
GDScript

## T-1142 (Jeroen's second/third hands-on findings): pure-function tests for
## AtlasWindowViewer's fit-and-center math (fit_window_view) and pole-wall
## pan clamp (clamp_pan_offset_to_pole_wall) — both extracted specifically so
## the "viewport + n -> zoom/offset" transform is unit-testable without a
## live Control tree.
class_name TestAtlasWindowGeometry
extends GdUnitTestSuite
const AtlasWindowGeometry := preload("res://ui/implant/apps/atlas/atlas_window_geometry.gd")
const AtlasDescendGeometry := preload("res://ui/implant/apps/atlas/atlas_descend_geometry.gd")
const MIN_ZOOM: float = 0.5
const MAX_ZOOM: float = 8.0
const CELL_PIXEL_SIZE: float = 16.0
# =============================================================================
# fit_window_view — the "postage stamp" fix (item 2)
# =============================================================================
## n=32, cell_px=16 -> native composite is 512x512. T-1145 item 1: COVER
## fit derives zoom from the LARGER viewport dimension (1920, not 1080) with
## NO margin factor — zoom = 1920 / 512 = 3.75 — well inside [MIN_ZOOM,
## MAX_ZOOM], so the clamp is a no-op here.
func test_fit_window_view_computes_expected_zoom_for_a_wide_viewport() -> void:
var fit: Dictionary = AtlasWindowGeometry.fit_window_view(
Vector2(1920.0, 1080.0), 32, CELL_PIXEL_SIZE, MIN_ZOOM, MAX_ZOOM
)
var expected_zoom: float = 1920.0 / 512.0
assert_float(fit["zoom"]).is_equal_approx(expected_zoom, 0.001)
## The composite must be CENTERED — offset.x/.y each leave an equal margin on
## both sides of the (n*cell_px*zoom)-sized composite (a NEGATIVE "margin" is
## fine and expected under cover — it just means the composite overhangs
## that axis, checked separately by test_fit_window_view_covers_with_no_gap).
func test_fit_window_view_centers_the_composite() -> void:
var viewport := Vector2(1920.0, 1080.0)
var fit: Dictionary = AtlasWindowGeometry.fit_window_view(
viewport, 32, CELL_PIXEL_SIZE, MIN_ZOOM, MAX_ZOOM
)
var composite_scaled: float = 32.0 * CELL_PIXEL_SIZE * float(fit["zoom"])
var offset: Vector2 = fit["offset"]
# The composite's right/bottom edge is offset + composite_scaled — the
# margin on the far side must equal the margin on the near side (offset).
var right_margin: float = viewport.x - (offset.x + composite_scaled)
var bottom_margin: float = viewport.y - (offset.y + composite_scaled)
assert_float(right_margin).is_equal_approx(offset.x, 0.01)
assert_float(bottom_margin).is_equal_approx(offset.y, 0.01)
## T-1145 item 1 (Jeroen's round-2 finding, KALLAST window): a wide viewport
## must show NO side margins — the composite's LONG axis (the one the cover
## zoom is derived from) must land EXACTLY at the viewport edges (offset ~=
## 0 on that axis), and the SHORT axis must OVERHANG past both edges
## (negative margin — the composite is bigger than the viewport there,
## exactly what "cover" means). This is the literal assertion the coordinator
## asked for: no side margins at 16:9.
func test_fit_window_view_covers_with_no_gap_on_the_long_axis() -> void:
var viewport := Vector2(1920.0, 1080.0)
var fit: Dictionary = AtlasWindowGeometry.fit_window_view(
viewport, 32, CELL_PIXEL_SIZE, MIN_ZOOM, MAX_ZOOM
)
var composite_scaled: float = 32.0 * CELL_PIXEL_SIZE * float(fit["zoom"])
var offset: Vector2 = fit["offset"]
# Long axis (X, 1920 > 1080): the composite must span EXACTLY the
# viewport width — zero margin on both sides.
assert_float(offset.x).override_failure_message(
"the long (cover) axis must have NO side margin — offset.x should be ~0"
).is_equal_approx(0.0, 0.5)
var right_margin: float = viewport.x - (offset.x + composite_scaled)
assert_float(right_margin).override_failure_message(
"the long (cover) axis's far edge must have NO margin either"
).is_equal_approx(0.0, 0.5)
# Short axis (Y, 1080 < 1920): the composite must OVERHANG (negative
# margin) past BOTH edges — this is the data that extends into pan-space.
assert_float(offset.y).override_failure_message(
"the short axis must OVERHANG past the top edge (negative offset)"
).is_less(0.0)
## A TALL viewport (portrait) must cover the same way, just with the axes
## swapped — long axis (Y) gets zero margin, short axis (X) overhangs.
func test_fit_window_view_covers_a_tall_viewport_too() -> void:
var viewport := Vector2(1080.0, 1920.0)
var fit: Dictionary = AtlasWindowGeometry.fit_window_view(
viewport, 32, CELL_PIXEL_SIZE, MIN_ZOOM, MAX_ZOOM
)
var offset: Vector2 = fit["offset"]
assert_float(offset.y).override_failure_message(
"the long (cover) axis (Y, portrait) must have NO side margin"
).is_equal_approx(0.0, 0.5)
assert_float(offset.x).override_failure_message(
"the short axis (X, portrait) must overhang past the left edge"
).is_less(0.0)
## A perfectly square viewport needs NO overhang on either axis — cover and
## contain agree exactly at a 1:1 aspect ratio (the degenerate case where
## "long" and "short" axis are the same).
func test_fit_window_view_square_viewport_has_no_overhang_either_axis() -> void:
var fit: Dictionary = AtlasWindowGeometry.fit_window_view(
Vector2(1024.0, 1024.0), 32, CELL_PIXEL_SIZE, MIN_ZOOM, MAX_ZOOM
)
assert_vector(fit["offset"]).is_equal_approx(Vector2.ZERO, Vector2(0.5, 0.5))
## Jeroen's exact bug: an n=32 composite (512px native) in a real ~1920px
## viewport must NOT render at zoom=1.0 (the old, unfitted "postage stamp"
## behavior) — the fit must scale it up to fill (now: COVER) the viewport.
func test_fit_window_view_scales_up_a_small_composite_to_fill_the_viewport() -> void:
var fit: Dictionary = AtlasWindowGeometry.fit_window_view(
Vector2(1920.0, 1080.0), 32, CELL_PIXEL_SIZE, MIN_ZOOM, MAX_ZOOM
)
assert_float(fit["zoom"]).override_failure_message(
"a 512px composite in a 1920x1080 viewport must be scaled UP, not left at 1.0"
).is_greater(1.0)
## A huge n (e.g. n=64 at a tiny viewport) must clamp to MIN_ZOOM, never
## shrink the composite into illegibility below the floor.
func test_fit_window_view_clamps_to_min_zoom_for_a_tiny_viewport() -> void:
var fit: Dictionary = AtlasWindowGeometry.fit_window_view(
Vector2(200.0, 150.0), 64, CELL_PIXEL_SIZE, MIN_ZOOM, MAX_ZOOM
)
assert_float(fit["zoom"]).is_equal_approx(MIN_ZOOM, 0.001)
## A small n (e.g. n=2) at a huge viewport must clamp to MAX_ZOOM, never
## scale past the ceiling.
func test_fit_window_view_clamps_to_max_zoom_for_a_tiny_composite() -> void:
var fit: Dictionary = AtlasWindowGeometry.fit_window_view(
Vector2(3840.0, 2160.0), 2, CELL_PIXEL_SIZE, MIN_ZOOM, MAX_ZOOM
)
assert_float(fit["zoom"]).is_equal_approx(MAX_ZOOM, 0.001)
## Degenerate inputs (zero viewport, zero n) must not divide by zero — a safe
## fallback (zoom=1.0, offset=ZERO), never a crash or NaN.
func test_fit_window_view_degenerate_inputs_are_safe() -> void:
var fit_zero_viewport: Dictionary = AtlasWindowGeometry.fit_window_view(
Vector2.ZERO, 32, CELL_PIXEL_SIZE, MIN_ZOOM, MAX_ZOOM
)
assert_float(fit_zero_viewport["zoom"]).is_equal_approx(1.0, 0.001)
var fit_zero_n: Dictionary = AtlasWindowGeometry.fit_window_view(
Vector2(1920.0, 1080.0), 0, CELL_PIXEL_SIZE, MIN_ZOOM, MAX_ZOOM
)
assert_float(fit_zero_n["zoom"]).is_equal_approx(1.0, 0.001)
# =============================================================================
# clamp_pan_offset_to_pole_wall — item 5 (pole hard wall, row axis only)
# =============================================================================
## Deep inside the valid range (window nowhere near a pole), the clamp must
## be a no-op — offset passes through unchanged.
func test_pole_wall_clamp_is_a_noop_far_from_the_poles() -> void:
var offset := Vector2(10.0, 20.0)
var clamped: Vector2 = AtlasWindowGeometry.clamp_pan_offset_to_pole_wall(
offset, Vector2(1920.0, 1080.0), Vector2i(0, 0), 32, 4785, CELL_PIXEL_SIZE, 1.0
)
assert_that(clamped).is_equal(offset)
## X is NEVER clamped by the pole wall (item 6: east-west is seamless) — even
## an absurdly large X offset passes through untouched.
func test_pole_wall_clamp_never_touches_x() -> void:
var offset := Vector2(999999.0, 0.0)
var clamped: Vector2 = AtlasWindowGeometry.clamp_pan_offset_to_pole_wall(
offset, Vector2(1920.0, 1080.0), Vector2i(0, 0), 32, 4785, CELL_PIXEL_SIZE, 1.0
)
assert_float(clamped.x).is_equal_approx(999999.0, 0.001)
## The core pole-wall behavior: dragging FAR past the north pole (offset.y
## driven to an extreme) must clamp — the resulting offset must be LESS than
## the extreme requested, and a SECOND, even-more-extreme drag must produce
## the SAME clamped value (further dragging is inert once pinned at the wall).
func test_pole_wall_clamp_pins_offset_when_dragged_past_the_pole() -> void:
var rows_half := 100
var held_center := Vector2i(0, 90) # near the south pole already (row 90 of 100)
var extreme_offset := Vector2(0.0, 5000.0) # a huge downward drag
var clamped: Vector2 = AtlasWindowGeometry.clamp_pan_offset_to_pole_wall(
extreme_offset, Vector2(800.0, 800.0), held_center, 32, rows_half, CELL_PIXEL_SIZE, 1.0
)
assert_float(clamped.y).override_failure_message(
"an extreme drag toward the pole must be clamped, not pass through"
).is_less(extreme_offset.y)
var even_more_extreme := Vector2(0.0, 50000.0)
var clamped_again: Vector2 = AtlasWindowGeometry.clamp_pan_offset_to_pole_wall(
even_more_extreme, Vector2(800.0, 800.0), held_center, 32, rows_half, CELL_PIXEL_SIZE, 1.0
)
assert_float(clamped_again.y).override_failure_message(
"further dragging past an already-pinned wall must be inert (same clamped value)"
).is_equal_approx(clamped.y, 0.01)
## Symmetric check on the north side: a huge UPWARD drag near the north pole
## also clamps.
func test_pole_wall_clamp_pins_offset_on_the_north_side_too() -> void:
var rows_half := 100
var held_center := Vector2i(0, -90) # near the north pole
var extreme_offset := Vector2(0.0, -5000.0) # a huge upward drag
var clamped: Vector2 = AtlasWindowGeometry.clamp_pan_offset_to_pole_wall(
extreme_offset, Vector2(800.0, 800.0), held_center, 32, rows_half, CELL_PIXEL_SIZE, 1.0
)
assert_float(clamped.y).override_failure_message(
"an extreme drag toward the north pole must be clamped"
).is_greater(extreme_offset.y)
## rows_half <= 0 (a no-radius body, or a degenerate district_extent()) means
## "no wall concept" — the clamp is a no-op, matching
## canonicalize_district_center()'s own no-radius identity disposition.
func test_pole_wall_clamp_is_noop_when_rows_half_is_zero() -> void:
var offset := Vector2(0.0, 999999.0)
var clamped: Vector2 = AtlasWindowGeometry.clamp_pan_offset_to_pole_wall(
offset, Vector2(800.0, 800.0), Vector2i(0, 0), 32, 0, CELL_PIXEL_SIZE, 1.0
)
assert_that(clamped).is_equal(offset)
## Tiny-body edge case (documented open item in atlas_window_viewer.gd's own
## _clamp_offset_to_pole_wall doc): a window TALLER than the whole planet's
## row span (n=64 window, rows_half=10 -> pole-to-pole is only 20 districts)
## must not crash or produce an inverted/degenerate clamp range — the offset
## still comes back as a finite Vector2, and repeated extreme drags still
## converge to a stable pinned value (not NaN, not unbounded).
func test_pole_wall_clamp_handles_a_window_taller_than_the_planet() -> void:
var rows_half := 10
var held_n := 64
var held_center := Vector2i(0, 0)
var clamped: Vector2 = AtlasWindowGeometry.clamp_pan_offset_to_pole_wall(
Vector2(0.0, 999999.0), Vector2(800.0, 800.0), held_center, held_n, rows_half,
CELL_PIXEL_SIZE, 1.0
)
assert_bool(is_finite(clamped.y)).override_failure_message(
"a window taller than the planet's row span must still produce a finite clamp"
).is_true()
var clamped_again: Vector2 = AtlasWindowGeometry.clamp_pan_offset_to_pole_wall(
Vector2(0.0, 9999999.0), Vector2(800.0, 800.0), held_center, held_n, rows_half,
CELL_PIXEL_SIZE, 1.0
)
assert_float(clamped_again.y).is_equal_approx(clamped.y, 0.01)
# =============================================================================
# Cross-check: clamp bounds derived from district_extent() (the SAME source
# canonicalize_district_center() uses) — confirms the two T-1142 fixes (item
# 5 pole wall, item 6a wrap/clamp) agree on what "the pole" even is.
# =============================================================================
func test_pole_wall_rows_half_matches_canonicalize_rows_half() -> void:
var radius_km := 6238.4 # GJ380c
var extent: Dictionary = AtlasDescendGeometry.district_extent(radius_km)
var rows_half: int = int(extent["rows_half"])
# A center exactly at (0, rows_half) must canonicalize to itself (already
# at the pole boundary, not past it) — pins that the SAME rows_half both
# fixes consume describes an inclusive boundary, not an exclusive one.
var canonical: Vector2i = AtlasDescendGeometry.canonicalize_district_center(
Vector2i(0, rows_half), radius_km
)
assert_int(canonical.y).is_equal(rows_half)