@crazyhappyone/auto-graph 0.2.9 → 0.2.10
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 +311 -44
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +311 -44
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +311 -44
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +21 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +311 -44
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -4808,7 +4808,10 @@ function findObstacleFreePath(source, target, obstacles, options = {}, diagnosti
|
|
|
4808
4808
|
detail: {
|
|
4809
4809
|
xsCount: xs.length,
|
|
4810
4810
|
ysCount: ys.length,
|
|
4811
|
-
maxNodes
|
|
4811
|
+
maxNodes,
|
|
4812
|
+
obstacleCount: obstacles.length,
|
|
4813
|
+
stage: "corridor-filtered",
|
|
4814
|
+
...corridorMargin === void 0 ? {} : { corridorMargin }
|
|
4812
4815
|
}
|
|
4813
4816
|
});
|
|
4814
4817
|
return null;
|
|
@@ -4854,7 +4857,14 @@ function findObstacleFreePath(source, target, obstacles, options = {}, diagnosti
|
|
|
4854
4857
|
severity: "warning",
|
|
4855
4858
|
code: "routing.astar.grid_overflow",
|
|
4856
4859
|
message: `A* full-retry grid overflow: ${xsFull.length * ysFull.length} nodes > ${maxNodes} limit. Falling back to heuristic routing.`,
|
|
4857
|
-
detail: {
|
|
4860
|
+
detail: {
|
|
4861
|
+
xsCount: xsFull.length,
|
|
4862
|
+
ysCount: ysFull.length,
|
|
4863
|
+
maxNodes,
|
|
4864
|
+
obstacleCount: obstacles.length,
|
|
4865
|
+
stage: "full-retry",
|
|
4866
|
+
...corridorMargin === void 0 ? {} : { corridorMargin }
|
|
4867
|
+
}
|
|
4858
4868
|
});
|
|
4859
4869
|
return null;
|
|
4860
4870
|
}
|
|
@@ -5097,6 +5107,64 @@ function areCollinear(a, b, c) {
|
|
|
5097
5107
|
return a.x === b.x && b.x === c.x || a.y === b.y && b.y === c.y;
|
|
5098
5108
|
}
|
|
5099
5109
|
|
|
5110
|
+
// src/routing/budget.ts
|
|
5111
|
+
var MIN_CORNER_BUDGET = 600;
|
|
5112
|
+
var MAX_CORNER_BUDGET = 3e3;
|
|
5113
|
+
var MIN_NODE_BUDGET = 4e3;
|
|
5114
|
+
var MAX_NODE_BUDGET = 64e3;
|
|
5115
|
+
var CORNERS_PER_OBSTACLE = 12;
|
|
5116
|
+
var CORNER_HEADROOM = 2;
|
|
5117
|
+
var GRID_SAFETY_FACTOR = 3;
|
|
5118
|
+
var CORRIDOR_SCALING_K = 0.5;
|
|
5119
|
+
var CORRIDOR_SCALING_BASE = 200;
|
|
5120
|
+
function computeRoutingBudget(cornerObstacles, allObstacles, corridorMargin, overrides = {}) {
|
|
5121
|
+
const adaptiveMaxCorners = deriveMaxCorners(
|
|
5122
|
+
cornerObstacles.length,
|
|
5123
|
+
corridorMargin
|
|
5124
|
+
);
|
|
5125
|
+
const adaptiveMaxNodes = deriveMaxNodes(
|
|
5126
|
+
allObstacles.length,
|
|
5127
|
+
corridorMargin
|
|
5128
|
+
);
|
|
5129
|
+
return {
|
|
5130
|
+
maxCorners: resolveBudget(
|
|
5131
|
+
overrides.maxCorners,
|
|
5132
|
+
adaptiveMaxCorners,
|
|
5133
|
+
MIN_CORNER_BUDGET,
|
|
5134
|
+
MAX_CORNER_BUDGET
|
|
5135
|
+
),
|
|
5136
|
+
maxNodes: resolveBudget(
|
|
5137
|
+
overrides.maxNodes,
|
|
5138
|
+
adaptiveMaxNodes,
|
|
5139
|
+
MIN_NODE_BUDGET,
|
|
5140
|
+
MAX_NODE_BUDGET
|
|
5141
|
+
),
|
|
5142
|
+
cornerObstacleCount: cornerObstacles.length,
|
|
5143
|
+
gridObstacleCount: allObstacles.length,
|
|
5144
|
+
corridorMargin
|
|
5145
|
+
};
|
|
5146
|
+
}
|
|
5147
|
+
function deriveMaxCorners(obstacleCount, corridorMargin) {
|
|
5148
|
+
const base = 2 + obstacleCount * CORNERS_PER_OBSTACLE * CORNER_HEADROOM;
|
|
5149
|
+
const corridorFactor = corridorScalingFactor(corridorMargin);
|
|
5150
|
+
return Math.ceil(base * corridorFactor);
|
|
5151
|
+
}
|
|
5152
|
+
function deriveMaxNodes(obstacleCount, corridorMargin) {
|
|
5153
|
+
const base = 4 * obstacleCount * obstacleCount + 4 * obstacleCount + 100;
|
|
5154
|
+
const corridorFactor = corridorScalingFactor(corridorMargin);
|
|
5155
|
+
return Math.ceil(base * GRID_SAFETY_FACTOR * corridorFactor);
|
|
5156
|
+
}
|
|
5157
|
+
function corridorScalingFactor(corridorMargin) {
|
|
5158
|
+
return 1 + corridorMargin / CORRIDOR_SCALING_BASE * CORRIDOR_SCALING_K;
|
|
5159
|
+
}
|
|
5160
|
+
function resolveBudget(override, adaptive, min, max) {
|
|
5161
|
+
const chosen = override !== void 0 && Number.isFinite(override) && override >= 1 ? override : adaptive;
|
|
5162
|
+
return clamp(chosen, min, max);
|
|
5163
|
+
}
|
|
5164
|
+
function clamp(value, min, max) {
|
|
5165
|
+
return Math.max(min, Math.min(max, value));
|
|
5166
|
+
}
|
|
5167
|
+
|
|
5100
5168
|
// src/routing/visibility-router.ts
|
|
5101
5169
|
function findCornerGraphPath(source, target, obstacles, options = {}, diagnostics) {
|
|
5102
5170
|
const margin = options.margin ?? 0;
|
|
@@ -5110,7 +5178,12 @@ function findCornerGraphPath(source, target, obstacles, options = {}, diagnostic
|
|
|
5110
5178
|
severity: "warning",
|
|
5111
5179
|
code: "routing.visibility.corner_overflow",
|
|
5112
5180
|
message: `Corner graph overflow: ${vertices.length} vertices > ${maxCorners}. Falling back to grid A*.`,
|
|
5113
|
-
detail: {
|
|
5181
|
+
detail: {
|
|
5182
|
+
vertexCount: vertices.length,
|
|
5183
|
+
maxCorners,
|
|
5184
|
+
obstacleCount: obstacles.length,
|
|
5185
|
+
...options.corridorMargin === void 0 ? {} : { corridorMargin: options.corridorMargin }
|
|
5186
|
+
}
|
|
5114
5187
|
});
|
|
5115
5188
|
return null;
|
|
5116
5189
|
}
|
|
@@ -5384,7 +5457,7 @@ function segmentCrossesAnyObstacle(a, b, obstacles) {
|
|
|
5384
5457
|
}
|
|
5385
5458
|
|
|
5386
5459
|
// src/routing/routes.ts
|
|
5387
|
-
function checkBacktracking(points, source, target, diagnostics) {
|
|
5460
|
+
function checkBacktracking(points, source, target, diagnostics, maxRatio) {
|
|
5388
5461
|
if (points.length < 2) return;
|
|
5389
5462
|
const direct = Math.hypot(target.x - source.x, target.y - source.y);
|
|
5390
5463
|
if (direct <= 0) return;
|
|
@@ -5394,7 +5467,7 @@ function checkBacktracking(points, source, target, diagnostics) {
|
|
|
5394
5467
|
const b = points[i + 1];
|
|
5395
5468
|
routeLen += Math.hypot(b.x - a.x, b.y - a.y);
|
|
5396
5469
|
}
|
|
5397
|
-
const threshold =
|
|
5470
|
+
const threshold = maxRatio ?? 20;
|
|
5398
5471
|
if (routeLen > direct * threshold) {
|
|
5399
5472
|
diagnostics.push({
|
|
5400
5473
|
severity: "warning",
|
|
@@ -5412,8 +5485,20 @@ function routeEdge(input) {
|
|
|
5412
5485
|
const diagnostics = [];
|
|
5413
5486
|
const softObstacles = input.obstacles ?? [];
|
|
5414
5487
|
const hardObstacles = input.hardObstacles ?? [];
|
|
5488
|
+
let bestRejectedPath;
|
|
5489
|
+
let bestRejectedCrossings = Number.POSITIVE_INFINITY;
|
|
5415
5490
|
const softObstacleIndex = input.obstacleIndex ?? createBoxSpatialIndex(indexedBoxes(softObstacles));
|
|
5416
5491
|
const hardObstacleIndex = input.hardObstacleIndex ?? createBoxSpatialIndex(indexedBoxes(hardObstacles));
|
|
5492
|
+
const recordRejected = (candidate) => {
|
|
5493
|
+
if (routeIntersectsObstacles(candidate, hardObstacles, hardObstacleIndex)) {
|
|
5494
|
+
return;
|
|
5495
|
+
}
|
|
5496
|
+
const crossings = countObstacleCrossings(candidate, softObstacles);
|
|
5497
|
+
if (crossings < bestRejectedCrossings) {
|
|
5498
|
+
bestRejectedCrossings = crossings;
|
|
5499
|
+
bestRejectedPath = candidate;
|
|
5500
|
+
}
|
|
5501
|
+
};
|
|
5417
5502
|
const maxAttempts = input.maxRoutingAttempts ?? 5;
|
|
5418
5503
|
const defaultAnchors = defaultAnchorsForGeometry(
|
|
5419
5504
|
input.source.box,
|
|
@@ -5473,20 +5558,32 @@ function routeEdge(input) {
|
|
|
5473
5558
|
targetAnchor
|
|
5474
5559
|
);
|
|
5475
5560
|
const allObstacles = [...softObstacles, ...hardObstacles];
|
|
5561
|
+
const corridorMargin = input.corridorMargin ?? 32;
|
|
5476
5562
|
const corridorObstacles = filterObstaclesByCorridor(
|
|
5477
5563
|
source,
|
|
5478
5564
|
target,
|
|
5479
5565
|
allObstacles,
|
|
5480
5566
|
[],
|
|
5481
5567
|
// endpointObstacles passed separately via options
|
|
5482
|
-
|
|
5568
|
+
corridorMargin
|
|
5483
5569
|
);
|
|
5484
5570
|
const cornerObstacles = corridorObstacles.length === 0 && allObstacles.length > 0 ? allObstacles : corridorObstacles;
|
|
5571
|
+
const budget = computeRoutingBudget(
|
|
5572
|
+
cornerObstacles,
|
|
5573
|
+
allObstacles,
|
|
5574
|
+
corridorMargin,
|
|
5575
|
+
{ maxCorners: input.maxCorners, maxNodes: input.maxNodes }
|
|
5576
|
+
);
|
|
5485
5577
|
let cornerPath = findCornerGraphPath(
|
|
5486
5578
|
source,
|
|
5487
5579
|
target,
|
|
5488
5580
|
cornerObstacles,
|
|
5489
|
-
{
|
|
5581
|
+
{
|
|
5582
|
+
endpointObstacles,
|
|
5583
|
+
margin: 2,
|
|
5584
|
+
maxCorners: budget.maxCorners,
|
|
5585
|
+
corridorMargin
|
|
5586
|
+
},
|
|
5490
5587
|
diagnostics
|
|
5491
5588
|
);
|
|
5492
5589
|
if (cornerPath === null && cornerObstacles.length < allObstacles.length) {
|
|
@@ -5494,7 +5591,12 @@ function routeEdge(input) {
|
|
|
5494
5591
|
source,
|
|
5495
5592
|
target,
|
|
5496
5593
|
allObstacles,
|
|
5497
|
-
{
|
|
5594
|
+
{
|
|
5595
|
+
endpointObstacles,
|
|
5596
|
+
margin: 2,
|
|
5597
|
+
maxCorners: budget.maxCorners,
|
|
5598
|
+
corridorMargin
|
|
5599
|
+
},
|
|
5498
5600
|
diagnostics
|
|
5499
5601
|
);
|
|
5500
5602
|
}
|
|
@@ -5502,7 +5604,12 @@ function routeEdge(input) {
|
|
|
5502
5604
|
source,
|
|
5503
5605
|
target,
|
|
5504
5606
|
allObstacles,
|
|
5505
|
-
{
|
|
5607
|
+
{
|
|
5608
|
+
endpointObstacles,
|
|
5609
|
+
margin: 0,
|
|
5610
|
+
corridorMargin,
|
|
5611
|
+
maxNodes: budget.maxNodes
|
|
5612
|
+
},
|
|
5506
5613
|
diagnostics
|
|
5507
5614
|
);
|
|
5508
5615
|
if (path !== null && path.length >= 2) {
|
|
@@ -5519,15 +5626,27 @@ function routeEdge(input) {
|
|
|
5519
5626
|
softObstacles,
|
|
5520
5627
|
softObstacleIndex
|
|
5521
5628
|
) && !routeIntersectsObstacles(finalized, hardObstacles, hardObstacleIndex)) {
|
|
5522
|
-
checkBacktracking(
|
|
5629
|
+
checkBacktracking(
|
|
5630
|
+
finalized,
|
|
5631
|
+
source,
|
|
5632
|
+
target,
|
|
5633
|
+
diagnostics,
|
|
5634
|
+
input.maxBacktrackingRatio
|
|
5635
|
+
);
|
|
5523
5636
|
return { points: finalized, diagnostics };
|
|
5524
5637
|
}
|
|
5638
|
+
recordRejected(finalized);
|
|
5525
5639
|
if (cornerPath !== null) {
|
|
5526
5640
|
const fullCornerPath = cornerObstacles.length < allObstacles.length ? findCornerGraphPath(
|
|
5527
5641
|
source,
|
|
5528
5642
|
target,
|
|
5529
5643
|
allObstacles,
|
|
5530
|
-
{
|
|
5644
|
+
{
|
|
5645
|
+
endpointObstacles,
|
|
5646
|
+
margin: 2,
|
|
5647
|
+
maxCorners: budget.maxCorners,
|
|
5648
|
+
corridorMargin
|
|
5649
|
+
},
|
|
5531
5650
|
diagnostics
|
|
5532
5651
|
) : null;
|
|
5533
5652
|
if (fullCornerPath !== null && fullCornerPath.length >= 2) {
|
|
@@ -5548,15 +5667,27 @@ function routeEdge(input) {
|
|
|
5548
5667
|
hardObstacles,
|
|
5549
5668
|
hardObstacleIndex
|
|
5550
5669
|
)) {
|
|
5551
|
-
checkBacktracking(
|
|
5670
|
+
checkBacktracking(
|
|
5671
|
+
fullFinalized,
|
|
5672
|
+
source,
|
|
5673
|
+
target,
|
|
5674
|
+
diagnostics,
|
|
5675
|
+
input.maxBacktrackingRatio
|
|
5676
|
+
);
|
|
5552
5677
|
return { points: fullFinalized, diagnostics };
|
|
5553
5678
|
}
|
|
5679
|
+
recordRejected(fullFinalized);
|
|
5554
5680
|
}
|
|
5555
5681
|
const gridPath = findObstacleFreePath(
|
|
5556
5682
|
source,
|
|
5557
5683
|
target,
|
|
5558
5684
|
allObstacles,
|
|
5559
|
-
{
|
|
5685
|
+
{
|
|
5686
|
+
endpointObstacles,
|
|
5687
|
+
margin: 0,
|
|
5688
|
+
corridorMargin,
|
|
5689
|
+
maxNodes: budget.maxNodes
|
|
5690
|
+
},
|
|
5560
5691
|
diagnostics
|
|
5561
5692
|
);
|
|
5562
5693
|
if (gridPath !== null && gridPath.length >= 2) {
|
|
@@ -5577,9 +5708,16 @@ function routeEdge(input) {
|
|
|
5577
5708
|
hardObstacles,
|
|
5578
5709
|
hardObstacleIndex
|
|
5579
5710
|
)) {
|
|
5580
|
-
checkBacktracking(
|
|
5711
|
+
checkBacktracking(
|
|
5712
|
+
gridFinalized,
|
|
5713
|
+
source,
|
|
5714
|
+
target,
|
|
5715
|
+
diagnostics,
|
|
5716
|
+
input.maxBacktrackingRatio
|
|
5717
|
+
);
|
|
5581
5718
|
return { points: gridFinalized, diagnostics };
|
|
5582
5719
|
}
|
|
5720
|
+
recordRejected(gridFinalized);
|
|
5583
5721
|
}
|
|
5584
5722
|
}
|
|
5585
5723
|
}
|
|
@@ -5643,7 +5781,8 @@ function routeEdge(input) {
|
|
|
5643
5781
|
finalizedClean,
|
|
5644
5782
|
candidate.points[0],
|
|
5645
5783
|
candidate.points[candidate.points.length - 1],
|
|
5646
|
-
diagnostics
|
|
5784
|
+
diagnostics,
|
|
5785
|
+
input.maxBacktrackingRatio
|
|
5647
5786
|
);
|
|
5648
5787
|
return { points: finalizedClean, diagnostics };
|
|
5649
5788
|
}
|
|
@@ -5709,13 +5848,41 @@ function routeEdge(input) {
|
|
|
5709
5848
|
code: "routing.obstacle.unavoidable",
|
|
5710
5849
|
message: "No bounded orthogonal route candidate avoided all soft obstacles."
|
|
5711
5850
|
});
|
|
5712
|
-
|
|
5713
|
-
|
|
5714
|
-
|
|
5851
|
+
const finalizedSoftBest = finalizeRoute(
|
|
5852
|
+
bestPoints2,
|
|
5853
|
+
softObstacles,
|
|
5854
|
+
hardObstacles,
|
|
5855
|
+
diagnostics
|
|
5856
|
+
);
|
|
5857
|
+
let softFallback = finalizedSoftBest;
|
|
5858
|
+
if (bestRejectedPath !== void 0) {
|
|
5859
|
+
const finalizedRejected = finalizeRoute(
|
|
5860
|
+
bestRejectedPath,
|
|
5715
5861
|
softObstacles,
|
|
5716
5862
|
hardObstacles,
|
|
5717
5863
|
diagnostics
|
|
5718
|
-
)
|
|
5864
|
+
);
|
|
5865
|
+
const rejectedCrossings = countObstacleCrossings(
|
|
5866
|
+
finalizedRejected,
|
|
5867
|
+
softObstacles
|
|
5868
|
+
);
|
|
5869
|
+
const heuristicCrossings = countObstacleCrossings(
|
|
5870
|
+
finalizedSoftBest,
|
|
5871
|
+
softObstacles
|
|
5872
|
+
);
|
|
5873
|
+
if (rejectedCrossings < heuristicCrossings) {
|
|
5874
|
+
softFallback = finalizedRejected;
|
|
5875
|
+
}
|
|
5876
|
+
}
|
|
5877
|
+
checkBacktracking(
|
|
5878
|
+
softFallback,
|
|
5879
|
+
softFallback[0],
|
|
5880
|
+
softFallback[softFallback.length - 1],
|
|
5881
|
+
diagnostics,
|
|
5882
|
+
input.maxBacktrackingRatio
|
|
5883
|
+
);
|
|
5884
|
+
return {
|
|
5885
|
+
points: softFallback,
|
|
5719
5886
|
diagnostics
|
|
5720
5887
|
};
|
|
5721
5888
|
}
|
|
@@ -5747,6 +5914,22 @@ function routeEdge(input) {
|
|
|
5747
5914
|
maxAttempts
|
|
5748
5915
|
);
|
|
5749
5916
|
}
|
|
5917
|
+
if (bestRejectedPath !== void 0) {
|
|
5918
|
+
diagnostics.push({
|
|
5919
|
+
severity: "warning",
|
|
5920
|
+
code: "routing.obstacle.unavoidable",
|
|
5921
|
+
message: "Using A* route with minor soft-obstacle crossings to avoid hard evidence obstacles."
|
|
5922
|
+
});
|
|
5923
|
+
return {
|
|
5924
|
+
points: finalizeRoute(
|
|
5925
|
+
bestRejectedPath,
|
|
5926
|
+
softObstacles,
|
|
5927
|
+
hardObstacles,
|
|
5928
|
+
diagnostics
|
|
5929
|
+
),
|
|
5930
|
+
diagnostics
|
|
5931
|
+
};
|
|
5932
|
+
}
|
|
5750
5933
|
diagnostics.push({
|
|
5751
5934
|
severity: "error",
|
|
5752
5935
|
code: "routing.evidence.crossing_forbidden",
|
|
@@ -5794,13 +5977,41 @@ function routeEdge(input) {
|
|
|
5794
5977
|
code: "routing.obstacle.unavoidable",
|
|
5795
5978
|
message: "No bounded orthogonal route candidate avoided all obstacles."
|
|
5796
5979
|
});
|
|
5797
|
-
|
|
5798
|
-
|
|
5799
|
-
|
|
5980
|
+
const finalizedBestPoints = finalizeRoute(
|
|
5981
|
+
bestPoints,
|
|
5982
|
+
softObstacles,
|
|
5983
|
+
hardObstacles,
|
|
5984
|
+
diagnostics
|
|
5985
|
+
);
|
|
5986
|
+
let fallbackPoints = finalizedBestPoints;
|
|
5987
|
+
if (bestRejectedPath !== void 0) {
|
|
5988
|
+
const finalizedRejected = finalizeRoute(
|
|
5989
|
+
bestRejectedPath,
|
|
5800
5990
|
softObstacles,
|
|
5801
5991
|
hardObstacles,
|
|
5802
5992
|
diagnostics
|
|
5803
|
-
)
|
|
5993
|
+
);
|
|
5994
|
+
const rejectedCrossings = countObstacleCrossings(
|
|
5995
|
+
finalizedRejected,
|
|
5996
|
+
softObstacles
|
|
5997
|
+
);
|
|
5998
|
+
const heuristicCrossings = countObstacleCrossings(
|
|
5999
|
+
finalizedBestPoints,
|
|
6000
|
+
softObstacles
|
|
6001
|
+
);
|
|
6002
|
+
if (rejectedCrossings < heuristicCrossings) {
|
|
6003
|
+
fallbackPoints = finalizedRejected;
|
|
6004
|
+
}
|
|
6005
|
+
}
|
|
6006
|
+
checkBacktracking(
|
|
6007
|
+
fallbackPoints,
|
|
6008
|
+
fallbackPoints[0],
|
|
6009
|
+
fallbackPoints[fallbackPoints.length - 1],
|
|
6010
|
+
diagnostics,
|
|
6011
|
+
input.maxBacktrackingRatio
|
|
6012
|
+
);
|
|
6013
|
+
return {
|
|
6014
|
+
points: fallbackPoints,
|
|
5804
6015
|
diagnostics
|
|
5805
6016
|
};
|
|
5806
6017
|
}
|
|
@@ -6299,6 +6510,24 @@ function routeIntersectsObstacles(points, obstacles, spatialIndex) {
|
|
|
6299
6510
|
}
|
|
6300
6511
|
return false;
|
|
6301
6512
|
}
|
|
6513
|
+
function countObstacleCrossings(points, obstacles) {
|
|
6514
|
+
let count = 0;
|
|
6515
|
+
for (const obstacle of obstacles) {
|
|
6516
|
+
validateBox(obstacle);
|
|
6517
|
+
for (let pointIndex = 0; pointIndex < points.length - 1; pointIndex += 1) {
|
|
6518
|
+
const a = points[pointIndex];
|
|
6519
|
+
const b = points[pointIndex + 1];
|
|
6520
|
+
if (a === void 0 || b === void 0) {
|
|
6521
|
+
continue;
|
|
6522
|
+
}
|
|
6523
|
+
if (intersectsAabb(segmentBox2(a, b), obstacle)) {
|
|
6524
|
+
count += 1;
|
|
6525
|
+
break;
|
|
6526
|
+
}
|
|
6527
|
+
}
|
|
6528
|
+
}
|
|
6529
|
+
return count;
|
|
6530
|
+
}
|
|
6302
6531
|
function routeIntersectsEndpointInteriors(points, endpointInteriors) {
|
|
6303
6532
|
for (let index = 0; index < points.length - 1; index += 1) {
|
|
6304
6533
|
const a = points[index];
|
|
@@ -6744,7 +6973,8 @@ function solveDiagram(diagram, options = {}) {
|
|
|
6744
6973
|
constrained.boxes,
|
|
6745
6974
|
constrained.locks,
|
|
6746
6975
|
options?.overlapSpacing ?? 40,
|
|
6747
|
-
Math.max(0, options?.minLaneGutter ?? 0)
|
|
6976
|
+
Math.max(0, options?.minLaneGutter ?? 0),
|
|
6977
|
+
options.distributeContainedChildren ?? true
|
|
6748
6978
|
);
|
|
6749
6979
|
removeResolvedOverlapDiagnostics(diagnostics, constrained.boxes);
|
|
6750
6980
|
diagnostics.push(...swimlaneContracts.diagnostics);
|
|
@@ -6911,7 +7141,8 @@ function solveDiagram(diagram, options = {}) {
|
|
|
6911
7141
|
diagram.direction,
|
|
6912
7142
|
options,
|
|
6913
7143
|
diagnostics,
|
|
6914
|
-
coordinatedGroups
|
|
7144
|
+
coordinatedGroups,
|
|
7145
|
+
contentBounds
|
|
6915
7146
|
);
|
|
6916
7147
|
const edgeTextAnnotations = coordinateEdgeTextAnnotations(
|
|
6917
7148
|
coordinatedEdges,
|
|
@@ -7333,7 +7564,7 @@ function reportCjkTypographyDiagnostics(path, typography, previousStyle, diagnos
|
|
|
7333
7564
|
function containsCjk(value) {
|
|
7334
7565
|
return /[\u3400-\u4dbf\u4e00-\u9fff\uf900-\ufaff]/u.test(value);
|
|
7335
7566
|
}
|
|
7336
|
-
function applySwimlaneLayoutContracts(swimlanes, constraints, edges, topToBottomFlow, nodeBoxes, locks, overlapSpacing, laneGutter) {
|
|
7567
|
+
function applySwimlaneLayoutContracts(swimlanes, constraints, edges, topToBottomFlow, nodeBoxes, locks, overlapSpacing, laneGutter, distributeContainedChildren) {
|
|
7337
7568
|
const layouts = /* @__PURE__ */ new Map();
|
|
7338
7569
|
const diagnostics = [];
|
|
7339
7570
|
const movedChildIds = /* @__PURE__ */ new Set();
|
|
@@ -7352,7 +7583,9 @@ function applySwimlaneLayoutContracts(swimlanes, constraints, edges, topToBottom
|
|
|
7352
7583
|
locks,
|
|
7353
7584
|
diagnostics,
|
|
7354
7585
|
movedChildIds,
|
|
7355
|
-
laneGutter
|
|
7586
|
+
laneGutter,
|
|
7587
|
+
constraints,
|
|
7588
|
+
distributeContainedChildren
|
|
7356
7589
|
);
|
|
7357
7590
|
if (layout2 !== void 0) {
|
|
7358
7591
|
layouts.set(swimlane.id, layout2);
|
|
@@ -7544,7 +7777,7 @@ function isStackRunaway(boxes, nodes, direction, options) {
|
|
|
7544
7777
|
const maxWidth = Math.max(...nodeBoxes.map((box) => box.width));
|
|
7545
7778
|
return xSpread <= Math.max(maxWidth, options.overlapSpacing ?? 40);
|
|
7546
7779
|
}
|
|
7547
|
-
function applySingleSwimlaneContract(swimlane, edges, topToBottomFlow, nodeBoxes, locks, diagnostics, movedChildIds, laneGutter) {
|
|
7780
|
+
function applySingleSwimlaneContract(swimlane, edges, topToBottomFlow, nodeBoxes, locks, diagnostics, movedChildIds, laneGutter, constraints, distributeContainedChildren) {
|
|
7548
7781
|
const headerHeight = swimlane.headerHeight ?? 28;
|
|
7549
7782
|
const padding = swimlane.padding ?? 16;
|
|
7550
7783
|
const laneBounds = swimlane.lanes.map((lane) => {
|
|
@@ -7569,7 +7802,9 @@ function applySingleSwimlaneContract(swimlane, edges, topToBottomFlow, nodeBoxes
|
|
|
7569
7802
|
locks,
|
|
7570
7803
|
diagnostics,
|
|
7571
7804
|
movedChildIds,
|
|
7572
|
-
laneGutter
|
|
7805
|
+
laneGutter,
|
|
7806
|
+
constraints,
|
|
7807
|
+
distributeContainedChildren
|
|
7573
7808
|
);
|
|
7574
7809
|
}
|
|
7575
7810
|
return applyHorizontalSwimlaneContract(
|
|
@@ -7584,13 +7819,29 @@ function applySingleSwimlaneContract(swimlane, edges, topToBottomFlow, nodeBoxes
|
|
|
7584
7819
|
laneGutter
|
|
7585
7820
|
);
|
|
7586
7821
|
}
|
|
7587
|
-
function applyVerticalSwimlaneContract(swimlane, edges, topToBottomFlow, nodeBoxes, laneBounds, headerHeight, padding, locks, diagnostics, movedChildIds, laneGutter) {
|
|
7822
|
+
function applyVerticalSwimlaneContract(swimlane, edges, topToBottomFlow, nodeBoxes, laneBounds, headerHeight, padding, locks, diagnostics, movedChildIds, laneGutter, constraints, distributeContainedChildren) {
|
|
7588
7823
|
const populatedBounds = laneBounds.filter(
|
|
7589
7824
|
(box) => box !== void 0
|
|
7590
7825
|
);
|
|
7591
7826
|
const top = Math.min(...populatedBounds.map((box) => box.y));
|
|
7592
7827
|
const left = Math.min(...populatedBounds.map((box) => box.x));
|
|
7593
7828
|
const maxChildHeight = Math.max(...populatedBounds.map((box) => box.height));
|
|
7829
|
+
const containedChildIds = /* @__PURE__ */ new Set();
|
|
7830
|
+
if (distributeContainedChildren) {
|
|
7831
|
+
for (const c of constraints) {
|
|
7832
|
+
if (c.kind !== "containment") continue;
|
|
7833
|
+
if (nodeBoxes.get(c.containerId) === void 0) continue;
|
|
7834
|
+
const distributable = c.childIds.filter((childId) => {
|
|
7835
|
+
if (nodeBoxes.get(childId) === void 0) return false;
|
|
7836
|
+
const lock = locks.get(childId);
|
|
7837
|
+
return lock === void 0 || lock.source === "fixed-position";
|
|
7838
|
+
});
|
|
7839
|
+
if (distributable.length < 2) continue;
|
|
7840
|
+
for (const childId of distributable) {
|
|
7841
|
+
containedChildIds.add(childId);
|
|
7842
|
+
}
|
|
7843
|
+
}
|
|
7844
|
+
}
|
|
7594
7845
|
const flowRanks = topToBottomFlow ? rankVerticalSwimlaneChildren(swimlane, edges) : /* @__PURE__ */ new Map();
|
|
7595
7846
|
const maxRank = flowRanks.size === 0 ? 0 : Math.max(...Array.from(flowRanks.values()));
|
|
7596
7847
|
const rankStackGap = Math.max(8, padding / 2);
|
|
@@ -7607,7 +7858,8 @@ function applyVerticalSwimlaneContract(swimlane, edges, topToBottomFlow, nodeBox
|
|
|
7607
7858
|
nodeBoxes,
|
|
7608
7859
|
flowRanks,
|
|
7609
7860
|
locks,
|
|
7610
|
-
rankStackGap
|
|
7861
|
+
rankStackGap,
|
|
7862
|
+
containedChildIds
|
|
7611
7863
|
);
|
|
7612
7864
|
const slotWidth = Math.max(
|
|
7613
7865
|
Math.max(...populatedBounds.map((box) => box.width)),
|
|
@@ -7629,7 +7881,10 @@ function applyVerticalSwimlaneContract(swimlane, edges, topToBottomFlow, nodeBox
|
|
|
7629
7881
|
const distributable = lane.children.filter(
|
|
7630
7882
|
(childId) => !locks.has(childId)
|
|
7631
7883
|
);
|
|
7632
|
-
|
|
7884
|
+
const coveredByContainment = lane.children.some(
|
|
7885
|
+
(childId) => containedChildIds.has(childId)
|
|
7886
|
+
);
|
|
7887
|
+
if (!coveredByContainment && distributable.length >= CROSS_AXIS_SPREAD_THRESHOLD) {
|
|
7633
7888
|
moveRankedVerticalLaneChildren(
|
|
7634
7889
|
lane.children,
|
|
7635
7890
|
nodeBoxes,
|
|
@@ -7657,6 +7912,9 @@ function applyVerticalSwimlaneContract(swimlane, edges, topToBottomFlow, nodeBox
|
|
|
7657
7912
|
);
|
|
7658
7913
|
continue;
|
|
7659
7914
|
}
|
|
7915
|
+
const rankedCoveredByContainment = lane.children.some(
|
|
7916
|
+
(childId) => containedChildIds.has(childId)
|
|
7917
|
+
);
|
|
7660
7918
|
moveRankedVerticalLaneChildren(
|
|
7661
7919
|
lane.children,
|
|
7662
7920
|
nodeBoxes,
|
|
@@ -7667,7 +7925,8 @@ function applyVerticalSwimlaneContract(swimlane, edges, topToBottomFlow, nodeBox
|
|
|
7667
7925
|
rankSpacing,
|
|
7668
7926
|
rankStackGap,
|
|
7669
7927
|
{ x: target.x, y: laneContentTop },
|
|
7670
|
-
slotWidth - padding * 2
|
|
7928
|
+
slotWidth - padding * 2,
|
|
7929
|
+
rankedCoveredByContainment
|
|
7671
7930
|
);
|
|
7672
7931
|
}
|
|
7673
7932
|
return {
|
|
@@ -7783,9 +8042,12 @@ function crossAxisSpreadWidth(items, gap) {
|
|
|
7783
8042
|
0
|
|
7784
8043
|
);
|
|
7785
8044
|
}
|
|
7786
|
-
function maxCrossAxisSpreadWidth(swimlane, nodeBoxes, flowRanks, locks, gap) {
|
|
8045
|
+
function maxCrossAxisSpreadWidth(swimlane, nodeBoxes, flowRanks, locks, gap, containedChildIds) {
|
|
7787
8046
|
let maxWidth = 0;
|
|
7788
8047
|
for (const lane of swimlane.lanes) {
|
|
8048
|
+
if (containedChildIds !== void 0 && lane.children.some((childId) => containedChildIds.has(childId))) {
|
|
8049
|
+
continue;
|
|
8050
|
+
}
|
|
7789
8051
|
for (const stack of rankStacks(
|
|
7790
8052
|
lane.children,
|
|
7791
8053
|
nodeBoxes,
|
|
@@ -7798,7 +8060,7 @@ function maxCrossAxisSpreadWidth(swimlane, nodeBoxes, flowRanks, locks, gap) {
|
|
|
7798
8060
|
}
|
|
7799
8061
|
return maxWidth;
|
|
7800
8062
|
}
|
|
7801
|
-
function moveRankedVerticalLaneChildren(childIds, nodeBoxes, locks, diagnostics, movedChildIds, flowRanks, rankSpacing, rankStackGap, target, contentWidth) {
|
|
8063
|
+
function moveRankedVerticalLaneChildren(childIds, nodeBoxes, locks, diagnostics, movedChildIds, flowRanks, rankSpacing, rankStackGap, target, contentWidth, suppressSpread) {
|
|
7802
8064
|
for (const [rank, stack] of rankStacks(childIds, nodeBoxes, flowRanks)) {
|
|
7803
8065
|
const unlocked = [];
|
|
7804
8066
|
for (const item of stack) {
|
|
@@ -7827,7 +8089,7 @@ function moveRankedVerticalLaneChildren(childIds, nodeBoxes, locks, diagnostics,
|
|
|
7827
8089
|
}
|
|
7828
8090
|
nodeBoxes.set(childId, next);
|
|
7829
8091
|
} else {
|
|
7830
|
-
const shouldSpread = unlocked.length >= CROSS_AXIS_SPREAD_THRESHOLD;
|
|
8092
|
+
const shouldSpread = !suppressSpread && unlocked.length >= CROSS_AXIS_SPREAD_THRESHOLD;
|
|
7831
8093
|
if (!shouldSpread) {
|
|
7832
8094
|
let yOffset = 0;
|
|
7833
8095
|
for (const { childId, box } of unlocked) {
|
|
@@ -8956,14 +9218,21 @@ function evidenceOverlapDiagnostic(block, conflict) {
|
|
|
8956
9218
|
}
|
|
8957
9219
|
};
|
|
8958
9220
|
}
|
|
8959
|
-
function coordinateEdges(edges, nodes, coordinatedNodes, obstacles, softObstacles, textObstacles, hardObstacles, direction, options, diagnostics, groups) {
|
|
9221
|
+
function coordinateEdges(edges, nodes, coordinatedNodes, obstacles, softObstacles, textObstacles, hardObstacles, direction, options, diagnostics, groups, contentBounds) {
|
|
8960
9222
|
const coordinated = [];
|
|
8961
9223
|
const coordinatedNodeById = new Map(
|
|
8962
9224
|
coordinatedNodes.map((node) => [node.id, node])
|
|
8963
9225
|
);
|
|
9226
|
+
const corridorMarginOption = options.corridorMargin ?? "auto";
|
|
9227
|
+
const corridorMargin = typeof corridorMarginOption === "number" ? corridorMarginOption : Math.max(
|
|
9228
|
+
200,
|
|
9229
|
+
Math.hypot(contentBounds.width, contentBounds.height) * 0.3
|
|
9230
|
+
);
|
|
9231
|
+
const routingGutter = options.routingGutter ?? 160;
|
|
9232
|
+
const queryGutter = (options.routeKind ?? "orthogonal") === "obstacle-avoiding" ? Math.max(routingGutter, corridorMargin) : routingGutter;
|
|
8964
9233
|
const nodeObstacleIndex = createBoxSpatialIndex(
|
|
8965
9234
|
obstacles.map((box, index) => ({ id: `node-obstacle:${index}`, box })),
|
|
8966
|
-
|
|
9235
|
+
queryGutter
|
|
8967
9236
|
);
|
|
8968
9237
|
for (const edge of edges) {
|
|
8969
9238
|
const source = nodes.get(edge.source.nodeId);
|
|
@@ -8985,11 +9254,7 @@ function coordinateEdges(edges, nodes, coordinatedNodes, obstacles, softObstacle
|
|
|
8985
9254
|
const sourcePort = coordinatedNodeById.get(edge.source.nodeId)?.ports?.find((port) => port.id === edge.source.portId);
|
|
8986
9255
|
const targetPort = coordinatedNodeById.get(edge.target.nodeId)?.ports?.find((port) => port.id === edge.target.portId);
|
|
8987
9256
|
const routeTextObstacles = textObstacles.filter((annotation) => !isEdgeConnectedTextAnnotation(edge, annotation)).map((annotation) => annotation.box);
|
|
8988
|
-
const corridor = edgeCorridorBox(
|
|
8989
|
-
source.box,
|
|
8990
|
-
target.box,
|
|
8991
|
-
options.routingGutter ?? 160
|
|
8992
|
-
);
|
|
9257
|
+
const corridor = edgeCorridorBox(source.box, target.box, queryGutter);
|
|
8993
9258
|
const routeNodeObstacles = queryBoxSpatialIndex(nodeObstacleIndex, corridor).map((entry) => entry.box).filter(
|
|
8994
9259
|
(obstacle) => !sameBox(obstacle, source.obstacleBox) && !sameBox(obstacle, target.obstacleBox)
|
|
8995
9260
|
);
|
|
@@ -9007,7 +9272,9 @@ function coordinateEdges(edges, nodes, coordinatedNodes, obstacles, softObstacle
|
|
|
9007
9272
|
...routeTextObstacles
|
|
9008
9273
|
],
|
|
9009
9274
|
hardObstacles,
|
|
9010
|
-
|
|
9275
|
+
corridorMargin,
|
|
9276
|
+
...options.maxRoutingAttempts === void 0 ? {} : { maxRoutingAttempts: options.maxRoutingAttempts },
|
|
9277
|
+
...options.maxBacktrackingRatio === void 0 ? {} : { maxBacktrackingRatio: options.maxBacktrackingRatio }
|
|
9011
9278
|
});
|
|
9012
9279
|
diagnostics.push(
|
|
9013
9280
|
...route.diagnostics.map((diagnostic) => ({
|