我正在使用 dagre-d3 并创建了这个示例图。是否可以避免标记为1的样本中的交叉问题。也许这可以将边缘连接标记到节点的底部。我不确定如何实现此功能。有人可以帮忙吗?
此外,是否可以将边缘 2、3 和 4 拉直。有足够的空间来拉直它们。我只是不知道该怎么做。
任何帮助,将不胜感激。
泽山
我正在使用 dagre-d3 并创建了这个示例图。是否可以避免标记为1的样本中的交叉问题。也许这可以将边缘连接标记到节点的底部。我不确定如何实现此功能。有人可以帮忙吗?
此外,是否可以将边缘 2、3 和 4 拉直。有足够的空间来拉直它们。我只是不知道该怎么做。
任何帮助,将不胜感激。
泽山
这是您要解决的一个非常重要的问题。AFAIK dagre 使用Brandes-Köpf算法的变体来计算比对。即使你能理解那个算法,调整它也很困难。相反,使用完全不同的算法可能会给您带来更好的结果:在yFiles for HTML 的分层/Sugiyama 样式布局的实现中,使用了 Simplex Network Rank Assignment 算法,它通常不会遇到您在您的问题中看到的问题(否则非常好) 绘画。GraphViz 也有该算法的实现,所以也许使用它也会为您的示例提供更好的结果。Simplex 方法的计算成本要高得多,但很容易配置和微调。
关于您的第一点:通过快速查看来源,我无法理解为什么问题出现在您的 Dagre 样本中。看起来传入边缘的端口分配无法正常工作 - 端口的位置应根据上一层节点的相对位置进行排序。看起来不仅仅是贝塞尔控制点是错误的。
// Create a new directed graph
var g = new dagreD3.graphlib.Graph().setGraph({});
// function to shuffle the list...
function shuffle(a) {
var j, x, i;
for (i = a.length; i; i -= 1) {
j = Math.floor(Math.random() * i);
x = a[i - 1];
a[i - 1] = a[j];
a[j] = x;
}
return a;
}
var nodes = ["10007154_1100", "148570017_1100", "148570018_1100", "148570019_1100",
"148570025_1100", "148570010_1100", "148570021_1100", "148570020_1100",
"148570026_1100", "148570011_1100", "148570022_1100", "148570010_1200", "148570020_1200", "148570026_1200", "148570023_1100", "148570011_1200",
"148570023_1200"
];
// collect edges to a list
var edgeList = [
["10007154_1100", "148570017_1100", {
"label": ""
}],
["148570017_1100", "148570018_1100", {
"label": ""
}],
["148570018_1100", "148570019_1100", {
"label": ""
}],
["148570018_1100", "148570025_1100", {
"label": ""
}],
["148570019_1100", "148570020_1100", {
"label": ""
}],
["148570019_1100", "148570021_1100", {
"label": ""
}],
["148570019_1100", "148570010_1100", {
"label": ""
}],
["148570025_1100", "148570010_1100", {
"label": ""
}],
["148570025_1100", "148570026_1100", {
"label": ""
}],
["148570021_1100", "148570022_1100", {
"label": ""
}],
["148570010_1100", "148570011_1100", {
"label": ""
}],
["148570010_1100", "148570010_1200", {
"label": ""
}],
["148570020_1100", "148570020_1200", {
"label": ""
}],
["148570026_1100", "148570026_1200", {
"label": ""
}],
["148570026_1200", "148570011_1200", {
"label": ""
}],
["148570010_1200", "148570011_1200", {
"label": ""
}],
["148570022_1100", "148570023_1100", {
"label": ""
}],
["148570023_1100", "148570023_1200", {
"label": ""
}]
];
// Automatically label each of the nodes
var svg = d3.select("svg"),
inner = svg.select("g");
function render_graph(render) {
var max_cnt = 100; // try 100 times, if optimal not found, give up
var iter_cnt = 0;
var optimalArray, best_result;
while (max_cnt--) {
var g = new dagreD3.graphlib.Graph().setGraph({});
nodes.forEach(function(node) {
g.setNode(node, {
label: node
});
});
// set edges... randomize the list
var list = shuffle(edgeList);
if (!optimalArray) optimalArray = list;
edgeList.forEach((edge) => {
g.setEdge.apply(g, edge);
})
// Set the rankdir
g.graph().rankdir = "LR";
g.graph().nodesep = 60;
render(inner, g);
var nn = svg.select(".edgePaths");
var paths = nn[0][0];
var fc = paths.firstChild;
var boxes = [];
while (fc) {
// console.log(fc.firstChild.getAttribute("d"))
var path = fc.firstChild.getAttribute("d");
var coords = path.split(/,|L/).map(function(c) {
var n = c;
if ((c[0] == "M" || c[0] == "L")) n = c.substring(1);
return parseFloat(n);
})
boxes.push({
left: coords[0],
top: coords[1],
right: coords[coords.length - 2],
bottom: coords[coords.length - 1]
});
// console.log(coords);
fc = fc.nextSibling;
}
// console.log("boxes", boxes);
var collisionCnt = 0;
boxes.forEach(function(a) {
// --> test for collisions against other nodes...
boxes.forEach(function(b) {
if (a == b) return;
// test if outside
if ((a.right < b.left) ||
(a.left > b.right) ||
(a.top > b.bottom) ||
(a.bottom < b.top)) {
// test if inside
if (a.left >= b.left && a.left <= b.right || a.right >= b.left && a.right <= b.right) {
if (a.top <= b.top && a.top >= b.bottom) {
collisionCnt++;
}
if (a.bottom <= b.top && a.bottom >= b.bottom) {
collisionCnt++;
}
}
} else {
collisionCnt++;
}
})
})
console.log("collisions ", collisionCnt);
if (collisionCnt == 0) {
optimalArray = list.slice();
console.log("Iteration cnt ", iter_cnt);
break;
}
if (typeof(best_result) == "undefined") {
best_result = collisionCnt;
} else {
if (collisionCnt < best_result) {
optimalArray = list.slice();
best_result = collisionCnt;
}
}
iter_cnt++;
}
// if no optimal was found just render what was found...
if (best_result >= 0) {
var g = new dagreD3.graphlib.Graph().setGraph({});
nodes.forEach(function(node) {
g.setNode(node, {
label: node
});
});
optimalArray.forEach((edge) => {
g.setEdge.apply(g, edge);
})
g.graph().rankdir = "LR";
g.graph().nodesep = 60;
render(inner, g);
}
// Center the graph
var initialScale = 0.75;
zoom
.translate([(svg.attr("width") - g.graph().width * initialScale) / 2, 20])
.scale(initialScale)
.event(svg);
svg.attr('height', g.graph().height * initialScale + 40);
}
// Set up zoom support
var zoom = d3.behavior.zoom().on("zoom", function() {
inner.attr("transform", "translate(" + d3.event.translate + ")" +
"scale(" + d3.event.scale + ")");
});
svg.call(zoom);
// Create the renderer
var render = new dagreD3.render();
render_graph(render);
// Run the renderer. This is what draws the final graph.