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/gfx_v2_item6_parity.rs

//! **GFX_V2 item 6 — the parity proof: a 2D map IS a 3D view under constraints.**
//!
//! This is the test file the item exists for. `facett-core::render::view::View` is
//! now the ONE projection writer for `facett-map` (2D ortho Mercator) and
//! `facett-map3d` (3D orbit terrain); this proves the unification is real rather
//! than nominal, and it lives in `facett-map3d` because that is the only crate that
//! can see **all three** of `View`, `facett-map`'s 2D writers and
//! `OrbitCamera` at once (`facett-core` sits below both skins, which is exactly why
//! the guard that used to claim this could not have worked — see
//! `facett_core::render::camera`'s `camera_seam_3d_holds_the_orbit_projection_properties`).
//!
//! ## What is asserted, and how each assertion is kept from being hollow
//!
//! * **The 3D lane did not drift** — the delegated `OrbitCamera` is compared to the
//!   pre-unification look-at + hand-assembled matrix, written out verbatim below as
//!   an *independent* reference. Comparing `OrbitCamera` to `View` would be a
//!   tautology now that it delegates, so it is deliberately not done.
//! * **2D == constrained 3D, in pixels** — the same triangles are rasterized
//!   through both paths and the two frame buffers are compared byte for byte, at a
//!   non-identity zoom, a non-zero translation, and starting from a genuinely
//!   rotated + tilted 3D pose that is then constrained.
//! * **The constraint is load-bearing** — the mutation tests leave the tilt on, and
//!   let the terrain height through, and require the parity to go RED.
//! * **The 3D path is not a no-op** — asserted to foreshorten, to run post-FX and to
//!   read the heightfield when unconstrained, so "they agree" cannot mean "both do
//!   nothing".

use egui::{Color32, Pos2, Rect};
use facett_core::render::view::{PostFx, Projection, Terrain, View};
use facett_map3d::camera::{OrbitCamera, V3};
use facett_map3d::{raster_zbuffer, ShadedTri};
use glam::Vec3;

// ── the scene every parity test shares (non-identity by construction) ─────────

/// A pane that is deliberately **not** square and whose centre is **not** the
/// origin, so a transform that drops the translation cannot pass.
const PANE: (f32, f32) = (800.0, 600.0);
/// Origin-local Mercator ground points. Spread over a real dataset span, and
/// deliberately none of them at the camera target.
/// Spread over a **city-scale** span so that at the test zoom the figure covers a
/// few hundred pixels: a mutation that shifts geometry by a fraction of a percent
/// has to be able to move a measurable number of pixels, or the mutation check would
/// be measuring the rasterizer's grid rather than the transform.
const GROUND: [[f32; 2]; 6] = [
    [0.000_00, 0.000_00],
    [0.055_00, 0.012_50],
    [0.028_00, 0.061_00],
    [-0.038_00, 0.033_00],
    [0.062_00, 0.048_00],
    [-0.044_00, -0.021_00],
];

fn pane_rect() -> Rect {
    // Off-centre on purpose: the pane centre is (417, 271), not (400, 300).
    Rect::from_min_max(Pos2::new(17.0, -29.0), Pos2::new(817.0, 571.0))
}

/// Rasterize a triangle fan over the projected points into a real pixel buffer —
/// **applied output**, not a state round-trip.
fn rasterize(px: &[(f32, f32)]) -> Vec<Color32> {
    let mut tris = Vec::new();
    for i in 1..px.len() - 1 {
        let p = [
            Pos2::new(px[0].0, px[0].1),
            Pos2::new(px[i].0, px[i].1),
            Pos2::new(px[i + 1].0, px[i + 1].1),
        ];
        tris.push(ShadedTri {
            pts: p,
            vz: [1.0, 1.0, 1.0],
            depth: 1.0 + i as f32 * 0.01,
            col: Color32::from_rgb(40 + (i as u8) * 30, 90, 200),
        });
    }
    raster_zbuffer(&tris, PANE.0 as usize, PANE.1 as usize, Pos2::ZERO, 1.0)
}

fn lit(buf: &[Color32]) -> usize {
    buf.iter().filter(|c| c.a() != 0).count()
}

// ── 1. the 3D lane did not drift ─────────────────────────────────────────────

/// The pre-unification `OrbitCamera` math, written out verbatim as an
/// **independent** reference: the `normalize(fwd × world_up)` look-at basis and the
/// hand-assembled `P·V`. Nothing here calls the unified core.
fn historical_orbit_reference(cam: &OrbitCamera, aspect: f32) -> ([f32; 3], [f32; 16]) {
    // eye = target + distance·(cos e·sin a, sin e, cos e·cos a)
    let (se, ce) = cam.elevation.sin_cos();
    let (sa, ca) = cam.azimuth.sin_cos();
    let eye = [
        cam.target.x + ce * sa * cam.distance,
        cam.target.y + se * cam.distance,
        cam.target.z + ce * ca * cam.distance,
    ];
    // fwd = normalize(target − eye); right = normalize(fwd × up); up = right × fwd
    let norm = |v: [f32; 3]| {
        let l = (v[0] * v[0] + v[1] * v[1] + v[2] * v[2]).sqrt().max(1e-9);
        [v[0] / l, v[1] / l, v[2] / l]
    };
    let cross = |a: [f32; 3], b: [f32; 3]| {
        [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]]
    };
    let dot = |a: [f32; 3], b: [f32; 3]| a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
    let fwd = norm([cam.target.x - eye[0], cam.target.y - eye[1], cam.target.z - eye[2]]);
    let right = norm(cross(fwd, [0.0, 1.0, 0.0]));
    let up = norm(cross(right, fwd));

    let (tx, ty, tz) = (-dot(right, eye), -dot(up, eye), -dot(fwd, eye));
    let near = (cam.distance - 2.0).max(0.05);
    // READ from the camera, not re-literalled as `distance + 4.0`. What this reference
    // guards is the BASIS — the closed-form frame against the cross-product one — and
    // the far plane is a shared INPUT to the projection, not part of that identity.
    // Transcribing it here made this file fail the day `far_plane` grew its
    // `FAR_MIN_DISTANCE` floor (A-2a: the ground-aware flight dolly can be centimetres
    // long, and the frustum must not shrink with it) at the two sweep poses whose
    // distance is under 2.0 — a drift of 2.58e-3 in the depth row, with the projected
    // pixels unmoved at 9.2e-5. One writer instead.
    let far = cam.far_plane();
    let f = 1.0 / (cam.fov_y * 0.5).tan();
    let (sx, sy) = (f / aspect.max(1e-6), f);
    let a = far / (far - near);
    let b = -far * near / (far - near);

    let mut m = [0.0f32; 16];
    let mut set = |row: usize, col: usize, v: f32| m[col * 4 + row] = v;
    set(0, 0, sx * right[0]);
    set(0, 1, sx * right[1]);
    set(0, 2, sx * right[2]);
    set(0, 3, sx * tx);
    set(1, 0, sy * up[0]);
    set(1, 1, sy * up[1]);
    set(1, 2, sy * up[2]);
    set(1, 3, sy * ty);
    set(2, 0, a * fwd[0]);
    set(2, 1, a * fwd[1]);
    set(2, 2, a * fwd[2]);
    set(2, 3, a * tz + b);
    set(3, 0, fwd[0]);
    set(3, 1, fwd[1]);
    set(3, 2, fwd[2]);
    set(3, 3, tz);
    (eye, m)
}

/// **The real cross-crate drift guard** — the one the hollow
/// `camera_seam_3d_matches_orbit_camera_math` claimed to be.
///
/// The delegated `OrbitCamera` must still reproduce the historical look-at math it
/// replaced. The closed-form frame is an *algebraic* identity with the cross-product
/// one, not a bitwise one (`sin`/`cos` vs `cross`/`normalize`), so this asserts a
/// stated relative bound rather than `to_bits` equality — and the bound is justified
/// by measurement: the worst drift observed over the sweep is printed and asserted
/// to be under 1e-5, which at a 600 px viewport is under a hundredth of a pixel.
/// A looser epsilon is NOT acceptable here and the non-vacuity check below pins it.
#[test]
fn the_delegated_orbit_camera_still_matches_the_historical_lookat_math() {
    let poses: [(f32, f32, f32, f32); 6] = [
        // (azimuth_deg, elevation_deg, distance, fov_deg)
        (37.0, 12.0, 3.2, 50.0),
        (-115.0, 62.0, 1.4, 35.0),
        (223.0, 80.0, 8.0, 65.0),
        (91.0, 3.5, 0.6, 50.0),
        (180.0, 45.0, 5.0, 50.0),
        (-44.0, 88.0, 12.0, 20.0),
    ];
    let mut worst_eye = 0.0f32;
    let mut worst_mat = 0.0f32;
    let mut worst_px = 0.0f32;

    for (az, el, dist, fov) in poses {
        let cam = OrbitCamera {
            target: V3::new(0.35, 0.10, -0.20),
            azimuth: az.to_radians(),
            elevation: el.to_radians(),
            distance: dist,
            fov_y: fov.to_radians(),
            ..Default::default()
        };

        let aspect = PANE.0 / PANE.1;
        let (ref_eye, ref_m) = historical_orbit_reference(&cam, aspect);

        let e = cam.eye();
        for (got, want) in [(e.x, ref_eye[0]), (e.y, ref_eye[1]), (e.z, ref_eye[2])] {
            worst_eye = worst_eye.max((got - want).abs());
        }
        let m = cam.view_proj(aspect);
        for i in 0..16 {
            worst_mat = worst_mat.max((m[i] - ref_m[i]).abs());
        }

        // And the APPLIED OUTPUT: projected pixels, which is what actually ships.
        for g in GROUND {
            let p = cam.project(V3::new(g[0], 0.0, g[1]), (417.0, 271.0), 300.0);
            if !p.visible {
                continue;
            }
            // Project the same point through the historical matrix.
            let v = [g[0], 0.0, g[1], 1.0];
            let mut o = [0.0f32; 4];
            for row in 0..4 {
                o[row] = (0..4).map(|c| ref_m[c * 4 + row] * v[c]).sum();
            }
            if o[3] <= 1e-6 {
                continue;
            }
            // The matrix lane is NDC-centred; compare the pixel magnitude of the
            // offset from centre, which is the quantity both lanes agree on.
            let ndc_x = o[0] / o[3];
            let ref_px = 417.0 + ndc_x * (PANE.0 * 0.5);
            worst_px = worst_px.max((p.x - ref_px).abs());
        }
    }

    println!("[item6] worst |Δeye| = {worst_eye:e}, |Δmatrix| = {worst_mat:e}, |Δpx| = {worst_px:.6}");
    assert!(worst_eye < 1e-6, "eye drifted from the historical math by {worst_eye:e}");
    assert!(worst_mat < 1e-5, "view_proj drifted from the historical math by {worst_mat:e}");
    assert!(worst_px < 0.01, "projected pixels drifted by {worst_px} px");

    // NON-VACUITY: the bound is tight enough to catch a real error. A camera one
    // degree off must blow straight through it — otherwise the epsilons above
    // would be tolerating drift, not measuring its absence.
    let a = OrbitCamera {
        target: V3::new(0.35, 0.10, -0.20),
        azimuth: 37f32.to_radians(),
        elevation: 12f32.to_radians(),
        distance: 3.2,
        ..Default::default()
    };
    let mut b = a.clone();
    b.azimuth = 38f32.to_radians();
    let (_, mb) = historical_orbit_reference(&b, PANE.0 / PANE.1);
    let ma = a.view_proj(PANE.0 / PANE.1);
    let off = (0..16).map(|i| (ma[i] - mb[i]).abs()).fold(0.0f32, f32::max);
    assert!(off > 1e-3, "a 1-degree error must exceed the tolerance, was only {off:e}");
}

