From c23574d97420f9cc07dd27524f7881224e0d9b44 Mon Sep 17 00:00:00 2001 From: Dmitrii Samsonov Date: Tue, 29 Oct 2024 20:25:22 +0200 Subject: [PATCH] incremental layout drawing + refactor --- Cargo.lock | 1 + examples/animated_nodes/src/node.rs | 4 +- examples/demo/src/main.rs | 26 ++++-- examples/flex_nodes/src/node.rs | 4 +- examples/layouts/Cargo.toml | 1 + examples/layouts/src/main.rs | 130 ++++++++++++++------------ examples/wasm_custom_draw/src/node.rs | 4 +- src/draw/displays_default/node.rs | 4 +- src/draw/drawer.rs | 2 - src/elements/node.rs | 15 +-- src/graph.rs | 61 +++++++++++- src/graph_view.rs | 28 ++++-- src/helpers.rs | 113 +++------------------- src/layouts/hierarchical/layout.rs | 42 +++++---- src/layouts/layout.rs | 14 ++- src/layouts/random/layout.rs | 31 +++--- src/lib.rs | 4 +- 17 files changed, 245 insertions(+), 239 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b124ecd..748f51c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3356,6 +3356,7 @@ dependencies = [ "egui 0.29.0", "egui_graphs 0.22.0", "petgraph", + "rand", ] [[package]] diff --git a/examples/animated_nodes/src/node.rs b/examples/animated_nodes/src/node.rs index c8d93e4..78c877c 100644 --- a/examples/animated_nodes/src/node.rs +++ b/examples/animated_nodes/src/node.rs @@ -62,7 +62,7 @@ impl From> for NodeShapeAnimated { fn from(node_props: NodeProps) -> Self { Self { label: node_props.label.clone(), - loc: node_props.location(), + loc: node_props.location, dragged: node_props.dragged, clockwise: node_props.payload.get_is_clockwise(), @@ -145,7 +145,7 @@ impl DisplayNode< fn update(&mut self, state: &NodeProps) { self.label = state.label.clone(); - self.loc = state.location(); + self.loc = state.location; self.dragged = state.dragged; self.clockwise = state.payload.get_is_clockwise(); } diff --git a/examples/demo/src/main.rs b/examples/demo/src/main.rs index 49dc394..c49a36b 100644 --- a/examples/demo/src/main.rs +++ b/examples/demo/src/main.rs @@ -5,7 +5,7 @@ use drawers::ValuesSectionDebug; use eframe::{run_native, App, CreationContext}; use egui::{CollapsingHeader, Context, Pos2, ScrollArea, Ui, Vec2}; use egui_graphs::events::Event; -use egui_graphs::{random_graph, DefaultGraphView, Edge, Graph, Node}; +use egui_graphs::{random_graph, to_graph, DefaultGraphView, Edge, Graph, Node}; use fdg::fruchterman_reingold::{FruchtermanReingold, FruchtermanReingoldConfiguration}; use fdg::nalgebra::{Const, OPoint}; use fdg::{Force, ForceGraph}; @@ -50,12 +50,15 @@ impl DemoApp { let settings_graph = settings::SettingsGraph::default(); let settings_simulation = settings::SettingsSimulation::default(); - let mut g = random_graph(settings_graph.count_node, settings_graph.count_edge); + let mut g = to_graph(&random_graph( + settings_graph.count_node, + settings_graph.count_edge, + )); let mut force = init_force(&settings_simulation); - let mut sim = fdg::init_force_graph_uniform(g.g.clone(), 1.0); + let mut sim = fdg::init_force_graph_uniform(g.graph().clone(), 1.0); force.apply(&mut sim); - g.g.node_weights_mut().for_each(|node| { + g.nodes_mut().for_each(|node| { let point: fdg::nalgebra::OPoint> = sim.node_weight(node.id()).unwrap().1; node.set_location(Pos2::new(point.coords.x, point.coords.y)); @@ -102,7 +105,7 @@ impl DemoApp { /// sync locations computed by the simulation with egui_graphs::Graph nodes. fn sync(&mut self) { - self.g.g.node_weights_mut().for_each(|node| { + self.g.nodes_mut().for_each(|node| { let sim_computed_point: OPoint> = self.sim.node_weight(node.id()).unwrap().1; node.set_location(Pos2::new( @@ -151,7 +154,7 @@ impl DemoApp { } let random_n_idx = rand::thread_rng().gen_range(0..nodes_cnt); - self.g.g.node_indices().nth(random_n_idx) + self.g.node_indices().nth(random_n_idx) } fn random_edge_idx(&self) -> Option { @@ -161,7 +164,7 @@ impl DemoApp { } let random_e_idx = rand::thread_rng().gen_range(0..edges_cnt); - self.g.g.edge_indices().nth(random_e_idx) + self.g.edge_indices().nth(random_e_idx) } fn remove_random_node(&mut self) { @@ -447,12 +450,15 @@ impl DemoApp { let settings_graph = settings::SettingsGraph::default(); let settings_simulation = settings::SettingsSimulation::default(); - let mut g = random_graph(settings_graph.count_node, settings_graph.count_edge); + let mut g = to_graph(&random_graph( + settings_graph.count_node, + settings_graph.count_edge, + )); let mut force = init_force(&self.settings_simulation); - let mut sim = fdg::init_force_graph_uniform(g.g.clone(), 1.0); + let mut sim = fdg::init_force_graph_uniform(g.graph().clone(), 1.0); force.apply(&mut sim); - g.g.node_weights_mut().for_each(|node| { + g.nodes_mut().for_each(|node| { let point: fdg::nalgebra::OPoint> = sim.node_weight(node.id()).unwrap().1; node.set_location(Pos2::new(point.coords.x, point.coords.y)); diff --git a/examples/flex_nodes/src/node.rs b/examples/flex_nodes/src/node.rs index 3470640..59cb513 100644 --- a/examples/flex_nodes/src/node.rs +++ b/examples/flex_nodes/src/node.rs @@ -15,7 +15,7 @@ impl From> for NodeShapeFlex { fn from(node_props: NodeProps) -> Self { Self { label: node_props.label.clone(), - loc: node_props.location(), + loc: node_props.location, size_x: 0., size_y: 0., @@ -67,7 +67,7 @@ impl DisplayNode fn update(&mut self, state: &NodeProps) { self.label = state.label.clone(); - self.loc = state.location(); + self.loc = state.location; } } diff --git a/examples/layouts/Cargo.toml b/examples/layouts/Cargo.toml index ffa91b3..6d9415b 100644 --- a/examples/layouts/Cargo.toml +++ b/examples/layouts/Cargo.toml @@ -10,3 +10,4 @@ egui_graphs = { path = "../../" } egui = "0.29" eframe = "0.29" petgraph = "0.6" +rand = "0.8.5" diff --git a/examples/layouts/src/main.rs b/examples/layouts/src/main.rs index c4fa0a0..cf80a68 100644 --- a/examples/layouts/src/main.rs +++ b/examples/layouts/src/main.rs @@ -1,10 +1,11 @@ use eframe::{run_native, App, CreationContext, NativeOptions}; use egui::Context; use egui_graphs::{ - random_graph, DefaultEdgeShape, DefaultNodeShape, Graph, GraphView, LayoutHierarchical, - LayoutRandom, LayoutStateHierarchical, LayoutStateRandom, + random_graph, to_graph, DefaultEdgeShape, DefaultNodeShape, Graph, GraphView, + LayoutHierarchical, LayoutRandom, LayoutStateHierarchical, LayoutStateRandom, }; use petgraph::{stable_graph::DefaultIx, Directed}; +use rand::Rng; #[derive(Clone, PartialEq)] enum Layout { @@ -21,6 +22,7 @@ struct Settings { pub struct LayoutsApp { settings: Settings, g: Graph, + reset_cache: bool, } impl LayoutsApp { @@ -30,40 +32,13 @@ impl LayoutsApp { num_nodes: 25, num_edges: 25, }; + let g = to_graph(&random_graph(settings.num_nodes, settings.num_edges)); Self { + g, settings: settings.clone(), - g: random_graph(settings.num_nodes, settings.num_edges), + reset_cache: false, } } - - fn clear_cache(&mut self, ui: &mut egui::Ui) { - match self.settings.layout { - Layout::Hierarchical => { - GraphView::< - (), - (), - Directed, - DefaultIx, - DefaultNodeShape, - DefaultEdgeShape, - LayoutStateHierarchical, - LayoutHierarchical, - >::clear_cache(ui); - } - Layout::Random => { - GraphView::< - (), - (), - Directed, - DefaultIx, - DefaultNodeShape, - DefaultEdgeShape, - LayoutStateRandom, - LayoutRandom, - >::clear_cache(ui); - } - }; - } } impl App for LayoutsApp { @@ -82,33 +57,68 @@ impl App for LayoutsApp { ) .changed() { - self.clear_cache(ui); + self.reset_cache = true; }; if ui .radio_value(&mut self.settings.layout, Layout::Random, "Random") .changed() { - self.clear_cache(ui); + self.reset_cache = true; }; }); ui.horizontal(|ui| { ui.label("Number of nodes"); - if ui - .add(egui::Slider::new(&mut self.settings.num_nodes, 1..=250)) - .changed() - { - self.clear_cache(ui); - self.g = random_graph(self.settings.num_nodes, self.settings.num_edges); + let mut value = self.settings.num_nodes; + if ui.add(egui::Slider::new(&mut value, 1..=250)).changed() { + let delta = value as isize - self.settings.num_nodes as isize; + if delta > 0 { + for _ in 0..delta { + self.g.add_node(()); + } + } else { + for _ in 0..-delta { + let idx = self.g.node_indices().last().unwrap(); + self.g.remove_node(idx); + } + } + + self.settings.num_nodes = value; + self.settings.num_edges = self.g.edge_count(); }; }); ui.horizontal(|ui| { ui.label("Number of edges"); - if ui - .add(egui::Slider::new(&mut self.settings.num_edges, 1..=250)) - .changed() - { - self.clear_cache(ui); - self.g = random_graph(self.settings.num_nodes, self.settings.num_edges); + let mut value = self.settings.num_edges; + if ui.add(egui::Slider::new(&mut value, 1..=250)).changed() { + let delta = value as isize - self.settings.num_edges as isize; + if delta > 0 { + for _ in 0..delta { + let mut rng = rand::thread_rng(); + let start = self + .g + .node_indices() + .nth(rng.gen_range(0..self.g.node_count())) + .unwrap(); + let end = self + .g + .node_indices() + .nth(rng.gen_range(0..self.g.node_count())) + .unwrap(); + self.g.add_edge(start, end, ()); + } + } else { + for _ in 0..-delta { + let idx = self.g.edge_indices().last().unwrap(); + self.g.remove_edge(idx); + } + } + + self.settings.num_edges = value; + }; + }); + ui.horizontal(|ui| { + if ui.button("redraw").changed() { + self.reset_cache = true; }; }); }); @@ -116,7 +126,7 @@ impl App for LayoutsApp { egui::CentralPanel::default().show(ctx, |ui| { match self.settings.layout { Layout::Hierarchical => { - ui.add(&mut GraphView::< + let w = &mut GraphView::< _, _, _, @@ -125,19 +135,23 @@ impl App for LayoutsApp { _, LayoutStateHierarchical, LayoutHierarchical, - >::new(&mut self.g)); + >::new(&mut self.g); + if self.reset_cache { + w.clear_cache(ui); + self.reset_cache = false; + } + ui.add(w); } Layout::Random => { - ui.add(&mut GraphView::< - _, - _, - _, - _, - _, - _, - LayoutStateRandom, - LayoutRandom, - >::new(&mut self.g)); + let w = + &mut GraphView::<_, _, _, _, _, _, LayoutStateRandom, LayoutRandom>::new( + &mut self.g, + ); + if self.reset_cache { + w.clear_cache(ui); + self.reset_cache = false; + } + ui.add(w); } }; }); diff --git a/examples/wasm_custom_draw/src/node.rs b/examples/wasm_custom_draw/src/node.rs index 636e4a1..5f04a70 100644 --- a/examples/wasm_custom_draw/src/node.rs +++ b/examples/wasm_custom_draw/src/node.rs @@ -25,7 +25,7 @@ impl From> for NodeShapeAnimated { fn from(node_props: NodeProps) -> Self { Self { label: node_props.label.clone(), - loc: node_props.location(), + loc: node_props.location, dragged: node_props.dragged, angle_rad: Default::default(), @@ -120,7 +120,7 @@ impl DisplayNode fn update(&mut self, state: &NodeProps) { self.label = state.label.clone(); - self.loc = state.location(); + self.loc = state.location; self.dragged = state.dragged; } } diff --git a/src/draw/displays_default/node.rs b/src/draw/displays_default/node.rs index b6c032a..4b081fd 100644 --- a/src/draw/displays_default/node.rs +++ b/src/draw/displays_default/node.rs @@ -26,7 +26,7 @@ pub struct DefaultNodeShape { impl From> for DefaultNodeShape { fn from(node_props: NodeProps) -> Self { DefaultNodeShape { - pos: node_props.location(), + pos: node_props.location, selected: node_props.selected, dragged: node_props.dragged, label_text: node_props.label.to_string(), @@ -101,7 +101,7 @@ impl DisplayNode } fn update(&mut self, state: &NodeProps) { - self.pos = state.location(); + self.pos = state.location; self.selected = state.selected; self.dragged = state.dragged; self.label_text = state.label.to_string(); diff --git a/src/draw/drawer.rs b/src/draw/drawer.rs index f7be370..8fb24de 100644 --- a/src/draw/drawer.rs +++ b/src/draw/drawer.rs @@ -73,7 +73,6 @@ where fn draw_nodes(&mut self) { self.g - .g .node_indices() .collect::>() .into_iter() @@ -99,7 +98,6 @@ where fn draw_edges(&mut self) { self.g - .g .edge_indices() .collect::>() .into_iter() diff --git a/src/elements/node.rs b/src/elements/node.rs index ee114b6..cba8bc4 100644 --- a/src/elements/node.rs +++ b/src/elements/node.rs @@ -20,20 +20,15 @@ where pub label: String, pub selected: bool, pub dragged: bool, + pub location: Pos2, color: Option, - location: Pos2, - location_user: Option, } impl NodeProps where N: Clone, { - pub fn location(&self) -> Pos2 { - self.location_user.unwrap_or(self.location) - } - pub fn color(&self) -> Option { self.color } @@ -103,7 +98,6 @@ where payload, location: Pos2::default(), color: Option::default(), - location_user: Option::default(), label: String::default(), selected: bool::default(), dragged: bool::default(), @@ -162,15 +156,10 @@ where } pub fn location(&self) -> Pos2 { - self.props.location() + self.props.location } pub fn set_location(&mut self, loc: Pos2) { - self.props.location_user = Some(loc); - } - - // TODO: why crate? how to use by external layoyuts?? do we need this func??? - pub(crate) fn set_layout_location(&mut self, loc: Pos2) { self.props.location = loc; } diff --git a/src/graph.rs b/src/graph.rs index 93c549f..e422349 100644 --- a/src/graph.rs +++ b/src/graph.rs @@ -35,10 +35,13 @@ pub struct Graph< Dn: DisplayNode, De: DisplayEdge, { - pub g: StableGraphType, + g: StableGraphType, selected_nodes: Vec>, selected_edges: Vec>, dragged_node: Option>, + new_nodes: Vec>, + new_nodes_no_location: Vec>, + removed_nodes: Vec>, } impl From<&StableGraph> for Graph @@ -65,14 +68,37 @@ where De: DisplayEdge, { pub fn new(g: StableGraphType) -> Self { + let new_nodes = g.node_indices().collect::>(); + let new_nodes_no_location = g.node_indices().collect::>(); + Self { g, + new_nodes, + new_nodes_no_location, + selected_nodes: Vec::default(), selected_edges: Vec::default(), dragged_node: Option::default(), + removed_nodes: Vec::default(), } } + pub fn graph(&self) -> &StableGraphType { + &self.g + } + + pub fn new_nodes(&self) -> &Vec> { + &self.new_nodes + } + + pub fn new_nodes_no_location(&self) -> &Vec> { + &self.new_nodes_no_location + } + + pub fn removed_nodes(&self) -> &Vec> { + &self.removed_nodes + } + /// Finds node by position. Can be optimized by using a spatial index like quad-tree if needed. pub fn node_by_screen_pos(&self, meta: &Metadata, screen_pos: Pos2) -> Option> { let pos_in_graph = meta.screen_to_canvas_pos(screen_pos); @@ -119,6 +145,9 @@ where graph_node.set_location(Pos2::default()); graph_node.set_label(idx.index().to_string()); + self.new_nodes.push(idx); + self.new_nodes_no_location.push(idx); + idx } @@ -134,6 +163,8 @@ where graph_node.set_location(location); graph_node.set_label(idx.index().to_string()); + self.new_nodes.push(idx); + idx } @@ -159,6 +190,8 @@ where graph_node.set_location(location); graph_node.set_label(label); + self.new_nodes.push(idx); + idx } @@ -171,6 +204,8 @@ where self.remove_edges_between(*n, idx); } + self.removed_nodes.push(idx); + self.g.remove_node(idx) } @@ -299,6 +334,10 @@ where self.g.node_weight_mut(i) } + pub fn nodes_mut(&mut self) -> impl Iterator> { + self.g.node_weights_mut() + } + pub fn edge_mut(&mut self, i: EdgeIndex) -> Option<&mut Edge> { self.g.edge_weight_mut(i) } @@ -350,4 +389,24 @@ where pub fn node_count(&self) -> usize { self.g.node_count() } + + pub fn node_indices(&self) -> impl Iterator> + '_ { + self.g.node_indices() + } + + pub fn edge_indices(&self) -> impl Iterator> + '_ { + self.g.edge_indices() + } + + pub fn externals(&self, dir: Direction) -> impl Iterator> + '_ { + self.g.externals(dir) + } + + pub fn neighbors_directed( + &self, + idx: NodeIndex, + dir: Direction, + ) -> impl Iterator> + '_ { + self.g.neighbors_directed(idx, dir) + } } diff --git a/src/graph_view.rs b/src/graph_view.rs index 6067d30..1a719b8 100644 --- a/src/graph_view.rs +++ b/src/graph_view.rs @@ -1,4 +1,4 @@ -use std::marker::PhantomData; +use std::{collections::HashSet, marker::PhantomData}; use crate::{ draw::{DefaultEdgeShape, DefaultNodeShape, DrawContext, Drawer}, @@ -96,11 +96,15 @@ where L: Layout, { fn ui(self, ui: &mut Ui) -> Response { - self.sync_layout(ui); - let mut meta = Metadata::load(ui); self.sync_state(&mut meta); + GraphView::::sync_layout( + ui, + self.g, + &self.g.new_nodes_no_location().iter().copied().collect(), + ); + let (resp, p) = ui.allocate_painter(ui.available_size(), Sense::click_and_drag()); self.handle_fit_to_screen(&resp, &mut meta); self.handle_navigation(ui, &resp, &mut meta); @@ -174,10 +178,10 @@ where self } - /// Clears cached values of layout and metadata. - pub fn clear_cache(ui: &mut Ui) { + /// Clears cached values of layout and metadata. Usefull when you want to switch between different layouts. + pub fn clear_cache(&mut self, ui: &mut Ui) { GraphView::::reset_metadata(ui); - GraphView::::reset_layout(ui); + GraphView::::reset_layout(ui, self.g); } /// Resets navigation metadata @@ -186,10 +190,12 @@ where } /// Resets layout state - pub fn reset_layout(ui: &mut Ui) { + pub fn reset_layout(ui: &mut Ui, g: &mut Graph) { ui.data_mut(|data| { data.insert_persisted(Id::new(KEY_LAYOUT), S::default()); }); + + GraphView::::sync_layout(ui, g, &g.node_indices().collect()); } #[cfg(feature = "events")] @@ -199,13 +205,17 @@ where self } - fn sync_layout(&mut self, ui: &mut Ui) { + fn sync_layout( + ui: &mut Ui, + g: &mut Graph, + not_placed: &HashSet>, + ) { ui.data_mut(|data| { let state = data .get_persisted::(Id::new(KEY_LAYOUT)) .unwrap_or_default(); let mut layout = L::from_state(state); - layout.next(self.g); + layout.next(g, not_placed); data.insert_persisted(Id::new(KEY_LAYOUT), layout.state()); }); diff --git a/src/helpers.rs b/src/helpers.rs index aefb5b9..cdadaa3 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -2,104 +2,13 @@ use crate::{DisplayEdge, DisplayNode, Edge, Graph, Node}; use egui::Vec2; use petgraph::{ graph::IndexType, - stable_graph::{EdgeIndex, NodeIndex, StableGraph}, + stable_graph::{NodeIndex, StableGraph}, visit::IntoNodeReferences, EdgeType, }; use rand::Rng; use std::collections::HashMap; -/// Helper function which adds user's node to the [`super::Graph`] instance. -/// -/// If graph is not empty it picks any node position and adds new node in the vicinity of it. -pub fn add_node(g: &mut Graph, n: &N) -> NodeIndex -where - N: Clone, - E: Clone, - Ty: EdgeType, - Ix: IndexType, - Dn: DisplayNode, - De: DisplayEdge, -{ - add_node_custom(g, n, default_node_transform) -} - -/// Helper function which adds user's node to the [`super::Graph`] instance with custom node transform function. -/// -/// If graph is not empty it picks any node position and adds new node in the vicinity of it. -pub fn add_node_custom( - g: &mut Graph, - n: &N, - node_transform: impl FnOnce(&mut Node), -) -> NodeIndex -where - N: Clone, - E: Clone, - Ty: EdgeType, - Ix: IndexType, - Dn: DisplayNode, - De: DisplayEdge, -{ - let idx = NodeIndex::new(g.g.node_count() + 1); - let mut n = Node::new(n.clone()); - - n.set_id(idx); - - node_transform(&mut n); - - g.g.add_node(n) -} - -/// Helper function which adds user's edge to the [`super::Graph`] instance. -pub fn add_edge( - g: &mut Graph, - start: NodeIndex, - end: NodeIndex, - e: &E, -) -> EdgeIndex -where - N: Clone, - E: Clone, - Ty: EdgeType, - Ix: IndexType, - Dn: DisplayNode, - De: DisplayEdge, -{ - add_edge_custom( - g, - start, - end, - e, - default_edge_transform::, - ) -} - -/// Helper function which adds user's edge to the [`super::Graph`] instance with custom edge transform function. -pub fn add_edge_custom( - g: &mut Graph, - start: NodeIndex, - end: NodeIndex, - e: &E, - edge_transform: impl FnOnce(&mut Edge), -) -> EdgeIndex -where - N: Clone, - E: Clone, - Ty: EdgeType, - Ix: IndexType, - Dn: DisplayNode, - De: DisplayEdge, -{ - let mut edge = Edge::new(e.clone()); - - edge.set_id(EdgeIndex::::new(g.g.edge_count() + 1)); - edge.set_order(g.g.edges_connecting(start, end).count()); - - edge_transform(&mut edge); - - g.g.add_edge(start, end, edge) -} - /// Helper function which transforms [`petgraph::stable_graph::StableGraph`] into the [`super::Graph`] required by the [`super::GraphView`] widget. /// /// The function creates a new `StableGraph` where nodes and edges are represented by [`super::Node`] and [`super::Edge`] respectively. @@ -251,7 +160,7 @@ pub fn node_size Graph { +pub fn random_graph(num_nodes: usize, num_edges: usize) -> StableGraph<(), ()> { let mut rng = rand::thread_rng(); let mut graph = StableGraph::new(); @@ -266,7 +175,7 @@ pub fn random_graph(num_nodes: usize, num_edges: usize) -> Graph { graph.add_edge(NodeIndex::new(source), NodeIndex::new(target), ()); } - to_graph(&graph) + graph } #[cfg(test)] @@ -287,13 +196,13 @@ mod tests { let input_g = to_graph::<_, _, _, _, DefaultNodeShape, DefaultEdgeShape>(&user_g); - assert_eq!(user_g.node_count(), input_g.g.node_count()); - assert_eq!(user_g.edge_count(), input_g.g.edge_count()); + assert_eq!(user_g.node_count(), input_g.node_count()); + assert_eq!(user_g.edge_count(), input_g.edge_count()); assert_eq!(user_g.is_directed(), input_g.is_directed()); - for (user_idx, input_idx) in input_g.g.node_indices().zip(user_g.node_indices()) { + for (user_idx, input_idx) in input_g.node_indices().zip(user_g.node_indices()) { let user_n = user_g.node_weight(user_idx).unwrap(); - let input_n = input_g.g.node_weight(input_idx).unwrap(); + let input_n = input_g.node(input_idx).unwrap(); assert_eq!(*input_n.payload(), *user_n); assert_eq!(*input_n.label(), format!("node {}", user_idx.index())); @@ -312,13 +221,13 @@ mod tests { let input_g = to_graph::<_, _, _, _, DefaultNodeShape, DefaultEdgeShape>(&user_g); - assert_eq!(user_g.node_count(), input_g.g.node_count()); - assert_eq!(user_g.edge_count(), input_g.g.edge_count()); + assert_eq!(user_g.node_count(), input_g.node_count()); + assert_eq!(user_g.edge_count(), input_g.edge_count()); assert_eq!(user_g.is_directed(), input_g.is_directed()); - for (user_idx, input_idx) in input_g.g.node_indices().zip(user_g.node_indices()) { + for (user_idx, input_idx) in input_g.node_indices().zip(user_g.node_indices()) { let user_n = user_g.node_weight(user_idx).unwrap(); - let input_n = input_g.g.node_weight(input_idx).unwrap(); + let input_n = input_g.node(input_idx).unwrap(); assert_eq!(*input_n.payload(), *user_n); assert_eq!(*input_n.label(), format!("node {}", user_idx.index())); diff --git a/src/layouts/hierarchical/layout.rs b/src/layouts/hierarchical/layout.rs index e380895..ac36213 100644 --- a/src/layouts/hierarchical/layout.rs +++ b/src/layouts/hierarchical/layout.rs @@ -18,9 +18,7 @@ const ROW_DIST: usize = 50; const NODE_DIST: usize = 50; #[derive(Debug, Default, Clone, Serialize, Deserialize)] -pub struct State { - triggered: bool, -} +pub struct State {} impl LayoutState for State {} @@ -30,8 +28,11 @@ pub struct Hierarchical { } impl Layout for Hierarchical { - fn next(&mut self, g: &mut Graph) - where + fn next( + &mut self, + g: &mut Graph, + not_placed: &HashSet>, + ) where N: Clone, E: Clone, Ty: EdgeType, @@ -39,26 +40,20 @@ impl Layout for Hierarchical { Dn: DisplayNode, De: DisplayEdge, { - if self.state.triggered { - return; - } - let mut visited = HashSet::new(); let mut max_col = 0; - g.g.externals(Incoming) + g.externals(Incoming) .collect::>>() .iter() .enumerate() .for_each(|(i, root_idx)| { visited.insert(*root_idx); - let curr_max_col = build_tree(g, &mut visited, root_idx, 0, i); + let curr_max_col = build_tree(g, &mut visited, not_placed, root_idx, 0, i); if curr_max_col > max_col { max_col = curr_max_col; }; }); - - self.state.triggered = true; } fn state(&self) -> State { @@ -73,6 +68,7 @@ impl Layout for Hierarchical { fn build_tree( g: &mut Graph, visited: &mut HashSet>, + not_placed: &HashSet>, root_idx: &NodeIndex, start_row: usize, start_col: usize, @@ -85,14 +81,19 @@ where Dn: DisplayNode, De: DisplayEdge, { + let placed_idx = NodeIndex::new(root_idx.index()); + if !not_placed.contains(&placed_idx) { + return start_col; + }; + let y = start_row * ROW_DIST; let x = start_col * NODE_DIST; - let node = &mut g.g[*root_idx]; - node.set_layout_location(Pos2::new(x as f32, y as f32)); + let node = g.node_mut(*root_idx).unwrap(); + node.set_location(Pos2::new(x as f32, y as f32)); let mut max_col = start_col; - g.g.neighbors_directed(*root_idx, Outgoing) + g.neighbors_directed(*root_idx, Outgoing) .collect::>>() .iter() .enumerate() @@ -103,7 +104,14 @@ where visited.insert(*neighbour_idx); - let curr_max_col = build_tree(g, visited, neighbour_idx, start_row + 1, start_col + i); + let curr_max_col = build_tree( + g, + visited, + not_placed, + neighbour_idx, + start_row + 1, + start_col + i, + ); if curr_max_col > max_col { max_col = curr_max_col; }; diff --git a/src/layouts/layout.rs b/src/layouts/layout.rs index 8ecf7cd..884c8e3 100644 --- a/src/layouts/layout.rs +++ b/src/layouts/layout.rs @@ -1,5 +1,10 @@ +use std::collections::HashSet; + use egui::util::id_type_map::SerializableAny; -use petgraph::{stable_graph::IndexType, EdgeType}; +use petgraph::{ + stable_graph::{IndexType, NodeIndex}, + EdgeType, +}; use crate::{DisplayEdge, DisplayNode, Graph}; @@ -13,8 +18,11 @@ where fn from_state(state: S) -> impl Layout; /// Called on every frame. It should update the graph layout aka nodes locations. - fn next(&mut self, g: &mut Graph) - where + fn next( + &mut self, + g: &mut Graph, + not_placed: &HashSet>, + ) where N: Clone, E: Clone, Ty: EdgeType, diff --git a/src/layouts/random/layout.rs b/src/layouts/random/layout.rs index f1581e7..ce9cce5 100644 --- a/src/layouts/random/layout.rs +++ b/src/layouts/random/layout.rs @@ -1,5 +1,7 @@ +use std::collections::HashSet; + use egui::Pos2; -use petgraph::stable_graph::IndexType; +use petgraph::stable_graph::{IndexType, NodeIndex}; use rand::Rng; use serde::{Deserialize, Serialize}; @@ -7,12 +9,11 @@ use crate::{ layouts::{Layout, LayoutState}, Graph, }; + const SPAWN_SIZE: f32 = 250.; #[derive(Debug, Default, Clone, Serialize, Deserialize)] -pub struct State { - triggered: bool, -} +pub struct State {} impl LayoutState for State {} @@ -23,8 +24,11 @@ pub struct Random { } impl Layout for Random { - fn next(&mut self, g: &mut Graph) - where + fn next( + &mut self, + g: &mut Graph, + not_placed: &HashSet>, + ) where N: Clone, E: Clone, Ty: petgraph::EdgeType, @@ -32,19 +36,18 @@ impl Layout for Random { Dn: crate::DisplayNode, De: crate::DisplayEdge, { - if self.state.triggered { - return; - } - let mut rng = rand::thread_rng(); - for node in g.g.node_weights_mut() { - node.set_layout_location(Pos2::new( + for node in g.nodes_mut() { + let idx = NodeIndex::new(node.id().index()); + if !not_placed.contains(&idx) { + continue; + }; + + node.set_location(Pos2::new( rng.gen_range(0. ..SPAWN_SIZE), rng.gen_range(0. ..SPAWN_SIZE), )); } - - self.state.triggered = true; } fn state(&self) -> State { diff --git a/src/lib.rs b/src/lib.rs index 45ef8e2..4f96eb3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,8 +12,8 @@ pub use elements::{Edge, EdgeProps, Node, NodeProps}; pub use graph::Graph; pub use graph_view::{DefaultGraphView, GraphView}; pub use helpers::{ - add_edge, add_edge_custom, add_node, add_node_custom, default_edge_transform, - default_node_transform, node_size, random_graph, to_graph, to_graph_custom, + default_edge_transform, default_node_transform, node_size, random_graph, to_graph, + to_graph_custom, }; pub use layouts::hierarchical::{ Hierarchical as LayoutHierarchical, State as LayoutStateHierarchical,