Skip to content

Instantly share code, notes, and snippets.

@kevinfjbecker
Created January 1, 2012 21:53
Show Gist options
  • Save kevinfjbecker/1548445 to your computer and use it in GitHub Desktop.
Save kevinfjbecker/1548445 to your computer and use it in GitHub Desktop.
Force-based Graph Layout in JavaScript
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>Graph Layout</title>
<style>
article, aside, figure, footer, header, hgroup,
menu, nav, section { display: block; }
</style>
</head>
<body>
<p id="hello">Hello World</p>
<script src="layout.js"></script>
</body>
</html>
var MIN_SYSTEM_ENERGY = 1;
var GRAVITY_CONST = 1;
var SPRING_CONST = 1;
var MASS = 1;
var TIME_STEP = 1;
var DAMPING = 0.5; // 0 < damping < 1
var i, j;
var k, maxSteps = 2; // DEBUG
var u, v;
var dist;
var totalKineticEnergy;
var netForce;
var gravityForceMagnitude;
var springForceMagnitude;
var G = {
    vertices: [{
        edges: [1, 4]
    }, {
        edges: [0, 2, 3, 4]
    }, {
        edges: [1, 3]
    }, {
        edges: [1, 2, 4]
    }, {
        edges: [0, 1, 3]
    }]
};
// set up initial node velocities to (0,0)
for(i = 0; i < G.vertices.length; i += 1) {
G.vertices[i].velocity = {x: 0, y: 0};
}
// set up initial node positions to (0,0)
for(i = 0; i < G.vertices.length; i += 1) {
var center = {x: 50, y: 50};
G.vertices[i].position = {
x: center.x + Math.sin(i) * i,
y: center.y + Math.cos(i) * i
};
}
k = 0;
do {
totalKineticEnergy = 0;
for (i = 0; i < G.vertices.length; i += 1) {
u = G.vertices[i];
netForce = {x: 0, y: 0};
for (j = 0; j < G.vertices.length; j += 1) {
if (j !== i) {
v = G.vertices[j];
dx = v.position.x - u.position.x;
dy = v.position.y - u.position.y;
dist = Math.sqrt(dx * dx + dy * dy);
gravityForceMagnitude =
GRAVITY_CONST * (MASS + MASS) / (dist * dist);
gravityForce = {
x: gravityForceMagnitude * dx / dist,
y: gravityForceMagnitude * dy / dist
};
netForce.x += gravityForce.x;
netForce.y += gravityForce.y;
}
}
for (j = 0; j < G.vertices[i].edges.length; j += 1) {
v = G.vertices[G.vertices[i].edges[j]];
dx = v.position.x - u.position.x;
dy = v.position.y - u.position.y;
dist = Math.sqrt(dx * dx + dy * dy);
springForceMagnitude = -SPRING_CONST * dist;
springForce = {
x: springForceMagnitude * dx / dist,
y: springForceMagnitude * dy / dist
};
netForce.x += springForce.x;
netForce.y += springForce.y;
}
u.velocity = {
x: (u.velocity.x + TIME_STEP * netForce.x) * DAMPING,
y: (u.velocity.y + TIME_STEP * netForce.y) * DAMPING
};
u.position = {
x: u.position.x + TIME_STEP * u.velocity.x,
y: u.position.y + TIME_STEP * u.velocity.y
};
// alert('u: ' + JSON.stringify(u)); // DEBUG
// alert('gravityForce: ' + JSON.stringify(gravityForce)); // DEBUG
// alert('springForce: ' + JSON.stringify(springForce)); // DEBUG
// alert('netForce: ' + JSON.stringify(netForce)); // DEBUG
totalKineticEnergy += MASS *
(u.velocity.x * u.velocity.x + u.velocity.y * u.velocity.y);
}
k++;
} while (totalKineticEnergy > MIN_SYSTEM_ENERGY && k < maxSteps);
// alert(totalKineticEnergy); // DEBUG
// alert(k); // DEBUG
if (document.getElementById('hello')) {
document.getElementById('hello').innerHTML = JSON.stringify(G);
}
@kevinfjbecker
Copy link
Author

This isn't working yet =(

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment