Built with blockbuilder.org
Last active
February 9, 2018 14:05
-
-
Save RemiFaure/bfcfdabe65b2759cc3030be332d9464f to your computer and use it in GitHub Desktop.
TP layout
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
license: mit |
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
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>Force-Directed Layout with Convex Hull</title> | |
<script src="https://d3js.org/d3.v4.min.js"></script> | |
</head> | |
<body> | |
<style> | |
line { | |
fill: white; | |
stroke: black; | |
stroke-width: 1; | |
} | |
.background { | |
fill: none; | |
stroke: none; | |
} | |
.polygons { | |
fill: none; | |
stroke: #000; | |
} | |
</style> | |
<script type="text/javascript"> | |
// parameters | |
var w = 960, | |
h = 500, | |
n = 100, | |
nb_groups = 36, | |
nb_simulation = 120, | |
fill = d3.scaleOrdinal(d3.schemeCategory20), | |
graph = {}; | |
// we generate n random nodes in nb_groups | |
graph.nodes = d3.range(n).map(function(d, i) { | |
return {id: i, group: i % nb_groups, r: 10*Math.random()} | |
}); | |
// we don't generate links, yet | |
graph.links = [ | |
{source: graph.nodes[0], target: graph.nodes[1]} | |
]; | |
// main svg canvas | |
var svg = d3.select("body").append("svg") | |
.attr("width", w) | |
.attr("height", h); | |
// we separate ndoes using 3 arbitrary columns | |
var cols = Math.ceil(Math.sqrt(nb_groups)); | |
// horizontal scale for the columns | |
var x = d3.scaleLinear() | |
.domain([0, cols]) | |
.range([w/4, 3*w/4]); | |
// MAIN LOOP | |
// sets positions for each node based on index | |
// -we store the pixel values | |
// -those are calculated only once | |
graph.nodes.forEach(function(d, i) { | |
var col = d.group % cols; | |
d.x = x(col); | |
d.y = Math.trunc(d.group/cols); | |
}); | |
// simulation of positions | |
var simulation = d3.forceSimulation() | |
.force("link", d3.forceLink().id(function(d) { return d.id; })) | |
.force("charge", d3.forceManyBody()) | |
.force("x", d3.forceX(function(d, i) { | |
return d.x; | |
})) | |
.force("y", d3.forceY(function(d, i) { | |
return d.y; | |
})) | |
.force("collide", d3.forceCollide(12).radius(function(d) { return d.r + 0.5; }).iterations(2)) | |
.force("center", d3.forceCenter(w / 2, h / 2)) | |
.stop(); | |
// apply the simulation to nodes | |
simulation | |
.nodes(graph.nodes); | |
// apply the simulation to links | |
simulation.force("link") | |
.links(graph.links); | |
// we run the simulation nb_simulation times | |
for (var i = 0; i < nb_simulation; ++i) simulation.tick(); | |
// we create a Voronoi partition based on nodes positions | |
var voronoi = d3.voronoi() | |
.extent([[0, 0], [w, h]]) | |
.x(function(d) { return d.x; }) | |
.y(function(d) { return d.y; }) | |
// we draw the voronoi partition using paths | |
var polygon = svg.append("g") | |
.attr("class", "polygons") | |
.selectAll("path") | |
.data(voronoi.polygons(graph.nodes)) | |
.enter() | |
.append("path") | |
.attr("id", function(d) { return "_" + d.data.id; }) | |
.style("fill", "white") | |
.on("mouseenter", function(d) { | |
// highlight nodes in the current partition | |
d3.select(this).style("fill", "red").transition().style("fill", "white") | |
d3.selectAll("circle#" + d3.select(this).attr("id")) | |
.attr("r", 10) | |
.transition() | |
.attr("r", function(d) { return d.r; }) | |
}) | |
.on("mouseleave", function(d) { | |
}) | |
.call(redrawPolygon); | |
// create nodes using the graph.nodes object | |
var node = svg.selectAll("circle.node") | |
.data(graph.nodes) | |
.enter() | |
.append("circle") | |
.attr("class", "node") | |
.attr("r", function(d) { return d.r; }) | |
.attr("id", function(d) { return "_" + d.id; }) | |
.style("fill", function(d, i) { return fill(i); }) | |
.style("stroke", function(d, i) { return d3.rgb(fill(i)).darker(2); }) | |
.style("stroke-width", 1.5); | |
// draw the nodes | |
node | |
.attr("cx", function(d) { return d.x; }) | |
.attr("cy", function(d) { return d.y; }); | |
// create links using the graph.links object | |
var link = svg.append("g") | |
.attr("class", "links") | |
.selectAll("line") | |
.data(graph.links) | |
.enter() | |
.append("line") | |
.attr("stroke-width", function(d) { return Math.sqrt(d.value); }); | |
// draw links | |
link | |
.attr("x1", function(d) { return d.source.x; }) | |
.attr("y1", function(d) { return d.source.y; }) | |
.attr("x2", function(d) { return d.target.x; }) | |
.attr("y2", function(d) { return d.target.y; }); | |
// function to draw the vornoi partition | |
function redrawPolygon(polygon) { | |
polygon | |
.attr("d", function(d) { return d ? "M" + d.join("L") + "Z" : null; }); | |
} | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment