@tscircuit/schematic-trace-solver 0.0.144 → 0.0.146

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.
@@ -0,0 +1,28 @@
1
+ import { expect, test } from "bun:test"
2
+ import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver"
3
+ import inputProblem from "../../assets/inline-net-label-anchored-label-clearance.json"
4
+ import "tests/fixtures/matcher"
5
+
6
+ test("regular labels are drawn beyond adjacent inline labels", () => {
7
+ const solver = new SchematicTracePipelineSolver(inputProblem as any)
8
+
9
+ solver.solve()
10
+
11
+ const output = solver.inlineNetLabelSolver!.getOutput()
12
+ const regularLabels = output.netLabelPlacements.filter((label) =>
13
+ ["D_MINUS", "D_PLUS"].includes(label.netId ?? ""),
14
+ )
15
+ expect(solver.inlineNetLabelSolver!.stats.pushedAnchoredNetLabelCount).toBe(2)
16
+ expect(regularLabels).toHaveLength(2)
17
+ expect(regularLabels[0]!.anchorPoint.x).toBeCloseTo(
18
+ regularLabels[1]!.anchorPoint.x,
19
+ )
20
+ expect(regularLabels[0]!.anchorPoint.x).toBeLessThan(-0.7)
21
+ expect(
22
+ output.traces.filter((trace) =>
23
+ trace.mspPairId.startsWith("inline-net-label-clearance-"),
24
+ ),
25
+ ).toHaveLength(2)
26
+
27
+ expect(solver).toMatchSolverSnapshot(import.meta.path)
28
+ })
@@ -0,0 +1,227 @@
1
+ import { expect, test } from "bun:test"
2
+ import type { NetLabelPlacement } from "lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver"
3
+ import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
4
+ import type { InputProblem } from "lib/types/InputProblem"
5
+ import { pushAnchoredNetLabelsAwayFromInlineLabels } from "lib/solvers/InlineNetLabelSolver/pushAnchoredNetLabelsAwayFromInlineLabels"
6
+ import type { InlineNetLabelPlacement } from "lib/solvers/InlineNetLabelSolver/InlineNetLabelSolver"
7
+
8
+ test("pushes a regular endpoint label and its wick past nearby inline text", () => {
9
+ const inputProblem: InputProblem = {
10
+ chips: [
11
+ {
12
+ chipId: "U1",
13
+ center: { x: 0, y: 0 },
14
+ width: 1,
15
+ height: 1,
16
+ pins: [],
17
+ },
18
+ ],
19
+ directConnections: [],
20
+ netConnections: [],
21
+ availableNetLabelOrientations: {},
22
+ }
23
+ const netLabelPlacements: NetLabelPlacement[] = [
24
+ {
25
+ globalConnNetId: "regular-net",
26
+ netId: "D_PLUS",
27
+ mspConnectionPairIds: [],
28
+ pinIds: ["U1.1"],
29
+ orientation: "x-",
30
+ anchorPoint: { x: -0.7, y: 0.2 },
31
+ center: { x: -1.2, y: 0.2 },
32
+ width: 1,
33
+ height: 0.2,
34
+ },
35
+ ]
36
+ const traces: SolvedTracePath[] = [
37
+ {
38
+ mspPairId: "available-net-orientation-0-D_PLUS",
39
+ dcConnNetId: "regular-net",
40
+ globalConnNetId: "regular-net",
41
+ pins: [] as any,
42
+ tracePath: [
43
+ { x: -0.5, y: 0.2 },
44
+ { x: -0.7, y: 0.2 },
45
+ ],
46
+ mspConnectionPairIds: [],
47
+ pinIds: ["U1.1"],
48
+ },
49
+ ]
50
+ const inlineNetLabelPlacements: InlineNetLabelPlacement[] = [
51
+ {
52
+ globalConnNetId: "inline-net",
53
+ netId: "SWCLK",
54
+ pinIds: ["U1.2"],
55
+ stubTracePath: [
56
+ { x: -0.5, y: 0 },
57
+ { x: -1.7, y: 0 },
58
+ ],
59
+ axis: "x",
60
+ anchorPoint: { x: -1.1, y: 0 },
61
+ center: { x: -1.1, y: 0.11 },
62
+ width: 1.2,
63
+ height: 0.12,
64
+ side: "y+",
65
+ },
66
+ ]
67
+
68
+ const output = pushAnchoredNetLabelsAwayFromInlineLabels({
69
+ inputProblem,
70
+ traces,
71
+ netLabelPlacements,
72
+ inlineNetLabelPlacements,
73
+ })
74
+
75
+ expect(output.movedLabelCount).toBe(1)
76
+ expect(output.netLabelPlacements[0]!.anchorPoint.x).toBeCloseTo(-1.75)
77
+ expect(output.netLabelPlacements[0]!.center.x).toBeCloseTo(-2.25)
78
+ expect(output.traces[0]!.tracePath[0]).toEqual({ x: -0.5, y: 0.2 })
79
+ expect(output.traces[0]!.tracePath[1]!.x).toBeCloseTo(-1.75)
80
+ expect(output.traces[0]!.tracePath[1]!.y).toBeCloseTo(0.2)
81
+ })
82
+
83
+ test("leaves a label without a movable connector in place", () => {
84
+ const label: NetLabelPlacement = {
85
+ globalConnNetId: "regular-net",
86
+ netId: "D_PLUS",
87
+ mspConnectionPairIds: ["routed-pair"],
88
+ pinIds: ["U1.1", "J1.1"],
89
+ orientation: "x-",
90
+ anchorPoint: { x: -0.7, y: 0.2 },
91
+ center: { x: -1.2, y: 0.2 },
92
+ width: 1,
93
+ height: 0.2,
94
+ }
95
+ const inlineLabel: InlineNetLabelPlacement = {
96
+ globalConnNetId: "inline-net",
97
+ netId: "SWCLK",
98
+ pinIds: ["U1.2"],
99
+ axis: "x",
100
+ anchorPoint: { x: -1.1, y: 0 },
101
+ center: { x: -1.1, y: 0.11 },
102
+ width: 1.2,
103
+ height: 0.12,
104
+ side: "y+",
105
+ }
106
+
107
+ const output = pushAnchoredNetLabelsAwayFromInlineLabels({
108
+ inputProblem: {
109
+ chips: [],
110
+ directConnections: [],
111
+ netConnections: [],
112
+ availableNetLabelOrientations: {},
113
+ },
114
+ traces: [],
115
+ netLabelPlacements: [label],
116
+ inlineNetLabelPlacements: [inlineLabel],
117
+ })
118
+
119
+ expect(output.movedLabelCount).toBe(0)
120
+ expect(output.netLabelPlacements[0]).toEqual(label)
121
+ })
122
+
123
+ test("moves a contiguous label row together and shoves an anchored obstacle", () => {
124
+ const inputProblem: InputProblem = {
125
+ chips: [
126
+ {
127
+ chipId: "U1",
128
+ center: { x: 0, y: 0.2 },
129
+ width: 1.4,
130
+ height: 1,
131
+ pins: [
132
+ { pinId: "U1.minus", x: -0.7, y: 0.4, _facingDirection: "x-" },
133
+ { pinId: "U1.plus", x: -0.7, y: 0.2, _facingDirection: "x-" },
134
+ { pinId: "U1.inline", x: -0.7, y: 0, _facingDirection: "x-" },
135
+ { pinId: "U1.gnd1", x: -0.7, y: 0.5, _facingDirection: "x-" },
136
+ { pinId: "U1.gnd2", x: -2.5, y: 0.5, _facingDirection: "x+" },
137
+ ],
138
+ },
139
+ ],
140
+ directConnections: [],
141
+ netConnections: [],
142
+ availableNetLabelOrientations: {},
143
+ }
144
+ const netLabelPlacements: NetLabelPlacement[] = [
145
+ {
146
+ globalConnNetId: "minus-net",
147
+ netId: "D_MINUS",
148
+ mspConnectionPairIds: [],
149
+ pinIds: ["U1.minus"],
150
+ orientation: "x-",
151
+ anchorPoint: { x: -0.7, y: 0.4 },
152
+ center: { x: -1.2, y: 0.4 },
153
+ width: 1,
154
+ height: 0.2,
155
+ },
156
+ {
157
+ globalConnNetId: "plus-net",
158
+ netId: "D_PLUS",
159
+ mspConnectionPairIds: [],
160
+ pinIds: ["U1.plus"],
161
+ orientation: "x-",
162
+ anchorPoint: { x: -0.7, y: 0.2 },
163
+ center: { x: -1.2, y: 0.2 },
164
+ width: 1,
165
+ height: 0.2,
166
+ },
167
+ {
168
+ globalConnNetId: "gnd-net",
169
+ netId: "GND",
170
+ mspConnectionPairIds: ["gnd-route"],
171
+ pinIds: ["U1.gnd1", "U1.gnd2"],
172
+ orientation: "y-",
173
+ anchorPoint: { x: -2.5, y: 0.5 },
174
+ center: { x: -2.5, y: 0.3 },
175
+ width: 0.4,
176
+ height: 0.4,
177
+ },
178
+ ]
179
+ const traces: SolvedTracePath[] = [
180
+ {
181
+ mspPairId: "gnd-route",
182
+ dcConnNetId: "gnd-net",
183
+ globalConnNetId: "gnd-net",
184
+ pins: [] as any,
185
+ tracePath: [
186
+ { x: -0.7, y: 0.5 },
187
+ { x: -2.5, y: 0.5 },
188
+ ],
189
+ mspConnectionPairIds: ["gnd-route"],
190
+ pinIds: ["U1.gnd1", "U1.gnd2"],
191
+ },
192
+ ]
193
+ const inlineNetLabelPlacements: InlineNetLabelPlacement[] = [
194
+ {
195
+ globalConnNetId: "inline-net",
196
+ netId: "SWCLK",
197
+ pinIds: ["U1.inline"],
198
+ stubTracePath: [
199
+ { x: -0.7, y: 0 },
200
+ { x: -1.9, y: 0 },
201
+ ],
202
+ axis: "x",
203
+ anchorPoint: { x: -1.3, y: 0 },
204
+ center: { x: -1.1, y: 0.11 },
205
+ width: 1.2,
206
+ height: 0.12,
207
+ side: "y+",
208
+ },
209
+ ]
210
+
211
+ const output = pushAnchoredNetLabelsAwayFromInlineLabels({
212
+ inputProblem,
213
+ traces,
214
+ netLabelPlacements,
215
+ inlineNetLabelPlacements,
216
+ })
217
+
218
+ expect(output.movedLabelCount).toBe(3)
219
+ expect(output.netLabelPlacements[0]!.anchorPoint.x).toBeCloseTo(-1.75)
220
+ expect(output.netLabelPlacements[1]!.anchorPoint.x).toBeCloseTo(-1.75)
221
+ expect(output.netLabelPlacements[2]!.center.x).toBeLessThan(-2.5)
222
+ expect(
223
+ output.traces.filter((trace) =>
224
+ trace.mspPairId.startsWith("inline-net-label-clearance-"),
225
+ ),
226
+ ).toHaveLength(3)
227
+ })
@@ -44,3 +44,83 @@ test("two-pin direct connections use Euclidean max distance", () => {
44
44
  expect(directConnectionSolver.mspConnectionPairs).toHaveLength(1)
45
45
  expect(netConnectionSolver.mspConnectionPairs).toHaveLength(0)
46
46
  })
