|
31 | 31 | // Properties
|
32 | 32 | ppn: 10,
|
33 | 33 | ppe: 3,
|
| 34 | + ppq: 2, |
34 | 35 | maxForce: 10,
|
35 | 36 | iterations: 0,
|
36 | 37 | converged: false,
|
37 |
| - barnesHutDepthLimit: 20, |
38 | 38 |
|
39 | 39 | // Possible to change through config
|
40 | 40 | settings: {
|
|
46 | 46 | strongGravityMode: false,
|
47 | 47 | gravity: 1,
|
48 | 48 | barnesHutOptimize: false,
|
49 |
| - barnesHutTheta: 1.2 |
| 49 | + barnesHutTheta: 1.2, |
| 50 | + barnesHutDepthLimit: 4 |
50 | 51 | }
|
51 | 52 | };
|
52 | 53 |
|
|
125 | 126 | throw 'NaN alert!';
|
126 | 127 | }
|
127 | 128 |
|
128 |
| - /** |
129 |
| - * Barnes-Hut function |
130 |
| - */ |
131 |
| - function BarnesHut(nodes, depth) { |
132 |
| - var i, j, n, l, mass, size, distance; |
133 |
| - |
134 |
| - // Region |
135 |
| - var r = { |
136 |
| - size: 0, |
137 |
| - nodes: nodes, |
138 |
| - subregions: [], |
139 |
| - depth: depth, |
140 |
| - mass: 0, |
141 |
| - massCenterX: 0, |
142 |
| - massCenterY: 0, |
143 |
| - massSumX: 0, |
144 |
| - massSumY: 0 |
145 |
| - }; |
146 |
| - |
147 |
| - // Updating mass and geometry |
148 |
| - if (r.nodes.length > 1) { |
149 |
| - |
150 |
| - // Iterating through nodes |
151 |
| - for (i = 0, l = r.nodes.length; i < l; i++) { |
152 |
| - n = r.nodes[i]; |
153 |
| - |
154 |
| - mass = W.nodeMatrix[np(n, 'mass')]; |
155 |
| - r.mass += mass; |
156 |
| - r.massSumX += W.nodeMatrix[np(n, 'x')] * mass; |
157 |
| - r.massSumY += W.nodeMatrix[np(n, 'y')] * mass; |
158 |
| - } |
159 |
| - |
160 |
| - var massCenterX = r.massSumX / r.mass, |
161 |
| - massCenterY = r.massSumY / r.mass; |
162 |
| - |
163 |
| - // Computing size |
164 |
| - // MATH: something is amiss in the max thingy under here |
165 |
| - for (i = 0, l = r.nodes.length; i < l; i++) { |
166 |
| - n = r.nodes[i]; |
167 |
| - |
168 |
| - distance = 2 * Math.sqrt( |
169 |
| - Math.pow((W.nodeMatrix[np(n, 'x')] - r.massCenterX), 2) + |
170 |
| - Math.pow((W.nodeMatrix[np(n, 'y')] - r.massCenterY), 2) |
171 |
| - ); |
172 |
| - size = (size === undefined) ? |
173 |
| - distance : |
174 |
| - Math.max(size, distance); |
175 |
| - } |
176 |
| - } |
177 |
| - |
178 |
| - // Updating region |
179 |
| - r.massCenterX = massCenterX; |
180 |
| - r.massCenterY = massCenterY; |
181 |
| - r.size = size; |
182 |
| - |
183 |
| - // Build subregions |
184 |
| - if (r.nodes.length > 1) { |
185 |
| - var subareas = [[], [], [], []], |
186 |
| - nextDepth = r.depth + 1; |
187 |
| - |
188 |
| - // TODO: write this better. this is a scandal. |
189 |
| - for (i = 0, l = r.nodes.length; i < l; i++) { |
190 |
| - n = r.nodes[i]; |
191 |
| - |
192 |
| - if (W.nodeMatrix[np(n, 'x')] < r.massCenterX) { |
193 |
| - |
194 |
| - // Left |
195 |
| - if ((W.nodeMatrix[np(n, 'y')] < r.massCenterY)) |
196 |
| - subareas[0].push(n); |
197 |
| - else |
198 |
| - subareas[1].push(n); |
199 |
| - } |
200 |
| - else { |
201 |
| - |
202 |
| - // Right |
203 |
| - if ((W.nodeMatrix[np(n, 'y')] < r.massCenterY)) |
204 |
| - subareas[2].push(n); |
205 |
| - else |
206 |
| - subareas[3].push(n); |
207 |
| - } |
208 |
| - } |
209 |
| - |
210 |
| - for (i = 0; i < 4; i++) { |
211 |
| - if (subareas[i].length > 0) { |
212 |
| - if (nextDepth <= W.barnesHutDepthLimit && |
213 |
| - subareas[i].length < r.nodes.length) { |
214 |
| - r.subregions.push(BarnesHut(subareas[i], nextDepth)); |
215 |
| - } |
216 |
| - else { |
217 |
| - for (j = 0, l = subareas[i].length; j < l; j++) |
218 |
| - r.subregions.push( |
219 |
| - BarnesHut([subareas[i][j]], nextDepth) |
220 |
| - ); |
221 |
| - } |
222 |
| - } |
223 |
| - } |
224 |
| - } |
225 |
| - |
226 |
| - // Forces application |
227 |
| - r.applyForce = function(n) { |
228 |
| - var coefficient = W.settings.scalingRatio, |
229 |
| - xDist, |
230 |
| - yDist, |
231 |
| - distance, |
232 |
| - factor, |
233 |
| - n1, |
234 |
| - n2, |
235 |
| - i, |
236 |
| - l; |
237 |
| - |
238 |
| - if (this.nodes.length < 2) { |
239 |
| - n1 = n; |
240 |
| - n2 = this.nodes[0]; |
241 |
| - |
242 |
| - // Apply the force node to node |
243 |
| - // Common to both methods |
244 |
| - xDist = W.nodeMatrix[np(n1, 'x')] - W.nodeMatrix[np(n2, 'x')]; |
245 |
| - yDist = W.nodeMatrix[np(n1, 'y')] - W.nodeMatrix[np(n2, 'y')]; |
246 |
| - |
247 |
| - if (W.settings.adjustSize) { |
248 |
| - |
249 |
| - //-- Anticollision Linear Repulsion |
250 |
| - distance = Math.sqrt(xDist * xDist + yDist * yDist) - |
251 |
| - W.nodeMatrix[np(n1, 'size')] - |
252 |
| - W.nodeMatrix[np(n2, 'size')]; |
253 |
| - |
254 |
| - if (distance > 0) { |
255 |
| - factor = coefficient * |
256 |
| - W.nodeMatrix[np(n1, 'mass')] * |
257 |
| - W.nodeMatrix[np(n2, 'mass')] / |
258 |
| - distance / distance; |
259 |
| - |
260 |
| - // Updating nodes' dx and dy |
261 |
| - W.nodeMatrix[np(n1, 'dx')] += xDist * factor; |
262 |
| - W.nodeMatrix[np(n1, 'dy')] += yDist * factor; |
263 |
| - |
264 |
| - W.nodeMatrix[np(n2, 'dx')] += xDist * factor; |
265 |
| - W.nodeMatrix[np(n2, 'dy')] += yDist * factor; |
266 |
| - } |
267 |
| - else if (distance < 0) { |
268 |
| - factor = 100 * coefficient * |
269 |
| - W.nodeMatrix[np(n1, 'mass')] * |
270 |
| - W.nodeMatrix[np(n2, 'mass')]; |
271 |
| - |
272 |
| - // Updating nodes' dx and dy |
273 |
| - W.nodeMatrix[np(n1, 'dx')] += xDist * factor; |
274 |
| - W.nodeMatrix[np(n1, 'dy')] += yDist * factor; |
275 |
| - |
276 |
| - W.nodeMatrix[np(n2, 'dx')] -= xDist * factor; |
277 |
| - W.nodeMatrix[np(n2, 'dy')] -= yDist * factor; |
278 |
| - } |
279 |
| - } |
280 |
| - else { |
281 |
| - |
282 |
| - //-- Linear Repulsion |
283 |
| - distance = Math.sqrt(xDist * xDist + yDist * yDist); |
284 |
| - |
285 |
| - if (distance > 0) { |
286 |
| - factor = W.settings.scalingRatio * |
287 |
| - W.nodeMatrix[np(n1, 'mass')] * |
288 |
| - W.nodeMatrix[np(n2, 'mass')] / |
289 |
| - distance / distance; |
290 |
| - |
291 |
| - // Updating nodes' dx and dy |
292 |
| - W.nodeMatrix[np(n1, 'dx')] += xDist * factor; |
293 |
| - W.nodeMatrix[np(n1, 'dy')] += yDist * factor; |
294 |
| - |
295 |
| - W.nodeMatrix[np(n2, 'dx')] -= xDist * factor; |
296 |
| - W.nodeMatrix[np(n2, 'dy')] -= yDist * factor; |
297 |
| - } |
298 |
| - } |
299 |
| - } |
300 |
| - else { |
301 |
| - distance = Math.sqrt( |
302 |
| - (Math.pow(W.nodeMatrix[np(n, 'x')], 2)) + |
303 |
| - (Math.pow(W.nodeMatrix[np(n, 'y')], 2)) |
304 |
| - ); |
305 |
| - |
306 |
| - if (distance * W.settings.barnesHutTheta > this.size) { |
307 |
| - xDist = W.nodeMatrix[np(n, 'x')] - this.massCenterX; |
308 |
| - yDist = W.nodeMatrix[np(n, 'y')] - this.massCenterY; |
309 |
| - |
310 |
| - if (W.settings.adjustSize) { |
311 |
| - |
312 |
| - //-- Linear Anti-collision Repulsion |
313 |
| - if (distance > 0) { |
314 |
| - factor = coefficient * W.nodeMatrix[np(n, 'mass')] * |
315 |
| - this.mass / distance / distance; |
316 |
| - |
317 |
| - W.nodeMatrix[np(n, 'dx')] += xDist * factor; |
318 |
| - W.nodeMatrix[np(n, 'dy')] += yDist * factor; |
319 |
| - } |
320 |
| - else if (distance < 0) { |
321 |
| - factor = -coefficient * W.nodeMatrix[np(n, 'mass')] * |
322 |
| - this.mass /distance; |
323 |
| - |
324 |
| - W.nodeMatrix[np(n, 'dx')] += xDist * factor; |
325 |
| - W.nodeMatrix[np(n, 'dy')] += yDist * factor; |
326 |
| - } |
327 |
| - } |
328 |
| - else { |
329 |
| - |
330 |
| - //-- Linear Repulsion |
331 |
| - if (distance > 0) { |
332 |
| - factor = coefficient * W.nodeMatrix[np(n, 'mass')] * |
333 |
| - this.mass / distance / distance; |
334 |
| - |
335 |
| - W.nodeMatrix[np(n, 'dx')] += xDist * factor; |
336 |
| - W.nodeMatrix[np(n, 'dy')] += yDist * factor; |
337 |
| - } |
338 |
| - } |
339 |
| - } |
340 |
| - else { |
341 |
| - for (i = 0, l = this.subregions.length; i < l; i++) { |
342 |
| - this.subregions[i].applyForce(n); |
343 |
| - } |
344 |
| - } |
345 |
| - } |
346 |
| - } |
347 |
| - |
348 |
| - // Returning the object |
349 |
| - return r; |
350 |
| - } |
351 |
| - |
352 | 129 | /**
|
353 | 130 | * Algorithm initialization
|
354 | 131 | */
|
|
372 | 149 |
|
373 | 150 | // MATH: get distances stuff and power 2 issues
|
374 | 151 | function pass() {
|
375 |
| - var n, n1, n2, e, w, g; |
| 152 | + var a, i, j, l, n, n1, n2, q, e, w, g; |
376 | 153 |
|
377 | 154 | var barnesHutNodes = [],
|
378 | 155 | rootRegion,
|
|
388 | 165 | // 1) Initializing layout data
|
389 | 166 | //-----------------------------
|
390 | 167 |
|
391 |
| - // Resetting positions |
| 168 | + // Resetting positions & computing max values |
392 | 169 | for (n = 0; n < W.nodesLength; n += W.ppn) {
|
393 | 170 | W.nodeMatrix[np(n, 'old_dx')] = W.nodeMatrix[np(n, 'dx')];
|
394 | 171 | W.nodeMatrix[np(n, 'old_dy')] = W.nodeMatrix[np(n, 'dy')];
|
395 | 172 | W.nodeMatrix[np(n, 'dx')] = 0;
|
396 | 173 | W.nodeMatrix[np(n, 'dy')] = 0;
|
397 | 174 | }
|
398 | 175 |
|
399 |
| - // Barnes-Hut root region |
400 |
| - if (W.settings.barnesHutOptimize) { |
401 |
| - |
402 |
| - for (n = 0; n < W.nodesLength; n += W.ppn) { |
403 |
| - barnesHutNodes.push(n); |
404 |
| - } |
405 |
| - rootRegion = BarnesHut(barnesHutNodes) |
406 |
| - } |
407 |
| - |
408 | 176 | // If outbound attraction distribution, compensate
|
409 | 177 | if (W.settings.outboundAttractionDistribution) {
|
410 | 178 | outboundAttCompensation = 0;
|
|
416 | 184 | }
|
417 | 185 |
|
418 | 186 |
|
| 187 | + // 1.bis) Barnes-Hut computation |
| 188 | + //------------------------------ |
| 189 | + if (W.settings.barnesHutOptimize) { |
| 190 | + |
| 191 | + // Necessary variables |
| 192 | + // UInt16 should be enough, if not, switch to UInt32 |
| 193 | + var quadNb = Math.pow(2, 2 * W.settings.barnesHutDepthLimit), |
| 194 | + quadRowNb = Math.pow(2, W.settings.barnesHutDepthLimit), |
| 195 | + quads = new UInt16Array(quadNb), |
| 196 | + quadsProperties = new Float32Array(quadNb * W.ppq), |
| 197 | + quadsSteps = new UInt16Array(quadNb), |
| 198 | + quadsAcc = new UInt16Array(quadNb), |
| 199 | + nodesQuads = new UInt16Array(W.nodesLength), |
| 200 | + quadsNodes = new UInt16Array(W.nodesLength), |
| 201 | + maxX = 0, |
| 202 | + maxY = 0, |
| 203 | + minX = 0, |
| 204 | + minY = 0, |
| 205 | + xIndex, |
| 206 | + yIndex; |
| 207 | + |
| 208 | + // Retrieving max values |
| 209 | + // OPTIMIZE: this could be computed on the n loop preceding this one |
| 210 | + // but at the cost of doing it even when Barnes-Hut is disabled |
| 211 | + for (n = 0; n < W.nodesLength; n += W.ppn) { |
| 212 | + maxX = Math.max(maxX, W.nodeMatrix[np(n, 'x')]); |
| 213 | + minX = Math.min(minX, W.nodeMatrix[np(n, 'x')]); |
| 214 | + maxY = Math.max(maxY, W.nodeMatrix[np(n, 'y')]); |
| 215 | + minY = Math.min(minY, W.nodeMatrix[np(n, 'y')]); |
| 216 | + } |
| 217 | + |
| 218 | + // Adding epsilon to max values |
| 219 | + maxX += 0.0001 * (maxX - minX); |
| 220 | + maxY += 0.0001 * (maxY - minY); |
| 221 | + |
| 222 | + // Assigning nodes to quads |
| 223 | + for (n = 0, i = 0; n < W.nodesLength; n += W.ppn, i++) { |
| 224 | + xIndex = ((W.nodeMatrix[np(n, 'x')] - minX) / (maxX - minX) * |
| 225 | + Math.pow(2, W.settings.barnesHutDepthLimit)) | 0; |
| 226 | + |
| 227 | + yIndex = ((W.nodeMatrix[np(n, 'y')] - minY) / (maxY - minY) * |
| 228 | + Math.pow(2, W.settings.barnesHutDepthLimit)) | 0; |
| 229 | + |
| 230 | + // OPTIMIZE: possible to gain some really little time here |
| 231 | + quads[xIndex * quadRowNb + yIndex] += 1; |
| 232 | + nodesQuads[i] = xIndex * quadRowNb + yIndex; |
| 233 | + } |
| 234 | + |
| 235 | + // Computing quad steps |
| 236 | + // ALEXIS: here we need to build the second array containing nodes ids |
| 237 | + // in order for the quads iteration in force applications later. |
| 238 | + for (a = 0, i = 0; i < quadNb; i++) { |
| 239 | + a += quads[i]; |
| 240 | + quadsSteps[i] = a; |
| 241 | + } |
| 242 | + } |
| 243 | + |
| 244 | + |
419 | 245 | // 2) Repulsion
|
420 | 246 | //--------------
|
421 | 247 | // NOTES: adjustSize = antiCollision & scalingRatio = coefficient
|
422 | 248 |
|
423 | 249 | if (W.settings.barnesHutOptimize) {
|
424 | 250 |
|
425 |
| - // Applying repulsion through regions |
426 |
| - for (n = 0; n < W.nodesLength; n += W.ppn) { |
427 |
| - rootRegion.applyForce(n); |
428 |
| - } |
| 251 | + // Applying forces through Barnes-Hut |
| 252 | + // TODO |
429 | 253 | }
|
430 | 254 | else {
|
431 | 255 |
|
|
0 commit comments