@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/cli/index.js
CHANGED
|
@@ -1391,6 +1391,28 @@ function applyLayoutConstraints(input) {
|
|
|
1391
1391
|
if (input.distributeContainedChildren) {
|
|
1392
1392
|
yieldFixedPositionLocks(input, boxes, locks);
|
|
1393
1393
|
}
|
|
1394
|
+
if (input.swimlanes !== void 0 && input.swimlanes.length > 0 && input.distributeSwimlaneChildren) {
|
|
1395
|
+
for (const swimlane of input.swimlanes) {
|
|
1396
|
+
if (swimlane.layout === "contract") continue;
|
|
1397
|
+
for (const lane of swimlane.lanes) {
|
|
1398
|
+
const fixedChildren = [];
|
|
1399
|
+
let participantCount = 0;
|
|
1400
|
+
for (const childId of lane.children) {
|
|
1401
|
+
const lock = locks.get(childId);
|
|
1402
|
+
if (lock === void 0) {
|
|
1403
|
+
participantCount += 1;
|
|
1404
|
+
} else if (lock.source === "fixed-position") {
|
|
1405
|
+
participantCount += 1;
|
|
1406
|
+
fixedChildren.push(childId);
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
if (participantCount < 2) continue;
|
|
1410
|
+
for (const childId of fixedChildren) {
|
|
1411
|
+
locks.delete(childId);
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1394
1416
|
applyContainment(input.constraints, boxes, locks, diagnostics, false);
|
|
1395
1417
|
applyRelative(input.constraints, boxes, locks, diagnostics);
|
|
1396
1418
|
applyAlign(input.constraints, boxes, locks, diagnostics);
|
|
@@ -1410,6 +1432,9 @@ function applyLayoutConstraints(input) {
|
|
|
1410
1432
|
applyDistributeContained(input, boxes, locks, diagnostics);
|
|
1411
1433
|
dedupReplayDiagnostics(diagnostics, diagBefore);
|
|
1412
1434
|
}
|
|
1435
|
+
if (input.swimlanes !== void 0 && input.swimlanes.length > 0 && input.distributeSwimlaneChildren) {
|
|
1436
|
+
distributeSwimlaneChildren(input, boxes, locks, diagnostics);
|
|
1437
|
+
}
|
|
1413
1438
|
removeResolvedConstraintDiagnostics(input.constraints, boxes, diagnostics);
|
|
1414
1439
|
reportOverlaps(
|
|
1415
1440
|
boxes,
|
|
@@ -2249,9 +2274,6 @@ function applyDistributeContained(input, boxes, locks, diagnostics) {
|
|
|
2249
2274
|
}
|
|
2250
2275
|
});
|
|
2251
2276
|
}
|
|
2252
|
-
if (input.swimlanes !== void 0 && input.swimlanes.length > 0 && input.distributeSwimlaneChildren) {
|
|
2253
|
-
distributeSwimlaneChildren(input, boxes, locks, diagnostics);
|
|
2254
|
-
}
|
|
2255
2277
|
}
|
|
2256
2278
|
function distributeSwimlaneChildren(input, boxes, locks, diagnostics) {
|
|
2257
2279
|
const spread = input.distributeSwimlaneChildren === "spread";
|
|
@@ -2291,6 +2313,7 @@ function distributeSwimlaneChildren(input, boxes, locks, diagnostics) {
|
|
|
2291
2313
|
effectiveGap = minGap + remaining / (unlocked.length - 1);
|
|
2292
2314
|
}
|
|
2293
2315
|
unlocked.sort((a, b) => a.box[axis] - b.box[axis]);
|
|
2316
|
+
reserved.sort((a, b) => a.start - b.start);
|
|
2294
2317
|
let pos = contentStart;
|
|
2295
2318
|
for (const child of unlocked) {
|
|
2296
2319
|
pos = advancePastReserved(pos, child.box[mainSize], reserved, minGap);
|
|
@@ -4082,15 +4105,12 @@ var BinaryHeap = class {
|
|
|
4082
4105
|
let smallestIdx = idx;
|
|
4083
4106
|
const leftIdx = (idx << 1) + 1;
|
|
4084
4107
|
const rightIdx = leftIdx + 1;
|
|
4085
|
-
if (leftIdx < size && this._less(
|
|
4086
|
-
this._data[leftIdx],
|
|
4087
|
-
this._data[smallestIdx]
|
|
4088
|
-
)) {
|
|
4108
|
+
if (leftIdx < size && this._less(this._data[leftIdx], entry)) {
|
|
4089
4109
|
smallestIdx = leftIdx;
|
|
4090
4110
|
}
|
|
4091
4111
|
if (rightIdx < size && this._less(
|
|
4092
4112
|
this._data[rightIdx],
|
|
4093
|
-
this._data[
|
|
4113
|
+
smallestIdx === leftIdx ? this._data[leftIdx] : entry
|
|
4094
4114
|
)) {
|
|
4095
4115
|
smallestIdx = rightIdx;
|
|
4096
4116
|
}
|
|
@@ -4157,8 +4177,59 @@ function findObstacleFreePath(source, target, obstacles, options = {}, diagnosti
|
|
|
4157
4177
|
turnPenalty,
|
|
4158
4178
|
segmentPenalty
|
|
4159
4179
|
);
|
|
4160
|
-
if (path
|
|
4161
|
-
|
|
4180
|
+
if (path !== null) {
|
|
4181
|
+
const simplified = simplifyRoute(path);
|
|
4182
|
+
const filteredSet = new Set(filtered);
|
|
4183
|
+
if (useCorridor && obstacles.some((o) => !filteredSet.has(o))) {
|
|
4184
|
+
let crossesExcluded = false;
|
|
4185
|
+
for (let i = 0; i < simplified.length - 1; i++) {
|
|
4186
|
+
const a = simplified[i];
|
|
4187
|
+
const b = simplified[i + 1];
|
|
4188
|
+
for (const obs of obstacles) {
|
|
4189
|
+
if (filteredSet.has(obs)) continue;
|
|
4190
|
+
if (segmentCrossesBoxStrict(a, b, obs, margin)) {
|
|
4191
|
+
crossesExcluded = true;
|
|
4192
|
+
break;
|
|
4193
|
+
}
|
|
4194
|
+
}
|
|
4195
|
+
if (crossesExcluded) break;
|
|
4196
|
+
}
|
|
4197
|
+
if (!crossesExcluded) return simplified;
|
|
4198
|
+
} else {
|
|
4199
|
+
return simplified;
|
|
4200
|
+
}
|
|
4201
|
+
}
|
|
4202
|
+
if (!useCorridor) return null;
|
|
4203
|
+
const xsFull = collectXs(source, target, obstacles, margin);
|
|
4204
|
+
const ysFull = collectYs(source, target, obstacles, margin);
|
|
4205
|
+
if (xsFull.length * ysFull.length > maxNodes) {
|
|
4206
|
+
diagnostics?.push({
|
|
4207
|
+
severity: "warning",
|
|
4208
|
+
code: "routing.astar.grid_overflow",
|
|
4209
|
+
message: `A* full-retry grid overflow: ${xsFull.length * ysFull.length} nodes > ${maxNodes} limit. Falling back to heuristic routing.`,
|
|
4210
|
+
detail: { xsCount: xsFull.length, ysCount: ysFull.length, maxNodes }
|
|
4211
|
+
});
|
|
4212
|
+
return null;
|
|
4213
|
+
}
|
|
4214
|
+
const { nodes: nodesFull, nodeIndex: idxFull } = buildGraph(xsFull, ysFull);
|
|
4215
|
+
connectHorizontalEdges(
|
|
4216
|
+
nodesFull,
|
|
4217
|
+
ysFull,
|
|
4218
|
+
obstacles,
|
|
4219
|
+
endpointObstacles,
|
|
4220
|
+
margin
|
|
4221
|
+
);
|
|
4222
|
+
connectVerticalEdges(nodesFull, xsFull, obstacles, endpointObstacles, margin);
|
|
4223
|
+
const pathFull = aStarSearch(
|
|
4224
|
+
nodesFull,
|
|
4225
|
+
idxFull,
|
|
4226
|
+
source,
|
|
4227
|
+
target,
|
|
4228
|
+
turnPenalty,
|
|
4229
|
+
segmentPenalty
|
|
4230
|
+
);
|
|
4231
|
+
if (pathFull === null) return null;
|
|
4232
|
+
return simplifyRoute(pathFull);
|
|
4162
4233
|
}
|
|
4163
4234
|
function filterObstaclesByCorridor(source, target, obstacles, endpointObstacles, margin) {
|
|
4164
4235
|
const cx1 = Math.min(source.x, target.x) - margin;
|
|
@@ -4385,7 +4456,7 @@ function findCornerGraphPath(source, target, obstacles, options = {}, diagnostic
|
|
|
4385
4456
|
const turnPenalty = options.turnPenalty ?? 50;
|
|
4386
4457
|
const segmentPenalty = options.segmentPenalty ?? 1;
|
|
4387
4458
|
const endpointObstacles = options.endpointObstacles ?? [];
|
|
4388
|
-
const maxCorners = options.maxCorners ??
|
|
4459
|
+
const maxCorners = options.maxCorners ?? 600;
|
|
4389
4460
|
const vertices = collectCornerVertices(source, target, obstacles, margin);
|
|
4390
4461
|
if (vertices.length > maxCorners) {
|
|
4391
4462
|
diagnostics?.push({
|
|
@@ -4754,17 +4825,36 @@ function routeEdge(input) {
|
|
|
4754
4825
|
input.source.center,
|
|
4755
4826
|
targetAnchor
|
|
4756
4827
|
);
|
|
4757
|
-
const
|
|
4828
|
+
const allObstacles = [...softObstacles, ...hardObstacles];
|
|
4829
|
+
const corridorObstacles = filterObstaclesByCorridor(
|
|
4758
4830
|
source,
|
|
4759
4831
|
target,
|
|
4760
|
-
|
|
4832
|
+
allObstacles,
|
|
4833
|
+
[],
|
|
4834
|
+
// endpointObstacles passed separately via options
|
|
4835
|
+
32
|
|
4836
|
+
);
|
|
4837
|
+
const cornerObstacles = corridorObstacles.length === 0 && allObstacles.length > 0 ? allObstacles : corridorObstacles;
|
|
4838
|
+
let cornerPath = findCornerGraphPath(
|
|
4839
|
+
source,
|
|
4840
|
+
target,
|
|
4841
|
+
cornerObstacles,
|
|
4761
4842
|
{ endpointObstacles, margin: 2 },
|
|
4762
4843
|
diagnostics
|
|
4763
4844
|
);
|
|
4845
|
+
if (cornerPath === null && cornerObstacles.length < allObstacles.length) {
|
|
4846
|
+
cornerPath = findCornerGraphPath(
|
|
4847
|
+
source,
|
|
4848
|
+
target,
|
|
4849
|
+
allObstacles,
|
|
4850
|
+
{ endpointObstacles, margin: 2 },
|
|
4851
|
+
diagnostics
|
|
4852
|
+
);
|
|
4853
|
+
}
|
|
4764
4854
|
const path = cornerPath ?? findObstacleFreePath(
|
|
4765
4855
|
source,
|
|
4766
4856
|
target,
|
|
4767
|
-
|
|
4857
|
+
allObstacles,
|
|
4768
4858
|
{ endpointObstacles, margin: 0 },
|
|
4769
4859
|
diagnostics
|
|
4770
4860
|
);
|
|
@@ -4785,6 +4875,66 @@ function routeEdge(input) {
|
|
|
4785
4875
|
checkBacktracking(finalized, source, target, diagnostics);
|
|
4786
4876
|
return { points: finalized, diagnostics };
|
|
4787
4877
|
}
|
|
4878
|
+
if (cornerPath !== null) {
|
|
4879
|
+
const fullCornerPath = cornerObstacles.length < allObstacles.length ? findCornerGraphPath(
|
|
4880
|
+
source,
|
|
4881
|
+
target,
|
|
4882
|
+
allObstacles,
|
|
4883
|
+
{ endpointObstacles, margin: 2 },
|
|
4884
|
+
diagnostics
|
|
4885
|
+
) : null;
|
|
4886
|
+
if (fullCornerPath !== null && fullCornerPath.length >= 2) {
|
|
4887
|
+
const fullFinalized = finalizeRoute(
|
|
4888
|
+
fullCornerPath,
|
|
4889
|
+
softObstacles,
|
|
4890
|
+
hardObstacles,
|
|
4891
|
+
diagnostics,
|
|
4892
|
+
softObstacleIndex,
|
|
4893
|
+
hardObstacleIndex
|
|
4894
|
+
);
|
|
4895
|
+
if (!routeIntersectsObstacles(
|
|
4896
|
+
fullFinalized,
|
|
4897
|
+
softObstacles,
|
|
4898
|
+
softObstacleIndex
|
|
4899
|
+
) && !routeIntersectsObstacles(
|
|
4900
|
+
fullFinalized,
|
|
4901
|
+
hardObstacles,
|
|
4902
|
+
hardObstacleIndex
|
|
4903
|
+
)) {
|
|
4904
|
+
checkBacktracking(fullFinalized, source, target, diagnostics);
|
|
4905
|
+
return { points: fullFinalized, diagnostics };
|
|
4906
|
+
}
|
|
4907
|
+
}
|
|
4908
|
+
const gridPath = findObstacleFreePath(
|
|
4909
|
+
source,
|
|
4910
|
+
target,
|
|
4911
|
+
allObstacles,
|
|
4912
|
+
{ endpointObstacles, margin: 0 },
|
|
4913
|
+
diagnostics
|
|
4914
|
+
);
|
|
4915
|
+
if (gridPath !== null && gridPath.length >= 2) {
|
|
4916
|
+
const gridFinalized = finalizeRoute(
|
|
4917
|
+
gridPath,
|
|
4918
|
+
softObstacles,
|
|
4919
|
+
hardObstacles,
|
|
4920
|
+
diagnostics,
|
|
4921
|
+
softObstacleIndex,
|
|
4922
|
+
hardObstacleIndex
|
|
4923
|
+
);
|
|
4924
|
+
if (!routeIntersectsObstacles(
|
|
4925
|
+
gridFinalized,
|
|
4926
|
+
softObstacles,
|
|
4927
|
+
softObstacleIndex
|
|
4928
|
+
) && !routeIntersectsObstacles(
|
|
4929
|
+
gridFinalized,
|
|
4930
|
+
hardObstacles,
|
|
4931
|
+
hardObstacleIndex
|
|
4932
|
+
)) {
|
|
4933
|
+
checkBacktracking(gridFinalized, source, target, diagnostics);
|
|
4934
|
+
return { points: gridFinalized, diagnostics };
|
|
4935
|
+
}
|
|
4936
|
+
}
|
|
4937
|
+
}
|
|
4788
4938
|
}
|
|
4789
4939
|
}
|
|
4790
4940
|
}
|
|
@@ -5674,7 +5824,7 @@ function solveDiagram(diagram, options = {}) {
|
|
|
5674
5824
|
edges: styledEdges
|
|
5675
5825
|
});
|
|
5676
5826
|
diagnostics.push(...layout2.diagnostics);
|
|
5677
|
-
const initialNodeBoxes = initialLayoutMode === "positions" ? layout2.boxes : wrapVerticalStackIfNeeded(
|
|
5827
|
+
const initialNodeBoxes = initialLayoutMode === "positions" || diagram.direction !== "LR" && diagram.direction !== "RL" ? layout2.boxes : wrapVerticalStackIfNeeded(
|
|
5678
5828
|
layout2.boxes,
|
|
5679
5829
|
styledNodes,
|
|
5680
5830
|
styledEdges,
|
|
@@ -5682,7 +5832,8 @@ function solveDiagram(diagram, options = {}) {
|
|
|
5682
5832
|
options,
|
|
5683
5833
|
diagnostics
|
|
5684
5834
|
);
|
|
5685
|
-
if ((diagram.direction === "TB" || diagram.direction === "BT") && options.maxRowDepth !== void 0) {
|
|
5835
|
+
if ((diagram.direction === "TB" || diagram.direction === "BT") && (options.maxRowDepth !== void 0 || options.targetAspectRatio !== void 0)) {
|
|
5836
|
+
const diagCountBefore = diagnostics.length;
|
|
5686
5837
|
const rewrapped = wrapHorizontalStackIfNeeded(
|
|
5687
5838
|
initialNodeBoxes,
|
|
5688
5839
|
styledNodes,
|
|
@@ -5693,6 +5844,20 @@ function solveDiagram(diagram, options = {}) {
|
|
|
5693
5844
|
for (const [id, box] of rewrapped) {
|
|
5694
5845
|
initialNodeBoxes.set(id, box);
|
|
5695
5846
|
}
|
|
5847
|
+
if (diagnostics.length > diagCountBefore) {
|
|
5848
|
+
for (const node of styledNodes) {
|
|
5849
|
+
if (node.position !== void 0 && rewrapped.has(node.id)) {
|
|
5850
|
+
const rwBox = rewrapped.get(node.id);
|
|
5851
|
+
const idx = styledNodes.indexOf(node);
|
|
5852
|
+
if (idx !== -1) {
|
|
5853
|
+
styledNodes[idx] = {
|
|
5854
|
+
...node,
|
|
5855
|
+
position: { x: rwBox.x, y: rwBox.y }
|
|
5856
|
+
};
|
|
5857
|
+
}
|
|
5858
|
+
}
|
|
5859
|
+
}
|
|
5860
|
+
}
|
|
5696
5861
|
}
|
|
5697
5862
|
if (useRecursive && "groupBoxes" in layout2) {
|
|
5698
5863
|
const recursiveLayout = layout2;
|
|
@@ -5706,7 +5871,7 @@ function solveDiagram(diagram, options = {}) {
|
|
|
5706
5871
|
overlapSpacing: options?.overlapSpacing ?? 40,
|
|
5707
5872
|
...options.minSiblingGap === void 0 ? {} : { minSiblingGap: options.minSiblingGap },
|
|
5708
5873
|
distributeContainedChildren: options.distributeContainedChildren ?? true,
|
|
5709
|
-
distributeSwimlaneChildren: options.distributeSwimlaneChildren
|
|
5874
|
+
...options.distributeSwimlaneChildren !== void 0 ? { distributeSwimlaneChildren: options.distributeSwimlaneChildren } : {},
|
|
5710
5875
|
swimlanes: styledSwimlanes,
|
|
5711
5876
|
boxes: initialNodeBoxes,
|
|
5712
5877
|
nodes: styledNodes,
|
|
@@ -6422,9 +6587,14 @@ function wrapHorizontalStackIfNeeded(boxes, nodes, direction, options, diagnosti
|
|
|
6422
6587
|
if (!isStackRunaway(boxes, nodes, direction, options)) {
|
|
6423
6588
|
return new Map(boxes);
|
|
6424
6589
|
}
|
|
6425
|
-
|
|
6426
|
-
if (maxRowDepth === void 0 || nodes.length <= maxRowDepth) {
|
|
6427
|
-
|
|
6590
|
+
let maxRowDepth = options.maxRowDepth;
|
|
6591
|
+
if (maxRowDepth === void 0 || maxRowDepth <= 0 || nodes.length <= maxRowDepth) {
|
|
6592
|
+
if (maxRowDepth === void 0 && options.targetAspectRatio !== void 0) {
|
|
6593
|
+
maxRowDepth = Math.ceil(Math.sqrt(nodes.length));
|
|
6594
|
+
if (nodes.length <= maxRowDepth) return new Map(boxes);
|
|
6595
|
+
} else {
|
|
6596
|
+
return new Map(boxes);
|
|
6597
|
+
}
|
|
6428
6598
|
}
|
|
6429
6599
|
const ordered = [...nodes].sort((a, b) => {
|
|
6430
6600
|
const ba = boxes.get(a.id);
|
|
@@ -6485,10 +6655,10 @@ function reportVerticalRunaway(boxes, nodes, edges, direction, options, diagnost
|
|
|
6485
6655
|
});
|
|
6486
6656
|
}
|
|
6487
6657
|
function isStackRunaway(boxes, nodes, direction, options) {
|
|
6488
|
-
if (options.maxStackDepth === void 0 && options.preferredAspectRatio === void 0) {
|
|
6658
|
+
if (options.maxStackDepth === void 0 && options.preferredAspectRatio === void 0 && options.targetAspectRatio === void 0 && options.maxRowDepth === void 0) {
|
|
6489
6659
|
return false;
|
|
6490
6660
|
}
|
|
6491
|
-
if (nodes.length < 2
|
|
6661
|
+
if (nodes.length < 2) {
|
|
6492
6662
|
return false;
|
|
6493
6663
|
}
|
|
6494
6664
|
const nodeBoxes = nodes.map((node) => boxes.get(node.id)).filter((box) => box !== void 0);
|
|
@@ -6496,11 +6666,18 @@ function isStackRunaway(boxes, nodes, direction, options) {
|
|
|
6496
6666
|
return false;
|
|
6497
6667
|
}
|
|
6498
6668
|
const bounds = unionBoxes(nodeBoxes);
|
|
6499
|
-
const
|
|
6500
|
-
const
|
|
6501
|
-
|
|
6669
|
+
const isHorizontal = direction === "TB" || direction === "BT";
|
|
6670
|
+
const aspectRatio = isHorizontal ? bounds.height <= 0 ? Number.POSITIVE_INFINITY : bounds.width / bounds.height : bounds.width <= 0 ? Number.POSITIVE_INFINITY : bounds.height / bounds.width;
|
|
6671
|
+
const preferred = isHorizontal ? options.targetAspectRatio ?? options.preferredAspectRatio ?? 3 : options.preferredAspectRatio ?? 3;
|
|
6672
|
+
if ((options.preferredAspectRatio !== void 0 || options.targetAspectRatio !== void 0) && aspectRatio < preferred) {
|
|
6502
6673
|
return false;
|
|
6503
6674
|
}
|
|
6675
|
+
if (isHorizontal) {
|
|
6676
|
+
const yCenters = nodeBoxes.map((box) => box.y + box.height / 2);
|
|
6677
|
+
const ySpread = Math.max(...yCenters) - Math.min(...yCenters);
|
|
6678
|
+
const maxHeight = Math.max(...nodeBoxes.map((box) => box.height));
|
|
6679
|
+
return ySpread <= Math.max(maxHeight, options.overlapSpacing ?? 40);
|
|
6680
|
+
}
|
|
6504
6681
|
const xCenters = nodeBoxes.map((box) => box.x + box.width / 2);
|
|
6505
6682
|
const xSpread = Math.max(...xCenters) - Math.min(...xCenters);
|
|
6506
6683
|
const maxWidth = Math.max(...nodeBoxes.map((box) => box.width));
|
|
@@ -6564,7 +6741,17 @@ function applyVerticalSwimlaneContract(swimlane, edges, topToBottomFlow, nodeBox
|
|
|
6564
6741
|
);
|
|
6565
6742
|
const rankSpacing = Math.max(96, maxRankStackHeight + padding);
|
|
6566
6743
|
const contentHeight = maxRank === 0 ? maxChildHeight : maxRankStackHeight + maxRank * rankSpacing;
|
|
6567
|
-
const
|
|
6744
|
+
const spreadWidth = maxCrossAxisSpreadWidth(
|
|
6745
|
+
swimlane,
|
|
6746
|
+
nodeBoxes,
|
|
6747
|
+
flowRanks,
|
|
6748
|
+
locks,
|
|
6749
|
+
rankStackGap
|
|
6750
|
+
);
|
|
6751
|
+
const slotWidth = Math.max(
|
|
6752
|
+
Math.max(...populatedBounds.map((box) => box.width)),
|
|
6753
|
+
spreadWidth
|
|
6754
|
+
) + padding * 2;
|
|
6568
6755
|
const laneStep = slotWidth + laneGutter;
|
|
6569
6756
|
const laneContentTop = top + headerHeight + padding;
|
|
6570
6757
|
for (let index = 0; index < swimlane.lanes.length; index += 1) {
|
|
@@ -6578,6 +6765,24 @@ function applyVerticalSwimlaneContract(swimlane, edges, topToBottomFlow, nodeBox
|
|
|
6578
6765
|
y: laneContentTop
|
|
6579
6766
|
};
|
|
6580
6767
|
if (maxRank === 0) {
|
|
6768
|
+
const distributable = lane.children.filter(
|
|
6769
|
+
(childId) => !locks.has(childId)
|
|
6770
|
+
);
|
|
6771
|
+
if (distributable.length >= CROSS_AXIS_SPREAD_THRESHOLD) {
|
|
6772
|
+
moveRankedVerticalLaneChildren(
|
|
6773
|
+
lane.children,
|
|
6774
|
+
nodeBoxes,
|
|
6775
|
+
locks,
|
|
6776
|
+
diagnostics,
|
|
6777
|
+
movedChildIds,
|
|
6778
|
+
flowRanks,
|
|
6779
|
+
rankSpacing,
|
|
6780
|
+
rankStackGap,
|
|
6781
|
+
{ x: target.x, y: laneContentTop },
|
|
6782
|
+
slotWidth - padding * 2
|
|
6783
|
+
);
|
|
6784
|
+
continue;
|
|
6785
|
+
}
|
|
6581
6786
|
moveLaneChildren(
|
|
6582
6787
|
lane.children,
|
|
6583
6788
|
nodeBoxes,
|
|
@@ -6600,10 +6805,8 @@ function applyVerticalSwimlaneContract(swimlane, edges, topToBottomFlow, nodeBox
|
|
|
6600
6805
|
flowRanks,
|
|
6601
6806
|
rankSpacing,
|
|
6602
6807
|
rankStackGap,
|
|
6603
|
-
{
|
|
6604
|
-
|
|
6605
|
-
y: laneContentTop
|
|
6606
|
-
}
|
|
6808
|
+
{ x: target.x, y: laneContentTop },
|
|
6809
|
+
slotWidth - padding * 2
|
|
6607
6810
|
);
|
|
6608
6811
|
}
|
|
6609
6812
|
return {
|
|
@@ -6712,31 +6915,99 @@ function maxVerticalRankStackHeight(swimlane, nodeBoxes, flowRanks, gap) {
|
|
|
6712
6915
|
}
|
|
6713
6916
|
return maxHeight;
|
|
6714
6917
|
}
|
|
6715
|
-
|
|
6918
|
+
var CROSS_AXIS_SPREAD_THRESHOLD = 3;
|
|
6919
|
+
function crossAxisSpreadWidth(items, gap) {
|
|
6920
|
+
return items.reduce(
|
|
6921
|
+
(sum, item, index) => sum + item.box.width + (index === 0 ? 0 : gap),
|
|
6922
|
+
0
|
|
6923
|
+
);
|
|
6924
|
+
}
|
|
6925
|
+
function maxCrossAxisSpreadWidth(swimlane, nodeBoxes, flowRanks, locks, gap) {
|
|
6926
|
+
let maxWidth = 0;
|
|
6927
|
+
for (const lane of swimlane.lanes) {
|
|
6928
|
+
for (const stack of rankStacks(
|
|
6929
|
+
lane.children,
|
|
6930
|
+
nodeBoxes,
|
|
6931
|
+
flowRanks
|
|
6932
|
+
).values()) {
|
|
6933
|
+
const unlocked = stack.filter((item) => !locks.has(item.childId));
|
|
6934
|
+
if (unlocked.length < CROSS_AXIS_SPREAD_THRESHOLD) continue;
|
|
6935
|
+
maxWidth = Math.max(maxWidth, crossAxisSpreadWidth(unlocked, gap));
|
|
6936
|
+
}
|
|
6937
|
+
}
|
|
6938
|
+
return maxWidth;
|
|
6939
|
+
}
|
|
6940
|
+
function moveRankedVerticalLaneChildren(childIds, nodeBoxes, locks, diagnostics, movedChildIds, flowRanks, rankSpacing, rankStackGap, target, contentWidth) {
|
|
6716
6941
|
for (const [rank, stack] of rankStacks(childIds, nodeBoxes, flowRanks)) {
|
|
6717
|
-
|
|
6942
|
+
const unlocked = [];
|
|
6718
6943
|
for (const item of stack) {
|
|
6719
|
-
|
|
6720
|
-
if (locks.has(childId)) {
|
|
6944
|
+
if (locks.has(item.childId)) {
|
|
6721
6945
|
diagnostics.push({
|
|
6722
6946
|
severity: "warning",
|
|
6723
6947
|
code: "constraints.locked-target-not-moved",
|
|
6724
|
-
message: `Locked child ${childId} was not moved into contract swimlane slot.`,
|
|
6948
|
+
message: `Locked child ${item.childId} was not moved into contract swimlane slot.`,
|
|
6725
6949
|
path: ["swimlanes"],
|
|
6726
|
-
detail: { nodeId: childId }
|
|
6950
|
+
detail: { nodeId: item.childId }
|
|
6727
6951
|
});
|
|
6728
|
-
|
|
6952
|
+
} else {
|
|
6953
|
+
unlocked.push(item);
|
|
6729
6954
|
}
|
|
6955
|
+
}
|
|
6956
|
+
if (unlocked.length === 0) continue;
|
|
6957
|
+
if (unlocked.length === 1) {
|
|
6958
|
+
const { childId, box } = unlocked[0];
|
|
6730
6959
|
const next = {
|
|
6731
6960
|
...box,
|
|
6732
|
-
x:
|
|
6733
|
-
y: target.y + rank * rankSpacing
|
|
6961
|
+
x: target.x + (contentWidth - box.width) / 2,
|
|
6962
|
+
y: target.y + rank * rankSpacing
|
|
6734
6963
|
};
|
|
6735
6964
|
if (next.x !== box.x || next.y !== box.y) {
|
|
6736
6965
|
movedChildIds.add(childId);
|
|
6737
6966
|
}
|
|
6738
6967
|
nodeBoxes.set(childId, next);
|
|
6739
|
-
|
|
6968
|
+
} else {
|
|
6969
|
+
const shouldSpread = unlocked.length >= CROSS_AXIS_SPREAD_THRESHOLD;
|
|
6970
|
+
if (!shouldSpread) {
|
|
6971
|
+
let yOffset = 0;
|
|
6972
|
+
for (const { childId, box } of unlocked) {
|
|
6973
|
+
const next = {
|
|
6974
|
+
...box,
|
|
6975
|
+
x: target.x + (contentWidth - box.width) / 2,
|
|
6976
|
+
y: target.y + rank * rankSpacing + yOffset
|
|
6977
|
+
};
|
|
6978
|
+
if (next.x !== box.x || next.y !== box.y) {
|
|
6979
|
+
movedChildIds.add(childId);
|
|
6980
|
+
}
|
|
6981
|
+
nodeBoxes.set(childId, next);
|
|
6982
|
+
yOffset += box.height + rankStackGap;
|
|
6983
|
+
}
|
|
6984
|
+
} else {
|
|
6985
|
+
const packedWidth = crossAxisSpreadWidth(unlocked, rankStackGap);
|
|
6986
|
+
let xCursor = target.x + Math.max(0, (contentWidth - packedWidth) / 2);
|
|
6987
|
+
for (const { childId, box } of unlocked) {
|
|
6988
|
+
const next = {
|
|
6989
|
+
...box,
|
|
6990
|
+
x: xCursor,
|
|
6991
|
+
y: target.y + rank * rankSpacing
|
|
6992
|
+
};
|
|
6993
|
+
if (next.x !== box.x || next.y !== box.y) {
|
|
6994
|
+
movedChildIds.add(childId);
|
|
6995
|
+
}
|
|
6996
|
+
nodeBoxes.set(childId, next);
|
|
6997
|
+
xCursor += box.width + rankStackGap;
|
|
6998
|
+
}
|
|
6999
|
+
diagnostics.push({
|
|
7000
|
+
severity: "info",
|
|
7001
|
+
code: "swimlane_contract.cross_axis_distributed",
|
|
7002
|
+
message: `Spread ${unlocked.length} same-rank children horizontally in contract lane (rank ${rank}).`,
|
|
7003
|
+
path: ["swimlanes"],
|
|
7004
|
+
detail: {
|
|
7005
|
+
rank,
|
|
7006
|
+
childCount: unlocked.length,
|
|
7007
|
+
contentWidth
|
|
7008
|
+
}
|
|
7009
|
+
});
|
|
7010
|
+
}
|
|
6740
7011
|
}
|
|
6741
7012
|
}
|
|
6742
7013
|
}
|
|
@@ -7075,7 +7346,7 @@ function coordinateNodes(nodes, boxes, options, diagnostics) {
|
|
|
7075
7346
|
});
|
|
7076
7347
|
continue;
|
|
7077
7348
|
}
|
|
7078
|
-
const ports = node.ports === void 0 ? void 0 : coordinatePorts(node, box, options.portShifting
|
|
7349
|
+
const ports = node.ports === void 0 ? void 0 : coordinatePorts(node, box, options.portShifting);
|
|
7079
7350
|
const geometry = computeShapeGeometry({
|
|
7080
7351
|
shape: node.shape,
|
|
7081
7352
|
box,
|
|
@@ -7169,7 +7440,7 @@ function expandNodeBoxesForPorts(nodes, boxes, options, diagnostics) {
|
|
|
7169
7440
|
}
|
|
7170
7441
|
}
|
|
7171
7442
|
}
|
|
7172
|
-
function coordinatePorts(node, nodeBox, portShifting
|
|
7443
|
+
function coordinatePorts(node, nodeBox, portShifting) {
|
|
7173
7444
|
const portsBySide = /* @__PURE__ */ new Map();
|
|
7174
7445
|
for (const port of node.ports ?? []) {
|
|
7175
7446
|
const ports = portsBySide.get(port.side) ?? [];
|
|
@@ -7192,9 +7463,7 @@ function coordinatePorts(node, nodeBox, portShifting, diagnostics) {
|
|
|
7192
7463
|
side,
|
|
7193
7464
|
index,
|
|
7194
7465
|
sorted.length,
|
|
7195
|
-
portShifting
|
|
7196
|
-
diagnostics,
|
|
7197
|
-
node.id
|
|
7466
|
+
portShifting
|
|
7198
7467
|
);
|
|
7199
7468
|
const box = portBox(anchor);
|
|
7200
7469
|
coordinated.push({ ...port, box, anchor });
|
|
@@ -7202,32 +7471,16 @@ function coordinatePorts(node, nodeBox, portShifting, diagnostics) {
|
|
|
7202
7471
|
}
|
|
7203
7472
|
return coordinated.sort((a, b) => a.id.localeCompare(b.id));
|
|
7204
7473
|
}
|
|
7205
|
-
function portAnchor(nodeBox, side, index, count, portShifting
|
|
7474
|
+
function portAnchor(nodeBox, side, index, count, portShifting) {
|
|
7206
7475
|
const shiftingEnabled = portShifting?.enabled ?? true;
|
|
7207
7476
|
const requestedSpacing = portShifting?.spacing ?? 24;
|
|
7208
7477
|
const maxOffset = side === "left" || side === "right" ? nodeBox.height / 2 : nodeBox.width / 2;
|
|
7209
7478
|
const availableSpan = 2 * maxOffset;
|
|
7210
7479
|
const minSpacing = PORT_BOX_SIZE + MIN_PORT_EDGE_GAP;
|
|
7211
|
-
const
|
|
7480
|
+
const spacing = shiftingEnabled && count > 1 ? Math.max(
|
|
7212
7481
|
Math.min(requestedSpacing, availableSpan / (count - 1)),
|
|
7213
7482
|
minSpacing
|
|
7214
7483
|
) : requestedSpacing;
|
|
7215
|
-
if (shiftingEnabled && count > 1 && effectiveSpacing < requestedSpacing && diagnostics !== void 0 && nodeId !== void 0) {
|
|
7216
|
-
diagnostics.push({
|
|
7217
|
-
severity: "warning",
|
|
7218
|
-
code: "port_constraint_overlap",
|
|
7219
|
-
message: `Port spacing on ${nodeId} ${side} compressed from ${requestedSpacing}px to ${Math.round(effectiveSpacing)}px for ${count} ports.`,
|
|
7220
|
-
path: ["nodes", nodeId, "ports"],
|
|
7221
|
-
detail: {
|
|
7222
|
-
nodeId,
|
|
7223
|
-
side,
|
|
7224
|
-
requestedSpacing,
|
|
7225
|
-
effectiveSpacing: Math.round(effectiveSpacing),
|
|
7226
|
-
portCount: count
|
|
7227
|
-
}
|
|
7228
|
-
});
|
|
7229
|
-
}
|
|
7230
|
-
const spacing = effectiveSpacing;
|
|
7231
7484
|
const centeredOffset = shiftingEnabled ? (index - (count - 1) / 2) * spacing : 0;
|
|
7232
7485
|
switch (side) {
|
|
7233
7486
|
case "left":
|