forked from allain/JavaScript-Concept-Map
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSpringLayout.js
83 lines (62 loc) · 2.37 KB
/
SpringLayout.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
window.SpringLayout = function() {
var DAMPING = 0.7;
var COULOMB_FACTOR = -100;
var SPRING_CONSTANT = 0.05;
var SPRING_LENGTH = 100;
function coulombRepulsion(c1, c2) {
var f = new Vector(c2.x - c1.x, c2.y - c1.y);
var lengthSquared = f.x * f.x + f.y * f.y;
f.scale(lengthSquared > 0 ? 1 / lengthSquared : 1);
f.scale(COULOMB_FACTOR);
return f;
}
function hookeAttraction(c1, c2, desiredLength) {
var f = new Vector(c2.x - c1.x, c2.y - c1.y);
var length = f.length()
return f.normalize().scale(length - desiredLength).scale(SPRING_CONSTANT);
}
this.layoutMap = function(conceptMap, conceptMapUI) {
var targetPos = [];
var stepCount = 0;
var totalEnergy = 0;
Relation.clearCaches();
for (var i = 0; i < conceptMap.concepts.length ; i++) {
var f = new Vector(0,0);
var c1 = conceptMap.concepts[i];
targetPos[c1] = new Vector(c1.pos.x, c1.pos.y);
if (c1.$html.hasClass("ui-draggable-dragging")) {
c1.v = new Vector(0,0);
continue;
}
for (var j = 0; j < conceptMap.concepts.length; j++) {
var c2 = conceptMap.concepts[j];
var coulombForce = coulombRepulsion(c1.pos, c2.pos);
f.translate(coulombForce.x, coulombForce.y);
}
for (var rel = 0; rel < conceptMap.relations.length; rel ++) {
var relation = conceptMap.relations[rel];
var hookeForce;
if (relation.from == c1) {
var edgePoints = relation.getEdgePoints();
hookeForce = hookeAttraction(edgePoints.p1, edgePoints.p2, SPRING_LENGTH);
f.translate(hookeForce);
} else if (relation.to == c1) {
var edgePoints = relation.getEdgePoints();
hookeForce = hookeAttraction(edgePoints.p2, edgePoints.p1, SPRING_LENGTH);
f.translate(hookeForce);
}
}
c1.v.translate(f);
c1.v.scale(DAMPING);
targetPos[c1].translate(c1.v.x, c1.v.y);
}
for (var i = 0; i < conceptMap.concepts.length ; i++) {
var c1 = conceptMap.concepts[i];
c1.pos.x = Math.min(Math.max(targetPos[c1].x, 0), conceptMapUI.canvas.width);
c1.pos.y = Math.min(Math.max(0, targetPos[c1].y), conceptMapUI.canvas.height - 30);
totalEnergy += c1.v.length() * c1.v.length();
}
conceptMapUI.repaint();
return totalEnergy > 2.5 * conceptMap.concepts.length;
}
}