D3
OG
Old school D3 from simpler times
All examples
By author
By category
About
ArnaudBru
Full window
Github gist
TP Layout
Built with
blockbuilder.org
<!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 = 150, nb_groups = 9, size = Math.ceil(Math.sqrt(nb_groups)) nb_simulation = 200, fill = d3.scaleOrdinal(d3.schemeCategory20), graph = {}, anim = true; // 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 = []; graph.nodes.forEach(function(d,i){ graph.nodes.forEach(function(e,j) { if(d !== e && Math.random() < .02){ graph.links.push({source:d,target:e}) } }) }) // main svg canvas var svg = d3.select("body").append("svg") .attr("width", w) .attr("height", h) .on("mouseleave", function(d) { if(anim){ d3.selectAll(".links").style("opacity",1) } }) // horizontal scale for the columns var x = d3.scaleLinear() .domain([0, size]) .range([w/4, 3*w/4]); // vertical scale for the columns var y = d3.scaleLinear() .domain([0, size]) .range([h/4, 3*h/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 % size; var row = Math.floor(d.group/size); d.x = x(col); d.y = y(row); }); // 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("mousemove", 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; }) if (anim){ d3.selectAll(".links").style("opacity",.1) d3.selectAll(".links").filter(function(e){ if(e.target == d.data|| e.source ==d.data){ return true; } }) .style("opacity",1) } }) .on("click", function(d) { anim = !anim; d3.selectAll(".links").style("opacity",.1) d3.selectAll(".links").filter(function(e){ if(e.target == d.data|| e.source ==d.data){ return true; } }) .style("opacity",1) }) .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") .selectAll("line") .data(graph.links) .enter() .append("line") .attr("class", "links") .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>
https://d3js.org/d3.v4.min.js