// ── 2. the centrepiece: 2D == constrained 3D, in pixels ──────────────────────

/// **The 2D lane's projected pixels, from an INDEPENDENT reference.**
///
/// This is the pre-unification affine written out verbatim — `facett-map`'s
/// `MapTransform::project` / `project::to_screen` before item 6. It deliberately
/// does NOT call `facett-map`, because those functions now delegate to `View`:
/// comparing them to `View` would be comparing X to X and would prove nothing. The
/// shipping lanes are checked against this same reference in
/// `the_shipping_2d_lanes_are_all_on_the_unified_path`.
fn reference_2d_pixels(px_per_unit: [f32; 2], ref_xy: [f32; 2], centre: (f32, f32)) -> Vec<(f32, f32)> {
    GROUND
        .iter()
        .map(|g| {
            (
                (g[0] - ref_xy[0]) * px_per_unit[0] + centre.0,
                (g[1] - ref_xy[1]) * px_per_unit[1] + centre.1,
            )
        })
        .collect()
}

/// Build the 3D lane's projected pixels via the unified view, constrained.
fn three_d_pixels(v: &View, centre: (f32, f32)) -> Vec<(f32, f32)> {
    GROUND
        .iter()
        .map(|g| {
            let p = v.project_ground(*g, centre, PANE.1 * 0.5);
            (p.x, p.y)
        })
        .collect()
}

/// **THE PARITY ASSERTION.** Start from a genuinely rotated + tilted perspective
/// 3D view, constrain it (tilt 0, bearing 0, ortho, `Z = 0`), and it must land
/// *exactly* on `facett-map`'s 2D writer — the same triangles rasterizing to
/// byte-identical frame buffers.
///
/// Every value is deliberately away from the identity: zoom is 3072 px/unit (not
/// 1), the camera target is `(0.0011, 0.0009)` (not the origin), the pane centre is
/// `(417, 271)` (not `(400, 300)` and not `(0, 0)`), and the starting pose is
/// `azimuth 127°, tilt 58°` (not `0, 0`). A parity test sitting on the identity
/// cannot tell a working transform from a dead one.
#[test]
fn two_d_ortho_equals_three_d_constrained_in_projected_pixels() {
    let ref_xy = [0.001_10f32, 0.000_90f32];
    let centre = (pane_rect().center().x, pane_rect().center().y);
    assert!(
        (centre.0 - 417.0).abs() < 1e-3 && (centre.1 - 271.0).abs() < 1e-3,
        "the pane centre must be off-centre for this test to mean anything, got {centre:?}"
    );

    // A real 3D view: rotated, tilted, perspective, heightfield, full post stack.
    let mut v = View::perspective_3d(
        Vec3::new(ref_xy[0], 0.0, ref_xy[1]),
        127f32.to_radians(),
        58f32.to_radians(),
        4.0,
        50f32.to_radians(),
    );
    assert!(!v.is_constrained_2d(), "must start genuinely unconstrained");
    assert!(v.post.any() && v.terrain == Terrain::Heightfield, "and with the 3D stack on");

    // ── constrain it: THIS is the "2D is a constrained 3D" step ──
    v.constrain_2d(PANE.1 * 0.5);
    assert!(v.is_constrained_2d(), "the constraint must hold after collapsing");
    let Projection::Ortho { .. } = v.projection else { panic!("must be ortho") };
    // Override the derived scale with a big, definitely-not-1 map zoom.
    let px_per_unit = [3072.0f32, 3072.0f32];
    v.projection = Projection::Ortho { px_per_unit };
    assert!(px_per_unit[0] > 1000.0, "the zoom must be far from the identity");

    let a = reference_2d_pixels(px_per_unit, ref_xy, centre);
    let b = three_d_pixels(&v, centre);

    // Coordinate parity first, so a failure names the point.
    let mut worst = 0.0f32;
    for (i, (p, q)) in a.iter().zip(b.iter()).enumerate() {
        worst = worst.max((p.0 - q.0).abs()).max((p.1 - q.1).abs());
        assert_eq!(p.0.to_bits(), q.0.to_bits(), "x differs at ground point {i}: {p:?} vs {q:?}");
        assert_eq!(p.1.to_bits(), q.1.to_bits(), "y differs at ground point {i}: {p:?} vs {q:?}");
    }
    assert_eq!(worst, 0.0, "the reduction is exact, not approximate");

    // NON-IDENTITY: the projection must have actually moved these points apart and
    // away from the pane centre, or we compared six copies of one pixel.
    let spread = a.iter().fold(0.0f32, |m, p| m.max((p.0 - centre.0).abs().max((p.1 - centre.1).abs())));
    assert!(spread > 3.0, "the projected points must spread across the pane, spread was {spread}");
    let distinct: std::collections::HashSet<(u32, u32)> =
        a.iter().map(|p| (p.0.to_bits(), p.1.to_bits())).collect();
    assert_eq!(distinct.len(), GROUND.len(), "every ground point must project distinctly");

    // ── APPLIED OUTPUT: rasterize both and compare the frame buffers ──
    let fa = rasterize(&a);
    let fb = rasterize(&b);
    assert!(lit(&fa) > 200, "the 2D lane must actually draw pixels, lit {}", lit(&fa));
    assert_eq!(lit(&fa), lit(&fb), "both lanes must light the same pixel count");
    assert!(fa == fb, "the two rasterized frames must be byte-identical");
}

// ── 3. the mutations: break the constraint, the parity must go RED ────────────

/// **MUTATION — leave the tilt on.** The constraint is load-bearing: if the parity
/// still held with a non-zero tilt, it would be proving nothing.
///
/// Rather than a magic "N pixels changed" threshold, this asserts the **quantitative
/// prediction** a tilted orthographic view makes: the vertical extent must shrink by
/// exactly `cos(tilt)` and the horizontal extent must not move at all. If the
/// constraint were a no-op the ratio would be 1.0, and the assertion below brackets
/// it tightly enough that it cannot be.
#[test]
fn leaving_the_tilt_on_breaks_the_parity() {
    let ref_xy = [0.001_10f32, 0.000_90f32];
    let centre = (pane_rect().center().x, pane_rect().center().y);
    let px_per_unit = [3072.0f32, 3072.0f32];
    let tilt_deg = 12.0f32;

    let mut v = View::ortho_2d_map(ref_xy, px_per_unit);
    let a = reference_2d_pixels(px_per_unit, ref_xy, centre);
    assert!(three_d_pixels(&v, centre) == a, "the constrained view must agree to begin with");

    // Violate exactly ONE constraint.
    v.tilt = tilt_deg.to_radians();
    assert!(!v.is_constrained_2d(), "the predicate must notice");
    let b = three_d_pixels(&v, centre);
    assert!(b != a, "a {tilt_deg}-degree tilt must move the projection");

    // THE PREDICTION: y foreshortens by cos(tilt), x is untouched.
    let extent = |p: &[(f32, f32)]| {
        let (mut x0, mut x1, mut y0, mut y1) = (f32::MAX, f32::MIN, f32::MAX, f32::MIN);
        for q in p {
            x0 = x0.min(q.0);
            x1 = x1.max(q.0);
            y0 = y0.min(q.1);
            y1 = y1.max(q.1);
        }
        (x1 - x0, y1 - y0)
    };
    let (ax, ay) = extent(&a);
    let (bx, by) = extent(&b);
    assert!(ax > 200.0 && ay > 150.0, "the figure must be big enough to measure: {ax} x {ay}");
    let want = tilt_deg.to_radians().cos();
    println!("[item6] tilt {tilt_deg} deg: y extent {ay:.1} -> {by:.1} (ratio {:.5}, cos = {want:.5})", by / ay);
    assert!((by / ay - want).abs() < 1e-4, "y must foreshorten by cos(tilt), ratio was {}", by / ay);
    assert!((bx - ax).abs() < 1e-3, "x must be untouched by tilt, {ax} -> {bx}");
    assert!(want < 0.99, "the mutation must be a real violation, cos was {want}");

    // ...and it must move PIXELS, not just coordinates.
    let (fa, fb) = (rasterize(&a), rasterize(&b));
    assert!(lit(&fa) > 5_000, "the figure must actually cover pixels, lit {}", lit(&fa));
    assert!(fa != fb, "the rasterized frames must differ");
    let moved = fa.iter().zip(fb.iter()).filter(|(x, y)| x != y).count();
    println!("[item6] and it moved {moved} pixels of {}", fa.len());
    assert!(moved > 1_000, "only {moved} pixels moved");
}

/// **MUTATION — leave the bearing on.** The other rotational constraint, checked
/// separately so one of them cannot mask the other.
///
/// The quantitative prediction here is that the figure **rotates by exactly the
/// bearing**: the screen-space angle of a chord between two ground points must turn
/// by the bearing angle, and every distance must be preserved (a rotation is rigid).
#[test]
fn leaving_the_bearing_on_breaks_the_parity() {
    let ref_xy = [0.001_10f32, 0.000_90f32];
    let centre = (pane_rect().center().x, pane_rect().center().y);
    let px_per_unit = [3072.0f32, 3072.0f32];
    let bearing_deg = 15.0f32;

    let mut v = View::ortho_2d_map(ref_xy, px_per_unit);
    let a = reference_2d_pixels(px_per_unit, ref_xy, centre);
    assert!(three_d_pixels(&v, centre) == a, "must agree while constrained");

    v.set_compass_bearing_deg(bearing_deg);
    assert!(!v.is_constrained_2d(), "the predicate must notice a {bearing_deg}-degree bearing");
    let b = three_d_pixels(&v, centre);
    assert!(b != a, "a {bearing_deg}-degree bearing must move the projection");

    // THE PREDICTION: rigid rotation by `bearing_deg`, chord lengths preserved.
    let chord = |p: &[(f32, f32)], i: usize, j: usize| (p[j].0 - p[i].0, p[j].1 - p[i].1);
    let ang = |(x, y): (f32, f32)| y.atan2(x).to_degrees();
    let len = |(x, y): (f32, f32)| (x * x + y * y).sqrt();
    let mut turned = Vec::new();
    for (i, j) in [(0usize, 1usize), (0, 2), (3, 4), (1, 5)] {
        let (ca, cb) = (chord(&a, i, j), chord(&b, i, j));
        assert!(len(ca) > 50.0, "the chord {i}-{j} must be long enough to measure: {}", len(ca));
        assert!(
            (len(cb) - len(ca)).abs() < 0.05,
            "a rotation is rigid: chord {i}-{j} went {} -> {}",
            len(ca),
            len(cb)
        );
        let mut d = ang(cb) - ang(ca);
        while d > 180.0 { d -= 360.0 }
        while d < -180.0 { d += 360.0 }
        turned.push(d);
    }
    println!("[item6] bearing {bearing_deg} deg turned the chords by {turned:?}");
    for d in &turned {
        assert!(
            (d.abs() - bearing_deg).abs() < 0.05,
            "every chord must turn by exactly the bearing, one turned {d}"
        );
    }

    let (fa, fb) = (rasterize(&a), rasterize(&b));
    let moved = fa.iter().zip(fb.iter()).filter(|(x, y)| x != y).count();
    println!("[item6] and it moved {moved} pixels");
    assert!(moved > 1_000, "only {moved} pixels moved");
}

