@tscircuit/schematic-trace-solver 0.0.98 → 0.0.100

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 (26) hide show
  1. package/dist/index.d.ts +1 -0
  2. package/dist/index.js +335 -225
  3. package/lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver.ts +19 -9
  4. package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2.ts +43 -1
  5. package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/generateEndpointCollisionDetours.ts +84 -0
  6. package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/mid.ts +13 -3
  7. package/package.json +1 -1
  8. package/tests/bug-reports/bug-report-20260706T220324Z/__snapshots__/bug-report-20260706T220324Z.snap.svg +18 -18
  9. package/tests/bug-reports/bug-report-20260707T092615Z/__snapshots__/bug-report-20260707T092615Z.snap.svg +7 -7
  10. package/tests/bug-reports/bug-report-20260707T134549Z/__snapshots__/bug-report-20260707T134549Z.snap.svg +5 -7
  11. package/tests/bug-reports/bug-report-20260707T140410Z/__snapshots__/bug-report-20260707T140410Z.snap.svg +6 -12
  12. package/tests/examples/__snapshots__/example09.snap.svg +9 -13
  13. package/tests/examples/__snapshots__/example21.snap.svg +30 -32
  14. package/tests/examples/__snapshots__/example29.snap.svg +13 -19
  15. package/tests/repros/__snapshots__/repro129-host-custom-symbol-passives.snap.svg +60 -0
  16. package/tests/repros/__snapshots__/repro47-endpoint-obstacle-detour.snap.svg +65 -0
  17. package/tests/repros/__snapshots__/repro5-escape-padded-text-obstacles.snap.svg +74 -0
  18. package/tests/repros/assets/repro129-host-custom-symbol-passives.input.json +94 -0
  19. package/tests/repros/assets/repro47-endpoint-obstacle-detour.input.json +133 -0
  20. package/tests/repros/assets/repro5-escape-padded-text-obstacles.input.json +202 -0
  21. package/tests/repros/repro129-host-custom-symbol-passives.test.ts +16 -0
  22. package/tests/repros/repro47-endpoint-obstacle-detour.test.ts +19 -0
  23. package/tests/repros/repro5-escape-padded-text-obstacles.test.ts +17 -0
  24. package/tests/solvers/MspConnectionPairSolver/msp-connection-pair-solver-direct-connection-distance.test.ts +46 -0
  25. package/tests/solvers/SchematicTraceSingleLineSolver2/candidate-mids-from-set.test.ts +39 -0
  26. package/tests/solvers/SchematicTraceSingleLineSolver2/generate-endpoint-collision-detours.test.ts +38 -0
