@tscircuit/schematic-trace-solver 0.0.173 → 0.0.175

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 (43) hide show
  1. package/dist/index.d.ts +2 -0
  2. package/dist/index.js +200 -74
  3. package/lib/solvers/AvailableNetOrientationSolver/AvailableNetOrientationSolver.ts +12 -1
  4. package/lib/solvers/InlineNetLabelSolver/InlineNetLabelSolver.ts +16 -1
  5. package/lib/solvers/InlineNetLabelSolver/restoreReroutesAroundSupersededLabels.ts +174 -0
  6. package/lib/solvers/NetLabelToTraceSolver/NetLabelToTraceSolver.ts +2 -30
  7. package/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts +9 -0
  8. package/lib/utils/doesPathCoincideWithTraces.ts +17 -4
  9. package/lib/utils/pathIntersectsRenderedLabel.ts +33 -0
  10. package/package.json +1 -1
  11. package/site/bug-reports/bug-report-20260831T133233Z.page.tsx +4 -0
  12. package/site/bug-reports/bug-report-20260901T055358Z.page.tsx +4 -0
  13. package/site/bug-reports/bug-report-20260901T064117Z.page.tsx +4 -0
  14. package/site/bug-reports/bug-report-20260901T134241Z.page.tsx +4 -0
  15. package/tests/bug-reports/bug-report-20260721T221026Z/__snapshots__/bug-report-20260721T221026Z.snap.svg +18 -18
  16. package/tests/bug-reports/bug-report-20260831T133233Z/__snapshots__/bug-report-20260831T133233Z.snap.svg +85 -0
  17. package/tests/bug-reports/bug-report-20260831T133233Z/bug-report-20260831T133233Z.json +172 -0
  18. package/tests/bug-reports/bug-report-20260831T133233Z/bug-report-20260831T133233Z.test.ts +25 -0
  19. package/tests/bug-reports/bug-report-20260901T055358Z/__snapshots__/bug-report-20260901T055358Z.snap.svg +676 -0
  20. package/tests/bug-reports/bug-report-20260901T055358Z/bug-report-20260901T055358Z.json +2467 -0
  21. package/tests/bug-reports/bug-report-20260901T055358Z/bug-report-20260901T055358Z.test.ts +59 -0
  22. package/tests/bug-reports/bug-report-20260901T064117Z/__snapshots__/bug-report-20260901T064117Z.snap.svg +134 -0
  23. package/tests/bug-reports/bug-report-20260901T064117Z/bug-report-20260901T064117Z.json +215 -0
  24. package/tests/bug-reports/bug-report-20260901T064117Z/bug-report-20260901T064117Z.test.ts +12 -0
  25. package/tests/bug-reports/bug-report-20260901T134241Z/__snapshots__/bug-report-20260901T134241Z.snap.svg +115 -0
  26. package/tests/bug-reports/bug-report-20260901T134241Z/bug-report-20260901T134241Z.json +359 -0
  27. package/tests/bug-reports/bug-report-20260901T134241Z/bug-report-20260901T134241Z.test.ts +12 -0
  28. package/tests/repros/__snapshots__/repro-bq40z60-current-sense.snap.svg +94 -0
  29. package/tests/repros/__snapshots__/repro-pmp11282-isolated-dcdc.snap.svg +10 -10
  30. package/tests/repros/__snapshots__/repro-ti-power-output-section.snap.svg +87 -87
  31. package/tests/repros/__snapshots__/repro-tida-01389-hbridge.snap.svg +47 -153
  32. package/tests/repros/__snapshots__/repro-tida010076-page02.snap.svg +1625 -1625
  33. package/tests/repros/__snapshots__/repro-trellis-core-gnd-net-label-overlap.snap.svg +134 -0
  34. package/tests/repros/__snapshots__/repro-wireless-mouse-charger-section.snap.svg +164 -0
  35. package/tests/repros/assets/repro-bq40z60-current-sense.input.json +158 -0
  36. package/tests/repros/assets/repro-tida-01389-hbridge.input.json +67 -657
  37. package/tests/repros/assets/repro-trellis-core-gnd-net-label-overlap.input.json +178 -0
  38. package/tests/repros/assets/repro-wireless-mouse-charger-section.input.json +591 -0
  39. package/tests/repros/repro-bq40z60-current-sense.test.ts +18 -0
  40. package/tests/repros/repro-tida-01389-hbridge.test.ts +27 -6
  41. package/tests/repros/repro-trellis-core-gnd-net-label-overlap.test.ts +73 -0
  42. package/tests/repros/repro-wireless-mouse-charger-section.test.ts +16 -0
  43. package/tests/solvers/InlineNetLabelSolver/restore-reroutes-around-superseded-labels.test.ts +186 -0
@@ -0,0 +1,73 @@
1
+ import { expect, test } from "bun:test"
2
+ import type { NetLabelPlacement } from "lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver"
3
+ import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver"
4
+ import type { InputProblem } from "lib/types/InputProblem"
5
+ import "tests/fixtures/matcher"
6
+ import inputProblem from "./assets/repro-trellis-core-gnd-net-label-overlap.input.json"
7
+
8
+ const getLabelBounds = (label: NetLabelPlacement) => ({
9
+ minX: label.center.x - label.width / 2,
10
+ maxX: label.center.x + label.width / 2,
11
+ minY: label.center.y - label.height / 2,
12
+ maxY: label.center.y + label.height / 2,
13
+ })
14
+
15
+ const getOverlapArea = (
16
+ first: ReturnType<typeof getLabelBounds>,
17
+ second: ReturnType<typeof getLabelBounds>,
18
+ ) => {
19
+ const width =
20
+ Math.min(first.maxX, second.maxX) - Math.max(first.minX, second.minX)
21
+ const height =
22
+ Math.min(first.maxY, second.maxY) - Math.max(first.minY, second.minY)
23
+
24
+ return width > 0 && height > 0 ? width * height : 0
25
+ }
26
+
27
+ // Captured from @tscircuit/core repro175. The dense T113 analog-pin column
28
+ // forces Net_U3F_VRA2 to fall back from its requested y+ rail orientation to
29
+ // the pin-facing x+ orientation. The fallback reports a 0.42 x 0.20 label box
30
+ // even though the rendered text is 1.56 wide. The pipeline then finishes with
31
+ // the U3-side GND label overlapping both VRA2 and P1V8.
32
+ //
33
+ // This test pins the current buggy output. A fix should change the two positive
34
+ // overlap assertions to expect 0 and update the visual snapshot.
35
+ test("repro Trellis Core GND label overlaps VRA2 and P1V8", () => {
36
+ const solver = new SchematicTracePipelineSolver(inputProblem as InputProblem)
37
+
38
+ solver.solve()
39
+
40
+ const labels =
41
+ solver.netLabelNetLabelCollisionSolver!.getOutput().netLabelPlacements
42
+ const vra2Label = labels.find(
43
+ (label) =>
44
+ label.netId === "Net_U3F_VRA2" &&
45
+ label.pinIds.includes("schematic_port_1"),
46
+ )!
47
+ const p1v8Label = labels.find(
48
+ (label) =>
49
+ label.netId === "P1V8" && label.pinIds.includes("schematic_port_0"),
50
+ )!
51
+ const groundLabel = labels.find(
52
+ (label) =>
53
+ label.netId === "GND" &&
54
+ label.pinIds.includes("schematic_port_2") &&
55
+ label.pinIds.includes("schematic_port_4"),
56
+ )!
57
+
58
+ expect(vra2Label).toBeDefined()
59
+ expect(p1v8Label).toBeDefined()
60
+ expect(groundLabel).toBeDefined()
61
+ expect(vra2Label).toMatchObject({
62
+ orientation: "x+",
63
+ width: 0.42,
64
+ height: 0.2,
65
+ })
66
+ expect(
67
+ getOverlapArea(getLabelBounds(groundLabel), getLabelBounds(vra2Label)),
68
+ ).toBeGreaterThan(0)
69
+ expect(
70
+ getOverlapArea(getLabelBounds(groundLabel), getLabelBounds(p1v8Label)),
71
+ ).toBeGreaterThan(0)
72
+ expect(solver).toMatchSolverSnapshot(import.meta.path)
73
+ })
@@ -0,0 +1,16 @@
1
+ import { expect, test } from "bun:test"
2
+ import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver"
3
+ import inputProblem from "./assets/repro-wireless-mouse-charger-section.input.json"
4
+ import "tests/fixtures/matcher"
5
+
6
+ // Generated from @tscircuit/core 0.0.1817 at commit 45888ed648781adfd34e7078c4d4b336133cbbe9.
7
+ // The circuit reproduces the wireless mouse's MCP73831 charger, charge LED
8
+ // loop, programming resistor, VBAT capacitor, and protected battery connector.
9
+ // The fixture is the solverParams payload emitted by core's solver:started event.
10
+ test("repro wireless mouse charger section", () => {
11
+ const solver = new SchematicTracePipelineSolver(inputProblem as any)
12
+
13
+ solver.solve()
14
+
15
+ expect(solver).toMatchSolverSnapshot(import.meta.path)
16
+ })
@@ -0,0 +1,186 @@
1
+ import { expect, test } from "bun:test"
2
+ import type { InlineNetLabelPlacement } from "lib/solvers/InlineNetLabelSolver/InlineNetLabelSolver"
3
+ import { restoreReroutesAroundSupersededLabels } from "lib/solvers/InlineNetLabelSolver/restoreReroutesAroundSupersededLabels"
4
+ import type { NetLabelPlacement } from "lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver"
5
+ import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
6
+ import type { CompletedTraceReroute } from "lib/solvers/TraceElbowTransitionSimplificationSolver/types"
7
+ import type { InputProblem } from "lib/types/InputProblem"
8
+
9
+ const inputProblem: InputProblem = {
10
+ chips: [],
11
+ directConnections: [],
12
+ netConnections: [],
13
+ availableNetLabelOrientations: {},
14
+ textBoxes: [],
15
+ }
16
+
17
+ const initialPath = [
18
+ { x: 0, y: 0 },
19
+ { x: 4, y: 0 },
20
+ ]
21
+
22
+ const reroutedPath = [
23
+ { x: 0, y: 0 },
24
+ { x: 1.8, y: 0 },
25
+ { x: 1.8, y: 0.5 },
26
+ { x: 2.2, y: 0.5 },
27
+ { x: 2.2, y: 0 },
28
+ { x: 4, y: 0 },
29
+ ]
30
+
31
+ const initialTrace: SolvedTracePath = {
32
+ mspPairId: "ground-trace",
33
+ dcConnNetId: "ground-global",
34
+ globalConnNetId: "ground-global",
35
+ userNetId: "GND",
36
+ pins: [
37
+ { pinId: "ground-pin-1", chipId: "left", x: 0, y: 0 },
38
+ { pinId: "ground-pin-2", chipId: "right", x: 4, y: 0 },
39
+ ],
40
+ tracePath: initialPath,
41
+ mspConnectionPairIds: ["ground-trace"],
42
+ pinIds: ["ground-pin-1", "ground-pin-2"],
43
+ }
44
+
45
+ const supersededLabel: NetLabelPlacement = {
46
+ globalConnNetId: "label-global",
47
+ dcConnNetId: "label-global",
48
+ netId: "CC2",
49
+ mspConnectionPairIds: ["label-host"],
50
+ pinIds: ["label-pin-1", "label-pin-2"],
51
+ orientation: "y+",
52
+ anchorPoint: { x: 2, y: -0.2 },
53
+ center: { x: 2, y: 0 },
54
+ width: 0.2,
55
+ height: 0.4,
56
+ }
57
+
58
+ const clearInlineLabel: InlineNetLabelPlacement = {
59
+ globalConnNetId: "label-global",
60
+ netId: "CC2",
61
+ mspPairId: "label-host",
62
+ pinIds: ["label-pin-1", "label-pin-2"],
63
+ axis: "x",
64
+ anchorPoint: { x: 2, y: -0.5 },
65
+ center: { x: 2, y: -0.6 },
66
+ width: 0.4,
67
+ height: 0.1,
68
+ side: "y-",
69
+ }
70
+
71
+ const completedReroute: CompletedTraceReroute = {
72
+ initialTrace,
73
+ reroutedTracePath: reroutedPath,
74
+ label: supersededLabel,
75
+ detourCount: 0,
76
+ }
77
+
78
+ const makeReroutedTrace = (tracePath = reroutedPath): SolvedTracePath => ({
79
+ ...initialTrace,
80
+ tracePath,
81
+ })
82
+
83
+ const restore = ({
84
+ traces = [makeReroutedTrace()],
85
+ netLabelPlacements = [],
86
+ inlineNetLabelPlacements = [clearInlineLabel],
87
+ }: {
88
+ traces?: SolvedTracePath[]
89
+ netLabelPlacements?: NetLabelPlacement[]
90
+ inlineNetLabelPlacements?: InlineNetLabelPlacement[]
91
+ } = {}) =>
92
+ restoreReroutesAroundSupersededLabels({
93
+ inputProblem,
94
+ traces,
95
+ netLabelPlacements,
96
+ inlineNetLabelPlacements,
97
+ completedReroutes: [completedReroute],
98
+ })
99
+
100
+ test("restores the exact route after its blocking label is superseded", () => {
101
+ const result = restore()
102
+
103
+ expect(result.restoredTraceCount).toBe(1)
104
+ expect(result.traces[0]!.tracePath).toEqual(initialPath)
105
+ })
106
+
107
+ test("keeps the detour when inline conversion did not supersede the label", () => {
108
+ const result = restore({ inlineNetLabelPlacements: [] })
109
+
110
+ expect(result.restoredTraceCount).toBe(0)
111
+ expect(result.traces[0]!.tracePath).toEqual(reroutedPath)
112
+ })
113
+
114
+ test("keeps the detour when the final inline label occupies the old route", () => {
115
+ const result = restore({
116
+ inlineNetLabelPlacements: [
117
+ {
118
+ ...clearInlineLabel,
119
+ anchorPoint: { x: 2, y: 0 },
120
+ center: { x: 2, y: 0 },
121
+ },
122
+ ],
123
+ })
124
+
125
+ expect(result.restoredTraceCount).toBe(0)
126
+ expect(result.traces[0]!.tracePath).toEqual(reroutedPath)
127
+ })
128
+
129
+ test("keeps the detour when another trace crosses the old route", () => {
130
+ const blockingTrace: SolvedTracePath = {
131
+ ...initialTrace,
132
+ mspPairId: "blocking-trace",
133
+ dcConnNetId: "blocking-global",
134
+ globalConnNetId: "blocking-global",
135
+ userNetId: "BLOCKING",
136
+ tracePath: [
137
+ { x: 2, y: -1 },
138
+ { x: 2, y: 1 },
139
+ ],
140
+ }
141
+ const result = restore({ traces: [makeReroutedTrace(), blockingTrace] })
142
+
143
+ expect(result.restoredTraceCount).toBe(0)
144
+ expect(result.traces[0]!.tracePath).toEqual(reroutedPath)
145
+ })
146
+
147
+ test("allows an unchanged pre-existing trace crossing", () => {
148
+ const existingCrossing: SolvedTracePath = {
149
+ ...initialTrace,
150
+ mspPairId: "existing-crossing",
151
+ dcConnNetId: "existing-crossing-global",
152
+ globalConnNetId: "existing-crossing-global",
153
+ userNetId: "EXISTING_CROSSING",
154
+ tracePath: [
155
+ { x: 1, y: -1 },
156
+ { x: 1, y: 1 },
157
+ ],
158
+ }
159
+ const result = restore({ traces: [makeReroutedTrace(), existingCrossing] })
160
+
161
+ expect(result.restoredTraceCount).toBe(1)
162
+ expect(result.traces[0]!.tracePath).toEqual(initialPath)
163
+ })
164
+
165
+ test("keeps the detour when a retained anchored label occupies the old route", () => {
166
+ const retainedLabel: NetLabelPlacement = {
167
+ ...supersededLabel,
168
+ globalConnNetId: "retained-label-global",
169
+ dcConnNetId: "retained-label-global",
170
+ netId: "RETAINED",
171
+ }
172
+ const result = restore({ netLabelPlacements: [retainedLabel] })
173
+
174
+ expect(result.restoredTraceCount).toBe(0)
175
+ expect(result.traces[0]!.tracePath).toEqual(reroutedPath)
176
+ })
177
+
178
+ test("does not overwrite a trace changed after the recorded reroute", () => {
179
+ const changedPath = reroutedPath.map((point) => ({ ...point }))
180
+ changedPath[2]!.y = 0.6
181
+ changedPath[3]!.y = 0.6
182
+ const result = restore({ traces: [makeReroutedTrace(changedPath)] })
183
+
184
+ expect(result.restoredTraceCount).toBe(0)
185
+ expect(result.traces[0]!.tracePath).toEqual(changedPath)
186
+ })