Javascript D3.js如何将强制布局的节点安排在圆上
我已经开发了一个部队布局来表示社会群体之间的关系。现在,我想让节点分布在一个圆圈中,链接将它们连接起来。最好的方法是什么 代码的完整版本(没有数据)在这里 (为什么我也必须在这里包含代码?这里是主要部分) 这是: 此网络图使用D3 force布局来绘制节点和链接,但我们没有使用D3.force()来查找最佳节点位置,而是绘制了一条不可见的圆弧,并沿圆周均匀放置节点Javascript D3.js如何将强制布局的节点安排在圆上,javascript,d3.js,force-layout,Javascript,D3.js,Force Layout,我已经开发了一个部队布局来表示社会群体之间的关系。现在,我想让节点分布在一个圆圈中,链接将它们连接起来。最好的方法是什么 代码的完整版本(没有数据)在这里 (为什么我也必须在这里包含代码?这里是主要部分) 这是: 此网络图使用D3 force布局来绘制节点和链接,但我们没有使用D3.force()来查找最佳节点位置,而是绘制了一条不可见的圆弧,并沿圆周均匀放置节点 JS-Bin line.node-link,path.node-link{ 填充:无; 笔画:黑色 } 圆节点{ 填充物:白色;
JS-Bin
line.node-link,path.node-link{
填充:无;
笔画:黑色
}
圆节点{
填充物:白色;
笔画:黑色
}
圆。节点+文本{
文本锚定:中间;
}
正文{
字体系列:无衬线;
指针事件:无;
}
//随机节点数(除非更改节点直径,否则在>25时会变得拥挤)
var-num=20;
//返回0和num之间的随机整数
函数getRandomInt(){返回Math.floor(Math.random()*(num));}
//节点返回一个{id:1,fixed:true}的[列表]
var nodes=d3.range(num.map)(函数(d){return{id:d};});
//links返回{source:0,target:1}的[list](值指节点的标记)
var links=d3.range(num).map(函数(d){return{source:getRandomInt(),target:getRandomInt()};});
可变宽度=500,
高度=500;
var-force=d3.layout.force()
.节点(节点)
.链接(links)
.尺寸([宽度、高度]);
//沿圆弧均匀地分隔节点
var circleCoord=函数(节点、索引、num_节点){
var percentage=circle.node().getTotalLength();
var pointAtLength=函数(l){return circle.node().getPointAtLength(l)};
var sectionLength=(周长)/num_节点;
var位置=截面长度*索引+截面长度/2;
返回点长度(圆周位置)
}
//淡出未连接到节点d的线
var是连接的=函数(d,不透明度){
lines.transition()样式(“笔划不透明度”,函数(o){
返回o.source==d | | o.target==d?1:不透明度;
});
}
var svg=d3.选择(“正文”).追加(“svg”)
.attr(“宽度”,宽度)
.attr(“高度”,高度);
//用于放置节点的不可见圆
//它实际上是两个圆弧,所以我们可以使用getPointAtLength()和getTotalLength()方法
var dim=宽度-80
var circle=svg.append(“路径”)
.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(“填充”和“#f5”);
force.start();
//设置容器节点的坐标
forEach(函数(n,i){
var coord=circleCoord(n,i,nodes.length)
n、 x=坐标x
n、 y=坐标y
});
//将此链接用于直线链接。。。
//var lines=svg.selectAll(“line.node链接”)
//.data(links).enter().append(“行”)
//.attr(“类”、“节点链接”)
//.attr(“x1”,函数(d){返回d.source.x;})
//.attr(“y1”,函数(d){返回d.source.y;})
//.attr(“x2”,函数(d){返回d.target.x;})
//.attr(“y2”,函数(d){返回d.target.y;});
//…或将此链接用于曲线链接
var lines=svg.selectAll(“path.node链接”)
.data(links).enter().append(“路径”)
.attr(“类”、“节点链接”)
.attr(“d”,函数(d){
变量dx=d.target.x-d.source.x,
dy=d.target.y-d.source.y,
dr=Math.sqrt(dx*dx+dy*dy);
返回“M”+
d、 source.x+“,”+
d、 source.y+“A”+
dr+“,“+dr+”0,1”+
d、 target.x+“,”+
d、 目标.y;
});
var gnodes=svg.selectAll('g.gnode')
.data(节点).enter().append('g')
.attr(“转换”,函数(d){
返回“translate(“+d.x+”,“+d.y+”)
})
.classed('gnode',true);
var node=gnodes.append(“圆”)
.attr(“r”,25)
.attr(“类”、“节点”)
.on(“鼠标指针”,功能(d){
是否已连接(d,0.1)
node.transition().duration(100).attr(“r”,25)
d3.选择(this).transition().duration(100).attr(“r”,30)
})
.on(“鼠标移动”,功能(d){
node.transition().duration(100).attr(“r”,25);
是连接的(d,1);
});
var labels=gnodes.append(“文本”)
.attr(“dy”,4)
.text(函数(d){return d.id})
听起来部队布局不合适。也许是a?我查看了径向树,但似乎因为它们是用于层次结构的,所以它不支持我需要的多对多关系。(我可能错了。)好吧,但无论如何,正如你所知道的那样,原力的布局是不合适的。出于好奇,你有没有做到这一点?我遇到了一个类似的问题。我测试了D3PLUS,但也不完美
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
<!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>