## T-1182 tests: step_canvas_transport.gd — the D-255(a) six-rung stepped ## transport state machine (rung ladder, cursor-anchored step math, ## viewport-fit extent, world<->canvas-local projection). All pure ## functions, no scene tree needed. class_name TestStepCanvasTransport extends GdUnitTestSuite const StepCanvasTransport := preload("res://ui/implant/apps/atlas/step_canvas/step_canvas_transport.gd") ## GJ380c — the body this session eyeballed throughout. const BODY_R_KM: float = 6_238.4 # ============================================================================= # Rung ladder — index <-> name, scroll clamping # ============================================================================= func test_rung_at_index_zero_is_global() -> void: assert_str(StepCanvasTransport.rung_at_index(0)).is_equal(StepCanvasTransport.RUNG_GLOBAL) func test_rung_at_index_five_is_chunk_the_deepest() -> void: assert_str(StepCanvasTransport.rung_at_index(5)).is_equal(StepCanvasTransport.RUNG_CHUNK) func test_rung_at_index_clamps_out_of_range_indices() -> void: assert_str(StepCanvasTransport.rung_at_index(-3)).is_equal(StepCanvasTransport.RUNG_GLOBAL) assert_str(StepCanvasTransport.rung_at_index(99)).is_equal(StepCanvasTransport.RUNG_CHUNK) func test_index_for_rung_round_trips_every_ladder_entry() -> void: for i in range(StepCanvasTransport.RUNG_LADDER.size()): var rung: String = StepCanvasTransport.rung_at_index(i) assert_int(StepCanvasTransport.index_for_rung(rung)).is_equal(i) func test_index_for_rung_unrecognized_returns_negative_one() -> void: assert_int(StepCanvasTransport.index_for_rung("Sector")).is_equal(-1) func test_scroll_step_descends_one_notch_at_a_time() -> void: assert_int(StepCanvasTransport.scroll_step(0, 1)).is_equal(1) assert_int(StepCanvasTransport.scroll_step(2, 1)).is_equal(3) func test_scroll_step_ascends_one_notch_at_a_time() -> void: assert_int(StepCanvasTransport.scroll_step(3, -1)).is_equal(2) func test_scroll_step_clamps_at_the_deepest_rung() -> void: assert_int(StepCanvasTransport.scroll_step(5, 1)).is_equal(5) func test_scroll_step_clamps_at_the_global_opener() -> void: assert_int(StepCanvasTransport.scroll_step(0, -1)).is_equal(0) func test_scroll_step_zero_direction_is_a_no_op() -> void: assert_int(StepCanvasTransport.scroll_step(2, 0)).is_equal(2) # ============================================================================= # D-243 rung EXTENT — pinned against the same metre values scale.rs uses. # Post-inversion (D-255 amendment, pair session 2026-07-26) the D-243 constant # is the rung's CELL SIZE, not its gridunit spacing; spacing is derived below. # ============================================================================= func test_rung_extent_matches_d243_metre_values() -> void: assert_float(StepCanvasTransport.RUNG_EXTENT_M["Region"]).is_equal_approx(204_800.0, 0.01) assert_float(StepCanvasTransport.RUNG_EXTENT_M["District"]).is_equal_approx(2_048.0, 0.01) assert_float(StepCanvasTransport.RUNG_EXTENT_M["Quarter"]).is_equal_approx(512.0, 0.01) assert_float(StepCanvasTransport.RUNG_EXTENT_M["Block"]).is_equal_approx(128.0, 0.01) assert_float(StepCanvasTransport.RUNG_EXTENT_M["Chunk"]).is_equal_approx(64.0, 0.01) # Global is deliberately absent — the elastic seam has no constant extent. assert_bool(StepCanvasTransport.RUNG_EXTENT_M.has("Global")).is_false() ## The inversion's core contract: the SHORTER canvas axis spans exactly one ## cell of the rung's level, whatever the viewport shape. Must agree with the ## server's own StepCanvasRung::spacing_m() — both sides derive it from the ## same three inputs, so this test and its Rust twin pin one contract. func test_shorter_axis_spans_exactly_one_rung_cell() -> void: for rung in ["Region", "District", "Quarter", "Block", "Chunk"]: var cell_m: float = StepCanvasTransport.RUNG_EXTENT_M[rung] for extent in [Vector2i(960, 540), Vector2i(540, 960), Vector2i(700, 700)]: var spacing: float = StepCanvasTransport.spacing_for_rung(rung, extent, BODY_R_KM) var short: float = float(mini(extent.x, extent.y)) assert_float(spacing * short).override_failure_message( "%s at %s: short axis spans %f m, want %f" % [rung, extent, spacing * short, cell_m] ).is_equal_approx(cell_m, 0.001) ## Spacing follows the canvas, not the rung — halving the cell count over the ## same rung doubles the pitch (a clamped canvas covers the same ground more ## coarsely; it does not cover less ground). func test_spacing_scales_inversely_with_cell_count() -> void: var fine: float = StepCanvasTransport.spacing_for_rung( "District", Vector2i(960, 540), BODY_R_KM ) var coarse: float = StepCanvasTransport.spacing_for_rung( "District", Vector2i(480, 270), BODY_R_KM ) assert_float(coarse).is_equal_approx(fine * 2.0, 0.001) ## Global is the one rung whose spacing comes from the body: the full 2*PI*R ## circumference wraps the canvas WIDTH. This is D-243's elastic seam, and the ## only place a body radius enters the ladder at all. func test_global_spacing_is_circumference_over_width() -> void: var extent := Vector2i(960, 480) var spacing: float = StepCanvasTransport.spacing_for_rung("Global", extent, BODY_R_KM) var circumference_m: float = TAU * BODY_R_KM * 1000.0 assert_float(spacing * 960.0).is_equal_approx(circumference_m, 1.0) # Twice the body, twice the pitch at the same cell count. var double: float = StepCanvasTransport.spacing_for_rung("Global", extent, BODY_R_KM * 2.0) assert_float(double).is_equal_approx(spacing * 2.0, 0.001) ## A degenerate canvas must not divide by zero — an infinity here would poison ## every world-metre computation downstream. func test_zero_extent_does_not_divide_by_zero() -> void: for rung in ["Global", "Chunk"]: var spacing: float = StepCanvasTransport.spacing_for_rung(rung, Vector2i.ZERO, BODY_R_KM) assert_bool(is_finite(spacing)).override_failure_message( "%s produced %f" % [rung, spacing] ).is_true() # ============================================================================= # Display ratio — uniform 2x2 since the extent inversion, PRESENTATION only # (D-255(a): never touches a cache key or a wire request) # ============================================================================= ## The four deep rungs share ONE display ratio, whatever its current value — ## asserting the relationship rather than the literal, so tuning ## DISPLAY_RATIO_DEEP (pair session 2026-07-26: 1.0 -> 2.0, the cost/looks ## experiment) doesn't require editing a test that isn't about the number. ## The value itself is a presentation tunable (D-255(a)); what must hold is ## that the deep rungs agree with each other and stay a texel-exact integer. func test_display_ratio_deep_rungs_all_share_the_deep_ratio() -> void: var deep: float = StepCanvasTransport.DISPLAY_RATIO_DEEP for rung in ["District", "Quarter", "Block", "Chunk"]: assert_float(StepCanvasTransport.display_ratio_for_rung(rung)).is_equal_approx(deep, 0.001) assert_float(deep).override_failure_message( "the deep display ratio must be a whole number of screen px per gridunit —" + " a fractional ratio reintroduces the sub-pixel blur D-255 texel-exactness prevents" ).is_equal_approx(floorf(deep), 0.0001) assert_float(deep).is_greater(0.0) ## Every rung reads at the deep ratio since the extent inversion — Region ## because it is a real 262x466 km map rather than an orbital envelope, and ## Global because 5 px/gridunit asked for fewer cells than the heightmap ## already stores. Asserted as a relationship, not a literal, so the ratio ## stays a tunable. func test_every_rung_uses_the_deep_display_ratio() -> void: var deep: float = StepCanvasTransport.DISPLAY_RATIO_DEEP for rung in ["Global", "Region", "District", "Quarter", "Block", "Chunk"]: assert_float(StepCanvasTransport.display_ratio_for_rung(rung)).override_failure_message( "%s must read at the deep ratio" % rung ).is_equal_approx(deep, 0.001) ## Region LEFT the orbital set with the extent inversion — it now rides the ## full courses-aware derive, which is most of why the top of the ladder used ## to read flat (no rivers at all above District). func test_is_orbital_rung_true_only_for_global() -> void: assert_bool(StepCanvasTransport.is_orbital_rung("Global")).is_true() for rung in ["Region", "District", "Quarter", "Block", "Chunk"]: assert_bool(StepCanvasTransport.is_orbital_rung(rung)).override_failure_message( "%s must take the full derive" % rung ).is_false() # ============================================================================= # Viewport-fit extent — the client half of "viewport-sized canvas" # ============================================================================= ## A deep-rung request asks for viewport_px / DISPLAY_RATIO_DEEP gridunits — ## derived from the constant, not the literal, so the ratio stays tunable ## (see the deep-ratio test above). This IS the cost lever: doubling the ## ratio quarters the requested cell count. func test_viewport_fit_extent_at_deep_ratio_divides_by_the_deep_ratio() -> void: var deep: float = StepCanvasTransport.DISPLAY_RATIO_DEEP var viewport := Vector2(800.0, 600.0) var extent: Vector2i = StepCanvasTransport.viewport_fit_extent(viewport, "Chunk") var expected := Vector2i(int(ceil(viewport.x / deep)), int(ceil(viewport.y / deep))) assert_that(extent).is_equal(expected) func test_viewport_fit_extent_at_global_divides_by_the_display_ratio() -> void: var extent: Vector2i = StepCanvasTransport.viewport_fit_extent(Vector2(1000.0, 500.0), "Global") assert_that(extent).is_equal(Vector2i(500, 250)) func test_viewport_fit_extent_clamps_to_the_fixed_canvas_max_axis() -> void: var extent: Vector2i = StepCanvasTransport.viewport_fit_extent( Vector2(20_000.0, 20_000.0), "Chunk" ) assert_int(extent.x).is_equal(StepCanvasTransport.FIXED_CANVAS_MAX_AXIS) assert_int(extent.y).is_equal(StepCanvasTransport.FIXED_CANVAS_MAX_AXIS) func test_viewport_fit_extent_never_produces_a_zero_axis() -> void: var extent: Vector2i = StepCanvasTransport.viewport_fit_extent(Vector2(0.0, 0.0), "Chunk") assert_int(extent.x).is_greater_equal(1) assert_int(extent.y).is_greater_equal(1) # ============================================================================= # Gridunit snapping — cache-key stability for repeated "same spot" requests # ============================================================================= ## Post-inversion the snap lattice is the CANVAS pitch, not a per-rung ## constant. A 1x1-cell District canvas puts the whole 2,048 m cell in one ## gridunit, so this pins the same arithmetic the old test did. func test_snap_to_gridunit_snaps_to_the_canvas_pitch() -> void: var one_cell := Vector2i(1, 1) # pitch == the rung's whole cell: 2,048 m var snapped: Vector2i = StepCanvasTransport.snap_to_gridunit( Vector2(2100.0, -1000.0), "District", one_cell, BODY_R_KM ) assert_int(snapped.x).is_equal(2048) assert_int(snapped.y).is_equal(0) ## Sub-metre pitches must NOT collapse to the origin. The pre-inversion ## implementation multiplied by `int(spacing)`, which truncates to 0 once the ## pitch drops below 1 m — and at a real viewport Chunk's pitch is ~0.12 m, so ## every request centre would have snapped to (0,0) and the viewer would have ## silently panned to the equator on every step. Guards that regression. func test_snap_to_gridunit_survives_sub_metre_pitch() -> void: var extent := Vector2i(960, 540) # Chunk pitch here is ~0.119 m var snapped: Vector2i = StepCanvasTransport.snap_to_gridunit( Vector2(123_456.0, -7_890.0), "Chunk", extent, BODY_R_KM ) assert_int(snapped.x).is_equal(123_456) assert_int(snapped.y).is_equal(-7_890) func test_snap_to_gridunit_is_idempotent_once_already_on_grid() -> void: var one_cell := Vector2i(1, 1) var once: Vector2i = StepCanvasTransport.snap_to_gridunit( Vector2(4096.0, 6144.0), "District", one_cell, BODY_R_KM ) var world_again := Vector2(once.x, once.y) var twice: Vector2i = StepCanvasTransport.snap_to_gridunit( world_again, "District", one_cell, BODY_R_KM ) assert_that(once).is_equal(twice) # ============================================================================= # World <-> canvas-local projection — the shared transform both the terrain # and annotation layers agree on by construction # ============================================================================= func test_world_m_to_canvas_local_centers_the_world_center_on_the_canvas_center() -> void: var extent := Vector2i(64, 64) var rung := "District" var world_center := Vector2(10_000.0, 20_000.0) var local: Vector2 = StepCanvasTransport.world_m_to_canvas_local( world_center, world_center, rung, extent, BODY_R_KM ) var expected_center: Vector2 = StepCanvasTransport.canvas_footprint_px(rung, extent) * 0.5 assert_that(local).is_equal_approx(expected_center, Vector2(0.01, 0.01)) ## world_m_to_canvas_local() and canvas_local_to_world_m() must be exact ## inverses of one another — a round trip through both must recover the ## original world point (within float tolerance). This is the invariant the ## cursor-anchored scroll step depends on: whatever point the cursor reads ## as "under it" before a scroll must be the SAME point after re-deriving ## from the new step's own frame. func test_world_to_local_and_back_round_trips() -> void: var extent := Vector2i(128, 96) var rung := "Quarter" var world_center := Vector2(50_000.0, -30_000.0) var original_world := Vector2(51_200.0, -29_500.0) var local: Vector2 = StepCanvasTransport.world_m_to_canvas_local( original_world, world_center, rung, extent, BODY_R_KM ) var recovered_world: Vector2 = StepCanvasTransport.canvas_local_to_world_m( local, world_center, rung, extent, BODY_R_KM ) assert_that(recovered_world).is_equal_approx(original_world, Vector2(0.5, 0.5)) func test_canvas_footprint_px_is_extent_times_display_ratio() -> void: var footprint: Vector2 = StepCanvasTransport.canvas_footprint_px("Global", Vector2i(100, 50)) assert_that(footprint).is_equal(Vector2(200.0, 100.0)) # 2x2 deep ratio ## Half-extent is now half the rung's own CELL on the short axis, whatever ## the cell count — that is the inversion restated as an invariant. func test_half_extent_m_is_half_the_rung_cell() -> void: for extent in [Vector2i(64, 64), Vector2i(960, 540), Vector2i(7, 3)]: var half: float = StepCanvasTransport.half_extent_m("District", extent, BODY_R_KM) assert_float(half).is_equal_approx(2048.0 * 0.5, 0.01) # ============================================================================= # Rung liveness — replaces the T-1189 extent cap, which the D-255 extent # inversion superseded (pair session 2026-07-26). A canvas can no longer # over-request a body: its cell count is viewport-driven and its GROUND extent # is the rung's own cell size. The residual question is whether a rung's cell # is bigger than the whole body, which is answered by OMITTING the rung rather # than by serving a squashed canvas. # ============================================================================= func test_cap_extent_to_body_is_now_a_no_op() -> void: var extent := Vector2i(384, 216) assert_that(StepCanvasTransport.cap_extent_to_body(extent, "Region", Vector2i(177, 88))).is_equal( extent ) ## All six rungs are live on every populated body in systems.db — the smallest ## is a 734 km-radius moon whose 4,611 km circumference swallows a Region cell ## seventeen times over. Pinned so a future rung resize that would silently ## kill a rung on inhabited worlds fails here first. func test_every_rung_is_live_on_the_smallest_populated_body() -> void: for rung in ["Global", "Region", "District", "Quarter", "Block", "Chunk"]: assert_bool(StepCanvasTransport.is_rung_live_on_body(rung, 733.9)).override_failure_message( "%s must stay live on the smallest populated body" % rung ).is_true() ## ...but a rung whose cell exceeds the body is not a step down the ladder at ## all — it would zoom OUT. The only two such bodies in systems.db are Phobos ## (11 km) and Deimos (6 km), and Sol is out of generator bounds entirely, so ## this guard never fires on in-scope content. Kept because it is free and the ## failure it prevents is silent. func test_region_is_not_live_on_a_sub_region_body() -> void: assert_bool(StepCanvasTransport.is_rung_live_on_body("Region", 6.0)).is_false() assert_bool(StepCanvasTransport.is_rung_live_on_body("District", 6.0)).is_true() ## An absent radius (an asteroid belt is not a sphere and has no ## equirectangular surface at all) must not silently collapse the ladder — ## the caller has a bigger problem than liveness and should see it. func test_liveness_is_permissive_without_a_radius() -> void: assert_bool(StepCanvasTransport.is_rung_live_on_body("Region", 0.0)).is_true() # ============================================================================= # T-1189/T-1192: shared letterbox/centering mechanism # ============================================================================= func test_center_offset_centers_a_smaller_canvas_in_a_larger_viewport() -> void: var offset: Vector2 = StepCanvasTransport.center_offset( Vector2(800.0, 400.0), Vector2(1920.0, 1080.0) ) assert_that(offset).is_equal(Vector2((1920.0 - 800.0) * 0.5, (1080.0 - 400.0) * 0.5)) func test_center_offset_is_zero_when_canvas_exactly_fills_the_viewport() -> void: var offset: Vector2 = StepCanvasTransport.center_offset( Vector2(1920.0, 1080.0), Vector2(1920.0, 1080.0) ) assert_that(offset).is_equal(Vector2.ZERO) func test_center_offset_goes_negative_when_the_canvas_overflows_the_viewport() -> void: # A canvas bigger than the viewport on an axis crops rather than shrinks # (matches every fixed rung's own "canvas can exceed the viewport" # precedent) — a negative offset on that axis is the correct, honest # result, not clamped to zero. var offset: Vector2 = StepCanvasTransport.center_offset( Vector2(2000.0, 400.0), Vector2(1920.0, 1080.0) ) assert_float(offset.x).is_less(0.0) assert_float(offset.y).is_greater(0.0) ## D-255 texel-exactness: an integer-px/gridunit canvas (fit_scale_ratio()) ## can still land on an ODD-vs-viewport remainder that halves to a .5px ## boundary — GJ1c's own 1593x792 footprint in a 1628x1080 available area ## ((1628-1593)*0.5 = 17.5) is the real case this guards. The offset must be ## FLOORED to a whole pixel, never left fractional (a fractional offset ## would blur the texel grid this whole mechanism exists to keep crisp). func test_center_offset_floors_a_half_pixel_remainder_to_a_whole_pixel() -> void: var offset: Vector2 = StepCanvasTransport.center_offset( Vector2(1593.0, 792.0), Vector2(1628.0, 1080.0) ) assert_float(offset.x).is_equal_approx(17.0, 0.001) assert_float(offset.y).is_equal_approx(144.0, 0.001) # ============================================================================= # T-1192: Global integer PIXELS-PER-GRIDUNIT fit — D-255 amendment # 2026-07-25 (rung-0's display ratio is viewport-fitted per body to an # INTEGER px/gridunit ratio; the fractional-fit branch a prior round of this # ticket carried was a mis-citation of D-255 — the record's actual mandate # is unqualified texel-exact — and independently a real bug (Hoshe): a # canvas exceeding the viewport on one axis could fit_scale() down to # sub-1x, violating the "never below native resolution" invariant. Fixed at # the root by fitting the INTEGER ratio, not a fraction of the whole # footprint — this lattice is fine-grained (per gridunit, not per 5px-base # footprint step), so the coverage-threshold escape hatch this section used # to need does not come up: the achievable ratios are close enough together # that the largest one that fits is always a good use of the frame. # ============================================================================= func test_fit_scale_ratio_picks_the_largest_px_per_gridunit_that_fits_both_axes() -> void: # GJ1c reference case (T-1183 eyeball): 177x88 gridunits against a full # 1920x1080 viewport (no legend reservation) — floor(1920/177)=10, # floor(1080/88)=12, the tighter axis (x) wins. var ratio: int = StepCanvasTransport.fit_scale_ratio( Vector2(177.0, 88.0), Vector2(1920.0, 1080.0) ) assert_int(ratio).is_equal(10) ## The GJ1c reference case AFTER the legend column is reserved (T-1192, the ## StepCanvasViewer end-to-end scenario): available area shrinks to ## 1628x1080 — floor(1628/177)=9, floor(1080/88)=12 — the lead's own cited ## reference number for this exact case. func test_fit_scale_ratio_matches_the_gj1c_legend_reserved_reference_case() -> void: var ratio: int = StepCanvasTransport.fit_scale_ratio( Vector2(177.0, 88.0), Vector2(1628.0, 1080.0) ) assert_int(ratio).is_equal(9) ## GJ1c at 4K, legend column reserved (3840 - 292 = 3548 available) — the ## lead's own cited reference number for a large viewport. func test_fit_scale_ratio_matches_the_gj1c_4k_reference_case() -> void: var ratio: int = StepCanvasTransport.fit_scale_ratio( Vector2(177.0, 88.0), Vector2(3548.0, 2160.0) ) assert_int(ratio).is_equal(20) ## Hoshe's repro, now a regression test: a canvas whose BASE footprint ## (885x440 px at the old 5x5-multiple framing) exceeds a narrow 348px-wide ## available viewport used to make fit_scale() return 0.39x — a sub-1x ## downscale violating "never below native resolution". The integer ## px/gridunit ratio floors at 1 instead: draws at native (1 px/gridunit) ## and crops/pans, exactly like every other fixed rung's own ## exceeds-the-viewport precedent. func test_fit_scale_ratio_floors_at_one_when_gridunits_exceed_a_narrow_viewport() -> void: var ratio: int = StepCanvasTransport.fit_scale_ratio( Vector2(177.0, 88.0), Vector2(348.0, 1080.0) ) assert_int(ratio).is_equal(1) ## A small moon's Global canvas (few gridunits) in a large viewport still ## only wins as large an integer ratio as fits — no special-casing for a ## small canvas, same formula, much larger achievable ratio. func test_fit_scale_ratio_a_tiny_moon_canvas_wins_a_large_integer_ratio() -> void: var ratio: int = StepCanvasTransport.fit_scale_ratio( Vector2(20.0, 20.0), Vector2(1920.0, 1080.0) ) assert_int(ratio).is_equal(54) ## Exact-fit boundary: the viewport is PRECISELY `extent * 3` on both axes — ## the ratio must land exactly on 3, not overshoot to 4 (floor(exact) must ## not round up) and not undershoot to 2 (an exact multiple is a legal fit, ## not treated as "just barely doesn't fit"). func test_fit_scale_ratio_exact_multiple_boundary_lands_on_the_multiple() -> void: var ratio: int = StepCanvasTransport.fit_scale_ratio( Vector2(100.0, 50.0), Vector2(300.0, 150.0) ) assert_int(ratio).is_equal(3) ## One pixel short of the exact multiple must drop to the NEXT integer down ## — confirms the boundary isn't fuzzy/off-by-one in the other direction. func test_fit_scale_ratio_one_pixel_short_of_the_multiple_drops_a_step() -> void: var ratio: int = StepCanvasTransport.fit_scale_ratio( Vector2(100.0, 50.0), Vector2(299.0, 150.0) ) assert_int(ratio).is_equal(2) func test_fit_scale_ratio_is_bounded_by_the_tighter_axis() -> void: # Wide-but-short viewport: x could fit 10x, y only fits 1x — the smaller # wins (never overflow either axis). var ratio: int = StepCanvasTransport.fit_scale_ratio( Vector2(100.0, 100.0), Vector2(1000.0, 150.0) ) assert_int(ratio).is_equal(1) func test_fit_scale_ratio_handles_a_zero_extent_axis_without_dividing_by_zero() -> void: var ratio: int = StepCanvasTransport.fit_scale_ratio(Vector2.ZERO, Vector2(800.0, 600.0)) assert_int(ratio).is_equal(1) ## fit_scale_from_ratio() converts the INTEGER px/gridunit ratio into the ## `_canvas.scale` multiplier applied on top of a texture already rendered ## at the rung's own base display ratio (5x5 for Global) — this multiplier ## itself may be a non-integer float (9/5 = 1.8), and that is CORRECT: ## texel-exactness is about the final ratio being a whole number, not the ## Node2D scale field. func test_fit_scale_from_ratio_divides_by_the_base_display_ratio() -> void: var scale: float = StepCanvasTransport.fit_scale_from_ratio(9, 5.0) assert_float(scale).is_equal_approx(1.8, 0.001) func test_fit_scale_from_ratio_at_the_gj1c_4k_reference_case() -> void: var scale: float = StepCanvasTransport.fit_scale_from_ratio(20, 5.0) assert_float(scale).is_equal_approx(4.0, 0.001) func test_fit_scale_from_ratio_handles_a_zero_base_ratio_without_dividing_by_zero() -> void: var scale: float = StepCanvasTransport.fit_scale_from_ratio(9, 0.0) assert_float(scale).is_greater(0.0) # ============================================================================= # T-1192: shared legend-column reservation constant # ============================================================================= ## StepCanvasTransport is the CANONICAL source for this width (T-1192 ## review fix) — step_canvas_legend.gd's own RESERVED_COLUMN_PX is now a ## direct read of THIS constant, not an independently-typed literal, so ## there is no separate cross-pin test needed here; this just pins the ## canonical value itself (260 panel width + 16*2 margin = 292). func test_legend_column_px_is_the_panel_width_plus_margin_on_both_sides() -> void: assert_float(StepCanvasTransport.LEGEND_COLUMN_PX).is_equal_approx(292.0, 0.01) ## Global must FILL its drawable area on at least one axis, at every window ## shape (Jeroen, 2026-07-27: "it looks unacceptable if it does not fill the ## screen across at least one axis"). Exactly one axis can fill — the canvas is ## equirectangular 2:1 and the aspect cannot follow the viewport — so the other ## letterboxes, and which one binds depends on whether the drawable area is ## wider or narrower than 2:1. func test_global_fill_extent_fills_one_axis_at_every_window_shape() -> void: var ratio: float = StepCanvasTransport.DISPLAY_RATIO_DEEP for drawable in [ Vector2(1628.0, 1080.0), # 1920 window minus legend — wider than 2:1? no Vector2(2268.0, 1080.0), # 2560 window — wider than 2:1, height binds Vector2(2268.0, 1440.0), Vector2(3148.0, 1440.0), Vector2(988.0, 720.0), ]: var cells: Vector2i = StepCanvasTransport.global_fill_extent(drawable) assert_int(cells.x).override_failure_message( "%s: canvas must stay 2:1, got %s" % [drawable, cells] ).is_equal(cells.y * 2) var drawn := Vector2(float(cells.x) * ratio, float(cells.y) * ratio) assert_bool(drawn.x <= drawable.x and drawn.y <= drawable.y).override_failure_message( "%s: drawn %s overflows the drawable area" % [drawable, drawn] ).is_true() # The binding axis must be within one ratio-step of exact — i.e. there # is no room left for another whole gridunit on that axis. var slack_x: float = drawable.x - drawn.x var slack_y: float = drawable.y - drawn.y assert_bool(slack_x < ratio * 2.0 or slack_y < ratio).override_failure_message( "%s: fills NEITHER axis — drawn %s leaves %s slack" % [drawable, drawn, Vector2(slack_x, slack_y)] ).is_true() ## A degenerate drawable area must still yield a legal 2:1 canvas rather than ## a zero-row one, which would divide by zero downstream in spacing_for_rung(). func test_global_fill_extent_floors_at_one_row() -> void: var cells: Vector2i = StepCanvasTransport.global_fill_extent(Vector2(1.0, 1.0)) assert_int(cells.y).is_greater_equal(1) assert_int(cells.x).is_equal(cells.y * 2)