@tscircuit/schematic-trace-solver 0.0.171 → 0.0.173
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 +76 -13
- package/lib/solvers/NetLabelToTraceSolver/NetLabelToTraceSolver.ts +68 -14
- package/lib/solvers/SameNetJunctionAlignmentSolver/placeGroundRailLabelsAtOuterEnd.ts +49 -0
- package/package.json +1 -1
- package/tests/repros/__snapshots__/repro-battery-management-bq24073.snap.svg +191 -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-tida010076-j4-ground-bus.snap.svg +1 -8
- package/tests/repros/__snapshots__/repro-tida010076-page02.snap.svg +2 -58
- 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-tida-01389-hbridge.input.json +780 -0
- package/tests/repros/assets/repro-tida010076-page02.input.json +2 -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-tida-01389-hbridge.test.ts +23 -0
- package/tests/repros/repro-tida010076-j4-ground-bus.test.ts +1 -0
- package/tests/repros/repro-tmp1075-ground-label-overlap.test.ts +30 -0
package/dist/index.js
CHANGED
|
@@ -13307,6 +13307,33 @@ var placeGroundRailLabelsAtOuterEnd = ({
|
|
|
13307
13307
|
}
|
|
13308
13308
|
}
|
|
13309
13309
|
}
|
|
13310
|
+
for (let index = 0; index < output.length; index++) {
|
|
13311
|
+
const label = output[index];
|
|
13312
|
+
if (label.orientation !== "y-") continue;
|
|
13313
|
+
const connection = inputProblem.netConnections.find(
|
|
13314
|
+
(candidate) => candidate.isGround && candidate.netId === label.netId && candidate.pinIds.length > 2
|
|
13315
|
+
);
|
|
13316
|
+
if (!connection) continue;
|
|
13317
|
+
const anchorPoint = traces.filter((trace) => trace.globalConnNetId === label.globalConnNetId).flatMap((trace) => trace.tracePath).filter(
|
|
13318
|
+
(point) => nearlyEqual(point.x, label.anchorPoint.x) && point.y < label.anchorPoint.y
|
|
13319
|
+
).sort((a, b) => a.y - b.y)[0];
|
|
13320
|
+
if (!anchorPoint) continue;
|
|
13321
|
+
const center = getCenterFromAnchor(
|
|
13322
|
+
anchorPoint,
|
|
13323
|
+
label.orientation,
|
|
13324
|
+
label.width,
|
|
13325
|
+
label.height
|
|
13326
|
+
);
|
|
13327
|
+
const bounds = getRectBounds(center, label.width, label.height);
|
|
13328
|
+
if (obstacles.some((obstacle) => rectsOverlap(bounds, obstacle)) || traceCrossesBoundsInterior(bounds, traceMap) || output.some(
|
|
13329
|
+
(other, otherIndex) => otherIndex !== index && rectsOverlap(
|
|
13330
|
+
bounds,
|
|
13331
|
+
getRectBounds(other.center, other.width, other.height)
|
|
13332
|
+
)
|
|
13333
|
+
))
|
|
13334
|
+
continue;
|
|
13335
|
+
output[index] = { ...label, anchorPoint, center };
|
|
13336
|
+
}
|
|
13310
13337
|
return output;
|
|
13311
13338
|
};
|
|
13312
13339
|
|
|
@@ -15625,24 +15652,42 @@ var NetLabelToTraceSolver = class extends BaseSolver {
|
|
|
15625
15652
|
}
|
|
15626
15653
|
}
|
|
15627
15654
|
}
|
|
15628
|
-
candidates.push(
|
|
15655
|
+
candidates.push(
|
|
15656
|
+
...this.buildRoutedComponentCandidates(groundGlobalConnNetIds)
|
|
15657
|
+
);
|
|
15629
15658
|
candidates.sort(
|
|
15630
15659
|
(first, second) => first.perpendicularOffset - second.perpendicularOffset || first.routeDistance - second.routeDistance || first.key.localeCompare(second.key)
|
|
15631
15660
|
);
|
|
15632
15661
|
return candidates;
|
|
15633
15662
|
}
|
|
15634
|
-
buildRoutedComponentCandidates() {
|
|
15663
|
+
buildRoutedComponentCandidates(groundGlobalConnNetIds) {
|
|
15635
15664
|
const { netConnMap } = getConnectivityMapsFromInputProblem(
|
|
15636
15665
|
this.inputProblem
|
|
15637
15666
|
);
|
|
15638
15667
|
const candidates = [];
|
|
15639
|
-
|
|
15640
|
-
|
|
15668
|
+
const directConnections = new Set(
|
|
15669
|
+
this.inputProblem.directConnections
|
|
15670
|
+
);
|
|
15671
|
+
for (const connection of [
|
|
15672
|
+
...this.inputProblem.netConnections.filter(
|
|
15673
|
+
(connection2) => connection2.pinIds.length > 2 && connection2.isGround === false
|
|
15674
|
+
),
|
|
15675
|
+
...this.inputProblem.directConnections
|
|
15676
|
+
]) {
|
|
15677
|
+
const isDirectConnection = directConnections.has(connection);
|
|
15678
|
+
const globalConnNetId = netConnMap.getNetConnectedToId(
|
|
15679
|
+
connection.pinIds[0]
|
|
15680
|
+
);
|
|
15681
|
+
if (!globalConnNetId || isDirectConnection && groundGlobalConnNetIds.has(globalConnNetId))
|
|
15641
15682
|
continue;
|
|
15642
|
-
|
|
15643
|
-
if (
|
|
15683
|
+
let connectionPinIds = connection.pinIds;
|
|
15684
|
+
if (isDirectConnection) {
|
|
15685
|
+
connectionPinIds = [...this.pinMap.keys()].filter(
|
|
15686
|
+
(pinId) => netConnMap.getNetConnectedToId(pinId) === globalConnNetId
|
|
15687
|
+
);
|
|
15688
|
+
}
|
|
15644
15689
|
const traceConnectedPinComponents = getTraceConnectedPinComponents({
|
|
15645
|
-
pinIds:
|
|
15690
|
+
pinIds: connectionPinIds,
|
|
15646
15691
|
traces: this.outputTraces.filter(
|
|
15647
15692
|
(trace) => trace.globalConnNetId === globalConnNetId
|
|
15648
15693
|
)
|
|
@@ -15651,6 +15696,8 @@ var NetLabelToTraceSolver = class extends BaseSolver {
|
|
|
15651
15696
|
for (let secondIndex = firstIndex + 1; secondIndex < traceConnectedPinComponents.length; secondIndex++) {
|
|
15652
15697
|
const firstComponent = traceConnectedPinComponents[firstIndex];
|
|
15653
15698
|
const secondComponent = traceConnectedPinComponents[secondIndex];
|
|
15699
|
+
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])))
|
|
15700
|
+
continue;
|
|
15654
15701
|
const firstLabel = this.input.netLabelPlacements.find(
|
|
15655
15702
|
(label) => label.globalConnNetId === globalConnNetId && label.mspConnectionPairIds.length > 0 && label.pinIds.some(
|
|
15656
15703
|
(pinId) => firstComponent.pinIds.includes(pinId)
|
|
@@ -15670,11 +15717,23 @@ var NetLabelToTraceSolver = class extends BaseSolver {
|
|
|
15670
15717
|
continue;
|
|
15671
15718
|
}
|
|
15672
15719
|
let bestCandidate;
|
|
15673
|
-
|
|
15674
|
-
|
|
15720
|
+
let firstPinIds = firstComponent.pinIds;
|
|
15721
|
+
let secondPinIds = secondComponent.pinIds;
|
|
15722
|
+
if (isDirectConnection) {
|
|
15723
|
+
firstPinIds = connection.pinIds.filter(
|
|
15724
|
+
(pinId) => firstComponent.pinIds.includes(pinId)
|
|
15725
|
+
);
|
|
15726
|
+
secondPinIds = connection.pinIds.filter(
|
|
15727
|
+
(pinId) => secondComponent.pinIds.includes(pinId)
|
|
15728
|
+
);
|
|
15729
|
+
}
|
|
15730
|
+
for (const firstPinId of firstPinIds) {
|
|
15731
|
+
for (const secondPinId of secondPinIds) {
|
|
15675
15732
|
const firstPin = this.pinMap.get(firstPinId);
|
|
15676
15733
|
const secondPin = this.pinMap.get(secondPinId);
|
|
15677
15734
|
if (!firstPin || !secondPin) continue;
|
|
15735
|
+
if (isDirectConnection && firstPin.chipId === secondPin.chipId)
|
|
15736
|
+
continue;
|
|
15678
15737
|
const perpendicularOffset = getPerpendicularOffset(
|
|
15679
15738
|
firstPin,
|
|
15680
15739
|
secondPin
|
|
@@ -15693,6 +15752,10 @@ var NetLabelToTraceSolver = class extends BaseSolver {
|
|
|
15693
15752
|
continue;
|
|
15694
15753
|
}
|
|
15695
15754
|
const routeDistance = Math.abs(firstPin.x - secondPin.x) + Math.abs(firstPin.y - secondPin.y);
|
|
15755
|
+
let recoveryMode = "routed_components";
|
|
15756
|
+
if (isDirectConnection) {
|
|
15757
|
+
recoveryMode = "routed_direct_connection";
|
|
15758
|
+
}
|
|
15696
15759
|
const candidate = {
|
|
15697
15760
|
firstLabel,
|
|
15698
15761
|
secondLabel,
|
|
@@ -15700,8 +15763,8 @@ var NetLabelToTraceSolver = class extends BaseSolver {
|
|
|
15700
15763
|
perpendicularOffset,
|
|
15701
15764
|
routeDistance,
|
|
15702
15765
|
key: getCanonicalPairKey(firstPin.pinId, secondPin.pinId),
|
|
15703
|
-
recoveryMode
|
|
15704
|
-
netConnectionPinIds:
|
|
15766
|
+
recoveryMode,
|
|
15767
|
+
netConnectionPinIds: connectionPinIds
|
|
15705
15768
|
};
|
|
15706
15769
|
if (!bestCandidate || candidate.routeDistance < bestCandidate.routeDistance || candidate.routeDistance === bestCandidate.routeDistance && (candidate.perpendicularOffset < bestCandidate.perpendicularOffset || candidate.perpendicularOffset === bestCandidate.perpendicularOffset && candidate.key.localeCompare(bestCandidate.key) < 0)) {
|
|
15707
15770
|
bestCandidate = candidate;
|
|
@@ -15740,7 +15803,7 @@ var NetLabelToTraceSolver = class extends BaseSolver {
|
|
|
15740
15803
|
(trace) => !this.isSupersededConnectorTrace(trace, candidate)
|
|
15741
15804
|
);
|
|
15742
15805
|
let collisionTraces = retainedTraces;
|
|
15743
|
-
if (candidate.recoveryMode
|
|
15806
|
+
if (candidate.recoveryMode !== "fallback_labels") {
|
|
15744
15807
|
collisionTraces = retainedTraces.filter(
|
|
15745
15808
|
(trace) => trace.globalConnNetId !== candidate.firstLabel.globalConnNetId
|
|
15746
15809
|
);
|
|
@@ -15766,7 +15829,7 @@ var NetLabelToTraceSolver = class extends BaseSolver {
|
|
|
15766
15829
|
pinIds: [firstPin.pinId, secondPin.pinId]
|
|
15767
15830
|
};
|
|
15768
15831
|
this.outputTraces = [...retainedTraces, recoveredTrace];
|
|
15769
|
-
if (candidate.recoveryMode
|
|
15832
|
+
if (candidate.recoveryMode !== "routed_components") {
|
|
15770
15833
|
this.outputNetLabelPlacements = this.outputNetLabelPlacements.filter(
|
|
15771
15834
|
(label) => label !== candidate.firstLabel && label !== candidate.secondLabel
|
|
15772
15835
|
);
|
|
@@ -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,
|
|
@@ -118,5 +118,54 @@ export const placeGroundRailLabelsAtOuterEnd = ({
|
|
|
118
118
|
}
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
|
+
|
|
122
|
+
for (let index = 0; index < output.length; index++) {
|
|
123
|
+
const label = output[index]!
|
|
124
|
+
if (label.orientation !== "y-") continue
|
|
125
|
+
// Use producer metadata so ground aliases do not require name matching.
|
|
126
|
+
const connection = inputProblem.netConnections.find(
|
|
127
|
+
(candidate) =>
|
|
128
|
+
candidate.isGround &&
|
|
129
|
+
candidate.netId === label.netId &&
|
|
130
|
+
candidate.pinIds.length > 2,
|
|
131
|
+
)
|
|
132
|
+
if (!connection) continue
|
|
133
|
+
|
|
134
|
+
// Reuse the lowest point on the existing column without changing the trace.
|
|
135
|
+
const anchorPoint = traces
|
|
136
|
+
.filter((trace) => trace.globalConnNetId === label.globalConnNetId)
|
|
137
|
+
.flatMap((trace) => trace.tracePath)
|
|
138
|
+
.filter(
|
|
139
|
+
(point) =>
|
|
140
|
+
nearlyEqual(point.x, label.anchorPoint.x) &&
|
|
141
|
+
point.y < label.anchorPoint.y,
|
|
142
|
+
)
|
|
143
|
+
.sort((a, b) => a.y - b.y)[0]
|
|
144
|
+
if (!anchorPoint) continue
|
|
145
|
+
|
|
146
|
+
const center = getCenterFromAnchor(
|
|
147
|
+
anchorPoint,
|
|
148
|
+
label.orientation,
|
|
149
|
+
label.width,
|
|
150
|
+
label.height,
|
|
151
|
+
)
|
|
152
|
+
const bounds = getRectBounds(center, label.width, label.height)
|
|
153
|
+
// Keep the original anchor when the ground symbol would collide.
|
|
154
|
+
if (
|
|
155
|
+
obstacles.some((obstacle) => rectsOverlap(bounds, obstacle)) ||
|
|
156
|
+
traceCrossesBoundsInterior(bounds, traceMap) ||
|
|
157
|
+
output.some(
|
|
158
|
+
(other, otherIndex) =>
|
|
159
|
+
otherIndex !== index &&
|
|
160
|
+
rectsOverlap(
|
|
161
|
+
bounds,
|
|
162
|
+
getRectBounds(other.center, other.width, other.height),
|
|
163
|
+
),
|
|
164
|
+
)
|
|
165
|
+
)
|
|
166
|
+
continue
|
|
167
|
+
|
|
168
|
+
output[index] = { ...label, anchorPoint, center }
|
|
169
|
+
}
|
|
121
170
|
return output
|
|
122
171
|
}
|