/// **MUTATION — let `Z` through.** GFX_V2's third constraint is `Z(x,y) = 0`. With
/// `Terrain::Flat` a heightfield sample must be *discarded*; with `Heightfield` it
/// must reach the pixel. Driven with a real, non-zero sample.
#[test]
fn letting_the_terrain_height_through_breaks_the_flat_plane() {
    let ref_xy = [0.001_10f32, 0.000_90f32];
    let centre = (pane_rect().center().x, pane_rect().center().y);
    let sample = 0.35f32; // a real hill, in design units

    // The 2D view discards it: the projected pixel is identical with and without.
    let flat = View::ortho_2d_map(ref_xy, [3072.0, 3072.0]);
    assert_eq!(flat.terrain_height(sample), 0.0, "Flat must pin Z to 0");
    let flat_lifted = flat.project_world(
        Vec3::new(GROUND[2][0], flat.terrain_height(sample), GROUND[2][1]),
        centre,
        PANE.1 * 0.5,
    );
    let flat_ground = flat.project_ground(GROUND[2], centre, PANE.1 * 0.5);
    assert_eq!(flat_lifted.x.to_bits(), flat_ground.x.to_bits(), "Z cannot reach a 2D pixel");
    assert_eq!(flat_lifted.y.to_bits(), flat_ground.y.to_bits(), "Z cannot reach a 2D pixel");

    // Now the MUTATION: flip the terrain mode and the same sample MUST move the
    // pixel. (Tilted, because a height change under a perfectly top-down ortho
    // camera legitimately projects to the same pixel — that is what "plan view"
    // means, and a mutation test that ignored it would be testing nothing.)
    let mut hf = flat;
    hf.terrain = Terrain::Heightfield;
    hf.tilt = 40f32.to_radians();
    assert_eq!(hf.terrain_height(sample), sample, "Heightfield must pass Z through");
    let lifted = hf.project_world(
        Vec3::new(GROUND[2][0], hf.terrain_height(sample), GROUND[2][1]),
        centre,
        PANE.1 * 0.5,
    );
    let ground = hf.project_ground(GROUND[2], centre, PANE.1 * 0.5);
    let dy = (lifted.y - ground.y).abs();
    println!("[item6] a {sample}-unit hill moved the pixel {dy:.2} px once Z was let through");
    assert!(dy > 10.0, "letting Z through must move the pixel, moved only {dy}");
}

// ── 4. non-vacuity: the 3D path is doing real work when unconstrained ────────

/// "2D and constrained-3D agree" would be worthless if the 3D path were itself a
/// no-op. This asserts the unconstrained 3D mode genuinely foreshortens, genuinely
/// reads the heightfield, and genuinely asks for the post stack — while the 2D mode
/// does none of those.
#[test]
fn the_unconstrained_three_d_path_is_not_a_no_op() {
    let centre = (417.0f32, 271.0f32);
    let half_h = PANE.1 * 0.5;
    let v3 = View::perspective_3d(Vec3::ZERO, 0.0, 55f32.to_radians(), 3.0, 50f32.to_radians());
    let v2 = View::ortho_2d_map([0.0, 0.0], [3072.0, 3072.0]);

    // PERSPECTIVE: two points at the same lateral offset but different depths must
    // NOT project to the same lateral pixel offset. Under ortho they must.
    let (near, far) = (Vec3::new(0.2, 0.0, 0.6), Vec3::new(0.2, 0.0, -0.6));
    let (pn, pf) = (v3.project_world(near, centre, half_h), v3.project_world(far, centre, half_h));
    assert!(pn.visible && pf.visible, "both probes must be inside the 3D frustum");
    let conv = ((pn.x - centre.0).abs() - (pf.x - centre.0).abs()).abs();
    println!("[item6] perspective convergence over 1.2 units of depth: {conv:.2} px");
    assert!(conv > 5.0, "perspective must foreshorten, converged only {conv} px");

    let (on, of) = (v2.project_world(near, centre, half_h), v2.project_world(far, centre, half_h));
    assert_eq!(on.x.to_bits(), of.x.to_bits(), "ortho must NOT foreshorten laterally");

    // TERRAIN + POST: the modes must genuinely differ in what they ask the
    // renderer to do, which is why 2D costs nothing extra.
    assert_eq!(v3.terrain, Terrain::Heightfield, "3D reads the heightfield");
    assert_eq!(v2.terrain, Terrain::Flat, "2D pins it flat");
    assert_eq!(v3.post, PostFx::ALL, "3D runs the post stack");
    assert_eq!(v2.post, PostFx::NONE, "2D bypasses it");
    assert!(v3.post.any() && !v2.post.any(), "and `any()` must separate them");
}

