Repository: blitzarx1/egui_graphs (language: rust)

Issue:
Large graphs can crash egui
The error is: `epaint-0.28.1/src/text/font.rs:92:9: assertion failed: scale_in_pixels > 0.0`. I am running into the error frequently with large force-directed graphs. Unfortunately, the nature of force-directed graphs means finding a minimal reproducible example is difficult. The error-message seems to provide enough of a hint though. I assume `scale_in_pixels` is getting rounded to `0` somewhere.


Discussion / hints:

Fix patch:
diff --git a/Cargo.lock b/Cargo.lock
index ee62695..b124ecd 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -650,6 +650,16 @@ dependencies = [
  "petgraph",
 ]
 
+[[package]]
+name = "basic_custom"
+version = "0.1.0"
+dependencies = [
+ "eframe",
+ "egui 0.29.0",
+ "egui_graphs 0.22.0",
+ "petgraph",
+]
+
 [[package]]
 name = "bevy"
 version = "0.14.2"
@@ -2226,6 +2236,7 @@ dependencies = [
  "epaint 0.29.0",
  "log",
  "nohash-hasher",
+ "ron",
  "serde",
 ]
 
@@ -2303,7 +2314,6 @@ dependencies = [
  "petgraph",
  "rand",
  "serde",
- "serde_json",
 ]
 
 [[package]]
@@ -3338,6 +3348,16 @@ dependencies = [
  "petgraph",
 ]
 
+[[package]]
+name = "layouts"
+version = "0.1.0"
+dependencies = [
+ "eframe",
+ "egui 0.29.0",
+ "egui_graphs 0.22.0",
+ "petgraph",
+]
+
 [[package]]
 name = "lazy_static"
 version = "1.5.0"
