networkx spring_layout copy
This commit is contained in:
parent
7892fa5ac2
commit
a503ba80ed
|
@ -0,0 +1,132 @@
|
|||
use crate::graph::{new_edge_matrix, EdgeMatrix, Node, NodeVector};
|
||||
use crate::model::ForceModel;
|
||||
|
||||
/**
|
||||
* Translation of the NetworkX spring_layout function.
|
||||
*/
|
||||
pub struct NetworkXModel {
|
||||
edges: EdgeMatrix,
|
||||
size: usize,
|
||||
k: f32, // optimal distance
|
||||
t: f32, // temperature
|
||||
dt: f32, // temperature decrease
|
||||
}
|
||||
|
||||
impl NetworkXModel {
|
||||
pub fn new() -> Self {
|
||||
NetworkXModel {
|
||||
edges: new_edge_matrix(0),
|
||||
size: 0,
|
||||
k: 1.0,
|
||||
t: 1.0,
|
||||
dt: 0.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ForceModel for NetworkXModel {
|
||||
fn init(&mut self, edges: EdgeMatrix, size: usize, iterations: usize) {
|
||||
self.k = (1.0 / size as f32).sqrt();
|
||||
self.t = 0.1;
|
||||
self.dt = self.t / ((iterations + 1) as f32);
|
||||
self.t += self.dt; // prepare is called before the first step
|
||||
self.edges = edges;
|
||||
self.size = size;
|
||||
}
|
||||
|
||||
fn prepare(&mut self, _nodes: &NodeVector) {
|
||||
self.t -= self.dt; // decrease temperature
|
||||
}
|
||||
|
||||
fn step(&self, nodes: &NodeVector, i_node: usize) -> Node {
|
||||
let node = nodes[i_node].read().unwrap();
|
||||
let edges = self.edges.read().unwrap();
|
||||
|
||||
let node_x = node.x;
|
||||
let node_y = node.y;
|
||||
|
||||
let mut displacement_x = 0.0;
|
||||
let mut displacement_y = 0.0;
|
||||
|
||||
for o in 0..self.size {
|
||||
if o == i_node {
|
||||
continue;
|
||||
}
|
||||
|
||||
let edge = edges[i_node][o].weight;
|
||||
|
||||
let o_x: f32;
|
||||
let o_y: f32;
|
||||
{
|
||||
let other = nodes[o].read().unwrap();
|
||||
o_x = other.x;
|
||||
o_y = other.y;
|
||||
}
|
||||
// difference between node and other
|
||||
let delta_x = node_x - o_x;
|
||||
let delta_y = node_y - o_y;
|
||||
// distance between node and other
|
||||
let dist = (delta_x * delta_x + delta_y * delta_y).sqrt();
|
||||
// enforce minimum distance of 0.01
|
||||
let dist = if dist < 0.01 { 0.01 } else { dist };
|
||||
|
||||
// displacement "force"
|
||||
displacement_x += delta_x * (self.k.powi(2) / dist.powi(2) - edge * dist / self.k);
|
||||
displacement_y += delta_y * (self.k.powi(2) / dist.powi(2) - edge * dist / self.k);
|
||||
}
|
||||
// update positions
|
||||
let length = (displacement_x * displacement_x + displacement_y * displacement_y).sqrt();
|
||||
let length = if length < 0.01 { 0.01 } else { length };
|
||||
let delta_pos_x = displacement_x * self.t / length;
|
||||
let delta_pos_y = displacement_y * self.t / length;
|
||||
|
||||
Node {
|
||||
x: node_x + delta_pos_x,
|
||||
y: node_y + delta_pos_y,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::graph::{new_edge_matrix, new_node_vector};
|
||||
|
||||
#[test]
|
||||
fn test_simple_spring_model_attraction() {
|
||||
let mut model = NetworkXModel::new();
|
||||
let edges = new_edge_matrix(2);
|
||||
edges.write().unwrap()[0][1].weight = 1.0;
|
||||
edges.write().unwrap()[1][0].weight = 1.0;
|
||||
model.init(edges, 2, 1);
|
||||
|
||||
let nodes = new_node_vector(2);
|
||||
nodes[0].write().unwrap().x = 0.0;
|
||||
nodes[0].write().unwrap().y = 0.0;
|
||||
nodes[1].write().unwrap().x = 1.0;
|
||||
nodes[1].write().unwrap().y = 1.0;
|
||||
|
||||
model.prepare(&nodes);
|
||||
let node = model.step(&nodes, 0);
|
||||
assert!(node.x > 0.0);
|
||||
assert!(node.y > 0.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simple_spring_model_repulsion() {
|
||||
let mut model = NetworkXModel::new();
|
||||
let edges = new_edge_matrix(2);
|
||||
model.init(edges, 2, 1);
|
||||
|
||||
let nodes = new_node_vector(2);
|
||||
nodes[0].write().unwrap().x = 0.0;
|
||||
nodes[0].write().unwrap().y = 0.0;
|
||||
nodes[1].write().unwrap().x = 1.0;
|
||||
nodes[1].write().unwrap().y = 1.0;
|
||||
|
||||
model.prepare(&nodes);
|
||||
let node = model.step(&nodes, 0);
|
||||
assert!(node.x < 0.0);
|
||||
assert!(node.y < 0.0);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue