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

nordisk/facett

facett-flow/tests/host.rs

#![cfg(feature = "host")]
use egui::{Rect, pos2, vec2};
use facett_flow::{Camera, CpuStyle, Edge, FlowScene, Graph, Hit, Node, PaintOpts, View};
fn graph() -> Graph {
    Graph {
        nodes: vec![
            Node {
                id: "a".into(),
                label: "A".into(),
                position: [0.0, 0.0],
                group: "g".into(),
                kind: 0,
            },
            Node {
                id: "b".into(),
                label: "B".into(),
                position: [200.0, 100.0],
                group: "g".into(),
                kind: 2,
            },
        ],
        edges: vec![Edge {
            from: "a".into(),
            to: "b".into(),
        }],
    }
}
#[test]
fn mapping_matches_camera_and_roundtrips() {
    let rect = Rect::from_min_size(pos2(31.0, 43.0), vec2(900.0, 640.0));
    let bounds = Rect::from_min_size(pos2(-20.0, -30.0), vec2(500.0, 200.0));
    for i in 0..50 {
        let camera = Camera {
            centre: [i as f32 * 1.37, -i as f32 * 3.2],
            zoom: 0.1 + i as f32 * 0.25,
        };
        let view = camera.view(rect, bounds);
        let point = pos2(41.0, 78.0);
        let fit =
            ((rect.width() - 64.0) / bounds.width()).min((rect.height() - 64.0) / bounds.height());
        let expected =
            rect.center() + (point - pos2(camera.centre[0], camera.centre[1])) * fit * camera.zoom;
        assert!(view.screen(point).distance(expected) < 0.001);
        assert!(view.world(view.screen(point)).distance(point) < 0.001);
    }
    let nav = facett_core::nav::Navigable {
        scale: 2.0,
        offset: [51.0, -19.0],
        ..Default::default()
    };
    let v = View::from_navigable(&nav, rect);
    assert_eq!(v.world(v.screen(pos2(12.0, 7.0))), pos2(12.0, 7.0));
}
#[test]
fn cache_identity_tracks_geometry_and_replacement_is_atomic() {
    let g = graph();
    let mut scene = FlowScene::with_key(g.clone(), 17).unwrap();
    let generation = scene.generation();
    assert_eq!(
        generation,
        FlowScene::with_key(g.clone(), 17).unwrap().generation()
    );
    assert_ne!(
        generation,
        FlowScene::with_key(g.clone(), 18).unwrap().generation()
    );
    scene.replace(g.clone()).unwrap();
    assert_eq!(generation, scene.generation());
    let mut moved = g.clone();
    moved.nodes[0].position[0] += 1.0;
    scene.replace(moved).unwrap();
    assert_ne!(generation, scene.generation());
    let before = scene.graph().clone();
    let mut invalid = g;
    invalid.edges[0].to = "missing".into();
    assert!(scene.replace(invalid).is_err());
    assert_eq!(&before, scene.graph());
}
#[test]
fn cubic_endpoints_and_edge_picking_match() {
    let scene = FlowScene::new(graph()).unwrap();
    assert_eq!(scene.edge_point(0, 0.0), Some(pos2(0.0, 0.0)));
    assert_eq!(scene.edge_point(0, 1.0), Some(pos2(200.0, 100.0)));
    assert_eq!(scene.edge_point(0, 0.5), Some(pos2(100.0, 50.0)));
    assert_eq!(scene.hit(pos2(100.0, 50.0), 1.0), Some(Hit::Edge(0)));
    assert_eq!(scene.hit(pos2(100.0, 75.0), 1.0), None);
    assert!(
        scene
            .hits_in(Rect::from_center_size(pos2(100.0, 50.0), vec2(4.0, 4.0)))
            .contains(&Hit::Edge(0))
    );
}
#[test]
fn label_budget_does_not_limit_visible_nodes_or_cpu_paint() {
    let scene = FlowScene::new(graph()).unwrap();
    let ctx = egui::Context::default();
    let rect = Rect::from_min_size(pos2(0.0, 0.0), vec2(400.0, 300.0));
    let mut painted = None;
    let mut output = ctx.run_ui(
        egui::RawInput {
            screen_rect: Some(rect),
            ..Default::default()
        },
        |ui| {
            painted = Some(facett_flow::paint(
                ui,
                egui::Id::new("host"),
                rect,
                &scene,
                &View {
                    scale: 1.0,
                    offset: vec2(50.0, 50.0),
                },
                &PaintOpts {
                    cpu: CpuStyle::Full { max_edges: 100 },
                    label_budget: 1,
                    ..Default::default()
                },
            ));
        },
    );
    let painted = painted.unwrap();
    assert_eq!(painted.on_screen.len(), 1);
    assert_eq!(painted.visible_nodes.len(), 2);
    assert!(painted.truncated);
    assert!(!painted.gpu);
    assert!(!output.shapes.is_empty());
    output.textures_delta.clear();
}
#[cfg(feature = "boxes")]
#[test]
fn boxes_extent_picking_and_endpoint_clipping() {
    let mut scene = FlowScene::new(graph()).unwrap();
    scene
        .set_sizes(facett_flow::attr::Sizes {
            nodes: vec![[80.0, 40.0]; 2],
        })
        .unwrap();
    assert_eq!(scene.hit(pos2(39.0, 19.0), 0.0), Some(Hit::Node(0)));
    assert_eq!(scene.hit(pos2(-40.0, 0.0), 0.0), Some(Hit::Node(0)));
    assert_eq!(scene.hit(pos2(-42.0, 0.0), 0.0), None);
    assert_eq!(scene.edge_point(0, 0.0), Some(pos2(40.0, 0.0)));
    assert_eq!(scene.edge_point(0, 1.0), Some(pos2(160.0, 100.0)));
    let before = scene.attribute_generation();
    assert!(
        scene
            .set_sizes(facett_flow::attr::Sizes {
                nodes: vec![[f32::NAN, 1.0]; 2]
            })
            .is_err()
    );
    assert_eq!(before, scene.attribute_generation());
}
#[cfg(feature = "status")]
#[test]
fn status_is_atomic_and_absent_status_uses_plain_scene() {
    use facett_core::Elm;
    use facett_flow::{
        FlowGraph, Msg,
        attr::{Status, StatusTable},
    };
    use std::sync::Arc;
    let mut scene = FlowScene::new(graph()).unwrap();
    scene
        .set_status(StatusTable {
            nodes: vec![None; 2],
            edges: vec![None],
        })
        .unwrap();
    assert_eq!(scene.attribute_generation(), None);
    let geom = scene.generation();
    scene
        .set_status(StatusTable {
            nodes: vec![Some(Status::Error); 2],
            edges: vec![None],
        })
        .unwrap();
    assert_eq!(geom, scene.generation());
    let attr = scene.attribute_generation();
    assert!(
        scene
            .set_status(StatusTable {
                nodes: vec![],
                edges: vec![]
            })
            .is_err()
    );
    assert_eq!(attr, scene.attribute_generation());
    let mut flow = FlowGraph::new("status", graph()).unwrap();
    let before = flow.state().clone();
    flow.update(Msg::Status(Arc::new(StatusTable {
        nodes: vec![],
        edges: vec![],
    })));
    assert_eq!(&before, flow.state());
    let mut replacement = graph();
    replacement.nodes.reverse();
    scene.replace(replacement).unwrap();
    assert!(scene.status().is_none());
}
#[cfg(feature = "rate")]
#[test]
fn rate_invalid_input_does_not_change_state_and_none_keeps_plain() {
    use facett_flow::attr::Rates;
    let mut scene = FlowScene::new(graph()).unwrap();
    scene.set_rates(Rates { edges: vec![None] }).unwrap();
    assert_eq!(scene.attribute_generation(), None);
    scene
        .set_rates(Rates {
            edges: vec![Some(0.0)],
        })
        .unwrap();
    let before = scene.attribute_generation();
    assert!(
        scene
            .set_rates(Rates {
                edges: vec![Some(-1.0)]
            })
            .is_err()
    );
    assert_eq!(before, scene.attribute_generation());
}
#[cfg(feature = "compact-state")]
#[test]
fn compact_is_explicit_and_full_elm_model_roundtrips() {
    use facett_core::{Elm, Facet};
    use facett_flow::FlowGraph;
    let mut flow = FlowGraph::new("large", Graph::demo(100_000)).unwrap();
    assert!(flow.state_json().get("graph").is_some());
    flow.set_compact_state(true);
    let json = flow.state_json();
    assert!(serde_json::to_vec(&json).unwrap().len() < 2048);
    assert_eq!(json["node_count"], 100_000);
    assert!(json.get("graph_digest").is_some());
    let restored = FlowGraph::from_model(
        serde_json::from_value(serde_json::to_value(flow.state()).unwrap()).unwrap(),
    )
    .unwrap();
    assert_eq!(flow.state(), restored.state());
}
#[cfg(feature = "layered")]
#[test]
fn layered_layout_deterministic_and_directed() {
    let mut a = graph();
    let mut b = graph();
    a.layered(Default::default()).unwrap();
    b.layered(Default::default()).unwrap();
    assert_eq!(a, b);
    assert!(a.nodes[0].position[0] < a.nodes[1].position[0]);
}

