diff --git a/Cargo.lock b/Cargo.lock index cf42fc73..eccb6a5d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1001,7 +1001,7 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f06daa26ffb82d90ba772256c0ba286f6c305c392f6976c9822717974805837c" dependencies = [ - "glam 0.25.0", + "glam", "serde", ] @@ -1011,7 +1011,7 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0d7ef7f2a826d0b19f059035831ce00a5e930435cc53c61e045773d0483f67a" dependencies = [ - "glam 0.25.0", + "glam", ] [[package]] @@ -1057,7 +1057,7 @@ dependencies = [ "bevy_utils", "downcast-rs", "erased-serde", - "glam 0.25.0", + "glam", "serde", "smol_str", "thiserror", @@ -1707,20 +1707,6 @@ dependencies = [ "crossbeam-utils", ] -[[package]] -name = "configurable" -version = "0.1.0" -dependencies = [ - "crossbeam", - "eframe", - "egui 0.27.2", - "egui_graphs 0.20.0", - "fdg-sim", - "petgraph", - "rand", - "serde_json", -] - [[package]] name = "console_error_panic_hook" version = "0.1.7" @@ -1993,6 +1979,20 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" +[[package]] +name = "demo" +version = "0.1.0" +dependencies = [ + "crossbeam", + "eframe", + "egui 0.27.2", + "egui_graphs 0.20.0", + "fdg", + "petgraph", + "rand", + "serde_json", +] + [[package]] name = "derivative" version = "2.2.0" @@ -2207,6 +2207,12 @@ dependencies = [ "serde_json", ] +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + [[package]] name = "emath" version = "0.26.0" @@ -2234,7 +2240,7 @@ checksum = "95ed933078d2e659745df651f4c180511cd582e5b9414ff896e7d50d207e3103" dependencies = [ "const_panic", "encase_derive", - "glam 0.25.0", + "glam", "thiserror", ] @@ -2469,15 +2475,16 @@ dependencies = [ ] [[package]] -name = "fdg-sim" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2417e237094dfd115a4aa11b2a3dc6cda45e8d36572a7862ba9a48402f7441a3" +name = "fdg" +version = "1.0.0" +source = "git+https://github.com/grantshandy/fdg#50755f1dea20249d753600e7e7e51ca33e87b5a1" dependencies = [ - "glam 0.21.3", - "hashlink", + "nalgebra", + "num-traits", "petgraph", - "quad-rand", + "rand", + "rayon", + "rustc-hash", ] [[package]] @@ -2684,12 +2691,6 @@ dependencies = [ "xml-rs", ] -[[package]] -name = "glam" -version = "0.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "518faa5064866338b013ff9b2350dc318e14cc4fcd6cb8206d7e7c9886c98815" - [[package]] name = "glam" version = "0.25.0" @@ -2909,15 +2910,6 @@ dependencies = [ "serde", ] -[[package]] -name = "hashlink" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" -dependencies = [ - "hashbrown", -] - [[package]] name = "hassle-rs" version = "0.11.0" @@ -2952,7 +2944,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f33ddb7f7143d9e703c072e88b98cd8b9719f174137a671429351bd2ee43c02a" dependencies = [ "constgebra", - "glam 0.25.0", + "glam", ] [[package]] @@ -3256,6 +3248,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + [[package]] name = "libudev-sys" version = "0.1.4" @@ -3327,6 +3325,16 @@ dependencies = [ "regex-automata 0.1.10", ] +[[package]] +name = "matrixmultiply" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7574c1cf36da4798ab73da5b215bbf444f50718207754cb522201d78d1cd0ff2" +dependencies = [ + "autocfg", + "rawpointer", +] + [[package]] name = "memchr" version = "2.6.4" @@ -3442,6 +3450,35 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "nalgebra" +version = "0.32.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5c17de023a86f59ed79891b2e5d5a94c705dbe904a5b5c9c952ea6221b03e4" +dependencies = [ + "approx", + "matrixmultiply", + "nalgebra-macros", + "num-complex", + "num-rational", + "num-traits", + "rand", + "rand_distr", + "simba", + "typenum", +] + +[[package]] +name = "nalgebra-macros" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "254a5372af8fc138e36684761d3c0cdb758a4410e938babcff1c860ce14ddbfc" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + [[package]] name = "ndk" version = "0.7.0" @@ -3571,6 +3608,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-complex" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23c6602fda94a57c990fe0df199a035d83576b496aa29f4e634a8ac6004e68a6" +dependencies = [ + "num-traits", +] + [[package]] name = "num-derive" version = "0.3.3" @@ -3610,6 +3656,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", + "libm", ] [[package]] @@ -3990,12 +4037,6 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f89dff0959d98c9758c88826cc002e2c3d0b9dfac4139711d1f30de442f1139b" -[[package]] -name = "quad-rand" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "658fa1faf7a4cc5f057c9ee5ef560f717ad9d8dc66d975267f709624d6e1ab88" - [[package]] name = "quick-xml" version = "0.30.0" @@ -4050,6 +4091,16 @@ dependencies = [ "getrandom", ] +[[package]] +name = "rand_distr" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31" +dependencies = [ + "num-traits", + "rand", +] + [[package]] name = "range-alloc" version = "0.1.3" @@ -4068,6 +4119,32 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42a9830a0e1b9fb145ebb365b8bc4ccd75f290f98c0247deafbbe2c75cefb544" +[[package]] +name = "rawpointer" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" + +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "rectangle-pack" version = "0.4.2" @@ -4214,6 +4291,15 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +[[package]] +name = "safe_arch" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3460605018fdc9612bce72735cba0d27efbcd9904780d44c7e3a9948f96148a" +dependencies = [ + "bytemuck", +] + [[package]] name = "same-file" version = "1.0.6" @@ -4325,6 +4411,19 @@ dependencies = [ "libc", ] +[[package]] +name = "simba" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "061507c94fc6ab4ba1c9a0305018408e312e17c041eb63bef8aa726fa33aceae" +dependencies = [ + "approx", + "num-complex", + "num-traits", + "paste", + "wide", +] + [[package]] name = "simd-adler32" version = "0.3.7" @@ -5198,6 +5297,16 @@ dependencies = [ "web-sys", ] +[[package]] +name = "wide" +version = "0.7.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2caba658a80831539b30698ae9862a72db6697dfdd7151e46920f5f2755c3ce2" +dependencies = [ + "bytemuck", + "safe_arch", +] + [[package]] name = "widestring" version = "1.0.2" diff --git a/examples/configurable/README.md b/examples/configurable/README.md deleted file mode 100644 index 9b6866f6..00000000 --- a/examples/configurable/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# Configurable -Configurable example where you can toggle settings of the `GraphView` widget and see the result immediately. -It also contains controls to play with the graph. - -This example also demonstrates the usage of force-directed layout implemented on client side. - -## run -```bash -cargo run --release -p configurable -``` diff --git a/examples/configurable/Cargo.toml b/examples/demo/Cargo.toml similarity index 80% rename from examples/configurable/Cargo.toml rename to examples/demo/Cargo.toml index f3fead5a..5739cd82 100644 --- a/examples/configurable/Cargo.toml +++ b/examples/demo/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "configurable" +name = "demo" version = "0.1.0" authors = ["Dmitrii Samsonov "] license = "MIT" @@ -11,6 +11,6 @@ egui = "0.27" eframe = "0.27" serde_json = "1.0" petgraph = "0.6" -fdg-sim = "0.9" +fdg = { git = "https://github.com/grantshandy/fdg" } rand = "0.8" crossbeam = "0.8" diff --git a/examples/demo/README.md b/examples/demo/README.md new file mode 100644 index 00000000..aae89dfd --- /dev/null +++ b/examples/demo/README.md @@ -0,0 +1,7 @@ +# Demo +Demo example allows to alter settings of the `GraphView` widget and see the results immediately. This example also demonstrates the usage of force-directed graph layout implemented on client side with interactive control as well. + +## run +```bash +cargo run --release -p demo +``` diff --git a/examples/configurable/src/main.rs b/examples/demo/src/main.rs similarity index 71% rename from examples/configurable/src/main.rs rename to examples/demo/src/main.rs index 92c5292a..9de42eda 100644 --- a/examples/configurable/src/main.rs +++ b/examples/demo/src/main.rs @@ -2,24 +2,24 @@ use std::time::Instant; use crossbeam::channel::{unbounded, Receiver, Sender}; use eframe::{run_native, App, CreationContext}; -use egui::{CollapsingHeader, Context, Pos2, ScrollArea, Slider, Ui}; +use egui::{CollapsingHeader, Context, Pos2, ScrollArea, Slider, Ui, Vec2}; use egui_graphs::events::Event; -use egui_graphs::{to_graph, DefaultEdgeShape, DefaultNodeShape, Graph, GraphView}; -use fdg_sim::glam::Vec3; -use fdg_sim::{ForceGraph, ForceGraphHelper, Simulation, SimulationParameters}; +use egui_graphs::{to_graph, DefaultEdgeShape, DefaultNodeShape, Edge, Graph, GraphView, 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::Directed; use rand::Rng; -use settings::{SettingsGraph, SettingsInteraction, SettingsNavigation, SettingsStyle}; -mod settings; - -const SIMULATION_DT: f32 = 0.035; const EVENTS_LIMIT: usize = 100; -pub struct ConfigurableApp { +pub struct DemoApp { g: Graph<(), (), Directed, DefaultIx>, - sim: Simulation<(), f32>, + sim: ForceGraph, Edge<(), ()>>, + force: FruchtermanReingold, + + settings_simulation: SettingsSimulation, settings_graph: SettingsGraph, settings_interaction: SettingsInteraction, @@ -41,19 +41,41 @@ pub struct ConfigurableApp { zoom: Option, } -impl ConfigurableApp { +impl DemoApp { fn new(_: &CreationContext<'_>) -> Self { let settings_graph = SettingsGraph::default(); - let (g, sim) = generate(&settings_graph); + let settings_simulation = SettingsSimulation::default(); + + let mut g = generate_random_graph(settings_graph.count_node, settings_graph.count_edge); + + let mut force = FruchtermanReingold { + conf: FruchtermanReingoldConfiguration { + dt: settings_simulation.dt, + cooloff_factor: settings_simulation.cooloff_factor, + scale: settings_simulation.scale, + }, + ..Default::default() + }; + let mut sim = fdg::init_force_graph_uniform(g.g.clone(), 1.0); + force.apply(&mut sim); + g.g.node_weights_mut().for_each(|node| { + let point: fdg::nalgebra::OPoint> = + sim.node_weight(node.id()).unwrap().1; + node.set_location(Pos2::new(point.coords.x, point.coords.y)); + }); + let (event_publisher, event_consumer) = unbounded(); + Self { g, sim, + force, event_consumer, event_publisher, settings_graph, + settings_simulation, settings_interaction: SettingsInteraction::default(), settings_navigation: SettingsNavigation::default(), @@ -72,68 +94,24 @@ impl ConfigurableApp { } } + /// applies forces if simulation is running fn update_simulation(&mut self) { if self.simulation_stopped { return; } - // the following manipulations is a hack to avoid having looped edges in the simulation - // because they cause the simulation to blow up; - // this is the issue of the fdg_sim engine we use for the simulation - // https://github.com/grantshandy/fdg/issues/10 - // * remove loop edges - // * update simulation - // * restore loop edges - - // remove looped edges - let looped_nodes = { - let graph = self.sim.get_graph_mut(); - let mut looped_nodes = vec![]; - let mut looped_edges = vec![]; - graph.edge_indices().for_each(|idx| { - let edge = graph.edge_endpoints(idx).unwrap(); - let looped = edge.0 == edge.1; - if looped { - looped_nodes.push((edge.0, ())); - looped_edges.push(idx); - } - }); - - for idx in looped_edges { - graph.remove_edge(idx); - } - - self.sim.update(SIMULATION_DT); - - looped_nodes - }; - - // restore looped edges - let graph = self.sim.get_graph_mut(); - for (idx, _) in looped_nodes.iter() { - graph.add_edge(*idx, *idx, 1.); - } + self.force.apply(&mut self.sim); } - /// Syncs the graph with the simulation. - /// - /// Changes location of nodes in `g` according to the locations in `sim`. If node from `g` is dragged its location is prioritized - /// over the location of the corresponding node from `sim` and this location is set to the node from the `sim`. - /// - /// If node or edge is selected it is added to the corresponding selected field in `self`. + /// sync locations computed by the simulation with egui_graphs::Graph nodes. fn sync_graph_with_simulation(&mut self) { - let g_indices = self.g.g.node_indices().collect::>(); - for g_n_idx in &g_indices { - let g_n = self.g.g.node_weight_mut(*g_n_idx).unwrap(); - let sim_n = self.sim.get_graph_mut().node_weight_mut(*g_n_idx).unwrap(); - - let loc = sim_n.location; - g_n.set_location(Pos2::new(loc.x, loc.y)); - } - - // reset the weights of the edges - self.sim.get_graph_mut().edge_weights_mut().for_each(|w| { - *w = 1.; + self.g.g.node_weights_mut().for_each(|node| { + let sim_computed_point: OPoint> = + self.sim.node_weight(node.id()).unwrap().1; + node.set_location(Pos2::new( + sim_computed_point.coords.x, + sim_computed_point.coords.y, + )); }); } @@ -148,18 +126,6 @@ impl ConfigurableApp { } } - fn reset_graph(&mut self, ui: &mut Ui) { - let settings_graph = SettingsGraph::default(); - let (g, sim) = generate(&settings_graph); - - self.g = g; - self.sim = sim; - self.settings_graph = settings_graph; - self.last_events = Vec::default(); - - GraphView::<(), (), Directed, DefaultIx>::reset_metadata(ui); - } - fn handle_events(&mut self) { self.event_consumer.try_iter().for_each(|e| { if self.last_events.len() > EVENTS_LIMIT { @@ -188,10 +154,9 @@ impl ConfigurableApp { } Event::NodeMove(payload) => { let node_id = NodeIndex::new(payload.id); - let diff = Vec3::new(payload.diff[0], payload.diff[1], 0.); - let node = self.sim.get_graph_mut().node_weight_mut(node_id).unwrap(); - node.location += diff; + self.sim.node_weight_mut(node_id).unwrap().1.coords.x = payload.new_pos[0]; + self.sim.node_weight_mut(node_id).unwrap().1.coords.y = payload.new_pos[1]; } _ => {} } @@ -231,24 +196,27 @@ impl ConfigurableApp { let random_n = self.g.node(random_n_idx.unwrap()).unwrap(); - // location of new node is in surrounging of random existing node + // location of new node is in in the closest surrounding of random existing node let mut rng = rand::thread_rng(); let location = Pos2::new( random_n.location().x + 10. + rng.gen_range(0. ..50.), random_n.location().y + 10. + rng.gen_range(0. ..50.), ); - let idx = self.g.add_node_with_location((), location); + let g_idx = self.g.add_node_with_location((), location); + + let sim_node = egui_graphs::Node::new(()); + let sim_node_loc = fdg::nalgebra::Point2::new(location.x, location.y); + + let sim_idx = self.sim.add_node((sim_node, sim_node_loc)); - 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); + assert_eq!(g_idx, sim_idx); } fn remove_node(&mut self, idx: NodeIndex) { self.g.remove_node(idx); - self.sim.get_graph_mut().remove_node(idx).unwrap(); + self.sim.remove_node(idx).unwrap(); // update edges count self.settings_graph.count_edge = self.g.edge_count(); @@ -264,7 +232,7 @@ impl ConfigurableApp { fn add_edge(&mut self, start: NodeIndex, end: NodeIndex) { self.g.add_edge(start, end, ()); - self.sim.get_graph_mut().add_edge(start, end, 1.); + self.sim.add_edge(start, end, egui_graphs::Edge::new(())); } fn remove_random_edge(&mut self) { @@ -281,17 +249,24 @@ impl ConfigurableApp { 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(); + let sim_idx = self.sim.find_edge(start, end).unwrap(); + self.sim.remove_edge(sim_idx).unwrap(); } - fn draw_section_app(&mut self, ui: &mut Ui) { - CollapsingHeader::new("App Config") + fn draw_section_simulation(&mut self, ui: &mut Ui) { + CollapsingHeader::new("Simulation") .default_open(true) .show(ui, |ui| { + ui.horizontal_wrapped(|ui| { + ui.style_mut().spacing.item_spacing = Vec2::new(0., 0.); + ui.label("Force-Directed Simulation is done with "); + ui.hyperlink_to("fdg project", "https://github.com/grantshandy/fdg"); + }); + + ui.separator(); ui.add_space(10.); - ui.label("Simulation"); + ui.label("Config"); ui.separator(); ui.horizontal(|ui| { @@ -305,12 +280,15 @@ impl ConfigurableApp { self.simulation_stopped = !self.simulation_stopped; }; if ui.button("reset").clicked() { - self.reset_graph(ui); + self.reset(); } }); ui.add_space(10.); + self.draw_simulation_config_sliders(ui); + ui.add_space(10.); + ui.separator(); self.draw_counts_sliders(ui); ui.add_space(10.); @@ -419,7 +397,10 @@ impl ConfigurableApp { }); CollapsingHeader::new("Last Events").default_open(true).show(ui, |ui| { - ScrollArea::vertical().auto_shrink([false, true]).max_height(200.).show(ui, |ui| { + if ui.button("clear").clicked() { + self.last_events.clear(); + } + ScrollArea::vertical().auto_shrink([false, true]).show(ui, |ui| { self.last_events.iter().rev().for_each(|event| { ui.label(event); }); @@ -474,15 +455,82 @@ impl ConfigurableApp { }); }); } + + fn draw_simulation_config_sliders(&mut self, ui: &mut Ui) { + let mut changed = false; + + ui.horizontal(|ui| { + let resp = ui.add(Slider::new(&mut self.settings_simulation.dt, 0.00..=1.).text("dt")); + if resp.changed() { + changed = true + } + }); + ui.horizontal(|ui| { + let resp = ui.add( + Slider::new(&mut self.settings_simulation.cooloff_factor, 0.0..=1.) + .text("cooloff_factor"), + ); + if resp.changed() { + changed = true + } + }); + ui.horizontal(|ui| { + let resp = + ui.add(Slider::new(&mut self.settings_simulation.scale, 0.0..=300.).text("scale")); + if resp.changed() { + changed = true + } + }); + + if changed { + self.force = FruchtermanReingold { + conf: FruchtermanReingoldConfiguration { + dt: self.settings_simulation.dt, + cooloff_factor: self.settings_simulation.cooloff_factor, + scale: self.settings_simulation.scale, + }, + ..Default::default() + }; + } + } + + fn reset(&mut self) { + let settings_graph = SettingsGraph::default(); + let settings_simulation = SettingsSimulation::default(); + + let mut g = generate_random_graph(settings_graph.count_node, settings_graph.count_edge); + + let mut force = FruchtermanReingold { + conf: FruchtermanReingoldConfiguration { + dt: settings_simulation.dt, + cooloff_factor: settings_simulation.cooloff_factor, + scale: settings_simulation.scale, + }, + ..Default::default() + }; + let mut sim = fdg::init_force_graph_uniform(g.g.clone(), 1.0); + force.apply(&mut sim); + g.g.node_weights_mut().for_each(|node| { + let point: fdg::nalgebra::OPoint> = + sim.node_weight(node.id()).unwrap().1; + node.set_location(Pos2::new(point.coords.x, point.coords.y)); + }); + + self.settings_simulation = settings_simulation; + self.settings_graph = settings_graph; + self.sim = sim; + self.g = g; + self.force = force; + } } -impl App for ConfigurableApp { +impl App for DemoApp { fn update(&mut self, ctx: &Context, _: &mut eframe::Frame) { egui::SidePanel::right("right_panel") .min_width(250.) .show(ctx, |ui| { ScrollArea::vertical().show(ui, |ui| { - self.draw_section_app(ui); + self.draw_section_simulation(ui); ui.add_space(10.); self.draw_section_debug(ui); ui.add_space(10.); @@ -520,39 +568,11 @@ impl App for ConfigurableApp { self.handle_events(); self.sync_graph_with_simulation(); - self.update_simulation(); self.update_fps(); } } -fn generate(settings: &SettingsGraph) -> (Graph<(), (), Directed, DefaultIx>, Simulation<(), f32>) { - let g = generate_random_graph(settings.count_node, settings.count_edge); - let sim = construct_simulation(&g); - - (g, sim) -} - -fn construct_simulation(g: &Graph<(), (), Directed, DefaultIx>) -> Simulation<(), f32> { - // create force graph - let mut force_graph = ForceGraph::with_capacity(g.g.node_count(), g.g.edge_count()); - g.g.node_indices().for_each(|idx| { - let idx = idx.index(); - force_graph.add_force_node(format!("{idx}").as_str(), ()); - }); - g.g.edge_indices().for_each(|idx| { - let (source, target) = g.g.edge_endpoints(idx).unwrap(); - force_graph.add_edge(source, target, 1.); - }); - - // initialize simulation - let mut params = SimulationParameters::default(); - let force = fdg_sim::force::fruchterman_reingold_weighted(100., 0.5); - params.set_force(force); - - Simulation::from_graph(force_graph, params) -} - fn generate_random_graph(node_count: usize, edge_count: usize) -> Graph<(), ()> { let mut rng = rand::thread_rng(); let mut graph = StableGraph::new(); @@ -576,9 +596,73 @@ fn generate_random_graph(node_count: usize, edge_count: usize) -> Graph<(), ()> fn main() { let native_options = eframe::NativeOptions::default(); run_native( - "egui_graphs_configurable_demo", + "egui_graphs_demo", native_options, - Box::new(|cc| Box::new(ConfigurableApp::new(cc))), + Box::new(|cc| Box::new(DemoApp::new(cc))), ) .unwrap(); } + +struct SettingsSimulation { + dt: f32, + cooloff_factor: f32, + scale: f32, +} + +impl Default for SettingsSimulation { + fn default() -> Self { + Self { + dt: 0.03, + cooloff_factor: 0.7, + scale: 100., + } + } +} + +struct SettingsGraph { + pub count_node: usize, + pub count_edge: usize, +} + +impl Default for SettingsGraph { + fn default() -> Self { + Self { + count_node: 25, + count_edge: 50, + } + } +} + +#[derive(Default)] +struct SettingsInteraction { + pub dragging_enabled: bool, + pub node_clicking_enabled: bool, + pub node_selection_enabled: bool, + pub node_selection_multi_enabled: bool, + pub edge_clicking_enabled: bool, + pub edge_selection_enabled: bool, + pub edge_selection_multi_enabled: bool, +} + +struct SettingsNavigation { + pub fit_to_screen_enabled: bool, + pub zoom_and_pan_enabled: bool, + pub screen_padding: f32, + pub zoom_speed: f32, +} + +impl Default for SettingsNavigation { + fn default() -> Self { + Self { + screen_padding: 0.3, + zoom_speed: 0.1, + fit_to_screen_enabled: true, + zoom_and_pan_enabled: false, + } + } +} + +#[derive(Default)] +struct SettingsStyle { + pub labels_always: bool, +} diff --git a/examples/configurable/src/settings.rs b/examples/demo/src/settings.rs similarity index 100% rename from examples/configurable/src/settings.rs rename to examples/demo/src/settings.rs