Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/facett

facett-map3d-flight/tests/sweden_flythrough.rs

//! **End-to-end Sweden fly-through (headless, FC-7).** Composes the whole "fly the
//! whole of Sweden" chain and asserts it as behaviour, not "didn't panic":
//!
//! 1. a [`FlightController`] pilots a [`FlightCamera`] along a **scripted input
//!    path** (WASD + mouse-look, injected `dt` — no clock);
//! 2. each frame drives a [`TileLoader`] over a synthetic Sweden [`InMemoryTileSource`]
//!    (`frame` → priority requests, worker streams `TileLoaded`, `poll` drains);
//! 3. we assert the **camera moved** across the country AND that tiles **streamed in +
//!    evicted** as the view shifted, staying inside the tile budget.
//!
//! Run: `cargo test -p facett-map3d-flight --test sweden_flythrough`
//! (add `--features testmatrix` to emit the functional row).

use std::sync::Arc;

use facett_core::Theme;
use facett_map::Way;
use facett_map3d_flight::sweden::SpatialKey;
use facett_map3d_flight::{
    ControlFrame, FlightCamera, FlightController, InMemoryTileSource, Msg, TileLoader, WorldPos,
};

/// Fill an in-memory source with `per_tile` ways for every z-`z` tile in a lon/lat
/// window over south-central Sweden — the region the scripted flight crosses.
fn sweden_source(z: u8, per_tile: usize) -> InMemoryTileSource {
    let mut src = InMemoryTileSource::new();
    let mut lat = 58.6;
    while lat <= 60.0 {
        let mut lon = 16.5;
        while lon <= 19.5 {
            let k = SpatialKey::from_lonlat(lon, lat, z);
            src.insert(k, ways_at(k, per_tile));
            lon += 0.02;
        }
        lat += 0.02;
    }
    src
}

fn ways_at(key: SpatialKey, n: usize) -> Vec<Way> {
    let (lon, lat) = key.center_lonlat();
    let mut out = Vec::new();
    for i in 0..n {
        let d = 0.0003 * (i as f64 + 1.0);
        let ring = [(lon, lat), (lon + d, lat), (lon + d, lat + d), (lon, lat + d), (lon, lat)];
        let tags = vec![("building".to_string(), "yes".to_string())];
        if let Some(w) = Way::from_lonlat(i as i64, &ring, &tags, &Theme::default()) {
            out.push(w);
        }
    }
    out
}

#[test]
fn scripted_flight_streams_sweden_tiles_as_the_camera_flies() {
    let z = 13u8;
    let radius = 6_000.0;
    let budget = 12;
    let src = sweden_source(z, 6);
    let mut loader = TileLoader::new(Arc::new(src), 128);

    let ctl = FlightController::default();
    // Start over Norrköping-ish, look east (+X), a few km up.
    let mut cam = FlightCamera::free_fly(WorldPos::from_lonlat(16.6, 58.9, 3_000.0));
    let start = cam.position;

    // Scripted path: hold W + Shift (boosted forward flight, due east since yaw=0
    // looks +X), a fixed dt — deterministic, no wall-clock. A straight run so the
    // path stays inside the fixture window while crossing many tiles.
    let frame_input = ControlFrame { w: true, shift: true, ..Default::default() };
    let dt = 1.0 / 60.0;

    let mut ever_loaded: std::collections::HashSet<SpatialKey> = Default::default();
    let mut ever_evicted: std::collections::HashSet<SpatialKey> = Default::default();
    let mut total_streamed = 0usize;

    for _ in 0..2500 {
        ctl.apply(&mut cam, &frame_input, dt);
        let origin = cam.make_origin(256.0);
        let report = loader.frame(origin, z, radius, budget);
        for k in report.evicted {
            ever_evicted.insert(k);
        }
        // Deterministically settle this frame's requests.
        for msg in loader.wait_idle() {
            if let Msg::TileLoaded(k, _) = msg {
                ever_loaded.insert(k);
                total_streamed += 1;
            }
        }
        // Budget is honoured every frame.
        assert!(loader.resident_count() <= budget, "resident tiles within budget: {}", loader.resident_count());
    }

    // The camera actually flew across the country (tens of km east).
    let flew = cam.position.ground_distance(start);
    assert!(flew > 30_000.0, "the camera flew a long way across Sweden ({flew:.0} m)");
    // Tiles streamed in as new ground came into view, and old tiles were evicted.
    assert!(ever_loaded.len() > budget, "the flight streamed many distinct tiles ({})", ever_loaded.len());
    assert!(!ever_evicted.is_empty(), "tiles behind the camera were evicted");
    assert!(total_streamed >= ever_loaded.len(), "every distinct tile was streamed at least once");
    // The final resident set is exactly what is in view now (nothing stale).
    let in_view: std::collections::HashSet<SpatialKey> =
        facett_map3d_flight::tiles_in_view(cam.make_origin(256.0), z, radius).into_iter().take(budget).collect();
    let resident = loader.resident_keys();
    assert!(resident.is_subset(&in_view) || in_view.is_subset(&resident), "resident set tracks the current view");

    eprintln!(
        "[sweden_flythrough] flew={flew:.0} m, distinct_tiles={}, streamed={total_streamed}, evicted={}, final_resident={}",
        ever_loaded.len(), ever_evicted.len(), resident.len()
    );

    #[cfg(feature = "testmatrix")]
    facett_core::testmatrix::emit(
        "facett-osm3d-sweden",
        "sweden_flythrough",
        flew > 30_000.0 && ever_loaded.len() > budget && !ever_evicted.is_empty(),
        &format!("flew_m={flew:.0} distinct_tiles={} evicted={} final_resident={}", ever_loaded.len(), ever_evicted.len(), resident.len()),
    );
}