D3.js D3带边界框的力定向布局
我是D3新手,在设置强制导向布局的边界时遇到困难。我已经设法拼凑(从示例中)我想要的东西,但我需要包含图表。在tick函数中,变换/平移将正确显示图形,但当我将cx和cy与Math.max/min一起使用时(请参见注释代码),节点将固定到 左上角,线被正确包含 下面是我所拥有的。。。我做错了什么D3.js D3带边界框的力定向布局,d3.js,force-layout,D3.js,Force Layout,我是D3新手,在设置强制导向布局的边界时遇到困难。我已经设法拼凑(从示例中)我想要的东西,但我需要包含图表。在tick函数中,变换/平移将正确显示图形,但当我将cx和cy与Math.max/min一起使用时(请参见注释代码),节点将固定到 左上角,线被正确包含 下面是我所拥有的。。。我做错了什么 var w=960, h=500, r=8, z = d3.scale.category20(); var color = d3.scale.category20(); var force = d
var w=960, h=500, r=8, z = d3.scale.category20();
var color = d3.scale.category20();
var force = d3.layout.force()
.linkDistance( function(d) { return (d.value*180) } )
.linkStrength( function(d) { return (1/(1+d.value)) } )
.charge(-1000)
//.gravity(.08)
.size([w, h]);
var vis = d3.select("#chart").append("svg:svg")
.attr("width", w)
.attr("height", h)
.append("svg:g")
.attr("transform", "translate(" + w / 4 + "," + h / 3 + ")");
vis.append("svg:rect")
.attr("width", w)
.attr("height", h)
.style("stroke", "#000");
d3.json("miserables.json", function(json) {
var link = vis.selectAll("line.link")
.data(json.links);
link.enter().append("svg:line")
.attr("class", "link")
.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.source.x; })
.attr("y2", function(d) { return d.source.y; })
.style("stroke-width", function(d) { return (1/(1+d.value))*5 });
var node = vis.selectAll("g.node")
.data(json.nodes);
var nodeEnter = node.enter().append("svg:g")
.attr("class", "node")
.on("mouseover", fade(.1))
.on("mouseout", fade(1))
.call(force.drag);
nodeEnter.append("svg:circle")
.attr("r", r)
.style("fill", function(d) { return z(d.group); })
.style("stroke", function(d) { return
d3.rgb(z(d.group)).darker(); });
nodeEnter.append("svg:text")
.attr("text-anchor", "middle")
.attr("dy", ".35em")
.text(function(d) { return d.name; });
force
.nodes(json.nodes)
.links(json.links)
.on("tick", tick)
.start();
function tick() {
// This works
node.attr("transform", function(d) { return "translate(" + d.x + ","
+ d.y + ")"; });
// This contains the lines within the boundary, but the nodes are
stuck in the top left corner
//node.attr("cx", function(d) { return d.x = Math.max(r, Math.min(w
- r, d.x)); })
// .attr("cy", function(d) { return d.y = Math.max(r, Math.min(h -
r, d.y)); });
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; });
}
var linkedByIndex = {};
json.links.forEach(function(d) {
linkedByIndex[d.source.index + "," + d.target.index] = 1;
});
function isConnected(a, b) {
return linkedByIndex[a.index + "," + b.index] ||
linkedByIndex[b.index + "," + a.index] || a.index == b.index;
}
function fade(opacity) {
return function(d) {
node.style("stroke-opacity", function(o) {
thisOpacity = isConnected(d, o) ? 1 : opacity;
this.setAttribute('fill-opacity', thisOpacity);
return thisOpacity;
});
link.style("stroke-opacity", opacity).style("stroke-opacity",
function(o) {
return o.source === d || o.target === d ? 1 : opacity;
});
};
}
});
我的脑子里有一个问题。position Verlet集成允许您在“tick”事件侦听器中定义几何约束(例如边界框和);只需移动节点以符合约束,模拟将相应地进行调整
也就是说,重力绝对是解决这个问题的更灵活的方法,因为它允许用户暂时将图形拖动到边界框之外,然后图形将恢复。根据图形的大小和显示区域的大小,您应该尝试不同的重力和电荷的相对强度(排斥力),以使图形适合。注释的代码在节点上工作,根据您的定义,节点是svg g(分组)元素,不操作cx/cy属性。选择节点内的圆元素以激活这些属性:
node.select("circle") // select the circle element in that node
.attr("cx", function(d) { return d.x = Math.max(r, Math.min(w - r, d.x)); })
.attr("cy", function(d) { return d.y = Math.max(r, Math.min(h - r, d.y)); });
自定义部队也是一种可能的解决方案。我更喜欢这种方法,因为不仅显示的节点被重新定位,而且整个模拟都与边界力一起工作
let simulation = d3.forceSimulation(nodes)
...
.force("bounds", boxingForce);
// Custom force to put all nodes in a box
function boxingForce() {
const radius = 500;
for (let node of nodes) {
// Of the positions exceed the box, set them to the boundary position.
// You may want to include your nodes width to not overlap with the box.
node.x = Math.max(-radius, Math.min(radius, node.x));
node.y = Math.max(-radius, Math.min(radius, node.y));
}
}
我已经使用了一些参数,并决定除非有人有解决方案,否则我将只使用大量的.gravity()。如果图形非常大,它仍然会引起问题,但是其他方面应该包含节点。关键是add
node.attr(“cx”,函数(d){return d.x=Math.max(r,Math.min(width-r,d.x));}).attr(“cy”,函数(d){return d.y=Math.max(r,Math.min(height-r,d.y));})
如果使用path
而不是line
,则需要为'attr('d',function(){…});`还有,是否有办法提供边界坐标(仅在定义的4个角内移动)