facett-geomap/tests/callchain_matrix.rs
//! **Render call-chain coverage matrix** for `facett-geomap` (LAW: every fn in
//! the input→pixel chain TRACKED + VERIFIED; return-asserted where it proves
//! correctness, EMIT where it can't).
//!
//! The 2D OSM-viewer chain (`OsmView`):
//!
//! ```text
//! read points/ways (lon,lat)
//! → MercatorCamera (to_px / to_merc / viewport_merc_bbox)
//! → zoom-to-cursor / pan / fit (zoom_to / pan_px / fit_merc_bbox)
//! → PointGrid cull + LOD stride (build / visible_count / visible_into)
//! → MapIndex LOD-tier + bbox cull (way_lod / query_visible)
//! → point-stride budget (point_stride / budget_stride)
//! → clip to widget rect (clip::clip_polyline / clip_polygon)
//! → paint fills+lines+points (Facet::ui CPU painter)
//! → pixels (egui_kittest robot drive)
//! ```
//!
//! ~1000 matrix cases: zoom levels × extents × LOD tiers × canvas sizes ×
//! datasets, fed real WKB/GeoParquet-shaped `(lon,lat)` input, asserting at each
//! stage. EMIT-doctrine rows cover the rasterized output the return can't expose.
//!
//! Run: `cargo test -p facett-geomap --test callchain_matrix`
use egui::{Rect, pos2, vec2};
use facett_core::harness;
use facett_geomap::{MercatorCamera, OsmView, PointGrid, mercator};
// ── matrix axes ───────────────────────────────────────────────────────────────
const ZOOMS: &[f64] = &[128.0, 1_000.0, 4_000.0, 20_000.0, 100_000.0, 1_000_000.0];
const EXTENTS: &[f64] = &[8.0, 2.0, 0.5, 0.1, 0.02];
const CANVASES: &[(f32, f32)] = &[(200.0, 150.0), (400.0, 300.0), (800.0, 600.0), (1280.0, 720.0), (300.0, 900.0)];
const CENTRES: &[(f64, f64)] = &[
(0.0, 0.0),
(9.55, 47.15),
(18.07, 59.33),
(-122.4, 37.8),
(139.7, 35.7),
(151.2, -33.9),
(-43.2, -22.9),
(37.6, 55.75),
];
fn rect(w: f32, h: f32) -> Rect {
Rect::from_min_size(pos2(0.0, 0.0), vec2(w, h))
}
/// A deterministic point cloud of `n` points in an `ext`-degree box at a centre.
fn cloud(lon: f64, lat: f64, ext: f64, n: usize) -> Vec<(f64, f64)> {
let mut s: u64 = 0x9E3779B97F4A7C15 ^ (lon.to_bits() ^ lat.to_bits());
let mut next = || {
s ^= s << 13;
s ^= s >> 7;
s ^= s << 17;
(s >> 11) as f64 / (1u64 << 53) as f64
};
(0..n).map(|_| (lon - ext / 2.0 + next() * ext, lat - ext / 2.0 + next() * ext)).collect()
}
// ── stage 1+2: MercatorCamera projection + zoom-to-cursor anchor invariance ────
/// INJECT-ASSERT — the camera's RETURN proves correctness: `to_px`/`to_merc`
/// round-trip exactly, `viewport_merc_bbox` brackets the rect corners, and
/// zoom-to-cursor keeps the anchored geo point FIXED on screen (the slippy-map
/// standard). Walked over zooms × centres × canvases × anchors.
#[test]
fn camera_roundtrip_viewport_and_anchor_invariance() {
let mut cases = 0usize;
let mut ok = 0usize;
for &(cw, ch) in CANVASES {
let r = rect(cw, ch);
for &z in ZOOMS {
for &(lon, lat) in CENTRES {
let (mx, my) = mercator(lon, lat);
let cam = MercatorCamera { center: (mx, my), zoom: z, target_zoom: z, pitch_deg: 0.0 };
// round-trip px↔merc
for &anchor in &[r.center(), r.min, pos2(cw * 0.75, ch * 0.25)] {
let m = cam.to_merc(anchor, r);
let back = cam.to_px(m.0, m.1, r);
assert!((back.x - anchor.x).abs() < 1e-2 && (back.y - anchor.y).abs() < 1e-2, "px↔merc roundtrip");
}
// viewport bbox brackets the rect's projected corners
let vp = cam.viewport_merc_bbox(r);
let a = cam.to_merc(r.min, r);
let b = cam.to_merc(r.max, r);
assert!(vp[0] <= a.0.min(b.0) as f32 + 1e-4 && vp[2] >= a.0.max(b.0) as f32 - 1e-4, "vp x brackets");
// zoom-to-cursor: the anchor geo point is unchanged after a zoom.
// The feature's contract holds while the camera centre is not
// clamped at the world edge (the [0,1] clamp can't compensate past
// the edge — an expected, documented limit), so we only assert
// fixity in the unclamped regime and skip the edge cells.
let mut c2 = cam;
let anchor = pos2(cw * 0.7, ch * 0.3);
let before = c2.to_merc(anchor, r);
c2.zoom_to(2.0, anchor, r);
let clamped = c2.center.0 <= 1e-9 || c2.center.0 >= 1.0 - 1e-9 || c2.center.1 <= 1e-9 || c2.center.1 >= 1.0 - 1e-9;
assert!(c2.zoom >= cam.zoom, "zoom_to(2) did not shrink zoom");
if clamped {
continue; // edge cell — the clamp legitimately moves the anchor
}
let after = c2.to_merc(anchor, r);
// tolerance scaled to ~1px at this zoom (merc units = px / zoom).
let tol = (2.0 / c2.zoom).max(1e-6);
let fixed = (before.0 - after.0).abs() < tol && (before.1 - after.1).abs() < tol;
assert!(fixed, "zoom anchored (tol {tol:.2e}): {before:?} vs {after:?}");
cases += 1;
if fixed {
ok += 1;
}
}
}
}
let total = CANVASES.len() * ZOOMS.len() * CENTRES.len();
assert!(cases > total / 2, "most cells in the unclamped regime were tested: {cases}/{total}");
assert_eq!(ok, cases, "every unclamped cell kept the anchor fixed");
#[cfg(feature = "testmatrix")]
facett_core::testmatrix::emit(
"facett-geomap.camera",
"roundtrip_viewport_and_anchor_fixed",
ok == cases,
&format!("{ok}/{cases} unclamped camera cases (zoom-to-cursor anchored, {total} total)"),
);
}
/// INJECT-ASSERT — `fit_merc_bbox` returns a camera whose viewport CONTAINS the
/// fitted data bbox (with margin) and whose centre is the bbox centre. Walked
/// over extents × centres × canvases.
#[test]
fn fit_merc_bbox_contains_the_data() {
let mut cases = 0usize;
let mut ok = 0usize;
for &(cw, ch) in CANVASES {
let r = rect(cw, ch);
for &(lon, lat) in CENTRES {
for &ext in EXTENTS {
let (x0, y0) = mercator(lon - ext / 2.0, lat + ext / 2.0);
let (x1, y1) = mercator(lon + ext / 2.0, lat - ext / 2.0);
let mut cam = MercatorCamera::default();
cam.fit_merc_bbox([x0, y0, x1, y1], r);
// centre is the bbox centre
assert!((cam.center.0 - (x0 + x1) / 2.0).abs() < 1e-6, "centre x = bbox mid");
// the fitted viewport contains the data bbox
let vp = cam.viewport_merc_bbox(r);
let contains = vp[0] as f64 <= x0 + 1e-6 && vp[1] as f64 <= y0 + 1e-6 && vp[2] as f64 >= x1 - 1e-6 && vp[3] as f64 >= y1 - 1e-6;
assert!(contains, "fitted viewport {vp:?} contains data [{x0},{y0},{x1},{y1}]");
cases += 1;
if contains {
ok += 1;
}
}
}
}
#[cfg(feature = "testmatrix")]
facett_core::testmatrix::emit(
"facett-geomap.camera",
"fit_contains_data",
ok == cases,
&format!("{ok}/{cases} fit cases"),
);
assert_eq!(ok, cases, "every case in the sweep passed ({ok}/{cases})");
}
// ── stage 3: PointGrid cull + LOD decimation ───────────────────────────────────
/// INJECT-ASSERT — `PointGrid::visible_count`'s RETURN is exact (a tight bbox
/// sees only the contained points; the whole-world bbox sees all), and
/// `visible_into` with a stride keeps ⌈visible/stride⌉ points and is monotone:
/// a bigger stride keeps no more. Walked over centres × extents × strides.
#[test]
fn point_grid_cull_is_exact_and_stride_is_monotone() {
let mut cases = 0usize;
let mut ok = 0usize;
for &(lon, lat) in CENTRES {
for &ext in EXTENTS {
let pts = cloud(lon, lat, ext, 1_000);
let grid = PointGrid::build(pts.clone());
assert_eq!(grid.len(), 1_000);
// whole-world sees all
assert_eq!(grid.visible_count([0.0, 0.0, 1.0, 1.0]), 1_000, "whole world sees all");
// a tight box around the centre sees a subset, and visible_into agrees
let (mx, my) = mercator(lon, lat);
let d = (ext * 0.25 / 360.0) as f32;
let vp = [mx as f32 - d, my as f32 - d, mx as f32 + d, my as f32 + d];
let n = grid.visible_count(vp);
assert!(n <= 1_000);
// stride monotonicity + count formula
let mut prev = usize::MAX;
for stride in [1usize, 2, 4, 16] {
let mut out = Vec::new();
grid.visible_into(vp, stride, &mut out);
// kept == ⌈n/stride⌉ (the grid keeps every stride-th in-rect point)
let expect = n.div_ceil(stride);
assert_eq!(out.len(), expect, "stride {stride}: kept {} == ceil({n}/{stride})", out.len());
assert!(out.len() <= prev || prev == usize::MAX, "bigger stride keeps no more");
prev = out.len();
// every kept point is inside the rect
for &(px, py) in &out {
assert!(px >= vp[0] && px <= vp[2] && py >= vp[1] && py <= vp[3], "kept point in rect");
}
}
cases += 1;
ok += 1;
}
}
#[cfg(feature = "testmatrix")]
facett_core::testmatrix::emit(
"facett-geomap.point_grid",
"cull_exact_stride_monotone",
ok == cases,
&format!("{ok}/{cases} grid cases"),
);
assert_eq!(ok, cases, "every case in the sweep passed ({ok}/{cases})");
}
// ── stage 4+5: way LOD + point stride monotone in zoom, budget cap ─────────────
/// INJECT-ASSERT — `way_lod` and `point_stride` are monotone in zoom (zooming in
/// → finer LOD, smaller stride), and `budget_stride` caps drawn points to the
/// budget while never going below the zoom-LOD floor. Driven through OsmView's
/// public navigation surface at each zoom.
#[test]
fn way_lod_and_point_stride_monotone_in_zoom_with_budget_cap() {
let (points, ways) = facett_geomap::osm2d::sample_dataset(2_000);
let r = rect(800.0, 600.0);
let mut prev_stride = usize::MAX;
let mut ok = true;
// Walk the zoom axis from zoomed-OUT (high stride) to zoomed-IN (low stride)
// and assert the zoom-LOD stride floor is NON-INCREASING (finer LOD as we
// zoom in). ZOOMS is sorted ascending, so the stride must be non-increasing.
for &z in ZOOMS {
let mut v = OsmView::new("z", points.clone(), ways.clone());
v.fit_to(r);
v.camera.zoom = z;
v.camera.target_zoom = z;
let st = stride_floor(&mut v);
if prev_stride != usize::MAX {
ok &= st <= prev_stride;
assert!(st <= prev_stride, "zoom {z}: stride {st} <= prev {prev_stride}");
}
prev_stride = st;
}
// budget cap: a dense fit caps drawn points; render proves drawn <= budget.
let dense: Vec<(f64, f64)> = cloud(9.55, 47.15, 0.18, 120_000);
let mut dv = OsmView::new("dense", dense, vec![]);
dv.fit_to(r);
let res = harness::headless_render(&mut dv);
let drawn = res.state["drawn_points"].as_u64().unwrap() as usize;
let max = res.state["max_drawn_points"].as_u64().unwrap() as usize;
assert!(drawn <= max, "drawn {drawn} <= budget {max}");
ok &= drawn <= max;
#[cfg(feature = "testmatrix")]
facett_core::testmatrix::emit(
"facett-geomap.lod_stride",
"monotone_and_budget_capped",
ok,
&format!("strides monotone; dense drawn={drawn}<=budget={max}"),
);
assert!(ok, "stride monotone in zoom AND dense fit capped to the budget");
}
/// Read the OsmView's zoom-LOD point stride floor at its current zoom, via a
/// render (which writes `zoom_lod_stride` into state_json — the observable seam).
fn stride_floor(v: &mut OsmView) -> usize {
let res = harness::headless_render(v);
res.state["zoom_lod_stride"].as_u64().unwrap() as usize
}
// (stage 6 — the viewport clip — is a private module; it is verified end-to-end
// through the high-zoom render-matrix cells below, which route way geometry
// through `clip_polyline`/`clip_polygon`, plus its own in-module unit tests +
// testmatrix rows in `osm2d/clip.rs`.)
// ── stage 7: paint (headless) — the ~1000-case render sweep ─────────────────────
/// **THE MATRIX** (zooms × extents × centres × canvases). For each cell: build a
/// real OsmView (point cloud + the sample ways), FIT, set the zoom, render
/// headless at the canvas size, and:
/// - inject-ASSERT the state_json (points/ways present, drawn ≤ budget, stride
/// ≥ 1, visible ≤ total),
/// - EMIT: the headless render DREW vertices (non-blank).
#[test]
fn render_matrix_zooms_x_extents_x_centres_x_canvases() {
let mut cases = 0usize;
let mut drew = 0usize;
let mut bounded = 0usize;
let (_, sample_ways) = facett_geomap::osm2d::sample_dataset(4);
for &(lon, lat) in CENTRES {
for &ext in EXTENTS {
let pts = cloud(lon, lat, ext, 800);
for &z in ZOOMS {
for &(cw, ch) in CANVASES {
let mut v = OsmView::new("cell", pts.clone(), sample_ways.clone());
v.fit_to(rect(cw, ch));
v.camera.zoom = z;
v.camera.target_zoom = z;
let r = harness::render_sized(&mut v, (cw, ch));
// inject-assert observable state
assert_eq!(r.state["points"], 800, "all points reported");
let drawn = r.state["drawn_points"].as_u64().unwrap() as usize;
let max = r.state["max_drawn_points"].as_u64().unwrap() as usize;
let stride = r.state["point_stride"].as_u64().unwrap() as usize;
let vis = r.state["visible_points"].as_u64().unwrap() as usize;
assert!(stride >= 1, "stride >= 1");
assert!(drawn <= max, "drawn <= budget");
assert!(vis <= 800, "visible <= total");
if drawn <= max && stride >= 1 {
bounded += 1;
}
if r.drew() {
drew += 1;
}
cases += 1;
}
}
}
}
assert!(cases >= 1000, "the matrix has >= 1000 cases, got {cases}");
assert_eq!(bounded, cases, "every cell stayed within the draw budget, {bounded}/{cases}");
assert_eq!(drew, cases, "every populated cell drew vertices (non-blank), {drew}/{cases}");
eprintln!("[geomap-matrix] {cases} cells, all drew, all within budget");
#[cfg(feature = "testmatrix")]
facett_core::testmatrix::emit(
"facett-geomap.render_matrix",
"all_cells_draw_within_budget",
drew == cases && bounded == cases,
&format!("{cases} cells (zooms×extents×centres×canvases), {drew} drew, {bounded} budget-ok"),
);
}
/// Negative cell: an EMPTY OsmView draws only the hint (few verts) while a
/// populated one draws far more — the non-blank proxy is not vacuous.
#[test]
fn empty_geomap_cell_is_separable_from_populated() {
let mut empty = OsmView::new("empty", vec![], vec![]);
let re = harness::headless_render(&mut empty);
assert_eq!(re.state["points"], 0);
let (pts, ways) = facett_geomap::osm2d::sample_dataset(2_000);
let mut full = OsmView::new("full", pts, ways);
let r = rect(800.0, 600.0);
full.fit_to(r);
// Zoom to a building-scale camera: at the whole-dataset FIT the building-vertex
// LOD culls the point layer outright (that frame is a sea of corner dots — see
// `tests/osm_point_lod.rs`), so a "populated draws more ink" probe has to look
// at a camera where the corner layer is actually admitted.
full.update(facett_geomap::osm2d::Msg::ZoomTo { k: 3.0, anchor: r.center(), rect: r });
while full.camera_mut().tick() {}
let rf = harness::headless_render(&mut full);
assert!(rf.vertices > re.vertices * 2, "populated >> empty: {} vs {}", rf.vertices, re.vertices);
#[cfg(feature = "testmatrix")]
facett_core::testmatrix::emit(
"facett-geomap.render_matrix",
"empty_vs_populated_separable",
rf.vertices > re.vertices * 2,
&format!("empty={} populated={} verts", re.vertices, rf.vertices),
);
}