facett-map3d/tests/scene_scale_metric_constants.rs
//! **Every render/extrude constant that means a LENGTH is metric — asserted at TWO
//! SCALES, because one scale cannot see this defect.**
//!
//! Three separate bugs in this crate have been the same mistake: an absolute
//! design-unit constant weighed against a metrically scaled scene. `GROUND_LIFT` and
//! `BUILDING_RISE_EPS` were the first two. `extrude::WATER_HEIGHT` (`5.0e-3 du`) and
//! the fragment shader's height haze (`0.25 du`) were the third and fourth, plus the
//! SSAO radius (`0.16 du`).
//!
//! A design unit is **whatever the data's span makes it**: 625 m in the fixture,
//! 1875 m in the 3 km town window `korp` flies, 62 741 m in a region load. So an
//! absolute `5.0e-3 du` water plane is `3.1 m`, `9.4 m` and `174 m` of clearance in
//! those three scenes — the same literal, three different physical objects.
//!
//! **Why two scales, and why these two.** A guard that measures at one span cannot
//! distinguish `f(du_per_m)` from a constant: any single measurement is consistent
//! with both. Worse is the IDENTITY-VALUE trap — a scene that happens to sit near
//! `du_per_m == 1` makes a scaled constant and an unscaled one numerically equal. Both
//! spans here are decades away from `du_per_m == 1` (`5.3e-4` and `1.6e-5`) and a
//! factor **33 apart** from each other, so a constant that failed to scale shows up as
//! a 33× error and cannot hide.
//!
//! Each guard carries its own **RED**: the legacy absolute is run through the same
//! assertion and asserted to FAIL it. A guard that has never reported red is not a
//! guard (LAW 2), and these specific guards replace ones that could not.
use facett_map::MapPalette;
use facett_map3d::backend;
use facett_map3d::extrude;
use facett_map::Way;
/// A square town of `n×n` little building rings spread over `span_deg` of longitude /
/// latitude, centred on Stockholm's latitude so the metre-per-degree factor is real.
/// The SAME topology at every span — only the ground it covers changes, which is
/// exactly the variable these constants must track and the shape of the bug.
fn town(span_deg: f64, n: usize) -> Vec<Way> {
let pal = MapPalette::cartographic(true);
let (lon0, lat0) = (18.0_f64, 59.33_f64);
let step = span_deg / n as f64;
// A building footprint that stays a fixed FRACTION of the span, so the geometry is
// similar at both scales and only `du_per_m` moves.
let d = step * 0.35;
let mut ways = Vec::with_capacity(n * n + 1);
for j in 0..n {
for i in 0..n {
let (x, y) = (lon0 + i as f64 * step, lat0 + j as f64 * step);
let ring = [(x, y), (x + d, y), (x + d, y + d), (x, y + d), (x, y)];
if let Some(w) = Way::from_lonlat_with(
(j * n + i) as i64 + 1,
&ring,
&[("building".to_string(), "yes".to_string()), ("height".to_string(), "12".to_string())],
&pal,
) {
ways.push(w);
}
}
}
// One lake, so the water plane has something to be the plane OF.
let (x, y) = (lon0 + span_deg * 0.55, lat0 + span_deg * 0.55);
let lake = [(x, y), (x + span_deg * 0.2, y), (x + span_deg * 0.2, y + span_deg * 0.2), (x, y + span_deg * 0.2), (x, y)];
if let Some(w) = Way::from_lonlat_with(
900_001,
&lake,
&[("natural".to_string(), "water".to_string())],
&pal,
) {
ways.push(w);
}
ways
}
/// The 3 km town window `densest_cluster_subset` produces — `korp`'s actual viewport.
const CITY_DEG: f64 = 0.0270; // ≈ 3.0 km of latitude
/// A region load, ~17× wider. `du_per_m` here is `3.2e-5`: an absolute constant that
/// "looked right" in the city is off by 17× in this scene, and vice versa.
///
/// Deliberately kept INSIDE [`extrude::MIN_GROUND_BAND`]'s documented honest bound
/// (`du_per_m > 2.5e-5`, i.e. a span under ~64 km). Past that bound `ground_band` — and
/// `water_height`, which is floored above it — stop tracking the scene and start
/// meaning "a clear margin above the kerb" instead of an exact metre count. That is
/// documented, deliberate behaviour, and [`past_the_honest_bound_the_floor_takes_over`]
/// asserts it *as such* rather than letting this table quietly tolerate it.
const REGION_DEG: f64 = 0.4500; // ≈ 50 km of latitude
/// Past the honest bound — where `MIN_GROUND_BAND` governs instead of the metre.
const CONTINENT_DEG: f64 = 0.9000; // ≈ 100 km of latitude
/// The two scenes, with their measured scales — printed so a failure reads as a
/// number and not as a boolean (LAW 7).
fn two_scales() -> ((Vec<Way>, f32), (Vec<Way>, f32)) {
let city = town(CITY_DEG, 14);
let region = town(REGION_DEG, 14);
let dc = extrude::design_units_per_metre(&city).expect("the city has a world span");
let dr = extrude::design_units_per_metre(®ion).expect("the region has a world span");
println!(
" city span {:8.0} m du_per_m {dc:.3e} metres_per_du {:9.1}",
extrude::world_span_metres(&city).unwrap(),
1.0 / dc
);
println!(
" region span {:8.0} m du_per_m {dr:.3e} metres_per_du {:9.1}",
extrude::world_span_metres(®ion).unwrap(),
1.0 / dr
);
assert!(dc / dr > 10.0, "the two scales are decades apart, not a rounding error: {:.1}×", dc / dr);
assert!(dr > 2.5e-5, "the region stays inside MIN_GROUND_BAND's honest bound ({dr:e})");
assert!(dc < 0.1 && dr < 0.1, "neither scene sits near the du_per_m == 1 identity trap ({dc:e}, {dr:e})");
((city, dc), (region, dr))
}
/// A design-unit length read back as **metres** at the scale it was computed for.
fn metres(du: f32, du_per_m: f32) -> f32 {
du / du_per_m
}
/// **THE guard.** Each metric length must read the SAME number of metres in both
/// scenes; the legacy absolute must not. Every constant, both directions, one table.
#[test]
fn every_length_constant_means_the_same_metres_at_both_scales() {
let ((city, dc), (region, dr)) = two_scales();
// (name, du at city, du at region, the metres it claims to be, tolerance %)
let rows: Vec<(&str, f32, f32, f32)> = vec![
("ground_band", extrude::ground_band(Some(dc)), extrude::ground_band(Some(dr)), extrude::GROUND_BAND_METRES),
(
"building_rise_eps",
extrude::building_rise_eps(Some(dc)),
extrude::building_rise_eps(Some(dr)),
extrude::BUILDING_RISE_METRES,
),
("water_height", extrude::water_height(Some(dc)), extrude::water_height(Some(dr)), extrude::WATER_RISE_METRES),
(
"haze_scale_height",
backend::haze_scale_height(Some(dc)),
backend::haze_scale_height(Some(dr)),
backend::HAZE_SCALE_HEIGHT_METRES,
),
("ssao_radius", backend::ssao_radius(Some(dc)), backend::ssao_radius(Some(dr)), backend::SSAO_RADIUS_METRES),
// The route overlay's lift — band-relative, so its metre value is the band's
// times ROUTE_LIFT_BANDS rather than a constant of its own.
(
"route_lift",
extrude::route_lift(Some(dc)),
extrude::route_lift(Some(dr)),
extrude::GROUND_BAND_METRES * extrude::ROUTE_LIFT_BANDS,
),
// The water lane's last two raw design units: the Gerstner amplitude and the
// base wavelength, both world lengths `water.wgsl` displaces vertices by.
(
"wave_amplitude",
extrude::wave_amplitude(Some(dc)),
extrude::wave_amplitude(Some(dr)),
extrude::WAVE_AMPLITUDE_METRES,
),
(
"wave_wavelength",
extrude::wave_wavelength(Some(dc)),
extrude::wave_wavelength(Some(dr)),
extrude::WAVE_WAVELENGTH_METRES,
),
// …and the grid that has to carry them, derived from the wavelength.
(
"water_grid_cell",
extrude::water_grid_cell(Some(dc)),
extrude::water_grid_cell(Some(dr)),
extrude::WAVE_WAVELENGTH_METRES * extrude::GERSTNER_OCTAVE_WAVELENGTHS[2] / 4.0,
),
];
println!("\n {:<20}{:>13}{:>13}{:>12}{:>12}{:>10}", "constant", "city du", "region du", "city m", "region m", "want m");
let mut failed = Vec::new();
for (name, du_city, du_region, want_m) in &rows {
let (m_city, m_region) = (metres(*du_city, dc), metres(*du_region, dr));
println!(" {name:<20}{du_city:>13.3e}{du_region:>13.3e}{m_city:>12.3}{m_region:>12.3}{want_m:>10.3}");
// The design-unit values MUST differ — if they agree, the constant is absolute.
if (du_city - du_region).abs() / du_city.max(1e-12) < 0.05 {
failed.push(format!("{name}: same design units at both scales ⇒ it is an ABSOLUTE, not a length"));
}
// …and the metres MUST agree, with each other and with the stated constant.
for (label, got) in [("city", m_city), ("region", m_region)] {
if (got - want_m).abs() / want_m > 0.05 {
failed.push(format!("{name} at {label} scale: {got:.3} m, but the constant says {want_m:.3} m"));
}
}
}
assert!(failed.is_empty(), "metric constants that are not metric:\n - {}", failed.join("\n - "));
// The scenes are the ones the constants were read from — not a detached table.
let (_, _, water_city) = extrude::build_with_water(&city, 0.22);
let (_, _, water_region) = extrude::build_with_water(®ion, 0.22);
for (label, w, dpm) in [("city", &water_city, dc), ("region", &water_region, dr)] {
assert!(w.triangle_count() > 0, "{label}: the lake tessellated");
let y = w.positions[0][1];
assert!(
w.positions.iter().all(|p| (p[1] - y).abs() < 1e-9),
"{label}: one water plane, not several"
);
let m = metres(y, dpm);
assert!(
(m - extrude::WATER_RISE_METRES).abs() < 0.05,
"{label}: the BUILT water grid sits {m:.3} m up, want {:.3} m",
extrude::WATER_RISE_METRES
);
}
}
/// **"Your route" is painted ON the street, at both scales — asserted on the EMITTED
/// route vertices, not on a constant.**
///
/// `ROUTE_LIFT` was the one constant DERIVED from `GROUND_LIFT` (`× 1.6`) and the one
/// the `GROUND_LIFT` fix did not take with it: the streets moved to
/// `ground_band(du_per_m)` and the route overlay stayed absolute. In `korp`'s 3 km town
/// window that is 3.6 m of ribbon over a road top at 0.49 m — a ratio of 7.4, so the
/// route floats ~3.1 m over the street it claims to be painted on, in a scene where a
/// storey is 3 m. The doc on it promises "sub-pixel … reads as painted on the ground".
///
/// The bound is the scene's OWN road top ([`extrude::ground_top`] of its band), so this
/// is a relation between two things in the same scene rather than a number to re-tune.
#[test]
fn the_route_ribbon_is_painted_on_the_street_at_both_scales() {
let ((city, dc), (region, dr)) = two_scales();
/// The most a route ribbon may float above the road tops, in metres. It has to
/// clear them (no z-fight) and it has to read as paint, not as a bridge.
const MAX_FLOAT_M: f32 = 0.5;
for (label, ways, dpm) in [("city", &city, dc), ("region", ®ion, dr)] {
// A two-point route through the middle of the town, in the Mercator-unit space
// `project_route` expects (the same space `Way::points` lives in).
let pts: Vec<(f32, f32)> = vec![ways[0].points[0], ways[ways.len() - 2].points[0]];
let route = extrude::project_route(ways, &pts);
assert!(route.len() == 2, "{label}: the route projected");
let y = route[0][1];
let road_top = extrude::ground_top(extrude::ground_band(Some(dpm)));
let float_m = metres(y - road_top, dpm);
println!(
" {label:<8} route y {y:.3e} du ({:.3} m) road top {road_top:.3e} du ({:.3} m) \
floats {float_m:>7.3} m",
metres(y, dpm),
metres(road_top, dpm)
);
assert!(y > road_top, "{label}: the route must draw OVER the streets (no z-fight)");
assert!(
float_m <= MAX_FLOAT_M,
"{label}: \"your route\" floats {float_m:.2} m over the street it is painted on \
(a storey is 3 m) — the ribbon lift is absolute while the streets are metric"
);
}
}
/// **RED.** The same assertion, run against the legacy absolutes the fix replaced.
/// It must FAIL — that is the proof the guard above can see the disease it guards.
/// Every number here is what the shipped code actually did on 2026-08-04.
#[test]
fn the_legacy_absolutes_fail_the_two_scale_guard() {
let ((_, dc), (_, dr)) = two_scales();
let absolutes: [(&str, f32); 10] = [
("WATER_HEIGHT", extrude::WATER_HEIGHT),
// The water lane's legacy raw design units.
("WAVE_AMPLITUDE_DU", extrude::WAVE_AMPLITUDE_DU),
("WAVE_WAVELENGTH_DU", extrude::WAVE_WAVELENGTH_DU),
("WATER_GRID_CELL_DU", extrude::WATER_GRID_CELL_DU),
("haze layer (0.25 du)", backend::HAZE_SCALE_HEIGHT_DU),
("SSAO radius (0.16 du)", backend::SSAO_RADIUS_DU),
// The route-overlay family: the constants derived from GROUND_LIFT /
// ROAD_MIN_HALF_W that the metric fixes had not taken with them.
("ROUTE_LIFT", extrude::ROUTE_LIFT),
("ROUTE_HALF_W", extrude::ROUTE_HALF_W),
("ROUTE_MARKER_R", extrude::ROUTE_MARKER_R),
("CHEVRON_CADENCE_DU", extrude::CHEVRON_CADENCE_DU),
];
println!("\n {:<24}{:>12}{:>12}{:>10}", "absolute", "city m", "region m", "ratio");
for (name, du) in absolutes {
let (m_city, m_region) = (metres(du, dc), metres(du, dr));
let ratio = m_region / m_city;
println!(" {name:<24}{m_city:>12.1}{m_region:>12.1}{ratio:>10.1}×");
assert!(
ratio > 10.0,
"{name} would have to disagree with itself across the two scales for the guard \
above to be meaningful — it does not, so the guard is blind ({ratio:.1}×)"
);
}
}
/// The two names for the scene's scale are **one number**, not two — `lod`'s
/// `metres_per_design_unit` and `extrude`'s `design_units_per_metre` are exact
/// reciprocals by definition (both are `DESIGN_FILL / world_span_metres`), and this
/// asserts the definition rather than trusting the doc comment that states it.
#[test]
fn scene_scale_is_one_number() {
for (label, ways) in [("city", town(CITY_DEG, 8)), ("region", town(REGION_DEG, 8))].iter() {
let (label, ways) = (*label, ways);
let du_per_m = extrude::design_units_per_metre(ways).expect("has a span");
let m_per_du = facett_map3d::metres_per_design_unit(ways);
let rel = ((1.0 / du_per_m) - m_per_du).abs() / m_per_du;
println!(" {label:<8} 1/du_per_m {:12.4} metres_per_du {m_per_du:12.4} rel {rel:.2e}", 1.0 / du_per_m);
assert!(rel < 1e-5, "{label}: the two scale names disagree by {rel:.2e} — that is two scales, not one");
}
}
/// **The `None` arm — the ONLY branch where the two scale names can actually diverge.**
///
/// [`scene_scale_is_one_number`] sweeps `city` and `region`, i.e. the two CALIBRATED
/// scenes, where both names are `DESIGN_FILL / world_span_metres` by construction and
/// cannot disagree. The branch that can is the span-less one: `design_units_per_metre`
/// returns `None` and every consumer picks its OWN fallback, while
/// `lod::metres_per_design_unit` silently returns [`lod::DEFAULT_METRES_PER_DESIGN_UNIT`]
/// as a naked `f32` no caller can tell apart from a calibrated value (the
/// identity-value trap: 625 is a perfectly believable number).
///
/// This asserts the fallback through the **applied output** — the design-space `y` the
/// relief heightfield actually emits for a known metric relief — not through a constant
/// round-trip. Recovering `metres per design unit` from the emitted mesh must land on
/// `lod`'s default; a second, private fallback shows up as a ratio.
///
/// RED before the fix (`extrude.rs` relief field carried its own `unwrap_or(2.0e-4)`):
/// the mesh says 5000 m/du where the LOD lever says 625 — **8× apart, on precisely the
/// span-less data both are written for**.
#[test]
fn the_spanless_fallback_is_the_same_number_on_both_sides() {
/// A pure north-south ramp over the relief builder's own procedural patch
/// (lat 59.00 → 59.05 when the data has no bbox): 0 m at the south edge,
/// `RELIEF_M` at the north. Deterministic, and its peak deviation (±5 m) is far
/// under `MAX_TERRAIN_RELIEF / du_per_m` at either candidate scale, so the
/// auto-fit ceiling cannot mask the number under test.
struct Ramp;
const RELIEF_M: f32 = 10.0;
impl facett_map3d::terrain::Elevation for Ramp {
fn elevation_at(&self, _lon_deg: f64, lat_deg: f64) -> f32 {
(((lat_deg - 59.0) / 0.05) as f32).clamp(0.0, 1.0) * RELIEF_M
}
}
// Span-less data: the fixture / degenerate case. Both names must agree HERE.
let ways: Vec<Way> = Vec::new();
assert!(
extrude::design_units_per_metre(&ways).is_none(),
"this fixture must actually be the None branch, or the guard tests nothing"
);
let lod_default = facett_map3d::metres_per_design_unit(&ways);
let mut mesh = extrude::Mesh3::default();
extrude::relief_terrain_into(&mut mesh, &ways, &Ramp, 24, 1.0);
let (lo, hi) = mesh
.positions
.iter()
.fold((f32::MAX, f32::MIN), |(lo, hi), p| (lo.min(p[1]), hi.max(p[1])));
let span_du = hi - lo;
assert!(span_du > 0.0, "the ramp must produce relief at all ({span_du:e} du)");
// What the emitted geometry says one design unit is worth, in metres.
let mesh_m_per_du = RELIEF_M / span_du;
let ratio = mesh_m_per_du / lod_default;
println!(
" spanless: relief {RELIEF_M} m → {span_du:.5} du ⇒ {mesh_m_per_du:.1} m/du \
lod default {lod_default:.1} m/du ratio {ratio:.2}×"
);
assert!(
(ratio - 1.0).abs() < 0.02,
"the relief field and the LOD lever disagree about the span-less scale by {ratio:.2}× \
(mesh {mesh_m_per_du:.1} m/du vs lod {lod_default:.1} m/du) — that is two writers of \
one number, on the only data where they can differ"
);
}
/// **Past the honest bound the FLOOR governs, not the metre — and that is on purpose.**
///
/// `ground_band` stops shrinking at [`extrude::MIN_GROUND_BAND`] (`du_per_m < 2.5e-5`,
/// a span past ~64 km) so the flat-cover band never sinks under the `f32` depth
/// epsilon, and `water_height` is floored above it for the same reason. So at 100 km
/// the band reads ~0.6 m and the water ~0.87 m rather than the stated 0.4 / 0.6.
///
/// A guard that let the main table tolerate this would be tolerating exactly the
/// symptom it exists to catch. Asserted separately, as the documented behaviour:
/// **the value is the floor, and the ORDERING still holds.**
#[test]
fn past_the_honest_bound_the_floor_takes_over() {
let ways = town(CONTINENT_DEG, 10);
let dpm = extrude::design_units_per_metre(&ways).expect("has a span");
assert!(dpm < 2.5e-5, "this scene is past MIN_GROUND_BAND's bound ({dpm:e})");
let band = extrude::ground_band(Some(dpm));
let water = extrude::water_height(Some(dpm));
println!(
" 100 km: du_per_m {dpm:e} band {:.3} m (metric would be {:.3}) water {:.3} m",
band / dpm,
extrude::GROUND_BAND_METRES,
water / dpm
);
assert!(
(band - extrude::MIN_GROUND_BAND).abs() < 1e-9,
"the band is pinned at MIN_GROUND_BAND, not at the metric value: {band:e}"
);
assert!(band / dpm > extrude::GROUND_BAND_METRES, "the floor is LOOSER than the metre here, never tighter");
// The ordering that actually matters survives the floor.
assert!(water > extrude::ground_top(band), "water still clears the cover past the bound");
assert!(water < extrude::MIN_DEFAULT_BUILDING_HEIGHT_M * dpm, "water still stays under the shortest house");
}
/// Data with **no world span at all** keeps every legacy absolute, so a scene the
/// extruder cannot scale renders byte-for-byte as before the metric fix.
///
/// **The `None` branch is narrower than it reads.** `Map3D::synthetic` — the rabbit
/// fixture — carries real lon/lat and therefore DOES have a span (`du_per_m ≈ 1.9e-4`,
/// ~5.2 km), so it takes the METRIC path, not this one. Recorded here because the doc
/// comments on `ground_band` / `building_rise_eps` call the fixture the archetypal
/// `None` case and it is not one; only genuinely span-less input reaches these lines.
#[test]
fn span_less_scenes_keep_the_legacy_absolutes() {
assert_eq!(extrude::water_height(None), extrude::WATER_HEIGHT);
assert_eq!(extrude::wave_amplitude(None), extrude::WAVE_AMPLITUDE_DU);
assert_eq!(extrude::wave_wavelength(None), extrude::WAVE_WAVELENGTH_DU);
assert_eq!(extrude::water_grid_cell(None), extrude::WATER_GRID_CELL_DU);
assert_eq!(backend::haze_scale_height(None), backend::HAZE_SCALE_HEIGHT_DU);
assert_eq!(backend::ssao_radius(None), backend::SSAO_RADIUS_DU);
assert_eq!(extrude::ground_band(None), extrude::GROUND_LIFT);
assert_eq!(extrude::design_units_per_metre(&[]), None, "empty data has no scale");
let rabbit = facett_map3d::Map3D::synthetic("fixture");
let dpm = rabbit
.design_units_per_metre()
.expect("MEASURED 2026-08-04: the rabbit fixture DOES carry a world span");
println!(" rabbit fixture: du_per_m {dpm:e} ⇒ metres_per_du {:.1}", 1.0 / dpm);
assert!(dpm > 0.0, "…so the fixture is on the metric path, not the legacy one");
}
/// **The water plane sits between the ground cover and the shortest house — at BOTH
/// scales.** The physical property `WATER_HEIGHT` was supposed to have and had at
/// exactly one span. Stated as an ordering, so it cannot pass by sitting on a number.
#[test]
fn water_sits_above_the_ground_cover_and_below_the_houses_at_both_scales() {
for (label, dpm) in [("city", extrude::design_units_per_metre(&town(CITY_DEG, 8)).unwrap()),
("region", extrude::design_units_per_metre(&town(REGION_DEG, 8)).unwrap())] {
let top = extrude::ground_top(extrude::ground_band(Some(dpm)));
let water = extrude::water_height(Some(dpm));
let house = extrude::MIN_DEFAULT_BUILDING_HEIGHT_M * dpm;
println!(
" {label:<8} ground_top {:8.3} m water {:8.3} m min house {:8.3} m",
metres(top, dpm),
metres(water, dpm),
metres(house, dpm)
);
assert!(water > top, "{label}: water ({water:e}) must clear the ground cover ({top:e})");
assert!(water < house, "{label}: water ({water:e}) must stay under the shortest house ({house:e})");
}
}
/// **The sea does not cut through the ground it sits on — at BOTH scales, and by
/// CONSTRUCTION rather than by tuning.**
///
/// `water.wgsl` sums three Gerstner octaves, so the deepest a trough can reach below
/// the water plane is [`extrude::gerstner_octave_sum`] times the base amplitude. The
/// plane itself is [`extrude::water_height`] above the terrain quad at `y = 0`. The
/// property is therefore one inequality, and [`extrude::wave_amplitude`] enforces it
/// with a cap instead of leaving it to whoever last edited a literal.
///
/// The `y` under test is read off the **built water mesh**, not off the constant —
/// `build_with_water` is the thing the renderer uploads.
///
/// RED (below): the shipped absolute `0.0020 du` is 12.5× the whole clearance in the
/// 3 km town window, so the troughs pass clean through the terrain.
#[test]
fn the_summed_wave_trough_never_reaches_the_terrain_plane() {
let ((city, dc), (region, dr)) = two_scales();
let sum = extrude::gerstner_octave_sum();
assert!(sum > 1.0, "the octaves sum to more than the base amplitude ({sum})");
for (label, ways, dpm) in [("city", &city, dc), ("region", ®ion, dr)] {
let (_, _, water) = extrude::build_with_water(ways, 0.22);
assert!(water.triangle_count() > 0, "{label}: the lake tessellated");
let plane = water.positions[0][1];
let amp = extrude::wave_amplitude(Some(dpm));
let trough = plane - sum * amp;
println!(
" {label:<8} plane {:7.3} m amp {:7.3} m summed trough {:7.3} m \
clearance {:7.3} m",
metres(plane, dpm),
metres(amp, dpm),
metres(sum * amp, dpm),
metres(trough, dpm)
);
assert!(
trough > 0.0,
"{label}: the summed wave trough reaches {:.3} m — BELOW the terrain plane the \
water is drawn on, so the sea cuts through the ground",
metres(trough, dpm)
);
// …and the amplitude is still a real chop, not a cap that flattened it away.
assert!(
metres(amp, dpm) > 0.5 * extrude::WAVE_AMPLITUDE_METRES,
"{label}: the cap swallowed the metric amplitude ({:.3} m of a wanted {:.3} m) — \
the constant is decoration if the clamp always wins",
metres(amp, dpm),
extrude::WAVE_AMPLITUDE_METRES
);
}
}
/// **RED for [`the_summed_wave_trough_never_reaches_the_terrain_plane`].** The same
/// inequality, run against the amplitude the shipped code actually used
/// (`0.0020 du`, absolute). It must FAIL at both scales — otherwise the guard above is
/// passing on a property that was never violated and proves nothing.
#[test]
fn the_legacy_wave_amplitude_cuts_through_the_ground() {
let ((_, dc), (_, dr)) = two_scales();
let sum = extrude::gerstner_octave_sum();
for (label, dpm) in [("city", dc), ("region", dr)] {
let plane = extrude::water_height(Some(dpm));
let trough = plane - sum * extrude::WAVE_AMPLITUDE_DU;
println!(
" {label:<8} legacy amp {:9.1} m plane {:7.3} m trough {:11.1} m \
overshoot {:6.1}×",
metres(extrude::WAVE_AMPLITUDE_DU, dpm),
metres(plane, dpm),
metres(trough, dpm),
sum * extrude::WAVE_AMPLITUDE_DU / plane
);
assert!(
trough < 0.0,
"{label}: the legacy absolute would have had to already satisfy the guard for it \
to mean anything — it does not violate the inequality, so the guard is blind"
);
}
}
/// **The Rust copy of the shader's octave multipliers cannot drift from the shader.**
///
/// [`extrude::wave_amplitude`]'s cap is only sound if
/// [`extrude::gerstner_octave_sum`] is the sum the GPU actually applies, and those
/// three multipliers live in `water.wgsl`. Two copies of one number is exactly the
/// shape LAW 5 forbids, and a doc comment saying "keep these in sync" is not a guard —
/// so this reads the multipliers back out of the shader source.
///
/// Carries its own RED: the same matcher is run over a MUTATED copy of the shader and
/// asserted to fail, so a matcher that silently matches nothing cannot pass.
#[test]
fn the_shader_octaves_match_the_rust_copy() {
const SRC: &str = include_str!("../src/water.wgsl");
/// What the shader must contain if it applies exactly these multipliers.
fn octaves_missing_from(src: &str) -> Vec<String> {
let mut missing = Vec::new();
let calls = src.matches("gerstner(p, t,").count();
if calls != extrude::GERSTNER_OCTAVE_AMPLITUDES.len() {
missing.push(format!("{calls} gerstner octaves in the shader, {} in Rust", extrude::GERSTNER_OCTAVE_AMPLITUDES.len()));
}
for (i, (a, w)) in extrude::GERSTNER_OCTAVE_AMPLITUDES
.iter()
.zip(extrude::GERSTNER_OCTAVE_WAVELENGTHS.iter())
.enumerate()
.skip(1)
{
for needle in [format!("amp * {a}"), format!("base_wl * {w}")] {
if !src.contains(&needle) {
missing.push(format!("octave {i}: shader has no `{needle}`"));
}
}
}
// Octave 0 is applied bare — `amp,` and `base_wl,` with no multiplier.
for needle in ["base_wl,", " amp,"] {
if !src.contains(needle) {
missing.push(format!("octave 0: shader has no bare `{needle}`"));
}
}
missing
}
let missing = octaves_missing_from(SRC);
println!(
" rust amps {:?} wavelens {:?} sum {:.2}",
extrude::GERSTNER_OCTAVE_AMPLITUDES,
extrude::GERSTNER_OCTAVE_WAVELENGTHS,
extrude::gerstner_octave_sum()
);
assert!(missing.is_empty(), "water.wgsl and the Rust copy have drifted:\n - {}", missing.join("\n - "));
// RED: mutate the shader's second octave and the matcher must see it.
let mutated = SRC.replacen(
&format!("amp * {}", extrude::GERSTNER_OCTAVE_AMPLITUDES[1]),
"amp * 0.91",
1,
);
assert_ne!(mutated, SRC, "the mutation must actually change the source, or the RED is vacuous");
let caught = octaves_missing_from(&mutated);
println!(" RED: mutated octave 1 ⇒ {} finding(s): {caught:?}", caught.len());
assert!(!caught.is_empty(), "the matcher passed a shader whose octave 1 was changed — it matches nothing");
}
/// **The water grid actually resolves the wave it carries — measured on the EMITTED
/// vertices, not on the constants.**
///
/// The cell size and the wavelength were two independent literals (`0.018 du` and
/// `0.14 du`), self-consistent at one scale and at no other. They are one number now
/// ([`extrude::water_grid_cell`] is derived from [`extrude::wave_wavelength`]), and
/// this measures the spacing the tessellator really emitted against the shortest
/// octave the shader really displaces by.
///
/// **Honest bound:** the per-ring resolution is clamped to a vertex budget (96), so a
/// lake wider than `96 ·` cell is under-resolved on purpose. The assertion is
/// therefore stated as "either the budget is saturated, or the wave is resolved" —
/// and it PRINTS which arm it took, so a scene that silently slid into the budget arm
/// is visible rather than green.
#[test]
fn the_water_grid_resolves_the_shortest_octave_or_says_it_is_budget_bound() {
let ((city, dc), (region, dr)) = two_scales();
for (label, ways, dpm) in [("city", &city, dc), ("region", ®ion, dr)] {
let (_, _, water) = extrude::build_with_water(ways, 0.22);
assert!(water.triangle_count() > 0, "{label}: the lake tessellated");
// The emitted grid is axis-aligned; the smallest non-zero x gap between two
// vertices IS the cell the tessellator chose.
let mut xs: Vec<f32> = water.positions.iter().map(|p| p[0]).collect();
xs.sort_by(|a, b| a.partial_cmp(b).unwrap());
let gap = xs
.windows(2)
.map(|w| w[1] - w[0])
.filter(|d| *d > 1e-9)
.fold(f32::MAX, f32::min);
assert!(gap < f32::MAX, "{label}: the grid has more than one column");
let shortest = extrude::wave_wavelength(Some(dpm)) * extrude::GERSTNER_OCTAVE_WAVELENGTHS[2];
let per_wave = shortest / gap;
let budget_bound = gap > extrude::water_grid_cell(Some(dpm)) * 1.5;
println!(
" {label:<8} cell {:8.3} m shortest octave {:8.3} m {per_wave:5.2} cells/wave \
{} verts {}",
metres(gap, dpm),
metres(shortest, dpm),
if budget_bound { "BUDGET-BOUND" } else { "resolved" },
water.vertex_count()
);
assert!(
per_wave >= 3.0 || budget_bound,
"{label}: {per_wave:.2} cells across the shortest wave and the vertex budget is NOT \
saturated — the grid and the wavelength have drifted apart again"
);
// A budget-bound ring must cost the BUDGET density, not the ceiling: the whole
// point of dropping to it is that spending 26× the vertices only buys aliasing.
if budget_bound {
let ceiling = (extrude::WATER_MAX_RES + 1) * (extrude::WATER_MAX_RES + 1);
assert!(
water.vertex_count() < ceiling / 4,
"{label}: budget-bound and still {} verts of a {ceiling}-vertex ceiling — the \
fallback did not fire and the ring pays full price to alias its ripple",
water.vertex_count()
);
}
}
}
/// **The wave decides the grid, and the budget decides when to stop — both arms
/// exercised, on the SAME function the extruder calls.**
///
/// [`extrude::water_grid_res`] has two arms and only one of them fires on the scenes
/// the metric table sweeps, so a table-only guard would leave the resolving arm
/// untested. Here the ring extent is swept across the crossover and both arms are
/// asserted, including that the crossover is where the derivation says it is.
///
/// The RED it replaces: the shipped `extent / 0.018` gave the SAME resolution for a
/// pond and for a lake at every scene span, because `0.018` is a fraction of the scene
/// and not a length — so no ring size could ever have moved it.
#[test]
fn the_grid_tracks_the_wave_until_the_vertex_budget_binds() {
let ((city, dc), _) = two_scales();
let _ = city;
let cell = extrude::water_grid_cell(Some(dc));
let crossover = cell * extrude::WATER_MAX_RES as f32;
println!(
" cell {:.3} m crossover ring {:.1} m budget res {} max res {}",
metres(cell, dc),
metres(crossover, dc),
extrude::WATER_BUDGET_RES,
extrude::WATER_MAX_RES
);
// A pond well inside the budget: the resolution tracks the extent, linearly.
let (small, twice) = (crossover * 0.25, crossover * 0.5);
let (rs, rt) = (extrude::water_grid_res(small, Some(dc)), extrude::water_grid_res(twice, Some(dc)));
println!(" ring {:7.1} m ⇒ res {rs:3} ring {:7.1} m ⇒ res {rt:3}", metres(small, dc), metres(twice, dc));
assert!(rs >= extrude::WATER_MIN_RES && rt <= extrude::WATER_MAX_RES, "both stay inside the budget");
assert!(
(rt as f32 / rs as f32 - 2.0).abs() < 0.15,
"a ring twice as wide wants twice the cells ({rs} → {rt}) — the grid is not tracking the wave"
);
// Past the crossover the budget takes over, and it costs the BUDGET density.
let big = crossover * 4.0;
let rb = extrude::water_grid_res(big, Some(dc));
println!(" ring {:7.1} m ⇒ res {rb:3} (budget arm)", metres(big, dc));
assert_eq!(rb, extrude::WATER_BUDGET_RES, "past the budget the ring drops to the budget density");
assert!(
rb < rt,
"the budget arm must be CHEAPER than the ring half its size ({rb} vs {rt}) — otherwise \
the fallback is spending vertices to alias"
);
// Span-less scenes are byte-for-byte what they were: the legacy fraction.
let legacy = extrude::water_grid_res(0.32, None);
println!(" span-less ring 0.32 du ⇒ res {legacy} (legacy extent/{})", extrude::WATER_GRID_CELL_DU);
assert_eq!(legacy, (0.32_f32 / extrude::WATER_GRID_CELL_DU).ceil() as usize);
}
/// **The street-label LOD ladder is the SAME ruler as the altitude ladder — measured
/// on the labels the gate actually admits, at two scales.**
///
/// `facett-map3d` carried two LOD ladders: `lod`'s, in honest metres
/// (`WATER_ON_ALTITUDE_M = 300.0`), and `Map3D::label_lod_distance`'s, in raw design
/// units (`0.95 / 0.45 / 0.18 / 0.10`). In `korp`'s 3 km town window a design unit is
/// ~1 800 m, so "residential/service: only near street level" gated at ~324 m; in a
/// region load the same literal is kilometres. The ladder measured the VIEWPORT, not
/// the ground.
///
/// The oracle is `Map3D::street_labels_gated_in`, not the constant — a count of the
/// anchors the gate lets through, which is what a viewer sees. `street_labels` (how
/// many EXIST) cannot move and could never have shown this.
#[test]
fn the_label_ladder_gates_on_metres_at_both_scales() {
use facett_map::style::Lod;
/// The town, plus a named street of every LOD tier laid across it — the fixture
/// `town()` has no roads, so a ladder guard built on it alone would be vacuous.
fn town_with_streets(span_deg: f64) -> Vec<Way> {
let pal = MapPalette::cartographic(true);
let (lon0, lat0) = (18.0_f64, 59.33_f64);
let mut ways = town(span_deg, 14);
for (i, class) in ["motorway", "secondary", "residential", "footway"].iter().enumerate() {
let y = lat0 + span_deg * (0.2 + 0.15 * i as f64);
let line = [(lon0, y), (lon0 + span_deg, y)];
if let Some(w) = Way::from_lonlat_with(
800_001 + i as i64,
&line,
&[
("highway".to_string(), (*class).to_string()),
("name".to_string(), format!("{class} street")),
],
&pal,
) {
ways.push(w);
}
}
ways
}
let (city, region) = (town_with_streets(CITY_DEG), town_with_streets(REGION_DEG));
let dc = extrude::design_units_per_metre(&city).expect("the city has a world span");
let dr = extrude::design_units_per_metre(®ion).expect("the region has a world span");
assert!(dc / dr > 10.0, "the two scales stay decades apart with the streets in ({:.1}×)", dc / dr);
// The probe is chosen so the guard can DISCRIMINATE, which is a stronger property
// than "the numbers agree". At 400 m the metric ladder admits exactly the
// `Country` rung (600 m) and no other, in BOTH scenes. The legacy design-unit
// ladder admits two in the city (its rungs land at 1702 / 806 / 322 m there) and
// all three in the region (28 362 / 13 435 / 5 374 m) — so 2 ≠ 3, and the
// equality below is a real assertion instead of an arithmetic identity.
// `the_legacy_label_ladder_gates_on_the_viewport` runs exactly that arm and
// asserts it fails. A first draft of this test probed at 90 m, where the legacy
// ladder also admits every rung at both scales: the equality would have held for
// the broken ladder too.
const PROBE_M: f32 = 400.0;
let mut counts = Vec::new();
for (label, ways, dpm) in [("city", &city, dc), ("region", ®ion, dr)] {
let map = facett_map3d::Map3D::from_ways(label, ways, 0.22);
let mpu = map.metres_per_design_unit();
assert!(
((1.0 / dpm) - mpu).abs() / mpu < 1e-3,
"{label}: the Map3D read a different scale ({mpu}) than the table ({})",
1.0 / dpm
);
// The dolly that puts the eye PROBE_M metres out, in this scene's units.
let dolly_du = PROBE_M / mpu;
let admitted = map.street_labels_gated_in(dolly_du);
println!(
" {label:<8} m/du {mpu:9.1} dolly {dolly_du:.3e} du = {PROBE_M} m \
anchors {:4} gated in {admitted:4}",
map.street_label_count()
);
// The tier boundaries themselves, read through the scene's own scale.
for lod in [Lod::Country, Lod::Region, Lod::City, Lod::Detail] {
let edge_m = facett_map3d::Map3D::label_lod_distance_m(lod);
let just_in = map.label_shows_at(lod, (edge_m * 0.99) / mpu);
let just_out = map.label_shows_at(lod, (edge_m * 1.01) / mpu);
assert!(
just_in && !just_out,
"{label}: {lod:?} does not switch at {edge_m} m through this scene's scale \
(in={just_in} out={just_out}) — the ladder is not reading the scale"
);
}
counts.push((label, admitted));
}
// The SAME metric dolly must admit the SAME tiers in both scenes. The two towns
// are the same topology at two spans, so the counts are comparable directly.
let (a, b) = (counts[0].1, counts[1].1);
println!(" city gated-in {a} region gated-in {b}");
assert_eq!(
a, b,
"at {PROBE_M} m the two scenes admit different label sets ({a} vs {b}) — the ladder \
is measuring the viewport, not the ground"
);
assert!(a > 0, "the probe must actually admit labels, or the equality above is vacuous (0 == 0)");
}
/// **RED for [`the_label_ladder_gates_on_metres_at_both_scales`].** The ladder as it
/// shipped — design-unit literals compared against `cam.distance` — run through the
/// SAME "same metres ⇒ same label set" question at the SAME probe. It must give two
/// different answers in the two scenes, or the guard above is passing on an identity.
#[test]
fn the_legacy_label_ladder_gates_on_the_viewport() {
let ((city, dc), (region, dr)) = two_scales();
let (_, _) = (&city, ®ion);
/// The ladder exactly as it was, in design units, biggest rung first.
const LEGACY_DU: [f32; 3] = [0.95, 0.45, 0.18];
const PROBE_M: f32 = 400.0;
let mut admitted = Vec::new();
for (label, dpm) in [("city", dc), ("region", dr)] {
let dolly_du = PROBE_M * dpm;
let n = LEGACY_DU.iter().filter(|&&rung| dolly_du <= rung).count();
println!(
" {label:<8} rungs {:9.0} / {:9.0} / {:9.0} m at {PROBE_M} m ⇒ {n} of 3 admitted",
LEGACY_DU[0] / dpm,
LEGACY_DU[1] / dpm,
LEGACY_DU[2] / dpm
);
admitted.push(n);
}
assert_ne!(
admitted[0], admitted[1],
"the legacy ladder would have to already admit the same label set at the same METRIC \
distance in both scenes for the guard above to mean anything — it admits {} and {}",
admitted[0], admitted[1]
);
let (m_city, m_region) = (LEGACY_DU[2] / dc, LEGACY_DU[2] / dr);
assert!(
m_region > 1_000.0,
"…and the region's \"only near street level\" rung is {m_region:.0} m against the \
city's {m_city:.0} m: the ladder measured the viewport, not the ground"
);
}
/// **The flat-Earth ruler is a PHYSICAL number, and nothing pinned it.**
///
/// `M_PER_DEG_LAT` was declared nine times across this crate's sources and tests
/// (`granska-20`) — the production copy plus eight private ones in test files that
/// build their fixtures from it. Those copies made every fixture agree with the code
/// under test only by coincidence of nobody editing either side, and no test asserted
/// the VALUE at all: a typo in the production constant would have moved the extruder
/// and left every fixture measuring the old earth, silently, because the fixtures
/// carried their own.
///
/// The eight copies are gone (the tests import the extruder's constant now), so the
/// remaining risk is the opposite one: one writer, wrong. This pins it — against the
/// WGS-84 meridian, not against itself.
#[test]
fn the_flat_earth_ruler_is_the_wgs84_meridian() {
// WGS-84 polar circumference / 360. The mean degree of latitude is 111 132.95 m at
// the equator and 111 693.9 m at the pole; the extruder uses the round 111 320,
// which is the ~45° value and the conventional flat-Earth constant. Bound the
// check by the real spread rather than by the constant itself.
let meridian_deg = 40_007_863.0 / 360.0; // m per degree, WGS-84 polar circumference
let err = (extrude::M_PER_DEG_LAT - meridian_deg).abs() / meridian_deg;
println!(" M_PER_DEG_LAT {:.1} m WGS-84 mean {meridian_deg:.1} m rel {err:.4}", extrude::M_PER_DEG_LAT);
assert!(err < 0.005, "the latitude degree is {:.1} m — that is not the WGS-84 meridian ({meridian_deg:.1} m, rel {err:.4})", extrude::M_PER_DEG_LAT);
// And the longitude writer must be the SAME ruler shrunk by cos(lat) — checked at
// three latitudes, including Sweden's, because a constant-longitude bug is invisible
// at the equator (cos 0 = 1: the identity value).
for (lat, want) in [(0.0, 1.0), (59.3293, 59.3293_f64.to_radians().cos()), (69.0, 69.0_f64.to_radians().cos())] {
let ratio = extrude::m_per_deg_lon(lat) / extrude::M_PER_DEG_LAT;
println!(" lat {lat:>8.4} m_per_deg_lon/M_PER_DEG_LAT {ratio:.6} want cos(lat) {want:.6}");
assert!((ratio - want).abs() < 1e-6, "at {lat}° the longitude degree is {ratio:.6} of the latitude degree, not cos(lat) = {want:.6}");
}
}