From d36d458b74baf9c2b366639934b6e2d8e26b7be3 Mon Sep 17 00:00:00 2001 From: Jeroen Schweitzer Date: Mon, 27 Jul 2026 21:13:04 +0200 Subject: [PATCH] =?UTF-8?q?fix(client):=20Global=20drew=20at=20half=20size?= =?UTF-8?q?=20=E2=80=94=20request=20and=20fit=20disagreed=20about=20the=20?= =?UTF-8?q?legend?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Jeroen: 'that does not fit the viewport'. Correct — Global rendered a real map into roughly a quarter of the available area. _letterbox_scale_for() reserves LEGEND_COLUMN_PX before computing the Global fit, but _request_extent() sized the request against the FULL Control width. Because that fit is an INTEGER pixels-per-gridunit ratio, the disagreement does not degrade gracefully: at a 1920-wide window we asked for 960 gridunits but could only fit floor(1628/960) = 1 px each, so the canvas drew at HALF the intended scale with room to spare on every side. The request is now sized to the drawable area, so both sides agree: 814 gridunits at 2 px = 1628 px, plus the 292 px legend column = exactly 1920. Verified through the capture harness — canvas_scale went 0.5 -> 1.0. Only Global reserves the column, so only Global adjusts; the fixed rungs are untouched. Also corrects the regression test I wrote yesterday, which computed its expectation from the full viewport and so encoded the bug. Client suite 1830 / 1804 passed / 0 failed / 26 skipped. Co-Authored-By: Claude --- client/tests/test_step_canvas_viewer.gd | 10 +++++++++- .../atlas/step_canvas/step_canvas_viewer.gd | 17 ++++++++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/client/tests/test_step_canvas_viewer.gd b/client/tests/test_step_canvas_viewer.gd index da0d3cc5d..63c93bce1 100644 --- a/client/tests/test_step_canvas_viewer.gd +++ b/client/tests/test_step_canvas_viewer.gd @@ -810,8 +810,16 @@ func test_request_extent_ignores_a_not_yet_laid_out_viewport() -> void: for degenerate in [Vector2.ZERO, Vector2(4.0, 4.0), Vector2(1920.0, 2.0)]: v.size = degenerate var extent: Vector2i = v._request_extent() + # The fallback is sized to the DRAWABLE area, so Global's reserved + # legend column comes off the width first — the request must match what + # can actually be drawn, or the Global integer fit drops a whole step + # and the map renders at half size in a window with room to spare. + var drawable := Vector2( + StepCanvasViewer.FALLBACK_VIEWPORT_PX.x - StepCanvasTransport.LEGEND_COLUMN_PX, + StepCanvasViewer.FALLBACK_VIEWPORT_PX.y + ) var expected: Vector2i = StepCanvasTransport.viewport_fit_extent( - StepCanvasViewer.FALLBACK_VIEWPORT_PX, v.get_held_rung() + drawable, v.get_held_rung() ) assert_that(extent).override_failure_message( "a %s viewport must fall back, not be taken literally — got %s" % [degenerate, extent] diff --git a/client/ui/implant/apps/atlas/step_canvas/step_canvas_viewer.gd b/client/ui/implant/apps/atlas/step_canvas/step_canvas_viewer.gd index 754bc6428..e2eef7faf 100644 --- a/client/ui/implant/apps/atlas/step_canvas/step_canvas_viewer.gd +++ b/client/ui/implant/apps/atlas/step_canvas/step_canvas_viewer.gd @@ -501,7 +501,22 @@ func _request_extent() -> Vector2i: # Treat anything below a plausible panel size as "not laid out yet". if viewport.x < MIN_LAID_OUT_VIEWPORT_PX or viewport.y < MIN_LAID_OUT_VIEWPORT_PX: viewport = FALLBACK_VIEWPORT_PX - var fit: Vector2i = StepCanvasTransport.viewport_fit_extent(viewport, _held_rung) + # Ask for the area we can actually DRAW into, not the whole Control. + # + # Global reserves LEGEND_COLUMN_PX on the left (_letterbox_scale_for() / + # _recompute_canvas_transform() both do). Sizing the request against the + # full width while fitting the draw against the reduced width makes the two + # disagree, and because the Global fit is an INTEGER px-per-gridunit ratio + # the disagreement does not degrade gracefully: at a 1920-wide window we + # asked for 960 gridunits but could only fit floor(1628/960) = 1 px each, + # so the map drew at HALF SIZE in a window with room to spare (eyeballed on + # Lendel 2026-07-27). Reserving the column here makes the request exactly + # what fits at the intended ratio, so the fit lands on 2 and the canvas + # fills the panel. + var drawable: Vector2 = viewport + if _held_rung == StepCanvasTransport.RUNG_GLOBAL: + drawable.x = maxf(viewport.x - StepCanvasTransport.LEGEND_COLUMN_PX, 1.0) + var fit: Vector2i = StepCanvasTransport.viewport_fit_extent(drawable, _held_rung) return StepCanvasTransport.cap_extent_to_body(fit, _held_rung, _global_body_extent)