@tscircuit/schematic-trace-solver 0.0.115 → 0.0.117
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 +22 -1
- package/dist/index.js +281 -0
- package/lib/solvers/AvailableNetOrientationSolver/AvailableNetOrientationSolver.ts +75 -0
- package/lib/solvers/SameNetJunctionAlignmentSolver/SameNetJunctionAlignmentSolver.ts +71 -0
- package/lib/solvers/SameNetJunctionAlignmentSolver/alignSameNetJunctions.ts +223 -0
- package/lib/solvers/SameNetJunctionAlignmentSolver/pathIntersectsAnyNetLabel.ts +22 -0
- package/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts +17 -0
- package/package.json +1 -1
- package/site/bug-reports/bug-report-20260730T061837Z.page.tsx +4 -0
- package/site/bug-reports/bug-report-20260731T052229Z.page.tsx +4 -0
- package/tests/bug-reports/bug-report-20260721T221026Z/__snapshots__/bug-report-20260721T221026Z.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-20260728T144234Z/bug-report-20260728T144234Z.test.ts +6 -0
- package/tests/bug-reports/bug-report-20260730T061837Z/__snapshots__/bug-report-20260730T061837Z.snap.svg +123 -0
- package/tests/bug-reports/bug-report-20260730T061837Z/bug-report-20260730T061837Z.json +614 -0
- package/tests/bug-reports/bug-report-20260730T061837Z/bug-report-20260730T061837Z.test.ts +34 -0
- package/tests/bug-reports/bug-report-20260731T052229Z/__snapshots__/bug-report-20260731T052229Z.snap.svg +93 -0
- package/tests/bug-reports/bug-report-20260731T052229Z/bug-report-20260731T052229Z.json +299 -0
- package/tests/bug-reports/bug-report-20260731T052229Z/bug-report-20260731T052229Z.test.ts +43 -0
- package/tests/repros/__snapshots__/board-15984-same-net-junction.snap.svg +110 -0
- package/tests/repros/assets/board-15984-schematic-trace-input.json +518 -0
- package/tests/repros/board-15984-same-net-junction.test.ts +30 -0
package/dist/index.d.ts
CHANGED
|
@@ -782,6 +782,8 @@ declare class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
782
782
|
private findValidCandidateInShiftColumn;
|
|
783
783
|
private evaluateCandidate;
|
|
784
784
|
private getCandidateConnectorTrace;
|
|
785
|
+
private isStandaloneSinglePinNetLabel;
|
|
786
|
+
private labelIntersectsDifferentNetTrace;
|
|
785
787
|
private getSearchStartAnchor;
|
|
786
788
|
private getWickOffsetAnchor;
|
|
787
789
|
private getSideOffsetAnchor;
|
|
@@ -1047,6 +1049,24 @@ declare class UnroutedTraceRecoverySolver extends BaseSolver {
|
|
|
1047
1049
|
visualize(): graphics_debug.GraphicsObject;
|
|
1048
1050
|
}
|
|
1049
1051
|
|
|
1052
|
+
interface SameNetJunctionAlignmentSolverInput {
|
|
1053
|
+
inputProblem: InputProblem;
|
|
1054
|
+
traces: SolvedTracePath[];
|
|
1055
|
+
netLabelPlacements: NetLabelPlacement[];
|
|
1056
|
+
}
|
|
1057
|
+
/** Turns separate same-net load traces into one shared rail with short junction branches. */
|
|
1058
|
+
declare class SameNetJunctionAlignmentSolver extends BaseSolver {
|
|
1059
|
+
private input;
|
|
1060
|
+
outputTraces: SolvedTracePath[];
|
|
1061
|
+
constructor(input: SameNetJunctionAlignmentSolverInput);
|
|
1062
|
+
_step(): void;
|
|
1063
|
+
getOutput(): {
|
|
1064
|
+
traces: SolvedTracePath[];
|
|
1065
|
+
netLabelPlacements: NetLabelPlacement[];
|
|
1066
|
+
};
|
|
1067
|
+
visualize(): GraphicsObject;
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1050
1070
|
/**
|
|
1051
1071
|
* Pipeline solver that runs a series of solvers to find the best schematic layout.
|
|
1052
1072
|
* Coordinates the entire layout process from chip partitioning through final packing.
|
|
@@ -1081,13 +1101,14 @@ declare class SchematicTracePipelineSolver extends BaseSolver {
|
|
|
1081
1101
|
netLabelTraceCollisionSolver?: NetLabelTraceCollisionSolver;
|
|
1082
1102
|
traceCleanupSolver2?: TraceCleanupSolver;
|
|
1083
1103
|
netLabelNetLabelCollisionSolver?: NetLabelNetLabelCollisionSolver;
|
|
1104
|
+
sameNetJunctionAlignmentSolver?: SameNetJunctionAlignmentSolver;
|
|
1084
1105
|
startTimeOfPhase: Record<string, number>;
|
|
1085
1106
|
endTimeOfPhase: Record<string, number>;
|
|
1086
1107
|
timeSpentOnPhase: Record<string, number>;
|
|
1087
1108
|
firstIterationOfPhase: Record<string, number>;
|
|
1088
1109
|
inputProblem: InputProblem;
|
|
1089
1110
|
hideRatsNet: boolean;
|
|
1090
|
-
pipelineDef: (PipelineStep<typeof MspConnectionPairSolver> | PipelineStep<typeof SchematicTraceLinesSolver> | PipelineStep<typeof LongDistancePairSolver> | PipelineStep<typeof UnroutedTraceRecoverySolver> | PipelineStep<typeof TraceOverlapShiftSolver> | PipelineStep<typeof NetLabelPlacementSolver> | PipelineStep<typeof TraceLabelOverlapAvoidanceSolver> | PipelineStep<typeof TraceCleanupSolver> | PipelineStep<typeof Example28Solver> | PipelineStep<typeof AvailableNetOrientationSolver> | PipelineStep<typeof RailNetLabelCornerPlacementSolver> | PipelineStep<typeof TraceAnchoredNetLabelOverlapSolver> | PipelineStep<typeof NetLabelTraceCollisionSolver> | PipelineStep<typeof NetLabelNetLabelCollisionSolver>)[];
|
|
1111
|
+
pipelineDef: (PipelineStep<typeof MspConnectionPairSolver> | PipelineStep<typeof SchematicTraceLinesSolver> | PipelineStep<typeof LongDistancePairSolver> | PipelineStep<typeof UnroutedTraceRecoverySolver> | PipelineStep<typeof TraceOverlapShiftSolver> | PipelineStep<typeof NetLabelPlacementSolver> | PipelineStep<typeof TraceLabelOverlapAvoidanceSolver> | PipelineStep<typeof TraceCleanupSolver> | PipelineStep<typeof Example28Solver> | PipelineStep<typeof AvailableNetOrientationSolver> | PipelineStep<typeof RailNetLabelCornerPlacementSolver> | PipelineStep<typeof TraceAnchoredNetLabelOverlapSolver> | PipelineStep<typeof NetLabelTraceCollisionSolver> | PipelineStep<typeof NetLabelNetLabelCollisionSolver> | PipelineStep<typeof SameNetJunctionAlignmentSolver>)[];
|
|
1091
1112
|
constructor(inputProblem: InputProblem, opts?: Options);
|
|
1092
1113
|
getConstructorParams(): ConstructorParameters<typeof SchematicTracePipelineSolver>;
|
|
1093
1114
|
currentPipelineStepIndex: number;
|
package/dist/index.js
CHANGED
|
@@ -7895,6 +7895,34 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
7895
7895
|
indices.push(i);
|
|
7896
7896
|
}
|
|
7897
7897
|
}
|
|
7898
|
+
const blockedStandaloneLabelIndex = indices.find((index) => {
|
|
7899
|
+
const label = this.outputNetLabelPlacements[index];
|
|
7900
|
+
const orientations = this.getAvailableOrientations(label);
|
|
7901
|
+
return this.isPortOnlyLabel(label) && this.isStandaloneSinglePinNetLabel(label) && orientations.length === 1 && isYOrientation(orientations[0]) && this.labelIntersectsDifferentNetTrace(label);
|
|
7902
|
+
});
|
|
7903
|
+
if (blockedStandaloneLabelIndex === void 0) return indices;
|
|
7904
|
+
const blockedLabel = this.outputNetLabelPlacements[blockedStandaloneLabelIndex];
|
|
7905
|
+
const requiredOrientation = this.getAvailableOrientations(blockedLabel)[0];
|
|
7906
|
+
const chipSide = this.getChipSideForPoint(blockedLabel.anchorPoint);
|
|
7907
|
+
const affectedSlots = [];
|
|
7908
|
+
const labelsToOrder = [];
|
|
7909
|
+
for (let slot = 0; slot < indices.length; slot++) {
|
|
7910
|
+
const index = indices[slot];
|
|
7911
|
+
const label = this.outputNetLabelPlacements[index];
|
|
7912
|
+
const orientations = this.getAvailableOrientations(label);
|
|
7913
|
+
if (this.getChipSideForPoint(label.anchorPoint) === chipSide && orientations.length === 1 && orientations[0] === requiredOrientation) {
|
|
7914
|
+
affectedSlots.push(slot);
|
|
7915
|
+
labelsToOrder.push(index);
|
|
7916
|
+
}
|
|
7917
|
+
}
|
|
7918
|
+
labelsToOrder.sort((a, b) => {
|
|
7919
|
+
const aY = this.outputNetLabelPlacements[a].anchorPoint.y;
|
|
7920
|
+
const bY = this.outputNetLabelPlacements[b].anchorPoint.y;
|
|
7921
|
+
return requiredOrientation === "y+" ? bY - aY : aY - bY;
|
|
7922
|
+
});
|
|
7923
|
+
for (let i = 0; i < affectedSlots.length; i++) {
|
|
7924
|
+
indices[affectedSlots[i]] = labelsToOrder[i];
|
|
7925
|
+
}
|
|
7898
7926
|
return indices;
|
|
7899
7927
|
}
|
|
7900
7928
|
shouldProcessLabel(label) {
|
|
@@ -8213,6 +8241,28 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
8213
8241
|
candidate.orientation
|
|
8214
8242
|
);
|
|
8215
8243
|
}
|
|
8244
|
+
isStandaloneSinglePinNetLabel(label) {
|
|
8245
|
+
return this.inputProblem.netConnections.some(
|
|
8246
|
+
(connection) => connection.netId === label.netId && connection.pinIds.length === 1 && connection.pinIds[0] === label.pinIds[0]
|
|
8247
|
+
);
|
|
8248
|
+
}
|
|
8249
|
+
labelIntersectsDifferentNetTrace(label) {
|
|
8250
|
+
const { width, height } = getDimsForOrientation({
|
|
8251
|
+
orientation: label.orientation,
|
|
8252
|
+
netLabelWidth: this.getNetLabelWidth(label),
|
|
8253
|
+
netLabelHeight: this.getNetLabelHeight(label)
|
|
8254
|
+
});
|
|
8255
|
+
const center = getCenterFromAnchor(
|
|
8256
|
+
label.anchorPoint,
|
|
8257
|
+
label.orientation,
|
|
8258
|
+
width,
|
|
8259
|
+
height
|
|
8260
|
+
);
|
|
8261
|
+
const bounds = getRectBounds(center, width, height);
|
|
8262
|
+
return Object.values(this.traceMap).some(
|
|
8263
|
+
(trace) => trace.globalConnNetId !== label.globalConnNetId && tracePathIntersectsBounds(trace.tracePath, bounds)
|
|
8264
|
+
);
|
|
8265
|
+
}
|
|
8216
8266
|
getSearchStartAnchor(label, orientation) {
|
|
8217
8267
|
const anchorPoint = this.getWickOffsetAnchor(label.anchorPoint, orientation);
|
|
8218
8268
|
const { width, height } = getDimsForOrientation({
|
|
@@ -10717,6 +10767,222 @@ var UnroutedTraceRecoverySolver = class extends BaseSolver {
|
|
|
10717
10767
|
}
|
|
10718
10768
|
};
|
|
10719
10769
|
|
|
10770
|
+
// lib/solvers/SameNetJunctionAlignmentSolver/pathIntersectsAnyNetLabel.ts
|
|
10771
|
+
var pathIntersectsAnyNetLabel = ({
|
|
10772
|
+
path,
|
|
10773
|
+
netLabelPlacements
|
|
10774
|
+
}) => {
|
|
10775
|
+
for (const label of netLabelPlacements) {
|
|
10776
|
+
const labelBounds = getRectBounds(label.center, label.width, label.height);
|
|
10777
|
+
for (let index = 0; index < path.length - 1; index++) {
|
|
10778
|
+
if (segmentIntersectsRect2(path[index], path[index + 1], labelBounds)) {
|
|
10779
|
+
return true;
|
|
10780
|
+
}
|
|
10781
|
+
}
|
|
10782
|
+
}
|
|
10783
|
+
return false;
|
|
10784
|
+
};
|
|
10785
|
+
|
|
10786
|
+
// lib/solvers/SameNetJunctionAlignmentSolver/alignSameNetJunctions.ts
|
|
10787
|
+
var MAX_ALIGNED_LOAD_PIN_OFFSET = 0.2;
|
|
10788
|
+
var getSharedPin = ({
|
|
10789
|
+
donorTrace,
|
|
10790
|
+
branchTrace
|
|
10791
|
+
}) => {
|
|
10792
|
+
const branchPinIds = new Set(branchTrace.pins.map((pin) => pin.pinId));
|
|
10793
|
+
return donorTrace.pins.find((pin) => branchPinIds.has(pin.pinId)) ?? null;
|
|
10794
|
+
};
|
|
10795
|
+
var getOtherPin = ({
|
|
10796
|
+
trace,
|
|
10797
|
+
sharedPin
|
|
10798
|
+
}) => trace.pins.find((pin) => pin.pinId !== sharedPin.pinId) ?? null;
|
|
10799
|
+
var getLongestHorizontalSegment = (trace) => {
|
|
10800
|
+
let longest = null;
|
|
10801
|
+
for (let index = 0; index < trace.tracePath.length - 1; index++) {
|
|
10802
|
+
const start = trace.tracePath[index];
|
|
10803
|
+
const end = trace.tracePath[index + 1];
|
|
10804
|
+
if (!isHorizontal2(start, end)) continue;
|
|
10805
|
+
if (!longest || Math.abs(end.x - start.x) > Math.abs(longest.end.x - longest.start.x)) {
|
|
10806
|
+
longest = { start, end };
|
|
10807
|
+
}
|
|
10808
|
+
}
|
|
10809
|
+
return longest;
|
|
10810
|
+
};
|
|
10811
|
+
var getJunctionPoint = ({
|
|
10812
|
+
segment,
|
|
10813
|
+
sharedPin
|
|
10814
|
+
}) => {
|
|
10815
|
+
const startDistance = Math.abs(segment.start.x - sharedPin.x);
|
|
10816
|
+
const endDistance = Math.abs(segment.end.x - sharedPin.x);
|
|
10817
|
+
if (startDistance <= endDistance) return segment.start;
|
|
10818
|
+
return segment.end;
|
|
10819
|
+
};
|
|
10820
|
+
var railIsOnFacingSide = ({
|
|
10821
|
+
railY,
|
|
10822
|
+
pin
|
|
10823
|
+
}) => {
|
|
10824
|
+
if (pin._facingDirection === "y+") return railY > pin.y;
|
|
10825
|
+
return false;
|
|
10826
|
+
};
|
|
10827
|
+
var getAlignedBranchPath = ({
|
|
10828
|
+
donorTrace,
|
|
10829
|
+
branchTrace
|
|
10830
|
+
}) => {
|
|
10831
|
+
const sharedPin = getSharedPin({ donorTrace, branchTrace });
|
|
10832
|
+
if (!sharedPin) return null;
|
|
10833
|
+
const donorOtherPin = getOtherPin({ trace: donorTrace, sharedPin });
|
|
10834
|
+
if (!donorOtherPin) return null;
|
|
10835
|
+
const otherPin = getOtherPin({ trace: branchTrace, sharedPin });
|
|
10836
|
+
if (!otherPin) return null;
|
|
10837
|
+
if (Math.abs(sharedPin.y - otherPin.y) > MAX_ALIGNED_LOAD_PIN_OFFSET) {
|
|
10838
|
+
return null;
|
|
10839
|
+
}
|
|
10840
|
+
const donorRail = getLongestHorizontalSegment(donorTrace);
|
|
10841
|
+
if (!donorRail) return null;
|
|
10842
|
+
const branchRail = getLongestHorizontalSegment(branchTrace);
|
|
10843
|
+
if (branchRail && nearlyEqual(branchRail.start.y, donorRail.start.y)) {
|
|
10844
|
+
return null;
|
|
10845
|
+
}
|
|
10846
|
+
if (!railIsOnFacingSide({ railY: donorRail.start.y, pin: otherPin })) {
|
|
10847
|
+
return null;
|
|
10848
|
+
}
|
|
10849
|
+
const junction = getJunctionPoint({ segment: donorRail, sharedPin });
|
|
10850
|
+
const extendsDonorRail = donorOtherPin.x < junction.x && otherPin.x > junction.x || donorOtherPin.x > junction.x && otherPin.x < junction.x;
|
|
10851
|
+
if (!extendsDonorRail) return null;
|
|
10852
|
+
const sharedToOther = simplifyPath([
|
|
10853
|
+
{ x: sharedPin.x, y: sharedPin.y },
|
|
10854
|
+
{ x: junction.x, y: sharedPin.y },
|
|
10855
|
+
{ x: junction.x, y: junction.y },
|
|
10856
|
+
{ x: otherPin.x, y: junction.y },
|
|
10857
|
+
{ x: otherPin.x, y: otherPin.y }
|
|
10858
|
+
]);
|
|
10859
|
+
if (branchTrace.pins[0].pinId === sharedPin.pinId) return sharedToOther;
|
|
10860
|
+
return [...sharedToOther].reverse();
|
|
10861
|
+
};
|
|
10862
|
+
var candidateIsClear = ({
|
|
10863
|
+
candidateTrace,
|
|
10864
|
+
traces,
|
|
10865
|
+
inputProblem,
|
|
10866
|
+
netLabelPlacements
|
|
10867
|
+
}) => {
|
|
10868
|
+
const obstacles = getObstacleRects(inputProblem);
|
|
10869
|
+
if (isPathCollidingWithObstacles(candidateTrace.tracePath, obstacles)) {
|
|
10870
|
+
return false;
|
|
10871
|
+
}
|
|
10872
|
+
const otherNetTraces = traces.filter(
|
|
10873
|
+
(trace) => trace.globalConnNetId !== candidateTrace.globalConnNetId
|
|
10874
|
+
);
|
|
10875
|
+
if (doesPathCoincideWithTraces(candidateTrace.tracePath, otherNetTraces)) {
|
|
10876
|
+
return false;
|
|
10877
|
+
}
|
|
10878
|
+
return !pathIntersectsAnyNetLabel({
|
|
10879
|
+
path: candidateTrace.tracePath,
|
|
10880
|
+
netLabelPlacements
|
|
10881
|
+
});
|
|
10882
|
+
};
|
|
10883
|
+
var alignSameNetJunctions = ({
|
|
10884
|
+
inputProblem,
|
|
10885
|
+
traces,
|
|
10886
|
+
netLabelPlacements
|
|
10887
|
+
}) => {
|
|
10888
|
+
let outputTraces = [...traces];
|
|
10889
|
+
const alignedBranchTraceIds = /* @__PURE__ */ new Set();
|
|
10890
|
+
let alignedJunctionCount = 0;
|
|
10891
|
+
for (const donorTraceId of traces.map((trace) => trace.mspPairId)) {
|
|
10892
|
+
const donorTrace = outputTraces.find(
|
|
10893
|
+
(trace) => trace.mspPairId === donorTraceId
|
|
10894
|
+
);
|
|
10895
|
+
for (const branchTrace of outputTraces) {
|
|
10896
|
+
if (alignedBranchTraceIds.has(branchTrace.mspPairId)) continue;
|
|
10897
|
+
if (donorTrace.mspPairId === branchTrace.mspPairId) continue;
|
|
10898
|
+
if (donorTrace.globalConnNetId !== branchTrace.globalConnNetId) continue;
|
|
10899
|
+
const candidatePath = getAlignedBranchPath({ donorTrace, branchTrace });
|
|
10900
|
+
if (!candidatePath) continue;
|
|
10901
|
+
const candidateTrace = { ...branchTrace, tracePath: candidatePath };
|
|
10902
|
+
const originalPair = [donorTrace, branchTrace];
|
|
10903
|
+
const candidatePair = [donorTrace, candidateTrace];
|
|
10904
|
+
const removesVisibleSegment = getVisibleTraceSegmentCount(candidatePair) < getVisibleTraceSegmentCount(originalPair);
|
|
10905
|
+
const shortensVisibleTrace = getVisibleTraceLength(candidatePair) < getVisibleTraceLength(originalPair) && !nearlyEqual(
|
|
10906
|
+
getVisibleTraceLength(candidatePair),
|
|
10907
|
+
getVisibleTraceLength(originalPair)
|
|
10908
|
+
);
|
|
10909
|
+
if (!removesVisibleSegment && !shortensVisibleTrace) {
|
|
10910
|
+
continue;
|
|
10911
|
+
}
|
|
10912
|
+
if (!candidateIsClear({
|
|
10913
|
+
candidateTrace,
|
|
10914
|
+
traces: outputTraces,
|
|
10915
|
+
inputProblem,
|
|
10916
|
+
netLabelPlacements
|
|
10917
|
+
})) {
|
|
10918
|
+
continue;
|
|
10919
|
+
}
|
|
10920
|
+
outputTraces = outputTraces.map((trace) => {
|
|
10921
|
+
if (trace.mspPairId === branchTrace.mspPairId) return candidateTrace;
|
|
10922
|
+
return trace;
|
|
10923
|
+
});
|
|
10924
|
+
alignedBranchTraceIds.add(branchTrace.mspPairId);
|
|
10925
|
+
alignedJunctionCount++;
|
|
10926
|
+
}
|
|
10927
|
+
}
|
|
10928
|
+
return { traces: outputTraces, alignedJunctionCount };
|
|
10929
|
+
};
|
|
10930
|
+
|
|
10931
|
+
// lib/solvers/SameNetJunctionAlignmentSolver/SameNetJunctionAlignmentSolver.ts
|
|
10932
|
+
var SameNetJunctionAlignmentSolver = class extends BaseSolver {
|
|
10933
|
+
input;
|
|
10934
|
+
outputTraces;
|
|
10935
|
+
constructor(input) {
|
|
10936
|
+
super();
|
|
10937
|
+
this.input = input;
|
|
10938
|
+
this.outputTraces = input.traces;
|
|
10939
|
+
}
|
|
10940
|
+
_step() {
|
|
10941
|
+
const result = alignSameNetJunctions(this.input);
|
|
10942
|
+
this.outputTraces = result.traces;
|
|
10943
|
+
this.stats.alignedJunctionCount = result.alignedJunctionCount;
|
|
10944
|
+
this.solved = true;
|
|
10945
|
+
}
|
|
10946
|
+
getOutput() {
|
|
10947
|
+
return {
|
|
10948
|
+
traces: this.outputTraces,
|
|
10949
|
+
netLabelPlacements: this.input.netLabelPlacements
|
|
10950
|
+
};
|
|
10951
|
+
}
|
|
10952
|
+
visualize() {
|
|
10953
|
+
const graphics = visualizeInputProblem(this.input.inputProblem);
|
|
10954
|
+
graphics.lines ??= [];
|
|
10955
|
+
graphics.rects ??= [];
|
|
10956
|
+
graphics.points ??= [];
|
|
10957
|
+
for (const trace of this.outputTraces) {
|
|
10958
|
+
graphics.lines.push({
|
|
10959
|
+
points: trace.tracePath,
|
|
10960
|
+
strokeColor: "purple"
|
|
10961
|
+
});
|
|
10962
|
+
}
|
|
10963
|
+
for (const label of this.input.netLabelPlacements) {
|
|
10964
|
+
const labelRect = {
|
|
10965
|
+
center: label.center,
|
|
10966
|
+
width: label.width,
|
|
10967
|
+
height: label.height,
|
|
10968
|
+
fill: getColorFromString(label.globalConnNetId, 0.35),
|
|
10969
|
+
strokeColor: getColorFromString(label.globalConnNetId, 0.9),
|
|
10970
|
+
label: `netId: ${label.netId}
|
|
10971
|
+
globalConnNetId: ${label.globalConnNetId}`
|
|
10972
|
+
};
|
|
10973
|
+
graphics.rects.push(labelRect);
|
|
10974
|
+
graphics.points.push({
|
|
10975
|
+
x: label.anchorPoint.x,
|
|
10976
|
+
y: label.anchorPoint.y,
|
|
10977
|
+
color: getColorFromString(label.globalConnNetId, 0.9),
|
|
10978
|
+
label: `anchorPoint
|
|
10979
|
+
orientation: ${label.orientation}`
|
|
10980
|
+
});
|
|
10981
|
+
}
|
|
10982
|
+
return graphics;
|
|
10983
|
+
}
|
|
10984
|
+
};
|
|
10985
|
+
|
|
10720
10986
|
// lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts
|
|
10721
10987
|
function definePipelineStep(solverName, solverClass, getConstructorParams, opts = {}) {
|
|
10722
10988
|
return {
|
|
@@ -10747,6 +11013,7 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
|
|
|
10747
11013
|
netLabelTraceCollisionSolver;
|
|
10748
11014
|
traceCleanupSolver2;
|
|
10749
11015
|
netLabelNetLabelCollisionSolver;
|
|
11016
|
+
sameNetJunctionAlignmentSolver;
|
|
10750
11017
|
startTimeOfPhase;
|
|
10751
11018
|
endTimeOfPhase;
|
|
10752
11019
|
timeSpentOnPhase;
|
|
@@ -11019,6 +11286,20 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
|
|
|
11019
11286
|
netLabelPlacements: instance.netLabelTraceCollisionSolver.getOutput().netLabelPlacements
|
|
11020
11287
|
}
|
|
11021
11288
|
]
|
|
11289
|
+
),
|
|
11290
|
+
definePipelineStep(
|
|
11291
|
+
"sameNetJunctionAlignmentSolver",
|
|
11292
|
+
SameNetJunctionAlignmentSolver,
|
|
11293
|
+
(instance) => {
|
|
11294
|
+
const collisionOutput = instance.netLabelNetLabelCollisionSolver.getOutput();
|
|
11295
|
+
return [
|
|
11296
|
+
{
|
|
11297
|
+
inputProblem: instance.inputProblem,
|
|
11298
|
+
traces: instance.netLabelNetLabelCollisionSolver.traces,
|
|
11299
|
+
netLabelPlacements: collisionOutput.netLabelPlacements
|
|
11300
|
+
}
|
|
11301
|
+
];
|
|
11302
|
+
}
|
|
11022
11303
|
)
|
|
11023
11304
|
];
|
|
11024
11305
|
constructor(inputProblem, opts) {
|
|
@@ -110,6 +110,52 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
110
110
|
}
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
+
const blockedStandaloneLabelIndex = indices.find((index) => {
|
|
114
|
+
const label = this.outputNetLabelPlacements[index]!
|
|
115
|
+
const orientations = this.getAvailableOrientations(label)
|
|
116
|
+
return (
|
|
117
|
+
this.isPortOnlyLabel(label) &&
|
|
118
|
+
this.isStandaloneSinglePinNetLabel(label) &&
|
|
119
|
+
orientations.length === 1 &&
|
|
120
|
+
isYOrientation(orientations[0]!) &&
|
|
121
|
+
this.labelIntersectsDifferentNetTrace(label)
|
|
122
|
+
)
|
|
123
|
+
})
|
|
124
|
+
if (blockedStandaloneLabelIndex === undefined) return indices
|
|
125
|
+
|
|
126
|
+
const blockedLabel =
|
|
127
|
+
this.outputNetLabelPlacements[blockedStandaloneLabelIndex]!
|
|
128
|
+
const requiredOrientation = this.getAvailableOrientations(blockedLabel)[0]!
|
|
129
|
+
const chipSide = this.getChipSideForPoint(blockedLabel.anchorPoint)
|
|
130
|
+
const affectedSlots: number[] = []
|
|
131
|
+
const labelsToOrder: number[] = []
|
|
132
|
+
|
|
133
|
+
for (let slot = 0; slot < indices.length; slot++) {
|
|
134
|
+
const index = indices[slot]!
|
|
135
|
+
const label = this.outputNetLabelPlacements[index]!
|
|
136
|
+
const orientations = this.getAvailableOrientations(label)
|
|
137
|
+
if (
|
|
138
|
+
this.getChipSideForPoint(label.anchorPoint) === chipSide &&
|
|
139
|
+
orientations.length === 1 &&
|
|
140
|
+
orientations[0] === requiredOrientation
|
|
141
|
+
) {
|
|
142
|
+
affectedSlots.push(slot)
|
|
143
|
+
labelsToOrder.push(index)
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Place vertical labels on the same chip side from the outward end inward.
|
|
148
|
+
// The standalone label is corrected first, then a lower neighbor yields
|
|
149
|
+
// space during its own placement, keeping the standalone connector direct.
|
|
150
|
+
labelsToOrder.sort((a, b) => {
|
|
151
|
+
const aY = this.outputNetLabelPlacements[a]!.anchorPoint.y
|
|
152
|
+
const bY = this.outputNetLabelPlacements[b]!.anchorPoint.y
|
|
153
|
+
return requiredOrientation === "y+" ? bY - aY : aY - bY
|
|
154
|
+
})
|
|
155
|
+
for (let i = 0; i < affectedSlots.length; i++) {
|
|
156
|
+
indices[affectedSlots[i]!] = labelsToOrder[i]!
|
|
157
|
+
}
|
|
158
|
+
|
|
113
159
|
return indices
|
|
114
160
|
}
|
|
115
161
|
|
|
@@ -551,6 +597,35 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
551
597
|
)
|
|
552
598
|
}
|
|
553
599
|
|
|
600
|
+
private isStandaloneSinglePinNetLabel(label: NetLabelPlacement) {
|
|
601
|
+
return this.inputProblem.netConnections.some(
|
|
602
|
+
(connection) =>
|
|
603
|
+
connection.netId === label.netId &&
|
|
604
|
+
connection.pinIds.length === 1 &&
|
|
605
|
+
connection.pinIds[0] === label.pinIds[0],
|
|
606
|
+
)
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
private labelIntersectsDifferentNetTrace(label: NetLabelPlacement) {
|
|
610
|
+
const { width, height } = getDimsForOrientation({
|
|
611
|
+
orientation: label.orientation,
|
|
612
|
+
netLabelWidth: this.getNetLabelWidth(label),
|
|
613
|
+
netLabelHeight: this.getNetLabelHeight(label),
|
|
614
|
+
})
|
|
615
|
+
const center = getCenterFromAnchor(
|
|
616
|
+
label.anchorPoint,
|
|
617
|
+
label.orientation,
|
|
618
|
+
width,
|
|
619
|
+
height,
|
|
620
|
+
)
|
|
621
|
+
const bounds = getRectBounds(center, width, height)
|
|
622
|
+
return Object.values(this.traceMap).some(
|
|
623
|
+
(trace) =>
|
|
624
|
+
trace.globalConnNetId !== label.globalConnNetId &&
|
|
625
|
+
tracePathIntersectsBounds(trace.tracePath, bounds),
|
|
626
|
+
)
|
|
627
|
+
}
|
|
628
|
+
|
|
554
629
|
private getSearchStartAnchor(
|
|
555
630
|
label: NetLabelPlacement,
|
|
556
631
|
orientation: FacingDirection,
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { GraphicsObject, Rect } from "graphics-debug"
|
|
2
|
+
import { BaseSolver } from "lib/solvers/BaseSolver/BaseSolver"
|
|
3
|
+
import type { NetLabelPlacement } from "lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver"
|
|
4
|
+
import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
|
|
5
|
+
import { visualizeInputProblem } from "lib/solvers/SchematicTracePipelineSolver/visualizeInputProblem"
|
|
6
|
+
import type { InputProblem } from "lib/types/InputProblem"
|
|
7
|
+
import { getColorFromString } from "lib/utils/getColorFromString"
|
|
8
|
+
import { alignSameNetJunctions } from "./alignSameNetJunctions"
|
|
9
|
+
|
|
10
|
+
interface SameNetJunctionAlignmentSolverInput {
|
|
11
|
+
inputProblem: InputProblem
|
|
12
|
+
traces: SolvedTracePath[]
|
|
13
|
+
netLabelPlacements: NetLabelPlacement[]
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Turns separate same-net load traces into one shared rail with short junction branches. */
|
|
17
|
+
export class SameNetJunctionAlignmentSolver extends BaseSolver {
|
|
18
|
+
private input: SameNetJunctionAlignmentSolverInput
|
|
19
|
+
outputTraces: SolvedTracePath[]
|
|
20
|
+
|
|
21
|
+
constructor(input: SameNetJunctionAlignmentSolverInput) {
|
|
22
|
+
super()
|
|
23
|
+
this.input = input
|
|
24
|
+
this.outputTraces = input.traces
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
override _step() {
|
|
28
|
+
const result = alignSameNetJunctions(this.input)
|
|
29
|
+
this.outputTraces = result.traces
|
|
30
|
+
this.stats.alignedJunctionCount = result.alignedJunctionCount
|
|
31
|
+
this.solved = true
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
getOutput() {
|
|
35
|
+
return {
|
|
36
|
+
traces: this.outputTraces,
|
|
37
|
+
netLabelPlacements: this.input.netLabelPlacements,
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
override visualize(): GraphicsObject {
|
|
42
|
+
const graphics = visualizeInputProblem(this.input.inputProblem)
|
|
43
|
+
graphics.lines ??= []
|
|
44
|
+
graphics.rects ??= []
|
|
45
|
+
graphics.points ??= []
|
|
46
|
+
for (const trace of this.outputTraces) {
|
|
47
|
+
graphics.lines.push({
|
|
48
|
+
points: trace.tracePath,
|
|
49
|
+
strokeColor: "purple",
|
|
50
|
+
})
|
|
51
|
+
}
|
|
52
|
+
for (const label of this.input.netLabelPlacements) {
|
|
53
|
+
const labelRect: Rect & { strokeColor: string } = {
|
|
54
|
+
center: label.center,
|
|
55
|
+
width: label.width,
|
|
56
|
+
height: label.height,
|
|
57
|
+
fill: getColorFromString(label.globalConnNetId, 0.35),
|
|
58
|
+
strokeColor: getColorFromString(label.globalConnNetId, 0.9),
|
|
59
|
+
label: `netId: ${label.netId}\nglobalConnNetId: ${label.globalConnNetId}`,
|
|
60
|
+
}
|
|
61
|
+
graphics.rects.push(labelRect)
|
|
62
|
+
graphics.points.push({
|
|
63
|
+
x: label.anchorPoint.x,
|
|
64
|
+
y: label.anchorPoint.y,
|
|
65
|
+
color: getColorFromString(label.globalConnNetId, 0.9),
|
|
66
|
+
label: `anchorPoint\norientation: ${label.orientation}`,
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
return graphics
|
|
70
|
+
}
|
|
71
|
+
}
|