pub mod analysis;
pub mod backend;
pub mod clip;
pub mod community;
pub mod cpu;
pub mod decorated_view;
pub mod depgraph_layout;
pub mod fdeb;
pub mod gpu_layout;
pub mod l0;
pub mod metro;
pub mod model;
#[cfg(feature = "gpu")]
pub mod gpu;
pub use backend::{Backend, GpuProbe, decide, probe_from_env};
pub use clip::{GraphClipReport, WidgetRect, pick, render_graph_clipped};
pub use cpu::{Rendered, render as render_cpu};
pub use decorated_view::{DecoratedGraphView, downstream_of};
pub use depgraph_layout::{DepEdge, DepGraphLayout, EdgeClass, LaidEdge, LaidNode};
pub use gpu_layout::{relax_cpu, step_cpu, GraphCsr, LayoutParams, LayoutState};
pub use fdeb::{bundle, occupancy, BundleParams, BundledEdge};
pub use community::{
fracture_t, fractured_positions, louvain, meta_graph, visible_count, Communities, MetaGraph, MetaNode,
};
pub use l0::{render_to_rgba_l0, lower as lower_to_l0};
pub use metro::{MetroBranch, MetroLine, MetroMap, MetroStation, MetroView, StationKind};
pub use model::{
BOX_H, BOX_W, Camera, Color, Decorations, GraphEdge, GraphModel, GraphNode, NodeDecoration, Pos,
};
pub fn render_to_rgba(
backend: Backend,
model: &GraphModel,
decorations: &Decorations,
camera: &Camera,
width: u32,
height: u32,
background: Color,
) -> Rendered {
#[cfg(feature = "testmatrix")]
facett_core::testmatrix::emit(
"facett-graphview::render_to_rgba",
"render_dispatch",
true,
&format!("backend={backend:?} nodes={} edges={}", model.nodes.len(), model.edges.len()),
);
match backend {
Backend::GpuVello => {
#[cfg(feature = "gpu")]
{
let _scene = gpu::build_scene(model, decorations, camera);
}
cpu::render(model, decorations, camera, width, height, background)
}
Backend::CpuVello => cpu::render(model, decorations, camera, width, height, background),
}
}
#[cfg(test)]
mod tests {
use super::*;
fn sample_graph() -> GraphModel {
let nodes = vec![
GraphNode {
id: "a".into(),
label: "alpha".into(),
fill: Color::rgb(60, 90, 160),
stroke: Color::WHITE,
pos: Pos::new(0.0, 0.0),
},
GraphNode {
id: "b".into(),
label: "beta".into(),
fill: Color::rgb(60, 140, 90),
stroke: Color::WHITE,
pos: Pos::new(300.0, -80.0),
},
GraphNode {
id: "c".into(),
label: "gamma".into(),
fill: Color::rgb(160, 90, 60),
stroke: Color::WHITE,
pos: Pos::new(300.0, 80.0),
},
];
let edges = vec![
GraphEdge { from: "a".into(), to: "b".into(), color: Color::rgb(200, 200, 220), dashed: false, label: None },
GraphEdge { from: "a".into(), to: "c".into(), color: Color::rgb(200, 200, 220), dashed: true, label: None },
];
GraphModel { nodes, edges }
}
#[test]
fn cpu_render_produces_nonblank_frame() {
let model = sample_graph();
let cam = Camera::fit(&model, 640.0, 480.0, 40.0);
let backend = decide(GpuProbe::cpu_only());
assert_eq!(backend, Backend::CpuVello);
let frame = render_to_rgba(
backend,
&model,
&Decorations::default(),
&cam,
640,
480,
Color::rgb(18, 20, 28),
);
assert_eq!(frame.rgba.len(), 640 * 480 * 4);
let mut seen = std::collections::HashSet::new();
for px in frame.rgba.chunks_exact(4) {
seen.insert([px[0], px[1], px[2]]);
if seen.len() > 3 {
break;
}
}
assert!(seen.len() > 3, "vello_cpu drew a real graph, not a flat pane");
}
#[test]
fn camera_fit_centers_world() {
let model = sample_graph();
let cam = Camera::fit(&model, 640.0, 480.0, 40.0);
let (min_x, min_y, max_x, max_y) = model.world_bounds().unwrap();
let (wcx, wcy) = ((min_x + max_x) * 0.5, (min_y + max_y) * 0.5);
let (sx, sy) = cam.project(Pos::new(wcx, wcy));
assert!((sx - 320.0).abs() < 1.0, "x centered, got {sx}");
assert!((sy - 240.0).abs() < 1.0, "y centered, got {sy}");
}
}