@tscircuit/schematic-trace-solver 0.0.188 → 0.0.190
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 +2 -0
- package/dist/index.js +275 -228
- package/lib/solvers/InlineNetLabelSolver/InlineNetLabelSolver.ts +1 -0
- package/lib/solvers/InlineNetLabelSolver/getAnchoredNetLabelRenderedBounds.ts +13 -0
- package/lib/solvers/NetLabelNetLabelCollisionSolver/NetLabelNetLabelCollisionSolver.ts +11 -2
- package/lib/solvers/SameNetJunctionAlignmentSolver/SameNetJunctionAlignmentSolver.ts +4 -9
- package/lib/solvers/SameNetJunctionAlignmentSolver/alignSameNetJunctions.ts +17 -9
- package/lib/solvers/SameNetJunctionAlignmentSolver/collapseSameNetCycles.ts +75 -38
- package/lib/solvers/TraceCleanupSolver/sub-solver/generateLShapeRerouteCandidates.ts +5 -1
- package/package.json +1 -1
- package/site/SchematicTracePipelineSolver/repro-robot-controller-imu-tof.page.tsx +7 -0
- package/tests/bug-reports/bug-report-20260730T061837Z/__snapshots__/bug-report-20260730T061837Z.snap.svg +2 -2
- package/tests/repros/__snapshots__/repro-pmp11282-isolated-dcdc.snap.svg +18 -18
- package/tests/repros/__snapshots__/repro-repeated-power-rail-junctions.snap.svg +255 -0
- package/tests/repros/__snapshots__/repro-robot-controller-imu-tof.snap.svg +199 -0
- package/tests/repros/__snapshots__/repro-robot-controller-imu-tof.source.svg +33 -0
- package/tests/repros/__snapshots__/rp2040-robot-controller-disconnected-gnd.snap.svg +103 -0
- package/tests/repros/assets/repro-repeated-power-rail-junctions.input.json +927 -0
- package/tests/repros/assets/repro-robot-controller-imu-tof.input.json +540 -0
- package/tests/repros/assets/rp2040-robot-controller.input.json +349 -0
- package/tests/repros/repro-repeated-power-rail-junctions.test.ts +22 -0
- package/tests/repros/repro-robot-controller-imu-tof.test.ts +83 -0
- package/tests/repros/rp2040-robot-controller-disconnected-gnd.test.ts +59 -0
- package/tests/solvers/InlineNetLabelSolver/get-anchored-net-label-rendered-bounds.test.ts +24 -0
- package/tests/solvers/SameNetJunctionAlignmentSolver/collapse-same-net-cycles.test.ts +30 -0
- package/tests/solvers/TraceCleanupSolver/perpendicular-detours-preserve-endpoints.test.ts +60 -0
package/dist/index.d.ts
CHANGED
|
@@ -1148,6 +1148,8 @@ interface NetLabelNetLabelCollisionSolverParams {
|
|
|
1148
1148
|
useRenderedLabelBounds?: boolean;
|
|
1149
1149
|
/** Already placed labels that are obstacles, but must not be moved. */
|
|
1150
1150
|
fixedNetLabelPlacements?: NetLabelPlacement[];
|
|
1151
|
+
/** Existing connector identities from AvailableNetOrientationSolver. */
|
|
1152
|
+
netLabelConnectorTraceIds?: ReadonlySet<MspConnectionPairId>;
|
|
1151
1153
|
inputProblem: InputProblem;
|
|
1152
1154
|
traces: SolvedTracePath[];
|
|
1153
1155
|
netLabelPlacements: NetLabelPlacement[];
|
package/dist/index.js
CHANGED
|
@@ -7005,13 +7005,15 @@ var generatePerpendicularTraceDetours = ({
|
|
|
7005
7005
|
bounds2[highBound] + clearance
|
|
7006
7006
|
])
|
|
7007
7007
|
].filter((coordinate) => coordinate !== void 0);
|
|
7008
|
+
const remainingPath = path.slice(index + 2);
|
|
7009
|
+
if (remainingPath.length === 0) remainingPath.push(end);
|
|
7008
7010
|
return [...new Set(detourCoordinates)].map(
|
|
7009
7011
|
(detour) => simplifyPath([
|
|
7010
7012
|
...path.slice(0, index + 1),
|
|
7011
7013
|
{ ...start, [movingAxis]: gate },
|
|
7012
7014
|
{ ...start, [movingAxis]: gate, [detourAxis]: detour },
|
|
7013
7015
|
{ ...end, [detourAxis]: detour },
|
|
7014
|
-
...
|
|
7016
|
+
...remainingPath
|
|
7015
7017
|
])
|
|
7016
7018
|
);
|
|
7017
7019
|
};
|
|
@@ -12403,6 +12405,17 @@ globalConnNetId: ${label.globalConnNetId}`
|
|
|
12403
12405
|
// lib/solvers/InlineNetLabelSolver/getAnchoredNetLabelRenderedBounds.ts
|
|
12404
12406
|
var getAnchoredNetLabelRenderedBounds = (placement) => {
|
|
12405
12407
|
if (placement.orientation !== "x-" && placement.orientation !== "x+") {
|
|
12408
|
+
if (placement.netLabelText && placement.width > placement.height) {
|
|
12409
|
+
const width2 = placement.height;
|
|
12410
|
+
const height2 = placement.width;
|
|
12411
|
+
const center2 = getCenterFromAnchor(
|
|
12412
|
+
placement.anchorPoint,
|
|
12413
|
+
placement.orientation,
|
|
12414
|
+
width2,
|
|
12415
|
+
height2
|
|
12416
|
+
);
|
|
12417
|
+
return getRectBounds(center2, width2, height2);
|
|
12418
|
+
}
|
|
12406
12419
|
return getRectBounds(placement.center, placement.width, placement.height);
|
|
12407
12420
|
}
|
|
12408
12421
|
const width = Math.max(placement.width, placement.height);
|
|
@@ -12565,8 +12578,10 @@ var NetLabelNetLabelCollisionSolver = class extends BaseSolver {
|
|
|
12565
12578
|
status: null
|
|
12566
12579
|
};
|
|
12567
12580
|
};
|
|
12568
|
-
const
|
|
12569
|
-
|
|
12581
|
+
const isAnchoredOnConnector = this.traces.some(
|
|
12582
|
+
(trace) => this.params.netLabelConnectorTraceIds?.has(trace.mspPairId) && trace.globalConnNetId === label.globalConnNetId && tracePathContainsPoint(trace.tracePath, label.anchorPoint)
|
|
12583
|
+
);
|
|
12584
|
+
if (label.mspConnectionPairIds.length === 0 || isAnchoredOnConnector) {
|
|
12570
12585
|
const allOrientations = ["x+", "x-", "y+", "y-"];
|
|
12571
12586
|
const orderedOrientations = [
|
|
12572
12587
|
label.orientation,
|
|
@@ -13260,7 +13275,7 @@ var UnroutedTraceRecoverySolver = class extends BaseSolver {
|
|
|
13260
13275
|
};
|
|
13261
13276
|
|
|
13262
13277
|
// lib/solvers/SameNetJunctionAlignmentSolver/alignSameNetJunctions.ts
|
|
13263
|
-
import { getSegmentIntersection as
|
|
13278
|
+
import { getSegmentIntersection as getSegmentIntersection4 } from "@tscircuit/math-utils/line-intersections";
|
|
13264
13279
|
|
|
13265
13280
|
// lib/solvers/SameNetJunctionAlignmentSolver/pathIntersectsAnyNetLabel.ts
|
|
13266
13281
|
var NET_LABEL_INTERIOR_INSET = 1e-6;
|
|
@@ -13579,6 +13594,242 @@ var collapseRedundantSharedEndpointStubs = ({
|
|
|
13579
13594
|
return outputTraces;
|
|
13580
13595
|
};
|
|
13581
13596
|
|
|
13597
|
+
// lib/solvers/SameNetJunctionAlignmentSolver/collapseSameNetCycles.ts
|
|
13598
|
+
import { getSegmentIntersection as getSegmentIntersection3 } from "@tscircuit/math-utils/line-intersections";
|
|
13599
|
+
var TRACE_LENGTH_EPSILON = 1e-6;
|
|
13600
|
+
var getTracePathStartingAtPin = (trace, pin) => {
|
|
13601
|
+
const pathStart = trace.tracePath[0];
|
|
13602
|
+
const pathEnd = trace.tracePath.at(-1);
|
|
13603
|
+
if (!pathStart || !pathEnd) return null;
|
|
13604
|
+
if (pointsEqual2(pathStart, pin)) return trace.tracePath;
|
|
13605
|
+
if (pointsEqual2(pathEnd, pin)) return [...trace.tracePath].reverse();
|
|
13606
|
+
return null;
|
|
13607
|
+
};
|
|
13608
|
+
var getPerpendicularPathCrossings = (targetPath, donorPath) => {
|
|
13609
|
+
const crossings = [];
|
|
13610
|
+
const sharedEndpoint = targetPath[0];
|
|
13611
|
+
for (let targetSegmentIndex = 0; targetSegmentIndex < targetPath.length - 1; targetSegmentIndex++) {
|
|
13612
|
+
const targetStart = targetPath[targetSegmentIndex];
|
|
13613
|
+
const targetEnd = targetPath[targetSegmentIndex + 1];
|
|
13614
|
+
const targetOrientation = getRailOrientation(targetStart, targetEnd);
|
|
13615
|
+
if (!targetOrientation) continue;
|
|
13616
|
+
for (let donorSegmentIndex = 0; donorSegmentIndex < donorPath.length - 1; donorSegmentIndex++) {
|
|
13617
|
+
const donorStart = donorPath[donorSegmentIndex];
|
|
13618
|
+
const donorEnd = donorPath[donorSegmentIndex + 1];
|
|
13619
|
+
const donorOrientation = getRailOrientation(donorStart, donorEnd);
|
|
13620
|
+
if (!donorOrientation || donorOrientation === targetOrientation) continue;
|
|
13621
|
+
const intersectionPoint = getSegmentIntersection3(
|
|
13622
|
+
targetStart,
|
|
13623
|
+
targetEnd,
|
|
13624
|
+
donorStart,
|
|
13625
|
+
donorEnd
|
|
13626
|
+
);
|
|
13627
|
+
if (!intersectionPoint || pointsEqual2(intersectionPoint, sharedEndpoint)) {
|
|
13628
|
+
continue;
|
|
13629
|
+
}
|
|
13630
|
+
crossings.push({
|
|
13631
|
+
intersectionPoint,
|
|
13632
|
+
targetSegmentIndex,
|
|
13633
|
+
donorSegmentIndex
|
|
13634
|
+
});
|
|
13635
|
+
}
|
|
13636
|
+
}
|
|
13637
|
+
return crossings;
|
|
13638
|
+
};
|
|
13639
|
+
var buildCollapsedSelfCyclePath = (path, firstSegmentIndex, secondSegmentIndex) => {
|
|
13640
|
+
const intersectionPoint = getSegmentIntersection3(
|
|
13641
|
+
path[firstSegmentIndex],
|
|
13642
|
+
path[firstSegmentIndex + 1],
|
|
13643
|
+
path[secondSegmentIndex],
|
|
13644
|
+
path[secondSegmentIndex + 1]
|
|
13645
|
+
);
|
|
13646
|
+
if (!intersectionPoint) return path;
|
|
13647
|
+
return simplifyPath([
|
|
13648
|
+
...path.slice(0, firstSegmentIndex + 1),
|
|
13649
|
+
intersectionPoint,
|
|
13650
|
+
...path.slice(secondSegmentIndex + 1)
|
|
13651
|
+
]);
|
|
13652
|
+
};
|
|
13653
|
+
var buildCollapsedCyclePath = ({
|
|
13654
|
+
targetTrace,
|
|
13655
|
+
targetPath,
|
|
13656
|
+
donorPath,
|
|
13657
|
+
crossing
|
|
13658
|
+
}) => {
|
|
13659
|
+
const pathFromSharedPin = simplifyPath([
|
|
13660
|
+
...donorPath.slice(0, crossing.donorSegmentIndex + 1),
|
|
13661
|
+
crossing.intersectionPoint,
|
|
13662
|
+
...targetPath.slice(crossing.targetSegmentIndex + 1)
|
|
13663
|
+
]);
|
|
13664
|
+
if (pointsEqual2(targetTrace.tracePath[0], targetPath[0])) {
|
|
13665
|
+
return pathFromSharedPin;
|
|
13666
|
+
}
|
|
13667
|
+
return pathFromSharedPin.reverse();
|
|
13668
|
+
};
|
|
13669
|
+
var getNetLabelPlacementsForCycleCollapse = ({
|
|
13670
|
+
targetTrace,
|
|
13671
|
+
tracePath,
|
|
13672
|
+
netLabelPlacements
|
|
13673
|
+
}) => {
|
|
13674
|
+
const candidateNetLabelPlacements = moveAttachedLabelsToReroutedTrace({
|
|
13675
|
+
trace: targetTrace,
|
|
13676
|
+
originalTracePath: targetTrace.tracePath,
|
|
13677
|
+
reroutedTracePath: tracePath,
|
|
13678
|
+
netLabelPlacements
|
|
13679
|
+
});
|
|
13680
|
+
for (let labelIndex = 0; labelIndex < netLabelPlacements.length; labelIndex++) {
|
|
13681
|
+
const label = netLabelPlacements[labelIndex];
|
|
13682
|
+
if (label.globalConnNetId !== targetTrace.globalConnNetId) continue;
|
|
13683
|
+
if (!tracePathContainsPoint(targetTrace.tracePath, label.anchorPoint)) {
|
|
13684
|
+
continue;
|
|
13685
|
+
}
|
|
13686
|
+
const candidateLabel = candidateNetLabelPlacements[labelIndex];
|
|
13687
|
+
if (!tracePathContainsPoint(tracePath, candidateLabel.anchorPoint)) {
|
|
13688
|
+
return null;
|
|
13689
|
+
}
|
|
13690
|
+
}
|
|
13691
|
+
const otherNetLabelPlacements = candidateNetLabelPlacements.filter(
|
|
13692
|
+
(label) => label.globalConnNetId !== targetTrace.globalConnNetId
|
|
13693
|
+
);
|
|
13694
|
+
if (pathIntersectsAnyNetLabel({
|
|
13695
|
+
path: tracePath,
|
|
13696
|
+
netLabelPlacements: otherNetLabelPlacements
|
|
13697
|
+
})) {
|
|
13698
|
+
return null;
|
|
13699
|
+
}
|
|
13700
|
+
const sameNetLabelPlacements = candidateNetLabelPlacements.filter(
|
|
13701
|
+
(label) => label.globalConnNetId === targetTrace.globalConnNetId
|
|
13702
|
+
);
|
|
13703
|
+
if (pathEntersAnyNetLabel({
|
|
13704
|
+
path: tracePath,
|
|
13705
|
+
netLabelPlacements: sameNetLabelPlacements
|
|
13706
|
+
})) {
|
|
13707
|
+
return null;
|
|
13708
|
+
}
|
|
13709
|
+
return candidateNetLabelPlacements;
|
|
13710
|
+
};
|
|
13711
|
+
var candidateIsBetter = (candidate, bestCandidate) => {
|
|
13712
|
+
if (!bestCandidate) return true;
|
|
13713
|
+
const visibleLengthDelta = candidate.netVisibleLength - bestCandidate.netVisibleLength;
|
|
13714
|
+
if (Math.abs(visibleLengthDelta) > TRACE_LENGTH_EPSILON) {
|
|
13715
|
+
return visibleLengthDelta < 0;
|
|
13716
|
+
}
|
|
13717
|
+
return candidate.netVisibleSegmentCount < bestCandidate.netVisibleSegmentCount;
|
|
13718
|
+
};
|
|
13719
|
+
var getBestCycleCollapseCandidate = ({
|
|
13720
|
+
targetTrace,
|
|
13721
|
+
traces,
|
|
13722
|
+
netLabelPlacements
|
|
13723
|
+
}) => {
|
|
13724
|
+
const sameNetTraces = traces.filter(
|
|
13725
|
+
(trace) => trace.globalConnNetId === targetTrace.globalConnNetId
|
|
13726
|
+
);
|
|
13727
|
+
const baselineNetVisibleLength = getVisibleTraceLength(sameNetTraces);
|
|
13728
|
+
const baselineTargetVisibleLength = getVisibleTraceLength([targetTrace]);
|
|
13729
|
+
let bestCandidate = null;
|
|
13730
|
+
const considerTracePath = (tracePath) => {
|
|
13731
|
+
const candidateTrace = { ...targetTrace, tracePath };
|
|
13732
|
+
const candidateTargetVisibleLength = getVisibleTraceLength([candidateTrace]);
|
|
13733
|
+
if (candidateTargetVisibleLength > baselineTargetVisibleLength + TRACE_LENGTH_EPSILON) {
|
|
13734
|
+
return;
|
|
13735
|
+
}
|
|
13736
|
+
const candidateNetLabelPlacements = getNetLabelPlacementsForCycleCollapse({
|
|
13737
|
+
targetTrace,
|
|
13738
|
+
tracePath,
|
|
13739
|
+
netLabelPlacements
|
|
13740
|
+
});
|
|
13741
|
+
if (!candidateNetLabelPlacements) return;
|
|
13742
|
+
const candidateNetTraces = sameNetTraces.map((trace) => {
|
|
13743
|
+
if (trace.mspPairId === targetTrace.mspPairId) return candidateTrace;
|
|
13744
|
+
return trace;
|
|
13745
|
+
});
|
|
13746
|
+
const netVisibleLength = getVisibleTraceLength(candidateNetTraces);
|
|
13747
|
+
if (netVisibleLength >= baselineNetVisibleLength - TRACE_LENGTH_EPSILON) {
|
|
13748
|
+
return;
|
|
13749
|
+
}
|
|
13750
|
+
const candidate = {
|
|
13751
|
+
tracePath,
|
|
13752
|
+
netLabelPlacements: candidateNetLabelPlacements,
|
|
13753
|
+
netVisibleLength,
|
|
13754
|
+
netVisibleSegmentCount: getVisibleTraceSegmentCount(candidateNetTraces)
|
|
13755
|
+
};
|
|
13756
|
+
if (candidateIsBetter(candidate, bestCandidate)) {
|
|
13757
|
+
bestCandidate = candidate;
|
|
13758
|
+
}
|
|
13759
|
+
};
|
|
13760
|
+
const selfCrossings = findPerpendicularPathCrossings(
|
|
13761
|
+
targetTrace.tracePath,
|
|
13762
|
+
targetTrace.tracePath,
|
|
13763
|
+
{ includeTerminalSegments: true }
|
|
13764
|
+
).filter(
|
|
13765
|
+
({ pathSegmentIndex, otherPathSegmentIndex }) => pathSegmentIndex < otherPathSegmentIndex
|
|
13766
|
+
);
|
|
13767
|
+
for (const crossing of selfCrossings) {
|
|
13768
|
+
considerTracePath(
|
|
13769
|
+
buildCollapsedSelfCyclePath(
|
|
13770
|
+
targetTrace.tracePath,
|
|
13771
|
+
crossing.pathSegmentIndex,
|
|
13772
|
+
crossing.otherPathSegmentIndex
|
|
13773
|
+
)
|
|
13774
|
+
);
|
|
13775
|
+
}
|
|
13776
|
+
for (const donorTrace of sameNetTraces) {
|
|
13777
|
+
if (donorTrace.mspPairId === targetTrace.mspPairId) continue;
|
|
13778
|
+
const sharedPin = getSharedPin({
|
|
13779
|
+
donorTrace,
|
|
13780
|
+
branchTrace: targetTrace
|
|
13781
|
+
});
|
|
13782
|
+
if (!sharedPin) continue;
|
|
13783
|
+
const targetPath = getTracePathStartingAtPin(targetTrace, sharedPin);
|
|
13784
|
+
const donorPath = getTracePathStartingAtPin(donorTrace, sharedPin);
|
|
13785
|
+
if (!targetPath || !donorPath) continue;
|
|
13786
|
+
const crossings = getPerpendicularPathCrossings(targetPath, donorPath);
|
|
13787
|
+
for (const crossing of crossings) {
|
|
13788
|
+
const tracePath = buildCollapsedCyclePath({
|
|
13789
|
+
targetTrace,
|
|
13790
|
+
targetPath,
|
|
13791
|
+
donorPath,
|
|
13792
|
+
crossing
|
|
13793
|
+
});
|
|
13794
|
+
considerTracePath(tracePath);
|
|
13795
|
+
}
|
|
13796
|
+
}
|
|
13797
|
+
return bestCandidate;
|
|
13798
|
+
};
|
|
13799
|
+
var collapseSameNetCycles = ({
|
|
13800
|
+
traces,
|
|
13801
|
+
netLabelPlacements
|
|
13802
|
+
}) => {
|
|
13803
|
+
const outputTraces = [...traces];
|
|
13804
|
+
let outputNetLabelPlacements = [...netLabelPlacements];
|
|
13805
|
+
let collapsedCycleCount = 0;
|
|
13806
|
+
let traceIndex = 0;
|
|
13807
|
+
while (traceIndex < outputTraces.length) {
|
|
13808
|
+
const targetTrace = outputTraces[traceIndex];
|
|
13809
|
+
const candidate = getBestCycleCollapseCandidate({
|
|
13810
|
+
targetTrace,
|
|
13811
|
+
traces: outputTraces,
|
|
13812
|
+
netLabelPlacements: outputNetLabelPlacements
|
|
13813
|
+
});
|
|
13814
|
+
if (!candidate) {
|
|
13815
|
+
traceIndex++;
|
|
13816
|
+
continue;
|
|
13817
|
+
}
|
|
13818
|
+
outputTraces[traceIndex] = {
|
|
13819
|
+
...targetTrace,
|
|
13820
|
+
tracePath: candidate.tracePath
|
|
13821
|
+
};
|
|
13822
|
+
outputNetLabelPlacements = candidate.netLabelPlacements;
|
|
13823
|
+
collapsedCycleCount++;
|
|
13824
|
+
traceIndex = 0;
|
|
13825
|
+
}
|
|
13826
|
+
return {
|
|
13827
|
+
traces: outputTraces,
|
|
13828
|
+
netLabelPlacements: outputNetLabelPlacements,
|
|
13829
|
+
collapsedCycleCount
|
|
13830
|
+
};
|
|
13831
|
+
};
|
|
13832
|
+
|
|
13582
13833
|
// lib/solvers/SameNetJunctionAlignmentSolver/alignSameNetJunctions.ts
|
|
13583
13834
|
var MAX_ALIGNED_LOAD_PIN_OFFSET = 0.2;
|
|
13584
13835
|
var MAX_SAME_NET_LABEL_BOUNDARY_RAIL_OFFSET = 0.2;
|
|
@@ -13659,8 +13910,10 @@ var getHorizontalPinApproachPoint = ({
|
|
|
13659
13910
|
return approachesFromFacingSide ? approachPoint : null;
|
|
13660
13911
|
};
|
|
13661
13912
|
var getAttachedLabelIndexes = (trace, netLabelPlacements) => netLabelPlacements.flatMap((label, index) => {
|
|
13662
|
-
|
|
13663
|
-
|
|
13913
|
+
if (isLabelAttachedToTrace(label, trace) && tracePathContainsPoint(trace.tracePath, label.anchorPoint)) {
|
|
13914
|
+
return [index];
|
|
13915
|
+
}
|
|
13916
|
+
return [];
|
|
13664
13917
|
});
|
|
13665
13918
|
var moveAttachedLabels = ({
|
|
13666
13919
|
trace,
|
|
@@ -13961,7 +14214,7 @@ var pathsIntersect = (firstPath, secondPath) => {
|
|
|
13961
14214
|
}
|
|
13962
14215
|
for (let firstIndex = 1; firstIndex < firstPath.length; firstIndex++) {
|
|
13963
14216
|
for (let secondIndex = 1; secondIndex < secondPath.length; secondIndex++) {
|
|
13964
|
-
if (
|
|
14217
|
+
if (getSegmentIntersection4(
|
|
13965
14218
|
firstPath[firstIndex - 1],
|
|
13966
14219
|
firstPath[firstIndex],
|
|
13967
14220
|
secondPath[secondIndex - 1],
|
|
@@ -14145,7 +14398,7 @@ var candidateIsClear = ({
|
|
|
14145
14398
|
const intersections2 = /* @__PURE__ */ new Set();
|
|
14146
14399
|
for (let i = 1; i < path.length; i++) {
|
|
14147
14400
|
for (let j = 1; j < otherTrace.tracePath.length; j++) {
|
|
14148
|
-
const point =
|
|
14401
|
+
const point = getSegmentIntersection4(
|
|
14149
14402
|
path[i - 1],
|
|
14150
14403
|
path[i],
|
|
14151
14404
|
otherTrace.tracePath[j - 1],
|
|
@@ -14428,225 +14681,22 @@ var alignSameNetJunctions = ({
|
|
|
14428
14681
|
}
|
|
14429
14682
|
}
|
|
14430
14683
|
}
|
|
14431
|
-
const
|
|
14684
|
+
const cycleCollapse = collapseSameNetCycles({
|
|
14432
14685
|
traces: outputTraces,
|
|
14433
|
-
netLabelPlacements: outputNetLabelPlacements
|
|
14686
|
+
netLabelPlacements: outputNetLabelPlacements
|
|
14687
|
+
});
|
|
14688
|
+
outputTraces = collapseRedundantSharedEndpointStubs({
|
|
14689
|
+
traces: cycleCollapse.traces,
|
|
14690
|
+
netLabelPlacements: cycleCollapse.netLabelPlacements,
|
|
14434
14691
|
netLabelConnectorTraceIds,
|
|
14435
14692
|
multiPinNetPinIds: getMultiPinNetPinIds(inputProblem)
|
|
14436
14693
|
});
|
|
14437
|
-
|
|
14438
|
-
return {
|
|
14439
|
-
traces: outputTraces,
|
|
14440
|
-
netLabelPlacements: outputNetLabelPlacements,
|
|
14441
|
-
alignedJunctionCount
|
|
14442
|
-
};
|
|
14443
|
-
};
|
|
14444
|
-
|
|
14445
|
-
// lib/solvers/SameNetJunctionAlignmentSolver/collapseSameNetCycles.ts
|
|
14446
|
-
import { getSegmentIntersection as getSegmentIntersection4 } from "@tscircuit/math-utils/line-intersections";
|
|
14447
|
-
var TRACE_LENGTH_EPSILON = 1e-6;
|
|
14448
|
-
var getTracePathStartingAtPin = (trace, pin) => {
|
|
14449
|
-
const pathStart = trace.tracePath[0];
|
|
14450
|
-
const pathEnd = trace.tracePath.at(-1);
|
|
14451
|
-
if (!pathStart || !pathEnd) return null;
|
|
14452
|
-
if (pointsEqual2(pathStart, pin)) return trace.tracePath;
|
|
14453
|
-
if (pointsEqual2(pathEnd, pin)) return [...trace.tracePath].reverse();
|
|
14454
|
-
return null;
|
|
14455
|
-
};
|
|
14456
|
-
var getPerpendicularPathCrossings = (targetPath, donorPath) => {
|
|
14457
|
-
const crossings = [];
|
|
14458
|
-
const sharedEndpoint = targetPath[0];
|
|
14459
|
-
for (let targetSegmentIndex = 0; targetSegmentIndex < targetPath.length - 1; targetSegmentIndex++) {
|
|
14460
|
-
const targetStart = targetPath[targetSegmentIndex];
|
|
14461
|
-
const targetEnd = targetPath[targetSegmentIndex + 1];
|
|
14462
|
-
const targetOrientation = getRailOrientation(targetStart, targetEnd);
|
|
14463
|
-
if (!targetOrientation) continue;
|
|
14464
|
-
for (let donorSegmentIndex = 0; donorSegmentIndex < donorPath.length - 1; donorSegmentIndex++) {
|
|
14465
|
-
const donorStart = donorPath[donorSegmentIndex];
|
|
14466
|
-
const donorEnd = donorPath[donorSegmentIndex + 1];
|
|
14467
|
-
const donorOrientation = getRailOrientation(donorStart, donorEnd);
|
|
14468
|
-
if (!donorOrientation || donorOrientation === targetOrientation) continue;
|
|
14469
|
-
const intersectionPoint = getSegmentIntersection4(
|
|
14470
|
-
targetStart,
|
|
14471
|
-
targetEnd,
|
|
14472
|
-
donorStart,
|
|
14473
|
-
donorEnd
|
|
14474
|
-
);
|
|
14475
|
-
if (!intersectionPoint || pointsEqual2(intersectionPoint, sharedEndpoint)) {
|
|
14476
|
-
continue;
|
|
14477
|
-
}
|
|
14478
|
-
crossings.push({
|
|
14479
|
-
intersectionPoint,
|
|
14480
|
-
targetSegmentIndex,
|
|
14481
|
-
donorSegmentIndex
|
|
14482
|
-
});
|
|
14483
|
-
}
|
|
14484
|
-
}
|
|
14485
|
-
return crossings;
|
|
14486
|
-
};
|
|
14487
|
-
var buildCollapsedCyclePath = ({
|
|
14488
|
-
targetTrace,
|
|
14489
|
-
targetPath,
|
|
14490
|
-
donorPath,
|
|
14491
|
-
crossing
|
|
14492
|
-
}) => {
|
|
14493
|
-
const pathFromSharedPin = simplifyPath([
|
|
14494
|
-
...donorPath.slice(0, crossing.donorSegmentIndex + 1),
|
|
14495
|
-
crossing.intersectionPoint,
|
|
14496
|
-
...targetPath.slice(crossing.targetSegmentIndex + 1)
|
|
14497
|
-
]);
|
|
14498
|
-
if (pointsEqual2(targetTrace.tracePath[0], targetPath[0])) {
|
|
14499
|
-
return pathFromSharedPin;
|
|
14500
|
-
}
|
|
14501
|
-
return pathFromSharedPin.reverse();
|
|
14502
|
-
};
|
|
14503
|
-
var getNetLabelPlacementsForCycleCollapse = ({
|
|
14504
|
-
targetTrace,
|
|
14505
|
-
tracePath,
|
|
14506
|
-
netLabelPlacements
|
|
14507
|
-
}) => {
|
|
14508
|
-
const candidateNetLabelPlacements = moveAttachedLabelsToReroutedTrace({
|
|
14509
|
-
trace: targetTrace,
|
|
14510
|
-
originalTracePath: targetTrace.tracePath,
|
|
14511
|
-
reroutedTracePath: tracePath,
|
|
14512
|
-
netLabelPlacements
|
|
14513
|
-
});
|
|
14514
|
-
for (let labelIndex = 0; labelIndex < netLabelPlacements.length; labelIndex++) {
|
|
14515
|
-
const label = netLabelPlacements[labelIndex];
|
|
14516
|
-
if (label.globalConnNetId !== targetTrace.globalConnNetId) continue;
|
|
14517
|
-
if (!tracePathContainsPoint(targetTrace.tracePath, label.anchorPoint)) {
|
|
14518
|
-
continue;
|
|
14519
|
-
}
|
|
14520
|
-
const candidateLabel = candidateNetLabelPlacements[labelIndex];
|
|
14521
|
-
if (!tracePathContainsPoint(tracePath, candidateLabel.anchorPoint)) {
|
|
14522
|
-
return null;
|
|
14523
|
-
}
|
|
14524
|
-
}
|
|
14525
|
-
const otherNetLabelPlacements = candidateNetLabelPlacements.filter(
|
|
14526
|
-
(label) => label.globalConnNetId !== targetTrace.globalConnNetId
|
|
14527
|
-
);
|
|
14528
|
-
if (pathIntersectsAnyNetLabel({
|
|
14529
|
-
path: tracePath,
|
|
14530
|
-
netLabelPlacements: otherNetLabelPlacements
|
|
14531
|
-
})) {
|
|
14532
|
-
return null;
|
|
14533
|
-
}
|
|
14534
|
-
const sameNetLabelPlacements = candidateNetLabelPlacements.filter(
|
|
14535
|
-
(label) => label.globalConnNetId === targetTrace.globalConnNetId
|
|
14536
|
-
);
|
|
14537
|
-
if (pathEntersAnyNetLabel({
|
|
14538
|
-
path: tracePath,
|
|
14539
|
-
netLabelPlacements: sameNetLabelPlacements
|
|
14540
|
-
})) {
|
|
14541
|
-
return null;
|
|
14542
|
-
}
|
|
14543
|
-
return candidateNetLabelPlacements;
|
|
14544
|
-
};
|
|
14545
|
-
var candidateIsBetter = (candidate, bestCandidate) => {
|
|
14546
|
-
if (!bestCandidate) return true;
|
|
14547
|
-
const visibleLengthDelta = candidate.netVisibleLength - bestCandidate.netVisibleLength;
|
|
14548
|
-
if (Math.abs(visibleLengthDelta) > TRACE_LENGTH_EPSILON) {
|
|
14549
|
-
return visibleLengthDelta < 0;
|
|
14550
|
-
}
|
|
14551
|
-
return candidate.netVisibleSegmentCount < bestCandidate.netVisibleSegmentCount;
|
|
14552
|
-
};
|
|
14553
|
-
var getBestCycleCollapseCandidate = ({
|
|
14554
|
-
targetTrace,
|
|
14555
|
-
traces,
|
|
14556
|
-
netLabelPlacements
|
|
14557
|
-
}) => {
|
|
14558
|
-
const sameNetTraces = traces.filter(
|
|
14559
|
-
(trace) => trace.globalConnNetId === targetTrace.globalConnNetId
|
|
14560
|
-
);
|
|
14561
|
-
const baselineNetVisibleLength = getVisibleTraceLength(sameNetTraces);
|
|
14562
|
-
const baselineTargetVisibleLength = getVisibleTraceLength([targetTrace]);
|
|
14563
|
-
let bestCandidate = null;
|
|
14564
|
-
for (const donorTrace of sameNetTraces) {
|
|
14565
|
-
if (donorTrace.mspPairId === targetTrace.mspPairId) continue;
|
|
14566
|
-
const sharedPin = getSharedPin({
|
|
14567
|
-
donorTrace,
|
|
14568
|
-
branchTrace: targetTrace
|
|
14569
|
-
});
|
|
14570
|
-
if (!sharedPin) continue;
|
|
14571
|
-
const targetPath = getTracePathStartingAtPin(targetTrace, sharedPin);
|
|
14572
|
-
const donorPath = getTracePathStartingAtPin(donorTrace, sharedPin);
|
|
14573
|
-
if (!targetPath || !donorPath) continue;
|
|
14574
|
-
const crossings = getPerpendicularPathCrossings(targetPath, donorPath);
|
|
14575
|
-
for (const crossing of crossings) {
|
|
14576
|
-
const tracePath = buildCollapsedCyclePath({
|
|
14577
|
-
targetTrace,
|
|
14578
|
-
targetPath,
|
|
14579
|
-
donorPath,
|
|
14580
|
-
crossing
|
|
14581
|
-
});
|
|
14582
|
-
const candidateTrace = { ...targetTrace, tracePath };
|
|
14583
|
-
const candidateTargetVisibleLength = getVisibleTraceLength([
|
|
14584
|
-
candidateTrace
|
|
14585
|
-
]);
|
|
14586
|
-
if (candidateTargetVisibleLength > baselineTargetVisibleLength + TRACE_LENGTH_EPSILON) {
|
|
14587
|
-
continue;
|
|
14588
|
-
}
|
|
14589
|
-
const candidateNetLabelPlacements = getNetLabelPlacementsForCycleCollapse(
|
|
14590
|
-
{
|
|
14591
|
-
targetTrace,
|
|
14592
|
-
tracePath,
|
|
14593
|
-
netLabelPlacements
|
|
14594
|
-
}
|
|
14595
|
-
);
|
|
14596
|
-
if (!candidateNetLabelPlacements) continue;
|
|
14597
|
-
const candidateNetTraces = sameNetTraces.map((trace) => {
|
|
14598
|
-
if (trace.mspPairId === targetTrace.mspPairId) return candidateTrace;
|
|
14599
|
-
return trace;
|
|
14600
|
-
});
|
|
14601
|
-
const netVisibleLength = getVisibleTraceLength(candidateNetTraces);
|
|
14602
|
-
if (netVisibleLength >= baselineNetVisibleLength - TRACE_LENGTH_EPSILON) {
|
|
14603
|
-
continue;
|
|
14604
|
-
}
|
|
14605
|
-
const netVisibleSegmentCount = getVisibleTraceSegmentCount(candidateNetTraces);
|
|
14606
|
-
const candidate = {
|
|
14607
|
-
tracePath,
|
|
14608
|
-
netLabelPlacements: candidateNetLabelPlacements,
|
|
14609
|
-
netVisibleLength,
|
|
14610
|
-
netVisibleSegmentCount
|
|
14611
|
-
};
|
|
14612
|
-
if (candidateIsBetter(candidate, bestCandidate)) {
|
|
14613
|
-
bestCandidate = candidate;
|
|
14614
|
-
}
|
|
14615
|
-
}
|
|
14616
|
-
}
|
|
14617
|
-
return bestCandidate;
|
|
14618
|
-
};
|
|
14619
|
-
var collapseSameNetCycles = ({
|
|
14620
|
-
traces,
|
|
14621
|
-
netLabelPlacements
|
|
14622
|
-
}) => {
|
|
14623
|
-
const outputTraces = [...traces];
|
|
14624
|
-
let outputNetLabelPlacements = [...netLabelPlacements];
|
|
14625
|
-
let collapsedCycleCount = 0;
|
|
14626
|
-
let traceIndex = 0;
|
|
14627
|
-
while (traceIndex < outputTraces.length) {
|
|
14628
|
-
const targetTrace = outputTraces[traceIndex];
|
|
14629
|
-
const candidate = getBestCycleCollapseCandidate({
|
|
14630
|
-
targetTrace,
|
|
14631
|
-
traces: outputTraces,
|
|
14632
|
-
netLabelPlacements: outputNetLabelPlacements
|
|
14633
|
-
});
|
|
14634
|
-
if (!candidate) {
|
|
14635
|
-
traceIndex++;
|
|
14636
|
-
continue;
|
|
14637
|
-
}
|
|
14638
|
-
outputTraces[traceIndex] = {
|
|
14639
|
-
...targetTrace,
|
|
14640
|
-
tracePath: candidate.tracePath
|
|
14641
|
-
};
|
|
14642
|
-
outputNetLabelPlacements = candidate.netLabelPlacements;
|
|
14643
|
-
collapsedCycleCount++;
|
|
14644
|
-
traceIndex = 0;
|
|
14645
|
-
}
|
|
14694
|
+
outputNetLabelPlacements = cycleCollapse.netLabelPlacements;
|
|
14646
14695
|
return {
|
|
14647
14696
|
traces: outputTraces,
|
|
14648
14697
|
netLabelPlacements: outputNetLabelPlacements,
|
|
14649
|
-
|
|
14698
|
+
alignedJunctionCount,
|
|
14699
|
+
collapsedCycleCount: cycleCollapse.collapsedCycleCount
|
|
14650
14700
|
};
|
|
14651
14701
|
};
|
|
14652
14702
|
|
|
@@ -14759,18 +14809,14 @@ var SameNetJunctionAlignmentSolver = class extends BaseSolver {
|
|
|
14759
14809
|
}
|
|
14760
14810
|
_step() {
|
|
14761
14811
|
const alignment = alignSameNetJunctions(this.input);
|
|
14762
|
-
|
|
14763
|
-
traces: alignment.traces,
|
|
14764
|
-
netLabelPlacements: alignment.netLabelPlacements
|
|
14765
|
-
});
|
|
14766
|
-
this.outputTraces = cycleCollapse.traces;
|
|
14812
|
+
this.outputTraces = alignment.traces;
|
|
14767
14813
|
this.outputNetLabelPlacements = placeGroundRailLabelsAtOuterEnd({
|
|
14768
14814
|
inputProblem: this.input.inputProblem,
|
|
14769
|
-
traces:
|
|
14770
|
-
netLabelPlacements:
|
|
14815
|
+
traces: alignment.traces,
|
|
14816
|
+
netLabelPlacements: alignment.netLabelPlacements
|
|
14771
14817
|
});
|
|
14772
14818
|
this.stats.alignedJunctionCount = alignment.alignedJunctionCount;
|
|
14773
|
-
this.stats.collapsedCycleCount =
|
|
14819
|
+
this.stats.collapsedCycleCount = alignment.collapsedCycleCount;
|
|
14774
14820
|
this.solved = true;
|
|
14775
14821
|
}
|
|
14776
14822
|
getOutput() {
|
|
@@ -17098,6 +17144,7 @@ var InlineNetLabelSolver = class _InlineNetLabelSolver extends BaseSolver {
|
|
|
17098
17144
|
traces: [...outputTraces, ...terminalTraces],
|
|
17099
17145
|
netLabelPlacements: retainedNetLabelPlacements,
|
|
17100
17146
|
fixedNetLabelPlacements: fixedLabels,
|
|
17147
|
+
netLabelConnectorTraceIds: this.netLabelConnectorTraceIds,
|
|
17101
17148
|
useRenderedLabelBounds: activeInlinePlacements.length > 0
|
|
17102
17149
|
});
|
|
17103
17150
|
if (activeInlinePlacements.length > 0) labelCollisionSolver.solve();
|
|
@@ -1193,6 +1193,7 @@ export class InlineNetLabelSolver extends BaseSolver {
|
|
|
1193
1193
|
traces: [...outputTraces, ...terminalTraces],
|
|
1194
1194
|
netLabelPlacements: retainedNetLabelPlacements,
|
|
1195
1195
|
fixedNetLabelPlacements: fixedLabels,
|
|
1196
|
+
netLabelConnectorTraceIds: this.netLabelConnectorTraceIds,
|
|
1196
1197
|
useRenderedLabelBounds: activeInlinePlacements.length > 0,
|
|
1197
1198
|
})
|
|
1198
1199
|
if (activeInlinePlacements.length > 0) labelCollisionSolver.solve()
|
|
@@ -19,6 +19,19 @@ export const getAnchoredNetLabelRenderedBounds = (
|
|
|
19
19
|
placement: NetLabelPlacement,
|
|
20
20
|
): Bounds => {
|
|
21
21
|
if (placement.orientation !== "x-" && placement.orientation !== "x+") {
|
|
22
|
+
// Named vertical tags render their long text extent along y. Width-only
|
|
23
|
+
// placeholders retain the supplied envelope instead.
|
|
24
|
+
if (placement.netLabelText && placement.width > placement.height) {
|
|
25
|
+
const width = placement.height
|
|
26
|
+
const height = placement.width
|
|
27
|
+
const center = getCenterFromAnchor(
|
|
28
|
+
placement.anchorPoint,
|
|
29
|
+
placement.orientation,
|
|
30
|
+
width,
|
|
31
|
+
height,
|
|
32
|
+
)
|
|
33
|
+
return getRectBounds(center, width, height)
|
|
34
|
+
}
|
|
22
35
|
return getRectBounds(placement.center, placement.width, placement.height)
|
|
23
36
|
}
|
|
24
37
|
|
|
@@ -2,6 +2,7 @@ import { getAnchoredNetLabelRenderedBounds } from "../InlineNetLabelSolver/getAn
|
|
|
2
2
|
import type { GraphicsObject } from "graphics-debug"
|
|
3
3
|
import { BaseSolver } from "lib/solvers/BaseSolver/BaseSolver"
|
|
4
4
|
import type { NetLabelPlacement } from "lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver"
|
|
5
|
+
import { tracePathContainsPoint } from "lib/solvers/RailNetLabelCornerPlacementSolver/geometry"
|
|
5
6
|
import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
|
|
6
7
|
import type { MspConnectionPairId } from "lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver"
|
|
7
8
|
import type { InputProblem } from "lib/types/InputProblem"
|
|
@@ -96,6 +97,8 @@ export interface NetLabelNetLabelCollisionSolverParams {
|
|
|
96
97
|
useRenderedLabelBounds?: boolean
|
|
97
98
|
/** Already placed labels that are obstacles, but must not be moved. */
|
|
98
99
|
fixedNetLabelPlacements?: NetLabelPlacement[]
|
|
100
|
+
/** Existing connector identities from AvailableNetOrientationSolver. */
|
|
101
|
+
netLabelConnectorTraceIds?: ReadonlySet<MspConnectionPairId>
|
|
99
102
|
inputProblem: InputProblem
|
|
100
103
|
traces: SolvedTracePath[]
|
|
101
104
|
netLabelPlacements: NetLabelPlacement[]
|
|
@@ -245,9 +248,15 @@ export class NetLabelNetLabelCollisionSolver extends BaseSolver {
|
|
|
245
248
|
}
|
|
246
249
|
}
|
|
247
250
|
|
|
248
|
-
|
|
251
|
+
// A label on an extended connector must keep that attachment point.
|
|
252
|
+
const isAnchoredOnConnector = this.traces.some(
|
|
253
|
+
(trace) =>
|
|
254
|
+
this.params.netLabelConnectorTraceIds?.has(trace.mspPairId) &&
|
|
255
|
+
trace.globalConnNetId === label.globalConnNetId &&
|
|
256
|
+
tracePathContainsPoint(trace.tracePath, label.anchorPoint),
|
|
257
|
+
)
|
|
249
258
|
|
|
250
|
-
if (
|
|
259
|
+
if (label.mspConnectionPairIds.length === 0 || isAnchoredOnConnector) {
|
|
251
260
|
const allOrientations: FacingDirection[] = ["x+", "x-", "y+", "y-"]
|
|
252
261
|
const orderedOrientations = [
|
|
253
262
|
label.orientation,
|