diff --git a/Cargo.lock b/Cargo.lock index 19ed0f4..df86535 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -139,7 +139,7 @@ dependencies = [ "log", "ndk", "ndk-context", - "ndk-sys", + "ndk-sys 0.5.0+25.2.9519653", "num_enum", "thiserror", ] @@ -159,6 +159,15 @@ dependencies = [ "libc", ] +[[package]] +name = "approx" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +dependencies = [ + "num-traits", +] + [[package]] name = "arboard" version = "3.4.0" @@ -1112,6 +1121,12 @@ dependencies = [ "serde_json", ] +[[package]] +name = "either" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" + [[package]] name = "emath" version = "0.27.2" @@ -1263,6 +1278,35 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "fdg" +version = "1.0.0" +source = "git+https://github.com/grantshandy/fdg#50755f1dea20249d753600e7e7e51ca33e87b5a1" +dependencies = [ + "nalgebra", + "num-traits", + "petgraph", + "rand", + "rayon", + "rustc-hash", +] + +[[package]] +name = "fdg-example" +version = "0.1.0" +dependencies = [ + "asd", + "fdg", + "macroquad", + "nalgebra", + "num-traits", + "petgraph", + "petgraph-gen", + "rand", + "rayon", + "rustc-hash", +] + [[package]] name = "fdg-sim" version = "0.9.1" @@ -1291,6 +1335,16 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "fontdue" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0793f5137567643cf65ea42043a538804ff0fbf288649e2141442b602d81f9bc" +dependencies = [ + "hashbrown 0.13.2", + "ttf-parser 0.15.2", +] + [[package]] name = "foreign-types" version = "0.5.0" @@ -1560,7 +1614,7 @@ checksum = "cc11df1ace8e7e564511f53af41f3e42ddc95b56fd07b3f4445d2a6048bc682c" dependencies = [ "bitflags 2.5.0", "gpu-descriptor-types", - "hashbrown", + "hashbrown 0.14.5", ] [[package]] @@ -1572,6 +1626,15 @@ dependencies = [ "bitflags 2.5.0", ] +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + [[package]] name = "hashbrown" version = "0.14.5" @@ -1588,7 +1651,7 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" dependencies = [ - "hashbrown", + "hashbrown 0.14.5", ] [[package]] @@ -1674,7 +1737,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.14.5", ] [[package]] @@ -1786,6 +1849,12 @@ dependencies = [ "windows-targets 0.52.5", ] +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + [[package]] name = "libredox" version = "0.0.2" @@ -1831,6 +1900,28 @@ version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +[[package]] +name = "macroquad" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d983c03e9171099b2eb2067206c78cb37904ea071bb9ea4602c43ed45bb7097" +dependencies = [ + "bumpalo", + "fontdue", + "glam", + "image", + "macroquad_macro", + "miniquad", + "quad-rand", + "slotmap", +] + +[[package]] +name = "macroquad_macro" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5cecfede1e530599c8686f7f2d609489101d3d63741a6dc423afc997ce3fcc8" + [[package]] name = "malloc_buf" version = "0.0.6" @@ -1840,6 +1931,16 @@ dependencies = [ "libc", ] +[[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.7.2" @@ -1888,6 +1989,18 @@ dependencies = [ "paste", ] +[[package]] +name = "miniquad" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17282b286b736c9ef6f79c630a5173669491a5e2f13a03fe556e59f7b577cd2" +dependencies = [ + "libc", + "ndk-sys 0.2.2", + "objc", + "winapi", +] + [[package]] name = "miniz_oxide" version = "0.7.3" @@ -1918,6 +2031,35 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "nalgebra" +version = "0.32.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ea4908d4f23254adda3daa60ffef0f1ac7b8c3e9a864cf3cc154b251908a2ef" +dependencies = [ + "approx", + "matrixmultiply", + "nalgebra-macros", + "num-complex", + "num-rational", + "num-traits", + "rand", + "rand_distr", + "simba", + "typenum", +] + +[[package]] +name = "nalgebra-macros" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91761aed67d03ad966ef783ae962ef9bbaca728d2dd7ceb7939ec110fffad998" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "ndk" version = "0.8.0" @@ -1927,7 +2069,7 @@ dependencies = [ "bitflags 2.5.0", "jni-sys", "log", - "ndk-sys", + "ndk-sys 0.5.0+25.2.9519653", "num_enum", "raw-window-handle 0.5.2", "raw-window-handle 0.6.2", @@ -1940,6 +2082,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" +[[package]] +name = "ndk-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1bcdd74c20ad5d95aacd60ef9ba40fdf77f767051040541df557b7a9b2a2121" + [[package]] name = "ndk-sys" version = "0.5.0+25.2.9519653" @@ -1967,6 +2115,34 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -1974,6 +2150,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", + "libm", ] [[package]] @@ -2188,7 +2365,7 @@ version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b41438d2fc63c46c74a2203bf5ccd82c41ba04347b2fcf5754f230b167067d5" dependencies = [ - "ttf-parser", + "ttf-parser 0.21.1", ] [[package]] @@ -2242,6 +2419,17 @@ dependencies = [ "indexmap", ] +[[package]] +name = "petgraph-gen" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d83bcf72cdb33a6aa1f0865ae3ae7c34ec7eba52042262d53c2bb441276a0940" +dependencies = [ + "petgraph", + "rand", + "rustc-hash", +] + [[package]] name = "pin-project-lite" version = "0.2.14" @@ -2415,6 +2603,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 = "raw-window-handle" version = "0.5.2" @@ -2427,6 +2625,32 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" +[[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 = "redox_syscall" version = "0.3.5" @@ -2528,6 +2752,15 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +[[package]] +name = "safe_arch" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f398075ce1e6a179b46f51bd88d0598b92b00d3551f1a2d4ac49e771b56ac354" +dependencies = [ + "bytemuck", +] + [[package]] name = "same-file" version = "1.0.6" @@ -2624,6 +2857,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" @@ -2892,6 +3138,12 @@ dependencies = [ "once_cell", ] +[[package]] +name = "ttf-parser" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b3e06c9b9d80ed6b745c7159c40b311ad2916abb34a49e9be2653b90db0d8dd" + [[package]] name = "ttf-parser" version = "0.21.1" @@ -3289,7 +3541,7 @@ dependencies = [ "log", "metal", "naga", - "ndk-sys", + "ndk-sys 0.5.0+25.2.9519653", "objc", "once_cell", "parking_lot", @@ -3316,6 +3568,16 @@ dependencies = [ "web-sys", ] +[[package]] +name = "wide" +version = "0.7.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21e005a4cc35784183a9e39cb22e9a9c46353ef6a7f113fd8d36ddc58c15ef3c" +dependencies = [ + "bytemuck", + "safe_arch", +] + [[package]] name = "widestring" version = "1.1.0" @@ -3632,7 +3894,7 @@ dependencies = [ "log", "memmap2", "ndk", - "ndk-sys", + "ndk-sys 0.5.0+25.2.9519653", "objc2 0.4.1", "once_cell", "orbclient", diff --git a/examples/configurable/src/main.rs b/examples/configurable/src/main.rs index 1428373..818e6fd 100644 --- a/examples/configurable/src/main.rs +++ b/examples/configurable/src/main.rs @@ -6,19 +6,19 @@ use asd::gfa::{Entry, Orientation}; use asd::parser; 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, Ui}; use egui_graphs::events::Event; -use egui_graphs::{to_graph, DefaultEdgeShape, DefaultNodeShape, Graph, GraphView}; +use egui_graphs::{DefaultEdgeShape, DefaultNodeShape, Graph, GraphView}; use fdg_sim::glam::Vec3; use fdg_sim::{ForceGraph, ForceGraphHelper, Simulation, SimulationParameters}; -use petgraph::stable_graph::{DefaultIx, EdgeIndex, NodeIndex, StableGraph}; +use petgraph::stable_graph::{DefaultIx, NodeIndex, StableGraph}; use petgraph::Directed; -use rand::Rng; use settings::{SettingsInteraction, SettingsNavigation, SettingsStyle}; mod settings; -const SIMULATION_DT: f32 = 0.035; +// const SIMULATION_DT: f32 = 0.035; +const SIMULATION_DT: f32 = 0.075; const EVENTS_LIMIT: usize = 100; pub struct ConfigurableApp { @@ -314,10 +314,6 @@ impl ConfigurableApp { ui.add_space(10.); - self.draw_counts_sliders(ui); - - ui.add_space(10.); - ui.separator(); }); } @@ -443,40 +439,42 @@ impl ConfigurableApp { }; ui.label(format!("FPS: {:.1}", self.fps)); + ui.label(format!("Nodes: {}", self.g.node_count())); + ui.label(format!("Edges: {}", self.g.edge_count())); }); } - fn draw_counts_sliders(&mut self, ui: &mut Ui) { - // ui.horizontal(|ui| { - // let before = self.settings_graph.count_node as i32; - - // ui.add(Slider::new(&mut self.settings_graph.count_node, 1..=2500).text("nodes")); - - // let delta = self.settings_graph.count_node as i32 - before; - // (0..delta.abs()).for_each(|_| { - // if delta > 0 { - // self.add_random_node(); - // return; - // }; - // self.remove_random_node(); - // }); - // }); - - // ui.horizontal(|ui| { - // let before = self.settings_graph.count_edge as i32; - - // ui.add(Slider::new(&mut self.settings_graph.count_edge, 0..=5000).text("edges")); - - // let delta = self.settings_graph.count_edge as i32 - before; - // (0..delta.abs()).for_each(|_| { - // if delta > 0 { - // self.add_random_edge(); - // return; - // }; - // self.remove_random_edge(); - // }); - // }); - } + // fn draw_counts_sliders(&mut self, ui: &mut Ui) { + // // ui.horizontal(|ui| { + // // let before = self.settings_graph.count_node as i32; + + // // ui.add(Slider::new(&mut self.settings_graph.count_node, 1..=2500).text("nodes")); + + // // let delta = self.settings_graph.count_node as i32 - before; + // // (0..delta.abs()).for_each(|_| { + // // if delta > 0 { + // // self.add_random_node(); + // // return; + // // }; + // // self.remove_random_node(); + // // }); + // // }); + + // // ui.horizontal(|ui| { + // // let before = self.settings_graph.count_edge as i32; + + // // ui.add(Slider::new(&mut self.settings_graph.count_edge, 0..=5000).text("edges")); + + // // let delta = self.settings_graph.count_edge as i32 - before; + // // (0..delta.abs()).for_each(|_| { + // // if delta > 0 { + // // self.add_random_edge(); + // // return; + // // }; + // // self.remove_random_edge(); + // // }); + // // }); + // } } impl App for ConfigurableApp { diff --git a/examples/fdg-example/Cargo.toml b/examples/fdg-example/Cargo.toml new file mode 100644 index 0000000..1b95ac4 --- /dev/null +++ b/examples/fdg-example/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "fdg-example" +version = "0.1.0" +edition = "2021" + +[dependencies] +nalgebra = { version = "0.32.3", features = ["rand"] } +petgraph = { version = "0.6.4", features = [ + "stable_graph", +], default-features = false } +num-traits = "0.2.17" +rand = "0.8.5" +rustc-hash = "1.1.0" +rayon = { version = "1.8.0", optional = true } +macroquad = "0.4.4" +petgraph-gen = "0.1.3" +fdg = { git = "https://github.com/grantshandy/fdg" } +asd = { path = "../../" } diff --git a/examples/fdg-example/src/main.rs b/examples/fdg-example/src/main.rs new file mode 100644 index 0000000..6398079 --- /dev/null +++ b/examples/fdg-example/src/main.rs @@ -0,0 +1,147 @@ +use std::{collections::HashMap, env}; + +use asd::{ + gfa::{Entry, Orientation}, + parser, +}; +use fdg::{ + fruchterman_reingold::{FruchtermanReingold, FruchtermanReingoldConfiguration}, + nalgebra::Rotation2, + petgraph::Graph, + simple::Center, + Force, ForceGraph, +}; + +use macroquad::prelude::*; + +#[macroquad::main("fdg demo")] +async fn main() { + let mut graph = Graph::<(String, Orientation), ()>::new(); + + let file = std::fs::File::open(env::args().nth(1).expect("missing gfa file argument")).unwrap(); + let entries = parser::parse_source(file).unwrap(); + + let mut index_map = HashMap::new(); + + for entry in entries { + println!("{:?}", entry); + + if let Entry::Link { + from, + from_orient, + to, + to_orient, + } = entry + { + // add first node if not present + let a = index_map + .entry(from.clone()) + .or_insert_with(|| graph.add_node((from.clone(), from_orient))) + .to_owned(); + + // add second node if not present + let b = index_map + .entry(to.clone()) + .or_insert_with(|| graph.add_node((to.clone(), to_orient))) + .to_owned(); + + graph.add_edge(a, b, ()); + } + } + + let mut force_graph: ForceGraph = + fdg::init_force_graph_uniform(graph, 200.0); + + // custom closure force which rotates each node + // let mut rotate = |graph: &mut ForceGraph| { + // graph + // .node_weights_mut() + // .for_each(|(_, p)| *p = Rotation2::new(0.005).transform_point(p)) + // }; + let mut force = FruchtermanReingold { + conf: FruchtermanReingoldConfiguration { + scale: 400.0, + ..Default::default() + }, + ..Default::default() + }; + + loop { + println!("frame"); + force.apply_many(&mut force_graph, 1); + + Center::default().apply(&mut force_graph); + // rotate.apply(&mut force_graph); + + let scale = calculate_scale(&force_graph); + + clear_background(WHITE); + + for idx in force_graph.edge_indices() { + let ((_, source), (_, target)) = force_graph + .edge_endpoints(idx) + .map(|(a, b)| { + ( + force_graph.node_weight(a).unwrap(), + force_graph.node_weight(b).unwrap(), + ) + }) + .unwrap(); + + draw_line( + translate_x(source.coords.column(0)[0], scale), + translate_y(source.coords.column(0)[1], scale), + translate_x(target.coords.column(0)[0], scale), + translate_y(target.coords.column(0)[1], scale), + 4.0, + BLACK, + ); + } + + for ((name, orient), pos) in force_graph.node_weights() { + let x = translate_x(pos.coords.column(0)[0], scale); + let y = translate_y(pos.coords.column(0)[1], scale); + + draw_circle(x, y, 20.0 * scale, RED); + draw_text( + format!("{}{}", name, orient).as_str(), + x - 30.0 * scale, + y - 30.0 * scale, + 40.0 * scale, + BLACK, + ); + } + + next_frame().await + } +} + +fn translate_x(x: f32, scale: f32) -> f32 { + (screen_width() / 2.0) + (x * scale) +} + +fn translate_y(y: f32, scale: f32) -> f32 { + (screen_height() / 2.0) + (y * scale) +} + +fn calculate_scale(graph: &ForceGraph) -> f32 { + let (min_x, max_x, min_y, max_y) = graph.node_weights().fold( + (f32::MAX, f32::MIN, f32::MAX, f32::MIN), + |(min_x, max_x, min_y, max_y), (_, pos)| { + ( + min_x.min(pos.coords.column(0)[0]), + max_x.max(pos.coords.column(0)[0]), + min_y.min(pos.coords.column(0)[1]), + max_y.max(pos.coords.column(0)[1]), + ) + }, + ); + + let graph_width = max_x - min_x; + let graph_height = max_y - min_y; + + let scale_x = screen_width() / graph_width; + let scale_y = screen_height() / graph_height; + + scale_x.min(scale_y) * 0.9 // add some padding +}