@tscircuit/schematic-trace-solver 0.0.61 → 0.0.63

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.
@@ -0,0 +1,449 @@
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 netLabelHeightOf(label: NetLabelPlacement): number | undefined {
174
+ if (label.orientation === "x+" || label.orientation === "x-")
175
+ return label.height
176
+ return label.width
177
+ }
178
+
179
+ private buildCandidatesForLabel(label: NetLabelPlacement): Candidate[] {
180
+ const netLabelWidth = this.netLabelWidthOf(label)
181
+ const netLabelHeight = this.netLabelHeightOf(label)
182
+ const candidates: Candidate[] = []
183
+
184
+ const buildCandidate = (
185
+ orientation: FacingDirection,
186
+ anchor: { x: number; y: number },
187
+ hostPairId?: MspConnectionPairId,
188
+ hostSegIndex?: number,
189
+ ): Candidate => {
190
+ const { width, height } = getDimsForOrientation({
191
+ orientation,
192
+ netLabelWidth,
193
+ netLabelHeight,
194
+ })
195
+ const baseCenter = getCenterFromAnchor(anchor, orientation, width, height)
196
+ const outwardDir = OUTWARD_DIR[orientation]
197
+ const center = {
198
+ x: baseCenter.x + outwardDir.x * ANCHOR_TRACE_CLEARANCE,
199
+ y: baseCenter.y + outwardDir.y * ANCHOR_TRACE_CLEARANCE,
200
+ }
201
+ return {
202
+ orientation,
203
+ anchor,
204
+ center,
205
+ width,
206
+ height,
207
+ bounds: getRectBounds(center, width, height),
208
+ hostPairId,
209
+ hostSegIndex,
210
+ status: null,
211
+ }
212
+ }
213
+
214
+ const isPortOnly = label.mspConnectionPairIds.length === 0
215
+
216
+ if (isPortOnly) {
217
+ const allOrientations: FacingDirection[] = ["x+", "x-", "y+", "y-"]
218
+ const orderedOrientations = [
219
+ label.orientation,
220
+ ...allOrientations.filter((o) => o !== label.orientation),
221
+ ]
222
+ for (const orientation of orderedOrientations) {
223
+ candidates.push(buildCandidate(orientation, label.anchorPoint))
224
+ }
225
+ } else {
226
+ for (const mspPairId of label.mspConnectionPairIds) {
227
+ const trace = this.traceMap[mspPairId]
228
+ if (!trace) continue
229
+ const pts = trace.tracePath
230
+ for (let si = 0; si < pts.length - 1; si++) {
231
+ const segStart = pts[si]!
232
+ const segEnd = pts[si + 1]!
233
+ const isHorizontal =
234
+ Math.abs(segStart.y - segEnd.y) < SEGMENT_PARALLEL_EPS
235
+ const isVertical =
236
+ Math.abs(segStart.x - segEnd.x) < SEGMENT_PARALLEL_EPS
237
+ if (!isHorizontal && !isVertical) continue
238
+ let perpendicularOrientations: FacingDirection[]
239
+ if (isHorizontal) {
240
+ perpendicularOrientations = ["y+", "y-"]
241
+ } else {
242
+ perpendicularOrientations = ["x+", "x-"]
243
+ }
244
+ for (const anchor of sampleAnchorsAlongSegment(segStart, segEnd)) {
245
+ for (const orientation of perpendicularOrientations) {
246
+ candidates.push(
247
+ buildCandidate(
248
+ orientation,
249
+ anchor,
250
+ mspPairId as MspConnectionPairId,
251
+ si,
252
+ ),
253
+ )
254
+ }
255
+ }
256
+ }
257
+ }
258
+ }
259
+
260
+ return candidates
261
+ }
262
+
263
+ private checkCandidate(
264
+ candidate: Candidate,
265
+ movingLabelNetId: string,
266
+ obstacleLabels: NetLabelPlacement[],
267
+ ): CandidateStatus {
268
+ const { bounds, hostPairId, hostSegIndex } = candidate
269
+
270
+ if (this.chipIndex.getChipsInBounds(bounds).length > 0)
271
+ return "chip-collision"
272
+
273
+ if (
274
+ rectIntersectsAnyTrace(bounds, this.traceMap, hostPairId, hostSegIndex)
275
+ .hasIntersection
276
+ ) {
277
+ return "trace-collision"
278
+ }
279
+
280
+ for (const obstacle of obstacleLabels) {
281
+ if (obstacle.globalConnNetId === movingLabelNetId) continue
282
+ if (boundsOverlap(bounds, this.labelBounds(obstacle)))
283
+ return "label-collision"
284
+ }
285
+
286
+ return "ok"
287
+ }
288
+
289
+ private beginSearchForLabel(label: NetLabelPlacement) {
290
+ this.currentLabelToMove = label
291
+ this.candidateQueue = this.buildCandidatesForLabel(label)
292
+ this.candidateIndex = 0
293
+ this.candidateResults = []
294
+ }
295
+
296
+ private clearActiveSearch() {
297
+ this.currentCollision = null
298
+ this.currentLabelToMove = null
299
+ this.labelsToTry = []
300
+ this.candidateQueue = []
301
+ this.candidateIndex = 0
302
+ this.candidateResults = []
303
+ }
304
+
305
+ override _step() {
306
+ if (!this.currentCollision) {
307
+ const pair = this.findNextCollidingPair()
308
+ if (!pair) {
309
+ this.solved = true
310
+ return
311
+ }
312
+ this.currentCollision = pair
313
+ this.labelsToTry = [pair[1], pair[0]]
314
+ this.beginSearchForLabel(this.labelsToTry.shift()!)
315
+ return
316
+ }
317
+
318
+ if (this.candidateIndex >= this.candidateQueue.length) {
319
+ if (this.labelsToTry.length > 0) {
320
+ this.beginSearchForLabel(this.labelsToTry.shift()!)
321
+ } else {
322
+ this.skippedCollisionKeys.add(
323
+ this.collisionKey(this.currentCollision[0], this.currentCollision[1]),
324
+ )
325
+ this.clearActiveSearch()
326
+ }
327
+ return
328
+ }
329
+
330
+ const candidate = this.candidateQueue[this.candidateIndex++]!
331
+ const [labelA, labelB] = this.currentCollision
332
+ let fixedLabel: NetLabelPlacement
333
+ if (this.currentLabelToMove === labelB) {
334
+ fixedLabel = labelA
335
+ } else {
336
+ fixedLabel = labelB
337
+ }
338
+ const obstacleLabels = [
339
+ ...this.outputNetLabelPlacements.filter(
340
+ (l) => l !== labelA && l !== labelB,
341
+ ),
342
+ fixedLabel,
343
+ ]
344
+
345
+ const status = this.checkCandidate(
346
+ candidate,
347
+ this.currentLabelToMove!.globalConnNetId,
348
+ obstacleLabels,
349
+ )
350
+ candidate.status = status
351
+ this.candidateResults.push({ ...candidate })
352
+
353
+ if (status === "ok") {
354
+ const idx = this.outputNetLabelPlacements.indexOf(
355
+ this.currentLabelToMove!,
356
+ )
357
+ if (idx !== -1) {
358
+ this.outputNetLabelPlacements[idx] = {
359
+ ...this.currentLabelToMove!,
360
+ orientation: candidate.orientation,
361
+ anchorPoint: candidate.anchor,
362
+ width: candidate.width,
363
+ height: candidate.height,
364
+ center: candidate.center,
365
+ }
366
+ }
367
+ this.clearActiveSearch()
368
+ }
369
+ }
370
+
371
+ override visualize(): GraphicsObject {
372
+ const graphics = visualizeInputProblem(this.inputProblem)
373
+ if (!graphics.lines) graphics.lines = []
374
+ if (!graphics.rects) graphics.rects = []
375
+ if (!graphics.points) graphics.points = []
376
+
377
+ for (const trace of this.traces) {
378
+ graphics.lines.push({
379
+ points: trace.tracePath,
380
+ strokeColor: "purple",
381
+ } as any)
382
+ }
383
+
384
+ for (const label of this.outputNetLabelPlacements) {
385
+ const isInActiveCollision =
386
+ this.currentCollision != null &&
387
+ (label === this.currentCollision[0] ||
388
+ label === this.currentCollision[1])
389
+
390
+ let labelFill: string
391
+ let labelStroke: string
392
+ let labelText: string
393
+ let pointColor: string
394
+ if (isInActiveCollision) {
395
+ labelFill = "rgba(255, 0, 0, 0.2)"
396
+ labelStroke = "red"
397
+ labelText = `netId: ${label.netId}\nglobalConnNetId: ${label.globalConnNetId}\n⚠ COLLIDING`
398
+ pointColor = "red"
399
+ } else {
400
+ labelFill = getColorFromString(label.globalConnNetId, 0.35)
401
+ labelStroke = getColorFromString(label.globalConnNetId, 0.9)
402
+ labelText = `netId: ${label.netId}\nglobalConnNetId: ${label.globalConnNetId}`
403
+ pointColor = getColorFromString(label.globalConnNetId, 0.9)
404
+ }
405
+
406
+ graphics.rects.push({
407
+ center: label.center,
408
+ width: label.width,
409
+ height: label.height,
410
+ fill: labelFill,
411
+ strokeColor: labelStroke,
412
+ label: labelText,
413
+ } as any)
414
+ graphics.points.push({
415
+ x: label.anchorPoint.x,
416
+ y: label.anchorPoint.y,
417
+ color: pointColor,
418
+ label: `anchorPoint\norientation: ${label.orientation}`,
419
+ } as any)
420
+ }
421
+
422
+ const movingNetId = this.currentLabelToMove
423
+ ? this.currentLabelToMove.netId
424
+ : "?"
425
+ for (const c of this.candidateResults) {
426
+ const statusColor = CANDIDATE_STATUS_COLOR[c.status!]
427
+ const statusFill = CANDIDATE_STATUS_FILL[c.status!]
428
+ let strokeDash: string | undefined
429
+ if (c.status !== "ok") strokeDash = "4 2"
430
+ graphics.rects.push({
431
+ center: c.center,
432
+ width: c.width,
433
+ height: c.height,
434
+ fill: statusFill,
435
+ strokeColor: statusColor,
436
+ strokeDash,
437
+ label: `candidate: ${c.status}\norientation: ${c.orientation}\nmoving: ${movingNetId}`,
438
+ } as any)
439
+ graphics.points.push({
440
+ x: c.anchor.x,
441
+ y: c.anchor.y,
442
+ color: statusColor,
443
+ label: `candidate anchor\n${c.status}`,
444
+ } as any)
445
+ }
446
+
447
+ return graphics
448
+ }
449
+ }
@@ -280,6 +280,26 @@ export class NetLabelPlacementSolver extends BaseSolver {
280
280
  )?.netLabelWidth
