fix: proper LCA and bounding box for multi-root ELK layout
1. findCommonParent: replaced with correct lowest common ancestor algorithm using ancestor set intersection (previous version only walked from node 'a', not a true LCA) 2. Bounding box: compute totalWidth/totalHeight from actual positioned node coordinates instead of rootNode.getWidth/Height. The rootNode dimensions don't account for handler sections in separate ELK roots. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -355,8 +355,25 @@ public class ElkDiagramRenderer implements DiagramRenderer {
|
|||||||
positionedEdges.add(new PositionedEdge(sourceId, targetId, label, points));
|
positionedEdges.add(new PositionedEdge(sourceId, targetId, label, points));
|
||||||
}
|
}
|
||||||
|
|
||||||
double totalWidth = rootNode.getWidth();
|
// Compute bounding box from actual positioned node coordinates
|
||||||
double totalHeight = rootNode.getHeight();
|
// (not rootNode dimensions, which ignore handler roots)
|
||||||
|
double totalWidth = 0;
|
||||||
|
double totalHeight = 0;
|
||||||
|
for (PositionedNode pn : positionedNodes) {
|
||||||
|
double right = pn.x() + pn.width();
|
||||||
|
double bottom = pn.y() + pn.height();
|
||||||
|
if (right > totalWidth) totalWidth = right;
|
||||||
|
if (bottom > totalHeight) totalHeight = bottom;
|
||||||
|
// Also check children bounds for compounds
|
||||||
|
if (pn.children() != null) {
|
||||||
|
for (PositionedNode child : pn.children()) {
|
||||||
|
double cr = pn.x() + child.x() + child.width();
|
||||||
|
double cb = pn.y() + child.y() + child.height();
|
||||||
|
if (cr > totalWidth) totalWidth = cr;
|
||||||
|
if (cb > totalHeight) totalHeight = cb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
DiagramLayout layout = new DiagramLayout(totalWidth, totalHeight, positionedNodes, positionedEdges);
|
DiagramLayout layout = new DiagramLayout(totalWidth, totalHeight, positionedNodes, positionedEdges);
|
||||||
return new LayoutResult(layout, nodeColors, compoundInfos);
|
return new LayoutResult(layout, nodeColors, compoundInfos);
|
||||||
@@ -572,19 +589,25 @@ public class ElkDiagramRenderer implements DiagramRenderer {
|
|||||||
return current;
|
return current;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Proper lowest common ancestor of two ELK nodes. */
|
||||||
private ElkNode findCommonParent(ElkNode a, ElkNode b) {
|
private ElkNode findCommonParent(ElkNode a, ElkNode b) {
|
||||||
if (a.getParent() == b.getParent()) {
|
// Collect all ancestors of 'a' (including a itself)
|
||||||
return a.getParent();
|
Set<ElkNode> ancestorsOfA = new HashSet<>();
|
||||||
|
ElkNode current = a;
|
||||||
|
while (current != null) {
|
||||||
|
ancestorsOfA.add(current);
|
||||||
|
current = current.getParent();
|
||||||
}
|
}
|
||||||
// If one is the parent of the other
|
// Walk up from 'b' until we find a common ancestor
|
||||||
if (a.getParent() != null && a.getParent() == b) return b;
|
current = b;
|
||||||
if (b.getParent() != null && b.getParent() == a) return a;
|
while (current != null) {
|
||||||
// Default: root (grandparent)
|
if (ancestorsOfA.contains(current)) {
|
||||||
ElkNode parent = a.getParent();
|
return current;
|
||||||
while (parent != null && parent.getParent() != null) {
|
}
|
||||||
parent = parent.getParent();
|
current = current.getParent();
|
||||||
}
|
}
|
||||||
return parent != null ? parent : a.getParent();
|
// Fallback: root of 'a'
|
||||||
|
return getElkRoot(a);
|
||||||
}
|
}
|
||||||
|
|
||||||
private double getAbsoluteX(ElkNode node, ElkNode root) {
|
private double getAbsoluteX(ElkNode node, ElkNode root) {
|
||||||
|
|||||||
Reference in New Issue
Block a user