@tscircuit/schematic-trace-solver 0.0.160 → 0.0.162

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.
Files changed (63) hide show
  1. package/dist/index.d.ts +5 -0
  2. package/dist/index.js +221 -29
  3. package/lib/solvers/AvailableNetOrientationSolver/AvailableNetOrientationSolver.ts +210 -6
  4. package/lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver.ts +28 -21
  5. package/lib/solvers/MspConnectionPairSolver/getMspConnectionPairsFromPins.ts +3 -3
  6. package/lib/solvers/MspConnectionPairSolver/shouldSeparateGroundNetRows.ts +40 -0
  7. package/lib/solvers/TraceCleanupSolver/sameNetRailAlignment/getFixedLabelCoordinate.ts +44 -13
  8. package/lib/types/InputProblem.ts +3 -0
  9. package/package.json +1 -1
  10. package/tests/bug-reports/bug-report-20260706T220324Z/__snapshots__/bug-report-20260706T220324Z.snap.svg +1 -1
  11. package/tests/bug-reports/bug-report-20260728T144234Z/__snapshots__/bug-report-20260728T144234Z.snap.svg +1 -1
  12. package/tests/bug-reports/bug-report-20260728T225606Z/__snapshots__/bug-report-20260728T225606Z.snap.svg +79 -79
  13. package/tests/bug-reports/bug-report-20260730T061837Z/__snapshots__/bug-report-20260730T061837Z.snap.svg +85 -85
  14. package/tests/bug-reports/bug-report-20260731T052229Z/__snapshots__/bug-report-20260731T052229Z.snap.svg +1 -1
  15. package/tests/bug-reports/bug-report-20260804T095800Z/__snapshots__/bug-report-20260804T095800Z.snap.svg +163 -163
  16. package/tests/bug-reports/bug-report-20260804T171919Z/__snapshots__/bug-report-20260804T171919Z.snap.svg +547 -547
  17. package/tests/bug-reports/bug-report-20260804T225912Z/__snapshots__/bug-report-20260804T225912Z.snap.svg +13 -13
  18. package/tests/bug-reports/bug-report-20260805T061316Z/__snapshots__/bug-report-20260805T061316Z.snap.svg +267 -267
  19. package/tests/bug-reports/bug-report-20260806T061548Z/__snapshots__/bug-report-20260806T061548Z.snap.svg +267 -267
  20. package/tests/bug-reports/bug-report-20260806T093501Z/__snapshots__/bug-report-20260806T093501Z.snap.svg +109 -109
  21. package/tests/bug-reports/bug-report-20260811T075142Z/__snapshots__/bug-report-20260811T075142Z.snap.svg +1 -1
  22. package/tests/bug-reports/bug-report-20260815T073240Z/__snapshots__/bug-report-20260815T073240Z.snap.svg +49 -49
  23. package/tests/bug-reports/bug-report-20260819T091818Z/__snapshots__/bug-report-20260819T091818Z.snap.svg +1 -1
  24. package/tests/bug-reports/bug-report-20260825T045913Z/__snapshots__/bug-report-20260825T045913Z.snap.svg +103 -103
  25. package/tests/bug-reports/bug-report-20260825T103621Z/__snapshots__/bug-report-20260825T103621Z.snap.svg +103 -103
  26. package/tests/bug-reports/bug-report-20260826T072956Z/__snapshots__/bug-report-20260826T072956Z.snap.svg +163 -163
  27. package/tests/examples/__snapshots__/example51.snap.svg +568 -568
  28. package/tests/fixtures/convertSolverOutputToCircuitJson.test.ts +16 -1
  29. package/tests/fixtures/convertSolverOutputToCircuitJson.ts +23 -12
  30. package/tests/repros/__snapshots__/board-1273-trace-overlap-cycle.snap.svg +736 -736
  31. package/tests/repros/__snapshots__/board-15984-same-net-junction.snap.svg +79 -79
  32. package/tests/repros/__snapshots__/board1096-usb-label-overlap-iteration.snap.svg +127 -127
  33. package/tests/repros/__snapshots__/repro-bluetooth-controller-ground-decoupling-groups.snap.svg +115 -0
  34. package/tests/repros/__snapshots__/repro-board-589-regulator-section.snap.svg +31 -31
  35. package/tests/repros/__snapshots__/repro-board-648-esp12f-section.snap.svg +121 -121
  36. package/tests/repros/__snapshots__/repro-bq25895-cross-net-trace-overlap.snap.svg +43 -43
  37. package/tests/repros/__snapshots__/repro-core-555-power-cross-net-overlap.snap.svg +19 -19
  38. package/tests/repros/__snapshots__/repro-core-ground-inline-label-fallback.snap.svg +277 -0
  39. package/tests/repros/__snapshots__/repro-core-subcircuit-missing-ground.snap.svg +19 -19
  40. package/tests/repros/__snapshots__/repro-core-tb67s579ftg-inline-label-routing.snap.svg +7 -7
  41. package/tests/repros/__snapshots__/repro-dc-power-isolated-routing.snap.svg +37 -37
  42. package/tests/repros/__snapshots__/repro-hdc2080-ground-label-alignment.snap.svg +137 -0
  43. package/tests/repros/__snapshots__/repro-nrf52810-clock-routing.snap.svg +61 -61
  44. package/tests/repros/__snapshots__/repro-pga300-redundant-ground-traces.snap.svg +115 -115
  45. package/tests/repros/__snapshots__/repro-pmp11282-isolated-dcdc.snap.svg +743 -743
  46. package/tests/repros/__snapshots__/repro-rp2040-usb-cc2-ground-overlap.snap.svg +43 -43
  47. package/tests/repros/__snapshots__/repro-ti-power-output-section.snap.svg +1 -1
  48. package/tests/repros/__snapshots__/repro-tida010076-j4-ground-bus.snap.svg +17 -17
  49. package/tests/repros/__snapshots__/repro-tida010076-page02.snap.svg +1131 -1131
  50. package/tests/repros/__snapshots__/repro-type-c-16pin-self-connection.snap.svg +7 -7
  51. package/tests/repros/__snapshots__/repro-type-c-16pin-to-resistor.snap.svg +7 -7
  52. package/tests/repros/__snapshots__/repro-usb-power-vbus-label-detour.snap.svg +666 -666
  53. package/tests/repros/__snapshots__/repro161-power-section-autolayout.snap.svg +73 -73
  54. package/tests/repros/__snapshots__/repro48-555-timer-vcc-rail-label.snap.svg +73 -73
  55. package/tests/repros/assets/repro-bluetooth-controller-ground-decoupling-groups.input.json +214 -0
  56. package/tests/repros/assets/repro-core-ground-inline-label-fallback.input.json +863 -0
  57. package/tests/repros/repro-bluetooth-controller-ground-decoupling-groups.test.ts +18 -0
  58. package/tests/repros/repro-core-ground-inline-label-fallback.test.ts +34 -0
  59. package/tests/repros/repro-hdc2080-ground-label-alignment.test.ts +239 -0
  60. package/tests/repros/repro-tida010076-j4-ground-bus.test.ts +14 -10
  61. package/tests/repros/repro-tida010076-page02.test.ts +2 -2
  62. package/tests/solvers/InlineNetLabelSolver/__snapshots__/inline-net-label02.snap.svg +1 -1
  63. package/tests/solvers/MspConnectionPairSolver/msp-connection-pair-solver-ground-net-rows.test.ts +57 -0
