facett-map3d/tests/robot_ui.rs
//! **ROBOT-UI** — drive the LIVE OSM-3D `Map3D` Facet through egui_kittest exactly
//! as the wasm web demo does (CPU painter, the path the live `nordisk.codeberg.page`
//! OSM-3D tab runs), orbit/dolly to the screenshot's failing street-level pose, read
//! back `state_json`, and assert the rendered frame is **CONFINED** (no ink outside
//! the widget rect) AND **non-blank** — a smeared/escaped frame would be a RED row.
//!
//! "You know the drill — robot auto UI." This is the live-surface proof (not an
//! isolated unit call): it builds the real city, allocates the view a SUB-rect of a
//! larger sentinel-filled canvas, drives input, renders, and diffs the surrounding
//! margin band against a no-view baseline. The clip fix (near clip + screen-extent
//! bound + ground-floor elevation) must leave the margin byte-identical.
//!
//! Run: `cargo test -p facett-map3d --features testmatrix --test robot_ui`
use egui::Color32;
use egui_kittest::Harness;
use facett_core::{Facet, Theme};
use facett_map::Way;
use facett_map3d::Map3D;
fn building(id: i64, ring: &[(f64, f64)]) -> Way {
let tags = vec![("building".to_string(), "yes".to_string())];
Way::from_lonlat(id, ring, &tags, &Theme::default()).expect("a building way")
}
/// A denser Sigtuna-ish city (the real `from_geometry` demo path: cluster + frame).
fn sigtuna_city() -> Vec<Way> {
let mut ways = Vec::new();
let mut id = 1;
for r in 0..10 {
for c in 0..10 {
let bx = 17.70 + c as f64 * 0.0009;
let by = 59.61 + r as f64 * 0.0009;
ways.push(building(
id,
&[(bx, by), (bx + 0.0005, by), (bx + 0.0005, by + 0.0005), (bx, by + 0.0005), (bx, by)],
));
id += 1;
}
}
ways
}
fn lit_pixels(img: &image::RgbaImage) -> usize {
let mut hist = std::collections::HashMap::<u32, u32>::new();
for p in img.pixels() {
let key = ((p.0[0] as u32 >> 3) << 10) | ((p.0[1] as u32 >> 3) << 5) | (p.0[2] as u32 >> 3);
*hist.entry(key).or_default() += 1;
}
let (modal, _) = hist.iter().max_by_key(|(_, c)| **c).map(|(k, c)| (*k, *c)).unwrap();
let (mr, mg, mb) = (((modal >> 10) & 0x1f) as i32, ((modal >> 5) & 0x1f) as i32, (modal & 0x1f) as i32);
let mut lit = 0usize;
for p in img.pixels() {
let (r, g, b) = ((p.0[0] >> 3) as i32, (p.0[1] >> 3) as i32, (p.0[2] >> 3) as i32);
if (r - mr).abs() + (g - mg).abs() + (b - mb).abs() > 3 {
lit += 1;
}
}
lit
}
/// Drive the live OSM-3D tab to the failing street-level pose and prove the frame
/// is confined to its rect and non-blank. The view is built once OUTSIDE the
/// closures and mutated by the robot (orbit-drag + dolly) so the same camera the
/// user reaches is the one rendered.
#[test]
fn robot_drives_osm3d_to_failing_pose_frame_confined_and_non_blank() {
const CANVAS: f32 = 600.0;
const MARGIN: f32 = 90.0;
let sentinel = Color32::from_rgb(255, 0, 255); // magenta — the map never paints this
// Build the real city ONCE; the robot orbits/dollies it to the failing pose.
let make_view = || -> Map3D {
let mut v = Map3D::from_geometry("OSM 3D", &sigtuna_city());
v.spin = false;
// Robot navigation: orbit toward a low heading + dolly all the way in to the
// street-level pose the screenshot shows (the smear pose). Using the public
// camera API the live drag/wheel handlers call.
v.cam.orbit(800.0, -400.0); // big drag: swing azimuth + push elevation DOWN
v.cam.dolly(600.0, None); // wheel in hard (toward min_distance)
v.cam.settle();
v
};
let render = |draw_view: bool| -> (image::RgbaImage, serde_json::Value) {
use std::cell::RefCell;
use std::rc::Rc;
let view = Rc::new(RefCell::new(make_view()));
let v2 = view.clone();
let mut harness = Harness::builder().with_size(egui::vec2(CANVAS, CANVAS)).build_ui(move |ui| {
let inner = ui.max_rect().shrink(MARGIN);
ui.painter().rect_filled(inner, egui::CornerRadius::ZERO, sentinel);
if draw_view {
let mut child = ui.new_child(egui::UiBuilder::new().max_rect(inner));
Facet::ui(&mut *v2.borrow_mut(), &mut child);
}
});
harness.run_steps(3);
let img = harness.render().expect("headless render produced an image");
let state = view.borrow().state_json();
(img, state)
};
let (drawn, state) = render(true);
let (baseline, _) = render(false);
assert_eq!(drawn.dimensions(), baseline.dimensions());
// ── read back state_json (the robot reads what the user sees, as data) ──
eprintln!("[robot_ui] state = {state}");
assert_eq!(state["title"], "OSM 3D");
// The robot drove the camera below street-level; clamp_elev must keep it ABOVE
// ground (elevation floor ≈ 0.02 rad) — never a below-horizon below-ground view.
let elev_deg = state["camera"]["elevation_deg"].as_f64().expect("elevation in state");
assert!(elev_deg >= 1.0, "the ground-floor clamp keeps the eye above the terrain (elev={elev_deg:.2}°)");
// The camera dollied in (close distance) and projected real triangles.
let dist = state["camera"]["distance"].as_f64().unwrap();
assert!(dist > 0.0, "camera distance reported");
let vis = state["visible_triangles"].as_u64().unwrap();
assert!(vis > 0, "the dolled-in pose still projects visible triangles (vis={vis})");
// ── (a) non-blank: a lit city fills many pixels ──
let lit = lit_pixels(&drawn);
assert!(lit > 300, "the live frame is non-blank (lit_px={lit})");
// ── (b) CONFINED: the margin band is byte-identical to the no-view baseline —
// NO ink escaped the widget rect (a smear would change margin pixels) ──
let (w, h) = drawn.dimensions();
let m_px = MARGIN.round() as u32;
let mut leaked = 0u64;
let mut band = 0u64;
for y in 0..h {
for x in 0..w {
let outside = x < m_px || x >= w.saturating_sub(m_px) || y < m_px || y >= h.saturating_sub(m_px);
if !outside {
continue;
}
band += 1;
if drawn.get_pixel(x, y) != baseline.get_pixel(x, y) {
leaked += 1;
}
}
}
assert!(band > 0);
let frac = leaked as f64 / band as f64;
eprintln!("[robot_ui] lit_px={lit}, margin band {band}px, leaked {leaked} ({:.4}%), elev={elev_deg:.2}° dist={dist:.2}", frac * 100.0);
// Write the robot's frame so an agent can Read it.
let dir = std::env::var_os("NORNIR_WAREHOUSE_ROOT")
.map(|r| std::path::PathBuf::from(r).join("facett-map3d"))
.unwrap_or_else(|| std::env::temp_dir().join("facett-map3d"));
std::fs::create_dir_all(&dir).ok();
let png = dir.join("robot_ui_failing_pose.png");
drawn.save(&png).ok();
eprintln!("[robot_ui] frame written: {}", png.display());
#[cfg(feature = "testmatrix")]
facett_core::testmatrix::emit(
"facett-map3d",
"robot_ui_confined",
frac < 0.0005 && lit > 300,
&format!("lit_px={lit} margin_leaked={leaked} frac={:.5} elev_deg={elev_deg:.2} png={}", frac, png.display()),
);
assert!(frac < 0.0005, "the live OSM-3D frame is confined — {leaked}px ({:.3}%) escaped the rect", frac * 100.0);
}