@tscircuit/schematic-trace-solver 0.0.114 → 0.0.116

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 (27) hide show
  1. package/dist/index.d.ts +22 -9
  2. package/dist/index.js +248 -3
  3. package/lib/solvers/SameNetJunctionAlignmentSolver/SameNetJunctionAlignmentSolver.ts +71 -0
  4. package/lib/solvers/SameNetJunctionAlignmentSolver/alignSameNetJunctions.ts +223 -0
  5. package/lib/solvers/SameNetJunctionAlignmentSolver/pathIntersectsAnyNetLabel.ts +22 -0
  6. package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2.ts +9 -9
  7. package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/rect.ts +25 -9
  8. package/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts +17 -0
  9. package/package.json +2 -2
  10. package/site/bug-reports/bug-report-20260730T061837Z.page.tsx +4 -0
  11. package/tests/bug-reports/bug-report-20260721T221026Z/__snapshots__/bug-report-20260721T221026Z.snap.svg +1 -1
  12. package/tests/bug-reports/bug-report-20260728T144234Z/__snapshots__/bug-report-20260728T144234Z.snap.svg +1 -1
  13. package/tests/bug-reports/bug-report-20260728T144234Z/bug-report-20260728T144234Z.test.ts +6 -0
  14. package/tests/bug-reports/bug-report-20260730T061837Z/__snapshots__/bug-report-20260730T061837Z.snap.svg +123 -0
  15. package/tests/bug-reports/bug-report-20260730T061837Z/bug-report-20260730T061837Z.json +614 -0
  16. package/tests/bug-reports/bug-report-20260730T061837Z/bug-report-20260730T061837Z.test.ts +34 -0
  17. package/tests/repros/__snapshots__/board-15984-same-net-junction.snap.svg +110 -0
  18. package/tests/repros/__snapshots__/repro-type-c-16pin-self-connection.snap.svg +54 -0
  19. package/tests/repros/__snapshots__/repro-type-c-16pin-to-resistor.snap.svg +56 -0
  20. package/tests/repros/__snapshots__/repro48-555-timer-vcc-rail-label.snap.svg +87 -0
  21. package/tests/repros/assets/board-15984-schematic-trace-input.json +518 -0
  22. package/tests/repros/assets/repro48-555-timer-vcc-rail-label.input.json +276 -0
  23. package/tests/repros/board-15984-same-net-junction.test.ts +30 -0
  24. package/tests/repros/repro-type-c-16pin-self-connection.test.ts +97 -0
  25. package/tests/repros/repro-type-c-16pin-to-resistor.test.ts +120 -0
  26. package/tests/repros/repro48-555-timer-vcc-rail-label.test.ts +23 -0
  27. package/tests/solvers/SchematicTraceSingleLineSolver2/text-box-obstacle-gap.test.ts +43 -0
@@ -0,0 +1,276 @@
1
+ {
2
+ "chips": [
3
+ {
4
+ "chipId": "schematic_component_0",
5
+ "center": {
6
+ "x": 0.08000000000000007,
7
+ "y": -0.29500000000000004
8
+ },
9
+ "width": 2.4000000000000004,
10
+ "height": 1,
11
+ "pins": [
12
+ {
13
+ "pinId": "schematic_port_0",
14
+ "x": 1.2800000000000002,
15
+ "y": -0.5950000000000001
16
+ },
17
+ {
18
+ "pinId": "schematic_port_1",
19
+ "x": -1.12,
20
+ "y": -0.5950000000000001
21
+ },
22
+ {
23
+ "pinId": "schematic_port_2",
24
+ "x": 1.2800000000000002,
25
+ "y": -0.19500000000000006
26
+ },
27
+ {
28
+ "pinId": "schematic_port_3",
29
+ "x": -1.12,
30
+ "y": 0.0050000000000000044
31
+ },
32
+ {
33
+ "pinId": "schematic_port_4",
34
+ "x": -1.12,
35
+ "y": -0.195
36
+ },
37
+ {
38
+ "pinId": "schematic_port_5",
39
+ "x": -1.12,
40
+ "y": -0.395
41
+ },
42
+ {
43
+ "pinId": "schematic_port_6",
44
+ "x": 1.2800000000000002,
45
+ "y": -0.3950000000000001
46
+ },
47
+ {
48
+ "pinId": "schematic_port_7",
49
+ "x": 1.2800000000000002,
50
+ "y": 0.0050000000000000044
51
+ }
52
+ ]
53
+ },
54
+ {
55
+ "chipId": "schematic_component_1",
56
+ "center": {
57
+ "x": 2.3675,
58
+ "y": 0.8949999999999999
59
+ },
60
+ "width": 0.845,
61
+ "height": 0.5999999999999999,
62
+ "pins": [
63
+ {
64
+ "pinId": "schematic_port_8",
65
+ "x": 2.27,
66
+ "y": 1.1949999999999998,
67
+ "_facingDirection": "y+"
68
+ },
69
+ {
70
+ "pinId": "schematic_port_9",
71
+ "x": 2.27,
72
+ "y": 0.595,
73
+ "_facingDirection": "y-"
74
+ }
75
+ ]
76
+ },
77
+ {
78
+ "chipId": "schematic_component_2",
79
+ "center": {
80
+ "x": 2.2800000000000002,
81
+ "y": -0.39500000000000013
82
+ },
83
+ "width": 0.6000000000000001,
84
+ "height": 0.6799999999999999,
85
+ "pins": [
86
+ {
87
+ "pinId": "schematic_port_10",
88
+ "x": 1.9800000000000002,
89
+ "y": -0.39500000000000013
90
+ },
91
+ {
92
+ "pinId": "schematic_port_11",
93
+ "x": 2.58,
94
+ "y": -0.39500000000000013
95
+ }
96
+ ]
97
+ },
98
+ {
99
+ "chipId": "schematic_component_3",
100
+ "center": {
101
+ "x": -1.0099999999999996,
102
+ "y": -2.6999999999999993
103
+ },
104
+ "width": 0.9199999999999999,
105
+ "height": 0.7600000000000007,
106
+ "pins": [
107
+ {
108
+ "pinId": "schematic_port_12",
109
+ "x": -1.1449999999999996,
110
+ "y": -2.319999999999999,
111
+ "_facingDirection": "y+"
112
+ },
113
+ {
114
+ "pinId": "schematic_port_13",
115
+ "x": -1.1449999999999996,
116
+ "y": -3.0799999999999996,
117
+ "_facingDirection": "y-"
118
+ }
119
+ ]
120
+ },
121
+ {
122
+ "chipId": "schematic_component_4",
123
+ "center": {
124
+ "x": -2.0549999999999997,
125
+ "y": -0.4950000000000002
126
+ },
127
+ "width": 0.9200000000000004,
128
+ "height": 0.76,
129
+ "pins": [
130
+ {
131
+ "pinId": "schematic_port_14",
132
+ "x": -2.19,
133
+ "y": -0.11500000000000021,
134
+ "_facingDirection": "y+"
135
+ },
136
+ {
137
+ "pinId": "schematic_port_15",
138
+ "x": -2.19,
139
+ "y": -0.8750000000000002,
140
+ "_facingDirection": "y-"
141
+ }
142
+ ]
143
+ },
144
+ {
145
+ "chipId": "schematic_component_5",
146
+ "center": {
147
+ "x": 1.58,
148
+ "y": -3.1949999999999994
149
+ },
150
+ "width": 2.2,
151
+ "height": 0.7999999999999998,
152
+ "pins": [
153
+ {
154
+ "pinId": "schematic_port_16",
155
+ "x": 0.48,
156
+ "y": -2.994999999999999
157
+ },
158
+ {
159
+ "pinId": "schematic_port_17",
160
+ "x": 0.48,
161
+ "y": -3.1949999999999994
162
+ },
163
+ {
164
+ "pinId": "schematic_port_18",
165
+ "x": 0.48,
166
+ "y": -3.3949999999999996
167
+ }
168
+ ]
169
+ }
170
+ ],
171
+ "directConnections": [
172
+ {
173
+ "netId": "U1.CTRL to C2.pin1",
174
+ "pinIds": [
175
+ "schematic_port_4",
176
+ "schematic_port_14"
177
+ ]
178
+ },
179
+ {
180
+ "netId": "R1.pin2 to U1.DISCH",
181
+ "pinIds": [
182
+ "schematic_port_9",
183
+ "schematic_port_6"
184
+ ]
185
+ },
186
+ {
187
+ "netId": "U1.DISCH to R2.pin1",
188
+ "pinIds": [
189
+ "schematic_port_6",
190
+ "schematic_port_10"
191
+ ]
192
+ }
193
+ ],
194
+ "netConnections": [
195
+ {
196
+ "netId": "GND",
197
+ "netLabelWidth": 0.42,
198
+ "netLabelHeight": 0.48,
199
+ "pinIds": [
200
+ "schematic_port_0",
201
+ "schematic_port_13",
202
+ "schematic_port_15",
203
+ "schematic_port_18"
204
+ ]
205
+ },
206
+ {
207
+ "netId": "NODE",
208
+ "netLabelWidth": 0.6,
209
+ "pinIds": [
210
+ "schematic_port_1",
211
+ "schematic_port_5",
212
+ "schematic_port_11",
213
+ "schematic_port_12"
214
+ ]
215
+ },
216
+ {
217
+ "netId": "OUT",
218
+ "netLabelWidth": 0.48,
219
+ "pinIds": [
220
+ "schematic_port_2",
221
+ "schematic_port_17"
222
+ ]
223
+ },
224
+ {
225
+ "netId": "VCC",
226
+ "netLabelWidth": 0.42,
227
+ "netLabelHeight": 0.48,
228
+ "pinIds": [
229
+ "schematic_port_3",
230
+ "schematic_port_7",
231
+ "schematic_port_8",
232
+ "schematic_port_16"
233
+ ]
234
+ }
235
+ ],
236
+ "textBoxes": [
237
+ {
238
+ "chipId": "schematic_component_0",
239
+ "center": {
240
+ "x": -0.6,
241
+ "y": 0.31999999999999995
242
+ },
243
+ "width": 0.36000000000000004,
244
+ "height": 0.24999999999999994,
245
+ "text": "U1"
246
+ },
247
+ {
248
+ "chipId": "schematic_component_5",
249
+ "center": {
250
+ "x": 1,
251
+ "y": -2.6799999999999997
252
+ },
253
+ "width": 0.3600000000000001,
254
+ "height": 0.24999999999999956,
255
+ "text": "J1"
256
+ }
257
+ ],
258
+ "availableNetLabelOrientations": {
259
+ "VCC": [
260
+ "y+"
261
+ ],
262
+ "GND": [
263
+ "y-"
264
+ ],
265
+ "OUT": [
266
+ "x-",
267
+ "x+"
268
+ ],
269
+ "NODE": [
270
+ "x-",
271
+ "x+"
272
+ ]
273
+ },
274
+ "maxMspPairDistance": 2.4,
275
+ "_hideRatsNet": false
276
+ }
@@ -0,0 +1,30 @@
1
+ import { expect, test } from "bun:test"
2
+ import { isPathCollidingWithObstacles } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/collisions"
3
+ import { getObstacleRects } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/rect"
4
+ import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver"
5
+ import type { InputProblem } from "lib/types/InputProblem"
6
+ import inputProblemJson from "./assets/board-15984-schematic-trace-input.json"
7
+ import "tests/fixtures/matcher"
8
+
9
+ test("board 15984 extends the V3V3 rail through schematic component 8", () => {
10
+ const inputProblem: InputProblem = JSON.parse(
11
+ JSON.stringify(inputProblemJson),
12
+ )
13
+ const solver = new SchematicTracePipelineSolver(inputProblem)
14
+
15
+ solver.solve()
16
+
17
+ const component8Trace =
18
+ solver.sameNetJunctionAlignmentSolver!.outputTraces.find(
19
+ (trace) => trace.mspPairId === "schematic_port_34-schematic_port_33",
20
+ )!
21
+ expect(component8Trace.tracePath[1]!.y).toBeCloseTo(-3.105)
22
+ expect(component8Trace.tracePath[2]!.y).toBeCloseTo(-3.105)
23
+ const obstacles = getObstacleRects(inputProblem)
24
+ const tracesCrossingComponents =
25
+ solver.sameNetJunctionAlignmentSolver!.outputTraces.filter((trace) =>
26
+ isPathCollidingWithObstacles(trace.tracePath, obstacles),
27
+ )
28
+ expect(tracesCrossingComponents).toHaveLength(0)
29
+ expect(solver).toMatchSolverSnapshot(import.meta.path)
30
+ })
@@ -0,0 +1,97 @@
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
+ // Captured from @tscircuit/core for a TYPE_C_16PIN_2MD chip whose pin 4 is
7
+ // connected directly to pin 5 on the same schematic component.
8
+ const inputProblem: InputProblem = {
9
+ chips: [
10
+ {
11
+ chipId: "schematic_component_0",
12
+ center: { x: 0, y: 0 },
13
+ width: 2.6,
14
+ height: 1,
15
+ pins: [
16
+ {
17
+ pinId: "schematic_port_0",
18
+ x: -1.3,
19
+ y: 0.30000000000000004,
20
+ },
21
+ {
22
+ pinId: "schematic_port_1",
23
+ x: -1.3,
24
+ y: 0.10000000000000003,
25
+ },
26
+ {
27
+ pinId: "schematic_port_2",
28
+ x: -1.3,
29
+ y: -0.09999999999999998,
30
+ },
31
+ {
32
+ pinId: "schematic_port_3",
33
+ x: -1.3,
34
+ y: -0.30000000000000004,
35
+ },
36
+ {
37
+ pinId: "schematic_port_4",
38
+ x: 1.3,
39
+ y: -0.30000000000000004,
40
+ },
41
+ {
42
+ pinId: "schematic_port_5",
43
+ x: 1.3,
44
+ y: -0.10000000000000003,
45
+ },
46
+ {
47
+ pinId: "schematic_port_6",
48
+ x: 1.3,
49
+ y: 0.09999999999999998,
50
+ },
51
+ {
52
+ pinId: "schematic_port_7",
53
+ x: 1.3,
54
+ y: 0.30000000000000004,
55
+ },
56
+ ],
57
+ },
58
+ ],
59
+ directConnections: [
60
+ {
61
+ netId: ".U1 > .pin4 to U1.pin5",
62
+ netLabelWidth: 0.96,
63
+ pinIds: ["schematic_port_3", "schematic_port_4"],
64
+ },
65
+ ],
66
+ netConnections: [],
67
+ textBoxes: [
68
+ {
69
+ chipId: "schematic_component_0",
70
+ center: { x: 0.06, y: -0.63 },
71
+ width: 1.92,
72
+ height: 0.17999999999999994,
73
+ text: "TYPE_C_16PIN_2MD",
74
+ },
75
+ {
76
+ chipId: "schematic_component_0",
77
+ center: { x: -0.78, y: 0.615 },
78
+ width: 0.3599999999999999,
79
+ height: 0.25,
80
+ text: "U1",
81
+ },
82
+ ],
83
+ availableNetLabelOrientations: {},
84
+ maxMspPairDistance: 2.4,
85
+ _hideRatsNet: false,
86
+ }
87
+
88
+ test("TYPE_C_16PIN_2MD routes pin 4 to pin 5 on the same chip", () => {
89
+ const solver = new SchematicTracePipelineSolver(inputProblem)
90
+
91
+ solver.solve()
92
+
93
+ expect(solver.netLabelTraceCollisionSolver!.getOutput().traces).toHaveLength(
94
+ 1,
95
+ )
96
+ expect(solver).toMatchSolverSnapshot(import.meta.path)
97
+ })
@@ -0,0 +1,120 @@
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
+ // Captured from @tscircuit/core for a TYPE_C_16PIN_2MD chip whose pin 5 is
7
+ // connected directly to a resistor on the opposite side of the chip.
8
+ const inputProblem: InputProblem = {
9
+ chips: [
10
+ {
11
+ chipId: "schematic_component_0",
12
+ center: { x: 0, y: 0 },
13
+ width: 2.6,
14
+ height: 1,
15
+ pins: [
16
+ {
17
+ pinId: "schematic_port_0",
18
+ x: -1.3,
19
+ y: 0.30000000000000004,
20
+ },
21
+ {
22
+ pinId: "schematic_port_1",
23
+ x: -1.3,
24
+ y: 0.10000000000000003,
25
+ },
26
+ {
27
+ pinId: "schematic_port_2",
28
+ x: -1.3,
29
+ y: -0.09999999999999998,
30
+ },
31
+ {
32
+ pinId: "schematic_port_3",
33
+ x: -1.3,
34
+ y: -0.30000000000000004,
35
+ },
36
+ {
37
+ pinId: "schematic_port_4",
38
+ x: 1.3,
39
+ y: -0.30000000000000004,
40
+ },
41
+ {
42
+ pinId: "schematic_port_5",
43
+ x: 1.3,
44
+ y: -0.10000000000000003,
45
+ },
46
+ {
47
+ pinId: "schematic_port_6",
48
+ x: 1.3,
49
+ y: 0.09999999999999998,
50
+ },
51
+ {
52
+ pinId: "schematic_port_7",
53
+ x: 1.3,
54
+ y: 0.30000000000000004,
55
+ },
56
+ ],
57
+ },
58
+ {
59
+ chipId: "schematic_component_1",
60
+ center: { x: -2, y: 0 },
61
+ width: 0.6000000000000001,
62
+ height: 0.6799999999999999,
63
+ pins: [
64
+ {
65
+ pinId: "schematic_port_8",
66
+ x: -2.3,
67
+ y: 0,
68
+ },
69
+ {
70
+ pinId: "schematic_port_9",
71
+ x: -1.7,
72
+ y: 0,
73
+ },
74
+ ],
75
+ },
76
+ ],
77
+ directConnections: [
78
+ {
79
+ netId: ".U1 > .pin5 to R1.pin1",
80
+ netLabelWidth: 0.96,
81
+ pinIds: ["schematic_port_4", "schematic_port_8"],
82
+ },
83
+ ],
84
+ netConnections: [],
85
+ textBoxes: [
86
+ {
87
+ chipId: "schematic_component_0",
88
+ center: { x: 0.06, y: -0.63 },
89
+ width: 1.92,
90
+ height: 0.17999999999999994,
91
+ text: "TYPE_C_16PIN_2MD",
92
+ },
93
+ {
94
+ chipId: "schematic_component_0",
95
+ center: { x: -0.78, y: 0.615 },
96
+ width: 0.3599999999999999,
97
+ height: 0.25,
98
+ text: "U1",
99
+ },
100
+ ],
101
+ availableNetLabelOrientations: {},
102
+ maxMspPairDistance: 2.4,
103
+ _hideRatsNet: false,
104
+ }
105
+
106
+ test("TYPE_C_16PIN_2MD routes pin 5 to R1 below the MPN text", () => {
107
+ const solver = new SchematicTracePipelineSolver(inputProblem)
108
+
109
+ solver.solve()
110
+
111
+ const traces = solver.netLabelTraceCollisionSolver!.getOutput().traces
112
+ const mpnTextBox = inputProblem.textBoxes![0]!
113
+ const mpnBottom = mpnTextBox.center.y - mpnTextBox.height / 2
114
+
115
+ expect(traces).toHaveLength(1)
116
+ expect(
117
+ Math.min(...traces[0]!.tracePath.map((point) => point.y)),
118
+ ).toBeLessThan(mpnBottom)
119
+ expect(solver).toMatchSolverSnapshot(import.meta.path)
120
+ })
@@ -0,0 +1,23 @@
1
+ import { expect, test } from "bun:test"
2
+ import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver"
3
+ import inputProblem from "./assets/repro48-555-timer-vcc-rail-label.input.json"
4
+ import "tests/fixtures/matcher"
5
+
6
+ // Captured from @tscircuit/core repro48. U1 pin 4 is a port-only member of the
7
+ // VCC net, but its required y+ rail placement falls back to a horizontal label.
8
+ test("core repro48 U1 pin4 VCC rail label falls back horizontally", () => {
9
+ const solver = new SchematicTracePipelineSolver(inputProblem as any)
10
+
11
+ solver.solve()
12
+
13
+ const u1Pin4VccLabel = solver.netLabelNetLabelCollisionSolver
14
+ ?.getOutput()
15
+ .netLabelPlacements.find((placement) =>
16
+ placement.pinIds.includes("schematic_port_3"),
17
+ )
18
+
19
+ expect(inputProblem.availableNetLabelOrientations.VCC).toEqual(["y+"])
20
+ expect(u1Pin4VccLabel?.netId).toBe("VCC")
21
+ expect(u1Pin4VccLabel?.orientation).toBe("x-")
22
+ expect(solver).toMatchSolverSnapshot(import.meta.path)
23
+ })
@@ -0,0 +1,43 @@
1
+ import { expect, test } from "bun:test"
2
+ import { getObstacleRects } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/rect"
3
+ import type { InputProblem } from "lib/types/InputProblem"
4
+
5
+ test("closes a sub-minimum gap between a chip and its attached text", () => {
6
+ const inputProblem: InputProblem = {
7
+ chips: [
8
+ {
9
+ chipId: "U1",
10
+ center: { x: 0, y: 0 },
11
+ width: 2.6,
12
+ height: 1,
13
+ pins: [],
14
+ },
15
+ ],
16
+ directConnections: [],
17
+ netConnections: [],
18
+ textBoxes: [
19
+ {
20
+ chipId: "U1",
21
+ center: { x: 0.06, y: -0.63 },
22
+ width: 1.92,
23
+ height: 0.18,
24
+ text: "TYPE_C_16PIN_2MD",
25
+ },
26
+ {
27
+ chipId: "U1",
28
+ center: { x: 0, y: -0.9 },
29
+ width: 1,
30
+ height: 0.2,
31
+ text: "clear gap",
32
+ },
33
+ ],
34
+ availableNetLabelOrientations: {},
35
+ }
36
+
37
+ const textObstacles = getObstacleRects(inputProblem).filter(
38
+ (obstacle) => obstacle.kind === "text_box",
39
+ )
40
+
41
+ expect(textObstacles[0]!.maxY).toBe(-0.5)
42
+ expect(textObstacles[1]!.maxY).toBe(-0.8)
43
+ })