vis-rails 0.0.4 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -13
- data/lib/vis/rails/version.rb +1 -1
- data/vendor/assets/component/emitter.js +162 -0
- data/vendor/assets/javascripts/vis.js +1 -0
- data/vendor/assets/vis/DataSet.js +8 -2
- data/vendor/assets/vis/DataView.js +8 -4
- data/vendor/assets/vis/graph/Edge.js +210 -78
- data/vendor/assets/vis/graph/Graph.js +474 -652
- data/vendor/assets/vis/graph/Node.js +119 -82
- data/vendor/assets/vis/graph/css/graph-manipulation.css +128 -0
- data/vendor/assets/vis/graph/css/graph-navigation.css +62 -0
- data/vendor/assets/vis/graph/graphMixins/ClusterMixin.js +1141 -0
- data/vendor/assets/vis/graph/graphMixins/HierarchicalLayoutMixin.js +296 -0
- data/vendor/assets/vis/graph/graphMixins/ManipulationMixin.js +433 -0
- data/vendor/assets/vis/graph/graphMixins/MixinLoader.js +201 -0
- data/vendor/assets/vis/graph/graphMixins/NavigationMixin.js +173 -0
- data/vendor/assets/vis/graph/graphMixins/SectorsMixin.js +552 -0
- data/vendor/assets/vis/graph/graphMixins/SelectionMixin.js +558 -0
- data/vendor/assets/vis/graph/graphMixins/physics/BarnesHut.js +373 -0
- data/vendor/assets/vis/graph/graphMixins/physics/HierarchialRepulsion.js +64 -0
- data/vendor/assets/vis/graph/graphMixins/physics/PhysicsMixin.js +513 -0
- data/vendor/assets/vis/graph/graphMixins/physics/Repulsion.js +66 -0
- data/vendor/assets/vis/graph/img/acceptDeleteIcon.png +0 -0
- data/vendor/assets/vis/graph/img/addNodeIcon.png +0 -0
- data/vendor/assets/vis/graph/img/backIcon.png +0 -0
- data/vendor/assets/vis/graph/img/connectIcon.png +0 -0
- data/vendor/assets/vis/graph/img/cross.png +0 -0
- data/vendor/assets/vis/graph/img/cross2.png +0 -0
- data/vendor/assets/vis/graph/img/deleteIcon.png +0 -0
- data/vendor/assets/vis/graph/img/downArrow.png +0 -0
- data/vendor/assets/vis/graph/img/editIcon.png +0 -0
- data/vendor/assets/vis/graph/img/leftArrow.png +0 -0
- data/vendor/assets/vis/graph/img/rightArrow.png +0 -0
- data/vendor/assets/vis/graph/img/upArrow.png +0 -0
- data/vendor/assets/vis/module/exports.js +0 -2
- data/vendor/assets/vis/module/header.js +2 -2
- data/vendor/assets/vis/module/imports.js +1 -2
- data/vendor/assets/vis/timeline/Controller.js +56 -45
- data/vendor/assets/vis/timeline/Range.js +68 -62
- data/vendor/assets/vis/timeline/Stack.js +11 -13
- data/vendor/assets/vis/timeline/TimeStep.js +43 -38
- data/vendor/assets/vis/timeline/Timeline.js +215 -93
- data/vendor/assets/vis/timeline/component/Component.js +19 -3
- data/vendor/assets/vis/timeline/component/CurrentTime.js +1 -1
- data/vendor/assets/vis/timeline/component/CustomTime.js +39 -120
- data/vendor/assets/vis/timeline/component/GroupSet.js +35 -1
- data/vendor/assets/vis/timeline/component/ItemSet.js +272 -9
- data/vendor/assets/vis/timeline/component/RootPanel.js +59 -47
- data/vendor/assets/vis/timeline/component/TimeAxis.js +10 -0
- data/vendor/assets/vis/timeline/component/css/item.css +53 -22
- data/vendor/assets/vis/timeline/component/item/Item.js +40 -5
- data/vendor/assets/vis/timeline/component/item/ItemBox.js +3 -1
- data/vendor/assets/vis/timeline/component/item/ItemPoint.js +3 -1
- data/vendor/assets/vis/timeline/component/item/ItemRange.js +67 -3
- data/vendor/assets/vis/timeline/component/item/ItemRangeOverflow.js +37 -9
- data/vendor/assets/vis/timeline/img/delete.png +0 -0
- data/vendor/assets/vis/util.js +169 -30
- metadata +39 -12
@@ -0,0 +1,373 @@
|
|
1
|
+
/**
|
2
|
+
* Created by Alex on 2/10/14.
|
3
|
+
*/
|
4
|
+
|
5
|
+
var barnesHutMixin = {
|
6
|
+
|
7
|
+
/**
|
8
|
+
* This function calculates the forces the nodes apply on eachother based on a gravitational model.
|
9
|
+
* The Barnes Hut method is used to speed up this N-body simulation.
|
10
|
+
*
|
11
|
+
* @private
|
12
|
+
*/
|
13
|
+
_calculateNodeForces : function() {
|
14
|
+
var node;
|
15
|
+
var nodes = this.calculationNodes;
|
16
|
+
var nodeIndices = this.calculationNodeIndices;
|
17
|
+
var nodeCount = nodeIndices.length;
|
18
|
+
|
19
|
+
this._formBarnesHutTree(nodes,nodeIndices);
|
20
|
+
|
21
|
+
var barnesHutTree = this.barnesHutTree;
|
22
|
+
|
23
|
+
// place the nodes one by one recursively
|
24
|
+
for (var i = 0; i < nodeCount; i++) {
|
25
|
+
node = nodes[nodeIndices[i]];
|
26
|
+
// starting with root is irrelevant, it never passes the BarnesHut condition
|
27
|
+
this._getForceContribution(barnesHutTree.root.children.NW,node);
|
28
|
+
this._getForceContribution(barnesHutTree.root.children.NE,node);
|
29
|
+
this._getForceContribution(barnesHutTree.root.children.SW,node);
|
30
|
+
this._getForceContribution(barnesHutTree.root.children.SE,node);
|
31
|
+
}
|
32
|
+
},
|
33
|
+
|
34
|
+
|
35
|
+
/**
|
36
|
+
* This function traverses the barnesHutTree. It checks when it can approximate distant nodes with their center of mass.
|
37
|
+
* If a region contains a single node, we check if it is not itself, then we apply the force.
|
38
|
+
*
|
39
|
+
* @param parentBranch
|
40
|
+
* @param node
|
41
|
+
* @private
|
42
|
+
*/
|
43
|
+
_getForceContribution : function(parentBranch,node) {
|
44
|
+
// we get no force contribution from an empty region
|
45
|
+
if (parentBranch.childrenCount > 0) {
|
46
|
+
var dx,dy,distance;
|
47
|
+
|
48
|
+
// get the distance from the center of mass to the node.
|
49
|
+
dx = parentBranch.centerOfMass.x - node.x;
|
50
|
+
dy = parentBranch.centerOfMass.y - node.y;
|
51
|
+
distance = Math.sqrt(dx * dx + dy * dy);
|
52
|
+
|
53
|
+
// BarnesHut condition
|
54
|
+
// original condition : s/d < theta = passed === d/s > 1/theta = passed
|
55
|
+
// calcSize = 1/s --> d * 1/s > 1/theta = passed
|
56
|
+
if (distance * parentBranch.calcSize > this.constants.physics.barnesHut.theta) {
|
57
|
+
// duplicate code to reduce function calls to speed up program
|
58
|
+
if (distance == 0) {
|
59
|
+
distance = 0.1*Math.random();
|
60
|
+
dx = distance;
|
61
|
+
}
|
62
|
+
var gravityForce = this.constants.physics.barnesHut.gravitationalConstant * parentBranch.mass * node.mass / (distance * distance * distance);
|
63
|
+
var fx = dx * gravityForce;
|
64
|
+
var fy = dy * gravityForce;
|
65
|
+
node.fx += fx;
|
66
|
+
node.fy += fy;
|
67
|
+
}
|
68
|
+
else {
|
69
|
+
// Did not pass the condition, go into children if available
|
70
|
+
if (parentBranch.childrenCount == 4) {
|
71
|
+
this._getForceContribution(parentBranch.children.NW,node);
|
72
|
+
this._getForceContribution(parentBranch.children.NE,node);
|
73
|
+
this._getForceContribution(parentBranch.children.SW,node);
|
74
|
+
this._getForceContribution(parentBranch.children.SE,node);
|
75
|
+
}
|
76
|
+
else { // parentBranch must have only one node, if it was empty we wouldnt be here
|
77
|
+
if (parentBranch.children.data.id != node.id) { // if it is not self
|
78
|
+
// duplicate code to reduce function calls to speed up program
|
79
|
+
if (distance == 0) {
|
80
|
+
distance = 0.5*Math.random();
|
81
|
+
dx = distance;
|
82
|
+
}
|
83
|
+
var gravityForce = this.constants.physics.barnesHut.gravitationalConstant * parentBranch.mass * node.mass / (distance * distance * distance);
|
84
|
+
var fx = dx * gravityForce;
|
85
|
+
var fy = dy * gravityForce;
|
86
|
+
node.fx += fx;
|
87
|
+
node.fy += fy;
|
88
|
+
}
|
89
|
+
}
|
90
|
+
}
|
91
|
+
}
|
92
|
+
},
|
93
|
+
|
94
|
+
/**
|
95
|
+
* This function constructs the barnesHut tree recursively. It creates the root, splits it and starts placing the nodes.
|
96
|
+
*
|
97
|
+
* @param nodes
|
98
|
+
* @param nodeIndices
|
99
|
+
* @private
|
100
|
+
*/
|
101
|
+
_formBarnesHutTree : function(nodes,nodeIndices) {
|
102
|
+
var node;
|
103
|
+
var nodeCount = nodeIndices.length;
|
104
|
+
|
105
|
+
var minX = Number.MAX_VALUE,
|
106
|
+
minY = Number.MAX_VALUE,
|
107
|
+
maxX =-Number.MAX_VALUE,
|
108
|
+
maxY =-Number.MAX_VALUE;
|
109
|
+
|
110
|
+
// get the range of the nodes
|
111
|
+
for (var i = 0; i < nodeCount; i++) {
|
112
|
+
var x = nodes[nodeIndices[i]].x;
|
113
|
+
var y = nodes[nodeIndices[i]].y;
|
114
|
+
if (x < minX) { minX = x; }
|
115
|
+
if (x > maxX) { maxX = x; }
|
116
|
+
if (y < minY) { minY = y; }
|
117
|
+
if (y > maxY) { maxY = y; }
|
118
|
+
}
|
119
|
+
// make the range a square
|
120
|
+
var sizeDiff = Math.abs(maxX - minX) - Math.abs(maxY - minY); // difference between X and Y
|
121
|
+
if (sizeDiff > 0) {minY -= 0.5 * sizeDiff; maxY += 0.5 * sizeDiff;} // xSize > ySize
|
122
|
+
else {minX += 0.5 * sizeDiff; maxX -= 0.5 * sizeDiff;} // xSize < ySize
|
123
|
+
|
124
|
+
|
125
|
+
var minimumTreeSize = 1e-5;
|
126
|
+
var rootSize = Math.max(minimumTreeSize,Math.abs(maxX - minX));
|
127
|
+
var halfRootSize = 0.5 * rootSize;
|
128
|
+
var centerX = 0.5 * (minX + maxX), centerY = 0.5 * (minY + maxY);
|
129
|
+
|
130
|
+
// construct the barnesHutTree
|
131
|
+
var barnesHutTree = {root:{
|
132
|
+
centerOfMass:{x:0,y:0}, // Center of Mass
|
133
|
+
mass:0,
|
134
|
+
range: {minX:centerX-halfRootSize,maxX:centerX+halfRootSize,
|
135
|
+
minY:centerY-halfRootSize,maxY:centerY+halfRootSize},
|
136
|
+
size: rootSize,
|
137
|
+
calcSize: 1 / rootSize,
|
138
|
+
children: {data:null},
|
139
|
+
maxWidth: 0,
|
140
|
+
level: 0,
|
141
|
+
childrenCount: 4
|
142
|
+
}};
|
143
|
+
this._splitBranch(barnesHutTree.root);
|
144
|
+
|
145
|
+
// place the nodes one by one recursively
|
146
|
+
for (i = 0; i < nodeCount; i++) {
|
147
|
+
node = nodes[nodeIndices[i]];
|
148
|
+
this._placeInTree(barnesHutTree.root,node);
|
149
|
+
}
|
150
|
+
|
151
|
+
// make global
|
152
|
+
this.barnesHutTree = barnesHutTree
|
153
|
+
},
|
154
|
+
|
155
|
+
|
156
|
+
_updateBranchMass : function(parentBranch, node) {
|
157
|
+
var totalMass = parentBranch.mass + node.mass;
|
158
|
+
var totalMassInv = 1/totalMass;
|
159
|
+
|
160
|
+
parentBranch.centerOfMass.x = parentBranch.centerOfMass.x * parentBranch.mass + node.x * node.mass;
|
161
|
+
parentBranch.centerOfMass.x *= totalMassInv;
|
162
|
+
|
163
|
+
parentBranch.centerOfMass.y = parentBranch.centerOfMass.y * parentBranch.mass + node.y * node.mass;
|
164
|
+
parentBranch.centerOfMass.y *= totalMassInv;
|
165
|
+
|
166
|
+
parentBranch.mass = totalMass;
|
167
|
+
var biggestSize = Math.max(Math.max(node.height,node.radius),node.width);
|
168
|
+
parentBranch.maxWidth = (parentBranch.maxWidth < biggestSize) ? biggestSize : parentBranch.maxWidth;
|
169
|
+
|
170
|
+
},
|
171
|
+
|
172
|
+
|
173
|
+
_placeInTree : function(parentBranch,node,skipMassUpdate) {
|
174
|
+
if (skipMassUpdate != true || skipMassUpdate === undefined) {
|
175
|
+
// update the mass of the branch.
|
176
|
+
this._updateBranchMass(parentBranch,node);
|
177
|
+
}
|
178
|
+
|
179
|
+
if (parentBranch.children.NW.range.maxX > node.x) { // in NW or SW
|
180
|
+
if (parentBranch.children.NW.range.maxY > node.y) { // in NW
|
181
|
+
this._placeInRegion(parentBranch,node,"NW");
|
182
|
+
}
|
183
|
+
else { // in SW
|
184
|
+
this._placeInRegion(parentBranch,node,"SW");
|
185
|
+
}
|
186
|
+
}
|
187
|
+
else { // in NE or SE
|
188
|
+
if (parentBranch.children.NW.range.maxY > node.y) { // in NE
|
189
|
+
this._placeInRegion(parentBranch,node,"NE");
|
190
|
+
}
|
191
|
+
else { // in SE
|
192
|
+
this._placeInRegion(parentBranch,node,"SE");
|
193
|
+
}
|
194
|
+
}
|
195
|
+
},
|
196
|
+
|
197
|
+
|
198
|
+
_placeInRegion : function(parentBranch,node,region) {
|
199
|
+
switch (parentBranch.children[region].childrenCount) {
|
200
|
+
case 0: // place node here
|
201
|
+
parentBranch.children[region].children.data = node;
|
202
|
+
parentBranch.children[region].childrenCount = 1;
|
203
|
+
this._updateBranchMass(parentBranch.children[region],node);
|
204
|
+
break;
|
205
|
+
case 1: // convert into children
|
206
|
+
// if there are two nodes exactly overlapping (on init, on opening of cluster etc.)
|
207
|
+
// we move one node a pixel and we do not put it in the tree.
|
208
|
+
if (parentBranch.children[region].children.data.x == node.x &&
|
209
|
+
parentBranch.children[region].children.data.y == node.y) {
|
210
|
+
node.x += Math.random();
|
211
|
+
node.y += Math.random();
|
212
|
+
this._placeInTree(parentBranch,node, true);
|
213
|
+
}
|
214
|
+
else {
|
215
|
+
this._splitBranch(parentBranch.children[region]);
|
216
|
+
this._placeInTree(parentBranch.children[region],node);
|
217
|
+
}
|
218
|
+
break;
|
219
|
+
case 4: // place in branch
|
220
|
+
this._placeInTree(parentBranch.children[region],node);
|
221
|
+
break;
|
222
|
+
}
|
223
|
+
},
|
224
|
+
|
225
|
+
|
226
|
+
/**
|
227
|
+
* this function splits a branch into 4 sub branches. If the branch contained a node, we place it in the subbranch
|
228
|
+
* after the split is complete.
|
229
|
+
*
|
230
|
+
* @param parentBranch
|
231
|
+
* @private
|
232
|
+
*/
|
233
|
+
_splitBranch : function(parentBranch) {
|
234
|
+
// if the branch is filled with a node, replace the node in the new subset.
|
235
|
+
var containedNode = null;
|
236
|
+
if (parentBranch.childrenCount == 1) {
|
237
|
+
containedNode = parentBranch.children.data;
|
238
|
+
parentBranch.mass = 0; parentBranch.centerOfMass.x = 0; parentBranch.centerOfMass.y = 0;
|
239
|
+
}
|
240
|
+
parentBranch.childrenCount = 4;
|
241
|
+
parentBranch.children.data = null;
|
242
|
+
this._insertRegion(parentBranch,"NW");
|
243
|
+
this._insertRegion(parentBranch,"NE");
|
244
|
+
this._insertRegion(parentBranch,"SW");
|
245
|
+
this._insertRegion(parentBranch,"SE");
|
246
|
+
|
247
|
+
if (containedNode != null) {
|
248
|
+
this._placeInTree(parentBranch,containedNode);
|
249
|
+
}
|
250
|
+
},
|
251
|
+
|
252
|
+
|
253
|
+
/**
|
254
|
+
* This function subdivides the region into four new segments.
|
255
|
+
* Specifically, this inserts a single new segment.
|
256
|
+
* It fills the children section of the parentBranch
|
257
|
+
*
|
258
|
+
* @param parentBranch
|
259
|
+
* @param region
|
260
|
+
* @param parentRange
|
261
|
+
* @private
|
262
|
+
*/
|
263
|
+
_insertRegion : function(parentBranch, region) {
|
264
|
+
var minX,maxX,minY,maxY;
|
265
|
+
var childSize = 0.5 * parentBranch.size;
|
266
|
+
switch (region) {
|
267
|
+
case "NW":
|
268
|
+
minX = parentBranch.range.minX;
|
269
|
+
maxX = parentBranch.range.minX + childSize;
|
270
|
+
minY = parentBranch.range.minY;
|
271
|
+
maxY = parentBranch.range.minY + childSize;
|
272
|
+
break;
|
273
|
+
case "NE":
|
274
|
+
minX = parentBranch.range.minX + childSize;
|
275
|
+
maxX = parentBranch.range.maxX;
|
276
|
+
minY = parentBranch.range.minY;
|
277
|
+
maxY = parentBranch.range.minY + childSize;
|
278
|
+
break;
|
279
|
+
case "SW":
|
280
|
+
minX = parentBranch.range.minX;
|
281
|
+
maxX = parentBranch.range.minX + childSize;
|
282
|
+
minY = parentBranch.range.minY + childSize;
|
283
|
+
maxY = parentBranch.range.maxY;
|
284
|
+
break;
|
285
|
+
case "SE":
|
286
|
+
minX = parentBranch.range.minX + childSize;
|
287
|
+
maxX = parentBranch.range.maxX;
|
288
|
+
minY = parentBranch.range.minY + childSize;
|
289
|
+
maxY = parentBranch.range.maxY;
|
290
|
+
break;
|
291
|
+
}
|
292
|
+
|
293
|
+
|
294
|
+
parentBranch.children[region] = {
|
295
|
+
centerOfMass:{x:0,y:0},
|
296
|
+
mass:0,
|
297
|
+
range:{minX:minX,maxX:maxX,minY:minY,maxY:maxY},
|
298
|
+
size: 0.5 * parentBranch.size,
|
299
|
+
calcSize: 2 * parentBranch.calcSize,
|
300
|
+
children: {data:null},
|
301
|
+
maxWidth: 0,
|
302
|
+
level: parentBranch.level+1,
|
303
|
+
childrenCount: 0
|
304
|
+
};
|
305
|
+
},
|
306
|
+
|
307
|
+
|
308
|
+
/**
|
309
|
+
* This function is for debugging purposed, it draws the tree.
|
310
|
+
*
|
311
|
+
* @param ctx
|
312
|
+
* @param color
|
313
|
+
* @private
|
314
|
+
*/
|
315
|
+
_drawTree : function(ctx,color) {
|
316
|
+
if (this.barnesHutTree !== undefined) {
|
317
|
+
|
318
|
+
ctx.lineWidth = 1;
|
319
|
+
|
320
|
+
this._drawBranch(this.barnesHutTree.root,ctx,color);
|
321
|
+
}
|
322
|
+
},
|
323
|
+
|
324
|
+
|
325
|
+
/**
|
326
|
+
* This function is for debugging purposes. It draws the branches recursively.
|
327
|
+
*
|
328
|
+
* @param branch
|
329
|
+
* @param ctx
|
330
|
+
* @param color
|
331
|
+
* @private
|
332
|
+
*/
|
333
|
+
_drawBranch : function(branch,ctx,color) {
|
334
|
+
if (color === undefined) {
|
335
|
+
color = "#FF0000";
|
336
|
+
}
|
337
|
+
|
338
|
+
if (branch.childrenCount == 4) {
|
339
|
+
this._drawBranch(branch.children.NW,ctx);
|
340
|
+
this._drawBranch(branch.children.NE,ctx);
|
341
|
+
this._drawBranch(branch.children.SE,ctx);
|
342
|
+
this._drawBranch(branch.children.SW,ctx);
|
343
|
+
}
|
344
|
+
ctx.strokeStyle = color;
|
345
|
+
ctx.beginPath();
|
346
|
+
ctx.moveTo(branch.range.minX,branch.range.minY);
|
347
|
+
ctx.lineTo(branch.range.maxX,branch.range.minY);
|
348
|
+
ctx.stroke();
|
349
|
+
|
350
|
+
ctx.beginPath();
|
351
|
+
ctx.moveTo(branch.range.maxX,branch.range.minY);
|
352
|
+
ctx.lineTo(branch.range.maxX,branch.range.maxY);
|
353
|
+
ctx.stroke();
|
354
|
+
|
355
|
+
ctx.beginPath();
|
356
|
+
ctx.moveTo(branch.range.maxX,branch.range.maxY);
|
357
|
+
ctx.lineTo(branch.range.minX,branch.range.maxY);
|
358
|
+
ctx.stroke();
|
359
|
+
|
360
|
+
ctx.beginPath();
|
361
|
+
ctx.moveTo(branch.range.minX,branch.range.maxY);
|
362
|
+
ctx.lineTo(branch.range.minX,branch.range.minY);
|
363
|
+
ctx.stroke();
|
364
|
+
|
365
|
+
/*
|
366
|
+
if (branch.mass > 0) {
|
367
|
+
ctx.circle(branch.centerOfMass.x, branch.centerOfMass.y, 3*branch.mass);
|
368
|
+
ctx.stroke();
|
369
|
+
}
|
370
|
+
*/
|
371
|
+
}
|
372
|
+
|
373
|
+
};
|
@@ -0,0 +1,64 @@
|
|
1
|
+
/**
|
2
|
+
* Created by Alex on 2/10/14.
|
3
|
+
*/
|
4
|
+
|
5
|
+
var hierarchalRepulsionMixin = {
|
6
|
+
|
7
|
+
|
8
|
+
/**
|
9
|
+
* Calculate the forces the nodes apply on eachother based on a repulsion field.
|
10
|
+
* This field is linearly approximated.
|
11
|
+
*
|
12
|
+
* @private
|
13
|
+
*/
|
14
|
+
_calculateNodeForces : function() {
|
15
|
+
var dx, dy, distance, fx, fy, combinedClusterSize,
|
16
|
+
repulsingForce, node1, node2, i, j;
|
17
|
+
|
18
|
+
var nodes = this.calculationNodes;
|
19
|
+
var nodeIndices = this.calculationNodeIndices;
|
20
|
+
|
21
|
+
// approximation constants
|
22
|
+
var b = 5;
|
23
|
+
var a_base = 0.5*-b;
|
24
|
+
|
25
|
+
|
26
|
+
// repulsing forces between nodes
|
27
|
+
var nodeDistance = this.constants.physics.hierarchicalRepulsion.nodeDistance;
|
28
|
+
var minimumDistance = nodeDistance;
|
29
|
+
|
30
|
+
// we loop from i over all but the last entree in the array
|
31
|
+
// j loops from i+1 to the last. This way we do not double count any of the indices, nor i == j
|
32
|
+
for (i = 0; i < nodeIndices.length-1; i++) {
|
33
|
+
|
34
|
+
node1 = nodes[nodeIndices[i]];
|
35
|
+
for (j = i+1; j < nodeIndices.length; j++) {
|
36
|
+
node2 = nodes[nodeIndices[j]];
|
37
|
+
|
38
|
+
dx = node2.x - node1.x;
|
39
|
+
dy = node2.y - node1.y;
|
40
|
+
distance = Math.sqrt(dx * dx + dy * dy);
|
41
|
+
|
42
|
+
var a = a_base / minimumDistance;
|
43
|
+
if (distance < 2*minimumDistance) {
|
44
|
+
repulsingForce = a * distance + b; // linear approx of 1 / (1 + Math.exp((distance / minimumDistance - 1) * steepness))
|
45
|
+
|
46
|
+
// normalize force with
|
47
|
+
if (distance == 0) {
|
48
|
+
distance = 0.01;
|
49
|
+
}
|
50
|
+
else {
|
51
|
+
repulsingForce = repulsingForce/distance;
|
52
|
+
}
|
53
|
+
fx = dx * repulsingForce;
|
54
|
+
fy = dy * repulsingForce;
|
55
|
+
|
56
|
+
node1.fx -= fx;
|
57
|
+
node1.fy -= fy;
|
58
|
+
node2.fx += fx;
|
59
|
+
node2.fy += fy;
|
60
|
+
}
|
61
|
+
}
|
62
|
+
}
|
63
|
+
}
|
64
|
+
}
|
@@ -0,0 +1,513 @@
|
|
1
|
+
/**
|
2
|
+
* Created by Alex on 2/6/14.
|
3
|
+
*/
|
4
|
+
|
5
|
+
|
6
|
+
var physicsMixin = {
|
7
|
+
|
8
|
+
/**
|
9
|
+
* Toggling barnes Hut calculation on and off.
|
10
|
+
*
|
11
|
+
* @private
|
12
|
+
*/
|
13
|
+
_toggleBarnesHut : function() {
|
14
|
+
this.constants.physics.barnesHut.enabled = !this.constants.physics.barnesHut.enabled;
|
15
|
+
this._loadSelectedForceSolver();
|
16
|
+
this.moving = true;
|
17
|
+
this.start();
|
18
|
+
},
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
/**
|
23
|
+
* This loads the node force solver based on the barnes hut or repulsion algorithm
|
24
|
+
*
|
25
|
+
* @private
|
26
|
+
*/
|
27
|
+
_loadSelectedForceSolver : function() {
|
28
|
+
// this overloads the this._calculateNodeForces
|
29
|
+
if (this.constants.physics.barnesHut.enabled == true) {
|
30
|
+
this._clearMixin(repulsionMixin);
|
31
|
+
this._clearMixin(hierarchalRepulsionMixin);
|
32
|
+
|
33
|
+
this.constants.physics.centralGravity = this.constants.physics.barnesHut.centralGravity;
|
34
|
+
this.constants.physics.springLength = this.constants.physics.barnesHut.springLength;
|
35
|
+
this.constants.physics.springConstant = this.constants.physics.barnesHut.springConstant;
|
36
|
+
this.constants.physics.damping = this.constants.physics.barnesHut.damping;
|
37
|
+
|
38
|
+
this._loadMixin(barnesHutMixin);
|
39
|
+
}
|
40
|
+
else if (this.constants.physics.hierarchicalRepulsion.enabled == true) {
|
41
|
+
this._clearMixin(barnesHutMixin);
|
42
|
+
this._clearMixin(repulsionMixin);
|
43
|
+
|
44
|
+
this.constants.physics.centralGravity = this.constants.physics.hierarchicalRepulsion.centralGravity;
|
45
|
+
this.constants.physics.springLength = this.constants.physics.hierarchicalRepulsion.springLength;
|
46
|
+
this.constants.physics.springConstant = this.constants.physics.hierarchicalRepulsion.springConstant;
|
47
|
+
this.constants.physics.damping = this.constants.physics.hierarchicalRepulsion.damping;
|
48
|
+
|
49
|
+
this._loadMixin(hierarchalRepulsionMixin);
|
50
|
+
}
|
51
|
+
else {
|
52
|
+
this._clearMixin(barnesHutMixin);
|
53
|
+
this._clearMixin(hierarchalRepulsionMixin);
|
54
|
+
this.barnesHutTree = undefined;
|
55
|
+
|
56
|
+
this.constants.physics.centralGravity = this.constants.physics.repulsion.centralGravity;
|
57
|
+
this.constants.physics.springLength = this.constants.physics.repulsion.springLength;
|
58
|
+
this.constants.physics.springConstant = this.constants.physics.repulsion.springConstant;
|
59
|
+
this.constants.physics.damping = this.constants.physics.repulsion.damping;
|
60
|
+
|
61
|
+
this._loadMixin(repulsionMixin);
|
62
|
+
}
|
63
|
+
},
|
64
|
+
|
65
|
+
/**
|
66
|
+
* Before calculating the forces, we check if we need to cluster to keep up performance and we check
|
67
|
+
* if there is more than one node. If it is just one node, we dont calculate anything.
|
68
|
+
*
|
69
|
+
* @private
|
70
|
+
*/
|
71
|
+
_initializeForceCalculation : function() {
|
72
|
+
// stop calculation if there is only one node
|
73
|
+
if (this.nodeIndices.length == 1) {
|
74
|
+
this.nodes[this.nodeIndices[0]]._setForce(0,0);
|
75
|
+
}
|
76
|
+
else {
|
77
|
+
// if there are too many nodes on screen, we cluster without repositioning
|
78
|
+
if (this.nodeIndices.length > this.constants.clustering.clusterThreshold && this.constants.clustering.enabled == true) {
|
79
|
+
this.clusterToFit(this.constants.clustering.reduceToNodes, false);
|
80
|
+
}
|
81
|
+
|
82
|
+
// we now start the force calculation
|
83
|
+
this._calculateForces();
|
84
|
+
}
|
85
|
+
},
|
86
|
+
|
87
|
+
|
88
|
+
/**
|
89
|
+
* Calculate the external forces acting on the nodes
|
90
|
+
* Forces are caused by: edges, repulsing forces between nodes, gravity
|
91
|
+
* @private
|
92
|
+
*/
|
93
|
+
_calculateForces : function() {
|
94
|
+
// Gravity is required to keep separated groups from floating off
|
95
|
+
// the forces are reset to zero in this loop by using _setForce instead
|
96
|
+
// of _addForce
|
97
|
+
|
98
|
+
this._calculateGravitationalForces();
|
99
|
+
this._calculateNodeForces();
|
100
|
+
|
101
|
+
|
102
|
+
if (this.constants.smoothCurves == true) {
|
103
|
+
this._calculateSpringForcesWithSupport();
|
104
|
+
}
|
105
|
+
else {
|
106
|
+
this._calculateSpringForces();
|
107
|
+
}
|
108
|
+
},
|
109
|
+
|
110
|
+
|
111
|
+
/**
|
112
|
+
* Smooth curves are created by adding invisible nodes in the center of the edges. These nodes are also
|
113
|
+
* handled in the calculateForces function. We then use a quadratic curve with the center node as control.
|
114
|
+
* This function joins the datanodes and invisible (called support) nodes into one object.
|
115
|
+
* We do this so we do not contaminate this.nodes with the support nodes.
|
116
|
+
*
|
117
|
+
* @private
|
118
|
+
*/
|
119
|
+
_updateCalculationNodes : function() {
|
120
|
+
if (this.constants.smoothCurves == true) {
|
121
|
+
this.calculationNodes = {};
|
122
|
+
this.calculationNodeIndices = [];
|
123
|
+
|
124
|
+
for (var nodeId in this.nodes) {
|
125
|
+
if (this.nodes.hasOwnProperty(nodeId)) {
|
126
|
+
this.calculationNodes[nodeId] = this.nodes[nodeId];
|
127
|
+
}
|
128
|
+
}
|
129
|
+
var supportNodes = this.sectors['support']['nodes'];
|
130
|
+
for (var supportNodeId in supportNodes) {
|
131
|
+
if (supportNodes.hasOwnProperty(supportNodeId)) {
|
132
|
+
if (this.edges.hasOwnProperty(supportNodes[supportNodeId].parentEdgeId)) {
|
133
|
+
this.calculationNodes[supportNodeId] = supportNodes[supportNodeId];
|
134
|
+
}
|
135
|
+
else {
|
136
|
+
supportNodes[supportNodeId]._setForce(0,0);
|
137
|
+
}
|
138
|
+
}
|
139
|
+
}
|
140
|
+
|
141
|
+
for (var idx in this.calculationNodes) {
|
142
|
+
if (this.calculationNodes.hasOwnProperty(idx)) {
|
143
|
+
this.calculationNodeIndices.push(idx);
|
144
|
+
}
|
145
|
+
}
|
146
|
+
}
|
147
|
+
else {
|
148
|
+
this.calculationNodes = this.nodes;
|
149
|
+
this.calculationNodeIndices = this.nodeIndices;
|
150
|
+
}
|
151
|
+
},
|
152
|
+
|
153
|
+
|
154
|
+
/**
|
155
|
+
* this function applies the central gravity effect to keep groups from floating off
|
156
|
+
*
|
157
|
+
* @private
|
158
|
+
*/
|
159
|
+
_calculateGravitationalForces : function() {
|
160
|
+
var dx, dy, distance, node, i;
|
161
|
+
var nodes = this.calculationNodes;
|
162
|
+
var gravity = this.constants.physics.centralGravity;
|
163
|
+
var gravityForce = 0;
|
164
|
+
|
165
|
+
for (i = 0; i < this.calculationNodeIndices.length; i++) {
|
166
|
+
node = nodes[this.calculationNodeIndices[i]];
|
167
|
+
node.damping = this.constants.physics.damping; // possibly add function to alter damping properties of clusters.
|
168
|
+
// gravity does not apply when we are in a pocket sector
|
169
|
+
if (this._sector() == "default" && gravity != 0) {
|
170
|
+
dx = -node.x;
|
171
|
+
dy = -node.y;
|
172
|
+
distance = Math.sqrt(dx*dx + dy*dy);
|
173
|
+
gravityForce = gravity / distance;
|
174
|
+
|
175
|
+
node.fx = dx * gravityForce;
|
176
|
+
node.fy = dy * gravityForce;
|
177
|
+
}
|
178
|
+
else {
|
179
|
+
node.fx = 0;
|
180
|
+
node.fy = 0;
|
181
|
+
}
|
182
|
+
}
|
183
|
+
},
|
184
|
+
|
185
|
+
|
186
|
+
/**
|
187
|
+
* this function calculates the effects of the springs in the case of unsmooth curves.
|
188
|
+
*
|
189
|
+
* @private
|
190
|
+
*/
|
191
|
+
_calculateSpringForces : function() {
|
192
|
+
var edgeLength, edge, edgeId;
|
193
|
+
var dx, dy, fx, fy, springForce, length;
|
194
|
+
var edges = this.edges;
|
195
|
+
|
196
|
+
// forces caused by the edges, modelled as springs
|
197
|
+
for (edgeId in edges) {
|
198
|
+
if (edges.hasOwnProperty(edgeId)) {
|
199
|
+
edge = edges[edgeId];
|
200
|
+
if (edge.connected) {
|
201
|
+
// only calculate forces if nodes are in the same sector
|
202
|
+
if (this.nodes.hasOwnProperty(edge.toId) && this.nodes.hasOwnProperty(edge.fromId)) {
|
203
|
+
edgeLength = edge.customLength ? edge.length : this.constants.physics.springLength;
|
204
|
+
// this implies that the edges between big clusters are longer
|
205
|
+
edgeLength += (edge.to.clusterSize + edge.from.clusterSize - 2) * this.constants.clustering.edgeGrowth;
|
206
|
+
|
207
|
+
dx = (edge.from.x - edge.to.x);
|
208
|
+
dy = (edge.from.y - edge.to.y);
|
209
|
+
length = Math.sqrt(dx * dx + dy * dy);
|
210
|
+
|
211
|
+
if (length == 0) {
|
212
|
+
length = 0.01;
|
213
|
+
}
|
214
|
+
|
215
|
+
springForce = this.constants.physics.springConstant * (edgeLength - length) / length;
|
216
|
+
|
217
|
+
fx = dx * springForce;
|
218
|
+
fy = dy * springForce;
|
219
|
+
|
220
|
+
edge.from.fx += fx;
|
221
|
+
edge.from.fy += fy;
|
222
|
+
edge.to.fx -= fx;
|
223
|
+
edge.to.fy -= fy;
|
224
|
+
}
|
225
|
+
}
|
226
|
+
}
|
227
|
+
}
|
228
|
+
},
|
229
|
+
|
230
|
+
|
231
|
+
/**
|
232
|
+
* This function calculates the springforces on the nodes, accounting for the support nodes.
|
233
|
+
*
|
234
|
+
* @private
|
235
|
+
*/
|
236
|
+
_calculateSpringForcesWithSupport : function() {
|
237
|
+
var edgeLength, edge, edgeId, combinedClusterSize;
|
238
|
+
var edges = this.edges;
|
239
|
+
|
240
|
+
// forces caused by the edges, modelled as springs
|
241
|
+
for (edgeId in edges) {
|
242
|
+
if (edges.hasOwnProperty(edgeId)) {
|
243
|
+
edge = edges[edgeId];
|
244
|
+
if (edge.connected) {
|
245
|
+
// only calculate forces if nodes are in the same sector
|
246
|
+
if (this.nodes.hasOwnProperty(edge.toId) && this.nodes.hasOwnProperty(edge.fromId)) {
|
247
|
+
if (edge.via != null) {
|
248
|
+
var node1 = edge.to;
|
249
|
+
var node2 = edge.via;
|
250
|
+
var node3 = edge.from;
|
251
|
+
|
252
|
+
edgeLength = edge.customLength ? edge.length : this.constants.physics.springLength;
|
253
|
+
|
254
|
+
combinedClusterSize = node1.clusterSize + node3.clusterSize - 2;
|
255
|
+
|
256
|
+
// this implies that the edges between big clusters are longer
|
257
|
+
edgeLength += combinedClusterSize * this.constants.clustering.edgeGrowth;
|
258
|
+
this._calculateSpringForce(node1,node2,0.5*edgeLength);
|
259
|
+
this._calculateSpringForce(node2,node3,0.5*edgeLength);
|
260
|
+
}
|
261
|
+
}
|
262
|
+
}
|
263
|
+
}
|
264
|
+
}
|
265
|
+
},
|
266
|
+
|
267
|
+
|
268
|
+
/**
|
269
|
+
* This is the code actually performing the calculation for the function above. It is split out to avoid repetition.
|
270
|
+
*
|
271
|
+
* @param node1
|
272
|
+
* @param node2
|
273
|
+
* @param edgeLength
|
274
|
+
* @private
|
275
|
+
*/
|
276
|
+
_calculateSpringForce : function(node1,node2,edgeLength) {
|
277
|
+
var dx, dy, fx, fy, springForce, length;
|
278
|
+
|
279
|
+
dx = (node1.x - node2.x);
|
280
|
+
dy = (node1.y - node2.y);
|
281
|
+
length = Math.sqrt(dx * dx + dy * dy);
|
282
|
+
|
283
|
+
springForce = this.constants.physics.springConstant * (edgeLength - length) / length;
|
284
|
+
|
285
|
+
if (length == 0) {
|
286
|
+
length = 0.01;
|
287
|
+
}
|
288
|
+
|
289
|
+
fx = dx * springForce;
|
290
|
+
fy = dy * springForce;
|
291
|
+
|
292
|
+
node1.fx += fx;
|
293
|
+
node1.fy += fy;
|
294
|
+
node2.fx -= fx;
|
295
|
+
node2.fy -= fy;
|
296
|
+
},
|
297
|
+
|
298
|
+
|
299
|
+
/**
|
300
|
+
* Load the HTML for the physics config and bind it
|
301
|
+
* @private
|
302
|
+
*/
|
303
|
+
_loadPhysicsConfiguration : function() {
|
304
|
+
if (this.physicsConfiguration === undefined) {
|
305
|
+
var hierarchicalLayoutDirections = ["LR","RL","UD","DU"];
|
306
|
+
this.physicsConfiguration = document.createElement('div');
|
307
|
+
this.physicsConfiguration.className = "PhysicsConfiguration";
|
308
|
+
this.physicsConfiguration.innerHTML = '' +
|
309
|
+
'<table><tr><td><b>Simulation Mode:</b></td></tr>' +
|
310
|
+
'<tr>' +
|
311
|
+
'<td width="120px"><input type="radio" name="graph_physicsMethod" id="graph_physicsMethod1" value="BH" checked="checked">Barnes Hut</td>' +
|
312
|
+
'<td width="120px"><input type="radio" name="graph_physicsMethod" id="graph_physicsMethod2" value="R">Repulsion</td>'+
|
313
|
+
'<td width="120px"><input type="radio" name="graph_physicsMethod" id="graph_physicsMethod3" value="H">Hierarchical</td>' +
|
314
|
+
'</tr>'+
|
315
|
+
'</table>' +
|
316
|
+
'<table id="graph_BH_table" style="display:none">'+
|
317
|
+
'<tr><td><b>Barnes Hut</b></td></tr>'+
|
318
|
+
'<tr>'+
|
319
|
+
'<td width="150px">gravitationalConstant</td><td>0</td><td><input type="range" min="500" max="20000" value="' + (-1* this.constants.physics.barnesHut.gravitationalConstant) + '" step="25" style="width:300px" id="graph_BH_gc"></td><td width="50px">-20000</td><td><input value="' + (-1* this.constants.physics.barnesHut.gravitationalConstant) + '" id="graph_BH_gc_value" style="width:60px"></td>'+
|
320
|
+
'</tr>'+
|
321
|
+
'<tr>'+
|
322
|
+
'<td width="150px">centralGravity</td><td>0</td><td><input type="range" min="0" max="3" value="' + this.constants.physics.barnesHut.centralGravity + '" step="0.05" style="width:300px" id="graph_BH_cg"></td><td>3</td><td><input value="' + this.constants.physics.barnesHut.centralGravity + '" id="graph_BH_cg_value" style="width:60px"></td>'+
|
323
|
+
'</tr>'+
|
324
|
+
'<tr>'+
|
325
|
+
'<td width="150px">springLength</td><td>0</td><td><input type="range" min="0" max="500" value="' + this.constants.physics.barnesHut.springLength + '" step="1" style="width:300px" id="graph_BH_sl"></td><td>500</td><td><input value="' + this.constants.physics.barnesHut.springLength + '" id="graph_BH_sl_value" style="width:60px"></td>'+
|
326
|
+
'</tr>'+
|
327
|
+
'<tr>'+
|
328
|
+
'<td width="150px">springConstant</td><td>0</td><td><input type="range" min="0" max="0.5" value="' + this.constants.physics.barnesHut.springConstant + '" step="0.001" style="width:300px" id="graph_BH_sc"></td><td>0.5</td><td><input value="' + this.constants.physics.barnesHut.springConstant + '" id="graph_BH_sc_value" style="width:60px"></td>'+
|
329
|
+
'</tr>'+
|
330
|
+
'<tr>'+
|
331
|
+
'<td width="150px">damping</td><td>0</td><td><input type="range" min="0" max="0.3" value="' + this.constants.physics.barnesHut.damping + '" step="0.005" style="width:300px" id="graph_BH_damp"></td><td>0.3</td><td><input value="' + this.constants.physics.barnesHut.damping + '" id="graph_BH_damp_value" style="width:60px"></td>'+
|
332
|
+
'</tr>'+
|
333
|
+
'</table>'+
|
334
|
+
'<table id="graph_R_table" style="display:none">'+
|
335
|
+
'<tr><td><b>Repulsion</b></td></tr>'+
|
336
|
+
'<tr>'+
|
337
|
+
'<td width="150px">nodeDistance</td><td>0</td><td><input type="range" min="0" max="300" value="' + this.constants.physics.repulsion.nodeDistance + '" step="1" style="width:300px" id="graph_R_nd"></td><td width="50px">300</td><td><input value="' + this.constants.physics.repulsion.nodeDistance + '" id="graph_R_nd_value" style="width:60px"></td>'+
|
338
|
+
'</tr>'+
|
339
|
+
'<tr>'+
|
340
|
+
'<td width="150px">centralGravity</td><td>0</td><td><input type="range" min="0" max="3" value="' + this.constants.physics.repulsion.centralGravity + '" step="0.05" style="width:300px" id="graph_R_cg"></td><td>3</td><td><input value="' + this.constants.physics.repulsion.centralGravity + '" id="graph_R_cg_value" style="width:60px"></td>'+
|
341
|
+
'</tr>'+
|
342
|
+
'<tr>'+
|
343
|
+
'<td width="150px">springLength</td><td>0</td><td><input type="range" min="0" max="500" value="' + this.constants.physics.repulsion.springLength + '" step="1" style="width:300px" id="graph_R_sl"></td><td>500</td><td><input value="' + this.constants.physics.repulsion.springLength + '" id="graph_R_sl_value" style="width:60px"></td>'+
|
344
|
+
'</tr>'+
|
345
|
+
'<tr>'+
|
346
|
+
'<td width="150px">springConstant</td><td>0</td><td><input type="range" min="0" max="0.5" value="' + this.constants.physics.repulsion.springConstant + '" step="0.001" style="width:300px" id="graph_R_sc"></td><td>0.5</td><td><input value="' + this.constants.physics.repulsion.springConstant + '" id="graph_R_sc_value" style="width:60px"></td>'+
|
347
|
+
'</tr>'+
|
348
|
+
'<tr>'+
|
349
|
+
'<td width="150px">damping</td><td>0</td><td><input type="range" min="0" max="0.3" value="' + this.constants.physics.repulsion.damping + '" step="0.005" style="width:300px" id="graph_R_damp"></td><td>0.3</td><td><input value="' + this.constants.physics.repulsion.damping + '" id="graph_R_damp_value" style="width:60px"></td>'+
|
350
|
+
'</tr>'+
|
351
|
+
'</table>'+
|
352
|
+
'<table id="graph_H_table" style="display:none">'+
|
353
|
+
'<tr><td width="150"><b>Hierarchical</b></td></tr>'+
|
354
|
+
'<tr>'+
|
355
|
+
'<td width="150px">nodeDistance</td><td>0</td><td><input type="range" min="0" max="300" value="' + this.constants.physics.hierarchicalRepulsion.nodeDistance + '" step="1" style="width:300px" id="graph_H_nd"></td><td width="50px">300</td><td><input value="' + this.constants.physics.hierarchicalRepulsion.nodeDistance + '" id="graph_H_nd_value" style="width:60px"></td>'+
|
356
|
+
'</tr>'+
|
357
|
+
'<tr>'+
|
358
|
+
'<td width="150px">centralGravity</td><td>0</td><td><input type="range" min="0" max="3" value="' + this.constants.physics.hierarchicalRepulsion.centralGravity + '" step="0.05" style="width:300px" id="graph_H_cg"></td><td>3</td><td><input value="' + this.constants.physics.hierarchicalRepulsion.centralGravity + '" id="graph_H_cg_value" style="width:60px"></td>'+
|
359
|
+
'</tr>'+
|
360
|
+
'<tr>'+
|
361
|
+
'<td width="150px">springLength</td><td>0</td><td><input type="range" min="0" max="500" value="' + this.constants.physics.hierarchicalRepulsion.springLength + '" step="1" style="width:300px" id="graph_H_sl"></td><td>500</td><td><input value="' + this.constants.physics.hierarchicalRepulsion.springLength + '" id="graph_H_sl_value" style="width:60px"></td>'+
|
362
|
+
'</tr>'+
|
363
|
+
'<tr>'+
|
364
|
+
'<td width="150px">springConstant</td><td>0</td><td><input type="range" min="0" max="0.5" value="' + this.constants.physics.hierarchicalRepulsion.springConstant + '" step="0.001" style="width:300px" id="graph_H_sc"></td><td>0.5</td><td><input value="' + this.constants.physics.hierarchicalRepulsion.springConstant + '" id="graph_H_sc_value" style="width:60px"></td>'+
|
365
|
+
'</tr>'+
|
366
|
+
'<tr>'+
|
367
|
+
'<td width="150px">damping</td><td>0</td><td><input type="range" min="0" max="0.3" value="' + this.constants.physics.hierarchicalRepulsion.damping + '" step="0.005" style="width:300px" id="graph_H_damp"></td><td>0.3</td><td><input value="' + this.constants.physics.hierarchicalRepulsion.damping + '" id="graph_H_damp_value" style="width:60px"></td>'+
|
368
|
+
'</tr>'+
|
369
|
+
'<tr>'+
|
370
|
+
'<td width="150px">direction</td><td>1</td><td><input type="range" min="0" max="3" value="' + hierarchicalLayoutDirections.indexOf(this.constants.hierarchicalLayout.direction) + '" step="1" style="width:300px" id="graph_H_direction"></td><td>4</td><td><input value="' + this.constants.hierarchicalLayout.direction + '" id="graph_H_direction_value" style="width:60px"></td>'+
|
371
|
+
'</tr>'+
|
372
|
+
'<tr>'+
|
373
|
+
'<td width="150px">levelSeparation</td><td>1</td><td><input type="range" min="0" max="' + this.constants.hierarchicalLayout.levelSeparation + '" value="150" step="1" style="width:300px" id="graph_H_levsep"></td><td>500</td><td><input value="' + this.constants.hierarchicalLayout.levelSeparation + '" id="graph_H_levsep_value" style="width:60px"></td>'+
|
374
|
+
'</tr>'+
|
375
|
+
'<tr>'+
|
376
|
+
'<td width="150px">nodeSpacing</td><td>1</td><td><input type="range" min="0" max="' + this.constants.hierarchicalLayout.nodeSpacing + '" value="100" step="1" style="width:300px" id="graph_H_nspac"></td><td>500</td><td><input value="' + this.constants.hierarchicalLayout.nodeSpacing + '" id="graph_H_nspac_value" style="width:60px"></td>'+
|
377
|
+
'</tr>'+
|
378
|
+
'</table>'
|
379
|
+
this.containerElement.parentElement.insertBefore(this.physicsConfiguration,this.containerElement);
|
380
|
+
|
381
|
+
|
382
|
+
|
383
|
+
var rangeElement;
|
384
|
+
rangeElement = document.getElementById('graph_BH_gc');
|
385
|
+
rangeElement.onchange = showValueOfRange.bind(this,'graph_BH_gc',-1,"physics_barnesHut_gravitationalConstant");
|
386
|
+
rangeElement = document.getElementById('graph_BH_cg');
|
387
|
+
rangeElement.onchange = showValueOfRange.bind(this,'graph_BH_cg',1,"physics_centralGravity");
|
388
|
+
rangeElement = document.getElementById('graph_BH_sc');
|
389
|
+
rangeElement.onchange = showValueOfRange.bind(this,'graph_BH_sc',1,"physics_springConstant");
|
390
|
+
rangeElement = document.getElementById('graph_BH_sl');
|
391
|
+
rangeElement.onchange = showValueOfRange.bind(this,'graph_BH_sl',1,"physics_springLength");
|
392
|
+
rangeElement = document.getElementById('graph_BH_damp');
|
393
|
+
rangeElement.onchange = showValueOfRange.bind(this,'graph_BH_damp',1,"physics_damping");
|
394
|
+
|
395
|
+
|
396
|
+
rangeElement = document.getElementById('graph_R_nd');
|
397
|
+
rangeElement.onchange = showValueOfRange.bind(this,'graph_R_nd',1,"physics_repulsion_nodeDistance");
|
398
|
+
rangeElement = document.getElementById('graph_R_cg');
|
399
|
+
rangeElement.onchange = showValueOfRange.bind(this,'graph_R_cg',1,"physics_centralGravity");
|
400
|
+
rangeElement = document.getElementById('graph_R_sc');
|
401
|
+
rangeElement.onchange = showValueOfRange.bind(this,'graph_R_sc',1,"physics_springConstant");
|
402
|
+
rangeElement = document.getElementById('graph_R_sl');
|
403
|
+
rangeElement.onchange = showValueOfRange.bind(this,'graph_R_sl',1,"physics_springLength");
|
404
|
+
rangeElement = document.getElementById('graph_R_damp');
|
405
|
+
rangeElement.onchange = showValueOfRange.bind(this,'graph_R_damp',1,"physics_damping");
|
406
|
+
|
407
|
+
rangeElement = document.getElementById('graph_H_nd');
|
408
|
+
rangeElement.onchange = showValueOfRange.bind(this,'graph_H_nd',1,"physics_hierarchicalRepulsion_nodeDistance");
|
409
|
+
rangeElement = document.getElementById('graph_H_cg');
|
410
|
+
rangeElement.onchange = showValueOfRange.bind(this,'graph_H_cg',1,"physics_centralGravity");
|
411
|
+
rangeElement = document.getElementById('graph_H_sc');
|
412
|
+
rangeElement.onchange = showValueOfRange.bind(this,'graph_H_sc',1,"physics_springConstant");
|
413
|
+
rangeElement = document.getElementById('graph_H_sl');
|
414
|
+
rangeElement.onchange = showValueOfRange.bind(this,'graph_H_sl',1,"physics_springLength");
|
415
|
+
rangeElement = document.getElementById('graph_H_damp');
|
416
|
+
rangeElement.onchange = showValueOfRange.bind(this,'graph_H_damp',1,"physics_damping");
|
417
|
+
rangeElement = document.getElementById('graph_H_direction');
|
418
|
+
rangeElement.onchange = showValueOfRange.bind(this,'graph_H_direction',hierarchicalLayoutDirections,"hierarchicalLayout_direction");
|
419
|
+
rangeElement = document.getElementById('graph_H_levsep');
|
420
|
+
rangeElement.onchange = showValueOfRange.bind(this,'graph_H_levsep',1,"hierarchicalLayout_levelSeparation");
|
421
|
+
rangeElement = document.getElementById('graph_H_nspac');
|
422
|
+
rangeElement.onchange = showValueOfRange.bind(this,'graph_H_nspac',1,"hierarchicalLayout_nodeSpacing");
|
423
|
+
|
424
|
+
var radioButton1 = document.getElementById("graph_physicsMethod1");
|
425
|
+
var radioButton2 = document.getElementById("graph_physicsMethod2");
|
426
|
+
var radioButton3 = document.getElementById("graph_physicsMethod3");
|
427
|
+
|
428
|
+
radioButton2.checked = true;
|
429
|
+
if (this.constants.physics.barnesHut.enabled) {
|
430
|
+
radioButton1.checked = true;
|
431
|
+
}
|
432
|
+
if (this.constants.hierarchicalLayout.enabled) {
|
433
|
+
radioButton3.checked = true;
|
434
|
+
}
|
435
|
+
|
436
|
+
switchConfigurations.apply(this);
|
437
|
+
|
438
|
+
radioButton1.onchange = switchConfigurations.bind(this);
|
439
|
+
radioButton2.onchange = switchConfigurations.bind(this);
|
440
|
+
radioButton3.onchange = switchConfigurations.bind(this);
|
441
|
+
}
|
442
|
+
},
|
443
|
+
|
444
|
+
_overWriteGraphConstants : function(constantsVariableName, value) {
|
445
|
+
var nameArray = constantsVariableName.split("_");
|
446
|
+
if (nameArray.length == 1) {
|
447
|
+
this.constants[nameArray[0]] = value;
|
448
|
+
}
|
449
|
+
else if (nameArray.length == 2) {
|
450
|
+
this.constants[nameArray[0]][nameArray[1]] = value;
|
451
|
+
}
|
452
|
+
else if (nameArray.length == 3) {
|
453
|
+
this.constants[nameArray[0]][nameArray[1]][nameArray[2]] = value;
|
454
|
+
}
|
455
|
+
}
|
456
|
+
}
|
457
|
+
|
458
|
+
|
459
|
+
function switchConfigurations () {
|
460
|
+
var ids = ["graph_BH_table","graph_R_table","graph_H_table"]
|
461
|
+
var radioButton = document.querySelector('input[name="graph_physicsMethod"]:checked').value;
|
462
|
+
var tableId = "graph_" + radioButton + "_table";
|
463
|
+
var table = document.getElementById(tableId);
|
464
|
+
table.style.display = "block";
|
465
|
+
for (var i = 0; i < ids.length; i++) {
|
466
|
+
if (ids[i] != tableId) {
|
467
|
+
table = document.getElementById(ids[i]);
|
468
|
+
table.style.display = "none";
|
469
|
+
}
|
470
|
+
}
|
471
|
+
this._restoreNodes();
|
472
|
+
if (radioButton == "R") {
|
473
|
+
this.constants.hierarchicalLayout.enabled = false;
|
474
|
+
this.constants.physics.hierarchicalRepulsion.enabeled = false;
|
475
|
+
this.constants.physics.barnesHut.enabled = false;
|
476
|
+
}
|
477
|
+
else if (radioButton == "H") {
|
478
|
+
this.constants.hierarchicalLayout.enabled = true;
|
479
|
+
this.constants.physics.hierarchicalRepulsion.enabeled = true;
|
480
|
+
this.constants.physics.barnesHut.enabled = false;
|
481
|
+
this._setupHierarchicalLayout();
|
482
|
+
}
|
483
|
+
else {
|
484
|
+
this.constants.hierarchicalLayout.enabled = false;
|
485
|
+
this.constants.physics.hierarchicalRepulsion.enabeled = false;
|
486
|
+
this.constants.physics.barnesHut.enabled = true;
|
487
|
+
}
|
488
|
+
this._loadSelectedForceSolver();
|
489
|
+
this.moving = true;
|
490
|
+
this.start();
|
491
|
+
}
|
492
|
+
|
493
|
+
function showValueOfRange (id,map,constantsVariableName) {
|
494
|
+
var valueId = id + "_value";
|
495
|
+
var rangeValue = document.getElementById(id).value;
|
496
|
+
if (constantsVariableName == "hierarchicalLayout_direction" ||
|
497
|
+
constantsVariableName == "hierarchicalLayout_levelSeparation" ||
|
498
|
+
constantsVariableName == "hierarchicalLayout_nodeSpacing") {
|
499
|
+
this._setupHierarchicalLayout();
|
500
|
+
}
|
501
|
+
|
502
|
+
if (map instanceof Array) {
|
503
|
+
document.getElementById(valueId).value = map[parseInt(rangeValue)];
|
504
|
+
this._overWriteGraphConstants(constantsVariableName,map[parseInt(rangeValue)]);
|
505
|
+
}
|
506
|
+
else {
|
507
|
+
document.getElementById(valueId).value = map * parseFloat(rangeValue);
|
508
|
+
this._overWriteGraphConstants(constantsVariableName,map * parseFloat(rangeValue));
|
509
|
+
}
|
510
|
+
this.moving = true;
|
511
|
+
this.start();
|
512
|
+
};
|
513
|
+
|