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

nordisk/facett

facett-graphpan/tests/graph_opts.rs

//! **`draw_graph_opts` — the one-call interactive canvas with `PaintOpts`.**
//!
//! `draw_graph_opts` is the seam a scale/"universe" host mounts: it does everything
//! [`facett_graphpan::draw_graph`] does (allocate the painter, pan/drag + scroll-zoom,
//! click-to-select) but takes [`PaintOpts`] so the host can route the node/edge cloud
//! through the shared instanced GPU renderer, and RETURNS a [`LegibilityReport`] whose
//! `gpu_cloud` flag names the lane that drew.
//!
//! These run headless (a plain `egui::Context` frame — no GPU stack, no renderer
//! installed), i.e. in exactly the state that must FALL BACK: opting into the GPU here
//! draws every node on the CPU and reports `gpu_cloud == false`. It is the same
//! fail-safe `gpu_fallback.rs` pins for `GraphPanView`, asserted on the free function.

use std::cell::RefCell;

use egui::{pos2, Color32};
use facett_graphpan::{
    draw_graph_opts, Decorations, GraphEdge, GraphModel, GraphNode, GraphView, LegibilityReport,
    PaintOpts,
};

fn node(id: &str, x: f32, y: f32) -> GraphNode {
    GraphNode {
        id: id.into(),
        label: id.into(),
        fill: Color32::from_rgb(120, 200, 255),
        stroke: Color32::from_rgb(30, 40, 60),
        pos: pos2(x, y),
    }
}

fn grid(n: usize) -> GraphModel {
    let side = (n as f32).sqrt().ceil() as usize;
    let nodes: Vec<GraphNode> = (0..n)
        .map(|i| {
            let (r, c) = (i / side, i % side);
            node(&format!("n{i}"), c as f32 * 60.0 - 300.0, r as f32 * 40.0 - 200.0)
        })
        .collect();
    let edges: Vec<GraphEdge> = (0..n.saturating_sub(1))
        .map(|i| GraphEdge {
            from: format!("n{i}"),
            to: format!("n{}", i + 1),
            color: Color32::WHITE,
            dashed: false,
            label: None,
        })
        .collect();
    GraphModel { nodes, edges }
}

/// Run ONE headless egui frame that paints `model` through `draw_graph_opts` with `opts`,
/// and return the frame's `LegibilityReport` plus the tessellated vertex count (the "did it
/// actually draw?" measure — a fallback CPU paint dwarfs a blank pane).
fn frame(model: &GraphModel, opts: PaintOpts) -> (LegibilityReport, usize) {
    let ctx = egui::Context::default();
    let report = RefCell::new(LegibilityReport::default());
    let mut view = GraphView {
        dot_px: Some(3.0), // point-cloud lane, like the universe star-field
        ..Default::default()
    };
    let mut out = ctx.run_ui(egui::RawInput::default(), |ui| {
        egui::CentralPanel::default().show(ui, |ui| {
            let (_resp, r) = draw_graph_opts(
                ui,
                model,
                &Decorations::default(),
                &mut view,
                Color32::from_gray(14),
                Color32::from_gray(235),
                Color32::from_rgb(0x39, 0x87, 0xe5),
                Color32::from_gray(120),
                opts,
            );
            *report.borrow_mut() = r;
        });
    });
    out.textures_delta.clear();
    let verts: usize = ctx
        .tessellate(out.shapes, out.pixels_per_point)
        .iter()
        .map(|c| match &c.primitive {
            egui::epaint::Primitive::Mesh(m) => m.vertices.len(),
            egui::epaint::Primitive::Callback(_) => 0,
        })
        .sum();
    (report.into_inner(), verts)
}

/// GPU opt-in with NO renderer installed draws every node on the CPU (a real, non-blank
/// frame) and honestly reports `gpu_cloud == false` — the fail-safe, on the free function.
#[test]
fn gpu_opt_in_without_a_renderer_falls_back_and_says_so() {
    // Only meaningful on a box with no cloud renderer installed (headless => none).
    assert!(
        !facett_graphpan::legible::gpu_cloud_available(),
        "headless test: no cloud renderer must be installed"
    );

    let model = grid(256);
    let (off, v_off) = frame(&model, PaintOpts::default());
    let (gpu, v_gpu) = frame(&model, PaintOpts::default().with_gpu(true));

    assert!(!off.gpu_cloud, "gpu off => CPU lane");
    assert!(!gpu.gpu_cloud, "gpu opt-in with no renderer must report the CPU fallback lane");
    // The fallback drew the SAME frame as the plain canvas — vertex for vertex, never blank.
    assert_eq!(v_gpu, v_off, "the GPU fallback paints the CPU frame vertex for vertex ({v_gpu} vs {v_off})");
    assert!(v_gpu > 256, "a 256-node point cloud is a real, non-blank frame (got {v_gpu} verts)");
}

/// The click/pan interaction survives the opts path: `draw_graph_opts` returns a
/// `GraphResponse` (here: no pointer, so nothing clicked) and never panics on an empty
/// model — the seam is a drop-in for `draw_graph`.
#[test]
fn empty_model_is_safe_and_reports_nothing_drawn_on_the_gpu() {
    let (r, _v) = frame(&GraphModel::default(), PaintOpts::default().with_gpu(true));
    assert!(!r.gpu_cloud, "empty model never claims a GPU paint");
}