bpmn-elk-layout 1.0.2 → 1.1.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.
package/dist/index.js CHANGED
@@ -303,7 +303,6 @@ function isDebugEnabled() {
303
303
  if (cliMode) return false;
304
304
  return typeof process !== "undefined" && process.env?.["DEBUG"] === "true";
305
305
  }
306
- var DEBUG = typeof process !== "undefined" && process.env?.["DEBUG"] === "true";
307
306
 
308
307
  // src/layout/edge-routing/edge-fixer.ts
309
308
  var EdgeFixer = class {
@@ -374,6 +373,10 @@ var EdgeFixer = class {
374
373
  const containerNodes = nodesByContainer.get(currentContainerId) ?? /* @__PURE__ */ new Map();
375
374
  for (const edge of node.edges) {
376
375
  if (edge.sections && edge.sections.length > 0) {
376
+ const hasPoolRelativeCoords = edge._poolRelativeCoords === true;
377
+ if (hasPoolRelativeCoords) {
378
+ continue;
379
+ }
377
380
  this.fixEdgeIfCrossing(edge, containerNodes, containerOffsetX, containerOffsetY);
378
381
  }
379
382
  }
@@ -2499,7 +2502,15 @@ var LaneArranger = class {
2499
2502
  const waypoints = [];
2500
2503
  waypoints.push({ x: startX, y: startY });
2501
2504
  if (Math.abs(startY - endY) > 10) {
2502
- const midX = (startX + endX) / 2;
2505
+ const midX = this.findClearMidX(
2506
+ startX,
2507
+ endX,
2508
+ startY,
2509
+ endY,
2510
+ sourceId,
2511
+ targetId,
2512
+ nodePositions
2513
+ );
2503
2514
  waypoints.push({ x: midX, y: startY });
2504
2515
  waypoints.push({ x: midX, y: endY });
2505
2516
  }
@@ -2517,6 +2528,109 @@ var LaneArranger = class {
2517
2528
  }
2518
2529
  }
2519
2530
  }
2531
+ /**
2532
+ * Find a clear X position for vertical edge segment that avoids obstacles.
2533
+ * Checks all three segments of the L-shaped path:
2534
+ * 1. Horizontal from (startX, startY) to (midX, startY)
2535
+ * 2. Vertical from (midX, startY) to (midX, endY)
2536
+ * 3. Horizontal from (midX, endY) to (endX, endY)
2537
+ */
2538
+ findClearMidX(startX, endX, startY, endY, sourceId, targetId, nodePositions) {
2539
+ const margin = 15;
2540
+ const minY = Math.min(startY, endY);
2541
+ const maxY = Math.max(startY, endY);
2542
+ const rangeMinX = Math.min(startX, endX);
2543
+ const rangeMaxX = Math.max(startX, endX);
2544
+ if (isDebugEnabled()) {
2545
+ console.log(`[BPMN] findClearMidX: startX=${startX}, endX=${endX}, startY=${startY}, endY=${endY}`);
2546
+ }
2547
+ const allObstacles = [];
2548
+ for (const [nodeId, pos] of nodePositions) {
2549
+ if (nodeId === sourceId || nodeId === targetId) continue;
2550
+ const nodeLeft = pos.x;
2551
+ const nodeRight = pos.x + pos.width;
2552
+ const nodeTop = pos.y;
2553
+ const nodeBottom = pos.y + pos.height;
2554
+ const xOverlap = nodeRight > rangeMinX && nodeLeft < rangeMaxX;
2555
+ const yOverlapVertical = nodeBottom > minY && nodeTop < maxY;
2556
+ const yContainsStartY = nodeTop <= startY && nodeBottom >= startY;
2557
+ const yContainsEndY = nodeTop <= endY && nodeBottom >= endY;
2558
+ if (xOverlap && (yOverlapVertical || yContainsStartY || yContainsEndY)) {
2559
+ allObstacles.push({
2560
+ x: nodeLeft,
2561
+ y: nodeTop,
2562
+ width: pos.width,
2563
+ height: pos.height,
2564
+ right: nodeRight,
2565
+ bottom: nodeBottom,
2566
+ id: nodeId
2567
+ });
2568
+ if (isDebugEnabled()) {
2569
+ console.log(`[BPMN] findClearMidX: obstacle ${nodeId}: x=[${nodeLeft}, ${nodeRight}], y=[${nodeTop}, ${nodeBottom}]`);
2570
+ }
2571
+ }
2572
+ }
2573
+ if (allObstacles.length === 0) {
2574
+ return (startX + endX) / 2;
2575
+ }
2576
+ const isValidMidX = (midX) => {
2577
+ for (const obs of allObstacles) {
2578
+ const seg1MinX = Math.min(startX, midX);
2579
+ const seg1MaxX = Math.max(startX, midX);
2580
+ if (obs.y <= startY && obs.bottom >= startY && // Y range contains startY
2581
+ obs.right > seg1MinX && obs.x < seg1MaxX) {
2582
+ return false;
2583
+ }
2584
+ if (obs.x <= midX && obs.right >= midX && // X range contains midX
2585
+ obs.bottom > minY && obs.y < maxY) {
2586
+ return false;
2587
+ }
2588
+ const seg2MinX = Math.min(midX, endX);
2589
+ const seg2MaxX = Math.max(midX, endX);
2590
+ if (obs.y <= endY && obs.bottom >= endY && // Y range contains endY
2591
+ obs.right > seg2MinX && obs.x < seg2MaxX) {
2592
+ return false;
2593
+ }
2594
+ }
2595
+ return true;
2596
+ };
2597
+ const candidates = [];
2598
+ candidates.push((startX + endX) / 2);
2599
+ candidates.push(startX + margin);
2600
+ candidates.push(endX - margin);
2601
+ for (const obs of allObstacles) {
2602
+ candidates.push(obs.x - margin);
2603
+ candidates.push(obs.right + margin);
2604
+ }
2605
+ const simpleMidX = (startX + endX) / 2;
2606
+ const validCandidates = candidates.filter((x) => x >= rangeMinX && x <= rangeMaxX).sort((a, b) => Math.abs(a - simpleMidX) - Math.abs(b - simpleMidX));
2607
+ for (const candidate of validCandidates) {
2608
+ if (isValidMidX(candidate)) {
2609
+ if (isDebugEnabled()) {
2610
+ console.log(`[BPMN] findClearMidX: found valid position ${candidate}`);
2611
+ }
2612
+ return candidate;
2613
+ }
2614
+ }
2615
+ const leftMost = Math.min(...allObstacles.map((o) => o.x)) - margin;
2616
+ const rightMost = Math.max(...allObstacles.map((o) => o.right)) + margin;
2617
+ if (leftMost >= rangeMinX && isValidMidX(leftMost)) {
2618
+ if (isDebugEnabled()) {
2619
+ console.log(`[BPMN] findClearMidX: routing left of all obstacles at ${leftMost}`);
2620
+ }
2621
+ return leftMost;
2622
+ }
2623
+ if (rightMost <= rangeMaxX && isValidMidX(rightMost)) {
2624
+ if (isDebugEnabled()) {
2625
+ console.log(`[BPMN] findClearMidX: routing right of all obstacles at ${rightMost}`);
2626
+ }
2627
+ return rightMost;
2628
+ }
2629
+ if (isDebugEnabled()) {
2630
+ console.log(`[BPMN] findClearMidX: no valid route found, using midpoint ${simpleMidX}`);
2631
+ }
2632
+ return simpleMidX;
2633
+ }
2520
2634
  };
2521
2635
 
2522
2636
  // src/layout/post-processing/pool-arranger.ts
@@ -2597,7 +2711,7 @@ var PoolArranger = class {
2597
2711
  pool.y = currentY;
2598
2712
  if (isBlackBox) {
2599
2713
  pool.width = maxPoolWidth;
2600
- pool.height = pool.height ?? 60;
2714
+ pool.height = 60;
2601
2715
  } else if (hasLanes) {
2602
2716
  pool.width = maxPoolWidth;
2603
2717
  } else {
@@ -5109,6 +5223,7 @@ var DiagramBuilder = class {
5109
5223
  }
5110
5224
  }
5111
5225
  }
5226
+ this.ensureOrthogonalWaypoints(waypoints);
5112
5227
  const edgeModel = {
5113
5228
  id: `${edge.id}_di`,
5114
5229
  bpmnElement: edge.id,
@@ -5175,6 +5290,36 @@ var DiagramBuilder = class {
5175
5290
  y: (lastPoint?.y ?? 0) - labelHeight - 4
5176
5291
  };
5177
5292
  }
5293
+ /**
5294
+ * Ensure all waypoint segments are orthogonal (horizontal or vertical)
5295
+ * If a diagonal segment is found, insert intermediate bend points to create
5296
+ * an L-shaped orthogonal path.
5297
+ *
5298
+ * Strategy: For diagonal segments, we use "horizontal first" - go horizontally
5299
+ * to the target X, then vertically to the target Y.
5300
+ */
5301
+ ensureOrthogonalWaypoints(waypoints) {
5302
+ if (waypoints.length < 2) return;
5303
+ const tolerance = 1;
5304
+ let i = 0;
5305
+ while (i < waypoints.length - 1) {
5306
+ const current = waypoints[i];
5307
+ const next = waypoints[i + 1];
5308
+ if (!current || !next) {
5309
+ i++;
5310
+ continue;
5311
+ }
5312
+ const dx = Math.abs(next.x - current.x);
5313
+ const dy = Math.abs(next.y - current.y);
5314
+ if (dx > tolerance && dy > tolerance) {
5315
+ const bendPoint = { x: next.x, y: current.y };
5316
+ waypoints.splice(i + 1, 0, bendPoint);
5317
+ i++;
5318
+ } else {
5319
+ i++;
5320
+ }
5321
+ }
5322
+ }
5178
5323
  };
5179
5324
 
5180
5325
  // src/transform/model-builder.ts