Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Layouts #218

Merged
merged 15 commits into from
Oct 27, 2024
22 changes: 21 additions & 1 deletion Cargo.lock

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

13 changes: 7 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,21 @@ keywords = ["egui", "ui", "graph", "node-graph"]
categories = ["gui", "visualization"]

[dependencies]
egui = { version = "0.29", default-features = false }
egui = { version = "0.29", default-features = false, features = [
"persistence",
] }
rand = "0.8"
petgraph = { version = "0.6", default-features = false, features = [
"stable_graph",
"matrix_graph",
"serde-1",
] }
serde = { version = "1.0", features = ["derive"] }

crossbeam = { version = "0.8", optional = true }
serde = { version = "1.0", features = ["derive"], optional = true }
serde_json = { version = "1.0", optional = true }

[features]
egui_persistence = ["dep:serde"]
events = ["dep:serde", "dep:serde_json", "dep:crossbeam"]
serde = ["dep:serde", "egui/serde", "petgraph/serde", "petgraph/serde-1"]
events = ["dep:crossbeam"]

[workspace]
members = ["examples/*"]
Expand Down
44 changes: 22 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ The project implements a Widget for the egui framework, enabling easy visualizat
- [x] Style configuration via egui context styles;
- [x] Dark/Light theme support via egui context styles;
- [x] Events reporting to extend the graph functionality by the user handling them;
- [ ] Layots and custom layout mechanism;
- [x] Layots and custom layout mechanism;

## Status
The project is on track for a stable release v1.0.0. For the moment, breaking releases are very possible.
Expand All @@ -25,19 +25,13 @@ Please use master branch for the latest updates.

