@tscircuit/schematic-trace-solver 0.0.161 → 0.0.163
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 +63 -23
- package/lib/solvers/AvailableNetOrientationSolver/AvailableNetOrientationSolver.ts +21 -3
- package/lib/solvers/AvailableNetOrientationSolver/types.ts +1 -0
- package/lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver.ts +28 -21
- package/lib/solvers/MspConnectionPairSolver/getMspConnectionPairsFromPins.ts +3 -3
- package/lib/solvers/MspConnectionPairSolver/shouldSeparateGroundNetRows.ts +40 -0
- package/lib/types/InputProblem.ts +3 -0
- package/package.json +1 -1
- package/tests/repros/__snapshots__/repro-bluetooth-controller-ground-decoupling-groups.snap.svg +41 -32
- package/tests/repros/__snapshots__/repro-isolated-rs485-isow7841.snap.svg +308 -0
- package/tests/repros/__snapshots__/repro-normal-net-label-branch-from-junction.snap.svg +93 -0
- package/tests/repros/assets/repro-isolated-rs485-isow7841.input.json +1992 -0
- package/tests/repros/repro-isolated-rs485-isow7841.test.ts +21 -0
- package/tests/repros/repro-normal-net-label-branch-from-junction.test.ts +73 -0
- package/tests/solvers/MspConnectionPairSolver/msp-connection-pair-solver-ground-net-rows.test.ts +57 -0
package/dist/index.d.ts
CHANGED
|
@@ -141,6 +141,8 @@ interface InputDirectConnection {
|
|
|
141
141
|
interface InputNetConnection {
|
|
142
142
|
netId: string;
|
|
143
143
|
pinIds: Array<PinId>;
|
|
144
|
+
/** True when the authoritative source net is electrical ground. */
|
|
145
|
+
isGround?: boolean;
|
|
144
146
|
/**
|
|
145
147
|
* User-facing text to render for this net label. `netId` remains the stable
|
|
146
148
|
* connectivity identifier and may be an internal id. When this is omitted
|
|
@@ -823,7 +825,7 @@ type CandidateLabel = {
|
|
|
823
825
|
height: number;
|
|
824
826
|
};
|
|
825
827
|
type CandidateStatus$2 = "valid" | "chip-collision" | "text-collision" | "trace-collision" | "trace-clearance-violation" | "netlabel-collision";
|
|
826
|
-
type CandidatePhase = "rotate" | "trace-anchor" | "outward-trace-anchor" | "shift" | "lateral-shift";
|
|
828
|
+
type CandidatePhase = "rotate" | "trace-anchor" | "connected-rail-shift" | "outward-trace-anchor" | "shift" | "lateral-shift";
|
|
827
829
|
type EvaluatedCandidate = CandidateLabel & {
|
|
828
830
|
status: CandidateStatus$2;
|
|
829
831
|
selected: boolean;
|
|
@@ -900,6 +902,7 @@ declare class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
900
902
|
private getNetLabelHeight;
|
|
901
903
|
private getCandidateStatus;
|
|
902
904
|
private isAcceptableTraceAnchorChipCollision;
|
|
905
|
+
private staysOutsideConnectedPinRow;
|
|
903
906
|
private getBoundsStatus;
|
|
904
907
|
private isTraceTooCloseToLabel;
|
|
905
908
|
private getLabelTraceClearanceBounds;
|
package/dist/index.js
CHANGED
|
@@ -539,6 +539,30 @@ var getLabeledConnectionRouteReason = ({
|
|
|
539
539
|
};
|
|
540
540
|
var isLabeledPeripheralConnection = (params) => getLabeledConnectionRouteReason(params) !== null;
|
|
541
541
|
|
|
542
|
+
// lib/solvers/MspConnectionPairSolver/shouldSeparateGroundNetRows.ts
|
|
543
|
+
var SAME_RAIL_Y_TOLERANCE = 1e-6;
|
|
544
|
+
var MIN_GROUPED_RAIL_PIN_COUNT = 3;
|
|
545
|
+
function isGroupedHorizontalRail({
|
|
546
|
+
pin,
|
|
547
|
+
netPins
|
|
548
|
+
}) {
|
|
549
|
+
const sameRailPins = netPins.filter(
|
|
550
|
+
(otherPin) => Math.abs(otherPin.y - pin.y) <= SAME_RAIL_Y_TOLERANCE
|
|
551
|
+
);
|
|
552
|
+
const railChipIds = new Set(sameRailPins.map((railPin) => railPin.chipId));
|
|
553
|
+
return railChipIds.size >= MIN_GROUPED_RAIL_PIN_COUNT;
|
|
554
|
+
}
|
|
555
|
+
function shouldSeparateGroundNetRows({
|
|
556
|
+
netConnection,
|
|
557
|
+
netPins,
|
|
558
|
+
pin1,
|
|
559
|
+
pin2
|
|
560
|
+
}) {
|
|
561
|
+
if (!netConnection?.isGround || pin1.chipId === pin2.chipId) return false;
|
|
562
|
+
if (Math.abs(pin1.y - pin2.y) <= SAME_RAIL_Y_TOLERANCE) return false;
|
|
563
|
+
return isGroupedHorizontalRail({ pin: pin1, netPins }) && isGroupedHorizontalRail({ pin: pin2, netPins });
|
|
564
|
+
}
|
|
565
|
+
|
|
542
566
|
// lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver.ts
|
|
543
567
|
var DEFAULT_MAX_MSP_PAIR_DISTANCE = 1;
|
|
544
568
|
var getPinPairKey = (pinIds) => [...pinIds].sort().join("::");
|
|
@@ -643,36 +667,41 @@ var MspConnectionPairSolver = class extends BaseSolver {
|
|
|
643
667
|
return;
|
|
644
668
|
}
|
|
645
669
|
const globalConnNetId = this.globalConnMap.getNetConnectedToId(pin1);
|
|
646
|
-
const
|
|
670
|
+
const userNetId2 = this.userNetIdByPinId[pin1] ?? this.userNetIdByPinId[pin2];
|
|
647
671
|
const suppressNetLabel = pairDistance > this.maxMspPairDistance && labeledConnectionRouteReason === "overlapping-fallback-labels";
|
|
648
672
|
this.addMspConnectionPair({
|
|
649
673
|
mspPairId: `${pin1}-${pin2}`,
|
|
650
674
|
dcConnNetId: dcNetId,
|
|
651
675
|
globalConnNetId,
|
|
652
|
-
userNetId,
|
|
676
|
+
userNetId: userNetId2,
|
|
653
677
|
suppressNetLabel,
|
|
654
678
|
pins: [p1, p2]
|
|
655
679
|
});
|
|
656
680
|
return;
|
|
657
681
|
}
|
|
658
682
|
const pinIdMap = new Map(Object.entries(this.pinMap));
|
|
659
|
-
const
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
683
|
+
const userNetId = this.userNetIdByPinId[directlyConnectedPins[0]];
|
|
684
|
+
const netConnection = this.inputProblem.netConnections.find(
|
|
685
|
+
(connection) => connection.netId === userNetId
|
|
686
|
+
);
|
|
687
|
+
const directlyConnectedPinObjects = directlyConnectedPins.map(
|
|
688
|
+
(pinId) => this.pinMap[pinId]
|
|
689
|
+
);
|
|
690
|
+
const msp = getOrthogonalMinimumSpanningTree(directlyConnectedPinObjects, {
|
|
691
|
+
maxDistance: this.maxMspPairDistance,
|
|
692
|
+
forbidEdge: (a, b) => !this.canRouteGroundPair(a.pinId, b.pinId) || shouldSeparateGroundNetRows({
|
|
693
|
+
netConnection,
|
|
694
|
+
netPins: directlyConnectedPinObjects,
|
|
695
|
+
pin1: a,
|
|
696
|
+
pin2: b
|
|
697
|
+
}) || arePinsInDifferentSchematicSections(this.inputProblem, a, b) || doesPairCrossRestrictedCenterLines({
|
|
698
|
+
inputProblem: this.inputProblem,
|
|
699
|
+
chipMap: this.chipMap,
|
|
700
|
+
pinIdMap,
|
|
701
|
+
p1: a,
|
|
702
|
+
p2: b
|
|
703
|
+
})
|
|
704
|
+
});
|
|
676
705
|
for (const [pin1, pin2] of msp) {
|
|
677
706
|
const p1Obj = this.pinMap[pin1];
|
|
678
707
|
const p2Obj = this.pinMap[pin2];
|
|
@@ -689,12 +718,12 @@ var MspConnectionPairSolver = class extends BaseSolver {
|
|
|
689
718
|
continue;
|
|
690
719
|
}
|
|
691
720
|
const globalConnNetId = this.globalConnMap.getNetConnectedToId(pin1);
|
|
692
|
-
const
|
|
721
|
+
const pairUserNetId = this.userNetIdByPinId[pin1] ?? this.userNetIdByPinId[pin2];
|
|
693
722
|
this.addMspConnectionPair({
|
|
694
723
|
mspPairId: `${pin1}-${pin2}`,
|
|
695
724
|
dcConnNetId: dcNetId,
|
|
696
725
|
globalConnNetId,
|
|
697
|
-
userNetId,
|
|
726
|
+
userNetId: pairUserNetId,
|
|
698
727
|
pins: [p1Obj, p2Obj]
|
|
699
728
|
});
|
|
700
729
|
}
|
|
@@ -8802,6 +8831,7 @@ function getPaddedBounds(bounds, padding) {
|
|
|
8802
8831
|
|
|
8803
8832
|
// lib/solvers/AvailableNetOrientationSolver/AvailableNetOrientationSolver.ts
|
|
8804
8833
|
var LABEL_TRACE_CLEARANCE2 = 0.1;
|
|
8834
|
+
var CONNECTED_PIN_ROW_OVERLAP_TOLERANCE = 0.1;
|
|
8805
8835
|
var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
8806
8836
|
inputProblem;
|
|
8807
8837
|
traces;
|
|
@@ -9823,7 +9853,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
9823
9853
|
label
|
|
9824
9854
|
});
|
|
9825
9855
|
if (boundsStatus !== "valid") {
|
|
9826
|
-
if (phase !== "trace-anchor" || boundsStatus !== "chip-collision" || !this.isAcceptableTraceAnchorChipCollision(candidate, label, bounds)) {
|
|
9856
|
+
if (phase !== "trace-anchor" && phase !== "connected-rail-shift" || boundsStatus !== "chip-collision" || !this.isAcceptableTraceAnchorChipCollision(candidate, label, bounds) || phase === "connected-rail-shift" && !this.staysOutsideConnectedPinRow({ candidate, label, bounds })) {
|
|
9827
9857
|
return boundsStatus;
|
|
9828
9858
|
}
|
|
9829
9859
|
const nonChipBoundsStatus = this.getBoundsStatus({
|
|
@@ -9877,6 +9907,15 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
9877
9907
|
return anchorPoint.y < chipBounds.minY - EPS12 || anchorPoint.y > chipBounds.maxY + EPS12;
|
|
9878
9908
|
});
|
|
9879
9909
|
}
|
|
9910
|
+
staysOutsideConnectedPinRow(params) {
|
|
9911
|
+
const { candidate, label, bounds } = params;
|
|
9912
|
+
const pin = this.pinMap[label.pinIds[0]];
|
|
9913
|
+
if (!pin || !isYOrientation(candidate.orientation)) return false;
|
|
9914
|
+
if (candidate.anchorPoint.x < pin.x) {
|
|
9915
|
+
return bounds.maxX <= pin.x + CONNECTED_PIN_ROW_OVERLAP_TOLERANCE + EPS12;
|
|
9916
|
+
}
|
|
9917
|
+
return bounds.minX >= pin.x - CONNECTED_PIN_ROW_OVERLAP_TOLERANCE - EPS12;
|
|
9918
|
+
}
|
|
9880
9919
|
getBoundsStatus(candidateBoundsCheck) {
|
|
9881
9920
|
const { bounds, labelIndex, label, ignoreChipCollisions } = candidateBoundsCheck;
|
|
9882
9921
|
if (!ignoreChipCollisions) {
|
|
@@ -10031,8 +10070,9 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
10031
10070
|
orientation,
|
|
10032
10071
|
direction,
|
|
10033
10072
|
baseAnchor: connectorSource,
|
|
10034
|
-
maxSearchDistance: this.
|
|
10073
|
+
maxSearchDistance: this.getSearchDistanceLimit(label, orientation),
|
|
10035
10074
|
outwardDistance: 0,
|
|
10075
|
+
phase: "connected-rail-shift",
|
|
10036
10076
|
stopOnTraceCollision: false,
|
|
10037
10077
|
connectorSource,
|
|
10038
10078
|
startDistance: 0
|
|
@@ -51,6 +51,7 @@ import { visualizeAvailableNetOrientationSolver } from "./visualize"
|
|
|
51
51
|
import { AvailableNetOrientationObstacleIndex } from "./AvailableNetOrientationObstacleIndex"
|
|
52
52
|
|
|
53
53
|
const LABEL_TRACE_CLEARANCE = 0.1
|
|
54
|
+
const CONNECTED_PIN_ROW_OVERLAP_TOLERANCE = 0.1
|
|
54
55
|
|
|
55
56
|
export class AvailableNetOrientationSolver extends BaseSolver {
|
|
56
57
|
inputProblem: InputProblem
|
|
@@ -1443,9 +1444,11 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
1443
1444
|
})
|
|
1444
1445
|
if (boundsStatus !== "valid") {
|
|
1445
1446
|
if (
|
|
1446
|
-
phase !== "trace-anchor" ||
|
|
1447
|
+
(phase !== "trace-anchor" && phase !== "connected-rail-shift") ||
|
|
1447
1448
|
boundsStatus !== "chip-collision" ||
|
|
1448
|
-
!this.isAcceptableTraceAnchorChipCollision(candidate, label, bounds)
|
|
1449
|
+
!this.isAcceptableTraceAnchorChipCollision(candidate, label, bounds) ||
|
|
1450
|
+
(phase === "connected-rail-shift" &&
|
|
1451
|
+
!this.staysOutsideConnectedPinRow({ candidate, label, bounds }))
|
|
1449
1452
|
) {
|
|
1450
1453
|
return boundsStatus
|
|
1451
1454
|
}
|
|
@@ -1525,6 +1528,20 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
1525
1528
|
})
|
|
1526
1529
|
}
|
|
1527
1530
|
|
|
1531
|
+
private staysOutsideConnectedPinRow(params: {
|
|
1532
|
+
candidate: CandidateLabel
|
|
1533
|
+
label: NetLabelPlacement
|
|
1534
|
+
bounds: Bounds
|
|
1535
|
+
}) {
|
|
1536
|
+
const { candidate, label, bounds } = params
|
|
1537
|
+
const pin = this.pinMap[label.pinIds[0]!]
|
|
1538
|
+
if (!pin || !isYOrientation(candidate.orientation)) return false
|
|
1539
|
+
if (candidate.anchorPoint.x < pin.x) {
|
|
1540
|
+
return bounds.maxX <= pin.x + CONNECTED_PIN_ROW_OVERLAP_TOLERANCE + EPS
|
|
1541
|
+
}
|
|
1542
|
+
return bounds.minX >= pin.x - CONNECTED_PIN_ROW_OVERLAP_TOLERANCE - EPS
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1528
1545
|
private getBoundsStatus(candidateBoundsCheck: {
|
|
1529
1546
|
bounds: Bounds
|
|
1530
1547
|
labelIndex: number
|
|
@@ -1756,8 +1773,9 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
1756
1773
|
orientation,
|
|
1757
1774
|
direction,
|
|
1758
1775
|
baseAnchor: connectorSource,
|
|
1759
|
-
maxSearchDistance: this.
|
|
1776
|
+
maxSearchDistance: this.getSearchDistanceLimit(label, orientation),
|
|
1760
1777
|
outwardDistance: 0,
|
|
1778
|
+
phase: "connected-rail-shift",
|
|
1761
1779
|
stopOnTraceCollision: false,
|
|
1762
1780
|
connectorSource,
|
|
1763
1781
|
startDistance: 0,
|
|
@@ -16,6 +16,7 @@ import { getConnectivityMapsFromInputProblem } from "./getConnectivityMapFromInp
|
|
|
16
16
|
import { getGroundConnectionPolicy } from "./getGroundConnectionPolicy"
|
|
17
17
|
import { getOrthogonalMinimumSpanningTree } from "./getMspConnectionPairsFromPins"
|
|
18
18
|
import { getLabeledConnectionRouteReason } from "./isLabeledPeripheralConnection"
|
|
19
|
+
import { shouldSeparateGroundNetRows } from "./shouldSeparateGroundNetRows"
|
|
19
20
|
|
|
20
21
|
export type MspConnectionPairId = string
|
|
21
22
|
export const DEFAULT_MAX_MSP_PAIR_DISTANCE = 1
|
|
@@ -194,26 +195,32 @@ export class MspConnectionPairSolver extends BaseSolver {
|
|
|
194
195
|
PinId,
|
|
195
196
|
InputPin & { chipId: string }
|
|
196
197
|
>
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
maxDistance: this.maxMspPairDistance,
|
|
201
|
-
forbidEdge: (a, b) =>
|
|
202
|
-
!this.canRouteGroundPair(a.pinId, b.pinId) ||
|
|
203
|
-
arePinsInDifferentSchematicSections(
|
|
204
|
-
this.inputProblem,
|
|
205
|
-
a as InputPin & { chipId: string },
|
|
206
|
-
b as InputPin & { chipId: string },
|
|
207
|
-
) ||
|
|
208
|
-
doesPairCrossRestrictedCenterLines({
|
|
209
|
-
inputProblem: this.inputProblem,
|
|
210
|
-
chipMap: this.chipMap,
|
|
211
|
-
pinIdMap,
|
|
212
|
-
p1: a as InputPin & { chipId: string },
|
|
213
|
-
p2: b as InputPin & { chipId: string },
|
|
214
|
-
}),
|
|
215
|
-
},
|
|
198
|
+
const userNetId = this.userNetIdByPinId[directlyConnectedPins[0]!]
|
|
199
|
+
const netConnection = this.inputProblem.netConnections.find(
|
|
200
|
+
(connection) => connection.netId === userNetId,
|
|
216
201
|
)
|
|
202
|
+
const directlyConnectedPinObjects = directlyConnectedPins.map(
|
|
203
|
+
(pinId) => this.pinMap[pinId]!,
|
|
204
|
+
)
|
|
205
|
+
const msp = getOrthogonalMinimumSpanningTree(directlyConnectedPinObjects, {
|
|
206
|
+
maxDistance: this.maxMspPairDistance,
|
|
207
|
+
forbidEdge: (a, b) =>
|
|
208
|
+
!this.canRouteGroundPair(a.pinId, b.pinId) ||
|
|
209
|
+
shouldSeparateGroundNetRows({
|
|
210
|
+
netConnection,
|
|
211
|
+
netPins: directlyConnectedPinObjects,
|
|
212
|
+
pin1: a,
|
|
213
|
+
pin2: b,
|
|
214
|
+
}) ||
|
|
215
|
+
arePinsInDifferentSchematicSections(this.inputProblem, a, b) ||
|
|
216
|
+
doesPairCrossRestrictedCenterLines({
|
|
217
|
+
inputProblem: this.inputProblem,
|
|
218
|
+
chipMap: this.chipMap,
|
|
219
|
+
pinIdMap,
|
|
220
|
+
p1: a,
|
|
221
|
+
p2: b,
|
|
222
|
+
}),
|
|
223
|
+
})
|
|
217
224
|
|
|
218
225
|
for (const [pin1, pin2] of msp) {
|
|
219
226
|
const p1Obj = this.pinMap[pin1!]!
|
|
@@ -237,13 +244,13 @@ export class MspConnectionPairSolver extends BaseSolver {
|
|
|
237
244
|
}
|
|
238
245
|
|
|
239
246
|
const globalConnNetId = this.globalConnMap.getNetConnectedToId(pin1!)!
|
|
240
|
-
const
|
|
247
|
+
const pairUserNetId =
|
|
241
248
|
this.userNetIdByPinId[pin1!] ?? this.userNetIdByPinId[pin2!]
|
|
242
249
|
this.addMspConnectionPair({
|
|
243
250
|
mspPairId: `${pin1}-${pin2}`,
|
|
244
251
|
dcConnNetId: dcNetId,
|
|
245
252
|
globalConnNetId,
|
|
246
|
-
userNetId,
|
|
253
|
+
userNetId: pairUserNetId,
|
|
247
254
|
pins: [p1Obj, p2Obj],
|
|
248
255
|
})
|
|
249
256
|
}
|
|
@@ -16,11 +16,11 @@ import type { InputPin, PinId } from "lib/types/InputProblem"
|
|
|
16
16
|
* - When multiple candidates have the same distance, we pick the one
|
|
17
17
|
* whose pinId is lexicographically smallest (for stable output).
|
|
18
18
|
*/
|
|
19
|
-
export function getOrthogonalMinimumSpanningTree(
|
|
20
|
-
pins:
|
|
19
|
+
export function getOrthogonalMinimumSpanningTree<Pin extends InputPin>(
|
|
20
|
+
pins: Pin[],
|
|
21
21
|
opts: {
|
|
22
22
|
maxDistance?: number
|
|
23
|
-
forbidEdge?: (a:
|
|
23
|
+
forbidEdge?: (a: Pin, b: Pin) => boolean
|
|
24
24
|
} = {},
|
|
25
25
|
): Array<[PinId, PinId]> {
|
|
26
26
|
const n = pins.length
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { InputNetConnection, InputPin } from "lib/types/InputProblem"
|
|
2
|
+
|
|
3
|
+
const SAME_RAIL_Y_TOLERANCE = 1e-6
|
|
4
|
+
const MIN_GROUPED_RAIL_PIN_COUNT = 3
|
|
5
|
+
|
|
6
|
+
function isGroupedHorizontalRail({
|
|
7
|
+
pin,
|
|
8
|
+
netPins,
|
|
9
|
+
}: {
|
|
10
|
+
pin: InputPin & { chipId: string }
|
|
11
|
+
netPins: Array<InputPin & { chipId: string }>
|
|
12
|
+
}) {
|
|
13
|
+
// Multiple pins from one symbol do not establish a grouped component rail.
|
|
14
|
+
const sameRailPins = netPins.filter(
|
|
15
|
+
(otherPin) => Math.abs(otherPin.y - pin.y) <= SAME_RAIL_Y_TOLERANCE,
|
|
16
|
+
)
|
|
17
|
+
const railChipIds = new Set(sameRailPins.map((railPin) => railPin.chipId))
|
|
18
|
+
return railChipIds.size >= MIN_GROUPED_RAIL_PIN_COUNT
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function shouldSeparateGroundNetRows({
|
|
22
|
+
netConnection,
|
|
23
|
+
netPins,
|
|
24
|
+
pin1,
|
|
25
|
+
pin2,
|
|
26
|
+
}: {
|
|
27
|
+
netConnection?: InputNetConnection
|
|
28
|
+
netPins: Array<InputPin & { chipId: string }>
|
|
29
|
+
pin1: InputPin & { chipId: string }
|
|
30
|
+
pin2: InputPin & { chipId: string }
|
|
31
|
+
}) {
|
|
32
|
+
if (!netConnection?.isGround || pin1.chipId === pin2.chipId) return false
|
|
33
|
+
if (Math.abs(pin1.y - pin2.y) <= SAME_RAIL_Y_TOLERANCE) return false
|
|
34
|
+
|
|
35
|
+
// Net labels preserve ground connectivity without a cross-row MSP edge.
|
|
36
|
+
return (
|
|
37
|
+
isGroupedHorizontalRail({ pin: pin1, netPins }) &&
|
|
38
|
+
isGroupedHorizontalRail({ pin: pin2, netPins })
|
|
39
|
+
)
|
|
40
|
+
}
|
|
@@ -98,6 +98,9 @@ export interface InputNetConnection {
|
|
|
98
98
|
netId: string
|
|
99
99
|
pinIds: Array<PinId>
|
|
100
100
|
|
|
101
|
+
/** True when the authoritative source net is electrical ground. */
|
|
102
|
+
isGround?: boolean
|
|
103
|
+
|
|
101
104
|
/**
|
|
102
105
|
* User-facing text to render for this net label. `netId` remains the stable
|
|
103
106
|
* connectivity identifier and may be an internal id. When this is omitted
|