@@ -27,115 +27,86 @@ impl<'a> From<&'a NodeTemplate> for fj::Node {
27
27
28
28
pub struct ForceLayoutProvider {
29
29
simulation : fj:: Simulation ,
30
- node_index : ahash:: HashMap < NodeId , usize > ,
31
30
pub request : LayoutRequest ,
32
31
}
33
32
33
+ fn considered_edges ( request : & LayoutRequest ) -> Vec < ( usize , usize ) > {
34
+ let node_index: ahash:: HashMap < NodeId , usize > = request
35
+ . all_nodes ( )
36
+ . enumerate ( )
37
+ . map ( |( i, ( id, _) ) | ( id, i) )
38
+ . collect ( ) ;
39
+ request
40
+ . all_edges ( )
41
+ . filter ( |( id, _) | !id. is_self_edge ( ) )
42
+ . map ( |( id, _) | ( node_index[ & id. source ] , node_index[ & id. target ] ) )
43
+ . collect ( )
44
+ }
45
+
34
46
impl ForceLayoutProvider {
35
47
pub fn new ( request : LayoutRequest ) -> Self {
36
- Self :: new_impl ( request, None )
37
- }
38
-
39
- pub fn new_with_previous ( request : LayoutRequest , layout : & Layout ) -> Self {
40
- Self :: new_impl ( request, Some ( layout) )
41
- }
42
-
43
- // TODO(grtlr): Consider consuming the old layout to avoid re-allocating the extents.
44
- // That logic has to be revised when adding the blueprints anyways.
45
- fn new_impl ( request : LayoutRequest , layout : Option < & Layout > ) -> Self {
46
- let nodes = request. graphs . iter ( ) . flat_map ( |( _, graph_template) | {
47
- graph_template. nodes . iter ( ) . map ( |n| {
48
- let mut fj_node = fj:: Node :: from ( n. 1 ) ;
49
- if let Some ( rect) = layout. and_then ( |l| l. get_node ( n. 0 ) ) {
50
- let pos = rect. center ( ) ;
51
- fj_node = fj_node. position ( pos. x as f64 , pos. y as f64 ) ;
52
- }
53
-
54
- ( n. 0 , fj_node)
55
- } )
56
- } ) ;
57
-
58
- let mut node_index = ahash:: HashMap :: default ( ) ;
59
- let all_nodes: Vec < fj:: Node > = nodes
60
- . enumerate ( )
61
- . map ( |( i, n) | {
62
- node_index. insert ( * n. 0 , i) ;
63
- n. 1
64
- } )
65
- . collect ( ) ;
66
-
67
- let all_edges_iter = request
68
- . graphs
69
- . iter ( )
70
- . flat_map ( |( _, graph_template) | graph_template. edges . iter ( ) ) ;
71
-
72
- // Looking at self-edges does not make sense in a force-based layout, so we filter those out.
73
- let considered_edges = all_edges_iter
74
- . clone ( )
75
- . filter ( |( id, _) | !id. is_self_edge ( ) )
76
- . map ( |( id, _) | ( node_index[ & id. source ] , node_index[ & id. target ] ) ) ;
48
+ let nodes = request. all_nodes ( ) . map ( |( _, v) | fj:: Node :: from ( v) ) ;
49
+ let edges = considered_edges ( & request) ;
77
50
78
51
// TODO(grtlr): Currently we guesstimate good forces. Eventually these should be exposed as blueprints.
79
52
let simulation = fj:: SimulationBuilder :: default ( )
80
53
. with_alpha_decay ( 0.01 ) // TODO(grtlr): slows down the simulation for demo
81
- . build ( all_nodes)
82
- . add_force (
83
- "link" ,
84
- fj:: Link :: new ( considered_edges) . distance ( 50.0 ) . iterations ( 2 ) ,
85
- )
54
+ . build ( nodes)
55
+ . add_force ( "link" , fj:: Link :: new ( edges) . distance ( 50.0 ) . iterations ( 2 ) )
86
56
. add_force ( "charge" , fj:: ManyBody :: new ( ) )
87
57
// TODO(grtlr): This is a small stop-gap until we have blueprints to prevent nodes from flying away.
88
58
. add_force ( "x" , fj:: PositionX :: new ( ) . strength ( 0.01 ) )
89
59
. add_force ( "y" , fj:: PositionY :: new ( ) . strength ( 0.01 ) ) ;
90
60
91
61
Self {
92
62
simulation,
93
- node_index,
94
63
request,
95
64
}
96
65
}
97
66
98
- pub fn init ( & self ) -> Layout {
99
- let positions = self . simulation . positions ( ) . collect :: < Vec < _ > > ( ) ;
100
- let mut extents = ahash:: HashMap :: default ( ) ;
101
-
102
- for graph in self . request . graphs . values ( ) {
103
- for ( id, node) in & graph. nodes {
104
- let i = self . node_index [ id] ;
105
- let [ x, y] = positions[ i] ;
106
- let pos = Pos2 :: new ( x as f32 , y as f32 ) ;
107
- extents. insert ( * id, Rect :: from_center_size ( pos, node. size ) ) ;
67
+ pub fn new_with_previous ( request : LayoutRequest , layout : & Layout ) -> Self {
68
+ let nodes = request. all_nodes ( ) . map ( |( id, v) | {
69
+ if let Some ( rect) = layout. get_node ( & id) {
70
+ let pos = rect. center ( ) ;
71
+ fj:: Node :: from ( v) . position ( pos. x as f64 , pos. y as f64 )
72
+ } else {
73
+ fj:: Node :: from ( v)
108
74
}
109
- }
75
+ } ) ;
76
+ let edges = considered_edges ( & request) ;
77
+
78
+ // TODO(grtlr): Currently we guesstimate good forces. Eventually these should be exposed as blueprints.
79
+ let simulation = fj:: SimulationBuilder :: default ( )
80
+ . with_alpha_decay ( 0.01 ) // TODO(grtlr): slows down the simulation for demo
81
+ . build ( nodes)
82
+ . add_force ( "link" , fj:: Link :: new ( edges) . distance ( 50.0 ) . iterations ( 2 ) )
83
+ . add_force ( "charge" , fj:: ManyBody :: new ( ) )
84
+ // TODO(grtlr): This is a small stop-gap until we have blueprints to prevent nodes from flying away.
85
+ . add_force ( "x" , fj:: PositionX :: new ( ) . strength ( 0.01 ) )
86
+ . add_force ( "y" , fj:: PositionY :: new ( ) . strength ( 0.01 ) ) ;
110
87
111
- Layout {
112
- nodes : extents,
113
- // Without any real node positions, we probably don't want to draw edges either.
114
- edges : ahash:: HashMap :: default ( ) ,
115
- entities : Vec :: new ( ) ,
88
+ Self {
89
+ simulation,
90
+ request,
116
91
}
117
92
}
118
93
119
- /// Returns `true` if finished.
120
- pub fn tick ( & mut self , layout : & mut Layout ) -> bool {
121
- self . simulation . tick ( 1 ) ;
122
-
123
- let positions = self . simulation . positions ( ) . collect :: < Vec < _ > > ( ) ;
94
+ fn layout ( & self ) -> Layout {
95
+ // We make use of the fact here that the simulation is stable, i.e. the
96
+ // order of the nodes is the same as in the `request`.
97
+ let mut positions = self . simulation . positions ( ) ;
124
98
125
- // We clear all unnecessary data from the previous layout, but keep its space allocated.
126
- layout. entities . clear ( ) ;
127
- layout. edges . clear ( ) ;
99
+ let mut layout = Layout :: empty ( ) ;
128
100
129
101
for ( entity, graph) in & self . request . graphs {
130
102
let mut current_rect = Rect :: NOTHING ;
131
103
132
- for node in graph. nodes . keys ( ) {
133
- let extent = layout. nodes . get_mut ( node) . expect ( "node has to be present" ) ;
134
- let i = self . node_index [ node] ;
135
- let [ x, y] = positions[ i] ;
104
+ for ( node, template) in & graph. nodes {
105
+ let [ x, y] = positions. next ( ) . expect ( "positions has to match the layout" ) ;
136
106
let pos = Pos2 :: new ( x as f32 , y as f32 ) ;
137
- extent. set_center ( pos) ;
138
- current_rect = current_rect. union ( * extent) ;
107
+ let extent = Rect :: from_center_size ( pos, template. size ) ;
108
+ current_rect = current_rect. union ( extent) ;
109
+ layout. nodes . insert ( * node, extent) ;
139
110
}
140
111
141
112
layout. entities . push ( ( entity. clone ( ) , current_rect) ) ;
@@ -248,6 +219,16 @@ impl ForceLayoutProvider {
248
219
}
249
220
}
250
221
222
+ layout
223
+ }
224
+
225
+ /// Returns `true` if finished.
226
+ pub fn tick ( & mut self ) -> Layout {
227
+ self . simulation . tick ( 1 ) ;
228
+ self . layout ( )
229
+ }
230
+
231
+ pub fn is_finished ( & self ) -> bool {
251
232
self . simulation . finished ( )
252
233
}
253
234
}
0 commit comments