diff --git a/Cargo.toml b/Cargo.toml
index 5f5bbf0..942d442 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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/*"]
diff --git a/README.md b/README.md
index 85ad534..27b6608 100644
--- a/README.md
+++ b/README.md
@@ -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.
@@ -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
@@ -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(());
@@ -74,7 +69,7 @@ fn generate_graph() -> egui_graphs::Graph {
     g.add_edge(b, c, ());
     g.add_edge(c, a, ());
 
-    Graph::from(&g)
+    g
 }
 ```
 
@@ -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();
@@ -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.
diff --git a/examples/animated_nodes/src/node.rs b/examples/animated_nodes/src/node.rs
index e38cd18..c8d93e4 100644
--- a/examples/animated_nodes/src/node.rs
+++ b/examples/animated_nodes/src/node.rs
@@ -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(),
 
@@ -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();
     }
diff --git a/examples/basic/src/main.rs b/examples/basic/src/main.rs
index 183a134..79ffe84 100644
--- a/examples/basic/src/main.rs
+++ b/examples/basic/src/main.rs
@@ -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 {
@@ -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(());
@@ -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();
diff --git a/examples/basic_custom/Cargo.toml b/examples/basic_custom/Cargo.toml
new file mode 100644
index 0000000..9dbdae3
--- /dev/null
+++ b/examples/basic_custom/Cargo.toml
@@ -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"
diff --git a/examples/basic_custom/README.md b/examples/basic_custom/README.md
new file mode 100644
index 0000000..48a17ed
--- /dev/null
+++ b/examples/basic_custom/README.md
@@ -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
+```
\ No newline at end of file
diff --git a/examples/basic_custom/src/main.rs b/examples/basic_custom/src/main.rs
new file mode 100644
index 0000000..3a4f260
--- /dev/null
+++ b/examples/basic_custom/src/main.rs
@@ -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();
+}
diff --git a/examples/demo/src/main.rs b/examples/demo/src/main.rs
index 6f4a4d4..49dc394 100644
--- a/examples/demo/src/main.rs
+++ b/examples/demo/src/main.rs
@@ -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;
 
@@ -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);
@@ -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);
@@ -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)
@@ -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 {
diff --git a/examples/flex_nodes/src/main.rs b/examples/flex_nodes/src/main.rs
index 1cd7511..56d8541 100644
--- a/examples/flex_nodes/src/main.rs
+++ b/examples/flex_nodes/src/main.rs
@@ -1,6 +1,6 @@
 use eframe::{run_native, App, CreationContext};
 use egui::{CentralPanel, Context, SidePanel, TextEdit};
-use egui_graphs::{DefaultEdgeShape, Graph, GraphView, SettingsInteraction, SettingsNavigation};
+use egui_graphs::{Graph, GraphView, SettingsInteraction, SettingsNavigation};
 use node::NodeShapeFlex;
 use petgraph::{
     stable_graph::{DefaultIx, EdgeIndex, NodeIndex, StableGraph},
@@ -10,7 +10,7 @@ use petgraph::{
 mod node;
 
 pub struct FlexNodesApp {
-    g: Graph<(), (), Directed, DefaultIx, NodeShapeFlex, DefaultEdgeShape>,
+    g: Graph<(), (), Directed, DefaultIx, NodeShapeFlex>,
     label_input: String,
     selected_node: Option<NodeIndex>,
     selected_edge: Option<EdgeIndex>,
@@ -58,19 +58,18 @@ impl FlexNodesApp {
             }
         });
         CentralPanel::default().show(ctx, |ui| {
-            let widget =
-                &mut GraphView::<_, _, _, _, NodeShapeFlex, DefaultEdgeShape>::new(&mut self.g)
-                    .with_interactions(
-                        &SettingsInteraction::default()
-                            .with_dragging_enabled(true)
-                            .with_node_selection_enabled(true)
-                            .with_edge_selection_enabled(true),
-                    )
-                    .with_navigations(
-                        &SettingsNavigation::default()
-                            .with_fit_to_screen_enabled(false)
-                            .with_zoom_and_pan_enabled(true),
-                    );
+            let widget = &mut GraphView::<_, _, _, _, _, _>::new(&mut self.g)
+                .with_interactions(
+                    &SettingsInteraction::default()
+                        .with_dragging_enabled(true)
+                        .with_node_selection_enabled(true)
+                        .with_edge_selection_enabled(true),
+                )
+                .with_navigations(
+                    &SettingsNavigation::default()
+                        .with_fit_to_screen_enabled(false)
+                        .with_zoom_and_pan_enabled(true),
+                );
             ui.add(widget);
         });
     }
diff --git a/examples/flex_nodes/src/node.rs b/examples/flex_nodes/src/node.rs
index d52e9a7..3470640 100644
--- a/examples/flex_nodes/src/node.rs
+++ b/examples/flex_nodes/src/node.rs
@@ -14,8 +14,8 @@ pub struct NodeShapeFlex {
 impl<N: Clone> From<NodeProps<N>> for NodeShapeFlex {
     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(),
 
             size_x: 0.,
             size_y: 0.,
@@ -67,7 +67,7 @@ impl<N: Clone, E: Clone, Ty: EdgeType, Ix: IndexType> DisplayNode<N, E, Ty, Ix>
 
     fn update(&mut self, state: &NodeProps<N>) {
         self.label = state.label.clone();
-        self.loc = state.location;
+        self.loc = state.location();
     }
 }
 
diff --git a/examples/interactive/src/main.rs b/examples/interactive/src/main.rs
index 8dbf120..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::{Graph, GraphView, SettingsInteraction, SettingsStyle};
+use egui_graphs::{DefaultGraphView, Graph, SettingsInteraction, SettingsStyle};
 use petgraph::stable_graph::StableGraph;
 
 pub struct InteractiveApp {
@@ -9,7 +9,7 @@ pub struct InteractiveApp {
 
 impl InteractiveApp {
     fn new(_: &CreationContext<'_>) -> Self {
-        let g = generate_graph();
+        let g: Graph = generate_graph();
         Self { g }
     }
 }
@@ -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::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 acd8666..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::{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::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/layouts/Cargo.toml b/examples/layouts/Cargo.toml
new file mode 100644
index 0000000..ffa91b3
--- /dev/null
+++ b/examples/layouts/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "layouts"
+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"
diff --git a/examples/layouts/README.md b/examples/layouts/README.md
new file mode 100644
index 0000000..17210be
--- /dev/null
+++ b/examples/layouts/README.md
@@ -0,0 +1,7 @@
+# Layouts
+Example of usage of different lyouts implemented by `egui_graphs`.
+
+## run
+```bash
+cargo run --release -p layouts
+```
\ No newline at end of file
diff --git a/examples/layouts/src/main.rs b/examples/layouts/src/main.rs
new file mode 100644
index 0000000..c4fa0a0
--- /dev/null
+++ b/examples/layouts/src/main.rs
@@ -0,0 +1,154 @@
+use eframe::{run_native, App, CreationContext, NativeOptions};
+use egui::Context;
+use egui_graphs::{
+    random_graph, DefaultEdgeShape, DefaultNodeShape, Graph, GraphView, LayoutHierarchical,
+    LayoutRandom, LayoutStateHierarchical, LayoutStateRandom,
+};
+use petgraph::{stable_graph::DefaultIx, Directed};
+
+#[derive(Clone, PartialEq)]
+enum Layout {
+    Hierarchical,
+    Random,
+}
+
+#[derive(Clone)]
+struct Settings {
+    layout: Layout,
+    num_nodes: usize,
+    num_edges: usize,
+}
+pub struct LayoutsApp {
+    settings: Settings,
+    g: Graph,
+}
+
+impl LayoutsApp {
+    fn new(_: &CreationContext<'_>) -> Self {
+        let settings = Settings {
+            layout: Layout::Hierarchical,
+            num_nodes: 25,
+            num_edges: 25,
+        };
+        Self {
+            settings: settings.clone(),
+            g: random_graph(settings.num_nodes, settings.num_edges),
+        }
+    }
+
+    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 {
+    fn update(&mut self, ctx: &Context, _: &mut eframe::Frame) {
+        egui::SidePanel::right("right_panel")
+            .min_width(250.)
+            .show(ctx, |ui| {
+                ui.vertical(|ui| {
+                    ui.horizontal(|ui| {
+                        ui.label("Layout");
+                        if ui
+                            .radio_value(
+                                &mut self.settings.layout,
+                                Layout::Hierarchical,
+                                "Hierarchical",
+                            )
+                            .changed()
+                        {
+                            self.clear_cache(ui);
+                        };
+                        if ui
+                            .radio_value(&mut self.settings.layout, Layout::Random, "Random")
+                            .changed()
+                        {
+                            self.clear_cache(ui);
+                        };
+                    });
+                    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);
+                        };
+                    });
+                    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);
+                        };
+                    });
+                });
+            });
+        egui::CentralPanel::default().show(ctx, |ui| {
+            match self.settings.layout {
+                Layout::Hierarchical => {
+                    ui.add(&mut GraphView::<
+                        _,
+                        _,
+                        _,
+                        _,
+                        _,
+                        _,
+                        LayoutStateHierarchical,
+                        LayoutHierarchical,
+                    >::new(&mut self.g));
+                }
+                Layout::Random => {
+                    ui.add(&mut GraphView::<
+                        _,
+                        _,
+                        _,
+                        _,
+                        _,
+                        _,
+                        LayoutStateRandom,
+                        LayoutRandom,
+                    >::new(&mut self.g));
+                }
+            };
+        });
+    }
+}
+
+fn main() {
+    run_native(
+        "egui_graphs_layouts_demo",
+        NativeOptions::default(),
+        Box::new(|cc| Ok(Box::new(LayoutsApp::new(cc)))),
+    )
+    .unwrap();
+}
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 1196710..d4c10a1 100644
--- a/examples/undirected/src/main.rs
+++ b/examples/undirected/src/main.rs
@@ -1,13 +1,13 @@
 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::{DefaultIx, StableGraph, StableUnGraph},
+    stable_graph::{StableGraph, StableUnGraph},
     Undirected,
 };
 
 pub struct UndirectedApp {
-    g: Graph<(), (), Undirected, DefaultIx>,
+    g: Graph<(), (), Undirected>,
 }
 
 impl UndirectedApp {
@@ -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/wasm_custom_draw/src/node.rs b/examples/wasm_custom_draw/src/node.rs
index fb3d337..636e4a1 100644
--- a/examples/wasm_custom_draw/src/node.rs
+++ b/examples/wasm_custom_draw/src/node.rs
@@ -24,8 +24,8 @@ pub struct NodeShapeAnimated {
 impl<N: Clone> 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,
 
             angle_rad: Default::default(),
@@ -120,7 +120,7 @@ impl<N: Clone, E: Clone, Ty: EdgeType, Ix: IndexType> DisplayNode<N, E, Ty, Ix>
 
     fn update(&mut self, state: &NodeProps<N>) {
         self.label = state.label.clone();
-        self.loc = state.location;
+        self.loc = state.location();
         self.dragged = state.dragged;
     }
 }
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/displays_default/edge.rs b/src/draw/displays_default/edge.rs
index 65ee1c9..82e95fc 100644
--- a/src/draw/displays_default/edge.rs
+++ b/src/draw/displays_default/edge.rs
@@ -6,11 +6,10 @@ use egui::{
 };
 use petgraph::{stable_graph::IndexType, EdgeType};
 
-use crate::{draw::DrawContext, elements::EdgeProps, DisplayEdge, DisplayNode, Node};
+use crate::{draw::DrawContext, elements::EdgeProps, node_size, DisplayEdge, DisplayNode, Node};
 
 use super::edge_shape_builder::{EdgeShapeBuilder, TipProps};
 
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
 #[derive(Clone, Debug)]
 pub struct DefaultEdgeShape {
     pub order: usize,
@@ -275,17 +274,6 @@ impl DefaultEdgeShape {
     }
 }
 
-// TODO: export this func as common drawing func
-fn node_size<N: Clone, E: Clone, Ty: EdgeType, Ix: IndexType, D: DisplayNode<N, E, Ty, Ix>>(
-    node: &Node<N, E, Ty, Ix, D>,
-    dir: Vec2,
-) -> f32 {
-    let connector_left = node.display().closest_boundary_point(dir);
-    let connector_right = node.display().closest_boundary_point(-dir);
-
-    ((connector_right.to_vec2() - connector_left.to_vec2()) / 2.).length()
-}
-
 /// Returns the distance from line segment `a``b` to point `c`.
 /// Adapted from <https://stackoverflow.com/questions/1073336/circle-line-segment-collision-detection-algorithm>
 fn distance_segment_to_point(a: Pos2, b: Pos2, point: Pos2) -> f32 {
diff --git a/src/draw/displays_default/node.rs b/src/draw/displays_default/node.rs
index b354509..b6c032a 100644
--- a/src/draw/displays_default/node.rs
+++ b/src/draw/displays_default/node.rs
@@ -1,6 +1,6 @@
 use egui::{
     epaint::{CircleShape, TextShape},
-    FontFamily, FontId, Pos2, Shape, Stroke, Vec2,
+    Color32, FontFamily, FontId, Pos2, Shape, Stroke, Vec2,
 };
 use petgraph::{stable_graph::IndexType, EdgeType};
 
@@ -9,27 +9,28 @@ use crate::{draw::drawer::DrawContext, DisplayNode, NodeProps};
 /// This is the default node shape which is used to display nodes in the graph.
 ///
 /// You can use this implementation as an example for implementing your own custom node shapes.
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
 #[derive(Clone, Debug)]
 pub struct DefaultNodeShape {
     pub pos: Pos2,
 
     pub selected: bool,
     pub dragged: bool,
+    pub color: Option<Color32>,
 
     pub label_text: String,
 
-    /// Shape defined property
+    /// Shape dependent property
     pub radius: f32,
 }
 
 impl<N: Clone> From<NodeProps<N>> for DefaultNodeShape {
     fn from(node_props: NodeProps<N>) -> 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(),
+            color: node_props.color(),
 
             radius: 5.0,
         }
@@ -57,7 +58,12 @@ impl<N: Clone, E: Clone, Ty: EdgeType, Ix: IndexType> DisplayNode<N, E, Ty, Ix>
         } else {
             ctx.ctx.style().visuals.widgets.inactive
         };
-        let color = style.fg_stroke.color;
+
+        let color = if let Some(c) = self.color {
+            c
+        } else {
+            style.fg_stroke.color
+        };
 
         let circle_center = ctx.meta.canvas_to_screen_pos(self.pos);
         let circle_radius = ctx.meta.canvas_to_screen_size(self.radius);
@@ -95,11 +101,11 @@ impl<N: Clone, E: Clone, Ty: EdgeType, Ix: IndexType> DisplayNode<N, E, Ty, Ix>
     }
 
     fn update(&mut self, state: &NodeProps<N>) {
-        self.pos = state.location;
-        self.pos = state.location;
+        self.pos = state.location();
         self.selected = state.selected;
         self.dragged = state.dragged;
         self.label_text = state.label.to_string();
+        self.color = state.color();
     }
 }
 
diff --git a/src/draw/drawer.rs b/src/draw/drawer.rs
index 18e028b..f7be370 100644
--- a/src/draw/drawer.rs
+++ b/src/draw/drawer.rs
@@ -4,7 +4,11 @@ use egui::{Context, Painter, Shape};
 use petgraph::graph::IndexType;
 use petgraph::EdgeType;
 
-use crate::{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>
+pub struct Drawer<'a, N, E, Ty, Ix, Nd, Ed, S, L>
 where
     N: Clone,
     E: Clone,
@@ -25,14 +29,17 @@ where
     Ix: IndexType,
     Nd: DisplayNode<N, E, Ty, Ix>,
     Ed: DisplayEdge<N, E, Ty, Ix, Nd>,
+    S: LayoutState,
+    L: Layout<S>,
 {
     ctx: &'a DrawContext<'a>,
     g: &'a mut Graph<N, E, Ty, Ix, Nd, Ed>,
-    postponed: Vec<Shape>,
-    _marker: PhantomData<(Nd, Ed)>,
+    delayed: Vec<Shape>,
+
+    _marker: PhantomData<(Nd, Ed, L, S)>,
 }
 
-impl<'a, N, E, Ty, Ix, Nd, Ed> Drawer<'a, N, E, Ty, Ix, Nd, Ed>
+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,
@@ -40,12 +47,14 @@ where
     Ix: IndexType,
     Nd: DisplayNode<N, E, Ty, Ix>,
     Ed: DisplayEdge<N, E, Ty, Ix, Nd>,
+    S: LayoutState,
+    L: Layout<S>,
 {
     pub fn new(g: &'a mut Graph<N, E, Ty, Ix, Nd, Ed>, ctx: &'a DrawContext<'a>) -> Self {
         Drawer {
             ctx,
             g,
-            postponed: Vec::new(),
+            delayed: Vec::new(),
             _marker: PhantomData,
         }
     }
@@ -57,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());
         });
     }
@@ -78,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 {
@@ -110,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<E: Clone> {
     pub payload: E,
     pub order: usize,
@@ -18,8 +18,7 @@ pub struct EdgeProps<E: Clone> {
 }
 
 /// 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..ee114b6 100644
--- a/src/elements/node.rs
+++ b/src/elements/node.rs
@@ -1,26 +1,45 @@
 use std::fmt::Debug;
 use std::marker::PhantomData;
 
-use egui::Pos2;
+use egui::{Color32, Pos2};
 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)]
-pub struct NodeProps<N: Clone> {
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub struct NodeProps<N>
+where
+    N: Clone,
+{
     pub payload: N,
-    pub location: Pos2,
     pub label: String,
     pub selected: bool,
     pub dragged: bool,
+
+    color: Option<Color32>,
+    location: Pos2,
+    location_user: Option<Pos2>,
 }
 
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+impl<N> NodeProps<N>
+where
+    N: Clone,
+{
+    pub fn location(&self) -> Pos2 {
+        self.location_user.unwrap_or(self.location)
+    }
+
+    pub fn color(&self) -> Option<Color32> {
+        self.color
+    }
+}
+
+#[derive(Serialize, Deserialize)]
 pub struct Node<N, E, Ty = Directed, Ix = DefaultIx, D = DefaultNodeShape>
 where
     N: Clone,
@@ -83,6 +102,8 @@ where
         let props = NodeProps {
             payload,
             location: Pos2::default(),
+            color: Option::default(),
+            location_user: Option::default(),
             label: String::default(),
             selected: bool::default(),
             dragged: bool::default(),
@@ -132,11 +153,24 @@ where
         &mut self.props.payload
     }
 
+    pub fn color(&self) -> Option<Color32> {
+        self.props.color()
+    }
+
+    pub fn set_color(&mut self, color: Color32) {
+        self.props.color = Some(color);
+    }
+
     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 8673067..93c549f 100644
--- a/src/graph.rs
+++ b/src/graph.rs
@@ -8,6 +8,7 @@ use petgraph::{
     visit::{EdgeRef, IntoEdgeReferences, IntoNodeReferences},
     Direction, EdgeType,
 };
+use serde::{Deserialize, Serialize};
 
 use crate::draw::{DisplayEdge, DisplayNode};
 use crate::{metadata::Metadata, Edge, Node};
@@ -18,44 +19,50 @@ type StableGraphType<N, E, Ty, Ix, Dn, De> =
 
 /// 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<N, E, Ty, Ix> = DefaultNodeShape,
-    De: DisplayEdge<N, E, Ty, Ix, Dn> = DefaultEdgeShape,
-> {
+    N = (),
+    E = (),
+    Ty = Directed,
+    Ix = DefaultIx,
+    Dn = DefaultNodeShape,
+    De = DefaultEdgeShape,
+> where
+    N: Clone,
+    E: Clone,
+    Ty: EdgeType,
+    Ix: IndexType,
+    Dn: DisplayNode<N, E, Ty, Ix>,
+    De: DisplayEdge<N, E, Ty, Ix, Dn>,
+{
     pub g: StableGraphType<N, E, Ty, Ix, Dn, De>,
     selected_nodes: Vec<NodeIndex<Ix>>,
     selected_edges: Vec<EdgeIndex<Ix>>,
     dragged_node: Option<NodeIndex<Ix>>,
 }
 
-impl<
-        N: Clone,
-        E: Clone,
-        Ty: EdgeType,
-        Ix: IndexType,
-        Dn: DisplayNode<N, E, Ty, Ix>,
-        De: DisplayEdge<N, E, Ty, Ix, Dn>,
-    > From<&StableGraph<N, E, Ty, Ix>> for Graph<N, E, Ty, Ix, Dn, De>
+impl<N, E, Ty, Ix, Dn, De> From<&StableGraph<N, E, Ty, Ix>> for Graph<N, E, Ty, Ix, Dn, De>
+where
+    N: Clone,
+    E: Clone,
+    Ty: EdgeType,
+    Ix: IndexType,
+    Dn: DisplayNode<N, E, Ty, Ix>,
+    De: DisplayEdge<N, E, Ty, Ix, Dn>,
 {
     fn from(g: &StableGraph<N, E, Ty, Ix>) -> Self {
         to_graph(g)
     }
 }
 
-impl<
-        N: Clone,
-        E: Clone,
-        Ty: EdgeType,
-        Ix: IndexType,
-        Dn: DisplayNode<N, E, Ty, Ix>,
-        De: DisplayEdge<N, E, Ty, Ix, Dn>,
-    > Graph<N, E, Ty, Ix, Dn, De>
+impl<N, E, Ty, Ix, Dn, De> Graph<N, E, Ty, Ix, Dn, De>
+where
+    N: Clone,
+    E: Clone,
+    Ty: EdgeType,
+    Ix: IndexType,
+    Dn: DisplayNode<N, E, Ty, Ix>,
+    De: DisplayEdge<N, E, Ty, Ix, Dn>,
 {
     pub fn new(g: StableGraphType<N, E, Ty, Ix, Dn, De>) -> Self {
         Self {
diff --git a/src/graph_view.rs b/src/graph_view.rs
index 53faa28..6067d30 100644
--- a/src/graph_view.rs
+++ b/src/graph_view.rs
@@ -1,28 +1,41 @@
 use std::marker::PhantomData;
 
+use crate::{
+    draw::{DefaultEdgeShape, DefaultNodeShape, DrawContext, Drawer},
+    layouts::{self, Layout, LayoutState},
+    metadata::Metadata,
+    settings::{SettingsInteraction, SettingsNavigation, SettingsStyle},
+    DisplayEdge, DisplayNode, Graph,
+};
+
+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,
     PayloadNodeDeselect, PayloadNodeDoubleClick, PayloadNodeDragEnd, PayloadNodeDragStart,
     PayloadNodeMove, PayloadNodeSelect, PayloadPan, PayloadZoom,
 };
-use crate::{
-    draw::Drawer,
-    metadata::Metadata,
-    settings::SettingsNavigation,
-    settings::{SettingsInteraction, SettingsStyle},
-    Graph,
-};
-use crate::{
-    draw::{DefaultEdgeShape, DefaultNodeShape, DrawContext},
-    DisplayEdge, DisplayNode,
-};
 #[cfg(feature = "events")]
 use crossbeam::channel::Sender;
-use egui::{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};
 
 /// Widget for visualizing and interacting with graphs.
 ///
@@ -32,8 +45,8 @@ use petgraph::{stable_graph::NodeIndex, EdgeType};
 /// struct to visualize and interact with the graph. `N` and `E` is arbitrary client data associated with nodes and edges.
 /// You can customize the visualization and interaction behavior using [`SettingsInteraction`], [`SettingsNavigation`] and [`SettingsStyle`] structs.
 ///
-/// When any interaction or node property change occurs, the widget sends [Event] struct to the provided
-/// [Sender<Event>] channel, which can be set via the `with_interactions` method. The [Event] struct contains information about
+/// When any interaction or node property change occurs, the widget sends [`Event`] struct to the provided
+/// [`Sender<Event>`] channel, which can be set via the `with_interactions` method. The [`Event`] struct contains information about
 /// a change that occurred in the graph. Client can use this information to modify external state of his application if needed.
 ///
 /// When the user performs navigation actions (zoom & pan or fit to screen), they do not
@@ -47,6 +60,8 @@ pub struct GraphView<
     Ix = DefaultIx,
     Nd = DefaultNodeShape,
     Ed = DefaultEdgeShape,
+    S = layouts::random::State,
+    L = layouts::random::Random,
 > where
     N: Clone,
     E: Clone,
@@ -54,6 +69,8 @@ pub struct GraphView<
     Ix: IndexType,
     Nd: DisplayNode<N, E, Ty, Ix>,
     Ed: DisplayEdge<N, E, Ty, Ix, Nd>,
+    S: LayoutState,
+    L: Layout<S>,
 {
     g: &'a mut Graph<N, E, Ty, Ix, Nd, Ed>,
 
@@ -64,10 +81,10 @@ pub struct GraphView<
     #[cfg(feature = "events")]
     events_publisher: Option<&'a Sender<Event>>,
 
-    _marker: PhantomData<(Nd, Ed)>,
+    _marker: PhantomData<(Nd, Ed, L, S)>,
 }
 
-impl<'a, N, E, Ty, Ix, Nd, Ed> Widget for &mut GraphView<'a, N, E, Ty, Ix, Nd, Ed>
+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,
@@ -75,41 +92,43 @@ where
     Ix: IndexType,
     Nd: DisplayNode<N, E, Ty, Ix>,
     Ed: DisplayEdge<N, E, Ty, Ix, Nd>,
+    S: LayoutState,
+    L: Layout<S>,
 {
     fn ui(self, ui: &mut Ui) -> Response {
-        let (resp, p) = ui.allocate_painter(ui.available_size(), Sense::click_and_drag());
+        self.sync_layout(ui);
 
-        let mut meta = Metadata::get(ui);
+        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);
 
-        let is_directed = self.g.is_directed();
-        Drawer::<N, E, Ty, Ix, Nd, Ed>::new(
+        Drawer::<N, E, Ty, Ix, Nd, Ed, S, L>::new(
             self.g,
             &DrawContext {
                 ctx: ui.ctx(),
                 painter: &p,
                 meta: &meta,
-                is_directed,
+                is_directed: self.g.is_directed(),
                 style: &self.settings_style,
             },
         )
         .draw();
 
         meta.first_frame = false;
-        meta.store_into_ui(ui);
+        meta.save(ui);
+
         ui.ctx().request_repaint();
 
         resp
     }
 }
 
-impl<'a, N, E, Ty, Ix, Dn, De> GraphView<'a, N, E, Ty, Ix, Dn, De>
+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,
@@ -117,6 +136,8 @@ where
     Ix: IndexType,
     Dn: DisplayNode<N, E, Ty, Ix>,
     De: DisplayEdge<N, E, Ty, Ix, Dn>,
+    S: LayoutState,
+    L: Layout<S>,
 {
     /// Creates a new `GraphView` widget with default navigation and interactions settings.
     /// To customize navigation and interactions use `with_interactions` and `with_navigations` methods.
@@ -153,9 +174,22 @@ where
         self
     }
 
+    /// Clears cached values of layout and metadata.
+    pub fn clear_cache(ui: &mut Ui) {
+        GraphView::<N, E, Ty, Ix, Dn, De, S, L>::reset_metadata(ui);
+        GraphView::<N, E, Ty, Ix, Dn, De, S, L>::reset_layout(ui);
+    }
+
     /// Resets navigation metadata
     pub fn reset_metadata(ui: &mut Ui) {
-        Metadata::default().store_into_ui(ui);
+        Metadata::default().save(ui);
+    }
+
+    /// Resets layout state
+    pub fn reset_layout(ui: &mut Ui) {
+        ui.data_mut(|data| {
+            data.insert_persisted(Id::new(KEY_LAYOUT), S::default());
+        });
     }
 
     #[cfg(feature = "events")]
@@ -165,6 +199,18 @@ where
         self
     }
 
+    fn sync_layout(&mut self, ui: &mut Ui) {
+        ui.data_mut(|data| {
+            let state = data
+                .get_persisted::<S>(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 3488cb3..aefb5b9 100644
--- a/src/helpers.rs
+++ b/src/helpers.rs
@@ -1,5 +1,5 @@
 use crate::{DisplayEdge, DisplayNode, Edge, Graph, Node};
-use egui::Pos2;
+use egui::Vec2;
 use petgraph::{
     graph::IndexType,
     stable_graph::{EdgeIndex, NodeIndex, StableGraph},
@@ -9,32 +9,37 @@ use petgraph::{
 use rand::Rng;
 use std::collections::HashMap;
 
-pub const DEFAULT_SPAWN_SIZE: f32 = 250.;
-
 /// 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<N: Clone, E: Clone, Ty: EdgeType, Ix: IndexType, D: DisplayNode<N, E, Ty, Ix>>(
-    g: &mut Graph<N, E, Ty, Ix, D>,
-    n: &N,
-) -> NodeIndex<Ix> {
+pub fn add_node<N, E, Ty, Ix, Dn, De>(g: &mut Graph<N, E, Ty, Ix, Dn, De>, n: &N) -> NodeIndex<Ix>
+where
+    N: Clone,
+    E: Clone,
+    Ty: EdgeType,
+    Ix: IndexType,
+    Dn: DisplayNode<N, E, Ty, Ix>,
+    De: DisplayEdge<N, E, Ty, Ix, Dn>,
+{
     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<N, E, Ty, Ix, Dn, De>(
+    g: &mut Graph<N, E, Ty, Ix, Dn, De>,
+    n: &N,
+    node_transform: impl FnOnce(&mut Node<N, E, Ty, Ix, Dn>),
+) -> NodeIndex<Ix>
+where
     N: Clone,
     E: Clone,
     Ty: EdgeType,
     Ix: IndexType,
-    D: DisplayNode<N, E, Ty, Ix>,
->(
-    g: &mut Graph<N, E, Ty, Ix, D>,
-    n: &N,
-    node_transform: impl FnOnce(&mut Node<N, E, Ty, Ix, D>),
-) -> NodeIndex<Ix> {
+    Dn: DisplayNode<N, E, Ty, Ix>,
+    De: DisplayEdge<N, E, Ty, Ix, Dn>,
+{
     let idx = NodeIndex::new(g.g.node_count() + 1);
     let mut n = Node::new(n.clone());
 
@@ -46,19 +51,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<N, E, Ty, Ix, Dn, De>(
+    g: &mut Graph<N, E, Ty, Ix, Dn, De>,
+    start: NodeIndex<Ix>,
+    end: NodeIndex<Ix>,
+    e: &E,
+) -> EdgeIndex<Ix>
+where
     N: Clone,
     E: Clone,
     Ty: EdgeType,
     Ix: IndexType,
     Dn: DisplayNode<N, E, Ty, Ix>,
     De: DisplayEdge<N, E, Ty, Ix, Dn>,
->(
-    g: &mut Graph<N, E, Ty, Ix, Dn, De>,
-    start: NodeIndex<Ix>,
-    end: NodeIndex<Ix>,
-    e: &E,
-) -> EdgeIndex<Ix> {
+{
     add_edge_custom(
         g,
         start,
@@ -69,20 +75,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<N, E, Ty, Ix, Dn, De>(
+    g: &mut Graph<N, E, Ty, Ix, Dn, De>,
+    start: NodeIndex<Ix>,
+    end: NodeIndex<Ix>,
+    e: &E,
+    edge_transform: impl FnOnce(&mut Edge<N, E, Ty, Ix, Dn, De>),
+) -> EdgeIndex<Ix>
+where
     N: Clone,
     E: Clone,
     Ty: EdgeType,
     Ix: IndexType,
     Dn: DisplayNode<N, E, Ty, Ix>,
     De: DisplayEdge<N, E, Ty, Ix, Dn>,
->(
-    g: &mut Graph<N, E, Ty, Ix, Dn, De>,
-    start: NodeIndex<Ix>,
-    end: NodeIndex<Ix>,
-    e: &E,
-    edge_transform: impl FnOnce(&mut Edge<N, E, Ty, Ix, Dn, De>),
-) -> EdgeIndex<Ix> {
+{
     let mut edge = Edge::new(e.clone());
 
     edge.set_id(EdgeIndex::<Ix>::new(g.g.edge_count() + 1));
@@ -93,19 +100,11 @@ pub fn add_edge_custom<
     g.g.add_edge(start, end, edge)
 }
 
-/// Helper function which transforms users [`petgraph::stable_graph::StableGraph`] isntance into the version required by the [`super::GraphView`] widget.
-///
-/// The function creates a new `StableGraph` where the nodes and edges are encapsulated into
-/// Node and Edge structs respectively. New nodes and edges are created with [`default_node_transform`] and [`default_edge_transform`]
-/// functions. If you want to define custom transformation procedures (e.g. to use custom label for nodes), use [`to_graph_custom`] instead.
-///
-/// # Arguments
-/// * `g` - A reference to a [`petgraph::stable_graph::StableGraph`]. The graph can have any data type for nodes and edges, and
-///   can be either directed or undirected.
+/// Helper function which transforms [`petgraph::stable_graph::StableGraph`] into the [`super::Graph`] required by the [`super::GraphView`] widget.
 ///
-/// # Returns
-/// * A new [`petgraph::stable_graph::StableGraph`] with the same topology as the input graph, but the nodes and edges encapsulated
-///   into Node and Edge structs compatible as an input to [`super::GraphView`] widget.
+/// The function creates a new `StableGraph` where nodes and edges are represented by [`super::Node`] and [`super::Edge`] respectively.
+/// New nodes and edges are created with [`default_node_transform`] and [`default_edge_transform`] functions.
+/// If you want to define custom transformation procedures (e.g. to use custom label for nodes), use [`to_graph_custom`] instead.
 ///
 /// # Example
 /// ```
@@ -113,58 +112,53 @@ pub fn add_edge_custom<
 /// use egui_graphs::{to_graph, DefaultNodeShape, DefaultEdgeShape, Graph};
 /// use egui::Pos2;
 ///
-/// let mut user_graph: StableGraph<&str, &str> = StableGraph::new();
-/// let node1 = user_graph.add_node("A");
-/// let node2 = user_graph.add_node("B");
-/// user_graph.add_edge(node1, node2, "edge1");
+/// let mut g: StableGraph<&str, &str> = StableGraph::new();
+/// let node1 = g.add_node("A");
+/// let node2 = g.add_node("B");
+/// g.add_edge(node1, node2, "edge1");
 ///
-/// let input_graph: Graph<_, _, _, _, DefaultNodeShape, DefaultEdgeShape> = to_graph(&user_graph);
+/// let result: Graph<_, _, _, _, DefaultNodeShape, DefaultEdgeShape> = to_graph(&g);
 ///
-/// assert_eq!(input_graph.g.node_count(), 2);
-/// assert_eq!(input_graph.g.edge_count(), 1);
+/// assert_eq!(result.g.node_count(), 2);
+/// assert_eq!(result.g.edge_count(), 1);
 ///
-/// let mut input_indices = input_graph.g.node_indices();
-/// let input_node_1 = input_indices.next().unwrap();
-/// let input_node_2 = input_indices.next().unwrap();
-/// assert_eq!(*input_graph.g.node_weight(input_node_1).unwrap().payload(), "A");
-/// assert_eq!(*input_graph.g.node_weight(input_node_2).unwrap().payload(), "B");
+/// let mut indxs = result.g.node_indices();
+/// let result_node1 = indxs.next().unwrap();
+/// let result_node2 = indxs.next().unwrap();
+/// assert_eq!(*result.g.node_weight(result_node1).unwrap().payload(), "A");
+/// assert_eq!(*result.g.node_weight(result_node2).unwrap().payload(), "B");
 ///
-/// assert_eq!(*input_graph.g.edge_weight(input_graph.g.edge_indices().next().unwrap()).unwrap().payload(), "edge1");
+/// assert_eq!(*result.g.edge_weight(result.g.edge_indices().next().unwrap()).unwrap().payload(), "edge1");
 ///
-/// assert_eq!(*input_graph.g.node_weight(input_node_1).unwrap().label().clone(), format!("node {}", input_node_1.index()));
-/// assert_eq!(*input_graph.g.node_weight(input_node_2).unwrap().label().clone(), format!("node {}", input_node_2.index()));
-///
-/// let loc_1 = input_graph.g.node_weight(input_node_1).unwrap().location();
-/// let loc_2 = input_graph.g.node_weight(input_node_2).unwrap().location();
-/// assert!(loc_1 != Pos2::ZERO);
-/// assert!(loc_2 != Pos2::ZERO);
+/// assert_eq!(*result.g.node_weight(result_node1).unwrap().label().clone(), format!("node {}", result_node1.index()));
+/// assert_eq!(*result.g.node_weight(result_node2).unwrap().label().clone(), format!("node {}", result_node2.index()));
 /// ```
-pub fn to_graph<
+pub fn to_graph<N, E, Ty, Ix, Dn, De>(g: &StableGraph<N, E, Ty, Ix>) -> Graph<N, E, Ty, Ix, Dn, De>
+where
     N: Clone,
     E: Clone,
     Ty: EdgeType,
     Ix: IndexType,
     Dn: DisplayNode<N, E, Ty, Ix>,
     De: DisplayEdge<N, E, Ty, Ix, Dn>,
->(
-    g: &StableGraph<N, E, Ty, Ix>,
-) -> Graph<N, E, Ty, Ix, Dn, De> {
+{
     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<N, E, Ty, Ix, Dn, De>(
+    g: &StableGraph<N, E, Ty, Ix>,
+    mut node_transform: impl FnMut(&mut Node<N, E, Ty, Ix, Dn>),
+    mut edge_transform: impl FnMut(&mut Edge<N, E, Ty, Ix, Dn, De>),
+) -> Graph<N, E, Ty, Ix, Dn, De>
+where
     N: Clone,
     E: Clone,
     Ty: EdgeType,
     Ix: IndexType,
     Dn: DisplayNode<N, E, Ty, Ix>,
     De: DisplayEdge<N, E, Ty, Ix, Dn>,
->(
-    g: &StableGraph<N, E, Ty, Ix>,
-    mut node_transform: impl FnMut(&mut Node<N, E, Ty, Ix, Dn>),
-    mut edge_transform: impl FnMut(&mut Edge<N, E, Ty, Ix, Dn, De>),
-) -> Graph<N, E, Ty, Ix, Dn, De> {
+{
     transform(g, &mut node_transform, &mut edge_transform)
 }
 
@@ -180,7 +174,6 @@ pub fn default_node_transform<
     node: &mut Node<N, E, Ty, Ix, D>,
 ) {
     node.set_label(format!("node {}", node.id().index()));
-    node.set_location(random_location(DEFAULT_SPAWN_SIZE));
 }
 
 /// Default edge transform function. Keeps original data and creates a new edge.
@@ -197,23 +190,19 @@ pub fn default_edge_transform<
     edge.set_label(format!("edge {}", edge.id().index()));
 }
 
-fn random_location(size: f32) -> Pos2 {
-    let mut rng = rand::thread_rng();
-    Pos2::new(rng.gen_range(0. ..size), rng.gen_range(0. ..size))
-}
-
-fn transform<
+fn transform<N, E, Ty, Ix, Dn, De>(
+    input: &StableGraph<N, E, Ty, Ix>,
+    node_transform: &mut impl FnMut(&mut Node<N, E, Ty, Ix, Dn>),
+    edge_transform: &mut impl FnMut(&mut Edge<N, E, Ty, Ix, Dn, De>),
+) -> Graph<N, E, Ty, Ix, Dn, De>
+where
     N: Clone,
     E: Clone,
     Ty: EdgeType,
     Ix: IndexType,
     Dn: DisplayNode<N, E, Ty, Ix>,
     De: DisplayEdge<N, E, Ty, Ix, Dn>,
->(
-    input: &StableGraph<N, E, Ty, Ix>,
-    node_transform: &mut impl FnMut(&mut Node<N, E, Ty, Ix, Dn>),
-    edge_transform: &mut impl FnMut(&mut Edge<N, E, Ty, Ix, Dn, De>),
-) -> Graph<N, E, Ty, Ix, Dn, De> {
+{
     let mut g =
         StableGraph::<Node<N, E, Ty, Ix, Dn>, Edge<N, E, Ty, Ix, Dn, De>, Ty, Ix>::default();
 
@@ -252,6 +241,34 @@ fn transform<
     Graph::new(g)
 }
 
+pub fn node_size<N: Clone, E: Clone, Ty: EdgeType, Ix: IndexType, D: DisplayNode<N, E, Ty, Ix>>(
+    node: &Node<N, E, Ty, Ix, D>,
+    dir: Vec2,
+) -> f32 {
+    let connector_left = node.display().closest_boundary_point(dir);
+    let connector_right = node.display().closest_boundary_point(-dir);
+
+    ((connector_right.to_vec2() - connector_left.to_vec2()) / 2.).length()
+}
+
+pub fn random_graph(num_nodes: usize, num_edges: usize) -> Graph {
+    let mut rng = rand::thread_rng();
+    let mut graph = StableGraph::new();
+
+    for _ in 0..num_nodes {
+        graph.add_node(());
+    }
+
+    for _ in 0..num_edges {
+        let source = rng.gen_range(0..num_nodes);
+        let target = rng.gen_range(0..num_nodes);
+
+        graph.add_edge(NodeIndex::new(source), NodeIndex::new(target), ());
+    }
+
+    to_graph(&graph)
+}
+
 #[cfg(test)]
 mod tests {
     use crate::DefaultEdgeShape;
@@ -279,10 +296,6 @@ mod tests {
             let input_n = input_g.g.node_weight(input_idx).unwrap();
 
             assert_eq!(*input_n.payload(), *user_n);
-
-            assert!(input_n.location().x >= 0.0 && input_n.location().x <= DEFAULT_SPAWN_SIZE);
-            assert!(input_n.location().y >= 0.0 && input_n.location().y <= DEFAULT_SPAWN_SIZE);
-
             assert_eq!(*input_n.label(), format!("node {}", user_idx.index()));
 
             assert!(!input_n.selected());
@@ -308,10 +321,6 @@ mod tests {
             let input_n = input_g.g.node_weight(input_idx).unwrap();
 
             assert_eq!(*input_n.payload(), *user_n);
-
-            assert!(input_n.location().x >= 0.0 && input_n.location().x <= DEFAULT_SPAWN_SIZE);
-            assert!(input_n.location().y >= 0.0 && input_n.location().y <= DEFAULT_SPAWN_SIZE);
-
             assert_eq!(*input_n.label(), format!("node {}", user_idx.index()));
 
             assert!(!input_n.selected());
diff --git a/src/layouts/hierarchical/layout.rs b/src/layouts/hierarchical/layout.rs
new file mode 100644
index 0000000..e380895
--- /dev/null
+++ b/src/layouts/hierarchical/layout.rs
@@ -0,0 +1,113 @@
+use std::collections::HashSet;
+
+use egui::Pos2;
+use petgraph::{
+    csr::IndexType,
+    stable_graph::NodeIndex,
+    Direction::{Incoming, Outgoing},
+    EdgeType,
+};
+use serde::{Deserialize, Serialize};
+
+use crate::{
+    layouts::{Layout, LayoutState},
+    DisplayEdge, DisplayNode, Graph,
+};
+
+const ROW_DIST: usize = 50;
+const NODE_DIST: usize = 50;
+
+#[derive(Debug, Default, Clone, Serialize, Deserialize)]
+pub struct State {
+    triggered: bool,
+}
+
+impl LayoutState for State {}
+
+#[derive(Debug, Default)]
+pub struct Hierarchical {
+    state: State,
+}
+
+impl Layout<State> for Hierarchical {
+    fn next<N, E, Ty, Ix, Dn, De>(&mut self, g: &mut Graph<N, E, Ty, Ix, Dn, De>)
+    where
+        N: Clone,
+        E: Clone,
+        Ty: EdgeType,
+        Ix: IndexType,
+        Dn: DisplayNode<N, E, Ty, Ix>,
+        De: DisplayEdge<N, E, Ty, Ix, Dn>,
+    {
+        if self.state.triggered {
+            return;
+        }
+
+        let mut visited = HashSet::new();
+        let mut max_col = 0;
+        g.g.externals(Incoming)
+            .collect::<Vec<NodeIndex<Ix>>>()
+            .iter()
+            .enumerate()
+            .for_each(|(i, root_idx)| {
+                visited.insert(*root_idx);
+
+                let curr_max_col = build_tree(g, &mut visited, root_idx, 0, i);
+                if curr_max_col > max_col {
+                    max_col = curr_max_col;
+                };
+            });
+
+        self.state.triggered = true;
+    }
+
+    fn state(&self) -> State {
+        self.state.clone()
+    }
+
+    fn from_state(state: State) -> impl Layout<State> {
+        Hierarchical { state }
+    }
+}
+
+fn build_tree<N, E, Ty, Ix, Dn, De>(
+    g: &mut Graph<N, E, Ty, Ix, Dn, De>,
+    visited: &mut HashSet<NodeIndex<Ix>>,
+    root_idx: &NodeIndex<Ix>,
+    start_row: usize,
+    start_col: usize,
+) -> usize
+where
+    N: Clone,
+    E: Clone,
+    Ty: EdgeType,
+    Ix: IndexType,
+    Dn: DisplayNode<N, E, Ty, Ix>,
+    De: DisplayEdge<N, E, Ty, Ix, Dn>,
+{
+    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 mut max_col = start_col;
+    g.g.neighbors_directed(*root_idx, Outgoing)
+        .collect::<Vec<NodeIndex<Ix>>>()
+        .iter()
+        .enumerate()
+        .for_each(|(i, neighbour_idx)| {
+            if visited.contains(neighbour_idx) {
+                return;
+            };
+
+            visited.insert(*neighbour_idx);
+
+            let curr_max_col = build_tree(g, visited, neighbour_idx, start_row + 1, start_col + i);
+            if curr_max_col > max_col {
+                max_col = curr_max_col;
+            };
+        });
+
+    max_col
+}
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
new file mode 100644
index 0000000..8ecf7cd
--- /dev/null
+++ b/src/layouts/layout.rs
@@ -0,0 +1,27 @@
+use egui::util::id_type_map::SerializableAny;
+use petgraph::{stable_graph::IndexType, EdgeType};
+
+use crate::{DisplayEdge, DisplayNode, Graph};
+
+pub trait LayoutState: SerializableAny + Default {}
+
+pub trait Layout<S>: Default
+where
+    S: LayoutState,
+{
+    /// Creates a new layout from the given state. State is loaded and saved on every frame.
+    fn from_state(state: S) -> impl Layout<S>;
+
+    /// Called on every frame. It should update the graph layout aka nodes locations.
+    fn next<N, E, Ty, Ix, Dn, De>(&mut self, g: &mut Graph<N, E, Ty, Ix, Dn, De>)
+    where
+        N: Clone,
+        E: Clone,
+        Ty: EdgeType,
+        Ix: IndexType,
+        Dn: DisplayNode<N, E, Ty, Ix>,
+        De: DisplayEdge<N, E, Ty, Ix, Dn>;
+
+    /// Returns the current state of the layout.
+    fn state(&self) -> S;
+}
diff --git a/src/layouts/mod.rs b/src/layouts/mod.rs
new file mode 100644
index 0000000..e0c45f8
--- /dev/null
+++ b/src/layouts/mod.rs
@@ -0,0 +1,5 @@
+pub mod hierarchical;
+pub mod random;
+
+mod layout;
+pub use layout::{Layout, LayoutState};
diff --git a/src/layouts/random/layout.rs b/src/layouts/random/layout.rs
new file mode 100644
index 0000000..f1581e7
--- /dev/null
+++ b/src/layouts/random/layout.rs
@@ -0,0 +1,57 @@
+use egui::Pos2;
+use petgraph::stable_graph::IndexType;
+use rand::Rng;
+use serde::{Deserialize, Serialize};
+
+use crate::{
+    layouts::{Layout, LayoutState},
+    Graph,
+};
+const SPAWN_SIZE: f32 = 250.;
+
+#[derive(Debug, Default, Clone, Serialize, Deserialize)]
+pub struct State {
+    triggered: bool,
+}
+
+impl LayoutState for State {}
+
+/// Randomly places nodes on the canvas. Does not override existing locations. Applies once.
+#[derive(Debug, Default)]
+pub struct Random {
+    state: State,
+}
+
+impl Layout<State> for Random {
+    fn next<N, E, Ty, Ix, Dn, De>(&mut self, g: &mut Graph<N, E, Ty, Ix, Dn, De>)
+    where
+        N: Clone,
+        E: Clone,
+        Ty: petgraph::EdgeType,
+        Ix: IndexType,
+        Dn: crate::DisplayNode<N, E, Ty, Ix>,
+        De: crate::DisplayEdge<N, E, Ty, Ix, Dn>,
+    {
+        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(
+                rng.gen_range(0. ..SPAWN_SIZE),
+                rng.gen_range(0. ..SPAWN_SIZE),
+            ));
+        }
+
+        self.state.triggered = true;
+    }
+
+    fn state(&self) -> State {
+        self.state.clone()
+    }
+
+    fn from_state(state: State) -> impl Layout<State> {
+        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 3de48a8..45ef8e2 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -3,17 +3,22 @@ mod elements;
 mod graph;
 mod graph_view;
 mod helpers;
+mod layouts;
 mod metadata;
 mod settings;
 
 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,
+    default_node_transform, node_size, random_graph, to_graph, to_graph_custom,
 };
+pub use layouts::hierarchical::{
+    Hierarchical as LayoutHierarchical, State as LayoutStateHierarchical,
+};
+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 a62204d..361f165 100644
--- a/src/metadata.rs
+++ b/src/metadata.rs
@@ -1,13 +1,12 @@
 use egui::{Id, Pos2, Rect, Vec2};
 use petgraph::{stable_graph::IndexType, EdgeType};
+use serde::{Deserialize, Serialize};
 
-use crate::{DisplayNode, Node};
+use crate::{node_size, DisplayNode, Node};
 
-#[cfg_attr(
-    feature = "egui_persistence",
-    derive(serde::Serialize, serde::Deserialize)
-)]
-#[derive(Clone, Debug)]
+const KEY: &str = "egui_graphs_metadata";
+
+#[derive(Clone, Debug, Serialize, Deserialize)]
 struct Bounds {
     min: Vec2,
     max: Vec2,
@@ -33,27 +32,24 @@ impl Bounds {
         &mut self,
         n: &Node<N, E, Ty, Ix, D>,
     ) {
+        let size = node_size(n, Vec2::new(0., 1.));
         let loc = n.location();
-        if loc.x < self.min.x {
-            self.min.x = loc.x;
+        if loc.x + size < self.min.x {
+            self.min.x = loc.x + size;
         };
-        if loc.x > self.max.x {
-            self.max.x = loc.x;
+        if loc.x + size > self.max.x {
+            self.max.x = loc.x + size;
         };
-        if loc.y < self.min.y {
-            self.min.y = loc.y;
+        if loc.y - size < self.min.y {
+            self.min.y = loc.y - size;
         };
-        if loc.y > self.max.y {
-            self.max.y = loc.y;
+        if loc.y + size > self.max.y {
+            self.max.y = loc.y + size;
         };
     }
 }
 
-#[cfg_attr(
-    feature = "egui_persistence",
-    derive(serde::Serialize, serde::Deserialize)
-)]
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Serialize, Deserialize)]
 pub struct Metadata {
     /// Whether the frame is the first one
     pub first_frame: bool,
@@ -81,13 +77,16 @@ impl Default for Metadata {
 }
 
 impl Metadata {
-    pub fn get(ui: &egui::Ui) -> Self {
-        ui.data_mut(|data| data.get_persisted::<Metadata>(Id::NULL).unwrap_or_default())
+    pub fn load(ui: &egui::Ui) -> Self {
+        ui.data_mut(|data| {
+            data.get_persisted::<Metadata>(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);
         });
     }
 


Test patch: