@tscircuit/schematic-trace-solver 0.0.160 → 0.0.161
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 +3 -0
- package/dist/index.js +171 -8
- package/lib/solvers/AvailableNetOrientationSolver/AvailableNetOrientationSolver.ts +210 -6
- package/lib/solvers/TraceCleanupSolver/sameNetRailAlignment/getFixedLabelCoordinate.ts +44 -13
- package/package.json +1 -1
- package/tests/bug-reports/bug-report-20260706T220324Z/__snapshots__/bug-report-20260706T220324Z.snap.svg +1 -1
- package/tests/bug-reports/bug-report-20260728T144234Z/__snapshots__/bug-report-20260728T144234Z.snap.svg +1 -1
- package/tests/bug-reports/bug-report-20260728T225606Z/__snapshots__/bug-report-20260728T225606Z.snap.svg +79 -79
- package/tests/bug-reports/bug-report-20260730T061837Z/__snapshots__/bug-report-20260730T061837Z.snap.svg +85 -85
- package/tests/bug-reports/bug-report-20260731T052229Z/__snapshots__/bug-report-20260731T052229Z.snap.svg +1 -1
- package/tests/bug-reports/bug-report-20260804T095800Z/__snapshots__/bug-report-20260804T095800Z.snap.svg +163 -163
- package/tests/bug-reports/bug-report-20260804T171919Z/__snapshots__/bug-report-20260804T171919Z.snap.svg +547 -547
- package/tests/bug-reports/bug-report-20260804T225912Z/__snapshots__/bug-report-20260804T225912Z.snap.svg +13 -13
- package/tests/bug-reports/bug-report-20260805T061316Z/__snapshots__/bug-report-20260805T061316Z.snap.svg +267 -267
- package/tests/bug-reports/bug-report-20260806T061548Z/__snapshots__/bug-report-20260806T061548Z.snap.svg +267 -267
- package/tests/bug-reports/bug-report-20260806T093501Z/__snapshots__/bug-report-20260806T093501Z.snap.svg +109 -109
- package/tests/bug-reports/bug-report-20260811T075142Z/__snapshots__/bug-report-20260811T075142Z.snap.svg +1 -1
- package/tests/bug-reports/bug-report-20260815T073240Z/__snapshots__/bug-report-20260815T073240Z.snap.svg +49 -49
- package/tests/bug-reports/bug-report-20260819T091818Z/__snapshots__/bug-report-20260819T091818Z.snap.svg +1 -1
- package/tests/bug-reports/bug-report-20260825T045913Z/__snapshots__/bug-report-20260825T045913Z.snap.svg +103 -103
- package/tests/bug-reports/bug-report-20260825T103621Z/__snapshots__/bug-report-20260825T103621Z.snap.svg +103 -103
- package/tests/bug-reports/bug-report-20260826T072956Z/__snapshots__/bug-report-20260826T072956Z.snap.svg +163 -163
- package/tests/examples/__snapshots__/example51.snap.svg +568 -568
- package/tests/fixtures/convertSolverOutputToCircuitJson.test.ts +16 -1
- package/tests/fixtures/convertSolverOutputToCircuitJson.ts +23 -12
- package/tests/repros/__snapshots__/board-1273-trace-overlap-cycle.snap.svg +736 -736
- package/tests/repros/__snapshots__/board-15984-same-net-junction.snap.svg +79 -79
- package/tests/repros/__snapshots__/board1096-usb-label-overlap-iteration.snap.svg +127 -127
- package/tests/repros/__snapshots__/repro-bluetooth-controller-ground-decoupling-groups.snap.svg +106 -0
- package/tests/repros/__snapshots__/repro-board-589-regulator-section.snap.svg +31 -31
- package/tests/repros/__snapshots__/repro-board-648-esp12f-section.snap.svg +121 -121
- package/tests/repros/__snapshots__/repro-bq25895-cross-net-trace-overlap.snap.svg +43 -43
- package/tests/repros/__snapshots__/repro-core-555-power-cross-net-overlap.snap.svg +19 -19
- package/tests/repros/__snapshots__/repro-core-ground-inline-label-fallback.snap.svg +277 -0
- package/tests/repros/__snapshots__/repro-core-subcircuit-missing-ground.snap.svg +19 -19
- package/tests/repros/__snapshots__/repro-core-tb67s579ftg-inline-label-routing.snap.svg +7 -7
- package/tests/repros/__snapshots__/repro-dc-power-isolated-routing.snap.svg +37 -37
- package/tests/repros/__snapshots__/repro-hdc2080-ground-label-alignment.snap.svg +137 -0
- package/tests/repros/__snapshots__/repro-nrf52810-clock-routing.snap.svg +61 -61
- package/tests/repros/__snapshots__/repro-pga300-redundant-ground-traces.snap.svg +115 -115
- package/tests/repros/__snapshots__/repro-pmp11282-isolated-dcdc.snap.svg +743 -743
- package/tests/repros/__snapshots__/repro-rp2040-usb-cc2-ground-overlap.snap.svg +43 -43
- package/tests/repros/__snapshots__/repro-ti-power-output-section.snap.svg +1 -1
- package/tests/repros/__snapshots__/repro-tida010076-j4-ground-bus.snap.svg +17 -17
- package/tests/repros/__snapshots__/repro-tida010076-page02.snap.svg +1131 -1131
- package/tests/repros/__snapshots__/repro-type-c-16pin-self-connection.snap.svg +7 -7
- package/tests/repros/__snapshots__/repro-type-c-16pin-to-resistor.snap.svg +7 -7
- package/tests/repros/__snapshots__/repro-usb-power-vbus-label-detour.snap.svg +666 -666
- package/tests/repros/__snapshots__/repro161-power-section-autolayout.snap.svg +73 -73
- package/tests/repros/__snapshots__/repro48-555-timer-vcc-rail-label.snap.svg +73 -73
- package/tests/repros/assets/repro-bluetooth-controller-ground-decoupling-groups.input.json +214 -0
- package/tests/repros/assets/repro-core-ground-inline-label-fallback.input.json +863 -0
- package/tests/repros/repro-bluetooth-controller-ground-decoupling-groups.test.ts +18 -0
- package/tests/repros/repro-core-ground-inline-label-fallback.test.ts +34 -0
- package/tests/repros/repro-hdc2080-ground-label-alignment.test.ts +239 -0
- package/tests/repros/repro-tida010076-j4-ground-bus.test.ts +14 -10
- package/tests/repros/repro-tida010076-page02.test.ts +2 -2
- package/tests/solvers/InlineNetLabelSolver/__snapshots__/inline-net-label02.snap.svg +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -906,6 +906,9 @@ declare class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
906
906
|
private shouldCheckTraceClearanceForLabel;
|
|
907
907
|
private hasRoutedLabelOnSameNet;
|
|
908
908
|
private isOutwardHorizontalFallback;
|
|
909
|
+
private canPlaceVerticalLabelOnHostRail;
|
|
910
|
+
private getTextBlockedMultiPinRailTraces;
|
|
911
|
+
private findValidNearbyRailCandidate;
|
|
909
912
|
private hasPortOnlyLabelOnSameNet;
|
|
910
913
|
private hasOppositeVerticalRailOnSameChipSide;
|
|
911
914
|
private getSingleChipSideForNetConnection;
|
package/dist/index.js
CHANGED
|
@@ -5864,6 +5864,9 @@ var getLabelsLinkedToRailGroup = (group, netLabelPlacements, traces) => {
|
|
|
5864
5864
|
var getFixedLabelCoordinate = (group, netLabelPlacements, traces) => {
|
|
5865
5865
|
const traceMap = new Map(traces.map((trace) => [trace.mspPairId, trace]));
|
|
5866
5866
|
const groupTraceIds = new Set(group.map((segment) => segment.traceId));
|
|
5867
|
+
const groupIsWithinSingleComponent = [...groupTraceIds].every(
|
|
5868
|
+
(traceId) => traceMap.get(traceId)?.pins.every((pin) => pin.chipId === group[0].componentId) === true
|
|
5869
|
+
);
|
|
5867
5870
|
const groupHasOnlySingleRailTraces = [...groupTraceIds].every(
|
|
5868
5871
|
(traceId) => traceMap.get(traceId)?.tracePath.length === 4
|
|
5869
5872
|
);
|
|
@@ -5874,12 +5877,20 @@ var getFixedLabelCoordinate = (group, netLabelPlacements, traces) => {
|
|
|
5874
5877
|
netLabelPlacements,
|
|
5875
5878
|
traces
|
|
5876
5879
|
);
|
|
5877
|
-
const
|
|
5878
|
-
|
|
5880
|
+
const labelsOrientedAlongRail = new Set(
|
|
5881
|
+
linkedLabels.filter(
|
|
5882
|
+
(label) => orientation === "vertical" && (label.orientation === "y+" || label.orientation === "y-") || orientation === "horizontal" && (label.orientation === "x+" || label.orientation === "x-")
|
|
5883
|
+
)
|
|
5884
|
+
);
|
|
5885
|
+
const anchoringLabels = linkedLabels.filter((label) => {
|
|
5886
|
+
if (label.orientation !== group[0].componentFacingDirection && (!groupIsWithinSingleComponent || !labelsOrientedAlongRail.has(label))) {
|
|
5887
|
+
return false;
|
|
5888
|
+
}
|
|
5889
|
+
return label.mspConnectionPairIds.some((traceId) => {
|
|
5879
5890
|
const trace = traceMap.get(traceId);
|
|
5880
5891
|
return trace && tracePathContainsPoint(trace.tracePath, label.anchorPoint);
|
|
5881
|
-
})
|
|
5882
|
-
);
|
|
5892
|
+
});
|
|
5893
|
+
});
|
|
5883
5894
|
const coordinates = getDistinctCoordinates(
|
|
5884
5895
|
anchoringLabels.map(
|
|
5885
5896
|
(label) => orientation === "vertical" ? label.anchorPoint.x : label.anchorPoint.y
|
|
@@ -5896,12 +5907,21 @@ var getFixedLabelCoordinate = (group, netLabelPlacements, traces) => {
|
|
|
5896
5907
|
)
|
|
5897
5908
|
);
|
|
5898
5909
|
if (labelIsAnchoredToRoutedBackbone) return coordinate;
|
|
5910
|
+
if (anchoringLabels.some((label) => labelsOrientedAlongRail.has(label))) {
|
|
5911
|
+
const railGroupSpan = Math.max(...group.map((segment) => segment.maxAlong)) - Math.min(...group.map((segment) => segment.minAlong));
|
|
5912
|
+
const coordinateIsLocalToRailGroup = group.every(
|
|
5913
|
+
(segment) => Math.abs(segment.coordinate - coordinate) <= railGroupSpan + RAIL_ALIGNMENT_EPSILON
|
|
5914
|
+
);
|
|
5915
|
+
if (coordinateIsLocalToRailGroup) return coordinate;
|
|
5916
|
+
return null;
|
|
5917
|
+
}
|
|
5899
5918
|
const coordinateIsLocalToEveryTrace = group.every((segment) => {
|
|
5900
5919
|
const trace = traceMap.get(segment.traceId);
|
|
5901
5920
|
if (!trace) return false;
|
|
5902
5921
|
return Math.abs(segment.coordinate - coordinate) <= distance4(trace.pins[0], trace.pins[1]) + RAIL_ALIGNMENT_EPSILON;
|
|
5903
5922
|
});
|
|
5904
|
-
|
|
5923
|
+
if (coordinateIsLocalToEveryTrace) return coordinate;
|
|
5924
|
+
return null;
|
|
5905
5925
|
};
|
|
5906
5926
|
|
|
5907
5927
|
// lib/solvers/TraceCleanupSolver/sameNetRailAlignment/moveRailSegments.ts
|
|
@@ -8939,6 +8959,9 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
8939
8959
|
const orientations = this.getAvailableOrientations(label);
|
|
8940
8960
|
if (orientations.length === 0) return false;
|
|
8941
8961
|
if (!orientations.includes(label.orientation)) return true;
|
|
8962
|
+
if (this.getTextBlockedMultiPinRailTraces(label, orientations[0])) {
|
|
8963
|
+
return true;
|
|
8964
|
+
}
|
|
8942
8965
|
if (!this.crowdedPortOnlyLabelIndices.has(labelIndex)) return false;
|
|
8943
8966
|
const bounds = getRectBounds(label.center, label.width, label.height);
|
|
8944
8967
|
return this.outputNetLabelPlacements.some((otherLabel, otherIndex) => {
|
|
@@ -9060,8 +9083,34 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
9060
9083
|
);
|
|
9061
9084
|
const isTwoPinNet = netConnection?.pinIds.length === 2;
|
|
9062
9085
|
const isDistanceSplitVerticalRail = (netConnection?.pinIds.length ?? 0) > 2 && isYOrientation(requiredOrientation) && this.hasPortOnlyLabelOnSameNet(label);
|
|
9086
|
+
const textBlockedMultiPinRailTraces = this.getTextBlockedMultiPinRailTraces(
|
|
9087
|
+
label,
|
|
9088
|
+
requiredOrientation
|
|
9089
|
+
);
|
|
9063
9090
|
const isPairedSameSidePowerRail = isYOrientation(requiredOrientation) && this.hasOppositeVerticalRailOnSameChipSide(label, requiredOrientation);
|
|
9091
|
+
if (textBlockedMultiPinRailTraces) {
|
|
9092
|
+
const nearbyCandidate = this.findValidNearbyRailCandidate(
|
|
9093
|
+
label,
|
|
9094
|
+
requiredOrientation,
|
|
9095
|
+
labelIndex,
|
|
9096
|
+
textBlockedMultiPinRailTraces
|
|
9097
|
+
);
|
|
9098
|
+
if (nearbyCandidate) return nearbyCandidate;
|
|
9099
|
+
}
|
|
9064
9100
|
if (orientations.length === 1 && isYOrientation(requiredOrientation) && this.isOutwardHorizontalFallback(label) && this.hasTraceContinuingInOrientation(label, requiredOrientation) && (isDistanceSplitVerticalRail || isPairedSameSidePowerRail)) {
|
|
9101
|
+
const hostRailCandidate = this.findValidTraceAnchorCandidate(
|
|
9102
|
+
label,
|
|
9103
|
+
requiredOrientation,
|
|
9104
|
+
labelIndex
|
|
9105
|
+
);
|
|
9106
|
+
if (hostRailCandidate) {
|
|
9107
|
+
const canUseHostRail = this.canPlaceVerticalLabelOnHostRail(
|
|
9108
|
+
hostRailCandidate,
|
|
9109
|
+
label
|
|
9110
|
+
);
|
|
9111
|
+
if (canUseHostRail) return hostRailCandidate;
|
|
9112
|
+
hostRailCandidate.selected = false;
|
|
9113
|
+
}
|
|
9065
9114
|
const traceAnchorCandidate2 = this.findValidOutwardTraceAnchorCandidate(
|
|
9066
9115
|
label,
|
|
9067
9116
|
requiredOrientation,
|
|
@@ -9495,14 +9544,21 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
9495
9544
|
maxSearchDistance,
|
|
9496
9545
|
outwardDistance,
|
|
9497
9546
|
phase = "shift",
|
|
9498
|
-
stopOnTraceCollision = true
|
|
9547
|
+
stopOnTraceCollision = true,
|
|
9548
|
+
connectorSource,
|
|
9549
|
+
startDistance = LABEL_SEARCH_STEP
|
|
9499
9550
|
} = params;
|
|
9500
|
-
for (let distance7 =
|
|
9551
|
+
for (let distance7 = startDistance; distance7 <= maxSearchDistance + EPS12; distance7 += LABEL_SEARCH_STEP) {
|
|
9501
9552
|
const anchorPoint = {
|
|
9502
9553
|
x: baseAnchor.x + direction.x * distance7,
|
|
9503
9554
|
y: baseAnchor.y + direction.y * distance7
|
|
9504
9555
|
};
|
|
9505
|
-
const candidate = this.createCandidate(
|
|
9556
|
+
const candidate = this.createCandidate(
|
|
9557
|
+
label,
|
|
9558
|
+
anchorPoint,
|
|
9559
|
+
orientation,
|
|
9560
|
+
connectorSource
|
|
9561
|
+
);
|
|
9506
9562
|
const result = this.evaluateCandidate(
|
|
9507
9563
|
candidate,
|
|
9508
9564
|
label,
|
|
@@ -9878,6 +9934,113 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
9878
9934
|
const chipSide = this.getChipSideForPoint(label.anchorPoint);
|
|
9879
9935
|
return chipSide === "left" && label.orientation === "x-" || chipSide === "right" && label.orientation === "x+";
|
|
9880
9936
|
}
|
|
9937
|
+
canPlaceVerticalLabelOnHostRail(candidate, label) {
|
|
9938
|
+
const isOnHostRail = label.mspConnectionPairIds.some((traceId) => {
|
|
9939
|
+
const trace = this.traceMap[traceId];
|
|
9940
|
+
return trace && tracePathContainsPoint(trace.tracePath, candidate.anchorPoint);
|
|
9941
|
+
});
|
|
9942
|
+
if (!isOnHostRail) return false;
|
|
9943
|
+
const hostPin = this.pinMap[label.pinIds[0]];
|
|
9944
|
+
if (!hostPin) return false;
|
|
9945
|
+
if (label.pinIds.some(
|
|
9946
|
+
(pinId) => this.pinMap[pinId]?.chipId !== hostPin.chipId
|
|
9947
|
+
)) {
|
|
9948
|
+
return false;
|
|
9949
|
+
}
|
|
9950
|
+
const chip = this.chipObstacleSpatialIndex.chips.find(
|
|
9951
|
+
(chip2) => chip2.chipId === hostPin.chipId
|
|
9952
|
+
);
|
|
9953
|
+
if (!chip) return false;
|
|
9954
|
+
const labelBounds = getRectBounds(
|
|
9955
|
+
candidate.center,
|
|
9956
|
+
candidate.width,
|
|
9957
|
+
candidate.height
|
|
9958
|
+
);
|
|
9959
|
+
if (labelBounds.minY < chip.bounds.minY - EPS12 || labelBounds.maxY > chip.bounds.maxY + EPS12) {
|
|
9960
|
+
return false;
|
|
9961
|
+
}
|
|
9962
|
+
if (candidate.anchorPoint.x >= chip.bounds.minX - EPS12 && candidate.anchorPoint.x <= chip.bounds.maxX + EPS12) {
|
|
9963
|
+
return false;
|
|
9964
|
+
}
|
|
9965
|
+
return chip.pins.every(
|
|
9966
|
+
(pin) => label.pinIds.includes(pin.pinId) || pin.y <= labelBounds.minY + EPS12 || pin.y >= labelBounds.maxY - EPS12
|
|
9967
|
+
);
|
|
9968
|
+
}
|
|
9969
|
+
getTextBlockedMultiPinRailTraces(label, orientation) {
|
|
9970
|
+
if (!isYOrientation(orientation)) return null;
|
|
9971
|
+
const componentId = this.pinMap[label.pinIds[0]]?.chipId;
|
|
9972
|
+
const localTraces = Object.values(this.traceMap).filter(
|
|
9973
|
+
(trace) => trace.globalConnNetId === label.globalConnNetId && trace.pins.every((pin) => pin.chipId === componentId)
|
|
9974
|
+
);
|
|
9975
|
+
const connectedPinIds = new Set(label.pinIds);
|
|
9976
|
+
const connectedTraces = /* @__PURE__ */ new Set();
|
|
9977
|
+
for (let changed = true; changed; ) {
|
|
9978
|
+
changed = false;
|
|
9979
|
+
for (const trace of localTraces) {
|
|
9980
|
+
if (connectedTraces.has(trace)) continue;
|
|
9981
|
+
if (!trace.pinIds.some((pinId) => connectedPinIds.has(pinId))) continue;
|
|
9982
|
+
connectedTraces.add(trace);
|
|
9983
|
+
for (const pinId of trace.pinIds) connectedPinIds.add(pinId);
|
|
9984
|
+
changed = true;
|
|
9985
|
+
}
|
|
9986
|
+
}
|
|
9987
|
+
if (connectedPinIds.size <= 3) return null;
|
|
9988
|
+
const pins = [...connectedPinIds].map((pinId) => this.pinMap[pinId]);
|
|
9989
|
+
const direction = dir(orientation);
|
|
9990
|
+
const pinX = pins[0].x;
|
|
9991
|
+
if (pins.some((pin) => Math.abs(pin.x - pinX) > EPS12)) return null;
|
|
9992
|
+
const sidePins = this.inputProblem.chips.find((chip) => chip.chipId === componentId).pins.filter((pin) => Math.abs(pin.x - pinX) <= EPS12);
|
|
9993
|
+
const minY = Math.min(...pins.map((pin) => pin.y));
|
|
9994
|
+
const maxY = Math.max(...pins.map((pin) => pin.y));
|
|
9995
|
+
if (sidePins.some(
|
|
9996
|
+
(pin) => pin.y >= minY - EPS12 && pin.y <= maxY + EPS12 && !connectedPinIds.has(pin.pinId)
|
|
9997
|
+
) || orientation === "y-" && minY > Math.min(...sidePins.map((pin) => pin.y)) + EPS12 || orientation === "y+" && maxY < Math.max(...sidePins.map((pin) => pin.y)) - EPS12) {
|
|
9998
|
+
return null;
|
|
9999
|
+
}
|
|
10000
|
+
const outerPinPosition = Math.max(
|
|
10001
|
+
...pins.map((pin) => pin.x * direction.x + pin.y * direction.y)
|
|
10002
|
+
);
|
|
10003
|
+
const blockedByText = (this.inputProblem.textBoxes ?? []).some(
|
|
10004
|
+
(textBox) => textBox.chipId === componentId && textBox.center.x * direction.x + textBox.center.y * direction.y > outerPinPosition + EPS12
|
|
10005
|
+
);
|
|
10006
|
+
return blockedByText ? [...connectedTraces] : null;
|
|
10007
|
+
}
|
|
10008
|
+
findValidNearbyRailCandidate(label, orientation, labelIndex, localTraces) {
|
|
10009
|
+
if (!isYOrientation(orientation)) return null;
|
|
10010
|
+
const direction = dir(orientation);
|
|
10011
|
+
const connectorY = [
|
|
10012
|
+
...new Set(localTraces.flatMap((trace) => trace.pinIds))
|
|
10013
|
+
].map((pinId) => this.pinMap[pinId]).sort((a, b) => b.y * direction.y - a.y * direction.y)[0]?.y;
|
|
10014
|
+
if (connectorY === void 0) return null;
|
|
10015
|
+
const pinX = this.pinMap[label.pinIds[0]].x;
|
|
10016
|
+
const railXs = [
|
|
10017
|
+
...new Set(
|
|
10018
|
+
localTraces.flatMap((trace) => trace.tracePath.map((point) => point.x))
|
|
10019
|
+
)
|
|
10020
|
+
].filter((x) => Math.abs(x - pinX) > EPS12).sort((a, b) => Math.abs(a - pinX) - Math.abs(b - pinX));
|
|
10021
|
+
for (const x of railXs) {
|
|
10022
|
+
const connectorSource = { x, y: connectorY };
|
|
10023
|
+
if (!Object.values(this.traceMap).some(
|
|
10024
|
+
(trace) => trace.globalConnNetId === label.globalConnNetId && tracePathContainsPoint(trace.tracePath, connectorSource)
|
|
10025
|
+
)) {
|
|
10026
|
+
continue;
|
|
10027
|
+
}
|
|
10028
|
+
const candidate = this.findValidCandidateInShiftColumn({
|
|
10029
|
+
label,
|
|
10030
|
+
labelIndex,
|
|
10031
|
+
orientation,
|
|
10032
|
+
direction,
|
|
10033
|
+
baseAnchor: connectorSource,
|
|
10034
|
+
maxSearchDistance: this.maxSearchDistance,
|
|
10035
|
+
outwardDistance: 0,
|
|
10036
|
+
stopOnTraceCollision: false,
|
|
10037
|
+
connectorSource,
|
|
10038
|
+
startDistance: 0
|
|
10039
|
+
});
|
|
10040
|
+
if (candidate) return candidate;
|
|
10041
|
+
}
|
|
10042
|
+
return null;
|
|
10043
|
+
}
|
|
9881
10044
|
hasPortOnlyLabelOnSameNet(label) {
|
|
9882
10045
|
return this.outputNetLabelPlacements.some(
|
|
9883
10046
|
(otherLabel) => otherLabel !== label && otherLabel.globalConnNetId === label.globalConnNetId && this.isPortOnlyLabel(otherLabel)
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
getDimsForOrientation,
|
|
9
9
|
getRectBounds,
|
|
10
10
|
} from "lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/geometry"
|
|
11
|
+
import { tracePathContainsPoint } from "lib/solvers/RailNetLabelCornerPlacementSolver/geometry"
|
|
11
12
|
import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
|
|
12
13
|
import type {
|
|
13
14
|
ChipId,
|
|
@@ -250,6 +251,9 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
250
251
|
const orientations = this.getAvailableOrientations(label)
|
|
251
252
|
if (orientations.length === 0) return false
|
|
252
253
|
if (!orientations.includes(label.orientation)) return true
|
|
254
|
+
if (this.getTextBlockedMultiPinRailTraces(label, orientations[0]!)) {
|
|
255
|
+
return true
|
|
256
|
+
}
|
|
253
257
|
if (!this.crowdedPortOnlyLabelIndices.has(labelIndex)) return false
|
|
254
258
|
|
|
255
259
|
const bounds = getRectBounds(label.center, label.width, label.height)
|
|
@@ -407,9 +411,22 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
407
411
|
(netConnection?.pinIds.length ?? 0) > 2 &&
|
|
408
412
|
isYOrientation(requiredOrientation) &&
|
|
409
413
|
this.hasPortOnlyLabelOnSameNet(label)
|
|
414
|
+
const textBlockedMultiPinRailTraces = this.getTextBlockedMultiPinRailTraces(
|
|
415
|
+
label,
|
|
416
|
+
requiredOrientation,
|
|
417
|
+
)
|
|
410
418
|
const isPairedSameSidePowerRail =
|
|
411
419
|
isYOrientation(requiredOrientation) &&
|
|
412
420
|
this.hasOppositeVerticalRailOnSameChipSide(label, requiredOrientation)
|
|
421
|
+
if (textBlockedMultiPinRailTraces) {
|
|
422
|
+
const nearbyCandidate = this.findValidNearbyRailCandidate(
|
|
423
|
+
label,
|
|
424
|
+
requiredOrientation,
|
|
425
|
+
labelIndex,
|
|
426
|
+
textBlockedMultiPinRailTraces,
|
|
427
|
+
)
|
|
428
|
+
if (nearbyCandidate) return nearbyCandidate
|
|
429
|
+
}
|
|
413
430
|
if (
|
|
414
431
|
orientations.length === 1 &&
|
|
415
432
|
isYOrientation(requiredOrientation) &&
|
|
@@ -417,9 +434,22 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
417
434
|
this.hasTraceContinuingInOrientation(label, requiredOrientation) &&
|
|
418
435
|
(isDistanceSplitVerticalRail || isPairedSameSidePowerRail)
|
|
419
436
|
) {
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
437
|
+
const hostRailCandidate = this.findValidTraceAnchorCandidate(
|
|
438
|
+
label,
|
|
439
|
+
requiredOrientation,
|
|
440
|
+
labelIndex,
|
|
441
|
+
)
|
|
442
|
+
if (hostRailCandidate) {
|
|
443
|
+
const canUseHostRail = this.canPlaceVerticalLabelOnHostRail(
|
|
444
|
+
hostRailCandidate,
|
|
445
|
+
label,
|
|
446
|
+
)
|
|
447
|
+
if (canUseHostRail) return hostRailCandidate
|
|
448
|
+
hostRailCandidate.selected = false
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// Keep the established outward column when the direct rail end is not a
|
|
452
|
+
// clear segment of the connected component side.
|
|
423
453
|
const traceAnchorCandidate = this.findValidOutwardTraceAnchorCandidate(
|
|
424
454
|
label,
|
|
425
455
|
requiredOrientation,
|
|
@@ -785,7 +815,6 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
785
815
|
connectedPinIds.add(pinId)
|
|
786
816
|
}
|
|
787
817
|
}
|
|
788
|
-
|
|
789
818
|
// Multi-pin rails are routed as a chain of pairwise traces. Follow the
|
|
790
819
|
// pairs only when they share both a pin and an aligned vertical rail,
|
|
791
820
|
// which identifies one continuous rail. A shared pin alone can join
|
|
@@ -1012,6 +1041,8 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
1012
1041
|
outwardDistance: number
|
|
1013
1042
|
phase?: CandidatePhase
|
|
1014
1043
|
stopOnTraceCollision?: boolean
|
|
1044
|
+
connectorSource?: Point
|
|
1045
|
+
startDistance?: number
|
|
1015
1046
|
}) {
|
|
1016
1047
|
const {
|
|
1017
1048
|
label,
|
|
@@ -1023,10 +1054,12 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
1023
1054
|
outwardDistance,
|
|
1024
1055
|
phase = "shift",
|
|
1025
1056
|
stopOnTraceCollision = true,
|
|
1057
|
+
connectorSource,
|
|
1058
|
+
startDistance = LABEL_SEARCH_STEP,
|
|
1026
1059
|
} = params
|
|
1027
1060
|
|
|
1028
1061
|
for (
|
|
1029
|
-
let distance =
|
|
1062
|
+
let distance = startDistance;
|
|
1030
1063
|
distance <= maxSearchDistance + EPS;
|
|
1031
1064
|
distance += LABEL_SEARCH_STEP
|
|
1032
1065
|
) {
|
|
@@ -1034,7 +1067,12 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
1034
1067
|
x: baseAnchor.x + direction.x * distance,
|
|
1035
1068
|
y: baseAnchor.y + direction.y * distance,
|
|
1036
1069
|
}
|
|
1037
|
-
const candidate = this.createCandidate(
|
|
1070
|
+
const candidate = this.createCandidate(
|
|
1071
|
+
label,
|
|
1072
|
+
anchorPoint,
|
|
1073
|
+
orientation,
|
|
1074
|
+
connectorSource,
|
|
1075
|
+
)
|
|
1038
1076
|
const result = this.evaluateCandidate(
|
|
1039
1077
|
candidate,
|
|
1040
1078
|
label,
|
|
@@ -1563,6 +1601,172 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
1563
1601
|
)
|
|
1564
1602
|
}
|
|
1565
1603
|
|
|
1604
|
+
private canPlaceVerticalLabelOnHostRail(
|
|
1605
|
+
candidate: CandidateLabel,
|
|
1606
|
+
label: NetLabelPlacement,
|
|
1607
|
+
) {
|
|
1608
|
+
const isOnHostRail = label.mspConnectionPairIds.some((traceId) => {
|
|
1609
|
+
const trace = this.traceMap[traceId]
|
|
1610
|
+
return (
|
|
1611
|
+
trace && tracePathContainsPoint(trace.tracePath, candidate.anchorPoint)
|
|
1612
|
+
)
|
|
1613
|
+
})
|
|
1614
|
+
if (!isOnHostRail) return false
|
|
1615
|
+
|
|
1616
|
+
const hostPin = this.pinMap[label.pinIds[0]!]
|
|
1617
|
+
if (!hostPin) return false
|
|
1618
|
+
if (
|
|
1619
|
+
label.pinIds.some(
|
|
1620
|
+
(pinId) => this.pinMap[pinId]?.chipId !== hostPin.chipId,
|
|
1621
|
+
)
|
|
1622
|
+
) {
|
|
1623
|
+
return false
|
|
1624
|
+
}
|
|
1625
|
+
|
|
1626
|
+
const chip = this.chipObstacleSpatialIndex.chips.find(
|
|
1627
|
+
(chip) => chip.chipId === hostPin.chipId,
|
|
1628
|
+
)
|
|
1629
|
+
if (!chip) return false
|
|
1630
|
+
|
|
1631
|
+
const labelBounds = getRectBounds(
|
|
1632
|
+
candidate.center,
|
|
1633
|
+
candidate.width,
|
|
1634
|
+
candidate.height,
|
|
1635
|
+
)
|
|
1636
|
+
if (
|
|
1637
|
+
labelBounds.minY < chip.bounds.minY - EPS ||
|
|
1638
|
+
labelBounds.maxY > chip.bounds.maxY + EPS
|
|
1639
|
+
) {
|
|
1640
|
+
return false
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1643
|
+
if (
|
|
1644
|
+
candidate.anchorPoint.x >= chip.bounds.minX - EPS &&
|
|
1645
|
+
candidate.anchorPoint.x <= chip.bounds.maxX + EPS
|
|
1646
|
+
) {
|
|
1647
|
+
return false
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
return chip.pins.every(
|
|
1651
|
+
(pin) =>
|
|
1652
|
+
label.pinIds.includes(pin.pinId) ||
|
|
1653
|
+
pin.y <= labelBounds.minY + EPS ||
|
|
1654
|
+
pin.y >= labelBounds.maxY - EPS,
|
|
1655
|
+
)
|
|
1656
|
+
}
|
|
1657
|
+
|
|
1658
|
+
private getTextBlockedMultiPinRailTraces(
|
|
1659
|
+
label: NetLabelPlacement,
|
|
1660
|
+
orientation: FacingDirection,
|
|
1661
|
+
) {
|
|
1662
|
+
if (!isYOrientation(orientation)) return null
|
|
1663
|
+
const componentId = this.pinMap[label.pinIds[0]!]?.chipId
|
|
1664
|
+
const localTraces = Object.values(this.traceMap).filter(
|
|
1665
|
+
(trace) =>
|
|
1666
|
+
trace.globalConnNetId === label.globalConnNetId &&
|
|
1667
|
+
trace.pins.every((pin) => pin.chipId === componentId),
|
|
1668
|
+
)
|
|
1669
|
+
const connectedPinIds = new Set(label.pinIds)
|
|
1670
|
+
const connectedTraces = new Set<SolvedTracePath>()
|
|
1671
|
+
for (let changed = true; changed; ) {
|
|
1672
|
+
changed = false
|
|
1673
|
+
for (const trace of localTraces) {
|
|
1674
|
+
if (connectedTraces.has(trace)) continue
|
|
1675
|
+
if (!trace.pinIds.some((pinId) => connectedPinIds.has(pinId))) continue
|
|
1676
|
+
connectedTraces.add(trace)
|
|
1677
|
+
for (const pinId of trace.pinIds) connectedPinIds.add(pinId)
|
|
1678
|
+
changed = true
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1681
|
+
if (connectedPinIds.size <= 3) return null
|
|
1682
|
+
|
|
1683
|
+
const pins = [...connectedPinIds].map((pinId) => this.pinMap[pinId]!)
|
|
1684
|
+
const direction = dir(orientation)
|
|
1685
|
+
const pinX = pins[0]!.x
|
|
1686
|
+
if (pins.some((pin) => Math.abs(pin.x - pinX) > EPS)) return null
|
|
1687
|
+
const sidePins = this.inputProblem.chips
|
|
1688
|
+
.find((chip) => chip.chipId === componentId)!
|
|
1689
|
+
.pins.filter((pin) => Math.abs(pin.x - pinX) <= EPS)
|
|
1690
|
+
const minY = Math.min(...pins.map((pin) => pin.y))
|
|
1691
|
+
const maxY = Math.max(...pins.map((pin) => pin.y))
|
|
1692
|
+
if (
|
|
1693
|
+
sidePins.some(
|
|
1694
|
+
(pin) =>
|
|
1695
|
+
pin.y >= minY - EPS &&
|
|
1696
|
+
pin.y <= maxY + EPS &&
|
|
1697
|
+
!connectedPinIds.has(pin.pinId),
|
|
1698
|
+
) ||
|
|
1699
|
+
(orientation === "y-" &&
|
|
1700
|
+
minY > Math.min(...sidePins.map((pin) => pin.y)) + EPS) ||
|
|
1701
|
+
(orientation === "y+" &&
|
|
1702
|
+
maxY < Math.max(...sidePins.map((pin) => pin.y)) - EPS)
|
|
1703
|
+
) {
|
|
1704
|
+
return null
|
|
1705
|
+
}
|
|
1706
|
+
const outerPinPosition = Math.max(
|
|
1707
|
+
...pins.map((pin) => pin.x * direction.x + pin.y * direction.y),
|
|
1708
|
+
)
|
|
1709
|
+
const blockedByText = (this.inputProblem.textBoxes ?? []).some(
|
|
1710
|
+
(textBox) =>
|
|
1711
|
+
textBox.chipId === componentId &&
|
|
1712
|
+
textBox.center.x * direction.x + textBox.center.y * direction.y >
|
|
1713
|
+
outerPinPosition + EPS,
|
|
1714
|
+
)
|
|
1715
|
+
return blockedByText ? [...connectedTraces] : null
|
|
1716
|
+
}
|
|
1717
|
+
|
|
1718
|
+
private findValidNearbyRailCandidate(
|
|
1719
|
+
label: NetLabelPlacement,
|
|
1720
|
+
orientation: FacingDirection,
|
|
1721
|
+
labelIndex: number,
|
|
1722
|
+
localTraces: SolvedTracePath[],
|
|
1723
|
+
) {
|
|
1724
|
+
if (!isYOrientation(orientation)) return null
|
|
1725
|
+
const direction = dir(orientation)
|
|
1726
|
+
const connectorY = [
|
|
1727
|
+
...new Set(localTraces.flatMap((trace) => trace.pinIds)),
|
|
1728
|
+
]
|
|
1729
|
+
.map((pinId) => this.pinMap[pinId]!)
|
|
1730
|
+
.sort((a, b) => b.y * direction.y - a.y * direction.y)[0]?.y
|
|
1731
|
+
if (connectorY === undefined) return null
|
|
1732
|
+
|
|
1733
|
+
const pinX = this.pinMap[label.pinIds[0]!]!.x
|
|
1734
|
+
const railXs = [
|
|
1735
|
+
...new Set(
|
|
1736
|
+
localTraces.flatMap((trace) => trace.tracePath.map((point) => point.x)),
|
|
1737
|
+
),
|
|
1738
|
+
]
|
|
1739
|
+
.filter((x) => Math.abs(x - pinX) > EPS)
|
|
1740
|
+
.sort((a, b) => Math.abs(a - pinX) - Math.abs(b - pinX))
|
|
1741
|
+
|
|
1742
|
+
for (const x of railXs) {
|
|
1743
|
+
const connectorSource = { x, y: connectorY }
|
|
1744
|
+
if (
|
|
1745
|
+
!Object.values(this.traceMap).some(
|
|
1746
|
+
(trace) =>
|
|
1747
|
+
trace.globalConnNetId === label.globalConnNetId &&
|
|
1748
|
+
tracePathContainsPoint(trace.tracePath, connectorSource),
|
|
1749
|
+
)
|
|
1750
|
+
) {
|
|
1751
|
+
continue
|
|
1752
|
+
}
|
|
1753
|
+
const candidate = this.findValidCandidateInShiftColumn({
|
|
1754
|
+
label,
|
|
1755
|
+
labelIndex,
|
|
1756
|
+
orientation,
|
|
1757
|
+
direction,
|
|
1758
|
+
baseAnchor: connectorSource,
|
|
1759
|
+
maxSearchDistance: this.maxSearchDistance,
|
|
1760
|
+
outwardDistance: 0,
|
|
1761
|
+
stopOnTraceCollision: false,
|
|
1762
|
+
connectorSource,
|
|
1763
|
+
startDistance: 0,
|
|
1764
|
+
})
|
|
1765
|
+
if (candidate) return candidate
|
|
1766
|
+
}
|
|
1767
|
+
return null
|
|
1768
|
+
}
|
|
1769
|
+
|
|
1566
1770
|
private hasPortOnlyLabelOnSameNet(label: NetLabelPlacement) {
|
|
1567
1771
|
return this.outputNetLabelPlacements.some(
|
|
1568
1772
|
(otherLabel) =>
|
|
@@ -53,6 +53,12 @@ export const getFixedLabelCoordinate = (
|
|
|
53
53
|
) => {
|
|
54
54
|
const traceMap = new Map(traces.map((trace) => [trace.mspPairId, trace]))
|
|
55
55
|
const groupTraceIds = new Set(group.map((segment) => segment.traceId))
|
|
56
|
+
const groupIsWithinSingleComponent = [...groupTraceIds].every(
|
|
57
|
+
(traceId) =>
|
|
58
|
+
traceMap
|
|
59
|
+
.get(traceId)
|
|
60
|
+
?.pins.every((pin) => pin.chipId === group[0]!.componentId) === true,
|
|
61
|
+
)
|
|
56
62
|
const groupHasOnlySingleRailTraces = [...groupTraceIds].every(
|
|
57
63
|
(traceId) => traceMap.get(traceId)?.tracePath.length === 4,
|
|
58
64
|
)
|
|
@@ -66,16 +72,27 @@ export const getFixedLabelCoordinate = (
|
|
|
66
72
|
netLabelPlacements,
|
|
67
73
|
traces,
|
|
68
74
|
)
|
|
69
|
-
const
|
|
70
|
-
(
|
|
71
|
-
label
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}),
|
|
75
|
+
const labelsOrientedAlongRail = new Set(
|
|
76
|
+
linkedLabels.filter(
|
|
77
|
+
(label) =>
|
|
78
|
+
(orientation === "vertical" &&
|
|
79
|
+
(label.orientation === "y+" || label.orientation === "y-")) ||
|
|
80
|
+
(orientation === "horizontal" &&
|
|
81
|
+
(label.orientation === "x+" || label.orientation === "x-")),
|
|
82
|
+
),
|
|
78
83
|
)
|
|
84
|
+
const anchoringLabels = linkedLabels.filter((label) => {
|
|
85
|
+
if (
|
|
86
|
+
label.orientation !== group[0]!.componentFacingDirection &&
|
|
87
|
+
(!groupIsWithinSingleComponent || !labelsOrientedAlongRail.has(label))
|
|
88
|
+
) {
|
|
89
|
+
return false
|
|
90
|
+
}
|
|
91
|
+
return label.mspConnectionPairIds.some((traceId) => {
|
|
92
|
+
const trace = traceMap.get(traceId)
|
|
93
|
+
return trace && tracePathContainsPoint(trace.tracePath, label.anchorPoint)
|
|
94
|
+
})
|
|
95
|
+
})
|
|
79
96
|
const coordinates = getDistinctCoordinates(
|
|
80
97
|
anchoringLabels.map((label) =>
|
|
81
98
|
orientation === "vertical" ? label.anchorPoint.x : label.anchorPoint.y,
|
|
@@ -95,11 +112,24 @@ export const getFixedLabelCoordinate = (
|
|
|
95
112
|
(traceId) => (traceMap.get(traceId)?.tracePath.length ?? 0) > 4,
|
|
96
113
|
),
|
|
97
114
|
)
|
|
98
|
-
// A multi-turn label trace is an intentional routed backbone, so its
|
|
99
|
-
// coordinate may propagate through the
|
|
100
|
-
// a single movable rail remain subject to the per-connection locality gate.
|
|
115
|
+
// A multi-turn label trace is an intentional routed backbone, so its
|
|
116
|
+
// coordinate may propagate through the connected rail group.
|
|
101
117
|
if (labelIsAnchoredToRoutedBackbone) return coordinate
|
|
102
118
|
|
|
119
|
+
// A label at a same-component rail end may set the shared bus coordinate.
|
|
120
|
+
if (anchoringLabels.some((label) => labelsOrientedAlongRail.has(label))) {
|
|
121
|
+
const railGroupSpan =
|
|
122
|
+
Math.max(...group.map((segment) => segment.maxAlong)) -
|
|
123
|
+
Math.min(...group.map((segment) => segment.minAlong))
|
|
124
|
+
const coordinateIsLocalToRailGroup = group.every(
|
|
125
|
+
(segment) =>
|
|
126
|
+
Math.abs(segment.coordinate - coordinate) <=
|
|
127
|
+
railGroupSpan + RAIL_ALIGNMENT_EPSILON,
|
|
128
|
+
)
|
|
129
|
+
if (coordinateIsLocalToRailGroup) return coordinate
|
|
130
|
+
return null
|
|
131
|
+
}
|
|
132
|
+
|
|
103
133
|
// Rail cleanup is local: a label cannot pull a connection farther than the
|
|
104
134
|
// span of the pins that connection already joins.
|
|
105
135
|
const coordinateIsLocalToEveryTrace = group.every((segment) => {
|
|
@@ -110,5 +140,6 @@ export const getFixedLabelCoordinate = (
|
|
|
110
140
|
distance(trace.pins[0]!, trace.pins[1]!) + RAIL_ALIGNMENT_EPSILON
|
|
111
141
|
)
|
|
112
142
|
})
|
|
113
|
-
|
|
143
|
+
if (coordinateIsLocalToEveryTrace) return coordinate
|
|
144
|
+
return null
|
|
114
145
|
}
|