facett-map/tests/feature_count_memo.rs
//! **GG #12 — the applied-output count is memoised: a still frame walks no layer.**
//!
//! korp's flat canvas reads, after every paint, *how many features each layer drew* — the
//! "applied output" its Quality table and its robot drives assert on. It read that through
//! `MapLayer::feature_count(&cam)` per enabled layer, i.e. a second `ways.iter().filter(draws)`
//! walk over every way on every frame (the audit's "two walks per frame", row 12): the paint
//! walked the ways once through the draw-list memo, and the count walked them again, still
//! screen or not.
//!
//! The answer's inputs are exactly the draw list's — the layer epoch (geometry, enabled set)
//! and the camera — so the counts are memoised on `(layer_epoch, view)`, and a walk counter
//! is the oracle. Stated as WORK ELIMINATED, not milliseconds, for the reason
//! `tests/draw_list_memo.rs` gives at length (contention-dominated boxes).
//!
//! Driven RED first against `feature_counts` without the memo: 100 identical asks over 3
//! layers walked **300** layers; the memo's contract is **3**.
use egui::Color32;
use facett_map::Way;
use facett_map::layer::{LatLon, MapFacet, MapTheme, MapView, PointsLayer, WaysLayer};
const PANE: (f32, f32) = (1600.0, 1000.0);
fn ways(n: usize, seed: f64) -> Vec<Way> {
(0..n)
.map(|k| {
let f = k as f64;
let y = 59.0 + (f * 0.00013 + seed) % 0.20;
let x = 18.0 + (f * 0.00021 + seed) % 0.30;
let line: Vec<(f64, f64)> =
(0..12).map(|j| (x + j as f64 * 0.0004, y + ((j % 5) as f64) * 0.0003)).collect();
Way::line(k as i64, "road", line, Color32::GRAY, 1.0)
})
.collect()
}
fn facet() -> MapFacet {
let view = MapView::new(LatLon::new(59.10, 18.15), 11.0, PANE);
let pts: Vec<(f64, f64)> = (0..500).map(|k| (18.0 + k as f64 * 0.0005, 59.05 + (k % 7) as f64 * 0.01)).collect();
MapFacet::new("gg12", view, MapTheme::default())
.with_layer(Box::new(WaysLayer::new("roads-a", ways(4_000, 0.0))))
.with_layer(Box::new(WaysLayer::new("roads-b", ways(4_000, 0.07))))
.with_layer(Box::new(PointsLayer::new("points", pts, Color32::RED)))
}
#[test]
fn a_hundred_still_frames_walk_the_layers_once_and_the_counts_are_the_layers_own() {
let f = facet();
let first = f.feature_counts();
assert_eq!(first.len(), 3, "one row per enabled layer");
// The memo must answer with the SAME numbers the un-memoised per-layer read gives.
for (id, n) in first.iter() {
let lay = f.layers().iter().find(|l| l.id() == id).expect("a layer with that id");
assert_eq!(*n, lay.feature_count(f.view()), "layer {id}: memo vs direct count");
}
let total: usize = first.iter().map(|(_, n)| n).sum();
assert!(total > 0, "the fixture must be in view, or the test measures nothing");
assert_eq!(f.feature_count(), total, "the facet total is the sum of the rows");
let walks_after_first = f.feature_count_walk_count();
assert_eq!(walks_after_first, 3, "the first ask walks each of the 3 layers exactly once");
for _ in 0..100 {
let again = f.feature_counts();
assert_eq!(*again, *first, "a still frame answers the same rows");
}
assert_eq!(
f.feature_count_walk_count(),
walks_after_first,
"100 still frames walked {} layer(s); the contract is 0",
f.feature_count_walk_count() - walks_after_first
);
}
#[test]
fn a_camera_move_and_a_toggle_each_re_walk_once_and_nothing_else_does() {
let mut f = facet();
let _ = f.feature_counts();
let base = f.feature_count_walk_count();
// A pan: the answer can change, so the layers are walked again — once each.
f.view_mut().center.lon += 0.05;
let moved = f.feature_counts();
assert_eq!(f.feature_count_walk_count(), base + 3, "a pan re-walks the 3 layers once");
let _ = f.feature_counts();
assert_eq!(f.feature_count_walk_count(), base + 3, "the frame after the pan is still");
// A toggle: one layer fewer is enabled, so 2 walks, and the row for it is GONE — the
// caller must read disabled as absent, not as a stale count.
assert!(f.set_layer_enabled("roads-b", false));
let toggled = f.feature_counts();
assert_eq!(f.feature_count_walk_count(), base + 5, "a toggle re-walks the 2 enabled layers");
assert_eq!(toggled.len(), 2);
assert!(toggled.iter().all(|(id, _)| id != "roads-b"), "a disabled layer has no row");
assert_ne!(*toggled, *moved);
// A theme change is NOT an input to the count — no walk.
f.set_theme(MapTheme::default());
let _ = f.feature_counts();
assert_eq!(f.feature_count_walk_count(), base + 5, "the theme is not a count input");
}