Created
September 10, 2015 21:18
-
-
Save ngopal/bbc7ffb934112e5b809a to your computer and use it in GitHub Desktop.
Working on a simply implemented force-directed layout implementation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//constants | |
var K_r = 60; | |
var L = 50; | |
var delta_t = 0.4; | |
var MAX_DISP_SQ = 20; | |
var width = 300; | |
var height = width; | |
var body = d3.select('body'); | |
var svg = body.append('svg') | |
.attr("width", width) | |
.attr("height", height); | |
var points = []; | |
var Cscale = d3.scale.linear() | |
.domain([0,1]) | |
.range([0,height]); | |
function randomNode(nodes) { | |
return {"id":nodes.length+1, | |
"x":Cscale(Math.random()), | |
"y":Cscale(Math.random()), | |
"force_x":0, | |
"force_y":0, | |
"neighbors":[ | |
Math.floor(Math.random() * nodes.length) | |
]}; | |
} | |
for (var i = 0; i < 2; i++) { | |
points.push(randomNode(points)); | |
} | |
var circles = svg.selectAll("circle") | |
.data(points) | |
.enter() | |
.append("circle") | |
.attr("cx", function(d) { | |
return d.x; | |
}) | |
.attr("cy", function(d) { | |
return d.y; | |
}) | |
.attr("r", function(d) { | |
return 3; | |
}); | |
var iterations = 800; | |
while (iterations > 0) { | |
// ensure the loop terminates | |
iterations = iterations - 1; | |
// calculate repulsion between all pairs | |
for (var j = 0; j < points.length; j++) { | |
for (var k = j+1; k < points.length; k++) { | |
var dx = points[j].x - points[k].x; | |
var dy = points[j].y - points[k].y; | |
if (dx !== 0 || dy !== 0) { | |
var dSQ = dx*dx + dy*dy; | |
var dist = Math.sqrt(dSQ); | |
var force = K_r / dSQ; | |
var fx = force*dx / dist; | |
var fy = force*dy / dist; | |
points[j].force_x = points[j].force_x - fx; | |
points[j].force_y = points[j].force_y - fy; | |
points[k].force_x = points[k].force_x + fx; | |
points[k].force_y = points[k].force_y + fy; | |
} | |
} | |
} | |
// calculate spring forces | |
for (var j = 0; j < points.length; j++) { | |
for (var n = 0; n < points[j].neighbors.length; n++) { | |
if (points[j].id < points[n].id) { | |
var dx = points[n].x - points[j].x; | |
var dy = points[n].y - points[j].y; | |
if (dx !== 0 || dy !== 0) { | |
var dist = Math.sqrt(dx*dx + dy*dy); | |
var force = K_s * (dist - L); | |
var fx = force * dx / dist; | |
var fy = force * dy / dist; | |
points[j].force_x = points[j].force_x + fx; | |
points[j].force_y = points[j].force_y + fy; | |
points[n].force_x = points[n].force_x - fx; | |
points[n].force_y = points[n].force_y - fy; | |
} | |
} | |
} | |
} | |
// update x and y positions | |
for (var i = 0; i < points.length; i++) { | |
var dx = delta_t * points[i].force_x; | |
var dy = delta_t * points[i].force_y; | |
var displacementSQ = dx*dx + dy*dy; | |
if (displacementSQ > MAX_DISP_SQ) { | |
var s = Math.sqrt( MAX_DISP_SQ / displacementSQ); | |
dx = dx * s; | |
dy = dy * s; | |
} | |
points[i].x = points[i].x + dx; | |
points[i].y = points[i].y + dy; | |
} | |
console.log(points); | |
//update circles | |
svg.selectAll("circle") | |
.data(points) | |
.append("circle") | |
.attr("cx", function(d) { | |
return d.x; | |
}) | |
.attr("cy", function(d) { | |
return d.y; | |
}) | |
.attr("r", function(d) { | |
return 3; | |
}); | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment