D3.js如何将力布局的节点排列在圆上 [英] D3.js how do I arrange nodes of a force layout to be on a circle

查看:92
本文介绍了D3.js如何将力布局的节点排列在圆上的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我开发了一种力量布局来表示社交群体之间的关系。现在我想让节点分布在一个带有连接它们的链接的圆圈中。做这个的最好方式是什么?

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 the best way to do this?

代码的完整版本(没有数据)在这里 http://jsfiddle.net/PatriciaW/zZSJT/
(为什么我也必须在这里包含代码?这是主要部分)

The complete version of the code (without data) is here http://jsfiddle.net/PatriciaW/zZSJT/ (Why do I have to include code here too? Here is the main portion)

d3.json("/relationships?nocache=" + (new Date()).getTime(),function(error,members){
   var links=members.organizations.map(function(members) {
      return members.member;
      });

var nodes = {};

links.forEach(function(link) {
  link.source = nodes[link.xsource] || (nodes[link.xsource] = {source: link.xsource, name: link.xsource, category: link.categorysource, path: link.pathsource, desc: link.descsource, title: link.titlesource});
  link.target = nodes[link.xtarget] || (nodes[link.xtarget] = {target: link.xtarget, name: link.xtarget, category: link.categorytarget, path: link.pathtarget, desc: link.desctarget, title: link.titletarget});
});

force = d3.layout.force()
.nodes(d3.values(nodes))
.links(links)
.size([width, height])
.charge(-120)
.linkDistance(function() {return (Math.random() * 200) + 100;})
.linkStrength(0.5)
.on("tick", tick)
.start();

var link = svg.selectAll(".link")
.data(force.links())
.enter().append("line")
.attr("class", "link");

var node_drag = d3.behavior.drag()
    .on("dragstart", dragstart)
    .on("drag", dragmove)
    .on("dragend", dragend);

var loading = svg.append("text")
.attr("x", width / 2)
.attr("y", height / 2)
.attr("dy", ".35em")
.style("text-anchor", "middle")
.text("Simulating. One moment please…");

function dragstart(d, i) {
    force.stop() // stops the force auto positioning before you start dragging
}

function dragmove(d, i) {
    d.px += d3.event.dx;
    d.py += d3.event.dy;
    d.x += d3.event.dx;
    d.y += d3.event.dy; 
    tick(); // this is the key to make it work together with updating both px,py,x,y on d !
}

function dragend(d, i) {
    d.fixed = true; // of course set the node to fixed so the force doesn't include the node in its auto positioning stuff
    tick();
    force.resume();
};

var node = svg.selectAll(".node")
.data(force.nodes())
.enter().append("g")
.attr("class", "node")
.on("mouseover", mouseover)
.on("mouseout", mouseout)
.on("click", clickAlert)
.call(node_drag);

node.append("circle")
.attr("r", 8)
.style("fill", function(d) { 
   return categoryColour [d.category];
   })  

// add an image marker
node.append("image")
  .attr("x",-8)
  .attr("y",-8)
  .attr("width", 16)
  .attr("height", 16)
  .attr("xlink:href", function(d) {
      return categoryImage [d.category]
   })
  .on("click", clickAlert)
  .style("cursor", "pointer")

node.append("text")
  .attr("x", 12)
  .attr("dy", ".35em")
  .text(function(d) { 
      return d.name; 
      });
// Use a timeout to allow the rest of the page to load first.
setTimeout(function() {

// Run the layout a fixed number of times.
// The ideal number of times scales with graph complexity.
force.start();
for (var i = n * n; i > 0; --i) force.tick();
force.stop();

svg.selectAll("line")
.data(links)
.enter().append("line")
.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; });

svg.selectAll("circle")
.data(nodes)
.enter().append("circle")
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.attr("r", 4.5);

loading.remove();
}, 10);

function tick() {
link
  .attr("x1", function(d) { 
       return d.source.x + xadj; })
  .attr("y1", function(d) { 
       return d.source.y + yadj; })
  .attr("x2", function(d) { 
       return d.target.x +xadj; })
  .attr("y2", function(d) { 
       return d.target.y +yadj; });
node
  .attr("transform", function(d) { 
       return "translate(" + (d.x + xadj) + "," + (d.y + yadj) + ")"; 
  });
};

function mouseover() {
 d3.select(this).select("circle").transition()
  .duration(750)
  .attr("r", 16);
d3.select(this).select("text")
   .attr("font-size","34px")
   .style("font-weight", "bold");
};

function mouseout() {
d3.select(this).select("circle").transition()
 .duration(750)
 .attr("r", 8);
d3.select(this).select("text")
  .attr("font-size","12px")
  .style("font-weight", "normal");
};
}) // end json


推荐答案

这里是其他人的解决方案


此网络图使用D3力布局绘制节点和链接,但不是使用d3.force()来查找最佳节点位置,而是绘制一个不可见的弧并沿圆周均匀放置节点。 / p>

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>
<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>

这篇关于D3.js如何将力布局的节点排列在圆上的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