@tscircuit/schematic-trace-solver 0.0.81 → 0.0.82
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 +4 -1
- package/dist/index.js +89 -12
- package/lib/solvers/AvailableNetOrientationSolver/AvailableNetOrientationSolver.ts +126 -12
- package/lib/solvers/AvailableNetOrientationSolver/types.ts +5 -1
- package/package.json +1 -1
- package/tests/repros/rotated-components-rail-label.test.ts +68 -0
package/dist/index.d.ts
CHANGED
|
@@ -695,7 +695,7 @@ type CandidateLabel = {
|
|
|
695
695
|
height: number;
|
|
696
696
|
};
|
|
697
697
|
type CandidateStatus$2 = "valid" | "chip-collision" | "text-collision" | "trace-collision" | "trace-clearance-violation" | "netlabel-collision";
|
|
698
|
-
type CandidatePhase = "rotate" | "shift" | "lateral-shift";
|
|
698
|
+
type CandidatePhase = "rotate" | "trace-anchor" | "shift" | "lateral-shift";
|
|
699
699
|
type EvaluatedCandidate = CandidateLabel & {
|
|
700
700
|
status: CandidateStatus$2;
|
|
701
701
|
selected: boolean;
|
|
@@ -733,6 +733,8 @@ declare class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
733
733
|
private setCurrentLabel;
|
|
734
734
|
private findCorrectedCandidate;
|
|
735
735
|
private findValidRotatedCandidate;
|
|
736
|
+
private findValidTraceAnchorCandidate;
|
|
737
|
+
private getTraceAnchorCandidatePoints;
|
|
736
738
|
private getAvailableOrientations;
|
|
737
739
|
private findValidShiftedCandidate;
|
|
738
740
|
/**
|
|
@@ -757,6 +759,7 @@ declare class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
757
759
|
private getNetLabelWidth;
|
|
758
760
|
private getNetLabelHeight;
|
|
759
761
|
private getCandidateStatus;
|
|
762
|
+
private isAcceptableTraceAnchorChipCollision;
|
|
760
763
|
private getBoundsStatus;
|
|
761
764
|
private isTraceTooCloseToLabel;
|
|
762
765
|
private getLabelTraceClearanceBounds;
|
package/dist/index.js
CHANGED
|
@@ -6246,6 +6246,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
6246
6246
|
this.addConnectorTrace(label, candidate, labelIndex);
|
|
6247
6247
|
}
|
|
6248
6248
|
addConnectorTrace(label, candidate, labelIndex) {
|
|
6249
|
+
if (candidate.phase === "trace-anchor") return;
|
|
6249
6250
|
const tracePath = this.getCandidateConnectorTrace(label, candidate);
|
|
6250
6251
|
if (tracePath.length < 2) return;
|
|
6251
6252
|
const mspPairId = `available-net-orientation-${labelIndex}-${label.netId ?? label.globalConnNetId}`;
|
|
@@ -6280,6 +6281,12 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
6280
6281
|
orientations
|
|
6281
6282
|
);
|
|
6282
6283
|
if (rotatedCandidate) return rotatedCandidate;
|
|
6284
|
+
const traceAnchorCandidate = this.findValidTraceAnchorCandidate(
|
|
6285
|
+
label,
|
|
6286
|
+
orientations[0],
|
|
6287
|
+
labelIndex
|
|
6288
|
+
);
|
|
6289
|
+
if (traceAnchorCandidate) return traceAnchorCandidate;
|
|
6283
6290
|
const shiftedCandidate = this.findValidShiftedCandidate(
|
|
6284
6291
|
label,
|
|
6285
6292
|
orientations[0],
|
|
@@ -6313,6 +6320,46 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
6313
6320
|
}
|
|
6314
6321
|
return null;
|
|
6315
6322
|
}
|
|
6323
|
+
findValidTraceAnchorCandidate(label, orientation, labelIndex) {
|
|
6324
|
+
const direction = dir(orientation);
|
|
6325
|
+
const candidatePoints = this.getTraceAnchorCandidatePoints(label).sort(
|
|
6326
|
+
(a, b) => {
|
|
6327
|
+
const aAlongDirection = a.x * direction.x + a.y * direction.y;
|
|
6328
|
+
const bAlongDirection = b.x * direction.x + b.y * direction.y;
|
|
6329
|
+
return bAlongDirection - aAlongDirection;
|
|
6330
|
+
}
|
|
6331
|
+
);
|
|
6332
|
+
for (const anchorPoint of candidatePoints) {
|
|
6333
|
+
const candidate = this.createCandidate(label, anchorPoint, orientation);
|
|
6334
|
+
const result = this.evaluateCandidate(
|
|
6335
|
+
candidate,
|
|
6336
|
+
label,
|
|
6337
|
+
labelIndex,
|
|
6338
|
+
"trace-anchor"
|
|
6339
|
+
);
|
|
6340
|
+
this.currentCandidateResults.push(result);
|
|
6341
|
+
if (result.status === "valid") {
|
|
6342
|
+
result.selected = true;
|
|
6343
|
+
return result;
|
|
6344
|
+
}
|
|
6345
|
+
}
|
|
6346
|
+
return null;
|
|
6347
|
+
}
|
|
6348
|
+
getTraceAnchorCandidatePoints(label) {
|
|
6349
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6350
|
+
const points = [];
|
|
6351
|
+
for (const traceId of label.mspConnectionPairIds ?? []) {
|
|
6352
|
+
const trace = this.traceMap[traceId];
|
|
6353
|
+
if (!trace) continue;
|
|
6354
|
+
for (const point of trace.tracePath) {
|
|
6355
|
+
const key = `${point.x.toFixed(9)},${point.y.toFixed(9)}`;
|
|
6356
|
+
if (seen.has(key)) continue;
|
|
6357
|
+
seen.add(key);
|
|
6358
|
+
points.push(point);
|
|
6359
|
+
}
|
|
6360
|
+
}
|
|
6361
|
+
return points;
|
|
6362
|
+
}
|
|
6316
6363
|
getAvailableOrientations(label) {
|
|
6317
6364
|
const effectiveNetId = label.netId ?? label.globalConnNetId;
|
|
6318
6365
|
return this.inputProblem.availableNetLabelOrientations[effectiveNetId] ?? [];
|
|
@@ -6581,16 +6628,28 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
6581
6628
|
}
|
|
6582
6629
|
getCandidateStatus(params) {
|
|
6583
6630
|
const { candidate, label, labelIndex, phase } = params;
|
|
6631
|
+
const bounds = getRectBounds(
|
|
6632
|
+
candidate.center,
|
|
6633
|
+
candidate.width,
|
|
6634
|
+
candidate.height
|
|
6635
|
+
);
|
|
6584
6636
|
const boundsStatus = this.getBoundsStatus({
|
|
6585
|
-
bounds
|
|
6586
|
-
candidate.center,
|
|
6587
|
-
candidate.width,
|
|
6588
|
-
candidate.height
|
|
6589
|
-
),
|
|
6637
|
+
bounds,
|
|
6590
6638
|
labelIndex,
|
|
6591
6639
|
label
|
|
6592
6640
|
});
|
|
6593
|
-
if (boundsStatus !== "valid")
|
|
6641
|
+
if (boundsStatus !== "valid") {
|
|
6642
|
+
if (phase !== "trace-anchor" || boundsStatus !== "chip-collision" || !this.isAcceptableTraceAnchorChipCollision(candidate, label, bounds)) {
|
|
6643
|
+
return boundsStatus;
|
|
6644
|
+
}
|
|
6645
|
+
const nonChipBoundsStatus = this.getBoundsStatus({
|
|
6646
|
+
bounds,
|
|
6647
|
+
labelIndex,
|
|
6648
|
+
label,
|
|
6649
|
+
ignoreChipCollisions: true
|
|
6650
|
+
});
|
|
6651
|
+
if (nonChipBoundsStatus !== "valid") return nonChipBoundsStatus;
|
|
6652
|
+
}
|
|
6594
6653
|
const connectorTrace = this.getCandidateConnectorTrace(label, {
|
|
6595
6654
|
anchorPoint: candidate.anchorPoint,
|
|
6596
6655
|
orientation: candidate.orientation,
|
|
@@ -6613,17 +6672,35 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
6613
6672
|
}
|
|
6614
6673
|
return "valid";
|
|
6615
6674
|
}
|
|
6675
|
+
isAcceptableTraceAnchorChipCollision(candidate, label, bounds) {
|
|
6676
|
+
const collidingChips = this.chipObstacleSpatialIndex.getChipsInBounds(bounds);
|
|
6677
|
+
if (collidingChips.length === 0) return false;
|
|
6678
|
+
const labelChipIds = new Set(
|
|
6679
|
+
label.pinIds.map((pinId) => this.pinMap[pinId]?.chipId).filter((chipId) => Boolean(chipId))
|
|
6680
|
+
);
|
|
6681
|
+
return collidingChips.every((chip) => {
|
|
6682
|
+
if (!labelChipIds.has(chip.chipId)) return false;
|
|
6683
|
+
const { anchorPoint, orientation } = candidate;
|
|
6684
|
+
const chipBounds = chip.bounds;
|
|
6685
|
+
if (isYOrientation(orientation)) {
|
|
6686
|
+
return anchorPoint.x < chipBounds.minX - EPS8 || anchorPoint.x > chipBounds.maxX + EPS8;
|
|
6687
|
+
}
|
|
6688
|
+
return anchorPoint.y < chipBounds.minY - EPS8 || anchorPoint.y > chipBounds.maxY + EPS8;
|
|
6689
|
+
});
|
|
6690
|
+
}
|
|
6616
6691
|
getBoundsStatus(candidateBoundsCheck) {
|
|
6617
|
-
const { bounds, labelIndex, label } = candidateBoundsCheck;
|
|
6618
|
-
if (
|
|
6619
|
-
|
|
6692
|
+
const { bounds, labelIndex, label, ignoreChipCollisions } = candidateBoundsCheck;
|
|
6693
|
+
if (!ignoreChipCollisions) {
|
|
6694
|
+
if (this.chipObstacleSpatialIndex.getChipsInBounds(bounds).length > 0) {
|
|
6695
|
+
return "chip-collision";
|
|
6696
|
+
}
|
|
6697
|
+
if (this.sharesChipBoundary(bounds)) {
|
|
6698
|
+
return "chip-collision";
|
|
6699
|
+
}
|
|
6620
6700
|
}
|
|
6621
6701
|
if (rectIntersectsAnyTextBox(bounds, this.inputProblem)) {
|
|
6622
6702
|
return "text-collision";
|
|
6623
6703
|
}
|
|
6624
|
-
if (this.sharesChipBoundary(bounds)) {
|
|
6625
|
-
return "chip-collision";
|
|
6626
|
-
}
|
|
6627
6704
|
if (traceCrossesBoundsInterior(bounds, this.traceMap)) {
|
|
6628
6705
|
return "trace-collision";
|
|
6629
6706
|
}
|
|
@@ -146,6 +146,8 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
146
146
|
candidate: EvaluatedCandidate,
|
|
147
147
|
labelIndex: number,
|
|
148
148
|
) {
|
|
149
|
+
if (candidate.phase === "trace-anchor") return
|
|
150
|
+
|
|
149
151
|
const tracePath = this.getCandidateConnectorTrace(label, candidate)
|
|
150
152
|
if (tracePath.length < 2) return
|
|
151
153
|
|
|
@@ -187,6 +189,13 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
187
189
|
)
|
|
188
190
|
if (rotatedCandidate) return rotatedCandidate
|
|
189
191
|
|
|
192
|
+
const traceAnchorCandidate = this.findValidTraceAnchorCandidate(
|
|
193
|
+
label,
|
|
194
|
+
orientations[0]!,
|
|
195
|
+
labelIndex,
|
|
196
|
+
)
|
|
197
|
+
if (traceAnchorCandidate) return traceAnchorCandidate
|
|
198
|
+
|
|
190
199
|
const shiftedCandidate = this.findValidShiftedCandidate(
|
|
191
200
|
label,
|
|
192
201
|
orientations[0]!,
|
|
@@ -228,6 +237,57 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
228
237
|
return null
|
|
229
238
|
}
|
|
230
239
|
|
|
240
|
+
private findValidTraceAnchorCandidate(
|
|
241
|
+
label: NetLabelPlacement,
|
|
242
|
+
orientation: FacingDirection,
|
|
243
|
+
labelIndex: number,
|
|
244
|
+
) {
|
|
245
|
+
const direction = dir(orientation)
|
|
246
|
+
const candidatePoints = this.getTraceAnchorCandidatePoints(label).sort(
|
|
247
|
+
(a, b) => {
|
|
248
|
+
const aAlongDirection = a.x * direction.x + a.y * direction.y
|
|
249
|
+
const bAlongDirection = b.x * direction.x + b.y * direction.y
|
|
250
|
+
return bAlongDirection - aAlongDirection
|
|
251
|
+
},
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
for (const anchorPoint of candidatePoints) {
|
|
255
|
+
const candidate = this.createCandidate(label, anchorPoint, orientation)
|
|
256
|
+
const result = this.evaluateCandidate(
|
|
257
|
+
candidate,
|
|
258
|
+
label,
|
|
259
|
+
labelIndex,
|
|
260
|
+
"trace-anchor",
|
|
261
|
+
)
|
|
262
|
+
this.currentCandidateResults.push(result)
|
|
263
|
+
|
|
264
|
+
if (result.status === "valid") {
|
|
265
|
+
result.selected = true
|
|
266
|
+
return result
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
return null
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
private getTraceAnchorCandidatePoints(label: NetLabelPlacement) {
|
|
274
|
+
const seen = new Set<string>()
|
|
275
|
+
const points: Point[] = []
|
|
276
|
+
|
|
277
|
+
for (const traceId of label.mspConnectionPairIds ?? []) {
|
|
278
|
+
const trace = this.traceMap[traceId]
|
|
279
|
+
if (!trace) continue
|
|
280
|
+
for (const point of trace.tracePath) {
|
|
281
|
+
const key = `${point.x.toFixed(9)},${point.y.toFixed(9)}`
|
|
282
|
+
if (seen.has(key)) continue
|
|
283
|
+
seen.add(key)
|
|
284
|
+
points.push(point)
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
return points
|
|
289
|
+
}
|
|
290
|
+
|
|
231
291
|
private getAvailableOrientations(label: NetLabelPlacement) {
|
|
232
292
|
const effectiveNetId = label.netId ?? label.globalConnNetId
|
|
233
293
|
return this.inputProblem.availableNetLabelOrientations[effectiveNetId] ?? []
|
|
@@ -608,16 +668,33 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
608
668
|
phase: CandidatePhase
|
|
609
669
|
}) {
|
|
610
670
|
const { candidate, label, labelIndex, phase } = params
|
|
671
|
+
const bounds = getRectBounds(
|
|
672
|
+
candidate.center,
|
|
673
|
+
candidate.width,
|
|
674
|
+
candidate.height,
|
|
675
|
+
)
|
|
611
676
|
const boundsStatus = this.getBoundsStatus({
|
|
612
|
-
bounds
|
|
613
|
-
candidate.center,
|
|
614
|
-
candidate.width,
|
|
615
|
-
candidate.height,
|
|
616
|
-
),
|
|
677
|
+
bounds,
|
|
617
678
|
labelIndex,
|
|
618
679
|
label,
|
|
619
680
|
})
|
|
620
|
-
if (boundsStatus !== "valid")
|
|
681
|
+
if (boundsStatus !== "valid") {
|
|
682
|
+
if (
|
|
683
|
+
phase !== "trace-anchor" ||
|
|
684
|
+
boundsStatus !== "chip-collision" ||
|
|
685
|
+
!this.isAcceptableTraceAnchorChipCollision(candidate, label, bounds)
|
|
686
|
+
) {
|
|
687
|
+
return boundsStatus
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
const nonChipBoundsStatus = this.getBoundsStatus({
|
|
691
|
+
bounds,
|
|
692
|
+
labelIndex,
|
|
693
|
+
label,
|
|
694
|
+
ignoreChipCollisions: true,
|
|
695
|
+
})
|
|
696
|
+
if (nonChipBoundsStatus !== "valid") return nonChipBoundsStatus
|
|
697
|
+
}
|
|
621
698
|
|
|
622
699
|
const connectorTrace = this.getCandidateConnectorTrace(label, {
|
|
623
700
|
anchorPoint: candidate.anchorPoint,
|
|
@@ -647,22 +724,59 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
647
724
|
return "valid"
|
|
648
725
|
}
|
|
649
726
|
|
|
727
|
+
private isAcceptableTraceAnchorChipCollision(
|
|
728
|
+
candidate: CandidateLabel,
|
|
729
|
+
label: NetLabelPlacement,
|
|
730
|
+
bounds: Bounds,
|
|
731
|
+
) {
|
|
732
|
+
const collidingChips =
|
|
733
|
+
this.chipObstacleSpatialIndex.getChipsInBounds(bounds)
|
|
734
|
+
if (collidingChips.length === 0) return false
|
|
735
|
+
|
|
736
|
+
const labelChipIds = new Set(
|
|
737
|
+
label.pinIds
|
|
738
|
+
.map((pinId) => this.pinMap[pinId]?.chipId)
|
|
739
|
+
.filter((chipId): chipId is string => Boolean(chipId)),
|
|
740
|
+
)
|
|
741
|
+
|
|
742
|
+
return collidingChips.every((chip) => {
|
|
743
|
+
if (!labelChipIds.has(chip.chipId)) return false
|
|
744
|
+
|
|
745
|
+
const { anchorPoint, orientation } = candidate
|
|
746
|
+
const chipBounds = chip.bounds
|
|
747
|
+
if (isYOrientation(orientation)) {
|
|
748
|
+
return (
|
|
749
|
+
anchorPoint.x < chipBounds.minX - EPS ||
|
|
750
|
+
anchorPoint.x > chipBounds.maxX + EPS
|
|
751
|
+
)
|
|
752
|
+
}
|
|
753
|
+
return (
|
|
754
|
+
anchorPoint.y < chipBounds.minY - EPS ||
|
|
755
|
+
anchorPoint.y > chipBounds.maxY + EPS
|
|
756
|
+
)
|
|
757
|
+
})
|
|
758
|
+
}
|
|
759
|
+
|
|
650
760
|
private getBoundsStatus(candidateBoundsCheck: {
|
|
651
761
|
bounds: Bounds
|
|
652
762
|
labelIndex: number
|
|
653
763
|
label: NetLabelPlacement
|
|
764
|
+
ignoreChipCollisions?: boolean
|
|
654
765
|
}): CandidateStatus {
|
|
655
|
-
const { bounds, labelIndex, label } =
|
|
766
|
+
const { bounds, labelIndex, label, ignoreChipCollisions } =
|
|
767
|
+
candidateBoundsCheck
|
|
656
768
|
|
|
657
|
-
if (
|
|
658
|
-
|
|
769
|
+
if (!ignoreChipCollisions) {
|
|
770
|
+
if (this.chipObstacleSpatialIndex.getChipsInBounds(bounds).length > 0) {
|
|
771
|
+
return "chip-collision"
|
|
772
|
+
}
|
|
773
|
+
if (this.sharesChipBoundary(bounds)) {
|
|
774
|
+
return "chip-collision"
|
|
775
|
+
}
|
|
659
776
|
}
|
|
660
777
|
if (rectIntersectsAnyTextBox(bounds, this.inputProblem)) {
|
|
661
778
|
return "text-collision"
|
|
662
779
|
}
|
|
663
|
-
if (this.sharesChipBoundary(bounds)) {
|
|
664
|
-
return "chip-collision"
|
|
665
|
-
}
|
|
666
780
|
if (traceCrossesBoundsInterior(bounds, this.traceMap)) {
|
|
667
781
|
return "trace-collision"
|
|
668
782
|
}
|
|
@@ -35,7 +35,11 @@ export type CandidateStatus =
|
|
|
35
35
|
| "trace-clearance-violation"
|
|
36
36
|
| "netlabel-collision"
|
|
37
37
|
|
|
38
|
-
export type CandidatePhase =
|
|
38
|
+
export type CandidatePhase =
|
|
39
|
+
| "rotate"
|
|
40
|
+
| "trace-anchor"
|
|
41
|
+
| "shift"
|
|
42
|
+
| "lateral-shift"
|
|
39
43
|
|
|
40
44
|
export type EvaluatedCandidate = CandidateLabel & {
|
|
41
45
|
status: CandidateStatus
|
package/package.json
CHANGED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { expect, test } from "bun:test"
|
|
2
|
+
import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver"
|
|
3
|
+
import type { InputProblem } from "lib/types/InputProblem"
|
|
4
|
+
|
|
5
|
+
const inputProblem: InputProblem = {
|
|
6
|
+
chips: [
|
|
7
|
+
{
|
|
8
|
+
chipId: "D1",
|
|
9
|
+
center: { x: 7.9, y: 0 },
|
|
10
|
+
width: 0.7400000000000002,
|
|
11
|
+
height: 1.04,
|
|
12
|
+
pins: [
|
|
13
|
+
{ pinId: "D1.1", x: 8, y: -0.52 },
|
|
14
|
+
{ pinId: "D1.2", x: 8, y: 0.52 },
|
|
15
|
+
],
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
chipId: "D2",
|
|
19
|
+
center: { x: 10, y: 0.07 },
|
|
20
|
+
width: 1.0399999999999991,
|
|
21
|
+
height: 0.68,
|
|
22
|
+
pins: [
|
|
23
|
+
{ pinId: "D2.1", x: 9.48, y: 0 },
|
|
24
|
+
{ pinId: "D2.2", x: 10.52, y: 0 },
|
|
25
|
+
],
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
directConnections: [],
|
|
29
|
+
netConnections: [
|
|
30
|
+
{
|
|
31
|
+
netId: "VCC",
|
|
32
|
+
pinIds: ["D1.1", "D2.1"],
|
|
33
|
+
netLabelWidth: 0.48,
|
|
34
|
+
netLabelHeight: 0.42,
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
netId: "GND",
|
|
38
|
+
pinIds: ["D1.2", "D2.2"],
|
|
39
|
+
netLabelWidth: 0.48,
|
|
40
|
+
netLabelHeight: 0.42,
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
textBoxes: [],
|
|
44
|
+
availableNetLabelOrientations: {
|
|
45
|
+
GND: ["y-"],
|
|
46
|
+
VCC: ["y+"],
|
|
47
|
+
},
|
|
48
|
+
maxMspPairDistance: 0.1,
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
test("rotated component rail label stays on the existing trace", () => {
|
|
52
|
+
const solver = new SchematicTracePipelineSolver(inputProblem)
|
|
53
|
+
solver.solve()
|
|
54
|
+
|
|
55
|
+
const traces = solver.netLabelTraceCollisionSolver!.getOutput().traces
|
|
56
|
+
const labels =
|
|
57
|
+
solver.netLabelNetLabelCollisionSolver!.getOutput().netLabelPlacements
|
|
58
|
+
const gndLabel = labels.find((label) => label.netId === "GND")
|
|
59
|
+
|
|
60
|
+
expect(
|
|
61
|
+
traces.some((trace) =>
|
|
62
|
+
trace.mspPairId.startsWith("available-net-orientation"),
|
|
63
|
+
),
|
|
64
|
+
).toBe(false)
|
|
65
|
+
expect(gndLabel?.orientation).toBe("y-")
|
|
66
|
+
expect(gndLabel?.anchorPoint.x).toBeCloseTo(10.72)
|
|
67
|
+
expect(gndLabel?.anchorPoint.y).toBeCloseTo(0)
|
|
68
|
+
})
|