use glam::Vec3;
use petgraph::{EdgeType, Undirected};
use crate::ForceGraph;
use std::ops::RangeInclusive;
mod fruchterman_reingold;
mod handy;
pub use {
fruchterman_reingold::{fruchterman_reingold, fruchterman_reingold_weighted},
handy::handy,
hashlink::LinkedHashMap,
};
#[derive(Clone, Debug, PartialEq)]
pub enum Value {
Number(f32, RangeInclusive<f32>),
Bool(bool),
}
impl Value {
pub const fn bool(&self) -> Option<bool> {
match self {
Self::Bool(b) => Some(*b),
_ => None,
}
}
pub fn bool_mut(&mut self) -> Option<&mut bool> {
match self {
Self::Bool(b) => Some(b),
_ => None,
}
}
pub const fn number(&self) -> Option<f32> {
match self {
Self::Number(n, _) => Some(*n),
_ => None,
}
}
pub fn number_mut(&mut self) -> Option<&mut f32> {
match self {
Self::Number(n, _) => Some(n),
_ => None,
}
}
}
#[derive(Clone)]
pub struct Force<N, E, Ty = Undirected> {
pub dict: LinkedHashMap<String, Value>,
pub dict_default: LinkedHashMap<String, Value>,
pub name: &'static str,
pub continuous: bool,
pub info: Option<&'static str>,
pub update: fn(dict: &LinkedHashMap<String, Value>, graph: &mut ForceGraph<N, E, Ty>, dt: f32),
}
impl<N, E, Ty> Force<N, E, Ty> {
pub fn name(&self) -> &'static str {
self.name
}
pub fn info(&self) -> Option<&'static str> {
self.info
}
pub fn update(&self, graph: &mut ForceGraph<N, E, Ty>, dt: f32) {
(self.update)(&self.dict, graph, dt);
}
pub fn dict_mut(&mut self) -> &mut LinkedHashMap<String, Value> {
&mut self.dict
}
pub fn dict(&self) -> &LinkedHashMap<String, Value> {
&self.dict
}
pub fn reset(&mut self) {
self.dict = self.dict_default.clone();
}
pub fn continuous(&self) -> bool {
self.continuous
}
}
impl<N, E, Ty> PartialEq for Force<N, E, Ty> {
fn eq(&self, other: &Self) -> bool {
self.dict_default == other.dict_default
&& self.name == other.name
&& self.continuous == other.continuous
&& self.info == other.info
}
}
pub fn scale<N, E, Ty: EdgeType>() -> Force<N, E, Ty> {
fn update<N, E, Ty: EdgeType>(
dict: &LinkedHashMap<String, Value>,
graph: &mut ForceGraph<N, E, Ty>,
_dt: f32,
) {
let scale = dict.get("Scale Factor").unwrap().number().unwrap();
let center = Iterator::sum::<Vec3>(
graph
.node_weights()
.map(|x| x.location)
.collect::<Vec<Vec3>>()
.iter(),
) / graph.node_count() as f32;
for node in graph.node_weights_mut() {
node.location = ((node.location - center) * scale) + center;
}
}
let mut dict = LinkedHashMap::new();
dict.insert("Scale Factor".to_string(), Value::Number(1.5, 0.1..=2.0));
Force {
dict: dict.clone(),
dict_default: dict,
name: "Scale",
continuous: false,
info: Some("Scales the graph around its center."),
update,
}
}
pub fn translate<N, E, Ty: EdgeType>() -> Force<N, E, Ty> {
fn update<N, E, Ty: EdgeType>(
dict: &LinkedHashMap<String, Value>,
graph: &mut ForceGraph<N, E, Ty>,
_dt: f32,
) {
let distance = dict.get("Distance").unwrap().number().unwrap();
for node in graph.node_weights_mut() {
if dict.get("Up").unwrap().bool().unwrap() {
node.location.y -= distance;
}
if dict.get("Down").unwrap().bool().unwrap() {
node.location.y += distance;
}
if dict.get("Left").unwrap().bool().unwrap() {
node.location.x -= distance;
}
if dict.get("Right").unwrap().bool().unwrap() {
node.location.x += distance;
}
}
}
let mut dict = LinkedHashMap::new();
dict.insert("Distance".to_string(), Value::Number(7.0, 0.0..=100.0));
dict.insert("Up".to_string(), Value::Bool(false));
dict.insert("Down".to_string(), Value::Bool(false));
dict.insert("Left".to_string(), Value::Bool(false));
dict.insert("Right".to_string(), Value::Bool(false));
Force {
dict: dict.clone(),
dict_default: dict,
name: "Translate",
continuous: false,
info: Some("Moves the entire layout in any direction."),
update,
}
}
#[doc(hidden)]
pub fn unit_vector(a: Vec3, b: Vec3) -> Vec3 {
(b - a) / a.distance(b)
}