我正在尝试绘制一个力导向图,每个节点有多个边,其边缘看起来彼此不同,无论目标与源节点之间的距离如何。
TL;博士
linknum
并使用它来计算弧半径)如果2个连接节点之间的距离变得太大,则弧会相互合并 - 因为弧不能“扩展”超过与2重叠的假想圆的半径。
因此,应该看起来彼此不同的边缘合并成一个大弧。
我可以修复Arc半径以对应有效/合适的链接距离,这样就不会发生,但遗憾的是节点可由用户拖动/锚定(为简洁起见,我在下面的代码示例中省略了用户拖动/锚定代码)
我认为使用贝塞尔曲线而不是没有扩展限制的弧更有意义。但是,我不确定如何为这个案例计算控制点
拖动滑块会重绘图表,增加链接/边缘距离。大于75的值创建我正在谈论的弧/链接/边合并。
function draw(linkDistance) {
var links = [{
source: "Microsoft",
target: "Amazon",
type: "licensing"
}, {
source: "Microsoft",
target: "Amazon",
type: "suit"
}, {
source: "Microsoft",
target: "Amazon",
type: "resolved"
}];
//sort links by source, then target
links.sort(function(a, b) {
if (a.source > b.source) {
return 1;
}
else if (a.source < b.source) {
return -1;
}
else {
if (a.target > b.target) {
return 1;
}
if (a.target < b.target) {
return -1;
}
else {
return 0;
}
}
});
//any links with duplicate source and target get an incremented 'linknum'
for (var i = 0; i < links.length; i++) {
if (i != 0 &&
links[i].source == links[i - 1].source &&
links[i].target == links[i - 1].target) {
links[i].linknum = links[i - 1].linknum + 1;
}
else {
links[i].linknum = 1;
};
};
var nodes = {};
// Compute the distinct nodes from the links.
links.forEach(function(link) {
link.source = nodes[link.source] || (nodes[link.source] = {
name: link.source
});
link.target = nodes[link.target] || (nodes[link.target] = {
name: link.target
});
});
var w = 300,
h = 200;
var force = d3.layout.force()
.nodes(d3.values(nodes))
.links(links)
.size([w, h])
.linkDistance(linkDistance)
.charge(-300)
.on("tick", tick)
.start();
var svg = d3.select("#chart").append("svg:svg")
.attr("width", w)
.attr("height", h);
// Per-type markers, as they don't inherit styles.
svg.append("svg:defs").selectAll("marker")
.data(["suit", "licensing", "resolved"])
.enter().append("svg:marker")
.attr("id", String)
.attr("viewBox", "0 -5 10 10")
.attr("refX", 15)
.attr("refY", -1.5)
.attr("markerWidth", 6)
.attr("markerHeight", 6)
.attr("orient", "auto")
.append("svg:path")
.attr("d", "M0,-5L10,0L0,5");
var path = svg.append("svg:g").selectAll("path")
.data(force.links())
.enter().append("svg:path")
.attr("class", function(d) {
return "link " + d.type;
})
.attr("marker-end", function(d) {
return "url(#" + d.type + ")";
});
var circle = svg.append("svg:g").selectAll("circle")
.data(force.nodes())
.enter().append("svg:circle")
.attr("r", 6)
.call(force.drag);
var text = svg.append("svg:g").selectAll("g")
.data(force.nodes())
.enter().append("svg:g");
text.append("svg:text")
.attr("x", 8)
.attr("y", ".31em")
.text(function(d) {
return d.name;
});
// Use elliptical arc path segments to doubly-encode directionality.
function tick() {
path.attr("d", function(d) {
var dx = d.target.x - d.source.x,
dy = d.target.y - d.source.y,
dr = 75 / d.linknum; //linknum is defined above
return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y;
});
circle.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
text.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
}
}
window.onload = function() {
document.getElementById("range").addEventListener("input", function(e) {
var value = this.value;
document.getElementById("chart").innerHTML = "";
document.getElementById("label").innerHTML = value;
draw(value);
})
draw(70);
}
path.link {
fill: none;
stroke: #666;
stroke-width: 1.5px;
}
marker#licensing {
fill: green;
}
path.link.licensing {
stroke: green;
}
path.link.resolved {
stroke-dasharray: 0,2 1;
}
circle {
fill: #ccc;
stroke: #333;
stroke-width: 1.5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.9/d3.min.js"></script>
<input type="range" id="range" step="2" value="70" min="50" max="150">
<span id="label"></span>
<div id="chart"></div>
答案 0 :(得分:1)
path.attr("d", function(d) {
var dx = d.target.x - d.source.x,
dy = d.target.y - d.source.y;
var qx = dy / 1 * d.linknum, //linknum is defined above
qy = -dx / 1 * d.linknum;
var qx1 = (d.source.x + (dx / 2)) + qx,
qy1 = (d.source.y + (dy / 2)) + qy;
return "M"+d.source.x+" "+d.source.y+" C" + d.source.x + " " + d.source.y + " " + qx1 + " " + qy1 + " " + d.target.x + " " + d.target.y;
});
这会将它们从弧(路径语法中的A)转换为beziers(路径语法中的C)。控制点刚好从两个节点之间的线中心垂直伸出,“伸出”距离缩放到linknum变量。
http://jsfiddle.net/a5ua66zy/2/
聚苯乙烯。可以增加qx / qy变量中的'1'以将曲线收紧在一起
PS2。如果您不希望弧在被拖动时摇摆不定(即取决于节点之间的距离),您可以这样做:
var ds = Math.sqrt ((dx * dx) + (dy * dy));
var qx = (dy / ds) * 20 * d.linknum, //linknum is defined above
qy = -(dx / ds) * 20 * d.linknum;
// 20 is the separation between adjacent curves