// ── 5. the drift the unification EXPOSED, measured and pinned ────────────────

/// **The two skins turn `bearing_deg` opposite ways.** Found by building the
/// unified core, because nothing had ever compared the two cameras.
///
/// At the same nominal `bearing_deg = 90`, a point due north moves LEFT in
/// `facett-map`'s 2D camera and RIGHT in `facett-map3d`'s 3D camera. By compass
/// convention the 2D one is right: facing east puts north on your left. The 3D
/// camera's `bearing_deg` returns the turntable azimuth unchanged, and at azimuth
/// 90° the eye sits due *east* of the target — so the camera faces WEST while
/// reporting a heading of east.
///
/// This test does not fix it: flipping `OrbitCamera`'s sign would rotate a shipping
/// car-nav compass overlay and its route-follow, which is a product decision, not a
/// refactor. It pins the divergence with measured numbers so it cannot drift
/// further or be silently "fixed" in one skin only. `View::compass_bearing_deg`
/// exposes the correct clockwise sense for callers that want it.
#[test]
fn the_two_skins_disagree_about_which_way_bearing_turns() {
    // ── 2D: facett-map's camera ──
    let v2 = facett_map::layer::MapView::new(
        facett_map::layer::LatLon { lon: 17.6, lat: 59.6 },
        12.0,
        (800.0, 600.0),
    )
    .with_bearing(90.0);
    let c2 = v2.project(17.6, 59.6);
    let n2 = v2.project(17.6, 59.7); // due north
    let dx2 = n2.0 - c2.0;

    // ── 3D: facett-map3d's camera, same nominal bearing ──
    let mut cam = OrbitCamera { target: V3::new(0.0, 0.0, 0.0), ..Default::default() };
    cam.set_bearing_deg(90.0);
    cam.set_tilt_deg(60.0);
    cam.settle();
    let c3 = cam.project(V3::new(0.0, 0.0, 0.0), (400.0, 300.0), 300.0);
    let n3 = cam.project(V3::new(0.0, 0.0, -1.0), (400.0, 300.0), 300.0); // due north (Z is south)
    assert!(c3.visible && n3.visible, "both 3D probes must be visible");
    let dx3 = n3.x - c3.x;

    println!("[item6] bearing 90: north moves {dx2:+.1} px in 2D and {dx3:+.1} px in 3D");
    assert!(dx2 < -50.0, "2D must put north to the LEFT at bearing 90 (compass-correct), got {dx2}");
    assert!(dx3 > 50.0, "3D puts north to the RIGHT at bearing 90 (the divergence), got {dx3}");
    assert!(dx2.signum() != dx3.signum(), "the senses are opposite — this is the finding");

    // And the unified core names it precisely: azimuth 90 == compass bearing 270.
    let v = View::perspective_3d(Vec3::ZERO, 90f32.to_radians(), 0.5, 3.0, 0.9);
    assert!((v.azimuth_deg() - 90.0).abs() < 1e-3, "azimuth is 90");
    assert!(
        (v.compass_bearing_deg() - 270.0).abs() < 1e-3,
        "and the true compass heading is 270, got {}",
        v.compass_bearing_deg()
    );
    // The round-trip through the compass setter must land back on the same frame.
    let mut w = v;
    w.set_compass_bearing_deg(270.0);
    let (fa, _, _) = v.basis();
    let (fb, _, _) = w.basis();
    assert!((fa - fb).length() < 1e-6, "compass 270 must rebuild azimuth 90's frame");
}

/// **The shipping 2D lanes really are on the unified path.** The centrepiece parity
/// test compares the constrained 3D view against a reference affine written out by
/// hand, which proves the *core* reduces correctly but says nothing about whether
/// `facett-map` actually calls it. This closes that gap: the two ungated 2D writers
/// `facett-map` ships must reproduce the same reference, bit for bit.
///
/// (`facett_map::gpu::MapTransform` — the GPU lane's writer — is behind the `wgpu`
/// feature and is covered by the same reference in the `wgpu`-enabled test below,
/// which IS run, not skipped.)
#[test]
fn the_shipping_2d_lanes_are_all_on_the_unified_path() {
    let zoom = 3072.0f32;
    let offset = egui::Vec2::new(-517.5, 388.25);

    // Lane 1: `facett_map::project::to_screen`.
    for g in GROUND {
        let want = (g[0] * zoom + offset.x, g[1] * zoom + offset.y);
        let got = facett_map::project::to_screen(g[0], g[1], zoom, offset);
        assert_eq!(got.x.to_bits(), want.0.to_bits(), "to_screen x for {g:?}");
        assert_eq!(got.y.to_bits(), want.1.to_bits(), "to_screen y for {g:?}");
    }

    // Lane 2: `facett_map::layer::MapView::project`, at a NON-zero bearing and tilt
    // so the shared frame is genuinely exercised (at 0/0 the affine is the identity
    // and this would prove nothing).
    let v = facett_map::layer::MapView::new(
        facett_map::layer::LatLon { lon: 17.6, lat: 59.6 },
        13.0,
        (PANE.0, PANE.1),
    )
    .with_bearing(127.0)
    .with_tilt(58.0);
    let ws = v.world_px();
    let (cx, cy) = facett_map::mercator(17.6, 59.6);
    let mut moved = 0.0f32;
    for (lon, lat) in [(17.62, 59.61), (17.58, 59.59), (17.60, 59.64)] {
        let (px, py) = facett_map::mercator(lon, lat);
        let (dx, dy) = ((px - cx) * ws, (py - cy) * ws);
        // The verbatim pre-unification arithmetic.
        let (s, c) = (-(127.0f64).to_radians()).sin_cos();
        let rx = dx * c - dy * s;
        let ry = (dx * s + dy * c) * (58.0f64).to_radians().cos();
        let want = (PANE.0 * 0.5 + rx as f32, PANE.1 * 0.5 + ry as f32);
        let got = v.project(lon, lat);
        assert_eq!(got.0.to_bits(), want.0.to_bits(), "MapView x for ({lon}, {lat})");
        assert_eq!(got.1.to_bits(), want.1.to_bits(), "MapView y for ({lon}, {lat})");
        moved = moved.max((got.0 - PANE.0 * 0.5).abs());
    }
    assert!(moved > 20.0, "the MapView probes must move off the pane centre, max {moved}");
}

