@tscircuit/schematic-trace-solver 0.0.184 → 0.0.186
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 +10 -0
- package/dist/index.js +105 -12
- package/lib/solvers/AvailableNetOrientationSolver/AvailableNetOrientationSolver.ts +129 -10
- package/lib/solvers/UnroutedTraceRecoverySolver/UnroutedTraceRecoverySolver.ts +12 -4
- package/package.json +1 -1
- package/site/bug-reports/bug-report-20260905T041712Z.page.tsx +4 -0
- package/tests/bug-reports/bug-report-20260707T092615Z/__snapshots__/bug-report-20260707T092615Z.snap.svg +524 -533
- package/tests/bug-reports/bug-report-20260901T064117Z/__snapshots__/bug-report-20260901T064117Z.snap.svg +59 -71
- package/tests/bug-reports/bug-report-20260902T092425Z/__snapshots__/bug-report-20260902T092425Z.snap.svg +28 -28
- package/tests/bug-reports/bug-report-20260902T092425Z/bug-report-20260902T092425Z.test.ts +39 -0
- package/tests/bug-reports/bug-report-20260905T041712Z/__snapshots__/bug-report-20260905T041712Z.snap.svg +546 -0
- package/tests/bug-reports/bug-report-20260905T041712Z/bug-report-20260905T041712Z.json +2023 -0
- package/tests/bug-reports/bug-report-20260905T041712Z/bug-report-20260905T041712Z.test.ts +33 -0
- package/tests/repros/__snapshots__/repro-trellis-core-gnd-net-label-overlap.snap.svg +59 -71
- package/tests/repros/repro-trellis-core-gnd-net-label-overlap.test.ts +58 -13
- package/tests/solvers/UnroutedTraceRecoverySolver/local-elbow-obstacles.test.ts +74 -0
package/dist/index.d.ts
CHANGED
|
@@ -936,6 +936,16 @@ declare class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
936
936
|
private hasOppositeVerticalRailOnSameChipSide;
|
|
937
937
|
private getSingleChipSideForNetConnection;
|
|
938
938
|
private isPortOnlyLabel;
|
|
939
|
+
private getNetConnectionForLabel;
|
|
940
|
+
private isGroundLabel;
|
|
941
|
+
/**
|
|
942
|
+
* A vertical-only input stores its text extent in netLabelHeight. If its
|
|
943
|
+
* initial port-only placement is horizontal, use that extent for collision
|
|
944
|
+
* checks without changing the placement dimensions returned by the solver.
|
|
945
|
+
*/
|
|
946
|
+
private getHorizontalConstraintMismatchBounds;
|
|
947
|
+
private includeHorizontalConstraintMismatches;
|
|
948
|
+
private getLabelBoundsForCollisionWithGround;
|
|
939
949
|
private shouldIgnorePendingCrowdedLabel;
|
|
940
950
|
private intersectsAnyOtherNetLabel;
|
|
941
951
|
private sharesChipBoundary;
|
package/dist/index.js
CHANGED
|
@@ -9413,6 +9413,17 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
9413
9413
|
if (this.getTextBlockedMultiPinRailTraces(label, orientations[0])) {
|
|
9414
9414
|
return true;
|
|
9415
9415
|
}
|
|
9416
|
+
const hasSingleVerticalOrientation = orientations.length === 1 && isYOrientation(orientations[0]);
|
|
9417
|
+
if (hasSingleVerticalOrientation && this.isGroundLabel(label)) {
|
|
9418
|
+
const bounds2 = getRectBounds(label.center, label.width, label.height);
|
|
9419
|
+
const overlapsHorizontalConstraintMismatch = this.outputNetLabelPlacements.some((otherLabel, otherIndex) => {
|
|
9420
|
+
if (otherIndex === labelIndex) return false;
|
|
9421
|
+
if (otherLabel.globalConnNetId === label.globalConnNetId) return false;
|
|
9422
|
+
const otherBounds = this.getHorizontalConstraintMismatchBounds(otherLabel);
|
|
9423
|
+
return otherBounds ? rectsOverlap2(bounds2, otherBounds) : false;
|
|
9424
|
+
});
|
|
9425
|
+
if (overlapsHorizontalConstraintMismatch) return true;
|
|
9426
|
+
}
|
|
9416
9427
|
if (!this.crowdedPortOnlyLabelIndices.has(labelIndex)) return false;
|
|
9417
9428
|
const bounds = getRectBounds(label.center, label.width, label.height);
|
|
9418
9429
|
return this.outputNetLabelPlacements.some((otherLabel, otherIndex) => {
|
|
@@ -10386,15 +10397,17 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
10386
10397
|
return "text-collision";
|
|
10387
10398
|
}
|
|
10388
10399
|
}
|
|
10389
|
-
|
|
10390
|
-
connectorTrace
|
|
10391
|
-
|
|
10400
|
+
const nearbyLabelIndices = this.includeHorizontalConstraintMismatches(
|
|
10401
|
+
this.obstacleIndex.getLabelIndicesNearTracePath(connectorTrace),
|
|
10402
|
+
this.isGroundLabel(label)
|
|
10403
|
+
);
|
|
10404
|
+
for (const i of nearbyLabelIndices) {
|
|
10392
10405
|
if (i === labelIndex) continue;
|
|
10393
10406
|
if (this.shouldIgnorePendingCrowdedLabel(labelIndex, i)) continue;
|
|
10394
10407
|
const otherLabel = this.outputNetLabelPlacements[i];
|
|
10395
10408
|
if (tracePathIntersectsBounds(
|
|
10396
10409
|
connectorTrace,
|
|
10397
|
-
|
|
10410
|
+
this.getLabelBoundsForCollisionWithGround(otherLabel, label)
|
|
10398
10411
|
)) {
|
|
10399
10412
|
return "netlabel-collision";
|
|
10400
10413
|
}
|
|
@@ -10445,7 +10458,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
10445
10458
|
if (this.isTraceTooCloseToLabel(bounds, label)) {
|
|
10446
10459
|
return "trace-clearance-violation";
|
|
10447
10460
|
}
|
|
10448
|
-
if (this.intersectsAnyOtherNetLabel(bounds, labelIndex)) {
|
|
10461
|
+
if (this.intersectsAnyOtherNetLabel(bounds, labelIndex, label)) {
|
|
10449
10462
|
return "netlabel-collision";
|
|
10450
10463
|
}
|
|
10451
10464
|
return "valid";
|
|
@@ -10645,22 +10658,95 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
10645
10658
|
isPortOnlyLabel(label) {
|
|
10646
10659
|
return label.mspConnectionPairIds.length === 0;
|
|
10647
10660
|
}
|
|
10661
|
+
getNetConnectionForLabel(label) {
|
|
10662
|
+
return label.netId ? this.inputProblem.netConnections.find(
|
|
10663
|
+
(connection) => connection.netId === label.netId
|
|
10664
|
+
) : this.inputProblem.netConnections.find(
|
|
10665
|
+
(connection) => connection.pinIds.some((pinId) => label.pinIds.includes(pinId))
|
|
10666
|
+
);
|
|
10667
|
+
}
|
|
10668
|
+
isGroundLabel(label) {
|
|
10669
|
+
return this.getNetConnectionForLabel(label)?.isGround === true;
|
|
10670
|
+
}
|
|
10671
|
+
/**
|
|
10672
|
+
* A vertical-only input stores its text extent in netLabelHeight. If its
|
|
10673
|
+
* initial port-only placement is horizontal, use that extent for collision
|
|
10674
|
+
* checks without changing the placement dimensions returned by the solver.
|
|
10675
|
+
*/
|
|
10676
|
+
getHorizontalConstraintMismatchBounds(label) {
|
|
10677
|
+
if (!this.isPortOnlyLabel(label)) return null;
|
|
10678
|
+
if (label.orientation !== "x+" && label.orientation !== "x-") return null;
|
|
10679
|
+
const effectiveNetId = label.netId ?? label.globalConnNetId;
|
|
10680
|
+
const orientations = this.inputProblem.availableNetLabelOrientations[effectiveNetId] ?? [];
|
|
10681
|
+
if (orientations.length === 0 || orientations.some((orientation) => !isYOrientation(orientation))) {
|
|
10682
|
+
return null;
|
|
10683
|
+
}
|
|
10684
|
+
const renderedWidth = this.getNetConnectionForLabel(label)?.netLabelHeight;
|
|
10685
|
+
if (renderedWidth === void 0 || renderedWidth <= label.width + label.height + EPS13) {
|
|
10686
|
+
return null;
|
|
10687
|
+
}
|
|
10688
|
+
const placementBaseCenter = getCenterFromAnchor(
|
|
10689
|
+
label.anchorPoint,
|
|
10690
|
+
label.orientation,
|
|
10691
|
+
label.width,
|
|
10692
|
+
label.height
|
|
10693
|
+
);
|
|
10694
|
+
const renderedBaseCenter = getCenterFromAnchor(
|
|
10695
|
+
label.anchorPoint,
|
|
10696
|
+
label.orientation,
|
|
10697
|
+
renderedWidth,
|
|
10698
|
+
label.height
|
|
10699
|
+
);
|
|
10700
|
+
return getRectBounds(
|
|
10701
|
+
{
|
|
10702
|
+
x: renderedBaseCenter.x + label.center.x - placementBaseCenter.x,
|
|
10703
|
+
y: renderedBaseCenter.y + label.center.y - placementBaseCenter.y
|
|
10704
|
+
},
|
|
10705
|
+
renderedWidth,
|
|
10706
|
+
label.height
|
|
10707
|
+
);
|
|
10708
|
+
}
|
|
10709
|
+
includeHorizontalConstraintMismatches(labelIndices, include) {
|
|
10710
|
+
if (!include) return [...labelIndices].sort((a, b) => a - b);
|
|
10711
|
+
const result = new Set(labelIndices);
|
|
10712
|
+
for (let index = 0; index < this.outputNetLabelPlacements.length; index++) {
|
|
10713
|
+
if (this.getHorizontalConstraintMismatchBounds(
|
|
10714
|
+
this.outputNetLabelPlacements[index]
|
|
10715
|
+
)) {
|
|
10716
|
+
result.add(index);
|
|
10717
|
+
}
|
|
10718
|
+
}
|
|
10719
|
+
return [...result].sort((a, b) => a - b);
|
|
10720
|
+
}
|
|
10721
|
+
getLabelBoundsForCollisionWithGround(label, currentLabel) {
|
|
10722
|
+
if (this.isGroundLabel(currentLabel)) {
|
|
10723
|
+
const adjustedBounds = this.getHorizontalConstraintMismatchBounds(label);
|
|
10724
|
+
if (adjustedBounds) return adjustedBounds;
|
|
10725
|
+
}
|
|
10726
|
+
return getRectBounds(label.center, label.width, label.height);
|
|
10727
|
+
}
|
|
10648
10728
|
shouldIgnorePendingCrowdedLabel(labelIndex, otherLabelIndex) {
|
|
10649
10729
|
return this.crowdedPortOnlyLabelIndices.has(labelIndex) && this.crowdedPortOnlyLabelIndices.has(otherLabelIndex) && this.queuedLabelIndices.includes(otherLabelIndex);
|
|
10650
10730
|
}
|
|
10651
|
-
intersectsAnyOtherNetLabel(bounds, labelIndex) {
|
|
10731
|
+
intersectsAnyOtherNetLabel(bounds, labelIndex, currentLabel) {
|
|
10652
10732
|
const searchBounds = {
|
|
10653
10733
|
minX: bounds.minX - LABEL_SEARCH_STEP,
|
|
10654
10734
|
minY: bounds.minY - LABEL_SEARCH_STEP,
|
|
10655
10735
|
maxX: bounds.maxX + LABEL_SEARCH_STEP,
|
|
10656
10736
|
maxY: bounds.maxY + LABEL_SEARCH_STEP
|
|
10657
10737
|
};
|
|
10658
|
-
const nearbyLabelIndices = this.
|
|
10738
|
+
const nearbyLabelIndices = this.includeHorizontalConstraintMismatches(
|
|
10739
|
+
this.obstacleIndex.getLabelIndicesInBounds(searchBounds),
|
|
10740
|
+
this.isGroundLabel(currentLabel)
|
|
10741
|
+
);
|
|
10659
10742
|
for (const i of nearbyLabelIndices) {
|
|
10660
10743
|
if (i === labelIndex) continue;
|
|
10661
10744
|
if (this.shouldIgnorePendingCrowdedLabel(labelIndex, i)) continue;
|
|
10662
10745
|
const label = this.outputNetLabelPlacements[i];
|
|
10663
|
-
const otherBounds =
|
|
10746
|
+
const otherBounds = this.getLabelBoundsForCollisionWithGround(
|
|
10747
|
+
label,
|
|
10748
|
+
currentLabel
|
|
10749
|
+
);
|
|
10664
10750
|
if (i === this.blockedStandaloneLabelIndex && isYOrientation(label.orientation)) {
|
|
10665
10751
|
otherBounds.minX -= LABEL_SEARCH_STEP;
|
|
10666
10752
|
otherBounds.maxX += LABEL_SEARCH_STEP;
|
|
@@ -12659,6 +12745,7 @@ ${c.status}`
|
|
|
12659
12745
|
|
|
12660
12746
|
// lib/solvers/UnroutedTraceRecoverySolver/UnroutedTraceRecoverySolver.ts
|
|
12661
12747
|
import { distance as distance6, doSegmentsIntersect as doSegmentsIntersect3 } from "@tscircuit/math-utils";
|
|
12748
|
+
import { calculateElbow as calculateElbow3 } from "calculate-elbow";
|
|
12662
12749
|
var ROUTE_CLEARANCE = 0.2;
|
|
12663
12750
|
var COORDINATE_TOLERANCE = 1e-9;
|
|
12664
12751
|
var GROUND_NET_ID = "GND";
|
|
@@ -12711,7 +12798,7 @@ var getOuterBounds = (obstacles) => {
|
|
|
12711
12798
|
maxY: Math.max(...obstacles.map((obstacle) => obstacle.maxY))
|
|
12712
12799
|
};
|
|
12713
12800
|
};
|
|
12714
|
-
var
|
|
12801
|
+
var getPinConnectionCandidates = ({
|
|
12715
12802
|
connectionPair,
|
|
12716
12803
|
obstacles
|
|
12717
12804
|
}) => {
|
|
@@ -12733,7 +12820,13 @@ var getPerimeterCandidates = ({
|
|
|
12733
12820
|
outerBounds.minX - ROUTE_CLEARANCE,
|
|
12734
12821
|
outerBounds.maxX + ROUTE_CLEARANCE
|
|
12735
12822
|
];
|
|
12736
|
-
const candidates = [
|
|
12823
|
+
const candidates = [
|
|
12824
|
+
calculateElbow3(
|
|
12825
|
+
{ ...firstPin, facingDirection: firstPin._facingDirection },
|
|
12826
|
+
{ ...secondPin, facingDirection: secondPin._facingDirection },
|
|
12827
|
+
{ overshoot: ROUTE_CLEARANCE }
|
|
12828
|
+
)
|
|
12829
|
+
];
|
|
12737
12830
|
for (const channelY of horizontalChannels) {
|
|
12738
12831
|
candidates.push(
|
|
12739
12832
|
removeConsecutiveDuplicatePoints([
|
|
@@ -13053,11 +13146,11 @@ var UnroutedTraceRecoverySolver = class extends BaseSolver {
|
|
|
13053
13146
|
obstacles,
|
|
13054
13147
|
maxConnectionDistance: this.maxConnectionDistance
|
|
13055
13148
|
});
|
|
13056
|
-
const
|
|
13149
|
+
const pinConnectionCandidates = getPinConnectionCandidates({
|
|
13057
13150
|
connectionPair,
|
|
13058
13151
|
obstacles
|
|
13059
13152
|
});
|
|
13060
|
-
const candidates = [...junctionCandidates, ...
|
|
13153
|
+
const candidates = [...junctionCandidates, ...pinConnectionCandidates];
|
|
13061
13154
|
const rejectComponentBoundaryTravel = hasParallelFailedConnection({
|
|
13062
13155
|
connectionPair,
|
|
13063
13156
|
failedConnectionPairs: this.failedConnectionPairs
|
|
@@ -268,6 +268,22 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
268
268
|
if (this.getTextBlockedMultiPinRailTraces(label, orientations[0]!)) {
|
|
269
269
|
return true
|
|
270
270
|
}
|
|
271
|
+
|
|
272
|
+
const hasSingleVerticalOrientation =
|
|
273
|
+
orientations.length === 1 && isYOrientation(orientations[0]!)
|
|
274
|
+
if (hasSingleVerticalOrientation && this.isGroundLabel(label)) {
|
|
275
|
+
const bounds = getRectBounds(label.center, label.width, label.height)
|
|
276
|
+
const overlapsHorizontalConstraintMismatch =
|
|
277
|
+
this.outputNetLabelPlacements.some((otherLabel, otherIndex) => {
|
|
278
|
+
if (otherIndex === labelIndex) return false
|
|
279
|
+
if (otherLabel.globalConnNetId === label.globalConnNetId) return false
|
|
280
|
+
const otherBounds =
|
|
281
|
+
this.getHorizontalConstraintMismatchBounds(otherLabel)
|
|
282
|
+
return otherBounds ? rectsOverlap(bounds, otherBounds) : false
|
|
283
|
+
})
|
|
284
|
+
if (overlapsHorizontalConstraintMismatch) return true
|
|
285
|
+
}
|
|
286
|
+
|
|
271
287
|
if (!this.crowdedPortOnlyLabelIndices.has(labelIndex)) return false
|
|
272
288
|
|
|
273
289
|
const bounds = getRectBounds(label.center, label.width, label.height)
|
|
@@ -1627,16 +1643,18 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
1627
1643
|
}
|
|
1628
1644
|
}
|
|
1629
1645
|
|
|
1630
|
-
|
|
1631
|
-
connectorTrace,
|
|
1632
|
-
|
|
1646
|
+
const nearbyLabelIndices = this.includeHorizontalConstraintMismatches(
|
|
1647
|
+
this.obstacleIndex.getLabelIndicesNearTracePath(connectorTrace),
|
|
1648
|
+
this.isGroundLabel(label),
|
|
1649
|
+
)
|
|
1650
|
+
for (const i of nearbyLabelIndices) {
|
|
1633
1651
|
if (i === labelIndex) continue
|
|
1634
1652
|
if (this.shouldIgnorePendingCrowdedLabel(labelIndex, i)) continue
|
|
1635
1653
|
const otherLabel = this.outputNetLabelPlacements[i]!
|
|
1636
1654
|
if (
|
|
1637
1655
|
tracePathIntersectsBounds(
|
|
1638
1656
|
connectorTrace,
|
|
1639
|
-
|
|
1657
|
+
this.getLabelBoundsForCollisionWithGround(otherLabel, label),
|
|
1640
1658
|
)
|
|
1641
1659
|
) {
|
|
1642
1660
|
return "netlabel-collision"
|
|
@@ -1719,7 +1737,7 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
1719
1737
|
if (this.isTraceTooCloseToLabel(bounds, label)) {
|
|
1720
1738
|
return "trace-clearance-violation"
|
|
1721
1739
|
}
|
|
1722
|
-
if (this.intersectsAnyOtherNetLabel(bounds, labelIndex)) {
|
|
1740
|
+
if (this.intersectsAnyOtherNetLabel(bounds, labelIndex, label)) {
|
|
1723
1741
|
return "netlabel-collision"
|
|
1724
1742
|
}
|
|
1725
1743
|
return "valid"
|
|
@@ -2020,6 +2038,99 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
2020
2038
|
return label.mspConnectionPairIds.length === 0
|
|
2021
2039
|
}
|
|
2022
2040
|
|
|
2041
|
+
private getNetConnectionForLabel(label: NetLabelPlacement) {
|
|
2042
|
+
return label.netId
|
|
2043
|
+
? this.inputProblem.netConnections.find(
|
|
2044
|
+
(connection) => connection.netId === label.netId,
|
|
2045
|
+
)
|
|
2046
|
+
: this.inputProblem.netConnections.find((connection) =>
|
|
2047
|
+
connection.pinIds.some((pinId) => label.pinIds.includes(pinId)),
|
|
2048
|
+
)
|
|
2049
|
+
}
|
|
2050
|
+
|
|
2051
|
+
private isGroundLabel(label: NetLabelPlacement) {
|
|
2052
|
+
return this.getNetConnectionForLabel(label)?.isGround === true
|
|
2053
|
+
}
|
|
2054
|
+
|
|
2055
|
+
/**
|
|
2056
|
+
* A vertical-only input stores its text extent in netLabelHeight. If its
|
|
2057
|
+
* initial port-only placement is horizontal, use that extent for collision
|
|
2058
|
+
* checks without changing the placement dimensions returned by the solver.
|
|
2059
|
+
*/
|
|
2060
|
+
private getHorizontalConstraintMismatchBounds(label: NetLabelPlacement) {
|
|
2061
|
+
if (!this.isPortOnlyLabel(label)) return null
|
|
2062
|
+
if (label.orientation !== "x+" && label.orientation !== "x-") return null
|
|
2063
|
+
|
|
2064
|
+
const effectiveNetId = label.netId ?? label.globalConnNetId
|
|
2065
|
+
const orientations =
|
|
2066
|
+
this.inputProblem.availableNetLabelOrientations[effectiveNetId] ?? []
|
|
2067
|
+
if (
|
|
2068
|
+
orientations.length === 0 ||
|
|
2069
|
+
orientations.some((orientation) => !isYOrientation(orientation))
|
|
2070
|
+
) {
|
|
2071
|
+
return null
|
|
2072
|
+
}
|
|
2073
|
+
|
|
2074
|
+
const renderedWidth = this.getNetConnectionForLabel(label)?.netLabelHeight
|
|
2075
|
+
if (
|
|
2076
|
+
renderedWidth === undefined ||
|
|
2077
|
+
renderedWidth <= label.width + label.height + EPS
|
|
2078
|
+
) {
|
|
2079
|
+
return null
|
|
2080
|
+
}
|
|
2081
|
+
|
|
2082
|
+
const placementBaseCenter = getCenterFromAnchor(
|
|
2083
|
+
label.anchorPoint,
|
|
2084
|
+
label.orientation,
|
|
2085
|
+
label.width,
|
|
2086
|
+
label.height,
|
|
2087
|
+
)
|
|
2088
|
+
const renderedBaseCenter = getCenterFromAnchor(
|
|
2089
|
+
label.anchorPoint,
|
|
2090
|
+
label.orientation,
|
|
2091
|
+
renderedWidth,
|
|
2092
|
+
label.height,
|
|
2093
|
+
)
|
|
2094
|
+
return getRectBounds(
|
|
2095
|
+
{
|
|
2096
|
+
x: renderedBaseCenter.x + label.center.x - placementBaseCenter.x,
|
|
2097
|
+
y: renderedBaseCenter.y + label.center.y - placementBaseCenter.y,
|
|
2098
|
+
},
|
|
2099
|
+
renderedWidth,
|
|
2100
|
+
label.height,
|
|
2101
|
+
)
|
|
2102
|
+
}
|
|
2103
|
+
|
|
2104
|
+
private includeHorizontalConstraintMismatches(
|
|
2105
|
+
labelIndices: number[],
|
|
2106
|
+
include: boolean,
|
|
2107
|
+
) {
|
|
2108
|
+
if (!include) return [...labelIndices].sort((a, b) => a - b)
|
|
2109
|
+
|
|
2110
|
+
const result = new Set(labelIndices)
|
|
2111
|
+
for (let index = 0; index < this.outputNetLabelPlacements.length; index++) {
|
|
2112
|
+
if (
|
|
2113
|
+
this.getHorizontalConstraintMismatchBounds(
|
|
2114
|
+
this.outputNetLabelPlacements[index]!,
|
|
2115
|
+
)
|
|
2116
|
+
) {
|
|
2117
|
+
result.add(index)
|
|
2118
|
+
}
|
|
2119
|
+
}
|
|
2120
|
+
return [...result].sort((a, b) => a - b)
|
|
2121
|
+
}
|
|
2122
|
+
|
|
2123
|
+
private getLabelBoundsForCollisionWithGround(
|
|
2124
|
+
label: NetLabelPlacement,
|
|
2125
|
+
currentLabel: NetLabelPlacement,
|
|
2126
|
+
) {
|
|
2127
|
+
if (this.isGroundLabel(currentLabel)) {
|
|
2128
|
+
const adjustedBounds = this.getHorizontalConstraintMismatchBounds(label)
|
|
2129
|
+
if (adjustedBounds) return adjustedBounds
|
|
2130
|
+
}
|
|
2131
|
+
return getRectBounds(label.center, label.width, label.height)
|
|
2132
|
+
}
|
|
2133
|
+
|
|
2023
2134
|
private shouldIgnorePendingCrowdedLabel(
|
|
2024
2135
|
labelIndex: number,
|
|
2025
2136
|
otherLabelIndex: number,
|
|
@@ -2031,22 +2142,30 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
2031
2142
|
)
|
|
2032
2143
|
}
|
|
2033
2144
|
|
|
2034
|
-
private intersectsAnyOtherNetLabel(
|
|
2145
|
+
private intersectsAnyOtherNetLabel(
|
|
2146
|
+
bounds: Bounds,
|
|
2147
|
+
labelIndex: number,
|
|
2148
|
+
currentLabel: NetLabelPlacement,
|
|
2149
|
+
) {
|
|
2035
2150
|
const searchBounds = {
|
|
2036
2151
|
minX: bounds.minX - LABEL_SEARCH_STEP,
|
|
2037
2152
|
minY: bounds.minY - LABEL_SEARCH_STEP,
|
|
2038
2153
|
maxX: bounds.maxX + LABEL_SEARCH_STEP,
|
|
2039
2154
|
maxY: bounds.maxY + LABEL_SEARCH_STEP,
|
|
2040
2155
|
}
|
|
2041
|
-
const nearbyLabelIndices = this.
|
|
2042
|
-
.getLabelIndicesInBounds(searchBounds)
|
|
2043
|
-
.
|
|
2156
|
+
const nearbyLabelIndices = this.includeHorizontalConstraintMismatches(
|
|
2157
|
+
this.obstacleIndex.getLabelIndicesInBounds(searchBounds),
|
|
2158
|
+
this.isGroundLabel(currentLabel),
|
|
2159
|
+
)
|
|
2044
2160
|
|
|
2045
2161
|
for (const i of nearbyLabelIndices) {
|
|
2046
2162
|
if (i === labelIndex) continue
|
|
2047
2163
|
if (this.shouldIgnorePendingCrowdedLabel(labelIndex, i)) continue
|
|
2048
2164
|
const label = this.outputNetLabelPlacements[i]!
|
|
2049
|
-
const otherBounds =
|
|
2165
|
+
const otherBounds = this.getLabelBoundsForCollisionWithGround(
|
|
2166
|
+
label,
|
|
2167
|
+
currentLabel,
|
|
2168
|
+
)
|
|
2050
2169
|
if (
|
|
2051
2170
|
i === this.blockedStandaloneLabelIndex &&
|
|
2052
2171
|
isYOrientation(label.orientation)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Point } from "@tscircuit/math-utils"
|
|
2
2
|
import { distance, doSegmentsIntersect } from "@tscircuit/math-utils"
|
|
3
|
+
import { calculateElbow } from "calculate-elbow"
|
|
3
4
|
import { BaseSolver } from "lib/solvers/BaseSolver/BaseSolver"
|
|
4
5
|
import { getConnectivityMapsFromInputProblem } from "lib/solvers/MspConnectionPairSolver/getConnectivityMapFromInputProblem"
|
|
5
6
|
import {
|
|
@@ -86,7 +87,7 @@ const getOuterBounds = (obstacles: ObstacleRect[]) => {
|
|
|
86
87
|
}
|
|
87
88
|
}
|
|
88
89
|
|
|
89
|
-
const
|
|
90
|
+
const getPinConnectionCandidates = ({
|
|
90
91
|
connectionPair,
|
|
91
92
|
obstacles,
|
|
92
93
|
}: {
|
|
@@ -111,7 +112,14 @@ const getPerimeterCandidates = ({
|
|
|
111
112
|
outerBounds.minX - ROUTE_CLEARANCE,
|
|
112
113
|
outerBounds.maxX + ROUTE_CLEARANCE,
|
|
113
114
|
]
|
|
114
|
-
|
|
115
|
+
// Recovery uses actual text bounds, so try local elbows before the perimeter.
|
|
116
|
+
const candidates: Point[][] = [
|
|
117
|
+
calculateElbow(
|
|
118
|
+
{ ...firstPin, facingDirection: firstPin._facingDirection! },
|
|
119
|
+
{ ...secondPin, facingDirection: secondPin._facingDirection! },
|
|
120
|
+
{ overshoot: ROUTE_CLEARANCE },
|
|
121
|
+
),
|
|
122
|
+
]
|
|
115
123
|
|
|
116
124
|
for (const channelY of horizontalChannels) {
|
|
117
125
|
candidates.push(
|
|
@@ -526,11 +534,11 @@ export class UnroutedTraceRecoverySolver extends BaseSolver {
|
|
|
526
534
|
obstacles,
|
|
527
535
|
maxConnectionDistance: this.maxConnectionDistance,
|
|
528
536
|
})
|
|
529
|
-
const
|
|
537
|
+
const pinConnectionCandidates = getPinConnectionCandidates({
|
|
530
538
|
connectionPair,
|
|
531
539
|
obstacles,
|
|
532
540
|
})
|
|
533
|
-
const candidates = [...junctionCandidates, ...
|
|
541
|
+
const candidates = [...junctionCandidates, ...pinConnectionCandidates]
|
|
534
542
|
const rejectComponentBoundaryTravel = hasParallelFailedConnection({
|
|
535
543
|
connectionPair,
|
|
536
544
|
failedConnectionPairs: this.failedConnectionPairs,
|
package/package.json
CHANGED