facett-map/tests/rail_tier_pixels.rs
//! **THE RAILS ARE ON THE CANVAS AT THE CAMERA HE WAS LOOKING FROM** — pixels, not tiers
//! (`railtier-1`).
//!
//! `style::tests::a_national_railway_is_drawn_at_the_camera_a_country_is_looked_at_from`
//! asserts the DRAWN PREDICATE (`layer::lod_visible`). That is the right unit test and it is
//! still one step short of the claim: `lod_visible` returning `true` is a statement about a
//! comparison, and what the owner reported is a statement about a canvas. So this file paints
//! the picture and counts the ink.
//!
//! # What it measures
//!
//! One straight `railway=rail` line across a pane at camera **9.33** — the exact reading off
//! `$KORP_STATE.map.scene.applied.layers` on his live thin client, where the `rails` layer
//! was fed **7 217** ways and drew **0**. The measurement is the count of pixels whose hue
//! is `MapPalette::rail()` (the ink the new arm resolves), inside the canvas rows, against a
//! reference render with no rail layer at all.
//!
//! # Why the CPU painter, deliberately
//!
//! It is not a degraded mode. It is what draws under a bearing, under a tilt, on a host whose
//! adapter bring-up was refused, and in every `egui_kittest` harness test in this repo — and
//! it is what makes this file runnable with no GPU and no lock to contend for. `cpu_casing.rs`
//! makes the same argument at greater length and this file follows its harness exactly rather
//! than growing a second one.
//!
//! # THE RED
//!
//! Before `classify_with` grew its `railway` arm, `railway=rail` fell through to the generic
//! arm's `Lod::City` (camera 12) and the ink count at camera 9.33 was **0** — the pane
//! painted land and nothing else, which is what "fed 7 217, drew 0" looks like as a picture.
//!
//! Run: `cargo test -p facett-map --test rail_tier_pixels -- --test-threads=1`
use facett_map::layer::{LatLon, MapFacet, MapTheme, MapView, WaysLayer};
use facett_map::Way;
// **This file SIGSEGVs under the default test runner and always has** — two
// `Harness::render()` calls on two threads, one wgpu device each. MEASURED 2026-08-27:
// `signal: 11, SIGSEGV` on `cargo test -p facett-map --test rail_tier_pixels`, 2 passed at
// `--test-threads=1`. The header's "Run: … --test-threads=1" is true and reaches nobody who
// types a plain `cargo test`, so the flag is now a lock.
#[path = "common/one_gpu.rs"]
mod one_gpu;
const CANVAS: egui::Vec2 = egui::vec2(900.0, 640.0);
/// **The camera off `$KORP_STATE`.** 9.33, tier `District` — the zoom at which a Swede
/// looking for the rail network is looking at the rail network.
const HIS_CAMERA: f32 = 9.33;
/// A dead-straight east-west trunk line across the middle of the pane.
///
/// Straight and horizontal so the band is a clean run of pixels in any vertical column, and
/// long enough (0.4° of longitude, ~22 km) that it crosses the whole canvas at this zoom
/// rather than ending inside it.
///
/// The width is bumped the way `cpu_casing.rs` bumps its road's and for the same reason: at
/// the arm's own 1.5 px a rail's core is narrower than its antialias feather, so no pixel
/// comes back PURE `rail()` and a hue count measures the blend rather than the ink. The TIER
/// is what this file is about, and the tier is untouched by the width.
fn trunk_line(pal: &facett_map::MapPalette) -> Vec<Way> {
let lat = 59.32;
let pts: Vec<(f64, f64)> = (0..=20).map(|i| (17.90 + f64::from(i) * 0.02, lat)).collect();
let tags = vec![("railway".to_owned(), "rail".to_owned())];
let mut w = Way::from_lonlat_with(1, &pts, &tags, pal).expect("a straight rail line");
w.style.width = 12.0;
vec![w]
}
/// Render a pane at `zoom`, with the rail layer or without it.
///
/// **One theme for both halves**, the trap `cpu_casing.rs` documents: a way's colours are
/// resolved at construction from the palette handed to `Way::from_lonlat_with`, while the
/// pane paints with `MapTheme`'s own palette. Building the way from one and the pane from
/// another gives a correct picture and a test looking for the wrong hue.
fn render(zoom: f32, with_rail: bool) -> (MapTheme, image::RgbaImage) {
let theme = facett_core::Theme::default();
let map_theme = MapTheme::day(&theme);
let pal = map_theme.palette;
let view = MapView::new(LatLon::new(59.32, 18.10), zoom, (CANVAS.x, CANVAS.y));
let mut f = MapFacet::new("rail-tier-pixels", view, map_theme.clone());
if with_rail {
f.push_layer(Box::new(WaysLayer::new("net-rails", trunk_line(&pal))));
}
let mut h = egui_kittest::Harness::builder().with_size(CANVAS).build_ui(move |ui| {
f.ui(ui);
});
h.run_steps(3);
(map_theme, h.render().expect("the CPU painter rendered into a framebuffer"))
}
/// Chebyshev distance between an sRGB triple and a colour.
fn dist(a: [u8; 3], b: egui::Color32) -> i32 {
(i32::from(a[0]) - i32::from(b.r()))
.abs()
.max((i32::from(a[1]) - i32::from(b.g())).abs())
.max((i32::from(a[2]) - i32::from(b.b())).abs())
}
/// The rows of the image that are MAP CANVAS — mostly the land colour.
///
/// The harness draws the facet inside its own chrome and that chrome is pure black; without
/// finding the canvas first, a census counts the frame. `cpu_casing.rs` was measured wrong
/// exactly this way once (a 4.5 px road reported as a 640 px band).
fn canvas_rows(img: &image::RgbaImage, land: egui::Color32) -> (u32, u32) {
let mut rows: Vec<u32> = Vec::new();
for y in 0..img.height() {
let n = (0..img.width())
.filter(|&x| {
let p = img.get_pixel(x, y).0;
dist([p[0], p[1], p[2]], land) <= 6
})
.count();
if n as f32 > img.width() as f32 * 0.5 {
rows.push(y);
}
}
assert!(!rows.is_empty(), "no row of this render is mostly land — the pane never painted");
(rows[0], rows[rows.len() - 1])
}
/// Pixels within the canvas rows whose hue is `ink`, within `tol`.
fn ink_count(img: &image::RgbaImage, ink: egui::Color32, rows: (u32, u32), tol: i32) -> u64 {
let mut n = 0u64;
for y in rows.0..=rows.1 {
for x in 0..img.width() {
let p = img.get_pixel(x, y).0;
if dist([p[0], p[1], p[2]], ink) <= tol {
n += 1;
}
}
}
n
}
/// **A NATIONAL RAIL LINE PUTS INK ON THE CANVAS AT CAMERA 9.33.**
///
/// Three renders, because one number is not a measurement:
///
/// 1. **with the rail layer, at his camera** — the claim. Ink must be there.
/// 2. **without the rail layer, same camera** — the reference. Whatever the palette's rail
/// hue happens to coincide with elsewhere on an empty pane is subtracted, so "the land is
/// a shade of grey too" cannot be what passes this.
/// 3. **with the rail layer, at camera 5** — one rung below the tier ladder's start for
/// `Lod::Country`… which is 0, so this render must ALSO have ink. It is the non-vacuity
/// for the tier being `Country` and not merely "some tier that happens to include 9.33":
/// a trunk line is a country-scale object and must survive a country-scale camera.
#[test]
fn a_trunk_rail_line_paints_at_his_camera_and_at_a_country_camera() {
let _gpu = one_gpu::hold();
let (theme, with) = render(HIS_CAMERA, true);
let (_, without) = render(HIS_CAMERA, false);
let ink = theme.palette.rail();
let land = theme.palette.land;
let rows_with = canvas_rows(&with, land);
let rows_without = canvas_rows(&without, land);
// Tolerance 10, the same order `cpu_casing.rs` uses for a hue assert: wide enough that a
// pixel at the band's core counts, narrow enough that the land (a very different value —
// asserted below) cannot.
let n_with = ink_count(&with, ink, rows_with, 10);
let n_without = ink_count(&without, ink, rows_without, 10);
assert!(
dist([land.r(), land.g(), land.b()], ink) > 20,
"the rail ink {ink:?} and the land {land:?} are within 20 of each other — the census \
below cannot tell them apart, and a passing count would mean nothing"
);
assert_eq!(
n_without, 0,
"an empty pane already carries {n_without} px of the rail hue — the reference render \
is not a reference"
);
assert!(
n_with > 500,
"the rail layer put {n_with} px of {ink:?} on the canvas at camera {HIS_CAMERA}. \
On the owner's live client this layer was fed 7 217 ways and drew 0: `railway=*` fell \
through `classify_with` to the generic arm's Lod::City, a tier LOD_TIER_ZOOMS does \
not enter until camera 12, so every rail way was culled at every camera below it."
);
// ── AND AT A COUNTRY CAMERA. The tier is `Lod::Country`, floor 0, which is korp's own
// first Rails rung — so the line has to survive a camera three rungs wider than his.
//
// **The number here is a FOOTPRINT, not a band.** At camera 5 the fixture's 0.4° of
// longitude is ~9 px long (0.4/360 × 256·2⁵), so what lands on the canvas is a 9 px blob,
// `line_width_px(12, zoom 5)` tall — **6 px** since GG #27 made the CPU lane read the one
// width formula (0.5× floor at country scale) instead of a pinned 1.0×. The old threshold
// of 100 was cleared by a 9 × 12 blob by a hair and failed the 9 × 6 one at 60, which is
// the line being DRAWN THINNER at a wide camera, not the line being culled — exactly the
// distinction this arm exists to make. So: a reference render with no rail at this camera
// must carry no ink, and the rail render must carry more than a third of the 9 × 6
// footprint. A culled line reads 0 here; a tier that stops short of Country reads 0 here.
let (_, country) = render(5.0, true);
let (_, country_without) = render(5.0, false);
let rows_country = canvas_rows(&country, land);
let n_country = ink_count(&country, ink, rows_country, 10);
let n_country_without = ink_count(&country_without, ink, canvas_rows(&country_without, land), 10);
assert_eq!(
n_country_without, 0,
"an empty pane at camera 5 already carries {n_country_without} px of the rail hue"
);
assert!(
n_country > 18,
"at camera 5 the trunk line put only {n_country} px on the canvas (a 9 px long, \
6 px tall footprint is ~54). Lod::Country means every camera there is; a tier that \
merely happens to include 9.33 would pass the assertion above and read 0 here."
);
}
/// **A YARD IS NOT A TRUNK LINE — the declutter, in pixels.**
///
/// The test above would pass on a build that lifted every `railway=*` value to
/// `Lod::Country`, which is the clutter defect from the other side: `rails.parquet` carries
/// platforms, sidings, turntables and signal boxes, and korp's own ladder holds them to
/// camera 12. So the same pane, the same camera, one tag changed: the platform must put NO
/// ink on the canvas at 9.33 and must put ink on it at 12.
#[test]
fn a_platform_stays_off_the_canvas_until_the_camera_earns_it() {
let _gpu = one_gpu::hold();
let theme = facett_core::Theme::default();
let map_theme = MapTheme::day(&theme);
let pal = map_theme.palette;
let ink = pal.rail();
let land = pal.land;
let paint = |zoom: f32, value: &str| -> u64 {
let lat = 59.32;
let pts: Vec<(f64, f64)> = (0..=20).map(|i| (17.90 + f64::from(i) * 0.02, lat)).collect();
let tags = vec![("railway".to_owned(), value.to_owned())];
let mut w = Way::from_lonlat_with(1, &pts, &tags, &pal).expect("a straight rail way");
w.style.width = 12.0;
let view = MapView::new(LatLon::new(59.32, 18.10), zoom, (CANVAS.x, CANVAS.y));
let mut f = MapFacet::new("rail-tier-pixels", view, map_theme.clone());
f.push_layer(Box::new(WaysLayer::new("net-rails", vec![w])));
let mut h = egui_kittest::Harness::builder().with_size(CANVAS).build_ui(move |ui| {
f.ui(ui);
});
h.run_steps(3);
let img = h.render().expect("the CPU painter rendered into a framebuffer");
let rows = canvas_rows(&img, land);
ink_count(&img, ink, rows, 10)
};
let platform_at_his_camera = paint(HIS_CAMERA, "platform");
let platform_at_city = paint(12.0, "platform");
let trunk_at_his_camera = paint(HIS_CAMERA, "rail");
assert_eq!(
platform_at_his_camera, 0,
"a railway=platform put {platform_at_his_camera} px on the canvas at camera \
{HIS_CAMERA}. korp holds platforms, sidings and yards to camera 12; a painter that \
draws them at 9 is the clutter defect this ladder exists to prevent."
);
assert!(
platform_at_city > 500,
"…and at camera 12 the platform drew only {platform_at_city} px. A tier that never \
opens is a wall, not a rung."
);
assert!(
trunk_at_his_camera > 500,
"the trunk line drew {trunk_at_his_camera} px at the same camera the platform drew \
none — this is the pair that proves the ladder is a ladder and not one threshold"
);
}