facett-demo/tests/osm3d_spoke.rs
//! Regression (test-matrix), REAL DATA: the OSM 3D viewer fed real liechtenstein
//! GeoParquet must never project a "spoke" face — the starburst the user saw.
//! Builds `Map3D::from_geometry` exactly like the demo tab, sweeps the auto-orbit,
//! and asserts via `shade_faces` DATA that no projected face spans far past the
//! viewport (a near-plane projection artifact). Emits to the matrix.
//!
//! Sensitivity: with the `shade_faces` near-spoke reject removed, the orbit hits
//! an angle where the terrain quad straddles the near plane and `worst` jumps to
//! ~1e5 px → this test fails. With the fix it stays a few hundred px.
use egui::{Color32, Pos2};
use facett_demo::osm_tabs;
use facett_map3d::{Map3D, ViewPreset};
#[test]
fn osm3d_real_data_never_projects_spoke_faces() {
let geo = osm_tabs::load();
let mut m = Map3D::from_geometry("osm3d-spoke", &geo.ways_3d);
let half_h = 450.0f32;
// VIEWPORT-TIGHT cap over the REAL OSM fixture (was `half_h * 8.0`, the same
// loophole as facett-map3d/tests/no_spokes.rs but on real data).
// `SCREEN_BOUND_K = 1.0` ⇒ a fully-clipped face spans at most 2·half_h.
let cap = half_h * 2.0 + 1.0;
let center = Pos2::new(450.0, 450.0);
let mut worst = 0f32;
let mut faces_total = 0usize;
// (a) fixed presets incl. Front (horizon-level) — the LOW angle the auto-orbit
// never reaches, where the terrain quad's far edge straddles the near plane.
for p in [ViewPreset::Front, ViewPreset::Oblique, ViewPreset::Isometric, ViewPreset::Top] {
let mut mp = Map3D::from_geometry("osm3d-preset", &geo.ways_3d);
mp.view_preset(p);
let faces = mp.shade_faces(center, half_h, Color32::GRAY);
faces_total += faces.len();
let mut pw = 0f32;
for (_, t, _) in &faces {
let ex = t[0].x.max(t[1].x).max(t[2].x) - t[0].x.min(t[1].x).min(t[2].x);
let ey = t[0].y.max(t[1].y).max(t[2].y) - t[0].y.min(t[1].y).min(t[2].y);
pw = pw.max(ex.max(ey));
}
eprintln!("EMIT_REAL3D preset {p:?}: worst={pw:.0}px faces={}", faces.len());
worst = worst.max(pw);
}
// (b) auto-orbit sweep.
for _ in 0..24 {
m.tick(0.15);
let faces = m.shade_faces(center, half_h, Color32::GRAY);
faces_total += faces.len();
for (_, t, _) in &faces {
let ex = t[0].x.max(t[1].x).max(t[2].x) - t[0].x.min(t[1].x).min(t[2].x);
let ey = t[0].y.max(t[1].y).max(t[2].y) - t[0].y.min(t[1].y).min(t[2].y);
worst = worst.max(ex.max(ey));
}
}
eprintln!("EMIT_REAL3D worst_extent_px={worst:.0} cap_px={cap:.0} faces={faces_total} ways={}", geo.ways_3d.len());
// Read only by the testmatrix row below.
#[cfg(feature = "testmatrix")]
let ok = worst < cap && faces_total > 0;
assert!(faces_total > 0, "real liechtenstein rendered some faces");
assert!(worst < cap, "real-data projection spoke: a face spans {worst:.0}px (> cap {cap:.0})");
#[cfg(feature = "testmatrix")]
facett::core::testmatrix::emit(
"facett-map3d",
"osm3d_real_no_projection_spokes",
ok,
&format!("worst_extent_px={worst:.0} cap_px={cap:.0} faces={faces_total}"),
);
}