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.
@@ -44,13 +44,12 @@ function isDebugEnabled() {
44
44
  if (cliMode) return false;
45
45
  return typeof process !== "undefined" && process.env?.["DEBUG"] === "true";
46
46
  }
47
- var cliMode, DEBUG;
47
+ var cliMode;
48
48
  var init_debug = __esm({
49
49
  "src/utils/debug.ts"() {
50
50
  "use strict";
51
51
  init_cjs_shims();
52
52
  cliMode = false;
53
- DEBUG = typeof process !== "undefined" && process.env?.["DEBUG"] === "true";
54
53
  }
55
54
  });
56
55
 
@@ -390,6 +389,10 @@ var init_edge_fixer = __esm({
390
389
  const containerNodes = nodesByContainer.get(currentContainerId) ?? /* @__PURE__ */ new Map();
391
390
  for (const edge of node.edges) {
392
391
  if (edge.sections && edge.sections.length > 0) {
392
+ const hasPoolRelativeCoords = edge._poolRelativeCoords === true;
393
+ if (hasPoolRelativeCoords) {
394
+ continue;
395
+ }
393
396
  this.fixEdgeIfCrossing(edge, containerNodes, containerOffsetX, containerOffsetY);
394
397
  }
395
398
  }
@@ -2564,7 +2567,15 @@ var init_lane_arranger = __esm({
2564
2567
  const waypoints = [];
2565
2568
  waypoints.push({ x: startX, y: startY });
2566
2569
  if (Math.abs(startY - endY) > 10) {
2567
- const midX = (startX + endX) / 2;
2570
+ const midX = this.findClearMidX(
2571
+ startX,
2572
+ endX,
2573
+ startY,
2574
+ endY,
2575
+ sourceId,
2576
+ targetId,
2577
+ nodePositions
2578
+ );
2568
2579
  waypoints.push({ x: midX, y: startY });
2569
2580
  waypoints.push({ x: midX, y: endY });
2570
2581
  }
@@ -2582,6 +2593,109 @@ var init_lane_arranger = __esm({
2582
2593
  }
2583
2594
  }
2584
2595
  }
2596
+ /**
2597
+ * Find a clear X position for vertical edge segment that avoids obstacles.
2598
+ * Checks all three segments of the L-shaped path:
2599
+ * 1. Horizontal from (startX, startY) to (midX, startY)
2600
+ * 2. Vertical from (midX, startY) to (midX, endY)
2601
+ * 3. Horizontal from (midX, endY) to (endX, endY)
2602
+ */
2603
+ findClearMidX(startX, endX, startY, endY, sourceId, targetId, nodePositions) {
2604
+ const margin = 15;
2605
+ const minY = Math.min(startY, endY);
2606
+ const maxY = Math.max(startY, endY);
2607
+ const rangeMinX = Math.min(startX, endX);
2608
+ const rangeMaxX = Math.max(startX, endX);
2609
+ if (isDebugEnabled()) {
2610
+ console.log(`[BPMN] findClearMidX: startX=${startX}, endX=${endX}, startY=${startY}, endY=${endY}`);
2611
+ }
2612
+ const allObstacles = [];
2613
+ for (const [nodeId, pos] of nodePositions) {
2614
+ if (nodeId === sourceId || nodeId === targetId) continue;
2615
+ const nodeLeft = pos.x;
2616
+ const nodeRight = pos.x + pos.width;
2617
+ const nodeTop = pos.y;
2618
+ const nodeBottom = pos.y + pos.height;
2619
+ const xOverlap = nodeRight > rangeMinX && nodeLeft < rangeMaxX;
2620
+ const yOverlapVertical = nodeBottom > minY && nodeTop < maxY;
2621
+ const yContainsStartY = nodeTop <= startY && nodeBottom >= startY;
2622
+ const yContainsEndY = nodeTop <= endY && nodeBottom >= endY;
2623
+ if (xOverlap && (yOverlapVertical || yContainsStartY || yContainsEndY)) {
2624
+ allObstacles.push({
2625
+ x: nodeLeft,
2626
+ y: nodeTop,
2627
+ width: pos.width,
2628
+ height: pos.height,
2629
+ right: nodeRight,
2630
+ bottom: nodeBottom,
2631
+ id: nodeId
2632
+ });
2633
+ if (isDebugEnabled()) {
2634
+ console.log(`[BPMN] findClearMidX: obstacle ${nodeId}: x=[${nodeLeft}, ${nodeRight}], y=[${nodeTop}, ${nodeBottom}]`);
2635
+ }
2636
+ }
2637
+ }
2638
+ if (allObstacles.length === 0) {
2639
+ return (startX + endX) / 2;
2640
+ }
2641
+ const isValidMidX = (midX) => {
2642
+ for (const obs of allObstacles) {
2643
+ const seg1MinX = Math.min(startX, midX);
2644
+ const seg1MaxX = Math.max(startX, midX);
2645
+ if (obs.y <= startY && obs.bottom >= startY && // Y range contains startY
2646
+ obs.right > seg1MinX && obs.x < seg1MaxX) {
2647
+ return false;
2648
+ }
2649
+ if (obs.x <= midX && obs.right >= midX && // X range contains midX
2650
+ obs.bottom > minY && obs.y < maxY) {
2651
+ return false;
2652
+ }
2653
+ const seg2MinX = Math.min(midX, endX);
2654
+ const seg2MaxX = Math.max(midX, endX);
2655
+ if (obs.y <= endY && obs.bottom >= endY && // Y range contains endY
2656
+ obs.right > seg2MinX && obs.x < seg2MaxX) {
2657
+ return false;
2658
+ }
2659
+ }
2660
+ return true;
2661
+ };
2662
+ const candidates = [];
2663
+ candidates.push((startX + endX) / 2);
2664
+ candidates.push(startX + margin);
2665
+ candidates.push(endX - margin);
2666
+ for (const obs of allObstacles) {
2667
+ candidates.push(obs.x - margin);
2668
+ candidates.push(obs.right + margin);
2669
+ }
2670
+ const simpleMidX = (startX + endX) / 2;
2671
+ const validCandidates = candidates.filter((x) => x >= rangeMinX && x <= rangeMaxX).sort((a, b) => Math.abs(a - simpleMidX) - Math.abs(b - simpleMidX));
2672
+ for (const candidate of validCandidates) {
2673
+ if (isValidMidX(candidate)) {
2674
+ if (isDebugEnabled()) {
2675
+ console.log(`[BPMN] findClearMidX: found valid position ${candidate}`);
2676
+ }
2677
+ return candidate;
2678
+ }
2679
+ }
2680
+ const leftMost = Math.min(...allObstacles.map((o) => o.x)) - margin;
2681
+ const rightMost = Math.max(...allObstacles.map((o) => o.right)) + margin;
2682
+ if (leftMost >= rangeMinX && isValidMidX(leftMost)) {
2683
+ if (isDebugEnabled()) {
2684
+ console.log(`[BPMN] findClearMidX: routing left of all obstacles at ${leftMost}`);
2685
+ }
2686
+ return leftMost;
2687
+ }
2688
+ if (rightMost <= rangeMaxX && isValidMidX(rightMost)) {
2689
+ if (isDebugEnabled()) {
2690
+ console.log(`[BPMN] findClearMidX: routing right of all obstacles at ${rightMost}`);
2691
+ }
2692
+ return rightMost;
2693
+ }
2694
+ if (isDebugEnabled()) {
2695
+ console.log(`[BPMN] findClearMidX: no valid route found, using midpoint ${simpleMidX}`);
2696
+ }
2697
+ return simpleMidX;
2698
+ }
2585
2699
  };
2586
2700
  }
2587
2701
  });
