feat(ui): step-canvas map component — RTT terrain + stepped zoom (D-255, T-1182)
The two-layer client rebuild per D-255(a)(b)(e), replacing the _canvas.scale continuous-zoom model with one viewer, one path, all six rungs: - step_canvas_protocol.gd: StepCanvasRequest/Response codec against the T-1181 wire contract — incl. the discovered png_bytes subtlety (rmp_serde without serde_bytes emits a msgpack int-array, not bin; decode repacks via PackedByteArray before load_png_from_buffer) and the extent-echo rule (read the server-clamped extent, never assume the requested one). - step_canvas/ component: transport (six-rung ladder, cursor-anchored scroll steps, edge-scroll/WASD pan with re-request on edge crossing, hard reset-to-Global), RTT terrain layer (Image.set_pixel colorize per the c1 measured ruling, texture.update reuse on step-cross, NEAREST coarse / LINEAR fine per rung), unscaled screen-space annotation sibling (courses + settlement markers at literal px), in-memory LRU cache (Tier 1; T-1183 layers the disk tiers beneath), request lifecycle (pending retry, staleness gate, extent echo). - Full _canvas.scale retirement in the same change: the zoom-scaled canvas model, the _zs compensation family, select_rung / MAX_COVERAGE_M / compute_tile_grid, the orbital-mosaic-vs-window two-path split, _view_zoom/_canonical_fit_zoom — 10 source files deleted; their 14 test suites deleted with them (T-1157 dead-goldens rule; replacement visual-capture coverage is re-scoped T-1157). - Surviving surfaces kept per the ticket: atlas_window_cache.gd's LRU shape (the ticket's named file atlas_window_tile_set.gd was the retiring orchestrator; the real LRU shape lives in atlas_window_cache.gd — cited in step_canvas_cache.gd), overlay colors, legend/overlay-bar chrome, AtlasViewer descend geometry. Determinism boundary per D-255(e): the client interpolates only within the closed server-supplied input set. 7 new gdUnit suites (164 cases) incl. a real extent-echo bug caught by its own test during implementation. Full client suite green (exit 0) with the live-gated suites running against a worktree server build. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -7,6 +7,7 @@ signal atlas_layers_received(response: Dictionary)
|
||||
signal star_map_received(response: Dictionary) # T-949: StarMapResponse
|
||||
signal city_names_received(response: Dictionary) # T-949: CityNamesResponse
|
||||
signal browse_response_received(response: Dictionary) # T-1131/T-1133: BrowseResponse
|
||||
signal step_canvas_received(response: Dictionary) # T-1182, D-255(c): StepCanvasResponse
|
||||
signal handshake_complete
|
||||
signal handshake_failed(reason: String)
|
||||
|
||||
@@ -568,6 +569,33 @@ func request_browse_detail(kind: String, entity_id: String) -> void:
|
||||
)
|
||||
|
||||
|
||||
## Request one step-canvas data canvas (T-1182, D-255(c)) — the stepped Atlas
|
||||
## ladder's per-rung terrain/annotation payload. Live mode only; the response
|
||||
## arrives via step_canvas_received. `rung` is one of the six bare-string rung
|
||||
## tags (StepCanvasTransport.RUNG_*); `center`/`extent` are sent unconditionally
|
||||
## even for Global (the server ignores them for that rung — see
|
||||
## step_canvas_protocol.gd's own doc). No client-side polling loop here — the
|
||||
## D-225 poll/cache/enqueue pattern means a Pending response is the caller's
|
||||
## cue to retry, mirroring request_atlas_layers()'s own "fire and let the
|
||||
## response routing decide" shape.
|
||||
func request_step_canvas(
|
||||
body_id: String, rung: String, center: Vector2i, extent: Vector2i, min_wl_m: int = 0
|
||||
) -> void:
|
||||
if test_mode or _bridge == null or state != ConnectionState.CONNECTED:
|
||||
return
|
||||
var bytes := Protocol.encode_step_canvas_request(body_id, rung, center, extent, min_wl_m)
|
||||
if bytes.is_empty():
|
||||
return
|
||||
var err: int = _bridge.send_message(bytes)
|
||||
if err != OK:
|
||||
push_error(
|
||||
(
|
||||
"SimBridge: failed to send step canvas request for %s/%s: %s"
|
||||
% [body_id, rung, error_string(err)]
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
# Poll for snapshot from simulation.
|
||||
# In test mode delegates to test harness. In live mode, returns the last decoded snapshot.
|
||||
func poll_snapshot() -> Variant:
|
||||
@@ -609,6 +637,9 @@ func receive_bytes(bytes: PackedByteArray) -> void:
|
||||
if inbound.kind == "browse":
|
||||
browse_response_received.emit(inbound.value)
|
||||
return
|
||||
if inbound.kind == "step_canvas":
|
||||
step_canvas_received.emit(inbound.value)
|
||||
return
|
||||
if inbound.kind != "snapshot":
|
||||
push_warning("SimBridge: undecodable frame (%d bytes)" % bytes.size())
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user