The fractional fit branch is deleted, resolving both review findings at the root: tyre showed its comments cited D-255 for an exception the record does not contain (the language came from the lead's ticket text, not governance), and hoshe showed it returned sub-1x for a canvas exceeding the viewport on one axis. Replacement: fit_scale_ratio() chooses the largest integer pixels-per-gridunit R fitting both legend-reserved axes, floored at 1 (over-viewport draws native and crops like every fixed rung) — the fine-grained integer lattice (GJ1c 1080p -> 9px/gu = 1593x792, ~98% width; 4K -> 20) that makes the fractional hatch unnecessary. center_offset() floors to whole pixels (half-pixel centering would blur the texel grid). Doc comments cite the real sanction (D-255 amendment 2026-07-25, this branch). Legend column constant is now canonical in transport, read directly by the legend (was an independently-typed literal); its test asserts real geometry. New regression test proves _global_body_extent clears on body switch and never caps another body's requests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
434 lines
19 KiB
GDScript
434 lines
19 KiB
GDScript
## 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")
|
|
|
|
|
|
# =============================================================================
|
|
# 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 gridunit spacing — pinned against the same metre values scale.rs uses
|
|
# =============================================================================
|
|
|
|
|
|
func test_spacing_for_rung_matches_d243_metre_values() -> void:
|
|
assert_float(StepCanvasTransport.spacing_for_rung("Global")).is_equal_approx(204_800.0, 0.01)
|
|
assert_float(StepCanvasTransport.spacing_for_rung("Region")).is_equal_approx(204_800.0, 0.01)
|
|
assert_float(StepCanvasTransport.spacing_for_rung("District")).is_equal_approx(2_048.0, 0.01)
|
|
assert_float(StepCanvasTransport.spacing_for_rung("Quarter")).is_equal_approx(512.0, 0.01)
|
|
assert_float(StepCanvasTransport.spacing_for_rung("Block")).is_equal_approx(128.0, 0.01)
|
|
assert_float(StepCanvasTransport.spacing_for_rung("Chunk")).is_equal_approx(64.0, 0.01)
|
|
|
|
|
|
# =============================================================================
|
|
# Display ratio — deep/mid 1x1, shallow ~5x5, PRESENTATION only (D-255(a))
|
|
# =============================================================================
|
|
|
|
|
|
func test_display_ratio_deep_rungs_are_one_to_one() -> void:
|
|
for rung in ["District", "Quarter", "Block", "Chunk"]:
|
|
assert_float(StepCanvasTransport.display_ratio_for_rung(rung)).is_equal_approx(1.0, 0.001)
|
|
|
|
|
|
func test_display_ratio_shallow_rungs_use_the_five_x_five_fallback() -> void:
|
|
for rung in ["Global", "Region"]:
|
|
assert_float(StepCanvasTransport.display_ratio_for_rung(rung)).is_equal_approx(5.0, 0.001)
|
|
|
|
|
|
func test_is_orbital_rung_true_only_for_global_and_region() -> void:
|
|
assert_bool(StepCanvasTransport.is_orbital_rung("Global")).is_true()
|
|
assert_bool(StepCanvasTransport.is_orbital_rung("Region")).is_true()
|
|
assert_bool(StepCanvasTransport.is_orbital_rung("District")).is_false()
|
|
assert_bool(StepCanvasTransport.is_orbital_rung("Chunk")).is_false()
|
|
|
|
|
|
# =============================================================================
|
|
# Viewport-fit extent — the client half of "viewport-sized canvas"
|
|
# =============================================================================
|
|
|
|
|
|
func test_viewport_fit_extent_at_deep_ratio_matches_viewport_pixels() -> void:
|
|
# 1x1 ratio -> extent in gridunits == viewport px, 1:1.
|
|
var extent: Vector2i = StepCanvasTransport.viewport_fit_extent(Vector2(800.0, 600.0), "Chunk")
|
|
assert_that(extent).is_equal(Vector2i(800, 600))
|
|
|
|
|
|
func test_viewport_fit_extent_at_shallow_ratio_divides_by_the_display_ratio() -> void:
|
|
var extent: Vector2i = StepCanvasTransport.viewport_fit_extent(Vector2(1000.0, 500.0), "Region")
|
|
assert_that(extent).is_equal(Vector2i(200, 100))
|
|
|
|
|
|
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
|
|
# =============================================================================
|
|
|
|
|
|
func test_snap_to_gridunit_snaps_to_the_rungs_own_spacing() -> void:
|
|
var snapped: Vector2i = StepCanvasTransport.snap_to_gridunit(Vector2(2100.0, -1000.0), "District")
|
|
# District spacing = 2048 m: 2100 rounds to 1*2048=2048, -1000 rounds to 0.
|
|
assert_int(snapped.x).is_equal(2048)
|
|
assert_int(snapped.y).is_equal(0)
|
|
|
|
|
|
func test_snap_to_gridunit_is_idempotent_once_already_on_grid() -> void:
|
|
var once: Vector2i = StepCanvasTransport.snap_to_gridunit(Vector2(4096.0, 6144.0), "District")
|
|
var world_again := Vector2(once.x, once.y)
|
|
var twice: Vector2i = StepCanvasTransport.snap_to_gridunit(world_again, "District")
|
|
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
|
|
)
|
|
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
|
|
)
|
|
var recovered_world: Vector2 = StepCanvasTransport.canvas_local_to_world_m(
|
|
local, world_center, rung, extent
|
|
)
|
|
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("Region", Vector2i(100, 50))
|
|
assert_that(footprint).is_equal(Vector2(500.0, 250.0)) # 5x5 shallow ratio
|
|
|
|
|
|
func test_half_extent_m_is_half_the_cell_count_times_spacing() -> void:
|
|
var half: float = StepCanvasTransport.half_extent_m("District", 64)
|
|
assert_float(half).is_equal_approx(64.0 * 0.5 * 2048.0, 0.01)
|
|
|
|
|
|
# =============================================================================
|
|
# T-1189: extent cap to the body's own region grid — the sideways-repeat /
|
|
# pole-smear fix. The Global echo IS the cap (its canvas already equals the
|
|
# body's region grid, D-255(a): "the Global canvas IS the whole body at
|
|
# region spacing"), so no unit conversion is needed — Region shares Global's
|
|
# gridunit spacing exactly.
|
|
# =============================================================================
|
|
|
|
|
|
func test_cap_extent_to_body_clamps_region_to_the_global_echo() -> void:
|
|
# T-1183 eyeball: 384x216 requested at Region on GJ1c, whose Global echo
|
|
# is 177x88 — the requested extent overruns the body on both axes.
|
|
var capped: Vector2i = StepCanvasTransport.cap_extent_to_body(
|
|
Vector2i(384, 216), "Region", Vector2i(177, 88)
|
|
)
|
|
assert_that(capped).is_equal(Vector2i(177, 88))
|
|
|
|
|
|
func test_cap_extent_to_body_is_a_no_op_when_already_inside_the_grid() -> void:
|
|
var capped: Vector2i = StepCanvasTransport.cap_extent_to_body(
|
|
Vector2i(100, 40), "Region", Vector2i(177, 88)
|
|
)
|
|
assert_that(capped).is_equal(Vector2i(100, 40))
|
|
|
|
|
|
## Shape-generic per the ticket: the guard compares SPACING, not rung name,
|
|
## so it caps ANY rung sharing Global's spacing, not just a hardcoded
|
|
## "Region" check. District's spacing (2048 m) differs from Global's
|
|
## (204,800 m), so it must NEVER be capped by the body-grid cell count —
|
|
## capping cell counts across mismatched spacings would be a unit error.
|
|
func test_cap_extent_to_body_leaves_finer_rungs_uncapped() -> void:
|
|
var capped: Vector2i = StepCanvasTransport.cap_extent_to_body(
|
|
Vector2i(3000, 3000), "District", Vector2i(177, 88)
|
|
)
|
|
assert_that(capped).is_equal(Vector2i(3000, 3000))
|
|
|
|
|
|
## Cold-start fallback (T-1189, StepCanvasViewer's own documented choice):
|
|
## before any Global response has arrived, `_global_body_extent` is ZERO —
|
|
## cap_extent_to_body() must leave the request UNCAPPED on a non-positive
|
|
## axis (server clamps independently) rather than clamping to zero cells.
|
|
func test_cap_extent_to_body_uncapped_when_global_echo_not_yet_available() -> void:
|
|
var capped: Vector2i = StepCanvasTransport.cap_extent_to_body(
|
|
Vector2i(384, 216), "Region", Vector2i.ZERO
|
|
)
|
|
assert_that(capped).is_equal(Vector2i(384, 216))
|
|
|
|
|
|
## A mixed case: one axis of the Global echo has arrived-and-is-real, the
|
|
## other is still ZERO (shouldn't happen in practice since both arrive
|
|
## together, but the function must handle each axis independently rather
|
|
## than assuming both-or-neither).
|
|
func test_cap_extent_to_body_caps_only_the_positive_echo_axis() -> void:
|
|
var capped: Vector2i = StepCanvasTransport.cap_extent_to_body(
|
|
Vector2i(384, 216), "Region", Vector2i(177, 0)
|
|
)
|
|
assert_that(capped).is_equal(Vector2i(177, 216))
|
|
|
|
|
|
# =============================================================================
|
|
# 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)
|