diff --git a/Cargo.lock b/Cargo.lock index 3da357b..2d00fd5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -339,7 +339,7 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "basic" -version = "0.3.1" +version = "0.4.0" dependencies = [ "eframe", "egui", @@ -521,7 +521,7 @@ dependencies = [ [[package]] name = "configurable" -version = "0.3.1" +version = "0.4.0" dependencies = [ "eframe", "egui", @@ -751,7 +751,7 @@ dependencies = [ [[package]] name = "egui_graphs" -version = "0.3.1" +version = "0.4.0" dependencies = [ "egui", "petgraph", @@ -1145,7 +1145,7 @@ dependencies = [ [[package]] name = "interactive" -version = "0.3.1" +version = "0.4.0" dependencies = [ "eframe", "egui", diff --git a/Cargo.toml b/Cargo.toml index e957e5e..ea47405 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "egui_graphs" -version = "0.3.1" +version = "0.4.0" authors = ["Dmitrii Samsonov "] license = "MIT" homepage = "https://github.com/blitzarx1/egui_graphs" diff --git a/examples/basic/Cargo.toml b/examples/basic/Cargo.toml index 496e3df..af9904f 100644 --- a/examples/basic/Cargo.toml +++ b/examples/basic/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "basic" -version = "0.3.1" +version = "0.4.0" authors = ["Dmitrii Samsonov "] license = "MIT" edition = "2021" diff --git a/examples/configurable/Cargo.toml b/examples/configurable/Cargo.toml index 05a5e3d..5ba8525 100644 --- a/examples/configurable/Cargo.toml +++ b/examples/configurable/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "configurable" -version = "0.3.1" +version = "0.4.0" authors = ["Dmitrii Samsonov "] license = "MIT" edition = "2021" diff --git a/examples/configurable/src/main.rs b/examples/configurable/src/main.rs index afc0216..5acd2e1 100644 --- a/examples/configurable/src/main.rs +++ b/examples/configurable/src/main.rs @@ -5,7 +5,7 @@ use eframe::{run_native, App, CreationContext}; use egui::plot::{Line, Plot, PlotPoints}; use egui::{CollapsingHeader, Color32, Context, Pos2, Rect, ScrollArea, Slider, Ui, Vec2, Visuals}; use egui_graphs::{ - Changes, Edge, GraphView, Node, SettingsInteraction, SettingsNavigation, SettingsStyle, + Change, Edge, GraphView, Node, SettingsInteraction, SettingsNavigation, SettingsStyle, }; use fdg_sim::glam::Vec3; use fdg_sim::{ForceGraph, ForceGraphHelper, Simulation, SimulationParameters}; @@ -32,7 +32,7 @@ pub struct ConfigurableApp { selected_nodes: Vec>, selected_edges: Vec>, - last_changes: Vec, + last_changes: Vec, simulation_stopped: bool, dark_mode: bool, @@ -42,8 +42,8 @@ pub struct ConfigurableApp { last_update_time: Instant, frames_last_time_span: usize, - changes_receiver: Receiver, - changes_sender: Sender, + changes_receiver: Receiver, + changes_sender: Sender, } impl ConfigurableApp { diff --git a/examples/interactive/Cargo.toml b/examples/interactive/Cargo.toml index d4a73cf..e0717cb 100644 --- a/examples/interactive/Cargo.toml +++ b/examples/interactive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "interactive" -version = "0.3.1" +version = "0.4.0" authors = ["Dmitrii Samsonov "] license = "MIT" edition = "2021" diff --git a/src/change.rs b/src/change.rs new file mode 100644 index 0000000..11908f3 --- /dev/null +++ b/src/change.rs @@ -0,0 +1,95 @@ +use egui::Vec2; +use petgraph::stable_graph::{NodeIndex, EdgeIndex}; + +/// `ChangeNode` is a enum that stores the changes to `Node` properties. +#[derive(Debug, Clone)] +pub enum ChangeNode { + Location { id: NodeIndex, old: Vec2, new: Vec2 }, + + Selected { id: NodeIndex, old: bool, new: bool }, + + Dragged { id: NodeIndex, old: bool, new: bool }, +} + +impl ChangeNode { + pub(crate) fn change_location(id: NodeIndex, old: Vec2, new: Vec2) -> Self { + Self::Location { id, old, new } + } + + pub(crate) fn change_selected(id: NodeIndex, old: bool, new: bool) -> Self { + Self::Selected { id, old, new } + } + + pub(crate) fn change_dragged(id: NodeIndex, old: bool, new: bool) -> Self { + Self::Dragged { id, old, new } + } +} + +/// `ChangeEdge` is a enum that stores the changes to `Edge` properties. +#[derive(Debug, Clone)] +pub enum ChangeEdge { + Selected { id: EdgeIndex, old: bool, new: bool }, +} + +impl ChangeEdge { + pub(crate) fn change_selected(id: EdgeIndex, old: bool, new: bool) -> Self { + Self::Selected { id, old, new } + } +} + +/// Change is a enum that stores the changes to `Node` or `Edge` properties. +#[derive(Debug, Clone)] +pub enum Change { + Node(ChangeNode), + Edge(ChangeEdge), +} + +impl Change { + pub(crate) fn node(change: ChangeNode) -> Self { + Self::Node(change) + } + + pub(crate) fn edge(change: ChangeEdge) -> Self { + Self::Edge(change) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use egui::vec2; + + #[test] + fn test_change_enum() { + let node_id = NodeIndex::new(0); + let edge_id = EdgeIndex::new(0); + + let old_node_location = vec2(0.0, 0.0); + let new_node_location = vec2(10.0, 10.0); + + let node_change = Change::node(ChangeNode::change_location(node_id, old_node_location, new_node_location)); + + let node_selected_old = false; + let node_selected_new = true; + + let edge_change = Change::edge(ChangeEdge::change_selected(edge_id, node_selected_old, node_selected_new)); + + match node_change { + Change::Node(ChangeNode::Location { id, old, new }) => { + assert_eq!(id, node_id); + assert_eq!(old, old_node_location); + assert_eq!(new, new_node_location); + } + _ => panic!("Unexpected node change type"), + } + + match edge_change { + Change::Edge(ChangeEdge::Selected { id, old, new }) => { + assert_eq!(id, edge_id); + assert_eq!(old, node_selected_old); + assert_eq!(new, node_selected_new); + } + _ => panic!("Unexpected edge change type"), + } + } +} \ No newline at end of file diff --git a/src/changes.rs b/src/changes.rs deleted file mode 100644 index ca073b8..0000000 --- a/src/changes.rs +++ /dev/null @@ -1,146 +0,0 @@ -use std::collections::HashMap; - -use egui::Vec2; -use petgraph::stable_graph::NodeIndex; - -/// `Changes` is a struct that stores the changes that happened in the graph -#[derive(Default, Debug, Clone)] -pub struct Changes { - pub nodes: HashMap, -} - -impl Changes { - pub fn is_empty(&self) -> bool { - self.nodes.is_empty() - } - - pub(crate) fn set_location(&mut self, idx: NodeIndex, val: Vec2) { - match self.nodes.get_mut(&idx) { - Some(changes_node) => changes_node.set_location(val), - None => { - let mut changes_node = ChangesNode::default(); - changes_node.set_location(val); - self.nodes.insert(idx, changes_node); - } - }; - } - - pub(crate) fn set_clicked(&mut self, idx: NodeIndex, val: bool) { - match self.nodes.get_mut(&idx) { - Some(changes_node) => changes_node.set_clicked(val), - None => { - let mut changes_node = ChangesNode::default(); - changes_node.set_clicked(val); - self.nodes.insert(idx, changes_node); - } - }; - } - - pub(crate) fn select_node(&mut self, idx: NodeIndex) { - match self.nodes.get_mut(&idx) { - Some(changes_node) => changes_node.select(), - None => { - let mut changes_node = ChangesNode::default(); - changes_node.select(); - self.nodes.insert(idx, changes_node); - } - }; - } - - pub(crate) fn deselect_node(&mut self, idx: NodeIndex) { - match self.nodes.get_mut(&idx) { - Some(changes_node) => changes_node.deselect(), - None => { - let mut changes_node = ChangesNode::default(); - changes_node.deselect(); - self.nodes.insert(idx, changes_node); - } - }; - } - - pub(crate) fn set_dragged(&mut self, idx: NodeIndex, val: bool) { - match self.nodes.get_mut(&idx) { - Some(changes_node) => changes_node.set_dragged(val), - None => { - let mut changes_node = ChangesNode::default(); - changes_node.set_dragged(val); - self.nodes.insert(idx, changes_node); - } - }; - } -} - -/// Stores changes to the node properties -#[derive(Default, Debug, Clone)] -pub struct ChangesNode { - pub location: Option, - pub selected: Option, - pub dragged: Option, - pub clicked: Option, -} - -impl ChangesNode { - fn set_location(&mut self, new_location: Vec2) { - self.location = Some(new_location); - } - - fn select(&mut self) { - self.selected = Some(true) - } - - fn deselect(&mut self) { - self.selected = Some(false); - } - - fn set_dragged(&mut self, new_dragged: bool) { - self.dragged = Some(new_dragged); - } - - fn set_clicked(&mut self, new_clicked: bool) { - self.clicked = Some(new_clicked); - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_changes_default() { - let changes: Changes = Changes::default(); - assert_eq!(changes.nodes.len(), 0); - } - - #[test] - fn test_changes_node_default() { - let changes_node: ChangesNode = ChangesNode::default(); - assert!(changes_node.location.is_none()); - assert!(changes_node.selected.is_none()); - assert!(changes_node.dragged.is_none()); - assert!(changes_node.clicked.is_none()); - } - - #[test] - fn test_setters() { - let mut changes = Changes::default(); - let idx = NodeIndex::new(0); - - let location = Vec2::new(10.0, 10.0); - changes.set_location(idx, location); - assert_eq!(changes.nodes.get(&idx).unwrap().location.unwrap(), location); - - let clicked = true; - changes.set_clicked(idx, clicked); - assert_eq!(changes.nodes.get(&idx).unwrap().clicked.unwrap(), clicked); - - changes.select_node(idx); - assert!(changes.nodes.get(&idx).unwrap().selected.unwrap()); - - changes.deselect_node(idx); - assert!(!changes.nodes.get(&idx).unwrap().selected.unwrap()); - - let dragged = true; - changes.set_dragged(idx, dragged); - assert_eq!(changes.nodes.get(&idx).unwrap().dragged.unwrap(), dragged); - } -} diff --git a/src/graph_view.rs b/src/graph_view.rs index f995ce9..2b2425c 100644 --- a/src/graph_view.rs +++ b/src/graph_view.rs @@ -4,7 +4,9 @@ use std::{ }; use crate::{ - changes::Changes, + change::Change, + change::ChangeEdge, + change::ChangeNode, elements::Node, frame_state::FrameState, metadata::Metadata, @@ -17,7 +19,7 @@ use egui::{ Color32, Painter, Pos2, Rect, Response, Sense, Shape, Stroke, Ui, Vec2, Widget, }; use petgraph::{ - stable_graph::{NodeIndex, StableGraph}, + stable_graph::{EdgeIndex, NodeIndex, StableGraph}, visit::IntoNodeReferences, }; @@ -41,7 +43,7 @@ pub struct GraphView<'a, N: Clone, E: Clone> { settings_interaction: SettingsInteraction, setings_navigation: SettingsNavigation, settings_style: SettingsStyle, - changes_sender: Option<&'a Sender>, + changes_sender: Option<&'a Sender>, } impl<'a, N: Clone, E: Clone> Widget for &mut GraphView<'a, N, E> { @@ -88,7 +90,7 @@ impl<'a, N: Clone, E: Clone> GraphView<'a, N, E> { self } - pub fn with_changes(mut self, changes_sender: &'a Sender) -> Self { + pub fn with_changes(mut self, changes_sender: &'a Sender) -> Self { self.changes_sender = Some(changes_sender); self } @@ -186,13 +188,12 @@ impl<'a, N: Clone, E: Clone> GraphView<'a, N, E> { fn handle_node_click(&mut self, idx: NodeIndex, state: &FrameState) { if !self.settings_interaction.node_select { - self.click_node(idx); return; } let n = self.g.node_weight(idx).unwrap(); if n.selected { - self.deselect_node(idx); + self.set_node_selected(idx, false); return; } @@ -200,7 +201,7 @@ impl<'a, N: Clone, E: Clone> GraphView<'a, N, E> { self.deselect_all(state); } - self.select_node(idx); + self.set_node_selected(idx, true); } fn handle_nodes_drags( @@ -226,7 +227,8 @@ impl<'a, N: Clone, E: Clone> GraphView<'a, N, E> { } if resp.drag_released() && state.dragged.is_some() { - self.unset_dragged_node(state); + let n_idx = state.dragged.unwrap(); + self.set_dragged(n_idx, false); } } @@ -312,26 +314,18 @@ impl<'a, N: Clone, E: Clone> GraphView<'a, N, E> { meta.zoom = new_zoom; } - fn click_node(&self, idx: NodeIndex) { - let mut changes = Changes::default(); - changes.set_clicked(idx, true); - self.send_changes(changes); + fn set_node_selected(&mut self, idx: NodeIndex, val: bool) { + let n = self.g.node_weight_mut(idx).unwrap(); + let change = ChangeNode::change_selected(idx, n.selected, val); + n.selected = val; + self.send_changes(Change::node(change)); } - fn select_node(&mut self, idx: NodeIndex) { - self.g.node_weight_mut(idx).unwrap().selected = true; - - let mut changes = Changes::default(); - changes.select_node(idx); - self.send_changes(changes); - } - - fn deselect_node(&mut self, idx: NodeIndex) { - self.g.node_weight_mut(idx).unwrap().selected = false; - - let mut changes = Changes::default(); - changes.deselect_node(idx); - self.send_changes(changes); + fn set_edge_selected(&mut self, idx: EdgeIndex, val: bool) { + let e = self.g.edge_weight_mut(idx).unwrap(); + let change = ChangeEdge::change_selected(idx, e.selected, val); + e.selected = val; + self.send_changes(Change::edge(change)); } fn deselect_all(&mut self, state: &FrameState) { @@ -341,49 +335,36 @@ impl<'a, N: Clone, E: Clone> GraphView<'a, N, E> { let (selected_nodes, selected_edges) = state.selections.as_ref().unwrap().elements(); - let mut changes = Changes::default(); selected_nodes.iter().for_each(|idx| { - self.g.node_weight_mut(*idx).unwrap().selected = false; + self.set_node_selected(*idx, false); + + // TODO: create new changes self.g.node_weight_mut(*idx).unwrap().selected_child = false; self.g.node_weight_mut(*idx).unwrap().selected_parent = false; - changes.deselect_node(*idx); }); - if !changes.is_empty() { - self.send_changes(changes); - } selected_edges.iter().for_each(|idx| { - self.g.edge_weight_mut(*idx).unwrap().selected = false; + self.set_edge_selected(*idx, false); + + // TODO: create new changes self.g.edge_weight_mut(*idx).unwrap().selected_child = false; self.g.edge_weight_mut(*idx).unwrap().selected_parent = false; }); } fn set_dragged(&mut self, idx: NodeIndex, val: bool) { - self.g.node_weight_mut(idx).unwrap().dragged = val; - - let mut changes = Changes::default(); - changes.set_dragged(idx, val); - self.send_changes(changes); - } - - fn unset_dragged_node(&mut self, state: &FrameState) { - let n_idx = state.dragged.unwrap(); - let n = self.g.node_weight_mut(n_idx).unwrap(); - n.dragged = false; - - let mut changes = Changes::default(); - changes.set_dragged(n_idx, false); - self.send_changes(changes); + let n = self.g.node_weight_mut(idx).unwrap(); + let change = ChangeNode::change_dragged(idx, n.dragged, val); + n.dragged = val; + self.send_changes(Change::node(change)); } - fn move_node(&mut self, idx: NodeIndex, val: Vec2) { + fn move_node(&mut self, idx: NodeIndex, delta: Vec2) { let n = self.g.node_weight_mut(idx).unwrap(); - n.location += val; - - let mut changes = Changes::default(); - changes.set_location(idx, n.location); - self.send_changes(changes); + let new_loc = n.location + delta; + let change = ChangeNode::change_location(idx, n.location, new_loc); + n.location = new_loc; + self.send_changes(Change::node(change)); } fn precompute_state(&mut self) -> FrameState { @@ -836,7 +817,7 @@ impl<'a, N: Clone, E: Clone> GraphView<'a, N, E> { shapes } - fn send_changes(&self, changes: Changes) { + fn send_changes(&self, changes: Change) { if let Some(sender) = self.changes_sender { sender.send(changes).unwrap(); } diff --git a/src/lib.rs b/src/lib.rs index cbec388..829d296 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -mod changes; +mod change; mod elements; mod frame_state; mod graph_view; @@ -6,7 +6,7 @@ mod metadata; mod selections; mod settings; -pub use self::changes::{Changes, ChangesNode}; +pub use self::change::{Change, ChangeEdge, ChangeNode}; pub use self::elements::{Edge, Node}; pub use self::graph_view::GraphView; pub use self::settings::{SettingsInteraction, SettingsNavigation, SettingsStyle};