fix(ui): T-1172 round 2 — tile wrap resolution mirrored to the painter's own direction
Live round: the first clip removed only 133 pixels. Root cause (dossier- traced): resolve_morphology_zone wrapped the QUERY toward a tile's raw canonical center — the inverse of the painter's draw_col = nearest_wrap_image(tile_center, held_center) — so seam tiles tested containment against the wrong wrap-image and read real-but-wrong- location land cells. Fix mirrors the painter exactly: wrap the tile's own center toward held_center, test the (already held-wrapped) query against that. Index math itself was confirmed correct end-to-end and is now factored into shared AtlasWindowGeometry.cell_index_for_local_ offset() (full painter unification not applicable — the painter only iterates forward, never reverse-looks-up; documented). Post-fix: 1082 traced positions cross-checked against actual painted pixels, 0 real mismatches; regenerated captures show every dot/mouth on land. The one pre-existing test encoding the buggy direction as correct was replaced by positive+negative wrap-semantics tests (the negative one is the reliable revert discriminator) plus a wire-accurate seam-tile fixture with an honestly-documented proof limit (a single-tile fixture cannot distinguish the wrap directions; the multi-tile scan tests can). Also: two harness traps found and fixed in the lead's drive scratch — canvas_items stretch factor (root.size now matches the 1920x1080 base viewport) and mid-arrival snapshots (explicit is_fully_arrived wait; the round-1 faint result was partly a 4-of-6-tiles capture). 46/46 + 50/50 + five sibling suites regression-free; gdlint clean. Tickets: T-1172 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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)
|
||||
)
|
||||
|
||||
@@ -209,28 +209,160 @@ func test_resolve_tile_mode_empty_tile_list_is_no_data() -> void:
|
||||
assert_int(zone).is_equal(AtlasWindowWaterClip.MORPHOLOGY_ZONE_NO_DATA)
|
||||
|
||||
|
||||
## Wrap case: a tile whose CANONICAL center is near the antimeridian, queried
|
||||
## with a district position expressed in a DIFFERENT (unwrapped) longitude —
|
||||
## resolve_morphology_zone() must re-express the query against the tile's
|
||||
## own wrap-image before testing containment, the same discipline
|
||||
## AtlasWindowOverlay._draw_tile_mosaic() already uses for drawing.
|
||||
func test_resolve_tile_mode_wraps_the_query_to_the_tiles_own_image() -> void:
|
||||
## 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 2 (near the origin side of the wrap).
|
||||
var tile := {"center": Vector2i(2, 0), "window": _mock_district_window(Vector2i(2, 0), 4, morph)}
|
||||
# Query at column -98 — NOT canonical (canonicalizes to 2 under mod 100),
|
||||
# but expressed as the "west of origin" wrap-image the real district
|
||||
# position lives at (matching Lendel's own antimeridian repro shape from
|
||||
# the T-1156 nearest_wrap_image() tests).
|
||||
# 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(-98.0, 0.0), true, null, [tile], cols
|
||||
Vector2(-2.5, 0.0), true, null, [tile], cols, 0
|
||||
)
|
||||
assert_int(zone).override_failure_message(
|
||||
"a query expressed in a different (non-canonical) wrap-image of the same"
|
||||
+ " real position must still resolve against the tile whose canonical"
|
||||
+ " center it's periodic-equivalent to"
|
||||
"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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -262,7 +262,12 @@ func _pos(row: float, col: float, ctx: Dictionary) -> Vector2:
|
||||
## 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.
|
||||
## 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()
|
||||
@@ -271,8 +276,9 @@ func _is_drawn_water(district: Vector2, ctx: Dictionary) -> bool:
|
||||
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"]
|
||||
district, is_tile_mode, single_window, tiles, ctx["cols"], held_center.x
|
||||
)
|
||||
if zone == AtlasWindowWaterClip.MORPHOLOGY_ZONE_NO_DATA:
|
||||
return false
|
||||
|
||||
@@ -89,9 +89,13 @@ static func morphology_zone_in_window(district: Vector2, w: Variant) -> int:
|
||||
var grid_side: int = cell_grid_side_for_window(window)
|
||||
if grid_side <= 0:
|
||||
return MORPHOLOGY_ZONE_NO_DATA
|
||||
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)
|
||||
var idx: int = row * grid_side + col
|
||||
# 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])
|
||||
@@ -103,23 +107,37 @@ static func morphology_zone_in_window(district: Vector2, w: Variant) -> int:
|
||||
## 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
|
||||
## `[center - TILE_N/2, center + TILE_N/2)` 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. `district.x` is WRAP-RESOLVED against each tile's
|
||||
## own canonical center via AtlasWindowGeometry.nearest_wrap_image() before
|
||||
## the containment test — a tile's `center` is always canonical (wrapped
|
||||
## into `[0, cols)`), but the queried district position may be expressed in
|
||||
## a DIFFERENT wrap-image (e.g. a river dot near the antimeridian) — the
|
||||
## same "re-express before comparing" discipline
|
||||
## AtlasWindowOverlay._draw_tile_mosaic() and AtlasWindowNatureOverlay's own
|
||||
## _pos() already use. `cols <= 0` (no-radius body) is a safe no-op
|
||||
## passthrough (nearest_wrap_image()'s own contract).
|
||||
## 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
|
||||
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)
|
||||
@@ -128,13 +146,14 @@ static func resolve_morphology_zone(
|
||||
if not window is Dictionary:
|
||||
continue
|
||||
var tile_center: Vector2i = tile.get("center", Vector2i.ZERO)
|
||||
var wrapped_x: float = district.x
|
||||
var draw_col: int = tile_center.x
|
||||
if cols > 0:
|
||||
var wrapped_col: int = AtlasWindowGeometryRef.nearest_wrap_image(
|
||||
roundi(district.x), tile_center.x, cols
|
||||
)
|
||||
wrapped_x = float(wrapped_col) + (district.x - roundi(district.x))
|
||||
var zone: int = morphology_zone_in_window(Vector2(wrapped_x, district.y), window)
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user