/// The GPU lane's 2D writer, `MapTransform`, against the same independent
/// reference. Behind `wgpu` because the module is; **run** with
/// `cargo test -p facett-map3d --features wgpu`, not left to rot.
#[cfg(feature = "wgpu")]
#[test]
fn the_gpu_2d_writer_is_on_the_unified_path() {
    let ref_xy = [0.001_10f32, 0.000_90f32];
    let px_per_unit = [3072.0f32, 3072.0f32];
    let centre = (pane_rect().center().x, pane_rect().center().y);
    let t = facett_map::gpu::MapTransform::new(
        [0.0, 0.0],
        [ref_xy[0] as f64, ref_xy[1] as f64],
        px_per_unit[0],
        px_per_unit[1],
        pane_rect(),
    );
    let want = reference_2d_pixels(px_per_unit, ref_xy, centre);
    for (g, w) in GROUND.iter().zip(want.iter()) {
        let p = t.project(*g);
        assert_eq!(p[0].to_bits(), w.0.to_bits(), "MapTransform x for {g:?}");
        assert_eq!(p[1].to_bits(), w.1.to_bits(), "MapTransform y for {g:?}");
    }

    // And the cull inverse must round-trip the pane corners exactly, or the compute
    // cull would drop ways that are on screen.
    let bbox = t.viewport_bbox(pane_rect());
    assert!(bbox[0] < bbox[2] && bbox[1] < bbox[3], "cull bbox must be non-degenerate: {bbox:?}");
    // The envelope is the pane corners DILATED by the widest stroke the painter can draw
    // (`style::cull_pad_merc`, seam-15, `9c80a55` 2026-08-22 — three weeks after this
    // test was written, which is why it compared the bare pane span and has read
    // "cull span 0.27018228 vs 0.26041666" ever since: 30 px = 2 × the 15 px pad, on
    // every vendor, because this is CPU arithmetic behind the `wgpu` feature, not a
    // GPU measurement). The round-trip is still exact: the bare span plus both pads.
    let span_x = pane_rect().width() / px_per_unit[0];
    let pad = facett_map::style::cull_pad_merc(f64::from(px_per_unit[0])) as f32;
    assert!(pad > 0.0, "the cull pad must widen the envelope, got {pad}");
    let want = span_x + 2.0 * pad;
    assert!(
        (bbox[2] - bbox[0] - want).abs() < 1e-6,
        "cull span {} vs pane {span_x} + 2 x pad {pad} = {want}",
        bbox[2] - bbox[0]
    );
}

// ── 6. the post-FX bypass seam, and the honest limit of it today ─────────────

/// **The post-FX bypass is derived FROM the view**, not set beside it: one function
/// both modes ask, so "2D skips the post chain" cannot disagree with the view it
/// describes. This is what makes GFX_V2's "2D costs nothing extra" true — the
/// pipeline is the same pipeline and the passes are simply not recorded.
#[test]
fn the_post_stack_is_bypassed_by_the_2d_constraint_and_untouched_by_3d() {
    use facett_map3d::backend::SceneFlags;

    // Everything on, as the 3D terrain lane configures it.
    let full = SceneFlags {
        sky: true,
        ssao: true,
        soft_shadows: true,
        shadow_strength: 0.85,
        water_on: true,
        planar_reflection: true,
        bloom: true,
        flat_2d: false,
    };
    assert!(full.any_post(), "the 3D stack must want post passes to begin with");

    // A constrained 2D view forces the whole chain off.
    let v2 = View::ortho_2d_map([0.0, 0.0], [3072.0, 3072.0]);
    assert!(v2.is_constrained_2d(), "the 2D view must be constrained");
    let bypassed = full.constrain_to_view(&v2);
    assert!(bypassed.flat_2d, "the bypass flag must be set");
    assert!(!bypassed.any_post(), "and NO post pass may run");
    for (name, on) in [
        ("sky", bypassed.sky),
        ("ssao", bypassed.ssao),
        ("soft_shadows", bypassed.soft_shadows),
        ("bloom", bypassed.bloom),
        ("planar_reflection", bypassed.planar_reflection),
    ] {
        assert!(!on, "{name} must be off in 2D mode");
    }
    assert_eq!(bypassed.shadow_strength, 0.0, "and the shadow depth pass disabled");

    // An unconstrained 3D view must be left EXACTLY alone — this reconciler must
    // never enable a pass or silently disable one the 3D lane wanted.
    let v3 = View::perspective_3d(Vec3::ZERO, 0.7, 0.8, 3.0, 50f32.to_radians());
    assert!(!v3.is_constrained_2d());
    assert_eq!(full.constrain_to_view(&v3), full, "3D flags must pass through untouched");

    // NON-VACUITY: a view that misses ONE constraint must NOT get the bypass, or
    // `constrain_to_view` would be switching the post stack off for tilted views.
    let mut nearly = v2;
    nearly.tilt = 0.001;
    assert!(!nearly.is_constrained_2d(), "a hair of tilt breaks the constraint");
    assert!(full.constrain_to_view(&nearly).any_post(), "and must NOT bypass the post stack");
}

/// **The gap item 6 pinned is now CLOSED — and this is what closed it** (GFX_V2 item 7).
///
/// Item 6 left `flat_2d` as a tested seam that `Map3D` could not reach, for two
/// independent reasons, and pinned the gap so that "the day someone adds the ortho mode
/// it goes red and points at exactly what to finish". That day is this test. It now
/// asserts BOTH halves — the orbit still cannot reach the constraint by tilting, and the
/// plan MODE reaches it exactly — because each half without the other is a half-truth:
///
/// * without the reachable half, `flat_2d` is a flag nothing can set (item 6's honest
///   limit, and the unwired-detector trap);
/// * without the unreachable half, nothing records that an ordinary 3D orbit must NEVER
///   fall into the bypass. `is_constrained_2d()` tests `tilt == 0.0` exactly, so a bypass
///   reachable by dragging would switch the whole post chain off at one float value and
///   back on at the next — a visible pop mid-drag. The clamp staying put is the design,
///   not an omission.
///
/// The clamp was deliberately NOT relaxed. Its stated reason ("so the look-at `up` never
/// degenerates") IS stale — the closed-form basis is well-conditioned at the pole, which
/// `postfx_bypass::the_closed_form_basis_is_well_conditioned_at_the_pole_and_does_not_mirror`
/// now proves on the orthonormality AND on east staying east. But relaxing it would not
/// have helped: an exact-equality constraint is only coherent for a mode you enter. And
/// `MIN_ELEV` guards something else entirely and is still live.
#[test]
#[allow(non_snake_case, reason = "the capitals carry the assertion; the test id stays as published")]
fn the_2d_constraint_is_unreachable_by_TILTING_and_reachable_by_MODE() {
    let mut cam = OrbitCamera::default();

    // ── HALF ONE: the orbit still cannot get there, by any route the API offers. ──
    cam.set_tilt_deg(0.0);
    cam.settle();
    let by_setter = cam.view().tilt;
    assert!(by_setter != 0.0, "set_tilt_deg(0) is still clamped away from exact top-down");

    cam.apply_preset(facett_map3d::camera::ViewPreset::Top);
    cam.settle();
    let by_preset = cam.view().tilt;
    assert!(by_preset != 0.0, "ViewPreset::Top is still clamped");
    println!(
        "[item7] closest tilt reachable by ORBIT: {:.5} deg via set_tilt_deg, {:.5} deg via ViewPreset::Top",
        by_setter.to_degrees(),
        by_preset.to_degrees()
    );
    assert!(by_preset.to_degrees().abs() < 1.0, "close to top-down, still never exact");
    assert!(
        matches!(cam.view().projection, Projection::Perspective { .. }),
        "and the ORBIT is still perspective-only"
    );
    assert!(!cam.view().is_constrained_2d(), "so no amount of tilting reaches the constraint");
    let asked = facett_map3d::backend::SceneFlags { ssao: true, bloom: true, ..Default::default() };
    assert!(
        asked.constrain_to_view(&cam.view()).any_post(),
        "and a tilted 3D view keeps its post stack — the bypass must not be a cliff mid-drag"
    );

    // ── HALF TWO: the MODE gets there exactly, which is what makes the flag live. ──
    cam.plan_2d = true;
    cam.settle();
    let v = cam.view();
    assert_eq!(v.tilt, 0.0, "plan mode is EXACTLY top-down");
    assert_eq!(v.azimuth, 0.0, "and unrotated");
    assert!(matches!(v.projection, Projection::Ortho { .. }), "and orthographic");
    assert!(v.is_constrained_2d(), "so the 2D constraint is satisfied");
    let bypassed = asked.constrain_to_view(&v);
    assert!(bypassed.flat_2d, "flat_2d is now something a real camera can set");
    assert!(!bypassed.any_post(), "and the whole post chain goes off");
    // Every individual lever, not just the aggregate — `any_post()` could be satisfied
    // while one pass stayed on.
    assert!(!bypassed.sky && !bypassed.ssao && !bypassed.bloom, "no sky, no AO, no bloom");
    assert_eq!(bypassed.shadow_strength, 0.0, "no shadow depth pass");
    assert!(!bypassed.planar_reflection, "no planar reflection");

    // And leaving the mode restores the 3D lane byte for byte, so the mode is a mode and
    // not a one-way door.
    cam.plan_2d = false;
    cam.settle();
    assert!(!cam.view().is_constrained_2d(), "leaving plan mode returns to the 3D view");
    assert!(asked.constrain_to_view(&cam.view()).any_post(), "…with its post stack back");

    // The pass count itself is asserted ON THE DEVICE in `tests/postfx_bypass.rs`
    // (0 passes in plan mode, 10 in perspective). This test is the pure-predicate half.
}