@@ -0,0 +1,202 @@
1
+ {
2
+ "chips": [
3
+ {
4
+ "chipId": "schematic_component_0",
5
+ "center": {
6
+ "x": 0,
7
+ "y": 0
8
+ },
9
+ "width": 1.8,
10
+ "height": 5,
11
+ "pins": [
12
+ {
13
+ "pinId": "U1.1",
14
+ "x": 0.9,
15
+ "y": 1.2
16
+ },
17
+ {
18
+ "pinId": "U1.2",
19
+ "x": 0.9,
20
+ "y": 0.3999999999999999
21
+ },
22
+ {
23
+ "pinId": "U1.3",
24
+ "x": 0.9,
25
+ "y": 0.5999999999999999
26
+ },
27
+ {
28
+ "pinId": "U1.4",
29
+ "x": -0.9,
30
+ "y": 0.3999999999999999
31
+ },
32
+ {
33
+ "pinId": "U1.5",
34
+ "x": 0.9,
35
+ "y": 0.9999999999999998
36
+ },
37
+ {
38
+ "pinId": "U1.6",
39
+ "x": 0.9,
40
+ "y": -0.19999999999999996
41
+ },
42
+ {
43
+ "pinId": "U1.7",
44
+ "x": -0.9,
45
+ "y": -0.7999999999999998
46
+ },
47
+ {
48
+ "pinId": "U1.9",
49
+ "x": 0.9,
50
+ "y": 0.19999999999999996
51
+ },
52
+ {
53
+ "pinId": "U1.10",
54
+ "x": 0.9,
55
+ "y": 0
56
+ },
57
+ {
58
+ "pinId": "U1.11",
59
+ "x": 0.9,
60
+ "y": 0.7999999999999998
61
+ },
62
+ {
63
+ "pinId": "U1.12",
64
+ "x": 0.9,
65
+ "y": -1.2
66
+ },
67
+ {
68
+ "pinId": "U1.13",
69
+ "x": 0.9,
70
+ "y": -1
71
+ },
72
+ {
73
+ "pinId": "U1.14",
74
+ "x": 0.9,
75
+ "y": -0.7999999999999999
76
+ },
77
+ {
78
+ "pinId": "U1.15",
79
+ "x": -0.9,
80
+ "y": 1
81
+ },
82
+ {
83
+ "pinId": "U1.16",
84
+ "x": -0.9,
85
+ "y": 1.2
86
+ },
87
+ {
88
+ "pinId": "U1.17",
89
+ "x": -0.9,
90
+ "y": 0.5999999999999999
91
+ },
92
+ {
93
+ "pinId": "U1.18",
94
+ "x": -0.9,
95
+ "y": -0.9999999999999998
96
+ },
97
+ {
98
+ "pinId": "U1.19",
99
+ "x": -0.9,
100
+ "y": -0.19999999999999996
101
+ },
102
+ {
103
+ "pinId": "U1.20",
104
+ "x": -0.9,
105
+ "y": 0.7999999999999999
106
+ },
107
+ {
108
+ "pinId": "U1.21",
109
+ "x": -0.9,
110
+ "y": -1.2
111
+ },
112
+ {
113
+ "pinId": "U1.22",
114
+ "x": 0.9,
115
+ "y": -0.5999999999999999
116
+ },
117
+ {
118
+ "pinId": "U1.23",
119
+ "x": 0.9,
120
+ "y": -0.3999999999999999
121
+ },
122
+ {
123
+ "pinId": "U1.25",
124
+ "x": -0.9,
125
+ "y": -0.5999999999999999
126
+ },
127
+ {
128
+ "pinId": "U1.26",
129
+ "x": -0.9,
130
+ "y": -0.3999999999999999
131
+ },
132
+ {
133
+ "pinId": "U1.27",
134
+ "x": -0.9,
135
+ "y": 0.19999999999999996
136
+ },
137
+ {
138
+ "pinId": "U1.28",
139
+ "x": -0.9,
140
+ "y": 0
141
+ }
142
+ ]
143
+ },
144
+ {
145
+ "chipId": "schematic_component_1",
146
+ "center": {
147
+ "x": 2,
148
+ "y": 1.0699999999999998
149
+ },
150
+ "width": 1.04,
151
+ "height": 0.6799999999999999,
152
+ "pins": [
153
+ {
154
+ "pinId": "LED1.1",
155
+ "x": 1.48,
156
+ "y": 1,
157
+ "_facingDirection": "x-"
158
+ },
159
+ {
160
+ "pinId": "LED1.2",
161
+ "x": 2.52,
162
+ "y": 1,
163
+ "_facingDirection": "x+"
164
+ }
165
+ ]
166
+ }
167
+ ],
168
+ "directConnections": [
169
+ {
170
+ "pinIds": [
171
+ "LED1.2",
172
+ "U1.20"
173
+ ],
174
+ "netId": ".LED1 > port.right to .U1 > .pin20"
175
+ }
176
+ ],
177
+ "netConnections": [],
178
+ "textBoxes": [
179
+ {
180
+ "chipId": "schematic_component_0",
181
+ "center": {
182
+ "x": 0.15999999999999992,
183
+ "y": -2.63
184
+ },
185
+ "width": 1.3199999999999998,
186
+ "height": 0.17999999999999972,
187
+ "text": "part-number"
188
+ },
189
+ {
190
+ "chipId": "schematic_component_0",
191
+ "center": {
192
+ "x": -0.38,
193
+ "y": 2.615
194
+ },
195
+ "width": 0.36000000000000004,
196
+ "height": 0.24999999999999956,
197
+ "text": "U1"
198
+ }
199
+ ],
200
+ "availableNetLabelOrientations": {},
201
+ "maxMspPairDistance": 5
202
+ }
@@ -0,0 +1,16 @@
1
+ import { expect, test } from "bun:test"
2
+ import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver"
3
+ import "tests/fixtures/matcher"
4
+ import inputProblem from "./assets/repro129-host-custom-symbol-passives.input.json"
5
+
6
+ test("repro129: HOST custom symbol routes all passive connections", () => {
7
+ const solver = new SchematicTracePipelineSolver(inputProblem as any)
8
+
9
+ solver.solve()
10
+
11
+ expect(solver.schematicTraceLinesSolver!.solvedTracePaths).toHaveLength(4)
12
+ expect(solver.schematicTraceLinesSolver!.failedConnectionPairs).toHaveLength(
13
+ 0,
14
+ )
15
+ expect(solver).toMatchSolverSnapshot(import.meta.path)
16
+ })
@@ -0,0 +1,19 @@
1
+ import { expect, test } from "bun:test"
2
+ import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver"
3
+ import "tests/fixtures/matcher"
4
+ import inputProblem from "./assets/repro47-endpoint-obstacle-detour.input.json"
5
+
6
+ test("repro47: endpoint obstacle detours fall back to net labels", () => {
7
+ const solver = new SchematicTracePipelineSolver(inputProblem as any)
8
+
9
+ solver.solve()
10
+
11
+ expect(
12
+ solver
13
+ .schematicTraceLinesSolver!.failedConnectionPairs.map(
14
+ (pair) => pair.mspPairId,
15
+ )
16
+ .sort(),
17
+ ).toEqual(["J1.1-R1.1", "J1.3-R1.2"])
18
+ expect(solver).toMatchSolverSnapshot(import.meta.path)
19
+ })
@@ -0,0 +1,17 @@
1
+ import { expect, test } from "bun:test"
2
+ import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver"
3
+ import "tests/fixtures/matcher"
4
+ import inputProblem from "./assets/repro5-escape-padded-text-obstacles.input.json"
5
+
6
+ test("repro5: route escapes past padded text obstacles", () => {
7
+ const solver = new SchematicTracePipelineSolver(inputProblem as any)
8
+
9
+ solver.solve()
10
+
11
+ expect(
12
+ solver.schematicTraceLinesSolver!.failedConnectionPairs.map(
13
+ (pair) => pair.mspPairId,
14
+ ),
15
+ ).toEqual([])
16
+ expect(solver).toMatchSolverSnapshot(import.meta.path)
17
+ })
@@ -0,0 +1,46 @@
1
+ import { expect, test } from "bun:test"
2
+ import { MspConnectionPairSolver } from "lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver"
3
+ import type { InputProblem } from "lib/types/InputProblem"
4
+
5
+ const createInputProblem = (): InputProblem => ({
6
+ chips: [
7
+ {
8
+ chipId: "U1",
9
+ center: { x: 0, y: 0 },
10
+ width: 1,
11
+ height: 1,
12
+ pins: [{ pinId: "U1.1", x: 0, y: 0, _facingDirection: "x+" }],
13
+ },
14
+ {
15
+ chipId: "U2",
16
+ center: { x: 2, y: 2 },
17
+ width: 1,
18
+ height: 1,
19
+ pins: [{ pinId: "U2.1", x: 2, y: 2, _facingDirection: "x-" }],
20
+ },
21
+ ],
22
+ directConnections: [{ pinIds: ["U1.1", "U2.1"] }],
23
+ netConnections: [],
24
+ availableNetLabelOrientations: {},
25
+ maxMspPairDistance: 3,
26
+ })
27
+
28
+ test("two-pin direct connections use Euclidean max distance", () => {
29
+ const directConnectionSolver = new MspConnectionPairSolver({
30
+ inputProblem: createInputProblem(),
31
+ })
32
+ directConnectionSolver.solve()
33
+
34
+ const netConnectionProblem = createInputProblem()
35
+ netConnectionProblem.directConnections = []
36
+ netConnectionProblem.netConnections = [
37
+ { pinIds: ["U1.1", "U2.1"], netId: "SIG" },
38
+ ]
39
+ const netConnectionSolver = new MspConnectionPairSolver({
40
+ inputProblem: netConnectionProblem,
41
+ })
42
+ netConnectionSolver.solve()
43
+
44
+ expect(directConnectionSolver.mspConnectionPairs).toHaveLength(1)
45
+ expect(netConnectionSolver.mspConnectionPairs).toHaveLength(0)
46
+ })
@@ -0,0 +1,39 @@
1
+ import { expect, test } from "bun:test"
2
+ import { candidateMidsFromSet } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/mid"
3
+ import type { ObstacleRect } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/rect"
4
+
5
+ test("candidate mids include clearance beyond an open obstacle side", () => {
6
+ const previousObstacle: ObstacleRect = {
7
+ kind: "chip",
8
+ chipId: "U1",
9
+ minX: -0.9,
10
+ maxX: 0.9,
11
+ minY: -2.5,
12
+ maxY: 2.5,
13
+ }
14
+ const collidingObstacle: ObstacleRect = {
15
+ kind: "text_box",
16
+ textBox: {
17
+ center: { x: -0.38, y: 2.615 },
18
+ width: 0.36,
19
+ height: 0.25,
20
+ text: "U1",
21
+ },
22
+ minX: -1.01,
23
+ maxX: 0.25,
24
+ minY: 2.04,
25
+ maxY: 3.19,
26
+ }
27
+ const aabb = { minX: -0.9, maxX: 2.52, minY: 0.8, maxY: 1 }
28
+ const collisionRects = new Set([previousObstacle])
29
+
30
+ expect(
31
+ candidateMidsFromSet("y", collidingObstacle, collisionRects, aabb),
32
+ ).toEqual([1.42])
33
+
34
+ expect(
35
+ candidateMidsFromSet("y", collidingObstacle, collisionRects, aabb, {
36
+ allowOpenSideCandidates: true,
37
+ }),
38
+ ).toEqual([1.42, 3.39])
39
+ })
@@ -0,0 +1,38 @@
1
+ import { expect, test } from "bun:test"
2
+ import { generateEndpointCollisionDetours } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/generateEndpointCollisionDetours"
3
+
4
+ test("endpoint collision detours preserve both endpoint anchors", () => {
5
+ const path = [
6
+ { x: 0, y: 1 },
7
+ { x: 2, y: 1 },
8
+ { x: 2, y: -1 },
9
+ ]
10
+
11
+ const obstacle = {
12
+ minX: 1,
13
+ maxX: 3,
14
+ minY: 0,
15
+ maxY: 2,
16
+ kind: "chip" as const,
17
+ chipId: "U1",
18
+ }
19
+
20
+ const detours = [0, 1].flatMap((collidingSegmentIndex) =>
21
+ generateEndpointCollisionDetours({
22
+ path,
23
+ collidingSegmentIndex,
24
+ obstacle,
25
+ }),
26
+ )
27
+
28
+ expect(detours.length).toBeGreaterThan(0)
29
+ expect(
30
+ detours.every(
31
+ (detour) =>
32
+ detour[0]?.x === path[0]?.x &&
33
+ detour[0]?.y === path[0]?.y &&
34
+ detour.at(-1)?.x === path.at(-1)?.x &&
35
+ detour.at(-1)?.y === path.at(-1)?.y,
36
+ ),
37
+ ).toBe(true)
38
+ })