@statelyai/layout 0.5.1 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, '__esModule', { value: true });
2
- require('../layered-QzYtokgN.cjs');
3
- const require_elkjs = require('../elkjs-Bh_E59N_.cjs');
2
+ require('../layered-B1AE7Zpr.cjs');
3
+ const require_elkjs = require('../elkjs--K57ydUJ.cjs');
4
4
 
5
5
  exports.compileStatechartLayout = require_elkjs.compileStatechartLayout;
6
6
  exports.default = require_elkjs.ELK;
@@ -1,4 +1,4 @@
1
- import "../layered-CUwft7on.mjs";
2
- import { a as scoreStatechartLayout, i as layoutStatechart, n as getElkRoutes, r as compileStatechartLayout, t as ELK } from "../elkjs-Dk9yOk9U.mjs";
1
+ import "../layered-BtnmL9_b.mjs";
2
+ import { a as scoreStatechartLayout, i as layoutStatechart, n as getElkRoutes, r as compileStatechartLayout, t as ELK } from "../elkjs-C243vQwg.mjs";
3
3
 
4
4
  export { compileStatechartLayout, ELK as default, getElkRoutes, layoutStatechart, scoreStatechartLayout };
@@ -1,4 +1,4 @@
1
- require('../layered-QzYtokgN.cjs');
1
+ require('../layered-B1AE7Zpr.cjs');
2
2
  const require_elkjs_worker = require('./worker.cjs');
3
3
  const require_worker_client = require('../worker-client-D4oEvhAl.cjs');
4
4
 
@@ -1,4 +1,4 @@
1
- import "../layered-CUwft7on.mjs";
1
+ import "../layered-BtnmL9_b.mjs";
2
2
  import { Worker } from "./worker.mjs";
3
3
  import { t as WorkerELK } from "../worker-client-BNjwT1AJ.mjs";
4
4
 
@@ -1,5 +1,5 @@
1
- require('../layered-QzYtokgN.cjs');
2
- const require_elkjs = require('../elkjs-Bh_E59N_.cjs');
1
+ require('../layered-B1AE7Zpr.cjs');
2
+ const require_elkjs = require('../elkjs--K57ydUJ.cjs');
3
3
 
4
4
  //#region src/elkjs/worker.ts
