@tscircuit/schematic-trace-solver 0.0.185 → 0.0.187
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 -16
- package/lib/solvers/AvailableNetOrientationSolver/AvailableNetOrientationSolver.ts +129 -10
- package/lib/solvers/SameNetJunctionAlignmentSolver/alignSameNetJunctions.ts +14 -11
- package/package.json +1 -1
- package/site/bug-reports/bug-report-20260907T083336Z.page.tsx +4 -0
- 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-20260907T083336Z/__snapshots__/bug-report-20260907T083336Z.snap.svg +520 -0
- package/tests/bug-reports/bug-report-20260907T083336Z/bug-report-20260907T083336Z.json +2031 -0
- package/tests/bug-reports/bug-report-20260907T083336Z/bug-report-20260907T083336Z.test.ts +27 -0
- package/tests/repros/__snapshots__/board-1273-trace-overlap-cycle.snap.svg +2 -2
- 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/SameNetJunctionAlignmentSolver/parallel-load-rails.test.ts +24 -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;
|
|
@@ -13829,17 +13915,16 @@ var pathsIntersect = (firstPath, secondPath) => {
|
|
|
13829
13915
|
}
|
|
13830
13916
|
return false;
|
|
13831
13917
|
};
|
|
13832
|
-
var
|
|
13918
|
+
var preserveAttachedTraceJunctions = ({
|
|
13833
13919
|
originalTrace,
|
|
13834
13920
|
candidateTrace,
|
|
13835
|
-
traces
|
|
13836
|
-
netLabelConnectorTraceIds
|
|
13921
|
+
traces
|
|
13837
13922
|
}) => {
|
|
13838
13923
|
let outputTraces = traces.map(
|
|
13839
13924
|
(trace) => trace.mspPairId === originalTrace.mspPairId ? candidateTrace : trace
|
|
13840
13925
|
);
|
|
13841
13926
|
for (const connector of traces) {
|
|
13842
|
-
if (connector.mspPairId === originalTrace.mspPairId ||
|
|
13927
|
+
if (connector.mspPairId === originalTrace.mspPairId || connector.globalConnNetId !== originalTrace.globalConnNetId || !pathsIntersect(originalTrace.tracePath, connector.tracePath) || pathsIntersect(candidateTrace.tracePath, connector.tracePath)) {
|
|
13843
13928
|
continue;
|
|
13844
13929
|
}
|
|
13845
13930
|
const endpointIndex = [0, connector.tracePath.length - 1].find(
|
|
@@ -13850,6 +13935,11 @@ var preserveAttachedLabelConnectorJunctions = ({
|
|
|
13850
13935
|
);
|
|
13851
13936
|
if (endpointIndex === void 0) return null;
|
|
13852
13937
|
const originalJunction = connector.tracePath[endpointIndex];
|
|
13938
|
+
if (connector.pins.some(
|
|
13939
|
+
(pin) => nearlyEqual(pin.x, originalJunction.x) && nearlyEqual(pin.y, originalJunction.y)
|
|
13940
|
+
)) {
|
|
13941
|
+
return null;
|
|
13942
|
+
}
|
|
13853
13943
|
const movedJunction = getMovedAnchorPointForReroute(
|
|
13854
13944
|
originalJunction,
|
|
13855
13945
|
originalTrace.tracePath,
|
|
@@ -14228,11 +14318,10 @@ var alignSameNetJunctions = ({
|
|
|
14228
14318
|
netLabelPlacements: outputNetLabelPlacements,
|
|
14229
14319
|
attachedLabelIndexes
|
|
14230
14320
|
});
|
|
14231
|
-
const candidateTraces =
|
|
14321
|
+
const candidateTraces = preserveAttachedTraceJunctions({
|
|
14232
14322
|
originalTrace: branchTrace,
|
|
14233
14323
|
candidateTrace,
|
|
14234
|
-
traces: outputTraces
|
|
14235
|
-
netLabelConnectorTraceIds
|
|
14324
|
+
traces: outputTraces
|
|
14236
14325
|
});
|
|
14237
14326
|
if (!candidateTraces) continue;
|
|
14238
14327
|
if (!candidateIsClear({
|
|
@@ -14247,7 +14336,7 @@ var alignSameNetJunctions = ({
|
|
|
14247
14336
|
continue;
|
|
14248
14337
|
}
|
|
14249
14338
|
const movedConnectorsAreClear = candidateTraces.every((trace) => {
|
|
14250
|
-
if (trace.mspPairId === branchTrace.mspPairId
|
|
14339
|
+
if (trace.mspPairId === branchTrace.mspPairId) {
|
|
14251
14340
|
return true;
|
|
14252
14341
|
}
|
|
14253
14342
|
const originalConnector = outputTraces.find(
|
|
@@ -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)
|
|
@@ -648,20 +648,18 @@ const pathsIntersect = (firstPath: Point[], secondPath: Point[]) => {
|
|
|
648
648
|
}
|
|
649
649
|
|
|
650
650
|
/**
|
|
651
|
-
* Keep
|
|
651
|
+
* Keep same-net branches joined when their host trace moves. Only a
|
|
652
652
|
* connector endpoint is projected, and only along its existing first/last
|
|
653
653
|
* segment, so the label and the rest of its route stay fixed.
|
|
654
654
|
*/
|
|
655
|
-
const
|
|
655
|
+
const preserveAttachedTraceJunctions = ({
|
|
656
656
|
originalTrace,
|
|
657
657
|
candidateTrace,
|
|
658
658
|
traces,
|
|
659
|
-
netLabelConnectorTraceIds,
|
|
660
659
|
}: {
|
|
661
660
|
originalTrace: SolvedTracePath
|
|
662
661
|
candidateTrace: SolvedTracePath
|
|
663
662
|
traces: SolvedTracePath[]
|
|
664
|
-
netLabelConnectorTraceIds: ReadonlySet<MspConnectionPairId>
|
|
665
663
|
}): SolvedTracePath[] | null => {
|
|
666
664
|
let outputTraces = traces.map((trace) =>
|
|
667
665
|
trace.mspPairId === originalTrace.mspPairId ? candidateTrace : trace,
|
|
@@ -670,7 +668,6 @@ const preserveAttachedLabelConnectorJunctions = ({
|
|
|
670
668
|
for (const connector of traces) {
|
|
671
669
|
if (
|
|
672
670
|
connector.mspPairId === originalTrace.mspPairId ||
|
|
673
|
-
!netLabelConnectorTraceIds.has(connector.mspPairId) ||
|
|
674
671
|
connector.globalConnNetId !== originalTrace.globalConnNetId ||
|
|
675
672
|
!pathsIntersect(originalTrace.tracePath, connector.tracePath) ||
|
|
676
673
|
pathsIntersect(candidateTrace.tracePath, connector.tracePath)
|
|
@@ -686,6 +683,16 @@ const preserveAttachedLabelConnectorJunctions = ({
|
|
|
686
683
|
)
|
|
687
684
|
if (endpointIndex === undefined) return null
|
|
688
685
|
const originalJunction = connector.tracePath[endpointIndex]!
|
|
686
|
+
// Component pins are fixed; reject a rail move that would detach one.
|
|
687
|
+
if (
|
|
688
|
+
connector.pins.some(
|
|
689
|
+
(pin) =>
|
|
690
|
+
nearlyEqual(pin.x, originalJunction.x) &&
|
|
691
|
+
nearlyEqual(pin.y, originalJunction.y),
|
|
692
|
+
)
|
|
693
|
+
) {
|
|
694
|
+
return null
|
|
695
|
+
}
|
|
689
696
|
const movedJunction = getMovedAnchorPointForReroute(
|
|
690
697
|
originalJunction,
|
|
691
698
|
originalTrace.tracePath,
|
|
@@ -1250,11 +1257,10 @@ export const alignSameNetJunctions = ({
|
|
|
1250
1257
|
netLabelPlacements: outputNetLabelPlacements,
|
|
1251
1258
|
attachedLabelIndexes,
|
|
1252
1259
|
})
|
|
1253
|
-
const candidateTraces =
|
|
1260
|
+
const candidateTraces = preserveAttachedTraceJunctions({
|
|
1254
1261
|
originalTrace: branchTrace,
|
|
1255
1262
|
candidateTrace,
|
|
1256
1263
|
traces: outputTraces,
|
|
1257
|
-
netLabelConnectorTraceIds,
|
|
1258
1264
|
})
|
|
1259
1265
|
if (!candidateTraces) continue
|
|
1260
1266
|
if (
|
|
@@ -1272,10 +1278,7 @@ export const alignSameNetJunctions = ({
|
|
|
1272
1278
|
}
|
|
1273
1279
|
|
|
1274
1280
|
const movedConnectorsAreClear = candidateTraces.every((trace) => {
|
|
1275
|
-
if (
|
|
1276
|
-
trace.mspPairId === branchTrace.mspPairId ||
|
|
1277
|
-
!netLabelConnectorTraceIds.has(trace.mspPairId)
|
|
1278
|
-
) {
|
|
1281
|
+
if (trace.mspPairId === branchTrace.mspPairId) {
|
|
1279
1282
|
return true
|
|
1280
1283
|
}
|
|
1281
1284
|
const originalConnector = outputTraces.find(
|
package/package.json
CHANGED