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.
@@ -309,7 +309,6 @@ function isDebugEnabled() {
309
309
  if (cliMode) return false;
310
310
  return typeof process !== "undefined" && process.env?.["DEBUG"] === "true";
311
311
  }
312
- var DEBUG = typeof process !== "undefined" && process.env?.["DEBUG"] === "true";
313
312
 
314
313
  // src/layout/edge-routing/edge-fixer.ts
315
314
  var EdgeFixer = class {
@@ -380,6 +379,10 @@ var EdgeFixer = class {
380
379
  const containerNodes = nodesByContainer.get(currentContainerId) ?? /* @__PURE__ */ new Map();
381
380
  for (const edge of node.edges) {
382
381
  if (edge.sections && edge.sections.length > 0) {
382
+ const hasPoolRelativeCoords = edge._poolRelativeCoords === true;
383
+ if (hasPoolRelativeCoords) {
384
+ continue;
385
+ }
383
386
  this.fixEdgeIfCrossing(edge, containerNodes, containerOffsetX, containerOffsetY);
384
387
  }
385
388
  }
@@ -2505,7 +2508,15 @@ var LaneArranger = class {
2505
2508
  const waypoints = [];
2506
2509
  waypoints.push({ x: startX, y: startY });
2507
2510
  if (Math.abs(startY - endY) > 10) {
2508
- const midX = (startX + endX) / 2;
2511
+ const midX = this.findClearMidX(
2512
+ startX,
2513
+ endX,
2514
+ startY,
2515
+ endY,
2516
+ sourceId,
2517
+ targetId,
2518
+ nodePositions
2519
+ );
2509
2520
  waypoints.push({ x: midX, y: startY });
2510
2521
  waypoints.push({ x: midX, y: endY });
2511
2522
  }
@@ -2523,6 +2534,109 @@ var LaneArranger = class {
2523
2534
  }
2524
2535
  }
2525
2536
  }
2537
+ /**
2538
+ * Find a clear X position for vertical edge segment that avoids obstacles.
2539
+ * Checks all three segments of the L-shaped path:
2540
+ * 1. Horizontal from (startX, startY) to (midX, startY)
2541
+ * 2. Vertical from (midX, startY) to (midX, endY)
2542
+ * 3. Horizontal from (midX, endY) to (endX, endY)
2543
+ */
2544
+ findClearMidX(startX, endX, startY, endY, sourceId, targetId, nodePositions) {
2545
+ const margin = 15;
2546
+ const minY = Math.min(startY, endY);
2547
+ const maxY = Math.max(startY, endY);
2548
+ const rangeMinX = Math.min(startX, endX);
2549
+ const rangeMaxX = Math.max(startX, endX);
2550
+ if (isDebugEnabled()) {
2551
+ console.log(`[BPMN] findClearMidX: startX=${startX}, endX=${endX}, startY=${startY}, endY=${endY}`);
2552
+ }
2553
+ const allObstacles = [];
2554
+ for (const [nodeId, pos] of nodePositions) {
2555
+ if (nodeId === sourceId || nodeId === targetId) continue;
2556
+ const nodeLeft = pos.x;
2557
+ const nodeRight = pos.x + pos.width;
2558
+ const nodeTop = pos.y;
2559
+ const nodeBottom = pos.y + pos.height;
2560
+ const xOverlap = nodeRight > rangeMinX && nodeLeft < rangeMaxX;
2561
+ const yOverlapVertical = nodeBottom > minY && nodeTop < maxY;
2562
+ const yContainsStartY = nodeTop <= startY && nodeBottom >= startY;
2563
+ const yContainsEndY = nodeTop <= endY && nodeBottom >= endY;
2564
+ if (xOverlap && (yOverlapVertical || yContainsStartY || yContainsEndY)) {
2565
+ allObstacles.push({
2566
+ x: nodeLeft,
2567
+ y: nodeTop,
2568
+ width: pos.width,
2569
+ height: pos.height,
2570
+ right: nodeRight,
2571
+ bottom: nodeBottom,
2572
+ id: nodeId
2573
+ });
2574
+ if (isDebugEnabled()) {
2575
+ console.log(`[BPMN] findClearMidX: obstacle ${nodeId}: x=[${nodeLeft}, ${nodeRight}], y=[${nodeTop}, ${nodeBottom}]`);
2576
+ }
2577
+ }
2578
+ }
2579
+ if (allObstacles.length === 0) {
2580
+ return (startX + endX) / 2;
2581
+ }
2582
+ const isValidMidX = (midX) => {
2583
+ for (const obs of allObstacles) {
2584
+ const seg1MinX = Math.min(startX, midX);
2585
+ const seg1MaxX = Math.max(startX, midX);
2586
+ if (obs.y <= startY && obs.bottom >= startY && // Y range contains startY
2587
+ obs.right > seg1MinX && obs.x < seg1MaxX) {
2588
+ return false;
2589
+ }
2590
+ if (obs.x <= midX && obs.right >= midX && // X range contains midX
2591
+ obs.bottom > minY && obs.y < maxY) {
2592
+ return false;
2593
+ }
2594
+ const seg2MinX = Math.min(midX, endX);
2595
+ const seg2MaxX = Math.max(midX, endX);
2596
+ if (obs.y <= endY && obs.bottom >= endY && // Y range contains endY
2597
+ obs.right > seg2MinX && obs.x < seg2MaxX) {
2598
+ return false;
2599
+ }
2600
+ }
2601
+ return true;
2602
+ };
2603
+ const candidates = [];
2604
+ candidates.push((startX + endX) / 2);
2605
+ candidates.push(startX + margin);
2606
+ candidates.push(endX - margin);
2607
+ for (const obs of allObstacles) {
2608
+ candidates.push(obs.x - margin);
2609
+ candidates.push(obs.right + margin);
2610
+ }
2611
+ const simpleMidX = (startX + endX) / 2;
2612
+ const validCandidates = candidates.filter((x) => x >= rangeMinX && x <= rangeMaxX).sort((a, b) => Math.abs(a - simpleMidX) - Math.abs(b - simpleMidX));
2613
+ for (const candidate of validCandidates) {
2614
+ if (isValidMidX(candidate)) {
2615
+ if (isDebugEnabled()) {
2616
+ console.log(`[BPMN] findClearMidX: found valid position ${candidate}`);
2617
+ }
2618
+ return candidate;
2619
+ }
2620
+ }
2621
+ const leftMost = Math.min(...allObstacles.map((o) => o.x)) - margin;
2622
+ const rightMost = Math.max(...allObstacles.map((o) => o.right)) + margin;
2623
+ if (leftMost >= rangeMinX && isValidMidX(leftMost)) {
2624
+ if (isDebugEnabled()) {
2625
+ console.log(`[BPMN] findClearMidX: routing left of all obstacles at ${leftMost}`);
2626
+ }
2627
+ return leftMost;
2628
+ }
2629
+ if (rightMost <= rangeMaxX && isValidMidX(rightMost)) {
2630
+ if (isDebugEnabled()) {
2631
+ console.log(`[BPMN] findClearMidX: routing right of all obstacles at ${rightMost}`);
2632
+ }
2633
+ return rightMost;
2634
+ }
2635
+ if (isDebugEnabled()) {
2636
+ console.log(`[BPMN] findClearMidX: no valid route found, using midpoint ${simpleMidX}`);
2637
+ }
2638
+ return simpleMidX;
2639
+ }
2526
2640
  };
2527
2641
 
2528
2642
  // src/layout/post-processing/pool-arranger.ts
@@ -2603,7 +2717,7 @@ var PoolArranger = class {
2603
2717
  pool.y = currentY;
2604
2718
  if (isBlackBox) {
2605
2719
  pool.width = maxPoolWidth;
2606
- pool.height = pool.height ?? 60;
2720
+ pool.height = 60;
2607
2721
  } else if (hasLanes) {
2608
2722
  pool.width = maxPoolWidth;
2609
2723
  } else {
@@ -5115,6 +5229,7 @@ var DiagramBuilder = class {
5115
5229
  }
5116
5230
  }
5117
5231
  }
5232
+ this.ensureOrthogonalWaypoints(waypoints);
5118
5233
  const edgeModel = {
5119
5234
  id: `${edge.id}_di`,
5120
5235
  bpmnElement: edge.id,
@@ -5181,6 +5296,36 @@ var DiagramBuilder = class {
5181
5296
  y: (lastPoint?.y ?? 0) - labelHeight - 4
5182
5297
  };
5183
5298
  }
5299
+ /**
5300
+ * Ensure all waypoint segments are orthogonal (horizontal or vertical)
5301
+ * If a diagonal segment is found, insert intermediate bend points to create
5302
+ * an L-shaped orthogonal path.
5303
+ *
5304
+ * Strategy: For diagonal segments, we use "horizontal first" - go horizontally
5305
+ * to the target X, then vertically to the target Y.
5306
+ */
5307
+ ensureOrthogonalWaypoints(waypoints) {
5308
+ if (waypoints.length < 2) return;
5309
+ const tolerance = 1;
5310
+ let i = 0;
5311
+ while (i < waypoints.length - 1) {
5312
+ const current = waypoints[i];
5313
+ const next = waypoints[i + 1];
5314
+ if (!current || !next) {
5315
+ i++;
5316
+ continue;
5317
+ }
5318
+ const dx = Math.abs(next.x - current.x);
5319
+ const dy = Math.abs(next.y - current.y);
5320
+ if (dx > tolerance && dy > tolerance) {
5321
+ const bendPoint = { x: next.x, y: current.y };
5322
+ waypoints.splice(i + 1, 0, bendPoint);
5323
+ i++;
5324
+ } else {
5325
+ i++;
5326
+ }
5327
+ }
5328
+ }
5184
5329
  };
5185
5330
 
5186
5331
  // src/transform/model-builder.ts