1pub use vector::{Vector, P2d};
2
3mod vector;
4
5#[inline]
6fn attractive_force<V>(p1: &V, p2: &V, k_s: f32) -> V where V: Vector<Scalar=f32>
7{
8 let mut force = p1.sub(p2);
9 let length = force.length_squared().sqrt();
10 let strength = length / k_s;
11 force.scale(strength);
12 return force;
13}
14
15#[inline]
16fn repulsive_force<V>(p1: &V, p2: &V, k_r: f32) -> V where V: Vector<Scalar=f32>
17{
18 let mut force = p1.sub(p2);
19 let length_squared = force.length_squared();
20 if length_squared > 0.0 {
21 let strength = k_r / length_squared;
22 force.scale(strength);
23 }
24 return force;
25}
26
27fn calculate_node_forces<V>(forces: &mut[V], node_positions: &[V], node_neighbors: &[Vec<usize>],
28 k_r: f32, k_s: f32) where V: Vector<Scalar=f32> {
29 let n = forces.len();
30 assert!(node_positions.len() == n);
31 assert!(node_neighbors.len() == n);
32
33 for force in &mut forces[..] {
35 force.reset();
36 }
37
38 for i1 in 0 .. n - 1 {
40 for i2 in i1 + 1 .. n {
41 let force = repulsive_force(&node_positions[i1], &node_positions[i2], k_r);
42 forces[i1].add_scaled(1.0, &force);
43 forces[i2].add_scaled(-1.0, &force);
44 }
45 }
46
47 for i1 in 0 .. n {
49 for i2 in node_neighbors[i1].iter().map(|&i| i as usize) {
50 if i1 < i2 {
51 let force = attractive_force(&node_positions[i1], &node_positions[i2], k_s);
52 forces[i1].add_scaled(-1.0, &force);
53 forces[i2].add_scaled(1.0, &force);
54 }
55 }
56 }
57}
58
59fn update_node_positions<V>(forces: &[V], node_positions: &mut[V], step: f32, min_pos: &V, max_pos: &V) -> f32
60where V: Vector<Scalar=f32> {
61 let n = forces.len();
62 assert!(node_positions.len() == n);
63
64 let mut sum_distance = 0.0;
65
66 for i in 0..n {
67 let before = node_positions[i].clone();
68
69 let length = forces[i].length_squared().sqrt();
70 if length > 0.0 {
71 node_positions[i].add_scaled(step / length, &forces[i]);
73 }
74 node_positions[i].clip_within(min_pos, max_pos);
75
76 sum_distance += before.sub(&node_positions[i]).length_squared().sqrt();
78 }
79
80 return sum_distance;
81}
82
83pub fn fruchterman_reingold<F,V>(step_fn: F, max_iter: usize, converge_eps: f32, k_r: f32, k_s: f32, min_pos: &V, max_pos: &V,
84 node_positions: &mut[V], node_neighbors: &[Vec<usize>])
85where V: Vector<Scalar=f32>, F: Fn(usize) -> f32
86{
87 let n = node_positions.len();
88 assert!(node_neighbors.len() == n);
89
90 let mut forces: Vec<V> = (0..n).map(|_| V::new()).collect();
92
93 let mut iter: usize = 0;
94 while iter < max_iter {
95 let step = step_fn(iter);
96 iter += 1;
97
98 calculate_node_forces(&mut forces, node_positions, node_neighbors, k_r, k_s);
99 let dist_moved = update_node_positions(&forces, node_positions, step, min_pos, max_pos);
100 if dist_moved < converge_eps {
101 break;
102 }
103 }
104}
105
106pub fn typical_fruchterman_reingold_2d(area: f32, node_positions: &mut[P2d], node_neighbors: &[Vec<usize>]) {
107 let n = node_positions.len();
108 assert!(node_neighbors.len() == n);
109
110 const MAX_ITER: usize = 300;
111 const EPS: f32 = 0.1;
112 const OPT_PAIR_SQR_DIST_SCALE: f32 = 0.3;
113
114 let min_pos = P2d(0.0, 0.0);
115 let max_pos = P2d(1.0, 1.0);
116 let step_fn = |iter| { 1.0 / (1.0 + (iter as f32) * 0.1) };
117
118 let k_r: f32 = OPT_PAIR_SQR_DIST_SCALE * area / (n as f32);
119 let k_s = k_r.sqrt();
120
121 fruchterman_reingold(step_fn, MAX_ITER, EPS, k_r, k_s, &min_pos, &max_pos, node_positions, node_neighbors);
122}