1- use egui:: { Pos2 , Vec2 } ;
1+ use egui:: Vec2 ;
2+ use petgraph:: { csr:: IndexType , EdgeType } ;
23use serde:: { Deserialize , Serialize } ;
34
4- use crate :: layouts:: { Layout , LayoutState } ;
5+ use crate :: {
6+ layouts:: { Layout , LayoutState } ,
7+ DisplayEdge , DisplayNode , Graph ,
8+ } ;
59
6- const FORCE_CENTER_REPEL : f32 = 25.0 ;
7- const FORCE_NEIGHBOR_ATTR : f32 = 0.01 ;
8- const DT : f32 = 0.5 ;
10+ const DT : f32 = 0.05 ;
11+ const GRAVITY : f32 = 3.0 ;
12+ const EPSILON : f32 = 0.001 ;
913
1014#[ derive( Debug , Clone , Serialize , Deserialize ) ]
1115pub struct State {
@@ -30,87 +34,83 @@ impl Layout<State> for ForceDirected {
3034 ForceDirected { state }
3135 }
3236
33- fn next < N , E , Ty , Ix , Dn , De > ( & mut self , g : & mut crate :: Graph < N , E , Ty , Ix , Dn , De > )
37+ fn next < N , E , Ty , Ix , Dn , De > ( & mut self , g : & mut Graph < N , E , Ty , Ix , Dn , De > )
3438 where
3539 N : Clone ,
3640 E : Clone ,
37- Ty : petgraph :: EdgeType ,
38- Ix : petgraph :: csr :: IndexType ,
39- Dn : crate :: DisplayNode < N , E , Ty , Ix > ,
40- De : crate :: DisplayEdge < N , E , Ty , Ix , Dn > ,
41+ Ty : EdgeType ,
42+ Ix : IndexType ,
43+ Dn : DisplayNode < N , E , Ty , Ix > ,
44+ De : DisplayEdge < N , E , Ty , Ix , Dn > ,
4145 {
42- if !self . state . is_running {
46+ if !self . state . is_running || g . node_count ( ) == 0 {
4347 return ;
4448 }
4549
46- let center = find_center ( g) ;
47- let indices: Vec < _ > = g. g_mut ( ) . node_indices ( ) . collect ( ) ;
48- for idx in indices {
49- let loc = g. g_mut ( ) . node_weight ( idx) . unwrap ( ) . location ( ) ;
50- let vc = center - loc;
51- let vc_len_sq = vc. length ( ) . powi ( 2 ) ;
52- let dx_center = if vc_len_sq > 0.0 {
53- -vc * ( FORCE_CENTER_REPEL / vc_len_sq) * DT
54- } else {
55- Vec2 :: ZERO
56- } ;
57-
58- let dx_neighbor = g
59- . g ( )
60- . neighbors_undirected ( idx)
61- . map ( |nbr_idx| {
62- let vn = g. g ( ) . node_weight ( nbr_idx) . unwrap ( ) . location ( ) - loc;
63- if vn. length ( ) > 0.0 {
64- vn * FORCE_NEIGHBOR_ATTR * DT
65- } else {
66- Vec2 :: ZERO
67- }
68- } )
69- . fold ( Vec2 :: ZERO , |acc, v| acc + v) ;
70-
71- let new_loc = loc + dx_center + dx_neighbor;
50+ /* ----------------------------------------------------------------- */
51+ /* pre-computed values */
52+ /* ----------------------------------------------------------------- */
53+ let n = g. node_count ( ) as f32 ;
54+ let area = g. bounds ( ) . area ( ) . max ( 1.0 ) ;
55+ let k = ( area / n) . sqrt ( ) ; // ideal edge length
56+ let centre = g. bounds ( ) . center ( ) ;
57+
58+ let indices: Vec < _ > = g. g ( ) . node_indices ( ) . collect ( ) ;
59+ let mut disp: Vec < Vec2 > = vec ! [ Vec2 :: ZERO ; indices. len( ) ] ;
60+
61+ /* ----------------------------------------------------------------- */
62+ /* PASS 1 — node-to-node repulsion (O(|V|²)) */
63+ /* ----------------------------------------------------------------- */
64+ for i in 0 ..indices. len ( ) {
65+ for j in ( i + 1 ) ..indices. len ( ) {
66+ let idx_i = indices[ i] ;
67+ let idx_j = indices[ j] ;
68+
69+ let delta = g. g ( ) . node_weight ( idx_i) . unwrap ( ) . location ( )
70+ - g. g ( ) . node_weight ( idx_j) . unwrap ( ) . location ( ) ;
71+ let dist = delta. length ( ) . max ( EPSILON ) ; // no division by 0
72+
73+ let force = ( k * k) / dist;
74+ let dir = delta / dist; // unit vector
75+
76+ disp[ i] += dir * force; // push i
77+ disp[ j] -= dir * force; // equal & opposite push j
78+ }
79+ }
80+
81+ /* ----------------------------------------------------------------- */
82+ /* PASS 2 — edge attraction + centre gravity */
83+ /* ----------------------------------------------------------------- */
84+ for ( idx_pos, & idx) in indices. iter ( ) . enumerate ( ) {
85+ let loc = g. g ( ) . node_weight ( idx) . unwrap ( ) . location ( ) ;
86+
87+ // attract towards every neighbour
88+ for nbr in g. g ( ) . neighbors_undirected ( idx) {
89+ let delta = g. g ( ) . node_weight ( nbr) . unwrap ( ) . location ( ) - loc;
90+ let dist = delta. length ( ) . max ( EPSILON ) ;
91+
92+ let force = ( dist * dist) / k;
93+ disp[ idx_pos] += ( delta / dist) * force;
94+ }
95+
96+ // gentle gravity to centre
97+ disp[ idx_pos] += ( centre - loc) * GRAVITY ;
98+ }
99+
100+ /* ----------------------------------------------------------------- */
101+ /* integrate & write back */
102+ /* ----------------------------------------------------------------- */
103+ for ( idx_pos, & idx) in indices. iter ( ) . enumerate ( ) {
104+ let loc = g. g ( ) . node_weight ( idx) . unwrap ( ) . location ( ) ;
105+ let new_loc = loc + disp[ idx_pos] * DT ;
72106
73107 g. g_mut ( )
74108 . node_weight_mut ( idx)
75109 . unwrap ( )
76110 . set_location ( new_loc) ;
77111 }
78112 }
79-
80113 fn state ( & self ) -> State {
81114 self . state . clone ( )
82115 }
83116}
84-
85- fn find_center < N , E , Ty , Ix , Dn , De > ( g : & crate :: Graph < N , E , Ty , Ix , Dn , De > ) -> Pos2
86- where
87- N : Clone ,
88- E : Clone ,
89- Ty : petgraph:: EdgeType ,
90- Ix : petgraph:: csr:: IndexType ,
91- Dn : crate :: DisplayNode < N , E , Ty , Ix > ,
92- De : crate :: DisplayEdge < N , E , Ty , Ix , Dn > ,
93- {
94- let mut min_x = f32:: MAX ;
95- let mut max_x = f32:: MIN ;
96- let mut min_y = f32:: MAX ;
97- let mut max_y = f32:: MIN ;
98-
99- for node in g. g ( ) . node_weights ( ) {
100- let loc = node. location ( ) ;
101- if loc. x < min_x {
102- min_x = loc. x ;
103- }
104- if loc. x > max_x {
105- max_x = loc. x ;
106- }
107- if loc. y < min_y {
108- min_y = loc. y ;
109- }
110- if loc. y > max_y {
111- max_y = loc. y ;
112- }
113- }
114-
115- Pos2 :: new ( ( min_x + max_x) / 2.0 , ( min_y + max_y) / 2.0 )
116- }
0 commit comments