facett-map3d/tests/building_extrusion_proof.rs
//! Building-extrusion **height A/B** — a data proof, no GPU and no pixels
//! (mirrors `road_hierarchy_proof` / `bloom_proof` / `camera_nav_proof`).
//!
//! The extruder already lifts a building footprint into a prism whose height comes
//! from the OSM `height` / `building:levels` tag (`Way::height_m` →
//! `building_design_height`). This does NOT re-implement that (Law #1) — it PROVES,
//! on the extruded [`facett_map3d::extrude::Mesh3`], that a taller height tag yields
//! a taller mesh (max Y), strictly monotonic, with the same footprint vertex pattern,
//! deterministically. Building extrusion is always-on (no toggle) — the proof covers
//! the real default path.
//!
//! Run: `cargo test -p facett-map3d --test building_extrusion_proof [--features testmatrix]`
use facett_core::Theme;
use facett_map::Way;
use facett_map3d::extrude;
/// A ~100 m square building footprint at `(lon, lat)`, tagged `height=<m>`.
fn building(id: i64, lon: f64, lat: f64, height_m: f32) -> Way {
let d = 0.0010; // ~100 m
let pts = [
(lon, lat),
(lon + d, lat),
(lon + d, lat + d),
(lon, lat + d),
(lon, lat), // closed ring
];
let tags = vec![
("building".to_string(), "yes".to_string()),
("height".to_string(), format!("{height_m}")),
];
Way::from_lonlat(id, &pts, &tags, &Theme::default()).expect("valid building footprint")
}
/// Max Y (the height axis) of the extruded mesh for a fixed 2-building layout whose
/// ONLY varying input is the test building's `height` tag. A fixed short scale-anchor
/// pins the world span (so design-units-per-metre is identical across runs); the test
/// building is always taller than the anchor, so the global max Y is its rooftop.
fn top_y_and_verts(test_height_m: f32) -> (f32, usize) {
let ways = vec![
building(1, 9.500, 47.100, 3.0), // fixed short scale-anchor (span fixer)
building(2, 9.520, 47.100, test_height_m), // the varying test building
];
let (mesh, _tiles) = extrude::build(&ways, 0.02);
let top = mesh.positions.iter().map(|p| p[1]).fold(f32::MIN, f32::max);
(top, mesh.vertex_count())
}
#[test]
fn building_height_scales_with_the_osm_height_tag() {
let (h10, n10) = top_y_and_verts(10.0);
let (h40, n40) = top_y_and_verts(40.0);
let (h80, n80) = top_y_and_verts(80.0);
// Taller tag → taller extruded mesh, strictly monotonic.
assert!(h40 > h10 + 1e-4, "40 m taller than 10 m: {h40} vs {h10}");
assert!(h80 > h40 + 1e-4, "80 m taller than 40 m: {h80} vs {h40}");
// Same footprints → identical vertex pattern: it's HEIGHT, not more tessellation.
assert_eq!(n10, n40, "same footprint vertex count across heights");
assert_eq!(n40, n80, "same footprint vertex count across heights");
// Deterministic re-extrude (no RNG in the height path).
let (again, _) = top_y_and_verts(40.0);
assert_eq!(h40, again, "extrude is deterministic");
#[cfg(feature = "testmatrix")]
{
let ok = h40 > h10 && h80 > h40 && n10 == n40 && n40 == n80 && h40 == again;
facett_core::testmatrix::emit(
"facett-map3d",
"building_extrusion_height_by_tag",
ok,
&format!(
"h10={h10:.5} h40={h40:.5} h80={h80:.5} verts={n10} deterministic={}",
h40 == again
),
);
}
}