@tscircuit/schematic-trace-solver 0.0.118 → 0.0.120
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/dist/index.d.ts +7 -1
- package/dist/index.js +135 -4
- package/lib/solvers/AvailableNetOrientationSolver/AvailableNetOrientationSolver.ts +200 -6
- package/lib/solvers/AvailableNetOrientationSolver/types.ts +2 -0
- package/package.json +1 -1
- package/tests/bug-reports/bug-report-20260707T092615Z/__snapshots__/bug-report-20260707T092615Z.snap.svg +3 -3
- package/tests/bug-reports/bug-report-20260721T221026Z/__snapshots__/bug-report-20260721T221026Z.snap.svg +3 -3
- package/tests/bug-reports/bug-report-20260728T225606Z/__snapshots__/bug-report-20260728T225606Z.snap.svg +3 -3
- package/tests/repros/__snapshots__/repro-atmega328p-missing-gnd-netlabel.snap.svg +3 -3
- package/tests/repros/__snapshots__/repro-ground-label-on-lower-rail-end.snap.svg +58 -0
- package/tests/repros/__snapshots__/repro-power-group-rail-labels.snap.svg +58 -0
- package/tests/repros/__snapshots__/repro-rp2040-v3v3-label-placement.snap.svg +96 -0
- package/tests/repros/repro-ground-label-on-lower-rail-end.test.ts +84 -0
- package/tests/repros/repro-power-group-rail-labels.test.ts +95 -0
- package/tests/repros/repro-rp2040-v3v3-label-placement.test.ts +102 -0
|
@@ -0,0 +1,95 @@
|
|
|
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: "U1",
|
|
10
|
+
center: { x: 0, y: 0 },
|
|
11
|
+
width: 2,
|
|
12
|
+
height: 1.4,
|
|
13
|
+
pins: [
|
|
14
|
+
...[0.5, 0.25, 0, -0.25, -0.5].map((y, index) => ({
|
|
15
|
+
pinId: `U1.${index + 1}`,
|
|
16
|
+
x: -1,
|
|
17
|
+
y,
|
|
18
|
+
_facingDirection: "x-" as const,
|
|
19
|
+
})),
|
|
20
|
+
...[0.5, 0.25, 0, -0.25, -0.5].map((y, index) => ({
|
|
21
|
+
pinId: `U1.${index + 6}`,
|
|
22
|
+
x: 1,
|
|
23
|
+
y,
|
|
24
|
+
_facingDirection: "x+" as const,
|
|
25
|
+
})),
|
|
26
|
+
],
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
directConnections: [],
|
|
30
|
+
netConnections: [
|
|
31
|
+
{
|
|
32
|
+
netId: "VBAT",
|
|
33
|
+
pinIds: ["U1.1", "U1.2", "U1.3"],
|
|
34
|
+
netLabelWidth: 0.48,
|
|
35
|
+
netLabelHeight: 0.48,
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
netId: "GND",
|
|
39
|
+
pinIds: ["U1.4", "U1.5"],
|
|
40
|
+
netLabelWidth: 0.36,
|
|
41
|
+
netLabelHeight: 0.48,
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
textBoxes: [
|
|
45
|
+
{
|
|
46
|
+
chipId: "U1",
|
|
47
|
+
center: { x: -0.25, y: 0.85 },
|
|
48
|
+
width: 0.7,
|
|
49
|
+
height: 0.2,
|
|
50
|
+
text: "U1 Power",
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
availableNetLabelOrientations: {
|
|
54
|
+
VBAT: ["y+"],
|
|
55
|
+
GND: ["y-"],
|
|
56
|
+
},
|
|
57
|
+
maxMspPairDistance: 2.4,
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
test("places same-side VBAT and GND labels at opposite rail ends", () => {
|
|
61
|
+
const solver = new SchematicTracePipelineSolver(inputProblem)
|
|
62
|
+
|
|
63
|
+
solver.solve()
|
|
64
|
+
|
|
65
|
+
const outputLabels =
|
|
66
|
+
solver.netLabelNetLabelCollisionSolver!.getOutput().netLabelPlacements
|
|
67
|
+
const u1 = solver.inputProblem.chips.find((chip) => chip.chipId === "U1")!
|
|
68
|
+
const chipLeft = u1.center.x - u1.width / 2
|
|
69
|
+
|
|
70
|
+
for (const expected of [
|
|
71
|
+
{ netId: "VBAT", orientation: "y+" as const, anchorY: 0.5 },
|
|
72
|
+
{ netId: "GND", orientation: "y-" as const, anchorY: -0.5 },
|
|
73
|
+
]) {
|
|
74
|
+
const label = outputLabels.find(
|
|
75
|
+
(candidate) => candidate.netId === expected.netId,
|
|
76
|
+
)!
|
|
77
|
+
const connector = solver.availableNetOrientationSolver!.traces.find(
|
|
78
|
+
(trace) =>
|
|
79
|
+
trace.userNetId === expected.netId &&
|
|
80
|
+
trace.mspPairId.startsWith("available-net-orientation-"),
|
|
81
|
+
)!
|
|
82
|
+
const [connectorSource, connectorTarget] = connector.tracePath
|
|
83
|
+
const labelRight = label.center.x + label.width / 2
|
|
84
|
+
|
|
85
|
+
expect(label.orientation).toBe(expected.orientation)
|
|
86
|
+
expect(label.anchorPoint.y).toBeCloseTo(expected.anchorY)
|
|
87
|
+
expect(labelRight).toBeLessThan(chipLeft)
|
|
88
|
+
expect(connector.tracePath).toHaveLength(2)
|
|
89
|
+
expect(connectorSource!.y).toBeCloseTo(connectorTarget!.y)
|
|
90
|
+
expect(connectorTarget!.x).toBeLessThan(connectorSource!.x)
|
|
91
|
+
expect(connectorSource!.x - connectorTarget!.x).toBeLessThan(0.5)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
expect(solver).toMatchSolverSnapshot(import.meta.path)
|
|
95
|
+
})
|
|
@@ -0,0 +1,102 @@
|
|
|
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 createRp2040SidePins = (side: "left" | "right") =>
|
|
7
|
+
Array.from({ length: 22 }, (_, index) => ({
|
|
8
|
+
pinId: `U1.${side}.${index + 1}`,
|
|
9
|
+
x: side === "left" ? -1.35 : 1.35,
|
|
10
|
+
y: 2.8 - index * 0.2,
|
|
11
|
+
_facingDirection: side === "left" ? ("x-" as const) : ("x+" as const),
|
|
12
|
+
}))
|
|
13
|
+
|
|
14
|
+
// Reduced from the current @tscircuit/core input for an RP2040, then mirrored
|
|
15
|
+
// to cover both chip sides. The important details are the 0.2-unit pin pitch,
|
|
16
|
+
// 0.6-unit rail-label height, and a third distance-split member on each net.
|
|
17
|
+
// The reversed first pair preserves the MSP direction produced by the original
|
|
18
|
+
// multi-pin input (pin 10 to pin 1).
|
|
19
|
+
const inputProblem: InputProblem = {
|
|
20
|
+
chips: [
|
|
21
|
+
{
|
|
22
|
+
chipId: "U1",
|
|
23
|
+
center: { x: 0, y: 0 },
|
|
24
|
+
width: 1.9,
|
|
25
|
+
height: 6,
|
|
26
|
+
pins: [...createRp2040SidePins("left"), ...createRp2040SidePins("right")],
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
directConnections: [],
|
|
30
|
+
netConnections: [
|
|
31
|
+
{
|
|
32
|
+
netId: "V3V3_LEFT",
|
|
33
|
+
pinIds: ["U1.left.10", "U1.left.1", "U1.left.22"],
|
|
34
|
+
netLabelWidth: 0.42,
|
|
35
|
+
netLabelHeight: 0.6,
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
netId: "V3V3_RIGHT",
|
|
39
|
+
pinIds: ["U1.right.10", "U1.right.1", "U1.right.22"],
|
|
40
|
+
netLabelWidth: 0.42,
|
|
41
|
+
netLabelHeight: 0.6,
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
textBoxes: [
|
|
45
|
+
{
|
|
46
|
+
chipId: "U1",
|
|
47
|
+
center: { x: -0.83, y: 3.115 },
|
|
48
|
+
width: 0.36,
|
|
49
|
+
height: 0.25,
|
|
50
|
+
text: "U1",
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
availableNetLabelOrientations: {
|
|
54
|
+
V3V3_LEFT: ["y+"],
|
|
55
|
+
V3V3_RIGHT: ["y+"],
|
|
56
|
+
},
|
|
57
|
+
maxMspPairDistance: 2.4,
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
test("places RP2040 rail labels above and outside both chip sides", () => {
|
|
61
|
+
const solver = new SchematicTracePipelineSolver(inputProblem)
|
|
62
|
+
|
|
63
|
+
solver.solve()
|
|
64
|
+
|
|
65
|
+
const outputLabels =
|
|
66
|
+
solver.netLabelNetLabelCollisionSolver!.getOutput().netLabelPlacements
|
|
67
|
+
const u1 = solver.inputProblem.chips.find((chip) => chip.chipId === "U1")!
|
|
68
|
+
const chipLeft = u1.center.x - u1.width / 2
|
|
69
|
+
const chipRight = u1.center.x + u1.width / 2
|
|
70
|
+
|
|
71
|
+
for (const side of ["left", "right"] as const) {
|
|
72
|
+
const netId = `V3V3_${side.toUpperCase()}`
|
|
73
|
+
const label = outputLabels.find(
|
|
74
|
+
(candidate) =>
|
|
75
|
+
candidate.netId === netId && candidate.pinIds.includes(`U1.${side}.1`),
|
|
76
|
+
)!
|
|
77
|
+
const connector = solver.availableNetOrientationSolver!.traces.find(
|
|
78
|
+
(trace) =>
|
|
79
|
+
trace.userNetId === netId &&
|
|
80
|
+
trace.mspPairId.startsWith("available-net-orientation-"),
|
|
81
|
+
)!
|
|
82
|
+
const [connectorSource, connectorTarget] = connector.tracePath
|
|
83
|
+
const labelLeft = label.center.x - label.width / 2
|
|
84
|
+
const labelRight = label.center.x + label.width / 2
|
|
85
|
+
|
|
86
|
+
expect(label.orientation).toBe("y+")
|
|
87
|
+
expect(label.anchorPoint.y).toBeCloseTo(2.8)
|
|
88
|
+
expect(connector.tracePath).toHaveLength(2)
|
|
89
|
+
expect(connectorSource!.y).toBeCloseTo(connectorTarget!.y)
|
|
90
|
+
expect(Math.abs(connectorSource!.x - connectorTarget!.x)).toBeLessThan(0.5)
|
|
91
|
+
|
|
92
|
+
if (side === "left") {
|
|
93
|
+
expect(labelRight).toBeLessThan(chipLeft)
|
|
94
|
+
expect(connectorTarget!.x).toBeLessThan(connectorSource!.x)
|
|
95
|
+
} else {
|
|
96
|
+
expect(labelLeft).toBeGreaterThan(chipRight)
|
|
97
|
+
expect(connectorTarget!.x).toBeGreaterThan(connectorSource!.x)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
expect(solver).toMatchSolverSnapshot(import.meta.path)
|
|
102
|
+
})
|