#[cfg(feature = "rate")]
#[test]
fn cpu_zero_rate_has_no_motion_and_live_rate_moves() {
    use facett_flow::attr::Rates;
    let mut scene = FlowScene::new(graph()).unwrap();
    let render = |scene: &FlowScene, seconds: f64, min_packet_px: f32| {
        let ctx = egui::Context::default();
        let rect = Rect::from_min_size(pos2(0.0, 0.0), vec2(400.0, 300.0));
        let mut output = ctx.run_ui(
            egui::RawInput {
                screen_rect: Some(rect),
                ..Default::default()
            },
            |ui| {
                let opts = PaintOpts {
                    seconds,
                    cpu: CpuStyle::Full { max_edges: 100 },
                    #[cfg(feature = "lod")]
                    min_packet_px,
                    ..Default::default()
                };
                let _ = min_packet_px;
                facett_flow::paint(
                    ui,
                    egui::Id::new("cpu-rate"),
                    rect,
                    scene,
                    &View {
                        scale: 1.0,
                        offset: vec2(50.0, 50.0),
                    },
                    &opts,
                );
            },
        );
        output.textures_delta.clear();
        output.shapes
    };
    scene
        .set_rates(Rates {
            edges: vec![Some(0.0)],
        })
        .unwrap();
    assert_eq!(render(&scene, 0.0, 0.0), render(&scene, 0.43, 0.0));
    scene
        .set_rates(Rates {
            edges: vec![Some(1.0)],
        })
        .unwrap();
    assert_ne!(render(&scene, 0.0, 0.0), render(&scene, 0.43, 0.0));
    #[cfg(feature = "lod")]
    assert_eq!(render(&scene, 0.0, 500.0), render(&scene, 0.43, 500.0));
}