vis-rails 0.0.4 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
|