From 15c428519007c94117933af33bd92a9fffd4b06e Mon Sep 17 00:00:00 2001 From: starlord Date: Sun, 6 Oct 2024 15:08:47 +0300 Subject: [PATCH] poc persisted layout --- Cargo.toml | 1 + examples/basic/src/main.rs | 6 +- examples/basic_custom/src/main.rs | 8 +- examples/demo/src/main.rs | 4 +- examples/flex_nodes/src/main.rs | 2 +- examples/interactive/src/main.rs | 4 +- examples/label_change/src/main.rs | 4 +- examples/multiple/src/main.rs | 8 +- examples/undirected/src/main.rs | 13 ++- examples/window/src/main.rs | 6 +- src/draw/drawer.rs | 35 ++++--- src/elements/edge.rs | 7 +- src/elements/node.rs | 6 +- src/graph.rs | 75 +++++++------- src/graph_view.rs | 65 ++++++++---- src/helpers.rs | 104 +++++++++++--------- src/layouts/default.rs | 3 - src/layouts/hierarchical.rs | 23 ----- src/layouts/hierarchical/layout.rs | 40 ++++++++ src/layouts/hierarchical/mod.rs | 3 + src/layouts/layout.rs | 22 +++-- src/layouts/mod.rs | 10 +- src/layouts/{random.rs => random/layout.rs} | 41 +++++--- src/layouts/random/mod.rs | 3 + src/lib.rs | 6 +- src/metadata.rs | 6 +- 26 files changed, 293 insertions(+), 212 deletions(-) delete mode 100644 src/layouts/default.rs delete mode 100644 src/layouts/hierarchical.rs create mode 100644 src/layouts/hierarchical/layout.rs create mode 100644 src/layouts/hierarchical/mod.rs rename src/layouts/{random.rs => random/layout.rs} (54%) create mode 100644 src/layouts/random/mod.rs diff --git a/Cargo.toml b/Cargo.toml index 510a408..942d442 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ rand = "0.8" petgraph = { version = "0.6", default-features = false, features = [ "stable_graph", "matrix_graph", + "serde-1", ] } serde = { version = "1.0", features = ["derive"] } diff --git a/examples/basic/src/main.rs b/examples/basic/src/main.rs index 61be272..199a93b 100644 --- a/examples/basic/src/main.rs +++ b/examples/basic/src/main.rs @@ -1,7 +1,7 @@ use eframe::{run_native, App, CreationContext, NativeOptions}; use egui::Context; -use egui_graphs::{Graph, GraphView}; -use petgraph::stable_graph::StableGraph; +use egui_graphs::{DefaultEdgeShape, DefaultGraphView, DefaultNodeShape, Graph, GraphView}; +use petgraph::{csr::DefaultIx, stable_graph::StableGraph, Directed}; pub struct BasicApp { g: Graph, @@ -17,7 +17,7 @@ impl BasicApp { impl App for BasicApp { fn update(&mut self, ctx: &Context, _: &mut eframe::Frame) { egui::CentralPanel::default().show(ctx, |ui| { - ui.add(&mut GraphView::new(&mut self.g)); + ui.add(&mut DefaultGraphView::new(&mut self.g)); }); } } diff --git a/examples/basic_custom/src/main.rs b/examples/basic_custom/src/main.rs index 17e5fdf..00a5cd2 100644 --- a/examples/basic_custom/src/main.rs +++ b/examples/basic_custom/src/main.rs @@ -1,6 +1,6 @@ use eframe::{run_native, App, CreationContext, NativeOptions}; use egui::{Context, Pos2}; -use egui_graphs::{add_edge, add_node_custom, Graph, GraphView}; +use egui_graphs::{add_edge, add_node_custom, DefaultGraphView, Graph, SettingsStyle}; use petgraph::stable_graph::StableGraph; pub struct BasicCustomApp { @@ -16,6 +16,7 @@ impl BasicCustomApp { for position in positions { idxs.push(add_node_custom(&mut g, &(), |g_node| { g_node.set_location(position); + g_node.set_label(position.to_string()); })); } @@ -30,7 +31,10 @@ impl BasicCustomApp { impl App for BasicCustomApp { fn update(&mut self, ctx: &Context, _: &mut eframe::Frame) { egui::CentralPanel::default().show(ctx, |ui| { - ui.add(&mut GraphView::new(&mut self.g)); + ui.add( + &mut DefaultGraphView::new(&mut self.g) + .with_styles(&SettingsStyle::default().with_labels_always(true)), + ); }); } } diff --git a/examples/demo/src/main.rs b/examples/demo/src/main.rs index 6f4a4d4..3ed340c 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::{to_graph, Edge, Graph, GraphView, Node}; +use egui_graphs::{to_graph, DefaultGraphView, Edge, Graph, Node}; use fdg::fruchterman_reingold::{FruchtermanReingold, FruchtermanReingoldConfiguration}; use fdg::nalgebra::{Const, OPoint}; use fdg::{Force, ForceGraph}; @@ -511,7 +511,7 @@ impl App for DemoApp { let settings_style = &egui_graphs::SettingsStyle::new() .with_labels_always(self.settings_style.labels_always); ui.add( - &mut GraphView::new(&mut self.g) + &mut DefaultGraphView::new(&mut self.g) .with_interactions(settings_interaction) .with_navigations(settings_navigation) .with_styles(settings_style) diff --git a/examples/flex_nodes/src/main.rs b/examples/flex_nodes/src/main.rs index 743b577..56d8541 100644 --- a/examples/flex_nodes/src/main.rs +++ b/examples/flex_nodes/src/main.rs @@ -58,7 +58,7 @@ impl FlexNodesApp { } }); CentralPanel::default().show(ctx, |ui| { - let widget = &mut GraphView::new(&mut self.g) + let widget = &mut GraphView::<_, _, _, _, _, _>::new(&mut self.g) .with_interactions( &SettingsInteraction::default() .with_dragging_enabled(true) diff --git a/examples/interactive/src/main.rs b/examples/interactive/src/main.rs index 39aff1b..f92e070 100644 --- a/examples/interactive/src/main.rs +++ b/examples/interactive/src/main.rs @@ -1,6 +1,6 @@ use eframe::{run_native, App, CreationContext}; use egui::Context; -use egui_graphs::{layouts, Graph, GraphView, SettingsInteraction, SettingsStyle}; +use egui_graphs::{DefaultGraphView, Graph, SettingsInteraction, SettingsStyle}; use petgraph::stable_graph::StableGraph; pub struct InteractiveApp { @@ -27,7 +27,7 @@ impl App for InteractiveApp { .with_edge_selection_multi_enabled(true); let style_settings = &SettingsStyle::new().with_labels_always(true); ui.add( - &mut GraphView::<_, _, _, _, _, _, layouts::Default>::new(&mut self.g) + &mut DefaultGraphView::new(&mut self.g) .with_styles(style_settings) .with_interactions(interaction_settings), ); diff --git a/examples/label_change/src/main.rs b/examples/label_change/src/main.rs index 9254e6a..ec42fef 100644 --- a/examples/label_change/src/main.rs +++ b/examples/label_change/src/main.rs @@ -1,6 +1,6 @@ use eframe::{run_native, App, CreationContext}; use egui::{CentralPanel, Context, SidePanel, TextEdit}; -use egui_graphs::{layouts, Graph, GraphView, SettingsInteraction, SettingsStyle}; +use egui_graphs::{DefaultGraphView, Graph, SettingsInteraction, SettingsStyle}; use petgraph::stable_graph::{EdgeIndex, NodeIndex, StableGraph}; pub struct LabelChangeApp { @@ -52,7 +52,7 @@ impl LabelChangeApp { } }); CentralPanel::default().show(ctx, |ui| { - let widget = &mut GraphView::<_, _, _, _, _, _, layouts::Default>::new(&mut self.g) + let widget = &mut DefaultGraphView::new(&mut self.g) .with_interactions( &SettingsInteraction::default() .with_node_selection_enabled(true) diff --git a/examples/multiple/src/main.rs b/examples/multiple/src/main.rs index 5ceec53..95d3eb4 100644 --- a/examples/multiple/src/main.rs +++ b/examples/multiple/src/main.rs @@ -1,6 +1,6 @@ use eframe::{run_native, App, CreationContext, Frame}; use egui::{CentralPanel, Context, Layout, SidePanel}; -use egui_graphs::{Graph, GraphView, SettingsInteraction, SettingsNavigation}; +use egui_graphs::{DefaultGraphView, Graph, SettingsInteraction, SettingsNavigation}; use petgraph::stable_graph::StableGraph; pub struct BasicApp { @@ -23,7 +23,7 @@ impl App for BasicApp { .show(ctx, |ui| { ui.allocate_ui_with_layout(ui.max_rect().size(), Layout::default(), |ui| { ui.add( - &mut GraphView::new(&mut self.g) + &mut DefaultGraphView::new(&mut self.g) .with_navigations( &SettingsNavigation::default() .with_fit_to_screen_enabled(false) @@ -45,7 +45,7 @@ impl App for BasicApp { .resizable(true) .show(ctx, |ui| { ui.add( - &mut GraphView::new(&mut self.g) + &mut DefaultGraphView::new(&mut self.g) .with_navigations( &SettingsNavigation::default() .with_fit_to_screen_enabled(false) @@ -63,7 +63,7 @@ impl App for BasicApp { }); CentralPanel::default().show(ctx, |ui| { ui.add( - &mut GraphView::new(&mut self.g) + &mut DefaultGraphView::new(&mut self.g) .with_navigations( &SettingsNavigation::default() .with_fit_to_screen_enabled(false) diff --git a/examples/undirected/src/main.rs b/examples/undirected/src/main.rs index 9c2ac03..d4c10a1 100644 --- a/examples/undirected/src/main.rs +++ b/examples/undirected/src/main.rs @@ -1,6 +1,6 @@ use eframe::{run_native, App, CreationContext}; use egui::Context; -use egui_graphs::{Graph, GraphView}; +use egui_graphs::{Graph, GraphView, LayoutRandom, LayoutStateRandom}; use petgraph::{ stable_graph::{StableGraph, StableUnGraph}, Undirected, @@ -20,7 +20,16 @@ impl UndirectedApp { impl App for UndirectedApp { fn update(&mut self, ctx: &Context, _: &mut eframe::Frame) { egui::CentralPanel::default().show(ctx, |ui| { - ui.add(&mut GraphView::new(&mut self.g)); + ui.add(&mut GraphView::< + _, + _, + _, + _, + _, + _, + LayoutStateRandom, + LayoutRandom, + >::new(&mut self.g)); }); } } diff --git a/examples/window/src/main.rs b/examples/window/src/main.rs index 5ba2ac3..cae4568 100644 --- a/examples/window/src/main.rs +++ b/examples/window/src/main.rs @@ -1,10 +1,10 @@ use eframe::{run_native, App, CreationContext}; use egui::{Context, Window}; -use egui_graphs::{Graph, GraphView}; +use egui_graphs::{DefaultGraphView, Graph}; use petgraph::stable_graph::StableGraph; pub struct WindowApp { - g: Graph<(), ()>, + g: Graph, } impl WindowApp { @@ -17,7 +17,7 @@ impl WindowApp { impl App for WindowApp { fn update(&mut self, ctx: &Context, _: &mut eframe::Frame) { Window::new("graph").show(ctx, |ui| { - ui.add(&mut GraphView::new(&mut self.g)); + ui.add(&mut DefaultGraphView::new(&mut self.g)); }); } } diff --git a/src/draw/drawer.rs b/src/draw/drawer.rs index 6652992..1fc3bc9 100644 --- a/src/draw/drawer.rs +++ b/src/draw/drawer.rs @@ -1,10 +1,14 @@ use std::marker::PhantomData; -use egui::{Context, Painter, Shape}; +use egui::{util::id_type_map::SerializableAny, Context, Painter, Shape}; use petgraph::graph::IndexType; use petgraph::EdgeType; -use crate::{layouts::Layout, settings::SettingsStyle, Graph, Metadata}; +use crate::{ + layouts::{Layout, LayoutState}, + settings::SettingsStyle, + Graph, Metadata, +}; use super::{DisplayEdge, DisplayNode}; @@ -17,7 +21,7 @@ pub struct DrawContext<'a> { pub meta: &'a Metadata, } -pub struct Drawer<'a, N, E, Ty, Ix, Nd, Ed, L> +pub struct Drawer<'a, N, E, Ty, Ix, Nd, Ed, S, L> where N: Clone, E: Clone, @@ -25,15 +29,17 @@ where Ix: IndexType, Nd: DisplayNode, Ed: DisplayEdge, - L: Layout, + S: LayoutState, + L: Layout, { ctx: &'a DrawContext<'a>, - g: &'a mut Graph, - postponed: Vec, - _marker: PhantomData<(Nd, Ed, L)>, + g: &'a mut Graph, + delayed: Vec, + + _marker: PhantomData<(Nd, Ed, L, S)>, } -impl<'a, N, E, Ty, Ix, Nd, Ed, L> Drawer<'a, N, E, Ty, Ix, Nd, Ed, L> +impl<'a, N, E, Ty, Ix, Nd, Ed, S, L> Drawer<'a, N, E, Ty, Ix, Nd, Ed, S, L> where N: Clone, E: Clone, @@ -41,13 +47,14 @@ where Ix: IndexType, Nd: DisplayNode, Ed: DisplayEdge, - L: Layout, + S: LayoutState, + L: Layout, { - pub fn new(g: &'a mut Graph, ctx: &'a DrawContext<'a>) -> Self { + pub fn new(g: &'a mut Graph, ctx: &'a DrawContext<'a>) -> Self { Drawer { ctx, g, - postponed: Vec::new(), + delayed: Vec::new(), _marker: PhantomData, } } @@ -59,7 +66,7 @@ where } fn draw_postponed(&mut self) { - self.postponed.iter().for_each(|s| { + self.delayed.iter().for_each(|s| { self.ctx.painter.add(s.clone()); }); } @@ -80,7 +87,7 @@ where if n.selected() || n.dragged() { for s in shapes { - self.postponed.push(s); + self.delayed.push(s); } } else { for s in shapes { @@ -112,7 +119,7 @@ where if e.selected() { for s in shapes { - self.postponed.push(s); + self.delayed.push(s); } } else { for s in shapes { diff --git a/src/elements/edge.rs b/src/elements/edge.rs index b098687..882c366 100644 --- a/src/elements/edge.rs +++ b/src/elements/edge.rs @@ -4,12 +4,12 @@ use petgraph::{ stable_graph::{DefaultIx, EdgeIndex, IndexType}, Directed, EdgeType, }; +use serde::{Deserialize, Serialize}; use crate::{DefaultEdgeShape, DefaultNodeShape, DisplayEdge, DisplayNode}; /// Stores properties of an [Edge] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct EdgeProps { pub payload: E, pub order: usize, @@ -18,8 +18,7 @@ pub struct EdgeProps { } /// Stores properties of an edge that can be changed. Used to apply changes to the graph. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct Edge< N: Clone, E: Clone, diff --git a/src/elements/node.rs b/src/elements/node.rs index bded2b2..05f2be4 100644 --- a/src/elements/node.rs +++ b/src/elements/node.rs @@ -6,12 +6,12 @@ use petgraph::{ stable_graph::{DefaultIx, IndexType, NodeIndex}, Directed, EdgeType, }; +use serde::{Deserialize, Serialize}; use crate::{DefaultNodeShape, DisplayNode}; /// Stores properties of a [Node] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct NodeProps { pub payload: N, pub location: Pos2, @@ -20,7 +20,7 @@ pub struct NodeProps { pub dragged: bool, } -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Serialize, Deserialize)] pub struct Node where N: Clone, diff --git a/src/graph.rs b/src/graph.rs index 5038f1c..93c549f 100644 --- a/src/graph.rs +++ b/src/graph.rs @@ -1,5 +1,3 @@ -use std::marker::PhantomData; - use egui::Pos2; use petgraph::stable_graph::DefaultIx; use petgraph::Directed; @@ -10,9 +8,9 @@ use petgraph::{ visit::{EdgeRef, IntoEdgeReferences, IntoNodeReferences}, Direction, EdgeType, }; +use serde::{Deserialize, Serialize}; use crate::draw::{DisplayEdge, DisplayNode}; -use crate::layouts::{self, Layout}; use crate::{metadata::Metadata, Edge, Node}; use crate::{to_graph, DefaultEdgeShape, DefaultNodeShape}; @@ -21,63 +19,58 @@ type StableGraphType = /// Wrapper around [`petgraph::stable_graph::StableGraph`] compatible with [`super::GraphView`]. /// It is used to store graph data and provide access to it. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct Graph< - N: Clone = (), - E: Clone = (), - Ty: EdgeType = Directed, - Ix: IndexType = DefaultIx, - Dn: DisplayNode = DefaultNodeShape, - De: DisplayEdge = DefaultEdgeShape, - L: Layout = layouts::Default, -> { + N = (), + E = (), + Ty = Directed, + Ix = DefaultIx, + Dn = DefaultNodeShape, + De = DefaultEdgeShape, +> where + N: Clone, + E: Clone, + Ty: EdgeType, + Ix: IndexType, + Dn: DisplayNode, + De: DisplayEdge, +{ pub g: StableGraphType, selected_nodes: Vec>, selected_edges: Vec>, dragged_node: Option>, - - _marker: PhantomData, } -impl< - N: Clone, - E: Clone, - Ty: EdgeType, - Ix: IndexType, - Dn: DisplayNode, - De: DisplayEdge, - L: Layout, - > From<&StableGraph> for Graph +impl From<&StableGraph> for Graph +where + N: Clone, + E: Clone, + Ty: EdgeType, + Ix: IndexType, + Dn: DisplayNode, + De: DisplayEdge, { fn from(g: &StableGraph) -> Self { to_graph(g) } } -impl< - N: Clone, - E: Clone, - Ty: EdgeType, - Ix: IndexType, - Dn: DisplayNode, - De: DisplayEdge, - L: Layout, - > Graph +impl Graph +where + N: Clone, + E: Clone, + Ty: EdgeType, + Ix: IndexType, + Dn: DisplayNode, + De: DisplayEdge, { pub fn new(g: StableGraphType) -> Self { - let mut graph = Self { + Self { g, selected_nodes: Vec::default(), selected_edges: Vec::default(), dragged_node: Option::default(), - - _marker: PhantomData, - }; - - L::default().next(&mut graph); - - graph + } } /// Finds node by position. Can be optimized by using a spatial index like quad-tree if needed. diff --git a/src/graph_view.rs b/src/graph_view.rs index bc3ab99..c69aa3e 100644 --- a/src/graph_view.rs +++ b/src/graph_view.rs @@ -2,18 +2,32 @@ use std::marker::PhantomData; use crate::{ draw::{DefaultEdgeShape, DefaultNodeShape, DrawContext, Drawer}, - layouts::{self, Layout}, + layouts::{self, Layout, LayoutState}, metadata::Metadata, settings::{SettingsInteraction, SettingsNavigation, SettingsStyle}, DisplayEdge, DisplayNode, Graph, }; -use egui::{PointerButton, Pos2, Rect, Response, Sense, Ui, Vec2, Widget}; +use egui::{Id, PointerButton, Pos2, Rect, Response, Sense, Ui, Vec2, Widget}; use petgraph::{graph::EdgeIndex, stable_graph::DefaultIx}; use petgraph::{graph::IndexType, Directed}; use petgraph::{stable_graph::NodeIndex, EdgeType}; +const KEY_LAYOUT: &str = "egui_grpahs_layout"; + +pub type DefaultGraphView<'a> = GraphView< + 'a, + (), + (), + Directed, + DefaultIx, + DefaultNodeShape, + DefaultEdgeShape, + layouts::random::State, + layouts::random::Random, +>; + #[cfg(feature = "events")] use crate::events::{ Event, PayloadEdgeClick, PayloadEdgeDeselect, PayloadEdgeSelect, PayloadNodeClick, @@ -46,7 +60,8 @@ pub struct GraphView< Ix = DefaultIx, Nd = DefaultNodeShape, Ed = DefaultEdgeShape, - L = layouts::Default, + S = layouts::random::State, + L = layouts::random::Random, > where N: Clone, E: Clone, @@ -54,9 +69,10 @@ pub struct GraphView< Ix: IndexType, Nd: DisplayNode, Ed: DisplayEdge, - L: Layout, + S: LayoutState, + L: Layout, { - g: &'a mut Graph, + g: &'a mut Graph, settings_interaction: SettingsInteraction, settings_navigation: SettingsNavigation, @@ -65,10 +81,10 @@ pub struct GraphView< #[cfg(feature = "events")] events_publisher: Option<&'a Sender>, - _marker: PhantomData<(Nd, Ed, L)>, + _marker: PhantomData<(Nd, Ed, L, S)>, } -impl<'a, N, E, Ty, Ix, Nd, Ed, L> Widget for &mut GraphView<'a, N, E, Ty, Ix, Nd, Ed, L> +impl<'a, N, E, Ty, Ix, Nd, Ed, S, L> Widget for &mut GraphView<'a, N, E, Ty, Ix, Nd, Ed, S, L> where N: Clone, E: Clone, @@ -76,22 +92,22 @@ where Ix: IndexType, Nd: DisplayNode, Ed: DisplayEdge, - L: Layout, + S: LayoutState, + L: Layout, { fn ui(self, ui: &mut Ui) -> Response { - let mut meta = Metadata::get(ui); - self.sync_state(&mut meta); + self.sync_layout(ui); - L::default().next(self.g); + let mut meta = Metadata::load(ui); + self.sync_state(&mut meta); 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); self.handle_node_drag(&resp, &mut meta); self.handle_click(&resp, &mut meta); - Drawer::::new( + Drawer::::new( self.g, &DrawContext { ctx: ui.ctx(), @@ -104,7 +120,7 @@ where .draw(); meta.first_frame = false; - meta.store_into_ui(ui); + meta.save(ui); ui.ctx().request_repaint(); @@ -112,7 +128,7 @@ where } } -impl<'a, N, E, Ty, Ix, Dn, De, L> GraphView<'a, N, E, Ty, Ix, Dn, De, L> +impl<'a, N, E, Ty, Ix, Dn, De, S, L> GraphView<'a, N, E, Ty, Ix, Dn, De, S, L> where N: Clone, E: Clone, @@ -120,11 +136,12 @@ where Ix: IndexType, Dn: DisplayNode, De: DisplayEdge, - L: Layout, + S: LayoutState, + L: Layout, { /// Creates a new `GraphView` widget with default navigation and interactions settings. /// To customize navigation and interactions use `with_interactions` and `with_navigations` methods. - pub fn new(g: &'a mut Graph) -> Self { + pub fn new(g: &'a mut Graph) -> Self { Self { g, @@ -159,7 +176,7 @@ where /// Resets navigation metadata pub fn reset_metadata(ui: &mut Ui) { - Metadata::default().store_into_ui(ui); + Metadata::default().save(ui); } #[cfg(feature = "events")] @@ -169,6 +186,18 @@ where self } + fn sync_layout(&mut self, ui: &mut Ui) { + 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); + + data.insert_persisted(Id::new(KEY_LAYOUT), layout.state()); + }); + } + fn sync_state(&mut self, meta: &mut Metadata) { let mut selected_nodes = Vec::new(); let mut selected_edges = Vec::new(); diff --git a/src/helpers.rs b/src/helpers.rs index 71d81dd..af289e7 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -1,4 +1,4 @@ -use crate::{layouts::Layout, DisplayEdge, DisplayNode, Edge, Graph, Node}; +use crate::{DisplayEdge, DisplayNode, Edge, Graph, Node}; use petgraph::{ graph::IndexType, stable_graph::{EdgeIndex, NodeIndex, StableGraph}, @@ -10,27 +10,34 @@ 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 { +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< +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, - D: DisplayNode, ->( - g: &mut Graph, - n: &N, - node_transform: impl FnOnce(&mut Node), -) -> NodeIndex { + Dn: DisplayNode, + De: DisplayEdge, +{ let idx = NodeIndex::new(g.g.node_count() + 1); let mut n = Node::new(n.clone()); @@ -42,19 +49,20 @@ pub fn add_node_custom< } /// Helper function which adds user's edge to the [`super::Graph`] instance. -pub fn add_edge< +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, ->( - g: &mut Graph, - start: NodeIndex, - end: NodeIndex, - e: &E, -) -> EdgeIndex { +{ add_edge_custom( g, start, @@ -65,20 +73,21 @@ pub fn add_edge< } /// Helper function which adds user's edge to the [`super::Graph`] instance with custom edge transform function. -pub fn add_edge_custom< +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, ->( - g: &mut Graph, - start: NodeIndex, - end: NodeIndex, - e: &E, - edge_transform: impl FnOnce(&mut Edge), -) -> EdgeIndex { +{ let mut edge = Edge::new(e.clone()); edge.set_id(EdgeIndex::::new(g.g.edge_count() + 1)); @@ -127,33 +136,32 @@ pub fn add_edge_custom< /// assert!(loc_1 != Pos2::ZERO); /// assert!(loc_2 != Pos2::ZERO); /// ``` -pub fn to_graph< +pub fn to_graph(g: &StableGraph) -> Graph +where N: Clone, E: Clone, Ty: EdgeType, Ix: IndexType, Dn: DisplayNode, De: DisplayEdge, - L: Layout, ->( - g: &StableGraph, -) -> Graph { +{ transform(g, &mut default_node_transform, &mut default_edge_transform) } /// The same as [`to_graph`], but allows to define custom transformation procedures for nodes and edges. -pub fn to_graph_custom< +pub fn to_graph_custom( + g: &StableGraph, + mut node_transform: impl FnMut(&mut Node), + mut edge_transform: impl FnMut(&mut Edge), +) -> Graph +where N: Clone, E: Clone, Ty: EdgeType, Ix: IndexType, Dn: DisplayNode, De: DisplayEdge, ->( - g: &StableGraph, - mut node_transform: impl FnMut(&mut Node), - mut edge_transform: impl FnMut(&mut Edge), -) -> Graph { +{ transform(g, &mut node_transform, &mut edge_transform) } @@ -185,19 +193,19 @@ pub fn default_edge_transform< edge.set_label(format!("edge {}", edge.id().index())); } -fn transform< +fn transform( + input: &StableGraph, + node_transform: &mut impl FnMut(&mut Node), + edge_transform: &mut impl FnMut(&mut Edge), +) -> Graph +where N: Clone, E: Clone, Ty: EdgeType, Ix: IndexType, Dn: DisplayNode, De: DisplayEdge, - L: Layout, ->( - input: &StableGraph, - node_transform: &mut impl FnMut(&mut Node), - edge_transform: &mut impl FnMut(&mut Edge), -) -> Graph { +{ let mut g = StableGraph::, Edge, Ty, Ix>::default(); @@ -253,8 +261,7 @@ mod tests { let n2 = user_g.add_node("Node2"); user_g.add_edge(n1, n2, "Edge1"); - let input_g = - to_graph::<_, _, _, _, DefaultNodeShape, DefaultEdgeShape, layouts::Default>(&user_g); + 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()); @@ -283,8 +290,7 @@ mod tests { let n2 = user_g.add_node("Node2"); user_g.add_edge(n1, n2, "Edge1"); - let input_g = - to_graph::<_, _, _, _, DefaultNodeShape, DefaultEdgeShape, layouts::Default>(&user_g); + 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()); diff --git a/src/layouts/default.rs b/src/layouts/default.rs deleted file mode 100644 index 324f427..0000000 --- a/src/layouts/default.rs +++ /dev/null @@ -1,3 +0,0 @@ -use super::Random; - -pub type Default = Random; diff --git a/src/layouts/hierarchical.rs b/src/layouts/hierarchical.rs deleted file mode 100644 index 50b7c5a..0000000 --- a/src/layouts/hierarchical.rs +++ /dev/null @@ -1,23 +0,0 @@ -use crate::Graph; - -use super::Layout; - -#[derive(Default)] -pub struct Hierarchical {} - -impl Layout for Hierarchical { - fn next< - N: Clone, - E: Clone, - Ty: petgraph::EdgeType, - Ix: petgraph::csr::IndexType, - Dn: crate::DisplayNode, - De: crate::DisplayEdge, - L: Layout, - >( - &mut self, - g: &mut Graph, - ) { - todo!() - } -} diff --git a/src/layouts/hierarchical/layout.rs b/src/layouts/hierarchical/layout.rs new file mode 100644 index 0000000..88059cc --- /dev/null +++ b/src/layouts/hierarchical/layout.rs @@ -0,0 +1,40 @@ +use serde::{Deserialize, Serialize}; + +use crate::{ + layouts::{Layout, LayoutState}, + DisplayEdge, DisplayNode, Graph, +}; + +#[derive(Debug, Default, Clone, Serialize, Deserialize)] +pub struct State {} + +impl LayoutState for State {} + +#[derive(Debug, Default)] +pub struct Hierarchical { + state: State, +} + +impl Layout for Hierarchical { + fn next< + N: Clone, + E: Clone, + Ty: petgraph::EdgeType, + Ix: petgraph::csr::IndexType, + Dn: DisplayNode, + De: DisplayEdge, + >( + &mut self, + g: &mut Graph, + ) { + todo!() + } + + fn state(&self) -> State { + todo!() + } + + fn from_state(state: State) -> impl Layout { + Hierarchical { state } + } +} diff --git a/src/layouts/hierarchical/mod.rs b/src/layouts/hierarchical/mod.rs new file mode 100644 index 0000000..6b06c1f --- /dev/null +++ b/src/layouts/hierarchical/mod.rs @@ -0,0 +1,3 @@ +mod layout; + +pub use layout::{Hierarchical, State}; diff --git a/src/layouts/layout.rs b/src/layouts/layout.rs index 4f768c6..b510700 100644 --- a/src/layouts/layout.rs +++ b/src/layouts/layout.rs @@ -3,19 +3,23 @@ use petgraph::{stable_graph::IndexType, EdgeType}; use crate::{DisplayEdge, DisplayNode, Graph}; -pub trait Layout: Default { +pub trait LayoutState: SerializableAny + Default {} + +pub trait Layout: Default +where + S: LayoutState, +{ + fn from_state(state: S) -> impl Layout; + /// Called on every frame. It should update the graph layout aka nodes locations. - // TODO: maybe should have signature next(prev: impl Serializable, g), where prev is prev state?:w - fn next< + fn next(&mut self, g: &mut Graph) + where N: Clone, E: Clone, Ty: EdgeType, Ix: IndexType, Dn: DisplayNode, - De: DisplayEdge, - L: Layout, - >( - &mut self, - g: &mut Graph, - ); + De: DisplayEdge; + + fn state(&self) -> S; } diff --git a/src/layouts/mod.rs b/src/layouts/mod.rs index a999302..7ffced5 100644 --- a/src/layouts/mod.rs +++ b/src/layouts/mod.rs @@ -1,9 +1,5 @@ -mod default; -mod hierarchical; +pub mod hierarchical; mod layout; -mod random; +pub mod random; -pub use default::Default; -pub use hierarchical::Hierarchical; -pub use layout::Layout; -pub use random::Random; +pub use layout::{Layout, LayoutState}; diff --git a/src/layouts/random.rs b/src/layouts/random/layout.rs similarity index 54% rename from src/layouts/random.rs rename to src/layouts/random/layout.rs index 7c31698..e4876ac 100644 --- a/src/layouts/random.rs +++ b/src/layouts/random/layout.rs @@ -1,33 +1,38 @@ use egui::Pos2; use petgraph::stable_graph::IndexType; use rand::Rng; +use serde::{Deserialize, Serialize}; -use crate::Graph; +use crate::{ + layouts::{Layout, LayoutState}, + Graph, +}; +const SPAWN_SIZE: f32 = 250.; -use super::Layout; +#[derive(Debug, Default, Clone, Serialize, Deserialize)] +pub struct State { + triggered: bool, +} -const SPAWN_SIZE: f32 = 250.; +impl LayoutState for State {} /// Randomly places nodes on the canvas. Does not override existing locations. Applies once. -#[derive(Default)] +#[derive(Debug, Default)] pub struct Random { - triggered: bool, + state: State, } -impl Layout for Random { - fn next< +impl Layout for Random { + fn next(&mut self, g: &mut Graph) + where N: Clone, E: Clone, Ty: petgraph::EdgeType, Ix: IndexType, Dn: crate::DisplayNode, De: crate::DisplayEdge, - L: Layout, - >( - &mut self, - g: &mut Graph, - ) { - if self.triggered { + { + if self.state.triggered { return; } @@ -39,6 +44,14 @@ impl Layout for Random { )); } - self.triggered = true; + self.state.triggered = true; + } + + fn state(&self) -> State { + self.state.clone() + } + + fn from_state(state: State) -> impl Layout { + Self { state } } } diff --git a/src/layouts/random/mod.rs b/src/layouts/random/mod.rs new file mode 100644 index 0000000..8d596d8 --- /dev/null +++ b/src/layouts/random/mod.rs @@ -0,0 +1,3 @@ +mod layout; + +pub use layout::{Random, State}; diff --git a/src/lib.rs b/src/lib.rs index e65da0d..7de38a1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,19 +3,19 @@ mod elements; mod graph; mod graph_view; mod helpers; +mod layouts; mod metadata; mod settings; -pub mod layouts; - pub use draw::{DefaultEdgeShape, DefaultNodeShape, DisplayEdge, DisplayNode, DrawContext}; pub use elements::{Edge, EdgeProps, Node, NodeProps}; pub use graph::Graph; -pub use graph_view::GraphView; +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, to_graph, to_graph_custom, }; +pub use layouts::random::{Random as LayoutRandom, State as LayoutStateRandom}; pub use metadata::Metadata; pub use settings::{SettingsInteraction, SettingsNavigation, SettingsStyle}; diff --git a/src/metadata.rs b/src/metadata.rs index 4a011e7..e5a8587 100644 --- a/src/metadata.rs +++ b/src/metadata.rs @@ -76,16 +76,16 @@ impl Default for Metadata { } impl Metadata { - pub fn get(ui: &egui::Ui) -> Self { + pub fn load(ui: &egui::Ui) -> Self { ui.data_mut(|data| { data.get_persisted::(Id::new(KEY)) .unwrap_or_default() }) } - pub fn store_into_ui(self, ui: &mut egui::Ui) { + pub fn save(self, ui: &mut egui::Ui) { ui.data_mut(|data| { - data.insert_persisted(Id::NULL, self); + data.insert_persisted(Id::new(KEY), self); }); }