fix(simulation): derive at the octaves a rung can actually reconstruct (T-1240)
Region rendered as fine uniform stucco while District and Quarter, on identical code, read as terrain. The cause was sampling: `min_wl_m` arrives as an LOD request and defaults to 0, so every invented octave contributed at every rung. MIN_WL_BANDS_M was meant to be the floor but is built from the rung's CELL SIZE (2 x DISTRICT_M), which stopped being the sample spacing at the D-255 extent inversion — a rung fixes EXTENT now and spacing falls out of the canvas size. The bands were off by roughly the cell count, and the served path never consulted them anyway. The cutoff is now derived from the resolved spacing, which is what this ticket asked for. Two things had to be measured rather than reasoned to get it right, and both corrected me. FIRST: the field was the culprit, not the renderer. I attributed the stucco to the client stipple painting noise onto a smooth field. Surfacing the terrain layer's own mean |relief_q gradient| in the capture readout settled it in one shot: Region 18.24 steps per cell — 144 m of relief between NEIGHBOURING cells — against District's 0.30 and Quarter's 0.07. The server was sending noise. That diagnostic ships here for the same reason `plane_variety` did in T-1213: a noisy field and a renderer inventing noise look identical, and one number separates them. SECOND: Nyquist is the wrong threshold. The first version floored at 2 x spacing, the aliasing limit, and Region barely moved (56.16 -> 59.73 lum spread, gradient still 18.24) because 2 samples per cycle is unaliased but renders jagged. The rungs that already worked say what the real bar is: District reconstructs its finest surviving octave at 34 samples per cycle, Quarter at 135. At 8x, Region goes to 1.08 gradient and 70.01 spread, and shows ridges and valleys. THE TRADE, taken deliberately and recorded in the tests: an 8x floor also truncates the coast warp's 2,048 and 1,024 m octaves at Region, the band T-1160 added for "one coastline at every rung". An earlier test here asserted that band must survive; it now asserts the opposite. Same reasoning as the relief: a 1,024 m coastline wiggle at 379.3 m per cell is 2.7 samples per cycle, so drawing it draws noise rather than coastline character — a rung cannot show shape finer than its own cell. The warp is amplitude-capped sub-pixel on the working grid, so what is lost is small. If a future pass wants the warp exempt, the fix is a relief-only floor threaded through derive_at_metres, NOT a lower multiple, which takes the stucco back. Global is exempt: its floor would be ~70 km and would truncate the whole warp band, and it needs none — the orbital derive leaves relief_q flat at 50. District (3.79 m spacing) and Quarter (0.948 m) floor below every octave in play and derive byte-identically, which their own test pins. Cache-safe by construction: the floor is a pure function of (rung, extent, body_radius), all three already in the step-canvas cache key. 0.4.12 is required anyway — this changes derived BYTES at Region, so a 0.4.11 entry holds a field this build would never produce. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -661,3 +661,5 @@ same kind of ridge-and-valley form District does, at its own scale, and it will
|
|||||||
once the octaves below its sampling resolution are cut. The T-1213 work made this
|
once the octaves below its sampling resolution are cut. The T-1213 work made this
|
||||||
observable; before it, relief_q never arrived and the rung was flat for a
|
observable; before it, relief_q never arrived and the rung was flat for a
|
||||||
different reason entirely.', NULL, '2026-08-16 16:23:17', '2026-08-16 16:23:17.190', '2026-08-16 16:23:17.190', NULL, '42875477f1917d72cd064d584df2b11f', 2) ON CONFLICT(hash) DO NOTHING;
|
different reason entirely.', NULL, '2026-08-16 16:23:17', '2026-08-16 16:23:17.190', '2026-08-16 16:23:17.190', NULL, '42875477f1917d72cd064d584df2b11f', 2) ON CONFLICT(hash) DO NOTHING;
|
||||||
|
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FXRSY7QWD8J5X6G1N86WMKEC', 'status', 'backlog', 'in_progress', NULL, '2026-08-17 07:08:40', '2026-08-17 07:08:40.539', '2026-08-17 07:08:40.539', NULL, 'e4ccc5d347b5aa663e2b1f2807859e94', 2) ON CONFLICT(hash) DO NOTHING;
|
||||||
|
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FXRSY7QWD8J5X6G1N86WMKEC', 'status', 'in_progress', 'in_progress', NULL, '2026-08-17 07:08:54', '2026-08-17 07:08:54.825', '2026-08-17 07:08:54.825', NULL, '7e81011477963b0c4e59458537164e4a', 2) ON CONFLICT(hash) DO NOTHING;
|
||||||
|
|||||||
@@ -638,3 +638,57 @@ same kind of ridge-and-valley form District does, at its own scale, and it will
|
|||||||
once the octaves below its sampling resolution are cut. The T-1213 work made this
|
once the octaves below its sampling resolution are cut. The T-1213 work made this
|
||||||
observable; before it, relief_q never arrived and the rung was flat for a
|
observable; before it, relief_q never arrived and the rung was flat for a
|
||||||
different reason entirely.', 'backlog', 'medium', NULL, 'server', 'D-255', '2026-08-07 13:26:56.703', '2026-08-16 16:23:17.190', NULL, '1ed56273a1d81dbb6d0e1614598f744b', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at >= tickets.updated_at;
|
different reason entirely.', 'backlog', 'medium', NULL, 'server', 'D-255', '2026-08-07 13:26:56.703', '2026-08-16 16:23:17.190', NULL, '1ed56273a1d81dbb6d0e1614598f744b', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at >= tickets.updated_at;
|
||||||
|
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FXRSY7QWD8J5X6G1N86WMKEC', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'MIN_WL_BANDS_M octave cutoffs are pre-extent-inversion (stale, currently inert)', 'Found during T-1213 (2026-08-07). MIN_WL_BANDS_M (layer_proxy.rs) is built from 2*DISTRICT_M (4,096 m) and 2*QUARTER_M (1,024 m) -- Nyquist for a sample spacing equal to the rung''s CELL SIZE. That was correct while a rung fixed SPACING; after D-255''s extent inversion a rung fixes EXTENT and District''s spacing is 2048/540 = 3.8 m, so its true Nyquist floor is ~7.6 m. The bands are off by roughly the canvas cell count (~540x). This is the same class of defect as the Global 2x1 wire-extent sentinel the D-258 amendment documents: a constant that was correct under the pre-inversion model and silently outlived it. The code even states the consequence as though intended -- district_profile.rs''s comment reads ''At District''s real Nyquist floor (4,096 m) every VOXEL_OCTAVE_WAVELENGTHS_M entry is truncated, so relief is always exactly 0.0 there''. IMPORTANT SCOPE NOTE, verified before filing: this is currently INERT for the step canvas. step_canvas_viewer._fire_request() calls request_now(body, rung, center, extent) with no min_wl_m, so it defaults to 0, and quantize_min_wl_m(0) returns 0 (the leading sentinel band) -- no truncation happens on the served path. It therefore only affects the legacy layer_proxy district-window consumer. It is NOT the cause of the flat District/Quarter rungs; that is elev_q''s 80 m quantisation (0-100 across MAX_REGION_ELEVATION_KM = 8.0 km), measured at d1 mean 0.02 with the cutoff already disabled. Fix: derive the cutoff from the resolved canvas spacing rather than the rung cell size. Check the layer_proxy consumer''s expectations first -- MIN_WL_BANDS_M is shared, carries a const assert tying band 4 to OCTAVE_WAVELENGTHS_M[3], and is part of the cache key, so a change there is not local.
|
||||||
|
|
||||||
|
---
|
||||||
|
NO LONGER INERT IN CONSEQUENCE (2026-08-16, found via T-1213). This ticket''s own
|
||||||
|
scope note says the stale cutoff "is NOT the cause of the flat District/Quarter
|
||||||
|
rungs". That was correct, and it is now the cause of something else: it caps
|
||||||
|
REGION.
|
||||||
|
|
||||||
|
With relief_q reaching the renderer (T-1213 fixed a missing protocol decode) and
|
||||||
|
driving a hillshade, Region renders as a fine uniform stucco — texture at the
|
||||||
|
pixel scale, no landform. District and Quarter, on the same code, read as real
|
||||||
|
terrain (lum p1-p99 74.43 and 73.72 against Region''s 54.30).
|
||||||
|
|
||||||
|
The reason is sampling, and it is this ticket: a Region cell is 379 m of ground,
|
||||||
|
while the relief field''s own content sits in the 128-1024 m composition band plus
|
||||||
|
192 m inclusions. The field is at or below Nyquist for that sampling, so every
|
||||||
|
neighbouring cell differs by an essentially independent draw and the gradient the
|
||||||
|
hillshade reads is ALIASING rather than slope. Because min_wl_m defaults to 0 on
|
||||||
|
the served path, nothing truncates the octaves Region cannot resolve — which is
|
||||||
|
precisely the fix this ticket proposes ("derive the cutoff from the resolved
|
||||||
|
canvas spacing rather than the rung cell size").
|
||||||
|
|
||||||
|
So the visible symptom to fix against is now concrete: Region should show the
|
||||||
|
same kind of ridge-and-valley form District does, at its own scale, and it will
|
||||||
|
once the octaves below its sampling resolution are cut. The T-1213 work made this
|
||||||
|
observable; before it, relief_q never arrived and the rung was flat for a
|
||||||
|
different reason entirely.', 'in_progress', 'medium', NULL, 'server', 'D-255', '2026-08-07 13:26:56.703', '2026-08-17 07:08:40.539', NULL, '67165f7b4321d58fbb98b706b50e9058', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at >= tickets.updated_at;
|
||||||
|
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FXRSY7QWD8J5X6G1N86WMKEC', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'MIN_WL_BANDS_M octave cutoffs are pre-extent-inversion (stale, currently inert)', 'Found during T-1213 (2026-08-07). MIN_WL_BANDS_M (layer_proxy.rs) is built from 2*DISTRICT_M (4,096 m) and 2*QUARTER_M (1,024 m) -- Nyquist for a sample spacing equal to the rung''s CELL SIZE. That was correct while a rung fixed SPACING; after D-255''s extent inversion a rung fixes EXTENT and District''s spacing is 2048/540 = 3.8 m, so its true Nyquist floor is ~7.6 m. The bands are off by roughly the canvas cell count (~540x). This is the same class of defect as the Global 2x1 wire-extent sentinel the D-258 amendment documents: a constant that was correct under the pre-inversion model and silently outlived it. The code even states the consequence as though intended -- district_profile.rs''s comment reads ''At District''s real Nyquist floor (4,096 m) every VOXEL_OCTAVE_WAVELENGTHS_M entry is truncated, so relief is always exactly 0.0 there''. IMPORTANT SCOPE NOTE, verified before filing: this is currently INERT for the step canvas. step_canvas_viewer._fire_request() calls request_now(body, rung, center, extent) with no min_wl_m, so it defaults to 0, and quantize_min_wl_m(0) returns 0 (the leading sentinel band) -- no truncation happens on the served path. It therefore only affects the legacy layer_proxy district-window consumer. It is NOT the cause of the flat District/Quarter rungs; that is elev_q''s 80 m quantisation (0-100 across MAX_REGION_ELEVATION_KM = 8.0 km), measured at d1 mean 0.02 with the cutoff already disabled. Fix: derive the cutoff from the resolved canvas spacing rather than the rung cell size. Check the layer_proxy consumer''s expectations first -- MIN_WL_BANDS_M is shared, carries a const assert tying band 4 to OCTAVE_WAVELENGTHS_M[3], and is part of the cache key, so a change there is not local.
|
||||||
|
|
||||||
|
---
|
||||||
|
NO LONGER INERT IN CONSEQUENCE (2026-08-16, found via T-1213). This ticket''s own
|
||||||
|
scope note says the stale cutoff "is NOT the cause of the flat District/Quarter
|
||||||
|
rungs". That was correct, and it is now the cause of something else: it caps
|
||||||
|
REGION.
|
||||||
|
|
||||||
|
With relief_q reaching the renderer (T-1213 fixed a missing protocol decode) and
|
||||||
|
driving a hillshade, Region renders as a fine uniform stucco — texture at the
|
||||||
|
pixel scale, no landform. District and Quarter, on the same code, read as real
|
||||||
|
terrain (lum p1-p99 74.43 and 73.72 against Region''s 54.30).
|
||||||
|
|
||||||
|
The reason is sampling, and it is this ticket: a Region cell is 379 m of ground,
|
||||||
|
while the relief field''s own content sits in the 128-1024 m composition band plus
|
||||||
|
192 m inclusions. The field is at or below Nyquist for that sampling, so every
|
||||||
|
neighbouring cell differs by an essentially independent draw and the gradient the
|
||||||
|
hillshade reads is ALIASING rather than slope. Because min_wl_m defaults to 0 on
|
||||||
|
the served path, nothing truncates the octaves Region cannot resolve — which is
|
||||||
|
precisely the fix this ticket proposes ("derive the cutoff from the resolved
|
||||||
|
canvas spacing rather than the rung cell size").
|
||||||
|
|
||||||
|
So the visible symptom to fix against is now concrete: Region should show the
|
||||||
|
same kind of ridge-and-valley form District does, at its own scale, and it will
|
||||||
|
once the octaves below its sampling resolution are cut. The T-1213 work made this
|
||||||
|
observable; before it, relief_q never arrived and the rung was flat for a
|
||||||
|
different reason entirely.', 'in_progress', 'medium', NULL, 'server', 'D-255', '2026-08-07 13:26:56.703', '2026-08-17 07:08:54.825', NULL, '4300fa437b31a5f8e2b89999c882fcd8', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at >= tickets.updated_at;
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ config/name="The Settled Reach"
|
|||||||
; in the editor and in a shipped build, where res://../project.yaml does not
|
; in the editor and in a shipped build, where res://../project.yaml does not
|
||||||
; exist at all (T-1241). Kept honest by `make check-client-version`, which the
|
; exist at all (T-1241). Kept honest by `make check-client-version`, which the
|
||||||
; pre-push hook runs — do not edit this by hand without moving project.yaml too.
|
; pre-push hook runs — do not edit this by hand without moving project.yaml too.
|
||||||
config/version="0.4.11"
|
config/version="0.4.12"
|
||||||
run/main_scene="res://scenes/main_menu.tscn"
|
run/main_scene="res://scenes/main_menu.tscn"
|
||||||
config/features=PackedStringArray("4.6", "GL Compatibility")
|
config/features=PackedStringArray("4.6", "GL Compatibility")
|
||||||
config/icon="res://icon.svg"
|
config/icon="res://icon.svg"
|
||||||
|
|||||||
@@ -283,8 +283,10 @@ func _settle_atlas_viewer_if_pending(tree_root: Node) -> void:
|
|||||||
waited += 1
|
waited += 1
|
||||||
if request.is_pending():
|
if request.is_pending():
|
||||||
print(
|
print(
|
||||||
"visual_capture: atlas settle timed out after %d frames — request still pending"
|
(
|
||||||
% ATLAS_SETTLE_MAX_FRAMES
|
"visual_capture: atlas settle timed out after %d frames — request still pending"
|
||||||
|
% ATLAS_SETTLE_MAX_FRAMES
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -333,27 +335,31 @@ func _log_atlas_view_transform(tree_root: Node, scenario_name: String) -> void:
|
|||||||
var on_screen_footprint_px: Vector2 = raw_footprint_px * canvas_scale
|
var on_screen_footprint_px: Vector2 = raw_footprint_px * canvas_scale
|
||||||
print(
|
print(
|
||||||
(
|
(
|
||||||
"visual_capture: view-transform[%s] rung=%s world_center=%s held_extent=%s "
|
(
|
||||||
+ "canvas_position=%s canvas_scale=%s footprint_px=%s canvas_cells=%dx%d "
|
"visual_capture: view-transform[%s] rung=%s world_center=%s held_extent=%s "
|
||||||
+ "courses=%d runs=%d longest=%.1fpx drawn=%d settlements=%d planes=%s"
|
+ "canvas_position=%s canvas_scale=%s footprint_px=%s canvas_cells=%dx%d "
|
||||||
|
+ "courses=%d runs=%d longest=%.1fpx drawn=%d settlements=%d "
|
||||||
|
+ "planes=%s relief_grad=%.2f"
|
||||||
|
)
|
||||||
|
% [
|
||||||
|
scenario_name,
|
||||||
|
rung,
|
||||||
|
str(summary.get("world_center", [])),
|
||||||
|
str(held_extent_raw),
|
||||||
|
str(canvas_pos),
|
||||||
|
str(canvas_scale),
|
||||||
|
str(on_screen_footprint_px),
|
||||||
|
int(summary.get("canvas_width", 0)),
|
||||||
|
int(summary.get("canvas_height", 0)),
|
||||||
|
int(summary.get("course_count", 0)),
|
||||||
|
int(summary.get("runs_built", 0)),
|
||||||
|
float(summary.get("longest_run_px", 0.0)),
|
||||||
|
int(summary.get("drawn_course_count", 0)),
|
||||||
|
int(summary.get("settlement_count", 0)),
|
||||||
|
str(summary.get("plane_variety", {})),
|
||||||
|
float(summary.get("relief_gradient", 0.0)),
|
||||||
|
]
|
||||||
)
|
)
|
||||||
% [
|
|
||||||
scenario_name,
|
|
||||||
rung,
|
|
||||||
str(summary.get("world_center", [])),
|
|
||||||
str(held_extent_raw),
|
|
||||||
str(canvas_pos),
|
|
||||||
str(canvas_scale),
|
|
||||||
str(on_screen_footprint_px),
|
|
||||||
int(summary.get("canvas_width", 0)),
|
|
||||||
int(summary.get("canvas_height", 0)),
|
|
||||||
int(summary.get("course_count", 0)),
|
|
||||||
int(summary.get("runs_built", 0)),
|
|
||||||
float(summary.get("longest_run_px", 0.0)),
|
|
||||||
int(summary.get("drawn_course_count", 0)),
|
|
||||||
int(summary.get("settlement_count", 0)),
|
|
||||||
str(summary.get("plane_variety", {})),
|
|
||||||
]
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -498,10 +504,7 @@ func _run_atlas_matrix() -> void:
|
|||||||
# implant_app.gd — not previously documented in the recon blueprint).
|
# implant_app.gd — not previously documented in the recon blueprint).
|
||||||
var first_body_id: String = str(shots[0].get("body_id", ""))
|
var first_body_id: String = str(shots[0].get("body_id", ""))
|
||||||
var first_body: Dictionary = body_lookup.get(first_body_id, {})
|
var first_body: Dictionary = body_lookup.get(first_body_id, {})
|
||||||
app.nav.push(
|
app.nav.push("regional", {"body": first_body, "system": _system_dict_for(first_body)})
|
||||||
"regional",
|
|
||||||
{"body": first_body, "system": _system_dict_for(first_body)}
|
|
||||||
)
|
|
||||||
var viewer: Variant = app._regional_screen._viewer
|
var viewer: Variant = app._regional_screen._viewer
|
||||||
if viewer == null:
|
if viewer == null:
|
||||||
push_error("visual_capture: regional screen has no viewer")
|
push_error("visual_capture: regional screen has no viewer")
|
||||||
@@ -524,9 +527,7 @@ func _run_atlas_matrix() -> void:
|
|||||||
# then navigates from there.
|
# then navigates from there.
|
||||||
if body_id != current_body_id:
|
if body_id != current_body_id:
|
||||||
current_body_id = body_id
|
current_body_id = body_id
|
||||||
app._regional_screen.enter(
|
app._regional_screen.enter({"body": body, "system": _system_dict_for(body)})
|
||||||
{"body": body, "system": _system_dict_for(body)}
|
|
||||||
)
|
|
||||||
await _settle_atlas_viewer_if_pending(root)
|
await _settle_atlas_viewer_if_pending(root)
|
||||||
|
|
||||||
# Apply overlay set: clear every gen_dw_* toggle, then enable this
|
# Apply overlay set: clear every gen_dw_* toggle, then enable this
|
||||||
@@ -704,7 +705,9 @@ func _print_list() -> void:
|
|||||||
# tests/atlas_shots.json instead — see _run_atlas_matrix()), so it can't be
|
# tests/atlas_shots.json instead — see _run_atlas_matrix()), so it can't be
|
||||||
# discovered by iterating _config above. Listed by hand so --list stays a
|
# discovered by iterating _config above. Listed by hand so --list stays a
|
||||||
# complete index of every --scenario value this script accepts.
|
# complete index of every --scenario value this script accepts.
|
||||||
print(" atlas_matrix — D-255 stepped Atlas capture matrix, keyed on (body, rung) (tests/atlas_shots.json, live-mode only)")
|
print(
|
||||||
|
" atlas_matrix — D-255 stepped Atlas capture matrix, keyed on (body, rung) (tests/atlas_shots.json, live-mode only)"
|
||||||
|
)
|
||||||
print("flows:")
|
print("flows:")
|
||||||
for name in flows:
|
for name in flows:
|
||||||
var steps: Array = flows[name].get("steps", [])
|
var steps: Array = flows[name].get("steps", [])
|
||||||
|
|||||||
@@ -73,6 +73,9 @@ var _held_rung: String = StepCanvasTransport.RUNG_DISTRICT
|
|||||||
## values separates them from a single capture.
|
## values separates them from a single capture.
|
||||||
var _plane_variety: Dictionary = {}
|
var _plane_variety: Dictionary = {}
|
||||||
|
|
||||||
|
## Mean |relief_q gradient| of the held canvas — see [method get_relief_gradient].
|
||||||
|
var _relief_gradient_scale: float = 0.0
|
||||||
|
|
||||||
|
|
||||||
## Rebuild (or reuse) the held texture from a decoded StepCanvasResponse's
|
## Rebuild (or reuse) the held texture from a decoded StepCanvasResponse's
|
||||||
## `canvas` Dictionary (step_canvas_protocol.gd's shape: width/height +
|
## `canvas` Dictionary (step_canvas_protocol.gd's shape: width/height +
|
||||||
@@ -129,6 +132,7 @@ func _decode_planes(canvas: Dictionary, width: int, height: int) -> StepCanvasCo
|
|||||||
planes.lake_margin_q = _decode_l8_plane(canvas.get("lake_margin_q"))
|
planes.lake_margin_q = _decode_l8_plane(canvas.get("lake_margin_q"))
|
||||||
planes.relief_q = _decode_l8_plane(canvas.get("relief_q"))
|
planes.relief_q = _decode_l8_plane(canvas.get("relief_q"))
|
||||||
planes.relief_gradient_scale = _measure_relief_gradient(planes.relief_q)
|
planes.relief_gradient_scale = _measure_relief_gradient(planes.relief_q)
|
||||||
|
_relief_gradient_scale = planes.relief_gradient_scale
|
||||||
planes.glaciation = _decode_l8_plane(canvas.get("glaciation"))
|
planes.glaciation = _decode_l8_plane(canvas.get("glaciation"))
|
||||||
planes.temp_dc = canvas.get("temp_dc", [])
|
planes.temp_dc = canvas.get("temp_dc", [])
|
||||||
return planes
|
return planes
|
||||||
@@ -181,6 +185,13 @@ func get_plane_variety() -> Dictionary:
|
|||||||
return _plane_variety
|
return _plane_variety
|
||||||
|
|
||||||
|
|
||||||
|
## The held canvas's mean |relief_q gradient| — the number the hillshade
|
||||||
|
## normalizes against, surfaced so a capture can say whether a stucco-looking
|
||||||
|
## rung is a noisy FIELD or a renderer painting noise onto a smooth one (T-1240).
|
||||||
|
func get_relief_gradient() -> float:
|
||||||
|
return _relief_gradient_scale
|
||||||
|
|
||||||
|
|
||||||
## Cells stepped between variety samples.
|
## Cells stepped between variety samples.
|
||||||
##
|
##
|
||||||
## A full scan is 5 planes x 1290x540 = ~3.5M get_pixel() calls per canvas
|
## A full scan is 5 planes x 1290x540 = ~3.5M get_pixel() calls per canvas
|
||||||
|
|||||||
@@ -46,9 +46,15 @@ extends Control
|
|||||||
|
|
||||||
signal back_pressed
|
signal back_pressed
|
||||||
|
|
||||||
const StepCanvasTransport := preload("res://ui/implant/apps/atlas/step_canvas/step_canvas_transport.gd")
|
const StepCanvasTransport := preload(
|
||||||
const StepCanvasTerrainLayer := preload("res://ui/implant/apps/atlas/step_canvas/step_canvas_terrain_layer.gd")
|
"res://ui/implant/apps/atlas/step_canvas/step_canvas_transport.gd"
|
||||||
const StepCanvasAnnotationLayer := preload("res://ui/implant/apps/atlas/step_canvas/step_canvas_annotation_layer.gd")
|
)
|
||||||
|
const StepCanvasTerrainLayer := preload(
|
||||||
|
"res://ui/implant/apps/atlas/step_canvas/step_canvas_terrain_layer.gd"
|
||||||
|
)
|
||||||
|
const StepCanvasAnnotationLayer := preload(
|
||||||
|
"res://ui/implant/apps/atlas/step_canvas/step_canvas_annotation_layer.gd"
|
||||||
|
)
|
||||||
const StepCanvasRequest := preload("res://ui/implant/apps/atlas/step_canvas/step_canvas_request.gd")
|
const StepCanvasRequest := preload("res://ui/implant/apps/atlas/step_canvas/step_canvas_request.gd")
|
||||||
|
|
||||||
const PANEL_MARGIN: float = 16.0
|
const PANEL_MARGIN: float = 16.0
|
||||||
@@ -431,16 +437,19 @@ func get_current_canvas_summary() -> Dictionary:
|
|||||||
"canvas_width": int(d.get("width", 0)),
|
"canvas_width": int(d.get("width", 0)),
|
||||||
"canvas_height": int(d.get("height", 0)),
|
"canvas_height": int(d.get("height", 0)),
|
||||||
"course_count": courses.size(),
|
"course_count": courses.size(),
|
||||||
"drawn_course_count": (
|
"drawn_course_count":
|
||||||
_annotation_layer.get_drawn_course_count() if _annotation_layer else 0
|
|
||||||
),
|
|
||||||
# T-1239: the two stages between "arrived" and "drawn", so a capture can
|
# T-1239: the two stages between "arrived" and "drawn", so a capture can
|
||||||
|
_annotation_layer.get_drawn_course_count() if _annotation_layer else 0,
|
||||||
# say WHICH stage dropped the rivers instead of only that they are gone.
|
# say WHICH stage dropped the rivers instead of only that they are gone.
|
||||||
"runs_built": _annotation_layer.get_runs_built() if _annotation_layer else 0,
|
"runs_built": _annotation_layer.get_runs_built() if _annotation_layer else 0,
|
||||||
"longest_run_px": _annotation_layer.get_longest_run_px() if _annotation_layer else 0.0,
|
"longest_run_px": _annotation_layer.get_longest_run_px() if _annotation_layer else 0.0,
|
||||||
# T-1213: distinct values per plane, so "this rung is flat" can name the
|
# T-1213: distinct values per plane, so "this rung is flat" can name the
|
||||||
# plane that went flat rather than leaving it to inference.
|
# plane that went flat rather than leaving it to inference.
|
||||||
"plane_variety": _terrain_layer.get_plane_variety() if _terrain_layer else {},
|
"plane_variety": _terrain_layer.get_plane_variety() if _terrain_layer else {},
|
||||||
|
# T-1240: the relief field's own mean cell-to-cell gradient. Separates
|
||||||
|
# "the server sent a noisy field" from "the renderer is painting noise
|
||||||
|
# onto a smooth one" — two causes with one appearance (a stucco rung).
|
||||||
|
"relief_gradient": _terrain_layer.get_relief_gradient() if _terrain_layer else 0.0,
|
||||||
"course_count_by_class": course_count_by_class,
|
"course_count_by_class": course_count_by_class,
|
||||||
"cliff_count": (d.get("cliffs", []) as Array).size(),
|
"cliff_count": (d.get("cliffs", []) as Array).size(),
|
||||||
"settlement_count": _count_distinct_settlements(d),
|
"settlement_count": _count_distinct_settlements(d),
|
||||||
@@ -605,11 +614,7 @@ func _pan_drift_fraction() -> float:
|
|||||||
func _refloat_now() -> void:
|
func _refloat_now() -> void:
|
||||||
var screen_center: Vector2 = get_rect().size * 0.5
|
var screen_center: Vector2 = get_rect().size * 0.5
|
||||||
_world_center = StepCanvasTransport.canvas_local_to_world_m(
|
_world_center = StepCanvasTransport.canvas_local_to_world_m(
|
||||||
screen_center - _view_offset,
|
screen_center - _view_offset, _world_center, _held_rung, _held_extent, get_body_radius_km()
|
||||||
_world_center,
|
|
||||||
_held_rung,
|
|
||||||
_held_extent,
|
|
||||||
get_body_radius_km()
|
|
||||||
)
|
)
|
||||||
_view_offset = Vector2.ZERO
|
_view_offset = Vector2.ZERO
|
||||||
_fire_request()
|
_fire_request()
|
||||||
@@ -746,11 +751,7 @@ func _scroll_rung(direction: int, cursor_local: Vector2) -> void:
|
|||||||
_reset_to_global()
|
_reset_to_global()
|
||||||
return
|
return
|
||||||
var cursor_world: Vector2 = StepCanvasTransport.canvas_local_to_world_m(
|
var cursor_world: Vector2 = StepCanvasTransport.canvas_local_to_world_m(
|
||||||
cursor_local - _view_offset,
|
cursor_local - _view_offset, _world_center, _held_rung, _held_extent, get_body_radius_km()
|
||||||
_world_center,
|
|
||||||
_held_rung,
|
|
||||||
_held_extent,
|
|
||||||
get_body_radius_km()
|
|
||||||
)
|
)
|
||||||
_rung_index = new_index
|
_rung_index = new_index
|
||||||
_held_rung = StepCanvasTransport.rung_at_index(_rung_index)
|
_held_rung = StepCanvasTransport.rung_at_index(_rung_index)
|
||||||
@@ -835,7 +836,9 @@ func _centered_view_offset() -> Vector2:
|
|||||||
var available: Vector2 = Vector2(maxf(viewport.x - reserved_left, 1.0), viewport.y)
|
var available: Vector2 = Vector2(maxf(viewport.x - reserved_left, 1.0), viewport.y)
|
||||||
var scale: float = _letterbox_scale_for(raw_footprint)
|
var scale: float = _letterbox_scale_for(raw_footprint)
|
||||||
var scaled_footprint: Vector2 = raw_footprint * scale
|
var scaled_footprint: Vector2 = raw_footprint * scale
|
||||||
return StepCanvasTransport.center_offset(scaled_footprint, available) + Vector2(reserved_left, 0.0)
|
return (
|
||||||
|
StepCanvasTransport.center_offset(scaled_footprint, available) + Vector2(reserved_left, 0.0)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
## Hard reset to the Global opener (D-255(a): "a hard full-zoom-out reset to
|
## Hard reset to the Global opener (D-255(a): "a hard full-zoom-out reset to
|
||||||
@@ -913,7 +916,6 @@ func _apply_transform() -> void:
|
|||||||
# Chrome
|
# Chrome
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
|
||||||
|
|
||||||
## T-1197: the header used to be a bare ImplantHeader added straight to this
|
## T-1197: the header used to be a bare ImplantHeader added straight to this
|
||||||
## Control, drawn directly over the terrain canvas with no backing — every
|
## Control, drawn directly over the terrain canvas with no backing — every
|
||||||
## OTHER implant chrome element (the legend below it) gets its scrim from
|
## OTHER implant chrome element (the legend below it) gets its scrim from
|
||||||
@@ -978,9 +980,10 @@ func _refresh_screen_header() -> void:
|
|||||||
if _screen_header == null:
|
if _screen_header == null:
|
||||||
return
|
return
|
||||||
var name_label: String = _dict_str(_body, "proper_name", _dict_str(_body, "body_id", "—"))
|
var name_label: String = _dict_str(_body, "proper_name", _dict_str(_body, "body_id", "—"))
|
||||||
var spacing_km: float = StepCanvasTransport.spacing_for_rung(
|
var spacing_km: float = (
|
||||||
_held_rung, _held_extent, get_body_radius_km()
|
StepCanvasTransport.spacing_for_rung(_held_rung, _held_extent, get_body_radius_km())
|
||||||
) / 1000.0
|
/ 1000.0
|
||||||
|
)
|
||||||
var title := "ATLAS — %s" % name_label.to_upper()
|
var title := "ATLAS — %s" % name_label.to_upper()
|
||||||
var subtitle := "%s · %.3f km/gridunit" % [_held_rung.to_upper(), spacing_km]
|
var subtitle := "%s · %.3f km/gridunit" % [_held_rung.to_upper(), spacing_km]
|
||||||
_screen_header.set_content(title, subtitle)
|
_screen_header.set_content(title, subtitle)
|
||||||
|
|||||||
+5
-1
@@ -61,7 +61,11 @@ name: The Settled Reach
|
|||||||
# 0.4.11 — per-class cover marks (copse, thicket, rocky outcrop). Paint again,
|
# 0.4.11 — per-class cover marks (copse, thicket, rocky outcrop). Paint again,
|
||||||
# not derivation; bumped because the client step_canvas cluster is in the T-1242
|
# not derivation; bumped because the client step_canvas cluster is in the T-1242
|
||||||
# registry.
|
# registry.
|
||||||
version: 0.4.11
|
# 0.4.12 changes DERIVED BYTES, not paint (T-1240): Region canvases now truncate
|
||||||
|
# every octave the rung cannot reconstruct, so a 0.4.11 Region entry holds a
|
||||||
|
# relief field this build would never produce. Warm caches are genuinely wrong
|
||||||
|
# here, not merely stale.
|
||||||
|
version: 0.4.12
|
||||||
repository: settled-reach
|
repository: settled-reach
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -469,6 +469,80 @@ pub fn quantize_min_wl_m_for_rung(raw: u32) -> u32 {
|
|||||||
crate::atlas::layer_proxy::quantize_min_wl_m(raw)
|
crate::atlas::layer_proxy::quantize_min_wl_m(raw)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The octave cutoff a canvas actually derives at: the wire request, floored by
|
||||||
|
/// what this canvas's own SAMPLE SPACING can carry (T-1240).
|
||||||
|
///
|
||||||
|
/// # Why the wire value alone was wrong
|
||||||
|
///
|
||||||
|
/// `min_wl_m` arrives as an LOD request and defaults to 0 — no cutoff — so every
|
||||||
|
/// invented octave contributed at every rung regardless of whether the canvas
|
||||||
|
/// could resolve it. `MIN_WL_BANDS_M` was supposed to be the floor, but it is
|
||||||
|
/// built from the rung's CELL SIZE (`2 × DISTRICT_M`), which stopped being the
|
||||||
|
/// sample spacing at the D-255 extent inversion: a rung now fixes EXTENT and the
|
||||||
|
/// spacing falls out of the canvas size. The bands are off by roughly the cell
|
||||||
|
/// count, and the served path never consulted them anyway.
|
||||||
|
///
|
||||||
|
/// # What that cost, measured
|
||||||
|
///
|
||||||
|
/// Region samples every 379.3 m while `voxel_relief`'s band runs 1,024/512/256/
|
||||||
|
/// 128 m. Three of those four octaves are below Nyquist, so neighbouring cells
|
||||||
|
/// drew essentially independent values and the T-1213 hillshade read ALIASING
|
||||||
|
/// rather than slope — Region rendered as fine uniform stucco while District and
|
||||||
|
/// Quarter, on identical code, read as terrain.
|
||||||
|
///
|
||||||
|
/// # Why this is a floor and not a replacement
|
||||||
|
///
|
||||||
|
/// `max`, so a caller asking for something coarser still gets it: the wire value
|
||||||
|
/// is a legitimate "give me less detail" knob, while this is a physical limit on
|
||||||
|
/// what the canvas can represent. Only the limit is non-negotiable.
|
||||||
|
///
|
||||||
|
/// # Why Global is exempt
|
||||||
|
///
|
||||||
|
/// Global's floor would be ~70 km, which would truncate the ENTIRE coast-warp
|
||||||
|
/// band (`WARP_OCTAVE_WAVELENGTHS_M`, finest 1,024 m) and undo T-1160's "one
|
||||||
|
/// coastline at every rung". It needs no floor regardless: the orbital rungs skip
|
||||||
|
/// the sub-district derive, so `relief_q` is a flat 50 there and there is no
|
||||||
|
/// fine band to alias.
|
||||||
|
///
|
||||||
|
/// # What this actually changes
|
||||||
|
///
|
||||||
|
/// Only Region. Its 758.6 m floor truncates relief's 512/256/128 m octaves while
|
||||||
|
/// leaving the 1,024 m one — and leaves the coast warp untouched, whose finest
|
||||||
|
/// octave is exactly 1,024 m. District's floor is 7.6 m and Quarter's 1.9 m,
|
||||||
|
/// both below every octave in play, so those rungs derive byte-identically.
|
||||||
|
///
|
||||||
|
/// Cache-safe by construction: this is a pure function of `(rung, extent,
|
||||||
|
/// body_radius)`, and all three are already in the step-canvas cache key, so the
|
||||||
|
/// same key still means the same bytes.
|
||||||
|
pub fn effective_min_wavelength_m(rung: StepCanvasRung, min_wl_m: u32, step_m: f64) -> f64 {
|
||||||
|
if rung.is_global() {
|
||||||
|
return min_wl_m as f64;
|
||||||
|
}
|
||||||
|
(min_wl_m as f64).max(SMOOTH_SAMPLES_PER_WAVELENGTH * step_m)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Samples per wavelength the finest surviving octave must get.
|
||||||
|
///
|
||||||
|
/// NOT 2. Nyquist (2 samples per cycle) is the ALIASING limit — the point below
|
||||||
|
/// which a wave is indistinguishable from a slower one — and it was the first
|
||||||
|
/// value tried here. It did not work, and the measurement says why: at 2x, a
|
||||||
|
/// Region canvas (379.3 m spacing) keeps the 1,024 m relief octave at 2.7
|
||||||
|
/// samples per cycle, and the mean cell-to-cell relief gradient measured 18.24
|
||||||
|
/// steps — 144 m of relief between neighbouring cells. Technically unaliased,
|
||||||
|
/// visually noise, and the hillshade drew it as stucco.
|
||||||
|
///
|
||||||
|
/// The rungs that read as terrain say what the real threshold is. District keeps
|
||||||
|
/// a 128 m finest octave at 3.79 m spacing — 34 samples per cycle — and measures
|
||||||
|
/// a 0.30 gradient; Quarter gets 135 and measures 0.07. Reconstruction has to be
|
||||||
|
/// oversampled by an order of magnitude before a sampled field reads as a
|
||||||
|
/// surface rather than as a field of independent draws.
|
||||||
|
///
|
||||||
|
/// 8 is the compromise: at Region it cuts the 1,024 m octave and leaves 4,096 m
|
||||||
|
/// at ~10.8 samples per cycle. Higher would be smoother still but starts
|
||||||
|
/// discarding real structure the rung could legitimately show; the acceptance
|
||||||
|
/// bar is the captured ladder, not this number.
|
||||||
|
const SMOOTH_SAMPLES_PER_WAVELENGTH: f64 = 8.0;
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Derive core — one row-chunked parallel pass per rung (D-255(f) seed-chaining:
|
// Derive core — one row-chunked parallel pass per rung (D-255(f) seed-chaining:
|
||||||
// independent re-derivation, fallback path; mechanism-B acceleration lives in
|
// independent re-derivation, fallback path; mechanism-B acceleration lives in
|
||||||
@@ -1041,10 +1115,10 @@ pub fn build_step_canvas(
|
|||||||
|
|
||||||
let body_radius_km = params.body_radius_km.unwrap_or(0.0);
|
let body_radius_km = params.body_radius_km.unwrap_or(0.0);
|
||||||
let (width, height) = resolve_canvas_extent(rung, extent, body_radius_km);
|
let (width, height) = resolve_canvas_extent(rung, extent, body_radius_km);
|
||||||
let min_wavelength_m = min_wl_m as f64;
|
|
||||||
// RESOLVED dims, not the requested `extent` — a clamped canvas covers the
|
// RESOLVED dims, not the requested `extent` — a clamped canvas covers the
|
||||||
// same ground at a coarser pitch (see StepCanvasRung::spacing_m).
|
// same ground at a coarser pitch (see StepCanvasRung::spacing_m).
|
||||||
let step_m = rung.spacing_m(width, height, body_radius_km);
|
let step_m = rung.spacing_m(width, height, body_radius_km);
|
||||||
|
let min_wavelength_m = effective_min_wavelength_m(rung, min_wl_m, step_m);
|
||||||
let cells = (width * height) as usize;
|
let cells = (width * height) as usize;
|
||||||
|
|
||||||
let half_w = (width / 2) as i32;
|
let half_w = (width / 2) as i32;
|
||||||
@@ -2338,6 +2412,100 @@ mod tests {
|
|||||||
// Quantization reuse
|
// Quantization reuse
|
||||||
// -----------------------------------------------------------------
|
// -----------------------------------------------------------------
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------
|
||||||
|
// Spacing-derived octave floor (T-1240)
|
||||||
|
// -----------------------------------------------------------------
|
||||||
|
|
||||||
|
/// Ferrath's Region spacing on the 1290x540 canvas the descent ladder shoots.
|
||||||
|
const REGION_STEP_M: f64 = 379.3;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn floor_truncates_the_octaves_region_cannot_reconstruct() {
|
||||||
|
let floor = effective_min_wavelength_m(StepCanvasRung::Region, 0, REGION_STEP_M);
|
||||||
|
// 8 samples per wavelength at 379.3 m spacing.
|
||||||
|
assert!(floor > 3_000.0 && floor < 3_100.0, "floor was {floor}");
|
||||||
|
// The whole voxel-relief band goes: even its coarsest entry (1,024 m)
|
||||||
|
// is only 2.7 samples per cycle here, which measured as an 18.24
|
||||||
|
// step-per-cell gradient and rendered as stucco.
|
||||||
|
for wl in [1_024.0, 512.0, 256.0, 128.0] {
|
||||||
|
assert!(wl < floor, "octave {wl} must be truncated at Region");
|
||||||
|
}
|
||||||
|
// What remains is the terrain_detail band (>= 4,096 m), which Region
|
||||||
|
// samples ~10.8 times per cycle — the ridge-and-valley form it now shows.
|
||||||
|
assert!(4_096.0 > floor, "the 4,096 m terrain octave must survive");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// THE TRADE, recorded rather than hidden. An 8x reconstruction floor also
|
||||||
|
/// truncates the coast warp's two finest octaves (2,048 and 1,024 m) at
|
||||||
|
/// Region, and T-1160 added that band so there is "one coastline at every
|
||||||
|
/// rung". An earlier version of this test asserted the opposite — that the
|
||||||
|
/// 1,024 m warp octave must survive — and a 2x Nyquist floor honoured it.
|
||||||
|
///
|
||||||
|
/// It was changed deliberately, on the same reasoning that fixed the relief:
|
||||||
|
/// a 1,024 m coastline wiggle at 379.3 m per cell is 2.7 samples per cycle,
|
||||||
|
/// so drawing it draws NOISE, not coastline character. A rung cannot show
|
||||||
|
/// shape finer than its own cell, and pretending otherwise is what made this
|
||||||
|
/// rung unreadable. The warp is amplitude-capped sub-pixel on the working
|
||||||
|
/// grid (`WARP_AMPLITUDE_CAP_PX`), so what is lost at Region is small.
|
||||||
|
///
|
||||||
|
/// If a future pass wants the warp exempt, the fix is a relief-only floor
|
||||||
|
/// threaded through `derive_at_metres` — not a lower global multiple, which
|
||||||
|
/// would take the stucco back.
|
||||||
|
#[test]
|
||||||
|
fn coast_warp_fine_octaves_are_deliberately_truncated_at_region() {
|
||||||
|
let floor = effective_min_wavelength_m(StepCanvasRung::Region, 0, REGION_STEP_M);
|
||||||
|
for wl in [2_048.0, 1_024.0] {
|
||||||
|
assert!(
|
||||||
|
wl < floor,
|
||||||
|
"coast-warp octave {wl} survives Region's floor {floor} — if this \
|
||||||
|
fires, the multiple was lowered and the stucco is back"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// The coarse warp octaves — the ones that carry the coastline's actual
|
||||||
|
// shape at this scale — must still contribute.
|
||||||
|
assert!(
|
||||||
|
4_096.0 > floor,
|
||||||
|
"the coast warp must keep every octave Region can reconstruct"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Global is exempt: its floor would be ~70 km and would truncate the whole
|
||||||
|
/// coast-warp band. It needs none — the orbital derive leaves relief_q flat.
|
||||||
|
#[test]
|
||||||
|
fn global_takes_no_spacing_floor() {
|
||||||
|
let step_m = 35_267.0; // Ferrath Global
|
||||||
|
assert_eq!(
|
||||||
|
effective_min_wavelength_m(StepCanvasRung::Global, 0, step_m),
|
||||||
|
0.0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// District and Quarter sample far finer than any octave in play, so the
|
||||||
|
/// floor must be inert there — those rungs already read as terrain and this
|
||||||
|
/// change must not move them.
|
||||||
|
#[test]
|
||||||
|
fn floor_is_inert_at_the_rungs_that_already_resolve_their_band() {
|
||||||
|
for (rung, step_m) in [
|
||||||
|
(StepCanvasRung::District, 3.79_f64),
|
||||||
|
(StepCanvasRung::Quarter, 0.948_f64),
|
||||||
|
] {
|
||||||
|
let floor = effective_min_wavelength_m(rung, 0, step_m);
|
||||||
|
assert!(
|
||||||
|
floor < 128.0,
|
||||||
|
"{rung:?} floor {floor} would truncate the finest relief octave \
|
||||||
|
(128 m); this rung resolves its whole band and must not change"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The wire value is a coarser-detail REQUEST and still wins when it asks
|
||||||
|
/// for more truncation than the spacing requires.
|
||||||
|
#[test]
|
||||||
|
fn an_explicit_coarser_request_is_honoured_over_the_floor() {
|
||||||
|
let floor = effective_min_wavelength_m(StepCanvasRung::District, 4_096, 3.79);
|
||||||
|
assert_eq!(floor, 4_096.0);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn quantize_min_wl_m_for_rung_matches_layer_proxy_quantization() {
|
fn quantize_min_wl_m_for_rung_matches_layer_proxy_quantization() {
|
||||||
// Reusing the SAME banding function as the legacy carrier (see this
|
// Reusing the SAME banding function as the legacy carrier (see this
|
||||||
|
|||||||
Reference in New Issue
Block a user