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

nordisk/facett

facett-demo/src/demo_systemmap.rs

//! **The `System Map` tab** — a nice little **fluid system map** of the nordisk
//! constellation: components are **glass tanks/junctions** joined by translucent
//! **glass pipes** with **fluid visibly flowing through them** (fluid = data/activity
//! moving between components).
//!
//! [`facett_graph3d::SystemMap`] already IS a [`Facet`](facett::Facet) — a glass-pipe
//! skin over an embedded `Graph3D` (camera/layout/projection reused, L5) with the
//! fluid driven by [`facett_cfd::FluidField`] (the gatling-accelerated 1-D dye
//! advection). So — like [`crate::demo_codevault`] — the demo just hands the deck a
//! populated `SystemMap`; no wrapper.
//!
//! **The three zeroes** (documented on the frame path in
//! `facett-graph3d/src/pipes.rs`): zero-copy (the renderer borrows each pipe's dye
//! slice; the fluid double-buffer is pointer-swapped), zero-alloc (all sim + tint
//! buffers allocated once; the step is a test-proven no-alloc ping-pong), zero-wait
//! (the fluid advances via a non-blocking gatling fork-join through the single Elm
//! `update` path — the UI never blocks on the sim).

use egui::Color32;
use facett::graph3d::{Layout3D, SystemMap};

/// The stable tab title (the deck/discovery-contract key + the legend key). This is
/// also the picker label the user clicks to select the 🫧 system-map demo.
pub const TITLE: &str = "System Map";

/// Build the demo system map: a clean topology of the nordisk constellation —
/// `holger → nornir → dwarves → korp` plus `facett`, `skade`, `knut` — as labelled
/// glass tanks wired by glass pipes with fluid flowing between them. 7 components, a
/// handful of pipes; Force layout reads most like a plumbing/system diagram.
pub fn demo_systemmap() -> SystemMap {
    let c = |r, g, b| Color32::from_rgb(r, g, b);
    // Per-component categorical colours (the fluid in each outgoing pipe is tinted by
    // its source component, so you can read WHOSE data is flowing).
    let nodes: Vec<(String, Color32)> = vec![
        ("holger".to_string(), c(200, 150, 240)),  // 0 — artifact server
        ("nornir".to_string(), c(90, 170, 250)),   // 1 — code-knowledge server (hub)
        ("dwarves".to_string(), c(240, 170, 70)),  // 2 — workers
        ("korp".to_string(), c(230, 120, 150)),    // 3 — the consumer
        ("facett".to_string(), c(120, 210, 140)),  // 4 — UI constellation
        ("skade".to_string(), c(80, 200, 220)),    // 5 — catalog
        ("knut".to_string(), c(250, 210, 90)),     // 6 — population graph
    ];
    // The flow: artifacts + queries stream through nornir out to dwarves → korp, with
    // facett/skade/knut feeding the hub.
    let edges = vec![
        (0, 1), // holger → nornir  (artifacts)
        (1, 2), // nornir → dwarves (work)
        (2, 3), // dwarves → korp   (results)
        (1, 4), // nornir → facett  (viz feed)
        (4, 3), // facett → korp    (UI)
        (5, 1), // skade → nornir   (catalog)
        (6, 1), // knut → nornir    (population)
        (2, 6), // dwarves → knut   (enrichment)
    ];
    SystemMap::new(nodes, edges, Layout3D::Force).with_title(TITLE)
}

/// **The same map with REAL LIQUID** — the opt-in prototype (`SystemMap::with_liquid`).
/// Identical topology and paint, but the pipes carry gravity-driven liquid instead of
/// the decorative dye: drag **vertically** to tip the map and it pours downhill, pools
/// in the glass tanks, and cannot leave the bore. Charged from a fixed seed, stepped
/// from a host-supplied `dt` — reproducible.
///
/// **This is now the SHIPPED System Map tab** — the deck (`lib.rs`), the declarative
/// shell scene (`scene_demo.rs`) and the mega-matrix coverage cell all build
/// `demo_systemmap_liquid()`. [`demo_systemmap`] (the plain dye map) remains as a
/// **separate constructor on purpose**: it is the byte-identical status-quo map, still
/// covered by its own unit test + the `system_map.png` screenshot, so both the dye and
/// the real-liquid renders stay proven. Because the liquid observables are *additive*
/// (`state_json` keeps `fluid`/`glass_alpha` and merely ADDS `liquid`/`tilt`/`gravity`),
/// the full-deck goldens — which render tab 0, not this one — are unmoved by the swap.
///
/// Design: `facett/.nornir/systemmap-fluid-design.md`.
pub fn demo_systemmap_liquid() -> SystemMap {
    demo_systemmap().with_liquid(true)
}

#[cfg(test)]
mod tests {
    use super::*;
    use facett::Facet;

    #[test]
    fn demo_systemmap_is_the_populated_constellation_with_fluid_pipes() {
        let m = demo_systemmap();
        let j = m.state_json();
        assert_eq!(m.title(), TITLE);
        assert_eq!(j["components"], 7, "seven nordisk components as glass tanks");
        assert_eq!(j["pipes"], 8, "eight glass pipes");
        assert_eq!(j["fluid"]["pipes"], 8, "one fluid pipe per edge");
        assert!(j["glass_alpha"].as_f64().unwrap() < 1.0, "the pipes are translucent glass");
        // `demo_systemmap()` is the plain DYE constructor — the byte-identical status-quo
        // map, kept as the (now-unshipped) sibling so the dye render stays covered. It has
        // no `liquid` engine; the SHIPPED tab builds `demo_systemmap_liquid()` instead
        // (proven by `the_shipped_deck_system_map_tab_now_carries_real_liquid`).
        assert!(j.get("liquid").is_none(), "the plain dye constructor carries no liquid");
    }

    /// The opt-in liquid map is the SAME topology, charged and containable.
    #[test]
    fn demo_systemmap_liquid_is_the_same_map_with_real_liquid_in_it() {
        let mut m = demo_systemmap_liquid();
        assert_eq!(m.title(), TITLE);
        let j = m.state_json();
        assert_eq!(j["components"], 7);
        assert_eq!(j["pipes"], 8);
        assert_eq!(j["liquid"]["pipes"], 8, "one liquid tube per edge");
        let v0 = j["liquid"]["total_volume"].as_f64().unwrap();
        assert!(v0 > 1.0, "the map is charged with liquid");
        // Tip it right over and let it pour: volume survives, containment holds.
        let _ = m.update(facett::graph3d::SystemMapMsg::SetTilt(2.0));
        for _ in 0..600 {
            m.tick_fluid(1.0 / 60.0);
        }
        let j = m.state_json();
        assert!((j["liquid"]["total_volume"].as_f64().unwrap() - v0).abs() / v0 < 1e-3, "volume conserved");
        assert!(j["liquid"]["max_fill"].as_f64().unwrap() <= 1.0, "nothing escaped the pipes");
    }

    /// **The SHIPPED-tab guard.** The real-liquid map is promoted to the live gallery: the
    /// deck (`lib.rs`) builds `demo_systemmap_liquid()`, so the deck's composed
    /// `state_json` must expose the `System Map` facet WITH a real-liquid engine. This is
    /// the inverse of the pre-promotion invariant (`liquid.is_none()`): it goes RED the
    /// moment someone reverts the tab wiring back to the plain dye `demo_systemmap()`.
    #[test]
    fn the_shipped_deck_system_map_tab_now_carries_real_liquid() {
        let demo = crate::DemoState::new();
        let deck = demo.deck.state_json();
        let sm = &deck["facets"][TITLE];
        assert!(!sm.is_null(), "the deck ships a `{TITLE}` tab");
        // The shipped tab keeps the dye fluid + glass, AND now carries real liquid.
        assert_eq!(sm["pipes"], 8, "eight glass pipes");
        assert_eq!(sm["fluid"]["pipes"], 8, "the dye fluid is still present");
        assert!(sm["glass_alpha"].as_f64().unwrap() < 1.0, "translucent glass");
        assert!(sm.get("liquid").is_some(), "the SHIPPED System Map tab now carries REAL LIQUID");
        assert_eq!(sm["liquid"]["pipes"], 8, "one real-liquid tube per edge");
        assert!(sm["liquid"]["total_volume"].as_f64().unwrap() > 1.0, "the shipped map is charged with liquid");
        assert!(sm["liquid"]["max_fill"].as_f64().unwrap() <= 1.0, "the liquid is contained");
    }
}