@@ -2670,7 +2784,7 @@ var init_pool_arranger = __esm({
2670
2784
  pool.y = currentY;
2671
2785
  if (isBlackBox) {
2672
2786
  pool.width = maxPoolWidth;
2673
- pool.height = pool.height ?? 60;
2787
+ pool.height = 60;
2674
2788
  } else if (hasLanes) {
2675
2789
  pool.width = maxPoolWidth;
2676
2790
  } else {
@@ -5305,6 +5419,7 @@ var init_diagram_builder = __esm({
5305
5419
  }
5306
5420
  }
5307
5421
  }
5422
+ this.ensureOrthogonalWaypoints(waypoints);
5308
5423
  const edgeModel = {
5309
5424
  id: `${edge.id}_di`,
5310
5425
  bpmnElement: edge.id,
@@ -5371,6 +5486,36 @@ var init_diagram_builder = __esm({
5371
5486
  y: (lastPoint?.y ?? 0) - labelHeight - 4
5372
5487
  };
5373
5488
  }
5489
+ /**
5490
+ * Ensure all waypoint segments are orthogonal (horizontal or vertical)
5491
+ * If a diagonal segment is found, insert intermediate bend points to create
5492
+ * an L-shaped orthogonal path.
5493
+ *
5494
+ * Strategy: For diagonal segments, we use "horizontal first" - go horizontally
5495
+ * to the target X, then vertically to the target Y.
5496
+ */
5497
+ ensureOrthogonalWaypoints(waypoints) {
5498
+ if (waypoints.length < 2) return;
5499
+ const tolerance = 1;
5500
+ let i = 0;
5501
+ while (i < waypoints.length - 1) {
5502
+ const current = waypoints[i];
5503
+ const next = waypoints[i + 1];
5504
+ if (!current || !next) {
5505
+ i++;
5506
+ continue;
5507
+ }
5508
+ const dx = Math.abs(next.x - current.x);
5509
+ const dy = Math.abs(next.y - current.y);
5510
+ if (dx > tolerance && dy > tolerance) {
5511
+ const bendPoint = { x: next.x, y: current.y };
5512
+ waypoints.splice(i + 1, 0, bendPoint);
5513
+ i++;
5514
+ } else {
5515
+ i++;
5516
+ }
5517
+ }
5518
+ }
5374
5519
  };
5375
5520
  }
5376
5521
  });