fdg_sim/
simulation.rs

1use std::fmt;
2
3use crate::force::{self, Force};
4
5use super::ForceGraph;
6use glam::Vec3;
7use petgraph::{
8    graph::NodeIndex,
9    visit::{EdgeRef, IntoEdgeReferences},
10    EdgeType, Undirected,
11};
12use quad_rand::RandomRange;
13
14/// Number of dimensions to run the simulation in.
15#[derive(Debug, Copy, Clone, PartialEq, Eq)]
16pub enum Dimensions {
17    Two,
18    Three,
19}
20
21impl fmt::Display for Dimensions {
22    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23        match self {
24            Dimensions::Two => write!(f, "2"),
25            Dimensions::Three => write!(f, "3"),
26        }
27    }
28}
29
30/// Parameters for the simulation.
31#[derive(Clone)]
32pub struct SimulationParameters<N, E, Ty = Undirected> {
33    /// The width and height of the box that the nodes randomly start in at the beginning of the simulation.
34    pub node_start_size: f32,
35    /// The number of dimensions that the simulation will run in.
36    pub dimensions: Dimensions,
37    /// The force that dictates how the simulation behaves.
38    force: Force<N, E, Ty>,
39}
40
41impl<N, E, Ty: EdgeType> SimulationParameters<N, E, Ty> {
42    /// Create a new [`SimulationParameters`].
43    pub fn new(node_start_size: f32, dimensions: Dimensions, force: Force<N, E, Ty>) -> Self {
44        Self {
45            node_start_size,
46            dimensions,
47            force,
48        }
49    }
50
51    /// Retrieve a mutable reference to the internal [`Force`].
52    pub fn force_mut(&mut self) -> &mut Force<N, E, Ty> {
53        &mut self.force
54    }
55
56    /// Retrieve a reference to the internal [`Force`].
57    pub fn force(&self) -> &Force<N, E, Ty> {
58        &self.force
59    }
60
61    /// Create a new [`SimulationParameters`] from a [`Force`].
62    pub fn from_force(force: Force<N, E, Ty>) -> Self {
63        Self {
64            force,
65            ..Default::default()
66        }
67    }
68
69    /// Set the internal [`Force`].
70    pub fn set_force(&mut self, force: Force<N, E, Ty>) {
71        self.force = force;
72    }
73}
74
75impl<N, E, Ty: EdgeType> Default for SimulationParameters<N, E, Ty> {
76    fn default() -> Self {
77        Self {
78            node_start_size: 200.0,
79            dimensions: Dimensions::Two,
80            force: force::fruchterman_reingold(45.0, 0.975),
81        }
82    }
83}
84
85/// A simulation for managing the main event loop and forces.
86#[derive(Clone)]
87pub struct Simulation<N, E, Ty = Undirected> {
88    graph: ForceGraph<N, E, Ty>,
89    parameters: SimulationParameters<N, E, Ty>,
90}
91
92impl<N, E, Ty: EdgeType> Simulation<N, E, Ty> {
93    /// Create a simulation from a [`ForceGraph`].
94    pub fn from_graph(
95        graph: ForceGraph<N, E, Ty>,
96        parameters: SimulationParameters<N, E, Ty>,
97    ) -> Self {
98        let mut myself = Self { graph, parameters };
99
100        myself.reset_node_placement();
101
102        myself
103    }
104
105    /// Randomly place the nodes within the starting square.
106    /// In practice, this restarts the simulation.
107    pub fn reset_node_placement(&mut self) {
108        let half_node_start_width = self.parameters.node_start_size / 2.0;
109
110        for node in self.graph.node_weights_mut() {
111            let random_location = Vec3::new(
112                RandomRange::gen_range(-half_node_start_width, half_node_start_width),
113                RandomRange::gen_range(-half_node_start_width, half_node_start_width),
114                match self.parameters.dimensions {
115                    Dimensions::Three => RandomRange::gen_range(-half_node_start_width, half_node_start_width),
116                    Dimensions::Two => 0.0,
117                }
118            );
119           
120            node.velocity = Vec3::ZERO;
121            node.location = random_location;
122            node.old_location = random_location;
123        }
124    }
125
126    /// Update the graph's node's positions for a given interval.
127    pub fn update(&mut self, dt: f32) {
128        self.parameters.force().update(&mut self.graph, dt);
129    }
130
131    /// Update the graph's node's positions for a given interval with a custom [`Force`].
132    pub fn update_custom(&mut self, force: &Force<N, E, Ty>, dt: f32) {
133        force.update(&mut self.graph, dt)
134    }
135
136    /// Run a callback on all the nodes.
137    pub fn visit_nodes(&self, cb: &mut impl Fn(&Node<N>)) {
138        for n_idx in self.graph.node_indices() {
139            cb(&self.graph[n_idx]);
140        }
141    }
142
143    /// Run a callback on all of the edges.
144    pub fn visit_edges(&self, cb: &mut impl Fn(&Node<N>, &Node<N>)) {
145        for edge_ref in self.graph.edge_references() {
146            cb(
147                &self.graph[edge_ref.source()],
148                &self.graph[edge_ref.target()],
149            );
150        }
151    }
152
153    /// Retrieve a reference to the internal [`ForceGraph`].
154    pub fn get_graph(&self) -> &ForceGraph<N, E, Ty> {
155        &self.graph
156    }
157
158    /// Retrieve a mutable reference to the internal [`ForceGraph`].
159    pub fn get_graph_mut(&mut self) -> &mut ForceGraph<N, E, Ty> {
160        &mut self.graph
161    }
162
163    /// Set the internal [`ForceGraph`].
164    pub fn set_graph(&mut self, graph: ForceGraph<N, E, Ty>) {
165        self.graph = graph;
166    }
167
168    /// Retrieve a reference to the internal [`SimulationParameters`].
169    pub fn parameters(&self) -> &SimulationParameters<N, E, Ty> {
170        &self.parameters
171    }
172
173    /// Retreive a mutable reference to the internal [`SimulationParameters`].
174    pub fn parameters_mut(&mut self) -> &mut SimulationParameters<N, E, Ty> {
175        &mut self.parameters
176    }
177
178    /// Retreive a node from the graph based on a query.
179    pub fn find(&self, query: Vec3, radius: f32) -> Option<NodeIndex> {
180        let query_x = (query.x - radius)..=(query.x + radius);
181        let query_y = (query.y - radius)..=(query.y + radius);
182        let query_z = (query.z - radius)..=(query.z + radius);
183
184        for index in self.graph.node_indices() {
185            let node = &self.graph[index];
186
187            if query_x.contains(&node.location.x)
188                && query_y.contains(&node.location.y)
189                && query_z.contains(&node.location.z)
190            {
191                return Some(index);
192            }
193        }
194
195        None
196    }
197}
198
199impl<N, E, Ty: EdgeType> Default for Simulation<N, E, Ty> {
200    fn default() -> Self {
201        Self::from_graph(ForceGraph::default(), SimulationParameters::default())
202    }
203}
204
205/// A node on a [`ForceGraph`].
206#[derive(Clone, PartialEq)]
207#[cfg_attr(feature = "json", derive(serde::Serialize))]
208pub struct Node<N> {
209    /// The name of the node.
210    pub name: String,
211    /// Any arbitrary information you want to store within it.
212    pub data: N,
213    pub location: Vec3,
214    pub old_location: Vec3,
215    pub velocity: Vec3,
216}
217
218impl<N> Node<N> {
219    /// Create a new node with it's name and associated data.
220    pub fn new(name: impl AsRef<str>, data: N) -> Self {
221        Self {
222            name: name.as_ref().to_string(),
223            data,
224            location: Vec3::ZERO,
225            old_location: Vec3::ZERO,
226            velocity: Vec3::ZERO,
227        }
228    }
229
230    /// Create a new node with custom coordinates.
231    pub fn new_with_coords(name: impl AsRef<str>, data: N, location: Vec3) -> Self {
232        Self {
233            name: name.as_ref().to_string(),
234            data,
235            location,
236            old_location: Vec3::ZERO,
237            velocity: Vec3::ZERO,
238        }
239    }
240}
241
242impl<N: fmt::Debug> fmt::Debug for Node<N> {
243    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
244        f.debug_struct("Node")
245            .field("name", &self.name)
246            .field("data", &self.data)
247            .field("location", &self.location)
248            .finish()
249    }
250}