@tscircuit/schematic-trace-solver 0.0.120 → 0.0.122
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 +10 -3
- package/dist/index.js +350 -78
- package/lib/solvers/RailNetLabelCornerPlacementSolver/RailNetLabelCornerPlacementSolver.ts +223 -21
- package/lib/solvers/RailNetLabelCornerPlacementSolver/types.ts +3 -3
- package/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts +34 -9
- package/lib/solvers/TraceCleanupSolver/sameNetRailAlignment/evaluateRailGroup.ts +79 -59
- package/lib/solvers/TraceCleanupSolver/sameNetRailAlignment/getRailAlignmentFallbackCoordinates.ts +96 -0
- package/lib/utils/doesPathCoincideWithTraces.ts +71 -1
- package/package.json +1 -1
- package/site/bug-reports/bug-report-20260804T095800Z.page.tsx +4 -0
- package/tests/bug-reports/bug-report-20260706T213649Z/__snapshots__/bug-report-20260706T213649Z.snap.svg +4 -4
- package/tests/bug-reports/bug-report-20260706T220324Z/__snapshots__/bug-report-20260706T220324Z.snap.svg +5 -5
- package/tests/bug-reports/bug-report-20260717T042845Z/__snapshots__/bug-report-20260717T042845Z.snap.svg +12 -12
- package/tests/bug-reports/bug-report-20260721T221026Z/__snapshots__/bug-report-20260721T221026Z.snap.svg +2 -2
- package/tests/bug-reports/bug-report-20260728T144234Z/__snapshots__/bug-report-20260728T144234Z.snap.svg +2 -2
- package/tests/bug-reports/bug-report-20260804T095800Z/__snapshots__/bug-report-20260804T095800Z.snap.svg +135 -0
- package/tests/bug-reports/bug-report-20260804T095800Z/bug-report-20260804T095800Z.json +634 -0
- package/tests/bug-reports/bug-report-20260804T095800Z/bug-report-20260804T095800Z.test.ts +12 -0
- package/tests/examples/__snapshots__/example12.snap.svg +2 -2
- package/tests/examples/__snapshots__/example13.snap.svg +4 -4
- package/tests/examples/__snapshots__/example14.snap.svg +36 -36
- package/tests/examples/__snapshots__/example31.snap.svg +6 -6
- package/tests/examples/__snapshots__/example43.snap.svg +2 -2
- package/tests/examples/__snapshots__/example44.snap.svg +2 -2
- package/tests/repros/__snapshots__/board-15984-same-net-junction.snap.svg +2 -2
- package/tests/repros/__snapshots__/bugreport-001-gnd-overlap.snap.svg +26 -26
- package/tests/repros/__snapshots__/netlabel-connector-through-rail-label.snap.svg +2 -2
- package/tests/repros/__snapshots__/repro-atmega328p-fault-pullup.snap.svg +2 -2
- package/tests/repros/__snapshots__/repro-atmega328p-missing-gnd-netlabel.snap.svg +2 -2
- package/tests/repros/__snapshots__/repro-bq25895-cross-net-trace-overlap.snap.svg +101 -0
- package/tests/repros/__snapshots__/repro-missing-trace-netlabel.snap.svg +18 -18
- package/tests/repros/__snapshots__/repro-netlabel-overlap-trace.snap.svg +3 -3
- package/tests/repros/__snapshots__/repro-nrf52810-clock-routing.snap.svg +82 -0
- package/tests/repros/assets/repro-bq25895-cross-net-trace-overlap.input.json +498 -0
- package/tests/repros/assets/repro-nrf52810-clock-routing.input.json +234 -0
- package/tests/repros/repro-bq25895-cross-net-trace-overlap.test.ts +88 -0
- package/tests/repros/repro-missing-trace-netlabel.test.ts +12 -0
- package/tests/repros/repro-netlabel-overlap-trace.test.ts +38 -0
- package/tests/repros/repro-nrf52810-clock-routing.test.ts +28 -0
- package/tests/solvers/TraceCleanupSolver/alignSameNetRails-trace-clearance.test.ts +68 -0
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
{
|
|
2
|
+
"chips": [
|
|
3
|
+
{
|
|
4
|
+
"chipId": "schematic_component_30",
|
|
5
|
+
"center": { "x": -7, "y": 5 },
|
|
6
|
+
"width": 3.1999999999999993,
|
|
7
|
+
"height": 0.5999999999999996,
|
|
8
|
+
"pins": [
|
|
9
|
+
{ "pinId": "schematic_port_38", "x": -5.4, "y": 5.1 },
|
|
10
|
+
{ "pinId": "schematic_port_39", "x": -5.4, "y": 4.9 }
|
|
11
|
+
],
|
|
12
|
+
"sectionId": "hf-clock"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"chipId": "schematic_component_31",
|
|
16
|
+
"center": { "x": -4, "y": 5 },
|
|
17
|
+
"width": 1.08,
|
|
18
|
+
"height": 1.42,
|
|
19
|
+
"pins": [
|
|
20
|
+
{
|
|
21
|
+
"pinId": "schematic_port_40",
|
|
22
|
+
"x": -4,
|
|
23
|
+
"y": 5.71,
|
|
24
|
+
"_facingDirection": "y+"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"pinId": "schematic_port_41",
|
|
28
|
+
"x": -4.02,
|
|
29
|
+
"y": 4.29,
|
|
30
|
+
"_facingDirection": "y-"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"pinId": "schematic_port_42",
|
|
34
|
+
"x": -4.54,
|
|
35
|
+
"y": 4.99,
|
|
36
|
+
"_facingDirection": "x-"
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"pinId": "schematic_port_43",
|
|
40
|
+
"x": -3.46,
|
|
41
|
+
"y": 4.99,
|
|
42
|
+
"_facingDirection": "x+"
|
|
43
|
+
}
|
|
44
|
+
],
|
|
45
|
+
"sectionId": "hf-clock"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"chipId": "schematic_component_32",
|
|
49
|
+
"center": { "x": -4.9275, "y": 3 },
|
|
50
|
+
"width": 1.0450000000000008,
|
|
51
|
+
"height": 0.7600000000000007,
|
|
52
|
+
"pins": [
|
|
53
|
+
{
|
|
54
|
+
"pinId": "schematic_port_175",
|
|
55
|
+
"x": -5,
|
|
56
|
+
"y": 3.3800000000000003,
|
|
57
|
+
"_facingDirection": "y+"
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"pinId": "schematic_port_176",
|
|
61
|
+
"x": -5,
|
|
62
|
+
"y": 2.6199999999999997,
|
|
63
|
+
"_facingDirection": "y-"
|
|
64
|
+
}
|
|
65
|
+
],
|
|
66
|
+
"sectionId": "hf-clock"
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"chipId": "schematic_component_33",
|
|
70
|
+
"center": { "x": -2.9275, "y": 3 },
|
|
71
|
+
"width": 1.0450000000000004,
|
|
72
|
+
"height": 0.7600000000000007,
|
|
73
|
+
"pins": [
|
|
74
|
+
{
|
|
75
|
+
"pinId": "schematic_port_177",
|
|
76
|
+
"x": -3,
|
|
77
|
+
"y": 3.3800000000000003,
|
|
78
|
+
"_facingDirection": "y+"
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"pinId": "schematic_port_178",
|
|
82
|
+
"x": -3,
|
|
83
|
+
"y": 2.6199999999999997,
|
|
84
|
+
"_facingDirection": "y-"
|
|
85
|
+
}
|
|
86
|
+
],
|
|
87
|
+
"sectionId": "hf-clock"
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"chipId": "schematic_component_34",
|
|
91
|
+
"center": { "x": 3, "y": 5 },
|
|
92
|
+
"width": 3.1999999999999993,
|
|
93
|
+
"height": 0.5999999999999996,
|
|
94
|
+
"pins": [
|
|
95
|
+
{ "pinId": "schematic_port_44", "x": 4.6, "y": 5.1 },
|
|
96
|
+
{ "pinId": "schematic_port_45", "x": 4.6, "y": 4.9 }
|
|
97
|
+
],
|
|
98
|
+
"sectionId": "lf-clock"
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
"chipId": "schematic_component_35",
|
|
102
|
+
"center": { "x": 6, "y": 5 },
|
|
103
|
+
"width": 1.0999999999999996,
|
|
104
|
+
"height": 0.8499999999999996,
|
|
105
|
+
"pins": [
|
|
106
|
+
{
|
|
107
|
+
"pinId": "schematic_port_46",
|
|
108
|
+
"x": 5.45,
|
|
109
|
+
"y": 5,
|
|
110
|
+
"_facingDirection": "x-"
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
"pinId": "schematic_port_47",
|
|
114
|
+
"x": 6.55,
|
|
115
|
+
"y": 5,
|
|
116
|
+
"_facingDirection": "x+"
|
|
117
|
+
}
|
|
118
|
+
],
|
|
119
|
+
"sectionId": "lf-clock"
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
"chipId": "schematic_component_36",
|
|
123
|
+
"center": { "x": 5.0725, "y": 3 },
|
|
124
|
+
"width": 1.0450000000000008,
|
|
125
|
+
"height": 0.7600000000000007,
|
|
126
|
+
"pins": [
|
|
127
|
+
{
|
|
128
|
+
"pinId": "schematic_port_181",
|
|
129
|
+
"x": 5,
|
|
130
|
+
"y": 3.3800000000000003,
|
|
131
|
+
"_facingDirection": "y+"
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
"pinId": "schematic_port_182",
|
|
135
|
+
"x": 5,
|
|
136
|
+
"y": 2.6199999999999997,
|
|
137
|
+
"_facingDirection": "y-"
|
|
138
|
+
}
|
|
139
|
+
],
|
|
140
|
+
"sectionId": "lf-clock"
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
"chipId": "schematic_component_37",
|
|
144
|
+
"center": { "x": 7.0725, "y": 3 },
|
|
145
|
+
"width": 1.0450000000000008,
|
|
146
|
+
"height": 0.7600000000000007,
|
|
147
|
+
"pins": [
|
|
148
|
+
{
|
|
149
|
+
"pinId": "schematic_port_183",
|
|
150
|
+
"x": 7,
|
|
151
|
+
"y": 3.3800000000000003,
|
|
152
|
+
"_facingDirection": "y+"
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
"pinId": "schematic_port_184",
|
|
156
|
+
"x": 7,
|
|
157
|
+
"y": 2.6199999999999997,
|
|
158
|
+
"_facingDirection": "y-"
|
|
159
|
+
}
|
|
160
|
+
],
|
|
161
|
+
"sectionId": "lf-clock"
|
|
162
|
+
}
|
|
163
|
+
],
|
|
164
|
+
"directConnections": [
|
|
165
|
+
{
|
|
166
|
+
"netId": ".U1 > .XC1 to .X1 > .XTAL1",
|
|
167
|
+
"pinIds": ["schematic_port_38", "schematic_port_42"]
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
"netId": ".U1 > .XC2 to .X1 > .XTAL2",
|
|
171
|
+
"pinIds": ["schematic_port_39", "schematic_port_43"]
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
"netId": ".X1 > .XTAL1 to .C1 > .pin1",
|
|
175
|
+
"pinIds": ["schematic_port_42", "schematic_port_175"]
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
"netId": ".X1 > .XTAL2 to .C2 > .pin1",
|
|
179
|
+
"pinIds": ["schematic_port_43", "schematic_port_177"]
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
"netId": ".U1 > .XL1 to .X2 > .OSC1",
|
|
183
|
+
"pinIds": ["schematic_port_44", "schematic_port_46"]
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
"netId": ".U1 > .XL2 to .X2 > .OSC2",
|
|
187
|
+
"pinIds": ["schematic_port_45", "schematic_port_47"]
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
"netId": ".X2 > .OSC1 to .C11 > .pin1",
|
|
191
|
+
"pinIds": ["schematic_port_46", "schematic_port_181"]
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
"netId": ".X2 > .OSC2 to .C12 > .pin1",
|
|
195
|
+
"pinIds": ["schematic_port_47", "schematic_port_183"]
|
|
196
|
+
}
|
|
197
|
+
],
|
|
198
|
+
"netConnections": [
|
|
199
|
+
{
|
|
200
|
+
"netId": "GND",
|
|
201
|
+
"netLabelWidth": 0.42,
|
|
202
|
+
"netLabelHeight": 0.48,
|
|
203
|
+
"pinIds": [
|
|
204
|
+
"schematic_port_40",
|
|
205
|
+
"schematic_port_41",
|
|
206
|
+
"schematic_port_176",
|
|
207
|
+
"schematic_port_178",
|
|
208
|
+
"schematic_port_182",
|
|
209
|
+
"schematic_port_184"
|
|
210
|
+
]
|
|
211
|
+
}
|
|
212
|
+
],
|
|
213
|
+
"textBoxes": [
|
|
214
|
+
{
|
|
215
|
+
"chipId": "schematic_component_30",
|
|
216
|
+
"center": { "x": -7.539999999999999, "y": 5.43 },
|
|
217
|
+
"width": 1.3200000000000003,
|
|
218
|
+
"height": 0.17999999999999972,
|
|
219
|
+
"text": "U1 HF Clock"
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
"chipId": "schematic_component_34",
|
|
223
|
+
"center": { "x": 2.46, "y": 5.43 },
|
|
224
|
+
"width": 1.32,
|
|
225
|
+
"height": 0.17999999999999972,
|
|
226
|
+
"text": "U1 LF Clock"
|
|
227
|
+
}
|
|
228
|
+
],
|
|
229
|
+
"availableNetLabelOrientations": {
|
|
230
|
+
"GND": ["y-"]
|
|
231
|
+
},
|
|
232
|
+
"maxMspPairDistance": 2.4,
|
|
233
|
+
"_hideRatsNet": false
|
|
234
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { expect, test } from "bun:test"
|
|
2
|
+
import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver"
|
|
3
|
+
import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
|
|
4
|
+
import { getTraceCorners } from "lib/solvers/RailNetLabelCornerPlacementSolver/geometry"
|
|
5
|
+
import { SCHEMATIC_TRACE_MIN_VISUAL_CENTERLINE_CLEARANCE } from "lib/utils/doesPathCoincideWithTraces"
|
|
6
|
+
import "tests/fixtures/matcher"
|
|
7
|
+
import inputProblem from "./assets/repro-bq25895-cross-net-trace-overlap.input.json"
|
|
8
|
+
|
|
9
|
+
const EPSILON = 1e-6
|
|
10
|
+
|
|
11
|
+
const tracesHaveInsufficientParallelClearance = (
|
|
12
|
+
firstTrace: SolvedTracePath,
|
|
13
|
+
secondTrace: SolvedTracePath,
|
|
14
|
+
) => {
|
|
15
|
+
for (
|
|
16
|
+
let firstPointIndex = 0;
|
|
17
|
+
firstPointIndex < firstTrace.tracePath.length - 1;
|
|
18
|
+
firstPointIndex++
|
|
19
|
+
) {
|
|
20
|
+
const firstStart = firstTrace.tracePath[firstPointIndex]!
|
|
21
|
+
const firstEnd = firstTrace.tracePath[firstPointIndex + 1]!
|
|
22
|
+
const firstIsVertical = Math.abs(firstStart.x - firstEnd.x) < EPSILON
|
|
23
|
+
if (!firstIsVertical) continue
|
|
24
|
+
|
|
25
|
+
for (
|
|
26
|
+
let secondPointIndex = 0;
|
|
27
|
+
secondPointIndex < secondTrace.tracePath.length - 1;
|
|
28
|
+
secondPointIndex++
|
|
29
|
+
) {
|
|
30
|
+
const secondStart = secondTrace.tracePath[secondPointIndex]!
|
|
31
|
+
const secondEnd = secondTrace.tracePath[secondPointIndex + 1]!
|
|
32
|
+
const secondIsVertical = Math.abs(secondStart.x - secondEnd.x) < EPSILON
|
|
33
|
+
if (!secondIsVertical) continue
|
|
34
|
+
|
|
35
|
+
const xSeparation = Math.abs(firstStart.x - secondStart.x)
|
|
36
|
+
const verticalOverlap =
|
|
37
|
+
Math.min(
|
|
38
|
+
Math.max(firstStart.y, firstEnd.y),
|
|
39
|
+
Math.max(secondStart.y, secondEnd.y),
|
|
40
|
+
) -
|
|
41
|
+
Math.max(
|
|
42
|
+
Math.min(firstStart.y, firstEnd.y),
|
|
43
|
+
Math.min(secondStart.y, secondEnd.y),
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
if (
|
|
47
|
+
xSeparation + EPSILON <
|
|
48
|
+
SCHEMATIC_TRACE_MIN_VISUAL_CENTERLINE_CLEARANCE &&
|
|
49
|
+
verticalOverlap > EPSILON
|
|
50
|
+
) {
|
|
51
|
+
return true
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return false
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
test("BQ25895 CE and GND traces keep visible parallel clearance", () => {
|
|
60
|
+
const solver = new SchematicTracePipelineSolver(inputProblem as any)
|
|
61
|
+
solver.solve()
|
|
62
|
+
|
|
63
|
+
const traces = solver.netLabelTraceCollisionSolver!.getOutput().traces
|
|
64
|
+
const ceTraces = traces.filter(
|
|
65
|
+
(trace) => trace.userNetId === "J3.pin6 to U1.CE",
|
|
66
|
+
)
|
|
67
|
+
const groundTraces = traces.filter((trace) => trace.userNetId === "GND")
|
|
68
|
+
const hasInsufficientCeAndGroundClearance = ceTraces.some((ceTrace) =>
|
|
69
|
+
groundTraces.some((groundTrace) =>
|
|
70
|
+
tracesHaveInsufficientParallelClearance(ceTrace, groundTrace),
|
|
71
|
+
),
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
expect(hasInsufficientCeAndGroundClearance).toBe(false)
|
|
75
|
+
|
|
76
|
+
const output = solver.netLabelTraceCollisionSolver!.getOutput()
|
|
77
|
+
const groundLabel = output.netLabelPlacements.find(
|
|
78
|
+
(label) => label.netId === "GND",
|
|
79
|
+
)!
|
|
80
|
+
const groundLabelTrace = output.traces.find((trace) =>
|
|
81
|
+
groundLabel.mspConnectionPairIds.includes(trace.mspPairId),
|
|
82
|
+
)!
|
|
83
|
+
|
|
84
|
+
expect(getTraceCorners(groundLabelTrace.tracePath)).toContainEqual(
|
|
85
|
+
groundLabel.anchorPoint,
|
|
86
|
+
)
|
|
87
|
+
expect(solver).toMatchSolverSnapshot(import.meta.path)
|
|
88
|
+
})
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { expect, test } from "bun:test"
|
|
2
|
+
import { getTraceCorners } from "lib/solvers/RailNetLabelCornerPlacementSolver/geometry"
|
|
2
3
|
import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver"
|
|
3
4
|
import inputProblem from "./assets/repro-missing-trace-netlabel.input.json"
|
|
4
5
|
import "tests/fixtures/matcher"
|
|
@@ -10,5 +11,16 @@ test("repro148 missing trace becomes net labels", () => {
|
|
|
10
11
|
|
|
11
12
|
solver.solve()
|
|
12
13
|
|
|
14
|
+
const output = solver.sameNetJunctionAlignmentSolver!.getOutput()
|
|
15
|
+
const powerLabel = output.netLabelPlacements.find(
|
|
16
|
+
(label) => label.netId === "V3V3",
|
|
17
|
+
)!
|
|
18
|
+
const powerTrace = output.traces.find((trace) =>
|
|
19
|
+
powerLabel.mspConnectionPairIds.includes(trace.mspPairId),
|
|
20
|
+
)!
|
|
21
|
+
|
|
22
|
+
expect(getTraceCorners(powerTrace.tracePath)).toContainEqual(
|
|
23
|
+
powerLabel.anchorPoint,
|
|
24
|
+
)
|
|
13
25
|
expect(solver).toMatchSolverSnapshot(import.meta.path)
|
|
14
26
|
})
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import { expect, test } from "bun:test"
|
|
2
|
+
import { traceCrossesBoundsInterior } from "lib/solvers/AvailableNetOrientationSolver/geometry"
|
|
3
|
+
import {
|
|
4
|
+
getTraceCorners,
|
|
5
|
+
rectsOverlap,
|
|
6
|
+
} from "lib/solvers/RailNetLabelCornerPlacementSolver/geometry"
|
|
7
|
+
import { getRectBounds } from "lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/geometry"
|
|
2
8
|
import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver"
|
|
3
9
|
import inputProblem from "./assets/repro-netlabel-overlap-trace.input.json"
|
|
4
10
|
import "tests/fixtures/matcher"
|
|
@@ -10,5 +16,37 @@ test("repro147 netlabel overlaps trace", () => {
|
|
|
10
16
|
|
|
11
17
|
solver.solve()
|
|
12
18
|
|
|
19
|
+
const output = solver.sameNetJunctionAlignmentSolver!.getOutput()
|
|
20
|
+
const groundLabel = output.netLabelPlacements.find(
|
|
21
|
+
(label) => label.netId === "GND",
|
|
22
|
+
)!
|
|
23
|
+
const groundTrace = output.traces.find((trace) =>
|
|
24
|
+
groundLabel.mspConnectionPairIds.includes(trace.mspPairId),
|
|
25
|
+
)!
|
|
26
|
+
const groundLabelBounds = getRectBounds(
|
|
27
|
+
groundLabel.center,
|
|
28
|
+
groundLabel.width,
|
|
29
|
+
groundLabel.height,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
expect(getTraceCorners(groundTrace.tracePath)).toContainEqual(
|
|
33
|
+
groundLabel.anchorPoint,
|
|
34
|
+
)
|
|
35
|
+
expect(
|
|
36
|
+
inputProblem.chips.some((chip) =>
|
|
37
|
+
rectsOverlap(
|
|
38
|
+
groundLabelBounds,
|
|
39
|
+
getRectBounds(chip.center, chip.width, chip.height),
|
|
40
|
+
),
|
|
41
|
+
),
|
|
42
|
+
).toBe(false)
|
|
43
|
+
expect(
|
|
44
|
+
traceCrossesBoundsInterior(
|
|
45
|
+
groundLabelBounds,
|
|
46
|
+
Object.fromEntries(
|
|
47
|
+
output.traces.map((trace) => [trace.mspPairId, trace]),
|
|
48
|
+
),
|
|
49
|
+
),
|
|
50
|
+
).toBe(false)
|
|
13
51
|
expect(solver).toMatchSolverSnapshot(import.meta.path)
|
|
14
52
|
})
|
|
@@ -0,0 +1,28 @@
|
|
|
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/repro-nrf52810-clock-routing.input.json"
|
|
5
|
+
|
|
6
|
+
// Extracted from the nRF52810 clock-source schematic shown in the reproduction:
|
|
7
|
+
// a four-pin 32 MHz crystal and a two-pin 32.768 kHz crystal, each connected
|
|
8
|
+
// to an MCU schematic box, two load capacitors, and section-local GND labels.
|
|
9
|
+
test("repro nRF52810 HF and LF crystal trace/net-label routing", () => {
|
|
10
|
+
const solver = new SchematicTracePipelineSolver(inputProblem as any)
|
|
11
|
+
|
|
12
|
+
solver.solve()
|
|
13
|
+
|
|
14
|
+
expect(solver.mspConnectionPairSolver?.mspConnectionPairs).toHaveLength(11)
|
|
15
|
+
expect(
|
|
16
|
+
solver.schematicTraceLinesSolver?.failedConnectionPairs.map(
|
|
17
|
+
(pair) => pair.mspPairId,
|
|
18
|
+
),
|
|
19
|
+
).toEqual([
|
|
20
|
+
"schematic_port_39-schematic_port_43", // U1.XC2 -> X1.XTAL2
|
|
21
|
+
"schematic_port_45-schematic_port_47", // U1.XL2 -> X2.OSC2
|
|
22
|
+
"schematic_port_41-schematic_port_40", // X1.GND2 -> X1.GND1
|
|
23
|
+
])
|
|
24
|
+
expect(
|
|
25
|
+
solver.sameNetJunctionAlignmentSolver?.getOutput().netLabelPlacements,
|
|
26
|
+
).toHaveLength(10)
|
|
27
|
+
expect(solver).toMatchSolverSnapshot(import.meta.path)
|
|
28
|
+
})
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { expect, test } from "bun:test"
|
|
2
|
+
import {
|
|
3
|
+
SCHEMATIC_TRACE_MIN_VISUAL_CENTERLINE_CLEARANCE,
|
|
4
|
+
SCHEMATIC_TRACE_STROKE_WIDTH,
|
|
5
|
+
} from "lib/utils/doesPathCoincideWithTraces"
|
|
6
|
+
import {
|
|
7
|
+
align,
|
|
8
|
+
createTrace,
|
|
9
|
+
getVerticalRailTraces,
|
|
10
|
+
} from "./fixtures/alignSameNetRails"
|
|
11
|
+
|
|
12
|
+
test("aligns same-net rails at a safe coordinate beside different-net traces", () => {
|
|
13
|
+
const rails = getVerticalRailTraces()
|
|
14
|
+
const foreignLowerTrace = createTrace(
|
|
15
|
+
"foreign-lower",
|
|
16
|
+
[
|
|
17
|
+
{ x: -2.02, y: -2 },
|
|
18
|
+
{ x: -2.02, y: -0.2 },
|
|
19
|
+
],
|
|
20
|
+
[
|
|
21
|
+
{ pinId: "X1.1", chipId: "X1", x: -2.02, y: -2 },
|
|
22
|
+
{ pinId: "X2.1", chipId: "X2", x: -2.02, y: -0.2 },
|
|
23
|
+
],
|
|
24
|
+
"foreign-lower-net",
|
|
25
|
+
)
|
|
26
|
+
const foreignUpperTrace = createTrace(
|
|
27
|
+
"foreign-upper",
|
|
28
|
+
[
|
|
29
|
+
{ x: -3.02, y: 0.2 },
|
|
30
|
+
{ x: -3.02, y: 2 },
|
|
31
|
+
],
|
|
32
|
+
[
|
|
33
|
+
{ pinId: "X3.1", chipId: "X3", x: -3.02, y: 0.2 },
|
|
34
|
+
{ pinId: "X4.1", chipId: "X4", x: -3.02, y: 2 },
|
|
35
|
+
],
|
|
36
|
+
"foreign-upper-net",
|
|
37
|
+
)
|
|
38
|
+
const traces = [...rails, foreignLowerTrace, foreignUpperTrace]
|
|
39
|
+
|
|
40
|
+
const result = align(traces, {
|
|
41
|
+
eligibleTraceIds: new Set(rails.map((trace) => trace.mspPairId)),
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
expect(result.alignedRailGroupCount).toBe(1)
|
|
45
|
+
|
|
46
|
+
const alignedUpper = result.traces.find(
|
|
47
|
+
(trace) => trace.mspPairId === "upper",
|
|
48
|
+
)!
|
|
49
|
+
const alignedLower = result.traces.find(
|
|
50
|
+
(trace) => trace.mspPairId === "lower",
|
|
51
|
+
)!
|
|
52
|
+
const upperRailX = alignedUpper.tracePath[1]!.x
|
|
53
|
+
const lowerRailX = alignedLower.tracePath[1]!.x
|
|
54
|
+
|
|
55
|
+
expect(upperRailX).toBe(lowerRailX)
|
|
56
|
+
expect(
|
|
57
|
+
Math.abs(upperRailX - foreignLowerTrace.tracePath[0]!.x),
|
|
58
|
+
).toBeGreaterThan(SCHEMATIC_TRACE_STROKE_WIDTH)
|
|
59
|
+
expect(
|
|
60
|
+
Math.abs(upperRailX - foreignUpperTrace.tracePath[0]!.x),
|
|
61
|
+
).toBeGreaterThan(SCHEMATIC_TRACE_STROKE_WIDTH)
|
|
62
|
+
expect(
|
|
63
|
+
Math.min(
|
|
64
|
+
Math.abs(upperRailX - foreignLowerTrace.tracePath[0]!.x),
|
|
65
|
+
Math.abs(upperRailX - foreignUpperTrace.tracePath[0]!.x),
|
|
66
|
+
),
|
|
67
|
+
).toBeCloseTo(SCHEMATIC_TRACE_MIN_VISUAL_CENTERLINE_CLEARANCE, 6)
|
|
68
|
+
})
|