@tscircuit/schematic-trace-solver 0.0.61 → 0.0.62
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 +65 -5
- package/dist/index.js +344 -0
- package/lib/solvers/NetLabelNetLabelCollisionSolver/NetLabelNetLabelCollisionSolver.ts +441 -0
- package/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts +15 -0
- package/package.json +1 -1
- package/site/examples/example39.page.tsx +4 -0
- package/site/examples/example40.page.tsx +4 -0
- package/site/examples/example41.page.tsx +4 -0
- package/tests/assets/example39.json +71 -0
- package/tests/assets/example40.json +157 -0
- package/tests/assets/example41.json +81 -0
- package/tests/examples/__snapshots__/example39.snap.svg +107 -0
- package/tests/examples/__snapshots__/example40.snap.svg +231 -0
- package/tests/examples/__snapshots__/example41.snap.svg +119 -0
- package/tests/examples/example39.test.ts +12 -0
- package/tests/examples/example40.test.ts +12 -0
- package/tests/examples/example41.test.ts +12 -0
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
import type { GraphicsObject } from "graphics-debug"
|
|
2
|
+
import { BaseSolver } from "lib/solvers/BaseSolver/BaseSolver"
|
|
3
|
+
import type { NetLabelPlacement } from "lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver"
|
|
4
|
+
import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
|
|
5
|
+
import type { MspConnectionPairId } from "lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver"
|
|
6
|
+
import type { InputProblem } from "lib/types/InputProblem"
|
|
7
|
+
import type { FacingDirection } from "lib/utils/dir"
|
|
8
|
+
import {
|
|
9
|
+
getDimsForOrientation,
|
|
10
|
+
getCenterFromAnchor,
|
|
11
|
+
getRectBounds,
|
|
12
|
+
} from "lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/geometry"
|
|
13
|
+
import { rectIntersectsAnyTrace } from "lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/collisions"
|
|
14
|
+
import { ChipObstacleSpatialIndex } from "lib/data-structures/ChipObstacleSpatialIndex"
|
|
15
|
+
import { visualizeInputProblem } from "lib/solvers/SchematicTracePipelineSolver/visualizeInputProblem"
|
|
16
|
+
import { getColorFromString } from "lib/utils/getColorFromString"
|
|
17
|
+
|
|
18
|
+
type CandidateStatus =
|
|
19
|
+
| "ok"
|
|
20
|
+
| "chip-collision"
|
|
21
|
+
| "trace-collision"
|
|
22
|
+
| "label-collision"
|
|
23
|
+
|
|
24
|
+
const ANCHOR_TRACE_CLEARANCE = 1e-4
|
|
25
|
+
const SEGMENT_PARALLEL_EPS = 1e-6
|
|
26
|
+
const CANDIDATE_STEP = 0.1
|
|
27
|
+
|
|
28
|
+
const OUTWARD_DIR: Record<FacingDirection, { x: number; y: number }> = {
|
|
29
|
+
"x+": { x: 1, y: 0 },
|
|
30
|
+
"x-": { x: -1, y: 0 },
|
|
31
|
+
"y+": { x: 0, y: 1 },
|
|
32
|
+
"y-": { x: 0, y: -1 },
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const CANDIDATE_STATUS_COLOR: Record<CandidateStatus, string> = {
|
|
36
|
+
ok: "green",
|
|
37
|
+
"label-collision": "orange",
|
|
38
|
+
"trace-collision": "darkorange",
|
|
39
|
+
"chip-collision": "red",
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const CANDIDATE_STATUS_FILL: Record<CandidateStatus, string> = {
|
|
43
|
+
ok: "rgba(0, 200, 0, 0.25)",
|
|
44
|
+
"label-collision": "rgba(255, 160, 0, 0.2)",
|
|
45
|
+
"trace-collision": "rgba(200, 80, 0, 0.2)",
|
|
46
|
+
"chip-collision": "rgba(220, 0, 0, 0.15)",
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
type Candidate = {
|
|
50
|
+
orientation: FacingDirection
|
|
51
|
+
anchor: { x: number; y: number }
|
|
52
|
+
center: { x: number; y: number }
|
|
53
|
+
width: number
|
|
54
|
+
height: number
|
|
55
|
+
bounds: { minX: number; minY: number; maxX: number; maxY: number }
|
|
56
|
+
hostPairId?: MspConnectionPairId
|
|
57
|
+
hostSegIndex?: number
|
|
58
|
+
status: CandidateStatus | null
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function boundsOverlap(
|
|
62
|
+
a: { minX: number; minY: number; maxX: number; maxY: number },
|
|
63
|
+
b: { minX: number; minY: number; maxX: number; maxY: number },
|
|
64
|
+
): boolean {
|
|
65
|
+
return (
|
|
66
|
+
a.minX < b.maxX - 1e-9 &&
|
|
67
|
+
a.maxX > b.minX + 1e-9 &&
|
|
68
|
+
a.minY < b.maxY - 1e-9 &&
|
|
69
|
+
a.maxY > b.minY + 1e-9
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function sampleAnchorsAlongSegment(
|
|
74
|
+
a: { x: number; y: number },
|
|
75
|
+
b: { x: number; y: number },
|
|
76
|
+
): Array<{ x: number; y: number }> {
|
|
77
|
+
const dx = b.x - a.x
|
|
78
|
+
const dy = b.y - a.y
|
|
79
|
+
const len = Math.sqrt(dx * dx + dy * dy)
|
|
80
|
+
const steps = Math.max(1, Math.round(len / CANDIDATE_STEP))
|
|
81
|
+
const anchors: Array<{ x: number; y: number }> = []
|
|
82
|
+
for (let k = 0; k <= steps; k++) {
|
|
83
|
+
const t = k / steps
|
|
84
|
+
anchors.push({ x: a.x + t * dx, y: a.y + t * dy })
|
|
85
|
+
}
|
|
86
|
+
return anchors
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export interface NetLabelNetLabelCollisionSolverParams {
|
|
90
|
+
inputProblem: InputProblem
|
|
91
|
+
traces: SolvedTracePath[]
|
|
92
|
+
netLabelPlacements: NetLabelPlacement[]
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export class NetLabelNetLabelCollisionSolver extends BaseSolver {
|
|
96
|
+
inputProblem: InputProblem
|
|
97
|
+
traces: SolvedTracePath[]
|
|
98
|
+
netLabelPlacements: NetLabelPlacement[]
|
|
99
|
+
|
|
100
|
+
outputNetLabelPlacements: NetLabelPlacement[]
|
|
101
|
+
|
|
102
|
+
currentCollision: [NetLabelPlacement, NetLabelPlacement] | null = null
|
|
103
|
+
currentLabelToMove: NetLabelPlacement | null = null
|
|
104
|
+
candidateResults: Candidate[] = []
|
|
105
|
+
|
|
106
|
+
private chipIndex: ChipObstacleSpatialIndex
|
|
107
|
+
private traceMap: Record<MspConnectionPairId, SolvedTracePath>
|
|
108
|
+
private skippedCollisionKeys = new Set<string>()
|
|
109
|
+
|
|
110
|
+
private labelsToTry: NetLabelPlacement[] = []
|
|
111
|
+
private candidateQueue: Candidate[] = []
|
|
112
|
+
private candidateIndex = 0
|
|
113
|
+
|
|
114
|
+
constructor(params: NetLabelNetLabelCollisionSolverParams) {
|
|
115
|
+
super()
|
|
116
|
+
this.inputProblem = params.inputProblem
|
|
117
|
+
this.traces = params.traces
|
|
118
|
+
this.netLabelPlacements = params.netLabelPlacements
|
|
119
|
+
this.outputNetLabelPlacements = [...params.netLabelPlacements]
|
|
120
|
+
this.chipIndex =
|
|
121
|
+
params.inputProblem._chipObstacleSpatialIndex ??
|
|
122
|
+
new ChipObstacleSpatialIndex(params.inputProblem.chips)
|
|
123
|
+
this.traceMap = Object.fromEntries(
|
|
124
|
+
params.traces.map((t) => [t.mspPairId, t]),
|
|
125
|
+
) as Record<MspConnectionPairId, SolvedTracePath>
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
override getConstructorParams(): ConstructorParameters<
|
|
129
|
+
typeof NetLabelNetLabelCollisionSolver
|
|
130
|
+
>[0] {
|
|
131
|
+
return {
|
|
132
|
+
inputProblem: this.inputProblem,
|
|
133
|
+
traces: this.traces,
|
|
134
|
+
netLabelPlacements: this.netLabelPlacements,
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
getOutput() {
|
|
139
|
+
return { netLabelPlacements: this.outputNetLabelPlacements }
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
private labelBounds(label: NetLabelPlacement) {
|
|
143
|
+
return getRectBounds(label.center, label.width, label.height)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
private collisionKey(a: NetLabelPlacement, b: NetLabelPlacement) {
|
|
147
|
+
return [a.globalConnNetId, b.globalConnNetId].sort().join("::")
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
private findNextCollidingPair():
|
|
151
|
+
| [NetLabelPlacement, NetLabelPlacement]
|
|
152
|
+
| null {
|
|
153
|
+
const labels = this.outputNetLabelPlacements
|
|
154
|
+
for (let i = 0; i < labels.length; i++) {
|
|
155
|
+
for (let j = i + 1; j < labels.length; j++) {
|
|
156
|
+
const a = labels[i]!
|
|
157
|
+
const b = labels[j]!
|
|
158
|
+
if (a.globalConnNetId === b.globalConnNetId) continue
|
|
159
|
+
if (this.skippedCollisionKeys.has(this.collisionKey(a, b))) continue
|
|
160
|
+
if (boundsOverlap(this.labelBounds(a), this.labelBounds(b)))
|
|
161
|
+
return [a, b]
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return null
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
private netLabelWidthOf(label: NetLabelPlacement): number | undefined {
|
|
168
|
+
if (label.orientation === "x+" || label.orientation === "x-")
|
|
169
|
+
return label.width
|
|
170
|
+
return label.height
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
private buildCandidatesForLabel(label: NetLabelPlacement): Candidate[] {
|
|
174
|
+
const netLabelWidth = this.netLabelWidthOf(label)
|
|
175
|
+
const candidates: Candidate[] = []
|
|
176
|
+
|
|
177
|
+
const buildCandidate = (
|
|
178
|
+
orientation: FacingDirection,
|
|
179
|
+
anchor: { x: number; y: number },
|
|
180
|
+
hostPairId?: MspConnectionPairId,
|
|
181
|
+
hostSegIndex?: number,
|
|
182
|
+
): Candidate => {
|
|
183
|
+
const { width, height } = getDimsForOrientation({
|
|
184
|
+
orientation,
|
|
185
|
+
netLabelWidth,
|
|
186
|
+
})
|
|
187
|
+
const baseCenter = getCenterFromAnchor(anchor, orientation, width, height)
|
|
188
|
+
const outwardDir = OUTWARD_DIR[orientation]
|
|
189
|
+
const center = {
|
|
190
|
+
x: baseCenter.x + outwardDir.x * ANCHOR_TRACE_CLEARANCE,
|
|
191
|
+
y: baseCenter.y + outwardDir.y * ANCHOR_TRACE_CLEARANCE,
|
|
192
|
+
}
|
|
193
|
+
return {
|
|
194
|
+
orientation,
|
|
195
|
+
anchor,
|
|
196
|
+
center,
|
|
197
|
+
width,
|
|
198
|
+
height,
|
|
199
|
+
bounds: getRectBounds(center, width, height),
|
|
200
|
+
hostPairId,
|
|
201
|
+
hostSegIndex,
|
|
202
|
+
status: null,
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const isPortOnly = label.mspConnectionPairIds.length === 0
|
|
207
|
+
|
|
208
|
+
if (isPortOnly) {
|
|
209
|
+
const allOrientations: FacingDirection[] = ["x+", "x-", "y+", "y-"]
|
|
210
|
+
const orderedOrientations = [
|
|
211
|
+
label.orientation,
|
|
212
|
+
...allOrientations.filter((o) => o !== label.orientation),
|
|
213
|
+
]
|
|
214
|
+
for (const orientation of orderedOrientations) {
|
|
215
|
+
candidates.push(buildCandidate(orientation, label.anchorPoint))
|
|
216
|
+
}
|
|
217
|
+
} else {
|
|
218
|
+
for (const mspPairId of label.mspConnectionPairIds) {
|
|
219
|
+
const trace = this.traceMap[mspPairId]
|
|
220
|
+
if (!trace) continue
|
|
221
|
+
const pts = trace.tracePath
|
|
222
|
+
for (let si = 0; si < pts.length - 1; si++) {
|
|
223
|
+
const segStart = pts[si]!
|
|
224
|
+
const segEnd = pts[si + 1]!
|
|
225
|
+
const isHorizontal =
|
|
226
|
+
Math.abs(segStart.y - segEnd.y) < SEGMENT_PARALLEL_EPS
|
|
227
|
+
const isVertical =
|
|
228
|
+
Math.abs(segStart.x - segEnd.x) < SEGMENT_PARALLEL_EPS
|
|
229
|
+
if (!isHorizontal && !isVertical) continue
|
|
230
|
+
let perpendicularOrientations: FacingDirection[]
|
|
231
|
+
if (isHorizontal) {
|
|
232
|
+
perpendicularOrientations = ["y+", "y-"]
|
|
233
|
+
} else {
|
|
234
|
+
perpendicularOrientations = ["x+", "x-"]
|
|
235
|
+
}
|
|
236
|
+
for (const anchor of sampleAnchorsAlongSegment(segStart, segEnd)) {
|
|
237
|
+
for (const orientation of perpendicularOrientations) {
|
|
238
|
+
candidates.push(
|
|
239
|
+
buildCandidate(
|
|
240
|
+
orientation,
|
|
241
|
+
anchor,
|
|
242
|
+
mspPairId as MspConnectionPairId,
|
|
243
|
+
si,
|
|
244
|
+
),
|
|
245
|
+
)
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return candidates
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
private checkCandidate(
|
|
256
|
+
candidate: Candidate,
|
|
257
|
+
movingLabelNetId: string,
|
|
258
|
+
obstacleLabels: NetLabelPlacement[],
|
|
259
|
+
): CandidateStatus {
|
|
260
|
+
const { bounds, hostPairId, hostSegIndex } = candidate
|
|
261
|
+
|
|
262
|
+
if (this.chipIndex.getChipsInBounds(bounds).length > 0)
|
|
263
|
+
return "chip-collision"
|
|
264
|
+
|
|
265
|
+
if (
|
|
266
|
+
rectIntersectsAnyTrace(bounds, this.traceMap, hostPairId, hostSegIndex)
|
|
267
|
+
.hasIntersection
|
|
268
|
+
) {
|
|
269
|
+
return "trace-collision"
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
for (const obstacle of obstacleLabels) {
|
|
273
|
+
if (obstacle.globalConnNetId === movingLabelNetId) continue
|
|
274
|
+
if (boundsOverlap(bounds, this.labelBounds(obstacle)))
|
|
275
|
+
return "label-collision"
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return "ok"
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
private beginSearchForLabel(label: NetLabelPlacement) {
|
|
282
|
+
this.currentLabelToMove = label
|
|
283
|
+
this.candidateQueue = this.buildCandidatesForLabel(label)
|
|
284
|
+
this.candidateIndex = 0
|
|
285
|
+
this.candidateResults = []
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
private clearActiveSearch() {
|
|
289
|
+
this.currentCollision = null
|
|
290
|
+
this.currentLabelToMove = null
|
|
291
|
+
this.labelsToTry = []
|
|
292
|
+
this.candidateQueue = []
|
|
293
|
+
this.candidateIndex = 0
|
|
294
|
+
this.candidateResults = []
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
override _step() {
|
|
298
|
+
if (!this.currentCollision) {
|
|
299
|
+
const pair = this.findNextCollidingPair()
|
|
300
|
+
if (!pair) {
|
|
301
|
+
this.solved = true
|
|
302
|
+
return
|
|
303
|
+
}
|
|
304
|
+
this.currentCollision = pair
|
|
305
|
+
this.labelsToTry = [pair[1], pair[0]]
|
|
306
|
+
this.beginSearchForLabel(this.labelsToTry.shift()!)
|
|
307
|
+
return
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
if (this.candidateIndex >= this.candidateQueue.length) {
|
|
311
|
+
if (this.labelsToTry.length > 0) {
|
|
312
|
+
this.beginSearchForLabel(this.labelsToTry.shift()!)
|
|
313
|
+
} else {
|
|
314
|
+
this.skippedCollisionKeys.add(
|
|
315
|
+
this.collisionKey(this.currentCollision[0], this.currentCollision[1]),
|
|
316
|
+
)
|
|
317
|
+
this.clearActiveSearch()
|
|
318
|
+
}
|
|
319
|
+
return
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const candidate = this.candidateQueue[this.candidateIndex++]!
|
|
323
|
+
const [labelA, labelB] = this.currentCollision
|
|
324
|
+
let fixedLabel: NetLabelPlacement
|
|
325
|
+
if (this.currentLabelToMove === labelB) {
|
|
326
|
+
fixedLabel = labelA
|
|
327
|
+
} else {
|
|
328
|
+
fixedLabel = labelB
|
|
329
|
+
}
|
|
330
|
+
const obstacleLabels = [
|
|
331
|
+
...this.outputNetLabelPlacements.filter(
|
|
332
|
+
(l) => l !== labelA && l !== labelB,
|
|
333
|
+
),
|
|
334
|
+
fixedLabel,
|
|
335
|
+
]
|
|
336
|
+
|
|
337
|
+
const status = this.checkCandidate(
|
|
338
|
+
candidate,
|
|
339
|
+
this.currentLabelToMove!.globalConnNetId,
|
|
340
|
+
obstacleLabels,
|
|
341
|
+
)
|
|
342
|
+
candidate.status = status
|
|
343
|
+
this.candidateResults.push({ ...candidate })
|
|
344
|
+
|
|
345
|
+
if (status === "ok") {
|
|
346
|
+
const idx = this.outputNetLabelPlacements.indexOf(
|
|
347
|
+
this.currentLabelToMove!,
|
|
348
|
+
)
|
|
349
|
+
if (idx !== -1) {
|
|
350
|
+
this.outputNetLabelPlacements[idx] = {
|
|
351
|
+
...this.currentLabelToMove!,
|
|
352
|
+
orientation: candidate.orientation,
|
|
353
|
+
anchorPoint: candidate.anchor,
|
|
354
|
+
width: candidate.width,
|
|
355
|
+
height: candidate.height,
|
|
356
|
+
center: candidate.center,
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
this.clearActiveSearch()
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
override visualize(): GraphicsObject {
|
|
364
|
+
const graphics = visualizeInputProblem(this.inputProblem)
|
|
365
|
+
if (!graphics.lines) graphics.lines = []
|
|
366
|
+
if (!graphics.rects) graphics.rects = []
|
|
367
|
+
if (!graphics.points) graphics.points = []
|
|
368
|
+
|
|
369
|
+
for (const trace of this.traces) {
|
|
370
|
+
graphics.lines.push({
|
|
371
|
+
points: trace.tracePath,
|
|
372
|
+
strokeColor: "purple",
|
|
373
|
+
} as any)
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
for (const label of this.outputNetLabelPlacements) {
|
|
377
|
+
const isInActiveCollision =
|
|
378
|
+
this.currentCollision != null &&
|
|
379
|
+
(label === this.currentCollision[0] ||
|
|
380
|
+
label === this.currentCollision[1])
|
|
381
|
+
|
|
382
|
+
let labelFill: string
|
|
383
|
+
let labelStroke: string
|
|
384
|
+
let labelText: string
|
|
385
|
+
let pointColor: string
|
|
386
|
+
if (isInActiveCollision) {
|
|
387
|
+
labelFill = "rgba(255, 0, 0, 0.2)"
|
|
388
|
+
labelStroke = "red"
|
|
389
|
+
labelText = `netId: ${label.netId}\nglobalConnNetId: ${label.globalConnNetId}\n⚠ COLLIDING`
|
|
390
|
+
pointColor = "red"
|
|
391
|
+
} else {
|
|
392
|
+
labelFill = getColorFromString(label.globalConnNetId, 0.35)
|
|
393
|
+
labelStroke = getColorFromString(label.globalConnNetId, 0.9)
|
|
394
|
+
labelText = `netId: ${label.netId}\nglobalConnNetId: ${label.globalConnNetId}`
|
|
395
|
+
pointColor = getColorFromString(label.globalConnNetId, 0.9)
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
graphics.rects.push({
|
|
399
|
+
center: label.center,
|
|
400
|
+
width: label.width,
|
|
401
|
+
height: label.height,
|
|
402
|
+
fill: labelFill,
|
|
403
|
+
strokeColor: labelStroke,
|
|
404
|
+
label: labelText,
|
|
405
|
+
} as any)
|
|
406
|
+
graphics.points.push({
|
|
407
|
+
x: label.anchorPoint.x,
|
|
408
|
+
y: label.anchorPoint.y,
|
|
409
|
+
color: pointColor,
|
|
410
|
+
label: `anchorPoint\norientation: ${label.orientation}`,
|
|
411
|
+
} as any)
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
const movingNetId = this.currentLabelToMove
|
|
415
|
+
? this.currentLabelToMove.netId
|
|
416
|
+
: "?"
|
|
417
|
+
for (const c of this.candidateResults) {
|
|
418
|
+
const statusColor = CANDIDATE_STATUS_COLOR[c.status!]
|
|
419
|
+
const statusFill = CANDIDATE_STATUS_FILL[c.status!]
|
|
420
|
+
let strokeDash: string | undefined
|
|
421
|
+
if (c.status !== "ok") strokeDash = "4 2"
|
|
422
|
+
graphics.rects.push({
|
|
423
|
+
center: c.center,
|
|
424
|
+
width: c.width,
|
|
425
|
+
height: c.height,
|
|
426
|
+
fill: statusFill,
|
|
427
|
+
strokeColor: statusColor,
|
|
428
|
+
strokeDash,
|
|
429
|
+
label: `candidate: ${c.status}\norientation: ${c.orientation}\nmoving: ${movingNetId}`,
|
|
430
|
+
} as any)
|
|
431
|
+
graphics.points.push({
|
|
432
|
+
x: c.anchor.x,
|
|
433
|
+
y: c.anchor.y,
|
|
434
|
+
color: statusColor,
|
|
435
|
+
label: `candidate anchor\n${c.status}`,
|
|
436
|
+
} as any)
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
return graphics
|
|
440
|
+
}
|
|
441
|
+
}
|
|
@@ -26,6 +26,7 @@ import { AvailableNetOrientationSolver } from "../AvailableNetOrientationSolver/
|
|
|
26
26
|
import { VccNetLabelCornerPlacementSolver } from "../VccNetLabelCornerPlacementSolver/VccNetLabelCornerPlacementSolver"
|
|
27
27
|
import { TraceAnchoredNetLabelOverlapSolver } from "../TraceAnchoredNetLabelOverlapSolver/TraceAnchoredNetLabelOverlapSolver"
|
|
28
28
|
import { NetLabelTraceCollisionSolver } from "../NetLabelTraceCollisionSolver/NetLabelTraceCollisionSolver"
|
|
29
|
+
import { NetLabelNetLabelCollisionSolver } from "../NetLabelNetLabelCollisionSolver/NetLabelNetLabelCollisionSolver"
|
|
29
30
|
|
|
30
31
|
type PipelineStep<T extends new (...args: any[]) => BaseSolver> = {
|
|
31
32
|
solverName: string
|
|
@@ -80,6 +81,7 @@ export class SchematicTracePipelineSolver extends BaseSolver {
|
|
|
80
81
|
vccNetLabelCornerPlacementSolver?: VccNetLabelCornerPlacementSolver
|
|
81
82
|
traceAnchoredNetLabelOverlapSolver?: TraceAnchoredNetLabelOverlapSolver
|
|
82
83
|
netLabelTraceCollisionSolver?: NetLabelTraceCollisionSolver
|
|
84
|
+
netLabelNetLabelCollisionSolver?: NetLabelNetLabelCollisionSolver
|
|
83
85
|
|
|
84
86
|
startTimeOfPhase: Record<string, number>
|
|
85
87
|
endTimeOfPhase: Record<string, number>
|
|
@@ -300,6 +302,19 @@ export class SchematicTracePipelineSolver extends BaseSolver {
|
|
|
300
302
|
},
|
|
301
303
|
],
|
|
302
304
|
),
|
|
305
|
+
definePipelineStep(
|
|
306
|
+
"netLabelNetLabelCollisionSolver",
|
|
307
|
+
NetLabelNetLabelCollisionSolver,
|
|
308
|
+
(instance) => [
|
|
309
|
+
{
|
|
310
|
+
inputProblem: instance.inputProblem,
|
|
311
|
+
traces: instance.netLabelTraceCollisionSolver!.getOutput().traces,
|
|
312
|
+
netLabelPlacements:
|
|
313
|
+
instance.netLabelTraceCollisionSolver!.getOutput()
|
|
314
|
+
.netLabelPlacements,
|
|
315
|
+
},
|
|
316
|
+
],
|
|
317
|
+
),
|
|
303
318
|
]
|
|
304
319
|
|
|
305
320
|
constructor(inputProblem: InputProblem) {
|
package/package.json
CHANGED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{
|
|
2
|
+
"chips": [
|
|
3
|
+
{
|
|
4
|
+
"chipId": "schematic_component_0",
|
|
5
|
+
"center": {
|
|
6
|
+
"x": -6.5,
|
|
7
|
+
"y": -0.33
|
|
8
|
+
},
|
|
9
|
+
"width": 1.5,
|
|
10
|
+
"height": 0.8,
|
|
11
|
+
"pins": [
|
|
12
|
+
{
|
|
13
|
+
"pinId": "U3.24",
|
|
14
|
+
"x": -7.65,
|
|
15
|
+
"y": -0.22999999999999998
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"pinId": "U3.25",
|
|
19
|
+
"x": -7.65,
|
|
20
|
+
"y": -0.43000000000000016
|
|
21
|
+
}
|
|
22
|
+
],
|
|
23
|
+
"sectionId": "rp2040"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"chipId": "schematic_component_4",
|
|
27
|
+
"center": {
|
|
28
|
+
"x": -9.5,
|
|
29
|
+
"y": -0.4299999999999997
|
|
30
|
+
},
|
|
31
|
+
"width": 1.1,
|
|
32
|
+
"height": 0.388910699999999,
|
|
33
|
+
"pins": [
|
|
34
|
+
{
|
|
35
|
+
"pinId": "R5.1",
|
|
36
|
+
"x": -10.049999999999999,
|
|
37
|
+
"y": -0.4299999999999997
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"pinId": "R5.2",
|
|
41
|
+
"x": -8.95,
|
|
42
|
+
"y": -0.4299999999999997
|
|
43
|
+
}
|
|
44
|
+
],
|
|
45
|
+
"sectionId": "rp2040"
|
|
46
|
+
}
|
|
47
|
+
],
|
|
48
|
+
"directConnections": [
|
|
49
|
+
{
|
|
50
|
+
"pinIds": ["U3.25", "R5.2"],
|
|
51
|
+
"netId": ".U3 > .pin25 to .R5 > .pin2"
|
|
52
|
+
}
|
|
53
|
+
],
|
|
54
|
+
"netConnections": [
|
|
55
|
+
{
|
|
56
|
+
"netId": "SWCLK",
|
|
57
|
+
"pinIds": ["U3.24"],
|
|
58
|
+
"netLabelWidth": 0.72
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"netId": "SWD",
|
|
62
|
+
"pinIds": ["U3.25", "R5.2"],
|
|
63
|
+
"netLabelWidth": 0.48
|
|
64
|
+
}
|
|
65
|
+
],
|
|
66
|
+
"availableNetLabelOrientations": {
|
|
67
|
+
"SWCLK": ["x-", "x+"],
|
|
68
|
+
"SWD": ["x-", "x+"]
|
|
69
|
+
},
|
|
70
|
+
"maxMspPairDistance": 2.4
|
|
71
|
+
}
|