<!DOCTYPE html> <html> <head> <script src="http://d3js.org/d3.v3.min.js"></script> <meta charset="utf-8"> <title>JS Bin</title> <style> line.node-link, path.node-link { fill: none; stroke: black } circle.node { fill: white; stroke: black } circle.node+text { text-anchor: middle; } text { font-family: sans-serif; pointer-events: none; } </style> </head> <body> <script type="text/javascript"> // number of random nodes (gets crowded at >25 unless you change node diameter) var num = 20; // returns random int between 0 and num function getRandomInt() {return Math.floor(Math.random() * (num));} // nodes returns a [list] of {id: 1, fixed:true} var nodes = d3.range(num).map(function(d) { return {id: d}; }); // links returns a [list] of {source: 0, target: 1} (values refer to indicies of nodes) var links = d3.range(num).map(function(d) { return {source: getRandomInt(), target: getRandomInt()}; }); var width = 500, height = 500; var force = d3.layout.force() .nodes(nodes) .links(links) .size([width, height]); // evenly spaces nodes along arc var circleCoord = function(node, index, num_nodes){ var circumference = circle.node().getTotalLength(); var pointAtLength = function(l){return circle.node().getPointAtLength(l)}; var sectionLength = (circumference)/num_nodes; var position = sectionLength*index+sectionLength/2; return pointAtLength(circumference-position) } // fades out lines that aren't connected to node d var is_connected = function(d, opacity) { lines.transition().style("stroke-opacity", function(o) { return o.source === d || o.target === d ? 1 : opacity; }); } var svg = d3.select("body").append("svg") .attr("width", width) .attr("height", height); // invisible circle for placing nodes // it's actually two arcs so we can use the getPointAtLength() and getTotalLength() methods var dim = width-80 var circle = svg.append("path") .attr("d", "M 40, "+(dim/2+40)+" a "+dim/2+","+dim/2+" 0 1,0 "+dim+",0 a "+dim/2+","+dim/2+" 0 1,0 "+dim*-1+",0") .style("fill", "#f5f5f5"); force.start(); // set coordinates for container nodes nodes.forEach(function(n, i) { var coord = circleCoord(n, i, nodes.length) n.x = coord.x n.y = coord.y }); // use this one for straight line links... // var lines = svg.selectAll("line.node-link") // .data(links).enter().append("line") // .attr("class", "node-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; }); // ...or use this one for curved line links var lines = svg.selectAll("path.node-link") .data(links).enter().append("path") .attr("class", "node-link") .attr("d", function(d) { var dx = d.target.x - d.source.x, dy = d.target.y - d.source.y, dr = Math.sqrt(dx * dx + dy * dy); return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y; }); var gnodes = svg.selectAll('g.gnode') .data(nodes).enter().append('g') .attr("transform", function(d) { return "translate("+d.x+","+d.y+")" }) .classed('gnode', true); var node = gnodes.append("circle") .attr("r", 25) .attr("class", "node") .on("mouseenter", function(d) { is_connected(d, 0.1) node.transition().duration(100).attr("r", 25) d3.select(this).transition().duration(100).attr("r", 30) }) .on("mouseleave", function(d) { node.transition().duration(100).attr("r", 25); is_connected(d, 1); }); var labels = gnodes.append("text") .attr("dy", 4) .text(function(d){return d.id}) </script> </body> </html>