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");
}
}