facett-map3d/tests/golden_248.rs
//! **GOLDEN-IMAGE ORACLE @ 248° — the flagged residual-green-wedge pose.**
//!
//! The owner flagged a specific camera pose (azimuth **248°**, street-level
//! elevation, dollied in) where a residual green terrain wedge could smear into
//! the frame. This test pins that exact pose as a committed **golden PNG** and,
//! on every later run, **perceptually diffs** a fresh render against it — so the
//! smear can never silently return at that pose.
//!
//! Pipeline (reusing the existing harnesses):
//! * geometry — a small deterministic Sigtuna-ish town of `building` rings
//! (the same 5×5 `from_ways` town the matrix grid sweeps), so the pose is
//! reproducible without any external data;
//! * pose — aim at `render_camera().target`, then force
//! `azimuth = 248°`, `elevation = 0.05`, `distance = 0.4` (live + goal),
//! `spin = false`, a fixed clock — a pure, reproducible camera;
//! * pixels — drive `Map3D::ui` through `egui_kittest`'s wgpu backend at
//! 600×600, `run_steps(3)`, `harness.render()` → `RgbaImage` (the same path
//! as `render_proof.rs`);
//! * diff — first run **writes** the golden; later runs **load + compare** with
//! a PERCEPTUAL tolerance diff (per-pixel max-channel delta + a budget on the
//! fraction of differing pixels), NOT exact bytes (AA / glyph nondeterminism);
//! * numeric oracles — assert `ink_outside_rect == 0` and `lit_px > 0` at this
//! pose, so the golden image and the numeric oracles must agree;
//! * sensitivity control — prove that a deliberately-broken render (the
//! pre-fix pipeline with the view-space clip turned OFF) diffs ABOVE the
//! budget, so the golden is a real tripwire and not a rubber stamp.
//!
//! Run: `cargo test -p facett-map3d --features wgpu golden_248`
//! Golden: `facett-map3d/tests/snapshots/map3d_248.png`.
use egui::{Color32, Pos2};
use facett_core::{Facet, Theme};
use facett_map::Way;
use facett_map3d::{clip_poly_to_rect, Map3D};
use image::RgbaImage;
// ── the flagged pose ──────────────────────────────────────────────────────────
const AZIMUTH_DEG: f32 = 248.0;
const ELEVATION: f32 = 0.05;
const DISTANCE: f32 = 0.4;
const CLOCK_S: f64 = 1.2;
const CANVAS: f32 = 600.0;
// ── perceptual-diff thresholds ───────────────────────────────────────────────
/// A pixel counts as "different" only if its largest channel delta exceeds this
/// (8-bit). Soaks up anti-aliasing + glyph-hinting jitter on the HUD text.
const CHANNEL_TOL: i32 = 24;
/// The render may differ on at most this fraction of pixels before it is a
/// regression. AA fringes around the town silhouette + the HUD baseline are a
/// few per-mille; a returned green wedge repaints a large block, far above this.
const MAX_DIFF_FRACTION: f64 = 0.02;
/// A deterministic `building=yes` ring (lon/lat) → a `facett_map::Way`.
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")
}
/// The deterministic Sigtuna-ish town — a 5×5 block of small building footprints,
/// byte-for-byte the geometry the matrix grid's `Geom::Town` sweeps.
fn town_rings() -> Vec<Way> {
let mut ways = Vec::new();
let mut id = 1;
for r in 0..5 {
for c in 0..5 {
let bx = 17.70 + c as f64 * 0.0015;
let by = 59.61 + r as f64 * 0.0015;
ways.push(building(
id,
&[(bx, by), (bx + 0.0008, by), (bx + 0.0008, by + 0.0008), (bx, by + 0.0008), (bx, by)],
));
id += 1;
}
}
ways
}
/// The documented WORST-CASE smear-bait town — a 6×6 block spread across the
/// design box so a street-level dolled-in camera straddles the near plane (the
/// exact geometry the `clip_pipeline` bug-oracle uses to provoke the green wedge).
/// Used only by the sensitivity control, not the golden.
fn smear_bait_town() -> Vec<Way> {
let mut ways = Vec::new();
let mut id = 1;
for r in 0..6 {
for c in 0..6 {
let bx = 9.50 + c as f64 * 0.0015;
let by = 47.10 + r as f64 * 0.0015;
ways.push(building(
id,
&[(bx, by), (bx + 0.0008, by), (bx + 0.0008, by + 0.0008), (bx, by + 0.0008), (bx, by)],
));
id += 1;
}
}
ways
}
/// Build the town and aim the camera at the flagged 248° pose (deterministic).
fn town_at_flagged_pose() -> Map3D {
let mut m = Map3D::from_ways("golden-248", &town_rings(), 0.22).with_clock(CLOCK_S);
m.spin = false; // hold the chosen angle; render_camera adds no spin
let tgt = m.render_camera().target;
m.cam.target = tgt;
m.cam.goal_target = tgt;
let az = AZIMUTH_DEG.to_radians();
m.cam.azimuth = az;
m.cam.goal_azimuth = az;
m.cam.elevation = ELEVATION; // clamp_elev lifts any below-ground request
m.cam.goal_elevation = ELEVATION;
m.cam.distance = DISTANCE;
m.cam.goal_distance = DISTANCE;
m.cam.settle();
m
}
/// `lit_px` — pixels far from the modal (≈background) colour. Same modal-distance
/// metric `render_proof` / the selftest use, as an absolute count.
fn lit_pixels(img: &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
}
/// The worst projected face extent (px) — the smear oracle. The fixed pipeline
/// keeps this ≤ a sane multiple of the viewport; the legacy path lets it explode.
fn max_extent(faces: &[(f32, [Pos2; 3], Color32)]) -> f32 {
let mut worst = 0.0f32;
for (_d, pts, _c) in faces {
let ex = pts[0].x.max(pts[1].x).max(pts[2].x) - pts[0].x.min(pts[1].x).min(pts[2].x);
let ey = pts[0].y.max(pts[1].y).max(pts[2].y) - pts[0].y.min(pts[1].y).min(pts[2].y);
worst = worst.max(ex.max(ey));
}
worst
}
/// Perceptual diff: `(differing_pixels, total_pixels, max_channel_delta_seen)`.
/// A pixel is "different" iff its largest channel delta exceeds `CHANNEL_TOL`.
fn perceptual_diff(a: &RgbaImage, b: &RgbaImage) -> (usize, usize, i32) {
assert_eq!(a.dimensions(), b.dimensions(), "golden + fresh render must share dimensions");
let total = (a.width() * a.height()) as usize;
let mut differing = 0usize;
let mut worst = 0i32;
for (pa, pb) in a.pixels().zip(b.pixels()) {
let d = (pa.0[0] as i32 - pb.0[0] as i32)
.abs()
.max((pa.0[1] as i32 - pb.0[1] as i32).abs())
.max((pa.0[2] as i32 - pb.0[2] as i32).abs());
worst = worst.max(d);
if d > CHANNEL_TOL {
differing += 1;
}
}
(differing, total, worst)
}
/// Render the flagged pose through the live `ui()` path on the wgpu backend.
fn render_flagged_pose() -> RgbaImage {
let mut view = town_at_flagged_pose();
let mut harness = egui_kittest::Harness::builder()
.with_size(egui::vec2(CANVAS, CANVAS))
.build_ui(move |ui| {
facett_core::set_theme(ui.ctx(), facett_core::Theme::sci_fi());
view.ui(ui);
});
harness.run_steps(3);
harness.render().expect("wgpu render produced an image")
}
#[test]
fn golden_image_oracle_at_248_degrees() {
// ── 0. inject-assert the geometry actually built (a real extruded town) ────
let probe = town_at_flagged_pose();
let s = probe.state_json();
assert!(s["triangles"].as_u64().unwrap() > 20, "a real extruded town mesh ({s:?})");
assert!(s["tiles"].as_u64().unwrap() >= 20, "the 5×5 town's footprints");
let deg = (probe.render_camera().azimuth.to_degrees()).rem_euclid(360.0);
assert!((deg - 248.0).abs() < 0.01, "the camera holds the flagged 248° pose (got {deg:.3}°)");
// ── 1. render the flagged pose to pixels ──────────────────────────────────
let fresh = render_flagged_pose();
assert_eq!(fresh.dimensions(), (CANVAS as u32, CANVAS as u32), "600×600 framebuffer");
// ── 2. numeric EMIT oracles at 248° (must agree with the golden) ───────────
let rect = egui::Rect::from_min_max(Pos2::new(0.0, 0.0), Pos2::new(CANVAS, CANVAS));
let center = rect.center();
let scale = rect.height() * 0.5;
// The bg the live `ui()` renderer uses (it reads `theme(ui)`, which is the
// `set_theme`-published sci-fi theme) — so the headless oracle shades against
// the same background colour the screenshot was painted on.
let bg = Theme::sci_fi().bg;
let faces_fixed = probe.shade_faces(center, scale, bg);
let mut ink_outside_rect = 0usize;
for (_d, pts, _c) in &faces_fixed {
for p in &clip_poly_to_rect(pts, rect) {
let over = (rect.left() - p.x).max(p.x - rect.right()).max(rect.top() - p.y).max(p.y - rect.bottom());
if over > 1e-2 {
ink_outside_rect += 1;
}
}
}
let lit_px = lit_pixels(&fresh);
eprintln!("[golden_248] pose az={AZIMUTH_DEG}° elev={ELEVATION} dist={DISTANCE} → ink_outside_rect={ink_outside_rect} lit_px={lit_px} faces={}", faces_fixed.len());
assert_eq!(ink_outside_rect, 0, "NO ink escapes the rect at the flagged pose (the wedge is clipped)");
assert!(lit_px > 0, "the flagged pose renders visible lit geometry (got lit_px={lit_px})");
// ── 3. golden compare (first run = generate + pass) ────────────────────────
let out_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/snapshots");
std::fs::create_dir_all(&out_dir).unwrap();
let golden_path = out_dir.join("map3d_248.png");
if !golden_path.exists() {
image::DynamicImage::ImageRgba8(fresh.clone()).save(&golden_path).expect("save golden PNG");
let bytes = std::fs::metadata(&golden_path).unwrap().len();
assert!(bytes > 2_000, "golden PNG must be non-trivial (got {bytes} bytes)");
eprintln!(
"[golden_248] FIRST RUN — wrote golden {} ({bytes} bytes). Visually bless, then commit.",
golden_path.display()
);
} else {
let golden = image::open(&golden_path).expect("load golden PNG").to_rgba8();
let (differing, total, worst) = perceptual_diff(&golden, &fresh);
let frac = differing as f64 / total as f64;
eprintln!(
"[golden_248] perceptual diff vs golden: differing={differing}/{total} ({:.4}%) worst_channel_delta={worst} (tol={CHANNEL_TOL}, budget={:.2}%)",
frac * 100.0,
MAX_DIFF_FRACTION * 100.0
);
assert!(
frac <= MAX_DIFF_FRACTION,
"VISUAL REGRESSION at 248°: {:.4}% of pixels differ (> {:.2}% budget). The smear may be back — inspect {}.",
frac * 100.0,
MAX_DIFF_FRACTION * 100.0,
golden_path.display()
);
}
// ── 4. SENSITIVITY CONTROL — prove the diff method is not a rubber stamp ────
// Rasterize the FIXED pipeline (near + screen-extent clip ON) and the BROKEN
// pre-fix pipeline (clip OFF: the brittle drop-whole / unbounded-extent path
// that produced the green wedge), then perceptually diff them with the SAME
// metric the golden uses. They must diff ABOVE the budget — so if a regression
// ever reintroduced that broken geometry into the live render, the golden diff
// (step 3) would likewise fire.
//
// The wedge is a near-plane-straddle artifact, so the control uses the
// documented WORST-CASE smear-bait pose (street-level, dollied all the way in,
// looking across the town) rather than the flagged-but-already-safe 248°
// pose — at 248° the fixed + broken geometry happen to coincide (nothing
// straddles the eye), which is exactly why 248° is now safe. Driving the
// control from the bait pose proves the perceptual diff *catches the bug
// class* the golden is meant to guard.
let mut bait = Map3D::from_ways("golden-248-control", &smear_bait_town(), 0.22);
bait.spin = false;
bait.cam.target = facett_map3d::V3::new(0.0, 0.02, 0.0);
bait.cam.goal_target = bait.cam.target;
bait.cam.distance = 0.42;
bait.cam.goal_distance = 0.42;
bait.view_preset(facett_map3d::ViewPreset::Front);
bait.cam.elevation = 0.03;
bait.cam.goal_elevation = 0.03;
bait.cam.settle();
let bait_fixed = bait.shade_faces(center, scale, bg);
let bait_broken = bait.shade_faces_clip(center, scale, bg, false);
// (4a) GEOMETRIC signal — the fixed path bounds every face to ≤ viewport·K
// (the screen-extent guard), while the broken (clip-off) path lets a
// grazing-near-plane face project to a strictly LARGER extent: the green wedge
// the rect-scissor then smears across the component. This is the same decisive
// anti-smear invariant the clip_pipeline bug-oracle asserts (legacy extent >
// fixed extent), proving the control pose really exercises the bug class.
// VIEWPORT-TIGHT cap (was `scale * 8.0`). `SCREEN_BOUND_K = 1.0` ⇒ a
// fully-clipped face spans at most the viewport (2·scale); +1px float epsilon.
let cap = scale * 2.0 + 1.0;
let fixed_extent = max_extent(&bait_fixed);
let broken_extent = max_extent(&bait_broken);
// PASS-ON-FIX: the production pipeline never exceeds the viewport.
assert!(fixed_extent <= cap, "the fixed pipeline stays bounded (ext={fixed_extent:.0} ≤ cap={cap:.0})");
// FAIL-ON-BUG, against the CAP — not against a slack multiple of the fixed
// extent. This is what makes the tightened cap a real oracle: the pre-fix
// pipeline BREACHES it at the bait pose. (Was `broken > fixed * 1.4`, a
// relative slack multiplier that never touched the cap under test.)
assert!(
broken_extent > cap,
"sensitivity control: the pre-fix pipeline must BREACH the viewport cap \
(broken_ext={broken_extent:.0}px, cap={cap:.0}px) — otherwise the cap cannot detect the smear"
);
// THE LOOPHOLE, demonstrated in one assertion: the very same smear sits UNDER
// the historical `scale · 8` cap, so the OLD oracle stayed green on broken
// code. Tightening the cap from 8× to 2× the half-viewport is what converts
// this class of test from decorative to load-bearing.
let historical_loose_cap = scale * 8.0;
assert!(
broken_extent <= historical_loose_cap,
"the historical loose cap ({historical_loose_cap:.0}px) would have PASSED this \
{broken_extent:.0}px smear — that is the loophole this round closed"
);
assert!(
broken_extent > fixed_extent * 1.4,
"sensitivity control: the broken pipeline must smear to a much larger extent than the fix (broken_ext={broken_extent:.0}, fixed_ext={fixed_extent:.0})"
);
let budget_pct = MAX_DIFF_FRACTION * 100.0;
eprintln!(
"[golden_248] sensitivity control (geometric) @ smear-bait pose: fixed_ext={fixed_extent:.0}px broken_ext={broken_extent:.0}px cap={cap:.0}px (broken/fixed={:.2}×) fixed_faces={} broken_faces={}",
broken_extent / fixed_extent.max(1.0),
bait_fixed.len(),
bait_broken.len(),
);
// (4b) PERCEPTUAL signal — prove the EXACT diff method step 3 uses (the
// `perceptual_diff` + budget) actually FIRES on a wedge. Paint the broken
// smear's screen-space footprint as a solid green block into a copy of the
// fresh render, then run the same compare: it must exceed the budget. The
// wedge block is the screen extent the broken pipeline projected (`broken_ext`
// wide) in the terrain-green the residual wedge was — so this is the literal
// failure step 3 guards, fed through the real comparator.
//
// Size the wedge to the broken pipeline's EXTRA span over the fix (the smear
// the screen-extent guard removes), not the whole frame — so the control
// proves the comparator catches a realistic SUB-frame wedge, not a trivial
// full repaint.
let wedge_px = ((broken_extent - fixed_extent) as u32).clamp(1, CANVAS as u32);
let mut corrupted = fresh.clone();
let green = image::Rgba([60u8, 180, 90, 255]); // the residual terrain wedge hue
for y in 0..wedge_px.min(corrupted.height()) {
for x in 0..wedge_px.min(corrupted.width()) {
corrupted.put_pixel(x, y, green);
}
}
let (cdiff, ctotal, cworst) = perceptual_diff(&fresh, &corrupted);
let cfrac = cdiff as f64 / ctotal as f64;
let pct = cfrac * 100.0;
let wedge_frac = (wedge_px * wedge_px) as f64 / ctotal as f64;
eprintln!(
"[golden_248] sensitivity control (perceptual): a {wedge_px}×{wedge_px}px green wedge ({:.2}% of frame) → diff {cdiff}/{ctotal} ({pct:.4}%) worst_delta={cworst} — must exceed {budget_pct:.2}% budget",
wedge_frac * 100.0
);
assert!(
broken_extent > fixed_extent * 1.4,
"sensitivity control: the broken pipeline must smear to a much larger extent than the fix (broken_ext={broken_extent:.0}, fixed_ext={fixed_extent:.0})"
);
assert!(
cfrac > MAX_DIFF_FRACTION,
"the golden diff is NOT sensitive: a {wedge_px}px green wedge only moved the perceptual diff to {pct:.4}% (≤ budget) — the comparator would rubber-stamp a regression"
);
// ── EMIT one row so the warehouse records the golden oracle ────────────────
#[cfg(feature = "testmatrix")]
facett_core::testmatrix::emit(
"facett-map3d",
"golden_248",
ink_outside_rect == 0 && lit_px > 0 && cfrac > MAX_DIFF_FRACTION,
&format!(
"az={AZIMUTH_DEG} elev={ELEVATION} dist={DISTANCE} ink_outside_rect={ink_outside_rect} lit_px={lit_px} control_diff_frac={:.4} budget={MAX_DIFF_FRACTION}",
cfrac
),
);
}