20
20
positionUpdated: <function>, called when the position of the node has been updated
21
21
}
22
22
23
- Example:
23
+ Examples
24
24
25
25
create:
26
26
layout = new Layout.ForceDirected(graph, {width: 2000, height: 2000, iterations: 1000, layout: "3d"});
@@ -40,184 +40,183 @@ var Layout = Layout || {};
40
40
41
41
Layout . ForceDirected = function ( graph , options ) {
42
42
var options = options || { } ;
43
- var layout = options . layout || "2d" ;
43
+
44
+ this . layout = options . layout || "2d" ;
45
+ this . attraction_multiplier = options . attraction || 5 ;
46
+ this . repulsion_multiplier = options . repulsion || 0.75 ;
47
+ this . max_iterations = options . iterations || 1000 ;
48
+ this . graph = graph ;
49
+ this . width = options . width || 200 ;
50
+ this . height = options . height || 200 ;
51
+ this . finished = false ;
52
+
44
53
var callback_positionUpdated = options . positionUpdated ;
45
54
46
55
var EPSILON = 0.000001 ;
47
- var attraction_multiplier = options . attraction || 5 ;
48
56
var attraction_constant ;
49
- var repulsion_multiplier = options . repulsion || 0.75 ;
50
57
var repulsion_constant ;
51
58
var forceConstant ;
52
59
var layout_iterations = 0 ;
53
- var max_iterations = options . iterations || 1000 ;
54
60
var temperature = 0 ;
55
- var graph = graph ;
56
- var width = options . width || 200 ;
57
- var height = options . height || 200 ;
58
-
59
- this . finished = false ;
61
+ var nodes_length ;
62
+ var edges_length ;
63
+ var that = this ;
60
64
65
+ // performance test
66
+ var mean_time = 0 ;
67
+
61
68
this . init = function ( ) {
62
69
this . finished = false ;
63
- temperature = width / 10.0 ;
64
- forceConstant = Math . sqrt ( height * width / graph . nodes . length ) ;
65
- attraction_constant = attraction_multiplier * forceConstant ;
66
- repulsion_constant = repulsion_multiplier * forceConstant ;
70
+ temperature = this . width / 10.0 ;
71
+ nodes_length = this . graph . nodes . length ;
72
+ edges_length = this . graph . edges . length ;
73
+ forceConstant = Math . sqrt ( this . height * this . width / nodes_length ) ;
74
+ attraction_constant = this . attraction_multiplier * forceConstant ;
75
+ repulsion_constant = this . repulsion_multiplier * forceConstant ;
67
76
} ;
68
77
69
78
this . generate = function ( ) {
70
79
// TODO: stop if total force reached 0
71
- if ( layout_iterations < max_iterations ) {
72
- graph . nodes . forEach ( function ( node_v ) {
73
- calcRepulsion ( graph , node_v ) ;
74
- } ) ;
75
-
76
- graph . edges . forEach ( function ( edge ) {
77
- calcAttraction ( graph , edge ) ;
78
- } ) ;
79
-
80
- graph . nodes . forEach ( function ( node_v ) {
81
- calcPositions ( graph , node_v ) ;
82
- } ) ;
83
-
84
- // info.innerHTML = "node_force: " + parseInt(node_force) + "<br>edge_force: " + edge_force + "<br>div: " + (node_force-edge_force);
85
-
86
- // temperature *= (1.0 - (layout_iterations / max_iterations));
87
- layout_iterations ++ ;
88
- } else {
89
- this . finished = true ;
90
- return false ;
91
- }
92
- return true ;
93
- } ;
94
-
95
- var calcRepulsion = function ( graph , node_v ) {
96
- node_v . layout = node_v . layout || { } ;
97
- node_v . layout . offset_x = 0 ;
98
- node_v . layout . offset_y = 0 ;
99
- if ( layout === "3d" ) {
100
- node_v . layout . offset_z = 0 ;
101
- }
102
- node_v . layout . force = 0 ;
103
- node_v . layout . tmp_pos_x = node_v . layout . tmp_pos_x || node_v . position . x ;
104
- node_v . layout . tmp_pos_y = node_v . layout . tmp_pos_y || node_v . position . y ;
105
- if ( layout === "3d" ) {
106
- node_v . layout . tmp_pos_z = node_v . layout . tmp_pos_z || node_v . position . z ;
107
- }
108
-
109
- graph . nodes . forEach ( function ( node_u ) {
110
- if ( node_v . id != node_u . id ) {
111
- node_u . layout = node_u . layout || { } ;
112
- node_u . layout . tmp_pos_x = node_u . layout . tmp_pos_x || node_u . position . x ;
113
- node_u . layout . tmp_pos_y = node_u . layout . tmp_pos_y || node_u . position . y ;
114
- if ( layout === "3d" ) {
115
- node_u . layout . tmp_pos_z = node_u . layout . tmp_pos_z || node_u . position . z ;
80
+ if ( layout_iterations < this . max_iterations ) {
81
+ var start = new Date ( ) . getTime ( ) ;
82
+
83
+ // calculate repulsion
84
+ for ( var i = 0 ; i < nodes_length ; i ++ ) {
85
+ var node_v = graph . nodes [ i ] ;
86
+ node_v . layout = node_v . layout || { } ;
87
+ node_v . layout . offset_x = 0 ;
88
+ node_v . layout . offset_y = 0 ;
89
+ if ( this . layout === "3d" ) {
90
+ node_v . layout . offset_z = 0 ;
91
+ }
92
+ node_v . layout . force = 0 ;
93
+ node_v . layout . tmp_pos_x = node_v . layout . tmp_pos_x || node_v . position . x ;
94
+ node_v . layout . tmp_pos_y = node_v . layout . tmp_pos_y || node_v . position . y ;
95
+ if ( this . layout === "3d" ) {
96
+ node_v . layout . tmp_pos_z = node_v . layout . tmp_pos_z || node_v . position . z ;
116
97
}
117
98
118
- var delta_x = node_v . layout . tmp_pos_x - node_u . layout . tmp_pos_x ;
119
- var delta_y = node_v . layout . tmp_pos_y - node_u . layout . tmp_pos_y ;
120
- if ( layout === "3d" ) {
121
- var delta_z = node_v . layout . tmp_pos_z - node_u . layout . tmp_pos_z ;
99
+ for ( var j = 0 ; j < nodes_length ; j ++ ) {
100
+ var node_u = graph . nodes [ j ] ;
101
+ if ( node_v . id != node_u . id ) {
102
+ node_u . layout = node_u . layout || { } ;
103
+ node_u . layout . tmp_pos_x = node_u . layout . tmp_pos_x || node_u . position . x ;
104
+ node_u . layout . tmp_pos_y = node_u . layout . tmp_pos_y || node_u . position . y ;
105
+ if ( this . layout === "3d" ) {
106
+ node_u . layout . tmp_pos_z = node_u . layout . tmp_pos_z || node_u . position . z ;
107
+ }
108
+
109
+ var delta_x = node_v . layout . tmp_pos_x - node_u . layout . tmp_pos_x ;
110
+ var delta_y = node_v . layout . tmp_pos_y - node_u . layout . tmp_pos_y ;
111
+ if ( this . layout === "3d" ) {
112
+ var delta_z = node_v . layout . tmp_pos_z - node_u . layout . tmp_pos_z ;
113
+ }
114
+
115
+ var delta_length = Math . max ( EPSILON , Math . sqrt ( ( delta_x * delta_x ) + ( delta_y * delta_y ) ) ) ;
116
+ if ( this . layout === "3d" ) {
117
+ var delta_length_z = Math . max ( EPSILON , Math . sqrt ( ( delta_z * delta_z ) + ( delta_y * delta_y ) ) ) ;
118
+ }
119
+
120
+ var force = ( repulsion_constant * repulsion_constant ) / delta_length ;
121
+ if ( this . layout === "3d" ) {
122
+ var force_z = ( repulsion_constant * repulsion_constant ) / delta_length_z ;
123
+ }
124
+
125
+ node_v . layout . force += force ;
126
+ node_u . layout . force += force ;
127
+
128
+ node_v . layout . offset_x += ( delta_x / delta_length ) * force ;
129
+ node_v . layout . offset_y += ( delta_y / delta_length ) * force ;
130
+ if ( this . layout === "3d" ) {
131
+ node_v . layout . offset_z += ( delta_z / delta_length_z ) * force_z ;
132
+ }
133
+ }
122
134
}
135
+ }
123
136
137
+ // calc attraction
138
+ for ( var i = 0 ; i < edges_length ; i ++ ) {
139
+ var edge = graph . edges [ i ] ;
140
+ var delta_x = edge . source . layout . tmp_pos_x - edge . target . layout . tmp_pos_x ;
141
+ var delta_y = edge . source . layout . tmp_pos_y - edge . target . layout . tmp_pos_y ;
142
+ if ( this . layout === "3d" ) {
143
+ var delta_z = edge . source . layout . tmp_pos_z - edge . target . layout . tmp_pos_z ;
144
+ }
145
+
124
146
var delta_length = Math . max ( EPSILON , Math . sqrt ( ( delta_x * delta_x ) + ( delta_y * delta_y ) ) ) ;
125
- if ( layout === "3d" ) {
147
+ if ( this . layout === "3d" ) {
126
148
var delta_length_z = Math . max ( EPSILON , Math . sqrt ( ( delta_z * delta_z ) + ( delta_y * delta_y ) ) ) ;
127
149
}
128
-
129
- var force = ( repulsion_constant * repulsion_constant ) / delta_length ;
130
- if ( layout === "3d" ) {
131
- var force_z = ( repulsion_constant * repulsion_constant ) / delta_length_z ;
150
+ var force = ( delta_length * delta_length ) / attraction_constant ;
151
+ if ( this . layout === "3d" ) {
152
+ var force_z = ( delta_length_z * delta_length_z ) / attraction_constant ;
132
153
}
133
154
155
+ edge . source . layout . force -= force ;
156
+ edge . target . layout . force += force ;
134
157
135
- node_v . layout . force += force ;
136
- node_u . layout . force += force ;
137
-
138
- node_v . layout . offset_x += ( delta_x / delta_length ) * force ;
139
- node_v . layout . offset_y += ( delta_y / delta_length ) * force ;
140
- if ( layout === "3d" ) {
141
- node_v . layout . offset_z += ( delta_z / delta_length_z ) * force_z ;
158
+ edge . source . layout . offset_x -= ( delta_x / delta_length ) * force ;
159
+ edge . source . layout . offset_y -= ( delta_y / delta_length ) * force ;
160
+ if ( this . layout === "3d" ) {
161
+ edge . source . layout . offset_z -= ( delta_z / delta_length_z ) * force_z ;
142
162
}
143
- }
144
- } ) ;
145
- } ;
146
163
147
- var calcAttraction = function ( graph , edge ) {
148
- // var delta_x = edge.source.position.x - edge.target.position.x;
149
- // var delta_y = edge.source.position.y - edge.target.position.y;
150
- var delta_x = edge . source . layout . tmp_pos_x - edge . target . layout . tmp_pos_x ;
151
- var delta_y = edge . source . layout . tmp_pos_y - edge . target . layout . tmp_pos_y ;
152
- if ( layout === "3d" ) {
153
- var delta_z = edge . source . layout . tmp_pos_z - edge . target . layout . tmp_pos_z ;
154
- }
155
-
156
- var delta_length = Math . max ( EPSILON , Math . sqrt ( ( delta_x * delta_x ) + ( delta_y * delta_y ) ) ) ;
157
- if ( layout === "3d" ) {
158
- var delta_length_z = Math . max ( EPSILON , Math . sqrt ( ( delta_z * delta_z ) + ( delta_y * delta_y ) ) ) ;
159
- }
160
- var force = ( delta_length * delta_length ) / attraction_constant ;
161
- if ( layout === "3d" ) {
162
- var force_z = ( delta_length_z * delta_length_z ) / attraction_constant ;
163
- }
164
+ edge . target . layout . offset_x += ( delta_x / delta_length ) * force ;
165
+ edge . target . layout . offset_y += ( delta_y / delta_length ) * force ;
166
+ if ( this . layout === "3d" ) {
167
+ edge . target . layout . offset_z += ( delta_z / delta_length_z ) * force_z ;
168
+ }
169
+ }
170
+
171
+ // calc positions
172
+ for ( var i = 0 ; i < nodes_length ; i ++ ) {
173
+ var node = graph . nodes [ i ] ;
174
+ var delta_length = Math . max ( EPSILON , Math . sqrt ( node . layout . offset_x * node . layout . offset_x + node . layout . offset_y * node . layout . offset_y ) ) ;
175
+ if ( this . layout === "3d" ) {
176
+ var delta_length_z = Math . max ( EPSILON , Math . sqrt ( node . layout . offset_z * node . layout . offset_z + node . layout . offset_y * node . layout . offset_y ) ) ;
177
+ }
178
+ // alert(delta_length_z + " " + this.layout);
164
179
165
- edge . source . layout . force -= force ;
166
- edge . target . layout . force += force ;
180
+ node . layout . tmp_pos_x += ( node . layout . offset_x / delta_length ) * Math . min ( delta_length , temperature ) ;
181
+ node . layout . tmp_pos_y += ( node . layout . offset_y / delta_length ) * Math . min ( delta_length , temperature ) ;
182
+ if ( this . layout === "3d" ) {
183
+ node . layout . tmp_pos_z += ( node . layout . offset_z / delta_length_z ) * Math . min ( delta_length_z , temperature ) ;
184
+ }
167
185
168
- edge . source . layout . offset_x -= ( delta_x / delta_length ) * force ;
169
- edge . source . layout . offset_y -= ( delta_y / delta_length ) * force ;
170
- if ( layout === "3d" ) {
171
- edge . source . layout . offset_z -= ( delta_z / delta_length_z ) * force_z ;
172
- }
173
-
174
- edge . target . layout . offset_x += ( delta_x / delta_length ) * force ;
175
- edge . target . layout . offset_y += ( delta_y / delta_length ) * force ;
176
- if ( layout === "3d" ) {
177
- edge . target . layout . offset_z += ( delta_z / delta_length_z ) * force_z ;
178
- }
179
- } ;
186
+ var c = 200 ;
187
+ var updated = false ;
188
+ if ( node . position . x < ( node . layout . tmp_pos_x - c ) || node . position . x > ( node . layout . tmp_pos_x + c ) ) {
189
+ node . position . x -= ( node . position . x - node . layout . tmp_pos_x ) / 10 ;
190
+ updated = true ;
191
+ }
192
+ if ( node . position . y < ( node . layout . tmp_pos_y - c ) || node . position . y > ( node . layout . tmp_pos_y + c ) ) {
193
+ node . position . y -= ( node . position . y - node . layout . tmp_pos_y ) / 10 ;
194
+ updated = true ;
195
+ }
196
+ if ( this . layout === "3d" ) {
197
+ if ( node . position . z < ( node . layout . tmp_pos_z - c ) || node . position . z > ( node . layout . tmp_pos_z + c ) ) {
198
+ node . position . z -= ( node . position . z - node . layout . tmp_pos_z ) / 10 ;
199
+ updated = true ;
200
+ }
201
+ }
180
202
181
- var calcPositions = function ( graph , node ) {
182
- var delta_length = Math . max ( EPSILON , norm ( node ) ) ;
183
- if ( layout === "3d" ) {
184
- var delta_length_z = Math . max ( EPSILON , norm2 ( node ) ) ;
185
- }
186
-
187
- node . layout . tmp_pos_x += ( node . layout . offset_x / delta_length ) * Math . min ( delta_length , temperature ) ;
188
- node . layout . tmp_pos_y += ( node . layout . offset_y / delta_length ) * Math . min ( delta_length , temperature ) ;
189
- if ( layout === "3d" ) {
190
- node . layout . tmp_pos_z += ( node . layout . offset_z / delta_length_z ) * Math . min ( delta_length_z , temperature ) ;
191
- }
192
-
193
- var c = 200 ;
194
- var updated = false ;
195
- if ( node . position . x < ( node . layout . tmp_pos_x - c ) || node . position . x > ( node . layout . tmp_pos_x + c ) ) {
196
- node . position . x -= ( node . position . x - node . layout . tmp_pos_x ) / 10 ;
197
- updated = true ;
198
- }
199
- if ( node . position . y < ( node . layout . tmp_pos_y - c ) || node . position . y > ( node . layout . tmp_pos_y + c ) ) {
200
- node . position . y -= ( node . position . y - node . layout . tmp_pos_y ) / 10 ;
201
- updated = true ;
202
- }
203
- if ( layout === "3d" ) {
204
- if ( node . position . z < ( node . layout . tmp_pos_z - c ) || node . position . z > ( node . layout . tmp_pos_z + c ) ) {
205
- node . position . z -= ( node . position . z - node . layout . tmp_pos_z ) / 10 ;
206
- updated = true ;
203
+ if ( updated && typeof callback_positionUpdated === 'function' ) {
204
+ callback_positionUpdated ( node ) ;
205
+ }
207
206
}
208
- }
207
+ var end = new Date ( ) . getTime ( ) ;
208
+ mean_time += end - start ;
209
+ // info.innerHTML = "node_force: " + parseInt(node_force) + "<br>edge_force: " + edge_force + "<br>div: " + (node_force-edge_force);
209
210
210
- if ( updated && typeof callback_positionUpdated === 'function' ) {
211
- callback_positionUpdated ( node ) ;
211
+ // temperature *= (1.0 - (layout_iterations / this.max_iterations));
212
+ layout_iterations ++ ;
213
+ } else {
214
+ if ( ! this . finished ) {
215
+ console . log ( "Average time: " + ( mean_time / layout_iterations ) + " ms" ) ;
216
+ }
217
+ this . finished = true ;
218
+ return false ;
212
219
}
220
+ return true ;
213
221
} ;
214
-
215
- var norm = function ( node ) {
216
- return Math . sqrt ( node . layout . offset_x * node . layout . offset_x + node . layout . offset_y * node . layout . offset_y ) ;
217
- } ;
218
-
219
- var norm2 = function ( node ) {
220
- return Math . sqrt ( node . layout . offset_z * node . layout . offset_z + node . layout . offset_y * node . layout . offset_y ) ;
221
- } ;
222
-
223
222
} ;
0 commit comments