我正在创建一个基于树状布局的动态(交互式点击展开/折叠)组织结构图,但是我已经明确定义了自己的节点布局和链接例程,以允许自上而下和弯头路径链接。 我现在需要添加一个功能来支持混合模式布局,将一些节点垂直放置在它们的父节点下,而不是在较低的Y偏移上和相同的X平面上(水平地)显示它们。
我刚刚创建了一个原型,以最底层的深度工作,并觉得这是很好理解和功能。获取最大深度并与当前进行比较已经完成了这个,并且计算动态间距函数的技巧非常棘手。
有谁可以解释这种偏移如何与分离功能一起工作?我发现我需要将最底部的节点设置为0,因为任何其他值将导致宽度指数增长,因为更多节点被添加为子节点,即父节点被进一步推开。为了解决这个问题,我还为父母设置了静态间距值。
我尚未开始研究的另一件事是将混合布局结合起来如何管理高度。所以我想知道......我是否应该使用间距/分离功能,或者只是在我的更新中手工制作放置并输入数据事件。
这是我接受它的地方,对最初的结果感到满意 enter image description here
如果有人可以分享一些关于推出自定义布局的想法或经验,那就太好了。
非常感谢! 如果有人感兴趣,则使用以下代码来实现混合布局。
分离功能
.separation(function (a, b) {
var separation = a.parent == b.parent ? 1.2 : 2;
//return separation;
//-- SKIPPING THIS CODE
var nodeDepth = a.depth,
maxDepth = getMaxDepth(ancestorRoot) - 1;
if (maxDepth !== 0) {
var aX = Number(a.x || 0).toFixed(2),
aY = Number(a.y || 0).toFixed(2),
bX = Number(b.x || 0).toFixed(2),
bY = Number(b.y || 0).toFixed(2);
if (nodeDepth === maxDepth) {
if ((a.parent.ID !== b.parent.ID) && (a.parent.depth === b.parent.depth)) {
//--Parents are at the same depth so we can adjust accordingly.
var parentIndex = "";
console.error("\tDifferent parents, but both parents are at the same depth so the separation should be marginly adjusted.");
separation = .1;
}
else if ((a.parent.ID === b.parent.ID)) {
console.error("\tSame parents, no separation needed.");
console.warn("#Separation - " + separation + " For:\n\t" + a.Props.DisplayName + " [" + aX + "," + aY + "]" + " and " + b.Props.DisplayName + " [" + bX + "," + bY + "]");
separation = 0;
}
}
else if (nodeDepth === (maxDepth - 1)) {
separation = 1.5;
console.warn("#Separation - " + separation + " For:\n\t" + a.Props.DisplayName + " [" + aX + "," + aY + "]" + " and " + b.Props.DisplayName + " [" + bX + "," + bY + "]");
}
}
return separation;
//return 100;
});
NODE PLACEMENT
function nodeTransformEnd(d, depth, i) {
//console.info("End node transform for " + d.Props.DisplayName);
var x0 = d.px = d.x = (((width / 2) + d.x) - boxWidth / 2);
var y0 = d.py = d.y = (((height / 2) + d.y) - boxHeight / 2);
var nodeDepth = d.depth,
maxDepth = getMaxDepth(ancestorRoot) - 1;
if (maxDepth === 0) maxDepth = -1;
if (nodeDepth === maxDepth) {
var index = d.parent.children.indexOf(d);
console.log(d.Props.DisplayName + " -> " + d.parent.Props.DisplayName + " (Depth=" + d.parent.depth + ")");
console.log("\t Current depth: " + d.depth + ", Max depth: " + maxDepth + ", Sibling index: " + index);
//console.log("\t " + d.Props.DisplayName + " is index of " + index);
//return "translate(" + x0 + "," + y0 + ")";
x0 = d.px = d.x = (d.parent.x - ((boxWidth / 2) + 50));
y0 = d.py = d.y = (y0 + (index * (50 + 80)));
}
return "translate(" + x0 + "," + y0 + ")";
} // end: util- nodeTransformEnd
LINK PATH
function drawElbowLineEnd(d, depth, i) {
var nodeLinks = calculateNodeLinkPoints(d, depth, i),
s = nodeLinks.source,
t = nodeLinks.target;
var path = [];
var nodeDepth = d.target.depth,
maxDepth = getMaxDepth(ancestorRoot) - 1;
if (maxDepth === 0) maxDepth = -1;
if (nodeDepth === maxDepth) {
//-- Start at middle of parent node
path.push("M", s.x, ",", s.y);
//-- Vertical to middle of target node
path.push("V", (t.y + (boxHeight / 2)));
//-- Meet to the side of the child node
path.push("H", (t.x - boxWidth / 2));
//console.log(path.join(" "));
}
else {
//-- Normal horizontal layout - elbow
path.push("M", s.x, ",", s.y,
"V", ((s.y + t.y) / 2),
"H", t.x,
"V", t.y);
}
return path.join("");
} // end: util- drawElbowLineEnd
获得最大深度
var getMaxDepth = function getMaxDepth(startNode){
startNode = startNode || ancestorRoot;
var depth=0;
if(startNode.children){
startNode.children.forEach(function(child){
var maxDepthForCurrentTree = getMaxDepth(child);
if(maxDepthForCurrentTree > depth){
depth = maxDepthForCurrentTree;
}
});
}
return depth+1;
};