281
281
  }
282
282
 
283
+ private getNetLabelHeightForGroup(
284
+ group: OverlappingSameNetTraceGroup,
285
+ ): number | undefined {
286
+ if (group.netId) {
287
+ const ncHeight = this.inputProblem.netConnections.find(
288
+ (nc) => nc.netId === group.netId,
289
+ )?.netLabelHeight
290
+ if (ncHeight !== undefined) return ncHeight
291
+ }
292
+
293
+ const pinIds = group.overlappingTraces?.pins.map((p) => p.pinId) ?? []
294
+ if (group.portOnlyPinId) {
295
+ pinIds.push(group.portOnlyPinId)
296
+ }
297
+
298
+ return this.inputProblem.netConnections.find((nc) =>
299
+ nc.pinIds.some((pid) => pinIds.includes(pid)),
300
+ )?.netLabelHeight
301
+ }
302
+
283
303
  override _step() {
284
304
  if (this.activeSubSolver?.solved) {
285
305
  this.netLabelPlacements.push(this.activeSubSolver.netLabelPlacement!)
@@ -304,12 +324,14 @@ export class NetLabelPlacementSolver extends BaseSolver {
304
324
  ) {
305
325
  this.triedAnyOrientationFallbackForCurrentGroup = true
306
326
  const netLabelWidth = this.getNetLabelWidthForGroup(this.currentGroup)
327
+ const netLabelHeight = this.getNetLabelHeightForGroup(this.currentGroup)
307
328
  this.activeSubSolver = new SingleNetLabelPlacementSolver({
308
329
  inputProblem: this.inputProblem,
309
330
  inputTraceMap: this.inputTraceMap,
310
331
  overlappingSameNetTraceGroup: this.currentGroup,
311
332
  availableOrientations: fullOrients,
312
333
  netLabelWidth,
334
+ netLabelHeight,
313
335
  })
314
336
  return
315
337
  }
@@ -340,6 +362,7 @@ export class NetLabelPlacementSolver extends BaseSolver {
340
362
  this.triedAnyOrientationFallbackForCurrentGroup = false
341
363
 
342
364
  const netLabelWidth = this.getNetLabelWidthForGroup(this.currentGroup)
365
+ const netLabelHeight = this.getNetLabelHeightForGroup(this.currentGroup)
343
366
 
344
367
  this.activeSubSolver = new SingleNetLabelPlacementSolver({
345
368
  inputProblem: this.inputProblem,
@@ -349,6 +372,7 @@ export class NetLabelPlacementSolver extends BaseSolver {
349
372
  netId
350
373
  ] ?? ["x+", "x-", "y+", "y-"],
351
374
  netLabelWidth,
375
+ netLabelHeight,
352
376
  })
353
377
  }
354
378
 
@@ -58,8 +58,9 @@ export class SingleNetLabelPlacementSolver extends BaseSolver {
58
58
 
59
59
  chipObstacleSpatialIndex: ChipObstacleSpatialIndex
60
60
 
61
- // Optional override for the width of the net label (per netId)
61
+ // Optional override for the width/height of the net label (per netId)
62
62
  netLabelWidth?: number
63
+ netLabelHeight?: number
63
64
 
64
65
  netLabelPlacement: NetLabelPlacement | null = null
65
66
  testedCandidates: Array<{
@@ -79,6 +80,7 @@ export class SingleNetLabelPlacementSolver extends BaseSolver {
79
80
  overlappingSameNetTraceGroup: OverlappingSameNetTraceGroup
80
81
  availableOrientations: FacingDirection[]
81
82
  netLabelWidth?: number
83
+ netLabelHeight?: number
82
84
  }) {
83
85
  super()
84
86
  this.inputProblem = params.inputProblem
@@ -86,6 +88,7 @@ export class SingleNetLabelPlacementSolver extends BaseSolver {
86
88
  this.overlappingSameNetTraceGroup = params.overlappingSameNetTraceGroup
87
89
  this.availableOrientations = params.availableOrientations
88
90
  this.netLabelWidth = params.netLabelWidth
91
+ this.netLabelHeight = params.netLabelHeight
89
92
 
90
93
  this.chipObstacleSpatialIndex =
91
94
  params.inputProblem._chipObstacleSpatialIndex ??
@@ -101,6 +104,7 @@ export class SingleNetLabelPlacementSolver extends BaseSolver {
101
104
  overlappingSameNetTraceGroup: this.overlappingSameNetTraceGroup,
102
105
  availableOrientations: this.availableOrientations,
103
106
  netLabelWidth: this.netLabelWidth,
107
+ netLabelHeight: this.netLabelHeight,
104
108
  }
105
109
  }
106
110
 
@@ -119,6 +123,7 @@ export class SingleNetLabelPlacementSolver extends BaseSolver {
119
123
  overlappingSameNetTraceGroup: this.overlappingSameNetTraceGroup,
120
124
  availableOrientations: this.availableOrientations,
121
125
  netLabelWidth: this.netLabelWidth,
126
+ netLabelHeight: this.netLabelHeight,
122
127
  })
123
128
  this.testedCandidates.push(...res.testedCandidates)
124
129
  if (res.placement) {
@@ -230,6 +235,7 @@ export class SingleNetLabelPlacementSolver extends BaseSolver {
230
235
  const { width, height } = getDimsForOrientation({
231
236
  orientation,
232
237
  netLabelWidth: this.netLabelWidth,
238
+ netLabelHeight: this.netLabelHeight,
233
239
  })
234
240
  const center = getCenterFromAnchor(
235
241
  anchor,
@@ -6,23 +6,28 @@ export const NET_LABEL_HORIZONTAL_HEIGHT = 0.2
6
6
  export function getDimsForOrientation(params: {
7
7
  orientation: FacingDirection
8
8
  netLabelWidth?: number
9
+ netLabelHeight?: number
9
10
  }) {
10
- const { orientation, netLabelWidth } = params
11
+ const { orientation, netLabelWidth, netLabelHeight } = params
11
12
  const horizWidth =
12
13
  typeof netLabelWidth === "number"
13
14
  ? netLabelWidth
14
15
  : NET_LABEL_HORIZONTAL_WIDTH
16
+ const horizHeight =
17
+ typeof netLabelHeight === "number"
18
+ ? netLabelHeight
19
+ : NET_LABEL_HORIZONTAL_HEIGHT
15
20
 
16
21
  if (orientation === "y+" || orientation === "y-") {
17
22
  return {
18
- // Rotated, so width/height swap
19
- width: NET_LABEL_HORIZONTAL_HEIGHT,
23
+ // Rotated: horizontal length = netLabelHeight, vertical length = netLabelWidth
24
+ width: horizHeight,
20
25
  height: horizWidth,
21
26
  }
22
27
  }
23
28
  return {
24
29
  width: horizWidth,
25
- height: NET_LABEL_HORIZONTAL_HEIGHT,
30
+ height: horizHeight,
26
31
  }
27
32
  }
28
33
 
@@ -22,6 +22,7 @@ export function solveNetLabelPlacementForPortOnlyPin(params: {
22
22
  overlappingSameNetTraceGroup: OverlappingSameNetTraceGroup
23
23
  availableOrientations: FacingDirection[]
24
24
  netLabelWidth?: number
25
+ netLabelHeight?: number
25
26
  }): {
26
27
  placement: NetLabelPlacement | null
27
28
  testedCandidates: Array<{
@@ -43,6 +44,7 @@ export function solveNetLabelPlacementForPortOnlyPin(params: {
43
44
  overlappingSameNetTraceGroup,
44
45
  availableOrientations,
45
46
  netLabelWidth,
47
+ netLabelHeight,
46
48
  } = params
47
49
 
48
50
  const pinId = overlappingSameNetTraceGroup.portOnlyPinId
@@ -103,6 +105,7 @@ export function solveNetLabelPlacementForPortOnlyPin(params: {
103
105
  const { width, height } = getDimsForOrientation({
104
106
  orientation,
105
107
  netLabelWidth,
108
+ netLabelHeight,
106
109
  })
107
110
  // Place label fully outside the chip: shift center slightly outward
108
111
  const baseCenter = getCenterFromAnchor(anchor, orientation, width, height)
@@ -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) {