@tscircuit/schematic-trace-solver 0.0.118 → 0.0.120
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 +7 -1
- package/dist/index.js +135 -4
- package/lib/solvers/AvailableNetOrientationSolver/AvailableNetOrientationSolver.ts +200 -6
- package/lib/solvers/AvailableNetOrientationSolver/types.ts +2 -0
- package/package.json +1 -1
- package/tests/bug-reports/bug-report-20260707T092615Z/__snapshots__/bug-report-20260707T092615Z.snap.svg +3 -3
- package/tests/bug-reports/bug-report-20260721T221026Z/__snapshots__/bug-report-20260721T221026Z.snap.svg +3 -3
- package/tests/bug-reports/bug-report-20260728T225606Z/__snapshots__/bug-report-20260728T225606Z.snap.svg +3 -3
- package/tests/repros/__snapshots__/repro-atmega328p-missing-gnd-netlabel.snap.svg +3 -3
- package/tests/repros/__snapshots__/repro-ground-label-on-lower-rail-end.snap.svg +58 -0
- package/tests/repros/__snapshots__/repro-power-group-rail-labels.snap.svg +58 -0
- package/tests/repros/__snapshots__/repro-rp2040-v3v3-label-placement.snap.svg +96 -0
- package/tests/repros/repro-ground-label-on-lower-rail-end.test.ts +84 -0
- package/tests/repros/repro-power-group-rail-labels.test.ts +95 -0
- package/tests/repros/repro-rp2040-v3v3-label-placement.test.ts +102 -0
package/dist/index.d.ts
CHANGED
|
@@ -721,12 +721,13 @@ interface AvailableNetOrientationSolverParams {
|
|
|
721
721
|
type CandidateLabel = {
|
|
722
722
|
orientation: FacingDirection;
|
|
723
723
|
anchorPoint: Point;
|
|
724
|
+
connectorSource?: Point;
|
|
724
725
|
center: Point;
|
|
725
726
|
width: number;
|
|
726
727
|
height: number;
|
|
727
728
|
};
|
|
728
729
|
type CandidateStatus$2 = "valid" | "chip-collision" | "text-collision" | "trace-collision" | "trace-clearance-violation" | "netlabel-collision";
|
|
729
|
-
type CandidatePhase = "rotate" | "trace-anchor" | "shift" | "lateral-shift";
|
|
730
|
+
type CandidatePhase = "rotate" | "trace-anchor" | "outward-trace-anchor" | "shift" | "lateral-shift";
|
|
730
731
|
type EvaluatedCandidate = CandidateLabel & {
|
|
731
732
|
status: CandidateStatus$2;
|
|
732
733
|
selected: boolean;
|
|
@@ -768,6 +769,7 @@ declare class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
768
769
|
private hasTraceContinuingInOrientation;
|
|
769
770
|
private findValidRotatedCandidate;
|
|
770
771
|
private findValidTraceAnchorCandidate;
|
|
772
|
+
private findValidOutwardTraceAnchorCandidate;
|
|
771
773
|
private getTraceAnchorCandidatePoints;
|
|
772
774
|
private getAvailableOrientations;
|
|
773
775
|
private findValidShiftedCandidate;
|
|
@@ -801,6 +803,10 @@ declare class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
801
803
|
private getLabelTraceClearanceBounds;
|
|
802
804
|
private shouldCheckTraceClearanceForLabel;
|
|
803
805
|
private hasRoutedLabelOnSameNet;
|
|
806
|
+
private isOutwardHorizontalFallback;
|
|
807
|
+
private hasPortOnlyLabelOnSameNet;
|
|
808
|
+
private hasOppositeVerticalRailOnSameChipSide;
|
|
809
|
+
private getSingleChipSideForNetConnection;
|
|
804
810
|
private isPortOnlyLabel;
|
|
805
811
|
private intersectsAnyOtherNetLabel;
|
|
806
812
|
private sharesChipBoundary;
|
package/dist/index.js
CHANGED
|
@@ -7995,9 +7995,20 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
7995
7995
|
findCorrectedCandidate(label, labelIndex) {
|
|
7996
7996
|
const orientations = this.getAvailableOrientations(label);
|
|
7997
7997
|
const requiredOrientation = orientations[0];
|
|
7998
|
-
const
|
|
7999
|
-
(connection) => connection.netId === label.netId
|
|
7998
|
+
const netConnection = this.inputProblem.netConnections.find(
|
|
7999
|
+
(connection) => connection.netId === label.netId
|
|
8000
8000
|
);
|
|
8001
|
+
const isTwoPinNet = netConnection?.pinIds.length === 2;
|
|
8002
|
+
const isDistanceSplitVerticalRail = (netConnection?.pinIds.length ?? 0) > 2 && isYOrientation(requiredOrientation) && this.hasPortOnlyLabelOnSameNet(label);
|
|
8003
|
+
const isPairedSameSidePowerRail = isYOrientation(requiredOrientation) && this.hasOppositeVerticalRailOnSameChipSide(label, requiredOrientation);
|
|
8004
|
+
if (orientations.length === 1 && isYOrientation(requiredOrientation) && this.isOutwardHorizontalFallback(label) && this.hasTraceContinuingInOrientation(label, requiredOrientation) && (isDistanceSplitVerticalRail || isPairedSameSidePowerRail)) {
|
|
8005
|
+
const traceAnchorCandidate2 = this.findValidOutwardTraceAnchorCandidate(
|
|
8006
|
+
label,
|
|
8007
|
+
requiredOrientation,
|
|
8008
|
+
labelIndex
|
|
8009
|
+
);
|
|
8010
|
+
if (traceAnchorCandidate2) return traceAnchorCandidate2;
|
|
8011
|
+
}
|
|
8001
8012
|
if (isTwoPinNet && orientations.length === 1 && isYOrientation(requiredOrientation) && this.hasTraceContinuingInOrientation(label, requiredOrientation)) {
|
|
8002
8013
|
const alignedCandidate = this.findValidCandidateInShiftColumn({
|
|
8003
8014
|
label,
|
|
@@ -8099,6 +8110,72 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
8099
8110
|
}
|
|
8100
8111
|
return null;
|
|
8101
8112
|
}
|
|
8113
|
+
findValidOutwardTraceAnchorCandidate(label, orientation, labelIndex) {
|
|
8114
|
+
const direction = dir(orientation);
|
|
8115
|
+
const candidatePoints = this.getTraceAnchorCandidatePoints(label).sort(
|
|
8116
|
+
(a, b) => {
|
|
8117
|
+
const aAlongDirection = a.x * direction.x + a.y * direction.y;
|
|
8118
|
+
const bAlongDirection = b.x * direction.x + b.y * direction.y;
|
|
8119
|
+
return bAlongDirection - aAlongDirection;
|
|
8120
|
+
}
|
|
8121
|
+
);
|
|
8122
|
+
for (const connectorSource of candidatePoints) {
|
|
8123
|
+
const preservedColumnAnchor = this.getSearchStartAnchor(
|
|
8124
|
+
label,
|
|
8125
|
+
orientation
|
|
8126
|
+
);
|
|
8127
|
+
const preservedColumnCandidate = this.createCandidate(
|
|
8128
|
+
label,
|
|
8129
|
+
{
|
|
8130
|
+
x: preservedColumnAnchor.x,
|
|
8131
|
+
y: connectorSource.y
|
|
8132
|
+
},
|
|
8133
|
+
orientation,
|
|
8134
|
+
connectorSource
|
|
8135
|
+
);
|
|
8136
|
+
const preservedColumnResult = this.evaluateCandidate(
|
|
8137
|
+
preservedColumnCandidate,
|
|
8138
|
+
label,
|
|
8139
|
+
labelIndex,
|
|
8140
|
+
"outward-trace-anchor"
|
|
8141
|
+
);
|
|
8142
|
+
this.currentCandidateResults.push(preservedColumnResult);
|
|
8143
|
+
if (preservedColumnResult.status === "valid") {
|
|
8144
|
+
preservedColumnResult.selected = true;
|
|
8145
|
+
return preservedColumnResult;
|
|
8146
|
+
}
|
|
8147
|
+
const outwardDirection = this.getPerpendicularOutwardDirection(
|
|
8148
|
+
connectorSource,
|
|
8149
|
+
orientation
|
|
8150
|
+
);
|
|
8151
|
+
if (outwardDirection.x === 0 && outwardDirection.y === 0) continue;
|
|
8152
|
+
for (let distance5 = LABEL_SEARCH_STEP; distance5 <= this.maxSearchDistance + EPS11; distance5 += LABEL_SEARCH_STEP) {
|
|
8153
|
+
const anchorPoint = {
|
|
8154
|
+
x: connectorSource.x + outwardDirection.x * distance5,
|
|
8155
|
+
y: connectorSource.y + outwardDirection.y * distance5
|
|
8156
|
+
};
|
|
8157
|
+
const candidate = this.createCandidate(
|
|
8158
|
+
label,
|
|
8159
|
+
anchorPoint,
|
|
8160
|
+
orientation,
|
|
8161
|
+
connectorSource
|
|
8162
|
+
);
|
|
8163
|
+
const result = this.evaluateCandidate(
|
|
8164
|
+
candidate,
|
|
8165
|
+
label,
|
|
8166
|
+
labelIndex,
|
|
8167
|
+
"outward-trace-anchor",
|
|
8168
|
+
distance5
|
|
8169
|
+
);
|
|
8170
|
+
this.currentCandidateResults.push(result);
|
|
8171
|
+
if (result.status === "valid") {
|
|
8172
|
+
result.selected = true;
|
|
8173
|
+
return result;
|
|
8174
|
+
}
|
|
8175
|
+
}
|
|
8176
|
+
}
|
|
8177
|
+
return null;
|
|
8178
|
+
}
|
|
8102
8179
|
getTraceAnchorCandidatePoints(label) {
|
|
8103
8180
|
const seen = /* @__PURE__ */ new Set();
|
|
8104
8181
|
const points = [];
|
|
@@ -8256,7 +8333,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
8256
8333
|
]);
|
|
8257
8334
|
}
|
|
8258
8335
|
return getConnectorTracePath(
|
|
8259
|
-
label.anchorPoint,
|
|
8336
|
+
candidate.connectorSource ?? label.anchorPoint,
|
|
8260
8337
|
candidate.anchorPoint,
|
|
8261
8338
|
candidate.orientation
|
|
8262
8339
|
);
|
|
@@ -8390,7 +8467,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
8390
8467
|
}
|
|
8391
8468
|
return this.getSearchDistanceLimit(label, orientation);
|
|
8392
8469
|
}
|
|
8393
|
-
createCandidate(label, anchorPoint, orientation) {
|
|
8470
|
+
createCandidate(label, anchorPoint, orientation, connectorSource) {
|
|
8394
8471
|
const { width, height } = getDimsForOrientation({
|
|
8395
8472
|
orientation,
|
|
8396
8473
|
netLabelWidth: this.getNetLabelWidth(label),
|
|
@@ -8399,6 +8476,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
8399
8476
|
return {
|
|
8400
8477
|
orientation,
|
|
8401
8478
|
anchorPoint,
|
|
8479
|
+
connectorSource,
|
|
8402
8480
|
width,
|
|
8403
8481
|
height,
|
|
8404
8482
|
center: getCenterFromAnchor(anchorPoint, orientation, width, height)
|
|
@@ -8552,6 +8630,59 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
8552
8630
|
}
|
|
8553
8631
|
return false;
|
|
8554
8632
|
}
|
|
8633
|
+
isOutwardHorizontalFallback(label) {
|
|
8634
|
+
const chipSide = this.getChipSideForPoint(label.anchorPoint);
|
|
8635
|
+
return chipSide === "left" && label.orientation === "x-" || chipSide === "right" && label.orientation === "x+";
|
|
8636
|
+
}
|
|
8637
|
+
hasPortOnlyLabelOnSameNet(label) {
|
|
8638
|
+
return this.outputNetLabelPlacements.some(
|
|
8639
|
+
(otherLabel) => otherLabel !== label && otherLabel.globalConnNetId === label.globalConnNetId && this.isPortOnlyLabel(otherLabel)
|
|
8640
|
+
);
|
|
8641
|
+
}
|
|
8642
|
+
hasOppositeVerticalRailOnSameChipSide(label, orientation) {
|
|
8643
|
+
const currentConnection = this.inputProblem.netConnections.find(
|
|
8644
|
+
(connection) => connection.netId === label.netId
|
|
8645
|
+
);
|
|
8646
|
+
if (!currentConnection) return false;
|
|
8647
|
+
const currentSide = this.getSingleChipSideForNetConnection(currentConnection);
|
|
8648
|
+
if (!currentSide) return false;
|
|
8649
|
+
const oppositeOrientation = orientation === "y+" ? "y-" : "y+";
|
|
8650
|
+
return this.inputProblem.netConnections.some((connection) => {
|
|
8651
|
+
if (connection === currentConnection || connection.pinIds.length < 2) {
|
|
8652
|
+
return false;
|
|
8653
|
+
}
|
|
8654
|
+
const availableOrientations = this.inputProblem.availableNetLabelOrientations[connection.netId] ?? [];
|
|
8655
|
+
if (availableOrientations.length !== 1 || availableOrientations[0] !== oppositeOrientation) {
|
|
8656
|
+
return false;
|
|
8657
|
+
}
|
|
8658
|
+
const connectionSide = this.getSingleChipSideForNetConnection(connection);
|
|
8659
|
+
return connectionSide?.chipId === currentSide.chipId && connectionSide.side === currentSide.side;
|
|
8660
|
+
});
|
|
8661
|
+
}
|
|
8662
|
+
getSingleChipSideForNetConnection(connection) {
|
|
8663
|
+
if (connection.pinIds.length < 2) return null;
|
|
8664
|
+
const pins = connection.pinIds.map((pinId) => this.pinMap[pinId]);
|
|
8665
|
+
if (pins.some((pin) => !pin)) return null;
|
|
8666
|
+
const chipIds = new Set(pins.map((pin) => pin.chipId));
|
|
8667
|
+
if (chipIds.size !== 1) return null;
|
|
8668
|
+
const chipId = pins[0].chipId;
|
|
8669
|
+
const chip = this.chipObstacleSpatialIndex.chips.find(
|
|
8670
|
+
(candidate) => candidate.chipId === chipId
|
|
8671
|
+
);
|
|
8672
|
+
if (!chip) return null;
|
|
8673
|
+
const sides = new Set(
|
|
8674
|
+
pins.map((pin) => {
|
|
8675
|
+
if (Math.abs(pin.x - chip.bounds.minX) <= EPS11) return "left";
|
|
8676
|
+
if (Math.abs(pin.x - chip.bounds.maxX) <= EPS11) return "right";
|
|
8677
|
+
return null;
|
|
8678
|
+
})
|
|
8679
|
+
);
|
|
8680
|
+
if (sides.size !== 1 || sides.has(null)) return null;
|
|
8681
|
+
return {
|
|
8682
|
+
chipId,
|
|
8683
|
+
side: [...sides][0]
|
|
8684
|
+
};
|
|
8685
|
+
}
|
|
8555
8686
|
isPortOnlyLabel(label) {
|
|
8556
8687
|
return label.mspConnectionPairIds.length === 0;
|
|
8557
8688
|
}
|
|
@@ -9,7 +9,11 @@ import {
|
|
|
9
9
|
getRectBounds,
|
|
10
10
|
} from "lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/geometry"
|
|
11
11
|
import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
|
|
12
|
-
import type {
|
|
12
|
+
import type {
|
|
13
|
+
InputNetConnection,
|
|
14
|
+
InputPin,
|
|
15
|
+
InputProblem,
|
|
16
|
+
} from "lib/types/InputProblem"
|
|
13
17
|
import { dir, type FacingDirection } from "lib/utils/dir"
|
|
14
18
|
import { rectIntersectsAnyTextBox } from "lib/utils/textBoxBounds"
|
|
15
19
|
import { EPS, LABEL_SEARCH_STEP, WICK_CLEARANCE } from "./constants"
|
|
@@ -250,10 +254,35 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
250
254
|
private findCorrectedCandidate(label: NetLabelPlacement, labelIndex: number) {
|
|
251
255
|
const orientations = this.getAvailableOrientations(label)
|
|
252
256
|
const requiredOrientation = orientations[0]!
|
|
253
|
-
const
|
|
254
|
-
(connection) =>
|
|
255
|
-
connection.netId === label.netId && connection.pinIds.length === 2,
|
|
257
|
+
const netConnection = this.inputProblem.netConnections.find(
|
|
258
|
+
(connection) => connection.netId === label.netId,
|
|
256
259
|
)
|
|
260
|
+
const isTwoPinNet = netConnection?.pinIds.length === 2
|
|
261
|
+
const isDistanceSplitVerticalRail =
|
|
262
|
+
(netConnection?.pinIds.length ?? 0) > 2 &&
|
|
263
|
+
isYOrientation(requiredOrientation) &&
|
|
264
|
+
this.hasPortOnlyLabelOnSameNet(label)
|
|
265
|
+
const isPairedSameSidePowerRail =
|
|
266
|
+
isYOrientation(requiredOrientation) &&
|
|
267
|
+
this.hasOppositeVerticalRailOnSameChipSide(label, requiredOrientation)
|
|
268
|
+
if (
|
|
269
|
+
orientations.length === 1 &&
|
|
270
|
+
isYOrientation(requiredOrientation) &&
|
|
271
|
+
this.isOutwardHorizontalFallback(label) &&
|
|
272
|
+
this.hasTraceContinuingInOrientation(label, requiredOrientation) &&
|
|
273
|
+
(isDistanceSplitVerticalRail || isPairedSameSidePowerRail)
|
|
274
|
+
) {
|
|
275
|
+
// Keep the established outward column, but attach at the furthest trace
|
|
276
|
+
// point in the required vertical direction. This places y+ labels above
|
|
277
|
+
// a rail and y- labels below it while using a short horizontal connector.
|
|
278
|
+
const traceAnchorCandidate = this.findValidOutwardTraceAnchorCandidate(
|
|
279
|
+
label,
|
|
280
|
+
requiredOrientation,
|
|
281
|
+
labelIndex,
|
|
282
|
+
)
|
|
283
|
+
if (traceAnchorCandidate) return traceAnchorCandidate
|
|
284
|
+
}
|
|
285
|
+
|
|
257
286
|
if (
|
|
258
287
|
isTwoPinNet &&
|
|
259
288
|
orientations.length === 1 &&
|
|
@@ -390,6 +419,86 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
390
419
|
return null
|
|
391
420
|
}
|
|
392
421
|
|
|
422
|
+
private findValidOutwardTraceAnchorCandidate(
|
|
423
|
+
label: NetLabelPlacement,
|
|
424
|
+
orientation: FacingDirection,
|
|
425
|
+
labelIndex: number,
|
|
426
|
+
) {
|
|
427
|
+
const direction = dir(orientation)
|
|
428
|
+
const candidatePoints = this.getTraceAnchorCandidatePoints(label).sort(
|
|
429
|
+
(a, b) => {
|
|
430
|
+
const aAlongDirection = a.x * direction.x + a.y * direction.y
|
|
431
|
+
const bAlongDirection = b.x * direction.x + b.y * direction.y
|
|
432
|
+
return bAlongDirection - aAlongDirection
|
|
433
|
+
},
|
|
434
|
+
)
|
|
435
|
+
|
|
436
|
+
for (const connectorSource of candidatePoints) {
|
|
437
|
+
const preservedColumnAnchor = this.getSearchStartAnchor(
|
|
438
|
+
label,
|
|
439
|
+
orientation,
|
|
440
|
+
)
|
|
441
|
+
const preservedColumnCandidate = this.createCandidate(
|
|
442
|
+
label,
|
|
443
|
+
{
|
|
444
|
+
x: preservedColumnAnchor.x,
|
|
445
|
+
y: connectorSource.y,
|
|
446
|
+
},
|
|
447
|
+
orientation,
|
|
448
|
+
connectorSource,
|
|
449
|
+
)
|
|
450
|
+
const preservedColumnResult = this.evaluateCandidate(
|
|
451
|
+
preservedColumnCandidate,
|
|
452
|
+
label,
|
|
453
|
+
labelIndex,
|
|
454
|
+
"outward-trace-anchor",
|
|
455
|
+
)
|
|
456
|
+
this.currentCandidateResults.push(preservedColumnResult)
|
|
457
|
+
if (preservedColumnResult.status === "valid") {
|
|
458
|
+
preservedColumnResult.selected = true
|
|
459
|
+
return preservedColumnResult
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
const outwardDirection = this.getPerpendicularOutwardDirection(
|
|
463
|
+
connectorSource,
|
|
464
|
+
orientation,
|
|
465
|
+
)
|
|
466
|
+
if (outwardDirection.x === 0 && outwardDirection.y === 0) continue
|
|
467
|
+
|
|
468
|
+
for (
|
|
469
|
+
let distance = LABEL_SEARCH_STEP;
|
|
470
|
+
distance <= this.maxSearchDistance + EPS;
|
|
471
|
+
distance += LABEL_SEARCH_STEP
|
|
472
|
+
) {
|
|
473
|
+
const anchorPoint = {
|
|
474
|
+
x: connectorSource.x + outwardDirection.x * distance,
|
|
475
|
+
y: connectorSource.y + outwardDirection.y * distance,
|
|
476
|
+
}
|
|
477
|
+
const candidate = this.createCandidate(
|
|
478
|
+
label,
|
|
479
|
+
anchorPoint,
|
|
480
|
+
orientation,
|
|
481
|
+
connectorSource,
|
|
482
|
+
)
|
|
483
|
+
const result = this.evaluateCandidate(
|
|
484
|
+
candidate,
|
|
485
|
+
label,
|
|
486
|
+
labelIndex,
|
|
487
|
+
"outward-trace-anchor",
|
|
488
|
+
distance,
|
|
489
|
+
)
|
|
490
|
+
this.currentCandidateResults.push(result)
|
|
491
|
+
|
|
492
|
+
if (result.status === "valid") {
|
|
493
|
+
result.selected = true
|
|
494
|
+
return result
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
return null
|
|
500
|
+
}
|
|
501
|
+
|
|
393
502
|
private getTraceAnchorCandidatePoints(label: NetLabelPlacement) {
|
|
394
503
|
const seen = new Set<string>()
|
|
395
504
|
const points: Point[] = []
|
|
@@ -592,7 +701,7 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
592
701
|
label: NetLabelPlacement,
|
|
593
702
|
candidate: Pick<
|
|
594
703
|
EvaluatedCandidate,
|
|
595
|
-
"anchorPoint" | "orientation" | "phase"
|
|
704
|
+
"anchorPoint" | "connectorSource" | "orientation" | "phase"
|
|
596
705
|
>,
|
|
597
706
|
) {
|
|
598
707
|
if (candidate.phase === "lateral-shift") {
|
|
@@ -612,7 +721,7 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
612
721
|
}
|
|
613
722
|
|
|
614
723
|
return getConnectorTracePath(
|
|
615
|
-
label.anchorPoint,
|
|
724
|
+
candidate.connectorSource ?? label.anchorPoint,
|
|
616
725
|
candidate.anchorPoint,
|
|
617
726
|
candidate.orientation,
|
|
618
727
|
)
|
|
@@ -810,6 +919,7 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
810
919
|
label: NetLabelPlacement,
|
|
811
920
|
anchorPoint: Point,
|
|
812
921
|
orientation: FacingDirection,
|
|
922
|
+
connectorSource?: Point,
|
|
813
923
|
): CandidateLabel {
|
|
814
924
|
const { width, height } = getDimsForOrientation({
|
|
815
925
|
orientation,
|
|
@@ -819,6 +929,7 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
819
929
|
return {
|
|
820
930
|
orientation,
|
|
821
931
|
anchorPoint,
|
|
932
|
+
connectorSource,
|
|
822
933
|
width,
|
|
823
934
|
height,
|
|
824
935
|
center: getCenterFromAnchor(anchorPoint, orientation, width, height),
|
|
@@ -1029,6 +1140,89 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
1029
1140
|
return false
|
|
1030
1141
|
}
|
|
1031
1142
|
|
|
1143
|
+
private isOutwardHorizontalFallback(label: NetLabelPlacement) {
|
|
1144
|
+
const chipSide = this.getChipSideForPoint(label.anchorPoint)
|
|
1145
|
+
return (
|
|
1146
|
+
(chipSide === "left" && label.orientation === "x-") ||
|
|
1147
|
+
(chipSide === "right" && label.orientation === "x+")
|
|
1148
|
+
)
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
private hasPortOnlyLabelOnSameNet(label: NetLabelPlacement) {
|
|
1152
|
+
return this.outputNetLabelPlacements.some(
|
|
1153
|
+
(otherLabel) =>
|
|
1154
|
+
otherLabel !== label &&
|
|
1155
|
+
otherLabel.globalConnNetId === label.globalConnNetId &&
|
|
1156
|
+
this.isPortOnlyLabel(otherLabel),
|
|
1157
|
+
)
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
private hasOppositeVerticalRailOnSameChipSide(
|
|
1161
|
+
label: NetLabelPlacement,
|
|
1162
|
+
orientation: "y+" | "y-",
|
|
1163
|
+
) {
|
|
1164
|
+
const currentConnection = this.inputProblem.netConnections.find(
|
|
1165
|
+
(connection) => connection.netId === label.netId,
|
|
1166
|
+
)
|
|
1167
|
+
if (!currentConnection) return false
|
|
1168
|
+
|
|
1169
|
+
const currentSide =
|
|
1170
|
+
this.getSingleChipSideForNetConnection(currentConnection)
|
|
1171
|
+
if (!currentSide) return false
|
|
1172
|
+
|
|
1173
|
+
const oppositeOrientation = orientation === "y+" ? "y-" : "y+"
|
|
1174
|
+
return this.inputProblem.netConnections.some((connection) => {
|
|
1175
|
+
if (connection === currentConnection || connection.pinIds.length < 2) {
|
|
1176
|
+
return false
|
|
1177
|
+
}
|
|
1178
|
+
const availableOrientations =
|
|
1179
|
+
this.inputProblem.availableNetLabelOrientations[connection.netId] ?? []
|
|
1180
|
+
if (
|
|
1181
|
+
availableOrientations.length !== 1 ||
|
|
1182
|
+
availableOrientations[0] !== oppositeOrientation
|
|
1183
|
+
) {
|
|
1184
|
+
return false
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
const connectionSide = this.getSingleChipSideForNetConnection(connection)
|
|
1188
|
+
return (
|
|
1189
|
+
connectionSide?.chipId === currentSide.chipId &&
|
|
1190
|
+
connectionSide.side === currentSide.side
|
|
1191
|
+
)
|
|
1192
|
+
})
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
private getSingleChipSideForNetConnection(
|
|
1196
|
+
connection: InputNetConnection,
|
|
1197
|
+
): { chipId: string; side: "left" | "right" } | null {
|
|
1198
|
+
if (connection.pinIds.length < 2) return null
|
|
1199
|
+
|
|
1200
|
+
const pins = connection.pinIds.map((pinId) => this.pinMap[pinId])
|
|
1201
|
+
if (pins.some((pin) => !pin)) return null
|
|
1202
|
+
const chipIds = new Set(pins.map((pin) => pin!.chipId))
|
|
1203
|
+
if (chipIds.size !== 1) return null
|
|
1204
|
+
|
|
1205
|
+
const chipId = pins[0]!.chipId
|
|
1206
|
+
const chip = this.chipObstacleSpatialIndex.chips.find(
|
|
1207
|
+
(candidate) => candidate.chipId === chipId,
|
|
1208
|
+
)
|
|
1209
|
+
if (!chip) return null
|
|
1210
|
+
|
|
1211
|
+
const sides = new Set(
|
|
1212
|
+
pins.map((pin) => {
|
|
1213
|
+
if (Math.abs(pin!.x - chip.bounds.minX) <= EPS) return "left"
|
|
1214
|
+
if (Math.abs(pin!.x - chip.bounds.maxX) <= EPS) return "right"
|
|
1215
|
+
return null
|
|
1216
|
+
}),
|
|
1217
|
+
)
|
|
1218
|
+
if (sides.size !== 1 || sides.has(null)) return null
|
|
1219
|
+
|
|
1220
|
+
return {
|
|
1221
|
+
chipId,
|
|
1222
|
+
side: [...sides][0] as "left" | "right",
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1032
1226
|
private isPortOnlyLabel(label: NetLabelPlacement) {
|
|
1033
1227
|
return label.mspConnectionPairIds.length === 0
|
|
1034
1228
|
}
|
|
@@ -22,6 +22,7 @@ export type ChipSide = "left" | "right" | "top" | "bottom"
|
|
|
22
22
|
export type CandidateLabel = {
|
|
23
23
|
orientation: FacingDirection
|
|
24
24
|
anchorPoint: Point
|
|
25
|
+
connectorSource?: Point
|
|
25
26
|
center: Point
|
|
26
27
|
width: number
|
|
27
28
|
height: number
|
|
@@ -38,6 +39,7 @@ export type CandidateStatus =
|
|
|
38
39
|
export type CandidatePhase =
|
|
39
40
|
| "rotate"
|
|
40
41
|
| "trace-anchor"
|
|
42
|
+
| "outward-trace-anchor"
|
|
41
43
|
| "shift"
|
|
42
44
|
| "lateral-shift"
|
|
43
45
|
|