@tscircuit/schematic-trace-solver 0.0.159 → 0.0.160
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 +40 -9
- package/dist/index.js +636 -107
- package/lib/solvers/InlineNetLabelSolver/InlineNetLabelSolver.ts +536 -94
- package/lib/solvers/InlineNetLabelSolver/pushAnchoredNetLabelsAwayFromInlineLabels.ts +9 -7
- package/lib/solvers/InlineNetLabelSolver/pushInlineTerminalLabelsAwayFromAnchoredLabels.ts +295 -0
- package/lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver.ts +7 -35
- package/lib/solvers/SchematicTraceLinesSolver/getTraceConnectedPinComponents.ts +60 -0
- package/lib/types/InputProblem.ts +5 -4
- package/package.json +1 -1
- package/tests/bug-reports/bug-report-20260826T072956Z/__snapshots__/bug-report-20260826T072956Z.snap.svg +600 -792
- package/tests/bug-reports/bug-report-20260826T072956Z/bug-report-20260826T072956Z.test.ts +66 -1
- package/tests/repros/repro-usb-power-vbus-label-detour.test.ts +14 -0
- package/tests/solvers/InlineNetLabelSolver/multi-pin-connected-components.test.ts +252 -0
- package/tests/solvers/InlineNetLabelSolver/opposite-side-placement.test.ts +71 -0
- package/tests/solvers/InlineNetLabelSolver/push-anchored-net-labels-away.test.ts +8 -2
- package/tests/solvers/InlineNetLabelSolver/push-inline-terminal-labels-away.test.ts +91 -0
- package/tests/solvers/InlineNetLabelSolver/two-pin-terminal-stubs-atomic.test.ts +7 -1
|
@@ -1,12 +1,77 @@
|
|
|
1
1
|
import { expect, test } from "bun:test"
|
|
2
|
+
import { estimateInlineNetLabelWidth } from "lib/solvers/InlineNetLabelSolver/InlineNetLabelSolver"
|
|
2
3
|
import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver"
|
|
4
|
+
import type { InputProblem } from "lib/types/InputProblem"
|
|
3
5
|
import inputProblem from "./bug-report-20260826T072956Z.json"
|
|
4
6
|
import "tests/fixtures/matcher"
|
|
5
7
|
|
|
8
|
+
const multiPinSignalNetIds = [
|
|
9
|
+
"ISNS_A",
|
|
10
|
+
"ISNS_B",
|
|
11
|
+
"ISNS_C",
|
|
12
|
+
"MOT_A",
|
|
13
|
+
"MOT_B",
|
|
14
|
+
"MOT_C",
|
|
15
|
+
"S1_N",
|
|
16
|
+
"S1_P",
|
|
17
|
+
"S2_N",
|
|
18
|
+
"S2_P",
|
|
19
|
+
"S3_N",
|
|
20
|
+
"S3_P",
|
|
21
|
+
"SL_A",
|
|
22
|
+
"SL_B",
|
|
23
|
+
"SL_C",
|
|
24
|
+
]
|
|
25
|
+
|
|
6
26
|
test("bug-report-20260826T072956Z", () => {
|
|
7
|
-
const
|
|
27
|
+
const problem = structuredClone(inputProblem) as unknown as InputProblem
|
|
28
|
+
for (const connection of problem.netConnections) {
|
|
29
|
+
if (!multiPinSignalNetIds.includes(connection.netId)) continue
|
|
30
|
+
connection.allowInlineNetLabel = true
|
|
31
|
+
connection.inlineNetLabelHeight = 0.12
|
|
32
|
+
connection.inlineNetLabelWidth = estimateInlineNetLabelWidth(
|
|
33
|
+
connection.netId,
|
|
34
|
+
connection.inlineNetLabelHeight,
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const solver = new SchematicTracePipelineSolver(problem, {
|
|
39
|
+
hideRatsNet: true,
|
|
40
|
+
})
|
|
8
41
|
|
|
9
42
|
solver.solve()
|
|
10
43
|
|
|
44
|
+
const output = solver.inlineNetLabelSolver!.getOutput()
|
|
45
|
+
const expectedInlineNetIds = ["GH_B", ...multiPinSignalNetIds]
|
|
46
|
+
expect(
|
|
47
|
+
new Set(
|
|
48
|
+
output.inlineNetLabelPlacements
|
|
49
|
+
.filter((placement) =>
|
|
50
|
+
expectedInlineNetIds.includes(placement.netId ?? ""),
|
|
51
|
+
)
|
|
52
|
+
.map((placement) => placement.netId),
|
|
53
|
+
),
|
|
54
|
+
).toEqual(new Set(expectedInlineNetIds))
|
|
55
|
+
expect(
|
|
56
|
+
output.netLabelPlacements.filter((placement) =>
|
|
57
|
+
expectedInlineNetIds.includes(placement.netId ?? ""),
|
|
58
|
+
),
|
|
59
|
+
).toHaveLength(0)
|
|
60
|
+
expect(
|
|
61
|
+
output.inlineNetLabelPlacements.some(
|
|
62
|
+
(placement) => placement.netId === "GH_B" && placement.side === "y-",
|
|
63
|
+
),
|
|
64
|
+
).toBe(true)
|
|
65
|
+
for (const supersededConnectorTraceId of [
|
|
66
|
+
"schematic_port_152-schematic_port_150",
|
|
67
|
+
"schematic_port_151-schematic_port_148",
|
|
68
|
+
"schematic_port_149-schematic_port_147",
|
|
69
|
+
]) {
|
|
70
|
+
expect(
|
|
71
|
+
output.traces.some(
|
|
72
|
+
(trace) => trace.mspPairId === supersededConnectorTraceId,
|
|
73
|
+
),
|
|
74
|
+
).toBe(false)
|
|
75
|
+
}
|
|
11
76
|
expect(solver).toMatchSolverSnapshot(import.meta.path)
|
|
12
77
|
})
|
|
@@ -37,6 +37,13 @@ test("routes the usb power vbus connection without a label detour", () => {
|
|
|
37
37
|
label.pinIds.includes("schematic_port_54") &&
|
|
38
38
|
label.pinIds.includes("schematic_port_162"),
|
|
39
39
|
)
|
|
40
|
+
const inlineOutput = solver.inlineNetLabelSolver!.getOutput()
|
|
41
|
+
const usbHighSpeedInlineLabels = inlineOutput.inlineNetLabelPlacements.filter(
|
|
42
|
+
(placement) => placement.netId?.startsWith("USB_HS_"),
|
|
43
|
+
)
|
|
44
|
+
const usbHighSpeedAnchoredLabels = inlineOutput.netLabelPlacements.filter(
|
|
45
|
+
(placement) => placement.netId?.startsWith("USB_HS_"),
|
|
46
|
+
)
|
|
40
47
|
|
|
41
48
|
expect(gndLabel?.orientation).toBe("y-")
|
|
42
49
|
expect(gndLabel?.anchorPoint.x).toBeCloseTo(-2.34)
|
|
@@ -53,5 +60,12 @@ test("routes the usb power vbus connection without a label detour", () => {
|
|
|
53
60
|
{ x: 12.8, y: -5.8 },
|
|
54
61
|
{ x: 13, y: -5.8 },
|
|
55
62
|
])
|
|
63
|
+
// The available inline side of USB_HS_DM overlaps the retained USB_HS_DP
|
|
64
|
+
// tag. Keep both differential-pair nets anchored instead of producing a
|
|
65
|
+
// mixed, overlapping representation.
|
|
66
|
+
expect(usbHighSpeedInlineLabels).toHaveLength(0)
|
|
67
|
+
expect(
|
|
68
|
+
usbHighSpeedAnchoredLabels.map((placement) => placement.netId).sort(),
|
|
69
|
+
).toEqual(["USB_HS_DM", "USB_HS_DM", "USB_HS_DP", "USB_HS_DP"])
|
|
56
70
|
expect(solver).toMatchSolverSnapshot(import.meta.path)
|
|
57
71
|
})
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import { expect, test } from "bun:test"
|
|
2
|
+
import { InlineNetLabelSolver } from "lib/solvers/InlineNetLabelSolver/InlineNetLabelSolver"
|
|
3
|
+
import type { NetLabelPlacement } from "lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver"
|
|
4
|
+
import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
|
|
5
|
+
import type { InputProblem } from "lib/types/InputProblem"
|
|
6
|
+
|
|
7
|
+
test("a multi-pin net labels routed and disconnected components inline", () => {
|
|
8
|
+
const inputProblem: InputProblem = {
|
|
9
|
+
chips: [
|
|
10
|
+
{
|
|
11
|
+
chipId: "J1",
|
|
12
|
+
center: { x: 0, y: 0 },
|
|
13
|
+
width: 1,
|
|
14
|
+
height: 1,
|
|
15
|
+
pins: [
|
|
16
|
+
{
|
|
17
|
+
pinId: "J1.1",
|
|
18
|
+
x: -0.5,
|
|
19
|
+
y: 0.2,
|
|
20
|
+
_facingDirection: "x-",
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
pinId: "J1.2",
|
|
24
|
+
x: -0.5,
|
|
25
|
+
y: -0.2,
|
|
26
|
+
_facingDirection: "x-",
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
chipId: "U1",
|
|
32
|
+
center: { x: 2, y: 0 },
|
|
33
|
+
width: 1,
|
|
34
|
+
height: 1,
|
|
35
|
+
pins: [
|
|
36
|
+
{
|
|
37
|
+
pinId: "U1.1",
|
|
38
|
+
x: 2.5,
|
|
39
|
+
y: 0,
|
|
40
|
+
_facingDirection: "x+",
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
directConnections: [],
|
|
46
|
+
netConnections: [
|
|
47
|
+
{
|
|
48
|
+
netId: "MOTOR",
|
|
49
|
+
pinIds: ["J1.1", "J1.2", "U1.1"],
|
|
50
|
+
allowInlineNetLabel: true,
|
|
51
|
+
inlineNetLabelWidth: 0.6,
|
|
52
|
+
inlineNetLabelHeight: 0.12,
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
availableNetLabelOrientations: { MOTOR: ["x-", "x+"] },
|
|
56
|
+
}
|
|
57
|
+
const shortConnectorTrace: SolvedTracePath = {
|
|
58
|
+
mspPairId: "J1.1-J1.2",
|
|
59
|
+
mspConnectionPairIds: ["J1.1-J1.2"],
|
|
60
|
+
dcConnNetId: "MOTOR",
|
|
61
|
+
globalConnNetId: "MOTOR",
|
|
62
|
+
userNetId: "MOTOR",
|
|
63
|
+
pins: [
|
|
64
|
+
{ pinId: "J1.1", chipId: "J1", x: -0.5, y: 0.2 },
|
|
65
|
+
{ pinId: "J1.2", chipId: "J1", x: -0.5, y: -0.2 },
|
|
66
|
+
],
|
|
67
|
+
pinIds: ["J1.1", "J1.2"],
|
|
68
|
+
tracePath: [
|
|
69
|
+
{ x: -0.5, y: 0.2 },
|
|
70
|
+
{ x: -0.8, y: 0.2 },
|
|
71
|
+
{ x: -0.8, y: -0.2 },
|
|
72
|
+
{ x: -0.5, y: -0.2 },
|
|
73
|
+
],
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const solver = new InlineNetLabelSolver({
|
|
77
|
+
inputProblem,
|
|
78
|
+
traces: [shortConnectorTrace],
|
|
79
|
+
netLabelPlacements: [],
|
|
80
|
+
})
|
|
81
|
+
solver.solve()
|
|
82
|
+
|
|
83
|
+
const output = solver.getOutput()
|
|
84
|
+
expect(output.inlineNetLabelPlacements).toHaveLength(3)
|
|
85
|
+
expect(
|
|
86
|
+
output.inlineNetLabelPlacements.every(
|
|
87
|
+
(placement) => placement.stubTracePath !== undefined,
|
|
88
|
+
),
|
|
89
|
+
).toBe(true)
|
|
90
|
+
expect(output.traces).toHaveLength(0)
|
|
91
|
+
expect(output.netLabelPlacements).toHaveLength(0)
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
test("a short routed component is retained when replacing it would strand an intermediate pin", () => {
|
|
95
|
+
const inputProblem: InputProblem = {
|
|
96
|
+
chips: [
|
|
97
|
+
{
|
|
98
|
+
chipId: "J1",
|
|
99
|
+
center: { x: 0, y: 0 },
|
|
100
|
+
width: 1,
|
|
101
|
+
height: 1,
|
|
102
|
+
pins: [
|
|
103
|
+
{ pinId: "J1.1", x: -0.5, y: 0.2, _facingDirection: "x-" },
|
|
104
|
+
{ pinId: "J1.2", x: -0.5, y: -0.2, _facingDirection: "x-" },
|
|
105
|
+
],
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
chipId: "U1",
|
|
109
|
+
center: { x: 2, y: 0 },
|
|
110
|
+
width: 1,
|
|
111
|
+
height: 1,
|
|
112
|
+
pins: [{ pinId: "U1.1", x: 2.5, y: 0, _facingDirection: "x+" }],
|
|
113
|
+
},
|
|
114
|
+
],
|
|
115
|
+
directConnections: [{ pinIds: ["J1.1", "J1.2"] }],
|
|
116
|
+
netConnections: [
|
|
117
|
+
{
|
|
118
|
+
netId: "MOTOR",
|
|
119
|
+
pinIds: ["J1.1", "U1.1"],
|
|
120
|
+
allowInlineNetLabel: true,
|
|
121
|
+
inlineNetLabelWidth: 0.6,
|
|
122
|
+
inlineNetLabelHeight: 0.12,
|
|
123
|
+
},
|
|
124
|
+
],
|
|
125
|
+
availableNetLabelOrientations: { MOTOR: ["x-", "x+"] },
|
|
126
|
+
}
|
|
127
|
+
const shortTrace: SolvedTracePath = {
|
|
128
|
+
mspPairId: "J1.1-J1.2",
|
|
129
|
+
mspConnectionPairIds: ["J1.1-J1.2"],
|
|
130
|
+
dcConnNetId: "MOTOR",
|
|
131
|
+
globalConnNetId: "MOTOR",
|
|
132
|
+
userNetId: "MOTOR",
|
|
133
|
+
pins: [
|
|
134
|
+
{ pinId: "J1.1", chipId: "J1", x: -0.5, y: 0.2 },
|
|
135
|
+
{ pinId: "J1.2", chipId: "J1", x: -0.5, y: -0.2 },
|
|
136
|
+
],
|
|
137
|
+
pinIds: ["J1.1", "J1.2"],
|
|
138
|
+
tracePath: [
|
|
139
|
+
{ x: -0.5, y: 0.2 },
|
|
140
|
+
{ x: -0.8, y: 0.2 },
|
|
141
|
+
{ x: -0.8, y: -0.2 },
|
|
142
|
+
{ x: -0.5, y: -0.2 },
|
|
143
|
+
],
|
|
144
|
+
}
|
|
145
|
+
const anchoredLabels: NetLabelPlacement[] = [
|
|
146
|
+
{
|
|
147
|
+
globalConnNetId: "MOTOR",
|
|
148
|
+
netId: "MOTOR",
|
|
149
|
+
mspConnectionPairIds: ["J1.1-J1.2"],
|
|
150
|
+
pinIds: ["J1.1", "J1.2"],
|
|
151
|
+
orientation: "x-",
|
|
152
|
+
anchorPoint: { x: -0.8, y: 0 },
|
|
153
|
+
center: { x: -1.2, y: 0 },
|
|
154
|
+
width: 0.6,
|
|
155
|
+
height: 0.12,
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
globalConnNetId: "MOTOR",
|
|
159
|
+
netId: "MOTOR",
|
|
160
|
+
mspConnectionPairIds: [],
|
|
161
|
+
pinIds: ["U1.1"],
|
|
162
|
+
orientation: "x+",
|
|
163
|
+
anchorPoint: { x: 2.5, y: 0 },
|
|
164
|
+
center: { x: 2.9, y: 0 },
|
|
165
|
+
width: 0.6,
|
|
166
|
+
height: 0.12,
|
|
167
|
+
},
|
|
168
|
+
]
|
|
169
|
+
|
|
170
|
+
const solver = new InlineNetLabelSolver({
|
|
171
|
+
inputProblem,
|
|
172
|
+
traces: [shortTrace],
|
|
173
|
+
netLabelPlacements: anchoredLabels,
|
|
174
|
+
})
|
|
175
|
+
solver.solve()
|
|
176
|
+
|
|
177
|
+
const output = solver.getOutput()
|
|
178
|
+
expect(output.inlineNetLabelPlacements).toHaveLength(0)
|
|
179
|
+
expect(output.traces).toEqual([shortTrace])
|
|
180
|
+
expect(output.netLabelPlacements).toEqual(anchoredLabels)
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
test("a generated anchored-label connector becomes an inline terminal stub", () => {
|
|
184
|
+
const inputProblem: InputProblem = {
|
|
185
|
+
chips: [
|
|
186
|
+
{
|
|
187
|
+
chipId: "U1",
|
|
188
|
+
center: { x: 0, y: 0 },
|
|
189
|
+
width: 1,
|
|
190
|
+
height: 1,
|
|
191
|
+
pins: [{ pinId: "U1.1", x: -0.5, y: 0, _facingDirection: "x-" }],
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
chipId: "U2",
|
|
195
|
+
center: { x: 3, y: 0 },
|
|
196
|
+
width: 1,
|
|
197
|
+
height: 1,
|
|
198
|
+
pins: [{ pinId: "U2.1", x: 2.5, y: 0, _facingDirection: "x-" }],
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
chipId: "U3",
|
|
202
|
+
center: { x: 6, y: 0 },
|
|
203
|
+
width: 1,
|
|
204
|
+
height: 1,
|
|
205
|
+
pins: [{ pinId: "U3.1", x: 5.5, y: 0, _facingDirection: "x-" }],
|
|
206
|
+
},
|
|
207
|
+
],
|
|
208
|
+
directConnections: [],
|
|
209
|
+
netConnections: [
|
|
210
|
+
{
|
|
211
|
+
netId: "SIGNAL",
|
|
212
|
+
pinIds: ["U1.1", "U2.1", "U3.1"],
|
|
213
|
+
allowInlineNetLabel: true,
|
|
214
|
+
inlineNetLabelWidth: 0.6,
|
|
215
|
+
inlineNetLabelHeight: 0.12,
|
|
216
|
+
},
|
|
217
|
+
],
|
|
218
|
+
availableNetLabelOrientations: { SIGNAL: ["x-"] },
|
|
219
|
+
}
|
|
220
|
+
const generatedLabelConnector: SolvedTracePath = {
|
|
221
|
+
mspPairId: "available-net-orientation-0-SIGNAL",
|
|
222
|
+
mspConnectionPairIds: ["available-net-orientation-0-SIGNAL"],
|
|
223
|
+
dcConnNetId: "SIGNAL",
|
|
224
|
+
globalConnNetId: "SIGNAL",
|
|
225
|
+
userNetId: "SIGNAL",
|
|
226
|
+
pins: [
|
|
227
|
+
{ pinId: "U1.1", chipId: "U1", x: -0.5, y: 0 },
|
|
228
|
+
{ pinId: "U1.1", chipId: "U1", x: -0.5, y: 0 },
|
|
229
|
+
],
|
|
230
|
+
pinIds: ["U1.1"],
|
|
231
|
+
tracePath: [
|
|
232
|
+
{ x: -0.5, y: 0 },
|
|
233
|
+
{ x: -1.5, y: 0 },
|
|
234
|
+
],
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const solver = new InlineNetLabelSolver({
|
|
238
|
+
inputProblem,
|
|
239
|
+
traces: [generatedLabelConnector],
|
|
240
|
+
netLabelPlacements: [],
|
|
241
|
+
})
|
|
242
|
+
solver.solve()
|
|
243
|
+
|
|
244
|
+
const output = solver.getOutput()
|
|
245
|
+
expect(output.inlineNetLabelPlacements).toHaveLength(3)
|
|
246
|
+
expect(
|
|
247
|
+
output.inlineNetLabelPlacements.every(
|
|
248
|
+
(placement) => placement.stubTracePath !== undefined,
|
|
249
|
+
),
|
|
250
|
+
).toBe(true)
|
|
251
|
+
expect(output.traces).toHaveLength(0)
|
|
252
|
+
})
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { expect, test } from "bun:test"
|
|
2
|
+
import { InlineNetLabelSolver } from "lib/solvers/InlineNetLabelSolver/InlineNetLabelSolver"
|
|
3
|
+
import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
|
|
4
|
+
import type { InputProblem } from "lib/types/InputProblem"
|
|
5
|
+
|
|
6
|
+
test("an inline label tries the opposite side when the preferred side is blocked", () => {
|
|
7
|
+
const inputProblem: InputProblem = {
|
|
8
|
+
chips: [
|
|
9
|
+
{
|
|
10
|
+
chipId: "U1",
|
|
11
|
+
center: { x: -2, y: 0 },
|
|
12
|
+
width: 1,
|
|
13
|
+
height: 1,
|
|
14
|
+
pins: [{ pinId: "U1.1", x: -1.5, y: 0 }],
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
chipId: "U2",
|
|
18
|
+
center: { x: 2, y: 0 },
|
|
19
|
+
width: 1,
|
|
20
|
+
height: 1,
|
|
21
|
+
pins: [{ pinId: "U2.1", x: 1.5, y: 0 }],
|
|
22
|
+
},
|
|
23
|
+
],
|
|
24
|
+
directConnections: [
|
|
25
|
+
{
|
|
26
|
+
netId: "SIGNAL",
|
|
27
|
+
pinIds: ["U1.1", "U2.1"],
|
|
28
|
+
allowInlineNetLabel: true,
|
|
29
|
+
inlineNetLabelWidth: 0.8,
|
|
30
|
+
inlineNetLabelHeight: 0.12,
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
netConnections: [],
|
|
34
|
+
textBoxes: [
|
|
35
|
+
{
|
|
36
|
+
center: { x: 0, y: 0.11 },
|
|
37
|
+
width: 3,
|
|
38
|
+
height: 0.12,
|
|
39
|
+
text: "preferred-side obstacle",
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
availableNetLabelOrientations: { SIGNAL: ["x-", "x+"] },
|
|
43
|
+
}
|
|
44
|
+
const trace: SolvedTracePath = {
|
|
45
|
+
mspPairId: "U1.1-U2.1",
|
|
46
|
+
mspConnectionPairIds: ["U1.1-U2.1"],
|
|
47
|
+
dcConnNetId: "SIGNAL",
|
|
48
|
+
globalConnNetId: "SIGNAL",
|
|
49
|
+
userNetId: "SIGNAL",
|
|
50
|
+
pins: [
|
|
51
|
+
{ pinId: "U1.1", chipId: "U1", x: -1.5, y: 0 },
|
|
52
|
+
{ pinId: "U2.1", chipId: "U2", x: 1.5, y: 0 },
|
|
53
|
+
],
|
|
54
|
+
pinIds: ["U1.1", "U2.1"],
|
|
55
|
+
tracePath: [
|
|
56
|
+
{ x: -1.5, y: 0 },
|
|
57
|
+
{ x: 1.5, y: 0 },
|
|
58
|
+
],
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const solver = new InlineNetLabelSolver({
|
|
62
|
+
inputProblem,
|
|
63
|
+
traces: [trace],
|
|
64
|
+
netLabelPlacements: [],
|
|
65
|
+
})
|
|
66
|
+
solver.solve()
|
|
67
|
+
|
|
68
|
+
expect(solver.inlineNetLabelPlacements).toHaveLength(1)
|
|
69
|
+
expect(solver.inlineNetLabelPlacements[0]!.side).toBe("y-")
|
|
70
|
+
expect(solver.inlineNetLabelPlacements[0]!.center.y).toBeLessThan(0)
|
|
71
|
+
})
|
|
@@ -11,9 +11,15 @@ test("pushes a regular endpoint label and its wick past nearby inline text", ()
|
|
|
11
11
|
{
|
|
12
12
|
chipId: "U1",
|
|
13
13
|
center: { x: 0, y: 0 },
|
|
14
|
-
|
|
14
|
+
// The routing box includes component text, so the real pins can sit
|
|
15
|
+
// inside it. A generated connector may legitimately cross that
|
|
16
|
+
// expanded owner box on its way out from the pin.
|
|
17
|
+
width: 2,
|
|
15
18
|
height: 1,
|
|
16
|
-
pins: [
|
|
19
|
+
pins: [
|
|
20
|
+
{ pinId: "U1.1", x: -0.5, y: 0.2, _facingDirection: "x-" },
|
|
21
|
+
{ pinId: "U1.2", x: -0.5, y: 0, _facingDirection: "x-" },
|
|
22
|
+
],
|
|
17
23
|
},
|
|
18
24
|
],
|
|
19
25
|
directConnections: [],
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { expect, test } from "bun:test"
|
|
2
|
+
import type { InlineNetLabelPlacement } from "lib/solvers/InlineNetLabelSolver/InlineNetLabelSolver"
|
|
3
|
+
import { pushInlineTerminalLabelsAwayFromAnchoredLabels } from "lib/solvers/InlineNetLabelSolver/pushInlineTerminalLabelsAwayFromAnchoredLabels"
|
|
4
|
+
import type { NetLabelPlacement } from "lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver"
|
|
5
|
+
import type { InputProblem } from "lib/types/InputProblem"
|
|
6
|
+
|
|
7
|
+
test("moves an inline terminal row outward past a retained anchored label", () => {
|
|
8
|
+
const inputProblem: InputProblem = {
|
|
9
|
+
chips: [
|
|
10
|
+
{
|
|
11
|
+
chipId: "J1",
|
|
12
|
+
center: { x: 0, y: 0 },
|
|
13
|
+
width: 1,
|
|
14
|
+
height: 1,
|
|
15
|
+
pins: [
|
|
16
|
+
{ pinId: "J1.A0", x: -0.5, y: 0.2, _facingDirection: "x-" },
|
|
17
|
+
{ pinId: "J1.SCLK", x: -0.5, y: 0, _facingDirection: "x-" },
|
|
18
|
+
{ pinId: "J1.MISO", x: -0.5, y: -0.2, _facingDirection: "x-" },
|
|
19
|
+
],
|
|
20
|
+
},
|
|
21
|
+
],
|
|
22
|
+
directConnections: [],
|
|
23
|
+
netConnections: [],
|
|
24
|
+
availableNetLabelOrientations: {},
|
|
25
|
+
}
|
|
26
|
+
const anchoredNetLabelPlacements: NetLabelPlacement[] = [
|
|
27
|
+
{
|
|
28
|
+
globalConnNetId: "A0",
|
|
29
|
+
netId: "A0",
|
|
30
|
+
mspConnectionPairIds: [],
|
|
31
|
+
pinIds: ["J1.A0"],
|
|
32
|
+
orientation: "x-",
|
|
33
|
+
anchorPoint: { x: -0.5, y: 0.2 },
|
|
34
|
+
center: { x: -0.75, y: 0.2 },
|
|
35
|
+
width: 0.5,
|
|
36
|
+
height: 0.2,
|
|
37
|
+
},
|
|
38
|
+
]
|
|
39
|
+
const inlineNetLabelPlacements: InlineNetLabelPlacement[] = [
|
|
40
|
+
{
|
|
41
|
+
globalConnNetId: "SCLK",
|
|
42
|
+
netId: "SCLK",
|
|
43
|
+
pinIds: ["J1.SCLK"],
|
|
44
|
+
stubTracePath: [
|
|
45
|
+
{ x: -0.5, y: 0 },
|
|
46
|
+
{ x: -1.1, y: 0 },
|
|
47
|
+
],
|
|
48
|
+
axis: "x",
|
|
49
|
+
anchorPoint: { x: -0.8, y: 0 },
|
|
50
|
+
center: { x: -0.8, y: 0.11 },
|
|
51
|
+
width: 0.4,
|
|
52
|
+
height: 0.12,
|
|
53
|
+
side: "y+",
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
globalConnNetId: "MISO",
|
|
57
|
+
netId: "MISO",
|
|
58
|
+
pinIds: ["J1.MISO"],
|
|
59
|
+
stubTracePath: [
|
|
60
|
+
{ x: -0.5, y: -0.2 },
|
|
61
|
+
{ x: -1.1, y: -0.2 },
|
|
62
|
+
],
|
|
63
|
+
axis: "x",
|
|
64
|
+
anchorPoint: { x: -0.8, y: -0.2 },
|
|
65
|
+
center: { x: -0.8, y: -0.09 },
|
|
66
|
+
width: 0.4,
|
|
67
|
+
height: 0.12,
|
|
68
|
+
side: "y+",
|
|
69
|
+
},
|
|
70
|
+
]
|
|
71
|
+
|
|
72
|
+
const output = pushInlineTerminalLabelsAwayFromAnchoredLabels({
|
|
73
|
+
inputProblem,
|
|
74
|
+
traces: [],
|
|
75
|
+
anchoredNetLabelPlacements,
|
|
76
|
+
inlineNetLabelPlacements,
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
expect(output.movedGroupCount).toBe(1)
|
|
80
|
+
expect(output.inlineNetLabelPlacements[0]!.center.x).toBeLessThan(-0.8)
|
|
81
|
+
expect(output.inlineNetLabelPlacements[1]!.center.x).toBe(
|
|
82
|
+
output.inlineNetLabelPlacements[0]!.center.x,
|
|
83
|
+
)
|
|
84
|
+
expect(output.inlineNetLabelPlacements[0]!.stubTracePath![0]).toEqual({
|
|
85
|
+
x: -0.5,
|
|
86
|
+
y: 0,
|
|
87
|
+
})
|
|
88
|
+
expect(output.inlineNetLabelPlacements[0]!.stubTracePath![1]!.x).toBeLessThan(
|
|
89
|
+
-1.1,
|
|
90
|
+
)
|
|
91
|
+
})
|
|
@@ -36,7 +36,13 @@ test("two-pin terminal stubs fall back atomically when one endpoint is obstructe
|
|
|
36
36
|
center: { x: -0.85, y: 0.11 },
|
|
37
37
|
width: 1.1,
|
|
38
38
|
height: 0.12,
|
|
39
|
-
text: "obstacle",
|
|
39
|
+
text: "upper obstacle",
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
center: { x: -0.85, y: -0.11 },
|
|
43
|
+
width: 1.1,
|
|
44
|
+
height: 0.12,
|
|
45
|
+
text: "lower obstacle",
|
|
40
46
|
},
|
|
41
47
|
],
|
|
42
48
|
availableNetLabelOrientations: { NET_SIGNAL: ["x-", "x+"] },
|