@tscircuit/schematic-trace-solver 0.0.189 → 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.js CHANGED
@@ -13275,7 +13275,7 @@ var UnroutedTraceRecoverySolver = class extends BaseSolver {
13275
13275
  };
13276
13276
 
13277
13277
  // lib/solvers/SameNetJunctionAlignmentSolver/alignSameNetJunctions.ts
13278
- import { getSegmentIntersection as getSegmentIntersection3 } from "@tscircuit/math-utils/line-intersections";
13278
+ import { getSegmentIntersection as getSegmentIntersection4 } from "@tscircuit/math-utils/line-intersections";
13279
13279
 
13280
13280
  // lib/solvers/SameNetJunctionAlignmentSolver/pathIntersectsAnyNetLabel.ts
13281
13281
  var NET_LABEL_INTERIOR_INSET = 1e-6;
@@ -13594,6 +13594,242 @@ var collapseRedundantSharedEndpointStubs = ({
13594
13594
  return outputTraces;
13595
13595
  };
13596
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
+
13597
13833
  // lib/solvers/SameNetJunctionAlignmentSolver/alignSameNetJunctions.ts
13598
13834
  var MAX_ALIGNED_LOAD_PIN_OFFSET = 0.2;
13599
13835
  var MAX_SAME_NET_LABEL_BOUNDARY_RAIL_OFFSET = 0.2;
@@ -13978,7 +14214,7 @@ var pathsIntersect = (firstPath, secondPath) => {
13978
14214
  }
13979
14215
  for (let firstIndex = 1; firstIndex < firstPath.length; firstIndex++) {
13980
14216
  for (let secondIndex = 1; secondIndex < secondPath.length; secondIndex++) {
13981
- if (getSegmentIntersection3(
14217
+ if (getSegmentIntersection4(
13982
14218
  firstPath[firstIndex - 1],
13983
14219
  firstPath[firstIndex],
13984
14220
  secondPath[secondIndex - 1],
@@ -14162,7 +14398,7 @@ var candidateIsClear = ({
14162
14398
  const intersections2 = /* @__PURE__ */ new Set();
14163
14399
  for (let i = 1; i < path.length; i++) {
14164
14400
  for (let j = 1; j < otherTrace.tracePath.length; j++) {
14165
- const point = getSegmentIntersection3(
14401
+ const point = getSegmentIntersection4(
14166
14402
  path[i - 1],
14167
14403
  path[i],
14168
14404
  otherTrace.tracePath[j - 1],
@@ -14445,225 +14681,22 @@ var alignSameNetJunctions = ({
14445
14681
  }
14446
14682
  }
14447
14683
  }
14448
- const collapsedSharedEndpointStubs = collapseRedundantSharedEndpointStubs({
14684
+ const cycleCollapse = collapseSameNetCycles({
14449
14685
  traces: outputTraces,
14450
- netLabelPlacements: outputNetLabelPlacements,
14686
+ netLabelPlacements: outputNetLabelPlacements
14687
+ });
14688
+ outputTraces = collapseRedundantSharedEndpointStubs({
14689
+ traces: cycleCollapse.traces,
14690
+ netLabelPlacements: cycleCollapse.netLabelPlacements,
14451
14691
  netLabelConnectorTraceIds,
14452
14692
  multiPinNetPinIds: getMultiPinNetPinIds(inputProblem)
14453
14693
  });
14454
- outputTraces = collapsedSharedEndpointStubs;
14455
- return {
14456
- traces: outputTraces,
14457
- netLabelPlacements: outputNetLabelPlacements,
14458
- alignedJunctionCount
14459
- };
14460
- };
14461
-
14462
- // lib/solvers/SameNetJunctionAlignmentSolver/collapseSameNetCycles.ts
14463
- import { getSegmentIntersection as getSegmentIntersection4 } from "@tscircuit/math-utils/line-intersections";
14464
- var TRACE_LENGTH_EPSILON = 1e-6;
14465
- var getTracePathStartingAtPin = (trace, pin) => {
14466
- const pathStart = trace.tracePath[0];
14467
- const pathEnd = trace.tracePath.at(-1);
14468
- if (!pathStart || !pathEnd) return null;
14469
- if (pointsEqual2(pathStart, pin)) return trace.tracePath;
14470
- if (pointsEqual2(pathEnd, pin)) return [...trace.tracePath].reverse();
14471
- return null;
14472
- };
14473
- var getPerpendicularPathCrossings = (targetPath, donorPath) => {
14474
- const crossings = [];
14475
- const sharedEndpoint = targetPath[0];
14476
- for (let targetSegmentIndex = 0; targetSegmentIndex < targetPath.length - 1; targetSegmentIndex++) {
14477
- const targetStart = targetPath[targetSegmentIndex];
14478
- const targetEnd = targetPath[targetSegmentIndex + 1];
14479
- const targetOrientation = getRailOrientation(targetStart, targetEnd);
14480
- if (!targetOrientation) continue;
14481
- for (let donorSegmentIndex = 0; donorSegmentIndex < donorPath.length - 1; donorSegmentIndex++) {
14482
- const donorStart = donorPath[donorSegmentIndex];
14483
- const donorEnd = donorPath[donorSegmentIndex + 1];
14484
- const donorOrientation = getRailOrientation(donorStart, donorEnd);
14485
- if (!donorOrientation || donorOrientation === targetOrientation) continue;
14486
- const intersectionPoint = getSegmentIntersection4(
14487
- targetStart,
14488
- targetEnd,
14489
- donorStart,
14490
- donorEnd
14491
- );
14492
- if (!intersectionPoint || pointsEqual2(intersectionPoint, sharedEndpoint)) {
14493
- continue;
14494
- }
14495
- crossings.push({
14496
- intersectionPoint,
14497
- targetSegmentIndex,
14498
- donorSegmentIndex
14499
- });
14500
- }
14501
- }
14502
- return crossings;
14503
- };
14504
- var buildCollapsedCyclePath = ({
14505
- targetTrace,
14506
- targetPath,
14507
- donorPath,
14508
- crossing
14509
- }) => {
14510
- const pathFromSharedPin = simplifyPath([
14511
- ...donorPath.slice(0, crossing.donorSegmentIndex + 1),
14512
- crossing.intersectionPoint,
14513
- ...targetPath.slice(crossing.targetSegmentIndex + 1)
14514
- ]);
14515
- if (pointsEqual2(targetTrace.tracePath[0], targetPath[0])) {
14516
- return pathFromSharedPin;
14517
- }
14518
- return pathFromSharedPin.reverse();
14519
- };
14520
- var getNetLabelPlacementsForCycleCollapse = ({
14521
- targetTrace,
14522
- tracePath,
14523
- netLabelPlacements
14524
- }) => {
14525
- const candidateNetLabelPlacements = moveAttachedLabelsToReroutedTrace({
14526
- trace: targetTrace,
14527
- originalTracePath: targetTrace.tracePath,
14528
- reroutedTracePath: tracePath,
14529
- netLabelPlacements
14530
- });
14531
- for (let labelIndex = 0; labelIndex < netLabelPlacements.length; labelIndex++) {
14532
- const label = netLabelPlacements[labelIndex];
14533
- if (label.globalConnNetId !== targetTrace.globalConnNetId) continue;
14534
- if (!tracePathContainsPoint(targetTrace.tracePath, label.anchorPoint)) {
14535
- continue;
14536
- }
14537
- const candidateLabel = candidateNetLabelPlacements[labelIndex];
14538
- if (!tracePathContainsPoint(tracePath, candidateLabel.anchorPoint)) {
14539
- return null;
14540
- }
14541
- }
14542
- const otherNetLabelPlacements = candidateNetLabelPlacements.filter(
14543
- (label) => label.globalConnNetId !== targetTrace.globalConnNetId
14544
- );
14545
- if (pathIntersectsAnyNetLabel({
14546
- path: tracePath,
14547
- netLabelPlacements: otherNetLabelPlacements
14548
- })) {
14549
- return null;
14550
- }
14551
- const sameNetLabelPlacements = candidateNetLabelPlacements.filter(
14552
- (label) => label.globalConnNetId === targetTrace.globalConnNetId
14553
- );
14554
- if (pathEntersAnyNetLabel({
14555
- path: tracePath,
14556
- netLabelPlacements: sameNetLabelPlacements
14557
- })) {
14558
- return null;
14559
- }
14560
- return candidateNetLabelPlacements;
14561
- };
14562
- var candidateIsBetter = (candidate, bestCandidate) => {
14563
- if (!bestCandidate) return true;
14564
- const visibleLengthDelta = candidate.netVisibleLength - bestCandidate.netVisibleLength;
14565
- if (Math.abs(visibleLengthDelta) > TRACE_LENGTH_EPSILON) {
14566
- return visibleLengthDelta < 0;
14567
- }
14568
- return candidate.netVisibleSegmentCount < bestCandidate.netVisibleSegmentCount;
14569
- };
14570
- var getBestCycleCollapseCandidate = ({
14571
- targetTrace,
14572
- traces,
14573
- netLabelPlacements
14574
- }) => {
14575
- const sameNetTraces = traces.filter(
14576
- (trace) => trace.globalConnNetId === targetTrace.globalConnNetId
14577
- );
14578
- const baselineNetVisibleLength = getVisibleTraceLength(sameNetTraces);
14579
- const baselineTargetVisibleLength = getVisibleTraceLength([targetTrace]);
14580
- let bestCandidate = null;
14581
- for (const donorTrace of sameNetTraces) {
14582
- if (donorTrace.mspPairId === targetTrace.mspPairId) continue;
14583
- const sharedPin = getSharedPin({
14584
- donorTrace,
14585
- branchTrace: targetTrace
14586
- });
14587
- if (!sharedPin) continue;
14588
- const targetPath = getTracePathStartingAtPin(targetTrace, sharedPin);
14589
- const donorPath = getTracePathStartingAtPin(donorTrace, sharedPin);
14590
- if (!targetPath || !donorPath) continue;
14591
- const crossings = getPerpendicularPathCrossings(targetPath, donorPath);
14592
- for (const crossing of crossings) {
14593
- const tracePath = buildCollapsedCyclePath({
14594
- targetTrace,
14595
- targetPath,
14596
- donorPath,
14597
- crossing
14598
- });
14599
- const candidateTrace = { ...targetTrace, tracePath };
14600
- const candidateTargetVisibleLength = getVisibleTraceLength([
14601
- candidateTrace
14602
- ]);
14603
- if (candidateTargetVisibleLength > baselineTargetVisibleLength + TRACE_LENGTH_EPSILON) {
14604
- continue;
14605
- }
14606
- const candidateNetLabelPlacements = getNetLabelPlacementsForCycleCollapse(
14607
- {
14608
- targetTrace,
14609
- tracePath,
14610
- netLabelPlacements
14611
- }
14612
- );
14613
- if (!candidateNetLabelPlacements) continue;
14614
- const candidateNetTraces = sameNetTraces.map((trace) => {
14615
- if (trace.mspPairId === targetTrace.mspPairId) return candidateTrace;
14616
- return trace;
14617
- });
14618
- const netVisibleLength = getVisibleTraceLength(candidateNetTraces);
14619
- if (netVisibleLength >= baselineNetVisibleLength - TRACE_LENGTH_EPSILON) {
14620
- continue;
14621
- }
14622
- const netVisibleSegmentCount = getVisibleTraceSegmentCount(candidateNetTraces);
14623
- const candidate = {
14624
- tracePath,
14625
- netLabelPlacements: candidateNetLabelPlacements,
14626
- netVisibleLength,
14627
- netVisibleSegmentCount
14628
- };
14629
- if (candidateIsBetter(candidate, bestCandidate)) {
14630
- bestCandidate = candidate;
14631
- }
14632
- }
14633
- }
14634
- return bestCandidate;
14635
- };
14636
- var collapseSameNetCycles = ({
14637
- traces,
14638
- netLabelPlacements
14639
- }) => {
14640
- const outputTraces = [...traces];
14641
- let outputNetLabelPlacements = [...netLabelPlacements];
14642
- let collapsedCycleCount = 0;
14643
- let traceIndex = 0;
14644
- while (traceIndex < outputTraces.length) {
14645
- const targetTrace = outputTraces[traceIndex];
14646
- const candidate = getBestCycleCollapseCandidate({
14647
- targetTrace,
14648
- traces: outputTraces,
14649
- netLabelPlacements: outputNetLabelPlacements
14650
- });
14651
- if (!candidate) {
14652
- traceIndex++;
14653
- continue;
14654
- }
14655
- outputTraces[traceIndex] = {
14656
- ...targetTrace,
14657
- tracePath: candidate.tracePath
14658
- };
14659
- outputNetLabelPlacements = candidate.netLabelPlacements;
14660
- collapsedCycleCount++;
14661
- traceIndex = 0;
14662
- }
14694
+ outputNetLabelPlacements = cycleCollapse.netLabelPlacements;
14663
14695
  return {
14664
14696
  traces: outputTraces,
14665
14697
  netLabelPlacements: outputNetLabelPlacements,
14666
- collapsedCycleCount
14698
+ alignedJunctionCount,
14699
+ collapsedCycleCount: cycleCollapse.collapsedCycleCount
14667
14700
  };
14668
14701
  };
14669
14702
 
@@ -14776,18 +14809,14 @@ var SameNetJunctionAlignmentSolver = class extends BaseSolver {
14776
14809
  }
14777
14810
  _step() {
14778
14811
  const alignment = alignSameNetJunctions(this.input);
14779
- const cycleCollapse = collapseSameNetCycles({
14780
- traces: alignment.traces,
14781
- netLabelPlacements: alignment.netLabelPlacements
14782
- });
14783
- this.outputTraces = cycleCollapse.traces;
14812
+ this.outputTraces = alignment.traces;
14784
14813
  this.outputNetLabelPlacements = placeGroundRailLabelsAtOuterEnd({
14785
14814
  inputProblem: this.input.inputProblem,
14786
- traces: cycleCollapse.traces,
14787
- netLabelPlacements: cycleCollapse.netLabelPlacements
14815
+ traces: alignment.traces,
14816
+ netLabelPlacements: alignment.netLabelPlacements
14788
14817
  });
14789
14818
  this.stats.alignedJunctionCount = alignment.alignedJunctionCount;
14790
- this.stats.collapsedCycleCount = cycleCollapse.collapsedCycleCount;
14819
+ this.stats.collapsedCycleCount = alignment.collapsedCycleCount;
14791
14820
  this.solved = true;
14792
14821
  }
14793
14822
  getOutput() {
@@ -7,7 +7,6 @@ import { visualizeInputProblem } from "lib/solvers/SchematicTracePipelineSolver/
7
7
  import type { InputProblem } from "lib/types/InputProblem"
8
8
  import { getColorFromString } from "lib/utils/getColorFromString"
9
9
  import { alignSameNetJunctions } from "./alignSameNetJunctions"
10
- import { collapseSameNetCycles } from "./collapseSameNetCycles"
11
10
  import { placeGroundRailLabelsAtOuterEnd } from "./placeGroundRailLabelsAtOuterEnd"
12
11
 
13
12
  interface SameNetJunctionAlignmentSolverInput {
@@ -32,18 +31,14 @@ export class SameNetJunctionAlignmentSolver extends BaseSolver {
32
31
 
33
32
  override _step() {
34
33
  const alignment = alignSameNetJunctions(this.input)
35
- const cycleCollapse = collapseSameNetCycles({
36
- traces: alignment.traces,
37
- netLabelPlacements: alignment.netLabelPlacements,
38
- })
39
- this.outputTraces = cycleCollapse.traces
34
+ this.outputTraces = alignment.traces
40
35
  this.outputNetLabelPlacements = placeGroundRailLabelsAtOuterEnd({
41
36
  inputProblem: this.input.inputProblem,
42
- traces: cycleCollapse.traces,
43
- netLabelPlacements: cycleCollapse.netLabelPlacements,
37
+ traces: alignment.traces,
38
+ netLabelPlacements: alignment.netLabelPlacements,
44
39
  })
45
40
  this.stats.alignedJunctionCount = alignment.alignedJunctionCount
46
- this.stats.collapsedCycleCount = cycleCollapse.collapsedCycleCount
41
+ this.stats.collapsedCycleCount = alignment.collapsedCycleCount
47
42
  this.solved = true
48
43
  }
49
44
 
@@ -34,6 +34,7 @@ import {
34
34
  isCoordinateOnPinFacingSide,
35
35
  } from "./findNearestSharedPinExitRail"
36
36
  import { collapseRedundantSharedEndpointStubs } from "./collapseRedundantSharedEndpointStubs"
37
+ import { collapseSameNetCycles } from "./collapseSameNetCycles"
37
38
  import { getSharedPin } from "./getSharedPin"
38
39
 
39
40
  interface AlignSameNetJunctionsInput {
@@ -1314,17 +1315,24 @@ export const alignSameNetJunctions = ({
1314
1315
  }
1315
1316
  }
1316
1317
 
1317
- const collapsedSharedEndpointStubs = collapseRedundantSharedEndpointStubs({
1318
+ // Shared-stub collapse can remove the common pin from one trace path, so
1319
+ // collapse cycles while both paths still expose their shared endpoint.
1320
+ const cycleCollapse = collapseSameNetCycles({
1318
1321
  traces: outputTraces,
1319
1322
  netLabelPlacements: outputNetLabelPlacements,
1323
+ })
1324
+ outputTraces = collapseRedundantSharedEndpointStubs({
1325
+ traces: cycleCollapse.traces,
1326
+ netLabelPlacements: cycleCollapse.netLabelPlacements,
1320
1327
  netLabelConnectorTraceIds,
1321
1328
  multiPinNetPinIds: getMultiPinNetPinIds(inputProblem),
1322
1329
  })
1323
- outputTraces = collapsedSharedEndpointStubs
1330
+ outputNetLabelPlacements = cycleCollapse.netLabelPlacements
1324
1331
 
1325
1332
  return {
1326
1333
  traces: outputTraces,
1327
1334
  netLabelPlacements: outputNetLabelPlacements,
1328
1335
  alignedJunctionCount,
1336
+ collapsedCycleCount: cycleCollapse.collapsedCycleCount,
1329
1337
  }
1330
1338
  }