@tscircuit/schematic-trace-solver 0.0.112 → 0.0.114
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 +10 -0
- package/dist/index.js +128 -10
- package/lib/solvers/LongDistancePairSolver/LongDistancePairSolver.ts +55 -1
- package/lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver.ts +13 -4
- package/lib/solvers/MspConnectionPairSolver/isLabeledPeripheralConnection.ts +44 -0
- package/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts +30 -3
- package/lib/solvers/TraceOverlapShiftSolver/TraceOverlapIssueSolver/TraceOverlapIssueSolver.ts +11 -1
- package/lib/solvers/TraceOverlapShiftSolver/TraceOverlapShiftSolver.ts +12 -0
- package/package.json +1 -1
- package/site/bug-reports/bug-report-20260728T144234Z.page.tsx +4 -0
- package/site/bug-reports/bug-report-20260728T225606Z.page.tsx +4 -0
- package/tests/bug-reports/bug-report-20260707T092615Z/__snapshots__/bug-report-20260707T092615Z.snap.svg +1 -1
- package/tests/bug-reports/bug-report-20260728T144234Z/__snapshots__/bug-report-20260728T144234Z.snap.svg +108 -0
- package/tests/bug-reports/bug-report-20260728T144234Z/bug-report-20260728T144234Z.json +518 -0
- package/tests/bug-reports/bug-report-20260728T144234Z/bug-report-20260728T144234Z.test.ts +12 -0
- package/tests/bug-reports/bug-report-20260728T225606Z/__snapshots__/bug-report-20260728T225606Z.snap.svg +131 -0
- package/tests/bug-reports/bug-report-20260728T225606Z/bug-report-20260728T225606Z.json +1127 -0
- package/tests/bug-reports/bug-report-20260728T225606Z/bug-report-20260728T225606Z.test.ts +35 -0
- package/tests/repros/__snapshots__/repro-atmega328p-missing-gnd-netlabel.snap.svg +1 -1
- package/tests/repros/__snapshots__/repro-rp2040-usb-cc2-ground-overlap.snap.svg +70 -0
- package/tests/repros/assets/repro-rp2040-usb-cc2-ground-overlap.input.json +164 -0
- package/tests/repros/repro-rp2040-usb-cc2-ground-overlap.test.ts +95 -0
package/dist/index.d.ts
CHANGED
|
@@ -244,6 +244,7 @@ declare class TraceOverlapIssueSolver extends BaseSolver {
|
|
|
244
244
|
overlappingTraceSegments: OverlappingTraceSegmentLocator[];
|
|
245
245
|
interactionKind: TraceInteractionKind;
|
|
246
246
|
traceNetIslands: Record<ConnNetId$1, Array<SolvedTracePath>>;
|
|
247
|
+
traceIdsToShift?: Set<MspConnectionPairId>;
|
|
247
248
|
obstacleRects: ReturnType<typeof getObstacleRects>;
|
|
248
249
|
SHIFT_DISTANCE: number;
|
|
249
250
|
correctedTraceMap: Record<MspConnectionPairId, SolvedTracePath>;
|
|
@@ -252,6 +253,7 @@ declare class TraceOverlapIssueSolver extends BaseSolver {
|
|
|
252
253
|
overlappingTraceSegments: OverlappingTraceSegmentLocator[];
|
|
253
254
|
interactionKind: TraceInteractionKind;
|
|
254
255
|
traceNetIslands: Record<ConnNetId$1, Array<SolvedTracePath>>;
|
|
256
|
+
traceIdsToShift?: Set<MspConnectionPairId>;
|
|
255
257
|
});
|
|
256
258
|
_step(): void;
|
|
257
259
|
private applyOffsets;
|
|
@@ -284,6 +286,7 @@ declare class TraceOverlapShiftSolver extends BaseSolver {
|
|
|
284
286
|
inputProblem: InputProblem;
|
|
285
287
|
inputTracePaths: Array<SolvedTracePath>;
|
|
286
288
|
globalConnMap: ConnectivityMap;
|
|
289
|
+
traceIdsToShift?: Set<MspConnectionPairId>;
|
|
287
290
|
activeSubSolver: TraceOverlapIssueSolver | null;
|
|
288
291
|
/**
|
|
289
292
|
* A traceNetIsland is a set of traces that are connected via the globalConnMap
|
|
@@ -295,6 +298,7 @@ declare class TraceOverlapShiftSolver extends BaseSolver {
|
|
|
295
298
|
inputProblem: InputProblem;
|
|
296
299
|
inputTracePaths: Array<SolvedTracePath>;
|
|
297
300
|
globalConnMap: ConnectivityMap;
|
|
301
|
+
traceIdsToShift?: Set<MspConnectionPairId>;
|
|
298
302
|
});
|
|
299
303
|
getConstructorParams(): ConstructorParameters<typeof TraceOverlapShiftSolver>[0];
|
|
300
304
|
computeTraceNetIslands(): Record<ConnNetId, Array<SolvedTracePath>>;
|
|
@@ -582,6 +586,8 @@ declare class LongDistancePairSolver extends BaseSolver {
|
|
|
582
586
|
solvedLongDistanceTraces: SolvedTracePath[];
|
|
583
587
|
private queuedCandidatePairs;
|
|
584
588
|
private currentCandidatePair;
|
|
589
|
+
private queuedFailedConnectionPairs;
|
|
590
|
+
private currentFailedConnectionPair;
|
|
585
591
|
private subSolver;
|
|
586
592
|
private chipMap;
|
|
587
593
|
private inputProblem;
|
|
@@ -592,12 +598,15 @@ declare class LongDistancePairSolver extends BaseSolver {
|
|
|
592
598
|
inputProblem: InputProblem;
|
|
593
599
|
alreadySolvedTraces: SolvedTracePath[];
|
|
594
600
|
primaryMspConnectionPairs: MspConnectionPair[];
|
|
601
|
+
failedConnectionPairs: MspConnectionPair[];
|
|
595
602
|
});
|
|
596
603
|
getConstructorParams(): {
|
|
597
604
|
inputProblem: InputProblem;
|
|
598
605
|
alreadySolvedTraces: SolvedTracePath[];
|
|
599
606
|
primaryMspConnectionPairs: MspConnectionPair[];
|
|
607
|
+
failedConnectionPairs: MspConnectionPair[];
|
|
600
608
|
};
|
|
609
|
+
private acceptFailedConnectionPair;
|
|
601
610
|
_step(): void;
|
|
602
611
|
visualize(): graphics_debug.GraphicsObject;
|
|
603
612
|
getOutput(): {
|
|
@@ -1071,6 +1080,7 @@ declare class SchematicTracePipelineSolver extends BaseSolver {
|
|
|
1071
1080
|
traceCleanupSolver?: TraceCleanupSolver;
|
|
1072
1081
|
example28Solver?: Example28Solver;
|
|
1073
1082
|
availableNetOrientationSolver?: AvailableNetOrientationSolver;
|
|
1083
|
+
postLabelTraceOverlapShiftSolver?: TraceOverlapShiftSolver;
|
|
1074
1084
|
railNetLabelCornerPlacementSolver?: RailNetLabelCornerPlacementSolver;
|
|
1075
1085
|
traceAnchoredNetLabelOverlapSolver?: TraceAnchoredNetLabelOverlapSolver;
|
|
1076
1086
|
preAlignmentNetLabelTraceCollisionSolver?: NetLabelTraceCollisionSolver;
|
package/dist/index.js
CHANGED
|
@@ -420,6 +420,33 @@ function getOrthogonalMinimumSpanningTree(pins, opts = {}) {
|
|
|
420
420
|
return edges;
|
|
421
421
|
}
|
|
422
422
|
|
|
423
|
+
// lib/solvers/MspConnectionPairSolver/isLabeledPeripheralConnection.ts
|
|
424
|
+
var isLabeledPeripheralConnection = ({
|
|
425
|
+
inputProblem,
|
|
426
|
+
chipMap,
|
|
427
|
+
pins
|
|
428
|
+
}) => {
|
|
429
|
+
const [firstPin, secondPin] = pins;
|
|
430
|
+
const directConnection = inputProblem.directConnections.find(
|
|
431
|
+
(connection) => connection.pinIds.includes(firstPin.pinId) && connection.pinIds.includes(secondPin.pinId)
|
|
432
|
+
);
|
|
433
|
+
if (directConnection?.netLabelWidth === void 0) return false;
|
|
434
|
+
const firstChip = chipMap[firstPin.chipId];
|
|
435
|
+
const secondChip = chipMap[secondPin.chipId];
|
|
436
|
+
if (!firstChip || !secondChip) return false;
|
|
437
|
+
const hasSinglePinPeripheral = firstChip.pins.length === 1 || secondChip.pins.length === 1;
|
|
438
|
+
if (!hasSinglePinPeripheral) return false;
|
|
439
|
+
let firstFacingDirection = firstPin._facingDirection;
|
|
440
|
+
if (!firstFacingDirection) {
|
|
441
|
+
firstFacingDirection = getPinDirection(firstPin, firstChip);
|
|
442
|
+
}
|
|
443
|
+
let secondFacingDirection = secondPin._facingDirection;
|
|
444
|
+
if (!secondFacingDirection) {
|
|
445
|
+
secondFacingDirection = getPinDirection(secondPin, secondChip);
|
|
446
|
+
}
|
|
447
|
+
return firstFacingDirection === "x-" && secondFacingDirection === "x+" || firstFacingDirection === "x+" && secondFacingDirection === "x-";
|
|
448
|
+
};
|
|
449
|
+
|
|
423
450
|
// lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver.ts
|
|
424
451
|
var DEFAULT_MAX_MSP_PAIR_DISTANCE = 1;
|
|
425
452
|
var getPinPairKey = (pinIds) => [...pinIds].sort().join("::");
|
|
@@ -489,8 +516,16 @@ var MspConnectionPairSolver = class extends BaseSolver {
|
|
|
489
516
|
const p1 = this.pinMap[pin1];
|
|
490
517
|
const p2 = this.pinMap[pin2];
|
|
491
518
|
const pinPairKey = getPinPairKey([pin1, pin2]);
|
|
492
|
-
|
|
493
|
-
if (
|
|
519
|
+
let pairDistance = Math.abs(p1.x - p2.x) + Math.abs(p1.y - p2.y);
|
|
520
|
+
if (this.directConnectionPinPairKeys.has(pinPairKey)) {
|
|
521
|
+
pairDistance = distance(p1, p2);
|
|
522
|
+
}
|
|
523
|
+
const isLabeledPeripheral = isLabeledPeripheralConnection({
|
|
524
|
+
inputProblem: this.inputProblem,
|
|
525
|
+
chipMap: this.chipMap,
|
|
526
|
+
pins: [p1, p2]
|
|
527
|
+
});
|
|
528
|
+
if (pairDistance > this.maxMspPairDistance && !isLabeledPeripheral) {
|
|
494
529
|
return;
|
|
495
530
|
}
|
|
496
531
|
if (arePinsInDifferentSchematicSections(this.inputProblem, p1, p2)) {
|
|
@@ -1744,6 +1779,7 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
|
|
|
1744
1779
|
overlappingTraceSegments;
|
|
1745
1780
|
interactionKind;
|
|
1746
1781
|
traceNetIslands;
|
|
1782
|
+
traceIdsToShift;
|
|
1747
1783
|
obstacleRects;
|
|
1748
1784
|
SHIFT_DISTANCE = 0.1;
|
|
1749
1785
|
correctedTraceMap = {};
|
|
@@ -1752,6 +1788,7 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
|
|
|
1752
1788
|
this.overlappingTraceSegments = params.overlappingTraceSegments;
|
|
1753
1789
|
this.interactionKind = params.interactionKind;
|
|
1754
1790
|
this.traceNetIslands = params.traceNetIslands;
|
|
1791
|
+
this.traceIdsToShift = params.traceIdsToShift;
|
|
1755
1792
|
this.obstacleRects = getObstacleRects(params.inputProblem);
|
|
1756
1793
|
for (const { connNetId, pathsWithOverlap } of this.overlappingTraceSegments) {
|
|
1757
1794
|
for (const {
|
|
@@ -1769,7 +1806,13 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
|
|
|
1769
1806
|
return path.tracePath.length === 2;
|
|
1770
1807
|
});
|
|
1771
1808
|
const groupShouldStayInPlace = this.overlappingTraceSegments.map(
|
|
1772
|
-
|
|
1809
|
+
(group) => {
|
|
1810
|
+
if (!this.traceIdsToShift) return containsStraightPinToPinTrace(group);
|
|
1811
|
+
return group.pathsWithOverlap.every(({ solvedTracePathIndex }) => {
|
|
1812
|
+
const path = this.traceNetIslands[group.connNetId][solvedTracePathIndex];
|
|
1813
|
+
return !this.traceIdsToShift.has(path.mspPairId);
|
|
1814
|
+
});
|
|
1815
|
+
}
|
|
1773
1816
|
);
|
|
1774
1817
|
const someGroupCanShift = groupShouldStayInPlace.some(
|
|
1775
1818
|
(shouldStay) => !shouldStay
|
|
@@ -2049,6 +2092,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
2049
2092
|
inputProblem;
|
|
2050
2093
|
inputTracePaths;
|
|
2051
2094
|
globalConnMap;
|
|
2095
|
+
traceIdsToShift;
|
|
2052
2096
|
/**
|
|
2053
2097
|
* A traceNetIsland is a set of traces that are connected via the globalConnMap
|
|
2054
2098
|
*/
|
|
@@ -2060,6 +2104,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
2060
2104
|
this.inputProblem = params.inputProblem;
|
|
2061
2105
|
this.inputTracePaths = params.inputTracePaths;
|
|
2062
2106
|
this.globalConnMap = params.globalConnMap;
|
|
2107
|
+
this.traceIdsToShift = params.traceIdsToShift;
|
|
2063
2108
|
for (const tracePath of this.inputTracePaths) {
|
|
2064
2109
|
const { mspPairId } = tracePath;
|
|
2065
2110
|
this.correctedTraceMap[mspPairId] = tracePath;
|
|
@@ -2070,7 +2115,8 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
2070
2115
|
return {
|
|
2071
2116
|
inputProblem: this.inputProblem,
|
|
2072
2117
|
inputTracePaths: this.inputTracePaths,
|
|
2073
|
-
globalConnMap: this.globalConnMap
|
|
2118
|
+
globalConnMap: this.globalConnMap,
|
|
2119
|
+
traceIdsToShift: this.traceIdsToShift
|
|
2074
2120
|
};
|
|
2075
2121
|
}
|
|
2076
2122
|
computeTraceNetIslands() {
|
|
@@ -2158,6 +2204,9 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
2158
2204
|
if (!aVert && !aHorz) continue;
|
|
2159
2205
|
for (let pb = 0; pb < pathsB.length; pb++) {
|
|
2160
2206
|
const pathB = pathsB[pb];
|
|
2207
|
+
if (this.traceIdsToShift && !this.traceIdsToShift.has(pathA.mspPairId) && !this.traceIdsToShift.has(pathB.mspPairId)) {
|
|
2208
|
+
continue;
|
|
2209
|
+
}
|
|
2161
2210
|
const ptsB = pathB.tracePath;
|
|
2162
2211
|
for (let sb = 0; sb < ptsB.length - 1; sb++) {
|
|
2163
2212
|
const b1 = ptsB[sb];
|
|
@@ -2307,7 +2356,8 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
2307
2356
|
inputProblem: this.inputProblem,
|
|
2308
2357
|
interactionKind,
|
|
2309
2358
|
overlappingTraceSegments,
|
|
2310
|
-
traceNetIslands: this.traceNetIslands
|
|
2359
|
+
traceNetIslands: this.traceNetIslands,
|
|
2360
|
+
traceIdsToShift: this.traceIdsToShift
|
|
2311
2361
|
});
|
|
2312
2362
|
}
|
|
2313
2363
|
visualize() {
|
|
@@ -4480,6 +4530,13 @@ var LongDistancePairSolver = class extends BaseSolver {
|
|
|
4480
4530
|
pinMap.set(pin.pinId, { ...pin, chipId: chip.chipId });
|
|
4481
4531
|
}
|
|
4482
4532
|
}
|
|
4533
|
+
this.queuedFailedConnectionPairs = this.params.failedConnectionPairs.filter(
|
|
4534
|
+
(connectionPair) => isLabeledPeripheralConnection({
|
|
4535
|
+
inputProblem: this.inputProblem,
|
|
4536
|
+
chipMap: this.chipMap,
|
|
4537
|
+
pins: connectionPair.pins
|
|
4538
|
+
})
|
|
4539
|
+
);
|
|
4483
4540
|
const candidatePairs = [];
|
|
4484
4541
|
const addedPairKeys = /* @__PURE__ */ new Set();
|
|
4485
4542
|
for (const netId of Object.keys(netConnMap.netMap)) {
|
|
@@ -4520,6 +4577,8 @@ var LongDistancePairSolver = class extends BaseSolver {
|
|
|
4520
4577
|
solvedLongDistanceTraces = [];
|
|
4521
4578
|
queuedCandidatePairs = [];
|
|
4522
4579
|
currentCandidatePair = null;
|
|
4580
|
+
queuedFailedConnectionPairs = [];
|
|
4581
|
+
currentFailedConnectionPair = null;
|
|
4523
4582
|
subSolver = null;
|
|
4524
4583
|
chipMap = {};
|
|
4525
4584
|
inputProblem;
|
|
@@ -4529,8 +4588,31 @@ var LongDistancePairSolver = class extends BaseSolver {
|
|
|
4529
4588
|
getConstructorParams() {
|
|
4530
4589
|
return this.params;
|
|
4531
4590
|
}
|
|
4591
|
+
acceptFailedConnectionPair(tracePath) {
|
|
4592
|
+
const connectionPair = this.currentFailedConnectionPair;
|
|
4593
|
+
if (!connectionPair) return;
|
|
4594
|
+
const solvedTrace = {
|
|
4595
|
+
...connectionPair,
|
|
4596
|
+
tracePath,
|
|
4597
|
+
mspConnectionPairIds: [connectionPair.mspPairId],
|
|
4598
|
+
pinIds: connectionPair.pins.map((pin) => pin.pinId)
|
|
4599
|
+
};
|
|
4600
|
+
this.solvedLongDistanceTraces.push(solvedTrace);
|
|
4601
|
+
this.allSolvedTraces.push(solvedTrace);
|
|
4602
|
+
for (const pin of connectionPair.pins) {
|
|
4603
|
+
this.newlyConnectedPinIds.add(pin.pinId);
|
|
4604
|
+
}
|
|
4605
|
+
}
|
|
4532
4606
|
_step() {
|
|
4533
|
-
if (this.subSolver?.solved) {
|
|
4607
|
+
if (this.subSolver?.solved && this.currentFailedConnectionPair) {
|
|
4608
|
+
const tracePath = this.subSolver.solvedTracePath;
|
|
4609
|
+
if (tracePath) {
|
|
4610
|
+
this.acceptFailedConnectionPair(tracePath);
|
|
4611
|
+
}
|
|
4612
|
+
this.subSolver = null;
|
|
4613
|
+
this.currentCandidatePair = null;
|
|
4614
|
+
this.currentFailedConnectionPair = null;
|
|
4615
|
+
} else if (this.subSolver?.solved) {
|
|
4534
4616
|
const newTracePath = this.subSolver.solvedTracePath;
|
|
4535
4617
|
if (newTracePath && this.currentCandidatePair) {
|
|
4536
4618
|
const isTraceClear = !doesTraceOverlapWithExistingTraces(
|
|
@@ -4561,11 +4643,23 @@ var LongDistancePairSolver = class extends BaseSolver {
|
|
|
4561
4643
|
} else if (this.subSolver?.failed) {
|
|
4562
4644
|
this.subSolver = null;
|
|
4563
4645
|
this.currentCandidatePair = null;
|
|
4646
|
+
this.currentFailedConnectionPair = null;
|
|
4564
4647
|
}
|
|
4565
4648
|
if (this.subSolver) {
|
|
4566
4649
|
this.subSolver.step();
|
|
4567
4650
|
return;
|
|
4568
4651
|
}
|
|
4652
|
+
const failedConnectionPair = this.queuedFailedConnectionPairs.shift();
|
|
4653
|
+
if (failedConnectionPair) {
|
|
4654
|
+
this.currentFailedConnectionPair = failedConnectionPair;
|
|
4655
|
+
this.currentCandidatePair = failedConnectionPair.pins;
|
|
4656
|
+
this.subSolver = new SchematicTraceSingleLineSolver2({
|
|
4657
|
+
inputProblem: this.params.inputProblem,
|
|
4658
|
+
pins: failedConnectionPair.pins,
|
|
4659
|
+
chipMap: this.chipMap
|
|
4660
|
+
});
|
|
4661
|
+
return;
|
|
4662
|
+
}
|
|
4569
4663
|
while (this.queuedCandidatePairs.length > 0) {
|
|
4570
4664
|
const nextPair = this.queuedCandidatePairs.shift();
|
|
4571
4665
|
const [p1, p2] = nextPair;
|
|
@@ -10632,6 +10726,7 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
|
|
|
10632
10726
|
traceCleanupSolver;
|
|
10633
10727
|
example28Solver;
|
|
10634
10728
|
availableNetOrientationSolver;
|
|
10729
|
+
postLabelTraceOverlapShiftSolver;
|
|
10635
10730
|
railNetLabelCornerPlacementSolver;
|
|
10636
10731
|
traceAnchoredNetLabelOverlapSolver;
|
|
10637
10732
|
preAlignmentNetLabelTraceCollisionSolver;
|
|
@@ -10687,7 +10782,8 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
|
|
|
10687
10782
|
{
|
|
10688
10783
|
inputProblem: instance.inputProblem,
|
|
10689
10784
|
primaryMspConnectionPairs: instance.mspConnectionPairSolver.mspConnectionPairs,
|
|
10690
|
-
alreadySolvedTraces: instance.schematicTraceLinesSolver.solvedTracePaths
|
|
10785
|
+
alreadySolvedTraces: instance.schematicTraceLinesSolver.solvedTracePaths,
|
|
10786
|
+
failedConnectionPairs: instance.schematicTraceLinesSolver.failedConnectionPairs
|
|
10691
10787
|
}
|
|
10692
10788
|
],
|
|
10693
10789
|
{
|
|
@@ -10807,6 +10903,22 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
|
|
|
10807
10903
|
}
|
|
10808
10904
|
]
|
|
10809
10905
|
),
|
|
10906
|
+
definePipelineStep(
|
|
10907
|
+
"postLabelTraceOverlapShiftSolver",
|
|
10908
|
+
TraceOverlapShiftSolver,
|
|
10909
|
+
(instance) => [
|
|
10910
|
+
{
|
|
10911
|
+
inputProblem: instance.inputProblem,
|
|
10912
|
+
inputTracePaths: instance.availableNetOrientationSolver.traces,
|
|
10913
|
+
globalConnMap: instance.mspConnectionPairSolver.globalConnMap,
|
|
10914
|
+
traceIdsToShift: new Set(
|
|
10915
|
+
instance.availableNetOrientationSolver.traces.filter(
|
|
10916
|
+
(trace) => trace.mspPairId.startsWith("available-net-orientation-")
|
|
10917
|
+
).map((trace) => trace.mspPairId)
|
|
10918
|
+
)
|
|
10919
|
+
}
|
|
10920
|
+
]
|
|
10921
|
+
),
|
|
10810
10922
|
definePipelineStep(
|
|
10811
10923
|
"railNetLabelCornerPlacementSolver",
|
|
10812
10924
|
RailNetLabelCornerPlacementSolver,
|
|
@@ -10814,7 +10926,9 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
|
|
|
10814
10926
|
return [
|
|
10815
10927
|
{
|
|
10816
10928
|
inputProblem: instance.inputProblem,
|
|
10817
|
-
traces:
|
|
10929
|
+
traces: Object.values(
|
|
10930
|
+
instance.postLabelTraceOverlapShiftSolver.correctedTraceMap
|
|
10931
|
+
),
|
|
10818
10932
|
netLabelPlacements: instance.availableNetOrientationSolver.outputNetLabelPlacements
|
|
10819
10933
|
}
|
|
10820
10934
|
];
|
|
@@ -10826,7 +10940,9 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
|
|
|
10826
10940
|
(instance) => [
|
|
10827
10941
|
{
|
|
10828
10942
|
inputProblem: instance.inputProblem,
|
|
10829
|
-
traces:
|
|
10943
|
+
traces: Object.values(
|
|
10944
|
+
instance.postLabelTraceOverlapShiftSolver.correctedTraceMap
|
|
10945
|
+
),
|
|
10830
10946
|
netLabelPlacements: instance.railNetLabelCornerPlacementSolver.outputNetLabelPlacements
|
|
10831
10947
|
}
|
|
10832
10948
|
]
|
|
@@ -10837,7 +10953,9 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
|
|
|
10837
10953
|
(instance) => [
|
|
10838
10954
|
{
|
|
10839
10955
|
inputProblem: instance.inputProblem,
|
|
10840
|
-
traces:
|
|
10956
|
+
traces: Object.values(
|
|
10957
|
+
instance.postLabelTraceOverlapShiftSolver.correctedTraceMap
|
|
10958
|
+
),
|
|
10841
10959
|
netLabelPlacements: instance.traceAnchoredNetLabelOverlapSolver.outputNetLabelPlacements
|
|
10842
10960
|
}
|
|
10843
10961
|
]
|
|
@@ -13,6 +13,7 @@ import { visualizeInputProblem } from "../SchematicTracePipelineSolver/visualize
|
|
|
13
13
|
import type { SolvedTracePath } from "../SchematicTraceLinesSolver/SchematicTraceLinesSolver"
|
|
14
14
|
import type { ConnectivityMap } from "connectivity-map"
|
|
15
15
|
import { arePinsInDifferentSchematicSections } from "../../utils/arePinsInDifferentSchematicSections"
|
|
16
|
+
import { isLabeledPeripheralConnection } from "../MspConnectionPairSolver/isLabeledPeripheralConnection"
|
|
16
17
|
|
|
17
18
|
const NEAREST_NEIGHBOR_COUNT = 3
|
|
18
19
|
|
|
@@ -28,6 +29,8 @@ export class LongDistancePairSolver extends BaseSolver {
|
|
|
28
29
|
private currentCandidatePair:
|
|
29
30
|
| [InputPin & { chipId: string }, InputPin & { chipId: string }]
|
|
30
31
|
| null = null
|
|
32
|
+
private queuedFailedConnectionPairs: MspConnectionPair[] = []
|
|
33
|
+
private currentFailedConnectionPair: MspConnectionPair | null = null
|
|
31
34
|
private subSolver: SchematicTraceSingleLineSolver2 | null = null
|
|
32
35
|
private chipMap: Record<string, InputChip> = {}
|
|
33
36
|
private inputProblem: InputProblem
|
|
@@ -40,6 +43,7 @@ export class LongDistancePairSolver extends BaseSolver {
|
|
|
40
43
|
inputProblem: InputProblem
|
|
41
44
|
alreadySolvedTraces: SolvedTracePath[]
|
|
42
45
|
primaryMspConnectionPairs: MspConnectionPair[]
|
|
46
|
+
failedConnectionPairs: MspConnectionPair[]
|
|
43
47
|
},
|
|
44
48
|
) {
|
|
45
49
|
super()
|
|
@@ -66,6 +70,16 @@ export class LongDistancePairSolver extends BaseSolver {
|
|
|
66
70
|
pinMap.set(pin.pinId, { ...pin, chipId: chip.chipId })
|
|
67
71
|
}
|
|
68
72
|
}
|
|
73
|
+
// Retry failed MSP pairs with their existing identity before creating
|
|
74
|
+
// new nearest-neighbor candidates.
|
|
75
|
+
this.queuedFailedConnectionPairs = this.params.failedConnectionPairs.filter(
|
|
76
|
+
(connectionPair) =>
|
|
77
|
+
isLabeledPeripheralConnection({
|
|
78
|
+
inputProblem: this.inputProblem,
|
|
79
|
+
chipMap: this.chipMap,
|
|
80
|
+
pins: connectionPair.pins,
|
|
81
|
+
}),
|
|
82
|
+
)
|
|
69
83
|
|
|
70
84
|
// 2. Generate candidate pairs using N-Nearest-Neighbors approach
|
|
71
85
|
const candidatePairs: Array<
|
|
@@ -129,9 +143,36 @@ export class LongDistancePairSolver extends BaseSolver {
|
|
|
129
143
|
return this.params
|
|
130
144
|
}
|
|
131
145
|
|
|
146
|
+
private acceptFailedConnectionPair(tracePath: SolvedTracePath["tracePath"]) {
|
|
147
|
+
const connectionPair = this.currentFailedConnectionPair
|
|
148
|
+
if (!connectionPair) return
|
|
149
|
+
|
|
150
|
+
// Reuse the original pair so downstream solvers retain its connectivity
|
|
151
|
+
// IDs, user net ID, pins, and MSP pair ID.
|
|
152
|
+
const solvedTrace: SolvedTracePath = {
|
|
153
|
+
...connectionPair,
|
|
154
|
+
tracePath,
|
|
155
|
+
mspConnectionPairIds: [connectionPair.mspPairId],
|
|
156
|
+
pinIds: connectionPair.pins.map((pin) => pin.pinId),
|
|
157
|
+
}
|
|
158
|
+
this.solvedLongDistanceTraces.push(solvedTrace)
|
|
159
|
+
this.allSolvedTraces.push(solvedTrace)
|
|
160
|
+
for (const pin of connectionPair.pins) {
|
|
161
|
+
this.newlyConnectedPinIds.add(pin.pinId)
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
132
165
|
override _step() {
|
|
133
166
|
// 1. Check if a sub-solver has finished and process its result
|
|
134
|
-
if (this.subSolver?.solved) {
|
|
167
|
+
if (this.subSolver?.solved && this.currentFailedConnectionPair) {
|
|
168
|
+
const tracePath = this.subSolver.solvedTracePath
|
|
169
|
+
if (tracePath) {
|
|
170
|
+
this.acceptFailedConnectionPair(tracePath)
|
|
171
|
+
}
|
|
172
|
+
this.subSolver = null
|
|
173
|
+
this.currentCandidatePair = null
|
|
174
|
+
this.currentFailedConnectionPair = null
|
|
175
|
+
} else if (this.subSolver?.solved) {
|
|
135
176
|
const newTracePath = this.subSolver.solvedTracePath
|
|
136
177
|
if (newTracePath && this.currentCandidatePair) {
|
|
137
178
|
const isTraceClear = !doesTraceOverlapWithExistingTraces(
|
|
@@ -166,6 +207,7 @@ export class LongDistancePairSolver extends BaseSolver {
|
|
|
166
207
|
} else if (this.subSolver?.failed) {
|
|
167
208
|
this.subSolver = null
|
|
168
209
|
this.currentCandidatePair = null
|
|
210
|
+
this.currentFailedConnectionPair = null
|
|
169
211
|
}
|
|
170
212
|
|
|
171
213
|
// 2. If a sub-solver is already running, let it continue
|
|
@@ -174,6 +216,18 @@ export class LongDistancePairSolver extends BaseSolver {
|
|
|
174
216
|
return
|
|
175
217
|
}
|
|
176
218
|
|
|
219
|
+
const failedConnectionPair = this.queuedFailedConnectionPairs.shift()
|
|
220
|
+
if (failedConnectionPair) {
|
|
221
|
+
this.currentFailedConnectionPair = failedConnectionPair
|
|
222
|
+
this.currentCandidatePair = failedConnectionPair.pins
|
|
223
|
+
this.subSolver = new SchematicTraceSingleLineSolver2({
|
|
224
|
+
inputProblem: this.params.inputProblem,
|
|
225
|
+
pins: failedConnectionPair.pins,
|
|
226
|
+
chipMap: this.chipMap,
|
|
227
|
+
})
|
|
228
|
+
return
|
|
229
|
+
}
|
|
230
|
+
|
|
177
231
|
// 3. Find the next valid candidate pair and start a new sub-solver
|
|
178
232
|
while (this.queuedCandidatePairs.length > 0) {
|
|
179
233
|
const nextPair = this.queuedCandidatePairs.shift()!
|
|
@@ -14,6 +14,7 @@ import { visualizeInputProblem } from "../SchematicTracePipelineSolver/visualize
|
|
|
14
14
|
import { doesPairCrossRestrictedCenterLines } from "./doesPairCrossRestrictedCenterLines"
|
|
15
15
|
import { getConnectivityMapsFromInputProblem } from "./getConnectivityMapFromInputProblem"
|
|
16
16
|
import { getOrthogonalMinimumSpanningTree } from "./getMspConnectionPairsFromPins"
|
|
17
|
+
import { isLabeledPeripheralConnection } from "./isLabeledPeripheralConnection"
|
|
17
18
|
|
|
18
19
|
export type MspConnectionPairId = string
|
|
19
20
|
export const DEFAULT_MAX_MSP_PAIR_DISTANCE = 1
|
|
@@ -116,10 +117,18 @@ export class MspConnectionPairSolver extends BaseSolver {
|
|
|
116
117
|
const pinPairKey = getPinPairKey([pin1!, pin2!])
|
|
117
118
|
// Explicit source traces are classified by straight-line distance when
|
|
118
119
|
// their input is created; named nets retain the orthogonal route metric.
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
120
|
+
let pairDistance = Math.abs(p1.x - p2.x) + Math.abs(p1.y - p2.y)
|
|
121
|
+
if (this.directConnectionPinPairKeys.has(pinPairKey)) {
|
|
122
|
+
pairDistance = distance(p1, p2)
|
|
123
|
+
}
|
|
124
|
+
// Labeled one-pin peripherals need a real trace even when they are far
|
|
125
|
+
// apart; skipping the MSP pair would leave only the fallback path.
|
|
126
|
+
const isLabeledPeripheral = isLabeledPeripheralConnection({
|
|
127
|
+
inputProblem: this.inputProblem,
|
|
128
|
+
chipMap: this.chipMap,
|
|
129
|
+
pins: [p1, p2],
|
|
130
|
+
})
|
|
131
|
+
if (pairDistance > this.maxMspPairDistance && !isLabeledPeripheral) {
|
|
123
132
|
// Too far apart; skip creating an MSP pair for this net
|
|
124
133
|
return
|
|
125
134
|
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { InputChip, InputPin, InputProblem } from "lib/types/InputProblem"
|
|
2
|
+
import { getPinDirection } from "../SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/getPinDirection"
|
|
3
|
+
|
|
4
|
+
type PinWithChipId = InputPin & { chipId: string }
|
|
5
|
+
|
|
6
|
+
export const isLabeledPeripheralConnection = ({
|
|
7
|
+
inputProblem,
|
|
8
|
+
chipMap,
|
|
9
|
+
pins,
|
|
10
|
+
}: {
|
|
11
|
+
inputProblem: InputProblem
|
|
12
|
+
chipMap: Record<string, InputChip>
|
|
13
|
+
pins: [PinWithChipId, PinWithChipId]
|
|
14
|
+
}): boolean => {
|
|
15
|
+
const [firstPin, secondPin] = pins
|
|
16
|
+
const directConnection = inputProblem.directConnections.find(
|
|
17
|
+
(connection) =>
|
|
18
|
+
connection.pinIds.includes(firstPin.pinId) &&
|
|
19
|
+
connection.pinIds.includes(secondPin.pinId),
|
|
20
|
+
)
|
|
21
|
+
if (directConnection?.netLabelWidth === undefined) return false
|
|
22
|
+
|
|
23
|
+
const firstChip = chipMap[firstPin.chipId]
|
|
24
|
+
const secondChip = chipMap[secondPin.chipId]
|
|
25
|
+
if (!firstChip || !secondChip) return false
|
|
26
|
+
|
|
27
|
+
const hasSinglePinPeripheral =
|
|
28
|
+
firstChip.pins.length === 1 || secondChip.pins.length === 1
|
|
29
|
+
if (!hasSinglePinPeripheral) return false
|
|
30
|
+
|
|
31
|
+
let firstFacingDirection = firstPin._facingDirection
|
|
32
|
+
if (!firstFacingDirection) {
|
|
33
|
+
firstFacingDirection = getPinDirection(firstPin, firstChip)
|
|
34
|
+
}
|
|
35
|
+
let secondFacingDirection = secondPin._facingDirection
|
|
36
|
+
if (!secondFacingDirection) {
|
|
37
|
+
secondFacingDirection = getPinDirection(secondPin, secondChip)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
(firstFacingDirection === "x-" && secondFacingDirection === "x+") ||
|
|
42
|
+
(firstFacingDirection === "x+" && secondFacingDirection === "x-")
|
|
43
|
+
)
|
|
44
|
+
}
|
|
@@ -79,6 +79,7 @@ export class SchematicTracePipelineSolver extends BaseSolver {
|
|
|
79
79
|
traceCleanupSolver?: TraceCleanupSolver
|
|
80
80
|
example28Solver?: Example28Solver
|
|
81
81
|
availableNetOrientationSolver?: AvailableNetOrientationSolver
|
|
82
|
+
postLabelTraceOverlapShiftSolver?: TraceOverlapShiftSolver
|
|
82
83
|
railNetLabelCornerPlacementSolver?: RailNetLabelCornerPlacementSolver
|
|
83
84
|
traceAnchoredNetLabelOverlapSolver?: TraceAnchoredNetLabelOverlapSolver
|
|
84
85
|
preAlignmentNetLabelTraceCollisionSolver?: NetLabelTraceCollisionSolver
|
|
@@ -139,6 +140,8 @@ export class SchematicTracePipelineSolver extends BaseSolver {
|
|
|
139
140
|
instance.mspConnectionPairSolver!.mspConnectionPairs,
|
|
140
141
|
alreadySolvedTraces:
|
|
141
142
|
instance.schematicTraceLinesSolver!.solvedTracePaths,
|
|
143
|
+
failedConnectionPairs:
|
|
144
|
+
instance.schematicTraceLinesSolver!.failedConnectionPairs,
|
|
142
145
|
},
|
|
143
146
|
],
|
|
144
147
|
{
|
|
@@ -280,6 +283,24 @@ export class SchematicTracePipelineSolver extends BaseSolver {
|
|
|
280
283
|
},
|
|
281
284
|
],
|
|
282
285
|
),
|
|
286
|
+
definePipelineStep(
|
|
287
|
+
"postLabelTraceOverlapShiftSolver",
|
|
288
|
+
TraceOverlapShiftSolver,
|
|
289
|
+
(instance) => [
|
|
290
|
+
{
|
|
291
|
+
inputProblem: instance.inputProblem,
|
|
292
|
+
inputTracePaths: instance.availableNetOrientationSolver!.traces,
|
|
293
|
+
globalConnMap: instance.mspConnectionPairSolver!.globalConnMap,
|
|
294
|
+
traceIdsToShift: new Set(
|
|
295
|
+
instance
|
|
296
|
+
.availableNetOrientationSolver!.traces.filter((trace) =>
|
|
297
|
+
trace.mspPairId.startsWith("available-net-orientation-"),
|
|
298
|
+
)
|
|
299
|
+
.map((trace) => trace.mspPairId),
|
|
300
|
+
),
|
|
301
|
+
},
|
|
302
|
+
],
|
|
303
|
+
),
|
|
283
304
|
definePipelineStep(
|
|
284
305
|
"railNetLabelCornerPlacementSolver",
|
|
285
306
|
RailNetLabelCornerPlacementSolver,
|
|
@@ -287,7 +308,9 @@ export class SchematicTracePipelineSolver extends BaseSolver {
|
|
|
287
308
|
return [
|
|
288
309
|
{
|
|
289
310
|
inputProblem: instance.inputProblem,
|
|
290
|
-
traces:
|
|
311
|
+
traces: Object.values(
|
|
312
|
+
instance.postLabelTraceOverlapShiftSolver!.correctedTraceMap,
|
|
313
|
+
),
|
|
291
314
|
netLabelPlacements:
|
|
292
315
|
instance.availableNetOrientationSolver!.outputNetLabelPlacements,
|
|
293
316
|
},
|
|
@@ -300,7 +323,9 @@ export class SchematicTracePipelineSolver extends BaseSolver {
|
|
|
300
323
|
(instance) => [
|
|
301
324
|
{
|
|
302
325
|
inputProblem: instance.inputProblem,
|
|
303
|
-
traces:
|
|
326
|
+
traces: Object.values(
|
|
327
|
+
instance.postLabelTraceOverlapShiftSolver!.correctedTraceMap,
|
|
328
|
+
),
|
|
304
329
|
netLabelPlacements:
|
|
305
330
|
instance.railNetLabelCornerPlacementSolver!
|
|
306
331
|
.outputNetLabelPlacements,
|
|
@@ -313,7 +338,9 @@ export class SchematicTracePipelineSolver extends BaseSolver {
|
|
|
313
338
|
(instance) => [
|
|
314
339
|
{
|
|
315
340
|
inputProblem: instance.inputProblem,
|
|
316
|
-
traces:
|
|
341
|
+
traces: Object.values(
|
|
342
|
+
instance.postLabelTraceOverlapShiftSolver!.correctedTraceMap,
|
|
343
|
+
),
|
|
317
344
|
netLabelPlacements:
|
|
318
345
|
instance.traceAnchoredNetLabelOverlapSolver!
|
|
319
346
|
.outputNetLabelPlacements,
|
package/lib/solvers/TraceOverlapShiftSolver/TraceOverlapIssueSolver/TraceOverlapIssueSolver.ts
CHANGED
|
@@ -26,6 +26,7 @@ export class TraceOverlapIssueSolver extends BaseSolver {
|
|
|
26
26
|
overlappingTraceSegments: OverlappingTraceSegmentLocator[]
|
|
27
27
|
interactionKind: TraceInteractionKind
|
|
28
28
|
traceNetIslands: Record<ConnNetId, Array<SolvedTracePath>>
|
|
29
|
+
traceIdsToShift?: Set<MspConnectionPairId>
|
|
29
30
|
obstacleRects: ReturnType<typeof getObstacleRects>
|
|
30
31
|
|
|
31
32
|
SHIFT_DISTANCE = 0.1
|
|
@@ -37,11 +38,13 @@ export class TraceOverlapIssueSolver extends BaseSolver {
|
|
|
37
38
|
overlappingTraceSegments: OverlappingTraceSegmentLocator[]
|
|
38
39
|
interactionKind: TraceInteractionKind
|
|
39
40
|
traceNetIslands: Record<ConnNetId, Array<SolvedTracePath>>
|
|
41
|
+
traceIdsToShift?: Set<MspConnectionPairId>
|
|
40
42
|
}) {
|
|
41
43
|
super()
|
|
42
44
|
this.overlappingTraceSegments = params.overlappingTraceSegments
|
|
43
45
|
this.interactionKind = params.interactionKind
|
|
44
46
|
this.traceNetIslands = params.traceNetIslands
|
|
47
|
+
this.traceIdsToShift = params.traceIdsToShift
|
|
45
48
|
this.obstacleRects = getObstacleRects(params.inputProblem)
|
|
46
49
|
|
|
47
50
|
// Only add the relevant traces to the correctedTraceMap
|
|
@@ -76,7 +79,14 @@ export class TraceOverlapIssueSolver extends BaseSolver {
|
|
|
76
79
|
})
|
|
77
80
|
|
|
78
81
|
const groupShouldStayInPlace = this.overlappingTraceSegments.map(
|
|
79
|
-
|
|
82
|
+
(group) => {
|
|
83
|
+
if (!this.traceIdsToShift) return containsStraightPinToPinTrace(group)
|
|
84
|
+
return group.pathsWithOverlap.every(({ solvedTracePathIndex }) => {
|
|
85
|
+
const path =
|
|
86
|
+
this.traceNetIslands[group.connNetId][solvedTracePathIndex]!
|
|
87
|
+
return !this.traceIdsToShift!.has(path.mspPairId)
|
|
88
|
+
})
|
|
89
|
+
},
|
|
80
90
|
)
|
|
81
91
|
const someGroupCanShift = groupShouldStayInPlace.some(
|
|
82
92
|
(shouldStay) => !shouldStay,
|
|
@@ -34,6 +34,7 @@ export class TraceOverlapShiftSolver extends BaseSolver {
|
|
|
34
34
|
inputProblem: InputProblem
|
|
35
35
|
inputTracePaths: Array<SolvedTracePath>
|
|
36
36
|
globalConnMap: ConnectivityMap
|
|
37
|
+
traceIdsToShift?: Set<MspConnectionPairId>
|
|
37
38
|
|
|
38
39
|
declare activeSubSolver: TraceOverlapIssueSolver | null
|
|
39
40
|
|
|
@@ -50,11 +51,13 @@ export class TraceOverlapShiftSolver extends BaseSolver {
|
|
|
50
51
|
inputProblem: InputProblem
|
|
51
52
|
inputTracePaths: Array<SolvedTracePath>
|
|
52
53
|
globalConnMap: ConnectivityMap
|
|
54
|
+
traceIdsToShift?: Set<MspConnectionPairId>
|
|
53
55
|
}) {
|
|
54
56
|
super()
|
|
55
57
|
this.inputProblem = params.inputProblem
|
|
56
58
|
this.inputTracePaths = params.inputTracePaths
|
|
57
59
|
this.globalConnMap = params.globalConnMap
|
|
60
|
+
this.traceIdsToShift = params.traceIdsToShift
|
|
58
61
|
|
|
59
62
|
for (const tracePath of this.inputTracePaths) {
|
|
60
63
|
const { mspPairId } = tracePath
|
|
@@ -71,6 +74,7 @@ export class TraceOverlapShiftSolver extends BaseSolver {
|
|
|
71
74
|
inputProblem: this.inputProblem,
|
|
72
75
|
inputTracePaths: this.inputTracePaths,
|
|
73
76
|
globalConnMap: this.globalConnMap,
|
|
77
|
+
traceIdsToShift: this.traceIdsToShift,
|
|
74
78
|
}
|
|
75
79
|
}
|
|
76
80
|
|
|
@@ -211,6 +215,13 @@ export class TraceOverlapShiftSolver extends BaseSolver {
|
|
|
211
215
|
|
|
212
216
|
for (let pb = 0; pb < pathsB.length; pb++) {
|
|
213
217
|
const pathB = pathsB[pb]!
|
|
218
|
+
if (
|
|
219
|
+
this.traceIdsToShift &&
|
|
220
|
+
!this.traceIdsToShift.has(pathA.mspPairId) &&
|
|
221
|
+
!this.traceIdsToShift.has(pathB.mspPairId)
|
|
222
|
+
) {
|
|
223
|
+
continue
|
|
224
|
+
}
|
|
214
225
|
const ptsB = pathB.tracePath
|
|
215
226
|
for (let sb = 0; sb < ptsB.length - 1; sb++) {
|
|
216
227
|
const b1 = ptsB[sb]!
|
|
@@ -392,6 +403,7 @@ export class TraceOverlapShiftSolver extends BaseSolver {
|
|
|
392
403
|
interactionKind,
|
|
393
404
|
overlappingTraceSegments,
|
|
394
405
|
traceNetIslands: this.traceNetIslands,
|
|
406
|
+
traceIdsToShift: this.traceIdsToShift,
|
|
395
407
|
})
|
|
396
408
|
}
|
|
397
409
|
|