47
+
48
+ test("routes a distant labeled connection when opposed fallback labels would overlap", () => {
49
+ const inputProblem = createInputProblem()
50
+ inputProblem.maxMspPairDistance = 1
51
+ inputProblem.chips[0]!.sectionId = "power-output"
52
+ inputProblem.chips[1]!.sectionId = "power-output"
53
+ inputProblem.chips[0]!.pins.push({
54
+ pinId: "U1.2",
55
+ x: 0,
56
+ y: -0.2,
57
+ _facingDirection: "x+",
58
+ })
59
+ inputProblem.chips[1]!.pins.push({
60
+ pinId: "U2.2",
61
+ x: 2,
62
+ y: 1.8,
63
+ _facingDirection: "x-",
64
+ })
65
+ inputProblem.chips[1]!.pins[0]!.y = 0.1
66
+ inputProblem.directConnections[0]!.netId = "LONG_POWER_NAME"
67
+ inputProblem.directConnections[0]!.netLabelWidth = 1.2
68
+
69
+ const solver = new MspConnectionPairSolver({ inputProblem })
70
+ solver.solve()
71
+
72
+ expect(solver.mspConnectionPairs).toHaveLength(1)
73
+ expect(solver.mspConnectionPairs[0]?.suppressNetLabel).toBe(true)
74
+ })
75
+
76
+ test("keeps distant labeled fallback when opposed labels have room", () => {
77
+ const inputProblem = createInputProblem()
78
+ inputProblem.maxMspPairDistance = 1
79
+ inputProblem.chips[0]!.sectionId = "power-output"
80
+ inputProblem.chips[1]!.sectionId = "power-output"
81
+ inputProblem.chips[0]!.pins.push({
82
+ pinId: "U1.2",
83
+ x: 0,
84
+ y: -0.2,
85
+ _facingDirection: "x+",
86
+ })
87
+ inputProblem.chips[1]!.pins.push({
88
+ pinId: "U2.2",
89
+ x: 2,
90
+ y: 1.8,
91
+ _facingDirection: "x-",
92
+ })
93
+ inputProblem.chips[1]!.pins[0]!.y = 0.1
94
+ inputProblem.directConnections[0]!.netId = "SIG"
95
+ inputProblem.directConnections[0]!.netLabelWidth = 0.6
96
+
97
+ const solver = new MspConnectionPairSolver({ inputProblem })
98
+ solver.solve()
99
+
100
+ expect(solver.mspConnectionPairs).toHaveLength(0)
101
+ })
102
+
103
+ test("does not apply the overlap fallback outside a shared schematic section", () => {
104
+ const inputProblem = createInputProblem()
105
+ inputProblem.maxMspPairDistance = 1
106
+ inputProblem.chips[0]!.pins.push({
107
+ pinId: "U1.2",
108
+ x: 0,
109
+ y: -0.2,
110
+ _facingDirection: "x+",
111
+ })
112
+ inputProblem.chips[1]!.pins.push({
113
+ pinId: "U2.2",
114
+ x: 2,
115
+ y: 1.8,
116
+ _facingDirection: "x-",
117
+ })
118
+ inputProblem.chips[1]!.pins[0]!.y = 0.1
119
+ inputProblem.directConnections[0]!.netId = "LONG_POWER_NAME"
120
+ inputProblem.directConnections[0]!.netLabelWidth = 1.2
121
+
122
+ const solver = new MspConnectionPairSolver({ inputProblem })
123
+ solver.solve()
124
+
125
+ expect(solver.mspConnectionPairs).toHaveLength(0)
126
+ })