5
5
  /*******************************************************************************
@@ -1,5 +1,5 @@
1
- import "../layered-CUwft7on.mjs";
2
- import { t as ELK } from "../elkjs-Dk9yOk9U.mjs";
1
+ import "../layered-BtnmL9_b.mjs";
2
+ import { t as ELK } from "../elkjs-C243vQwg.mjs";
3
3
 
4
4
  //#region src/elkjs/worker.ts
5
5
  /*******************************************************************************
@@ -1,6 +1,6 @@
1
1
  const require_routing = require('./routing-Phxu1R0x.cjs');
2
- const require_layered = require('./layered-QzYtokgN.cjs');
3
- const require_layout_engine = require('./layout-engine-CQ5FcBPC.cjs');
2
+ const require_layered = require('./layered-B1AE7Zpr.cjs');
3
+ const require_layout_engine = require('./layout-engine-Dk5EklIn.cjs');
4
4
  let _statelyai_graph = require("@statelyai/graph");
5
5
 
6
6
  //#region src/layered/hierarchy-sweepiness.ts
@@ -1,6 +1,6 @@
1
1
  import { E as pathFromPoints, N as freeze, _ as pathFromSplinePoints } from "./routing-D-LfMXOF.mjs";
2
- import { $ as attachExternalPortDummy, R as createLayerSweepSession, S as CROSSING_MODEL, Z as minimizeHierarchyCrossings, b as elkjs0111ResultPolicy, c as elkLayeredOptionDefinitions, et as createExternalPortDummy, h as separateOpposingTracks, nt as createOrthogonalHypersegments, t as createLayeredScopePipeline, tt as externalPortDummyOf, v as isPerpendicularPortRestored, y as hierarchicalPortSides } from "./layered-CUwft7on.mjs";
3
- import { n as executeElkjs0111Layout } from "./layout-engine-DvDkMQM3.mjs";
2
+ import { $ as attachExternalPortDummy, R as createLayerSweepSession, S as CROSSING_MODEL, Z as minimizeHierarchyCrossings, b as elkjs0111ResultPolicy, c as elkLayeredOptionDefinitions, et as createExternalPortDummy, h as separateOpposingTracks, nt as createOrthogonalHypersegments, t as createLayeredScopePipeline, tt as externalPortDummyOf, v as isPerpendicularPortRestored, y as hierarchicalPortSides } from "./layered-BtnmL9_b.mjs";
3
+ import { n as executeElkjs0111Layout } from "./layout-engine-CHkJkgG6.mjs";
4
4
  import { createGraph } from "@statelyai/graph";
5
5
 
6
6
  //#region src/layered/hierarchy-sweepiness.ts
package/dist/index.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  const require_routing = require('./routing-Phxu1R0x.cjs');
2
- const require_layered = require('./layered-QzYtokgN.cjs');
3
- const require_layout_engine = require('./layout-engine-CQ5FcBPC.cjs');
2
+ const require_layered = require('./layered-B1AE7Zpr.cjs');
3
+ const require_layout_engine = require('./layout-engine-Dk5EklIn.cjs');
4
4
  let _statelyai_graph = require("@statelyai/graph");
5
5
 
6
6
  //#region src/layout.ts
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { C as getPathLength, D as roundCorners, E as pathFromPoints, O as segmentCrossesRect, P as routingPolicies, R as LayoutError, S as getPathBounds, T as getTangentAtLength, _ as pathFromSplinePoints, a as fanRouting, b as routeToPolylines, c as orthogonalRouting, d as routingStrategies, f as selfLoopRouting, g as getLayoutRoutes, h as trimBezierPathStart, i as curvedRouting, k as toSvgPath, l as parallelRouting, m as applyRoutePatches, n as bundleRouting, o as octilinearRouting, p as straightRouting, r as busRouting, s as organicRouting, t as bezierRouting, u as polylineRouting, v as routeToGraphPatch, w as getPointAtLength, x as flattenPath, y as routeToPoints, z as UnsupportedLayoutError } from "./routing-D-LfMXOF.mjs";
2
- import { A as breakCyclesByStronglyConnectedConnectivity, B as minimizeCrossingsInteractively, C as assignLayersByBreadthFirstModelOrder, D as assignLayersInteractively, E as assignLayersByLongestPathToSink, F as breakCyclesWithDepthFirstSearch, G as placeNodesInteractively, H as minimizeCrossingsWithMedian, I as breakCyclesWithModelOrderBreadthFirstSearch, J as routeEdgesWithPolylines, L as breakCyclesWithModelOrderDepthFirstSearch, M as breakCyclesGreedily, N as breakCyclesGreedilyByModelOrder, O as assignLayersWithCoffmanGraham, P as breakCyclesInteractively, Q as assignLayersWithNetworkSimplex, T as assignLayersByLongestPath, U as minimizeCrossingsWithModelOrder, V as minimizeCrossingsWithBarycenter, W as placeNodesInLayers, Y as routeEdgesWithSplines, _ as statechartHints, a as fromElkLayeredOptionId, c as elkLayeredOptionDefinitions, f as assignLayersWithStretchWidth, g as hint, i as layeredAlgorithm, j as breakCyclesByStronglyConnectedNodeType, k as breakCyclesByModelOrder, m as c, o as toElkLayeredOptions, p as assignLayersWithMinWidth, q as routeEdgesOrthogonally, r as getLayeredLayout, s as elkLayeredEnumValues, w as assignLayersByDepthFirstModelOrder } from "./layered-CUwft7on.mjs";
3
- import { a as getSporeCompactionLayout, c as sporeOverlapRemovalAlgorithm, d as getRectanglePackingLayout, f as rectanglePackingAlgorithm, g as getFixedLayout, h as fixedAlgorithm, i as getBuiltInLayoutAlgorithm, l as getRandomLayout, m as getBoxLayout, o as getSporeOverlapRemovalLayout, p as boxAlgorithm, r as executeLayoutAlgorithm, s as sporeCompactionAlgorithm, t as builtInLayoutAlgorithms, u as randomAlgorithm } from "./layout-engine-DvDkMQM3.mjs";
2
+ import { A as breakCyclesByStronglyConnectedConnectivity, B as minimizeCrossingsInteractively, C as assignLayersByBreadthFirstModelOrder, D as assignLayersInteractively, E as assignLayersByLongestPathToSink, F as breakCyclesWithDepthFirstSearch, G as placeNodesInteractively, H as minimizeCrossingsWithMedian, I as breakCyclesWithModelOrderBreadthFirstSearch, J as routeEdgesWithPolylines, L as breakCyclesWithModelOrderDepthFirstSearch, M as breakCyclesGreedily, N as breakCyclesGreedilyByModelOrder, O as assignLayersWithCoffmanGraham, P as breakCyclesInteractively, Q as assignLayersWithNetworkSimplex, T as assignLayersByLongestPath, U as minimizeCrossingsWithModelOrder, V as minimizeCrossingsWithBarycenter, W as placeNodesInLayers, Y as routeEdgesWithSplines, _ as statechartHints, a as fromElkLayeredOptionId, c as elkLayeredOptionDefinitions, f as assignLayersWithStretchWidth, g as hint, i as layeredAlgorithm, j as breakCyclesByStronglyConnectedNodeType, k as breakCyclesByModelOrder, m as c, o as toElkLayeredOptions, p as assignLayersWithMinWidth, q as routeEdgesOrthogonally, r as getLayeredLayout, s as elkLayeredEnumValues, w as assignLayersByDepthFirstModelOrder } from "./layered-BtnmL9_b.mjs";
3
+ import { a as getSporeCompactionLayout, c as sporeOverlapRemovalAlgorithm, d as getRectanglePackingLayout, f as rectanglePackingAlgorithm, g as getFixedLayout, h as fixedAlgorithm, i as getBuiltInLayoutAlgorithm, l as getRandomLayout, m as getBoxLayout, o as getSporeOverlapRemovalLayout, p as boxAlgorithm, r as executeLayoutAlgorithm, s as sporeCompactionAlgorithm, t as builtInLayoutAlgorithms, u as randomAlgorithm } from "./layout-engine-CHkJkgG6.mjs";
4
4
  import { getGraphIssues } from "@statelyai/graph";
5
5
 
6
6
  //#region src/layout.ts
@@ -1,4 +1,4 @@
1
- const require_layered = require('../layered-QzYtokgN.cjs');
1
+ const require_layered = require('../layered-B1AE7Zpr.cjs');
2
2
 
3
3
  exports.assignLayersByBreadthFirstModelOrder = require_layered.assignLayersByBreadthFirstModelOrder;
4
4
  exports.assignLayersByDepthFirstModelOrder = require_layered.assignLayersByDepthFirstModelOrder;
@@ -1,3 +1,3 @@
1
- import { A as breakCyclesByStronglyConnectedConnectivity, B as minimizeCrossingsInteractively, C as assignLayersByBreadthFirstModelOrder, D as assignLayersInteractively, E as assignLayersByLongestPathToSink, F as breakCyclesWithDepthFirstSearch, G as placeNodesInteractively, H as minimizeCrossingsWithMedian, I as breakCyclesWithModelOrderBreadthFirstSearch, J as routeEdgesWithPolylines, L as breakCyclesWithModelOrderDepthFirstSearch, M as breakCyclesGreedily, N as breakCyclesGreedilyByModelOrder, O as assignLayersWithCoffmanGraham, P as breakCyclesInteractively, Q as assignLayersWithNetworkSimplex, T as assignLayersByLongestPath, U as minimizeCrossingsWithModelOrder, V as minimizeCrossingsWithBarycenter, W as placeNodesInLayers, Y as routeEdgesWithSplines, a as fromElkLayeredOptionId, c as elkLayeredOptionDefinitions, d as placeNodesWithBrandesKoepf, f as assignLayersWithStretchWidth, i as layeredAlgorithm, j as breakCyclesByStronglyConnectedNodeType, k as breakCyclesByModelOrder, l as placeNodesWithNetworkSimplex, n as elkjs0111LayeredAlgorithm, o as toElkLayeredOptions, p as assignLayersWithMinWidth, q as routeEdgesOrthogonally, r as getLayeredLayout, s as elkLayeredEnumValues, t as createLayeredScopePipeline, u as placeNodesWithLinearSegments, w as assignLayersByDepthFirstModelOrder } from "../layered-CUwft7on.mjs";
1
+ import { A as breakCyclesByStronglyConnectedConnectivity, B as minimizeCrossingsInteractively, C as assignLayersByBreadthFirstModelOrder, D as assignLayersInteractively, E as assignLayersByLongestPathToSink, F as breakCyclesWithDepthFirstSearch, G as placeNodesInteractively, H as minimizeCrossingsWithMedian, I as breakCyclesWithModelOrderBreadthFirstSearch, J as routeEdgesWithPolylines, L as breakCyclesWithModelOrderDepthFirstSearch, M as breakCyclesGreedily, N as breakCyclesGreedilyByModelOrder, O as assignLayersWithCoffmanGraham, P as breakCyclesInteractively, Q as assignLayersWithNetworkSimplex, T as assignLayersByLongestPath, U as minimizeCrossingsWithModelOrder, V as minimizeCrossingsWithBarycenter, W as placeNodesInLayers, Y as routeEdgesWithSplines, a as fromElkLayeredOptionId, c as elkLayeredOptionDefinitions, d as placeNodesWithBrandesKoepf, f as assignLayersWithStretchWidth, i as layeredAlgorithm, j as breakCyclesByStronglyConnectedNodeType, k as breakCyclesByModelOrder, l as placeNodesWithNetworkSimplex, n as elkjs0111LayeredAlgorithm, o as toElkLayeredOptions, p as assignLayersWithMinWidth, q as routeEdgesOrthogonally, r as getLayeredLayout, s as elkLayeredEnumValues, t as createLayeredScopePipeline, u as placeNodesWithLinearSegments, w as assignLayersByDepthFirstModelOrder } from "../layered-BtnmL9_b.mjs";
2
2
 
3
3
  export { assignLayersByBreadthFirstModelOrder, assignLayersByDepthFirstModelOrder, assignLayersByLongestPath, assignLayersByLongestPathToSink, assignLayersInteractively, assignLayersWithCoffmanGraham, assignLayersWithMinWidth, assignLayersWithNetworkSimplex, assignLayersWithStretchWidth, breakCyclesByModelOrder, breakCyclesByStronglyConnectedConnectivity, breakCyclesByStronglyConnectedNodeType, breakCyclesGreedily, breakCyclesGreedilyByModelOrder, breakCyclesInteractively, breakCyclesWithDepthFirstSearch, breakCyclesWithModelOrderBreadthFirstSearch, breakCyclesWithModelOrderDepthFirstSearch, createLayeredScopePipeline, elkLayeredEnumValues, elkLayeredOptionDefinitions, elkjs0111LayeredAlgorithm, fromElkLayeredOptionId, getLayeredLayout, layeredAlgorithm, minimizeCrossingsInteractively, minimizeCrossingsWithBarycenter, minimizeCrossingsWithMedian, minimizeCrossingsWithModelOrder, placeNodesInLayers, placeNodesInteractively, placeNodesWithBrandesKoepf, placeNodesWithLinearSegments, placeNodesWithNetworkSimplex, routeEdgesOrthogonally, routeEdgesWithPolylines, routeEdgesWithSplines, toElkLayeredOptions };
@@ -1796,14 +1796,14 @@ function loopEnvelopes(input) {
1796
1796
  const allLoops = loopsByNodeId.get(node.id)?.slice() ?? [];
1797
1797
  const constraints = input.nodeSettings?.(node)?.portConstraints;
1798
1798
  if (allLoops.length && constraints && constraints !== "FREE" && constraints !== "UNDEFINED" && allLoops.every((edge) => edge.sourcePort !== void 0 && edge.targetPort !== void 0 && input.edgeSettings?.(edge)?.["edgeLabels.inline"] !== true && ((edge.width ?? 0) === 0 && (edge.height ?? 0) === 0 || (input.edgeSettings?.(edge)?.["edgeLabels.placement"] ?? "CENTER") === "CENTER"))) {
1799
- const size$1 = input.sizes.get(node.id) ?? {
1799
+ const size$2 = input.sizes.get(node.id) ?? {
1800
1800
  width: 0,
1801
1801
  height: 0
1802
1802
  };
1803
1803
  const rect = {
1804
1804
  x: 0,
1805
1805
  y: 0,
1806
- ...size$1
1806
+ ...size$2
1807
1807
  };
1808
1808
  const envelope$1 = {
1809
1809
  before: 0,
@@ -1811,7 +1811,7 @@ function loopEnvelopes(input) {
1811
1811
  flowBefore: 0,
1812
1812
  flowAfter: 0
1813
1813
  };
1814
- const horizontal$1 = input.direction === "right" || input.direction === "left";
1814
+ const horizontal$2 = input.direction === "right" || input.direction === "left";
1815
1815
  const reverse = input.direction === "left" || input.direction === "up";
1816
1816
  const spacing = Number(input.settings["spacing.nodeSelfLoop"] ?? 10);
1817
1817
  let supported = true;
@@ -1842,13 +1842,13 @@ function loopEnvelopes(input) {
1842
1842
  });
1843
1843
  }
1844
1844
  for (const point of route) {
1845
- const cross = horizontal$1 ? point.y : point.x;
1846
- const flow = horizontal$1 ? point.x : point.y;
1847
- const flowSize = horizontal$1 ? size$1.width : size$1.height;
1845
+ const cross = horizontal$2 ? point.y : point.x;
1846
+ const flow = horizontal$2 ? point.x : point.y;
1847
+ const flowSize$1 = horizontal$2 ? size$2.width : size$2.height;
1848
1848
  envelope$1.before = Math.max(envelope$1.before, -cross);
1849
- envelope$1.after = Math.max(envelope$1.after, cross - (horizontal$1 ? size$1.height : size$1.width));
1850
- envelope$1.flowBefore = Math.max(envelope$1.flowBefore, reverse ? flow - flowSize : -flow);
1851
- envelope$1.flowAfter = Math.max(envelope$1.flowAfter, reverse ? -flow : flow - flowSize);
1849
+ envelope$1.after = Math.max(envelope$1.after, cross - (horizontal$2 ? size$2.height : size$2.width));
1850
+ envelope$1.flowBefore = Math.max(envelope$1.flowBefore, reverse ? flow - flowSize$1 : -flow);
1851
+ envelope$1.flowAfter = Math.max(envelope$1.flowAfter, reverse ? -flow : flow - flowSize$1);
1852
1852
  }
1853
1853
  }
1854
1854
  if (supported) {
@@ -1879,10 +1879,22 @@ function loopEnvelopes(input) {
1879
1879
  flowAfter: "EAST",
1880
1880
  flowBefore: "WEST"
1881
1881
  };
1882
+ const horizontal$1 = input.direction === "right" || input.direction === "left";
1883
+ const size$1 = input.sizes.get(node.id) ?? {
1884
+ width: 0,
1885
+ height: 0
1886
+ };
1887
+ const flowSize = horizontal$1 ? size$1.width : size$1.height;
1882
1888
  for (const side of sides$1) {
1883
1889
  const selected = loops.filter((_, index) => sides$1[index % sides$1.length] === side);
1884
1890
  const canonical = canonicalSides[side];
1885
- envelope[side] = Math.max(0, ...selected.map((edge) => movableLoopTrack(input, selected, edge, canonical, ordering) + movableLoopLabelExtent(input, edge, canonical)));
1891
+ envelope[side] = Math.max(envelope[side], ...selected.map((edge) => movableLoopTrack(input, selected, edge, canonical, ordering) + movableLoopLabelExtent(input, edge, canonical)));
1892
+ if (!crossSide(canonical)) continue;
1893
+ for (const edge of selected) {
1894
+ const overhang = ((horizontal$1 ? edge.width : edge.height) ?? 0) - flowSize;
1895
+ envelope.flowBefore = Math.max(envelope.flowBefore, overhang / 2);
1896
+ envelope.flowAfter = Math.max(envelope.flowAfter, overhang / 2);
1897
+ }
1886
1898
  }
1887
1899
  envelopes.set(node.id, envelope);
1888
1900
  }
@@ -19830,16 +19842,20 @@ function* createLayeredScopePipeline(graph, options, context, fallbacks = {
19830
19842
  const forwardDummyRect = forwardDummy && placement.rectByNodeId.get(forwardDummy.id);
19831
19843
  const forwardDummyPort = forwardDummy?.ports?.[0];
19832
19844
  const forwardCross = forwardDummyRect && forwardDummyPort ? horizontal$1 ? forwardDummyRect.y + (forwardDummyPort.y ?? 0) : forwardDummyRect.x + (forwardDummyPort.x ?? 0) : horizontal$1 ? Math.round(forwardLabel.y + (forward.height ?? 0) / 2) : Math.round(forwardLabel.x + (forward.width ?? 0) / 2);
19833
- const alignPort = (node, portName) => {
19845
+ const alignedCross = (node, portName) => {
19834
19846
  const rect = placement.rectByNodeId.get(node.id);
19835
19847
  const port = node.ports?.find((candidate) => candidate.name === portName);
19836
- if (!rect || !port) return;
19848
+ if (!rect || !port) return void 0;
19837
19849
  const placedPort = placePorts(node.ports, rect, direction, (candidate) => input.portSettings?.(candidate, node), {
19838
19850
  ...options.settings,
19839
19851
  ...options.nodeSettings?.(node)
19840
19852
  }, (candidate) => getOrientedPortDirection(expanded.input, expanded.orientation, node, candidate))?.find((candidate) => candidate.name === portName);
19841
- if (!placedPort) return;
19842
- const nextCross = forwardCross - (horizontal$1 ? (placedPort.y ?? 0) + (placedPort.height ?? 0) / 2 : (placedPort.x ?? 0) + (placedPort.width ?? 0) / 2);
19853
+ if (!placedPort) return void 0;
19854
+ return forwardCross - (horizontal$1 ? (placedPort.y ?? 0) + (placedPort.height ?? 0) / 2 : (placedPort.x ?? 0) + (placedPort.width ?? 0) / 2);
19855
+ };
19856
+ const alignPort = (node, nextCross) => {
19857
+ const rect = placement.rectByNodeId.get(node.id);
19858
+ if (!rect || nextCross === void 0) return;
19843
19859
  const currentCross = horizontal$1 ? rect.y : rect.x;
19844
19860
  postCompactionNodeCrossDeltas.set(node.id, (postCompactionNodeCrossDeltas.get(node.id) ?? 0) + nextCross - currentCross);
19845
19861
  mutableRects.set(node.id, horizontal$1 ? {
@@ -19851,8 +19867,12 @@ function* createLayeredScopePipeline(graph, options, context, fallbacks = {
19851
19867
  });
19852
19868
  };
19853
19869
  if (isolatedAntiparallelPair) {
19854
- alignPort(nodesById.get(forward.sourceId), forward.sourcePort);
19855
- alignPort(nodesById.get(forward.targetId), forward.targetPort);
19870
+ const ends = [[nodesById.get(forward.sourceId), forward.sourcePort], [nodesById.get(forward.targetId), forward.targetPort]];
19871
+ const crosses$1 = ends.map(([node, port]) => alignedCross(node, port));
19872
+ if (!ends.some(([node], index) => {
19873
+ const rect = placement.rectByNodeId.get(node.id);
19874
+ return rect !== void 0 && crosses$1[index] !== void 0 && Math.abs(crosses$1[index] - (horizontal$1 ? rect.y : rect.x)) > 1e-9 && graph.edges.some((edge) => edge.sourceId === node.id && edge.targetId === node.id);
19875
+ })) ends.forEach(([node], index) => alignPort(node, crosses$1[index]));
19856
19876
  }
19857
19877
  }
19858
19878
  }
@@ -1796,14 +1796,14 @@ function loopEnvelopes(input) {
1796
1796
  const allLoops = loopsByNodeId.get(node.id)?.slice() ?? [];
1797
1797
  const constraints = input.nodeSettings?.(node)?.portConstraints;
1798
1798
  if (allLoops.length && constraints && constraints !== "FREE" && constraints !== "UNDEFINED" && allLoops.every((edge) => edge.sourcePort !== void 0 && edge.targetPort !== void 0 && input.edgeSettings?.(edge)?.["edgeLabels.inline"] !== true && ((edge.width ?? 0) === 0 && (edge.height ?? 0) === 0 || (input.edgeSettings?.(edge)?.["edgeLabels.placement"] ?? "CENTER") === "CENTER"))) {
1799
- const size$1 = input.sizes.get(node.id) ?? {
1799
+ const size$2 = input.sizes.get(node.id) ?? {
1800
1800
  width: 0,
1801
1801
  height: 0
1802
1802
  };
1803
1803
  const rect = {
1804
1804
  x: 0,
1805
1805
  y: 0,
1806
- ...size$1
1806
+ ...size$2
1807
1807
  };
1808
1808
  const envelope$1 = {
1809
1809
  before: 0,
@@ -1811,7 +1811,7 @@ function loopEnvelopes(input) {
1811
1811
  flowBefore: 0,
1812
1812
  flowAfter: 0
1813
1813
  };
1814
- const horizontal$1 = input.direction === "right" || input.direction === "left";
1814
+ const horizontal$2 = input.direction === "right" || input.direction === "left";
1815
1815
  const reverse = input.direction === "left" || input.direction === "up";
1816
1816
  const spacing = Number(input.settings["spacing.nodeSelfLoop"] ?? 10);
1817
1817
  let supported = true;
@@ -1842,13 +1842,13 @@ function loopEnvelopes(input) {
1842
1842
  });
1843
1843
  }
1844
1844
  for (const point of route) {
1845
- const cross = horizontal$1 ? point.y : point.x;
1846
- const flow = horizontal$1 ? point.x : point.y;
1847
- const flowSize = horizontal$1 ? size$1.width : size$1.height;
1845
+ const cross = horizontal$2 ? point.y : point.x;
1846
+ const flow = horizontal$2 ? point.x : point.y;
1847
+ const flowSize$1 = horizontal$2 ? size$2.width : size$2.height;
1848
1848
  envelope$1.before = Math.max(envelope$1.before, -cross);
1849
- envelope$1.after = Math.max(envelope$1.after, cross - (horizontal$1 ? size$1.height : size$1.width));
1850
- envelope$1.flowBefore = Math.max(envelope$1.flowBefore, reverse ? flow - flowSize : -flow);
1851
- envelope$1.flowAfter = Math.max(envelope$1.flowAfter, reverse ? -flow : flow - flowSize);
1849
+ envelope$1.after = Math.max(envelope$1.after, cross - (horizontal$2 ? size$2.height : size$2.width));
1850
+ envelope$1.flowBefore = Math.max(envelope$1.flowBefore, reverse ? flow - flowSize$1 : -flow);
1851
+ envelope$1.flowAfter = Math.max(envelope$1.flowAfter, reverse ? -flow : flow - flowSize$1);
1852
1852
  }
1853
1853
  }
1854
1854
  if (supported) {
@@ -1879,10 +1879,22 @@ function loopEnvelopes(input) {
1879
1879
  flowAfter: "EAST",
1880
1880
  flowBefore: "WEST"
1881
1881
  };
1882
+ const horizontal$1 = input.direction === "right" || input.direction === "left";
1883
+ const size$1 = input.sizes.get(node.id) ?? {
1884
+ width: 0,
1885
+ height: 0
1886
+ };
1887
+ const flowSize = horizontal$1 ? size$1.width : size$1.height;
1882
1888
  for (const side of sides$1) {
1883
1889
  const selected = loops.filter((_, index) => sides$1[index % sides$1.length] === side);
1884
1890
  const canonical = canonicalSides[side];
1885
- envelope[side] = Math.max(0, ...selected.map((edge) => movableLoopTrack(input, selected, edge, canonical, ordering) + movableLoopLabelExtent(input, edge, canonical)));
1891
+ envelope[side] = Math.max(envelope[side], ...selected.map((edge) => movableLoopTrack(input, selected, edge, canonical, ordering) + movableLoopLabelExtent(input, edge, canonical)));
1892
+ if (!crossSide(canonical)) continue;
1893
+ for (const edge of selected) {
1894
+ const overhang = ((horizontal$1 ? edge.width : edge.height) ?? 0) - flowSize;
1895
+ envelope.flowBefore = Math.max(envelope.flowBefore, overhang / 2);
1896
+ envelope.flowAfter = Math.max(envelope.flowAfter, overhang / 2);
1897
+ }
1886
1898
  }
1887
1899
  envelopes.set(node.id, envelope);
1888
1900
  }
@@ -19830,16 +19842,20 @@ function* createLayeredScopePipeline(graph, options, context, fallbacks = {
19830
19842
  const forwardDummyRect = forwardDummy && placement.rectByNodeId.get(forwardDummy.id);
19831
19843
  const forwardDummyPort = forwardDummy?.ports?.[0];
19832
19844
  const forwardCross = forwardDummyRect && forwardDummyPort ? horizontal$1 ? forwardDummyRect.y + (forwardDummyPort.y ?? 0) : forwardDummyRect.x + (forwardDummyPort.x ?? 0) : horizontal$1 ? Math.round(forwardLabel.y + (forward.height ?? 0) / 2) : Math.round(forwardLabel.x + (forward.width ?? 0) / 2);
19833
- const alignPort = (node, portName) => {
19845
+ const alignedCross = (node, portName) => {
19834
19846
  const rect = placement.rectByNodeId.get(node.id);
19835
19847
  const port = node.ports?.find((candidate) => candidate.name === portName);
19836
- if (!rect || !port) return;
19848
+ if (!rect || !port) return void 0;
19837
19849
  const placedPort = placePorts(node.ports, rect, direction, (candidate) => input.portSettings?.(candidate, node), {
19838
19850
  ...options.settings,
19839
19851
  ...options.nodeSettings?.(node)
19840
19852
  }, (candidate) => getOrientedPortDirection(expanded.input, expanded.orientation, node, candidate))?.find((candidate) => candidate.name === portName);
19841
- if (!placedPort) return;
19842
- const nextCross = forwardCross - (horizontal$1 ? (placedPort.y ?? 0) + (placedPort.height ?? 0) / 2 : (placedPort.x ?? 0) + (placedPort.width ?? 0) / 2);
19853
+ if (!placedPort) return void 0;
19854
+ return forwardCross - (horizontal$1 ? (placedPort.y ?? 0) + (placedPort.height ?? 0) / 2 : (placedPort.x ?? 0) + (placedPort.width ?? 0) / 2);
19855
+ };
19856
+ const alignPort = (node, nextCross) => {
19857
+ const rect = placement.rectByNodeId.get(node.id);
19858
+ if (!rect || nextCross === void 0) return;
19843
19859
  const currentCross = horizontal$1 ? rect.y : rect.x;
19844
19860
  postCompactionNodeCrossDeltas.set(node.id, (postCompactionNodeCrossDeltas.get(node.id) ?? 0) + nextCross - currentCross);
19845
19861
  mutableRects.set(node.id, horizontal$1 ? {
@@ -19851,8 +19867,12 @@ function* createLayeredScopePipeline(graph, options, context, fallbacks = {
19851
19867
  });
19852
19868
  };
19853
19869
  if (isolatedAntiparallelPair) {
19854
- alignPort(nodesById.get(forward.sourceId), forward.sourcePort);
19855
- alignPort(nodesById.get(forward.targetId), forward.targetPort);
19870
+ const ends = [[nodesById.get(forward.sourceId), forward.sourcePort], [nodesById.get(forward.targetId), forward.targetPort]];
19871
+ const crosses$1 = ends.map(([node, port]) => alignedCross(node, port));
19872
+ if (!ends.some(([node], index) => {
19873
+ const rect = placement.rectByNodeId.get(node.id);
19874
+ return rect !== void 0 && crosses$1[index] !== void 0 && Math.abs(crosses$1[index] - (horizontal$1 ? rect.y : rect.x)) > 1e-9 && graph.edges.some((edge) => edge.sourceId === node.id && edge.targetId === node.id);
19875
+ })) ends.forEach(([node], index) => alignPort(node, crosses$1[index]));
19856
19876
  }
19857
19877
  }
19858
19878
  }
@@ -1,4 +1,4 @@
1
- import { K as placePorts, X as JavaRandom, i as layeredAlgorithm, n as elkjs0111LayeredAlgorithm, x as setElkjs0111ResultPolicy, z as getPolylineMidpoint } from "./layered-CUwft7on.mjs";
1
+ import { K as placePorts, X as JavaRandom, i as layeredAlgorithm, n as elkjs0111LayeredAlgorithm, x as setElkjs0111ResultPolicy, z as getPolylineMidpoint } from "./layered-BtnmL9_b.mjs";
2
2
  import { getNodeSize } from "@statelyai/graph/layout";
3
3
 
4
4
  //#region src/fixed.ts
@@ -1,4 +1,4 @@
1
- const require_layered = require('./layered-QzYtokgN.cjs');
1
+ const require_layered = require('./layered-B1AE7Zpr.cjs');
2
2
  let _statelyai_graph_layout = require("@statelyai/graph/layout");
3
3
 
4
4
  //#region src/fixed.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@statelyai/layout",
3
- "version": "0.5.1",
3
+ "version": "0.5.2",
4
4
  "description": "Extensible, graph-native layout algorithms for @statelyai/graph",
5
5
  "license": "EPL-2.0 OR GPL-3.0-or-later",
6
6
  "repository": {
@@ -2520,10 +2520,10 @@ export function* createLayeredScopePipeline<N, E, G, P>(
2520
2520
  : horizontal
2521
2521
  ? Math.round(forwardLabel.y + (forward.height ?? 0) / 2)
2522
2522
  : Math.round(forwardLabel.x + (forward.width ?? 0) / 2);
2523
- const alignPort = (node: GraphNode, portName: string): void => {
2523
+ const alignedCross = (node: GraphNode, portName: string): number | undefined => {
2524
2524
  const rect = placement.rectByNodeId.get(node.id);
2525
2525
  const port = node.ports?.find((candidate) => candidate.name === portName);
2526
- if (!rect || !port) return;
2526
+ if (!rect || !port) return undefined;
2527
2527
  const placedPort = placePorts(
2528
2528
  node.ports,
2529
2529
  rect,
@@ -2533,11 +2533,15 @@ export function* createLayeredScopePipeline<N, E, G, P>(
2533
2533
  (candidate) =>
2534
2534
  getOrientedPortDirection(expanded.input, expanded.orientation, node, candidate),
2535
2535
  )?.find((candidate) => candidate.name === portName);
2536
- if (!placedPort) return;
2536
+ if (!placedPort) return undefined;
2537
2537
  const localCross = horizontal
2538
2538
  ? (placedPort.y ?? 0) + (placedPort.height ?? 0) / 2
2539
2539
  : (placedPort.x ?? 0) + (placedPort.width ?? 0) / 2;
2540
- const nextCross = forwardCross - localCross;
2540
+ return forwardCross - localCross;
2541
+ };
2542
+ const alignPort = (node: GraphNode, nextCross: number | undefined): void => {
2543
+ const rect = placement.rectByNodeId.get(node.id);
2544
+ if (!rect || nextCross === undefined) return;
2541
2545
  const currentCross = horizontal ? rect.y : rect.x;
2542
2546
  postCompactionNodeCrossDeltas.set(
2543
2547
  node.id,
@@ -2549,8 +2553,23 @@ export function* createLayeredScopePipeline<N, E, G, P>(
2549
2553
  );
2550
2554
  };
2551
2555
  if (isolatedAntiparallelPair) {
2552
- alignPort(nodesById.get(forward.sourceId)!, forward.sourcePort);
2553
- alignPort(nodesById.get(forward.targetId)!, forward.targetPort);
2556
+ const ends = [
2557
+ [nodesById.get(forward.sourceId)!, forward.sourcePort],
2558
+ [nodesById.get(forward.targetId)!, forward.targetPort],
2559
+ ] as const;
2560
+ const crosses = ends.map(([node, port]) => alignedCross(node, port));
2561
+ // ELK keeps a loop owner where BK placed it, and moving it here would
2562
+ // leave its loop and label behind, so such a pair stays as placed.
2563
+ const movesLoopOwner = ends.some(([node], index) => {
2564
+ const rect = placement.rectByNodeId.get(node.id);
2565
+ return (
2566
+ rect !== undefined &&
2567
+ crosses[index] !== undefined &&
2568
+ Math.abs(crosses[index]! - (horizontal ? rect.y : rect.x)) > 1e-9 &&
2569
+ graph.edges.some((edge) => edge.sourceId === node.id && edge.targetId === node.id)
2570
+ );
2571
+ });
2572
+ if (!movesLoopOwner) ends.forEach(([node], index) => alignPort(node, crosses[index]));
2554
2573
  }
2555
2574
  }
2556
2575
  }
@@ -222,17 +222,28 @@ export function loopEnvelopes(input: LayeredPhaseInput): ReadonlyMap<string, Loo
222
222
  flowAfter: "EAST",
223
223
  flowBefore: "WEST",
224
224
  } as const;
225
+ const horizontal = input.direction === "right" || input.direction === "left";
226
+ const size = input.sizes.get(node.id) ?? { width: 0, height: 0 };
227
+ const flowSize = horizontal ? size.width : size.height;
225
228
  for (const side of sides) {
226
229
  const selected = loops.filter((_, index) => sides[index % sides.length] === side);
227
230
  const canonical = canonicalSides[side];
228
231
  envelope[side] = Math.max(
229
- 0,
232
+ envelope[side],
230
233
  ...selected.map(
231
234
  (edge) =>
232
235
  movableLoopTrack(input, selected, edge, canonical, ordering) +
233
236
  movableLoopLabelExtent(input, edge, canonical),
234
237
  ),
235
238
  );
239
+ if (!crossSide(canonical)) continue;
240
+ // ELK centers cross-side loop labels on their node along the flow, and
241
+ // a label longer than its node widens the node's layer by the overhang.
242
+ for (const edge of selected) {
243
+ const overhang = ((horizontal ? edge.width : edge.height) ?? 0) - flowSize;
244
+ envelope.flowBefore = Math.max(envelope.flowBefore, overhang / 2);
245
+ envelope.flowAfter = Math.max(envelope.flowAfter, overhang / 2);
246
+ }
236
247
  }
237
248
  envelopes.set(node.id, envelope);
238
249
  }