Skip to content

Commit

Permalink
Add-delete-nodes-and-edges (#183)
Browse files Browse the repository at this point in the history
* Simplifies usage of the lib by removing bind method from public scope
* methods of Graph for add and delete nodes and edges which perform all
required internal actions for graph data to be consistent

solves #176, #135
  • Loading branch information
blitzarx1 authored Feb 25, 2024
1 parent 5ee784e commit 60f9596
Show file tree
Hide file tree
Showing 8 changed files with 261 additions and 87 deletions.
10 changes: 10 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

93 changes: 12 additions & 81 deletions examples/configurable/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@ use crossbeam::channel::{unbounded, Receiver, Sender};
use eframe::{run_native, App, CreationContext};
use egui::{CollapsingHeader, Context, Pos2, ScrollArea, Slider, Ui};
use egui_graphs::events::Event;
use egui_graphs::{to_graph, DefaultEdgeShape, DefaultNodeShape, Edge, Graph, GraphView, Node};
use egui_graphs::{to_graph, DefaultEdgeShape, DefaultNodeShape, Graph, GraphView};
use fdg_sim::glam::Vec3;
use fdg_sim::{ForceGraph, ForceGraphHelper, Simulation, SimulationParameters};
use petgraph::stable_graph::{DefaultIx, EdgeIndex, NodeIndex, StableGraph};
use petgraph::visit::EdgeRef;
use petgraph::Directed;
use rand::Rng;
use settings::{SettingsGraph, SettingsInteraction, SettingsNavigation, SettingsStyle};
Expand Down Expand Up @@ -200,7 +199,7 @@ impl ConfigurableApp {
}

fn random_node_idx(&self) -> Option<NodeIndex> {
let nodes_cnt = self.g.g.node_count();
let nodes_cnt = self.g.node_count();
if nodes_cnt == 0 {
return None;
}
Expand All @@ -210,7 +209,7 @@ impl ConfigurableApp {
}

fn random_edge_idx(&self) -> Option<EdgeIndex> {
let edges_cnt = self.g.g.edge_count();
let edges_cnt = self.g.edge_count();
if edges_cnt == 0 {
return None;
}
Expand All @@ -230,7 +229,7 @@ impl ConfigurableApp {
return;
}

let random_n = self.g.g.node_weight(random_n_idx.unwrap()).unwrap();
let random_n = self.g.node(random_n_idx.unwrap()).unwrap();

// location of new node is in surrounging of random existing node
let mut rng = rand::thread_rng();
Expand All @@ -239,31 +238,20 @@ impl ConfigurableApp {
random_n.location().y + 10. + rng.gen_range(0. ..50.),
);

let idx = self.g.g.add_node(Node::new(()));
self.g.g[idx].bind(idx, location);

let n = self.g.g.node_weight_mut(idx).unwrap();
let idx_string = idx.index().to_string();
n.set_label(idx_string);
let idx = self.g.add_node_with_location((), location);

let mut sim_node = fdg_sim::Node::new(idx.index().to_string().as_str(), ());
sim_node.location = Vec3::new(location.x, location.y, 0.);
self.sim.get_graph_mut().add_node(sim_node);
}

fn remove_node(&mut self, idx: NodeIndex) {
// before removing nodes we need to remove all edges connected to it
let neighbors = self.g.g.neighbors_undirected(idx).collect::<Vec<_>>();
for n in &neighbors {
self.remove_edges(idx, *n);
self.remove_edges(*n, idx);
}
self.g.remove_node(idx);

self.g.g.remove_node(idx).unwrap();
self.sim.get_graph_mut().remove_node(idx).unwrap();

// update edges count
self.settings_graph.count_edge = self.g.g.edge_count();
self.settings_graph.count_edge = self.g.edge_count();
}

fn add_random_edge(&mut self) {
Expand All @@ -274,11 +262,8 @@ impl ConfigurableApp {
}

fn add_edge(&mut self, start: NodeIndex, end: NodeIndex) {
let order = self.g.g.edges_connecting(start, end).count();
let idx = self.g.g.add_edge(start, end, Edge::new(()));
let e = self.g.g.edge_weight_mut(idx).unwrap();
e.bind(idx, order);
e.set_label(e.id().index().to_string());
self.g.add_edge(start, end, ());

self.sim.get_graph_mut().add_edge(start, end, 1.);
}

Expand All @@ -287,71 +272,17 @@ impl ConfigurableApp {
if random_e_idx.is_none() {
return;
}
let endpoints = self.g.g.edge_endpoints(random_e_idx.unwrap()).unwrap();
let endpoints = self.g.edge_endpoints(random_e_idx.unwrap()).unwrap();

self.remove_edge(endpoints.0, endpoints.1);
}

fn remove_edge(&mut self, start: NodeIndex, end: NodeIndex) {
let g_idx = self.g.g.find_edge(start, end);
if g_idx.is_none() {
return;
}

let order = self.g.g.edge_weight(g_idx.unwrap()).unwrap().order();

self.g.g.remove_edge(g_idx.unwrap()).unwrap();
let (g_idx, _) = self.g.edges_connecting(start, end).next().unwrap();
self.g.remove_edge(g_idx);

let sim_idx = self.sim.get_graph_mut().find_edge(start, end).unwrap();
self.sim.get_graph_mut().remove_edge(sim_idx).unwrap();

// update order of the edges
let siblings = self
.g
.g
.edges_connecting(start, end)
.map(|edge_ref| edge_ref.id())
.collect::<Vec<_>>();

for idx in &siblings {
let sibling_order = self.g.g.edge_weight(*idx).unwrap().order();
if sibling_order < order {
return;
}
self.g
.g
.edge_weight_mut(*idx)
.unwrap()
.set_order(sibling_order - 1);
}
}

/// Removes all edges between two nodes
fn remove_edges(&mut self, start: NodeIndex, end: NodeIndex) {
let g_idxs = self
.g
.g
.edges_connecting(start, end)
.map(|e| e.id())
.collect::<Vec<_>>();
if g_idxs.is_empty() {
return;
}

g_idxs.iter().for_each(|e| {
self.g.g.remove_edge(*e).unwrap();
});

let sim_idxs = self
.sim
.get_graph()
.edges_connecting(start, end)
.map(|e| e.id())
.collect::<Vec<_>>();

sim_idxs.iter().for_each(|e| {
self.sim.get_graph_mut().remove_edge(*e).unwrap();
});
}

fn draw_section_app(&mut self, ui: &mut Ui) {
Expand Down
12 changes: 12 additions & 0 deletions examples/window/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "window"
version = "0.1.0"
authors = ["Dmitrii Samsonov <[email protected]>"]
license = "MIT"
edition = "2021"

[dependencies]
egui_graphs = { path = "../../" }
egui = "0.26.0"
eframe = "0.26.0"
petgraph = "0.6"
54 changes: 54 additions & 0 deletions examples/window/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
use eframe::{run_native, App, CreationContext};
use egui::{Context, Window};
use egui_graphs::{DefaultEdgeShape, DefaultNodeShape, Graph, GraphView};
use petgraph::stable_graph::StableGraph;

pub struct WindowApp {
g: Graph<(), ()>,
}

impl WindowApp {
fn new(_: &CreationContext<'_>) -> Self {
let g = generate_graph();
Self { g: Graph::from(&g) }
}
}

impl App for WindowApp {
fn update(&mut self, ctx: &Context, _: &mut eframe::Frame) {
Window::new("graph").show(ctx, |ui| {
ui.add(&mut GraphView::<
_,
_,
_,
_,
DefaultNodeShape,
DefaultEdgeShape,
>::new(&mut self.g));
});
}
}

fn generate_graph() -> StableGraph<(), ()> {
let mut g = StableGraph::new();

let a = g.add_node(());
let b = g.add_node(());
let c = g.add_node(());

g.add_edge(a, b, ());
g.add_edge(b, c, ());
g.add_edge(c, a, ());

g
}

fn main() {
let native_options = eframe::NativeOptions::default();
run_native(
"egui_graphs_window_demo",
native_options,
Box::new(|cc| Box::new(WindowApp::new(cc))),
)
.unwrap();
}
2 changes: 1 addition & 1 deletion src/draw/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ mod displays_default;
mod drawer;

pub use displays::{DisplayEdge, DisplayNode};
pub use drawer::{DrawContext, Drawer};
pub use displays_default::DefaultEdgeShape;
pub use displays_default::DefaultNodeShape;
pub use drawer::{DrawContext, Drawer};
2 changes: 1 addition & 1 deletion src/elements/edge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ impl<
}

/// Binds edge to the actual node ends and fixes its index in the set of duplicate edges.
pub fn bind(&mut self, idx: EdgeIndex<Ix>, order: usize) {
pub(crate) fn bind(&mut self, idx: EdgeIndex<Ix>, order: usize) {
self.id = Some(idx);
self.props.order = order;
}
Expand Down
10 changes: 7 additions & 3 deletions src/elements/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ where
Ix: IndexType,
D: DisplayNode<N, E, Ty, Ix>,
{
/// Creates a new node with default properties
pub fn new(payload: N) -> Self {
let props = NodeProps {
payload,
Expand All @@ -86,6 +87,11 @@ where
dragged: bool::default(),
};

Node::new_with_props(props)
}

/// Creates a new node with custom properties
pub fn new_with_props(props: NodeProps<N>) -> Self {
let display = D::from(props.clone());
Self {
props,
Expand All @@ -108,14 +114,12 @@ where
&mut self.display
}

/// TODO: rethink this
/// Binds node to the actual node and position in the graph.
pub fn bind(&mut self, id: NodeIndex<Ix>, location: Pos2) {
pub(crate) fn bind(&mut self, id: NodeIndex<Ix>, location: Pos2) {
self.id = Some(id);
self.props.location = location;
}

// TODO: handle unbinded node
pub fn id(&self) -> NodeIndex<Ix> {
self.id.unwrap()
}
Expand Down
Loading

0 comments on commit 60f9596

Please sign in to comment.