feat(ui): the Atlas ladder bottoms out at Quarter (D-255, T-1213)
Quarter becomes the deepest navigable rung. Block and Chunk leave the ladder. The rule, from the amendment: the deepest Atlas rung is the one at which a screen pixel shows one subtile. At the uniform 2x2 px display ratio a 3440x1440 window gives 540 gridunits on the short axis, so Quarter's 512 m extent draws 0.948 m per gridunit -- about one voxel per gridunit and one 0.5 m subtile per pixel. Block (0.237) and Chunk (0.119) magnify beneath the finest datum that can exist, and measured as exactly that on Ferrath: one morphology zone, one vegetation class, an unbroken colour field. They were not missing a feature; there was nothing left to show them. CHUNK ITSELF IS UNTOUCHED. It remains D-243's 64 m stream/derive unit and is where Phase 5 derives first-person walkable content -- D-012's load-around-the-player is expressed in chunks. Block remains the 128 m generator planning unit. Both keep their enum variants, their extent_m answers and their wire vocabulary. What was retired is the claim that a MAP of one is worth looking at. DEEP_RUNGS moved with the floor, and this is the part worth reading twice. It is a mandatory D-255(d) hardening: a per-body retention cap bounding how much ground an exhaustive pan can hold resident at fine spacing. Left as [Block, Chunk] it would have guarded rungs no client can request -- dead code -- while the accumulation gap silently re-opened under Quarter, now the finest navigable rung at ~0.95 m per gridunit. It is now [District, Quarter]. A control that names its targets by rung has to follow the ladder when the ladder moves. Six tests pinned the old floor and were updated rather than deleted, since each was protecting a real property: the clamp tests now clamp at Quarter, and the disk-cache tests use Region for "shallow" (District is capped now) and Quarter for "deep". One new test pins the distinction the change turns on -- the retired rungs are absent from RUNG_LADDER but still present in RUNG_EXTENT_M, because viewability was retired, not vocabulary. Also removes the four capture scenarios for the retired rungs, including the two blank goldens that had been passing against blank captures. 1838 client tests, 0 failed. Server clippy clean, step_canvas suite green. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -80,13 +80,31 @@ pub enum StepCanvasRung {
|
||||
Region,
|
||||
/// Rung 2 — 2,048 m spacing (D-243 `DISTRICT_M`).
|
||||
District,
|
||||
/// Rung 3 — 512 m spacing (D-243 `QUARTER_M`).
|
||||
/// Rung 3 — 512 m extent (D-243 `QUARTER_M`). **The deepest NAVIGABLE
|
||||
/// rung** (D-255 amendment 2026-08-07): at the uniform 2×2 px display
|
||||
/// ratio it draws ~0.948 m per gridunit, about one voxel per gridunit and
|
||||
/// one 0.5 m subtile per screen pixel — the floor at which a further step
|
||||
/// would magnify rather than inform.
|
||||
Quarter,
|
||||
/// Rung 4 — 128 m spacing (D-243 `BLOCK_M`).
|
||||
/// Rung 4 — 128 m extent (D-243 `BLOCK_M`). **Retired from the navigable
|
||||
/// ladder** (D-255 amendment 2026-08-07): 0.237 m per gridunit is four
|
||||
/// gridunits per voxel, so it can only draw one voxel larger. Measured on
|
||||
/// Ferrath as a single morphology zone and a single vegetation class.
|
||||
///
|
||||
/// The variant is KEPT: it is still legal wire vocabulary, `extent_m`
|
||||
/// still answers for it, and `BLOCK_M` remains the generator's 128 m
|
||||
/// planning unit. Only the client's `RUNG_LADDER` stopped offering it.
|
||||
Block,
|
||||
/// Rung 5 — 64 m spacing (D-243 `CHUNK_M`), the deepest Atlas rung
|
||||
/// (D-255(a): tile/voxel is Phase-5 in-world content, never an Atlas
|
||||
/// rung).
|
||||
/// Rung 5 — 64 m extent (D-243 `CHUNK_M`). **Retired from the navigable
|
||||
/// ladder** (D-255 amendment 2026-08-07): 0.119 m per gridunit, eight
|
||||
/// gridunits per voxel.
|
||||
///
|
||||
/// Retired from the MAP only. The 64 m chunk remains D-243's stream/derive
|
||||
/// unit and is where Phase 5 derives first-person walkable content
|
||||
/// ([D-012](#d-012)'s load/unload-around-the-player is expressed in
|
||||
/// chunks) — it is arguably the most load-bearing unit in the cascade, and
|
||||
/// nothing about it changes here. What was removed is the claim that a
|
||||
/// *map* of one is worth looking at.
|
||||
Chunk,
|
||||
}
|
||||
|
||||
@@ -2469,4 +2487,117 @@ mod tests {
|
||||
);
|
||||
assert!(raw.settlement_id.iter().all(|&v| v == 0));
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// T-1213 diagnostic — where the ladder goes flat
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
/// Per-field variance down the rung ladder on a REAL body, at the real
|
||||
/// spacing, through the production canvas builder.
|
||||
///
|
||||
/// The descent ladder captured on 2026-08-06 showed every rung below
|
||||
/// Global rendering a uniform colour field. This reports WHICH field is
|
||||
/// flat and by how much, so the un-summarisation work targets a measured
|
||||
/// gap rather than an inferred one — the captures show the symptom, this
|
||||
/// shows which of the six derived fields produces it.
|
||||
///
|
||||
/// Reference numbers it exists to explain: Ferrath's heightmap is 1024x512
|
||||
/// over a 38,089 km circumference = 37.2 km per source pixel. Global draws
|
||||
/// at ~35.3 km/gridunit (about 1:1 with the source, and reads as a world);
|
||||
/// Region at 0.379 km/gridunit is ~98x finer than any stored datum, and
|
||||
/// District at ~0.0038 km/gridunit is ~9,800x finer. Everything below
|
||||
/// Global is therefore invented, not sampled.
|
||||
///
|
||||
/// `#[ignore]`: runs the full cascade for a body. Diagnostic, not a gate.
|
||||
///
|
||||
/// ```text
|
||||
/// cargo test --lib t1213_ladder_field_variance -- --ignored --nocapture
|
||||
/// ```
|
||||
#[test]
|
||||
#[ignore = "diagnostic — full cascade for one body; run explicitly"]
|
||||
fn t1213_ladder_field_variance() {
|
||||
let body = "GJ820Bc";
|
||||
let Ok((snapshot, params)) =
|
||||
crate::atlas::believability::cascade_snapshot_for_body(42, body)
|
||||
else {
|
||||
eprintln!("skip: {body} not loadable");
|
||||
return;
|
||||
};
|
||||
// `terrain_analysis` is transient and always None on a returned
|
||||
// snapshot — re-derive through the same call the cascade used. See
|
||||
// `believability::cascade_snapshot_for_body`'s doc.
|
||||
let (_l1, ta) = crate::atlas::layer1::run_layer1_with_moisture(
|
||||
&snapshot.heightmap,
|
||||
crate::atlas::district_profile::derive_moisture_ceiling_q(¶ms),
|
||||
);
|
||||
let river_network = snapshot
|
||||
.layer1
|
||||
.as_ref()
|
||||
.map(|l1| l1.river_network.clone())
|
||||
.unwrap_or_default();
|
||||
let placements = snapshot
|
||||
.layer3
|
||||
.as_ref()
|
||||
.map(|l3| l3.placements.clone())
|
||||
.unwrap_or_default();
|
||||
let climate = ClimateConstants::default();
|
||||
let seed = SeedChain::root(42).derive(crate::seed::SeedDomain::Body, 1);
|
||||
|
||||
// The land point the descent-ladder captures are anchored on (the
|
||||
// cascade's highest-scoring placement on this body).
|
||||
let center = (29_422_009_i64, -5_675_959_i64);
|
||||
// A 1290x540 canvas is what a 3440x1440 window produces at the 2x2
|
||||
// display ratio — the shape the captures were taken at.
|
||||
let extent = (1290u32, 540u32);
|
||||
|
||||
for rung in [
|
||||
StepCanvasRung::Global,
|
||||
StepCanvasRung::Region,
|
||||
StepCanvasRung::District,
|
||||
StepCanvasRung::Quarter,
|
||||
StepCanvasRung::Block,
|
||||
StepCanvasRung::Chunk,
|
||||
] {
|
||||
let raw = build_step_canvas(
|
||||
seed,
|
||||
body,
|
||||
¶ms,
|
||||
&ta,
|
||||
&river_network,
|
||||
&placements,
|
||||
rung,
|
||||
center,
|
||||
extent,
|
||||
&climate,
|
||||
0,
|
||||
);
|
||||
let spacing =
|
||||
rung.spacing_m(raw.width, raw.height, params.body_radius_km.unwrap_or(0.0));
|
||||
|
||||
let uniq_u8 = |v: &Vec<u8>| {
|
||||
v.iter().collect::<std::collections::BTreeSet<_>>().len()
|
||||
};
|
||||
let span_u8 = |v: &Vec<u8>| match (v.iter().min(), v.iter().max()) {
|
||||
(Some(a), Some(b)) => *b as i32 - *a as i32,
|
||||
_ => 0,
|
||||
};
|
||||
|
||||
println!(
|
||||
"{:8?} {:>9.1} m/gu {}x{} elev[uniq {:3} span {:3}] moist[uniq {:3} span {:3}] \
|
||||
morph[uniq {:2}] veg[uniq {:2}] glac[uniq {:2}] courses {}",
|
||||
rung,
|
||||
spacing,
|
||||
raw.width,
|
||||
raw.height,
|
||||
uniq_u8(&raw.elev_q),
|
||||
span_u8(&raw.elev_q),
|
||||
uniq_u8(&raw.moisture_q),
|
||||
span_u8(&raw.moisture_q),
|
||||
uniq_u8(&raw.morphology),
|
||||
uniq_u8(&raw.vegetation),
|
||||
uniq_u8(&raw.glaciation),
|
||||
raw.courses.len(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user