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.
@@ -254,7 +254,6 @@ function isDebugEnabled() {
254
254
  if (cliMode) return false;
255
255
  return typeof process !== "undefined" && process.env?.["DEBUG"] === "true";
256
256
  }
257
- var DEBUG = typeof process !== "undefined" && process.env?.["DEBUG"] === "true";
258
257
 
259
258
  // src/layout/edge-routing/edge-fixer.ts
260
259
  var EdgeFixer = class {
@@ -325,6 +324,10 @@ var EdgeFixer = class {
325
324
  const containerNodes = nodesByContainer.get(currentContainerId) ?? /* @__PURE__ */ new Map();
326
325
  for (const edge of node.edges) {
327
326
  if (edge.sections && edge.sections.length > 0) {
327
+ const hasPoolRelativeCoords = edge._poolRelativeCoords === true;
328
+ if (hasPoolRelativeCoords) {
329
+ continue;
330
+ }
328
331
  this.fixEdgeIfCrossing(edge, containerNodes, containerOffsetX, containerOffsetY);
329
332
  }
330
333
  }
@@ -2450,7 +2453,15 @@ var LaneArranger = class {
2450
2453
  const waypoints = [];
2451
2454
  waypoints.push({ x: startX, y: startY });
2452
2455
  if (Math.abs(startY - endY) > 10) {
2453
- const midX = (startX + endX) / 2;
2456
+ const midX = this.findClearMidX(
2457
+ startX,
2458
+ endX,
2459
+ startY,
2460
+ endY,
2461
+ sourceId,
2462
+ targetId,
2463
+ nodePositions
2464
+ );
2454
2465
  waypoints.push({ x: midX, y: startY });
2455
2466
  waypoints.push({ x: midX, y: endY });
2456
2467
  }
@@ -2468,6 +2479,109 @@ var LaneArranger = class {
2468
2479
  }
2469
2480
  }
2470
2481
  }
2482
+ /**
2483
+ * Find a clear X position for vertical edge segment that avoids obstacles.
2484
+ * Checks all three segments of the L-shaped path:
2485
+ * 1. Horizontal from (startX, startY) to (midX, startY)
2486
+ * 2. Vertical from (midX, startY) to (midX, endY)
2487
+ * 3. Horizontal from (midX, endY) to (endX, endY)
2488
+ */
2489
+ findClearMidX(startX, endX, startY, endY, sourceId, targetId, nodePositions) {
2490
+ const margin = 15;
2491
+ const minY = Math.min(startY, endY);
2492
+ const maxY = Math.max(startY, endY);
2493
+ const rangeMinX = Math.min(startX, endX);
2494
+ const rangeMaxX = Math.max(startX, endX);
2495
+ if (isDebugEnabled()) {
2496
+ console.log(`[BPMN] findClearMidX: startX=${startX}, endX=${endX}, startY=${startY}, endY=${endY}`);
2497
+ }
2498
+ const allObstacles = [];
2499
+ for (const [nodeId, pos] of nodePositions) {
2500
+ if (nodeId === sourceId || nodeId === targetId) continue;
2501
+ const nodeLeft = pos.x;
2502
+ const nodeRight = pos.x + pos.width;
2503
+ const nodeTop = pos.y;
2504
+ const nodeBottom = pos.y + pos.height;
2505
+ const xOverlap = nodeRight > rangeMinX && nodeLeft < rangeMaxX;
2506
+ const yOverlapVertical = nodeBottom > minY && nodeTop < maxY;
2507
+ const yContainsStartY = nodeTop <= startY && nodeBottom >= startY;
2508
+ const yContainsEndY = nodeTop <= endY && nodeBottom >= endY;
2509
+ if (xOverlap && (yOverlapVertical || yContainsStartY || yContainsEndY)) {
2510
+ allObstacles.push({
2511
+ x: nodeLeft,
2512
+ y: nodeTop,
2513
+ width: pos.width,
2514
+ height: pos.height,
2515
+ right: nodeRight,
2516
+ bottom: nodeBottom,
2517
+ id: nodeId
2518
+ });
2519
+ if (isDebugEnabled()) {
2520
+ console.log(`[BPMN] findClearMidX: obstacle ${nodeId}: x=[${nodeLeft}, ${nodeRight}], y=[${nodeTop}, ${nodeBottom}]`);
2521
+ }
2522
+ }
2523
+ }
2524
+ if (allObstacles.length === 0) {
2525
+ return (startX + endX) / 2;
2526
+ }
2527
+ const isValidMidX = (midX) => {
2528
+ for (const obs of allObstacles) {
2529
+ const seg1MinX = Math.min(startX, midX);
2530
+ const seg1MaxX = Math.max(startX, midX);
2531
+ if (obs.y <= startY && obs.bottom >= startY && // Y range contains startY
2532
+ obs.right > seg1MinX && obs.x < seg1MaxX) {
2533
+ return false;
2534
+ }
2535
+ if (obs.x <= midX && obs.right >= midX && // X range contains midX
2536
+ obs.bottom > minY && obs.y < maxY) {
2537
+ return false;
2538
+ }
2539
+ const seg2MinX = Math.min(midX, endX);
2540
+ const seg2MaxX = Math.max(midX, endX);
2541
+ if (obs.y <= endY && obs.bottom >= endY && // Y range contains endY
2542
+ obs.right > seg2MinX && obs.x < seg2MaxX) {
2543
+ return false;
2544
+ }
2545
+ }
2546
+ return true;
2547
+ };
2548
+ const candidates = [];
2549
+ candidates.push((startX + endX) / 2);
2550
+ candidates.push(startX + margin);
2551
+ candidates.push(endX - margin);
2552
+ for (const obs of allObstacles) {
2553
+ candidates.push(obs.x - margin);
2554
+ candidates.push(obs.right + margin);
2555
+ }
2556
+ const simpleMidX = (startX + endX) / 2;
2557
+ const validCandidates = candidates.filter((x) => x >= rangeMinX && x <= rangeMaxX).sort((a, b) => Math.abs(a - simpleMidX) - Math.abs(b - simpleMidX));
2558
+ for (const candidate of validCandidates) {
2559
+ if (isValidMidX(candidate)) {
2560
+ if (isDebugEnabled()) {
2561
+ console.log(`[BPMN] findClearMidX: found valid position ${candidate}`);
2562
+ }
2563
+ return candidate;
2564
+ }
2565
+ }
2566
+ const leftMost = Math.min(...allObstacles.map((o) => o.x)) - margin;
2567
+ const rightMost = Math.max(...allObstacles.map((o) => o.right)) + margin;
2568
+ if (leftMost >= rangeMinX && isValidMidX(leftMost)) {
2569
+ if (isDebugEnabled()) {
2570
+ console.log(`[BPMN] findClearMidX: routing left of all obstacles at ${leftMost}`);
2571
+ }
2572
+ return leftMost;
2573
+ }
2574
+ if (rightMost <= rangeMaxX && isValidMidX(rightMost)) {
2575
+ if (isDebugEnabled()) {
2576
+ console.log(`[BPMN] findClearMidX: routing right of all obstacles at ${rightMost}`);
2577
+ }
2578
+ return rightMost;
2579
+ }
2580
+ if (isDebugEnabled()) {
2581
+ console.log(`[BPMN] findClearMidX: no valid route found, using midpoint ${simpleMidX}`);
2582
+ }
2583
+ return simpleMidX;
2584
+ }
2471
2585
  };
2472
2586
 
2473
2587
  // src/layout/post-processing/pool-arranger.ts
@@ -2548,7 +2662,7 @@ var PoolArranger = class {
2548
2662
  pool.y = currentY;
2549
2663
  if (isBlackBox) {
2550
2664
  pool.width = maxPoolWidth;
2551
- pool.height = pool.height ?? 60;
2665
+ pool.height = 60;
2552
2666
  } else if (hasLanes) {
2553
2667
  pool.width = maxPoolWidth;
2554
2668
  } else {
@@ -5060,6 +5174,7 @@ var DiagramBuilder = class {
5060
5174
  }
5061
5175
  }
5062
5176
  }
5177
+ this.ensureOrthogonalWaypoints(waypoints);
5063
5178
  const edgeModel = {
5064
5179
  id: `${edge.id}_di`,
5065
5180
  bpmnElement: edge.id,
@@ -5126,6 +5241,36 @@ var DiagramBuilder = class {
5126
5241
  y: (lastPoint?.y ?? 0) - labelHeight - 4
5127
5242
  };
5128
5243
  }
5244
+ /**
5245
+ * Ensure all waypoint segments are orthogonal (horizontal or vertical)
5246
+ * If a diagonal segment is found, insert intermediate bend points to create
5247
+ * an L-shaped orthogonal path.
5248
+ *
5249
+ * Strategy: For diagonal segments, we use "horizontal first" - go horizontally
5250
+ * to the target X, then vertically to the target Y.
5251
+ */
5252
+ ensureOrthogonalWaypoints(waypoints) {
5253
+ if (waypoints.length < 2) return;
5254
+ const tolerance = 1;
5255
+ let i = 0;
5256
+ while (i < waypoints.length - 1) {
5257
+ const current = waypoints[i];
5258
+ const next = waypoints[i + 1];
5259
+ if (!current || !next) {
5260
+ i++;
5261
+ continue;
5262
+ }
5263
+ const dx = Math.abs(next.x - current.x);
5264
+ const dy = Math.abs(next.y - current.y);
5265
+ if (dx > tolerance && dy > tolerance) {
5266
+ const bendPoint = { x: next.x, y: current.y };
5267
+ waypoints.splice(i + 1, 0, bendPoint);
5268
+ i++;
5269
+ } else {
5270
+ i++;
5271
+ }
5272
+ }
5273
+ }
5129
5274
  };
5130
5275
 
5131
5276
  // src/transform/model-builder.ts