D3
OG
Old school D3 from simpler times
All examples
By author
By category
About
mnemonico
Full window
Github gist
Voronoi
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: 0.5; } .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 =6 , 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 // intialisation of the links list of the graph graph.links = []; // from the correction we've got in cours graph.nodes.forEach(function(d, i){ graph.nodes.forEach(function(e, j){ if(d !== e && Math.random() < .015){ graph.links.push({source: d, target: e}) } }) }); /* graph.links= [{source : graph.nodes[0],target : graph.nodes[2]}, {source : graph.nodes[1],target : graph.nodes[2]}, {source : graph.nodes[2],target : graph.nodes[99]} ] */ /* LINK NODES TO EACH OTHERS graph.nodes.forEach(function(d,i)){ graph.nodes.forEach(function(e,j)) } */ // 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)); //size of matrix //var size = Math.sqrt(nb_groups); // horizontal scale for the columns var x = d3.scaleLinear() .domain([0, cols]) .range([w/4, 3*w/4]); var y = d3.scaleLinear() .domain([0, cols]) .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 % cols; var row = Math.floor(d.group/cols) d.x = x(col); d.y = y(row); }); // simulation of positions // just commented force("link") function,so that nodes would be separaed based on groupes not attraction force 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; }); d3.selectAll(".links").style("opacity", .1).style("stroke-width", 1); d3.selectAll(".links").filter(function(e) { if(d.data === e.source || d.data === e.target){ return true; } else { return false; } }) .style("opacity", 1) .style("stroke-width", 1.5); }) .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 the links of each bde to the nodes it's connected to. var link = svg.append("g") .selectAll("line") .data(graph.links) .enter() .append("line") .attr("class", "links") .style("stroke","red") .style("stroke-width", 0.5); // 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