feat(ui): T-1170 B1+B2 — visibility-table split; Region skeleton chords from downstream pointers
B1 (Ruling 5c): nature block split out of atlas_window_geometry.gd (954/1000 cap pressure) into atlas_window_geometry_nature.gd; RIVER_ CLASS_VISIBLE_BY_RUNG replaced by SKELETON_CLASS_VISIBLE_BY_RUNG (Region-only now) + COURSE_CLASS_VISIBLE_BY_RUNG (District trunk+ tributary; Quarter all three — the pre-announced Quarter-rivers- return) with width/opacity companion tables as Araminta's single revisit point; deliberately opposite unknown-tag fallbacks per reader (skeleton->full, course->empty), documented. B2 (Ruling 5a): Region dot-scatter upgraded to connected chords via river_downstream — D8 direction decode (0-7 into drainage.rs's (row,col) delta table, antimeridian wrap-aware), sentinel chain ends (MOUTH=8 ring-on-land, EDGE_DRAIN=9 no ring, TERMINAL=10 reserved, decodes like EDGE_DRAIN so the future endorheic server needs no client change). Pure build_skeleton_chords() split from drawing for render-free testability. Chord clip rule (3g pick): segment clips if either endpoint OR midpoint is drawn water — three-point catches both narrow-inlet and long-chord failure modes at one extra lookup; documented. Self-caught during build: first draft misdecoded the pointer as a river_cells INDEX; rewired to direction decode against A1's real convention before leaving the branch. Dual revert-verified (direction sabotage -> 6 named failures incl. the chain-threading pin; midpoint-drop -> exactly the 1 named clip test). Suites: geometry-nature 86/86, geometry 130/130, nature-overlay 24/24; full sweep 3892 with only the 6 known pre-existing garment/gait failures untouched by this batch; gdlint clean. Tickets: T-1170 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -929,3 +929,51 @@ func test_recompute_offset_for_held_n_change_differs_for_different_held_n() -> v
|
||||
+ " _view_offset — reusing the same offset across the crossing is"
|
||||
+ " exactly the live round 4 bug (composite renders off-canvas)"
|
||||
).is_not_equal(offset_quarter)
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# T-1172 round 2: cell_index_for_local_offset() — the shared painter/clip
|
||||
# index formula (see its own doc for the "why shared, not duplicated" case).
|
||||
# T-1170: these tests moved here from test_atlas_window_geometry_nature.gd —
|
||||
# the function itself stayed on THIS file (AtlasWindowGeometry) rather than
|
||||
# moving to atlas_window_geometry_nature.gd, since it is shared with
|
||||
# AtlasWindowOverlay's terrain painter, a non-nature consumer — see that
|
||||
## file's own header doc for the full split rationale.
|
||||
# =============================================================================
|
||||
|
||||
|
||||
func test_cell_index_for_local_offset_top_left_is_zero_zero() -> void:
|
||||
var cell: Vector2i = AtlasWindowGeometry.cell_index_for_local_offset(0.0, 0.0, 6400, 64)
|
||||
assert_that(cell).is_equal(Vector2i(0, 0))
|
||||
|
||||
|
||||
## The exact live-repro numbers from T-1172 round 2's trace: a query whose
|
||||
## district-space local offset is (2594.09, 5593.15) inside a 6400-wide,
|
||||
## 64-cell-side window must resolve to (col=25, row=55) — pinned directly
|
||||
## against the LIVE captured values that closed the investigation (both the
|
||||
## painter's _build_tile_texture() and the clip independently produced this
|
||||
## exact pair for the same query in the live trace).
|
||||
func test_cell_index_for_local_offset_matches_the_live_trace_repro() -> void:
|
||||
var cell: Vector2i = AtlasWindowGeometry.cell_index_for_local_offset(
|
||||
2594.0849609375, 5593.15258789062, 6400, 64
|
||||
)
|
||||
assert_that(cell).override_failure_message(
|
||||
"must match the live-captured painter/clip agreement point from the"
|
||||
+ " T-1172 round 2 investigation — (col=25, row=55)"
|
||||
).is_equal(Vector2i(25, 55))
|
||||
|
||||
|
||||
func test_cell_index_for_local_offset_bottom_right_boundary_clamps_inside() -> void:
|
||||
# local offset == n (the exclusive upper boundary) must clamp to the LAST
|
||||
# cell, not overflow to a nonexistent grid_side'th cell.
|
||||
var cell: Vector2i = AtlasWindowGeometry.cell_index_for_local_offset(6400.0, 6400.0, 6400, 64)
|
||||
assert_that(cell).is_equal(Vector2i(63, 63))
|
||||
|
||||
|
||||
func test_cell_index_for_local_offset_zero_n_or_grid_side_returns_sentinel() -> void:
|
||||
assert_that(AtlasWindowGeometry.cell_index_for_local_offset(10.0, 10.0, 0, 64)).is_equal(
|
||||
Vector2i(-1, -1)
|
||||
)
|
||||
assert_that(AtlasWindowGeometry.cell_index_for_local_offset(10.0, 10.0, 6400, 0)).is_equal(
|
||||
Vector2i(-1, -1)
|
||||
)
|
||||
|
||||
@@ -1,15 +1,26 @@
|
||||
## T-1156 wave 1: pure-function tests for AtlasWindowGeometry's Layer-1
|
||||
## nature-overlay pixel mapping (layer1_pixel_to_world_m/world_m_to_district/
|
||||
## layer1_pixel_to_canvas_local) and per-rung visibility/filter policy
|
||||
## (river_class_visible_at_rung/confluences_visible_at_rung/
|
||||
## mouths_visible_at_rung/basins_visible_at_rung/attractors_visible_at_rung).
|
||||
## Split from test_atlas_window_geometry.gd (already close to the gdlint
|
||||
## max-file-lines cap) — same file-per-concern precedent as
|
||||
## test_atlas_window_colors.gd being separate from test_atlas_window_overlay.gd.
|
||||
## T-1156 wave 1 / T-1170: pure-function tests for AtlasWindowGeometryNature's
|
||||
## Layer-1 nature-overlay pixel mapping (layer1_pixel_to_world_m/
|
||||
## world_m_to_district/layer1_pixel_to_canvas_local), per-rung visibility/
|
||||
## filter policy (skeleton_class_visible_at_rung/course_class_visible_at_rung/
|
||||
## confluences_visible_at_rung/mouths_visible_at_rung/basins_visible_at_rung/
|
||||
## attractors_visible_at_rung), the D8 river_downstream decode
|
||||
## (d8_downstream_target), and course width/opacity readers
|
||||
## (course_class_width_px/course_class_opacity). Split from
|
||||
## test_atlas_window_geometry.gd (already close to the gdlint max-file-lines
|
||||
## cap) — same file-per-concern precedent as test_atlas_window_colors.gd being
|
||||
## separate from test_atlas_window_overlay.gd.
|
||||
##
|
||||
## T-1170: this file's SUBJECT preload moved from AtlasWindowGeometry to
|
||||
## AtlasWindowGeometryNature (the T-1170 split, see that file's own doc) —
|
||||
## every symbol tested below now lives there. cell_index_for_local_offset()
|
||||
## STAYED on AtlasWindowGeometry (shared with the non-nature terrain painter)
|
||||
## — its tests stay in test_atlas_window_geometry.gd, not duplicated here.
|
||||
class_name TestAtlasWindowGeometryNature
|
||||
extends GdUnitTestSuite
|
||||
|
||||
const AtlasWindowGeometry := preload("res://ui/implant/apps/atlas/atlas_window_geometry.gd")
|
||||
const AtlasWindowGeometryNature := preload(
|
||||
"res://ui/implant/apps/atlas/atlas_window_geometry_nature.gd"
|
||||
)
|
||||
|
||||
const CELL_PIXEL_SIZE: float = 16.0
|
||||
const DISTRICT_M: float = 2048.0
|
||||
@@ -25,7 +36,7 @@ const DISTRICT_M: float = 2048.0
|
||||
## Column 0 is world/longitude 0 on every body — no -0.5 centering, unlike
|
||||
## rows (longitude wraps and has no "half" concept the way latitude does).
|
||||
func test_layer1_pixel_to_world_m_col_zero_is_world_x_zero() -> void:
|
||||
var w: Vector2 = AtlasWindowGeometry.layer1_pixel_to_world_m(0.0, 0.0, 256.0, 128.0, 6371.0)
|
||||
var w: Vector2 = AtlasWindowGeometryNature.layer1_pixel_to_world_m(0.0, 0.0, 256.0, 128.0, 6371.0)
|
||||
assert_float(w.x).is_equal_approx(0.0, 0.001)
|
||||
|
||||
|
||||
@@ -36,7 +47,9 @@ func test_layer1_pixel_to_world_m_col_zero_is_world_x_zero() -> void:
|
||||
func test_layer1_pixel_to_world_m_row_zero_is_north_pole_negative_wy() -> void:
|
||||
var radius_km := 6371.0
|
||||
var grid_h := 128.0
|
||||
var w: Vector2 = AtlasWindowGeometry.layer1_pixel_to_world_m(0.0, 0.0, 256.0, grid_h, radius_km)
|
||||
var w: Vector2 = AtlasWindowGeometryNature.layer1_pixel_to_world_m(
|
||||
0.0, 0.0, 256.0, grid_h, radius_km
|
||||
)
|
||||
var meridian_m: float = PI * radius_km * 1000.0
|
||||
assert_float(w.y).is_equal_approx(-0.5 * meridian_m, 1.0)
|
||||
|
||||
@@ -46,7 +59,7 @@ func test_layer1_pixel_to_world_m_row_zero_is_north_pole_negative_wy() -> void:
|
||||
func test_layer1_pixel_to_world_m_last_row_is_south_pole_positive_wy() -> void:
|
||||
var radius_km := 6371.0
|
||||
var grid_h := 128.0
|
||||
var w: Vector2 = AtlasWindowGeometry.layer1_pixel_to_world_m(
|
||||
var w: Vector2 = AtlasWindowGeometryNature.layer1_pixel_to_world_m(
|
||||
grid_h - 1.0, 0.0, 256.0, grid_h, radius_km
|
||||
)
|
||||
var meridian_m: float = PI * radius_km * 1000.0
|
||||
@@ -57,7 +70,7 @@ func test_layer1_pixel_to_world_m_last_row_is_south_pole_positive_wy() -> void:
|
||||
## is world Y ~0 — halfway between the two poles. Not EXACT (the denominator
|
||||
## is grid_h - 1 = 127, not 128), so the tolerance is loose (200km).
|
||||
func test_layer1_pixel_to_world_m_mid_row_is_near_equator() -> void:
|
||||
var w: Vector2 = AtlasWindowGeometry.layer1_pixel_to_world_m(64.0, 0.0, 256.0, 128.0, 6371.0)
|
||||
var w: Vector2 = AtlasWindowGeometryNature.layer1_pixel_to_world_m(64.0, 0.0, 256.0, 128.0, 6371.0)
|
||||
assert_float(w.y).is_equal_approx(0.0, 200_000.0)
|
||||
|
||||
|
||||
@@ -66,7 +79,9 @@ func test_layer1_pixel_to_world_m_mid_row_is_near_equator() -> void:
|
||||
func test_layer1_pixel_to_world_m_full_width_col_is_full_circumference() -> void:
|
||||
var radius_km := 6371.0
|
||||
var grid_w := 256.0
|
||||
var w: Vector2 = AtlasWindowGeometry.layer1_pixel_to_world_m(0.0, grid_w, grid_w, 128.0, radius_km)
|
||||
var w: Vector2 = AtlasWindowGeometryNature.layer1_pixel_to_world_m(
|
||||
0.0, grid_w, grid_w, 128.0, radius_km
|
||||
)
|
||||
var circumference_m: float = TAU * radius_km * 1000.0
|
||||
assert_float(w.x).is_equal_approx(circumference_m, 5.0)
|
||||
|
||||
@@ -75,13 +90,13 @@ func test_layer1_pixel_to_world_m_full_width_col_is_full_circumference() -> void
|
||||
## — matching pixel_to_world_m()'s own no-radius fallback and
|
||||
## AtlasDescendGeometry.district_pos_at()'s no-radius branch on the inverse side.
|
||||
func test_layer1_pixel_to_world_m_no_radius_is_one_pixel_one_district_m() -> void:
|
||||
var w: Vector2 = AtlasWindowGeometry.layer1_pixel_to_world_m(3.0, 5.0, 64.0, 64.0, 0.0)
|
||||
var w: Vector2 = AtlasWindowGeometryNature.layer1_pixel_to_world_m(3.0, 5.0, 64.0, 64.0, 0.0)
|
||||
assert_that(w).is_equal(Vector2(5.0 * DISTRICT_M, 3.0 * DISTRICT_M))
|
||||
|
||||
|
||||
## Degenerate grid dims (grid_w/grid_h <= 0) must not divide-by-zero or crash.
|
||||
func test_layer1_pixel_to_world_m_zero_grid_dims_returns_zero() -> void:
|
||||
var w: Vector2 = AtlasWindowGeometry.layer1_pixel_to_world_m(1.0, 1.0, 0.0, 0.0, 6371.0)
|
||||
var w: Vector2 = AtlasWindowGeometryNature.layer1_pixel_to_world_m(1.0, 1.0, 0.0, 0.0, 6371.0)
|
||||
assert_that(w).is_equal(Vector2.ZERO)
|
||||
|
||||
|
||||
@@ -92,13 +107,16 @@ func test_layer1_pixel_to_world_m_zero_grid_dims_returns_zero() -> void:
|
||||
|
||||
|
||||
func test_world_m_to_district_divides_by_district_m() -> void:
|
||||
var d: Vector2 = AtlasWindowGeometry.world_m_to_district(Vector2(DISTRICT_M * 3.5, DISTRICT_M * -2.25))
|
||||
var d: Vector2 = AtlasWindowGeometryNature.world_m_to_district(
|
||||
Vector2(DISTRICT_M * 3.5, DISTRICT_M * -2.25)
|
||||
)
|
||||
assert_that(d).is_equal_approx(Vector2(3.5, -2.25), Vector2.ONE * 0.001)
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# layer1_pixel_to_canvas_local — the full composition, cross-checked against
|
||||
# district_to_canvas_local() called manually with the same intermediate value.
|
||||
# AtlasWindowGeometry.district_to_canvas_local() called manually with the
|
||||
# same intermediate value.
|
||||
# =============================================================================
|
||||
|
||||
|
||||
@@ -107,6 +125,7 @@ func test_world_m_to_district_divides_by_district_m() -> void:
|
||||
## test_district_to_canvas_local_center_district_lands_at_half_extent()
|
||||
## pins for the district-space function this one wraps.
|
||||
func test_layer1_pixel_to_canvas_local_matches_manual_composition() -> void:
|
||||
var AtlasWindowGeometry := preload("res://ui/implant/apps/atlas/atlas_window_geometry.gd")
|
||||
var radius_km := 6371.0
|
||||
var grid_w := 256.0
|
||||
var grid_h := 128.0
|
||||
@@ -115,14 +134,14 @@ func test_layer1_pixel_to_canvas_local_matches_manual_composition() -> void:
|
||||
var row := 40.0
|
||||
var col := 80.0
|
||||
|
||||
var result: Vector2 = AtlasWindowGeometry.layer1_pixel_to_canvas_local(
|
||||
var result: Vector2 = AtlasWindowGeometryNature.layer1_pixel_to_canvas_local(
|
||||
row, col, grid_w, grid_h, radius_km, held_center, held_n, CELL_PIXEL_SIZE
|
||||
)
|
||||
|
||||
var world_m: Vector2 = AtlasWindowGeometry.layer1_pixel_to_world_m(
|
||||
var world_m: Vector2 = AtlasWindowGeometryNature.layer1_pixel_to_world_m(
|
||||
row, col, grid_w, grid_h, radius_km
|
||||
)
|
||||
var district: Vector2 = AtlasWindowGeometry.world_m_to_district(world_m)
|
||||
var district: Vector2 = AtlasWindowGeometryNature.world_m_to_district(world_m)
|
||||
var expected: Vector2 = AtlasWindowGeometry.district_to_canvas_local(
|
||||
district, held_center, held_n, CELL_PIXEL_SIZE
|
||||
)
|
||||
@@ -130,94 +149,471 @@ func test_layer1_pixel_to_canvas_local_matches_manual_composition() -> void:
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Per-rung river-class visibility (Araminta's ruling, 2026-07-23) —
|
||||
# river_class_visible_at_rung()
|
||||
# T-1170 Ruling 2a-2d/5a: d8_downstream_target() — the river_downstream D8
|
||||
# pointer decode. Direction table CONFIRMED against Dudley's A1
|
||||
# (server/src/atlas/drainage.rs:35-44): 0=N(-1,0) 1=S(1,0) 2=E(0,1) 3=W(0,-1)
|
||||
# 4=NE(-1,1) 5=NW(-1,-1) 6=SE(1,1) 7=SW(1,-1). Sentinels: MOUTH=8,
|
||||
# EDGE_DRAIN=9, TERMINAL=10 (reserved).
|
||||
# =============================================================================
|
||||
|
||||
|
||||
func test_river_class_visible_at_rung_region_shows_every_class() -> void:
|
||||
func test_d8_downstream_target_north_decrements_row() -> void:
|
||||
var target: Variant = AtlasWindowGeometryNature.d8_downstream_target(10.0, 10.0, 0)
|
||||
assert_that(target).is_equal(Vector2(9.0, 10.0))
|
||||
|
||||
|
||||
func test_d8_downstream_target_south_increments_row() -> void:
|
||||
var target: Variant = AtlasWindowGeometryNature.d8_downstream_target(10.0, 10.0, 1)
|
||||
assert_that(target).is_equal(Vector2(11.0, 10.0))
|
||||
|
||||
|
||||
func test_d8_downstream_target_east_increments_col() -> void:
|
||||
var target: Variant = AtlasWindowGeometryNature.d8_downstream_target(10.0, 10.0, 2)
|
||||
assert_that(target).is_equal(Vector2(10.0, 11.0))
|
||||
|
||||
|
||||
func test_d8_downstream_target_west_decrements_col() -> void:
|
||||
var target: Variant = AtlasWindowGeometryNature.d8_downstream_target(10.0, 10.0, 3)
|
||||
assert_that(target).is_equal(Vector2(10.0, 9.0))
|
||||
|
||||
|
||||
func test_d8_downstream_target_diagonals_move_both_axes() -> void:
|
||||
# 4=NE, 5=NW, 6=SE, 7=SW — each a diagonal (row, col) delta of magnitude 1
|
||||
# on both axes, matching the direction letters' compass meaning.
|
||||
assert_that(AtlasWindowGeometryNature.d8_downstream_target(10.0, 10.0, 4)).is_equal(
|
||||
Vector2(9.0, 11.0)
|
||||
) # NE
|
||||
assert_that(AtlasWindowGeometryNature.d8_downstream_target(10.0, 10.0, 5)).is_equal(
|
||||
Vector2(9.0, 9.0)
|
||||
) # NW
|
||||
assert_that(AtlasWindowGeometryNature.d8_downstream_target(10.0, 10.0, 6)).is_equal(
|
||||
Vector2(11.0, 11.0)
|
||||
) # SE
|
||||
assert_that(AtlasWindowGeometryNature.d8_downstream_target(10.0, 10.0, 7)).is_equal(
|
||||
Vector2(11.0, 9.0)
|
||||
) # SW
|
||||
|
||||
|
||||
## MOUTH (8), EDGE_DRAIN (9), and TERMINAL (10, reserved) are all sentinels
|
||||
## >= RIVER_DOWNSTREAM_SENTINEL_BASE — every one must decode to `null` (chain
|
||||
## end, no segment to draw), not a direction lookup.
|
||||
func test_d8_downstream_target_sentinels_return_null() -> void:
|
||||
assert_that(
|
||||
AtlasWindowGeometryNature.d8_downstream_target(
|
||||
10.0, 10.0, AtlasWindowGeometryNature.RIVER_DOWNSTREAM_MOUTH
|
||||
)
|
||||
).is_null()
|
||||
assert_that(
|
||||
AtlasWindowGeometryNature.d8_downstream_target(
|
||||
10.0, 10.0, AtlasWindowGeometryNature.RIVER_DOWNSTREAM_EDGE_DRAIN
|
||||
)
|
||||
).is_null()
|
||||
assert_that(
|
||||
AtlasWindowGeometryNature.d8_downstream_target(
|
||||
10.0, 10.0, AtlasWindowGeometryNature.RIVER_DOWNSTREAM_TERMINAL
|
||||
)
|
||||
).is_null()
|
||||
|
||||
|
||||
## A malformed/out-of-range direction (negative, or >= sentinel base but not
|
||||
## one of the three named sentinels — e.g. a future reserved value) must also
|
||||
## decode to null, not crash on an out-of-bounds D8_DIRECTION_DELTAS index.
|
||||
func test_d8_downstream_target_out_of_range_returns_null_not_crash() -> void:
|
||||
assert_that(AtlasWindowGeometryNature.d8_downstream_target(10.0, 10.0, -1)).is_null()
|
||||
assert_that(AtlasWindowGeometryNature.d8_downstream_target(10.0, 10.0, 255)).is_null()
|
||||
|
||||
|
||||
## The sentinel base itself (8) is the exact boundary between the last real
|
||||
## direction (7=SW) and the first sentinel (8=MOUTH) — pin the boundary
|
||||
## exactly rather than relying only on the interior-value tests above.
|
||||
func test_d8_downstream_target_boundary_seven_is_direction_eight_is_sentinel() -> void:
|
||||
assert_that(AtlasWindowGeometryNature.d8_downstream_target(0.0, 0.0, 7)).is_not_null()
|
||||
assert_that(AtlasWindowGeometryNature.d8_downstream_target(0.0, 0.0, 8)).is_null()
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# T-1170 Ruling 5a: build_skeleton_chords() — the pure chain-CONSTRUCTION
|
||||
# function (no draw calls) AtlasWindowNatureOverlay._draw_skeleton_chords()
|
||||
# delegates to. This is the load-bearing chain-walking logic (visibility
|
||||
# filtering + D8 decode + sentinel chain-ends), tested here directly rather
|
||||
# than only through the draw-smoke suite's pixel proof.
|
||||
# =============================================================================
|
||||
|
||||
|
||||
## Two river cells, cell 0 flows SOUTH (direction 1) into cell 1's own grid
|
||||
## position — one segment constructed, from cell 0's position to cell 0's
|
||||
## position + (1, 0) [south]. cls read from river_class at the SAME index.
|
||||
func test_build_skeleton_chords_constructs_one_segment_for_a_simple_pair() -> void:
|
||||
var river_cells: Array = [[10, 10], [11, 10]]
|
||||
var river_class: Array = [
|
||||
AtlasWindowGeometryNature.RIVER_CLASS_TRUNK, AtlasWindowGeometryNature.RIVER_CLASS_TRUNK
|
||||
]
|
||||
var river_downstream: Array = [1, AtlasWindowGeometryNature.RIVER_DOWNSTREAM_MOUTH] # 1 = S
|
||||
var chords: Array = AtlasWindowGeometryNature.build_skeleton_chords(
|
||||
river_cells, river_class, river_downstream, "Region"
|
||||
)
|
||||
assert_int(chords.size()).override_failure_message(
|
||||
"cell 0 (flows S, a real direction) must construct one segment;"
|
||||
+ " cell 1 (MOUTH sentinel) must construct none — expected exactly 1 total"
|
||||
).is_equal(1)
|
||||
var chord: Dictionary = chords[0]
|
||||
assert_that(chord["from"]).is_equal(Vector2(10.0, 10.0))
|
||||
assert_that(chord["to"]).is_equal(Vector2(11.0, 10.0))
|
||||
assert_int(chord["cls"]).is_equal(AtlasWindowGeometryNature.RIVER_CLASS_TRUNK)
|
||||
|
||||
|
||||
## Sentinel chain ends: MOUTH, EDGE_DRAIN, and TERMINAL (reserved) must each
|
||||
## construct ZERO segments for their own cell — a chain-end has no downstream
|
||||
## neighbor to connect to, regardless of which sentinel flavor.
|
||||
func test_build_skeleton_chords_sentinel_chain_ends_construct_no_segment() -> void:
|
||||
var river_cells: Array = [[0, 0], [10, 10], [20, 20]]
|
||||
var river_class: Array = [
|
||||
AtlasWindowGeometryNature.RIVER_CLASS_TRUNK,
|
||||
AtlasWindowGeometryNature.RIVER_CLASS_TRUNK,
|
||||
AtlasWindowGeometryNature.RIVER_CLASS_TRUNK,
|
||||
]
|
||||
var river_downstream: Array = [
|
||||
AtlasWindowGeometryNature.RIVER_DOWNSTREAM_MOUTH,
|
||||
AtlasWindowGeometryNature.RIVER_DOWNSTREAM_EDGE_DRAIN,
|
||||
AtlasWindowGeometryNature.RIVER_DOWNSTREAM_TERMINAL,
|
||||
]
|
||||
var chords: Array = AtlasWindowGeometryNature.build_skeleton_chords(
|
||||
river_cells, river_class, river_downstream, "Region"
|
||||
)
|
||||
assert_array(chords).override_failure_message(
|
||||
"every cell is a sentinel chain-end (MOUTH/EDGE_DRAIN/TERMINAL) —"
|
||||
+ " zero segments must be constructed"
|
||||
).is_empty()
|
||||
|
||||
|
||||
## A downstream direction pointing at a class not visible at this rung's
|
||||
## SKELETON path is filtered by the UPSTREAM cell's own class, not the
|
||||
## target's — District shows NO skeleton classes at all (T-1170: skeleton
|
||||
## draws only at Region now), so a District query must construct zero
|
||||
## segments regardless of the fixture's directions.
|
||||
func test_build_skeleton_chords_district_rung_constructs_nothing() -> void:
|
||||
var river_cells: Array = [[10, 10], [11, 10]]
|
||||
var river_class: Array = [
|
||||
AtlasWindowGeometryNature.RIVER_CLASS_TRUNK, AtlasWindowGeometryNature.RIVER_CLASS_TRUNK
|
||||
]
|
||||
var river_downstream: Array = [1, AtlasWindowGeometryNature.RIVER_DOWNSTREAM_MOUTH]
|
||||
var chords: Array = AtlasWindowGeometryNature.build_skeleton_chords(
|
||||
river_cells, river_class, river_downstream, "District"
|
||||
)
|
||||
assert_array(chords).is_empty()
|
||||
|
||||
|
||||
## river_downstream shorter than river_cells (pre-T-1170 payload / graceful
|
||||
## empty-Vec decode) — cells with no corresponding index must construct no
|
||||
## segment, not crash on an out-of-bounds read.
|
||||
func test_build_skeleton_chords_missing_downstream_entries_construct_nothing() -> void:
|
||||
var river_cells: Array = [[10, 10], [11, 10], [12, 10]]
|
||||
var river_class: Array = [
|
||||
AtlasWindowGeometryNature.RIVER_CLASS_TRUNK,
|
||||
AtlasWindowGeometryNature.RIVER_CLASS_TRUNK,
|
||||
AtlasWindowGeometryNature.RIVER_CLASS_TRUNK,
|
||||
]
|
||||
var river_downstream: Array = [1] # only index 0 has a pointer
|
||||
var chords: Array = AtlasWindowGeometryNature.build_skeleton_chords(
|
||||
river_cells, river_class, river_downstream, "Region"
|
||||
)
|
||||
assert_int(chords.size()).is_equal(1)
|
||||
|
||||
|
||||
## An empty river_downstream array entirely (the actual wire shape Dudley's
|
||||
## `#[serde(default)]` produces for a pre-T-1170 payload) must construct zero
|
||||
## segments, not error.
|
||||
func test_build_skeleton_chords_empty_downstream_array_constructs_nothing() -> void:
|
||||
var river_cells: Array = [[10, 10], [11, 10]]
|
||||
var river_class: Array = [
|
||||
AtlasWindowGeometryNature.RIVER_CLASS_TRUNK, AtlasWindowGeometryNature.RIVER_CLASS_TRUNK
|
||||
]
|
||||
var chords: Array = AtlasWindowGeometryNature.build_skeleton_chords(
|
||||
river_cells, river_class, [], "Region"
|
||||
)
|
||||
assert_array(chords).is_empty()
|
||||
|
||||
|
||||
## A malformed river_cells entry (not an Array, or too short) is skipped
|
||||
## entirely — no segment constructed for it, no crash, and it does not
|
||||
## disturb construction for the OTHER (well-formed) entries in the same
|
||||
## fixture.
|
||||
func test_build_skeleton_chords_malformed_cell_entry_is_skipped_not_fatal() -> void:
|
||||
var river_cells: Array = [[10, 10], "not an array", [12, 10]]
|
||||
var river_class: Array = [
|
||||
AtlasWindowGeometryNature.RIVER_CLASS_TRUNK,
|
||||
AtlasWindowGeometryNature.RIVER_CLASS_TRUNK,
|
||||
AtlasWindowGeometryNature.RIVER_CLASS_TRUNK,
|
||||
]
|
||||
var river_downstream: Array = [1, 1, AtlasWindowGeometryNature.RIVER_DOWNSTREAM_MOUTH]
|
||||
var chords: Array = AtlasWindowGeometryNature.build_skeleton_chords(
|
||||
river_cells, river_class, river_downstream, "Region"
|
||||
)
|
||||
assert_int(chords.size()).override_failure_message(
|
||||
"the malformed middle entry must be skipped without disturbing the"
|
||||
+ " well-formed entries around it — expected exactly 1 (cell 0 -> S)"
|
||||
).is_equal(1)
|
||||
|
||||
|
||||
## river_class shorter than river_cells falls back to RIVER_CLASS_FALLBACK
|
||||
## (TRUNK) for the missing entry — the same graceful-decode posture
|
||||
## skeleton_class_visible_at_rung()'s own caller already relies on.
|
||||
func test_build_skeleton_chords_missing_class_entry_falls_back_to_trunk() -> void:
|
||||
var river_cells: Array = [[10, 10]]
|
||||
var river_downstream: Array = [1]
|
||||
var chords: Array = AtlasWindowGeometryNature.build_skeleton_chords(
|
||||
river_cells, [], river_downstream, "Region"
|
||||
)
|
||||
assert_int(chords.size()).is_equal(1)
|
||||
assert_int(chords[0]["cls"]).is_equal(AtlasWindowGeometryNature.RIVER_CLASS_FALLBACK)
|
||||
|
||||
|
||||
## REVERT-VERIFICATION pin (per the ticket brief's explicit ask to
|
||||
## revert-verify the most load-bearing construction path): a chain of THREE
|
||||
## cells (0 -> 1 -> MOUTH) must construct exactly TWO segments in the correct
|
||||
## from/to order — proving the chain doesn't just count sentinels correctly
|
||||
## in isolation (the tests above) but actually threads a multi-hop chain.
|
||||
## Breaking build_skeleton_chords() to, e.g., always connect cell i to cell
|
||||
## i+1 by INDEX (the old dot-scatter's adjacency, not a real D8 decode) would
|
||||
## still pass the single-pair test above by coincidence but fail this one,
|
||||
## since cell 1's OWN downstream direction (2 = E) does not point at
|
||||
## cell 2's grid position.
|
||||
func test_build_skeleton_chords_three_hop_chain_threads_correctly() -> void:
|
||||
var river_cells: Array = [[0, 0], [1, 0], [1, 5]] # cell 2 is NOT south of cell 1
|
||||
var river_class: Array = [
|
||||
AtlasWindowGeometryNature.RIVER_CLASS_TRUNK,
|
||||
AtlasWindowGeometryNature.RIVER_CLASS_TRUNK,
|
||||
AtlasWindowGeometryNature.RIVER_CLASS_TRUNK,
|
||||
]
|
||||
var river_downstream: Array = [
|
||||
1, # cell 0 -> S -> (1, 0), matches cell 1's own grid position
|
||||
2, # cell 1 -> E -> (1, 1) — NOT cell 2's position (1, 5)
|
||||
AtlasWindowGeometryNature.RIVER_DOWNSTREAM_MOUTH,
|
||||
]
|
||||
var chords: Array = AtlasWindowGeometryNature.build_skeleton_chords(
|
||||
river_cells, river_class, river_downstream, "Region"
|
||||
)
|
||||
assert_int(chords.size()).is_equal(2)
|
||||
assert_that(chords[0]["from"]).is_equal(Vector2(0.0, 0.0))
|
||||
assert_that(chords[0]["to"]).is_equal(Vector2(1.0, 0.0))
|
||||
assert_that(chords[1]["from"]).is_equal(Vector2(1.0, 0.0))
|
||||
# cell 1's OWN downstream (E) decodes to (1, 1), NOT cell 2's own listed
|
||||
# position (1, 5) — pinning that this function trusts the D8 DECODE, not
|
||||
# a by-index lookup into river_cells, exactly per the doc's "the decoded
|
||||
# target cell is not required to appear in river_cells" contract.
|
||||
assert_that(chords[1]["to"]).override_failure_message(
|
||||
"cell 1's downstream target must be its DECODED D8 neighbor (1,1),"
|
||||
+ " never a by-index lookup into river_cells (which would wrongly"
|
||||
+ " give (1,5), cell 2's own listed position)"
|
||||
).is_equal(Vector2(1.0, 1.0))
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# T-1170 Ruling 5c: the RIVER_CLASS_VISIBLE_BY_RUNG split —
|
||||
# skeleton_class_visible_at_rung() (Region+ chord-chain path, Ruling 5a) and
|
||||
# course_class_visible_at_rung() (District/Quarter windowed path, Ruling 5b).
|
||||
# =============================================================================
|
||||
|
||||
|
||||
func test_skeleton_class_visible_at_rung_region_shows_every_class() -> void:
|
||||
assert_bool(
|
||||
AtlasWindowGeometry.river_class_visible_at_rung(AtlasWindowGeometry.RIVER_CLASS_STREAM, "Region")
|
||||
).is_true()
|
||||
assert_bool(
|
||||
AtlasWindowGeometry.river_class_visible_at_rung(
|
||||
AtlasWindowGeometry.RIVER_CLASS_TRIBUTARY, "Region"
|
||||
AtlasWindowGeometryNature.skeleton_class_visible_at_rung(
|
||||
AtlasWindowGeometryNature.RIVER_CLASS_STREAM, "Region"
|
||||
)
|
||||
).is_true()
|
||||
assert_bool(
|
||||
AtlasWindowGeometry.river_class_visible_at_rung(AtlasWindowGeometry.RIVER_CLASS_TRUNK, "Region")
|
||||
AtlasWindowGeometryNature.skeleton_class_visible_at_rung(
|
||||
AtlasWindowGeometryNature.RIVER_CLASS_TRIBUTARY, "Region"
|
||||
)
|
||||
).is_true()
|
||||
assert_bool(
|
||||
AtlasWindowGeometryNature.skeleton_class_visible_at_rung(
|
||||
AtlasWindowGeometryNature.RIVER_CLASS_TRUNK, "Region"
|
||||
)
|
||||
).is_true()
|
||||
|
||||
|
||||
func test_river_class_visible_at_rung_district_shows_trunk_only() -> void:
|
||||
assert_bool(
|
||||
AtlasWindowGeometry.river_class_visible_at_rung(
|
||||
AtlasWindowGeometry.RIVER_CLASS_STREAM, "District"
|
||||
)
|
||||
).is_false()
|
||||
assert_bool(
|
||||
AtlasWindowGeometry.river_class_visible_at_rung(
|
||||
AtlasWindowGeometry.RIVER_CLASS_TRIBUTARY, "District"
|
||||
)
|
||||
).is_false()
|
||||
assert_bool(
|
||||
AtlasWindowGeometry.river_class_visible_at_rung(AtlasWindowGeometry.RIVER_CLASS_TRUNK, "District")
|
||||
).is_true()
|
||||
|
||||
|
||||
func test_river_class_visible_at_rung_quarter_shows_nothing() -> void:
|
||||
assert_bool(
|
||||
AtlasWindowGeometry.river_class_visible_at_rung(AtlasWindowGeometry.RIVER_CLASS_STREAM, "Quarter")
|
||||
).is_false()
|
||||
assert_bool(
|
||||
AtlasWindowGeometry.river_class_visible_at_rung(
|
||||
AtlasWindowGeometry.RIVER_CLASS_TRIBUTARY, "Quarter"
|
||||
)
|
||||
).is_false()
|
||||
assert_bool(
|
||||
AtlasWindowGeometry.river_class_visible_at_rung(AtlasWindowGeometry.RIVER_CLASS_TRUNK, "Quarter")
|
||||
).is_false()
|
||||
## T-1170: the skeleton path no longer draws AT ALL at District/Quarter (the
|
||||
## chord chain is Region-only — District/Quarter draw courses instead, the
|
||||
## OTHER table below) — this is a CHANGE from wave 1's original District
|
||||
## "trunk only" disposition on the single RIVER_CLASS_VISIBLE_BY_RUNG table.
|
||||
func test_skeleton_class_visible_at_rung_district_and_quarter_show_nothing() -> void:
|
||||
for cls in [
|
||||
AtlasWindowGeometryNature.RIVER_CLASS_STREAM,
|
||||
AtlasWindowGeometryNature.RIVER_CLASS_TRIBUTARY,
|
||||
AtlasWindowGeometryNature.RIVER_CLASS_TRUNK,
|
||||
]:
|
||||
assert_bool(
|
||||
AtlasWindowGeometryNature.skeleton_class_visible_at_rung(cls, "District")
|
||||
).override_failure_message(
|
||||
"the skeleton (chord-chain) path must show NOTHING at District —"
|
||||
+ " District draws courses instead (Ruling 5b)"
|
||||
).is_false()
|
||||
assert_bool(
|
||||
AtlasWindowGeometryNature.skeleton_class_visible_at_rung(cls, "Quarter")
|
||||
).is_false()
|
||||
|
||||
|
||||
## An unrecognized rung tag falls back to Region's fullest visibility set —
|
||||
## the cluster's existing "unrecognized -> safest/most permissive already-
|
||||
## shipped behavior" posture.
|
||||
func test_river_class_visible_at_rung_unknown_tag_falls_back_to_region() -> void:
|
||||
func test_skeleton_class_visible_at_rung_unknown_tag_falls_back_to_region() -> void:
|
||||
assert_bool(
|
||||
AtlasWindowGeometry.river_class_visible_at_rung(AtlasWindowGeometry.RIVER_CLASS_STREAM, "Bogus")
|
||||
AtlasWindowGeometryNature.skeleton_class_visible_at_rung(
|
||||
AtlasWindowGeometryNature.RIVER_CLASS_STREAM, "Bogus"
|
||||
)
|
||||
).is_true()
|
||||
|
||||
|
||||
func test_course_class_visible_at_rung_district_shows_trunk_and_tributary_only() -> void:
|
||||
assert_bool(
|
||||
AtlasWindowGeometryNature.course_class_visible_at_rung(
|
||||
AtlasWindowGeometryNature.RIVER_CLASS_STREAM, "District"
|
||||
)
|
||||
).override_failure_message("District courses must NOT show streams").is_false()
|
||||
assert_bool(
|
||||
AtlasWindowGeometryNature.course_class_visible_at_rung(
|
||||
AtlasWindowGeometryNature.RIVER_CLASS_TRIBUTARY, "District"
|
||||
)
|
||||
).is_true()
|
||||
assert_bool(
|
||||
AtlasWindowGeometryNature.course_class_visible_at_rung(
|
||||
AtlasWindowGeometryNature.RIVER_CLASS_TRUNK, "District"
|
||||
)
|
||||
).is_true()
|
||||
|
||||
|
||||
## The pre-announced wave-1 revisit executing: Quarter shows ALL THREE
|
||||
## classes on the course path — "Quarter rivers return".
|
||||
func test_course_class_visible_at_rung_quarter_shows_every_class() -> void:
|
||||
assert_bool(
|
||||
AtlasWindowGeometryNature.course_class_visible_at_rung(
|
||||
AtlasWindowGeometryNature.RIVER_CLASS_STREAM, "Quarter"
|
||||
)
|
||||
).override_failure_message("Quarter rivers return — streams must be visible").is_true()
|
||||
assert_bool(
|
||||
AtlasWindowGeometryNature.course_class_visible_at_rung(
|
||||
AtlasWindowGeometryNature.RIVER_CLASS_TRIBUTARY, "Quarter"
|
||||
)
|
||||
).is_true()
|
||||
assert_bool(
|
||||
AtlasWindowGeometryNature.course_class_visible_at_rung(
|
||||
AtlasWindowGeometryNature.RIVER_CLASS_TRUNK, "Quarter"
|
||||
)
|
||||
).is_true()
|
||||
|
||||
|
||||
## Region never carries courses (Ruling 1) — the course table has no Region
|
||||
## key at all, and this reader must fail to EMPTY (not fall back to "show
|
||||
## everything", the opposite fallback direction from the skeleton reader) so
|
||||
## a caller can never accidentally draw course polylines at Region.
|
||||
func test_course_class_visible_at_rung_region_shows_nothing() -> void:
|
||||
for cls in [
|
||||
AtlasWindowGeometryNature.RIVER_CLASS_STREAM,
|
||||
AtlasWindowGeometryNature.RIVER_CLASS_TRIBUTARY,
|
||||
AtlasWindowGeometryNature.RIVER_CLASS_TRUNK,
|
||||
]:
|
||||
assert_bool(
|
||||
AtlasWindowGeometryNature.course_class_visible_at_rung(cls, "Region")
|
||||
).override_failure_message(
|
||||
"courses must never be visible at Region — Region draws the skeleton"
|
||||
+ " chord chain, never windowed course content"
|
||||
).is_false()
|
||||
|
||||
|
||||
func test_course_class_visible_at_rung_unknown_tag_falls_back_to_empty() -> void:
|
||||
assert_bool(
|
||||
AtlasWindowGeometryNature.course_class_visible_at_rung(
|
||||
AtlasWindowGeometryNature.RIVER_CLASS_TRUNK, "Bogus"
|
||||
)
|
||||
).is_false()
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# T-1170 Ruling 5c: course_class_width_px() / course_class_opacity() —
|
||||
# functional-default companion tables to COURSE_CLASS_VISIBLE_BY_RUNG.
|
||||
# =============================================================================
|
||||
|
||||
|
||||
func test_course_class_width_px_trunk_widest_stream_thinnest() -> void:
|
||||
var stream_w: float = AtlasWindowGeometryNature.course_class_width_px(
|
||||
AtlasWindowGeometryNature.RIVER_CLASS_STREAM
|
||||
)
|
||||
var tributary_w: float = AtlasWindowGeometryNature.course_class_width_px(
|
||||
AtlasWindowGeometryNature.RIVER_CLASS_TRIBUTARY
|
||||
)
|
||||
var trunk_w: float = AtlasWindowGeometryNature.course_class_width_px(
|
||||
AtlasWindowGeometryNature.RIVER_CLASS_TRUNK
|
||||
)
|
||||
assert_float(trunk_w).override_failure_message(
|
||||
"trunk course width must be the WIDEST of the three classes"
|
||||
).is_greater(tributary_w)
|
||||
assert_float(tributary_w).override_failure_message(
|
||||
"tributary course width must be strictly between stream and trunk"
|
||||
).is_greater(stream_w)
|
||||
|
||||
|
||||
func test_course_class_opacity_trunk_most_opaque_stream_least() -> void:
|
||||
var stream_o: float = AtlasWindowGeometryNature.course_class_opacity(
|
||||
AtlasWindowGeometryNature.RIVER_CLASS_STREAM
|
||||
)
|
||||
var trunk_o: float = AtlasWindowGeometryNature.course_class_opacity(
|
||||
AtlasWindowGeometryNature.RIVER_CLASS_TRUNK
|
||||
)
|
||||
assert_float(trunk_o).is_greater(stream_o)
|
||||
assert_float(trunk_o).override_failure_message("trunk opacity must be fully opaque (1.0)").is_equal_approx(
|
||||
1.0, 0.0001
|
||||
)
|
||||
|
||||
|
||||
## An unrecognized class id falls back to the stream (thinnest/most transparent)
|
||||
## defaults on both tables — the documented, deliberate "unknown -> least
|
||||
## visually assertive" fallback.
|
||||
func test_course_class_width_and_opacity_unknown_class_falls_back_to_stream() -> void:
|
||||
var stream_w: float = AtlasWindowGeometryNature.course_class_width_px(
|
||||
AtlasWindowGeometryNature.RIVER_CLASS_STREAM
|
||||
)
|
||||
var stream_o: float = AtlasWindowGeometryNature.course_class_opacity(
|
||||
AtlasWindowGeometryNature.RIVER_CLASS_STREAM
|
||||
)
|
||||
assert_float(AtlasWindowGeometryNature.course_class_width_px(99)).is_equal_approx(
|
||||
stream_w, 0.0001
|
||||
)
|
||||
assert_float(AtlasWindowGeometryNature.course_class_opacity(99)).is_equal_approx(
|
||||
stream_o, 0.0001
|
||||
)
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Feature-group per-rung gates — confluences/mouths/basins/attractors.
|
||||
# =============================================================================
|
||||
|
||||
|
||||
func test_confluences_visible_at_rung_region_true_others_false() -> void:
|
||||
assert_bool(AtlasWindowGeometry.confluences_visible_at_rung("Region")).is_true()
|
||||
assert_bool(AtlasWindowGeometry.confluences_visible_at_rung("District")).is_false()
|
||||
assert_bool(AtlasWindowGeometry.confluences_visible_at_rung("Quarter")).is_false()
|
||||
assert_bool(AtlasWindowGeometryNature.confluences_visible_at_rung("Region")).is_true()
|
||||
assert_bool(AtlasWindowGeometryNature.confluences_visible_at_rung("District")).is_false()
|
||||
assert_bool(AtlasWindowGeometryNature.confluences_visible_at_rung("Quarter")).is_false()
|
||||
|
||||
|
||||
## Mouths get the one rung-based EXCEPTION in the whole table: District keeps
|
||||
## them visible (a mouth is always a landmark, per the ruling) — the only
|
||||
## feature group where District differs from Region's disposition.
|
||||
func test_mouths_visible_at_rung_region_and_district_true_quarter_false() -> void:
|
||||
assert_bool(AtlasWindowGeometry.mouths_visible_at_rung("Region")).is_true()
|
||||
assert_bool(AtlasWindowGeometry.mouths_visible_at_rung("District")).is_true()
|
||||
assert_bool(AtlasWindowGeometry.mouths_visible_at_rung("Quarter")).is_false()
|
||||
assert_bool(AtlasWindowGeometryNature.mouths_visible_at_rung("Region")).is_true()
|
||||
assert_bool(AtlasWindowGeometryNature.mouths_visible_at_rung("District")).is_true()
|
||||
assert_bool(AtlasWindowGeometryNature.mouths_visible_at_rung("Quarter")).is_false()
|
||||
|
||||
|
||||
func test_basins_visible_at_rung_region_only() -> void:
|
||||
assert_bool(AtlasWindowGeometry.basins_visible_at_rung("Region")).is_true()
|
||||
assert_bool(AtlasWindowGeometry.basins_visible_at_rung("District")).is_false()
|
||||
assert_bool(AtlasWindowGeometry.basins_visible_at_rung("Quarter")).is_false()
|
||||
assert_bool(AtlasWindowGeometryNature.basins_visible_at_rung("Region")).is_true()
|
||||
assert_bool(AtlasWindowGeometryNature.basins_visible_at_rung("District")).is_false()
|
||||
assert_bool(AtlasWindowGeometryNature.basins_visible_at_rung("Quarter")).is_false()
|
||||
|
||||
|
||||
func test_attractors_visible_at_rung_region_only() -> void:
|
||||
assert_bool(AtlasWindowGeometry.attractors_visible_at_rung("Region")).is_true()
|
||||
assert_bool(AtlasWindowGeometry.attractors_visible_at_rung("District")).is_false()
|
||||
assert_bool(AtlasWindowGeometry.attractors_visible_at_rung("Quarter")).is_false()
|
||||
assert_bool(AtlasWindowGeometryNature.attractors_visible_at_rung("Region")).is_true()
|
||||
assert_bool(AtlasWindowGeometryNature.attractors_visible_at_rung("District")).is_false()
|
||||
assert_bool(AtlasWindowGeometryNature.attractors_visible_at_rung("Quarter")).is_false()
|
||||
|
||||
|
||||
# =============================================================================
|
||||
@@ -234,7 +630,7 @@ func test_attractors_visible_at_rung_region_only() -> void:
|
||||
## must equal the input unchanged — no over/under-correction at the one zoom
|
||||
## level where compensation is a no-op by construction.
|
||||
func test_zoom_compensated_size_at_zoom_one_is_unchanged() -> void:
|
||||
assert_float(AtlasWindowGeometry.zoom_compensated_size(2.2, 1.0)).is_equal_approx(2.2, 0.0001)
|
||||
assert_float(AtlasWindowGeometryNature.zoom_compensated_size(2.2, 1.0)).is_equal_approx(2.2, 0.0001)
|
||||
|
||||
|
||||
## The exact regression shape: at Lendel's real orbital fit zoom (~0.0063,
|
||||
@@ -246,7 +642,9 @@ func test_zoom_compensated_size_at_zoom_one_is_unchanged() -> void:
|
||||
func test_zoom_compensated_size_at_orbital_zoom_scales_up_inversely() -> void:
|
||||
var view_zoom := 0.0063
|
||||
var screen_space_size := 2.2
|
||||
var compensated: float = AtlasWindowGeometry.zoom_compensated_size(screen_space_size, view_zoom)
|
||||
var compensated: float = AtlasWindowGeometryNature.zoom_compensated_size(
|
||||
screen_space_size, view_zoom
|
||||
)
|
||||
# Round-trip: compensated * view_zoom must reconstruct the original
|
||||
# screen-space size — this IS the property that makes the on-screen
|
||||
# result zoom-invariant (the canvas transform performs exactly this
|
||||
@@ -277,50 +675,7 @@ func test_uncompensated_radius_at_orbital_zoom_would_be_sub_pixel() -> void:
|
||||
## A degenerate zero (or negative) view_zoom must not divide-by-zero/produce
|
||||
## infinity/NaN — the floor guard keeps this function total.
|
||||
func test_zoom_compensated_size_zero_zoom_does_not_blow_up() -> void:
|
||||
var result: float = AtlasWindowGeometry.zoom_compensated_size(2.2, 0.0)
|
||||
var result: float = AtlasWindowGeometryNature.zoom_compensated_size(2.2, 0.0)
|
||||
assert_bool(is_finite(result)).override_failure_message(
|
||||
"a degenerate zero view_zoom must not produce inf/NaN"
|
||||
).is_true()
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# T-1172 round 2: cell_index_for_local_offset() — the shared painter/clip
|
||||
# index formula (see its own doc for the "why shared, not duplicated" case).
|
||||
# =============================================================================
|
||||
|
||||
|
||||
func test_cell_index_for_local_offset_top_left_is_zero_zero() -> void:
|
||||
var cell: Vector2i = AtlasWindowGeometry.cell_index_for_local_offset(0.0, 0.0, 6400, 64)
|
||||
assert_that(cell).is_equal(Vector2i(0, 0))
|
||||
|
||||
|
||||
## The exact live-repro numbers from T-1172 round 2's trace: a query whose
|
||||
## district-space local offset is (2594.09, 5593.15) inside a 6400-wide,
|
||||
## 64-cell-side window must resolve to (col=25, row=55) — pinned directly
|
||||
## against the LIVE captured values that closed the investigation (both the
|
||||
## painter's _build_tile_texture() and the clip independently produced this
|
||||
## exact pair for the same query in the live trace).
|
||||
func test_cell_index_for_local_offset_matches_the_live_trace_repro() -> void:
|
||||
var cell: Vector2i = AtlasWindowGeometry.cell_index_for_local_offset(
|
||||
2594.0849609375, 5593.15258789062, 6400, 64
|
||||
)
|
||||
assert_that(cell).override_failure_message(
|
||||
"must match the live-captured painter/clip agreement point from the"
|
||||
+ " T-1172 round 2 investigation — (col=25, row=55)"
|
||||
).is_equal(Vector2i(25, 55))
|
||||
|
||||
|
||||
func test_cell_index_for_local_offset_bottom_right_boundary_clamps_inside() -> void:
|
||||
# local offset == n (the exclusive upper boundary) must clamp to the LAST
|
||||
# cell, not overflow to a nonexistent grid_side'th cell.
|
||||
var cell: Vector2i = AtlasWindowGeometry.cell_index_for_local_offset(6400.0, 6400.0, 6400, 64)
|
||||
assert_that(cell).is_equal(Vector2i(63, 63))
|
||||
|
||||
|
||||
func test_cell_index_for_local_offset_zero_n_or_grid_side_returns_sentinel() -> void:
|
||||
assert_that(AtlasWindowGeometry.cell_index_for_local_offset(10.0, 10.0, 0, 64)).is_equal(
|
||||
Vector2i(-1, -1)
|
||||
)
|
||||
assert_that(AtlasWindowGeometry.cell_index_for_local_offset(10.0, 10.0, 6400, 0)).is_equal(
|
||||
Vector2i(-1, -1)
|
||||
)
|
||||
|
||||
@@ -442,3 +442,73 @@ func test_mouth_position_on_land_is_not_suppressed() -> void:
|
||||
assert_bool(o._is_drawn_water(Vector2(1.9, 1.9), ctx)).override_failure_message(
|
||||
"a mouth position over drawn land must render exactly as today (not clipped)"
|
||||
).is_false()
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# T-1170 Ruling 3g/5a: _segment_touches_drawn_water() — the CHORD SEGMENT
|
||||
# clip rule (both endpoints + midpoint), replacing the old per-point-only
|
||||
# clip for the skeleton-chord draw path. The water cell is (0,0) in district
|
||||
# space, per _mock_4x4_water_corner_window()'s own doc — spans roughly
|
||||
# [-0.5, 0.5) x [-0.5, 0.5) at this fixture's district granularity.
|
||||
# =============================================================================
|
||||
|
||||
|
||||
## Both endpoints on land, entirely away from the water cell — no clip.
|
||||
func test_segment_touches_drawn_water_false_when_fully_on_land() -> void:
|
||||
var stub := _ViewerStub.new()
|
||||
stub.district_window = _mock_4x4_water_corner_window()
|
||||
var o = _make_overlay(stub)
|
||||
var ctx: Dictionary = _ctx_for(stub)
|
||||
assert_bool(
|
||||
o._segment_touches_drawn_water(Vector2(1.0, 1.0), Vector2(1.9, 1.9), ctx)
|
||||
).override_failure_message(
|
||||
"a segment entirely on land (both endpoints, and therefore its"
|
||||
+ " midpoint) must not be clipped"
|
||||
).is_false()
|
||||
|
||||
|
||||
## Either endpoint alone on water clips the whole segment.
|
||||
func test_segment_touches_drawn_water_true_when_an_endpoint_is_on_water() -> void:
|
||||
var stub := _ViewerStub.new()
|
||||
stub.district_window = _mock_4x4_water_corner_window()
|
||||
var o = _make_overlay(stub)
|
||||
var ctx: Dictionary = _ctx_for(stub)
|
||||
assert_bool(
|
||||
o._segment_touches_drawn_water(Vector2(-1.9, -1.9), Vector2(1.9, 1.9), ctx)
|
||||
).override_failure_message(
|
||||
"a segment with EITHER endpoint over drawn water must be clipped"
|
||||
).is_true()
|
||||
|
||||
|
||||
## The decision this rule specifically exists to catch (Ruling 3g's ask, "pick
|
||||
## the visually cleaner rule, document it, test it"): BOTH endpoints on land,
|
||||
## on opposite sides of the water cell, with the MIDPOINT landing inside it —
|
||||
## an endpoints-only rule would miss this entirely (a chord visibly crossing
|
||||
## open water with neither end clipped). The midpoint sample must catch it.
|
||||
func test_segment_touches_drawn_water_true_when_only_midpoint_is_on_water() -> void:
|
||||
var stub := _ViewerStub.new()
|
||||
stub.district_window = _mock_4x4_water_corner_window()
|
||||
var o = _make_overlay(stub)
|
||||
var ctx: Dictionary = _ctx_for(stub)
|
||||
# The water cell is (col=0, row=0), spanning district [-2,-1) x [-2,-1) in
|
||||
# this n=4/grid_side=4 fixture (1:1 district-to-cell mapping). Pick
|
||||
# endpoints that EACH resolve to a DIFFERENT LAND cell adjacent to the
|
||||
# water corner — (-1.99, -0.9) resolves to (col=0, row=1), land; (-0.9,
|
||||
# -1.99) resolves to (col=1, row=0), land — but their MIDPOINT
|
||||
# (-1.445, -1.445) falls squarely inside the water cell (col=0, row=0).
|
||||
# Verified numerically, not eyeballed (see the two sanity asserts below).
|
||||
var from_district := Vector2(-1.99, -0.9)
|
||||
var to_district := Vector2(-0.9, -1.99)
|
||||
# Sanity: neither endpoint alone is clipped (both resolve to LAND cells)
|
||||
# — isolates the midpoint as the ONLY reason the segment clips below.
|
||||
assert_bool(o._is_drawn_water(from_district, ctx)).override_failure_message(
|
||||
"test setup invariant: the FROM endpoint alone must resolve to land"
|
||||
).is_false()
|
||||
assert_bool(o._is_drawn_water(to_district, ctx)).override_failure_message(
|
||||
"test setup invariant: the TO endpoint alone must resolve to land"
|
||||
).is_false()
|
||||
assert_bool(o._segment_touches_drawn_water(from_district, to_district, ctx)).override_failure_message(
|
||||
"a segment whose ENDPOINTS are both on land but whose MIDPOINT lands"
|
||||
+ " on drawn water must still be clipped — this is the exact failure"
|
||||
+ " mode an endpoints-only rule would miss (Ruling 3g's ask)"
|
||||
).is_true()
|
||||
|
||||
Reference in New Issue
Block a user