facett-map3d/tests/sigtuna_tour_proof.rs
//! **Sigtuna tour proof** (headless, data-level — no pixels, no GPU).
//!
//! The demo brief is a cinematic autopilot over the extruded OSM town: start HIGH
//! circling the houses from the air, DESCEND (bird → street), then DRIVE down the
//! streets. This asserts, as DATA read back off the live `Map3D` render camera:
//! 1. attaching the tour makes it ACTIVE and it walks Circle → Descend → Drive as
//! the clock advances (`state_json.tour_phase`),
//! 2. the camera genuinely DESCENDS — the render camera pulls far CLOSER in for the
//! drive than it sat for the air circle (bird → street),
//! 3. the air phase ORBITS (the render azimuth advances over the circle), and
//! 4. it's deterministic under a pinned clock (FC-7): same clock ⇒ same pose.
//!
//! No window: drives `Map3D` through `set_clock` + `render_camera` + `state_json`.
//!
//! Run: `cargo test -p facett-map3d --test sigtuna_tour_proof [--features testmatrix]`.
use facett_core::Facet; // brings `state_json()` into scope
use facett_map3d::{Map3D, TourPhase};
/// A tiny synthetic town (a filled building ring + a road line) so the tour has a
/// framed city + a followable street, without any fixture on disk.
fn town() -> Vec<facett_map::Way> {
use egui::Color32;
let fill = Color32::from_rgb(120, 160, 210);
let outline = Color32::from_rgb(30, 50, 70);
// Two building blocks so `frame_densest_cluster` has a real footprint.
let b1 = facett_map::Way::filled(
1,
"building",
vec![(9.500, 47.100), (9.502, 47.100), (9.502, 47.102), (9.500, 47.102), (9.500, 47.100)],
fill,
outline,
1.0,
);
let b2 = facett_map::Way::filled(
2,
"building",
vec![(9.504, 47.101), (9.506, 47.101), (9.506, 47.103), (9.504, 47.103), (9.504, 47.101)],
fill,
outline,
1.0,
);
// A road running across the town — the street the drive phase follows.
let road = facett_map::Way::line(
3,
"highway",
vec![(9.499, 47.1005), (9.503, 47.1015), (9.506, 47.1025)],
Color32::from_rgb(70, 78, 90),
2.0,
);
vec![b1, b2, road]
}
/// The waypoint chain (a road's `Way::points`, Mercator-unit) the route follows.
fn street_waypoints(ways: &[facett_map::Way]) -> Vec<(f32, f32)> {
ways.iter()
.find(|w| w.kind.contains("highway"))
.map(|w| w.points.clone())
.unwrap_or_default()
}
#[test]
fn sigtuna_tour_circles_descends_and_drives() {
let ways = town();
let wps = street_waypoints(&ways);
assert!(wps.len() >= 2, "the town has a followable street ({} waypoints)", wps.len());
let mut map = Map3D::from_geometry("Sigtuna tour", &ways).with_route(&ways, &wps).with_tour(true);
// Buildings actually extruded (houses visible): the mesh rose off the ground plane.
let raised = map.mesh().positions.iter().any(|p| p[1] > 0.02);
assert!(raised, "the town's buildings extrude into 3D prisms (a raised mesh vertex)");
assert!(map.tour_active(), "attaching the tour makes it active");
assert!(map.has_route(), "the drive phase has a street route to follow");
// (1) Walk the clock across a full loop; collect the phase + render-camera pose.
let total = 18.0 + 8.0 + 26.0; // circle + descend + drive (SigtunaTour defaults)
let mut saw_circle = false;
let mut saw_descend = false;
let mut saw_drive = false;
let mut air_dist = 0.0f32; // camera distance mid-air-circle
let mut street_dist = f32::INFINITY; // camera distance mid-drive
let mut az_start = 0.0f32;
let mut az_mid_air = 0.0f32;
// Keyframe altitude (metres) + houses-in-frame at the two ends of the descent.
let mut air_alt_m = 0.0f32; // altitude mid air-circle (HIGH)
let mut street_alt_m = f32::INFINITY; // altitude mid drive (LOW)
let mut circle_buildings = 0usize; // risen houses in frame at the circle altitude
let samples = 120;
for i in 0..=samples {
let t = i as f64 * total / samples as f64;
map.set_clock(t);
let phase = map.tour_phase().expect("tour is active → a phase");
let cam = map.render_camera();
match phase {
TourPhase::Circle => {
saw_circle = true;
if t < 1.0 {
az_start = cam.azimuth;
}
if (t - 12.0).abs() < 1.0 {
az_mid_air = cam.azimuth;
air_dist = cam.distance;
air_alt_m = map.altitude_m();
circle_buildings = circle_buildings.max(map.buildings_visible());
}
}
TourPhase::Descend => saw_descend = true,
TourPhase::Drive => {
saw_drive = true;
street_dist = street_dist.min(cam.distance);
street_alt_m = street_alt_m.min(map.altitude_m());
}
}
}
assert!(saw_circle && saw_descend && saw_drive, "the tour walks Circle → Descend → Drive across the loop");
// (KEYFRAME) The camera opens HIGH and ends LOW: the metric altitude mid air-circle
// is far above the altitude mid street-drive — the "overview → down the streets"
// arc the brief asks for, read straight off the calibrated `altitude_m`.
let descended_altitude = air_alt_m > street_alt_m * 2.0;
assert!(
descended_altitude,
"the tour opens high then drops to street level (air {air_alt_m:.0} m ≫ street {street_alt_m:.0} m)"
);
// (HOUSES) At the air-circle altitude the extruded houses are IN FRAME — the whole
// point of circling Sigtuna ("we want to SEE THE HOUSES"). RED-when-broken: a scene
// whose buildings don't extrude reports 0 here (see the lib `buildings_visible` gate).
assert!(circle_buildings > 0, "houses are visible at the air-circle altitude ({circle_buildings} risen verts in frame)");
// (2) It DESCENDS: the drive pulls far closer in than the air circle sat.
assert!(
air_dist > street_dist + 0.5,
"the tour descends bird → street (air dist {air_dist} ≫ street dist {street_dist})"
);
// (3) The air phase ORBITS: azimuth advanced from the start of the circle to mid-air.
let orbited = (az_mid_air - az_start).abs() > 0.1;
assert!(orbited, "the air circle orbits the skyline (az {az_start} → {az_mid_air})");
// (4) Determinism (FC-7): the same pinned clock replays the identical pose.
map.set_clock(30.0);
let a = map.render_camera();
map.set_clock(30.0);
let b = map.render_camera();
let deterministic = a.eye() == b.eye() && a.azimuth == b.azimuth && a.distance == b.distance;
assert!(deterministic, "the tour pose is a pure function of the clock");
// The state_json oracle surfaces the tour for a robot-UI / mega-matrix to assert.
map.set_clock(2.0);
let sj = map.state_json();
assert_eq!(sj["tour_active"], serde_json::json!(true), "state_json exposes tour_active");
assert_eq!(sj["tour_phase"], serde_json::json!("circle"), "state_json exposes the live phase");
#[cfg(feature = "testmatrix")]
{
let ok = saw_circle
&& saw_descend
&& saw_drive
&& air_dist > street_dist + 0.5
&& orbited
&& deterministic
&& raised
&& descended_altitude
&& circle_buildings > 0;
facett_core::testmatrix::emit(
"facett-map3d",
"sigtuna_tour_circle_descend_drive",
ok,
&format!(
"raised={raised} circle={saw_circle} descend={saw_descend} drive={saw_drive} \
air_dist={air_dist:.3} street_dist={street_dist:.3} orbited={orbited} det={deterministic} \
air_alt_m={air_alt_m:.0} street_alt_m={street_alt_m:.0} circle_buildings={circle_buildings}"
),
);
}
}