@tscircuit/schematic-trace-solver 0.0.49 → 0.0.51
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 +133 -1
- package/dist/index.js +1084 -41
- package/lib/solvers/AvailableNetOrientationSolver/AvailableNetOrientationSolver.ts +682 -0
- package/lib/solvers/AvailableNetOrientationSolver/constants.ts +6 -0
- package/lib/solvers/AvailableNetOrientationSolver/geometry.ts +192 -0
- package/lib/solvers/AvailableNetOrientationSolver/traces.ts +43 -0
- package/lib/solvers/AvailableNetOrientationSolver/types.ts +44 -0
- package/lib/solvers/AvailableNetOrientationSolver/visualize.ts +123 -0
- package/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts +30 -0
- package/lib/solvers/VccNetLabelCornerPlacementSolver/VccNetLabelCornerPlacementSolver.ts +268 -0
- package/lib/solvers/VccNetLabelCornerPlacementSolver/geometry.ts +59 -0
- package/lib/solvers/VccNetLabelCornerPlacementSolver/types.ts +37 -0
- package/lib/solvers/VccNetLabelCornerPlacementSolver/visualize.ts +114 -0
- package/package.json +1 -1
- package/tests/assets/example22.json +1 -1
- package/tests/examples/__snapshots__/example12.snap.svg +33 -30
- package/tests/examples/__snapshots__/example13.snap.svg +10 -4
- package/tests/examples/__snapshots__/example14.snap.svg +72 -66
- package/tests/examples/__snapshots__/example16.snap.svg +33 -27
- package/tests/examples/__snapshots__/example20.snap.svg +29 -23
- package/tests/examples/__snapshots__/example21.snap.svg +77 -65
- package/tests/examples/__snapshots__/example22.snap.svg +31 -25
- package/tests/examples/__snapshots__/example31.snap.svg +12 -12
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import type { GraphicsObject } from "graphics-debug"
|
|
2
|
+
import { traceCrossesBoundsInterior } from "lib/solvers/AvailableNetOrientationSolver/geometry"
|
|
3
|
+
import { BaseSolver } from "lib/solvers/BaseSolver/BaseSolver"
|
|
4
|
+
import type { NetLabelPlacement } from "lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver"
|
|
5
|
+
import {
|
|
6
|
+
getCenterFromAnchor,
|
|
7
|
+
getRectBounds,
|
|
8
|
+
} from "lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/geometry"
|
|
9
|
+
import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
|
|
10
|
+
import type { InputProblem } from "lib/types/InputProblem"
|
|
11
|
+
import {
|
|
12
|
+
getDistance,
|
|
13
|
+
getTraceCorners,
|
|
14
|
+
isTraceLine,
|
|
15
|
+
rectsOverlap,
|
|
16
|
+
tracePathContainsPoint,
|
|
17
|
+
} from "./geometry"
|
|
18
|
+
import type {
|
|
19
|
+
Bounds,
|
|
20
|
+
CornerCandidateStatus,
|
|
21
|
+
EvaluatedCornerCandidate,
|
|
22
|
+
TraceCornerCandidate,
|
|
23
|
+
VccNetLabelCornerPlacementSolverParams,
|
|
24
|
+
} from "./types"
|
|
25
|
+
import { visualizeVccNetLabelCornerPlacementSolver } from "./visualize"
|
|
26
|
+
|
|
27
|
+
export class VccNetLabelCornerPlacementSolver extends BaseSolver {
|
|
28
|
+
inputProblem: InputProblem
|
|
29
|
+
traces: SolvedTracePath[]
|
|
30
|
+
netLabelPlacements: NetLabelPlacement[]
|
|
31
|
+
|
|
32
|
+
outputNetLabelPlacements: NetLabelPlacement[]
|
|
33
|
+
queuedLabelIndices: number[] = []
|
|
34
|
+
currentLabelIndex: number | null = null
|
|
35
|
+
currentLabel: NetLabelPlacement | null = null
|
|
36
|
+
currentCandidateResults: EvaluatedCornerCandidate[] = []
|
|
37
|
+
|
|
38
|
+
private queuedCornerCandidates: TraceCornerCandidate[] = []
|
|
39
|
+
private shouldAdvanceToNextLabel = false
|
|
40
|
+
private traceMap: Record<string, SolvedTracePath>
|
|
41
|
+
|
|
42
|
+
constructor(params: VccNetLabelCornerPlacementSolverParams) {
|
|
43
|
+
super()
|
|
44
|
+
this.inputProblem = params.inputProblem
|
|
45
|
+
this.traces = params.traces
|
|
46
|
+
this.netLabelPlacements = params.netLabelPlacements
|
|
47
|
+
this.outputNetLabelPlacements = [...params.netLabelPlacements]
|
|
48
|
+
this.traceMap = Object.fromEntries(
|
|
49
|
+
params.traces.map((trace) => [trace.mspPairId, trace]),
|
|
50
|
+
)
|
|
51
|
+
this.queuedLabelIndices = this.getProcessableLabelIndices()
|
|
52
|
+
this.prepareNextLabel()
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
override getConstructorParams(): ConstructorParameters<
|
|
56
|
+
typeof VccNetLabelCornerPlacementSolver
|
|
57
|
+
>[0] {
|
|
58
|
+
return {
|
|
59
|
+
inputProblem: this.inputProblem,
|
|
60
|
+
traces: this.traces,
|
|
61
|
+
netLabelPlacements: this.netLabelPlacements,
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
override _step() {
|
|
66
|
+
if (this.shouldAdvanceToNextLabel) {
|
|
67
|
+
this.advanceToNextLabel()
|
|
68
|
+
return
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const label = this.currentLabel
|
|
72
|
+
const labelIndex = this.currentLabelIndex
|
|
73
|
+
if (label === null || labelIndex === null) {
|
|
74
|
+
this.finish()
|
|
75
|
+
return
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const candidate = this.queuedCornerCandidates.shift()
|
|
79
|
+
if (!candidate) {
|
|
80
|
+
this.shouldAdvanceToNextLabel = true
|
|
81
|
+
return
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const result = this.evaluateCornerCandidate(label, labelIndex, candidate)
|
|
85
|
+
this.currentCandidateResults.push(result)
|
|
86
|
+
|
|
87
|
+
if (result.status !== "valid") return
|
|
88
|
+
|
|
89
|
+
result.selected = true
|
|
90
|
+
this.applyCandidate(labelIndex, label, result)
|
|
91
|
+
this.shouldAdvanceToNextLabel = true
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
getOutput() {
|
|
95
|
+
return {
|
|
96
|
+
netLabelPlacements: this.outputNetLabelPlacements,
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
private getProcessableLabelIndices() {
|
|
101
|
+
const labelIndices: number[] = []
|
|
102
|
+
|
|
103
|
+
for (let i = 0; i < this.outputNetLabelPlacements.length; i++) {
|
|
104
|
+
const label = this.outputNetLabelPlacements[i]!
|
|
105
|
+
if (!this.shouldProcessLabel(label)) continue
|
|
106
|
+
|
|
107
|
+
labelIndices.push(i)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return labelIndices
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
private prepareNextLabel() {
|
|
114
|
+
const labelIndex = this.queuedLabelIndices.shift()
|
|
115
|
+
if (labelIndex === undefined) {
|
|
116
|
+
this.clearCurrentLabel()
|
|
117
|
+
return false
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const label = this.outputNetLabelPlacements[labelIndex]!
|
|
121
|
+
this.currentLabelIndex = labelIndex
|
|
122
|
+
this.currentLabel = label
|
|
123
|
+
this.currentCandidateResults = []
|
|
124
|
+
this.queuedCornerCandidates = this.getCornerCandidatesForLabel(label)
|
|
125
|
+
|
|
126
|
+
return true
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
private advanceToNextLabel() {
|
|
130
|
+
this.shouldAdvanceToNextLabel = false
|
|
131
|
+
if (!this.prepareNextLabel()) this.finish()
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
private clearCurrentLabel() {
|
|
135
|
+
this.currentLabelIndex = null
|
|
136
|
+
this.currentLabel = null
|
|
137
|
+
this.currentCandidateResults = []
|
|
138
|
+
this.queuedCornerCandidates = []
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
private finish() {
|
|
142
|
+
this.clearCurrentLabel()
|
|
143
|
+
this.solved = true
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
private evaluateCornerCandidate(
|
|
147
|
+
label: NetLabelPlacement,
|
|
148
|
+
labelIndex: number,
|
|
149
|
+
candidate: TraceCornerCandidate,
|
|
150
|
+
): EvaluatedCornerCandidate {
|
|
151
|
+
const center = getCenterFromAnchor(
|
|
152
|
+
candidate.anchorPoint,
|
|
153
|
+
label.orientation,
|
|
154
|
+
label.width,
|
|
155
|
+
label.height,
|
|
156
|
+
)
|
|
157
|
+
const bounds = getRectBounds(center, label.width, label.height)
|
|
158
|
+
|
|
159
|
+
return {
|
|
160
|
+
...candidate,
|
|
161
|
+
center,
|
|
162
|
+
width: label.width,
|
|
163
|
+
height: label.height,
|
|
164
|
+
status: this.getCandidateStatus(bounds, labelIndex),
|
|
165
|
+
selected: false,
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
private getCandidateStatus(
|
|
170
|
+
bounds: Bounds,
|
|
171
|
+
labelIndex: number,
|
|
172
|
+
): CornerCandidateStatus {
|
|
173
|
+
if (this.intersectsAnyChip(bounds)) return "chip-collision"
|
|
174
|
+
if (traceCrossesBoundsInterior(bounds, this.traceMap)) {
|
|
175
|
+
return "trace-collision"
|
|
176
|
+
}
|
|
177
|
+
if (this.intersectsAnyOtherNetLabel(bounds, labelIndex)) {
|
|
178
|
+
return "netlabel-collision"
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return "valid"
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
private applyCandidate(
|
|
185
|
+
labelIndex: number,
|
|
186
|
+
label: NetLabelPlacement,
|
|
187
|
+
candidate: EvaluatedCornerCandidate,
|
|
188
|
+
) {
|
|
189
|
+
this.outputNetLabelPlacements[labelIndex] = {
|
|
190
|
+
...label,
|
|
191
|
+
anchorPoint: candidate.anchorPoint,
|
|
192
|
+
center: candidate.center,
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
private shouldProcessLabel(label: NetLabelPlacement) {
|
|
197
|
+
return (
|
|
198
|
+
label.netId === "VCC" &&
|
|
199
|
+
this.getCornerCandidatesForLabel(label).length > 0
|
|
200
|
+
)
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
private intersectsAnyChip(bounds: Bounds) {
|
|
204
|
+
return this.inputProblem.chips.some((chip) =>
|
|
205
|
+
rectsOverlap(bounds, getRectBounds(chip.center, chip.width, chip.height)),
|
|
206
|
+
)
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
private intersectsAnyOtherNetLabel(bounds: Bounds, labelIndex: number) {
|
|
210
|
+
return this.outputNetLabelPlacements.some((label, index) => {
|
|
211
|
+
if (index === labelIndex) return false
|
|
212
|
+
return rectsOverlap(
|
|
213
|
+
bounds,
|
|
214
|
+
getRectBounds(label.center, label.width, label.height),
|
|
215
|
+
)
|
|
216
|
+
})
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
private getCornerCandidatesForLabel(label: NetLabelPlacement) {
|
|
220
|
+
const candidates: TraceCornerCandidate[] = []
|
|
221
|
+
const seenCornerKeys = new Set<string>()
|
|
222
|
+
|
|
223
|
+
for (const trace of this.getTraceLinesForLabel(label)) {
|
|
224
|
+
for (const anchorPoint of getTraceCorners(trace.tracePath)) {
|
|
225
|
+
const key = `${anchorPoint.x}:${anchorPoint.y}`
|
|
226
|
+
if (seenCornerKeys.has(key)) continue
|
|
227
|
+
seenCornerKeys.add(key)
|
|
228
|
+
candidates.push({
|
|
229
|
+
anchorPoint,
|
|
230
|
+
traceId: trace.mspPairId,
|
|
231
|
+
distance: getDistance(anchorPoint, label.anchorPoint),
|
|
232
|
+
})
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return candidates.sort((a, b) => a.distance - b.distance)
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
private getTraceLinesForLabel(label: NetLabelPlacement) {
|
|
240
|
+
return this.traces.filter(
|
|
241
|
+
(trace) => isTraceLine(trace) && this.isTraceForLabel(trace, label),
|
|
242
|
+
)
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
private isTraceForLabel(trace: SolvedTracePath, label: NetLabelPlacement) {
|
|
246
|
+
if (!tracePathContainsPoint(trace.tracePath, label.anchorPoint)) {
|
|
247
|
+
return false
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const traceIds = new Set(label.mspConnectionPairIds)
|
|
251
|
+
if (traceIds.size > 0) {
|
|
252
|
+
return traceIds.has(trace.mspPairId)
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return trace.globalConnNetId === label.globalConnNetId
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
override visualize(): GraphicsObject {
|
|
259
|
+
return visualizeVccNetLabelCornerPlacementSolver({
|
|
260
|
+
inputProblem: this.inputProblem,
|
|
261
|
+
traces: this.traces,
|
|
262
|
+
outputNetLabelPlacements: this.outputNetLabelPlacements,
|
|
263
|
+
currentLabel: this.currentLabel,
|
|
264
|
+
currentCandidateResults: this.currentCandidateResults,
|
|
265
|
+
solved: this.solved,
|
|
266
|
+
})
|
|
267
|
+
}
|
|
268
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { Point } from "@tscircuit/math-utils"
|
|
2
|
+
import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
|
|
3
|
+
import type { Bounds } from "./types"
|
|
4
|
+
|
|
5
|
+
export const EPS = 1e-6
|
|
6
|
+
|
|
7
|
+
export const isTraceLine = (trace: SolvedTracePath) =>
|
|
8
|
+
getUniquePinCount(trace.pinIds) >= 2
|
|
9
|
+
|
|
10
|
+
export const rectsOverlap = (a: Bounds, b: Bounds) =>
|
|
11
|
+
Math.min(a.maxX, b.maxX) - Math.max(a.minX, b.minX) > EPS &&
|
|
12
|
+
Math.min(a.maxY, b.maxY) - Math.max(a.minY, b.minY) > EPS
|
|
13
|
+
|
|
14
|
+
export const getTraceCorners = (path: Point[]) => {
|
|
15
|
+
const corners: Point[] = []
|
|
16
|
+
for (let i = 1; i < path.length - 1; i++) {
|
|
17
|
+
const previousPoint = path[i - 1]!
|
|
18
|
+
const cornerPoint = path[i]!
|
|
19
|
+
const nextPoint = path[i + 1]!
|
|
20
|
+
if (isTraceCorner(previousPoint, cornerPoint, nextPoint)) {
|
|
21
|
+
corners.push(cornerPoint)
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return corners
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const tracePathContainsPoint = (path: Point[], point: Point) => {
|
|
28
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
29
|
+
if (isPointOnSegment(point, path[i]!, path[i + 1]!)) return true
|
|
30
|
+
}
|
|
31
|
+
return false
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const getDistance = (a: Point, b: Point) =>
|
|
35
|
+
Math.abs(a.x - b.x) + Math.abs(a.y - b.y)
|
|
36
|
+
|
|
37
|
+
const getUniquePinCount = (pinIds: string[]) => new Set(pinIds).size
|
|
38
|
+
|
|
39
|
+
const isTraceCorner = (a: Point, b: Point, c: Point) =>
|
|
40
|
+
getSegmentOrientation(a, b) !== getSegmentOrientation(b, c)
|
|
41
|
+
|
|
42
|
+
const isPointOnSegment = (point: Point, start: Point, end: Point) => {
|
|
43
|
+
if (getSegmentOrientation(start, end) === "horizontal") {
|
|
44
|
+
return (
|
|
45
|
+
Math.abs(point.y - start.y) <= EPS &&
|
|
46
|
+
point.x >= Math.min(start.x, end.x) - EPS &&
|
|
47
|
+
point.x <= Math.max(start.x, end.x) + EPS
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
Math.abs(point.x - start.x) <= EPS &&
|
|
53
|
+
point.y >= Math.min(start.y, end.y) - EPS &&
|
|
54
|
+
point.y <= Math.max(start.y, end.y) + EPS
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const getSegmentOrientation = (a: Point, b: Point) =>
|
|
59
|
+
Math.abs(a.y - b.y) <= EPS ? "horizontal" : "vertical"
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Point } from "@tscircuit/math-utils"
|
|
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
|
+
|
|
6
|
+
export interface VccNetLabelCornerPlacementSolverParams {
|
|
7
|
+
inputProblem: InputProblem
|
|
8
|
+
traces: SolvedTracePath[]
|
|
9
|
+
netLabelPlacements: NetLabelPlacement[]
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type Bounds = {
|
|
13
|
+
minX: number
|
|
14
|
+
minY: number
|
|
15
|
+
maxX: number
|
|
16
|
+
maxY: number
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type TraceCornerCandidate = {
|
|
20
|
+
anchorPoint: Point
|
|
21
|
+
traceId: string
|
|
22
|
+
distance: number
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export type CornerCandidateStatus =
|
|
26
|
+
| "valid"
|
|
27
|
+
| "chip-collision"
|
|
28
|
+
| "trace-collision"
|
|
29
|
+
| "netlabel-collision"
|
|
30
|
+
|
|
31
|
+
export type EvaluatedCornerCandidate = TraceCornerCandidate & {
|
|
32
|
+
center: Point
|
|
33
|
+
width: number
|
|
34
|
+
height: number
|
|
35
|
+
status: CornerCandidateStatus
|
|
36
|
+
selected: boolean
|
|
37
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import type { GraphicsObject } from "graphics-debug"
|
|
2
|
+
import type { NetLabelPlacement } from "lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver"
|
|
3
|
+
import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
|
|
4
|
+
import { visualizeInputProblem } from "lib/solvers/SchematicTracePipelineSolver/visualizeInputProblem"
|
|
5
|
+
import type { InputProblem } from "lib/types/InputProblem"
|
|
6
|
+
import { getColorFromString } from "lib/utils/getColorFromString"
|
|
7
|
+
import type { EvaluatedCornerCandidate } from "./types"
|
|
8
|
+
|
|
9
|
+
const CANDIDATE_SELECTED_COLOR = "blue"
|
|
10
|
+
const CANDIDATE_REJECTED_COLOR = "red"
|
|
11
|
+
|
|
12
|
+
export const visualizeVccNetLabelCornerPlacementSolver = (state: {
|
|
13
|
+
inputProblem: InputProblem
|
|
14
|
+
traces: SolvedTracePath[]
|
|
15
|
+
outputNetLabelPlacements: NetLabelPlacement[]
|
|
16
|
+
currentLabel: NetLabelPlacement | null
|
|
17
|
+
currentCandidateResults: EvaluatedCornerCandidate[]
|
|
18
|
+
solved: boolean
|
|
19
|
+
}): GraphicsObject => {
|
|
20
|
+
const graphics = visualizeInputProblem(state.inputProblem)
|
|
21
|
+
ensureGraphicsArrays(graphics)
|
|
22
|
+
|
|
23
|
+
drawTraces(graphics, state.traces)
|
|
24
|
+
drawNetLabels(graphics, state.outputNetLabelPlacements)
|
|
25
|
+
|
|
26
|
+
if (!state.solved) {
|
|
27
|
+
drawCurrentLabel(graphics, state.currentLabel)
|
|
28
|
+
drawCurrentCandidates(graphics, state.currentCandidateResults)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return graphics
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const drawTraces = (graphics: GraphicsObject, traces: SolvedTracePath[]) => {
|
|
35
|
+
for (const trace of traces) {
|
|
36
|
+
graphics.lines!.push({
|
|
37
|
+
points: trace.tracePath,
|
|
38
|
+
strokeColor: "purple",
|
|
39
|
+
} as any)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const drawNetLabels = (
|
|
44
|
+
graphics: GraphicsObject,
|
|
45
|
+
labels: NetLabelPlacement[],
|
|
46
|
+
) => {
|
|
47
|
+
for (const label of labels) {
|
|
48
|
+
graphics.rects!.push({
|
|
49
|
+
center: label.center,
|
|
50
|
+
width: label.width,
|
|
51
|
+
height: label.height,
|
|
52
|
+
fill: getColorFromString(label.globalConnNetId, 0.35),
|
|
53
|
+
strokeColor: getColorFromString(label.globalConnNetId, 0.9),
|
|
54
|
+
label: `netId: ${label.netId}\nglobalConnNetId: ${label.globalConnNetId}`,
|
|
55
|
+
} as any)
|
|
56
|
+
graphics.points!.push({
|
|
57
|
+
x: label.anchorPoint.x,
|
|
58
|
+
y: label.anchorPoint.y,
|
|
59
|
+
color: getColorFromString(label.globalConnNetId, 0.9),
|
|
60
|
+
label: `anchorPoint\norientation: ${label.orientation}`,
|
|
61
|
+
} as any)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const drawCurrentLabel = (
|
|
66
|
+
graphics: GraphicsObject,
|
|
67
|
+
currentLabel: NetLabelPlacement | null,
|
|
68
|
+
) => {
|
|
69
|
+
if (!currentLabel) return
|
|
70
|
+
|
|
71
|
+
graphics.rects!.push({
|
|
72
|
+
center: currentLabel.center,
|
|
73
|
+
width: currentLabel.width,
|
|
74
|
+
height: currentLabel.height,
|
|
75
|
+
fill: "rgba(255, 0, 0, 0.2)",
|
|
76
|
+
strokeColor: CANDIDATE_REJECTED_COLOR,
|
|
77
|
+
label: `trace-line VCC target\n${currentLabel.netId ?? currentLabel.globalConnNetId}`,
|
|
78
|
+
} as any)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const drawCurrentCandidates = (
|
|
82
|
+
graphics: GraphicsObject,
|
|
83
|
+
candidates: EvaluatedCornerCandidate[],
|
|
84
|
+
) => {
|
|
85
|
+
for (const candidate of candidates) {
|
|
86
|
+
const color = candidate.selected
|
|
87
|
+
? CANDIDATE_SELECTED_COLOR
|
|
88
|
+
: CANDIDATE_REJECTED_COLOR
|
|
89
|
+
graphics.rects!.push({
|
|
90
|
+
center: candidate.center,
|
|
91
|
+
width: candidate.width,
|
|
92
|
+
height: candidate.height,
|
|
93
|
+
fill: candidate.selected
|
|
94
|
+
? "rgba(0, 0, 255, 0.2)"
|
|
95
|
+
: "rgba(255, 0, 0, 0.15)",
|
|
96
|
+
strokeColor: color,
|
|
97
|
+
strokeDash: candidate.selected ? undefined : "4 2",
|
|
98
|
+
label: `${candidate.selected ? "selected" : candidate.status} trace corner\ntrace: ${candidate.traceId}\ndistance: ${candidate.distance.toFixed(3)}`,
|
|
99
|
+
} as any)
|
|
100
|
+
graphics.points!.push({
|
|
101
|
+
...candidate.anchorPoint,
|
|
102
|
+
color,
|
|
103
|
+
label: `candidate anchor\n${candidate.status}`,
|
|
104
|
+
} as any)
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const ensureGraphicsArrays = (graphics: GraphicsObject) => {
|
|
109
|
+
if (!graphics.lines) graphics.lines = []
|
|
110
|
+
if (!graphics.points) graphics.points = []
|
|
111
|
+
if (!graphics.rects) graphics.rects = []
|
|
112
|
+
if (!graphics.circles) graphics.circles = []
|
|
113
|
+
if (!graphics.texts) graphics.texts = []
|
|
114
|
+
}
|
package/package.json
CHANGED
|
@@ -2,96 +2,99 @@
|
|
|
2
2
|
<rect width="100%" height="100%" fill="white" />
|
|
3
3
|
<g>
|
|
4
4
|
<circle data-type="point" data-label="J1.1
|
|
5
|
-
x+" data-x="1.1" data-y="0.2" cx="
|
|
5
|
+
x+" data-x="1.1" data-y="0.2" cx="301.5156017830609" cy="201.4588202080238" r="3" fill="hsl(218, 100%, 50%, 0.8)" />
|
|
6
6
|
</g>
|
|
7
7
|
<g>
|
|
8
8
|
<circle data-type="point" data-label="J1.2
|
|
9
|
-
x+" data-x="1.1" data-y="0" cx="
|
|
9
|
+
x+" data-x="1.1" data-y="0" cx="301.5156017830609" cy="225.2329658246657" r="3" fill="hsl(219, 100%, 50%, 0.8)" />
|
|
10
10
|
</g>
|
|
11
11
|
<g>
|
|
12
12
|
<circle data-type="point" data-label="J1.3
|
|
13
|
-
x+" data-x="1.1" data-y="-0.2" cx="
|
|
13
|
+
x+" data-x="1.1" data-y="-0.2" cx="301.5156017830609" cy="249.00711144130761" r="3" fill="hsl(220, 100%, 50%, 0.8)" />
|
|
14
14
|
</g>
|
|
15
15
|
<g>
|
|
16
16
|
<circle data-type="point" data-label="R1.1
|
|
17
|
-
x-" data-x="0.5499999999999996" data-y="-1.7944553499999996" cx="
|
|
17
|
+
x-" data-x="0.5499999999999996" data-y="-1.7944553499999996" cx="236.13670133729568" cy="438.5411797919762" r="3" fill="hsl(226, 100%, 50%, 0.8)" />
|
|
18
18
|
</g>
|
|
19
19
|
<g>
|
|
20
20
|
<circle data-type="point" data-label="R1.2
|
|
21
|
-
x+" data-x="1.6499999999999997" data-y="-1.7944553499999996" cx="
|
|
21
|
+
x+" data-x="1.6499999999999997" data-y="-1.7944553499999996" cx="366.8945022288261" cy="438.5411797919762" r="3" fill="hsl(227, 100%, 50%, 0.8)" />
|
|
22
22
|
</g>
|
|
23
23
|
<g>
|
|
24
24
|
<circle data-type="point" data-label="C1.1
|
|
25
|
-
x-" data-x="2.3099999999999996" data-y="0.01999999999999985" cx="
|
|
25
|
+
x-" data-x="2.3099999999999996" data-y="0.01999999999999985" cx="445.34918276374435" cy="222.85555126300153" r="3" fill="hsl(121, 100%, 50%, 0.8)" />
|
|
26
26
|
</g>
|
|
27
27
|
<g>
|
|
28
28
|
<circle data-type="point" data-label="C1.2
|
|
29
|
-
x+" data-x="3.41" data-y="0.01999999999999985" cx="
|
|
29
|
+
x+" data-x="3.41" data-y="0.01999999999999985" cx="576.1069836552749" cy="222.85555126300153" r="3" fill="hsl(122, 100%, 50%, 0.8)" />
|
|
30
30
|
</g>
|
|
31
31
|
<g>
|
|
32
32
|
<circle data-type="point" data-label="anchorPoint
|
|
33
|
-
orientation: y+" data-x="1.
|
|
33
|
+
orientation: y+" data-x="1.7049999999999998" data-y="0.2" cx="373.4323922734027" cy="201.4588202080238" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
|
|
34
34
|
</g>
|
|
35
35
|
<g>
|
|
36
36
|
<circle data-type="point" data-label="anchorPoint
|
|
37
|
-
orientation: x+" data-x="1.3" data-y="-0.4486138374999999" cx="
|
|
37
|
+
orientation: x+" data-x="1.3" data-y="-0.4486138374999999" cx="325.28974739970283" cy="278.5600193164933" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
|
|
38
38
|
</g>
|
|
39
39
|
<g>
|
|
40
40
|
<circle data-type="point" data-label="anchorPoint
|
|
41
|
-
orientation: y-" data-x="1.8499999999999996" data-y="-1.7944553499999996" cx="
|
|
41
|
+
orientation: y-" data-x="1.8499999999999996" data-y="-1.7944553499999996" cx="390.668647845468" cy="438.5411797919762" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
|
|
42
42
|
</g>
|
|
43
43
|
<g>
|
|
44
44
|
<circle data-type="point" data-label="anchorPoint
|
|
45
|
-
orientation:
|
|
45
|
+
orientation: y-" data-x="3.511" data-y="-0.4310000000000001" cx="588.1129271916791" cy="276.466249628529" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
|
|
46
46
|
</g>
|
|
47
47
|
<g>
|
|
48
|
-
<polyline data-points="1.1,0.2 2.3099999999999996,0.01999999999999985" data-type="line" data-label="" points="
|
|
48
|
+
<polyline data-points="1.1,0.2 2.3099999999999996,0.01999999999999985" data-type="line" data-label="" points="301.5156017830609,201.4588202080238 445.34918276374435,222.85555126300153" fill="none" stroke="hsl(190, 100%, 50%, 0.8)" stroke-width="1" stroke-dasharray="4 2" />
|
|
49
49
|
</g>
|
|
50
50
|
<g>
|
|
51
|
-
<polyline data-points="1.1,0 0.5499999999999996,-1.7944553499999996" data-type="line" data-label="" points="
|
|
51
|
+
<polyline data-points="1.1,0 0.5499999999999996,-1.7944553499999996" data-type="line" data-label="" points="301.5156017830609,225.2329658246657 236.13670133729568,438.5411797919762" fill="none" stroke="hsl(158, 100%, 50%, 0.8)" stroke-width="1" stroke-dasharray="4 2" />
|
|
52
52
|
</g>
|
|
53
53
|
<g>
|
|
54
|
-
<polyline data-points="1.1,-0.2 1.6499999999999997,-1.7944553499999996" data-type="line" data-label="" points="
|
|
54
|
+
<polyline data-points="1.1,-0.2 1.6499999999999997,-1.7944553499999996" data-type="line" data-label="" points="301.5156017830609,249.00711144130761 366.8945022288261,438.5411797919762" fill="none" stroke="hsl(157, 100%, 50%, 0.8)" stroke-width="1" stroke-dasharray="4 2" />
|
|
55
55
|
</g>
|
|
56
56
|
<g>
|
|
57
|
-
<polyline data-points="1.1,-0.2 3.41,0.01999999999999985" data-type="line" data-label="" points="
|
|
57
|
+
<polyline data-points="1.1,-0.2 3.41,0.01999999999999985" data-type="line" data-label="" points="301.5156017830609,249.00711144130761 576.1069836552749,222.85555126300153" fill="none" stroke="hsl(157, 100%, 50%, 0.8)" stroke-width="1" stroke-dasharray="4 2" />
|
|
58
58
|
</g>
|
|
59
59
|
<g>
|
|
60
|
-
<polyline data-points="1.6499999999999997,-1.7944553499999996 3.41,0.01999999999999985" data-type="line" data-label="" points="
|
|
60
|
+
<polyline data-points="1.6499999999999997,-1.7944553499999996 3.41,0.01999999999999985" data-type="line" data-label="" points="366.8945022288261,438.5411797919762 576.1069836552749,222.85555126300153" fill="none" stroke="hsl(157, 100%, 50%, 0.8)" stroke-width="1" stroke-dasharray="4 2" />
|
|
61
61
|
</g>
|
|
62
62
|
<g>
|
|
63
|
-
<polyline data-points="1.1,0.2 1.7049999999999998,0.2 1.7049999999999998,0.01999999999999985 2.3099999999999996,0.01999999999999985" data-type="line" data-label="" points="
|
|
63
|
+
<polyline data-points="1.1,0.2 1.7049999999999998,0.2 1.7049999999999998,0.01999999999999985 2.3099999999999996,0.01999999999999985" data-type="line" data-label="" points="301.5156017830609,201.4588202080238 373.4323922734027,201.4588202080238 373.4323922734027,222.85555126300153 445.34918276374435,222.85555126300153" fill="none" stroke="purple" stroke-width="1" />
|
|
64
64
|
</g>
|
|
65
65
|
<g>
|
|
66
|
-
<polyline data-points="1.1,0 1.3,0 1.3,-0.8972276749999998 0.34999999999999964,-0.8972276749999998 0.34999999999999964,-1.7944553499999996 0.5499999999999996,-1.7944553499999996" data-type="line" data-label="" points="
|
|
66
|
+
<polyline data-points="1.1,0 1.3,0 1.3,-0.8972276749999998 0.34999999999999964,-0.8972276749999998 0.34999999999999964,-1.7944553499999996 0.5499999999999996,-1.7944553499999996" data-type="line" data-label="" points="301.5156017830609,225.2329658246657 325.28974739970283,225.2329658246657 325.28974739970283,331.88707280832097 212.36255572065375,331.88707280832097 212.36255572065375,438.5411797919762 236.13670133729568,438.5411797919762" fill="none" stroke="purple" stroke-width="1" />
|
|
67
67
|
</g>
|
|
68
68
|
<g>
|
|
69
|
-
<polyline data-points="1.6499999999999997,-1.7944553499999996 1.8499999999999996,-1.7944553499999996 1.8499999999999996,-0.2 1.1,-0.2" data-type="line" data-label="" points="
|
|
69
|
+
<polyline data-points="1.6499999999999997,-1.7944553499999996 1.8499999999999996,-1.7944553499999996 1.8499999999999996,-0.2 1.1,-0.2" data-type="line" data-label="" points="366.8945022288261,438.5411797919762 390.668647845468,438.5411797919762 390.668647845468,249.00711144130761 301.5156017830609,249.00711144130761" fill="none" stroke="purple" stroke-width="1" />
|
|
70
70
|
</g>
|
|
71
71
|
<g>
|
|
72
|
-
<
|
|
72
|
+
<polyline data-points="3.41,0.01999999999999985 3.511,0.01999999999999985 3.511,-0.4310000000000001" data-type="line" data-label="" points="576.1069836552749,222.85555126300153 588.1129271916791,222.85555126300153 588.1129271916791,276.466249628529" fill="none" stroke="purple" stroke-width="1" />
|
|
73
73
|
</g>
|
|
74
74
|
<g>
|
|
75
|
-
<rect data-type="rect" data-label="
|
|
75
|
+
<rect data-type="rect" data-label="schematic_component_0" data-x="0" data-y="0" x="40.00000000000003" y="177.6846745913819" width="261.5156017830609" height="95.09658246656761" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.008412500000000002" />
|
|
76
76
|
</g>
|
|
77
77
|
<g>
|
|
78
|
-
<rect data-type="rect" data-label="
|
|
78
|
+
<rect data-type="rect" data-label="schematic_component_1" data-x="1.0999999999999996" data-y="-1.7944553499999996" x="236.13670133729568" y="415.42613075780093" width="130.75780089153045" height="46.230098068350514" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.008412500000000002" />
|
|
79
|
+
</g>
|
|
80
|
+
<g>
|
|
81
|
+
<rect data-type="rect" data-label="schematic_component_2" data-x="2.86" data-y="0.01999999999999985" x="445.34918276374435" y="172.92984546805354" width="130.75780089153056" height="99.85141158989597" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.008412500000000002" />
|
|
79
82
|
</g>
|
|
80
83
|
<g>
|
|
81
84
|
<rect data-type="rect" data-label="netId: VCC
|
|
82
|
-
globalConnNetId: connectivity_net0" data-x="1.
|
|
85
|
+
globalConnNetId: connectivity_net0" data-x="1.7049999999999998" data-y="0.42500000000000004" x="361.54531946508166" y="147.96699257057952" width="23.77414561664193" height="53.49182763744429" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.008412500000000002" />
|
|
83
86
|
</g>
|
|
84
87
|
<g>
|
|
85
88
|
<rect data-type="rect" data-label="netId: OUT
|
|
86
|
-
globalConnNetId: connectivity_net1" data-x="1.5250000000000001" data-y="-0.4486138374999999" x="
|
|
89
|
+
globalConnNetId: connectivity_net1" data-x="1.5250000000000001" data-y="-0.4486138374999999" x="325.28974739970283" y="266.6729465081724" width="53.49182763744432" height="23.774145616641874" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.008412500000000002" />
|
|
87
90
|
</g>
|
|
88
91
|
<g>
|
|
89
92
|
<rect data-type="rect" data-label="netId: GND
|
|
90
|
-
globalConnNetId: connectivity_net2" data-x="1.8499999999999996" data-y="-2.0194553499999994" x="
|
|
93
|
+
globalConnNetId: connectivity_net2" data-x="1.8499999999999996" data-y="-2.0194553499999994" x="378.78157503714704" y="438.54117979197616" width="23.77414561664193" height="53.49182763744432" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.008412500000000002" />
|
|
91
94
|
</g>
|
|
92
95
|
<g>
|
|
93
96
|
<rect data-type="rect" data-label="netId: GND
|
|
94
|
-
globalConnNetId: connectivity_net2" data-x="3.
|
|
97
|
+
globalConnNetId: connectivity_net2" data-x="3.511" data-y="-0.6560000000000001" x="576.2258543833581" y="276.466249628529" width="23.77414561664193" height="53.49182763744432" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.008412500000000002" />
|
|
95
98
|
</g>
|
|
96
99
|
<g id="crosshair" style="display: none">
|
|
97
100
|
<line id="crosshair-h" y1="0" y2="640" stroke="#666" stroke-width="0.5" />
|
|
@@ -121,12 +124,12 @@ globalConnNetId: connectivity_net2" data-x="3.636" data-y="0.01999999999999985"
|
|
|
121
124
|
|
|
122
125
|
// Calculate real coordinates using inverse transformation
|
|
123
126
|
const matrix = {
|
|
124
|
-
"a":
|
|
127
|
+
"a": 118.8707280832095,
|
|
125
128
|
"c": 0,
|
|
126
|
-
"e":
|
|
129
|
+
"e": 170.75780089153048,
|
|
127
130
|
"b": 0,
|
|
128
|
-
"d": -
|
|
129
|
-
"f":
|
|
131
|
+
"d": -118.8707280832095,
|
|
132
|
+
"f": 225.2329658246657
|
|
130
133
|
};
|
|
131
134
|
// Manually invert and apply the affine transform
|
|
132
135
|
// Since we only use translate and scale, we can directly compute:
|