@tscircuit/schematic-trace-solver 0.0.158 → 0.0.160
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 +41 -9
- package/dist/index.js +762 -110
- package/lib/solvers/InlineNetLabelSolver/InlineNetLabelSolver.ts +536 -94
- package/lib/solvers/InlineNetLabelSolver/pushAnchoredNetLabelsAwayFromInlineLabels.ts +9 -7
- package/lib/solvers/InlineNetLabelSolver/pushInlineTerminalLabelsAwayFromAnchoredLabels.ts +295 -0
- package/lib/solvers/LongDistancePairSolver/LongDistancePairSolver.ts +15 -8
- package/lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver.ts +5 -0
- package/lib/solvers/MspConnectionPairSolver/getGroundConnectionPolicy.ts +83 -0
- package/lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver.ts +7 -35
- package/lib/solvers/SameNetJunctionAlignmentSolver/SameNetJunctionAlignmentSolver.ts +6 -1
- package/lib/solvers/SameNetJunctionAlignmentSolver/placeGroundRailLabelsAtOuterEnd.ts +122 -0
- package/lib/solvers/SchematicTraceLinesSolver/getTraceConnectedPinComponents.ts +60 -0
- package/lib/types/InputProblem.ts +5 -4
- package/package.json +1 -1
- package/tests/bug-reports/bug-report-20260826T072956Z/__snapshots__/bug-report-20260826T072956Z.snap.svg +600 -783
- package/tests/bug-reports/bug-report-20260826T072956Z/bug-report-20260826T072956Z.test.ts +66 -1
- package/tests/fixtures/parallel-ground-rail.ts +53 -0
- package/tests/repros/__snapshots__/repro-mspm0l1306-capacitor-symmetry.snap.svg +34 -16
- package/tests/repros/repro-mspm0l1306-capacitor-symmetry.test.ts +40 -7
- package/tests/repros/repro-usb-power-vbus-label-detour.test.ts +14 -0
- package/tests/solvers/InlineNetLabelSolver/multi-pin-connected-components.test.ts +252 -0
- package/tests/solvers/InlineNetLabelSolver/opposite-side-placement.test.ts +71 -0
- package/tests/solvers/InlineNetLabelSolver/push-anchored-net-labels-away.test.ts +8 -2
- package/tests/solvers/InlineNetLabelSolver/push-inline-terminal-labels-away.test.ts +91 -0
- package/tests/solvers/InlineNetLabelSolver/two-pin-terminal-stubs-atomic.test.ts +7 -1
- package/tests/solvers/MspConnectionPairSolver/local-ground-branches.test.ts +105 -0
- package/tests/solvers/SameNetJunctionAlignmentSolver/ground-rail-label.test.ts +188 -0
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import type { Bounds, Point } from "@tscircuit/math-utils"
|
|
2
2
|
import type { GraphicsObject, Rect, Text } from "graphics-debug"
|
|
3
|
+
import type { ConnectivityMap } from "connectivity-map"
|
|
3
4
|
import { BaseSolver } from "lib/solvers/BaseSolver/BaseSolver"
|
|
5
|
+
import { getConnectivityMapsFromInputProblem } from "lib/solvers/MspConnectionPairSolver/getConnectivityMapFromInputProblem"
|
|
4
6
|
import type { NetLabelPlacement } from "lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver"
|
|
7
|
+
import { getTraceConnectedPinComponents } from "lib/solvers/SchematicTraceLinesSolver/getTraceConnectedPinComponents"
|
|
5
8
|
import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
|
|
6
9
|
import { getPinDirection } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/getPinDirection"
|
|
7
10
|
import { visualizeInputProblem } from "lib/solvers/SchematicTracePipelineSolver/visualizeInputProblem"
|
|
@@ -19,6 +22,7 @@ import {
|
|
|
19
22
|
getAxisAlignedSegments,
|
|
20
23
|
} from "./getAxisAlignedSegments"
|
|
21
24
|
import { pushAnchoredNetLabelsAwayFromInlineLabels } from "./pushAnchoredNetLabelsAwayFromInlineLabels"
|
|
25
|
+
import { pushInlineTerminalLabelsAwayFromAnchoredLabels } from "./pushInlineTerminalLabelsAwayFromAnchoredLabels"
|
|
22
26
|
|
|
23
27
|
export const DEFAULT_INLINE_NET_LABEL_HEIGHT = 0.18
|
|
24
28
|
|
|
@@ -101,13 +105,68 @@ export interface InlineNetLabelOutput {
|
|
|
101
105
|
|
|
102
106
|
type InlineEligibleConnection = InputDirectConnection | InputNetConnection
|
|
103
107
|
|
|
108
|
+
type QueuedInlineConnection =
|
|
109
|
+
| { kind: "direct_connection"; connection: InputDirectConnection }
|
|
110
|
+
| { kind: "net_connection"; connection: InputNetConnection }
|
|
111
|
+
|
|
112
|
+
interface NetConnectionInlineConversion {
|
|
113
|
+
globalConnNetId: string
|
|
114
|
+
placements: InlineNetLabelPlacement[]
|
|
115
|
+
supersededTraceIds: Set<string>
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
interface NetConnectionComponent {
|
|
119
|
+
pinIds: PinId[]
|
|
120
|
+
labeledPinIds: PinId[]
|
|
121
|
+
traces: SolvedTracePath[]
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const AVAILABLE_NET_ORIENTATION_TRACE_PREFIX = "available-net-orientation-"
|
|
125
|
+
|
|
126
|
+
const isGeneratedAnchoredLabelConnector = (trace: SolvedTracePath) =>
|
|
127
|
+
trace.mspPairId.startsWith(AVAILABLE_NET_ORIENTATION_TRACE_PREFIX)
|
|
128
|
+
|
|
104
129
|
const getPinPairKey = (pinIds: readonly string[]) =>
|
|
105
130
|
[...pinIds].sort().join("::")
|
|
106
131
|
|
|
132
|
+
const getTraceLength = (trace: SolvedTracePath) => {
|
|
133
|
+
let length = 0
|
|
134
|
+
for (let index = 0; index < trace.tracePath.length - 1; index++) {
|
|
135
|
+
const start = trace.tracePath[index]!
|
|
136
|
+
const end = trace.tracePath[index + 1]!
|
|
137
|
+
length += Math.abs(end.x - start.x) + Math.abs(end.y - start.y)
|
|
138
|
+
}
|
|
139
|
+
return length
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const setsEqual = <T>(first: Set<T>, second: Set<T>) =>
|
|
143
|
+
first.size === second.size && [...first].every((value) => second.has(value))
|
|
144
|
+
|
|
145
|
+
const getInlinePlacementBounds = (
|
|
146
|
+
placement: InlineNetLabelPlacement,
|
|
147
|
+
): Bounds => {
|
|
148
|
+
const renderedWidth =
|
|
149
|
+
placement.axis === "y" ? placement.height : placement.width
|
|
150
|
+
const renderedHeight =
|
|
151
|
+
placement.axis === "y" ? placement.width : placement.height
|
|
152
|
+
return {
|
|
153
|
+
minX: placement.center.x - renderedWidth / 2,
|
|
154
|
+
maxX: placement.center.x + renderedWidth / 2,
|
|
155
|
+
minY: placement.center.y - renderedHeight / 2,
|
|
156
|
+
maxY: placement.center.y + renderedHeight / 2,
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const getAnchoredPlacementBounds = (placement: NetLabelPlacement): Bounds => ({
|
|
161
|
+
minX: placement.center.x - placement.width / 2,
|
|
162
|
+
maxX: placement.center.x + placement.width / 2,
|
|
163
|
+
minY: placement.center.y - placement.height / 2,
|
|
164
|
+
maxY: placement.center.y + placement.height / 2,
|
|
165
|
+
})
|
|
166
|
+
|
|
107
167
|
/**
|
|
108
168
|
* Places "inline net labels" - net names drawn alongside the trace they belong
|
|
109
|
-
* to - for
|
|
110
|
-
* `allowInlineNetLabel`.
|
|
169
|
+
* to - for connections that opted in via `allowInlineNetLabel`.
|
|
111
170
|
*
|
|
112
171
|
* Any regular (anchored) net label placement for the same net is dropped, so a
|
|
113
172
|
* net is never labeled twice.
|
|
@@ -119,10 +178,12 @@ export class InlineNetLabelSolver extends BaseSolver {
|
|
|
119
178
|
|
|
120
179
|
inlineNetLabelPlacements: InlineNetLabelPlacement[] = []
|
|
121
180
|
|
|
122
|
-
/** Eligible
|
|
123
|
-
queuedConnections:
|
|
181
|
+
/** Eligible connections still waiting to be processed. */
|
|
182
|
+
queuedConnections: QueuedInlineConnection[]
|
|
124
183
|
|
|
125
184
|
private tracesByPinPairKey: Map<string, SolvedTracePath[]>
|
|
185
|
+
private globalConnMap: ConnectivityMap
|
|
186
|
+
private supersededTraceIdsByGlobalConnNetId = new Map<string, Set<string>>()
|
|
126
187
|
private hasAlignedPortOnlyStubs = false
|
|
127
188
|
private postProcessedOutput?: {
|
|
128
189
|
traces: SolvedTracePath[]
|
|
@@ -135,22 +196,35 @@ export class InlineNetLabelSolver extends BaseSolver {
|
|
|
135
196
|
this.inputProblem = input.inputProblem
|
|
136
197
|
this.traces = input.traces
|
|
137
198
|
this.inputNetLabelPlacements = input.netLabelPlacements
|
|
199
|
+
this.globalConnMap = getConnectivityMapsFromInputProblem(
|
|
200
|
+
this.inputProblem,
|
|
201
|
+
).netConnMap
|
|
138
202
|
|
|
139
203
|
this.queuedConnections = [
|
|
140
|
-
...this.inputProblem.directConnections
|
|
141
|
-
(
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
(connection) =>
|
|
145
|
-
|
|
146
|
-
connection
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
204
|
+
...this.inputProblem.directConnections
|
|
205
|
+
.filter(
|
|
206
|
+
(connection) => connection.allowInlineNetLabel && connection.netId,
|
|
207
|
+
)
|
|
208
|
+
.map((connection) => ({
|
|
209
|
+
kind: "direct_connection" as const,
|
|
210
|
+
connection,
|
|
211
|
+
})),
|
|
212
|
+
...this.inputProblem.netConnections
|
|
213
|
+
.filter(
|
|
214
|
+
(connection) =>
|
|
215
|
+
connection.allowInlineNetLabel &&
|
|
216
|
+
connection.pinIds.length >= 1 &&
|
|
217
|
+
connection.netId,
|
|
218
|
+
)
|
|
219
|
+
.map((connection) => ({
|
|
220
|
+
kind: "net_connection" as const,
|
|
221
|
+
connection,
|
|
222
|
+
})),
|
|
150
223
|
]
|
|
151
224
|
|
|
152
225
|
this.tracesByPinPairKey = new Map()
|
|
153
226
|
for (const trace of this.traces) {
|
|
227
|
+
if (isGeneratedAnchoredLabelConnector(trace)) continue
|
|
154
228
|
const key = getPinPairKey(trace.pins.map((p) => p.pinId))
|
|
155
229
|
const existing = this.tracesByPinPairKey.get(key)
|
|
156
230
|
if (existing) {
|
|
@@ -172,8 +246,53 @@ export class InlineNetLabelSolver extends BaseSolver {
|
|
|
172
246
|
}
|
|
173
247
|
|
|
174
248
|
override _step() {
|
|
175
|
-
const
|
|
176
|
-
if (
|
|
249
|
+
const queuedConnection = this.queuedConnections.shift()
|
|
250
|
+
if (queuedConnection) {
|
|
251
|
+
if (
|
|
252
|
+
queuedConnection.kind === "net_connection" &&
|
|
253
|
+
queuedConnection.connection.pinIds.length > 2
|
|
254
|
+
) {
|
|
255
|
+
const netConnections = [
|
|
256
|
+
queuedConnection.connection,
|
|
257
|
+
...this.queuedConnections
|
|
258
|
+
.filter(
|
|
259
|
+
(
|
|
260
|
+
queued,
|
|
261
|
+
): queued is Extract<
|
|
262
|
+
QueuedInlineConnection,
|
|
263
|
+
{ kind: "net_connection" }
|
|
264
|
+
> =>
|
|
265
|
+
queued.kind === "net_connection" &&
|
|
266
|
+
queued.connection.pinIds.length > 2,
|
|
267
|
+
)
|
|
268
|
+
.map((queued) => queued.connection),
|
|
269
|
+
]
|
|
270
|
+
this.queuedConnections = this.queuedConnections.filter(
|
|
271
|
+
(queued) =>
|
|
272
|
+
queued.kind !== "net_connection" ||
|
|
273
|
+
queued.connection.pinIds.length <= 2,
|
|
274
|
+
)
|
|
275
|
+
for (const conversion of this.computeNetConnectionInlineConversions(
|
|
276
|
+
netConnections,
|
|
277
|
+
)) {
|
|
278
|
+
this.inlineNetLabelPlacements.push(...conversion.placements)
|
|
279
|
+
const supersededTraceIds =
|
|
280
|
+
this.supersededTraceIdsByGlobalConnNetId.get(
|
|
281
|
+
conversion.globalConnNetId,
|
|
282
|
+
) ?? new Set<string>()
|
|
283
|
+
for (const traceId of conversion.supersededTraceIds) {
|
|
284
|
+
supersededTraceIds.add(traceId)
|
|
285
|
+
}
|
|
286
|
+
this.supersededTraceIdsByGlobalConnNetId.set(
|
|
287
|
+
conversion.globalConnNetId,
|
|
288
|
+
supersededTraceIds,
|
|
289
|
+
)
|
|
290
|
+
}
|
|
291
|
+
return
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const { connection } = queuedConnection
|
|
295
|
+
|
|
177
296
|
const routedTraces =
|
|
178
297
|
connection.pinIds.length === 2
|
|
179
298
|
? (this.tracesByPinPairKey.get(getPinPairKey(connection.pinIds)) ??
|
|
@@ -181,7 +300,11 @@ export class InlineNetLabelSolver extends BaseSolver {
|
|
|
181
300
|
: []
|
|
182
301
|
|
|
183
302
|
if (routedTraces.length > 0) {
|
|
184
|
-
const placement = this.computeInlinePlacement(
|
|
303
|
+
const placement = this.computeInlinePlacement({
|
|
304
|
+
connection,
|
|
305
|
+
trace: routedTraces[0]!,
|
|
306
|
+
pinIds: connection.pinIds,
|
|
307
|
+
})
|
|
185
308
|
if (placement) this.inlineNetLabelPlacements.push(placement)
|
|
186
309
|
} else {
|
|
187
310
|
// A one-pin net has one conventional endpoint label, while a skipped
|
|
@@ -218,6 +341,215 @@ export class InlineNetLabelSolver extends BaseSolver {
|
|
|
218
341
|
this.stats.inlineNetLabelCount = this.inlineNetLabelPlacements.length
|
|
219
342
|
}
|
|
220
343
|
|
|
344
|
+
/**
|
|
345
|
+
* Plans all opted-in multi-pin nets together. Short routed components can
|
|
346
|
+
* mutually block the outward endpoint stubs that replace them, so tentative
|
|
347
|
+
* replacements are ignored as obstacles only while their owning net still
|
|
348
|
+
* converts successfully. Failed conversions retain both their traces and
|
|
349
|
+
* their conventional labels, and the remaining conversions are retried.
|
|
350
|
+
*/
|
|
351
|
+
private computeNetConnectionInlineConversions(
|
|
352
|
+
connections: InputNetConnection[],
|
|
353
|
+
): NetConnectionInlineConversion[] {
|
|
354
|
+
const prospectiveTraceIdsByConnection = new Map<
|
|
355
|
+
InputNetConnection,
|
|
356
|
+
Set<string>
|
|
357
|
+
>()
|
|
358
|
+
for (const connection of connections) {
|
|
359
|
+
const prospectiveTraceIds = new Set<string>()
|
|
360
|
+
for (const component of this.getNetConnectionComponents(connection)) {
|
|
361
|
+
if (component.traces.length === 0) continue
|
|
362
|
+
const hasInlinePlacement = component.traces.some((trace) =>
|
|
363
|
+
Boolean(
|
|
364
|
+
this.computeInlinePlacement({
|
|
365
|
+
connection,
|
|
366
|
+
trace,
|
|
367
|
+
pinIds: component.pinIds,
|
|
368
|
+
}),
|
|
369
|
+
),
|
|
370
|
+
)
|
|
371
|
+
if (!hasInlinePlacement) {
|
|
372
|
+
for (const trace of component.traces) {
|
|
373
|
+
prospectiveTraceIds.add(trace.mspPairId)
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
prospectiveTraceIdsByConnection.set(connection, prospectiveTraceIds)
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
let ignoredTraceIds = new Set(
|
|
381
|
+
[...prospectiveTraceIdsByConnection.values()].flatMap((ids) => [...ids]),
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
while (true) {
|
|
385
|
+
const conversions = connections.map((connection) => ({
|
|
386
|
+
connection,
|
|
387
|
+
conversion: this.computeNetConnectionInlinePlacements(
|
|
388
|
+
connection,
|
|
389
|
+
ignoredTraceIds,
|
|
390
|
+
prospectiveTraceIdsByConnection.get(connection) ?? new Set(),
|
|
391
|
+
),
|
|
392
|
+
}))
|
|
393
|
+
const nextIgnoredTraceIds = new Set(
|
|
394
|
+
conversions.flatMap(({ conversion }) =>
|
|
395
|
+
conversion ? [...conversion.supersededTraceIds] : [],
|
|
396
|
+
),
|
|
397
|
+
)
|
|
398
|
+
if (setsEqual(ignoredTraceIds, nextIgnoredTraceIds)) {
|
|
399
|
+
return conversions.flatMap(({ conversion }) =>
|
|
400
|
+
conversion ? [conversion] : [],
|
|
401
|
+
)
|
|
402
|
+
}
|
|
403
|
+
ignoredTraceIds = nextIgnoredTraceIds
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* Splits an input net into the connected components created by its solved
|
|
409
|
+
* traces. This intentionally does not depend on anchored-label placement:
|
|
410
|
+
* a group whose anchored label failed still has the same routing topology.
|
|
411
|
+
*/
|
|
412
|
+
private getNetConnectionComponents(
|
|
413
|
+
connection: InputNetConnection,
|
|
414
|
+
): NetConnectionComponent[] {
|
|
415
|
+
const firstPinId = connection.pinIds[0]
|
|
416
|
+
if (!firstPinId) return []
|
|
417
|
+
const canonicalGlobalConnNetId =
|
|
418
|
+
this.globalConnMap.getNetConnectedToId(firstPinId)
|
|
419
|
+
if (!canonicalGlobalConnNetId) return []
|
|
420
|
+
|
|
421
|
+
const inputPinIds = new Set(
|
|
422
|
+
this.inputProblem.chips.flatMap((chip) =>
|
|
423
|
+
chip.pins.map((pin) => pin.pinId),
|
|
424
|
+
),
|
|
425
|
+
)
|
|
426
|
+
const pinIdsInGlobalNet = (
|
|
427
|
+
this.globalConnMap.getIdsConnectedToNet(
|
|
428
|
+
canonicalGlobalConnNetId,
|
|
429
|
+
) as string[]
|
|
430
|
+
).filter((id): id is PinId => inputPinIds.has(id))
|
|
431
|
+
const labeledPinIds = new Set(connection.pinIds)
|
|
432
|
+
|
|
433
|
+
return getTraceConnectedPinComponents({
|
|
434
|
+
pinIds: pinIdsInGlobalNet,
|
|
435
|
+
// Available-net-orientation traces only connect a pin to its generated
|
|
436
|
+
// anchored label. They are removed when that label becomes inline, so
|
|
437
|
+
// treating them as routed circuitry would leave text on a deleted wire.
|
|
438
|
+
traces: this.traces.filter(
|
|
439
|
+
(trace) => !isGeneratedAnchoredLabelConnector(trace),
|
|
440
|
+
),
|
|
441
|
+
})
|
|
442
|
+
.map((component) => ({
|
|
443
|
+
pinIds: component.pinIds,
|
|
444
|
+
labeledPinIds: component.pinIds.filter((pinId) =>
|
|
445
|
+
labeledPinIds.has(pinId),
|
|
446
|
+
),
|
|
447
|
+
traces: component.traces.sort(
|
|
448
|
+
(a, b) => getTraceLength(b) - getTraceLength(a),
|
|
449
|
+
),
|
|
450
|
+
}))
|
|
451
|
+
.filter((component) => component.labeledPinIds.length > 0)
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
private getGlobalConnNetId(
|
|
455
|
+
connection: InputNetConnection,
|
|
456
|
+
): string | undefined {
|
|
457
|
+
const firstPinId = connection.pinIds[0]
|
|
458
|
+
if (!firstPinId) return undefined
|
|
459
|
+
const connectionPinIds = new Set(connection.pinIds)
|
|
460
|
+
return (
|
|
461
|
+
this.traces.find((trace) =>
|
|
462
|
+
trace.pins.some((pin) => connectionPinIds.has(pin.pinId)),
|
|
463
|
+
)?.globalConnNetId ??
|
|
464
|
+
this.inputNetLabelPlacements.find(
|
|
465
|
+
(placement) => placement.netId === connection.netId,
|
|
466
|
+
)?.globalConnNetId ??
|
|
467
|
+
this.globalConnMap.getNetConnectedToId(firstPinId) ??
|
|
468
|
+
undefined
|
|
469
|
+
)
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* Converts every conventional label representation for an opted-in named
|
|
474
|
+
* net. Routed connected components become labels on their representative
|
|
475
|
+
* trace; disconnected pins become outward terminal stubs. The conversion is
|
|
476
|
+
* atomic so one blocked candidate cannot leave mixed label styles on a net.
|
|
477
|
+
*/
|
|
478
|
+
private computeNetConnectionInlinePlacements(
|
|
479
|
+
connection: InputNetConnection,
|
|
480
|
+
ignoredTraceIds: Set<string>,
|
|
481
|
+
forcedTerminalTraceIds: Set<string>,
|
|
482
|
+
): NetConnectionInlineConversion | null {
|
|
483
|
+
const components = this.getNetConnectionComponents(connection)
|
|
484
|
+
const globalConnNetId = this.getGlobalConnNetId(connection)
|
|
485
|
+
if (!globalConnNetId) return null
|
|
486
|
+
|
|
487
|
+
const inlinePlacements: InlineNetLabelPlacement[] = []
|
|
488
|
+
const supersededTraceIds = new Set<string>()
|
|
489
|
+
for (const component of components) {
|
|
490
|
+
const shouldUseTerminalStubs = component.traces.some((trace) =>
|
|
491
|
+
forcedTerminalTraceIds.has(trace.mspPairId),
|
|
492
|
+
)
|
|
493
|
+
|
|
494
|
+
let inlinePlacement: InlineNetLabelPlacement | null = null
|
|
495
|
+
if (!shouldUseTerminalStubs) {
|
|
496
|
+
for (const trace of component.traces) {
|
|
497
|
+
inlinePlacement = this.computeInlinePlacement({
|
|
498
|
+
connection,
|
|
499
|
+
trace,
|
|
500
|
+
pinIds: component.pinIds,
|
|
501
|
+
ignoredTraceIds,
|
|
502
|
+
})
|
|
503
|
+
if (inlinePlacement) break
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
if (inlinePlacement) {
|
|
507
|
+
inlinePlacements.push(inlinePlacement)
|
|
508
|
+
continue
|
|
509
|
+
}
|
|
510
|
+
if (component.traces.length > 0 && !shouldUseTerminalStubs) {
|
|
511
|
+
// This route originally had a valid inline placement, but a trace from
|
|
512
|
+
// a conversion that had to be rolled back now blocks it. Roll back
|
|
513
|
+
// this whole net as well instead of introducing a new replacement and
|
|
514
|
+
// allowing the batch plan to oscillate.
|
|
515
|
+
return null
|
|
516
|
+
}
|
|
517
|
+
if (component.labeledPinIds.length !== component.pinIds.length) {
|
|
518
|
+
// Removing this component's traces would strand an intermediate pin
|
|
519
|
+
// that was connected through the global net but was not an endpoint of
|
|
520
|
+
// this named connection. Retain the original routed representation.
|
|
521
|
+
return null
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// A routed component may be too short to carry its text. In that case,
|
|
525
|
+
// replace the component atomically with equivalent named terminal stubs
|
|
526
|
+
// at every endpoint. This preserves connectivity through the net name
|
|
527
|
+
// without retaining a short, redundant wire beside the new labels.
|
|
528
|
+
const terminalPlacements = component.pinIds.map((pinId) =>
|
|
529
|
+
this.computeTerminalInlinePlacement(
|
|
530
|
+
connection,
|
|
531
|
+
pinId,
|
|
532
|
+
globalConnNetId,
|
|
533
|
+
ignoredTraceIds,
|
|
534
|
+
),
|
|
535
|
+
)
|
|
536
|
+
if (terminalPlacements.some((placement) => placement === null)) {
|
|
537
|
+
return null
|
|
538
|
+
}
|
|
539
|
+
inlinePlacements.push(
|
|
540
|
+
...terminalPlacements.filter(
|
|
541
|
+
(placement): placement is InlineNetLabelPlacement =>
|
|
542
|
+
placement !== null,
|
|
543
|
+
),
|
|
544
|
+
)
|
|
545
|
+
for (const trace of component.traces) {
|
|
546
|
+
supersededTraceIds.add(trace.mspPairId)
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
return { globalConnNetId, placements: inlinePlacements, supersededTraceIds }
|
|
551
|
+
}
|
|
552
|
+
|
|
221
553
|
/**
|
|
222
554
|
* Converts one conventional endpoint placement into an inline label on a
|
|
223
555
|
* generated outward stub. The stub follows the pin's true facing direction;
|
|
@@ -227,6 +559,8 @@ export class InlineNetLabelSolver extends BaseSolver {
|
|
|
227
559
|
private computeTerminalInlinePlacement(
|
|
228
560
|
connection: InlineEligibleConnection,
|
|
229
561
|
pinId: PinId,
|
|
562
|
+
knownGlobalConnNetId?: string,
|
|
563
|
+
ignoredTraceIds = new Set<string>(),
|
|
230
564
|
): InlineNetLabelPlacement | null {
|
|
231
565
|
if (!connection.netId) return null
|
|
232
566
|
|
|
@@ -236,7 +570,9 @@ export class InlineNetLabelSolver extends BaseSolver {
|
|
|
236
570
|
placement.pinIds.length === 1 &&
|
|
237
571
|
placement.pinIds[0] === pinId,
|
|
238
572
|
)
|
|
239
|
-
|
|
573
|
+
const globalConnNetId =
|
|
574
|
+
knownGlobalConnNetId ?? anchoredPlacement?.globalConnNetId
|
|
575
|
+
if (!globalConnNetId) return null
|
|
240
576
|
|
|
241
577
|
const inputChip = this.inputProblem.chips.find((chip) =>
|
|
242
578
|
chip.pins.some((pin) => pin.pinId === pinId),
|
|
@@ -254,13 +590,14 @@ export class InlineNetLabelSolver extends BaseSolver {
|
|
|
254
590
|
// Leave a small wire tail at both ends of the text so it unmistakably
|
|
255
591
|
// reads as a label on a trace rather than free-standing text.
|
|
256
592
|
const stubLength = Math.max(width + 0.2, 0.6)
|
|
593
|
+
if (!inputPin && !anchoredPlacement) return null
|
|
257
594
|
const start = inputPin
|
|
258
595
|
? { x: inputPin.x, y: inputPin.y }
|
|
259
|
-
: anchoredPlacement
|
|
596
|
+
: anchoredPlacement!.anchorPoint
|
|
260
597
|
const direction =
|
|
261
598
|
inputPin && inputChip
|
|
262
599
|
? (inputPin._facingDirection ?? getPinDirection(inputPin, inputChip))
|
|
263
|
-
: anchoredPlacement
|
|
600
|
+
: anchoredPlacement!.orientation
|
|
264
601
|
const intendedEnd: Point =
|
|
265
602
|
direction === "x+"
|
|
266
603
|
? { x: start.x + stubLength, y: start.y }
|
|
@@ -275,73 +612,86 @@ export class InlineNetLabelSolver extends BaseSolver {
|
|
|
275
612
|
intendedEnd,
|
|
276
613
|
minimumLength: width + 2 * INLINE_NET_LABEL_TRACE_MARGIN,
|
|
277
614
|
traces: this.traces,
|
|
278
|
-
ownGlobalConnNetId:
|
|
615
|
+
ownGlobalConnNetId: globalConnNetId,
|
|
616
|
+
ignoredTraceIds,
|
|
279
617
|
})
|
|
280
618
|
if (!end) return null
|
|
281
619
|
|
|
282
620
|
const axis: InlineNetLabelPlacement["axis"] =
|
|
283
621
|
direction === "x+" || direction === "x-" ? "x" : "y"
|
|
284
|
-
const side: InlineNetLabelPlacement["side"] = axis === "x" ? "y+" : "x-"
|
|
285
622
|
const anchorPoint = {
|
|
286
623
|
x: (start.x + end.x) / 2,
|
|
287
624
|
y: (start.y + end.y) / 2,
|
|
288
625
|
}
|
|
289
626
|
const offset = height / 2 + INLINE_NET_LABEL_TRACE_MARGIN
|
|
290
|
-
const
|
|
291
|
-
|
|
292
|
-
? { x: anchorPoint.x, y: anchorPoint.y + offset }
|
|
293
|
-
: { x: anchorPoint.x - offset, y: anchorPoint.y }
|
|
294
|
-
|
|
295
|
-
const halfAlong = width / 2
|
|
296
|
-
const halfAcross = height / 2
|
|
297
|
-
const bounds: Bounds =
|
|
298
|
-
axis === "x"
|
|
299
|
-
? {
|
|
300
|
-
minX: center.x - halfAlong,
|
|
301
|
-
maxX: center.x + halfAlong,
|
|
302
|
-
minY: center.y - halfAcross,
|
|
303
|
-
maxY: center.y + halfAcross,
|
|
304
|
-
}
|
|
305
|
-
: {
|
|
306
|
-
minX: center.x - halfAcross,
|
|
307
|
-
maxX: center.x + halfAcross,
|
|
308
|
-
minY: center.y - halfAlong,
|
|
309
|
-
maxY: center.y + halfAlong,
|
|
310
|
-
}
|
|
627
|
+
const sides: InlineNetLabelPlacement["side"][] =
|
|
628
|
+
axis === "x" ? ["y+", "y-"] : ["x-", "x+"]
|
|
311
629
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
630
|
+
for (const side of sides) {
|
|
631
|
+
const center: Point =
|
|
632
|
+
side === "y+"
|
|
633
|
+
? { x: anchorPoint.x, y: anchorPoint.y + offset }
|
|
634
|
+
: side === "y-"
|
|
635
|
+
? { x: anchorPoint.x, y: anchorPoint.y - offset }
|
|
636
|
+
: side === "x-"
|
|
637
|
+
? { x: anchorPoint.x - offset, y: anchorPoint.y }
|
|
638
|
+
: { x: anchorPoint.x + offset, y: anchorPoint.y }
|
|
639
|
+
|
|
640
|
+
const halfAlong = width / 2
|
|
641
|
+
const halfAcross = height / 2
|
|
642
|
+
const bounds: Bounds =
|
|
643
|
+
axis === "x"
|
|
644
|
+
? {
|
|
645
|
+
minX: center.x - halfAlong,
|
|
646
|
+
maxX: center.x + halfAlong,
|
|
647
|
+
minY: center.y - halfAcross,
|
|
648
|
+
maxY: center.y + halfAcross,
|
|
649
|
+
}
|
|
650
|
+
: {
|
|
651
|
+
minX: center.x - halfAcross,
|
|
652
|
+
maxX: center.x + halfAcross,
|
|
653
|
+
minY: center.y - halfAlong,
|
|
654
|
+
maxY: center.y + halfAlong,
|
|
655
|
+
}
|
|
319
656
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
657
|
+
if (
|
|
658
|
+
this.isObstructed(bounds, {
|
|
659
|
+
ownGlobalConnNetId: globalConnNetId,
|
|
660
|
+
ignoredTraceIds,
|
|
661
|
+
})
|
|
662
|
+
) {
|
|
663
|
+
continue
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
return {
|
|
667
|
+
globalConnNetId,
|
|
668
|
+
netId: connection.netId,
|
|
669
|
+
netLabelText,
|
|
670
|
+
pinIds: [pinId],
|
|
671
|
+
stubTracePath: [start, end],
|
|
672
|
+
axis,
|
|
673
|
+
anchorPoint,
|
|
674
|
+
center,
|
|
675
|
+
width,
|
|
676
|
+
height,
|
|
677
|
+
side,
|
|
678
|
+
}
|
|
332
679
|
}
|
|
333
|
-
}
|
|
334
680
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
): InlineNetLabelPlacement | null {
|
|
338
|
-
// Only connections the router actually drew a trace for can carry an inline
|
|
339
|
-
// label - there's nothing to run parallel to otherwise.
|
|
340
|
-
const traces =
|
|
341
|
-
this.tracesByPinPairKey.get(getPinPairKey(connection.pinIds)) ?? []
|
|
342
|
-
if (traces.length === 0) return null
|
|
681
|
+
return null
|
|
682
|
+
}
|
|
343
683
|
|
|
344
|
-
|
|
684
|
+
private computeInlinePlacement({
|
|
685
|
+
connection,
|
|
686
|
+
trace,
|
|
687
|
+
pinIds,
|
|
688
|
+
ignoredTraceIds = new Set<string>(),
|
|
689
|
+
}: {
|
|
690
|
+
connection: InlineEligibleConnection
|
|
691
|
+
trace: SolvedTracePath
|
|
692
|
+
pinIds: PinId[]
|
|
693
|
+
ignoredTraceIds?: Set<string>
|
|
694
|
+
}): InlineNetLabelPlacement | null {
|
|
345
695
|
const segments = getAxisAlignedSegments(trace.tracePath)
|
|
346
696
|
if (segments.length === 0) return null
|
|
347
697
|
|
|
@@ -402,6 +752,7 @@ export class InlineNetLabelSolver extends BaseSolver {
|
|
|
402
752
|
this.isObstructed(bounds, {
|
|
403
753
|
ownTrace: trace,
|
|
404
754
|
ownGlobalConnNetId: trace.globalConnNetId,
|
|
755
|
+
ignoredTraceIds,
|
|
405
756
|
})
|
|
406
757
|
)
|
|
407
758
|
continue
|
|
@@ -411,7 +762,7 @@ export class InlineNetLabelSolver extends BaseSolver {
|
|
|
411
762
|
netId: connection.netId,
|
|
412
763
|
netLabelText,
|
|
413
764
|
mspPairId: trace.mspPairId,
|
|
414
|
-
pinIds: [...
|
|
765
|
+
pinIds: [...pinIds],
|
|
415
766
|
axis: segment.axis,
|
|
416
767
|
anchorPoint,
|
|
417
768
|
center,
|
|
@@ -434,6 +785,8 @@ export class InlineNetLabelSolver extends BaseSolver {
|
|
|
434
785
|
width,
|
|
435
786
|
height,
|
|
436
787
|
offset,
|
|
788
|
+
pinIds,
|
|
789
|
+
ignoredTraceIds,
|
|
437
790
|
})
|
|
438
791
|
if (spanPlacement) return spanPlacement
|
|
439
792
|
|
|
@@ -455,6 +808,8 @@ export class InlineNetLabelSolver extends BaseSolver {
|
|
|
455
808
|
width,
|
|
456
809
|
height,
|
|
457
810
|
offset,
|
|
811
|
+
pinIds,
|
|
812
|
+
ignoredTraceIds,
|
|
458
813
|
}: {
|
|
459
814
|
trace: SolvedTracePath
|
|
460
815
|
connection: InlineEligibleConnection
|
|
@@ -462,6 +817,8 @@ export class InlineNetLabelSolver extends BaseSolver {
|
|
|
462
817
|
width: number
|
|
463
818
|
height: number
|
|
464
819
|
offset: number
|
|
820
|
+
pinIds: PinId[]
|
|
821
|
+
ignoredTraceIds: Set<string>
|
|
465
822
|
}): InlineNetLabelPlacement | null {
|
|
466
823
|
const path = trace.tracePath
|
|
467
824
|
if (path.length < 2) return null
|
|
@@ -537,6 +894,7 @@ export class InlineNetLabelSolver extends BaseSolver {
|
|
|
537
894
|
this.isObstructed(bounds, {
|
|
538
895
|
ownTrace: trace,
|
|
539
896
|
ownGlobalConnNetId: trace.globalConnNetId,
|
|
897
|
+
ignoredTraceIds,
|
|
540
898
|
})
|
|
541
899
|
)
|
|
542
900
|
continue
|
|
@@ -551,7 +909,7 @@ export class InlineNetLabelSolver extends BaseSolver {
|
|
|
551
909
|
netId: connection.netId,
|
|
552
910
|
netLabelText,
|
|
553
911
|
mspPairId: trace.mspPairId,
|
|
554
|
-
pinIds: [...
|
|
912
|
+
pinIds: [...pinIds],
|
|
555
913
|
axis,
|
|
556
914
|
anchorPoint,
|
|
557
915
|
center,
|
|
@@ -574,9 +932,11 @@ export class InlineNetLabelSolver extends BaseSolver {
|
|
|
574
932
|
{
|
|
575
933
|
ownTrace,
|
|
576
934
|
ownGlobalConnNetId,
|
|
935
|
+
ignoredTraceIds = new Set<string>(),
|
|
577
936
|
}: {
|
|
578
937
|
ownTrace?: SolvedTracePath
|
|
579
938
|
ownGlobalConnNetId: string
|
|
939
|
+
ignoredTraceIds?: Set<string>
|
|
580
940
|
},
|
|
581
941
|
): boolean {
|
|
582
942
|
for (const chip of this.inputProblem.chips) {
|
|
@@ -594,6 +954,7 @@ export class InlineNetLabelSolver extends BaseSolver {
|
|
|
594
954
|
}
|
|
595
955
|
|
|
596
956
|
for (const trace of this.traces) {
|
|
957
|
+
if (ignoredTraceIds.has(trace.mspPairId)) continue
|
|
597
958
|
if (ownTrace && trace.mspPairId === ownTrace.mspPairId) continue
|
|
598
959
|
if (trace.globalConnNetId === ownGlobalConnNetId) continue
|
|
599
960
|
if (doesPathIntersectBounds(trace.tracePath, bounds)) return true
|
|
@@ -606,41 +967,119 @@ export class InlineNetLabelSolver extends BaseSolver {
|
|
|
606
967
|
* Net label placements superseded by an inline label. A net gets one label or
|
|
607
968
|
* the other, never both.
|
|
608
969
|
*/
|
|
609
|
-
private getSupersededNetLabelKeys(
|
|
970
|
+
private getSupersededNetLabelKeys(
|
|
971
|
+
placements = this.inlineNetLabelPlacements,
|
|
972
|
+
): Set<string> {
|
|
610
973
|
const keys = new Set<string>()
|
|
611
|
-
for (const placement of
|
|
974
|
+
for (const placement of placements) {
|
|
612
975
|
keys.add(placement.globalConnNetId)
|
|
613
976
|
}
|
|
614
977
|
return keys
|
|
615
978
|
}
|
|
616
979
|
|
|
617
|
-
private getOutputTraces(
|
|
980
|
+
private getOutputTraces(
|
|
981
|
+
supersededNetLabelKeys: Set<string>,
|
|
982
|
+
supersededTraceIds: Set<string>,
|
|
983
|
+
) {
|
|
618
984
|
return this.traces.filter(
|
|
619
985
|
(trace) =>
|
|
986
|
+
!supersededTraceIds.has(trace.mspPairId) &&
|
|
620
987
|
!(
|
|
621
988
|
supersededNetLabelKeys.has(trace.globalConnNetId) &&
|
|
622
|
-
trace
|
|
989
|
+
isGeneratedAnchoredLabelConnector(trace)
|
|
623
990
|
),
|
|
624
991
|
)
|
|
625
992
|
}
|
|
626
993
|
|
|
994
|
+
private getInlineGlobalsOverlappingAnchoredLabels({
|
|
995
|
+
inlineNetLabelPlacements,
|
|
996
|
+
anchoredNetLabelPlacements,
|
|
997
|
+
}: {
|
|
998
|
+
inlineNetLabelPlacements: InlineNetLabelPlacement[]
|
|
999
|
+
anchoredNetLabelPlacements: NetLabelPlacement[]
|
|
1000
|
+
}): Set<string> {
|
|
1001
|
+
const blockedGlobalConnNetIds = new Set<string>()
|
|
1002
|
+
for (const inlinePlacement of inlineNetLabelPlacements) {
|
|
1003
|
+
const inlineBounds = getInlinePlacementBounds(inlinePlacement)
|
|
1004
|
+
for (const anchoredPlacement of anchoredNetLabelPlacements) {
|
|
1005
|
+
if (
|
|
1006
|
+
anchoredPlacement.globalConnNetId === inlinePlacement.globalConnNetId
|
|
1007
|
+
) {
|
|
1008
|
+
continue
|
|
1009
|
+
}
|
|
1010
|
+
const anchoredBounds = getAnchoredPlacementBounds(anchoredPlacement)
|
|
1011
|
+
if (
|
|
1012
|
+
boundsOverlap(inlineBounds, anchoredBounds) ||
|
|
1013
|
+
(inlinePlacement.stubTracePath &&
|
|
1014
|
+
doesPathIntersectBounds(
|
|
1015
|
+
inlinePlacement.stubTracePath,
|
|
1016
|
+
anchoredBounds,
|
|
1017
|
+
))
|
|
1018
|
+
) {
|
|
1019
|
+
blockedGlobalConnNetIds.add(inlinePlacement.globalConnNetId)
|
|
1020
|
+
break
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
return blockedGlobalConnNetIds
|
|
1025
|
+
}
|
|
1026
|
+
|
|
627
1027
|
private buildPostProcessedOutput() {
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
1028
|
+
let activeInlinePlacements = this.inlineNetLabelPlacements
|
|
1029
|
+
while (true) {
|
|
1030
|
+
const supersededNetLabelKeys = this.getSupersededNetLabelKeys(
|
|
1031
|
+
activeInlinePlacements,
|
|
1032
|
+
)
|
|
1033
|
+
const supersededTraceIds = new Set(
|
|
1034
|
+
[...supersededNetLabelKeys].flatMap((globalConnNetId) => [
|
|
1035
|
+
...(this.supersededTraceIdsByGlobalConnNetId.get(globalConnNetId) ??
|
|
1036
|
+
[]),
|
|
1037
|
+
]),
|
|
1038
|
+
)
|
|
1039
|
+
const retainedNetLabelPlacements = this.inputNetLabelPlacements.filter(
|
|
1040
|
+
(placement) => !supersededNetLabelKeys.has(placement.globalConnNetId),
|
|
1041
|
+
)
|
|
1042
|
+
const outputTraces = this.getOutputTraces(
|
|
1043
|
+
supersededNetLabelKeys,
|
|
1044
|
+
supersededTraceIds,
|
|
1045
|
+
)
|
|
1046
|
+
const pushed = pushAnchoredNetLabelsAwayFromInlineLabels({
|
|
1047
|
+
inputProblem: this.inputProblem,
|
|
1048
|
+
traces: outputTraces,
|
|
1049
|
+
netLabelPlacements: retainedNetLabelPlacements,
|
|
1050
|
+
inlineNetLabelPlacements: activeInlinePlacements,
|
|
1051
|
+
})
|
|
1052
|
+
const blockedGlobalConnNetIds =
|
|
1053
|
+
this.getInlineGlobalsOverlappingAnchoredLabels({
|
|
1054
|
+
inlineNetLabelPlacements: activeInlinePlacements,
|
|
1055
|
+
anchoredNetLabelPlacements: pushed.netLabelPlacements,
|
|
1056
|
+
})
|
|
1057
|
+
if (blockedGlobalConnNetIds.size > 0) {
|
|
1058
|
+
const shiftedInlineTerminalLabels =
|
|
1059
|
+
pushInlineTerminalLabelsAwayFromAnchoredLabels({
|
|
1060
|
+
inputProblem: this.inputProblem,
|
|
1061
|
+
traces: pushed.traces,
|
|
1062
|
+
anchoredNetLabelPlacements: pushed.netLabelPlacements,
|
|
1063
|
+
inlineNetLabelPlacements: activeInlinePlacements,
|
|
1064
|
+
})
|
|
1065
|
+
if (shiftedInlineTerminalLabels.movedGroupCount > 0) {
|
|
1066
|
+
activeInlinePlacements =
|
|
1067
|
+
shiftedInlineTerminalLabels.inlineNetLabelPlacements
|
|
1068
|
+
continue
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
if (blockedGlobalConnNetIds.size === 0) {
|
|
1072
|
+
this.inlineNetLabelPlacements = activeInlinePlacements
|
|
1073
|
+
this.stats.pushedAnchoredNetLabelCount = pushed.movedLabelCount
|
|
1074
|
+
return {
|
|
1075
|
+
traces: pushed.traces,
|
|
1076
|
+
netLabelPlacements: pushed.netLabelPlacements,
|
|
1077
|
+
inlineNetLabelPlacements: activeInlinePlacements,
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
activeInlinePlacements = activeInlinePlacements.filter(
|
|
1081
|
+
(placement) => !blockedGlobalConnNetIds.has(placement.globalConnNetId),
|
|
1082
|
+
)
|
|
644
1083
|
}
|
|
645
1084
|
}
|
|
646
1085
|
|
|
@@ -841,12 +1280,14 @@ const getCollisionLimitedTerminalStubEnd = ({
|
|
|
841
1280
|
minimumLength,
|
|
842
1281
|
traces,
|
|
843
1282
|
ownGlobalConnNetId,
|
|
1283
|
+
ignoredTraceIds,
|
|
844
1284
|
}: {
|
|
845
1285
|
start: Point
|
|
846
1286
|
intendedEnd: Point
|
|
847
1287
|
minimumLength: number
|
|
848
1288
|
traces: SolvedTracePath[]
|
|
849
1289
|
ownGlobalConnNetId: string
|
|
1290
|
+
ignoredTraceIds: Set<string>
|
|
850
1291
|
}): Point | null => {
|
|
851
1292
|
const isHorizontal =
|
|
852
1293
|
Math.abs(intendedEnd.x - start.x) >= Math.abs(intendedEnd.y - start.y)
|
|
@@ -857,6 +1298,7 @@ const getCollisionLimitedTerminalStubEnd = ({
|
|
|
857
1298
|
let availableLength = intendedLength
|
|
858
1299
|
|
|
859
1300
|
for (const trace of traces) {
|
|
1301
|
+
if (ignoredTraceIds.has(trace.mspPairId)) continue
|
|
860
1302
|
if (trace.globalConnNetId === ownGlobalConnNetId) continue
|
|
861
1303
|
|
|
862
1304
|
for (
|