use std::fmt;
use crate::force::{self, Force};
use super::ForceGraph;
use glam::Vec3;
use petgraph::{
graph::NodeIndex,
visit::{EdgeRef, IntoEdgeReferences},
EdgeType, Undirected,
};
use quad_rand::RandomRange;
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Dimensions {
Two,
Three,
}
impl fmt::Display for Dimensions {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Dimensions::Two => write!(f, "2"),
Dimensions::Three => write!(f, "3"),
}
}
}
#[derive(Clone)]
pub struct SimulationParameters<N, E, Ty = Undirected> {
pub node_start_size: f32,
pub dimensions: Dimensions,
force: Force<N, E, Ty>,
}
impl<N, E, Ty: EdgeType> SimulationParameters<N, E, Ty> {
pub fn new(node_start_size: f32, dimensions: Dimensions, force: Force<N, E, Ty>) -> Self {
Self {
node_start_size,
dimensions,
force,
}
}
pub fn force_mut(&mut self) -> &mut Force<N, E, Ty> {
&mut self.force
}
pub fn force(&self) -> &Force<N, E, Ty> {
&self.force
}
pub fn from_force(force: Force<N, E, Ty>) -> Self {
Self {
force,
..Default::default()
}
}
pub fn set_force(&mut self, force: Force<N, E, Ty>) {
self.force = force;
}
}
impl<N, E, Ty: EdgeType> Default for SimulationParameters<N, E, Ty> {
fn default() -> Self {
Self {
node_start_size: 200.0,
dimensions: Dimensions::Two,
force: force::fruchterman_reingold(45.0, 0.975),
}
}
}
#[derive(Clone)]
pub struct Simulation<N, E, Ty = Undirected> {
graph: ForceGraph<N, E, Ty>,
parameters: SimulationParameters<N, E, Ty>,
}
impl<N, E, Ty: EdgeType> Simulation<N, E, Ty> {
pub fn from_graph(
graph: ForceGraph<N, E, Ty>,
parameters: SimulationParameters<N, E, Ty>,
) -> Self {
let mut myself = Self { graph, parameters };
myself.reset_node_placement();
myself
}
pub fn reset_node_placement(&mut self) {
let half_node_start_width = self.parameters.node_start_size / 2.0;
for node in self.graph.node_weights_mut() {
let random_location = Vec3::new(
RandomRange::gen_range(-half_node_start_width, half_node_start_width),
RandomRange::gen_range(-half_node_start_width, half_node_start_width),
match self.parameters.dimensions {
Dimensions::Three => RandomRange::gen_range(-half_node_start_width, half_node_start_width),
Dimensions::Two => 0.0,
}
);
node.velocity = Vec3::ZERO;
node.location = random_location;
node.old_location = random_location;
}
}
pub fn update(&mut self, dt: f32) {
self.parameters.force().update(&mut self.graph, dt);
}
pub fn update_custom(&mut self, force: &Force<N, E, Ty>, dt: f32) {
force.update(&mut self.graph, dt)
}
pub fn visit_nodes(&self, cb: &mut impl Fn(&Node<N>)) {
for n_idx in self.graph.node_indices() {
cb(&self.graph[n_idx]);
}
}
pub fn visit_edges(&self, cb: &mut impl Fn(&Node<N>, &Node<N>)) {
for edge_ref in self.graph.edge_references() {
cb(
&self.graph[edge_ref.source()],
&self.graph[edge_ref.target()],
);
}
}
pub fn get_graph(&self) -> &ForceGraph<N, E, Ty> {
&self.graph
}
pub fn get_graph_mut(&mut self) -> &mut ForceGraph<N, E, Ty> {
&mut self.graph
}
pub fn set_graph(&mut self, graph: ForceGraph<N, E, Ty>) {
self.graph = graph;
}
pub fn parameters(&self) -> &SimulationParameters<N, E, Ty> {
&self.parameters
}
pub fn parameters_mut(&mut self) -> &mut SimulationParameters<N, E, Ty> {
&mut self.parameters
}
pub fn find(&self, query: Vec3, radius: f32) -> Option<NodeIndex> {
let query_x = (query.x - radius)..=(query.x + radius);
let query_y = (query.y - radius)..=(query.y + radius);
let query_z = (query.z - radius)..=(query.z + radius);
for index in self.graph.node_indices() {
let node = &self.graph[index];
if query_x.contains(&node.location.x)
&& query_y.contains(&node.location.y)
&& query_z.contains(&node.location.z)
{
return Some(index);
}
}
None
}
}
impl<N, E, Ty: EdgeType> Default for Simulation<N, E, Ty> {
fn default() -> Self {
Self::from_graph(ForceGraph::default(), SimulationParameters::default())
}
}
#[derive(Clone, PartialEq)]
#[cfg_attr(feature = "json", derive(serde::Serialize))]
pub struct Node<N> {
pub name: String,
pub data: N,
pub location: Vec3,
pub old_location: Vec3,
pub velocity: Vec3,
}
impl<N> Node<N> {
pub fn new(name: impl AsRef<str>, data: N) -> Self {
Self {
name: name.as_ref().to_string(),
data,
location: Vec3::ZERO,
old_location: Vec3::ZERO,
velocity: Vec3::ZERO,
}
}
pub fn new_with_coords(name: impl AsRef<str>, data: N, location: Vec3) -> Self {
Self {
name: name.as_ref().to_string(),
data,
location,
old_location: Vec3::ZERO,
velocity: Vec3::ZERO,
}
}
}
impl<N: fmt::Debug> fmt::Debug for Node<N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Node")
.field("name", &self.name)
.field("data", &self.data)
.field("location", &self.location)
.finish()
}
}