diff --git a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/diagram/ElkDiagramRenderer.java b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/diagram/ElkDiagramRenderer.java index 89ae853e..8ba5cb7c 100644 --- a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/diagram/ElkDiagramRenderer.java +++ b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/diagram/ElkDiagramRenderer.java @@ -339,10 +339,9 @@ public class ElkDiagramRenderer implements DiagramRenderer { positionedEdges.add(new PositionedEdge(sourceId, targetId, label, points)); } - // Normalize: shift all nodes and edges so bounding box starts at (0, 0). - // ELK can place nodes at arbitrary positions within the root graph; - // normalizing ensures the output starts at the origin regardless. - normalizePositions(positionedNodes, positionedEdges); + // Note: normalization (shifting to 0,0 origin) is handled per-section + // in the frontend's useDiagramData.ts, which separates main flow from + // handler sections and normalizes each independently. // Compute bounding box from all positioned nodes and edges double totalWidth = 0; @@ -364,61 +363,6 @@ public class ElkDiagramRenderer implements DiagramRenderer { return new LayoutResult(layout, nodeColors, compoundInfos); } - /** - * Shift all positioned nodes and edge points so the bounding box starts at (0, 0). - * This compensates for ELK placing nodes at non-zero origins within its root graph. - */ - private void normalizePositions(List nodes, List edges) { - // Find minimum x/y across all nodes (recursively including children) - double minX = Double.MAX_VALUE; - double minY = Double.MAX_VALUE; - for (PositionedNode pn : allNodes(nodes)) { - if (pn.x() < minX) minX = pn.x(); - if (pn.y() < minY) minY = pn.y(); - } - for (PositionedEdge pe : edges) { - for (double[] pt : pe.points()) { - if (pt[0] < minX) minX = pt[0]; - if (pt[1] < minY) minY = pt[1]; - } - } - - if (minX <= 0 && minY <= 0) return; // Already at or past origin - - // Shift nodes — PositionedNode is a record, so we must rebuild - double shiftX = minX > 0 ? minX : 0; - double shiftY = minY > 0 ? minY : 0; - - for (int i = 0; i < nodes.size(); i++) { - nodes.set(i, shiftNode(nodes.get(i), shiftX, shiftY)); - } - - // Shift edge points in-place - for (PositionedEdge pe : edges) { - for (double[] pt : pe.points()) { - pt[0] -= shiftX; - pt[1] -= shiftY; - } - } - } - - /** Recursively shift a PositionedNode and its children by (dx, dy). */ - private PositionedNode shiftNode(PositionedNode pn, double dx, double dy) { - List shiftedChildren = List.of(); - if (pn.children() != null && !pn.children().isEmpty()) { - shiftedChildren = new ArrayList<>(); - for (PositionedNode child : pn.children()) { - shiftedChildren.add(shiftNode(child, dx, dy)); - } - } - return new PositionedNode( - pn.id(), pn.label(), pn.type(), - pn.x() - dx, pn.y() - dy, - pn.width(), pn.height(), - shiftedChildren - ); - } - // ---------------------------------------------------------------- // SVG drawing helpers // ---------------------------------------------------------------- diff --git a/ui/src/components/ProcessDiagram/useDiagramData.ts b/ui/src/components/ProcessDiagram/useDiagramData.ts index fbf6a3f9..a4d9e2df 100644 --- a/ui/src/components/ProcessDiagram/useDiagramData.ts +++ b/ui/src/components/ProcessDiagram/useDiagramData.ts @@ -55,20 +55,30 @@ export function useDiagramData( e => mainNodeIds.has(e.sourceId) && mainNodeIds.has(e.targetId), ); - // Compute main section bounding box + // Normalize main section to start at (0, 0) — ELK can place nodes + // at arbitrary positions within its root graph const mainBounds = computeBounds(mainNodes); + const mainOffX = mainBounds.minX; + const mainOffY = mainBounds.minY; + const shiftedMainNodes = shiftNodes(mainNodes, mainOffX, mainOffY); + const shiftedMainEdges = mainEdges.map(e => ({ + ...e, + points: e.points.map(p => [p[0] - mainOffX, p[1] - mainOffY]), + })); + const mainWidth = mainBounds.maxX - mainBounds.minX; + const mainHeight = mainBounds.maxY - mainBounds.minY; const sections: DiagramSection[] = [ { label: 'Main Route', - nodes: mainNodes, - edges: mainEdges, + nodes: shiftedMainNodes, + edges: shiftedMainEdges, offsetY: 0, }, ]; - let currentY = mainBounds.maxY + SECTION_GAP; - let maxWidth = mainBounds.maxX; + let currentY = mainHeight + SECTION_GAP; + let maxWidth = mainWidth; const addHandlerSections = ( handlers: { label: string; nodes: DiagramNode[] }[], @@ -106,7 +116,7 @@ export function useDiagramData( // Then error handlers addHandlerSections(errorSections, 'error'); - const totalWidth = Math.max(layout.width ?? 0, mainBounds.maxX, maxWidth); + const totalWidth = Math.max(layout.width ?? 0, mainWidth, maxWidth); const totalHeight = currentY; return { sections, totalWidth, totalHeight };