feat(ui): T-1161 — per-rung composite filter policy (Region NEAREST, District/Quarter LINEAR)
Araminta's ruling (PR #192 follow-up): filter keyed on rung IDENTITY via the window's own echoed granularity_v2 — Region (incl. the orbital tile mosaic, whose tiles are all Region-rung requests) samples NEAREST because GPU bilinear at 204.8 km/cell reads as smoothing-over-absence; District/ Quarter keep LINEAR where cell density earns the blend. One shared helper (_filter_for_granularity_v2) at both draw call sites; unknown/missing wire tags fall back to LINEAR (never trusted into NEAREST). COMPOSITE_ SMOOTH survives as the independent compile-time pipeline axis — the two-axes split is documented in the file header. Washes/border fades untouched per the ruling. Focused suite 44/44; revert-verified (helper hardcoded LINEAR -> exactly the three Region-NEAREST tests fail). Tickets: T-1161 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -34,6 +34,13 @@ static func _mock_window(n: int = 2) -> Dictionary:
|
||||
class _ViewerStub:
|
||||
var window: Variant = null
|
||||
var active_overlay: String = ""
|
||||
# T-1161: the viewer's currently-HELD rung tag — added alongside the
|
||||
# per-rung filter tests below. Not read by AtlasWindowOverlay today (the
|
||||
# overlay trusts each window dict's OWN echoed granularity_v2, per
|
||||
# cell_grid_side_for_window()'s precedent) but a real AtlasWindowViewer
|
||||
# exposes get_held_granularity_v2() (T-1153), so the stub carries it too
|
||||
# for parity with the real duck-typed interface.
|
||||
var held_granularity_v2: String = "District"
|
||||
|
||||
func get_district_window() -> Variant:
|
||||
return window
|
||||
@@ -47,7 +54,17 @@ class _ViewerStub:
|
||||
func is_tile_mode() -> bool:
|
||||
return false
|
||||
|
||||
func get_held_granularity_v2() -> String:
|
||||
return held_granularity_v2
|
||||
|
||||
|
||||
## T-1161 reframe: COMPOSITE_SMOOTH is now Axis 1 of TWO independent axes
|
||||
## (see the file header doc) — the texture-vs-flat-rects PIPELINE choice.
|
||||
## This assertion survives unchanged: the composite is still a TEXTURE at
|
||||
## every rung. Axis 2 (which FILTER that texture samples with) is now a
|
||||
## per-rung runtime decision covered separately below by the
|
||||
## _filter_for_granularity_v2() tests — it is no longer bundled into this
|
||||
## compile-time const.
|
||||
func test_composite_smooth_defaults_true() -> void:
|
||||
assert_bool(AtlasWindowOverlay.COMPOSITE_SMOOTH).override_failure_message(
|
||||
"T-1145 item 3 ships the smoothed composite as the DEFAULT presentation"
|
||||
@@ -265,3 +282,118 @@ func test_rebuild_handles_a_rung_swap_from_district_to_region() -> void:
|
||||
assert_int(o._cached_texture.get_width()).override_failure_message(
|
||||
"a rung swap must rebuild at the NEW rung's derived cell-grid resolution"
|
||||
).is_equal(2) # region_window's cell_grid_side is also 2 here (200/100) — same size, different data
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# T-1161: _filter_for_granularity_v2() — the per-rung sampling filter policy
|
||||
# (Araminta's ruling: Region incl. the orbital tile mosaic -> NEAREST;
|
||||
# District/Quarter -> LINEAR; no hysteresis, no px-per-cell threshold, keyed
|
||||
# purely on rung IDENTITY).
|
||||
# =============================================================================
|
||||
|
||||
|
||||
## Region is the sparse rung the ruling targets — GPU bilinear blending at
|
||||
## 204.8 km/cell reads as smoothing-over-absence, so it samples NEAREST.
|
||||
func test_filter_for_granularity_v2_region_is_nearest() -> void:
|
||||
assert_int(AtlasWindowOverlay._filter_for_granularity_v2("Region")).override_failure_message(
|
||||
"Region must sample TEXTURE_FILTER_NEAREST — dense-enough rungs get LINEAR,"
|
||||
+ " Region is the sparse one the ruling targets"
|
||||
).is_equal(CanvasItem.TEXTURE_FILTER_NEAREST)
|
||||
|
||||
|
||||
## District is dense enough that the bilinear blend reads as texture, not as
|
||||
## papering over sparse data — LINEAR is earned.
|
||||
func test_filter_for_granularity_v2_district_is_linear() -> void:
|
||||
assert_int(AtlasWindowOverlay._filter_for_granularity_v2("District")).override_failure_message(
|
||||
"District must sample TEXTURE_FILTER_LINEAR"
|
||||
).is_equal(CanvasItem.TEXTURE_FILTER_LINEAR)
|
||||
|
||||
|
||||
## Quarter (4x MORE cells than District) is denser still — also LINEAR.
|
||||
func test_filter_for_granularity_v2_quarter_is_linear() -> void:
|
||||
assert_int(AtlasWindowOverlay._filter_for_granularity_v2("Quarter")).override_failure_message(
|
||||
"Quarter must sample TEXTURE_FILTER_LINEAR"
|
||||
).is_equal(CanvasItem.TEXTURE_FILTER_LINEAR)
|
||||
|
||||
|
||||
## An unrecognized/empty tag must NEVER be trusted into the crisp NEAREST
|
||||
## treatment — mirrors cell_grid_side_for_window()'s own "unknown -> District"
|
||||
## fallback posture, failing toward the already-shipped LINEAR look rather
|
||||
## than an unintended NEAREST for a wire shape this code doesn't recognize.
|
||||
func test_filter_for_granularity_v2_unknown_falls_back_to_linear() -> void:
|
||||
assert_int(AtlasWindowOverlay._filter_for_granularity_v2("")).override_failure_message(
|
||||
"an empty/unrecognized granularity_v2 tag must fall back to LINEAR, never NEAREST"
|
||||
).is_equal(CanvasItem.TEXTURE_FILTER_LINEAR)
|
||||
assert_int(AtlasWindowOverlay._filter_for_granularity_v2("SomeFutureRung")).override_failure_message(
|
||||
"an unrecognized granularity_v2 tag must fall back to LINEAR, never NEAREST"
|
||||
).is_equal(CanvasItem.TEXTURE_FILTER_LINEAR)
|
||||
|
||||
|
||||
## Integration-shaped: a Region-rung window dict, drawn through the real
|
||||
## _draw() entry point via the _ViewerStub duck-typed interface (same
|
||||
## end-to-end shape as test_draw_builds_a_texture_for_a_region_rung_window()
|
||||
## above), must drive the NODE's own texture_filter property to NEAREST —
|
||||
## not just the helper function in isolation.
|
||||
func test_draw_sets_node_texture_filter_to_nearest_for_region_window() -> void:
|
||||
var o: AtlasWindowOverlay = auto_free(AtlasWindowOverlay.new())
|
||||
var stub := _ViewerStub.new()
|
||||
stub.held_granularity_v2 = "Region"
|
||||
stub.window = {
|
||||
"center": [0, 0],
|
||||
"n": 200,
|
||||
"granularity_v2": "Region",
|
||||
"morphology": PackedByteArray([8, 14, 0, 1]),
|
||||
"elev_q": PackedByteArray([40, 90, 5, 60]),
|
||||
"temp_dc": [120, 95, -32768, 60],
|
||||
"moisture_q": PackedByteArray([50, 30, 90, 20]),
|
||||
"vegetation": PackedByteArray([2, 1, 6, 3]),
|
||||
"glaciation": PackedByteArray([0, 0, 1, 2]),
|
||||
}
|
||||
o.viewer = stub
|
||||
o._draw()
|
||||
assert_int(o.texture_filter).override_failure_message(
|
||||
"a Region-rung window must drive the node's texture_filter to NEAREST after a draw"
|
||||
).is_equal(CanvasItem.TEXTURE_FILTER_NEAREST)
|
||||
|
||||
|
||||
## The District-rung counterpart of the above — confirms the node's
|
||||
## texture_filter lands on LINEAR (not left over from a previous NEAREST
|
||||
## draw, and not defaulting to NEAREST) for the dense rung.
|
||||
func test_draw_sets_node_texture_filter_to_linear_for_district_window() -> void:
|
||||
var o: AtlasWindowOverlay = auto_free(AtlasWindowOverlay.new())
|
||||
var stub := _ViewerStub.new()
|
||||
stub.window = _mock_window() # District (the default/omitted tag)
|
||||
o.viewer = stub
|
||||
o._draw()
|
||||
assert_int(o.texture_filter).override_failure_message(
|
||||
"a District-rung window must drive the node's texture_filter to LINEAR after a draw"
|
||||
).is_equal(CanvasItem.TEXTURE_FILTER_LINEAR)
|
||||
|
||||
|
||||
## A rung SWAP (Region -> District, on the SAME node) must flip texture_filter
|
||||
## along with it — confirms the property is recomputed every draw, not
|
||||
## sticky from the first rung the node ever rendered.
|
||||
func test_draw_flips_node_texture_filter_on_a_rung_swap() -> void:
|
||||
var o: AtlasWindowOverlay = auto_free(AtlasWindowOverlay.new())
|
||||
var stub := _ViewerStub.new()
|
||||
stub.window = {
|
||||
"center": [0, 0],
|
||||
"n": 200,
|
||||
"granularity_v2": "Region",
|
||||
"morphology": PackedByteArray([8, 14, 0, 1]),
|
||||
"elev_q": PackedByteArray([40, 90, 5, 60]),
|
||||
"temp_dc": [120, 95, -32768, 60],
|
||||
"moisture_q": PackedByteArray([50, 30, 90, 20]),
|
||||
"vegetation": PackedByteArray([2, 1, 6, 3]),
|
||||
"glaciation": PackedByteArray([0, 0, 1, 2]),
|
||||
}
|
||||
o.viewer = stub
|
||||
o._draw()
|
||||
assert_int(o.texture_filter).is_equal(CanvasItem.TEXTURE_FILTER_NEAREST)
|
||||
|
||||
stub.window = _mock_window() # swap to District
|
||||
o._draw()
|
||||
assert_int(o.texture_filter).override_failure_message(
|
||||
"swapping to a District-rung window must flip texture_filter to LINEAR,"
|
||||
+ " not leave it stuck at the previous rung's NEAREST"
|
||||
).is_equal(CanvasItem.TEXTURE_FILTER_LINEAR)
|
||||
|
||||
Reference in New Issue
Block a user