@@ -0,0 +1,18 @@
1
+ import { expect, test } from "bun:test"
2
+ import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver"
3
+ import type { InputProblem } from "lib/types/InputProblem"
4
+ import "tests/fixtures/matcher"
5
+ import inputProblem from "./assets/repro-bluetooth-controller-ground-decoupling-groups.input.json"
6
+
7
+ test("repro Bluetooth controller GND trace between decoupling groups", () => {
8
+ const solver = new SchematicTracePipelineSolver(
9
+ inputProblem as InputProblem,
10
+ {
11
+ hideRatsNet: true,
12
+ },
13
+ )
14
+
15
+ solver.solve()
16
+
17
+ expect(solver).toMatchSolverSnapshot(import.meta.path)
18
+ })
@@ -0,0 +1,34 @@
1
+ import { expect, test } from "bun:test"
2
+ import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver"
3
+ import type { InputProblem } from "lib/types/InputProblem"
4
+ import "tests/fixtures/matcher"
5
+ import inputProblemJson from "./assets/repro-core-ground-inline-label-fallback.input.json"
6
+
7
+ const signalNetIds = new Set(["step", "direction", "enable"])
8
+
9
+ // Captured from @tscircuit/core's
10
+ // ground-net-label-preserves-solver-orientation test. These three two-pin
11
+ // signal nets opt into inline labels, but the neighboring shared LOGIC_3V3
12
+ // and GND labels cause post-processing to fall them back to anchored labels.
13
+ test("repro: core inline signal labels fall back near shared rail labels", () => {
14
+ const inputProblem: InputProblem = JSON.parse(
15
+ JSON.stringify(inputProblemJson),
16
+ )
17
+ const solver = new SchematicTracePipelineSolver(inputProblem)
18
+
19
+ solver.solve()
20
+
21
+ const inlineOutput = solver.inlineNetLabelSolver!.getOutput()
22
+ const signalInlineLabels = inlineOutput.inlineNetLabelPlacements.filter(
23
+ (placement) => signalNetIds.has(placement.netId ?? ""),
24
+ )
25
+ const signalAnchoredLabels = inlineOutput.netLabelPlacements.filter(
26
+ (placement) => signalNetIds.has(placement.netId ?? ""),
27
+ )
28
+
29
+ expect(signalInlineLabels).toHaveLength(0)
30
+ expect(
31
+ signalAnchoredLabels.map((placement) => placement.netId).sort(),
32
+ ).toEqual(["direction", "direction", "enable", "enable", "step", "step"])
33
+ expect(solver).toMatchSolverSnapshot(import.meta.path)
34
+ })
@@ -0,0 +1,239 @@
1
+ import { expect, test } from "bun:test"
2
+ import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver"
3
+ import type { InputProblem } from "lib/types/InputProblem"
4
+ import "tests/fixtures/matcher"
5
+
6
+ const inputProblem: InputProblem = {
7
+ chips: [
8
+ {
9
+ chipId: "schematic_component_0",
10
+ center: { x: 4.5, y: 0 },
11
+ width: 3.4000000000000004,
12
+ height: 4.6,
13
+ pins: [
14
+ {
15
+ pinId: "schematic_port_0",
16
+ displayName: "VDD",
17
+ x: 2.8,
18
+ y: 0.8500000000000001,
19
+ },
20
+ {
21
+ pinId: "schematic_port_1",
22
+ displayName: "SCL",
23
+ x: 2.8,
24
+ y: 0.4000000000000001,
25
+ },
26
+ {
27
+ pinId: "schematic_port_2",
28
+ displayName: "SDA",
29
+ x: 2.8,
30
+ y: 0.20000000000000007,
31
+ },
32
+ {
33
+ pinId: "schematic_port_3",
34
+ displayName: "GPIO",
35
+ x: 2.8,
36
+ y: 0,
37
+ },
38
+ {
39
+ pinId: "schematic_port_4",
40
+ displayName: "GND",
41
+ x: 2.8,
42
+ y: -0.8500000000000001,
43
+ },
44
+ ],
45
+ },
46
+ {
47
+ chipId: "schematic_component_1",
48
+ center: { x: -2.1, y: 0 },
49
+ width: 4,
50
+ height: 4,
51
+ pins: [
52
+ {
53
+ pinId: "schematic_port_5",
54
+ displayName: "I2C_SDA",
55
+ x: -0.10000000000000009,
56
+ y: 0.5499999999999998,
57
+ },
58
+ {
59
+ pinId: "schematic_port_6",
60
+ displayName: "GND",
61
+ x: -0.10000000000000009,
62
+ y: -1.2000000000000002,
63
+ },
64
+ {
65
+ pinId: "schematic_port_7",
66
+ displayName: "I2C_ADDR",
67
+ x: -0.10000000000000009,
68
+ y: -0.5500000000000002,
69
+ },
70
+ {
71
+ pinId: "schematic_port_8",
72
+ displayName: "DRDY_INT",
73
+ x: -0.10000000000000009,
74
+ y: 0.09999999999999987,
75
+ },
76
+ {
77
+ pinId: "schematic_port_9",
78
+ displayName: "VDD",
79
+ x: -0.10000000000000009,
80
+ y: 1.2000000000000002,
81
+ },
82
+ {
83
+ pinId: "schematic_port_10",
84
+ displayName: "I2C_SCL",
85
+ x: -0.10000000000000009,
86
+ y: 0.7499999999999998,
87
+ },
88
+ ],
89
+ },
90
+ {
91
+ chipId: "schematic_component_2",
92
+ center: { x: 0.7949999999999999, y: 1.3 },
93
+ width: 1.0899999999999999,
94
+ height: 0.6000000000000001,
95
+ pins: [
96
+ {
97
+ pinId: "schematic_port_11",
98
+ displayName: "anode",
99
+ x: 0.7,
100
+ y: 1.6,
101
+ _facingDirection: "y+",
102
+ },
103
+ {
104
+ pinId: "schematic_port_12",
105
+ displayName: "cathode",
106
+ x: 0.7,
107
+ y: 1,
108
+ _facingDirection: "y-",
109
+ },
110
+ ],
111
+ },
112
+ {
113
+ chipId: "schematic_component_3",
114
+ center: { x: 1.5949999999999998, y: 1.3 },
115
+ width: 1.0899999999999996,
116
+ height: 0.6000000000000001,
117
+ pins: [
118
+ {
119
+ pinId: "schematic_port_13",
120
+ displayName: "anode",
121
+ x: 1.5,
122
+ y: 1.6,
123
+ _facingDirection: "y+",
124
+ },
125
+ {
126
+ pinId: "schematic_port_14",
127
+ displayName: "cathode",
128
+ x: 1.5,
129
+ y: 1,
130
+ _facingDirection: "y-",
131
+ },
132
+ ],
133
+ },
134
+ ],
135
+ directConnections: [],
136
+ netConnections: [
137
+ {
138
+ netId: "VDD",
139
+ netLabelWidth: 0.42,
140
+ netLabelHeight: 0.48,
141
+ pinIds: [
142
+ "schematic_port_0",
143
+ "schematic_port_9",
144
+ "schematic_port_11",
145
+ "schematic_port_13",
146
+ ],
147
+ },
148
+ {
149
+ netId: "SCL",
150
+ netLabelWidth: 0.48,
151
+ pinIds: ["schematic_port_1", "schematic_port_10", "schematic_port_12"],
152
+ },
153
+ {
154
+ netId: "SDA",
155
+ netLabelWidth: 0.48,
156
+ pinIds: ["schematic_port_2", "schematic_port_5", "schematic_port_14"],
157
+ },
158
+ {
159
+ netId: "DRDY_INT",
160
+ netLabelWidth: 1.08,
161
+ anchoredNetLabelWidth: 1.08,
162
+ allowInlineNetLabel: true,
163
+ inlineNetLabelHeight: 0.12,
164
+ inlineNetLabelWidth: 0.72,
165
+ pinIds: ["schematic_port_3", "schematic_port_8"],
166
+ },
167
+ {
168
+ netId: "GND",
169
+ netLabelWidth: 0.42,
170
+ netLabelHeight: 0.48,
171
+ pinIds: ["schematic_port_4", "schematic_port_6", "schematic_port_7"],
172
+ },
173
+ ],
174
+ textBoxes: [
175
+ {
176
+ chipId: "schematic_component_0",
177
+ center: { x: 3.38, y: -2.4299999999999997 },
178
+ width: 0.3599999999999999,
179
+ height: 0.17999999999999972,
180
+ text: "MCU",
181
+ },
182
+ {
183
+ chipId: "schematic_component_0",
184
+ center: { x: 3.3200000000000003, y: 2.415 },
185
+ width: 0.3600000000000003,
186
+ height: 0.24999999999999956,
187
+ text: "U2",
188
+ },
189
+ {
190
+ chipId: "schematic_component_1",
191
+ center: { x: -3.04, y: -2.13 },
192
+ width: 1.3199999999999998,
193
+ height: 0.17999999999999972,
194
+ text: "HDC2080DMBR",
195
+ },
196
+ {
197
+ chipId: "schematic_component_1",
198
+ center: { x: -3.58, y: 2.1149999999999998 },
199
+ width: 0.3600000000000003,
200
+ height: 0.24999999999999978,
201
+ text: "U1",
202
+ },
203
+ ],
204
+ availableNetLabelOrientations: {
205
+ VDD: ["y+"],
206
+ SCL: ["x-", "x+"],
207
+ SDA: ["x-", "x+"],
208
+ DRDY_INT: ["x-", "x+"],
209
+ GND: ["y-"],
210
+ },
211
+ maxMspPairDistance: 2.4,
212
+ _hideRatsNet: false,
213
+ }
214
+
215
+ test("aligns the HDC2080 GND label with its vertical rail", () => {
216
+ const solver = new SchematicTracePipelineSolver(inputProblem)
217
+
218
+ solver.solve()
219
+
220
+ const orientationOutput = solver.availableNetOrientationSolver!.getOutput()
221
+ const groundLabel = orientationOutput.netLabelPlacements.find(
222
+ (label) =>
223
+ label.netId === "GND" && label.pinIds.includes("schematic_port_6"),
224
+ )
225
+ const groundRailTrace = orientationOutput.traces.find(
226
+ (trace) => trace.mspPairId === "schematic_port_7-schematic_port_6",
227
+ )
228
+ const matchingGroundTraces = orientationOutput.traces.filter(
229
+ (trace) =>
230
+ trace.userNetId === "GND" &&
231
+ trace.pinIds.includes("schematic_port_6") &&
232
+ trace.pinIds.includes("schematic_port_7"),
233
+ )
234
+
235
+ expect(groundLabel?.anchorPoint.x).toBeCloseTo(0.1)
236
+ expect(groundRailTrace?.tracePath[1]?.x).toBeCloseTo(0.1)
237
+ expect(matchingGroundTraces).toHaveLength(1)
238
+ expect(solver).toMatchSolverSnapshot(import.meta.path)
239
+ })
@@ -59,20 +59,24 @@ test("repro TIDA-010076 J4 ground pins should share one vertical bus", () => {
59
59
  solver.solve()
60
60
 
61
61
  const output = solver.sameNetJunctionAlignmentSolver!.getOutput()
62
+ const groundConnection = inputProblem.netConnections[0]!
63
+ const groundPinIds = new Set(groundConnection.pinIds)
62
64
  const groundTraces = output.traces.filter(
63
65
  (trace) =>
64
- trace.userNetId === "NET_AGND" &&
65
- trace.pinIds.every((pinId) => pinId.startsWith("J4.")),
66
+ trace.userNetId === groundConnection.netId &&
67
+ trace.pinIds.every((pinId) => groundPinIds.has(pinId)),
66
68
  )
67
- const verticalBranchOffsets = new Set(
68
- groundTraces.map((trace) => trace.tracePath[1]!.x.toFixed(3)),
69
+ const verticalRailXs = groundTraces.map((trace) => trace.tracePath[1]!.x)
70
+ const groundLabel = output.netLabelPlacements.find(
71
+ (label) => label.netId === groundConnection.netId,
69
72
  )
70
73
 
71
- // Current mismatch: the first three GND pins use one vertical offset while
72
- // the final pair detours around the manufacturer text at a second offset.
73
- expect(verticalBranchOffsets.size).toBe(2)
74
- expect(
75
- output.netLabelPlacements.filter((label) => label.netId === "NET_AGND"),
76
- ).toHaveLength(1)
74
+ expect(new Set(verticalRailXs.map((x) => x.toFixed(3))).size).toBe(1)
75
+ expect(verticalRailXs[0]).toBeCloseTo(0.975)
76
+ expect(groundLabel?.anchorPoint.x).toBeCloseTo(verticalRailXs[0]!)
77
+ const productTextBox = inputProblem.textBoxes![0]!
78
+ expect(groundLabel?.anchorPoint.y).toBeLessThan(
79
+ productTextBox.center.y - productTextBox.height / 2,
80
+ )
77
81
  expect(solver).toMatchSolverSnapshot(import.meta.path)
78
82
  })
@@ -39,8 +39,8 @@ test("repro TIDA-010076 page 02 net-label placement", () => {
39
39
  )!
40
40
 
41
41
  expect(j5AgndLabel.orientation).toBe("y-")
42
- expect(j5AgndLabel.anchorPoint.y).toBeCloseTo(
43
- Math.min(...j5GroundPins.map((pin) => pin.y)),
42
+ expect(Math.abs(j5AgndLabel.anchorPoint.x - j5GroundPins[0]!.x)).toBeCloseTo(
43
+ 0.2,
44
44
  )
45
45
 
46
46
  const powerConnectorPinPairs = [
@@ -70,6 +70,6 @@ USER_LED_ANODE" data-x="1.5175" data-y="0" cx="324.7058823529412" cy="336.134453
70
70
  .port-label { fill: rgb(0, 100, 100); }
71
71
  .component-name { fill: rgb(0, 100, 100); }
72
72
 
73
- </style><rect class="boundary" x="0" y="0" width="1200" height="800"/><g class="trace sch-trace" data-layer="base" data-circuit-json-type="schematic_trace" data-schematic-trace-id="schematic_trace_0__stack1"><path d="M 461.18251928022 464.78149100256 L 602.6992287917735 464.78149100256 L 602.6992287917735 418.5089974293 L 744.215938303327 418.5089974293" class="trace-invisible-hover-outline sch-trace-hitbox" stroke="rgb(0, 150, 0)" fill="none" stroke-width="24.678663239071998px" stroke-linecap="round" opacity="0" stroke-linejoin="round"/><path class="sch-trace-path" d="M 461.18251928022 464.78149100256 L 602.6992287917735 464.78149100256 L 602.6992287917735 418.5089974293 L 744.215938303327 418.5089974293" stroke="rgb(0, 150, 0)" fill="none" stroke-width="3.0848329048839997px" stroke-linecap="round" stroke-linejoin="round"/></g><g class="trace-overlays sch-trace-overlays" data-layer="overlay" data-circuit-json-type="schematic_trace" data-schematic-trace-id="schematic_trace_0__stack1"/><g class="sch-component" data-circuit-json-type="schematic_component" data-schematic-component-id="schematic_component_0__stack1"><rect class="component chip sch-component-body" x="337.78920308486" y="341.3881748072" width="61.69665809768003" height="154.24164524419996" stroke-width="3.0848329048839997px" fill="rgb(255, 255, 194)" stroke="rgb(132, 0, 0)"/><rect class="component-overlay sch-component-overlay" x="337.78920308486" y="341.3881748072" width="61.69665809768003" height="154.24164524419996" fill="transparent"/><text class="sch-text" x="368.6375321337" y="418.5089974293" fill="#0f172a" text-anchor="middle" dominant-baseline="middle" font-family="sans-serif" font-size="27.763496143955997px" transform="rotate(0, 368.6375321337, 418.5089974293)">schematic_component_0</text><line class="component-pin sch-component-pin sch-port-line" x1="337.78920308486" y1="372.23650385604" x2="279.177377892064" y2="372.23650385604" stroke-width="3.0848329048839997px"/><g class="sch-port" data-schematic-port-id="source_port_0_0__stack1"><circle class="component-pin sch-component-pin sch-port-terminal" cx="276.09254498718" cy="372.23650385604" r="3.0848329048839997" stroke-width="3.0848329048839997px"/><rect x="273.007712082296" y="369.15167095115595" width="6.1696658097679995" height="6.1696658097679995" opacity="0"/></g><text class="pin-number sch-pin-number" x="306.94087403602" y="369.151670951156" style="font-family: sans-serif;" fill="rgb(169, 0, 0)" text-anchor="middle" dominant-baseline="auto" font-size="23.136246786629997px" transform=""></text><text class="pin-number sch-pin-label" x="353.21336760928" y="372.23650385604" style="font-family: sans-serif;" fill="rgb(169, 0, 0)" text-anchor="start" dominant-baseline="middle" font-size="23.136246786629997px" transform="">schematic_port_0</text><line class="component-pin sch-component-pin sch-port-line" x1="337.78920308486" y1="403.08483290488" x2="279.177377892064" y2="403.08483290488" stroke-width="3.0848329048839997px"/><g class="sch-port" data-schematic-port-id="source_port_0_1__stack1"><circle class="component-pin sch-component-pin sch-port-terminal" cx="276.09254498718" cy="403.08483290488" r="3.0848329048839997" stroke-width="3.0848329048839997px"/><rect x="273.007712082296" y="399.99999999999596" width="6.1696658097679995" height="6.1696658097679995" opacity="0"/></g><text class="pin-number sch-pin-number" x="306.94087403602" y="399.999999999996" style="font-family: sans-serif;" fill="rgb(169, 0, 0)" text-anchor="middle" dominant-baseline="auto" font-size="23.136246786629997px" transform=""></text><text class="pin-number sch-pin-label" x="353.21336760928" y="403.08483290488" style="font-family: sans-serif;" fill="rgb(169, 0, 0)" text-anchor="start" dominant-baseline="middle" font-size="23.136246786629997px" transform="">schematic_port_1</text><line class="component-pin sch-component-pin sch-port-line" x1="337.78920308486" y1="433.93316195372" x2="279.177377892064" y2="433.93316195372" stroke-width="3.0848329048839997px"/><g class="sch-port" data-schematic-port-id="source_port_0_2__stack1"><circle class="component-pin sch-component-pin sch-port-terminal" cx="276.09254498718" cy="433.93316195372" r="3.0848329048839997" stroke-width="3.0848329048839997px"/><rect x="273.007712082296" y="430.848329048836" width="6.1696658097679995" height="6.1696658097679995" opacity="0"/></g><text class="pin-number sch-pin-number" x="306.94087403602" y="430.848329048836" style="font-family: sans-serif;" fill="rgb(169, 0, 0)" text-anchor="middle" dominant-baseline="auto" font-size="23.136246786629997px" transform=""></text><text class="pin-number sch-pin-label" x="353.21336760928" y="433.93316195372" style="font-family: sans-serif;" fill="rgb(169, 0, 0)" text-anchor="start" dominant-baseline="middle" font-size="23.136246786629997px" transform="">schematic_port_2</text><line class="component-pin sch-component-pin sch-port-line" x1="337.78920308486" y1="464.78149100256" x2="279.177377892064" y2="464.78149100256" stroke-width="3.0848329048839997px"/><g class="sch-port" data-schematic-port-id="source_port_0_3__stack1"><circle class="component-pin sch-component-pin sch-port-terminal" cx="276.09254498718" cy="464.78149100256" r="3.0848329048839997" stroke-width="3.0848329048839997px"/><rect x="273.007712082296" y="461.696658097676" width="6.1696658097679995" height="6.1696658097679995" opacity="0"/></g><text class="pin-number sch-pin-number" x="306.94087403602" y="461.696658097676" style="font-family: sans-serif;" fill="rgb(169, 0, 0)" text-anchor="middle" dominant-baseline="auto" font-size="23.136246786629997px" transform=""></text><text class="pin-number sch-pin-label" x="353.21336760928" y="464.78149100256" style="font-family: sans-serif;" fill="rgb(169, 0, 0)" text-anchor="start" dominant-baseline="middle" font-size="23.136246786629997px" transform="">schematic_port_3</text><line class="component-pin sch-component-pin sch-port-line" x1="399.48586118254" y1="464.78149100256" x2="461.18251928022" y2="464.78149100256" stroke-width="3.0848329048839997px"/><g class="sch-port" data-schematic-port-id="source_port_0_4__stack1"><rect x="458.09768637533597" y="461.696658097676" width="6.1696658097679995" height="6.1696658097679995" opacity="0"/></g><text class="pin-number sch-pin-number" x="430.33419023138003" y="461.696658097676" style="font-family: sans-serif;" fill="rgb(169, 0, 0)" text-anchor="middle" dominant-baseline="auto" font-size="23.136246786629997px" transform=""></text><text class="pin-number sch-pin-label" x="384.06169665812" y="464.78149100256" style="font-family: sans-serif;" fill="rgb(169, 0, 0)" text-anchor="end" dominant-baseline="middle" font-size="23.136246786629997px" transform="">schematic_port_4</text><line class="component-pin sch-component-pin sch-port-line" x1="399.48586118254" y1="433.93316195372" x2="458.097686375336" y2="433.93316195372" stroke-width="3.0848329048839997px"/><g class="sch-port" data-schematic-port-id="source_port_0_5__stack1"><circle class="component-pin sch-component-pin sch-port-terminal" cx="461.18251928022" cy="433.93316195372" r="3.0848329048839997" stroke-width="3.0848329048839997px"/><rect x="458.09768637533597" y="430.848329048836" width="6.1696658097679995" height="6.1696658097679995" opacity="0"/></g><text class="pin-number sch-pin-number" x="430.33419023138003" y="430.848329048836" style="font-family: sans-serif;" fill="rgb(169, 0, 0)" text-anchor="middle" dominant-baseline="auto" font-size="23.136246786629997px" transform=""></text><text class="pin-number sch-pin-label" x="384.06169665812" y="433.93316195372" style="font-family: sans-serif;" fill="rgb(169, 0, 0)" text-anchor="end" dominant-baseline="middle" font-size="23.136246786629997px" transform="">schematic_port_5</text><line class="component-pin sch-component-pin sch-port-line" x1="399.48586118254" y1="403.08483290488" x2="458.097686375336" y2="403.08483290488" stroke-width="3.0848329048839997px"/><g class="sch-port" data-schematic-port-id="source_port_0_6__stack1"><circle class="component-pin sch-component-pin sch-port-terminal" cx="461.18251928022" cy="403.08483290488" r="3.0848329048839997" stroke-width="3.0848329048839997px"/><rect x="458.09768637533597" y="399.99999999999596" width="6.1696658097679995" height="6.1696658097679995" opacity="0"/></g><text class="pin-number sch-pin-number" x="430.33419023138003" y="399.999999999996" style="font-family: sans-serif;" fill="rgb(169, 0, 0)" text-anchor="middle" dominant-baseline="auto" font-size="23.136246786629997px" transform=""></text><text class="pin-number sch-pin-label" x="384.06169665812" y="403.08483290488" style="font-family: sans-serif;" fill="rgb(169, 0, 0)" text-anchor="end" dominant-baseline="middle" font-size="23.136246786629997px" transform="">schematic_port_6</text><line class="component-pin sch-component-pin sch-port-line" x1="399.48586118254" y1="372.23650385604" x2="458.097686375336" y2="372.23650385604" stroke-width="3.0848329048839997px"/><g class="sch-port" data-schematic-port-id="source_port_0_7__stack1"><circle class="component-pin sch-component-pin sch-port-terminal" cx="461.18251928022" cy="372.23650385604" r="3.0848329048839997" stroke-width="3.0848329048839997px"/><rect x="458.09768637533597" y="369.15167095115595" width="6.1696658097679995" height="6.1696658097679995" opacity="0"/></g><text class="pin-number sch-pin-number" x="430.33419023138003" y="369.151670951156" style="font-family: sans-serif;" fill="rgb(169, 0, 0)" text-anchor="middle" dominant-baseline="auto" font-size="23.136246786629997px" transform=""></text><text class="pin-number sch-pin-label" x="384.06169665812" y="372.23650385604" style="font-family: sans-serif;" fill="rgb(169, 0, 0)" text-anchor="end" dominant-baseline="middle" font-size="23.136246786629997px" transform="">schematic_port_7</text></g><g class="sch-component" data-circuit-json-type="schematic_component" data-schematic-component-id="schematic_component_1__stack1"><rect class="component chip sch-component-body" x="805.912596401007" y="327.506426735222" width="50.899742930585944" height="141.13110539844297" stroke-width="3.0848329048839997px" fill="rgb(255, 255, 194)" stroke="rgb(132, 0, 0)"/><rect class="component-overlay sch-component-overlay" x="805.912596401007" y="327.506426735222" width="50.899742930585944" height="141.13110539844297" fill="transparent"/><text class="sch-text" x="831.3624678663" y="398.0719794344435" fill="#0f172a" text-anchor="middle" dominant-baseline="middle" font-family="sans-serif" font-size="27.763496143955997px" transform="rotate(0, 831.3624678663, 398.0719794344435)">schematic_component_1</text><line class="component-pin sch-component-pin sch-port-line" x1="805.912596401007" y1="418.5089974293" x2="744.215938303327" y2="418.5089974293" stroke-width="3.0848329048839997px"/><g class="sch-port" data-schematic-port-id="source_port_1_0__stack1"><rect x="741.131105398443" y="415.42416452441597" width="6.1696658097679995" height="6.1696658097679995" opacity="0"/></g><text class="pin-number sch-pin-number" x="775.064267352167" y="415.42416452441597" style="font-family: sans-serif;" fill="rgb(169, 0, 0)" text-anchor="middle" dominant-baseline="auto" font-size="23.136246786629997px" transform=""></text><text class="pin-number sch-pin-label" x="821.3367609254269" y="418.5089974293" style="font-family: sans-serif;" fill="rgb(169, 0, 0)" text-anchor="start" dominant-baseline="middle" font-size="23.136246786629997px" transform="">schematic_port_8</text><line class="component-pin sch-component-pin sch-port-line" x1="856.812339331593" y1="418.5089974293" x2="915.424164524389" y2="418.5089974293" stroke-width="3.0848329048839997px"/><g class="sch-port" data-schematic-port-id="source_port_1_1__stack1"><circle class="component-pin sch-component-pin sch-port-terminal" cx="918.508997429273" cy="418.5089974293" r="3.0848329048839997" stroke-width="3.0848329048839997px"/><rect x="915.424164524389" y="415.42416452441597" width="6.1696658097679995" height="6.1696658097679995" opacity="0"/></g><text class="pin-number sch-pin-number" x="887.660668380433" y="415.42416452441597" style="font-family: sans-serif;" fill="rgb(169, 0, 0)" text-anchor="middle" dominant-baseline="auto" font-size="23.136246786629997px" transform=""></text><text class="pin-number sch-pin-label" x="841.388174807173" y="418.5089974293" style="font-family: sans-serif;" fill="rgb(169, 0, 0)" text-anchor="end" dominant-baseline="middle" font-size="23.136246786629997px" transform="">schematic_port_9</text></g><g class="schematic-port-hover sch-port-hover" data-schematic-port-id="source_port_0_0__stack1"><circle cx="276.09254498718" cy="372.23650385604" r="6.1696658097679995" fill="red" opacity="0"/></g><g class="schematic-port-hover sch-port-hover" data-schematic-port-id="source_port_0_1__stack1"><circle cx="276.09254498718" cy="403.08483290488" r="6.1696658097679995" fill="red" opacity="0"/></g><g class="schematic-port-hover sch-port-hover" data-schematic-port-id="source_port_0_2__stack1"><circle cx="276.09254498718" cy="433.93316195372" r="6.1696658097679995" fill="red" opacity="0"/></g><g class="schematic-port-hover sch-port-hover" data-schematic-port-id="source_port_0_3__stack1"><circle cx="276.09254498718" cy="464.78149100256" r="6.1696658097679995" fill="red" opacity="0"/></g><g class="schematic-port-hover sch-port-hover" data-schematic-port-id="source_port_0_4__stack1"><circle cx="461.18251928022" cy="464.78149100256" r="6.1696658097679995" fill="red" opacity="0"/></g><g class="schematic-port-hover sch-port-hover" data-schematic-port-id="source_port_0_5__stack1"><circle cx="461.18251928022" cy="433.93316195372" r="6.1696658097679995" fill="red" opacity="0"/></g><g class="schematic-port-hover sch-port-hover" data-schematic-port-id="source_port_0_6__stack1"><circle cx="461.18251928022" cy="403.08483290488" r="6.1696658097679995" fill="red" opacity="0"/></g><g class="schematic-port-hover sch-port-hover" data-schematic-port-id="source_port_0_7__stack1"><circle cx="461.18251928022" cy="372.23650385604" r="6.1696658097679995" fill="red" opacity="0"/></g><g class="schematic-port-hover sch-port-hover" data-schematic-port-id="source_port_1_0__stack1"><circle cx="744.215938303327" cy="418.5089974293" r="6.1696658097679995" fill="red" opacity="0"/></g><g class="schematic-port-hover sch-port-hover" data-schematic-port-id="source_port_1_1__stack1"><circle cx="918.508997429273" cy="418.5089974293" r="6.1696658097679995" fill="red" opacity="0"/></g><text class="sch-text" x="356.298200514164" y="323.650385604117" fill="#7e22ce" text-anchor="middle" dominant-baseline="middle" font-family="sans-serif" font-size="27.763496143955997px" transform="rotate(0, 356.298200514164, 323.650385604117)">U1</text><text class="sch-text" x="602.6992287917735" y="396.915167095112" fill="#047857" text-anchor="middle" dominant-baseline="middle" font-family="sans-serif" font-size="27.763496143955997px" transform="rotate(0, 602.6992287917735, 396.915167095112)">XXXXXXXXXXXXXX</text><rect class="schematic-box sch-box" x="328.53470437020803" y="304.370179948592" width="55.52699228791198" height="38.56041131105002" stroke-width="3.0848329048839997px" stroke="rgb(132, 0, 0)" fill="transparent" stroke-dasharray="24.678663239071998 12.339331619535999"/>
73
+ </style><rect class="boundary" x="0" y="0" width="1200" height="800"/><g class="trace sch-trace" data-layer="base" data-circuit-json-type="schematic_trace" data-schematic-trace-id="schematic_trace_0__stack1"><path d="M 402.60950605784 493.94221808017 L 607.8285181734233 493.94221808017 L 607.8285181734233 426.8406337372 L 813.0475302890065 426.8406337372" class="trace-invisible-hover-outline sch-trace-hitbox" stroke="rgb(0, 150, 0)" fill="none" stroke-width="35.787511649584px" stroke-linecap="round" opacity="0" stroke-linejoin="round"/><path class="sch-trace-path" d="M 402.60950605784 493.94221808017 L 607.8285181734233 493.94221808017 L 607.8285181734233 426.8406337372 L 813.0475302890065 426.8406337372" stroke="rgb(0, 150, 0)" fill="none" stroke-width="4.473438956198px" stroke-linecap="round" stroke-linejoin="round"/></g><g class="trace-overlays sch-trace-overlays" data-layer="overlay" data-circuit-json-type="schematic_trace" data-schematic-trace-id="schematic_trace_0__stack1"/><g class="sch-component" data-circuit-json-type="schematic_component" data-schematic-component-id="schematic_component_0__stack1"><rect class="component chip sch-component-body" x="223.67194780992" y="315.00465983225" width="89.46877912396005" height="223.67194780989996" stroke-width="4.473438956198px" fill="rgb(255, 255, 194)" stroke="rgb(132, 0, 0)"/><rect class="component-overlay sch-component-overlay" x="223.67194780992" y="315.00465983225" width="89.46877912396005" height="223.67194780989996" fill="transparent"/><line class="component-pin sch-component-pin sch-port-line" x1="223.67194780992" y1="359.73904939423" x2="138.676607642158" y2="359.73904939423" stroke-width="4.473438956198px"/><g class="sch-port" data-schematic-port-id="source_port_0_0__stack1"><circle class="component-pin sch-component-pin sch-port-terminal" cx="134.20316868596" cy="359.73904939423" r="4.473438956198" stroke-width="4.473438956198px"/><rect x="129.729729729762" y="355.265610438032" width="8.946877912396" height="8.946877912396" opacity="0"/></g><text class="pin-number sch-pin-number" x="178.93755824794" y="355.265610438032" style="font-family: sans-serif;" fill="rgb(169, 0, 0)" text-anchor="middle" dominant-baseline="auto" font-size="33.550792171485px" transform=""></text><line class="component-pin sch-component-pin sch-port-line" x1="223.67194780992" y1="404.47343895621" x2="138.676607642158" y2="404.47343895621" stroke-width="4.473438956198px"/><g class="sch-port" data-schematic-port-id="source_port_0_1__stack1"><circle class="component-pin sch-component-pin sch-port-terminal" cx="134.20316868596" cy="404.47343895621" r="4.473438956198" stroke-width="4.473438956198px"/><rect x="129.729729729762" y="400.000000000012" width="8.946877912396" height="8.946877912396" opacity="0"/></g><text class="pin-number sch-pin-number" x="178.93755824794" y="400.000000000012" style="font-family: sans-serif;" fill="rgb(169, 0, 0)" text-anchor="middle" dominant-baseline="auto" font-size="33.550792171485px" transform=""></text><line class="component-pin sch-component-pin sch-port-line" x1="223.67194780992" y1="449.20782851819" x2="138.676607642158" y2="449.20782851819" stroke-width="4.473438956198px"/><g class="sch-port" data-schematic-port-id="source_port_0_2__stack1"><circle class="component-pin sch-component-pin sch-port-terminal" cx="134.20316868596" cy="449.20782851819" r="4.473438956198" stroke-width="4.473438956198px"/><rect x="129.729729729762" y="444.73438956199203" width="8.946877912396" height="8.946877912396" opacity="0"/></g><text class="pin-number sch-pin-number" x="178.93755824794" y="444.734389561992" style="font-family: sans-serif;" fill="rgb(169, 0, 0)" text-anchor="middle" dominant-baseline="auto" font-size="33.550792171485px" transform=""></text><line class="component-pin sch-component-pin sch-port-line" x1="223.67194780992" y1="493.94221808017" x2="138.676607642158" y2="493.94221808017" stroke-width="4.473438956198px"/><g class="sch-port" data-schematic-port-id="source_port_0_3__stack1"><circle class="component-pin sch-component-pin sch-port-terminal" cx="134.20316868596" cy="493.94221808017" r="4.473438956198" stroke-width="4.473438956198px"/><rect x="129.729729729762" y="489.468779123972" width="8.946877912396" height="8.946877912396" opacity="0"/></g><text class="pin-number sch-pin-number" x="178.93755824794" y="489.468779123972" style="font-family: sans-serif;" fill="rgb(169, 0, 0)" text-anchor="middle" dominant-baseline="auto" font-size="33.550792171485px" transform=""></text><line class="component-pin sch-component-pin sch-port-line" x1="313.14072693388005" y1="493.94221808017" x2="402.60950605784" y2="493.94221808017" stroke-width="4.473438956198px"/><g class="sch-port" data-schematic-port-id="source_port_0_4__stack1"><rect x="398.136067101642" y="489.468779123972" width="8.946877912396" height="8.946877912396" opacity="0"/></g><text class="pin-number sch-pin-number" x="357.87511649586" y="489.468779123972" style="font-family: sans-serif;" fill="rgb(169, 0, 0)" text-anchor="middle" dominant-baseline="auto" font-size="33.550792171485px" transform=""></text><line class="component-pin sch-component-pin sch-port-line" x1="313.14072693388005" y1="449.20782851819" x2="398.136067101642" y2="449.20782851819" stroke-width="4.473438956198px"/><g class="sch-port" data-schematic-port-id="source_port_0_5__stack1"><circle class="component-pin sch-component-pin sch-port-terminal" cx="402.60950605784" cy="449.20782851819" r="4.473438956198" stroke-width="4.473438956198px"/><rect x="398.136067101642" y="444.73438956199203" width="8.946877912396" height="8.946877912396" opacity="0"/></g><text class="pin-number sch-pin-number" x="357.87511649586" y="444.734389561992" style="font-family: sans-serif;" fill="rgb(169, 0, 0)" text-anchor="middle" dominant-baseline="auto" font-size="33.550792171485px" transform=""></text><line class="component-pin sch-component-pin sch-port-line" x1="313.14072693388005" y1="404.47343895621" x2="398.136067101642" y2="404.47343895621" stroke-width="4.473438956198px"/><g class="sch-port" data-schematic-port-id="source_port_0_6__stack1"><circle class="component-pin sch-component-pin sch-port-terminal" cx="402.60950605784" cy="404.47343895621" r="4.473438956198" stroke-width="4.473438956198px"/><rect x="398.136067101642" y="400.000000000012" width="8.946877912396" height="8.946877912396" opacity="0"/></g><text class="pin-number sch-pin-number" x="357.87511649586" y="400.000000000012" style="font-family: sans-serif;" fill="rgb(169, 0, 0)" text-anchor="middle" dominant-baseline="auto" font-size="33.550792171485px" transform=""></text><line class="component-pin sch-component-pin sch-port-line" x1="313.14072693388005" y1="359.73904939423" x2="398.136067101642" y2="359.73904939423" stroke-width="4.473438956198px"/><g class="sch-port" data-schematic-port-id="source_port_0_7__stack1"><circle class="component-pin sch-component-pin sch-port-terminal" cx="402.60950605784" cy="359.73904939423" r="4.473438956198" stroke-width="4.473438956198px"/><rect x="398.136067101642" y="355.265610438032" width="8.946877912396" height="8.946877912396" opacity="0"/></g><text class="pin-number sch-pin-number" x="357.87511649586" y="355.265610438032" style="font-family: sans-serif;" fill="rgb(169, 0, 0)" text-anchor="middle" dominant-baseline="auto" font-size="33.550792171485px" transform=""></text></g><g class="sch-component" data-circuit-json-type="schematic_component" data-schematic-component-id="schematic_component_1__stack1"><rect class="component chip sch-component-body" x="902.5163094129665" y="294.874184529359" width="73.811742777267" height="204.6598322460585" stroke-width="4.473438956198px" fill="rgb(255, 255, 194)" stroke="rgb(132, 0, 0)"/><rect class="component-overlay sch-component-overlay" x="902.5163094129665" y="294.874184529359" width="73.811742777267" height="204.6598322460585" fill="transparent"/><line class="component-pin sch-component-pin sch-port-line" x1="902.5163094129665" y1="426.8406337372" x2="813.0475302890065" y2="426.8406337372" stroke-width="4.473438956198px"/><g class="sch-port" data-schematic-port-id="source_port_1_0__stack1"><rect x="808.5740913328085" y="422.367194781002" width="8.946877912396" height="8.946877912396" opacity="0"/></g><text class="pin-number sch-pin-number" x="857.7819198509866" y="422.367194781002" style="font-family: sans-serif;" fill="rgb(169, 0, 0)" text-anchor="middle" dominant-baseline="auto" font-size="33.550792171485px" transform=""></text><line class="component-pin sch-component-pin sch-port-line" x1="976.3280521902335" y1="426.8406337372" x2="1061.3233923579955" y2="426.8406337372" stroke-width="4.473438956198px"/><g class="sch-port" data-schematic-port-id="source_port_1_1__stack1"><circle class="component-pin sch-component-pin sch-port-terminal" cx="1065.7968313141935" cy="426.8406337372" r="4.473438956198" stroke-width="4.473438956198px"/><rect x="1061.3233923579955" y="422.367194781002" width="8.946877912396" height="8.946877912396" opacity="0"/></g><text class="pin-number sch-pin-number" x="1021.0624417522134" y="422.367194781002" style="font-family: sans-serif;" fill="rgb(169, 0, 0)" text-anchor="middle" dominant-baseline="auto" font-size="33.550792171485px" transform=""></text></g><g class="schematic-port-hover sch-port-hover" data-schematic-port-id="source_port_0_0__stack1"><circle cx="134.20316868596" cy="359.73904939423" r="8.946877912396" fill="red" opacity="0"/></g><g class="schematic-port-hover sch-port-hover" data-schematic-port-id="source_port_0_1__stack1"><circle cx="134.20316868596" cy="404.47343895621" r="8.946877912396" fill="red" opacity="0"/></g><g class="schematic-port-hover sch-port-hover" data-schematic-port-id="source_port_0_2__stack1"><circle cx="134.20316868596" cy="449.20782851819" r="8.946877912396" fill="red" opacity="0"/></g><g class="schematic-port-hover sch-port-hover" data-schematic-port-id="source_port_0_3__stack1"><circle cx="134.20316868596" cy="493.94221808017" r="8.946877912396" fill="red" opacity="0"/></g><g class="schematic-port-hover sch-port-hover" data-schematic-port-id="source_port_0_4__stack1"><circle cx="402.60950605784" cy="493.94221808017" r="8.946877912396" fill="red" opacity="0"/></g><g class="schematic-port-hover sch-port-hover" data-schematic-port-id="source_port_0_5__stack1"><circle cx="402.60950605784" cy="449.20782851819" r="8.946877912396" fill="red" opacity="0"/></g><g class="schematic-port-hover sch-port-hover" data-schematic-port-id="source_port_0_6__stack1"><circle cx="402.60950605784" cy="404.47343895621" r="8.946877912396" fill="red" opacity="0"/></g><g class="schematic-port-hover sch-port-hover" data-schematic-port-id="source_port_0_7__stack1"><circle cx="402.60950605784" cy="359.73904939423" r="8.946877912396" fill="red" opacity="0"/></g><g class="schematic-port-hover sch-port-hover" data-schematic-port-id="source_port_1_0__stack1"><circle cx="813.0475302890065" cy="426.8406337372" r="8.946877912396" fill="red" opacity="0"/></g><g class="schematic-port-hover sch-port-hover" data-schematic-port-id="source_port_1_1__stack1"><circle cx="1065.7968313141935" cy="426.8406337372" r="8.946877912396" fill="red" opacity="0"/></g><text class="sch-text" x="250.512581547108" y="289.2823858341115" fill="#7e22ce" text-anchor="middle" dominant-baseline="middle" font-family="sans-serif" font-size="40.260950605782px" transform="rotate(0, 250.512581547108, 289.2823858341115)">U1</text><text class="sch-text" x="607.8285181734233" y="395.526561043814" fill="#047857" text-anchor="middle" dominant-baseline="middle" font-family="sans-serif" font-size="40.260950605782px" transform="rotate(0, 607.8285181734233, 395.526561043814)">XXXXXXXXXXXXXX</text><rect class="schematic-box sch-box" x="210.251630941326" y="261.323392357874" width="80.52190121156403" height="55.91798695247496" stroke-width="4.473438956198px" stroke="rgb(132, 0, 0)" fill="transparent" stroke-dasharray="35.787511649584 17.893755824792"/>
74
74
  </g>
75
75
  </svg>
@@ -0,0 +1,57 @@
1
+ import { expect, test } from "bun:test"
2
+ import { MspConnectionPairSolver } from "lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver"
3
+ import type { InputNetConnection, InputProblem } from "lib/types/InputProblem"
4
+ import inputProblemJson from "../../repros/assets/repro-bluetooth-controller-ground-decoupling-groups.input.json"
5
+
6
+ function solveNetPairs({
7
+ inputProblem,
8
+ netConnection,
9
+ }: {
10
+ inputProblem: InputProblem
11
+ netConnection: InputNetConnection
12
+ }) {
13
+ const netPinIds = new Set(netConnection.pinIds)
14
+ const solver = new MspConnectionPairSolver({ inputProblem })
15
+
16
+ solver.solve()
17
+
18
+ return solver.mspConnectionPairs.filter((pair) =>
19
+ pair.pins.every((pin) => netPinIds.has(pin.pinId)),
20
+ )
21
+ }
22
+
23
+ test("keeps grouped ground rails in separate rows", () => {
24
+ const inputProblem = inputProblemJson as InputProblem
25
+ const groundConnection = inputProblem.netConnections.find(
26
+ (connection) => connection.isGround,
27
+ )!
28
+ const groundPairs = solveNetPairs({
29
+ inputProblem,
30
+ netConnection: groundConnection,
31
+ })
32
+ const crossRowPairs = groundPairs.filter(
33
+ (pair) => pair.pins[0].y !== pair.pins[1].y,
34
+ )
35
+
36
+ expect(groundPairs).toHaveLength(5)
37
+ expect(crossRowPairs).toHaveLength(0)
38
+ })
39
+
40
+ test("does not separate rows without ground metadata", () => {
41
+ const inputProblem = structuredClone(inputProblemJson) as InputProblem
42
+ const groundConnection = inputProblem.netConnections.find(
43
+ (connection) => connection.isGround,
44
+ )!
45
+ groundConnection.isGround = false
46
+
47
+ const netPairs = solveNetPairs({
48
+ inputProblem,
49
+ netConnection: groundConnection,
50
+ })
51
+ const crossRowPairs = netPairs.filter(
52
+ (pair) => pair.pins[0].y !== pair.pins[1].y,
53
+ )
54
+
55
+ expect(netPairs).toHaveLength(6)
56
+ expect(crossRowPairs).toHaveLength(1)
57
+ })