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

//! **Where the 3D pane's zoom coupling actually lives, in korp's own camera units.**
//!
//! [`extrude::town_window`] takes the viewport's half-span and clamps it into
//! `[TOWN_HALF_MIN_M, TOWN_HALF_MAX_M]`. `camera_picks_the_town::
//! the_window_follows_the_zoom_and_is_clamped_at_both_ends` proves that clamp is
//! monotone and strictly moving **in the band** — it samples viewport spans of 1 200,
//! 2 000 and 2 600 m. Those are correct numbers and they are also numbers korp's map
//! camera does not produce at any rung it serves: on a 1900x1150 pane the z12 viewport
//! is **37 042 m** (korp `5f0ba51`), so a 2 000 m viewport is camera ~16.2.
//!
//! The consequence is not stated anywhere, and it is the first thing a user meets:
//! **from z0 to z15.6 the extruded scene does not change at all.** Not approximately —
//! identically. MEASURED here on a synthetic 2.4 km town, and MEASURED on the live
//! korp-server through a real focused 1900x1150 Xvfb window (2026-08-05, RTX 4090
//! [discrete/Vulkan], `korp-ui` ORIGINAL atom): `metres_per_design_unit` **1 874.0** at
//! camera 9.30 and **1 875.0** at camera 14.10 — the same scene scale five zoom levels
//! apart, with `town_window.half_m = 1500.0` at both. It first moves at camera 16.10,
//! where `half_m` reads 715.1 and the design unit drops to 893.9.
//!
//! So this file does not assert that the flat band is wrong — the cap is deliberate and
//! [`extrude::TOWN_HALF_MAX_M`] argues for it from a measurement. It asserts **where the
//! band is**, in camera units, so the claim "the window follows the zoom" can never
//! again read wider than the evidence under it.

use facett_core::Theme;
use facett_map::Way;
use facett_map3d::{extrude, Map3D};

/// The ONE writer of the flat-Earth ruler these fixtures are built on: the extruder's
/// own [`facett_map3d::extrude::M_PER_DEG_LAT`]. A fixture built on a private copy of
/// 111_320 measures a DIFFERENT earth from the code under test the moment either side is
/// edited — and the copies agree until then, which is exactly how this survived in nine
/// files (`granska-20`).
use facett_map3d::extrude::M_PER_DEG_LAT;
const STOCKHOLM: (f64, f64) = (18.0686, 59.3293);
const SIGTUNA: (f64, f64) = (17.7239, 59.6169);

/// korp's MEASURED z12 viewport span on a 1900x1150 pane (korp `5f0ba51`:
/// `156543.03 * cos(59.33) / 2^12 = 19.49 m/px × 1900 px`). Every camera zoom below is
/// derived from this one measurement, so a pane-size change moves the whole ladder
/// together instead of half of it.
const KORP_Z12_SPAN_M: f64 = 37_042.0;

/// The viewport span, in metres, korp's map camera has at zoom `z`.
fn span_at(z: f64) -> f64 {
    KORP_Z12_SPAN_M * 2f64.powf(12.0 - z)
}

/// The camera zoom at which korp's viewport span is `span_m` — the inverse of
/// [`span_at`], so the two cannot drift.
fn zoom_at(span_m: f64) -> f64 {
    12.0 + (KORP_Z12_SPAN_M / span_m).log2()
}

use facett_map3d::extrude::m_per_deg_lon;

fn ring(lon: f64, lat: f64, half_lon: f64, half_lat: f64) -> Vec<(f64, f64)> {
    vec![
        (lon - half_lon, lat - half_lat),
        (lon + half_lon, lat - half_lat),
        (lon + half_lon, lat + half_lat),
        (lon - half_lon, lat + half_lat),
        (lon - half_lon, lat - half_lat),
    ]
}

fn tags(kv: &[(&str, &str)]) -> Vec<(String, String)> {
    kv.iter().map(|(k, v)| (k.to_string(), v.to_string())).collect()
}

fn houses(id0: i64, centre: (f64, f64), n: usize, pitch_m: f64) -> Vec<Way> {
    let (clon, clat) = centre;
    let mpdl = m_per_deg_lon(clat);
    let theme = Theme::default();
    let (h_lon, h_lat) = (6.0 / mpdl, 6.0 / M_PER_DEG_LAT);
    let mut ways = Vec::new();
    let mut id = id0;
    for j in 0..n {
        for i in 0..n {
            let dx = (i as f64 - (n as f64 - 1.0) * 0.5) * pitch_m;
            let dy = (j as f64 - (n as f64 - 1.0) * 0.5) * pitch_m;
            ways.extend(Way::from_lonlat(
                id,
                &ring(clon + dx / mpdl, clat + dy / M_PER_DEG_LAT, h_lon, h_lat),
                &tags(&[("building", "yes")]),
                &theme,
            ));
            id += 1;
        }
    }
    ways
}

/// A region load: a dense inner city (60x60 houses on a 40 m pitch, **~2.4 km across**,
/// deliberately WIDER than [`extrude::TOWN_HALF_MAX_M`] so a narrowing window really
/// does change what is in frame — a town smaller than the cap would hold the scene span
/// fixed for a second, unrelated reason and this file could not tell the two apart),
/// plus region ground cover and a far suburb.
fn region() -> Vec<Way> {
    let (clon, clat) = STOCKHOLM;
    let mpdl = m_per_deg_lon(clat);
    let half_lat = 0.5 * KORP_Z12_SPAN_M / M_PER_DEG_LAT;
    let half_lon = 0.5 * KORP_Z12_SPAN_M / mpdl;
    let theme = Theme::default();
    let mut ways = Vec::new();
    ways.extend(Way::from_lonlat(
        1,
        &ring(clon - half_lon * 0.3, clat + half_lat * 0.2, half_lon * 0.3, half_lat * 0.3),
        &tags(&[("natural", "water")]),
        &theme,
    ));
    ways.push(Way::line(
        2,
        "road",
        vec![(clon - half_lon, clat), (clon + half_lon, clat)],
        egui::Color32::GRAY,
        2.0,
    ));
    ways.extend(houses(1_000, STOCKHOLM, 60, 40.0));
    ways.extend(houses(90_000, SIGTUNA, 26, 32.0));
    ways
}

fn view(centre: (f64, f64), span_m: f64) -> extrude::ViewWindow {
    extrude::ViewWindow { lon: centre.0, lat: centre.1, span_m }
}

/// **The ladder, printed.** Not an assertion — the table a human reads next to the
/// screenshots. Every column here was reproduced on the live server through a real
/// window; see the module docs for the three readings that matter.
#[test]
fn print_the_ladder() {
    let ways = region();
    println!(
        "{:>5} {:>10} {:>9} {:>8} {:>10} {:>10} {:>22} {:>8}",
        "zoom", "span_m", "half_m", "houses", "scene_m", "m/du", "p50/p90/max px", "tris",
    );
    for zi in 9..=19 {
        let z = zi as f64;
        let v = view(STOCKHOLM, span_at(z));
        let w = extrude::town_window(&ways, Some(v)).expect("buildings");
        let mut map = Map3D::from_geometry_in_view("sweep", &ways, Some(v));
        let mpdu = map.metres_per_design_unit();
        let (p50, p90, mx) = (
            map.skyline_percentile_px(1150.0, 0.5),
            map.skyline_percentile_px(1150.0, 0.9),
            map.skyline_percentile_px(1150.0, 1.0),
        );
        map.cam.goal_distance = facett_map3d::camera::OrbitCamera::MIN_DISTANCE;
        map.cam.settle();
        let (near_p50, near_max) =
            (map.skyline_percentile_px(1150.0, 0.5), map.skyline_percentile_px(1150.0, 1.0));
        println!(
            "{zi:>5} {:>10.0} {:>9.0} {:>8} {:>10.0} {mpdu:>10.1} {p50:>6.2}/{p90:>6.2}/{mx:>6.2} {:>8}  \
             | dollied-in p50 {near_p50:.0} / max {near_max:.0} px",
            v.span_m,
            w.half_m,
            w.buildings,
            (mpdu * extrude::DESIGN_FILL) as f64,
            map.triangle_count(),
        );
    }
}

/// **THE CLAIM: the extruded scene moves at EVERY camera zoom korp can reach — the
/// window's travel is now inside the reachable range instead of below it.**
///
/// # What this test used to claim, and why that claim had to be retired
///
/// It used to assert the opposite, correctly: with `half_m = clamp(0.5·span, MIN, MAX)`
/// the window only moves for cameras whose viewport half-span lies between the bounds,
/// which put the band at camera (15.63, 17.53) — **past z15, the deepest rung korp's
/// pyramid cuts**. Its own module docs recorded the consequence on the live server:
/// `metres_per_design_unit` 1874.0 at camera 9.30 and 1875.0 at camera 14.10. Five zoom
/// levels, one scene.
///
/// Then the drive measured the other end: korp's map camera **stops at 16.30**
/// ([`extrude::TOWN_ZOOM_DEEP_SPAN_M`], viewport 1245 m — `aim 17` does not converge).
/// So the band was not merely late, it was UNREACHABLE: there is no camera at which the
/// old relation moved the window at all. The identity relation could not be rescued by
/// choosing better bounds, and [`extrude::town_half_for_span`] replaced it with a log
/// interpolation between the two anchors.
///
/// RED-WHEN-BROKEN, seen red both ways (2026-08-05):
///   * making `town_half_for_span` return `TOWN_HALF_MAX_M` unconditionally (the flat
///     band, i.e. the shipped defect) fails (a) and (b);
///   * equalising `TOWN_HALF_MIN_M` and `TOWN_HALF_MAX_M` collapses the travel to 1.00x
///     and fails (b).
#[test]
fn the_scene_moves_at_every_zoom_korp_can_reach() {
    let ways = region();
    let half_at = |z: f64| {
        extrude::town_window(&ways, Some(view(STOCKHOLM, span_at(z))))
            .expect("buildings")
            .half_m
    };
    // The reachable range, in camera zoom, from the two MEASURED anchors — not literals
    // beside them (L5), so moving an anchor moves the samples with it.
    let (wide, deep) = (
        zoom_at(extrude::TOWN_ZOOM_WIDE_SPAN_M),
        zoom_at(extrude::TOWN_ZOOM_DEEP_SPAN_M),
    );
    println!(
        "reachable camera range z{wide:.2} → z{deep:.2}; window {:.0} m → {:.0} m",
        half_at(wide),
        half_at(deep),
    );

    // (a) MONOTONE AND STRICT at every whole rung inside the reachable range — the
    //     sentence "the 3D pane does not zoom" restated as something the build fails on.
    //     Asserted rung by rung, because a pair of endpoints would pass on a relation
    //     that is flat for five levels and then steps.
    let rungs: Vec<f64> = ((wide.ceil() as i32)..=(deep.floor() as i32)).map(f64::from).collect();
    assert!(rungs.len() >= 4, "the reachable range must span several rungs: {rungs:?}");
    for w in rungs.windows(2) {
        let (a, b) = (half_at(w[0]), half_at(w[1]));
        assert!(
            b < a * 0.95,
            "the window must narrow at EVERY rung the user can reach: z{:.0} {a:.0} m → \
             z{:.0} {b:.0} m is the flat band this file exists to keep out",
            w[0],
            w[1],
        );
    }

    // (b) …BY A REAL AMOUNT over the range, so (a) cannot be satisfied by a 1 % crawl.
    let travel = half_at(wide) / half_at(deep);
    println!("total travel across the reachable range: {travel:.2}x");
    assert!(
        travel > 3.0,
        "the scene must really change across the range the user can drive: {travel:.2}x",
    );

    // (c) …stated on APPLIED OUTPUT, not on the clamp that produced it: the extruded
    //     scene's own design unit at the rungs korp's pyramid actually cuts.
    let du = |z: f64| Map3D::from_geometry_in_view("rung", &ways, Some(view(STOCKHOLM, span_at(z))))
        .metres_per_design_unit();
    let (du9, du12, du15) = (du(9.0), du(12.0), du(15.0));
    println!("metres_per_design_unit: z9 {du9:.1}  z12 {du12:.1}  z15 {du15:.1}");
    assert!(
        du15 < du12 * 0.75,
        "z12 and z15 must extrude DIFFERENT scene scales ({du12:.1} → {du15:.1} m per \
         design unit); equal is the flat band, which is what shipped",
    );

    // (d) …and the far end stays clamped, because a 159 km z9 viewport is not a scene.
    assert!(
        (du9 - du12).abs() < 1e-3,
        "above the wide anchor the window is capped and the scene is one scene \
         ({du9:.1} vs {du12:.1}) — that part is deliberate and must not drift",
    );
}

/// **THE CLAIM THE CAP OWES: at the cap — where the user spends z0 through z17 — the
/// median house must actually be legible.**
///
/// [`extrude::TOWN_HALF_MAX_M`] is the constant that decides what the 3D pane shows at
/// every zoom korp's pyramid serves, because the window is pinned to it through the
/// whole pyramid (the test above). It was derived from a legibility target of
/// [`extrude::LEGIBLE_MEDIAN_HOUSE_PX`] and then never measured against it: at the old
/// 1500 m the ladder's applied `p50` is **2.99 px**, 2.7x short of the 8 px the constant's
/// own docstring claimed for it — inside that same docstring's definition of the pancake.
///
/// So this asserts the target on APPLIED OUTPUT (a projected wall height in pixels
/// through the render camera), not on the constant. The constant is free to move; what
/// may not move is what the user sees at it.
///
/// RED-WHEN-BROKEN, seen red both ways (2026-08-05):
///   * `TOWN_HALF_MAX_M = 1500.0` (the old value) → `p50 2.99 px` → (a) fails.
///   * `TOWN_HALF_MIN_M = 400.0` (the old value) → deepest rung `p50 10.90 px` → (b) fails.
#[test]
fn the_capped_scene_is_legible_which_is_what_the_cap_owes() {
    const VIEWPORT_PX: f32 = 1150.0;
    let ways = region();
    let p50 = |span_m: f64| {
        Map3D::from_geometry_in_view("rung", &ways, Some(view(STOCKHOLM, span_m)))
            .skyline_percentile_px(VIEWPORT_PX, 0.5)
    };

    // (a) AT THE CAP. Any zoom wide enough to be clamped shows this one scene, so this
    //     single reading covers z0..z17 — every rung the tile pyramid actually cuts.
    let at_cap = p50(2.0 * extrude::TOWN_ZOOM_WIDE_SPAN_M); // a region overview: pinned at the cap
    println!(
        "median house at the cap ({} m half-window): {at_cap:.2} px on a {VIEWPORT_PX:.0} px pane \
         (target {})",
        extrude::TOWN_HALF_MAX_M,
        extrude::LEGIBLE_MEDIAN_HOUSE_PX,
    );
    assert!(
        f64::from(at_cap) >= extrude::LEGIBLE_MEDIAN_HOUSE_PX,
        "the capped scene is what the user sees at every rung korp serves, and its median \
         house stands {at_cap:.2} px — under the {} px this cap was derived from. A cap \
         that misses its own legibility target is the pancake with a smaller number on it",
        extrude::LEGIBLE_MEDIAN_HOUSE_PX,
    );

    // (b) AT THE FLOOR — the deep end is what "zoom down among the houses" means, and a
    //     floor that still frames a whole town from outside it has not got there. Held
    //     well above the cap's target so the two ends cannot be satisfied by one value.
    let at_floor = p50(extrude::TOWN_ZOOM_DEEP_SPAN_M); // the closest korp can be driven
    println!("median house at the floor ({} m): {at_floor:.2} px", extrude::TOWN_HALF_MIN_M);
    assert!(
        f64::from(at_floor) >= 3.0 * extrude::LEGIBLE_MEDIAN_HOUSE_PX,
        "at the deepest window the pane can reach the median house is {at_floor:.2} px — \
         still a model on a table, not a street you are standing in",
    );

    // (c) …and the RED CONTROL that keeps (a) and (b) from being one claim twice: the
    //     floor must be a strictly closer place than the cap, by a wide margin.
    assert!(
        at_floor > at_cap * 2.0,
        "cap {at_cap:.2} px → floor {at_floor:.2} px: the two ends of the band must be \
         different places, or this file is asserting one reading twice",
    );
}