Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/facett

facett-map3d/tests/near_plane_probe.rs

//! **The street-level near-plane oracle.** Was a *temporary diagnostic probe*
//! with **NO assertions at all** — only `eprintln!` — which the fat-list
//! inventory correctly flagged as vacuous (it violated the inject-and-assert
//! law: a test that cannot fail is not a test). It now asserts the contract it
//! was only ever printing, at the worst-case pose it was built to find:
//!
//! * the pose renders REAL geometry (a silently-empty street-level frame is the
//!   other failure mode a print-only probe hides);
//! * every projected face is bounded by the VIEWPORT (2·half_h) — the same
//!   viewport-tight cap the rest of the map3d smear suite now uses, never a
//!   loose multiple of it;
//! * after the live Sutherland-Hodgman rect clip **zero** vertices escape the
//!   widget rect — the real on-screen leak this pose was hunting.
//!
//! The pre-fix pipeline (`shade_faces_clip(.., near_clip = false)`) is driven
//! through the SAME probe as the fail-on-bug control, so each assertion is proven
//! to be RED when the defect is reintroduced.
use egui::{Color32, Pos2};
use facett_core::Theme;
use facett_map::Way;
use facett_map3d::{Map3D, V3, ViewPreset};

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")
}

fn 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
}

#[test]
fn probe_street_level_faces() {
    let mut m = Map3D::from_ways("probe", &town(), 0.22);
    m.spin = false;
    // Dolly all the way in, low angle, at the centre of the town — buildings
    // straddle the near plane.
    m.cam.target = V3::new(0.0, 0.02, 0.0);
    m.cam.goal_target = m.cam.target;
    m.cam.distance = 0.42;
    m.cam.goal_distance = 0.42;
    m.view_preset(ViewPreset::Front);
    m.cam.elevation = 0.03;
    m.cam.goal_elevation = 0.03;
    m.cam.settle();

    let rect = egui::Rect::from_min_max(Pos2::new(100.0, 80.0), Pos2::new(820.0, 700.0));
    let center = rect.center();
    let scale = rect.height() * 0.5;
    let faces = m.shade_faces(center, scale, Color32::from_rgb(20, 24, 40));
    eprintln!("faces emitted: {}", faces.len());
    let mut escaped = 0;
    let mut worst = 0.0f32;
    for (_d, pts, _c) in &faces {
        for p in pts {
            let dx = (p.x - center.x).abs() - rect.width() * 0.5;
            let dy = (p.y - center.y).abs() - rect.height() * 0.5;
            let over = dx.max(dy);
            if over > 0.0 {
                escaped += 1;
                worst = worst.max(over);
            }
        }
    }
    eprintln!("raw vertices escaping rect: {escaped}, worst overshoot {worst:.1}px");

    // Now emulate the EXACT live clip (Sutherland-Hodgman to the rect) and check
    // whether any clipped vertex still escapes — that's the real on-screen leak.
    let clip = |tri: &[Pos2; 3]| -> Vec<Pos2> {
        let mut poly: Vec<Pos2> = tri.to_vec();
        let edges: [(f32, u8); 4] = [(rect.left(), 0), (rect.right(), 1), (rect.top(), 2), (rect.bottom(), 3)];
        for (bound, which) in edges {
            if poly.is_empty() {
                break;
            }
            let inside = |p: &Pos2| match which {
                0 => p.x >= bound,
                1 => p.x <= bound,
                2 => p.y >= bound,
                _ => p.y <= bound,
            };
            let intersect = |a: &Pos2, b: &Pos2| -> Pos2 {
                let t = match which {
                    0 | 1 => (bound - a.x) / (b.x - a.x),
                    _ => (bound - a.y) / (b.y - a.y),
                };
                let t = t.clamp(0.0, 1.0);
                Pos2::new(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t)
            };
            let mut out: Vec<Pos2> = Vec::new();
            for i in 0..poly.len() {
                let cur = poly[i];
                let prev = poly[(i + poly.len() - 1) % poly.len()];
                let (cin, pin) = (inside(&cur), inside(&prev));
                if cin {
                    if !pin {
                        out.push(intersect(&prev, &cur));
                    }
                    out.push(cur);
                } else if pin {
                    out.push(intersect(&prev, &cur));
                }
            }
            poly = out;
        }
        poly
    };
    let eps = 1e-2;
    let mut clipped_escapes = 0;
    let mut worst_clipped = 0.0f32;
    for (_d, pts, _c) in &faces {
        for p in &clip(pts) {
            let over = ((p.x - center.x).abs() - rect.width() * 0.5).max((p.y - center.y).abs() - rect.height() * 0.5);
            if over > eps {
                clipped_escapes += 1;
                worst_clipped = worst_clipped.max(over);
            }
        }
    }
    eprintln!("AFTER clip: escapes {clipped_escapes}, worst {worst_clipped:.1}px");

    // ── THE ASSERTIONS (this file used to have none) ─────────────────────────
    assert!(!faces.is_empty(), "the street-level pose must render real geometry, not an empty frame");

    // (1) VIEWPORT-TIGHT extent bound. `SCREEN_BOUND_K = 1.0` ⇒ the screen-bound
    // clip box is ±scale, so a fully-clipped face spans at most 2·scale; +1px
    // float-boundary epsilon. Never a loose multiple of the viewport.
    let cap = scale * 2.0 + 1.0;
    let worst_extent = max_extent(&faces);
    assert!(
        worst_extent <= cap,
        "a face spans {worst_extent:.0}px at street level (viewport cap {cap:.0}px) — the smear regressed"
    );

    // (2) ZERO on-screen leak after the live rect clip. This is the exact
    // quantity the probe printed and never checked.
    assert_eq!(
        clipped_escapes, 0,
        "after the rect clip {clipped_escapes} vertices still escape (worst {worst_clipped:.1}px) — \
         the painter would ink outside its allocated rect"
    );

    // (3) FAIL-ON-BUG control: the pre-fix pipeline at this same pose BREACHES the
    // viewport cap, so assertion (1) is proven RED when the defect returns.
    let broken = m.shade_faces_clip(center, scale, Color32::from_rgb(20, 24, 40), false);
    let broken_extent = max_extent(&broken);
    eprintln!("[near_plane_probe] fixed_extent={worst_extent:.0}px broken_extent={broken_extent:.0}px cap={cap:.0}px");
    assert!(
        broken_extent > cap,
        "sensitivity control: the pre-fix pipeline must breach the viewport cap at the street-level \
         pose (broken={broken_extent:.0}px, cap={cap:.0}px) — otherwise assertion (1) cannot fail"
    );

    #[cfg(feature = "testmatrix")]
    facett_core::testmatrix::emit(
        "facett-map3d",
        "street_level_near_plane_bounded_and_leak_free",
        worst_extent <= cap && clipped_escapes == 0 && broken_extent > cap,
        &format!(
            "faces={} fixed_extent_px={worst_extent:.0} cap_px={cap:.0} clipped_escapes={clipped_escapes} broken_extent_px={broken_extent:.0}",
            faces.len()
        ),
    );
}

/// The largest projected extent (px) over a face list.
fn max_extent(faces: &[(f32, [Pos2; 3], Color32)]) -> f32 {
    faces
        .iter()
        .map(|(_, tri, _)| {
            let ex = tri[0].x.max(tri[1].x).max(tri[2].x) - tri[0].x.min(tri[1].x).min(tri[2].x);
            let ey = tri[0].y.max(tri[1].y).max(tri[2].y) - tri[0].y.min(tri[1].y).min(tri[2].y);
            ex.max(ey)
        })
        .fold(0.0, f32::max)
}