@tscircuit/capacity-autorouter 0.0.50 → 0.0.52
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/index.d.ts +5 -0
- package/dist/index.js +128 -42
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -559,6 +559,10 @@ declare class SingleHighDensityRouteSolver extends BaseSolver {
|
|
|
559
559
|
debug_nodesTooCloseToObstacle: Set<string>;
|
|
560
560
|
debug_nodePathToParentIntersectsObstacle: Set<string>;
|
|
561
561
|
debugEnabled: boolean;
|
|
562
|
+
initialNodeGridOffset: {
|
|
563
|
+
x: number;
|
|
564
|
+
y: number;
|
|
565
|
+
};
|
|
562
566
|
constructor(opts: {
|
|
563
567
|
connectionName: string;
|
|
564
568
|
obstacleRoutes: HighDensityIntraNodeRoute$1[];
|
|
@@ -1225,6 +1229,7 @@ declare class UnravelSectionSolver extends BaseSolver {
|
|
|
1225
1229
|
colorMap: Record<string, string>;
|
|
1226
1230
|
tunedNodeCapacityMap: Map<CapacityMeshNodeId, number>;
|
|
1227
1231
|
MAX_CANDIDATES: number;
|
|
1232
|
+
iterationsSinceImprovement: number;
|
|
1228
1233
|
selectedCandidateIndex: number | "best" | "original" | null;
|
|
1229
1234
|
queuedOrExploredCandidatePointModificationHashes: Set<string>;
|
|
1230
1235
|
constructor(params: {
|
package/dist/index.js
CHANGED
|
@@ -2205,6 +2205,7 @@ var SingleHighDensityRouteSolver = class extends BaseSolver {
|
|
|
2205
2205
|
debug_nodesTooCloseToObstacle;
|
|
2206
2206
|
debug_nodePathToParentIntersectsObstacle;
|
|
2207
2207
|
debugEnabled = true;
|
|
2208
|
+
initialNodeGridOffset;
|
|
2208
2209
|
constructor(opts) {
|
|
2209
2210
|
super();
|
|
2210
2211
|
this.bounds = opts.bounds;
|
|
@@ -2250,11 +2251,18 @@ var SingleHighDensityRouteSolver = class extends BaseSolver {
|
|
|
2250
2251
|
if (this.futureConnections && this.futureConnections.length === 0 && this.obstacleRoutes.length === 0) {
|
|
2251
2252
|
this.handleSimpleCases();
|
|
2252
2253
|
}
|
|
2254
|
+
const initialNodePosition = {
|
|
2255
|
+
x: Math.round(opts.A.x / (this.cellStep / 2)) * (this.cellStep / 2),
|
|
2256
|
+
y: Math.round(opts.A.y / (this.cellStep / 2)) * (this.cellStep / 2)
|
|
2257
|
+
};
|
|
2258
|
+
this.initialNodeGridOffset = {
|
|
2259
|
+
x: initialNodePosition.x - Math.round(opts.A.x / this.cellStep) * this.cellStep,
|
|
2260
|
+
y: initialNodePosition.y - Math.round(opts.A.y / this.cellStep) * this.cellStep
|
|
2261
|
+
};
|
|
2253
2262
|
this.candidates = new SingleRouteCandidatePriorityQueue([
|
|
2254
2263
|
{
|
|
2255
2264
|
...opts.A,
|
|
2256
|
-
|
|
2257
|
-
y: Math.floor(opts.A.y / this.cellStep) * this.cellStep,
|
|
2265
|
+
...initialNodePosition,
|
|
2258
2266
|
z: opts.A.z ?? 0,
|
|
2259
2267
|
g: 0,
|
|
2260
2268
|
h: 0,
|
|
@@ -2317,7 +2325,7 @@ var SingleHighDensityRouteSolver = class extends BaseSolver {
|
|
|
2317
2325
|
}
|
|
2318
2326
|
}
|
|
2319
2327
|
for (const via of route.vias) {
|
|
2320
|
-
if (distance(node, via) < this.viaDiameter / 2 + margin) {
|
|
2328
|
+
if (distance(node, via) < this.viaDiameter / 2 + this.traceThickness / 2 + margin) {
|
|
2321
2329
|
return true;
|
|
2322
2330
|
}
|
|
2323
2331
|
}
|
|
@@ -2484,7 +2492,13 @@ var SingleHighDensityRouteSolver = class extends BaseSolver {
|
|
|
2484
2492
|
goalDist,
|
|
2485
2493
|
currentNode.z === this.B.z
|
|
2486
2494
|
);
|
|
2487
|
-
if (goalDist <= this.cellStep * Math.SQRT2 && currentNode.z === this.B.z
|
|
2495
|
+
if (goalDist <= this.cellStep * Math.SQRT2 && currentNode.z === this.B.z && // Make sure the last segment doesn't intersect an obstacle
|
|
2496
|
+
!this.doesPathToParentIntersectObstacle({
|
|
2497
|
+
...currentNode,
|
|
2498
|
+
parent: currentNode,
|
|
2499
|
+
x: this.B.x,
|
|
2500
|
+
y: this.B.y
|
|
2501
|
+
})) {
|
|
2488
2502
|
this.solved = true;
|
|
2489
2503
|
this.setSolvedPath(currentNode);
|
|
2490
2504
|
}
|
|
@@ -2539,8 +2553,8 @@ z: ${this.B.z}`,
|
|
|
2539
2553
|
if (this.debug_nodePathToParentIntersectsObstacle.has(nodeKey)) continue;
|
|
2540
2554
|
graphics.rects.push({
|
|
2541
2555
|
center: {
|
|
2542
|
-
x: x + z * this.cellStep / 20,
|
|
2543
|
-
y: y + z * this.cellStep / 20
|
|
2556
|
+
x: x + this.initialNodeGridOffset.x + z * this.cellStep / 20,
|
|
2557
|
+
y: y + this.initialNodeGridOffset.y + z * this.cellStep / 20
|
|
2544
2558
|
},
|
|
2545
2559
|
fill: z === 0 ? `rgba(255,0,255,${0.3 - i / this.debug_exploredNodesOrdered.length * 0.2})` : `rgba(0,0,255,${0.3 - i / this.debug_exploredNodesOrdered.length * 0.2})`,
|
|
2546
2560
|
width: this.cellStep * 0.9,
|
|
@@ -4016,20 +4030,17 @@ function distance2(p1, p2) {
|
|
|
4016
4030
|
|
|
4017
4031
|
// lib/solvers/HighDensitySolver/TwoRouteHighDensitySolver/calculateSideTraversal.ts
|
|
4018
4032
|
var EPSILON2 = 1e-9;
|
|
4019
|
-
function calculateSegmentTraversal(startPoint, endPoint, bounds) {
|
|
4033
|
+
function calculateSegmentTraversal(startPoint, endPoint, bounds, turnDirection = "cw") {
|
|
4020
4034
|
const startAngle = pointToAngle(startPoint, bounds);
|
|
4021
|
-
|
|
4022
|
-
if (endAngle < startAngle) {
|
|
4023
|
-
endAngle += 2 * Math.PI;
|
|
4024
|
-
}
|
|
4035
|
+
const endAngle = pointToAngle(endPoint, bounds);
|
|
4025
4036
|
if (Math.abs(endAngle - startAngle) < EPSILON2) {
|
|
4026
4037
|
return { left: 0, top: 0, right: 0, bottom: 0 };
|
|
4027
4038
|
}
|
|
4028
|
-
return calculateSidePercentages(startAngle, endAngle, bounds);
|
|
4039
|
+
return calculateSidePercentages(startAngle, endAngle, bounds, turnDirection);
|
|
4029
4040
|
}
|
|
4030
|
-
function calculateTraversalPercentages(A, B, C, bounds) {
|
|
4031
|
-
const percentagesAB = calculateSegmentTraversal(A, B, bounds);
|
|
4032
|
-
const percentagesBC = calculateSegmentTraversal(B, C, bounds);
|
|
4041
|
+
function calculateTraversalPercentages(A, B, C, bounds, turnDirection) {
|
|
4042
|
+
const percentagesAB = calculateSegmentTraversal(A, B, bounds, turnDirection);
|
|
4043
|
+
const percentagesBC = calculateSegmentTraversal(B, C, bounds, turnDirection);
|
|
4033
4044
|
const totalPercentages = {
|
|
4034
4045
|
left: Math.min(1, percentagesAB.left + percentagesBC.left),
|
|
4035
4046
|
top: Math.min(1, percentagesAB.top + percentagesBC.top),
|
|
@@ -4066,7 +4077,7 @@ function pointToAngle(point, bounds) {
|
|
|
4066
4077
|
distance6 = Math.max(0, Math.min(perimeter, distance6));
|
|
4067
4078
|
return perimeter > EPSILON2 ? distance6 / perimeter * (2 * Math.PI) : 0;
|
|
4068
4079
|
}
|
|
4069
|
-
function calculateSidePercentages(startAngle, endAngle, bounds) {
|
|
4080
|
+
function calculateSidePercentages(startAngle, endAngle, bounds, turnDirection) {
|
|
4070
4081
|
const width = bounds.maxX - bounds.minX;
|
|
4071
4082
|
const height = bounds.maxY - bounds.minY;
|
|
4072
4083
|
if (width < EPSILON2 && height < EPSILON2)
|
|
@@ -4090,17 +4101,54 @@ function calculateSidePercentages(startAngle, endAngle, bounds) {
|
|
|
4090
4101
|
// Ends at 2PI
|
|
4091
4102
|
];
|
|
4092
4103
|
const result = { left: 0, top: 0, right: 0, bottom: 0 };
|
|
4093
|
-
const
|
|
4094
|
-
|
|
4104
|
+
const calculateTraversalOverlap = (sStart, sEnd, tStart, tEnd, wrapsAround) => {
|
|
4105
|
+
const effectiveSEnd = sEnd > 2 * Math.PI - EPSILON2 ? 2 * Math.PI : sEnd;
|
|
4106
|
+
if (effectiveSEnd <= sStart + EPSILON2) return 0;
|
|
4107
|
+
if (!wrapsAround) {
|
|
4108
|
+
const overlapStart = Math.max(sStart, tStart);
|
|
4109
|
+
const overlapEnd = Math.min(effectiveSEnd, tEnd);
|
|
4110
|
+
return Math.max(0, overlapEnd - overlapStart);
|
|
4111
|
+
} else {
|
|
4112
|
+
const overlap1Start = Math.max(sStart, tStart);
|
|
4113
|
+
const overlap1End = Math.min(effectiveSEnd, 2 * Math.PI);
|
|
4114
|
+
const overlap1 = Math.max(0, overlap1End - overlap1Start);
|
|
4115
|
+
const overlap2Start = Math.max(sStart, 0);
|
|
4116
|
+
const overlap2End = Math.min(effectiveSEnd, tEnd);
|
|
4117
|
+
const overlap2 = Math.max(0, overlap2End - overlap2Start);
|
|
4118
|
+
return overlap1 + overlap2;
|
|
4119
|
+
}
|
|
4120
|
+
};
|
|
4095
4121
|
for (const side of sides) {
|
|
4096
4122
|
const sideAngleRange = side.end - side.start;
|
|
4097
4123
|
if (sideAngleRange < EPSILON2 || side.length < EPSILON2) continue;
|
|
4098
|
-
|
|
4099
|
-
|
|
4100
|
-
|
|
4101
|
-
|
|
4124
|
+
let traversedAngleOnSide = 0;
|
|
4125
|
+
if (turnDirection === "cw") {
|
|
4126
|
+
const wraps = startAngle > endAngle + EPSILON2;
|
|
4127
|
+
traversedAngleOnSide = calculateTraversalOverlap(
|
|
4128
|
+
side.start,
|
|
4129
|
+
side.end,
|
|
4130
|
+
startAngle,
|
|
4131
|
+
endAngle,
|
|
4132
|
+
wraps
|
|
4133
|
+
);
|
|
4134
|
+
} else {
|
|
4135
|
+
const wraps = endAngle > startAngle + EPSILON2;
|
|
4136
|
+
traversedAngleOnSide = calculateTraversalOverlap(
|
|
4137
|
+
side.start,
|
|
4138
|
+
side.end,
|
|
4139
|
+
endAngle,
|
|
4140
|
+
// Start of equivalent CW traversal
|
|
4141
|
+
startAngle,
|
|
4142
|
+
// End of equivalent CW traversal
|
|
4143
|
+
wraps
|
|
4144
|
+
);
|
|
4145
|
+
}
|
|
4146
|
+
if (traversedAngleOnSide > EPSILON2) {
|
|
4102
4147
|
const percentage = traversedAngleOnSide / sideAngleRange;
|
|
4103
|
-
result[side.name] += Math.max(
|
|
4148
|
+
result[side.name] += Math.max(
|
|
4149
|
+
0,
|
|
4150
|
+
Number.isFinite(percentage) ? percentage : 0
|
|
4151
|
+
);
|
|
4104
4152
|
}
|
|
4105
4153
|
}
|
|
4106
4154
|
for (const key in result) {
|
|
@@ -4112,6 +4160,28 @@ function calculateSidePercentages(startAngle, endAngle, bounds) {
|
|
|
4112
4160
|
return result;
|
|
4113
4161
|
}
|
|
4114
4162
|
|
|
4163
|
+
// lib/solvers/HighDensitySolver/TwoRouteHighDensitySolver/computeTurnDirection.ts
|
|
4164
|
+
function triangleDirection({
|
|
4165
|
+
angleA,
|
|
4166
|
+
angleB,
|
|
4167
|
+
angleC
|
|
4168
|
+
}) {
|
|
4169
|
+
const Ax = Math.cos(angleA);
|
|
4170
|
+
const Ay = Math.sin(angleA);
|
|
4171
|
+
const Bx = Math.cos(angleB);
|
|
4172
|
+
const By = Math.sin(angleB);
|
|
4173
|
+
const Cx = Math.cos(angleC);
|
|
4174
|
+
const Cy = Math.sin(angleC);
|
|
4175
|
+
const signedArea = (Bx - Ax) * (Cy - Ay) - (By - Ay) * (Cx - Ax);
|
|
4176
|
+
return signedArea < 0 ? "ccw" : "cw";
|
|
4177
|
+
}
|
|
4178
|
+
function computeTurnDirection(A, B, C, bounds) {
|
|
4179
|
+
const angleA = pointToAngle(A, bounds);
|
|
4180
|
+
const angleB = pointToAngle(B, bounds);
|
|
4181
|
+
const angleC = pointToAngle(C, bounds);
|
|
4182
|
+
return triangleDirection({ angleA, angleB, angleC });
|
|
4183
|
+
}
|
|
4184
|
+
|
|
4115
4185
|
// lib/solvers/HighDensitySolver/TwoRouteHighDensitySolver/SingleTransitionCrossingRouteSolver.ts
|
|
4116
4186
|
var SingleTransitionCrossingRouteSolver = class extends BaseSolver {
|
|
4117
4187
|
// Input parameters
|
|
@@ -4199,7 +4269,14 @@ var SingleTransitionCrossingRouteSolver = class extends BaseSolver {
|
|
|
4199
4269
|
const A = flatRoute.A;
|
|
4200
4270
|
const B = ntrP1;
|
|
4201
4271
|
const C = flatRoute.B;
|
|
4202
|
-
const
|
|
4272
|
+
const turnDirection = computeTurnDirection(A, B, C, this.bounds);
|
|
4273
|
+
const sideTraversal = calculateTraversalPercentages(
|
|
4274
|
+
A,
|
|
4275
|
+
B,
|
|
4276
|
+
C,
|
|
4277
|
+
this.bounds,
|
|
4278
|
+
turnDirection
|
|
4279
|
+
);
|
|
4203
4280
|
const viaBounds = {
|
|
4204
4281
|
minX: this.bounds.minX + (sideTraversal.left > 0.5 ? marginFromBorderWithTrace : marginFromBorderWithoutTrace),
|
|
4205
4282
|
minY: this.bounds.minY + (sideTraversal.bottom > 0.5 ? marginFromBorderWithTrace : marginFromBorderWithoutTrace),
|
|
@@ -4277,23 +4354,29 @@ var SingleTransitionCrossingRouteSolver = class extends BaseSolver {
|
|
|
4277
4354
|
otherRouteStart.z !== flatStart.z ? otherRouteStart : otherRouteEnd,
|
|
4278
4355
|
this.traceThickness
|
|
4279
4356
|
);
|
|
4280
|
-
const
|
|
4357
|
+
const viaCircle = {
|
|
4281
4358
|
center: { x: via.x, y: via.y },
|
|
4282
4359
|
radius: minDistFromViaToTrace
|
|
4283
|
-
}
|
|
4284
|
-
const
|
|
4285
|
-
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
const
|
|
4289
|
-
const
|
|
4360
|
+
};
|
|
4361
|
+
const p1 = findPointToGetAroundCircle(flatStart, p2, viaCircle).E;
|
|
4362
|
+
const p3 = findPointToGetAroundCircle(p2, flatEnd, viaCircle).E;
|
|
4363
|
+
const p0_5 = findPointToGetAroundCircle(flatStart, p1, viaCircle).E;
|
|
4364
|
+
const p1_5 = findPointToGetAroundCircle(p1, p2, viaCircle).E;
|
|
4365
|
+
const p2_5 = findPointToGetAroundCircle(p2, p3, viaCircle).E;
|
|
4366
|
+
const p3_5 = findPointToGetAroundCircle(p3, flatEnd, viaCircle).E;
|
|
4367
|
+
const p2_better = findPointToGetAroundCircle(p1_5, p2_5, viaCircle).E;
|
|
4290
4368
|
return {
|
|
4291
4369
|
connectionName: flatRouteConnectionName,
|
|
4292
4370
|
route: [
|
|
4293
4371
|
{ x: flatStart.x, y: flatStart.y, z: flatStart.z ?? 0 },
|
|
4294
|
-
|
|
4295
|
-
{ x:
|
|
4296
|
-
|
|
4372
|
+
{ x: p0_5.x, y: p0_5.y, z: flatStart.z ?? 0 },
|
|
4373
|
+
{ x: p1.x, y: p1.y, z: flatStart.z ?? 0 },
|
|
4374
|
+
{ x: p1_5.x, y: p1_5.y, z: flatStart.z ?? 0 },
|
|
4375
|
+
// { x: p2.x, y: p2.y, z: flatStart.z ?? 0 },
|
|
4376
|
+
{ x: p2_better.x, y: p2_better.y, z: flatStart.z ?? 0 },
|
|
4377
|
+
{ x: p2_5.x, y: p2_5.y, z: flatStart.z ?? 0 },
|
|
4378
|
+
{ x: p3.x, y: p3.y, z: flatStart.z ?? 0 },
|
|
4379
|
+
{ x: p3_5.x, y: p3_5.y, z: flatStart.z ?? 0 },
|
|
4297
4380
|
{ x: flatEnd.x, y: flatEnd.y, z: flatEnd.z ?? 0 }
|
|
4298
4381
|
],
|
|
4299
4382
|
traceThickness: this.traceThickness,
|
|
@@ -5784,11 +5867,13 @@ var UnravelSectionSolver = class extends BaseSolver {
|
|
|
5784
5867
|
colorMap;
|
|
5785
5868
|
tunedNodeCapacityMap;
|
|
5786
5869
|
MAX_CANDIDATES = 500;
|
|
5870
|
+
iterationsSinceImprovement = 0;
|
|
5787
5871
|
selectedCandidateIndex = null;
|
|
5788
5872
|
queuedOrExploredCandidatePointModificationHashes = /* @__PURE__ */ new Set();
|
|
5789
5873
|
constructor(params) {
|
|
5790
5874
|
super();
|
|
5791
5875
|
this.MUTABLE_HOPS = params.MUTABLE_HOPS ?? this.MUTABLE_HOPS;
|
|
5876
|
+
this.MAX_ITERATIONS = 5e4;
|
|
5792
5877
|
this.nodeMap = params.nodeMap;
|
|
5793
5878
|
this.dedupedSegments = params.dedupedSegments;
|
|
5794
5879
|
if (params.dedupedSegmentMap) {
|
|
@@ -6183,6 +6268,7 @@ var UnravelSectionSolver = class extends BaseSolver {
|
|
|
6183
6268
|
}
|
|
6184
6269
|
_step() {
|
|
6185
6270
|
const candidate = this.candidates.shift();
|
|
6271
|
+
this.iterationsSinceImprovement++;
|
|
6186
6272
|
if (!candidate) {
|
|
6187
6273
|
this.solved = true;
|
|
6188
6274
|
return;
|
|
@@ -6190,6 +6276,7 @@ var UnravelSectionSolver = class extends BaseSolver {
|
|
|
6190
6276
|
this.lastProcessedCandidate = candidate;
|
|
6191
6277
|
if (candidate.f < (this.bestCandidate?.f ?? Infinity)) {
|
|
6192
6278
|
this.bestCandidate = candidate;
|
|
6279
|
+
this.iterationsSinceImprovement = 0;
|
|
6193
6280
|
}
|
|
6194
6281
|
this.getNeighbors(candidate).forEach((neighbor) => {
|
|
6195
6282
|
const isPartialHashExplored = this.queuedOrExploredCandidatePointModificationHashes.has(
|
|
@@ -6591,8 +6678,7 @@ var UnravelMultiSectionSolver = class extends BaseSolver {
|
|
|
6591
6678
|
}
|
|
6592
6679
|
this.activeSolver.step();
|
|
6593
6680
|
const { bestCandidate, originalCandidate, lastProcessedCandidate } = this.activeSolver;
|
|
6594
|
-
const
|
|
6595
|
-
const shouldEarlyStop = lastProcessedCandidate && lastProcessedCandidate.g > bestCandidate.g * giveUpFactor;
|
|
6681
|
+
const shouldEarlyStop = this.activeSolver.iterationsSinceImprovement > 200;
|
|
6596
6682
|
if (this.activeSolver.solved || shouldEarlyStop) {
|
|
6597
6683
|
const foundBetterSolution = bestCandidate && bestCandidate.g < originalCandidate.g;
|
|
6598
6684
|
if (foundBetterSolution) {
|
|
@@ -7909,13 +7995,13 @@ function minimumDistanceBetweenSegments(A1, A2, B1, B2) {
|
|
|
7909
7995
|
if (segmentsIntersect(A1, A2, B1, B2)) {
|
|
7910
7996
|
return 0;
|
|
7911
7997
|
}
|
|
7912
|
-
const distA1 =
|
|
7913
|
-
const distA2 =
|
|
7914
|
-
const distB1 =
|
|
7915
|
-
const distB2 =
|
|
7998
|
+
const distA1 = pointToSegmentDistance5(A1, B1, B2);
|
|
7999
|
+
const distA2 = pointToSegmentDistance5(A2, B1, B2);
|
|
8000
|
+
const distB1 = pointToSegmentDistance5(B1, A1, A2);
|
|
8001
|
+
const distB2 = pointToSegmentDistance5(B2, A1, A2);
|
|
7916
8002
|
return Math.min(distA1, distA2, distB1, distB2);
|
|
7917
8003
|
}
|
|
7918
|
-
function
|
|
8004
|
+
function pointToSegmentDistance5(P, Q1, Q2) {
|
|
7919
8005
|
const v = { x: Q2.x - Q1.x, y: Q2.y - Q1.y };
|
|
7920
8006
|
const w = { x: P.x - Q1.x, y: P.y - Q1.y };
|
|
7921
8007
|
const c1 = dotProduct(w, v);
|