Check the [demo example](https://github.com/blitzarx1/egui_graphs/tree/master/examples/demo) for the comprehensive overview of the widget possibilities.

## Features
### Events
Can be enabled with `events` feature. Events describe a change made in graph whether it changed zoom level or node dragging.
## Layouts
In addition to the basic graph display functionality, the project provides a layout mechanism to arrange the nodes in the graph. The `Layout` trait can be implemented by the library user allowing for custom layouts. The following layouts are coming from the box:
- [x] Random layout;
- [x] Hierarchical layout;
- [ ] Force-directed layout; (coming soon)

Combining this feature with custom node draw function allows to implement custom node behavior and drawing according to the events happening.

## Egui crates features support
### Persistence
To use egui `persistence` feature you need to enable `egui_persistence` feature of this crate. For example:
```toml
egui_graphs = { version = "0.22", features = ["egui_persistence"]}
egui = {version="0.29", features = ["persistence"]}
```
Check the [layouts example](https://github.com/blitzarx1/egui_graphs/blob/master/examples/layouts/src/main.rs).

## Examples
### Basic setup example
Expand All @@ -54,17 +48,18 @@ pub struct BasicApp {
Next, implement the `new()` function for the `BasicApp` struct.
```rust
impl BasicApp {
fn new(_: &CreationContext<'_>) -> Self {
Self { g: generate_graph() }
fn new(_: &eframe::CreationContext<'_>) -> Self {
let g = generate_graph();
Self { g: egui_graphs::Graph::from(&g) }
}
}
```

#### Step 3: Generating the graph.
Create a helper function called `generate_graph()`. In this example, we create three nodes with and three edges connecting them in a triangular pattern.
Create a helper function called `generate_graph()`. In this example, we create three nodes and three edges.
```rust
fn generate_graph() -> egui_graphs::Graph {
let mut g = petgraph::stable_graph::StableGraph::new();
fn generate_graph() -> petgraph::StableGraph<(), ()> {
let mut g = petgraph::StableGraph::new();

let a = g.add_node(());
let b = g.add_node(());
Expand All @@ -74,7 +69,7 @@ fn generate_graph() -> egui_graphs::Graph {
g.add_edge(b, c, ());
g.add_edge(c, a, ());

Graph::from(&g)
g
}
```

Expand All @@ -91,13 +86,12 @@ impl eframe::App for BasicApp {
```

#### Step 5: Running the application.
Finally, run the application using the `eframe::run_native()` function with the specified native options and the `BasicApp`.
Finally, run the application using the `eframe::run_native()` function.
```rust
fn main() {
let native_options = eframe::NativeOptions::default();
eframe::run_native(
"egui_graphs_basic_demo",
native_options,
eframe::NativeOptions::default(),
Box::new(|cc| Ok(Box::new(BasicApp::new(cc)))),
)
.unwrap();
Expand All @@ -106,3 +100,9 @@ fn main() {

![Screenshot 2023-10-14 at 23 49 49](https://github.com/blitzarx1/egui_graphs/assets/32969427/584b78de-bca3-421b-b003-9321fd3e1b13)
You can further customize the appearance and behavior of your graph by modifying the settings or adding more nodes and edges as needed.

## Features
### Events
Can be enabled with `events` feature. Events describe a change made in graph whether it changed zoom level or node dragging.

Combining this feature with custom node draw function allows to implement custom node behavior and drawing according to the events happening.
6 changes: 3 additions & 3 deletions examples/animated_nodes/src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ impl NodeShapeAnimated {
impl<N: Clone + IsClockwise> From<NodeProps<N>> for NodeShapeAnimated {
fn from(node_props: NodeProps<N>) -> Self {
Self {
label: node_props.label,
loc: node_props.location,
label: node_props.label.clone(),
loc: node_props.location(),
dragged: node_props.dragged,
clockwise: node_props.payload.get_is_clockwise(),

Expand Down Expand Up @@ -145,7 +145,7 @@ impl<N: Clone + IsClockwise, E: Clone, Ty: EdgeType, Ix: IndexType> DisplayNode<

fn update(&mut self, state: &NodeProps<N>) {
self.label = state.label.clone();
self.loc = state.location;
self.loc = state.location();
self.dragged = state.dragged;
self.clockwise = state.payload.get_is_clockwise();
}
Expand Down
18 changes: 8 additions & 10 deletions examples/basic/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use eframe::{run_native, App, CreationContext};
use eframe::{run_native, App, CreationContext, NativeOptions};
use egui::Context;
use egui_graphs::{Graph, GraphView};
use egui_graphs::{DefaultGraphView, Graph};
use petgraph::stable_graph::StableGraph;

pub struct BasicApp {
Expand All @@ -9,21 +9,20 @@ pub struct BasicApp {

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

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));
});
}
}

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

let a = g.add_node(());
Expand All @@ -34,14 +33,13 @@ fn generate_graph() -> Graph {
g.add_edge(b, c, ());
g.add_edge(c, a, ());

Graph::from(&g)
g
}

fn main() {
let native_options = eframe::NativeOptions::default();
run_native(
"egui_graphs_basic_demo",
native_options,
NativeOptions::default(),
Box::new(|cc| Ok(Box::new(BasicApp::new(cc)))),
)
.unwrap();
Expand Down
12 changes: 12 additions & 0 deletions examples/basic_custom/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "basic_custom"
version = "0.1.0"
authors = ["Dmitrii Samsonov <blitzarx1@gmail.com>"]
license = "MIT"
edition = "2021"

[dependencies]
egui_graphs = { path = "../../" }
egui = "0.29"
eframe = "0.29"
petgraph = "0.6"
7 changes: 7 additions & 0 deletions examples/basic_custom/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Basic
Basic example which demonstrates the usage of `GraphView` widget with helper functions.

## run
```bash
cargo run --release -p basic_custom
```
49 changes: 49 additions & 0 deletions examples/basic_custom/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
use eframe::{run_native, App, CreationContext, NativeOptions};
use egui::{Context, Pos2};
use egui_graphs::{DefaultGraphView, Graph, SettingsStyle};
use petgraph::stable_graph::StableGraph;

pub struct BasicCustomApp {
g: Graph,
}

impl BasicCustomApp {
fn new(_: &CreationContext<'_>) -> Self {
let mut g = Graph::new(StableGraph::default());

let positions = vec![Pos2::new(0., 0.), Pos2::new(50., 0.), Pos2::new(0., 50.)];
let mut idxs = Vec::with_capacity(positions.len());
for position in positions {
let idx = g.add_node_with_label_and_location((), position.to_string(), position);

idxs.push(idx);
}

g.add_edge(idxs[0], idxs[1], ());
g.add_edge(idxs[1], idxs[2], ());
g.add_edge(idxs[2], idxs[0], ());

Self { g }
}
}

impl App for BasicCustomApp {
fn update(&mut self, ctx: &Context, _: &mut eframe::Frame) {
egui::CentralPanel::default().show(ctx, |ui| {
ui.add(
&mut DefaultGraphView::new(&mut self.g)
.with_styles(&SettingsStyle::default().with_labels_always(true)),
);
});
}
}

fn main() {
let native_options = NativeOptions::default();
run_native(
"egui_graphs_basic_custom_demo",
native_options,
Box::new(|cc| Ok(Box::new(BasicCustomApp::new(cc)))),
)
.unwrap();
}
28 changes: 5 additions & 23 deletions examples/demo/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ 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::{random_graph, DefaultGraphView, Edge, Graph, Node};
use fdg::fruchterman_reingold::{FruchtermanReingold, FruchtermanReingoldConfiguration};
use fdg::nalgebra::{Const, OPoint};
use fdg::{Force, ForceGraph};
use petgraph::stable_graph::{DefaultIx, EdgeIndex, NodeIndex, StableGraph};
use petgraph::stable_graph::{DefaultIx, EdgeIndex, NodeIndex};
use petgraph::Directed;
use rand::Rng;

Expand Down Expand Up @@ -50,7 +50,7 @@ impl DemoApp {
let settings_graph = settings::SettingsGraph::default();
let settings_simulation = settings::SettingsSimulation::default();

let mut g = generate_random_graph(settings_graph.count_node, settings_graph.count_edge);
let mut g = 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);
Expand Down Expand Up @@ -447,7 +447,7 @@ impl DemoApp {
let settings_graph = settings::SettingsGraph::default();
let settings_simulation = settings::SettingsSimulation::default();

let mut g = generate_random_graph(settings_graph.count_node, settings_graph.count_edge);
let mut g = 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);
Expand Down Expand Up @@ -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)
Expand All @@ -526,24 +526,6 @@ impl App for DemoApp {
}
}

fn generate_random_graph(node_count: usize, edge_count: usize) -> Graph {
let mut rng = rand::thread_rng();
let mut graph = StableGraph::new();

for _ in 0..node_count {
graph.add_node(());
}

for _ in 0..edge_count {
let source = rng.gen_range(0..node_count);
let target = rng.gen_range(0..node_count);

graph.add_edge(NodeIndex::new(source), NodeIndex::new(target), ());
}

to_graph(&graph)
}

fn init_force(settings: &settings::SettingsSimulation) -> FruchtermanReingold<f32, 2> {
FruchtermanReingold {
conf: FruchtermanReingoldConfiguration {
Expand Down
Loading