@tscircuit/schematic-trace-solver 0.0.150 → 0.0.152
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/README.md +6 -4
- package/dist/index.d.ts +41 -2
- package/dist/index.js +360 -65
- package/lib/solvers/InlineNetLabelSolver/InlineNetLabelSolver.ts +111 -82
- package/lib/solvers/NetLabelToTraceSolver/NetLabelToTraceSolver.ts +338 -0
- package/lib/solvers/NetLabelTraceRecovery/getTraceRecoveryConnectivityMaps.ts +33 -0
- package/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts +15 -0
- package/lib/solvers/SchematicTracePipelineSolver/visualizeInputProblem.ts +1 -1
- package/lib/types/InputProblem.ts +7 -0
- package/package.json +1 -1
- package/tests/examples/__snapshots__/example51.snap.svg +5 -17
- package/tests/fixtures/convertSolverOutputToCircuitJson.test.ts +65 -2
- package/tests/fixtures/convertSolverOutputToCircuitJson.ts +31 -13
- package/tests/repros/__snapshots__/board1096-usb-label-overlap-iteration.snap.svg +3 -3
|
@@ -3,9 +3,11 @@ import {
|
|
|
3
3
|
any_circuit_element,
|
|
4
4
|
type SchematicPort,
|
|
5
5
|
type SchematicTrace,
|
|
6
|
+
type SourcePort,
|
|
6
7
|
} from "circuit-json"
|
|
7
8
|
import { convertCircuitJsonToSchematicSvg } from "circuit-to-svg"
|
|
8
9
|
import { BaseSolver } from "lib/solvers/BaseSolver/BaseSolver"
|
|
10
|
+
import { visualizeInputProblem } from "lib/solvers/SchematicTracePipelineSolver/visualizeInputProblem"
|
|
9
11
|
import type { InputProblem } from "lib/types/InputProblem"
|
|
10
12
|
import { convertSolverOutputToCircuitJson } from "./convertSolverOutputToCircuitJson"
|
|
11
13
|
|
|
@@ -17,7 +19,13 @@ const inputProblem: InputProblem = {
|
|
|
17
19
|
width: 1,
|
|
18
20
|
height: 1,
|
|
19
21
|
pins: [
|
|
20
|
-
{
|
|
22
|
+
{
|
|
23
|
+
pinId: "U1.1",
|
|
24
|
+
displayName: "VCC",
|
|
25
|
+
x: 0.5,
|
|
26
|
+
y: 0.2,
|
|
27
|
+
_facingDirection: "x+",
|
|
28
|
+
},
|
|
21
29
|
{ pinId: "U1.2", x: 0.5, y: -0.2, _facingDirection: "x+" },
|
|
22
30
|
],
|
|
23
31
|
},
|
|
@@ -44,6 +52,28 @@ const inputProblem: InputProblem = {
|
|
|
44
52
|
{ pinId: "LED1.2", x: 4.54, y: 0, _facingDirection: "x+" },
|
|
45
53
|
],
|
|
46
54
|
},
|
|
55
|
+
{
|
|
56
|
+
chipId: "schematic_component_3",
|
|
57
|
+
center: { x: 6, y: 0 },
|
|
58
|
+
width: 2.2,
|
|
59
|
+
height: 1,
|
|
60
|
+
pins: [
|
|
61
|
+
{
|
|
62
|
+
pinId: "schematic_port_opaque_input",
|
|
63
|
+
displayName: "VIN",
|
|
64
|
+
x: 4.9,
|
|
65
|
+
y: 0,
|
|
66
|
+
_facingDirection: "x-",
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
pinId: "schematic_port_opaque_output",
|
|
70
|
+
displayName: "VOUT",
|
|
71
|
+
x: 7.1,
|
|
72
|
+
y: 0,
|
|
73
|
+
_facingDirection: "x+",
|
|
74
|
+
},
|
|
75
|
+
],
|
|
76
|
+
},
|
|
47
77
|
],
|
|
48
78
|
directConnections: [
|
|
49
79
|
{
|
|
@@ -120,6 +150,15 @@ class SnapshotTestSolver extends BaseSolver {
|
|
|
120
150
|
|
|
121
151
|
test("solver snapshot Circuit JSON is semantic and omits the rats nest", () => {
|
|
122
152
|
const circuitJson = convertSolverOutputToCircuitJson(new SnapshotTestSolver())
|
|
153
|
+
const inputGraphics = visualizeInputProblem(inputProblem)
|
|
154
|
+
|
|
155
|
+
expect(
|
|
156
|
+
inputGraphics.points?.find((point) => point.x === 4.9 && point.y === 0)
|
|
157
|
+
?.label,
|
|
158
|
+
).toBe("VIN\nx-")
|
|
159
|
+
expect(
|
|
160
|
+
inputGraphics.points?.some((point) => point.label?.includes("opaque")),
|
|
161
|
+
).toBe(false)
|
|
123
162
|
|
|
124
163
|
for (const element of circuitJson) {
|
|
125
164
|
expect(any_circuit_element.safeParse(element).success).toBe(true)
|
|
@@ -127,7 +166,7 @@ test("solver snapshot Circuit JSON is semantic and omits the rats nest", () => {
|
|
|
127
166
|
|
|
128
167
|
expect(
|
|
129
168
|
circuitJson.filter((element) => element.type === "schematic_component"),
|
|
130
|
-
).toHaveLength(
|
|
169
|
+
).toHaveLength(4)
|
|
131
170
|
const genericBoxComponent = circuitJson.find(
|
|
132
171
|
(element) =>
|
|
133
172
|
element.type === "schematic_component" &&
|
|
@@ -146,6 +185,20 @@ test("solver snapshot Circuit JSON is semantic and omits the rats nest", () => {
|
|
|
146
185
|
genericBoxComponent.schematic_component_id,
|
|
147
186
|
),
|
|
148
187
|
).toMatchObject({ distance_from_component_edge: 0.4 })
|
|
188
|
+
expect(
|
|
189
|
+
circuitJson.find(
|
|
190
|
+
(element) =>
|
|
191
|
+
element.type === "source_port" &&
|
|
192
|
+
element.source_port_id === "source_port_0_0",
|
|
193
|
+
),
|
|
194
|
+
).toMatchObject({ name: "VCC", pin_number: 1 })
|
|
195
|
+
expect(
|
|
196
|
+
circuitJson.find(
|
|
197
|
+
(element) =>
|
|
198
|
+
element.type === "schematic_port" &&
|
|
199
|
+
element.source_port_id === "source_port_0_0",
|
|
200
|
+
),
|
|
201
|
+
).toMatchObject({ display_pin_label: "VCC", pin_number: 1 })
|
|
149
202
|
expect(
|
|
150
203
|
circuitJson.find(
|
|
151
204
|
(element) =>
|
|
@@ -176,6 +229,15 @@ test("solver snapshot Circuit JSON is semantic and omits the rats nest", () => {
|
|
|
176
229
|
size: { width: 1.13, height: 0.65 },
|
|
177
230
|
symbol_name: "led_right",
|
|
178
231
|
})
|
|
232
|
+
expect(
|
|
233
|
+
circuitJson
|
|
234
|
+
.filter(
|
|
235
|
+
(element): element is SourcePort =>
|
|
236
|
+
element.type === "source_port" &&
|
|
237
|
+
element.source_component_id === "source_component_3",
|
|
238
|
+
)
|
|
239
|
+
.map((sourcePort) => sourcePort.name),
|
|
240
|
+
).toEqual(["VIN", "VOUT"])
|
|
179
241
|
|
|
180
242
|
const capacitorPorts = circuitJson
|
|
181
243
|
.filter(
|
|
@@ -216,4 +278,5 @@ test("solver snapshot Circuit JSON is semantic and omits the rats nest", () => {
|
|
|
216
278
|
expect(svg).toContain('data-circuit-json-type="schematic_component"')
|
|
217
279
|
expect(svg).toContain('data-circuit-json-type="schematic_trace"')
|
|
218
280
|
expect(svg).toContain('data-circuit-json-type="schematic_net_label"')
|
|
281
|
+
expect(svg).not.toContain("schematic_port_opaque")
|
|
219
282
|
})
|
|
@@ -197,9 +197,17 @@ const getRefdes = (chip: InputChip) => {
|
|
|
197
197
|
const getPinLabel = (pinId: string) =>
|
|
198
198
|
pinId.includes(".") ? pinId.slice(pinId.indexOf(".") + 1) : pinId
|
|
199
199
|
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
return
|
|
200
|
+
const getPinDisplayName = (pin: InputPin) => {
|
|
201
|
+
if (pin.displayName !== undefined) return pin.displayName || undefined
|
|
202
|
+
return getPinLabel(pin.pinId)
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const getPinNumber = (pin: InputPin) => {
|
|
206
|
+
const idLabel = getPinLabel(pin.pinId)
|
|
207
|
+
if (/^\d+$/.test(idLabel)) return Number(idLabel)
|
|
208
|
+
return pin.displayName && /^\d+$/.test(pin.displayName)
|
|
209
|
+
? Number(pin.displayName)
|
|
210
|
+
: undefined
|
|
203
211
|
}
|
|
204
212
|
|
|
205
213
|
const getFacingDirection = (pin: InputPin, chip: InputChip): FacingDirection =>
|
|
@@ -258,8 +266,8 @@ const getDefaultSymbolBaseName = (refdes: string) => {
|
|
|
258
266
|
|
|
259
267
|
const getPinsInNumberOrder = (chip: InputChip) =>
|
|
260
268
|
[...chip.pins].sort((pinA, pinB) => {
|
|
261
|
-
const pinNumberA = getPinNumber(pinA
|
|
262
|
-
const pinNumberB = getPinNumber(pinB
|
|
269
|
+
const pinNumberA = getPinNumber(pinA)
|
|
270
|
+
const pinNumberB = getPinNumber(pinB)
|
|
263
271
|
if (pinNumberA === undefined || pinNumberB === undefined) return 0
|
|
264
272
|
return pinNumberA - pinNumberB
|
|
265
273
|
})
|
|
@@ -452,10 +460,12 @@ const getSnapshotSymbolGeometry = (
|
|
|
452
460
|
|
|
453
461
|
const unusedSymbolPorts = new Set(symbol.ports)
|
|
454
462
|
const matches = chip.pins.map((pin) => {
|
|
455
|
-
const
|
|
456
|
-
const symbolPort =
|
|
457
|
-
|
|
458
|
-
|
|
463
|
+
const pinDisplayName = getPinDisplayName(pin)
|
|
464
|
+
const symbolPort = pinDisplayName
|
|
465
|
+
? [...unusedSymbolPorts].find((port) =>
|
|
466
|
+
port.labels.includes(pinDisplayName),
|
|
467
|
+
)
|
|
468
|
+
: undefined
|
|
459
469
|
if (symbolPort) unusedSymbolPorts.delete(symbolPort)
|
|
460
470
|
return symbolPort ? { pin, symbolPort } : undefined
|
|
461
471
|
})
|
|
@@ -836,8 +846,16 @@ export const convertSolverOutputToCircuitJson = (
|
|
|
836
846
|
const schematicPortId = `schematic_port_${chipIndex}_${pinIndex}`
|
|
837
847
|
const facingDirection = getFacingDirection(pin, chip)
|
|
838
848
|
const sideOfComponent = facingDirectionToSide(facingDirection)
|
|
839
|
-
const
|
|
840
|
-
const pinNumber = getPinNumber(pin
|
|
849
|
+
const pinDisplayName = getPinDisplayName(pin)
|
|
850
|
+
const pinNumber = getPinNumber(pin)
|
|
851
|
+
const displayPinLabel =
|
|
852
|
+
pin.displayName !== undefined
|
|
853
|
+
? pin.displayName && !/^\d+$/.test(pin.displayName)
|
|
854
|
+
? pin.displayName
|
|
855
|
+
: undefined
|
|
856
|
+
: pinNumber === undefined
|
|
857
|
+
? pinDisplayName
|
|
858
|
+
: undefined
|
|
841
859
|
const distanceFromComponentEdge = symbolName
|
|
842
860
|
? Math.min(
|
|
843
861
|
0.2,
|
|
@@ -856,7 +874,7 @@ export const convertSolverOutputToCircuitJson = (
|
|
|
856
874
|
type: "source_port",
|
|
857
875
|
source_port_id: sourcePortId,
|
|
858
876
|
source_component_id: sourceComponentId,
|
|
859
|
-
name:
|
|
877
|
+
name: pinDisplayName ?? `pin${pinNumber ?? pinIndex + 1}`,
|
|
860
878
|
pin_number: pinNumber,
|
|
861
879
|
} satisfies SourcePort)
|
|
862
880
|
|
|
@@ -872,7 +890,7 @@ export const convertSolverOutputToCircuitJson = (
|
|
|
872
890
|
facing_direction: facingDirectionToCircuitJson(facingDirection),
|
|
873
891
|
side_of_component: sideOfComponent,
|
|
874
892
|
distance_from_component_edge: distanceFromComponentEdge,
|
|
875
|
-
display_pin_label:
|
|
893
|
+
display_pin_label: displayPinLabel,
|
|
876
894
|
pin_number: pinNumber,
|
|
877
895
|
} satisfies SchematicPort)
|
|
878
896
|
|
|
@@ -99,7 +99,7 @@ orientation: x-" data-x="4.5" data-y="-6.3" cx="234.0382716049383" cy="588.24691
|
|
|
99
99
|
const h = svg.getElementById('crosshair-h');
|
|
100
100
|
const v = svg.getElementById('crosshair-v');
|
|
101
101
|
const coords = svg.getElementById('coordinates');
|
|
102
|
-
|
|
102
|
+
|
|
103
103
|
crosshair.style.display = 'block';
|
|
104
104
|
h.setAttribute('x1', '0');
|
|
105
105
|
h.setAttribute('x2', '640');
|
|
@@ -118,13 +118,13 @@ orientation: x-" data-x="4.5" data-y="-6.3" cx="234.0382716049383" cy="588.24691
|
|
|
118
118
|
// y' = (y - ty) / sy
|
|
119
119
|
const sx = matrix.a;
|
|
120
120
|
const sy = matrix.d;
|
|
121
|
-
const tx = matrix.e;
|
|
121
|
+
const tx = matrix.e;
|
|
122
122
|
const ty = matrix.f;
|
|
123
123
|
const realPoint = {
|
|
124
124
|
x: (x - tx) / sx,
|
|
125
125
|
y: (y - ty) / sy // Flip y back since we used negative scale
|
|
126
126
|
}
|
|
127
|
-
|
|
127
|
+
|
|
128
128
|
coords.textContent = `(${realPoint.x.toFixed(2)}, ${realPoint.y.toFixed(2)})`;
|
|
129
129
|
coords.setAttribute('x', (x + 5).toString());
|
|
130
130
|
coords.setAttribute('y', (y - 5).toString());
|