Skip to content Skip to sidebar Skip to footer

D3.js How Do I Arrange Nodes Of A Force Layout To Be On A Circle

I have developed a force layout to represent relationships between social groups. Now I would like to get the nodes to be distributed in a circle with links joining them. What is t

Solution 1:

Here's someone else's solution:

This network graph uses the D3 force layout to draw nodes and links, but instead of using d3.force() to find the best node positions, we draw an invisible arc and evenly places nodes along the circumference.

<!DOCTYPE html><html><head><scriptsrc="http://d3js.org/d3.v3.min.js"></script><metacharset="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><scripttype="text/javascript">// number of random nodes (gets crowded at >25 unless you change node diameter)var num = 20;

// returns random int between 0 and numfunctiongetRandomInt() {returnMath.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 arcvar 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;
    returnpointAtLength(circumference-position)
}

// fades out lines that aren't connected to node dvar 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() methodsvar dim = width-80var 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 linksvar 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>

Post a Comment for "D3.js How Do I Arrange Nodes Of A Force Layout To Be On A Circle"