@tscircuit/schematic-trace-solver 0.0.51 → 0.0.53
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 -6
- package/dist/index.js +835 -80
- package/lib/solvers/AvailableNetOrientationSolver/AvailableNetOrientationSolver.ts +3 -39
- package/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts +16 -0
- package/lib/solvers/SchematicTracePipelineSolver/colorAvailableNetOrientationLabels.ts +54 -0
- package/lib/solvers/TraceAnchoredNetLabelOverlapSolver/TraceAnchoredNetLabelOverlapSolver.ts +331 -0
- package/lib/solvers/TraceAnchoredNetLabelOverlapSolver/candidates.ts +387 -0
- package/lib/solvers/TraceAnchoredNetLabelOverlapSolver/geometry.ts +254 -0
- package/lib/solvers/TraceAnchoredNetLabelOverlapSolver/types.ts +47 -0
- package/lib/solvers/TraceAnchoredNetLabelOverlapSolver/visualize.ts +159 -0
- package/package.json +1 -1
- package/tests/assets/example30.json +4 -1
- package/tests/examples/__snapshots__/example01.snap.svg +6 -3
- package/tests/examples/__snapshots__/example02.snap.svg +8 -4
- package/tests/examples/__snapshots__/example04.snap.svg +4 -2
- package/tests/examples/__snapshots__/example07.snap.svg +10 -5
- package/tests/examples/__snapshots__/example12.snap.svg +8 -4
- package/tests/examples/__snapshots__/example13.snap.svg +16 -8
- package/tests/examples/__snapshots__/example14.snap.svg +16 -8
- package/tests/examples/__snapshots__/example15.snap.svg +18 -9
- package/tests/examples/__snapshots__/example16.snap.svg +8 -4
- package/tests/examples/__snapshots__/example17.snap.svg +9 -5
- package/tests/examples/__snapshots__/example18.snap.svg +10 -5
- package/tests/examples/__snapshots__/example20.snap.svg +6 -3
- package/tests/examples/__snapshots__/example21.snap.svg +17 -12
- package/tests/examples/__snapshots__/example22.snap.svg +8 -4
- package/tests/examples/__snapshots__/example25.snap.svg +18 -9
- package/tests/examples/__snapshots__/example30.snap.svg +74 -62
- package/tests/examples/__snapshots__/example31.snap.svg +2 -1
- package/tests/fixtures/matcher.ts +21 -0
|
@@ -100,7 +100,7 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
100
100
|
const indices: number[] = []
|
|
101
101
|
|
|
102
102
|
for (let i = 0; i < this.outputNetLabelPlacements.length; i++) {
|
|
103
|
-
if (this.shouldProcessLabel(this.outputNetLabelPlacements[i]
|
|
103
|
+
if (this.shouldProcessLabel(this.outputNetLabelPlacements[i]!)) {
|
|
104
104
|
indices.push(i)
|
|
105
105
|
}
|
|
106
106
|
}
|
|
@@ -108,14 +108,9 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
108
108
|
return indices
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
-
private shouldProcessLabel(label: NetLabelPlacement
|
|
111
|
+
private shouldProcessLabel(label: NetLabelPlacement) {
|
|
112
112
|
const orientations = this.getAvailableOrientations(label)
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
return !(
|
|
116
|
-
orientations.includes(label.orientation) &&
|
|
117
|
-
this.isLabelPlacementValid(label, labelIndex)
|
|
118
|
-
)
|
|
113
|
+
return orientations.length > 0 && !orientations.includes(label.orientation)
|
|
119
114
|
}
|
|
120
115
|
|
|
121
116
|
private processLabel(labelIndex: number) {
|
|
@@ -441,27 +436,6 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
441
436
|
)?.netLabelWidth
|
|
442
437
|
}
|
|
443
438
|
|
|
444
|
-
private isLabelPlacementValid(label: NetLabelPlacement, labelIndex: number) {
|
|
445
|
-
return this.isCandidateValidWithOffset(
|
|
446
|
-
{
|
|
447
|
-
orientation: label.orientation,
|
|
448
|
-
anchorPoint: label.anchorPoint,
|
|
449
|
-
center: label.center,
|
|
450
|
-
width: label.width,
|
|
451
|
-
height: label.height,
|
|
452
|
-
},
|
|
453
|
-
labelIndex,
|
|
454
|
-
)
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
private isCandidateValidWithOffset(
|
|
458
|
-
candidate: CandidateLabel,
|
|
459
|
-
labelIndex: number,
|
|
460
|
-
) {
|
|
461
|
-
const bounds = this.getOffsetCollisionBounds(candidate)
|
|
462
|
-
return this.getBoundsStatus(bounds, labelIndex) === "valid"
|
|
463
|
-
}
|
|
464
|
-
|
|
465
439
|
private getCandidateStatus(
|
|
466
440
|
candidate: CandidateLabel,
|
|
467
441
|
label: NetLabelPlacement,
|
|
@@ -505,16 +479,6 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
505
479
|
return "valid"
|
|
506
480
|
}
|
|
507
481
|
|
|
508
|
-
private getOffsetCollisionBounds(candidate: CandidateLabel) {
|
|
509
|
-
const direction = dir(candidate.orientation)
|
|
510
|
-
const collisionTestOffset = 1e-4
|
|
511
|
-
const testCenter = {
|
|
512
|
-
x: candidate.center.x + direction.x * collisionTestOffset,
|
|
513
|
-
y: candidate.center.y + direction.y * collisionTestOffset,
|
|
514
|
-
}
|
|
515
|
-
return getRectBounds(testCenter, candidate.width, candidate.height)
|
|
516
|
-
}
|
|
517
|
-
|
|
518
482
|
private intersectsAnyOtherNetLabel(bounds: Bounds, labelIndex: number) {
|
|
519
483
|
for (let i = 0; i < this.outputNetLabelPlacements.length; i++) {
|
|
520
484
|
if (i === labelIndex) continue
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
} from "../SchematicTraceLinesSolver/SchematicTraceLinesSolver"
|
|
14
14
|
import { TraceOverlapShiftSolver } from "../TraceOverlapShiftSolver/TraceOverlapShiftSolver"
|
|
15
15
|
import { NetLabelPlacementSolver } from "../NetLabelPlacementSolver/NetLabelPlacementSolver"
|
|
16
|
+
import { colorAvailableNetOrientationLabels } from "./colorAvailableNetOrientationLabels"
|
|
16
17
|
import { visualizeInputProblem } from "./visualizeInputProblem"
|
|
17
18
|
import { TraceLabelOverlapAvoidanceSolver } from "../TraceLabelOverlapAvoidanceSolver/TraceLabelOverlapAvoidanceSolver"
|
|
18
19
|
import { correctPinsInsideChips } from "./correctPinsInsideChip"
|
|
@@ -23,6 +24,7 @@ import { TraceCleanupSolver } from "../TraceCleanupSolver/TraceCleanupSolver"
|
|
|
23
24
|
import { Example28Solver } from "../Example28Solver/Example28Solver"
|
|
24
25
|
import { AvailableNetOrientationSolver } from "../AvailableNetOrientationSolver/AvailableNetOrientationSolver"
|
|
25
26
|
import { VccNetLabelCornerPlacementSolver } from "../VccNetLabelCornerPlacementSolver/VccNetLabelCornerPlacementSolver"
|
|
27
|
+
import { TraceAnchoredNetLabelOverlapSolver } from "../TraceAnchoredNetLabelOverlapSolver/TraceAnchoredNetLabelOverlapSolver"
|
|
26
28
|
|
|
27
29
|
type PipelineStep<T extends new (...args: any[]) => BaseSolver> = {
|
|
28
30
|
solverName: string
|
|
@@ -75,6 +77,7 @@ export class SchematicTracePipelineSolver extends BaseSolver {
|
|
|
75
77
|
example28Solver?: Example28Solver
|
|
76
78
|
availableNetOrientationSolver?: AvailableNetOrientationSolver
|
|
77
79
|
vccNetLabelCornerPlacementSolver?: VccNetLabelCornerPlacementSolver
|
|
80
|
+
traceAnchoredNetLabelOverlapSolver?: TraceAnchoredNetLabelOverlapSolver
|
|
78
81
|
|
|
79
82
|
startTimeOfPhase: Record<string, number>
|
|
80
83
|
endTimeOfPhase: Record<string, number>
|
|
@@ -270,6 +273,18 @@ export class SchematicTracePipelineSolver extends BaseSolver {
|
|
|
270
273
|
]
|
|
271
274
|
},
|
|
272
275
|
),
|
|
276
|
+
definePipelineStep(
|
|
277
|
+
"traceAnchoredNetLabelOverlapSolver",
|
|
278
|
+
TraceAnchoredNetLabelOverlapSolver,
|
|
279
|
+
(instance) => [
|
|
280
|
+
{
|
|
281
|
+
inputProblem: instance.inputProblem,
|
|
282
|
+
traces: instance.availableNetOrientationSolver!.traces,
|
|
283
|
+
netLabelPlacements:
|
|
284
|
+
instance.vccNetLabelCornerPlacementSolver!.outputNetLabelPlacements,
|
|
285
|
+
},
|
|
286
|
+
],
|
|
287
|
+
),
|
|
273
288
|
]
|
|
274
289
|
|
|
275
290
|
constructor(inputProblem: InputProblem) {
|
|
@@ -389,6 +404,7 @@ export class SchematicTracePipelineSolver extends BaseSolver {
|
|
|
389
404
|
circles: visualizations.flatMap((v) => v.circles || []),
|
|
390
405
|
texts: visualizations.flatMap((v) => v.texts || []),
|
|
391
406
|
}
|
|
407
|
+
colorAvailableNetOrientationLabels(finalGraphics, this.inputProblem)
|
|
392
408
|
return finalGraphics
|
|
393
409
|
}
|
|
394
410
|
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { GraphicsObject } from "graphics-debug"
|
|
2
|
+
import type { InputProblem } from "lib/types/InputProblem"
|
|
3
|
+
|
|
4
|
+
const AVAILABLE_Y_PLUS_COLOR = "#93c5fd"
|
|
5
|
+
const AVAILABLE_Y_MINUS_COLOR = "#fca5a5"
|
|
6
|
+
const AVAILABLE_ORIENTATION_FILL_ALPHA = "66"
|
|
7
|
+
|
|
8
|
+
export const colorAvailableNetOrientationLabels = (
|
|
9
|
+
graphicsObject: GraphicsObject,
|
|
10
|
+
inputProblem: InputProblem,
|
|
11
|
+
) => {
|
|
12
|
+
const availableOrientations = inputProblem.availableNetLabelOrientations
|
|
13
|
+
if (!availableOrientations) return
|
|
14
|
+
|
|
15
|
+
for (const rect of graphicsObject.rects ?? []) {
|
|
16
|
+
const orientations = getAvailableOrientationsForRect(
|
|
17
|
+
rect.label,
|
|
18
|
+
availableOrientations,
|
|
19
|
+
)
|
|
20
|
+
if (!orientations) continue
|
|
21
|
+
|
|
22
|
+
const hasYPlus = orientations.includes("y+")
|
|
23
|
+
const hasYMinus = orientations.includes("y-")
|
|
24
|
+
if (hasYPlus === hasYMinus) continue
|
|
25
|
+
|
|
26
|
+
const color = hasYPlus ? AVAILABLE_Y_PLUS_COLOR : AVAILABLE_Y_MINUS_COLOR
|
|
27
|
+
|
|
28
|
+
rect.fill = `${color}${AVAILABLE_ORIENTATION_FILL_ALPHA}`
|
|
29
|
+
rect.stroke = color
|
|
30
|
+
rect.color = color
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const getAvailableOrientationsForRect = (
|
|
35
|
+
label: string | undefined,
|
|
36
|
+
availableOrientations: InputProblem["availableNetLabelOrientations"],
|
|
37
|
+
) => {
|
|
38
|
+
for (const netId of getNetIdsFromRectLabel(label)) {
|
|
39
|
+
if (Object.hasOwn(availableOrientations, netId)) {
|
|
40
|
+
return availableOrientations[netId]
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return undefined
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const getNetIdsFromRectLabel = (label: string | undefined) => {
|
|
48
|
+
if (!label) return []
|
|
49
|
+
|
|
50
|
+
return [
|
|
51
|
+
label.match(/^netId: (.+)$/m)?.[1],
|
|
52
|
+
label.match(/^globalConnNetId: (.+)$/m)?.[1],
|
|
53
|
+
].filter((netId): netId is string => Boolean(netId && netId !== "undefined"))
|
|
54
|
+
}
|
|
@@ -0,0 +1,331 @@
|
|
|
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 { getRectBounds } from "lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/geometry"
|
|
5
|
+
import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
|
|
6
|
+
import type { InputProblem } from "lib/types/InputProblem"
|
|
7
|
+
import { generateCandidatesAlongTrace } from "./candidates"
|
|
8
|
+
import {
|
|
9
|
+
getLabelBounds,
|
|
10
|
+
getTraceLocationsForPoint,
|
|
11
|
+
rectsOverlap,
|
|
12
|
+
rectsTouchOrOverlap,
|
|
13
|
+
traceCrossesBoundsInterior,
|
|
14
|
+
} from "./geometry"
|
|
15
|
+
import type {
|
|
16
|
+
Bounds,
|
|
17
|
+
CandidateStatus,
|
|
18
|
+
LabelCandidate,
|
|
19
|
+
LabelOverlap,
|
|
20
|
+
TraceAnchoredNetLabelOverlapSolverParams,
|
|
21
|
+
} from "./types"
|
|
22
|
+
import { visualizeTraceAnchoredNetLabelOverlapSolver } from "./visualize"
|
|
23
|
+
|
|
24
|
+
type ActiveOverlapSearch = {
|
|
25
|
+
overlap: LabelOverlap
|
|
26
|
+
queuedLabelIndices: number[]
|
|
27
|
+
labelIndex: number | null
|
|
28
|
+
candidates: LabelCandidate[]
|
|
29
|
+
candidateIndex: number
|
|
30
|
+
candidateResults: LabelCandidate[]
|
|
31
|
+
completed: boolean
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export class TraceAnchoredNetLabelOverlapSolver extends BaseSolver {
|
|
35
|
+
inputProblem: InputProblem
|
|
36
|
+
traces: SolvedTracePath[]
|
|
37
|
+
netLabelPlacements: NetLabelPlacement[]
|
|
38
|
+
|
|
39
|
+
outputNetLabelPlacements: NetLabelPlacement[]
|
|
40
|
+
currentOverlap: LabelOverlap | null = null
|
|
41
|
+
currentCandidateResults: LabelCandidate[] = []
|
|
42
|
+
|
|
43
|
+
private activeSearch: ActiveOverlapSearch | null = null
|
|
44
|
+
private skippedOverlapKeys = new Set<string>()
|
|
45
|
+
|
|
46
|
+
constructor(params: TraceAnchoredNetLabelOverlapSolverParams) {
|
|
47
|
+
super()
|
|
48
|
+
this.inputProblem = params.inputProblem
|
|
49
|
+
this.traces = params.traces
|
|
50
|
+
this.netLabelPlacements = params.netLabelPlacements
|
|
51
|
+
this.outputNetLabelPlacements = [...params.netLabelPlacements]
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
override getConstructorParams(): ConstructorParameters<
|
|
55
|
+
typeof TraceAnchoredNetLabelOverlapSolver
|
|
56
|
+
>[0] {
|
|
57
|
+
return {
|
|
58
|
+
inputProblem: this.inputProblem,
|
|
59
|
+
traces: this.traces,
|
|
60
|
+
netLabelPlacements: this.netLabelPlacements,
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
override _step() {
|
|
65
|
+
if (this.activeSearch?.completed) this.clearActiveSearch()
|
|
66
|
+
|
|
67
|
+
const search = this.getNextSearchWithCandidates()
|
|
68
|
+
if (!search) {
|
|
69
|
+
this.finish()
|
|
70
|
+
return
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
this.evaluateNextCandidate(search)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
getOutput() {
|
|
77
|
+
return {
|
|
78
|
+
netLabelPlacements: this.outputNetLabelPlacements,
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
private findNextOverlap() {
|
|
83
|
+
for (let i = 0; i < this.outputNetLabelPlacements.length; i++) {
|
|
84
|
+
for (let j = i + 1; j < this.outputNetLabelPlacements.length; j++) {
|
|
85
|
+
if (!this.isLabelEligible(i) && !this.isLabelEligible(j)) continue
|
|
86
|
+
|
|
87
|
+
const overlap = {
|
|
88
|
+
firstLabelIndex: i,
|
|
89
|
+
secondLabelIndex: j,
|
|
90
|
+
}
|
|
91
|
+
if (this.skippedOverlapKeys.has(this.getOverlapKey(overlap))) continue
|
|
92
|
+
if (this.labelsOverlap(overlap)) return overlap
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return null
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
private isLabelEligible(labelIndex: number) {
|
|
100
|
+
return this.getTraceLocationsForLabel(labelIndex).length > 0
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
private labelsOverlap(overlap: LabelOverlap) {
|
|
104
|
+
const first = this.outputNetLabelPlacements[overlap.firstLabelIndex]
|
|
105
|
+
const second = this.outputNetLabelPlacements[overlap.secondLabelIndex]
|
|
106
|
+
if (!first || !second) return false
|
|
107
|
+
|
|
108
|
+
return rectsOverlap(getLabelBounds(first), getLabelBounds(second))
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
private startNextOverlapSearch() {
|
|
112
|
+
const overlap = this.findNextOverlap()
|
|
113
|
+
if (!overlap) return null
|
|
114
|
+
|
|
115
|
+
const search: ActiveOverlapSearch = {
|
|
116
|
+
overlap,
|
|
117
|
+
queuedLabelIndices: this.getMoveLabelIndices(overlap),
|
|
118
|
+
labelIndex: null,
|
|
119
|
+
candidates: [],
|
|
120
|
+
candidateIndex: 0,
|
|
121
|
+
candidateResults: [],
|
|
122
|
+
completed: false,
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
this.setActiveSearch(search)
|
|
126
|
+
return search
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
private getMoveLabelIndices(overlap: LabelOverlap) {
|
|
130
|
+
const labelIndices: number[] = []
|
|
131
|
+
|
|
132
|
+
if (this.isLabelEligible(overlap.secondLabelIndex)) {
|
|
133
|
+
labelIndices.push(overlap.secondLabelIndex)
|
|
134
|
+
}
|
|
135
|
+
if (this.isLabelEligible(overlap.firstLabelIndex)) {
|
|
136
|
+
labelIndices.push(overlap.firstLabelIndex)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return labelIndices
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
private getNextSearchWithCandidates() {
|
|
143
|
+
while (true) {
|
|
144
|
+
const search = this.activeSearch ?? this.startNextOverlapSearch()
|
|
145
|
+
if (!search) return null
|
|
146
|
+
if (this.activateNextLabelSearch(search)) return search
|
|
147
|
+
|
|
148
|
+
this.skipSearch(search)
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
private activateNextLabelSearch(search: ActiveOverlapSearch) {
|
|
153
|
+
if (
|
|
154
|
+
search.labelIndex !== null &&
|
|
155
|
+
search.candidateIndex < search.candidates.length
|
|
156
|
+
) {
|
|
157
|
+
return true
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
while (search.queuedLabelIndices.length > 0) {
|
|
161
|
+
const labelIndex = search.queuedLabelIndices.shift()!
|
|
162
|
+
const label = this.outputNetLabelPlacements[labelIndex]
|
|
163
|
+
if (!label) continue
|
|
164
|
+
|
|
165
|
+
search.labelIndex = labelIndex
|
|
166
|
+
search.candidates = this.getCandidatesForLabel(label)
|
|
167
|
+
search.candidateIndex = 0
|
|
168
|
+
if (search.candidates.length > 0) return true
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
search.labelIndex = null
|
|
172
|
+
search.candidates = []
|
|
173
|
+
search.candidateIndex = 0
|
|
174
|
+
return false
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
private skipSearch(search: ActiveOverlapSearch) {
|
|
178
|
+
this.skippedOverlapKeys.add(this.getOverlapKey(search.overlap))
|
|
179
|
+
this.clearActiveSearch()
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
private evaluateNextCandidate(search: ActiveOverlapSearch) {
|
|
183
|
+
const labelIndex = search.labelIndex
|
|
184
|
+
if (labelIndex === null) return
|
|
185
|
+
|
|
186
|
+
const label = this.outputNetLabelPlacements[labelIndex]
|
|
187
|
+
const candidate = search.candidates[search.candidateIndex]
|
|
188
|
+
if (!label || !candidate) return
|
|
189
|
+
|
|
190
|
+
search.candidateIndex += 1
|
|
191
|
+
|
|
192
|
+
const evaluatedCandidate = {
|
|
193
|
+
...candidate,
|
|
194
|
+
status: this.getCandidateStatus(candidate, labelIndex),
|
|
195
|
+
}
|
|
196
|
+
search.candidateResults.push(evaluatedCandidate)
|
|
197
|
+
|
|
198
|
+
if (evaluatedCandidate.status !== "valid") return
|
|
199
|
+
|
|
200
|
+
evaluatedCandidate.selected = true
|
|
201
|
+
this.applyCandidate(labelIndex, label, evaluatedCandidate)
|
|
202
|
+
search.completed = true
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
private getCandidatesForLabel(label: NetLabelPlacement) {
|
|
206
|
+
const candidates: LabelCandidate[] = []
|
|
207
|
+
|
|
208
|
+
for (const traceLocation of getTraceLocationsForPoint(
|
|
209
|
+
label.anchorPoint,
|
|
210
|
+
this.traces,
|
|
211
|
+
).filter(({ trace }) => this.isTraceAssociatedWithLabel(trace, label))) {
|
|
212
|
+
candidates.push(
|
|
213
|
+
...generateCandidatesAlongTrace({
|
|
214
|
+
inputProblem: this.inputProblem,
|
|
215
|
+
label,
|
|
216
|
+
traceLocation,
|
|
217
|
+
}),
|
|
218
|
+
)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return candidates
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
private getCandidateStatus(
|
|
225
|
+
candidate: LabelCandidate,
|
|
226
|
+
labelIndex: number,
|
|
227
|
+
): CandidateStatus {
|
|
228
|
+
const bounds = getLabelBounds(candidate)
|
|
229
|
+
if (this.intersectsAnyChip(bounds)) return "chip-collision"
|
|
230
|
+
if (traceCrossesBoundsInterior(bounds, this.traces))
|
|
231
|
+
return "trace-collision"
|
|
232
|
+
if (this.intersectsAnyOtherLabel(bounds, labelIndex)) {
|
|
233
|
+
return "netlabel-collision"
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return "valid"
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
private applyCandidate(
|
|
240
|
+
labelIndex: number,
|
|
241
|
+
label: NetLabelPlacement,
|
|
242
|
+
candidate: LabelCandidate,
|
|
243
|
+
) {
|
|
244
|
+
this.outputNetLabelPlacements[labelIndex] = {
|
|
245
|
+
...label,
|
|
246
|
+
anchorPoint: candidate.anchorPoint,
|
|
247
|
+
center: candidate.center,
|
|
248
|
+
width: candidate.width,
|
|
249
|
+
height: candidate.height,
|
|
250
|
+
orientation: candidate.orientation,
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
private intersectsAnyChip(bounds: Bounds) {
|
|
255
|
+
return this.inputProblem.chips.some((chip) =>
|
|
256
|
+
rectsTouchOrOverlap(
|
|
257
|
+
bounds,
|
|
258
|
+
getRectBounds(chip.center, chip.width, chip.height),
|
|
259
|
+
),
|
|
260
|
+
)
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
private intersectsAnyOtherLabel(bounds: Bounds, labelIndex: number) {
|
|
264
|
+
return this.outputNetLabelPlacements.some((label, index) => {
|
|
265
|
+
if (index === labelIndex) return false
|
|
266
|
+
return rectsOverlap(bounds, getLabelBounds(label))
|
|
267
|
+
})
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
private getTraceLocationsForLabel(labelIndex: number) {
|
|
271
|
+
const label = this.outputNetLabelPlacements[labelIndex]
|
|
272
|
+
if (!label) return []
|
|
273
|
+
|
|
274
|
+
return getTraceLocationsForPoint(label.anchorPoint, this.traces).filter(
|
|
275
|
+
({ trace }) => this.isTraceAssociatedWithLabel(trace, label),
|
|
276
|
+
)
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
private isTraceAssociatedWithLabel(
|
|
280
|
+
trace: SolvedTracePath,
|
|
281
|
+
label: NetLabelPlacement,
|
|
282
|
+
) {
|
|
283
|
+
if (label.mspConnectionPairIds.includes(trace.mspPairId)) return true
|
|
284
|
+
if (label.netId !== undefined && label.netId === trace.userNetId) {
|
|
285
|
+
return true
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
return label.globalConnNetId === trace.globalConnNetId
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
private getOverlapKey(overlap: LabelOverlap) {
|
|
292
|
+
const first = this.outputNetLabelPlacements[overlap.firstLabelIndex]
|
|
293
|
+
const second = this.outputNetLabelPlacements[overlap.secondLabelIndex]
|
|
294
|
+
return [
|
|
295
|
+
overlap.firstLabelIndex,
|
|
296
|
+
overlap.secondLabelIndex,
|
|
297
|
+
first?.anchorPoint.x,
|
|
298
|
+
first?.anchorPoint.y,
|
|
299
|
+
second?.anchorPoint.x,
|
|
300
|
+
second?.anchorPoint.y,
|
|
301
|
+
].join(":")
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
private finish() {
|
|
305
|
+
this.clearActiveSearch()
|
|
306
|
+
this.solved = true
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
private setActiveSearch(search: ActiveOverlapSearch) {
|
|
310
|
+
this.activeSearch = search
|
|
311
|
+
this.currentOverlap = search.overlap
|
|
312
|
+
this.currentCandidateResults = search.candidateResults
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
private clearActiveSearch() {
|
|
316
|
+
this.activeSearch = null
|
|
317
|
+
this.currentOverlap = null
|
|
318
|
+
this.currentCandidateResults = []
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
override visualize(): GraphicsObject {
|
|
322
|
+
return visualizeTraceAnchoredNetLabelOverlapSolver({
|
|
323
|
+
inputProblem: this.inputProblem,
|
|
324
|
+
traces: this.traces,
|
|
325
|
+
outputNetLabelPlacements: this.outputNetLabelPlacements,
|
|
326
|
+
currentOverlap: this.currentOverlap,
|
|
327
|
+
currentCandidateResults: this.currentCandidateResults,
|
|
328
|
+
solved: this.solved,
|
|
329
|
+
})
|
|
330
|
+
}
|
|
331
|
+
}
|