@tscircuit/schematic-trace-solver 0.0.170 → 0.0.172
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.js +52 -14
- package/lib/solvers/AvailableNetOrientationSolver/AvailableNetOrientationSolver.ts +6 -1
- package/lib/solvers/NetLabelToTraceSolver/NetLabelToTraceSolver.ts +68 -14
- package/package.json +1 -1
- package/tests/bug-reports/bug-report-20260707T140410Z/__snapshots__/bug-report-20260707T140410Z.snap.svg +10 -10
- package/tests/bug-reports/bug-report-20260825T103621Z/__snapshots__/bug-report-20260825T103621Z.snap.svg +10 -10
- package/tests/bug-reports/bug-report-20260826T072956Z/__snapshots__/bug-report-20260826T072956Z.snap.svg +10 -10
- package/tests/repros/__snapshots__/repro-battery-management-bq24073.snap.svg +191 -0
- package/tests/repros/__snapshots__/repro-hdc3020-ground-label-alignment.snap.svg +110 -0
- package/tests/repros/__snapshots__/repro-powerbank-3v-system-power.snap.svg +11 -29
- package/tests/repros/__snapshots__/repro-tida-01389-hbridge.snap.svg +216 -0
- package/tests/repros/__snapshots__/repro-tmp1075-ground-label-overlap.snap.svg +166 -0
- package/tests/repros/assets/repro-battery-management-bq24073.input.json +721 -0
- package/tests/repros/assets/repro-hdc3020-ground-label-alignment.input.json +282 -0
- package/tests/repros/assets/repro-tida-01389-hbridge.input.json +780 -0
- package/tests/repros/assets/repro-tmp1075-ground-label-overlap.input.json +305 -0
- package/tests/repros/repro-battery-management-bq24073.test.ts +62 -0
- package/tests/repros/repro-hdc3020-ground-label-alignment.test.ts +25 -0
- package/tests/repros/repro-tida-01389-hbridge.test.ts +23 -0
- package/tests/repros/repro-tmp1075-ground-label-overlap.test.ts +18 -0
package/dist/index.js
CHANGED
|
@@ -10049,7 +10049,9 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
10049
10049
|
changed = true;
|
|
10050
10050
|
}
|
|
10051
10051
|
}
|
|
10052
|
-
if (connectedPinIds.size
|
|
10052
|
+
if (connectedPinIds.size < 3 || connectedPinIds.size === 3 && !this.hasPortOnlyLabelOnSameNet(label)) {
|
|
10053
|
+
return null;
|
|
10054
|
+
}
|
|
10053
10055
|
const pins = [...connectedPinIds].map((pinId) => this.pinMap[pinId]);
|
|
10054
10056
|
const direction = dir(orientation);
|
|
10055
10057
|
const pinX = pins[0].x;
|
|
@@ -15623,24 +15625,42 @@ var NetLabelToTraceSolver = class extends BaseSolver {
|
|
|
15623
15625
|
}
|
|
15624
15626
|
}
|
|
15625
15627
|
}
|
|
15626
|
-
candidates.push(
|
|
15628
|
+
candidates.push(
|
|
15629
|
+
...this.buildRoutedComponentCandidates(groundGlobalConnNetIds)
|
|
15630
|
+
);
|
|
15627
15631
|
candidates.sort(
|
|
15628
15632
|
(first, second) => first.perpendicularOffset - second.perpendicularOffset || first.routeDistance - second.routeDistance || first.key.localeCompare(second.key)
|
|
15629
15633
|
);
|
|
15630
15634
|
return candidates;
|
|
15631
15635
|
}
|
|
15632
|
-
buildRoutedComponentCandidates() {
|
|
15636
|
+
buildRoutedComponentCandidates(groundGlobalConnNetIds) {
|
|
15633
15637
|
const { netConnMap } = getConnectivityMapsFromInputProblem(
|
|
15634
15638
|
this.inputProblem
|
|
15635
15639
|
);
|
|
15636
15640
|
const candidates = [];
|
|
15637
|
-
|
|
15638
|
-
|
|
15641
|
+
const directConnections = new Set(
|
|
15642
|
+
this.inputProblem.directConnections
|
|
15643
|
+
);
|
|
15644
|
+
for (const connection of [
|
|
15645
|
+
...this.inputProblem.netConnections.filter(
|
|
15646
|
+
(connection2) => connection2.pinIds.length > 2 && connection2.isGround === false
|
|
15647
|
+
),
|
|
15648
|
+
...this.inputProblem.directConnections
|
|
15649
|
+
]) {
|
|
15650
|
+
const isDirectConnection = directConnections.has(connection);
|
|
15651
|
+
const globalConnNetId = netConnMap.getNetConnectedToId(
|
|
15652
|
+
connection.pinIds[0]
|
|
15653
|
+
);
|
|
15654
|
+
if (!globalConnNetId || isDirectConnection && groundGlobalConnNetIds.has(globalConnNetId))
|
|
15639
15655
|
continue;
|
|
15640
|
-
|
|
15641
|
-
if (
|
|
15656
|
+
let connectionPinIds = connection.pinIds;
|
|
15657
|
+
if (isDirectConnection) {
|
|
15658
|
+
connectionPinIds = [...this.pinMap.keys()].filter(
|
|
15659
|
+
(pinId) => netConnMap.getNetConnectedToId(pinId) === globalConnNetId
|
|
15660
|
+
);
|
|
15661
|
+
}
|
|
15642
15662
|
const traceConnectedPinComponents = getTraceConnectedPinComponents({
|
|
15643
|
-
pinIds:
|
|
15663
|
+
pinIds: connectionPinIds,
|
|
15644
15664
|
traces: this.outputTraces.filter(
|
|
15645
15665
|
(trace) => trace.globalConnNetId === globalConnNetId
|
|
15646
15666
|
)
|
|
@@ -15649,6 +15669,8 @@ var NetLabelToTraceSolver = class extends BaseSolver {
|
|
|
15649
15669
|
for (let secondIndex = firstIndex + 1; secondIndex < traceConnectedPinComponents.length; secondIndex++) {
|
|
15650
15670
|
const firstComponent = traceConnectedPinComponents[firstIndex];
|
|
15651
15671
|
const secondComponent = traceConnectedPinComponents[secondIndex];
|
|
15672
|
+
if (isDirectConnection && !(firstComponent.pinIds.includes(connection.pinIds[0]) && secondComponent.pinIds.includes(connection.pinIds[1]) || firstComponent.pinIds.includes(connection.pinIds[1]) && secondComponent.pinIds.includes(connection.pinIds[0])))
|
|
15673
|
+
continue;
|
|
15652
15674
|
const firstLabel = this.input.netLabelPlacements.find(
|
|
15653
15675
|
(label) => label.globalConnNetId === globalConnNetId && label.mspConnectionPairIds.length > 0 && label.pinIds.some(
|
|
15654
15676
|
(pinId) => firstComponent.pinIds.includes(pinId)
|
|
@@ -15668,11 +15690,23 @@ var NetLabelToTraceSolver = class extends BaseSolver {
|
|
|
15668
15690
|
continue;
|
|
15669
15691
|
}
|
|
15670
15692
|
let bestCandidate;
|
|
15671
|
-
|
|
15672
|
-
|
|
15693
|
+
let firstPinIds = firstComponent.pinIds;
|
|
15694
|
+
let secondPinIds = secondComponent.pinIds;
|
|
15695
|
+
if (isDirectConnection) {
|
|
15696
|
+
firstPinIds = connection.pinIds.filter(
|
|
15697
|
+
(pinId) => firstComponent.pinIds.includes(pinId)
|
|
15698
|
+
);
|
|
15699
|
+
secondPinIds = connection.pinIds.filter(
|
|
15700
|
+
(pinId) => secondComponent.pinIds.includes(pinId)
|
|
15701
|
+
);
|
|
15702
|
+
}
|
|
15703
|
+
for (const firstPinId of firstPinIds) {
|
|
15704
|
+
for (const secondPinId of secondPinIds) {
|
|
15673
15705
|
const firstPin = this.pinMap.get(firstPinId);
|
|
15674
15706
|
const secondPin = this.pinMap.get(secondPinId);
|
|
15675
15707
|
if (!firstPin || !secondPin) continue;
|
|
15708
|
+
if (isDirectConnection && firstPin.chipId === secondPin.chipId)
|
|
15709
|
+
continue;
|
|
15676
15710
|
const perpendicularOffset = getPerpendicularOffset(
|
|
15677
15711
|
firstPin,
|
|
15678
15712
|
secondPin
|
|
@@ -15691,6 +15725,10 @@ var NetLabelToTraceSolver = class extends BaseSolver {
|
|
|
15691
15725
|
continue;
|
|
15692
15726
|
}
|
|
15693
15727
|
const routeDistance = Math.abs(firstPin.x - secondPin.x) + Math.abs(firstPin.y - secondPin.y);
|
|
15728
|
+
let recoveryMode = "routed_components";
|
|
15729
|
+
if (isDirectConnection) {
|
|
15730
|
+
recoveryMode = "routed_direct_connection";
|
|
15731
|
+
}
|
|
15694
15732
|
const candidate = {
|
|
15695
15733
|
firstLabel,
|
|
15696
15734
|
secondLabel,
|
|
@@ -15698,8 +15736,8 @@ var NetLabelToTraceSolver = class extends BaseSolver {
|
|
|
15698
15736
|
perpendicularOffset,
|
|
15699
15737
|
routeDistance,
|
|
15700
15738
|
key: getCanonicalPairKey(firstPin.pinId, secondPin.pinId),
|
|
15701
|
-
recoveryMode
|
|
15702
|
-
netConnectionPinIds:
|
|
15739
|
+
recoveryMode,
|
|
15740
|
+
netConnectionPinIds: connectionPinIds
|
|
15703
15741
|
};
|
|
15704
15742
|
if (!bestCandidate || candidate.routeDistance < bestCandidate.routeDistance || candidate.routeDistance === bestCandidate.routeDistance && (candidate.perpendicularOffset < bestCandidate.perpendicularOffset || candidate.perpendicularOffset === bestCandidate.perpendicularOffset && candidate.key.localeCompare(bestCandidate.key) < 0)) {
|
|
15705
15743
|
bestCandidate = candidate;
|
|
@@ -15738,7 +15776,7 @@ var NetLabelToTraceSolver = class extends BaseSolver {
|
|
|
15738
15776
|
(trace) => !this.isSupersededConnectorTrace(trace, candidate)
|
|
15739
15777
|
);
|
|
15740
15778
|
let collisionTraces = retainedTraces;
|
|
15741
|
-
if (candidate.recoveryMode
|
|
15779
|
+
if (candidate.recoveryMode !== "fallback_labels") {
|
|
15742
15780
|
collisionTraces = retainedTraces.filter(
|
|
15743
15781
|
(trace) => trace.globalConnNetId !== candidate.firstLabel.globalConnNetId
|
|
15744
15782
|
);
|
|
@@ -15764,7 +15802,7 @@ var NetLabelToTraceSolver = class extends BaseSolver {
|
|
|
15764
15802
|
pinIds: [firstPin.pinId, secondPin.pinId]
|
|
15765
15803
|
};
|
|
15766
15804
|
this.outputTraces = [...retainedTraces, recoveredTrace];
|
|
15767
|
-
if (candidate.recoveryMode
|
|
15805
|
+
if (candidate.recoveryMode !== "routed_components") {
|
|
15768
15806
|
this.outputNetLabelPlacements = this.outputNetLabelPlacements.filter(
|
|
15769
15807
|
(label) => label !== candidate.firstLabel && label !== candidate.secondLabel
|
|
15770
15808
|
);
|
|
@@ -1695,7 +1695,12 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
1695
1695
|
changed = true
|
|
1696
1696
|
}
|
|
1697
1697
|
}
|
|
1698
|
-
if (
|
|
1698
|
+
if (
|
|
1699
|
+
connectedPinIds.size < 3 ||
|
|
1700
|
+
(connectedPinIds.size === 3 && !this.hasPortOnlyLabelOnSameNet(label))
|
|
1701
|
+
) {
|
|
1702
|
+
return null
|
|
1703
|
+
}
|
|
1699
1704
|
|
|
1700
1705
|
const pins = [...connectedPinIds].map((pinId) => this.pinMap[pinId]!)
|
|
1701
1706
|
const direction = dir(orientation)
|
|
@@ -40,7 +40,10 @@ interface CandidatePair {
|
|
|
40
40
|
perpendicularOffset: number
|
|
41
41
|
routeDistance: number
|
|
42
42
|
key: string
|
|
43
|
-
recoveryMode:
|
|
43
|
+
recoveryMode:
|
|
44
|
+
| "fallback_labels"
|
|
45
|
+
| "routed_components"
|
|
46
|
+
| "routed_direct_connection"
|
|
44
47
|
netConnectionPinIds?: PinId[]
|
|
45
48
|
}
|
|
46
49
|
|
|
@@ -260,7 +263,9 @@ export class NetLabelToTraceSolver extends BaseSolver {
|
|
|
260
263
|
}
|
|
261
264
|
}
|
|
262
265
|
|
|
263
|
-
candidates.push(
|
|
266
|
+
candidates.push(
|
|
267
|
+
...this.buildRoutedComponentCandidates(groundGlobalConnNetIds),
|
|
268
|
+
)
|
|
264
269
|
|
|
265
270
|
candidates.sort(
|
|
266
271
|
(first, second) =>
|
|
@@ -271,20 +276,42 @@ export class NetLabelToTraceSolver extends BaseSolver {
|
|
|
271
276
|
return candidates
|
|
272
277
|
}
|
|
273
278
|
|
|
274
|
-
private buildRoutedComponentCandidates(
|
|
279
|
+
private buildRoutedComponentCandidates(
|
|
280
|
+
groundGlobalConnNetIds: Set<GlobalConnNetId>,
|
|
281
|
+
) {
|
|
275
282
|
const { netConnMap } = getConnectivityMapsFromInputProblem(
|
|
276
283
|
this.inputProblem,
|
|
277
284
|
)
|
|
278
285
|
const candidates: CandidatePair[] = []
|
|
286
|
+
const directConnections = new Set<object>(
|
|
287
|
+
this.inputProblem.directConnections,
|
|
288
|
+
)
|
|
279
289
|
|
|
280
|
-
for (const connection of
|
|
281
|
-
|
|
290
|
+
for (const connection of [
|
|
291
|
+
...this.inputProblem.netConnections.filter(
|
|
292
|
+
(connection) =>
|
|
293
|
+
connection.pinIds.length > 2 && connection.isGround === false,
|
|
294
|
+
),
|
|
295
|
+
...this.inputProblem.directConnections,
|
|
296
|
+
]) {
|
|
297
|
+
const isDirectConnection = directConnections.has(connection)
|
|
298
|
+
const globalConnNetId = netConnMap.getNetConnectedToId(
|
|
299
|
+
connection.pinIds[0],
|
|
300
|
+
)
|
|
301
|
+
if (
|
|
302
|
+
!globalConnNetId ||
|
|
303
|
+
(isDirectConnection && groundGlobalConnNetIds.has(globalConnNetId))
|
|
304
|
+
)
|
|
282
305
|
continue
|
|
283
|
-
|
|
284
|
-
if (
|
|
306
|
+
let connectionPinIds: PinId[] = connection.pinIds
|
|
307
|
+
if (isDirectConnection) {
|
|
308
|
+
connectionPinIds = [...this.pinMap.keys()].filter(
|
|
309
|
+
(pinId) => netConnMap.getNetConnectedToId(pinId) === globalConnNetId,
|
|
310
|
+
)
|
|
311
|
+
}
|
|
285
312
|
|
|
286
313
|
const traceConnectedPinComponents = getTraceConnectedPinComponents({
|
|
287
|
-
pinIds:
|
|
314
|
+
pinIds: connectionPinIds,
|
|
288
315
|
traces: this.outputTraces.filter(
|
|
289
316
|
(trace) => trace.globalConnNetId === globalConnNetId,
|
|
290
317
|
),
|
|
@@ -302,6 +329,16 @@ export class NetLabelToTraceSolver extends BaseSolver {
|
|
|
302
329
|
) {
|
|
303
330
|
const firstComponent = traceConnectedPinComponents[firstIndex]!
|
|
304
331
|
const secondComponent = traceConnectedPinComponents[secondIndex]!
|
|
332
|
+
if (
|
|
333
|
+
isDirectConnection &&
|
|
334
|
+
!(
|
|
335
|
+
(firstComponent.pinIds.includes(connection.pinIds[0]) &&
|
|
336
|
+
secondComponent.pinIds.includes(connection.pinIds[1])) ||
|
|
337
|
+
(firstComponent.pinIds.includes(connection.pinIds[1]) &&
|
|
338
|
+
secondComponent.pinIds.includes(connection.pinIds[0]))
|
|
339
|
+
)
|
|
340
|
+
)
|
|
341
|
+
continue
|
|
305
342
|
const firstLabel = this.input.netLabelPlacements.find(
|
|
306
343
|
(label) =>
|
|
307
344
|
label.globalConnNetId === globalConnNetId &&
|
|
@@ -330,11 +367,23 @@ export class NetLabelToTraceSolver extends BaseSolver {
|
|
|
330
367
|
}
|
|
331
368
|
|
|
332
369
|
let bestCandidate: CandidatePair | undefined
|
|
333
|
-
|
|
334
|
-
|
|
370
|
+
let firstPinIds = firstComponent.pinIds
|
|
371
|
+
let secondPinIds = secondComponent.pinIds
|
|
372
|
+
if (isDirectConnection) {
|
|
373
|
+
firstPinIds = connection.pinIds.filter((pinId) =>
|
|
374
|
+
firstComponent.pinIds.includes(pinId),
|
|
375
|
+
)
|
|
376
|
+
secondPinIds = connection.pinIds.filter((pinId) =>
|
|
377
|
+
secondComponent.pinIds.includes(pinId),
|
|
378
|
+
)
|
|
379
|
+
}
|
|
380
|
+
for (const firstPinId of firstPinIds) {
|
|
381
|
+
for (const secondPinId of secondPinIds) {
|
|
335
382
|
const firstPin = this.pinMap.get(firstPinId)
|
|
336
383
|
const secondPin = this.pinMap.get(secondPinId)
|
|
337
384
|
if (!firstPin || !secondPin) continue
|
|
385
|
+
if (isDirectConnection && firstPin.chipId === secondPin.chipId)
|
|
386
|
+
continue
|
|
338
387
|
const perpendicularOffset = getPerpendicularOffset(
|
|
339
388
|
firstPin,
|
|
340
389
|
secondPin,
|
|
@@ -362,6 +411,11 @@ export class NetLabelToTraceSolver extends BaseSolver {
|
|
|
362
411
|
const routeDistance =
|
|
363
412
|
Math.abs(firstPin.x - secondPin.x) +
|
|
364
413
|
Math.abs(firstPin.y - secondPin.y)
|
|
414
|
+
let recoveryMode: CandidatePair["recoveryMode"] =
|
|
415
|
+
"routed_components"
|
|
416
|
+
if (isDirectConnection) {
|
|
417
|
+
recoveryMode = "routed_direct_connection"
|
|
418
|
+
}
|
|
365
419
|
const candidate: CandidatePair = {
|
|
366
420
|
firstLabel,
|
|
367
421
|
secondLabel,
|
|
@@ -369,8 +423,8 @@ export class NetLabelToTraceSolver extends BaseSolver {
|
|
|
369
423
|
perpendicularOffset,
|
|
370
424
|
routeDistance,
|
|
371
425
|
key: getCanonicalPairKey(firstPin.pinId, secondPin.pinId),
|
|
372
|
-
recoveryMode
|
|
373
|
-
netConnectionPinIds:
|
|
426
|
+
recoveryMode,
|
|
427
|
+
netConnectionPinIds: connectionPinIds,
|
|
374
428
|
}
|
|
375
429
|
if (
|
|
376
430
|
!bestCandidate ||
|
|
@@ -429,7 +483,7 @@ export class NetLabelToTraceSolver extends BaseSolver {
|
|
|
429
483
|
(trace) => !this.isSupersededConnectorTrace(trace, candidate),
|
|
430
484
|
)
|
|
431
485
|
let collisionTraces = retainedTraces
|
|
432
|
-
if (candidate.recoveryMode
|
|
486
|
+
if (candidate.recoveryMode !== "fallback_labels") {
|
|
433
487
|
collisionTraces = retainedTraces.filter(
|
|
434
488
|
(trace) =>
|
|
435
489
|
trace.globalConnNetId !== candidate.firstLabel.globalConnNetId,
|
|
@@ -466,7 +520,7 @@ export class NetLabelToTraceSolver extends BaseSolver {
|
|
|
466
520
|
}
|
|
467
521
|
|
|
468
522
|
this.outputTraces = [...retainedTraces, recoveredTrace]
|
|
469
|
-
if (candidate.recoveryMode
|
|
523
|
+
if (candidate.recoveryMode !== "routed_components") {
|
|
470
524
|
this.outputNetLabelPlacements = this.outputNetLabelPlacements.filter(
|
|
471
525
|
(label) =>
|
|
472
526
|
label !== candidate.firstLabel && label !== candidate.secondLabel,
|