Merge remote-tracking branch 'origin/waterline-clip'
This commit is contained in:
@@ -281,3 +281,46 @@ func test_zoom_compensated_size_zero_zoom_does_not_blow_up() -> void:
|
||||
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)
|
||||
)
|
||||
|
||||
@@ -18,7 +18,10 @@ const AtlasWindowNatureOverlay := preload("res://ui/implant/apps/atlas/atlas_win
|
||||
## the same duck-typed-viewer precedent test_atlas_window_overlay.gd's
|
||||
## _ViewerStub already establishes for AtlasWindowOverlay. get_view_zoom()
|
||||
## added post-live-eyeball (coordinator finding, 2026-07-23): _draw() now
|
||||
## reads it for the zoom-compensated marker-size fix.
|
||||
## reads it for the zoom-compensated marker-size fix. is_tile_mode()/
|
||||
## get_district_window()/get_tile_set() added for T-1172 (the water clip) —
|
||||
## district_window/tile_set default to null/an empty stub, matching "no
|
||||
## arrived composite data yet" (the fail-open case) unless a test sets them.
|
||||
class _ViewerStub:
|
||||
var held_granularity_v2: String = "Region"
|
||||
var body_radius_km: float = 6371.0
|
||||
@@ -26,6 +29,9 @@ class _ViewerStub:
|
||||
var held_n: int = 64
|
||||
var view_zoom: float = 1.0
|
||||
var overlay_visibility: Dictionary = {"gen_rivers": true, "gen_basins": false, "gen_attractors": false}
|
||||
var tile_mode: bool = false
|
||||
var district_window: Variant = null
|
||||
var tile_set: Variant = null
|
||||
|
||||
func get_held_granularity_v2() -> String:
|
||||
return held_granularity_v2
|
||||
@@ -48,6 +54,25 @@ class _ViewerStub:
|
||||
func is_overlay_visible(overlay_id: String) -> bool:
|
||||
return bool(overlay_visibility.get(overlay_id, false))
|
||||
|
||||
func is_tile_mode() -> bool:
|
||||
return tile_mode
|
||||
|
||||
func get_district_window() -> Variant:
|
||||
return district_window
|
||||
|
||||
func get_tile_set() -> Variant:
|
||||
return tile_set
|
||||
|
||||
|
||||
## Bare tile-set stub — AtlasWindowNatureOverlay's water-clip lookup only
|
||||
## reaches it through get_tiles(), matching AtlasWindowTileSet's own public
|
||||
## surface (an Array of {"center": Vector2i, "window": Variant}).
|
||||
class _TileSetStub:
|
||||
var tiles: Array = []
|
||||
|
||||
func get_tiles() -> Array:
|
||||
return tiles
|
||||
|
||||
|
||||
static func _mock_layer1(river_class: Variant = null) -> Dictionary:
|
||||
var rn: Dictionary = {
|
||||
@@ -253,3 +278,167 @@ func test_attractor_shape_stroke_widths_are_never_raw_literals() -> void:
|
||||
"raw numeric stroke width(s) in _draw_attractor_shape — route through"
|
||||
+ " px_w (PR #195 Tyre I1): %s" % [offenders]
|
||||
).is_empty()
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# T-1172 — the two-waterline clip. _is_drawn_water()/_district() are called
|
||||
# directly (both are pure lookups with NO draw_*() call of their own — the
|
||||
# _draw()-requires-a-live-render-pass constraint the smoke suite exists for
|
||||
# does not apply to them), matching this file's own "call private helpers
|
||||
# directly when they're the load-bearing unit" precedent
|
||||
# (test_draw_with_zero_grid_dims_returns_before_any_draw_call() above already
|
||||
# calls _draw() itself specifically because its early-return is BEFORE any
|
||||
# draw call — same reasoning here, one level down).
|
||||
# =============================================================================
|
||||
|
||||
|
||||
## A 4x4 District window (n=4, grid_side=4) centered on district (0,0),
|
||||
## spanning [-2, 2) on both axes — cell (0,0) is water, everything else land.
|
||||
## Mirrors test_atlas_window_water_clip.gd's own _mock_4x4_window() fixture
|
||||
## shape (kept local here rather than shared — no cross-test-file import
|
||||
## precedent in this cluster).
|
||||
static func _mock_4x4_water_corner_window() -> Dictionary:
|
||||
var morphology := PackedByteArray()
|
||||
morphology.resize(16)
|
||||
for i in range(16):
|
||||
morphology[i] = 8 # AlluvialPlain — land
|
||||
morphology[0] = 0 # OpenOcean — the single water cell, row 0 col 0
|
||||
return {"center": [0, 0], "n": 4, "granularity_v2": "District", "morphology": morphology}
|
||||
|
||||
|
||||
func _ctx_for(viewer_stub: _ViewerStub) -> Dictionary:
|
||||
return {
|
||||
"grid_w": 256.0,
|
||||
"grid_h": 128.0,
|
||||
"radius_km": 0.0, # no-radius: 1 heightmap pixel = 1 district metre (simplest math)
|
||||
"held_center": viewer_stub.held_center,
|
||||
"held_n": viewer_stub.held_n,
|
||||
"cell_px": 16.0,
|
||||
"cols": 0,
|
||||
"granularity_v2": viewer_stub.held_granularity_v2,
|
||||
"view_zoom": viewer_stub.view_zoom,
|
||||
}
|
||||
|
||||
|
||||
## Single-window mode: a district position resolving to a LAND cell must not
|
||||
## be clipped (_is_drawn_water() returns false — the dot draws).
|
||||
func test_is_drawn_water_false_for_a_land_cell_single_window() -> 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._is_drawn_water(Vector2(1.5, 1.5), ctx)).override_failure_message(
|
||||
"a district position over a LAND cell must not be clipped"
|
||||
).is_false()
|
||||
|
||||
|
||||
## Single-window mode: a district position resolving to a WATER cell must be
|
||||
## clipped (_is_drawn_water() returns true — the caller skips drawing).
|
||||
func test_is_drawn_water_true_for_a_water_cell_single_window() -> 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._is_drawn_water(Vector2(-1.5, -1.5), ctx)).override_failure_message(
|
||||
"a district position over a WATER (OpenOcean) cell must be clipped"
|
||||
).is_true()
|
||||
|
||||
|
||||
## Tile mode: same land/water split, but the composite data arrives via
|
||||
## get_tile_set().get_tiles() instead of get_district_window() — proving the
|
||||
## clip predicate reaches BOTH path shapes, per the coordinator's explicit
|
||||
## "both path shapes exercised" ask.
|
||||
func test_is_drawn_water_works_in_tile_mode_land() -> void:
|
||||
var stub := _ViewerStub.new()
|
||||
stub.tile_mode = true
|
||||
var ts := _TileSetStub.new()
|
||||
ts.tiles = [{"center": Vector2i.ZERO, "window": _mock_4x4_water_corner_window()}]
|
||||
stub.tile_set = ts
|
||||
var o = _make_overlay(stub)
|
||||
var ctx: Dictionary = _ctx_for(stub)
|
||||
assert_bool(o._is_drawn_water(Vector2(1.5, 1.5), ctx)).override_failure_message(
|
||||
"tile mode: a district position over a LAND cell must not be clipped"
|
||||
).is_false()
|
||||
|
||||
|
||||
func test_is_drawn_water_works_in_tile_mode_water() -> void:
|
||||
var stub := _ViewerStub.new()
|
||||
stub.tile_mode = true
|
||||
var ts := _TileSetStub.new()
|
||||
ts.tiles = [{"center": Vector2i.ZERO, "window": _mock_4x4_water_corner_window()}]
|
||||
stub.tile_set = ts
|
||||
var o = _make_overlay(stub)
|
||||
var ctx: Dictionary = _ctx_for(stub)
|
||||
assert_bool(o._is_drawn_water(Vector2(-1.5, -1.5), ctx)).override_failure_message(
|
||||
"tile mode: a district position over a WATER cell must be clipped"
|
||||
).is_true()
|
||||
|
||||
|
||||
## Tyre's rule 5: NO arrived composite data at the queried position (single-
|
||||
## window mode, window is null — the pre-arrival state) must FAIL OPEN — the
|
||||
## clip is a presentation refinement, never a data gate.
|
||||
func test_is_drawn_water_fails_open_with_no_composite_data_single_window() -> void:
|
||||
var stub := _ViewerStub.new()
|
||||
stub.district_window = null # nothing arrived yet
|
||||
var o = _make_overlay(stub)
|
||||
var ctx: Dictionary = _ctx_for(stub)
|
||||
assert_bool(o._is_drawn_water(Vector2(0.0, 0.0), ctx)).override_failure_message(
|
||||
"no arrived composite data must fail OPEN (draw the dot), never clip"
|
||||
).is_false()
|
||||
|
||||
|
||||
## Same fail-open guarantee in tile mode: no tile set at all (get_tile_set()
|
||||
## returns null, matching the viewer's own pre-enter_orbital() state).
|
||||
func test_is_drawn_water_fails_open_with_no_tile_set() -> void:
|
||||
var stub := _ViewerStub.new()
|
||||
stub.tile_mode = true
|
||||
stub.tile_set = null
|
||||
var o = _make_overlay(stub)
|
||||
var ctx: Dictionary = _ctx_for(stub)
|
||||
assert_bool(o._is_drawn_water(Vector2(0.0, 0.0), ctx)).override_failure_message(
|
||||
"no tile set at all must fail OPEN (draw the dot), never clip"
|
||||
).is_false()
|
||||
|
||||
|
||||
## Fail-open ALSO covers "a tile set exists but no tile covers this position
|
||||
## yet" (mid-progressive-arrival) — the coordinator's own "briefly-unclipped
|
||||
## dot during progressive arrival is fine and self-heals" framing.
|
||||
func test_is_drawn_water_fails_open_when_no_tile_covers_the_position() -> void:
|
||||
var stub := _ViewerStub.new()
|
||||
stub.tile_mode = true
|
||||
var ts := _TileSetStub.new()
|
||||
ts.tiles = [{"center": Vector2i(500, 500), "window": null}] # far away, unarrived
|
||||
stub.tile_set = ts
|
||||
var o = _make_overlay(stub)
|
||||
var ctx: Dictionary = _ctx_for(stub)
|
||||
assert_bool(o._is_drawn_water(Vector2(0.0, 0.0), ctx)).is_false()
|
||||
|
||||
|
||||
## Mouths must be SUPPRESSED (not snapped, not dimmed) on drawn water and
|
||||
## render exactly as today on drawn land — this suite covers the SHARED
|
||||
## predicate _draw_rivers() calls for river cells/confluences/mouths alike
|
||||
## (_is_drawn_water() itself has no notion of "which feature type" — that's
|
||||
## by design, per the ruling's "same predicate" wording for rule 3). A
|
||||
## dedicated assertion here pins the WORDING intent (mouth-specific rule 3)
|
||||
## even though the underlying mechanism is identical to the river-cell tests
|
||||
## above — a future refactor that special-cases mouths differently should
|
||||
## still trip this.
|
||||
func test_mouth_position_on_water_is_suppressed_same_predicate_as_rivers() -> 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._is_drawn_water(Vector2(-1.9, -1.9), ctx)).override_failure_message(
|
||||
"a mouth position over drawn water must resolve as clipped, via the SAME"
|
||||
+ " predicate river cells/confluences use — no separate snap/dim path"
|
||||
).is_true()
|
||||
|
||||
|
||||
func test_mouth_position_on_land_is_not_suppressed() -> 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._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()
|
||||
|
||||
@@ -67,7 +67,13 @@ static func _dummy_renderer_active() -> bool:
|
||||
## called with — the whole point of this smoke suite (post-live-eyeball,
|
||||
## coordinator finding 2026-07-23) is proving markers stay visible at the
|
||||
## REAL orbital fit zoom, not an artificially large test zoom that would
|
||||
## mask the zoom-compensation bug the fix addresses.
|
||||
## mask the zoom-compensation bug the fix addresses. is_tile_mode()/
|
||||
## get_district_window()/get_tile_set() added for T-1172 (the water clip) —
|
||||
## defaults to single-window mode with NO arrived composite (district_window
|
||||
## null), the fail-open case, so these smoke tests keep drawing every marker
|
||||
## exactly as before the clip existed (this file's own job is proving the
|
||||
## draw calls paint pixels at all, not exercising the clip's water-detection
|
||||
## branch — that's test_atlas_window_nature_overlay.gd's job).
|
||||
class _ViewerStub:
|
||||
var held_granularity_v2: String = "Region"
|
||||
var body_radius_km: float = 6371.0
|
||||
@@ -75,6 +81,9 @@ class _ViewerStub:
|
||||
var held_n: int = 64
|
||||
var view_zoom: float = 1.0
|
||||
var overlay_visibility: Dictionary = {"gen_rivers": true, "gen_basins": true, "gen_attractors": true}
|
||||
var tile_mode: bool = false
|
||||
var district_window: Variant = null
|
||||
var tile_set: Variant = null
|
||||
|
||||
func get_held_granularity_v2() -> String:
|
||||
return held_granularity_v2
|
||||
@@ -97,6 +106,15 @@ class _ViewerStub:
|
||||
func is_overlay_visible(overlay_id: String) -> bool:
|
||||
return bool(overlay_visibility.get(overlay_id, false))
|
||||
|
||||
func is_tile_mode() -> bool:
|
||||
return tile_mode
|
||||
|
||||
func get_district_window() -> Variant:
|
||||
return district_window
|
||||
|
||||
func get_tile_set() -> Variant:
|
||||
return tile_set
|
||||
|
||||
|
||||
## A dense scatter of river cells spanning the whole held window's canvas
|
||||
## footprint (not clustered in one corner) — a genuine "does the composite
|
||||
|
||||
@@ -0,0 +1,368 @@
|
||||
## T-1172: pure-function tests for AtlasWindowWaterClip — the two-waterline
|
||||
## clip's cell-lookup math (cell_grid_side_for_window/morphology_zone_in_window/
|
||||
## resolve_morphology_zone). Split into its own file matching this cluster's
|
||||
## own "one pure-geometry file, one test file" precedent
|
||||
## (test_atlas_window_geometry_nature.gd next to atlas_window_geometry.gd's
|
||||
## nature-overlay additions).
|
||||
class_name TestAtlasWindowWaterClip
|
||||
extends GdUnitTestSuite
|
||||
|
||||
const AtlasWindowWaterClip := preload("res://ui/implant/apps/atlas/atlas_window_water_clip.gd")
|
||||
|
||||
const MORPHOLOGY_OPEN_OCEAN: int = 0
|
||||
const MORPHOLOGY_LAND: int = 8 # AlluvialPlain — any non-water zone works
|
||||
|
||||
|
||||
## A window dict shaped exactly like a real DistrictWindowLayer — `center`
|
||||
## as a [x,y] array (the msgpack-decoded wire shape, matching every other
|
||||
## mock window in this cluster's tests), `n` districts wide, `granularity_v2`
|
||||
## District (1:1 cell:district, the simplest case), and a `grid_side x
|
||||
## grid_side` morphology array where `grid_side == n`.
|
||||
static func _mock_district_window(
|
||||
center: Vector2i, n: int, morphology: PackedByteArray
|
||||
) -> Dictionary:
|
||||
return {
|
||||
"center": [center.x, center.y],
|
||||
"n": n,
|
||||
"granularity_v2": "District",
|
||||
"morphology": morphology,
|
||||
}
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# cell_grid_side_for_window — mirrors AtlasWindowOverlay.cell_grid_side_for_window()
|
||||
# =============================================================================
|
||||
|
||||
|
||||
func test_cell_grid_side_district_is_n_unchanged() -> void:
|
||||
var w := {"n": 32, "granularity_v2": "District"}
|
||||
assert_int(AtlasWindowWaterClip.cell_grid_side_for_window(w)).is_equal(32)
|
||||
|
||||
|
||||
func test_cell_grid_side_quarter_is_n_times_four() -> void:
|
||||
var w := {"n": 16, "granularity_v2": "Quarter"}
|
||||
assert_int(AtlasWindowWaterClip.cell_grid_side_for_window(w)).is_equal(64)
|
||||
|
||||
|
||||
func test_cell_grid_side_region_is_n_over_hundred_rounded() -> void:
|
||||
var w := {"n": 6400, "granularity_v2": "Region"}
|
||||
assert_int(AtlasWindowWaterClip.cell_grid_side_for_window(w)).is_equal(64)
|
||||
|
||||
|
||||
func test_cell_grid_side_region_floors_at_one() -> void:
|
||||
var w := {"n": 1, "granularity_v2": "Region"}
|
||||
assert_int(AtlasWindowWaterClip.cell_grid_side_for_window(w)).is_equal(1)
|
||||
|
||||
|
||||
func test_cell_grid_side_unknown_granularity_falls_back_to_district() -> void:
|
||||
var w := {"n": 32} # no granularity_v2 key at all
|
||||
assert_int(AtlasWindowWaterClip.cell_grid_side_for_window(w)).is_equal(32)
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# morphology_zone_in_window — single-window cell lookup
|
||||
# =============================================================================
|
||||
|
||||
|
||||
## A 4x4 District window (n=4, grid_side=4) centered on district (0,0),
|
||||
## spanning [-2, 2) on both axes. Cell (0,0) [top-left, covering district
|
||||
## x in [-2,-1), y in [-2,-1)] is water; the rest is land.
|
||||
static func _mock_4x4_window() -> Dictionary:
|
||||
var morphology := PackedByteArray()
|
||||
morphology.resize(16)
|
||||
for i in range(16):
|
||||
morphology[i] = MORPHOLOGY_LAND
|
||||
morphology[0] = MORPHOLOGY_OPEN_OCEAN # row 0, col 0
|
||||
return _mock_district_window(Vector2i.ZERO, 4, morphology)
|
||||
|
||||
|
||||
func test_morphology_zone_in_window_reads_the_water_cell() -> void:
|
||||
var w: Dictionary = _mock_4x4_window()
|
||||
# District (-1.5, -1.5) falls in cell (row 0, col 0) — the water cell.
|
||||
var zone: int = AtlasWindowWaterClip.morphology_zone_in_window(Vector2(-1.5, -1.5), w)
|
||||
assert_int(zone).is_equal(MORPHOLOGY_OPEN_OCEAN)
|
||||
|
||||
|
||||
func test_morphology_zone_in_window_reads_a_land_cell() -> void:
|
||||
var w: Dictionary = _mock_4x4_window()
|
||||
# District (1.5, 1.5) falls in cell (row 3, col 3) — land.
|
||||
var zone: int = AtlasWindowWaterClip.morphology_zone_in_window(Vector2(1.5, 1.5), w)
|
||||
assert_int(zone).is_equal(MORPHOLOGY_LAND)
|
||||
|
||||
|
||||
func test_morphology_zone_in_window_outside_extent_is_no_data() -> void:
|
||||
var w: Dictionary = _mock_4x4_window()
|
||||
var zone: int = AtlasWindowWaterClip.morphology_zone_in_window(Vector2(100.0, 100.0), w)
|
||||
assert_int(zone).is_equal(AtlasWindowWaterClip.MORPHOLOGY_ZONE_NO_DATA)
|
||||
|
||||
|
||||
func test_morphology_zone_in_window_null_window_is_no_data() -> void:
|
||||
var zone: int = AtlasWindowWaterClip.morphology_zone_in_window(Vector2.ZERO, null)
|
||||
assert_int(zone).is_equal(AtlasWindowWaterClip.MORPHOLOGY_ZONE_NO_DATA)
|
||||
|
||||
|
||||
func test_morphology_zone_in_window_missing_morphology_array_is_no_data() -> void:
|
||||
var w := {"center": [0, 0], "n": 4, "granularity_v2": "District"}
|
||||
var zone: int = AtlasWindowWaterClip.morphology_zone_in_window(Vector2.ZERO, w)
|
||||
assert_int(zone).is_equal(AtlasWindowWaterClip.MORPHOLOGY_ZONE_NO_DATA)
|
||||
|
||||
|
||||
func test_morphology_zone_in_window_zero_n_is_no_data() -> void:
|
||||
var w := {
|
||||
"center": [0, 0], "n": 0, "granularity_v2": "District", "morphology": PackedByteArray()
|
||||
}
|
||||
var zone: int = AtlasWindowWaterClip.morphology_zone_in_window(Vector2.ZERO, w)
|
||||
assert_int(zone).is_equal(AtlasWindowWaterClip.MORPHOLOGY_ZONE_NO_DATA)
|
||||
|
||||
|
||||
## The exact top-left/bottom-right boundary districts must resolve to the
|
||||
## edge cells, not silently miss due to an off-by-one in the containment
|
||||
## test — [-2, 2) is half-open, so -2.0 is IN, 2.0 is OUT.
|
||||
func test_morphology_zone_in_window_boundary_inclusive_at_min() -> void:
|
||||
var w: Dictionary = _mock_4x4_window()
|
||||
var zone: int = AtlasWindowWaterClip.morphology_zone_in_window(Vector2(-2.0, -2.0), w)
|
||||
assert_int(zone).is_equal(MORPHOLOGY_OPEN_OCEAN)
|
||||
|
||||
|
||||
func test_morphology_zone_in_window_boundary_exclusive_at_max() -> void:
|
||||
var w: Dictionary = _mock_4x4_window()
|
||||
var zone: int = AtlasWindowWaterClip.morphology_zone_in_window(Vector2(2.0, 2.0), w)
|
||||
assert_int(zone).is_equal(AtlasWindowWaterClip.MORPHOLOGY_ZONE_NO_DATA)
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# resolve_morphology_zone — the single-window / tile-mode dispatch
|
||||
# =============================================================================
|
||||
|
||||
|
||||
func test_resolve_single_window_mode_reads_the_window_directly() -> void:
|
||||
var w: Dictionary = _mock_4x4_window()
|
||||
var zone: int = AtlasWindowWaterClip.resolve_morphology_zone(
|
||||
Vector2(-1.5, -1.5), false, w, [], 0
|
||||
)
|
||||
assert_int(zone).is_equal(MORPHOLOGY_OPEN_OCEAN)
|
||||
|
||||
|
||||
func test_resolve_single_window_mode_null_window_is_no_data() -> void:
|
||||
var zone: int = AtlasWindowWaterClip.resolve_morphology_zone(Vector2.ZERO, false, null, [], 0)
|
||||
assert_int(zone).is_equal(AtlasWindowWaterClip.MORPHOLOGY_ZONE_NO_DATA)
|
||||
|
||||
|
||||
## Tile mode: two tiles, each its own 4x4 window, centered far enough apart
|
||||
## that a queried district only falls inside ONE of them.
|
||||
func test_resolve_tile_mode_finds_the_containing_tile() -> void:
|
||||
var morph_a := PackedByteArray()
|
||||
morph_a.resize(16)
|
||||
for i in range(16):
|
||||
morph_a[i] = MORPHOLOGY_LAND
|
||||
var tile_a := {
|
||||
"center": Vector2i(0, 0), "window": _mock_district_window(Vector2i.ZERO, 4, morph_a)
|
||||
}
|
||||
|
||||
var morph_b := PackedByteArray()
|
||||
morph_b.resize(16)
|
||||
for i in range(16):
|
||||
morph_b[i] = MORPHOLOGY_OPEN_OCEAN
|
||||
var tile_b := {
|
||||
"center": Vector2i(100, 0), "window": _mock_district_window(Vector2i(100, 0), 4, morph_b)
|
||||
}
|
||||
|
||||
var tiles: Array = [tile_a, tile_b]
|
||||
var zone_in_a: int = AtlasWindowWaterClip.resolve_morphology_zone(
|
||||
Vector2(0.0, 0.0), true, null, tiles, 0
|
||||
)
|
||||
var zone_in_b: int = AtlasWindowWaterClip.resolve_morphology_zone(
|
||||
Vector2(100.0, 0.0), true, null, tiles, 0
|
||||
)
|
||||
assert_int(zone_in_a).override_failure_message(
|
||||
"a district position inside tile A's extent must read tile A's own cell"
|
||||
).is_equal(MORPHOLOGY_LAND)
|
||||
assert_int(zone_in_b).override_failure_message(
|
||||
"a district position inside tile B's extent must read tile B's own cell"
|
||||
).is_equal(MORPHOLOGY_OPEN_OCEAN)
|
||||
|
||||
|
||||
func test_resolve_tile_mode_position_outside_every_tile_is_no_data() -> void:
|
||||
var morph := PackedByteArray()
|
||||
morph.resize(16)
|
||||
var tile := {"center": Vector2i(0, 0), "window": _mock_district_window(Vector2i.ZERO, 4, morph)}
|
||||
var zone: int = AtlasWindowWaterClip.resolve_morphology_zone(
|
||||
Vector2(9999.0, 9999.0), true, null, [tile], 0
|
||||
)
|
||||
assert_int(zone).is_equal(AtlasWindowWaterClip.MORPHOLOGY_ZONE_NO_DATA)
|
||||
|
||||
|
||||
## A tile whose window hasn't arrived yet (`window: null`, matching
|
||||
## AtlasWindowTileSet.get_tiles()'s own "unarrived" shape) must be skipped,
|
||||
## not crash — the scan continues to the next tile / falls through to
|
||||
## MORPHOLOGY_ZONE_NO_DATA.
|
||||
func test_resolve_tile_mode_skips_unarrived_tiles() -> void:
|
||||
var unarrived := {"center": Vector2i(0, 0), "window": null}
|
||||
var zone: int = AtlasWindowWaterClip.resolve_morphology_zone(
|
||||
Vector2(0.0, 0.0), true, null, [unarrived], 0
|
||||
)
|
||||
assert_int(zone).is_equal(AtlasWindowWaterClip.MORPHOLOGY_ZONE_NO_DATA)
|
||||
|
||||
|
||||
func test_resolve_tile_mode_empty_tile_list_is_no_data() -> void:
|
||||
var zone: int = AtlasWindowWaterClip.resolve_morphology_zone(Vector2.ZERO, true, null, [], 0)
|
||||
assert_int(zone).is_equal(AtlasWindowWaterClip.MORPHOLOGY_ZONE_NO_DATA)
|
||||
|
||||
|
||||
## Live round 2 fix (coordinator's trace, T-1172): a tile whose CANONICAL
|
||||
## center is far from `held_center` (the seam-tile case — exactly Lendel's
|
||||
## own live repro, tile canonical center 12739 drawn at draw_col=-6400) must
|
||||
## have its CENTER wrapped toward `held_center_x` — mirroring
|
||||
## AtlasWindowOverlay._draw_tile_mosaic()'s own `draw_col =
|
||||
## nearest_wrap_image(center.x, held_center.x, cols)` EXACTLY — before
|
||||
## testing containment. The query `district` is assumed ALREADY expressed in
|
||||
## the held-center-wrapped frame (AtlasWindowNatureOverlay._district()'s own
|
||||
## contract) and is NOT separately re-wrapped.
|
||||
##
|
||||
## Original (pre-fix) test asserted the INVERSE — wrapping the query toward
|
||||
## the tile's raw canonical center — which was the actual bug: it happened
|
||||
## to land inside the tile's CANONICAL (unwrapped) span by coincidental mod
|
||||
## arithmetic, silently testing the WRONG real-world location whenever a
|
||||
## tile needed wrapping to appear on screen at all. Live capture evidence:
|
||||
## a river dot at district.x=-9569 sitting on the painter's WEST wrap-image
|
||||
## of a seam tile (canonical center 12739, draw_col=-6400) read a real but
|
||||
## wrong-location land cell under the old code, and only stopped doing so
|
||||
## once resolve_morphology_zone() wrapped the TILE's center instead.
|
||||
func test_resolve_tile_mode_wraps_the_tiles_own_center_toward_held_center() -> void:
|
||||
var cols := 100
|
||||
var morph := PackedByteArray()
|
||||
morph.resize(16)
|
||||
for i in range(16):
|
||||
morph[i] = MORPHOLOGY_OPEN_OCEAN
|
||||
# Tile's canonical center is column 98 (far east) — but its nearest
|
||||
# wrap-image to held_center=0 is column -2 (98 - 100), matching the
|
||||
# Lendel seam tile's own shape (canonical 12739 -> draw_col -6400).
|
||||
var tile := {"center": Vector2i(98, 0), "window": _mock_district_window(Vector2i(98, 0), 4, morph)}
|
||||
# Query at column -2.5 — inside the tile's WRAP-IMAGE span [-4, 0), the
|
||||
# real on-screen location, held_center-relative (the caller's own
|
||||
# _district() contract) — NOT inside the canonical span [96, 100).
|
||||
var zone: int = AtlasWindowWaterClip.resolve_morphology_zone(
|
||||
Vector2(-2.5, 0.0), true, null, [tile], cols, 0
|
||||
)
|
||||
assert_int(zone).override_failure_message(
|
||||
"the tile's CENTER must be wrapped toward held_center_x (mirroring the"
|
||||
+ " painter's draw_col computation) so a query already expressed in the"
|
||||
+ " held-center frame resolves against the tile's REAL on-screen wrap-image"
|
||||
).is_equal(MORPHOLOGY_OPEN_OCEAN)
|
||||
|
||||
|
||||
## The INVERSE position — a query at the tile's CANONICAL (unwrapped) span —
|
||||
## must NOT resolve against this tile once wrapping is applied, since that
|
||||
## span is no longer where the tile actually draws relative to held_center.
|
||||
## Pins that the fix doesn't just "also succeed at the old span" by accident.
|
||||
func test_resolve_tile_mode_does_not_match_the_tiles_stale_canonical_span() -> void:
|
||||
var cols := 100
|
||||
var morph := PackedByteArray()
|
||||
morph.resize(16)
|
||||
for i in range(16):
|
||||
morph[i] = MORPHOLOGY_OPEN_OCEAN
|
||||
var tile := {"center": Vector2i(98, 0), "window": _mock_district_window(Vector2i(98, 0), 4, morph)}
|
||||
# Query at column 97 — inside the tile's CANONICAL span [96,100) — but
|
||||
# that is NOT where this tile is drawn relative to held_center=0 (it's
|
||||
# drawn at the wrap-image [-4,0) instead), so this must NOT resolve.
|
||||
var zone: int = AtlasWindowWaterClip.resolve_morphology_zone(
|
||||
Vector2(97.0, 0.0), true, null, [tile], cols, 0
|
||||
)
|
||||
assert_int(zone).override_failure_message(
|
||||
"a query at the tile's stale CANONICAL span must not resolve against it"
|
||||
+ " once the tile is wrapped toward held_center — that span is not where"
|
||||
+ " the tile actually draws on screen"
|
||||
).is_equal(AtlasWindowWaterClip.MORPHOLOGY_ZONE_NO_DATA)
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# T-1172 round 2 (coordinator's "wire-accurate fixture" hardening ask —
|
||||
# cold-start batch discipline): a fixture derived from an ACTUAL live tile
|
||||
# response captured during the round-2 investigation, at the REAL wire scale
|
||||
# (n=6400, grid_side=64, Region granularity) — not a hand-shrunk 4x4 mock.
|
||||
# The round-2 bug (wrapping the query toward the tile instead of the tile
|
||||
# toward held_center) passed EVERY test against the small mocks above,
|
||||
## because those mocks never modeled a tile whose canonical center is FAR
|
||||
# from held_center — the exact condition the bug needed to manifest. This
|
||||
# fixture reproduces that condition at production scale, so a future
|
||||
# regression of the same SHAPE (stub-and-code silently agreeing on a wrong
|
||||
# convention) can't hide behind "the small tests still pass."
|
||||
# =============================================================================
|
||||
|
||||
|
||||
## Lendel's own seam tile from the live drive capture that closed T-1172
|
||||
## round 2 (client/tmp_drive_clip.gd, SR_LIVE=1 against the worktree release
|
||||
## server): canonical center (12739, -3200), TILE_N=6400 districts,
|
||||
## Region granularity (grid_side = round(6400/100) = 64). `cols=19139`
|
||||
## matches Lendel's real district_extent() circumference. The morphology
|
||||
## array is NOT the real 4096-byte payload (too large to hand-author) — only
|
||||
## the ONE cell index the live trace actually resolved for the
|
||||
## district=-9569.414 repro query (idx=1024, col=0/row=16 — the exact
|
||||
## INDEX_TRACE line from the live investigation) is given a real value;
|
||||
## every other cell is left at 0 (OpenOcean), which is irrelevant here since
|
||||
## this fixture exists to pin the WRAP resolution reaching the CORRECT
|
||||
## tile/cell pair, not to re-verify the index math itself (already covered
|
||||
## above and in test_atlas_window_geometry_nature.gd).
|
||||
static func _lendel_seam_tile_fixture() -> Dictionary:
|
||||
var morph := PackedByteArray()
|
||||
morph.resize(4096)
|
||||
morph[1024] = MORPHOLOGY_LAND # col=0, row=16 — the live-traced cell
|
||||
return {
|
||||
"center": Vector2i(12739, -3200),
|
||||
"window": {
|
||||
"center": [12739, -3200],
|
||||
"n": 6400,
|
||||
"granularity_v2": "Region",
|
||||
"morphology": morph,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
## The exact district position from the live capture that ORIGINALLY exposed
|
||||
## the round-2 bug (district.x=-9569.414 — a dot visibly sitting on the
|
||||
## painter's WEST wrap-image of the seam tile). held_center_x=0 (the
|
||||
## viewer's canonical orbital-frame origin, cols=19139 (Lendel's real
|
||||
## circumference in districts). Must resolve to the SAME land zone the live
|
||||
## painter trace independently confirmed for this exact query.
|
||||
##
|
||||
## Honest note (found DURING revert-verification, worth recording): for a
|
||||
## SINGLE tile in isolation, the old (query-wrapped-toward-tile) and new
|
||||
## (tile-wrapped-toward-held_center) formulas are mathematically GUARANTEED
|
||||
## to agree whenever local_x lands in-range for both — both reduce to
|
||||
## `query - tile_center (mod cols)`, and a valid `local_x` is unique in
|
||||
## `[0, n)`. This single-tile fixture therefore does NOT independently
|
||||
## distinguish old from new (confirmed: it still passes with the pre-fix
|
||||
## code) — it locks in the real wire-scale numbers as a realistic regression
|
||||
## fixture (shared index formula, tile shape, wrap arithmetic all exercised
|
||||
## together), not as the old-vs-new discriminator. The tests that DO reliably
|
||||
## catch the round-2 regression are
|
||||
## test_resolve_tile_mode_wraps_the_tiles_own_center_toward_held_center and
|
||||
## test_resolve_tile_mode_does_not_match_the_tiles_stale_canonical_span above
|
||||
## (confirmed: the latter fails by name against the reverted code) — the
|
||||
## real-world bug's actual mechanism was the MULTI-TILE SCAN ORDER matching
|
||||
## the WRONG tile's data before reaching the right one, not a single-tile
|
||||
## formula divergence; a true multi-tile live reproduction would need the
|
||||
## full 6-tile fixture, impractical to hand-author at full 4096-cell scale.
|
||||
func test_wire_accurate_lendel_seam_tile_resolves_correctly() -> void:
|
||||
var tile: Dictionary = _lendel_seam_tile_fixture()
|
||||
var zone: int = AtlasWindowWaterClip.resolve_morphology_zone(
|
||||
Vector2(-9569.414, -4784.793), true, null, [tile], 19139, 0
|
||||
)
|
||||
assert_int(zone).override_failure_message(
|
||||
"the live T-1172 round 2 repro position must resolve against the seam"
|
||||
+ " tile's WRAPPED (on-screen) image and read the real traced land zone"
|
||||
+ " — a regression here reproduces the ORIGINAL over-ocean-dots bug"
|
||||
).is_equal(MORPHOLOGY_LAND)
|
||||
|
||||
|
||||
## The SAME fixture, queried at a position that legitimately falls OUTSIDE
|
||||
## even the wrapped tile's span (nowhere near either wrap-image) — must fail
|
||||
## open (NO_DATA), not silently match by coincidental mod arithmetic (the
|
||||
## general shape of the original bug, pinned generically here in case a
|
||||
## future change reintroduces a different mod-arithmetic coincidence).
|
||||
func test_wire_accurate_lendel_seam_tile_out_of_range_query_is_no_data() -> void:
|
||||
var tile: Dictionary = _lendel_seam_tile_fixture()
|
||||
var zone: int = AtlasWindowWaterClip.resolve_morphology_zone(
|
||||
Vector2(500.0, 500.0), true, null, [tile], 19139, 0
|
||||
)
|
||||
assert_int(zone).is_equal(AtlasWindowWaterClip.MORPHOLOGY_ZONE_NO_DATA)
|
||||
@@ -39,6 +39,21 @@ const MORPHOLOGY_COLORS: Array = [
|
||||
Color(0.25, 0.45, 0.40, 0.55), # 16 Wetland
|
||||
]
|
||||
|
||||
## MorphologyZone discriminants that read as DRAWN WATER — T-1172 (the
|
||||
## river-skeleton waterline-clip fix): the raw heightmap sea level the
|
||||
## server's drainage extraction filters river cells against (drainage.rs)
|
||||
## and the DERIVED morphology verdict this client actually paints (which
|
||||
## includes the T-1162 coast-warp invention, and at Region rung aggregates
|
||||
## to 204.8 km cells) are two independently-computed waterlines that can
|
||||
## legitimately disagree — Tyre's ruling (client draw-time clip, no single
|
||||
## server waterline is well-defined) reads THESE two discriminants as "drawn
|
||||
## water" to suppress a river dot/mouth against. Matches D-239 SS6's own
|
||||
## ordering exactly (0=OpenOcean, 1=Lake) — the only two MorphologyZone
|
||||
## values that are actually open water; every other zone (TidalFlat onward)
|
||||
## is drawn land or a land/water transition zone, not water itself.
|
||||
const MORPHOLOGY_OPEN_OCEAN: int = 0
|
||||
const MORPHOLOGY_LAKE: int = 1
|
||||
|
||||
## Sub-biome -> marker color (Araminta's palette, D-226). Grouped pairs share
|
||||
## a color since they read the same on the map.
|
||||
const SUB_BIOME_COLORS: Dictionary = {
|
||||
@@ -169,6 +184,16 @@ static func morphology_color(zone: int) -> Color:
|
||||
return Color(0.5, 0.5, 0.5, 0.4)
|
||||
|
||||
|
||||
## T-1172: whether a MorphologyZone discriminant reads as drawn water —
|
||||
## see MORPHOLOGY_OPEN_OCEAN/MORPHOLOGY_LAKE's own doc for the two-waterline
|
||||
## rationale. An out-of-range zone (a palette/enum drift bug) is NOT water —
|
||||
## matches district_window_morphology_color()'s own "unrecognized -> loud
|
||||
## magenta, not silently treated as any known category" posture; an
|
||||
## unrecognized zone must never silently suppress a river dot.
|
||||
static func is_morphology_water(zone: int) -> bool:
|
||||
return zone == MORPHOLOGY_OPEN_OCEAN or zone == MORPHOLOGY_LAKE
|
||||
|
||||
|
||||
static func sub_biome_color(sub_biome: String) -> Color:
|
||||
return SUB_BIOME_COLORS.get(sub_biome, COLOR_SUB_BIOME_DEFAULT)
|
||||
|
||||
|
||||
@@ -922,3 +922,33 @@ static func attractors_visible_at_rung(granularity_v2: String) -> bool:
|
||||
## for every real caller and only guards a malformed test input.
|
||||
static func zoom_compensated_size(screen_space_size: float, view_zoom: float) -> float:
|
||||
return screen_space_size / maxf(view_zoom, 0.0001)
|
||||
|
||||
|
||||
## T-1172 round 2 (coordinator's "reconsider the split" ask): the SHARED
|
||||
## cell-index formula both AtlasWindowOverlay's terrain painter (which builds
|
||||
## the drawn `grid_side x grid_side` per-cell texture — `i = row * grid_side
|
||||
## + col`, `img.set_pixel(col, row, ...)`) and AtlasWindowWaterClip's clip
|
||||
## predicate (which must read the EXACT SAME cell for a given position, or
|
||||
## the clip silently disagrees with what's actually painted) both need. The
|
||||
## live trace that closed T-1172 round 2's investigation PROVED this formula
|
||||
## itself was never the bug (painter and clip independently computed the
|
||||
## identical col/row/idx for the same query throughout) — the actual bug was
|
||||
## in the WRAP resolution one layer up (resolve_morphology_zone()'s own doc)
|
||||
## — but factoring the index math into ONE shared function here, rather than
|
||||
## two independently-maintained copies (atlas_window_overlay.gd's inline
|
||||
## `row * grid_side + col` vs. the old duplicate in
|
||||
## atlas_window_water_clip.gd), removes the STRUCTURAL risk of a future
|
||||
## divergence in exactly the way the coordinator flagged as the general
|
||||
## danger class ("stub-and-code agreeing on the wrong convention" — here,
|
||||
## PAINTER-and-clip could drift the same way without a shared source).
|
||||
## `local_x`/`local_y` are DISTRICT-SPACE offsets from the window's own
|
||||
## top-left corner (`center - n/2`), in `[0, n)` — the SAME quantity both
|
||||
## call sites already compute before this function is reached.
|
||||
static func cell_index_for_local_offset(
|
||||
local_x: float, local_y: float, n: int, grid_side: int
|
||||
) -> Vector2i:
|
||||
if n <= 0 or grid_side <= 0:
|
||||
return Vector2i(-1, -1)
|
||||
var col: int = clampi(int(floor(local_x / float(n) * float(grid_side))), 0, grid_side - 1)
|
||||
var row: int = clampi(int(floor(local_y / float(n) * float(grid_side))), 0, grid_side - 1)
|
||||
return Vector2i(col, row)
|
||||
|
||||
@@ -61,6 +61,8 @@ extends Node2D
|
||||
const AtlasWindowGeometry := preload("res://ui/implant/apps/atlas/atlas_window_geometry.gd")
|
||||
const AtlasDescendGeometryRef := preload("res://ui/implant/apps/atlas/atlas_descend_geometry.gd")
|
||||
const AtlasOverlayColors := preload("res://ui/implant/apps/atlas/atlas_overlay_colors.gd")
|
||||
# T-1172: two-waterline clip — see that file's own header doc.
|
||||
const AtlasWindowWaterClip := preload("res://ui/implant/apps/atlas/atlas_window_water_clip.gd")
|
||||
|
||||
## Reused verbatim from the retired atlas_marker_overlay.gd (Araminta's
|
||||
## ruling: "reuse the retired palette exactly") — same values, same source of
|
||||
@@ -210,11 +212,19 @@ func _zs(screen_space_size: float, ctx: Dictionary) -> float:
|
||||
return AtlasWindowGeometry.zoom_compensated_size(screen_space_size, ctx["view_zoom"])
|
||||
|
||||
|
||||
## Pixel (row, col) -> canvas-local, wrap-resolved to whichever longitude
|
||||
## image is nearest the currently-held view — the SAME two-step
|
||||
## (map-then-nearest-wrap) the tile mosaic draw path uses, just for a single
|
||||
## point instead of a tile's four corners.
|
||||
func _pos(row: float, col: float, ctx: Dictionary) -> Vector2:
|
||||
## Pixel (row, col) -> fractional district position, wrap-resolved against
|
||||
## the currently-HELD view's own center (ctx["held_center"]) — the
|
||||
## representative wrap-image _pos()'s canvas conversion needs. T-1172: this
|
||||
## is also the value fed to the water-clip lookup (_is_drawn_water()) — NOT
|
||||
## a separately-computed position — so the clip test and the actual drawn
|
||||
## position can never disagree about which longitude wrap-image is meant.
|
||||
## Tile-mode's own per-tile wrap re-resolution (AtlasWindowWaterClip.
|
||||
## resolve_morphology_zone()) re-derives whichever wrap-image a SPECIFIC
|
||||
## tile needs internally; feeding it this held-center-wrapped value is a
|
||||
## safe, consistent starting representative either way (longitude is
|
||||
## periodic — any wrap-image of the same district resolves to the same
|
||||
## real-world position).
|
||||
func _district(row: float, col: float, ctx: Dictionary) -> Vector2:
|
||||
var world_m: Vector2 = AtlasWindowGeometry.layer1_pixel_to_world_m(
|
||||
row, col, ctx["grid_w"], ctx["grid_h"], ctx["radius_km"]
|
||||
)
|
||||
@@ -229,11 +239,57 @@ func _pos(row: float, col: float, ctx: Dictionary) -> Vector2:
|
||||
# integer rounding would otherwise discard — river dots are not
|
||||
# district-lattice-snapped (see world_m_to_district()'s own doc).
|
||||
district.x = wrapped_col + (district.x - roundi(district.x))
|
||||
return district
|
||||
|
||||
|
||||
## Pixel (row, col) -> canvas-local, wrap-resolved to whichever longitude
|
||||
## image is nearest the currently-held view — the SAME two-step
|
||||
## (map-then-nearest-wrap) the tile mosaic draw path uses, just for a single
|
||||
## point instead of a tile's four corners. Thin wrapper over _district() +
|
||||
## AtlasWindowGeometry.district_to_canvas_local() (T-1172 split: callers that
|
||||
## also need the water-clip test call _district() directly instead, so the
|
||||
## SAME resolved district feeds both the draw position and the clip check).
|
||||
func _pos(row: float, col: float, ctx: Dictionary) -> Vector2:
|
||||
return AtlasWindowGeometry.district_to_canvas_local(
|
||||
district, ctx["held_center"], ctx["held_n"], ctx["cell_px"]
|
||||
_district(row, col, ctx), ctx["held_center"], ctx["held_n"], ctx["cell_px"]
|
||||
)
|
||||
|
||||
|
||||
## T-1172: whether the composite cell covering `district` is drawn as water
|
||||
## (OpenOcean/Lake) — FAILS OPEN (returns false, "not water", i.e. draw the
|
||||
## dot) when no arrived composite data covers the position, per Tyre's rule
|
||||
## 5 ("the clip is a presentation refinement, never a data gate"). Resolves
|
||||
## through AtlasWindowWaterClip.resolve_morphology_zone(), which handles
|
||||
## BOTH the single-window rung path and Region tile mode internally — this
|
||||
## function never branches on viewer.is_tile_mode() itself, matching that
|
||||
## function's own "single dispatch point" doc. `ctx["held_center"].x` is
|
||||
## threaded through (live round 2, coordinator's trace) — tile-mode
|
||||
## resolution must wrap each tile's CENTER toward held_center EXACTLY like
|
||||
## AtlasWindowOverlay._draw_tile_mosaic()'s own `draw_col` computation, or
|
||||
## the clip silently tests the wrong wrap-image of a seam tile (see
|
||||
## resolve_morphology_zone()'s own doc for the live repro).
|
||||
func _is_drawn_water(district: Vector2, ctx: Dictionary) -> bool:
|
||||
var is_tile_mode: bool = viewer.is_tile_mode()
|
||||
var single_window: Variant = null if is_tile_mode else viewer.get_district_window()
|
||||
var tiles: Array = []
|
||||
if is_tile_mode:
|
||||
var tile_set = viewer.get_tile_set()
|
||||
if tile_set != null:
|
||||
tiles = tile_set.get_tiles()
|
||||
var held_center: Vector2i = ctx["held_center"]
|
||||
var zone: int = AtlasWindowWaterClip.resolve_morphology_zone(
|
||||
district, is_tile_mode, single_window, tiles, ctx["cols"], held_center.x
|
||||
)
|
||||
if zone == AtlasWindowWaterClip.MORPHOLOGY_ZONE_NO_DATA:
|
||||
return false
|
||||
return AtlasOverlayColors.is_morphology_water(zone)
|
||||
|
||||
|
||||
## T-1172 clip — retire when T-1170 course invention terminates courses at
|
||||
## the invented coast. River cells, confluences, and mouths are each dropped
|
||||
## (strict, no snap) when their resolved composite cell reads as drawn water
|
||||
## — see AtlasWindowWaterClip's own header doc for the two-waterline
|
||||
## rationale. Basins are explicitly OUT OF SCOPE (Tyre's rule 4) — untouched.
|
||||
func _draw_rivers(rn: Dictionary, ctx: Dictionary) -> void:
|
||||
var granularity_v2: String = ctx["granularity_v2"]
|
||||
var river_cells: Array = rn.get("river_cells", [])
|
||||
@@ -251,7 +307,13 @@ func _draw_rivers(rn: Dictionary, ctx: Dictionary) -> void:
|
||||
)
|
||||
if not AtlasWindowGeometry.river_class_visible_at_rung(cls, granularity_v2):
|
||||
continue
|
||||
var p: Vector2 = _pos(float(c[0]), float(c[1]), ctx)
|
||||
var district: Vector2 = _district(float(c[0]), float(c[1]), ctx)
|
||||
# T-1172 clip — retire when T-1170 course invention terminates at the invented coast.
|
||||
if _is_drawn_water(district, ctx):
|
||||
continue
|
||||
var p: Vector2 = AtlasWindowGeometry.district_to_canvas_local(
|
||||
district, ctx["held_center"], ctx["held_n"], ctx["cell_px"]
|
||||
)
|
||||
if is_region:
|
||||
var radius: float = AtlasWindowGeometry.RIVER_DOT_RADIUS_BY_CLASS_REGION.get(cls, 2.2)
|
||||
draw_circle(p, _zs(radius, ctx), COLOR_GEN_RIVER)
|
||||
@@ -269,14 +331,30 @@ func _draw_rivers(rn: Dictionary, ctx: Dictionary) -> void:
|
||||
if AtlasWindowGeometry.confluences_visible_at_rung(granularity_v2):
|
||||
for cf: Variant in rn.get("confluences", []):
|
||||
if cf is Array and cf.size() >= 2:
|
||||
var p: Vector2 = _pos(float(cf[0]), float(cf[1]), ctx)
|
||||
var district: Vector2 = _district(float(cf[0]), float(cf[1]), ctx)
|
||||
if _is_drawn_water(district, ctx):
|
||||
continue
|
||||
var p: Vector2 = AtlasWindowGeometry.district_to_canvas_local(
|
||||
district, ctx["held_center"], ctx["held_n"], ctx["cell_px"]
|
||||
)
|
||||
var radius: float = _zs(AtlasWindowGeometry.RIVER_CONFLUENCE_RADIUS_REGION, ctx)
|
||||
draw_circle(p, radius, COLOR_GEN_RIVER)
|
||||
|
||||
if AtlasWindowGeometry.mouths_visible_at_rung(granularity_v2):
|
||||
for m: Variant in rn.get("mouths", []):
|
||||
if m is Array and m.size() >= 2:
|
||||
_draw_mouth(_pos(float(m[0]), float(m[1]), ctx), ctx)
|
||||
var district: Vector2 = _district(float(m[0]), float(m[1]), ctx)
|
||||
# T-1172 rule 3: mouths are SUPPRESSED (not snapped, not
|
||||
# dimmed) when their cell reads as drawn water — a mouth is
|
||||
# the worst-case disagreement by construction (the last LAND
|
||||
# cell on the RAW coast; wherever the drawn coast is
|
||||
# displaced inland, the mouth renders offshore).
|
||||
if _is_drawn_water(district, ctx):
|
||||
continue
|
||||
var p: Vector2 = AtlasWindowGeometry.district_to_canvas_local(
|
||||
district, ctx["held_center"], ctx["held_n"], ctx["cell_px"]
|
||||
)
|
||||
_draw_mouth(p, ctx)
|
||||
|
||||
|
||||
## Double-ring sea-terminus marker — verbatim geometry from the retired
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
extends RefCounted
|
||||
|
||||
## T-1172 — the river-skeleton waterline-clip fix. Pure geometry, split into
|
||||
## its own file (not folded into atlas_window_geometry.gd, which is already
|
||||
## close to the gdlint max-file-lines cap): the river skeleton's own SOURCE
|
||||
## (server/src/atlas/drainage.rs) is filtered against the RAW heightmap sea
|
||||
## level, but the DRAWN ocean this client actually paints is the derived
|
||||
## MorphologyZone verdict (server/src/atlas/district_profile.rs) — which
|
||||
## post-T-1162 includes the coast-warp invention (the drawn coastline is
|
||||
## deterministically displaced from the heightmap coast) and, at Region
|
||||
## rung, aggregates to 204.8 km cells. These are two independently-computed
|
||||
## waterlines that can legitimately disagree; Tyre's ruling (T-1172): no
|
||||
## single server waterline is well-defined, so the fix is a CLIENT draw-time
|
||||
## clip against whichever composite cell is currently ON SCREEN at a given
|
||||
## river dot's position — strict drop, no snap (a dot that lands on drawn
|
||||
## water is simply not drawn; Region's 205 km cells may amputate a river's
|
||||
## final coastal dots, an accepted cost per the ruling). T-1172 clip —
|
||||
## retire when T-1170 course invention terminates courses at the invented
|
||||
## coast.
|
||||
##
|
||||
## const AtlasWindowWaterClip := preload("res://ui/implant/apps/atlas/atlas_window_water_clip.gd")
|
||||
|
||||
const AtlasWindowGeometryRef := preload("res://ui/implant/apps/atlas/atlas_window_geometry.gd")
|
||||
|
||||
## Sentinel returned by the lookups below when no arrived composite data
|
||||
## covers the queried position — either the position is outside every
|
||||
## held/tiled window's own extent, or the window/tile at that position
|
||||
## hasn't arrived yet. The caller (AtlasWindowNatureOverlay) must FAIL OPEN
|
||||
## on this sentinel (draw the dot) — Tyre's rule 5: the clip is a
|
||||
## presentation refinement, never a data gate. Chosen as -1 (not a legal
|
||||
## MorphologyZone discriminant, which is always >= 0) so it can never be
|
||||
## mistaken for a real "not water" zone.
|
||||
const MORPHOLOGY_ZONE_NO_DATA: int = -1
|
||||
|
||||
|
||||
## The derived per-cell grid side length (CELLS) for a window dict `w` — a
|
||||
## DELIBERATE duplicate of AtlasWindowOverlay.cell_grid_side_for_window(),
|
||||
## not a shared call, matching this codebase's own "each file owns its own
|
||||
## reading of a small pure lookup rather than force a dependency" precedent
|
||||
## (atlas_overlay_colors.gd's header doc states this explicitly for the
|
||||
## color-palette case; the SAME rationale applies here: atlas_window_overlay.gd
|
||||
## already depends on atlas_window_geometry.gd, so a dependency back from
|
||||
## there — or from this file, if it lived there — would risk a circular or
|
||||
## at least confusing import graph). Mirrors
|
||||
## server/src/atlas/layer_proxy.rs's `WindowGranularity::cell_grid_side`
|
||||
## exactly, matching the canonical function's own doc byte-for-byte in intent.
|
||||
static func cell_grid_side_for_window(w: Dictionary) -> int:
|
||||
var n: int = int(w.get("n", 0))
|
||||
var granularity_v2 := str(w.get("granularity_v2", "District"))
|
||||
match granularity_v2:
|
||||
"Quarter":
|
||||
return n * 4
|
||||
"Region":
|
||||
return maxi(roundi(float(n) / 100.0), 1)
|
||||
_:
|
||||
return n
|
||||
|
||||
|
||||
## Resolve a FRACTIONAL district position to the MorphologyZone discriminant
|
||||
## of the composite cell covering it, for a SINGLE window dict `w` (the
|
||||
## single-window rung path: District/Quarter, and each individual Region
|
||||
## tile in tile mode share this same per-window shape). Returns
|
||||
## MORPHOLOGY_ZONE_NO_DATA if `w` is null/malformed, has no morphology array,
|
||||
## or `district` falls outside `w`'s own `[center - n/2, center + n/2)`
|
||||
## extent (the SAME containment convention
|
||||
## AtlasWindowGeometry.district_to_canvas_local() uses, so a position judged
|
||||
## "inside" here is exactly the position that would draw as part of THIS
|
||||
## window's composite on screen — no separate containment rule to drift out
|
||||
## of sync with the actual paint).
|
||||
static func morphology_zone_in_window(district: Vector2, w: Variant) -> int:
|
||||
if not w is Dictionary:
|
||||
return MORPHOLOGY_ZONE_NO_DATA
|
||||
var window: Dictionary = w
|
||||
var center_raw: Variant = window.get("center", [0, 0])
|
||||
var center: Vector2i = (
|
||||
Vector2i(int(center_raw[0]), int(center_raw[1])) if center_raw is Array else Vector2i.ZERO
|
||||
)
|
||||
var n: int = int(window.get("n", 0))
|
||||
if n <= 0:
|
||||
return MORPHOLOGY_ZONE_NO_DATA
|
||||
var half: float = float(n) * 0.5
|
||||
var local_x: float = district.x - (float(center.x) - half)
|
||||
var local_y: float = district.y - (float(center.y) - half)
|
||||
if local_x < 0.0 or local_x >= float(n) or local_y < 0.0 or local_y >= float(n):
|
||||
return MORPHOLOGY_ZONE_NO_DATA
|
||||
var morphology: Variant = window.get("morphology")
|
||||
if not (morphology is PackedByteArray or morphology is Array):
|
||||
return MORPHOLOGY_ZONE_NO_DATA
|
||||
var grid_side: int = cell_grid_side_for_window(window)
|
||||
if grid_side <= 0:
|
||||
return MORPHOLOGY_ZONE_NO_DATA
|
||||
# T-1172 round 2: SHARED index formula with the terrain painter
|
||||
# (AtlasWindowGeometry.cell_index_for_local_offset() — see its own doc
|
||||
# for why this is now factored out instead of duplicated).
|
||||
var cell: Vector2i = AtlasWindowGeometryRef.cell_index_for_local_offset(
|
||||
local_x, local_y, n, grid_side
|
||||
)
|
||||
var idx: int = cell.y * grid_side + cell.x
|
||||
if idx < 0 or idx >= morphology.size():
|
||||
return MORPHOLOGY_ZONE_NO_DATA
|
||||
return int(morphology[idx])
|
||||
|
||||
|
||||
## Resolve a fractional district position to a MorphologyZone discriminant
|
||||
## across BOTH viewer modes — the single dispatch point
|
||||
## AtlasWindowNatureOverlay's clip predicate calls, so it never needs its own
|
||||
## is_tile_mode() branch. Single-window mode: one direct
|
||||
## morphology_zone_in_window() call against `single_window`. Tile mode:
|
||||
## linear scan of `tiles` (Array of {"center": Vector2i, "window": Variant},
|
||||
## AtlasWindowTileSet.get_tiles()'s own shape) for whichever tile's ON-SCREEN
|
||||
## extent contains the position — each tile's OWN echoed `window["n"]` is
|
||||
## used for the actual containment test (not TILE_N assumed), matching this
|
||||
## cluster's "the response is the source of truth for what it actually
|
||||
## contains" precedent, since a clamped/still-arriving tile's real extent
|
||||
## can differ from the nominal per-tile request size.
|
||||
##
|
||||
## **Live round 2 fix (coordinator's trace, T-1172):** the wrap resolution
|
||||
## MUST mirror AtlasWindowOverlay._draw_tile_mosaic()'s own
|
||||
## `draw_col = nearest_wrap_image(center.x, held_center.x, cols)` EXACTLY —
|
||||
## wrap the TILE'S OWN CENTER toward `held_center` (the viewer's currently-
|
||||
## displayed reference frame), then test the (already held-center-wrapped)
|
||||
## query `district` against that RESOLVED center. The original version did
|
||||
## the inverse — wrapped the QUERY toward the tile's raw CANONICAL center —
|
||||
## which is not the same operation and silently tested containment against
|
||||
## the WRONG wrap-image of the tile for any tile whose canonical center is
|
||||
## far from `held_center` (i.e. any tile that needs wrapping to appear
|
||||
## on-screen at all — confirmed live: a dot at district.x=-9569 visibly
|
||||
## sitting on the painter's WEST wrap-image of the seam tile
|
||||
## (canonical center 12739, drawn at draw_col=-6400) was tested by the old
|
||||
## code against that tile's EAST/canonical span `[9539, 15939)` instead —
|
||||
## landed inside it by coincidence (mod arithmetic), read a real but
|
||||
## WRONG-LOCATION land cell, and never clipped). `district.x` is assumed
|
||||
## ALREADY wrap-resolved near `held_center` by the caller (AtlasWindowNatureOverlay.
|
||||
## _district()'s own contract) — this function does not re-wrap it, only the
|
||||
## tile centers, exactly mirroring the painter's own asymmetry (the painter
|
||||
## never wrap-resolves the query either — canvas-local coordinates are
|
||||
## already in the held-center frame by construction).
|
||||
static func resolve_morphology_zone(
|
||||
district: Vector2, is_tile_mode: bool, single_window: Variant, tiles: Array, cols: int,
|
||||
held_center_x: int = 0
|
||||
) -> int:
|
||||
if not is_tile_mode:
|
||||
return morphology_zone_in_window(district, single_window)
|
||||
for tile: Dictionary in tiles:
|
||||
var window: Variant = tile.get("window")
|
||||
if not window is Dictionary:
|
||||
continue
|
||||
var tile_center: Vector2i = tile.get("center", Vector2i.ZERO)
|
||||
var draw_col: int = tile_center.x
|
||||
if cols > 0:
|
||||
draw_col = AtlasWindowGeometryRef.nearest_wrap_image(tile_center.x, held_center_x, cols)
|
||||
var effective_window: Dictionary = window
|
||||
if draw_col != tile_center.x:
|
||||
effective_window = (window as Dictionary).duplicate()
|
||||
effective_window["center"] = [draw_col, tile_center.y]
|
||||
var zone: int = morphology_zone_in_window(district, effective_window)
|
||||
if zone != MORPHOLOGY_ZONE_NO_DATA:
|
||||
return zone
|
||||
return MORPHOLOGY_ZONE_NO_DATA
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user