Skip to content

Instantly share code, notes, and snippets.

@MasseR
Created February 20, 2013 09:48
Show Gist options
  • Save MasseR/4994347 to your computer and use it in GitHub Desktop.
Save MasseR/4994347 to your computer and use it in GitHub Desktop.
var Node = function(idx, x,y) {
this.idx = idx;
this.xvel = 0;
this.yvel = 0;
this.xforce = 0;
this.yforce = 0;
this.x = x;
this.y = y;
this.dampen = 0.85;
this.color = 'rgb(' + (Math.floor(Math.random() * 255)) + ',' + (Math.floor(Math.random() * 255)) + ',' + (Math.floor(Math.random() * 255)) + ')';
this.render = function(ctx) {
ctx.fillStyle = this.color;
ctx.circle(this.x,this.y,15);
}
this.renderEdgeTo = function(ctx, node) {
ctx.beginPath();
ctx.moveTo(this.x, this.y);
ctx.lineTo(node.x, node.y);
ctx.closePath();
ctx.stroke();
};
this.distanceTo = function(that) {
return Math.sqrt(Math.pow(this.x - that.x,2) + Math.pow(this.y - that.y, 2));
};
this.clearForces = function() {
this.xforce = 0;
this.yforce = 0;
};
this.repel = function(that) {
// Coulombs law F = k * (|q1*q2|) / r^2
var dist = this.distanceTo(that);
dist = dist < 7 ? 7 : dist;
var xforce = (800 * ((this.x - that.x) / dist)) / Math.pow(dist, 2);
var yforce = (800 * ((this.y - that.y) / dist)) / Math.pow(dist, 2);
this.xforce += xforce;
this.yforce += yforce;
if(isNaN(this.xforce))
this.xforce = 2;
if(isNaN(this.yforce))
this.yforce = 2;
};
this.attract = function(that) {
// Hooke's law F = kX
// k = spring constant
// X = displacement from neutral position
// Assume neutral position is when the nodes are on top of each other
var dist = this.distanceTo(that);
var xforce = 0.01 * (that.x - this.x);
var yforce = 0.01 * (that.y - this.y);
this.xforce += xforce;
this.yforce += yforce;
};
this.update = function() {
this.xvel = (this.xvel + this.xforce) * 0.85;
this.yvel = (this.yvel + this.yforce) * 0.85;
this.x += this.xvel;
this.y += this.yvel;
};
this.netForces = function () {
return Math.abs(this.xforce) + Math.abs(this.yforce);
}
};
var Network = function(nodes, edges) {
this.nodes = nodes;
this.edges = edges;
this.render = function(ctx) {
var n = this.nodes.length;
this.nodes.forEach(function(node) {
node.render(ctx);
});
for(var i = 0; i < n; i++) {
for(var j = 0; j < n; j++) {
if(this.edges[i][j])
this.nodes[i].renderEdgeTo(ctx, this.nodes[j]);
}
}
};
this.iterate = function() {
var n = this.nodes.length;
var err = 0;
for(var i = 0; i < n; i++) {
var u = this.nodes[i];
u.clearForces();
for(var j = 0; j < n; j++) {
if(i == j)
continue;
var v = this.nodes[j];
u.repel(v);
if(this.edges[i][j] && i != j)
u.attract(v);
}
err += u.netForces();
}
for(var i = 0; i < n; i++) {
this.nodes[i].update();
}
return err;
};
};
var canvas = document.getElementById('graph');
if(canvas.getContext) {
var ctx = canvas.getContext('2d');
ctx.circle = function(x,y, r) {
this.beginPath();
this.arc(x, y, r / 2, 0, Math.PI * 2, true);
this.fill();
};
// Create a random network
var nodes = new Array();
var edges = new Array();
edges[0] = new Array();
edges[1] = new Array();
edges[2] = new Array();
edges[3] = new Array();
nodes[0] = new Node(1, 180, 166);
nodes[1] = new Node(2, 189, 197);
nodes[2] = new Node(3, 189, 203);
nodes[3] = new Node(4, 198, 203);
edges[0][0] = false;
edges[0][1] = true;
edges[0][2] = false;
edges[0][3] = false;
edges[1][0] = true;
edges[1][1] = false;
edges[1][2] = true;
edges[1][3] = false;
edges[2][0] = false;
edges[2][1] = true;
edges[2][2] = false;
edges[2][3] = false;
edges[3][0] = false;
edges[3][1] = false;
edges[3][2] = false;
edges[3][3] = false;
var network = new Network(nodes, edges);
var count = 0;
var error = 0;
function doRender() {
canvas.width = canvas.width;
var error = network.iterate();
error = error / nodes.length;
network.render(ctx);
// if(error > 0.2)
setTimeout(doRender, 25);
}
setTimeout(doRender, 25);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment