@crazyhappyone/auto-graph 0.2.7 → 0.2.9
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/cli/index.cjs +316 -63
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +316 -63
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +316 -63
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +316 -63
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -179,6 +179,28 @@ function applyLayoutConstraints(input) {
|
|
|
179
179
|
if (input.distributeContainedChildren) {
|
|
180
180
|
yieldFixedPositionLocks(input, boxes, locks);
|
|
181
181
|
}
|
|
182
|
+
if (input.swimlanes !== void 0 && input.swimlanes.length > 0 && input.distributeSwimlaneChildren) {
|
|
183
|
+
for (const swimlane of input.swimlanes) {
|
|
184
|
+
if (swimlane.layout === "contract") continue;
|
|
185
|
+
for (const lane of swimlane.lanes) {
|
|
186
|
+
const fixedChildren = [];
|
|
187
|
+
let participantCount = 0;
|
|
188
|
+
for (const childId of lane.children) {
|
|
189
|
+
const lock = locks.get(childId);
|
|
190
|
+
if (lock === void 0) {
|
|
191
|
+
participantCount += 1;
|
|
192
|
+
} else if (lock.source === "fixed-position") {
|
|
193
|
+
participantCount += 1;
|
|
194
|
+
fixedChildren.push(childId);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
if (participantCount < 2) continue;
|
|
198
|
+
for (const childId of fixedChildren) {
|
|
199
|
+
locks.delete(childId);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
182
204
|
applyContainment(input.constraints, boxes, locks, diagnostics, false);
|
|
183
205
|
applyRelative(input.constraints, boxes, locks, diagnostics);
|
|
184
206
|
applyAlign(input.constraints, boxes, locks, diagnostics);
|
|
@@ -198,6 +220,9 @@ function applyLayoutConstraints(input) {
|
|
|
198
220
|
applyDistributeContained(input, boxes, locks, diagnostics);
|
|
199
221
|
dedupReplayDiagnostics(diagnostics, diagBefore);
|
|
200
222
|
}
|
|
223
|
+
if (input.swimlanes !== void 0 && input.swimlanes.length > 0 && input.distributeSwimlaneChildren) {
|
|
224
|
+
distributeSwimlaneChildren(input, boxes, locks, diagnostics);
|
|
225
|
+
}
|
|
201
226
|
removeResolvedConstraintDiagnostics(input.constraints, boxes, diagnostics);
|
|
202
227
|
reportOverlaps(
|
|
203
228
|
boxes,
|
|
@@ -1037,9 +1062,6 @@ function applyDistributeContained(input, boxes, locks, diagnostics) {
|
|
|
1037
1062
|
}
|
|
1038
1063
|
});
|
|
1039
1064
|
}
|
|
1040
|
-
if (input.swimlanes !== void 0 && input.swimlanes.length > 0 && input.distributeSwimlaneChildren) {
|
|
1041
|
-
distributeSwimlaneChildren(input, boxes, locks, diagnostics);
|
|
1042
|
-
}
|
|
1043
1065
|
}
|
|
1044
1066
|
function distributeSwimlaneChildren(input, boxes, locks, diagnostics) {
|
|
1045
1067
|
const spread = input.distributeSwimlaneChildren === "spread";
|
|
@@ -1079,6 +1101,7 @@ function distributeSwimlaneChildren(input, boxes, locks, diagnostics) {
|
|
|
1079
1101
|
effectiveGap = minGap + remaining / (unlocked.length - 1);
|
|
1080
1102
|
}
|
|
1081
1103
|
unlocked.sort((a, b) => a.box[axis] - b.box[axis]);
|
|
1104
|
+
reserved.sort((a, b) => a.start - b.start);
|
|
1082
1105
|
let pos = contentStart;
|
|
1083
1106
|
for (const child of unlocked) {
|
|
1084
1107
|
pos = advancePastReserved(pos, child.box[mainSize], reserved, minGap);
|
|
@@ -4729,15 +4752,12 @@ var BinaryHeap = class {
|
|
|
4729
4752
|
let smallestIdx = idx;
|
|
4730
4753
|
const leftIdx = (idx << 1) + 1;
|
|
4731
4754
|
const rightIdx = leftIdx + 1;
|
|
4732
|
-
if (leftIdx < size && this._less(
|
|
4733
|
-
this._data[leftIdx],
|
|
4734
|
-
this._data[smallestIdx]
|
|
4735
|
-
)) {
|
|
4755
|
+
if (leftIdx < size && this._less(this._data[leftIdx], entry)) {
|
|
4736
4756
|
smallestIdx = leftIdx;
|
|
4737
4757
|
}
|
|
4738
4758
|
if (rightIdx < size && this._less(
|
|
4739
4759
|
this._data[rightIdx],
|
|
4740
|
-
this._data[
|
|
4760
|
+
smallestIdx === leftIdx ? this._data[leftIdx] : entry
|
|
4741
4761
|
)) {
|
|
4742
4762
|
smallestIdx = rightIdx;
|
|
4743
4763
|
}
|
|
@@ -4804,8 +4824,59 @@ function findObstacleFreePath(source, target, obstacles, options = {}, diagnosti
|
|
|
4804
4824
|
turnPenalty,
|
|
4805
4825
|
segmentPenalty
|
|
4806
4826
|
);
|
|
4807
|
-
if (path
|
|
4808
|
-
|
|
4827
|
+
if (path !== null) {
|
|
4828
|
+
const simplified = simplifyRoute(path);
|
|
4829
|
+
const filteredSet = new Set(filtered);
|
|
4830
|
+
if (useCorridor && obstacles.some((o) => !filteredSet.has(o))) {
|
|
4831
|
+
let crossesExcluded = false;
|
|
4832
|
+
for (let i = 0; i < simplified.length - 1; i++) {
|
|
4833
|
+
const a = simplified[i];
|
|
4834
|
+
const b = simplified[i + 1];
|
|
4835
|
+
for (const obs of obstacles) {
|
|
4836
|
+
if (filteredSet.has(obs)) continue;
|
|
4837
|
+
if (segmentCrossesBoxStrict(a, b, obs, margin)) {
|
|
4838
|
+
crossesExcluded = true;
|
|
4839
|
+
break;
|
|
4840
|
+
}
|
|
4841
|
+
}
|
|
4842
|
+
if (crossesExcluded) break;
|
|
4843
|
+
}
|
|
4844
|
+
if (!crossesExcluded) return simplified;
|
|
4845
|
+
} else {
|
|
4846
|
+
return simplified;
|
|
4847
|
+
}
|
|
4848
|
+
}
|
|
4849
|
+
if (!useCorridor) return null;
|
|
4850
|
+
const xsFull = collectXs(source, target, obstacles, margin);
|
|
4851
|
+
const ysFull = collectYs(source, target, obstacles, margin);
|
|
4852
|
+
if (xsFull.length * ysFull.length > maxNodes) {
|
|
4853
|
+
diagnostics?.push({
|
|
4854
|
+
severity: "warning",
|
|
4855
|
+
code: "routing.astar.grid_overflow",
|
|
4856
|
+
message: `A* full-retry grid overflow: ${xsFull.length * ysFull.length} nodes > ${maxNodes} limit. Falling back to heuristic routing.`,
|
|
4857
|
+
detail: { xsCount: xsFull.length, ysCount: ysFull.length, maxNodes }
|
|
4858
|
+
});
|
|
4859
|
+
return null;
|
|
4860
|
+
}
|
|
4861
|
+
const { nodes: nodesFull, nodeIndex: idxFull } = buildGraph(xsFull, ysFull);
|
|
4862
|
+
connectHorizontalEdges(
|
|
4863
|
+
nodesFull,
|
|
4864
|
+
ysFull,
|
|
4865
|
+
obstacles,
|
|
4866
|
+
endpointObstacles,
|
|
4867
|
+
margin
|
|
4868
|
+
);
|
|
4869
|
+
connectVerticalEdges(nodesFull, xsFull, obstacles, endpointObstacles, margin);
|
|
4870
|
+
const pathFull = aStarSearch(
|
|
4871
|
+
nodesFull,
|
|
4872
|
+
idxFull,
|
|
4873
|
+
source,
|
|
4874
|
+
target,
|
|
4875
|
+
turnPenalty,
|
|
4876
|
+
segmentPenalty
|
|
4877
|
+
);
|
|
4878
|
+
if (pathFull === null) return null;
|
|
4879
|
+
return simplifyRoute(pathFull);
|
|
4809
4880
|
}
|
|
4810
4881
|
function filterObstaclesByCorridor(source, target, obstacles, endpointObstacles, margin) {
|
|
4811
4882
|
const cx1 = Math.min(source.x, target.x) - margin;
|
|
@@ -5032,7 +5103,7 @@ function findCornerGraphPath(source, target, obstacles, options = {}, diagnostic
|
|
|
5032
5103
|
const turnPenalty = options.turnPenalty ?? 50;
|
|
5033
5104
|
const segmentPenalty = options.segmentPenalty ?? 1;
|
|
5034
5105
|
const endpointObstacles = options.endpointObstacles ?? [];
|
|
5035
|
-
const maxCorners = options.maxCorners ??
|
|
5106
|
+
const maxCorners = options.maxCorners ?? 600;
|
|
5036
5107
|
const vertices = collectCornerVertices(source, target, obstacles, margin);
|
|
5037
5108
|
if (vertices.length > maxCorners) {
|
|
5038
5109
|
diagnostics?.push({
|
|
@@ -5401,17 +5472,36 @@ function routeEdge(input) {
|
|
|
5401
5472
|
input.source.center,
|
|
5402
5473
|
targetAnchor
|
|
5403
5474
|
);
|
|
5404
|
-
const
|
|
5475
|
+
const allObstacles = [...softObstacles, ...hardObstacles];
|
|
5476
|
+
const corridorObstacles = filterObstaclesByCorridor(
|
|
5405
5477
|
source,
|
|
5406
5478
|
target,
|
|
5407
|
-
|
|
5479
|
+
allObstacles,
|
|
5480
|
+
[],
|
|
5481
|
+
// endpointObstacles passed separately via options
|
|
5482
|
+
32
|
|
5483
|
+
);
|
|
5484
|
+
const cornerObstacles = corridorObstacles.length === 0 && allObstacles.length > 0 ? allObstacles : corridorObstacles;
|
|
5485
|
+
let cornerPath = findCornerGraphPath(
|
|
5486
|
+
source,
|
|
5487
|
+
target,
|
|
5488
|
+
cornerObstacles,
|
|
5408
5489
|
{ endpointObstacles, margin: 2 },
|
|
5409
5490
|
diagnostics
|
|
5410
5491
|
);
|
|
5492
|
+
if (cornerPath === null && cornerObstacles.length < allObstacles.length) {
|
|
5493
|
+
cornerPath = findCornerGraphPath(
|
|
5494
|
+
source,
|
|
5495
|
+
target,
|
|
5496
|
+
allObstacles,
|
|
5497
|
+
{ endpointObstacles, margin: 2 },
|
|
5498
|
+
diagnostics
|
|
5499
|
+
);
|
|
5500
|
+
}
|
|
5411
5501
|
const path = cornerPath ?? findObstacleFreePath(
|
|
5412
5502
|
source,
|
|
5413
5503
|
target,
|
|
5414
|
-
|
|
5504
|
+
allObstacles,
|
|
5415
5505
|
{ endpointObstacles, margin: 0 },
|
|
5416
5506
|
diagnostics
|
|
5417
5507
|
);
|
|
@@ -5432,6 +5522,66 @@ function routeEdge(input) {
|
|
|
5432
5522
|
checkBacktracking(finalized, source, target, diagnostics);
|
|
5433
5523
|
return { points: finalized, diagnostics };
|
|
5434
5524
|
}
|
|
5525
|
+
if (cornerPath !== null) {
|
|
5526
|
+
const fullCornerPath = cornerObstacles.length < allObstacles.length ? findCornerGraphPath(
|
|
5527
|
+
source,
|
|
5528
|
+
target,
|
|
5529
|
+
allObstacles,
|
|
5530
|
+
{ endpointObstacles, margin: 2 },
|
|
5531
|
+
diagnostics
|
|
5532
|
+
) : null;
|
|
5533
|
+
if (fullCornerPath !== null && fullCornerPath.length >= 2) {
|
|
5534
|
+
const fullFinalized = finalizeRoute(
|
|
5535
|
+
fullCornerPath,
|
|
5536
|
+
softObstacles,
|
|
5537
|
+
hardObstacles,
|
|
5538
|
+
diagnostics,
|
|
5539
|
+
softObstacleIndex,
|
|
5540
|
+
hardObstacleIndex
|
|
5541
|
+
);
|
|
5542
|
+
if (!routeIntersectsObstacles(
|
|
5543
|
+
fullFinalized,
|
|
5544
|
+
softObstacles,
|
|
5545
|
+
softObstacleIndex
|
|
5546
|
+
) && !routeIntersectsObstacles(
|
|
5547
|
+
fullFinalized,
|
|
5548
|
+
hardObstacles,
|
|
5549
|
+
hardObstacleIndex
|
|
5550
|
+
)) {
|
|
5551
|
+
checkBacktracking(fullFinalized, source, target, diagnostics);
|
|
5552
|
+
return { points: fullFinalized, diagnostics };
|
|
5553
|
+
}
|
|
5554
|
+
}
|
|
5555
|
+
const gridPath = findObstacleFreePath(
|
|
5556
|
+
source,
|
|
5557
|
+
target,
|
|
5558
|
+
allObstacles,
|
|
5559
|
+
{ endpointObstacles, margin: 0 },
|
|
5560
|
+
diagnostics
|
|
5561
|
+
);
|
|
5562
|
+
if (gridPath !== null && gridPath.length >= 2) {
|
|
5563
|
+
const gridFinalized = finalizeRoute(
|
|
5564
|
+
gridPath,
|
|
5565
|
+
softObstacles,
|
|
5566
|
+
hardObstacles,
|
|
5567
|
+
diagnostics,
|
|
5568
|
+
softObstacleIndex,
|
|
5569
|
+
hardObstacleIndex
|
|
5570
|
+
);
|
|
5571
|
+
if (!routeIntersectsObstacles(
|
|
5572
|
+
gridFinalized,
|
|
5573
|
+
softObstacles,
|
|
5574
|
+
softObstacleIndex
|
|
5575
|
+
) && !routeIntersectsObstacles(
|
|
5576
|
+
gridFinalized,
|
|
5577
|
+
hardObstacles,
|
|
5578
|
+
hardObstacleIndex
|
|
5579
|
+
)) {
|
|
5580
|
+
checkBacktracking(gridFinalized, source, target, diagnostics);
|
|
5581
|
+
return { points: gridFinalized, diagnostics };
|
|
5582
|
+
}
|
|
5583
|
+
}
|
|
5584
|
+
}
|
|
5435
5585
|
}
|
|
5436
5586
|
}
|
|
5437
5587
|
}
|
|
@@ -6532,7 +6682,7 @@ function solveDiagram(diagram, options = {}) {
|
|
|
6532
6682
|
edges: styledEdges
|
|
6533
6683
|
});
|
|
6534
6684
|
diagnostics.push(...layout2.diagnostics);
|
|
6535
|
-
const initialNodeBoxes = initialLayoutMode === "positions" ? layout2.boxes : wrapVerticalStackIfNeeded(
|
|
6685
|
+
const initialNodeBoxes = initialLayoutMode === "positions" || diagram.direction !== "LR" && diagram.direction !== "RL" ? layout2.boxes : wrapVerticalStackIfNeeded(
|
|
6536
6686
|
layout2.boxes,
|
|
6537
6687
|
styledNodes,
|
|
6538
6688
|
styledEdges,
|
|
@@ -6540,7 +6690,8 @@ function solveDiagram(diagram, options = {}) {
|
|
|
6540
6690
|
options,
|
|
6541
6691
|
diagnostics
|
|
6542
6692
|
);
|
|
6543
|
-
if ((diagram.direction === "TB" || diagram.direction === "BT") && options.maxRowDepth !== void 0) {
|
|
6693
|
+
if ((diagram.direction === "TB" || diagram.direction === "BT") && (options.maxRowDepth !== void 0 || options.targetAspectRatio !== void 0)) {
|
|
6694
|
+
const diagCountBefore = diagnostics.length;
|
|
6544
6695
|
const rewrapped = wrapHorizontalStackIfNeeded(
|
|
6545
6696
|
initialNodeBoxes,
|
|
6546
6697
|
styledNodes,
|
|
@@ -6551,6 +6702,20 @@ function solveDiagram(diagram, options = {}) {
|
|
|
6551
6702
|
for (const [id, box] of rewrapped) {
|
|
6552
6703
|
initialNodeBoxes.set(id, box);
|
|
6553
6704
|
}
|
|
6705
|
+
if (diagnostics.length > diagCountBefore) {
|
|
6706
|
+
for (const node of styledNodes) {
|
|
6707
|
+
if (node.position !== void 0 && rewrapped.has(node.id)) {
|
|
6708
|
+
const rwBox = rewrapped.get(node.id);
|
|
6709
|
+
const idx = styledNodes.indexOf(node);
|
|
6710
|
+
if (idx !== -1) {
|
|
6711
|
+
styledNodes[idx] = {
|
|
6712
|
+
...node,
|
|
6713
|
+
position: { x: rwBox.x, y: rwBox.y }
|
|
6714
|
+
};
|
|
6715
|
+
}
|
|
6716
|
+
}
|
|
6717
|
+
}
|
|
6718
|
+
}
|
|
6554
6719
|
}
|
|
6555
6720
|
if (useRecursive && "groupBoxes" in layout2) {
|
|
6556
6721
|
const recursiveLayout = layout2;
|
|
@@ -6564,7 +6729,7 @@ function solveDiagram(diagram, options = {}) {
|
|
|
6564
6729
|
overlapSpacing: options?.overlapSpacing ?? 40,
|
|
6565
6730
|
...options.minSiblingGap === void 0 ? {} : { minSiblingGap: options.minSiblingGap },
|
|
6566
6731
|
distributeContainedChildren: options.distributeContainedChildren ?? true,
|
|
6567
|
-
distributeSwimlaneChildren: options.distributeSwimlaneChildren
|
|
6732
|
+
...options.distributeSwimlaneChildren !== void 0 ? { distributeSwimlaneChildren: options.distributeSwimlaneChildren } : {},
|
|
6568
6733
|
swimlanes: styledSwimlanes,
|
|
6569
6734
|
boxes: initialNodeBoxes,
|
|
6570
6735
|
nodes: styledNodes,
|
|
@@ -7283,9 +7448,14 @@ function wrapHorizontalStackIfNeeded(boxes, nodes, direction, options, diagnosti
|
|
|
7283
7448
|
if (!isStackRunaway(boxes, nodes, direction, options)) {
|
|
7284
7449
|
return new Map(boxes);
|
|
7285
7450
|
}
|
|
7286
|
-
|
|
7287
|
-
if (maxRowDepth === void 0 || nodes.length <= maxRowDepth) {
|
|
7288
|
-
|
|
7451
|
+
let maxRowDepth = options.maxRowDepth;
|
|
7452
|
+
if (maxRowDepth === void 0 || maxRowDepth <= 0 || nodes.length <= maxRowDepth) {
|
|
7453
|
+
if (maxRowDepth === void 0 && options.targetAspectRatio !== void 0) {
|
|
7454
|
+
maxRowDepth = Math.ceil(Math.sqrt(nodes.length));
|
|
7455
|
+
if (nodes.length <= maxRowDepth) return new Map(boxes);
|
|
7456
|
+
} else {
|
|
7457
|
+
return new Map(boxes);
|
|
7458
|
+
}
|
|
7289
7459
|
}
|
|
7290
7460
|
const ordered = [...nodes].sort((a, b) => {
|
|
7291
7461
|
const ba = boxes.get(a.id);
|
|
@@ -7346,10 +7516,10 @@ function reportVerticalRunaway(boxes, nodes, edges, direction, options, diagnost
|
|
|
7346
7516
|
});
|
|
7347
7517
|
}
|
|
7348
7518
|
function isStackRunaway(boxes, nodes, direction, options) {
|
|
7349
|
-
if (options.maxStackDepth === void 0 && options.preferredAspectRatio === void 0) {
|
|
7519
|
+
if (options.maxStackDepth === void 0 && options.preferredAspectRatio === void 0 && options.targetAspectRatio === void 0 && options.maxRowDepth === void 0) {
|
|
7350
7520
|
return false;
|
|
7351
7521
|
}
|
|
7352
|
-
if (nodes.length < 2
|
|
7522
|
+
if (nodes.length < 2) {
|
|
7353
7523
|
return false;
|
|
7354
7524
|
}
|
|
7355
7525
|
const nodeBoxes = nodes.map((node) => boxes.get(node.id)).filter((box) => box !== void 0);
|
|
@@ -7357,11 +7527,18 @@ function isStackRunaway(boxes, nodes, direction, options) {
|
|
|
7357
7527
|
return false;
|
|
7358
7528
|
}
|
|
7359
7529
|
const bounds = unionBoxes(nodeBoxes);
|
|
7360
|
-
const
|
|
7361
|
-
const
|
|
7362
|
-
|
|
7530
|
+
const isHorizontal = direction === "TB" || direction === "BT";
|
|
7531
|
+
const aspectRatio = isHorizontal ? bounds.height <= 0 ? Number.POSITIVE_INFINITY : bounds.width / bounds.height : bounds.width <= 0 ? Number.POSITIVE_INFINITY : bounds.height / bounds.width;
|
|
7532
|
+
const preferred = isHorizontal ? options.targetAspectRatio ?? options.preferredAspectRatio ?? 3 : options.preferredAspectRatio ?? 3;
|
|
7533
|
+
if ((options.preferredAspectRatio !== void 0 || options.targetAspectRatio !== void 0) && aspectRatio < preferred) {
|
|
7363
7534
|
return false;
|
|
7364
7535
|
}
|
|
7536
|
+
if (isHorizontal) {
|
|
7537
|
+
const yCenters = nodeBoxes.map((box) => box.y + box.height / 2);
|
|
7538
|
+
const ySpread = Math.max(...yCenters) - Math.min(...yCenters);
|
|
7539
|
+
const maxHeight = Math.max(...nodeBoxes.map((box) => box.height));
|
|
7540
|
+
return ySpread <= Math.max(maxHeight, options.overlapSpacing ?? 40);
|
|
7541
|
+
}
|
|
7365
7542
|
const xCenters = nodeBoxes.map((box) => box.x + box.width / 2);
|
|
7366
7543
|
const xSpread = Math.max(...xCenters) - Math.min(...xCenters);
|
|
7367
7544
|
const maxWidth = Math.max(...nodeBoxes.map((box) => box.width));
|
|
@@ -7425,7 +7602,17 @@ function applyVerticalSwimlaneContract(swimlane, edges, topToBottomFlow, nodeBox
|
|
|
7425
7602
|
);
|
|
7426
7603
|
const rankSpacing = Math.max(96, maxRankStackHeight + padding);
|
|
7427
7604
|
const contentHeight = maxRank === 0 ? maxChildHeight : maxRankStackHeight + maxRank * rankSpacing;
|
|
7428
|
-
const
|
|
7605
|
+
const spreadWidth = maxCrossAxisSpreadWidth(
|
|
7606
|
+
swimlane,
|
|
7607
|
+
nodeBoxes,
|
|
7608
|
+
flowRanks,
|
|
7609
|
+
locks,
|
|
7610
|
+
rankStackGap
|
|
7611
|
+
);
|
|
7612
|
+
const slotWidth = Math.max(
|
|
7613
|
+
Math.max(...populatedBounds.map((box) => box.width)),
|
|
7614
|
+
spreadWidth
|
|
7615
|
+
) + padding * 2;
|
|
7429
7616
|
const laneStep = slotWidth + laneGutter;
|
|
7430
7617
|
const laneContentTop = top + headerHeight + padding;
|
|
7431
7618
|
for (let index = 0; index < swimlane.lanes.length; index += 1) {
|
|
@@ -7439,6 +7626,24 @@ function applyVerticalSwimlaneContract(swimlane, edges, topToBottomFlow, nodeBox
|
|
|
7439
7626
|
y: laneContentTop
|
|
7440
7627
|
};
|
|
7441
7628
|
if (maxRank === 0) {
|
|
7629
|
+
const distributable = lane.children.filter(
|
|
7630
|
+
(childId) => !locks.has(childId)
|
|
7631
|
+
);
|
|
7632
|
+
if (distributable.length >= CROSS_AXIS_SPREAD_THRESHOLD) {
|
|
7633
|
+
moveRankedVerticalLaneChildren(
|
|
7634
|
+
lane.children,
|
|
7635
|
+
nodeBoxes,
|
|
7636
|
+
locks,
|
|
7637
|
+
diagnostics,
|
|
7638
|
+
movedChildIds,
|
|
7639
|
+
flowRanks,
|
|
7640
|
+
rankSpacing,
|
|
7641
|
+
rankStackGap,
|
|
7642
|
+
{ x: target.x, y: laneContentTop },
|
|
7643
|
+
slotWidth - padding * 2
|
|
7644
|
+
);
|
|
7645
|
+
continue;
|
|
7646
|
+
}
|
|
7442
7647
|
moveLaneChildren(
|
|
7443
7648
|
lane.children,
|
|
7444
7649
|
nodeBoxes,
|
|
@@ -7461,10 +7666,8 @@ function applyVerticalSwimlaneContract(swimlane, edges, topToBottomFlow, nodeBox
|
|
|
7461
7666
|
flowRanks,
|
|
7462
7667
|
rankSpacing,
|
|
7463
7668
|
rankStackGap,
|
|
7464
|
-
{
|
|
7465
|
-
|
|
7466
|
-
y: laneContentTop
|
|
7467
|
-
}
|
|
7669
|
+
{ x: target.x, y: laneContentTop },
|
|
7670
|
+
slotWidth - padding * 2
|
|
7468
7671
|
);
|
|
7469
7672
|
}
|
|
7470
7673
|
return {
|
|
@@ -7573,31 +7776,99 @@ function maxVerticalRankStackHeight(swimlane, nodeBoxes, flowRanks, gap) {
|
|
|
7573
7776
|
}
|
|
7574
7777
|
return maxHeight;
|
|
7575
7778
|
}
|
|
7576
|
-
|
|
7779
|
+
var CROSS_AXIS_SPREAD_THRESHOLD = 3;
|
|
7780
|
+
function crossAxisSpreadWidth(items, gap) {
|
|
7781
|
+
return items.reduce(
|
|
7782
|
+
(sum, item, index) => sum + item.box.width + (index === 0 ? 0 : gap),
|
|
7783
|
+
0
|
|
7784
|
+
);
|
|
7785
|
+
}
|
|
7786
|
+
function maxCrossAxisSpreadWidth(swimlane, nodeBoxes, flowRanks, locks, gap) {
|
|
7787
|
+
let maxWidth = 0;
|
|
7788
|
+
for (const lane of swimlane.lanes) {
|
|
7789
|
+
for (const stack of rankStacks(
|
|
7790
|
+
lane.children,
|
|
7791
|
+
nodeBoxes,
|
|
7792
|
+
flowRanks
|
|
7793
|
+
).values()) {
|
|
7794
|
+
const unlocked = stack.filter((item) => !locks.has(item.childId));
|
|
7795
|
+
if (unlocked.length < CROSS_AXIS_SPREAD_THRESHOLD) continue;
|
|
7796
|
+
maxWidth = Math.max(maxWidth, crossAxisSpreadWidth(unlocked, gap));
|
|
7797
|
+
}
|
|
7798
|
+
}
|
|
7799
|
+
return maxWidth;
|
|
7800
|
+
}
|
|
7801
|
+
function moveRankedVerticalLaneChildren(childIds, nodeBoxes, locks, diagnostics, movedChildIds, flowRanks, rankSpacing, rankStackGap, target, contentWidth) {
|
|
7577
7802
|
for (const [rank, stack] of rankStacks(childIds, nodeBoxes, flowRanks)) {
|
|
7578
|
-
|
|
7803
|
+
const unlocked = [];
|
|
7579
7804
|
for (const item of stack) {
|
|
7580
|
-
|
|
7581
|
-
if (locks.has(childId)) {
|
|
7805
|
+
if (locks.has(item.childId)) {
|
|
7582
7806
|
diagnostics.push({
|
|
7583
7807
|
severity: "warning",
|
|
7584
7808
|
code: "constraints.locked-target-not-moved",
|
|
7585
|
-
message: `Locked child ${childId} was not moved into contract swimlane slot.`,
|
|
7809
|
+
message: `Locked child ${item.childId} was not moved into contract swimlane slot.`,
|
|
7586
7810
|
path: ["swimlanes"],
|
|
7587
|
-
detail: { nodeId: childId }
|
|
7811
|
+
detail: { nodeId: item.childId }
|
|
7588
7812
|
});
|
|
7589
|
-
|
|
7813
|
+
} else {
|
|
7814
|
+
unlocked.push(item);
|
|
7590
7815
|
}
|
|
7816
|
+
}
|
|
7817
|
+
if (unlocked.length === 0) continue;
|
|
7818
|
+
if (unlocked.length === 1) {
|
|
7819
|
+
const { childId, box } = unlocked[0];
|
|
7591
7820
|
const next = {
|
|
7592
7821
|
...box,
|
|
7593
|
-
x:
|
|
7594
|
-
y: target.y + rank * rankSpacing
|
|
7822
|
+
x: target.x + (contentWidth - box.width) / 2,
|
|
7823
|
+
y: target.y + rank * rankSpacing
|
|
7595
7824
|
};
|
|
7596
7825
|
if (next.x !== box.x || next.y !== box.y) {
|
|
7597
7826
|
movedChildIds.add(childId);
|
|
7598
7827
|
}
|
|
7599
7828
|
nodeBoxes.set(childId, next);
|
|
7600
|
-
|
|
7829
|
+
} else {
|
|
7830
|
+
const shouldSpread = unlocked.length >= CROSS_AXIS_SPREAD_THRESHOLD;
|
|
7831
|
+
if (!shouldSpread) {
|
|
7832
|
+
let yOffset = 0;
|
|
7833
|
+
for (const { childId, box } of unlocked) {
|
|
7834
|
+
const next = {
|
|
7835
|
+
...box,
|
|
7836
|
+
x: target.x + (contentWidth - box.width) / 2,
|
|
7837
|
+
y: target.y + rank * rankSpacing + yOffset
|
|
7838
|
+
};
|
|
7839
|
+
if (next.x !== box.x || next.y !== box.y) {
|
|
7840
|
+
movedChildIds.add(childId);
|
|
7841
|
+
}
|
|
7842
|
+
nodeBoxes.set(childId, next);
|
|
7843
|
+
yOffset += box.height + rankStackGap;
|
|
7844
|
+
}
|
|
7845
|
+
} else {
|
|
7846
|
+
const packedWidth = crossAxisSpreadWidth(unlocked, rankStackGap);
|
|
7847
|
+
let xCursor = target.x + Math.max(0, (contentWidth - packedWidth) / 2);
|
|
7848
|
+
for (const { childId, box } of unlocked) {
|
|
7849
|
+
const next = {
|
|
7850
|
+
...box,
|
|
7851
|
+
x: xCursor,
|
|
7852
|
+
y: target.y + rank * rankSpacing
|
|
7853
|
+
};
|
|
7854
|
+
if (next.x !== box.x || next.y !== box.y) {
|
|
7855
|
+
movedChildIds.add(childId);
|
|
7856
|
+
}
|
|
7857
|
+
nodeBoxes.set(childId, next);
|
|
7858
|
+
xCursor += box.width + rankStackGap;
|
|
7859
|
+
}
|
|
7860
|
+
diagnostics.push({
|
|
7861
|
+
severity: "info",
|
|
7862
|
+
code: "swimlane_contract.cross_axis_distributed",
|
|
7863
|
+
message: `Spread ${unlocked.length} same-rank children horizontally in contract lane (rank ${rank}).`,
|
|
7864
|
+
path: ["swimlanes"],
|
|
7865
|
+
detail: {
|
|
7866
|
+
rank,
|
|
7867
|
+
childCount: unlocked.length,
|
|
7868
|
+
contentWidth
|
|
7869
|
+
}
|
|
7870
|
+
});
|
|
7871
|
+
}
|
|
7601
7872
|
}
|
|
7602
7873
|
}
|
|
7603
7874
|
}
|
|
@@ -7936,7 +8207,7 @@ function coordinateNodes(nodes, boxes, options, diagnostics) {
|
|
|
7936
8207
|
});
|
|
7937
8208
|
continue;
|
|
7938
8209
|
}
|
|
7939
|
-
const ports = node.ports === void 0 ? void 0 : coordinatePorts(node, box, options.portShifting
|
|
8210
|
+
const ports = node.ports === void 0 ? void 0 : coordinatePorts(node, box, options.portShifting);
|
|
7940
8211
|
const geometry = computeShapeGeometry({
|
|
7941
8212
|
shape: node.shape,
|
|
7942
8213
|
box,
|
|
@@ -8030,7 +8301,7 @@ function expandNodeBoxesForPorts(nodes, boxes, options, diagnostics) {
|
|
|
8030
8301
|
}
|
|
8031
8302
|
}
|
|
8032
8303
|
}
|
|
8033
|
-
function coordinatePorts(node, nodeBox, portShifting
|
|
8304
|
+
function coordinatePorts(node, nodeBox, portShifting) {
|
|
8034
8305
|
const portsBySide = /* @__PURE__ */ new Map();
|
|
8035
8306
|
for (const port of node.ports ?? []) {
|
|
8036
8307
|
const ports = portsBySide.get(port.side) ?? [];
|
|
@@ -8053,9 +8324,7 @@ function coordinatePorts(node, nodeBox, portShifting, diagnostics) {
|
|
|
8053
8324
|
side,
|
|
8054
8325
|
index,
|
|
8055
8326
|
sorted.length,
|
|
8056
|
-
portShifting
|
|
8057
|
-
diagnostics,
|
|
8058
|
-
node.id
|
|
8327
|
+
portShifting
|
|
8059
8328
|
);
|
|
8060
8329
|
const box = portBox(anchor);
|
|
8061
8330
|
coordinated.push({ ...port, box, anchor });
|
|
@@ -8063,32 +8332,16 @@ function coordinatePorts(node, nodeBox, portShifting, diagnostics) {
|
|
|
8063
8332
|
}
|
|
8064
8333
|
return coordinated.sort((a, b) => a.id.localeCompare(b.id));
|
|
8065
8334
|
}
|
|
8066
|
-
function portAnchor(nodeBox, side, index, count, portShifting
|
|
8335
|
+
function portAnchor(nodeBox, side, index, count, portShifting) {
|
|
8067
8336
|
const shiftingEnabled = portShifting?.enabled ?? true;
|
|
8068
8337
|
const requestedSpacing = portShifting?.spacing ?? 24;
|
|
8069
8338
|
const maxOffset = side === "left" || side === "right" ? nodeBox.height / 2 : nodeBox.width / 2;
|
|
8070
8339
|
const availableSpan = 2 * maxOffset;
|
|
8071
8340
|
const minSpacing = PORT_BOX_SIZE + MIN_PORT_EDGE_GAP;
|
|
8072
|
-
const
|
|
8341
|
+
const spacing = shiftingEnabled && count > 1 ? Math.max(
|
|
8073
8342
|
Math.min(requestedSpacing, availableSpan / (count - 1)),
|
|
8074
8343
|
minSpacing
|
|
8075
8344
|
) : requestedSpacing;
|
|
8076
|
-
if (shiftingEnabled && count > 1 && effectiveSpacing < requestedSpacing && diagnostics !== void 0 && nodeId !== void 0) {
|
|
8077
|
-
diagnostics.push({
|
|
8078
|
-
severity: "warning",
|
|
8079
|
-
code: "port_constraint_overlap",
|
|
8080
|
-
message: `Port spacing on ${nodeId} ${side} compressed from ${requestedSpacing}px to ${Math.round(effectiveSpacing)}px for ${count} ports.`,
|
|
8081
|
-
path: ["nodes", nodeId, "ports"],
|
|
8082
|
-
detail: {
|
|
8083
|
-
nodeId,
|
|
8084
|
-
side,
|
|
8085
|
-
requestedSpacing,
|
|
8086
|
-
effectiveSpacing: Math.round(effectiveSpacing),
|
|
8087
|
-
portCount: count
|
|
8088
|
-
}
|
|
8089
|
-
});
|
|
8090
|
-
}
|
|
8091
|
-
const spacing = effectiveSpacing;
|
|
8092
8345
|
const centeredOffset = shiftingEnabled ? (index - (count - 1) / 2) * spacing : 0;
|
|
8093
8346
|
switch (side) {
|
|
8094
8347
|
case "left":
|