@tscircuit/schematic-trace-solver 0.0.189 → 0.0.191

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.
Files changed (36) hide show
  1. package/dist/index.d.ts +2 -0
  2. package/dist/index.js +680 -428
  3. package/lib/solvers/InlineNetLabelSolver/getLocalTraceLabelShifts.ts +29 -0
  4. package/lib/solvers/LongDistancePairSolver/LongDistancePairSolver.ts +7 -0
  5. package/lib/solvers/LongDistancePairSolver/getParallelNetLabelPolicy.ts +98 -0
  6. package/lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver.ts +24 -2
  7. package/lib/solvers/MspConnectionPairSolver/getParallelRailPairs.ts +178 -0
  8. package/lib/solvers/NetLabelToTraceSolver/NetLabelToTraceSolver.ts +13 -0
  9. package/lib/solvers/SameNetJunctionAlignmentSolver/SameNetJunctionAlignmentSolver.ts +4 -9
  10. package/lib/solvers/SameNetJunctionAlignmentSolver/alignSameNetJunctions.ts +10 -2
  11. package/lib/solvers/SameNetJunctionAlignmentSolver/collapseSameNetCycles.ts +75 -38
  12. package/package.json +1 -1
  13. package/tests/bug-reports/bug-report-20260706T213649Z/__snapshots__/bug-report-20260706T213649Z.snap.svg +837 -799
  14. package/tests/bug-reports/bug-report-20260706T220324Z/__snapshots__/bug-report-20260706T220324Z.snap.svg +904 -866
  15. package/tests/bug-reports/bug-report-20260707T092615Z/__snapshots__/bug-report-20260707T092615Z.snap.svg +533 -524
  16. package/tests/bug-reports/bug-report-20260804T171919Z/__snapshots__/bug-report-20260804T171919Z.snap.svg +2 -2
  17. package/tests/bug-reports/bug-report-20260806T093501Z/__snapshots__/bug-report-20260806T093501Z.snap.svg +94 -94
  18. package/tests/bug-reports/bug-report-20260901T064117Z/bug-report-20260901T064117Z.test.ts +13 -2
  19. package/tests/bug-reports/bug-report-20260902T092425Z/__snapshots__/bug-report-20260902T092425Z.snap.svg +2 -2
  20. package/tests/bug-reports/bug-report-20260902T092425Z/bug-report-20260902T092425Z.test.ts +16 -2
  21. package/tests/bug-reports/bug-report-20260905T041712Z/__snapshots__/bug-report-20260905T041712Z.snap.svg +2 -2
  22. package/tests/bug-reports/bug-report-20260907T110144Z/__snapshots__/bug-report-20260907T110144Z.snap.svg +19 -58
  23. package/tests/bug-reports/bug-report-20260907T110144Z/bug-report-20260907T110144Z.test.ts +2 -2
  24. package/tests/bug-reports/bug-report-20260907T145640Z/__snapshots__/bug-report-20260907T145640Z.snap.svg +917 -933
  25. package/tests/repros/__snapshots__/repro-pmp11282-isolated-dcdc.snap.svg +18 -18
  26. package/tests/repros/__snapshots__/repro-repeated-power-rail-junctions.snap.svg +255 -0
  27. package/tests/repros/__snapshots__/repro-smartwatch-power-sheet.snap.svg +111 -75
  28. package/tests/repros/assets/repro-repeated-power-rail-junctions.input.json +927 -0
  29. package/tests/repros/repro-repeated-power-rail-junctions.test.ts +22 -0
  30. package/tests/repros/repro-trellis-core-decoupling-snake-traces.test.ts +66 -0
  31. package/tests/solvers/InlineNetLabelSolver/local-trace-label-shifts.test.ts +62 -0
  32. package/tests/solvers/LongDistancePairSolver/__snapshots__/decoupling-mixed-supplies.snap.svg +112 -0
  33. package/tests/solvers/LongDistancePairSolver/__snapshots__/horizontal-rails.snap.svg +79 -0
  34. package/tests/solvers/LongDistancePairSolver/__snapshots__/vertical-rails.snap.svg +86 -0
  35. package/tests/solvers/LongDistancePairSolver/parallel-decoupling-labels.test.ts +505 -0
  36. package/tests/solvers/SameNetJunctionAlignmentSolver/collapse-same-net-cycles.test.ts +30 -0
package/dist/index.js CHANGED
@@ -342,19 +342,19 @@ var doesPairCrossRestrictedCenterLines = (params) => {
342
342
  chipMap
343
343
  });
344
344
  if (restrictedCenterLines.size === 0) return false;
345
- const EPS18 = 1e-9;
345
+ const EPS19 = 1e-9;
346
346
  const crossesSegment = (a, b) => {
347
347
  for (const [, rcl] of restrictedCenterLines) {
348
348
  if (rcl.axes.has("x") && typeof rcl.x === "number") {
349
- if ((a.x - rcl.x) * (b.x - rcl.x) < -EPS18) return true;
349
+ if ((a.x - rcl.x) * (b.x - rcl.x) < -EPS19) return true;
350
350
  }
351
351
  if (rcl.axes.has("y") && typeof rcl.y === "number") {
352
- if ((a.y - rcl.y) * (b.y - rcl.y) < -EPS18) return true;
352
+ if ((a.y - rcl.y) * (b.y - rcl.y) < -EPS19) return true;
353
353
  }
354
354
  }
355
355
  return false;
356
356
  };
357
- if (Math.abs(p1.x - p2.x) < EPS18 || Math.abs(p1.y - p2.y) < EPS18) {
357
+ if (Math.abs(p1.x - p2.x) < EPS19 || Math.abs(p1.y - p2.y) < EPS19) {
358
358
  return crossesSegment({ x: p1.x, y: p1.y }, { x: p2.x, y: p2.y });
359
359
  }
360
360
  const elbowHV = { x: p2.x, y: p1.y };
@@ -608,6 +608,126 @@ function shouldSeparateGroundNetRows({
608
608
  return isGroupedHorizontalRail({ pin: pin1, netPins }) && isGroupedHorizontalRail({ pin: pin2, netPins });
609
609
  }
610
610
 
611
+ // lib/solvers/MspConnectionPairSolver/getParallelRailPairs.ts
612
+ var EPS = 1e-6;
613
+ var MAX_RAIL_SPACING_IN_TERMINAL_SPANS = 4;
614
+ var MAX_RAIL_SPACING_MULTIPLIER = 2;
615
+ var getRailPairKey = (first, second) => JSON.stringify([first, second].sort());
616
+ var getParallelRailPairs = (inputProblem, netConnMap, maxMspPairDistance) => {
617
+ const groundNetIds = /* @__PURE__ */ new Set();
618
+ const namedPinIds = /* @__PURE__ */ new Set();
619
+ const wiredPinIds = /* @__PURE__ */ new Set();
620
+ for (const connection of inputProblem.netConnections) {
621
+ for (const pinId of connection.pinIds) namedPinIds.add(pinId);
622
+ const netId = netConnMap.getNetConnectedToId(connection.netId);
623
+ if (netId && connection.isGround) {
624
+ groundNetIds.add(netId);
625
+ }
626
+ }
627
+ for (const connection of inputProblem.directConnections) {
628
+ const pinIds = connection.netLabelWidth === void 0 ? wiredPinIds : namedPinIds;
629
+ for (const pinId of connection.pinIds) pinIds.add(pinId);
630
+ }
631
+ const rows = [];
632
+ for (const chip of inputProblem.chips) {
633
+ if (chip.pins.length !== 2 || chip.symbolName && !chip.symbolName.startsWith("capacitor") || !chip.pins.every((pin) => namedPinIds.has(pin.pinId)) || chip.pins.some((pin) => wiredPinIds.has(pin.pinId)))
634
+ continue;
635
+ const nets = chip.pins.map(
636
+ (pin) => netConnMap.getNetConnectedToId(pin.pinId)
637
+ );
638
+ if (!nets[0] || !nets[1] || nets[0] === nets[1] || groundNetIds.has(nets[0]) === groundNetIds.has(nets[1]))
639
+ continue;
640
+ const directions = chip.pins.map(
641
+ (pin) => pin._facingDirection ?? getPinDirection(pin, chip)
642
+ );
643
+ if (directions[0][0] !== directions[1][0] || directions[0] === directions[1])
644
+ continue;
645
+ const axis = directions[0][0];
646
+ const along = axis === "x" ? "y" : "x";
647
+ if (Math.abs(chip.pins[0][along] - chip.pins[1][along]) > EPS) continue;
648
+ for (const [index, pin] of chip.pins.entries()) {
649
+ const direction = directions[index];
650
+ let row = rows.find(
651
+ (candidate) => candidate.sectionId === chip.sectionId && candidate.direction === direction && Math.abs(candidate.coordinate - pin[axis]) <= EPS
652
+ );
653
+ if (!row) {
654
+ row = {
655
+ sectionId: chip.sectionId,
656
+ direction,
657
+ coordinate: pin[axis],
658
+ axis,
659
+ pins: []
660
+ };
661
+ rows.push(row);
662
+ }
663
+ row.pins.push({
664
+ ...pin,
665
+ chipId: chip.chipId,
666
+ oppositeCoordinate: chip.pins[1 - index][axis]
667
+ });
668
+ }
669
+ }
670
+ const pairKeys = /* @__PURE__ */ new Set();
671
+ const extendedRailPinIds = /* @__PURE__ */ new Set();
672
+ const separatedRailPinIds = /* @__PURE__ */ new Set();
673
+ for (const row of rows) {
674
+ const along = row.axis === "x" ? "y" : "x";
675
+ row.pins.sort(
676
+ (a, b) => a[along] - b[along] || a.pinId.localeCompare(b.pinId)
677
+ );
678
+ for (let index = 1; index < row.pins.length; index++) {
679
+ const first = row.pins[index - 1];
680
+ const second = row.pins[index];
681
+ const spacing = second[along] - first[along];
682
+ const neighboringSpacings = [
683
+ first[along] - (row.pins[index - 2]?.[along] ?? first[along]),
684
+ (row.pins[index + 1]?.[along] ?? second[along]) - second[along]
685
+ ].filter((gap) => gap > EPS);
686
+ const maxRailSpacing = Math.max(
687
+ MAX_RAIL_SPACING_MULTIPLIER * maxMspPairDistance,
688
+ MAX_RAIL_SPACING_MULTIPLIER * (neighboringSpacings.length ? Math.min(...neighboringSpacings) : 0),
689
+ MAX_RAIL_SPACING_IN_TERMINAL_SPANS * Math.max(
690
+ Math.abs(first[row.axis] - first.oppositeCoordinate),
691
+ Math.abs(second[row.axis] - second.oppositeCoordinate)
692
+ )
693
+ );
694
+ if (spacing <= EPS || netConnMap.getNetConnectedToId(first.pinId) !== netConnMap.getNetConnectedToId(second.pinId))
695
+ continue;
696
+ if (spacing > maxRailSpacing + EPS) {
697
+ separatedRailPinIds.add(first.pinId);
698
+ separatedRailPinIds.add(second.pinId);
699
+ continue;
700
+ }
701
+ const minAcross = Math.min(
702
+ first[row.axis],
703
+ second[row.axis],
704
+ first.oppositeCoordinate,
705
+ second.oppositeCoordinate
706
+ );
707
+ const maxAcross = Math.max(
708
+ first[row.axis],
709
+ second[row.axis],
710
+ first.oppositeCoordinate,
711
+ second.oppositeCoordinate
712
+ );
713
+ if (inputProblem.chips.some((chip) => {
714
+ if (chip.chipId === first.chipId || chip.chipId === second.chipId)
715
+ return false;
716
+ const acrossSize = row.axis === "x" ? chip.width : chip.height;
717
+ const alongSize = along === "x" ? chip.width : chip.height;
718
+ return chip.center[row.axis] + acrossSize / 2 >= minAcross - EPS && chip.center[row.axis] - acrossSize / 2 <= maxAcross + EPS && chip.center[along] + alongSize / 2 > first[along] + EPS && chip.center[along] - alongSize / 2 < second[along] - EPS;
719
+ }))
720
+ continue;
721
+ pairKeys.add(getRailPairKey(first.pinId, second.pinId));
722
+ if (spacing > maxMspPairDistance) {
723
+ extendedRailPinIds.add(first.pinId);
724
+ extendedRailPinIds.add(second.pinId);
725
+ }
726
+ }
727
+ }
728
+ return { pairKeys, extendedRailPinIds, separatedRailPinIds };
729
+ };
730
+
611
731
  // lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver.ts
612
732
  var DEFAULT_MAX_MSP_PAIR_DISTANCE = 1;
613
733
  var getPinPairKey = (pinIds) => [...pinIds].sort().join("::");
@@ -624,6 +744,7 @@ var MspConnectionPairSolver = class extends BaseSolver {
624
744
  userNetIdByPinId;
625
745
  directConnectionPinPairKeys;
626
746
  canRouteGroundPair;
747
+ parallelRailPairKeys;
627
748
  constructor({ inputProblem }) {
628
749
  super();
629
750
  this.inputProblem = inputProblem;
@@ -632,6 +753,11 @@ var MspConnectionPairSolver = class extends BaseSolver {
632
753
  const { directConnMap, netConnMap } = getConnectivityMapsFromInputProblem(inputProblem);
633
754
  this.dcConnMap = directConnMap;
634
755
  this.globalConnMap = netConnMap;
756
+ this.parallelRailPairKeys = getParallelRailPairs(
757
+ inputProblem,
758
+ netConnMap,
759
+ this.maxMspPairDistance
760
+ ).pairKeys;
635
761
  this.pinMap = {};
636
762
  for (const chip of inputProblem.chips) {
637
763
  for (const pin of chip.pins) {
@@ -695,7 +821,7 @@ var MspConnectionPairSolver = class extends BaseSolver {
695
821
  chipMap: this.chipMap,
696
822
  pins: [p1, p2]
697
823
  });
698
- if (pairDistance > this.maxMspPairDistance && !labeledConnectionRouteReason) {
824
+ if (pairDistance > this.maxMspPairDistance && !this.parallelRailPairKeys.has(getRailPairKey(pin1, pin2)) && !labeledConnectionRouteReason) {
699
825
  return;
700
826
  }
701
827
  if (arePinsInDifferentSchematicSections(this.inputProblem, p1, p2)) {
@@ -733,8 +859,7 @@ var MspConnectionPairSolver = class extends BaseSolver {
733
859
  (pinId) => this.pinMap[pinId]
734
860
  );
735
861
  const msp = getOrthogonalMinimumSpanningTree(directlyConnectedPinObjects, {
736
- maxDistance: this.maxMspPairDistance,
737
- forbidEdge: (a, b) => !this.canRouteGroundPair(a.pinId, b.pinId) || shouldSeparateGroundNetRows({
862
+ forbidEdge: (a, b) => Math.abs(a.x - b.x) + Math.abs(a.y - b.y) > this.maxMspPairDistance && !this.parallelRailPairKeys.has(getRailPairKey(a.pinId, b.pinId)) || !this.canRouteGroundPair(a.pinId, b.pinId) || shouldSeparateGroundNetRows({
738
863
  netConnection,
739
864
  netPins: directlyConnectedPinObjects,
740
865
  pin1: a,
@@ -747,7 +872,11 @@ var MspConnectionPairSolver = class extends BaseSolver {
747
872
  p2: b
748
873
  })
749
874
  });
750
- for (const [pin1, pin2] of msp) {
875
+ for (let [pin1, pin2] of msp) {
876
+ if (this.parallelRailPairKeys.has(getRailPairKey(pin1, pin2)) && Math.abs(this.pinMap[pin1].x - this.pinMap[pin2].x) + Math.abs(this.pinMap[pin1].y - this.pinMap[pin2].y) > this.maxMspPairDistance && directlyConnectedPins.indexOf(pin1) > directlyConnectedPins.indexOf(pin2)) {
877
+ ;
878
+ [pin1, pin2] = [pin2, pin1];
879
+ }
751
880
  const p1Obj = this.pinMap[pin1];
752
881
  const p2Obj = this.pinMap[pin2];
753
882
  if (arePinsInDifferentSchematicSections(this.inputProblem, p1Obj, p2Obj)) {
@@ -971,10 +1100,10 @@ function calculateDirectShortPath(pin1, pin2) {
971
1100
  }
972
1101
 
973
1102
  // lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/collisions.ts
974
- var EPS = 1e-9;
975
- var isVertical = (a, b, eps = EPS) => Math.abs(a.x - b.x) < eps;
976
- var isHorizontal = (a, b, eps = EPS) => Math.abs(a.y - b.y) < eps;
977
- var segmentIntersectsRect = (a, b, r, eps = EPS) => {
1103
+ var EPS2 = 1e-9;
1104
+ var isVertical = (a, b, eps = EPS2) => Math.abs(a.x - b.x) < eps;
1105
+ var isHorizontal = (a, b, eps = EPS2) => Math.abs(a.y - b.y) < eps;
1106
+ var segmentIntersectsRect = (a, b, r, eps = EPS2) => {
978
1107
  const vert = isVertical(a, b, eps);
979
1108
  const horz = isHorizontal(a, b, eps);
980
1109
  if (!vert && !horz) return false;
@@ -994,7 +1123,7 @@ var segmentIntersectsRect = (a, b, r, eps = EPS) => {
994
1123
  return overlap > eps;
995
1124
  }
996
1125
  };
997
- var segmentOverlapsRectBoundary = (a, b, r, eps = EPS) => {
1126
+ var segmentOverlapsRectBoundary = (a, b, r, eps = EPS2) => {
998
1127
  if (isVertical(a, b, eps)) {
999
1128
  const onVerticalBoundary = Math.abs(a.x - r.minX) <= eps || Math.abs(a.x - r.maxX) <= eps;
1000
1129
  if (!onVerticalBoundary) return false;
@@ -1035,7 +1164,7 @@ var isPathCollidingWithObstacles = (path, obstacles) => {
1035
1164
  };
1036
1165
 
1037
1166
  // lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/mid.ts
1038
- var EPS2 = 1e-9;
1167
+ var EPS3 = 1e-9;
1039
1168
  var OBSTACLE_CLEARANCE = 0.2;
1040
1169
  var aabbFromPoints = (a, b) => ({
1041
1170
  minX: Math.min(a.x, b.x),
@@ -1043,7 +1172,7 @@ var aabbFromPoints = (a, b) => ({
1043
1172
  minY: Math.min(a.y, b.y),
1044
1173
  maxY: Math.max(a.y, b.y)
1045
1174
  });
1046
- var midBetweenPointAndRect = (axis, p, r, eps = EPS2) => {
1175
+ var midBetweenPointAndRect = (axis, p, r, eps = EPS3) => {
1047
1176
  if (axis === "x") {
1048
1177
  if (p.x < r.minX - eps) {
1049
1178
  return [(p.x + r.minX) / 2];
@@ -1063,7 +1192,7 @@ var midBetweenPointAndRect = (axis, p, r, eps = EPS2) => {
1063
1192
  }
1064
1193
  };
1065
1194
  var candidateMidsFromSet = (axis, colliding, collisionRects, aabb, opts = {}) => {
1066
- const { allowOpenSideCandidates = false, eps = EPS2 } = opts;
1195
+ const { allowOpenSideCandidates = false, eps = EPS3 } = opts;
1067
1196
  const setRects = [...collisionRects];
1068
1197
  if (axis === "x") {
1069
1198
  const leftBoundaries = [aabb.minX, ...setRects.map((r) => r.maxX)].filter(
@@ -1111,7 +1240,7 @@ var candidateMidsFromSet = (axis, colliding, collisionRects, aabb, opts = {}) =>
1111
1240
  };
1112
1241
 
1113
1242
  // lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/pathOps.ts
1114
- var EPS3 = 1e-9;
1243
+ var EPS4 = 1e-9;
1115
1244
  var hasOnlyNonzeroOrthogonalSegments = (path) => path.every((point, index) => {
1116
1245
  const nextPoint = path[index + 1];
1117
1246
  if (!nextPoint) return true;
@@ -1120,7 +1249,7 @@ var hasOnlyNonzeroOrthogonalSegments = (path) => path.every((point, index) => {
1120
1249
  }
1121
1250
  return Math.abs(point.x - nextPoint.x) + Math.abs(point.y - nextPoint.y) > 0;
1122
1251
  });
1123
- var shiftSegmentOrth = (pts, segIndex, axis, newCoord, eps = EPS3) => {
1252
+ var shiftSegmentOrth = (pts, segIndex, axis, newCoord, eps = EPS4) => {
1124
1253
  if (segIndex < 0 || segIndex >= pts.length - 1) {
1125
1254
  return null;
1126
1255
  }
@@ -1237,9 +1366,9 @@ var generateEndpointCollisionDetours = ({
1237
1366
  };
1238
1367
 
1239
1368
  // lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/generateInternalSegmentCollisionDetours.ts
1240
- var EPS4 = 1e-9;
1369
+ var EPS5 = 1e-9;
1241
1370
  var DEFAULT_CLEARANCE = 0.2;
1242
- var isStrictlyBetween = (value, a, b) => value > Math.min(a, b) + EPS4 && value < Math.max(a, b) - EPS4;
1371
+ var isStrictlyBetween = (value, a, b) => value > Math.min(a, b) + EPS5 && value < Math.max(a, b) - EPS5;
1243
1372
  var generateInternalSegmentCollisionDetours = ({
1244
1373
  path,
1245
1374
  collidingSegmentIndex,
@@ -1657,8 +1786,8 @@ var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
1657
1786
  if (!collision) {
1658
1787
  const first = path[0];
1659
1788
  const last = path[path.length - 1];
1660
- const EPS18 = 1e-9;
1661
- const samePoint = (p, q) => Math.abs(p.x - q.x) < EPS18 && Math.abs(p.y - q.y) < EPS18;
1789
+ const EPS19 = 1e-9;
1790
+ const samePoint = (p, q) => Math.abs(p.x - q.x) < EPS19 && Math.abs(p.y - q.y) < EPS19;
1662
1791
  if (samePoint(first, { x: PA.x, y: PA.y }) && samePoint(last, { x: PB.x, y: PB.y }) && (!this.hasAmbiguousPinDirections || pathMatchesPinDirections({
1663
1792
  path,
1664
1793
  pin1: { ...PA, _facingDirection: directions[0] },
@@ -2013,13 +2142,13 @@ var applyJogToTerminalSegment = ({
2013
2142
  segmentIndex: si,
2014
2143
  offset,
2015
2144
  JOG_SIZE,
2016
- EPS: EPS18 = 1e-6
2145
+ EPS: EPS19 = 1e-6
2017
2146
  }) => {
2018
2147
  if (si !== 0 && si !== pts.length - 2) return;
2019
2148
  const start = pts[si];
2020
2149
  const end = pts[si + 1];
2021
- const isVertical6 = Math.abs(start.x - end.x) < EPS18;
2022
- const isHorizontal5 = Math.abs(start.y - end.y) < EPS18;
2150
+ const isVertical6 = Math.abs(start.x - end.x) < EPS19;
2151
+ const isHorizontal5 = Math.abs(start.y - end.y) < EPS19;
2023
2152
  if (!isVertical6 && !isHorizontal5) return;
2024
2153
  const segDir = isVertical6 ? end.y > start.y ? 1 : -1 : end.x > start.x ? 1 : -1;
2025
2154
  if (pts.length === 2) {
@@ -2092,7 +2221,7 @@ var applyJogToTerminalSegment = ({
2092
2221
  };
2093
2222
 
2094
2223
  // lib/solvers/TraceOverlapShiftSolver/TraceOverlapIssueSolver/TraceOverlapIssueSolver.ts
2095
- var EPS5 = 1e-6;
2224
+ var EPS6 = 1e-6;
2096
2225
  var TraceOverlapIssueSolver = class extends BaseSolver {
2097
2226
  overlappingTraceSegments;
2098
2227
  interactionKind;
@@ -2189,7 +2318,7 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
2189
2318
  }
2190
2319
  applyOffsets(offsets) {
2191
2320
  const correctedTraceMap = { ...this.correctedTraceMap };
2192
- const eq = (a, b) => Math.abs(a - b) < EPS5;
2321
+ const eq = (a, b) => Math.abs(a - b) < EPS6;
2193
2322
  const samePoint = (p, q) => !!p && !!q && eq(p.x, q.x) && eq(p.y, q.y);
2194
2323
  this.overlappingTraceSegments.forEach((group, groupIndex) => {
2195
2324
  const offset = offsets[groupIndex];
@@ -2211,9 +2340,9 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
2211
2340
  const start = pts[si];
2212
2341
  const end = pts[si + 1];
2213
2342
  if (!start || !end) continue;
2214
- if (Math.abs(start.x - end.x) < EPS5) {
2343
+ if (Math.abs(start.x - end.x) < EPS6) {
2215
2344
  segmentAxes.set(si, "vertical");
2216
- } else if (Math.abs(start.y - end.y) < EPS5) {
2345
+ } else if (Math.abs(start.y - end.y) < EPS6) {
2217
2346
  segmentAxes.set(si, "horizontal");
2218
2347
  }
2219
2348
  }
@@ -2228,7 +2357,7 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
2228
2357
  segmentIndex: si,
2229
2358
  offset,
2230
2359
  JOG_SIZE,
2231
- EPS: EPS5
2360
+ EPS: EPS6
2232
2361
  });
2233
2362
  } else {
2234
2363
  const start = pts[si];
@@ -2315,7 +2444,7 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
2315
2444
  max = obstacle.maxY;
2316
2445
  }
2317
2446
  const next = orig + candidateOffset;
2318
- return orig <= min + EPS5 && next >= max - EPS5 || orig >= max - EPS5 && next <= min + EPS5;
2447
+ return orig <= min + EPS6 && next >= max - EPS6 || orig >= max - EPS6 && next <= min + EPS6;
2319
2448
  });
2320
2449
  const edges = blockingCollisions.map(({ start, isVertical: isVertical6, obstacle }) => {
2321
2450
  if (isVertical6) {
@@ -2352,8 +2481,8 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
2352
2481
  const start = trace.tracePath[traceSegmentIndex];
2353
2482
  const end = trace.tracePath[traceSegmentIndex + 1];
2354
2483
  if (!start || !end) continue;
2355
- const isVertical6 = Math.abs(start.x - end.x) < EPS5;
2356
- const isHorizontal5 = Math.abs(start.y - end.y) < EPS5;
2484
+ const isVertical6 = Math.abs(start.x - end.x) < EPS6;
2485
+ const isHorizontal5 = Math.abs(start.y - end.y) < EPS6;
2357
2486
  if (!isVertical6 && !isHorizontal5) continue;
2358
2487
  const shiftedSegment = isVertical6 ? [
2359
2488
  { ...start, x: start.x + offset },
@@ -2453,7 +2582,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
2453
2582
  return islands;
2454
2583
  }
2455
2584
  findNextOverlapIssue() {
2456
- const EPS18 = 2e-3;
2585
+ const EPS19 = 2e-3;
2457
2586
  const netIds = Object.keys(this.traceNetIslands);
2458
2587
  for (let i = 0; i < netIds.length; i++) {
2459
2588
  for (let j = i + 1; j < netIds.length; j++) {
@@ -2480,7 +2609,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
2480
2609
  segmentBIndex,
2481
2610
  rangeOverlap
2482
2611
  }) => {
2483
- if (rangeOverlap > EPS18) {
2612
+ if (rangeOverlap > EPS19) {
2484
2613
  const keyA = `${pathAIndex}:${segmentAIndex}`;
2485
2614
  const keyB = `${pathBIndex}:${segmentBIndex}`;
2486
2615
  if (!seenA.has(keyA)) {
@@ -2499,7 +2628,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
2499
2628
  }
2500
2629
  return;
2501
2630
  }
2502
- if (rangeOverlap < -EPS18) return;
2631
+ if (rangeOverlap < -EPS19) return;
2503
2632
  const pathA = pathsA[pathAIndex].tracePath;
2504
2633
  const pathB = pathsB[pathBIndex].tracePath;
2505
2634
  const segmentAStart = pathA[segmentAIndex];
@@ -2522,8 +2651,8 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
2522
2651
  for (let sa = 0; sa < ptsA.length - 1; sa++) {
2523
2652
  const a1 = ptsA[sa];
2524
2653
  const a2 = ptsA[sa + 1];
2525
- const aVert = Math.abs(a1.x - a2.x) < EPS18;
2526
- const aHorz = Math.abs(a1.y - a2.y) < EPS18;
2654
+ const aVert = Math.abs(a1.x - a2.x) < EPS19;
2655
+ const aHorz = Math.abs(a1.y - a2.y) < EPS19;
2527
2656
  if (!aVert && !aHorz) continue;
2528
2657
  for (let pb = 0; pb < pathsB.length; pb++) {
2529
2658
  const pathB = pathsB[pb];
@@ -2534,11 +2663,11 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
2534
2663
  for (let sb = 0; sb < ptsB.length - 1; sb++) {
2535
2664
  const b1 = ptsB[sb];
2536
2665
  const b2 = ptsB[sb + 1];
2537
- const bVert = Math.abs(b1.x - b2.x) < EPS18;
2538
- const bHorz = Math.abs(b1.y - b2.y) < EPS18;
2666
+ const bVert = Math.abs(b1.x - b2.x) < EPS19;
2667
+ const bHorz = Math.abs(b1.y - b2.y) < EPS19;
2539
2668
  if (!bVert && !bHorz) continue;
2540
2669
  if (aVert && bVert) {
2541
- if (Math.abs(a1.x - b1.x) < EPS18) {
2670
+ if (Math.abs(a1.x - b1.x) < EPS19) {
2542
2671
  recordCollinearInteraction({
2543
2672
  pathAIndex: pa,
2544
2673
  segmentAIndex: sa,
@@ -2548,7 +2677,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
2548
2677
  });
2549
2678
  }
2550
2679
  } else if (aHorz && bHorz) {
2551
- if (Math.abs(a1.y - b1.y) < EPS18) {
2680
+ if (Math.abs(a1.y - b1.y) < EPS19) {
2552
2681
  recordCollinearInteraction({
2553
2682
  pathAIndex: pa,
2554
2683
  segmentAIndex: sa,
@@ -2604,14 +2733,14 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
2604
2733
  return null;
2605
2734
  }
2606
2735
  findNextDiagonalSegment() {
2607
- const EPS18 = 2e-3;
2736
+ const EPS19 = 2e-3;
2608
2737
  for (const mspPairId in this.correctedTraceMap) {
2609
2738
  const tracePath = this.correctedTraceMap[mspPairId].tracePath;
2610
2739
  for (let i = 0; i < tracePath.length - 1; i++) {
2611
2740
  const p1 = tracePath[i];
2612
2741
  const p2 = tracePath[i + 1];
2613
- const isHorizontal5 = Math.abs(p1.y - p2.y) < EPS18;
2614
- const isVertical6 = Math.abs(p1.x - p2.x) < EPS18;
2742
+ const isHorizontal5 = Math.abs(p1.y - p2.y) < EPS19;
2743
+ const isVertical6 = Math.abs(p1.x - p2.x) < EPS19;
2615
2744
  if (!isHorizontal5 && !isVertical6) {
2616
2745
  return { mspPairId, tracePath, i, p1, p2 };
2617
2746
  }
@@ -2625,13 +2754,13 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
2625
2754
  return false;
2626
2755
  }
2627
2756
  const { mspPairId, tracePath, i, p1, p2 } = diagonalInfo;
2628
- const EPS18 = 2e-3;
2757
+ const EPS19 = 2e-3;
2629
2758
  const p0 = i > 0 ? tracePath[i - 1] : null;
2630
2759
  const p3 = i + 2 < tracePath.length ? tracePath[i + 2] : null;
2631
- const prevIsVertical = p0 ? Math.abs(p0.x - p1.x) < EPS18 : false;
2632
- const prevIsHorizontal = p0 ? Math.abs(p0.y - p1.y) < EPS18 : false;
2633
- const nextIsVertical = p3 ? Math.abs(p2.x - p3.x) < EPS18 : false;
2634
- const nextIsHorizontal = p3 ? Math.abs(p2.y - p3.y) < EPS18 : false;
2760
+ const prevIsVertical = p0 ? Math.abs(p0.x - p1.x) < EPS19 : false;
2761
+ const prevIsHorizontal = p0 ? Math.abs(p0.y - p1.y) < EPS19 : false;
2762
+ const nextIsVertical = p3 ? Math.abs(p2.x - p3.x) < EPS19 : false;
2763
+ const nextIsHorizontal = p3 ? Math.abs(p2.y - p3.y) < EPS19 : false;
2635
2764
  const elbow1 = { x: p1.x, y: p2.y };
2636
2765
  const elbow2 = { x: p2.x, y: p1.y };
2637
2766
  let score1 = 0;
@@ -2793,24 +2922,24 @@ var ChipObstacleSpatialIndex = class {
2793
2922
  };
2794
2923
 
2795
2924
  // lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/collisions.ts
2796
- function segmentIntersectsRect2(p1, p2, rect, EPS18 = 1e-9) {
2797
- const isVert = Math.abs(p1.x - p2.x) < EPS18;
2798
- const isHorz = Math.abs(p1.y - p2.y) < EPS18;
2925
+ function segmentIntersectsRect2(p1, p2, rect, EPS19 = 1e-9) {
2926
+ const isVert = Math.abs(p1.x - p2.x) < EPS19;
2927
+ const isHorz = Math.abs(p1.y - p2.y) < EPS19;
2799
2928
  if (!isVert && !isHorz) return false;
2800
2929
  if (isVert) {
2801
2930
  const x = p1.x;
2802
- if (x < rect.minX - EPS18 || x > rect.maxX + EPS18) return false;
2931
+ if (x < rect.minX - EPS19 || x > rect.maxX + EPS19) return false;
2803
2932
  const segMinY = Math.min(p1.y, p2.y);
2804
2933
  const segMaxY = Math.max(p1.y, p2.y);
2805
2934
  const overlap = Math.min(segMaxY, rect.maxY) - Math.max(segMinY, rect.minY);
2806
- return overlap > EPS18;
2935
+ return overlap > EPS19;
2807
2936
  } else {
2808
2937
  const y = p1.y;
2809
- if (y < rect.minY - EPS18 || y > rect.maxY + EPS18) return false;
2938
+ if (y < rect.minY - EPS19 || y > rect.maxY + EPS19) return false;
2810
2939
  const segMinX = Math.min(p1.x, p2.x);
2811
2940
  const segMaxX = Math.max(p1.x, p2.x);
2812
2941
  const overlap = Math.min(segMaxX, rect.maxX) - Math.max(segMinX, rect.minX);
2813
- return overlap > EPS18;
2942
+ return overlap > EPS19;
2814
2943
  }
2815
2944
  }
2816
2945
  function rectIntersectsAnyTrace(bounds, inputTraceMap, hostPathId, hostSegIndex) {
@@ -3209,14 +3338,14 @@ var SingleNetLabelPlacementSolver = class extends BaseSolver {
3209
3338
  };
3210
3339
  let bestCandidate = null;
3211
3340
  let bestScore = -Infinity;
3212
- const EPS18 = 1e-6;
3341
+ const EPS19 = 1e-6;
3213
3342
  for (const curr of tracesToScan) {
3214
3343
  const pts = curr.tracePath.slice();
3215
3344
  for (let si = 0; si < pts.length - 1; si++) {
3216
3345
  const a = pts[si];
3217
3346
  const b = pts[si + 1];
3218
- const isH = Math.abs(a.y - b.y) < EPS18;
3219
- const isV = Math.abs(a.x - b.x) < EPS18;
3347
+ const isH = Math.abs(a.y - b.y) < EPS19;
3348
+ const isV = Math.abs(a.x - b.x) < EPS19;
3220
3349
  if (!isH && !isV) continue;
3221
3350
  const segmentAllowed = isH ? ["y+", "y-"] : ["x+", "x-"];
3222
3351
  const candidateOrients = orientations.filter(
@@ -4992,19 +5121,81 @@ function doesTraceOverlapWithExistingTraces(newTracePath, existingTraces) {
4992
5121
  return false;
4993
5122
  }
4994
5123
 
5124
+ // lib/solvers/LongDistancePairSolver/getParallelNetLabelPolicy.ts
5125
+ import { ConnectivityMap as ConnectivityMap3 } from "connectivity-map";
5126
+ var getParallelNetLabelPolicy = (inputProblem, netConnMap, connectedPinIds) => {
5127
+ const maxDistance = inputProblem.maxMspPairDistance ?? DEFAULT_MAX_MSP_PAIR_DISTANCE;
5128
+ const { extendedRailPinIds, separatedRailPinIds } = getParallelRailPairs(
5129
+ inputProblem,
5130
+ netConnMap,
5131
+ maxDistance
5132
+ );
5133
+ const groundNetIds = /* @__PURE__ */ new Set();
5134
+ const namedPinIds = /* @__PURE__ */ new Set();
5135
+ for (const connection of inputProblem.netConnections) {
5136
+ const netId = netConnMap.getNetConnectedToId(connection.netId);
5137
+ if (!netId) continue;
5138
+ for (const pinId of connection.pinIds) namedPinIds.add(pinId);
5139
+ if (connection.isGround) {
5140
+ groundNetIds.add(netId);
5141
+ }
5142
+ }
5143
+ const banks = /* @__PURE__ */ new Map();
5144
+ for (const chip of inputProblem.chips) {
5145
+ if (chip.pins.length !== 2 || !chip.pins.every((pin) => namedPinIds.has(pin.pinId)) || chip.pins.some(
5146
+ (pin) => connectedPinIds.has(pin.pinId) && !extendedRailPinIds.has(pin.pinId)
5147
+ ))
5148
+ continue;
5149
+ const [first, second] = chip.pins.map(
5150
+ (pin) => netConnMap.getNetConnectedToId(pin.pinId)
5151
+ );
5152
+ if (!first || !second || first === second || groundNetIds.has(first) === groundNetIds.has(second)) {
5153
+ continue;
5154
+ }
5155
+ const key = JSON.stringify([
5156
+ chip.sectionId ?? null,
5157
+ ...[first, second].sort()
5158
+ ]);
5159
+ const bank = banks.get(key) ?? [];
5160
+ bank.push(chip.pins.map((pin) => pin.pinId));
5161
+ banks.set(key, bank);
5162
+ }
5163
+ const bankPinIds = new Set(
5164
+ [...banks.values()].filter((bank) => bank.length > 1).flat(2)
5165
+ );
5166
+ for (const pinId of [...extendedRailPinIds, ...separatedRailPinIds]) {
5167
+ bankPinIds.add(pinId);
5168
+ }
5169
+ const physicalConnMap = new ConnectivityMap3({});
5170
+ for (const connection of inputProblem.directConnections) {
5171
+ if (connection.netLabelWidth === void 0) {
5172
+ physicalConnMap.addConnections([connection.pinIds]);
5173
+ }
5174
+ }
5175
+ return (first, second) => {
5176
+ if (!bankPinIds.has(first.pinId) && !bankPinIds.has(second.pinId))
5177
+ return true;
5178
+ if (Math.abs(first.x - second.x) + Math.abs(first.y - second.y) <= maxDistance) {
5179
+ return true;
5180
+ }
5181
+ const physicalNetId = physicalConnMap.getNetConnectedToId(first.pinId);
5182
+ return physicalNetId !== void 0 && physicalNetId === physicalConnMap.getNetConnectedToId(second.pinId);
5183
+ };
5184
+ };
5185
+
4995
5186
  // lib/solvers/LongDistancePairSolver/LongDistancePairSolver.ts
4996
5187
  var NEAREST_NEIGHBOR_COUNT = 3;
4997
5188
  var distance3 = (p1, p2) => {
4998
5189
  return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));
4999
5190
  };
5000
5191
  var manhattanDistance = (p1, p2) => Math.abs(p1.x - p2.x) + Math.abs(p1.y - p2.y);
5001
- var EPS6 = 1e-9;
5002
- var between = (value, first, second) => value >= Math.min(first, second) - EPS6 && value <= Math.max(first, second) + EPS6;
5192
+ var EPS7 = 1e-9;
5193
+ var between = (value, first, second) => value >= Math.min(first, second) - EPS7 && value <= Math.max(first, second) + EPS7;
5003
5194
  var getAxisAlignedIntersection = (firstStart, firstEnd, secondStart, secondEnd) => {
5004
- const firstIsHorizontal = Math.abs(firstStart.y - firstEnd.y) <= EPS6;
5005
- const firstIsVertical = Math.abs(firstStart.x - firstEnd.x) <= EPS6;
5006
- const secondIsHorizontal = Math.abs(secondStart.y - secondEnd.y) <= EPS6;
5007
- const secondIsVertical = Math.abs(secondStart.x - secondEnd.x) <= EPS6;
5195
+ const firstIsHorizontal = Math.abs(firstStart.y - firstEnd.y) <= EPS7;
5196
+ const firstIsVertical = Math.abs(firstStart.x - firstEnd.x) <= EPS7;
5197
+ const secondIsHorizontal = Math.abs(secondStart.y - secondEnd.y) <= EPS7;
5198
+ const secondIsVertical = Math.abs(secondStart.x - secondEnd.x) <= EPS7;
5008
5199
  if (firstIsHorizontal && secondIsVertical) {
5009
5200
  const point = { x: secondStart.x, y: firstStart.y };
5010
5201
  return between(point.x, firstStart.x, firstEnd.x) && between(point.y, secondStart.y, secondEnd.y) ? point : null;
@@ -5013,7 +5204,7 @@ var getAxisAlignedIntersection = (firstStart, firstEnd, secondStart, secondEnd)
5013
5204
  const point = { x: firstStart.x, y: secondStart.y };
5014
5205
  return between(point.y, firstStart.y, firstEnd.y) && between(point.x, secondStart.x, secondEnd.x) ? point : null;
5015
5206
  }
5016
- if (firstIsHorizontal && secondIsHorizontal && Math.abs(firstStart.y - secondStart.y) <= EPS6) {
5207
+ if (firstIsHorizontal && secondIsHorizontal && Math.abs(firstStart.y - secondStart.y) <= EPS7) {
5017
5208
  const overlapMin = Math.max(
5018
5209
  Math.min(firstStart.x, firstEnd.x),
5019
5210
  Math.min(secondStart.x, secondEnd.x)
@@ -5022,13 +5213,13 @@ var getAxisAlignedIntersection = (firstStart, firstEnd, secondStart, secondEnd)
5022
5213
  Math.max(firstStart.x, firstEnd.x),
5023
5214
  Math.max(secondStart.x, secondEnd.x)
5024
5215
  );
5025
- if (overlapMin > overlapMax + EPS6) return null;
5216
+ if (overlapMin > overlapMax + EPS7) return null;
5026
5217
  return {
5027
5218
  x: Math.max(overlapMin, Math.min(firstStart.x, overlapMax)),
5028
5219
  y: firstStart.y
5029
5220
  };
5030
5221
  }
5031
- if (firstIsVertical && secondIsVertical && Math.abs(firstStart.x - secondStart.x) <= EPS6) {
5222
+ if (firstIsVertical && secondIsVertical && Math.abs(firstStart.x - secondStart.x) <= EPS7) {
5032
5223
  const overlapMin = Math.max(
5033
5224
  Math.min(firstStart.y, firstEnd.y),
5034
5225
  Math.min(secondStart.y, secondEnd.y)
@@ -5037,7 +5228,7 @@ var getAxisAlignedIntersection = (firstStart, firstEnd, secondStart, secondEnd)
5037
5228
  Math.max(firstStart.y, firstEnd.y),
5038
5229
  Math.max(secondStart.y, secondEnd.y)
5039
5230
  );
5040
- if (overlapMin > overlapMax + EPS6) return null;
5231
+ if (overlapMin > overlapMax + EPS7) return null;
5041
5232
  return {
5042
5233
  x: firstStart.x,
5043
5234
  y: Math.max(overlapMin, Math.min(firstStart.y, overlapMax))
@@ -5066,7 +5257,7 @@ var clipPathAtFirstIntersection = (tracePath, existingTraces) => {
5066
5257
  const intersection = intersections2[0];
5067
5258
  if (!intersection) continue;
5068
5259
  const clippedPath = tracePath.slice(0, pathIndex + 1);
5069
- if (distance3(clippedPath.at(-1), intersection) > EPS6) {
5260
+ if (distance3(clippedPath.at(-1), intersection) > EPS7) {
5070
5261
  clippedPath.push(intersection);
5071
5262
  }
5072
5263
  return clippedPath;
@@ -5089,6 +5280,11 @@ var LongDistancePairSolver = class extends BaseSolver {
5089
5280
  }
5090
5281
  const { netConnMap } = getConnectivityMapsFromInputProblem(inputProblem);
5091
5282
  this.netConnMap = netConnMap;
5283
+ const canRecoverParallelPair = getParallelNetLabelPolicy(
5284
+ inputProblem,
5285
+ netConnMap,
5286
+ primaryConnectedPinIds
5287
+ );
5092
5288
  const pinMap = /* @__PURE__ */ new Map();
5093
5289
  for (const chip of inputProblem.chips) {
5094
5290
  this.chipMap[chip.chipId] = chip;
@@ -5121,6 +5317,7 @@ var LongDistancePairSolver = class extends BaseSolver {
5121
5317
  const targetPin = pinMap.get(otherPinId);
5122
5318
  if (!targetPin) return [];
5123
5319
  if (!canRouteGroundPair(sourcePin.pinId, targetPin.pinId)) return [];
5320
+ if (!canRecoverParallelPair(sourcePin, targetPin)) return [];
5124
5321
  const isNamedTwoPinConnection = inputProblem.netConnections.some(
5125
5322
  (connection) => connection.pinIds.length === 2 && connection.pinIds.includes(sourcePin.pinId) && connection.pinIds.includes(targetPin.pinId)
5126
5323
  );
@@ -6065,9 +6262,9 @@ var getRailAlignmentFallbackCoordinates = ({
6065
6262
  import { distance as distance4 } from "@tscircuit/math-utils";
6066
6263
 
6067
6264
  // lib/solvers/RailNetLabelCornerPlacementSolver/geometry.ts
6068
- var EPS7 = 1e-6;
6265
+ var EPS8 = 1e-6;
6069
6266
  var isTraceLine = (trace) => getUniquePinCount(trace.pinIds) >= 2;
6070
- var rectsOverlap = (a, b) => Math.min(a.maxX, b.maxX) - Math.max(a.minX, b.minX) > EPS7 && Math.min(a.maxY, b.maxY) - Math.max(a.minY, b.minY) > EPS7;
6267
+ var rectsOverlap = (a, b) => Math.min(a.maxX, b.maxX) - Math.max(a.minX, b.minX) > EPS8 && Math.min(a.maxY, b.maxY) - Math.max(a.minY, b.minY) > EPS8;
6071
6268
  var getTraceCorners = (path) => {
6072
6269
  const corners = [];
6073
6270
  for (let i = 1; i < path.length - 1; i++) {
@@ -6091,11 +6288,11 @@ var getUniquePinCount = (pinIds) => new Set(pinIds).size;
6091
6288
  var isTraceCorner = (a, b, c) => getSegmentOrientation(a, b) !== getSegmentOrientation(b, c);
6092
6289
  var isPointOnSegment = (point, start, end) => {
6093
6290
  if (getSegmentOrientation(start, end) === "horizontal") {
6094
- return Math.abs(point.y - start.y) <= EPS7 && point.x >= Math.min(start.x, end.x) - EPS7 && point.x <= Math.max(start.x, end.x) + EPS7;
6291
+ return Math.abs(point.y - start.y) <= EPS8 && point.x >= Math.min(start.x, end.x) - EPS8 && point.x <= Math.max(start.x, end.x) + EPS8;
6095
6292
  }
6096
- return Math.abs(point.x - start.x) <= EPS7 && point.y >= Math.min(start.y, end.y) - EPS7 && point.y <= Math.max(start.y, end.y) + EPS7;
6293
+ return Math.abs(point.x - start.x) <= EPS8 && point.y >= Math.min(start.y, end.y) - EPS8 && point.y <= Math.max(start.y, end.y) + EPS8;
6097
6294
  };
6098
- var getSegmentOrientation = (a, b) => Math.abs(a.y - b.y) <= EPS7 ? "horizontal" : "vertical";
6295
+ var getSegmentOrientation = (a, b) => Math.abs(a.y - b.y) <= EPS8 ? "horizontal" : "vertical";
6099
6296
 
6100
6297
  // lib/solvers/TraceCleanupSolver/sameNetRailAlignment/getFixedLabelCoordinate.ts
6101
6298
  var getTransitivelyConnectedTraceIds = (group, traces) => {
@@ -6220,17 +6417,17 @@ var preservesLabelAnchors = (labels, before, after) => labels.every((label) => {
6220
6417
  });
6221
6418
 
6222
6419
  // lib/solvers/Example28Solver/types.ts
6223
- var EPS8 = 1e-9;
6420
+ var EPS9 = 1e-9;
6224
6421
 
6225
6422
  // lib/solvers/Example28Solver/segmentRunsAlongRectBoundary.ts
6226
6423
  var segmentRunsAlongRectBoundary = (start, end, rect) => {
6227
- const isVertical6 = Math.abs(start.x - end.x) < EPS8;
6228
- const isHorizontal5 = Math.abs(start.y - end.y) < EPS8;
6424
+ const isVertical6 = Math.abs(start.x - end.x) < EPS9;
6425
+ const isHorizontal5 = Math.abs(start.y - end.y) < EPS9;
6229
6426
  if (isVertical6) {
6230
- return Math.abs(start.x - rect.minX) < EPS8 || Math.abs(start.x - rect.maxX) < EPS8;
6427
+ return Math.abs(start.x - rect.minX) < EPS9 || Math.abs(start.x - rect.maxX) < EPS9;
6231
6428
  }
6232
6429
  if (isHorizontal5) {
6233
- return Math.abs(start.y - rect.minY) < EPS8 || Math.abs(start.y - rect.maxY) < EPS8;
6430
+ return Math.abs(start.y - rect.minY) < EPS9 || Math.abs(start.y - rect.maxY) < EPS9;
6234
6431
  }
6235
6432
  return false;
6236
6433
  };
@@ -6245,10 +6442,10 @@ var getPathLength4 = (path) => {
6245
6442
  return length;
6246
6443
  };
6247
6444
  var getDistance2 = (a, b) => Math.abs(a.x - b.x) + Math.abs(a.y - b.y);
6248
- var isAxisAlignedSegment = (start, end) => Math.abs(start.x - end.x) < EPS8 || Math.abs(start.y - end.y) < EPS8;
6249
- var getSegmentOrientation2 = (start, end) => Math.abs(start.y - end.y) < EPS8 ? "horizontal" : "vertical";
6445
+ var isAxisAlignedSegment = (start, end) => Math.abs(start.x - end.x) < EPS9 || Math.abs(start.y - end.y) < EPS9;
6446
+ var getSegmentOrientation2 = (start, end) => Math.abs(start.y - end.y) < EPS9 ? "horizontal" : "vertical";
6250
6447
  var projectPointToSegment = (point, start, end) => {
6251
- if (Math.abs(start.x - end.x) < EPS8) {
6448
+ if (Math.abs(start.x - end.x) < EPS9) {
6252
6449
  return {
6253
6450
  x: start.x,
6254
6451
  y: Math.min(
@@ -6257,7 +6454,7 @@ var projectPointToSegment = (point, start, end) => {
6257
6454
  )
6258
6455
  };
6259
6456
  }
6260
- if (Math.abs(start.y - end.y) < EPS8) {
6457
+ if (Math.abs(start.y - end.y) < EPS9) {
6261
6458
  return {
6262
6459
  x: Math.min(
6263
6460
  Math.max(point.x, Math.min(start.x, end.x)),
@@ -6300,9 +6497,9 @@ var findSegmentContainingPoint = (path, point) => {
6300
6497
  };
6301
6498
  var isPointWithinSegmentPrimaryRange = (point, segment) => {
6302
6499
  if (segment.orientation === "horizontal") {
6303
- return point.x >= Math.min(segment.start.x, segment.end.x) - EPS8 && point.x <= Math.max(segment.start.x, segment.end.x) + EPS8;
6500
+ return point.x >= Math.min(segment.start.x, segment.end.x) - EPS9 && point.x <= Math.max(segment.start.x, segment.end.x) + EPS9;
6304
6501
  }
6305
- return point.y >= Math.min(segment.start.y, segment.end.y) - EPS8 && point.y <= Math.max(segment.start.y, segment.end.y) + EPS8;
6502
+ return point.y >= Math.min(segment.start.y, segment.end.y) - EPS9 && point.y <= Math.max(segment.start.y, segment.end.y) + EPS9;
6306
6503
  };
6307
6504
  var findPreferredReroutedSegment = (path, originalSegmentIndex, originalSegmentCount, orientation, anchorPoint) => {
6308
6505
  const matchingSegments = getSegments(path).filter(
@@ -6330,18 +6527,18 @@ var projectPointToPath = (point, path) => {
6330
6527
  return bestPoint;
6331
6528
  };
6332
6529
  var segmentsIntersect = (a1, a2, b1, b2) => {
6333
- const aVertical = Math.abs(a1.x - a2.x) < EPS8;
6334
- const bVertical = Math.abs(b1.x - b2.x) < EPS8;
6335
- const between2 = (value, p1, p2) => value >= Math.min(p1, p2) - EPS8 && value <= Math.max(p1, p2) + EPS8;
6530
+ const aVertical = Math.abs(a1.x - a2.x) < EPS9;
6531
+ const bVertical = Math.abs(b1.x - b2.x) < EPS9;
6532
+ const between2 = (value, p1, p2) => value >= Math.min(p1, p2) - EPS9 && value <= Math.max(p1, p2) + EPS9;
6336
6533
  if (aVertical && bVertical) {
6337
- if (Math.abs(a1.x - b1.x) > EPS8) return false;
6534
+ if (Math.abs(a1.x - b1.x) > EPS9) return false;
6338
6535
  const overlap = Math.min(Math.max(a1.y, a2.y), Math.max(b1.y, b2.y)) - Math.max(Math.min(a1.y, a2.y), Math.min(b1.y, b2.y));
6339
- return overlap > EPS8;
6536
+ return overlap > EPS9;
6340
6537
  }
6341
6538
  if (!aVertical && !bVertical) {
6342
- if (Math.abs(a1.y - b1.y) > EPS8) return false;
6539
+ if (Math.abs(a1.y - b1.y) > EPS9) return false;
6343
6540
  const overlap = Math.min(Math.max(a1.x, a2.x), Math.max(b1.x, b2.x)) - Math.max(Math.min(a1.x, a2.x), Math.min(b1.x, b2.x));
6344
- return overlap > EPS8;
6541
+ return overlap > EPS9;
6345
6542
  }
6346
6543
  const verticalA = aVertical ? a1 : b1;
6347
6544
  const verticalB = aVertical ? a2 : b2;
@@ -6798,7 +6995,7 @@ var getTraceObstacles = (allTraces, excludeTraceId) => {
6798
6995
 
6799
6996
  // lib/solvers/TraceCleanupSolver/sub-solver/findIntersectionsWithObstacles.ts
6800
6997
  import { getSegmentIntersection } from "@tscircuit/math-utils/line-intersections";
6801
- var EPS9 = 1e-6;
6998
+ var EPS10 = 1e-6;
6802
6999
  var findIntersectionsWithObstacles = (p1, p2, obstacles) => {
6803
7000
  const intersections2 = [];
6804
7001
  for (const obstacle of obstacles) {
@@ -6817,7 +7014,7 @@ var findIntersectionsWithObstacles = (p1, p2, obstacles) => {
6817
7014
  }
6818
7015
  return intersections2;
6819
7016
  };
6820
- var isSamePoint = (first, second) => Math.abs(first.x - second.x) < EPS9 && Math.abs(first.y - second.y) < EPS9;
7017
+ var isSamePoint = (first, second) => Math.abs(first.x - second.x) < EPS10 && Math.abs(first.y - second.y) < EPS10;
6821
7018
  var findPerpendicularPathCrossings = (path, otherPath, options = {}) => {
6822
7019
  const crossings = [];
6823
7020
  const firstPathSegmentIndex = options.includeTerminalSegments ? 0 : 1;
@@ -6827,11 +7024,11 @@ var findPerpendicularPathCrossings = (path, otherPath, options = {}) => {
6827
7024
  for (let pathSegmentIndex = firstPathSegmentIndex; pathSegmentIndex < lastPathSegmentIndex; pathSegmentIndex++) {
6828
7025
  const start = path[pathSegmentIndex];
6829
7026
  const end = path[pathSegmentIndex + 1];
6830
- const isVertical6 = Math.abs(start.x - end.x) < EPS9;
7027
+ const isVertical6 = Math.abs(start.x - end.x) < EPS10;
6831
7028
  for (let otherPathSegmentIndex = firstOtherPathSegmentIndex; otherPathSegmentIndex < lastOtherPathSegmentIndex; otherPathSegmentIndex++) {
6832
7029
  const otherStart = otherPath[otherPathSegmentIndex];
6833
7030
  const otherEnd = otherPath[otherPathSegmentIndex + 1];
6834
- const otherIsVertical = Math.abs(otherStart.x - otherEnd.x) < EPS9;
7031
+ const otherIsVertical = Math.abs(otherStart.x - otherEnd.x) < EPS10;
6835
7032
  if (isVertical6 === otherIsVertical) continue;
6836
7033
  const intersection = getSegmentIntersection(
6837
7034
  start,
@@ -6851,8 +7048,8 @@ var findPerpendicularPathCrossings = (path, otherPath, options = {}) => {
6851
7048
  };
6852
7049
 
6853
7050
  // lib/solvers/TraceCleanupSolver/sub-solver/generateLShapeRerouteCandidates.ts
6854
- var EPS10 = 1e-6;
6855
- var isVertical3 = (a, b, eps = EPS10) => Math.abs(a.x - b.x) < eps;
7051
+ var EPS11 = 1e-6;
7052
+ var isVertical3 = (a, b, eps = EPS11) => Math.abs(a.x - b.x) < eps;
6856
7053
  var generateLShapeRerouteCandidates = ({
6857
7054
  lShape,
6858
7055
  rectangle,
@@ -6865,7 +7062,7 @@ var generateLShapeRerouteCandidates = ({
6865
7062
  let c2;
6866
7063
  let i1_padded = interactionPoint1;
6867
7064
  let i2_padded = interactionPoint2;
6868
- if (Math.abs(p2.x - x) < EPS10 && Math.abs(p2.y - (y + height)) < EPS10) {
7065
+ if (Math.abs(p2.x - x) < EPS11 && Math.abs(p2.y - (y + height)) < EPS11) {
6869
7066
  c2 = { x: x + width + padding, y: y - padding };
6870
7067
  if (isVertical3(p1, p2)) {
6871
7068
  i1_padded = { x: interactionPoint1.x, y: interactionPoint1.y - padding };
@@ -6877,7 +7074,7 @@ var generateLShapeRerouteCandidates = ({
6877
7074
  } else {
6878
7075
  i2_padded = { x: interactionPoint2.x + padding, y: interactionPoint2.y };
6879
7076
  }
6880
- } else if (Math.abs(p2.x - (x + width)) < EPS10 && Math.abs(p2.y - (y + height)) < EPS10) {
7077
+ } else if (Math.abs(p2.x - (x + width)) < EPS11 && Math.abs(p2.y - (y + height)) < EPS11) {
6881
7078
  c2 = { x: x - padding, y: y - padding };
6882
7079
  if (isVertical3(p1, p2)) {
6883
7080
  i1_padded = { x: interactionPoint1.x, y: interactionPoint1.y - padding };
@@ -6889,7 +7086,7 @@ var generateLShapeRerouteCandidates = ({
6889
7086
  } else {
6890
7087
  i2_padded = { x: interactionPoint2.x - padding, y: interactionPoint2.y };
6891
7088
  }
6892
- } else if (Math.abs(p2.x - x) < EPS10 && Math.abs(p2.y - y) < EPS10) {
7089
+ } else if (Math.abs(p2.x - x) < EPS11 && Math.abs(p2.y - y) < EPS11) {
6893
7090
  c2 = { x: x + width + padding, y: y + height + padding };
6894
7091
  if (isVertical3(p1, p2)) {
6895
7092
  i1_padded = { x: interactionPoint1.x, y: interactionPoint1.y + padding };
@@ -6901,7 +7098,7 @@ var generateLShapeRerouteCandidates = ({
6901
7098
  } else {
6902
7099
  i2_padded = { x: interactionPoint2.x + padding, y: interactionPoint2.y };
6903
7100
  }
6904
- } else if (Math.abs(p2.x - (x + width)) < EPS10 && Math.abs(p2.y - y) < EPS10) {
7101
+ } else if (Math.abs(p2.x - (x + width)) < EPS11 && Math.abs(p2.y - y) < EPS11) {
6905
7102
  c2 = { x: x - padding, y: y + height + padding };
6906
7103
  if (isVertical3(p1, p2)) {
6907
7104
  i1_padded = { x: interactionPoint1.x, y: interactionPoint1.y + padding };
@@ -6975,7 +7172,7 @@ var generatePerpendicularTraceDetours = ({
6975
7172
  const buildDetours = (path, index) => {
6976
7173
  const start = path[index];
6977
7174
  const end = path[index + 1];
6978
- const movingAxis = Math.abs(start.x - end.x) < EPS10 ? "y" : "x";
7175
+ const movingAxis = Math.abs(start.x - end.x) < EPS11 ? "y" : "x";
6979
7176
  const detourAxis = movingAxis === "x" ? "y" : "x";
6980
7177
  const bounds = obstacleBounds ?? {
6981
7178
  minX: Math.min(obstacleStart.x, obstacleEnd.x),
@@ -7602,9 +7799,9 @@ var UntangleTraceSubsolver = class extends BaseSolver {
7602
7799
  };
7603
7800
 
7604
7801
  // lib/solvers/TraceCleanupSolver/is4PointRectangle.ts
7605
- var EPS11 = 1e-6;
7606
- var sameX = (a, b) => Math.abs(a.x - b.x) <= EPS11;
7607
- var sameY = (a, b) => Math.abs(a.y - b.y) <= EPS11;
7802
+ var EPS12 = 1e-6;
7803
+ var sameX = (a, b) => Math.abs(a.x - b.x) <= EPS12;
7804
+ var sameY = (a, b) => Math.abs(a.y - b.y) <= EPS12;
7608
7805
  var is4PointRectangle = (path) => {
7609
7806
  if (path.length !== 4) return false;
7610
7807
  const [p0, p1, p2, p3] = path;
@@ -7614,7 +7811,7 @@ var is4PointRectangle = (path) => {
7614
7811
  };
7615
7812
 
7616
7813
  // lib/solvers/TraceCleanupSolver/rerouteGeneratedNetLabelConnectorCrossings.ts
7617
- var EPS12 = 1e-6;
7814
+ var EPS13 = 1e-6;
7618
7815
  var getConnectorObstacleBounds = (connector, segmentIndex, labels) => {
7619
7816
  const start = connector.tracePath[segmentIndex];
7620
7817
  const end = connector.tracePath[segmentIndex + 1];
@@ -7706,7 +7903,7 @@ var rerouteGeneratedNetLabelConnectorCrossings = ({
7706
7903
  chipBounds: [],
7707
7904
  clearance
7708
7905
  }).filter(
7709
- (candidate) => getPathLength4(candidate.path) <= getPathLength4(trace.tracePath) + EPS12 && countTurns(candidate.path) <= countTurns(trace.tracePath) + 2 && !isPathCollidingWithObstacles(
7906
+ (candidate) => getPathLength4(candidate.path) <= getPathLength4(trace.tracePath) + EPS13 && countTurns(candidate.path) <= countTurns(trace.tracePath) + 2 && !isPathCollidingWithObstacles(
7710
7907
  candidate.path,
7711
7908
  componentAndTextObstacles
7712
7909
  ) && !hasCollisionsWithLabels(candidate.path, foreignLabelBounds) && !isPathColliding(candidate.path, foreignTraces, trace.mspPairId).isColliding && !doesPathCoincideWithTraces(candidate.path, foreignTraces)
@@ -7717,7 +7914,7 @@ var rerouteGeneratedNetLabelConnectorCrossings = ({
7717
7914
  });
7718
7915
  candidates.sort((first, second) => {
7719
7916
  const lengthDifference = getPathLength4(first.path) - getPathLength4(second.path);
7720
- return Math.abs(lengthDifference) > EPS12 ? lengthDifference : countTurns(first.path) - countTurns(second.path);
7917
+ return Math.abs(lengthDifference) > EPS13 ? lengthDifference : countTurns(first.path) - countTurns(second.path);
7721
7918
  });
7722
7919
  const bestCandidate = candidates[0];
7723
7920
  if (!bestCandidate) break;
@@ -8099,7 +8296,7 @@ var doesPathRunAlongChipBoundary = (path, chipObstacles) => {
8099
8296
  const end = path[i + 1];
8100
8297
  for (const obstacle of chipObstacles) {
8101
8298
  if (!segmentRunsAlongRectBoundary(start, end, obstacle)) continue;
8102
- if (getSegmentOverlapWithRectSpan(start, end, obstacle) > EPS8) {
8299
+ if (getSegmentOverlapWithRectSpan(start, end, obstacle) > EPS9) {
8103
8300
  return true;
8104
8301
  }
8105
8302
  }
@@ -8114,13 +8311,13 @@ var getChipBoundaryOverlap = (path, chipObstacles) => {
8114
8311
  for (const obstacle of chipObstacles) {
8115
8312
  if (!segmentRunsAlongRectBoundary(start, end, obstacle)) continue;
8116
8313
  const overlap = getSegmentOverlapWithRectSpan(start, end, obstacle);
8117
- if (overlap > EPS8) total += overlap;
8314
+ if (overlap > EPS9) total += overlap;
8118
8315
  }
8119
8316
  }
8120
8317
  return total;
8121
8318
  };
8122
8319
  var getSegmentOverlapWithRectSpan = (start, end, rect) => {
8123
- const isVertical6 = Math.abs(start.x - end.x) < EPS8;
8320
+ const isVertical6 = Math.abs(start.x - end.x) < EPS9;
8124
8321
  if (isVertical6) {
8125
8322
  return Math.min(Math.max(start.y, end.y), rect.maxY) - Math.max(Math.min(start.y, end.y), rect.minY);
8126
8323
  }
@@ -8885,16 +9082,16 @@ var createPortOnlyLabelConnectorTrace = ({
8885
9082
  var LABEL_SEARCH_STEP = 0.05;
8886
9083
  var MAX_RECORDED_CANDIDATES = 2e3;
8887
9084
  var WICK_CLEARANCE = 1e-3;
8888
- var EPS13 = 1e-9;
8889
- var TRACE_BOUNDARY_TOLERANCE = WICK_CLEARANCE + EPS13;
9085
+ var EPS14 = 1e-9;
9086
+ var TRACE_BOUNDARY_TOLERANCE = WICK_CLEARANCE + EPS14;
8890
9087
  var CANDIDATE_SELECTED_COLOR2 = "blue";
8891
9088
  var CANDIDATE_REJECTED_COLOR2 = "red";
8892
9089
 
8893
9090
  // lib/solvers/AvailableNetOrientationSolver/geometry.ts
8894
9091
  var isYOrientation = (orientation) => orientation === "y+" || orientation === "y-";
8895
9092
  var isXOrientation = (orientation) => orientation === "x+" || orientation === "x-";
8896
- var rectsOverlap2 = (a, b) => Math.min(a.maxX, b.maxX) - Math.max(a.minX, b.minX) > EPS13 && Math.min(a.maxY, b.maxY) - Math.max(a.minY, b.minY) > EPS13;
8897
- var rangesOverlap = (minA, maxA, minB, maxB) => Math.min(maxA, maxB) - Math.max(minA, minB) > EPS13;
9093
+ var rectsOverlap2 = (a, b) => Math.min(a.maxX, b.maxX) - Math.max(a.minX, b.minX) > EPS14 && Math.min(a.maxY, b.maxY) - Math.max(a.minY, b.minY) > EPS14;
9094
+ var rangesOverlap = (minA, maxA, minB, maxB) => Math.min(maxA, maxB) - Math.max(minA, minB) > EPS14;
8898
9095
  var traceCrossesBoundsInterior = (bounds, traceMap) => {
8899
9096
  for (const trace of Object.values(traceMap)) {
8900
9097
  const points = trace.tracePath;
@@ -8917,7 +9114,7 @@ var segmentCrossesBoundsInterior = (p1, p2, bounds) => {
8917
9114
  return false;
8918
9115
  }
8919
9116
  if (sameX2(p1, p2)) {
8920
- if (p1.x <= interiorBounds.minX + EPS13 || p1.x >= interiorBounds.maxX - EPS13) {
9117
+ if (p1.x <= interiorBounds.minX + EPS14 || p1.x >= interiorBounds.maxX - EPS14) {
8921
9118
  return false;
8922
9119
  }
8923
9120
  return rangesOverlap(
@@ -8928,7 +9125,7 @@ var segmentCrossesBoundsInterior = (p1, p2, bounds) => {
8928
9125
  );
8929
9126
  }
8930
9127
  if (sameY2(p1, p2)) {
8931
- if (p1.y <= interiorBounds.minY + EPS13 || p1.y >= interiorBounds.maxY - EPS13) {
9128
+ if (p1.y <= interiorBounds.minY + EPS14 || p1.y >= interiorBounds.maxY - EPS14) {
8932
9129
  return false;
8933
9130
  }
8934
9131
  return rangesOverlap(
@@ -8976,8 +9173,8 @@ var simplifyOrthogonalPath = (path) => {
8976
9173
  return simplified;
8977
9174
  };
8978
9175
  var pointsEqual3 = (a, b) => sameX2(a, b) && sameY2(a, b);
8979
- var sameX2 = (a, b) => Math.abs(a.x - b.x) <= EPS13;
8980
- var sameY2 = (a, b) => Math.abs(a.y - b.y) <= EPS13;
9176
+ var sameX2 = (a, b) => Math.abs(a.x - b.x) <= EPS14;
9177
+ var sameY2 = (a, b) => Math.abs(a.y - b.y) <= EPS14;
8981
9178
  var getMaxSearchDistance = (inputProblem) => {
8982
9179
  const maxChipWidth = Math.max(
8983
9180
  ...inputProblem.chips.map((chip) => chip.width),
@@ -9136,7 +9333,7 @@ var AvailableNetOrientationObstacleIndex = class {
9136
9333
  }
9137
9334
  getTraceSegmentsInBounds(bounds) {
9138
9335
  if (!this.traceSegmentIndex) return [];
9139
- const searchBounds = getPaddedBounds(bounds, EPS13);
9336
+ const searchBounds = getPaddedBounds(bounds, EPS14);
9140
9337
  return this.traceSegmentIndex.search(
9141
9338
  searchBounds.minX,
9142
9339
  searchBounds.minY,
@@ -9149,7 +9346,7 @@ var AvailableNetOrientationObstacleIndex = class {
9149
9346
  for (let pointIndex = 0; pointIndex < tracePath.length - 1; pointIndex++) {
9150
9347
  const segmentBounds = getPaddedBounds(
9151
9348
  getSegmentBounds(tracePath[pointIndex], tracePath[pointIndex + 1]),
9152
- EPS13
9349
+ EPS14
9153
9350
  );
9154
9351
  for (const labelIndex of this.getLabelIndicesInBounds(segmentBounds)) {
9155
9352
  labelIndices.add(labelIndex);
@@ -9162,7 +9359,7 @@ var AvailableNetOrientationObstacleIndex = class {
9162
9359
  const start = tracePath[pointIndex];
9163
9360
  const end = tracePath[pointIndex + 1];
9164
9361
  const nearbyChips = this.chipObstacleSpatialIndex.getChipsInBounds(
9165
- getPaddedBounds(getSegmentBounds(start, end), EPS13)
9362
+ getPaddedBounds(getSegmentBounds(start, end), EPS14)
9166
9363
  );
9167
9364
  for (const chip of nearbyChips) {
9168
9365
  if (segmentCrossesBoundsInterior(start, end, chip.bounds)) return true;
@@ -9619,7 +9816,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
9619
9816
  labelIndex,
9620
9817
  orientations
9621
9818
  );
9622
- const rotatedCandidateNeedsLateralBranch = rotatedCandidate !== null && Math.abs(rotatedCandidate.anchorPoint.x - label.anchorPoint.x) > EPS13;
9819
+ const rotatedCandidateNeedsLateralBranch = rotatedCandidate !== null && Math.abs(rotatedCandidate.anchorPoint.x - label.anchorPoint.x) > EPS14;
9623
9820
  if (isDownwardGroundRail && rotatedCandidateNeedsLateralBranch) {
9624
9821
  const downwardRailCandidate = this.findValidCandidateInShiftColumn({
9625
9822
  label,
@@ -9678,7 +9875,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
9678
9875
  index,
9679
9876
  pin: this.pinMap[otherLabel.pinIds[0]]
9680
9877
  })).filter(
9681
- ({ index, pin: otherPin }) => this.crowdedPortOnlyLabelIndices.has(index) && otherPin?.chipId === pin.chipId && otherPin.x > pin.x + EPS13
9878
+ ({ index, pin: otherPin }) => this.crowdedPortOnlyLabelIndices.has(index) && otherPin?.chipId === pin.chipId && otherPin.x > pin.x + EPS14
9682
9879
  ).sort((a, b) => a.pin.x - b.pin.x)[0]?.pin;
9683
9880
  if (orientation === "y-" && nextPinToRight) return null;
9684
9881
  const anchorX = orientation === "y-" ? Math.max(
@@ -9711,7 +9908,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
9711
9908
  const centeredAnchorY = hasUpFacingStack ? (lowestUpFacingLabelY + highestUpFacingLabelY) / 2 : stackedAnchorY;
9712
9909
  const anchorYs = orientation === "y-" ? [centeredAnchorY, stackedAnchorY] : [stackedAnchorY];
9713
9910
  for (const anchorY of anchorYs) {
9714
- if (anchorY - label.anchorPoint.y > this.maxSearchDistance + EPS13) {
9911
+ if (anchorY - label.anchorPoint.y > this.maxSearchDistance + EPS14) {
9715
9912
  continue;
9716
9913
  }
9717
9914
  const candidate = this.createCandidate(
@@ -9740,11 +9937,11 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
9740
9937
  if (trace.globalConnNetId !== label.globalConnNetId) return false;
9741
9938
  const path = trace.tracePath;
9742
9939
  return path.some((point, pointIndex) => {
9743
- if (Math.abs(point.x - label.anchorPoint.x) > EPS13 || Math.abs(point.y - label.anchorPoint.y) > EPS13) {
9940
+ if (Math.abs(point.x - label.anchorPoint.x) > EPS14 || Math.abs(point.y - label.anchorPoint.y) > EPS14) {
9744
9941
  return false;
9745
9942
  }
9746
9943
  return [path[pointIndex - 1], path[pointIndex + 1]].some(
9747
- (neighbor) => neighbor && Math.abs(neighbor.x - point.x) <= EPS13 && (neighbor.y - point.y) * direction.y > EPS13
9944
+ (neighbor) => neighbor && Math.abs(neighbor.x - point.x) <= EPS14 && (neighbor.y - point.y) * direction.y > EPS14
9748
9945
  );
9749
9946
  });
9750
9947
  });
@@ -9834,7 +10031,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
9834
10031
  orientation
9835
10032
  );
9836
10033
  if (outwardDirection.x === 0 && outwardDirection.y === 0) continue;
9837
- for (let distance7 = LABEL_SEARCH_STEP; distance7 <= this.maxSearchDistance + EPS13; distance7 += LABEL_SEARCH_STEP) {
10034
+ for (let distance7 = LABEL_SEARCH_STEP; distance7 <= this.maxSearchDistance + EPS14; distance7 += LABEL_SEARCH_STEP) {
9838
10035
  const anchorPoint = {
9839
10036
  x: connectorSource.x + outwardDirection.x * distance7,
9840
10037
  y: connectorSource.y + outwardDirection.y * distance7
@@ -9912,7 +10109,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
9912
10109
  const furthestRowPoints = points.filter(
9913
10110
  (point) => Math.abs(
9914
10111
  point.x * direction.x + point.y * direction.y - furthestAlong
9915
- ) <= EPS13
10112
+ ) <= EPS14
9916
10113
  );
9917
10114
  const directHostPinIds = new Set(
9918
10115
  [...directHostTraceIds].flatMap(
@@ -9933,7 +10130,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
9933
10130
  preferNearestBendOnOutwardRow: adjacentTraces.some(
9934
10131
  (trace) => furthestRowPoints.some(
9935
10132
  (point) => trace.tracePath.some(
9936
- (tracePoint) => Math.abs(tracePoint.x - point.x) <= EPS13 && Math.abs(tracePoint.y - point.y) <= EPS13
10133
+ (tracePoint) => Math.abs(tracePoint.x - point.x) <= EPS14 && Math.abs(tracePoint.y - point.y) <= EPS14
9937
10134
  )
9938
10135
  )
9939
10136
  )
@@ -9945,7 +10142,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
9945
10142
  for (let i = 0; i < otherTrace.tracePath.length - 1; i++) {
9946
10143
  const start = otherTrace.tracePath[i];
9947
10144
  const end = otherTrace.tracePath[i + 1];
9948
- if (Math.abs(start.x - end.x) <= EPS13 && Math.abs(start.y - end.y) > EPS13) {
10145
+ if (Math.abs(start.x - end.x) <= EPS14 && Math.abs(start.y - end.y) > EPS14) {
9949
10146
  verticalRailXs.add(start.x);
9950
10147
  }
9951
10148
  }
@@ -9953,7 +10150,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
9953
10150
  for (let i = 0; i < trace.tracePath.length - 1; i++) {
9954
10151
  const start = trace.tracePath[i];
9955
10152
  const end = trace.tracePath[i + 1];
9956
- if (Math.abs(start.x - end.x) <= EPS13 && Math.abs(start.y - end.y) > EPS13 && [...verticalRailXs].some((x) => Math.abs(x - start.x) <= EPS13)) {
10153
+ if (Math.abs(start.x - end.x) <= EPS14 && Math.abs(start.y - end.y) > EPS14 && [...verticalRailXs].some((x) => Math.abs(x - start.x) <= EPS14)) {
9957
10154
  return true;
9958
10155
  }
9959
10156
  }
@@ -9972,7 +10169,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
9972
10169
  );
9973
10170
  const maxSearchDistance = this.getSearchDistanceLimit(label, orientation);
9974
10171
  const maxOutwardDistance = outwardDirection.x === 0 && outwardDirection.y === 0 ? 0 : this.maxSearchDistance;
9975
- for (let outwardDistance = 0; outwardDistance <= maxOutwardDistance + EPS13; outwardDistance += LABEL_SEARCH_STEP) {
10172
+ for (let outwardDistance = 0; outwardDistance <= maxOutwardDistance + EPS14; outwardDistance += LABEL_SEARCH_STEP) {
9976
10173
  const baseAnchor = {
9977
10174
  x: initialBaseAnchor.x + outwardDirection.x * outwardDistance,
9978
10175
  y: initialBaseAnchor.y + outwardDirection.y * outwardDistance
@@ -10048,7 +10245,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
10048
10245
  connectorSource,
10049
10246
  startDistance = LABEL_SEARCH_STEP
10050
10247
  } = params;
10051
- for (let distance7 = startDistance; distance7 <= maxSearchDistance + EPS13; distance7 += LABEL_SEARCH_STEP) {
10248
+ for (let distance7 = startDistance; distance7 <= maxSearchDistance + EPS14; distance7 += LABEL_SEARCH_STEP) {
10052
10249
  const anchorPoint = {
10053
10250
  x: baseAnchor.x + direction.x * distance7,
10054
10251
  y: baseAnchor.y + direction.y * distance7
@@ -10161,14 +10358,14 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
10161
10358
  for (let pointIndex = 0; pointIndex < hostTrace.tracePath.length - 1; pointIndex++) {
10162
10359
  const start = hostTrace.tracePath[pointIndex];
10163
10360
  const end = hostTrace.tracePath[pointIndex + 1];
10164
- const segmentIsPerpendicularToLabel = isYOrientation(orientation) ? Math.abs(start.y - end.y) <= EPS13 : Math.abs(start.x - end.x) <= EPS13;
10361
+ const segmentIsPerpendicularToLabel = isYOrientation(orientation) ? Math.abs(start.y - end.y) <= EPS14 : Math.abs(start.x - end.x) <= EPS14;
10165
10362
  if (!segmentIsPerpendicularToLabel) continue;
10166
10363
  const source = isYOrientation(orientation) ? { x: targetAnchor.x, y: start.y } : { x: start.x, y: targetAnchor.y };
10167
10364
  if (!tracePathContainsPoint([start, end], label.anchorPoint) || !tracePathContainsPoint([start, end], source)) {
10168
10365
  continue;
10169
10366
  }
10170
10367
  const targetDistanceAlongOrientation = (targetAnchor.x - source.x) * orientationDirection.x + (targetAnchor.y - source.y) * orientationDirection.y;
10171
- if (targetDistanceAlongOrientation >= -EPS13) {
10368
+ if (targetDistanceAlongOrientation >= -EPS14) {
10172
10369
  projectedSources.push(source);
10173
10370
  }
10174
10371
  }
@@ -10427,9 +10624,9 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
10427
10624
  const { anchorPoint, orientation } = candidate;
10428
10625
  const chipBounds = chip.bounds;
10429
10626
  if (isYOrientation(orientation)) {
10430
- return anchorPoint.x < chipBounds.minX - EPS13 || anchorPoint.x > chipBounds.maxX + EPS13;
10627
+ return anchorPoint.x < chipBounds.minX - EPS14 || anchorPoint.x > chipBounds.maxX + EPS14;
10431
10628
  }
10432
- return anchorPoint.y < chipBounds.minY - EPS13 || anchorPoint.y > chipBounds.maxY + EPS13;
10629
+ return anchorPoint.y < chipBounds.minY - EPS14 || anchorPoint.y > chipBounds.maxY + EPS14;
10433
10630
  });
10434
10631
  }
10435
10632
  staysOutsideConnectedPinRow(params) {
@@ -10437,9 +10634,9 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
10437
10634
  const pin = this.pinMap[label.pinIds[0]];
10438
10635
  if (!pin || !isYOrientation(candidate.orientation)) return false;
10439
10636
  if (candidate.anchorPoint.x < pin.x) {
10440
- return bounds.maxX <= pin.x + CONNECTED_PIN_ROW_OVERLAP_TOLERANCE + EPS13;
10637
+ return bounds.maxX <= pin.x + CONNECTED_PIN_ROW_OVERLAP_TOLERANCE + EPS14;
10441
10638
  }
10442
- return bounds.minX >= pin.x - CONNECTED_PIN_ROW_OVERLAP_TOLERANCE - EPS13;
10639
+ return bounds.minX >= pin.x - CONNECTED_PIN_ROW_OVERLAP_TOLERANCE - EPS14;
10443
10640
  }
10444
10641
  getBoundsStatus(candidateBoundsCheck) {
10445
10642
  const { bounds, labelIndex, label, ignoreChipCollisions } = candidateBoundsCheck;
@@ -10520,14 +10717,14 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
10520
10717
  candidate.width,
10521
10718
  candidate.height
10522
10719
  );
10523
- if (labelBounds.minY < chip.bounds.minY - EPS13 || labelBounds.maxY > chip.bounds.maxY + EPS13) {
10720
+ if (labelBounds.minY < chip.bounds.minY - EPS14 || labelBounds.maxY > chip.bounds.maxY + EPS14) {
10524
10721
  return false;
10525
10722
  }
10526
- if (candidate.anchorPoint.x >= chip.bounds.minX - EPS13 && candidate.anchorPoint.x <= chip.bounds.maxX + EPS13) {
10723
+ if (candidate.anchorPoint.x >= chip.bounds.minX - EPS14 && candidate.anchorPoint.x <= chip.bounds.maxX + EPS14) {
10527
10724
  return false;
10528
10725
  }
10529
10726
  return chip.pins.every(
10530
- (pin) => label.pinIds.includes(pin.pinId) || pin.y <= labelBounds.minY + EPS13 || pin.y >= labelBounds.maxY - EPS13
10727
+ (pin) => label.pinIds.includes(pin.pinId) || pin.y <= labelBounds.minY + EPS14 || pin.y >= labelBounds.maxY - EPS14
10531
10728
  );
10532
10729
  }
10533
10730
  getTextBlockedMultiPinRailTraces(label, orientation) {
@@ -10554,20 +10751,20 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
10554
10751
  const pins = [...connectedPinIds].map((pinId) => this.pinMap[pinId]);
10555
10752
  const direction = dir(orientation);
10556
10753
  const pinX = pins[0].x;
10557
- if (pins.some((pin) => Math.abs(pin.x - pinX) > EPS13)) return null;
10558
- const sidePins = this.inputProblem.chips.find((chip) => chip.chipId === componentId).pins.filter((pin) => Math.abs(pin.x - pinX) <= EPS13);
10754
+ if (pins.some((pin) => Math.abs(pin.x - pinX) > EPS14)) return null;
10755
+ const sidePins = this.inputProblem.chips.find((chip) => chip.chipId === componentId).pins.filter((pin) => Math.abs(pin.x - pinX) <= EPS14);
10559
10756
  const minY = Math.min(...pins.map((pin) => pin.y));
10560
10757
  const maxY = Math.max(...pins.map((pin) => pin.y));
10561
10758
  if (sidePins.some(
10562
- (pin) => pin.y >= minY - EPS13 && pin.y <= maxY + EPS13 && !connectedPinIds.has(pin.pinId)
10563
- ) || orientation === "y-" && minY > Math.min(...sidePins.map((pin) => pin.y)) + EPS13 || orientation === "y+" && maxY < Math.max(...sidePins.map((pin) => pin.y)) - EPS13) {
10759
+ (pin) => pin.y >= minY - EPS14 && pin.y <= maxY + EPS14 && !connectedPinIds.has(pin.pinId)
10760
+ ) || orientation === "y-" && minY > Math.min(...sidePins.map((pin) => pin.y)) + EPS14 || orientation === "y+" && maxY < Math.max(...sidePins.map((pin) => pin.y)) - EPS14) {
10564
10761
  return null;
10565
10762
  }
10566
10763
  const outerPinPosition = Math.max(
10567
10764
  ...pins.map((pin) => pin.x * direction.x + pin.y * direction.y)
10568
10765
  );
10569
10766
  const blockedByText = (this.inputProblem.textBoxes ?? []).some(
10570
- (textBox) => textBox.chipId === componentId && textBox.center.x * direction.x + textBox.center.y * direction.y > outerPinPosition + EPS13
10767
+ (textBox) => textBox.chipId === componentId && textBox.center.x * direction.x + textBox.center.y * direction.y > outerPinPosition + EPS14
10571
10768
  );
10572
10769
  return blockedByText ? [...connectedTraces] : null;
10573
10770
  }
@@ -10583,7 +10780,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
10583
10780
  ...new Set(
10584
10781
  localTraces.flatMap((trace) => trace.tracePath.map((point) => point.x))
10585
10782
  )
10586
- ].filter((x) => Math.abs(x - pinX) > EPS13).sort((a, b) => Math.abs(a - pinX) - Math.abs(b - pinX));
10783
+ ].filter((x) => Math.abs(x - pinX) > EPS14).sort((a, b) => Math.abs(a - pinX) - Math.abs(b - pinX));
10587
10784
  for (const x of railXs) {
10588
10785
  const connectorSource = { x, y: connectorY };
10589
10786
  if (!Object.values(this.traceMap).some(
@@ -10646,8 +10843,8 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
10646
10843
  if (!chip) return null;
10647
10844
  const sides = new Set(
10648
10845
  pins.map((pin) => {
10649
- if (Math.abs(pin.x - chip.bounds.minX) <= EPS13) return "left";
10650
- if (Math.abs(pin.x - chip.bounds.maxX) <= EPS13) return "right";
10846
+ if (Math.abs(pin.x - chip.bounds.minX) <= EPS14) return "left";
10847
+ if (Math.abs(pin.x - chip.bounds.maxX) <= EPS14) return "right";
10651
10848
  return null;
10652
10849
  })
10653
10850
  );
@@ -10684,7 +10881,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
10684
10881
  return null;
10685
10882
  }
10686
10883
  const renderedWidth = this.getNetConnectionForLabel(label)?.netLabelHeight;
10687
- if (renderedWidth === void 0 || renderedWidth <= label.width + label.height + EPS13) {
10884
+ if (renderedWidth === void 0 || renderedWidth <= label.width + label.height + EPS14) {
10688
10885
  return null;
10689
10886
  }
10690
10887
  const placementBaseCenter = getCenterFromAnchor(
@@ -10759,16 +10956,16 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
10759
10956
  }
10760
10957
  sharesChipBoundary(bounds) {
10761
10958
  const boundarySearchBounds = {
10762
- minX: bounds.minX - WICK_CLEARANCE - EPS13,
10763
- minY: bounds.minY - WICK_CLEARANCE - EPS13,
10764
- maxX: bounds.maxX + WICK_CLEARANCE + EPS13,
10765
- maxY: bounds.maxY + WICK_CLEARANCE + EPS13
10959
+ minX: bounds.minX - WICK_CLEARANCE - EPS14,
10960
+ minY: bounds.minY - WICK_CLEARANCE - EPS14,
10961
+ maxX: bounds.maxX + WICK_CLEARANCE + EPS14,
10962
+ maxY: bounds.maxY + WICK_CLEARANCE + EPS14
10766
10963
  };
10767
10964
  const nearbyChips = this.chipObstacleSpatialIndex.getChipsInBounds(boundarySearchBounds);
10768
10965
  for (const chip of nearbyChips) {
10769
10966
  const chipBounds = chip.bounds;
10770
- const adjacentToVerticalSide = Math.abs(bounds.minX - chipBounds.maxX) <= WICK_CLEARANCE + EPS13 || Math.abs(bounds.maxX - chipBounds.minX) <= WICK_CLEARANCE + EPS13;
10771
- const adjacentToHorizontalSide = Math.abs(bounds.minY - chipBounds.maxY) <= WICK_CLEARANCE + EPS13 || Math.abs(bounds.maxY - chipBounds.minY) <= WICK_CLEARANCE + EPS13;
10967
+ const adjacentToVerticalSide = Math.abs(bounds.minX - chipBounds.maxX) <= WICK_CLEARANCE + EPS14 || Math.abs(bounds.maxX - chipBounds.minX) <= WICK_CLEARANCE + EPS14;
10968
+ const adjacentToHorizontalSide = Math.abs(bounds.minY - chipBounds.maxY) <= WICK_CLEARANCE + EPS14 || Math.abs(bounds.maxY - chipBounds.minY) <= WICK_CLEARANCE + EPS14;
10772
10969
  if (adjacentToVerticalSide && rangesOverlap(
10773
10970
  bounds.minY,
10774
10971
  bounds.maxY,
@@ -10830,7 +11027,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
10830
11027
  let nearestDistance = Number.POSITIVE_INFINITY;
10831
11028
  for (const chip of this.chipObstacleSpatialIndex.chips) {
10832
11029
  const bounds = chip.bounds;
10833
- if (point.x < bounds.minX - EPS13 || point.x > bounds.maxX + EPS13 || point.y < bounds.minY - EPS13 || point.y > bounds.maxY + EPS13) {
11030
+ if (point.x < bounds.minX - EPS14 || point.x > bounds.maxX + EPS14 || point.y < bounds.minY - EPS14 || point.y > bounds.maxY + EPS14) {
10834
11031
  continue;
10835
11032
  }
10836
11033
  for (const [side, distance7] of getSideDistances(point, bounds)) {
@@ -10847,8 +11044,8 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
10847
11044
  let nearestDistanceSq = Number.POSITIVE_INFINITY;
10848
11045
  for (const chip of this.chipObstacleSpatialIndex.chips) {
10849
11046
  const bounds = chip.bounds;
10850
- const dx = point.x < bounds.minX - EPS13 ? bounds.minX - point.x : point.x > bounds.maxX + EPS13 ? point.x - bounds.maxX : 0;
10851
- const dy = point.y < bounds.minY - EPS13 ? bounds.minY - point.y : point.y > bounds.maxY + EPS13 ? point.y - bounds.maxY : 0;
11047
+ const dx = point.x < bounds.minX - EPS14 ? bounds.minX - point.x : point.x > bounds.maxX + EPS14 ? point.x - bounds.maxX : 0;
11048
+ const dy = point.y < bounds.minY - EPS14 ? bounds.minY - point.y : point.y > bounds.maxY + EPS14 ? point.y - bounds.maxY : 0;
10852
11049
  if (dx === 0 && dy === 0) continue;
10853
11050
  const distanceSq = dx ** 2 + dy ** 2;
10854
11051
  if (distanceSq >= nearestDistanceSq) continue;
@@ -11164,7 +11361,7 @@ var RailNetLabelCornerPlacementSolver = class extends BaseSolver {
11164
11361
  for (const anchorPoint of getTraceCorners(path)) {
11165
11362
  const cornerIndex = path.indexOf(anchorPoint);
11166
11363
  const anchorAlignedToPin = pins.some(
11167
- (pin) => Math.abs(pin.x - anchorPoint.x) <= EPS7
11364
+ (pin) => Math.abs(pin.x - anchorPoint.x) <= EPS8
11168
11365
  );
11169
11366
  const pinAligned = anchorAlignedToPin || allowRailAlignedFallback && this.isPinAlignedCorner(path, cornerIndex);
11170
11367
  if (!pinAligned) continue;
@@ -11204,12 +11401,12 @@ var RailNetLabelCornerPlacementSolver = class extends BaseSolver {
11204
11401
  return traceCrossesBoundsInterior(bounds, otherNetTraceMap);
11205
11402
  }
11206
11403
  pointsEqual(a, b) {
11207
- return Math.abs(a.x - b.x) <= EPS7 && Math.abs(a.y - b.y) <= EPS7;
11404
+ return Math.abs(a.x - b.x) <= EPS8 && Math.abs(a.y - b.y) <= EPS8;
11208
11405
  }
11209
11406
  getVerticalSegmentPointIndices(path, cornerIndex) {
11210
11407
  const corner = path[cornerIndex];
11211
- const previousIsVertical = Math.abs(path[cornerIndex - 1].x - corner.x) <= EPS7;
11212
- const nextIsVertical = Math.abs(path[cornerIndex + 1].x - corner.x) <= EPS7;
11408
+ const previousIsVertical = Math.abs(path[cornerIndex - 1].x - corner.x) <= EPS8;
11409
+ const nextIsVertical = Math.abs(path[cornerIndex + 1].x - corner.x) <= EPS8;
11213
11410
  if (previousIsVertical === nextIsVertical) return null;
11214
11411
  const indices = previousIsVertical ? [cornerIndex - 1, cornerIndex] : [cornerIndex, cornerIndex + 1];
11215
11412
  return indices;
@@ -11259,7 +11456,7 @@ var RailNetLabelCornerPlacementSolver = class extends BaseSolver {
11259
11456
  const end = otherTrace.tracePath[i + 1];
11260
11457
  if (!segmentCrossesBoundsInterior(start, end, labelBounds)) continue;
11261
11458
  const halfLabelWidth = label.width / 2;
11262
- if (Math.abs(start.x - end.x) <= EPS7) {
11459
+ if (Math.abs(start.x - end.x) <= EPS8) {
11263
11460
  shiftedCoordinates.add(
11264
11461
  start.x - halfLabelWidth - LABEL_TRACE_CLEARANCE3
11265
11462
  );
@@ -11277,7 +11474,7 @@ var RailNetLabelCornerPlacementSolver = class extends BaseSolver {
11277
11474
  }
11278
11475
  }
11279
11476
  return [...shiftedCoordinates].flatMap((x) => {
11280
- if (Math.abs(x - corner.x) <= EPS7) return [];
11477
+ if (Math.abs(x - corner.x) <= EPS8) return [];
11281
11478
  const reroutedTracePath = simplifyPath(
11282
11479
  path.map(
11283
11480
  (point, pointIndex) => verticalPointIndices.includes(pointIndex) ? { ...point, x } : point
@@ -11408,11 +11605,11 @@ var getOrientationConstraint = (inputProblem, label) => {
11408
11605
  };
11409
11606
 
11410
11607
  // lib/solvers/TraceAnchoredNetLabelOverlapSolver/geometry.ts
11411
- var EPS14 = 1e-6;
11608
+ var EPS15 = 1e-6;
11412
11609
  var TRACE_BOUNDARY_TOLERANCE2 = 1e-6;
11413
11610
  var getLabelBounds = (label) => getRectBounds(label.center, label.width, label.height);
11414
- var rectsOverlap3 = (a, b) => Math.min(a.maxX, b.maxX) - Math.max(a.minX, b.minX) > EPS14 && Math.min(a.maxY, b.maxY) - Math.max(a.minY, b.minY) > EPS14;
11415
- var rectsTouchOrOverlap = (a, b) => Math.min(a.maxX, b.maxX) - Math.max(a.minX, b.minX) >= -EPS14 && Math.min(a.maxY, b.maxY) - Math.max(a.minY, b.minY) >= -EPS14;
11611
+ var rectsOverlap3 = (a, b) => Math.min(a.maxX, b.maxX) - Math.max(a.minX, b.minX) > EPS15 && Math.min(a.maxY, b.maxY) - Math.max(a.minY, b.minY) > EPS15;
11612
+ var rectsTouchOrOverlap = (a, b) => Math.min(a.maxX, b.maxX) - Math.max(a.minX, b.minX) >= -EPS15 && Math.min(a.maxY, b.maxY) - Math.max(a.minY, b.minY) >= -EPS15;
11416
11613
  var traceCrossesBoundsInterior2 = (bounds, traces) => {
11417
11614
  for (const trace of traces) {
11418
11615
  const points = trace.tracePath;
@@ -11458,7 +11655,7 @@ var getPointAtTraceDistance = (trace, distance7) => {
11458
11655
  const end = trace.tracePath[i + 1];
11459
11656
  const segmentLength = getManhattanDistance(start, end);
11460
11657
  const nextDistance = pathDistance + segmentLength;
11461
- if (distance7 <= nextDistance + EPS14) {
11658
+ if (distance7 <= nextDistance + EPS15) {
11462
11659
  const offset = Math.max(
11463
11660
  0,
11464
11661
  Math.min(segmentLength, distance7 - pathDistance)
@@ -11497,7 +11694,7 @@ var segmentCrossesBoundsInterior2 = (start, end, bounds) => {
11497
11694
  return false;
11498
11695
  }
11499
11696
  if (isVertical4(start, end)) {
11500
- if (start.x <= interior.minX + EPS14 || start.x >= interior.maxX - EPS14) {
11697
+ if (start.x <= interior.minX + EPS15 || start.x >= interior.maxX - EPS15) {
11501
11698
  return false;
11502
11699
  }
11503
11700
  return rangesOverlap2(
@@ -11508,7 +11705,7 @@ var segmentCrossesBoundsInterior2 = (start, end, bounds) => {
11508
11705
  );
11509
11706
  }
11510
11707
  if (isHorizontal3(start, end)) {
11511
- if (start.y <= interior.minY + EPS14 || start.y >= interior.maxY - EPS14) {
11708
+ if (start.y <= interior.minY + EPS15 || start.y >= interior.maxY - EPS15) {
11512
11709
  return false;
11513
11710
  }
11514
11711
  return rangesOverlap2(
@@ -11522,10 +11719,10 @@ var segmentCrossesBoundsInterior2 = (start, end, bounds) => {
11522
11719
  };
11523
11720
  var isPointOnSegment2 = (point, start, end) => {
11524
11721
  if (isHorizontal3(start, end)) {
11525
- return Math.abs(point.y - start.y) <= EPS14 && point.x >= Math.min(start.x, end.x) - EPS14 && point.x <= Math.max(start.x, end.x) + EPS14;
11722
+ return Math.abs(point.y - start.y) <= EPS15 && point.x >= Math.min(start.x, end.x) - EPS15 && point.x <= Math.max(start.x, end.x) + EPS15;
11526
11723
  }
11527
11724
  if (isVertical4(start, end)) {
11528
- return Math.abs(point.x - start.x) <= EPS14 && point.y >= Math.min(start.y, end.y) - EPS14 && point.y <= Math.max(start.y, end.y) + EPS14;
11725
+ return Math.abs(point.x - start.x) <= EPS15 && point.y >= Math.min(start.y, end.y) - EPS15 && point.y <= Math.max(start.y, end.y) + EPS15;
11529
11726
  }
11530
11727
  return false;
11531
11728
  };
@@ -11533,9 +11730,9 @@ var getSegmentDirection = (start, end) => ({
11533
11730
  x: isVertical4(start, end) ? 0 : Math.sign(end.x - start.x),
11534
11731
  y: isHorizontal3(start, end) ? 0 : Math.sign(end.y - start.y)
11535
11732
  });
11536
- var isHorizontal3 = (start, end) => Math.abs(start.y - end.y) <= EPS14;
11537
- var isVertical4 = (start, end) => Math.abs(start.x - end.x) <= EPS14;
11538
- var rangesOverlap2 = (minA, maxA, minB, maxB) => Math.min(maxA, maxB) - Math.max(minA, minB) > EPS14;
11733
+ var isHorizontal3 = (start, end) => Math.abs(start.y - end.y) <= EPS15;
11734
+ var isVertical4 = (start, end) => Math.abs(start.x - end.x) <= EPS15;
11735
+ var rangesOverlap2 = (minA, maxA, minB, maxB) => Math.min(maxA, maxB) - Math.max(minA, minB) > EPS15;
11539
11736
  var getUniquePinCount2 = (pinIds) => new Set(pinIds).size;
11540
11737
 
11541
11738
  // lib/solvers/TraceAnchoredNetLabelOverlapSolver/candidates.ts
@@ -11592,7 +11789,7 @@ var getCandidateDistances = (traceLength, vertexDistances) => {
11592
11789
  for (const distance7 of vertexDistances) {
11593
11790
  distances.add(roundDistance(distance7));
11594
11791
  }
11595
- return [...distances].filter((distance7) => distance7 >= -EPS14 && distance7 <= traceLength + EPS14).sort((a, b) => a - b);
11792
+ return [...distances].filter((distance7) => distance7 >= -EPS15 && distance7 <= traceLength + EPS15).sort((a, b) => a - b);
11596
11793
  };
11597
11794
  var getOrientationsForPoint = (params) => {
11598
11795
  const { inputProblem, label, point, orientationConstraint } = params;
@@ -11681,13 +11878,13 @@ var getOutwardOrientationOptions = (point, chipBounds) => {
11681
11878
  };
11682
11879
  var getOutwardAxisOption = (params) => {
11683
11880
  const { value, min, max, center, positiveOrientation, negativeOrientation } = params;
11684
- if (value > max + EPS14) {
11881
+ if (value > max + EPS15) {
11685
11882
  return {
11686
11883
  orientation: positiveOrientation,
11687
11884
  outwardScore: 1 + value - max
11688
11885
  };
11689
11886
  }
11690
- if (value < min - EPS14) {
11887
+ if (value < min - EPS15) {
11691
11888
  return {
11692
11889
  orientation: negativeOrientation,
11693
11890
  outwardScore: 1 + min - value
@@ -11698,7 +11895,7 @@ var getOutwardAxisOption = (params) => {
11698
11895
  outwardScore: getNormalizedCenterDistance(value, center, max - min)
11699
11896
  };
11700
11897
  };
11701
- var getNormalizedCenterDistance = (value, center, span) => Math.abs(value - center) / Math.max(EPS14, span / 2);
11898
+ var getNormalizedCenterDistance = (value, center, span) => Math.abs(value - center) / Math.max(EPS15, span / 2);
11702
11899
  var ALL_ORIENTATIONS = ["x+", "x-", "y+", "y-"];
11703
11900
  var getFlippedOrientation = (orientation) => {
11704
11901
  switch (orientation) {
@@ -11766,7 +11963,7 @@ var getNetLabelHeight = (inputProblem, label) => {
11766
11963
  )?.netLabelHeight;
11767
11964
  };
11768
11965
  var roundDistance = (distance7) => Number(distance7.toFixed(6));
11769
- var isSamePlacement = (label, point, orientation) => Math.abs(point.x - label.anchorPoint.x) <= EPS14 && Math.abs(point.y - label.anchorPoint.y) <= EPS14 && orientation === label.orientation;
11966
+ var isSamePlacement = (label, point, orientation) => Math.abs(point.x - label.anchorPoint.x) <= EPS15 && Math.abs(point.y - label.anchorPoint.y) <= EPS15 && orientation === label.orientation;
11770
11967
 
11771
11968
  // lib/solvers/TraceAnchoredNetLabelOverlapSolver/visualize.ts
11772
11969
  var CANDIDATE_SELECTED_COLOR4 = "blue";
@@ -13275,7 +13472,7 @@ var UnroutedTraceRecoverySolver = class extends BaseSolver {
13275
13472
  };
13276
13473
 
13277
13474
  // lib/solvers/SameNetJunctionAlignmentSolver/alignSameNetJunctions.ts
13278
- import { getSegmentIntersection as getSegmentIntersection3 } from "@tscircuit/math-utils/line-intersections";
13475
+ import { getSegmentIntersection as getSegmentIntersection4 } from "@tscircuit/math-utils/line-intersections";
13279
13476
 
13280
13477
  // lib/solvers/SameNetJunctionAlignmentSolver/pathIntersectsAnyNetLabel.ts
13281
13478
  var NET_LABEL_INTERIOR_INSET = 1e-6;
@@ -13594,6 +13791,242 @@ var collapseRedundantSharedEndpointStubs = ({
13594
13791
  return outputTraces;
13595
13792
  };
13596
13793
 
13794
+ // lib/solvers/SameNetJunctionAlignmentSolver/collapseSameNetCycles.ts
13795
+ import { getSegmentIntersection as getSegmentIntersection3 } from "@tscircuit/math-utils/line-intersections";
13796
+ var TRACE_LENGTH_EPSILON = 1e-6;
13797
+ var getTracePathStartingAtPin = (trace, pin) => {
13798
+ const pathStart = trace.tracePath[0];
13799
+ const pathEnd = trace.tracePath.at(-1);
13800
+ if (!pathStart || !pathEnd) return null;
13801
+ if (pointsEqual2(pathStart, pin)) return trace.tracePath;
13802
+ if (pointsEqual2(pathEnd, pin)) return [...trace.tracePath].reverse();
13803
+ return null;
13804
+ };
13805
+ var getPerpendicularPathCrossings = (targetPath, donorPath) => {
13806
+ const crossings = [];
13807
+ const sharedEndpoint = targetPath[0];
13808
+ for (let targetSegmentIndex = 0; targetSegmentIndex < targetPath.length - 1; targetSegmentIndex++) {
13809
+ const targetStart = targetPath[targetSegmentIndex];
13810
+ const targetEnd = targetPath[targetSegmentIndex + 1];
13811
+ const targetOrientation = getRailOrientation(targetStart, targetEnd);
13812
+ if (!targetOrientation) continue;
13813
+ for (let donorSegmentIndex = 0; donorSegmentIndex < donorPath.length - 1; donorSegmentIndex++) {
13814
+ const donorStart = donorPath[donorSegmentIndex];
13815
+ const donorEnd = donorPath[donorSegmentIndex + 1];
13816
+ const donorOrientation = getRailOrientation(donorStart, donorEnd);
13817
+ if (!donorOrientation || donorOrientation === targetOrientation) continue;
13818
+ const intersectionPoint = getSegmentIntersection3(
13819
+ targetStart,
13820
+ targetEnd,
13821
+ donorStart,
13822
+ donorEnd
13823
+ );
13824
+ if (!intersectionPoint || pointsEqual2(intersectionPoint, sharedEndpoint)) {
13825
+ continue;
13826
+ }
13827
+ crossings.push({
13828
+ intersectionPoint,
13829
+ targetSegmentIndex,
13830
+ donorSegmentIndex
13831
+ });
13832
+ }
13833
+ }
13834
+ return crossings;
13835
+ };
13836
+ var buildCollapsedSelfCyclePath = (path, firstSegmentIndex, secondSegmentIndex) => {
13837
+ const intersectionPoint = getSegmentIntersection3(
13838
+ path[firstSegmentIndex],
13839
+ path[firstSegmentIndex + 1],
13840
+ path[secondSegmentIndex],
13841
+ path[secondSegmentIndex + 1]
13842
+ );
13843
+ if (!intersectionPoint) return path;
13844
+ return simplifyPath([
13845
+ ...path.slice(0, firstSegmentIndex + 1),
13846
+ intersectionPoint,
13847
+ ...path.slice(secondSegmentIndex + 1)
13848
+ ]);
13849
+ };
13850
+ var buildCollapsedCyclePath = ({
13851
+ targetTrace,
13852
+ targetPath,
13853
+ donorPath,
13854
+ crossing
13855
+ }) => {
13856
+ const pathFromSharedPin = simplifyPath([
13857
+ ...donorPath.slice(0, crossing.donorSegmentIndex + 1),
13858
+ crossing.intersectionPoint,
13859
+ ...targetPath.slice(crossing.targetSegmentIndex + 1)
13860
+ ]);
13861
+ if (pointsEqual2(targetTrace.tracePath[0], targetPath[0])) {
13862
+ return pathFromSharedPin;
13863
+ }
13864
+ return pathFromSharedPin.reverse();
13865
+ };
13866
+ var getNetLabelPlacementsForCycleCollapse = ({
13867
+ targetTrace,
13868
+ tracePath,
13869
+ netLabelPlacements
13870
+ }) => {
13871
+ const candidateNetLabelPlacements = moveAttachedLabelsToReroutedTrace({
13872
+ trace: targetTrace,
13873
+ originalTracePath: targetTrace.tracePath,
13874
+ reroutedTracePath: tracePath,
13875
+ netLabelPlacements
13876
+ });
13877
+ for (let labelIndex = 0; labelIndex < netLabelPlacements.length; labelIndex++) {
13878
+ const label = netLabelPlacements[labelIndex];
13879
+ if (label.globalConnNetId !== targetTrace.globalConnNetId) continue;
13880
+ if (!tracePathContainsPoint(targetTrace.tracePath, label.anchorPoint)) {
13881
+ continue;
13882
+ }
13883
+ const candidateLabel = candidateNetLabelPlacements[labelIndex];
13884
+ if (!tracePathContainsPoint(tracePath, candidateLabel.anchorPoint)) {
13885
+ return null;
13886
+ }
13887
+ }
13888
+ const otherNetLabelPlacements = candidateNetLabelPlacements.filter(
13889
+ (label) => label.globalConnNetId !== targetTrace.globalConnNetId
13890
+ );
13891
+ if (pathIntersectsAnyNetLabel({
13892
+ path: tracePath,
13893
+ netLabelPlacements: otherNetLabelPlacements
13894
+ })) {
13895
+ return null;
13896
+ }
13897
+ const sameNetLabelPlacements = candidateNetLabelPlacements.filter(
13898
+ (label) => label.globalConnNetId === targetTrace.globalConnNetId
13899
+ );
13900
+ if (pathEntersAnyNetLabel({
13901
+ path: tracePath,
13902
+ netLabelPlacements: sameNetLabelPlacements
13903
+ })) {
13904
+ return null;
13905
+ }
13906
+ return candidateNetLabelPlacements;
13907
+ };
13908
+ var candidateIsBetter = (candidate, bestCandidate) => {
13909
+ if (!bestCandidate) return true;
13910
+ const visibleLengthDelta = candidate.netVisibleLength - bestCandidate.netVisibleLength;
13911
+ if (Math.abs(visibleLengthDelta) > TRACE_LENGTH_EPSILON) {
13912
+ return visibleLengthDelta < 0;
13913
+ }
13914
+ return candidate.netVisibleSegmentCount < bestCandidate.netVisibleSegmentCount;
13915
+ };
13916
+ var getBestCycleCollapseCandidate = ({
13917
+ targetTrace,
13918
+ traces,
13919
+ netLabelPlacements
13920
+ }) => {
13921
+ const sameNetTraces = traces.filter(
13922
+ (trace) => trace.globalConnNetId === targetTrace.globalConnNetId
13923
+ );
13924
+ const baselineNetVisibleLength = getVisibleTraceLength(sameNetTraces);
13925
+ const baselineTargetVisibleLength = getVisibleTraceLength([targetTrace]);
13926
+ let bestCandidate = null;
13927
+ const considerTracePath = (tracePath) => {
13928
+ const candidateTrace = { ...targetTrace, tracePath };
13929
+ const candidateTargetVisibleLength = getVisibleTraceLength([candidateTrace]);
13930
+ if (candidateTargetVisibleLength > baselineTargetVisibleLength + TRACE_LENGTH_EPSILON) {
13931
+ return;
13932
+ }
13933
+ const candidateNetLabelPlacements = getNetLabelPlacementsForCycleCollapse({
13934
+ targetTrace,
13935
+ tracePath,
13936
+ netLabelPlacements
13937
+ });
13938
+ if (!candidateNetLabelPlacements) return;
13939
+ const candidateNetTraces = sameNetTraces.map((trace) => {
13940
+ if (trace.mspPairId === targetTrace.mspPairId) return candidateTrace;
13941
+ return trace;
13942
+ });
13943
+ const netVisibleLength = getVisibleTraceLength(candidateNetTraces);
13944
+ if (netVisibleLength >= baselineNetVisibleLength - TRACE_LENGTH_EPSILON) {
13945
+ return;
13946
+ }
13947
+ const candidate = {
13948
+ tracePath,
13949
+ netLabelPlacements: candidateNetLabelPlacements,
13950
+ netVisibleLength,
13951
+ netVisibleSegmentCount: getVisibleTraceSegmentCount(candidateNetTraces)
13952
+ };
13953
+ if (candidateIsBetter(candidate, bestCandidate)) {
13954
+ bestCandidate = candidate;
13955
+ }
13956
+ };
13957
+ const selfCrossings = findPerpendicularPathCrossings(
13958
+ targetTrace.tracePath,
13959
+ targetTrace.tracePath,
13960
+ { includeTerminalSegments: true }
13961
+ ).filter(
13962
+ ({ pathSegmentIndex, otherPathSegmentIndex }) => pathSegmentIndex < otherPathSegmentIndex
13963
+ );
13964
+ for (const crossing of selfCrossings) {
13965
+ considerTracePath(
13966
+ buildCollapsedSelfCyclePath(
13967
+ targetTrace.tracePath,
13968
+ crossing.pathSegmentIndex,
13969
+ crossing.otherPathSegmentIndex
13970
+ )
13971
+ );
13972
+ }
13973
+ for (const donorTrace of sameNetTraces) {
13974
+ if (donorTrace.mspPairId === targetTrace.mspPairId) continue;
13975
+ const sharedPin = getSharedPin({
13976
+ donorTrace,
13977
+ branchTrace: targetTrace
13978
+ });
13979
+ if (!sharedPin) continue;
13980
+ const targetPath = getTracePathStartingAtPin(targetTrace, sharedPin);
13981
+ const donorPath = getTracePathStartingAtPin(donorTrace, sharedPin);
13982
+ if (!targetPath || !donorPath) continue;
13983
+ const crossings = getPerpendicularPathCrossings(targetPath, donorPath);
13984
+ for (const crossing of crossings) {
13985
+ const tracePath = buildCollapsedCyclePath({
13986
+ targetTrace,
13987
+ targetPath,
13988
+ donorPath,
13989
+ crossing
13990
+ });
13991
+ considerTracePath(tracePath);
13992
+ }
13993
+ }
13994
+ return bestCandidate;
13995
+ };
13996
+ var collapseSameNetCycles = ({
13997
+ traces,
13998
+ netLabelPlacements
13999
+ }) => {
14000
+ const outputTraces = [...traces];
14001
+ let outputNetLabelPlacements = [...netLabelPlacements];
14002
+ let collapsedCycleCount = 0;
14003
+ let traceIndex = 0;
14004
+ while (traceIndex < outputTraces.length) {
14005
+ const targetTrace = outputTraces[traceIndex];
14006
+ const candidate = getBestCycleCollapseCandidate({
14007
+ targetTrace,
14008
+ traces: outputTraces,
14009
+ netLabelPlacements: outputNetLabelPlacements
14010
+ });
14011
+ if (!candidate) {
14012
+ traceIndex++;
14013
+ continue;
14014
+ }
14015
+ outputTraces[traceIndex] = {
14016
+ ...targetTrace,
14017
+ tracePath: candidate.tracePath
14018
+ };
14019
+ outputNetLabelPlacements = candidate.netLabelPlacements;
14020
+ collapsedCycleCount++;
14021
+ traceIndex = 0;
14022
+ }
14023
+ return {
14024
+ traces: outputTraces,
14025
+ netLabelPlacements: outputNetLabelPlacements,
14026
+ collapsedCycleCount
14027
+ };
14028
+ };
14029
+
13597
14030
  // lib/solvers/SameNetJunctionAlignmentSolver/alignSameNetJunctions.ts
13598
14031
  var MAX_ALIGNED_LOAD_PIN_OFFSET = 0.2;
13599
14032
  var MAX_SAME_NET_LABEL_BOUNDARY_RAIL_OFFSET = 0.2;
@@ -13978,7 +14411,7 @@ var pathsIntersect = (firstPath, secondPath) => {
13978
14411
  }
13979
14412
  for (let firstIndex = 1; firstIndex < firstPath.length; firstIndex++) {
13980
14413
  for (let secondIndex = 1; secondIndex < secondPath.length; secondIndex++) {
13981
- if (getSegmentIntersection3(
14414
+ if (getSegmentIntersection4(
13982
14415
  firstPath[firstIndex - 1],
13983
14416
  firstPath[firstIndex],
13984
14417
  secondPath[secondIndex - 1],
@@ -14162,7 +14595,7 @@ var candidateIsClear = ({
14162
14595
  const intersections2 = /* @__PURE__ */ new Set();
14163
14596
  for (let i = 1; i < path.length; i++) {
14164
14597
  for (let j = 1; j < otherTrace.tracePath.length; j++) {
14165
- const point = getSegmentIntersection3(
14598
+ const point = getSegmentIntersection4(
14166
14599
  path[i - 1],
14167
14600
  path[i],
14168
14601
  otherTrace.tracePath[j - 1],
@@ -14445,225 +14878,22 @@ var alignSameNetJunctions = ({
14445
14878
  }
14446
14879
  }
14447
14880
  }
14448
- const collapsedSharedEndpointStubs = collapseRedundantSharedEndpointStubs({
14881
+ const cycleCollapse = collapseSameNetCycles({
14449
14882
  traces: outputTraces,
14450
- netLabelPlacements: outputNetLabelPlacements,
14883
+ netLabelPlacements: outputNetLabelPlacements
14884
+ });
14885
+ outputTraces = collapseRedundantSharedEndpointStubs({
14886
+ traces: cycleCollapse.traces,
14887
+ netLabelPlacements: cycleCollapse.netLabelPlacements,
14451
14888
  netLabelConnectorTraceIds,
14452
14889
  multiPinNetPinIds: getMultiPinNetPinIds(inputProblem)
14453
14890
  });
14454
- outputTraces = collapsedSharedEndpointStubs;
14891
+ outputNetLabelPlacements = cycleCollapse.netLabelPlacements;
14455
14892
  return {
14456
14893
  traces: outputTraces,
14457
14894
  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
- }
14663
- return {
14664
- traces: outputTraces,
14665
- netLabelPlacements: outputNetLabelPlacements,
14666
- collapsedCycleCount
14895
+ alignedJunctionCount,
14896
+ collapsedCycleCount: cycleCollapse.collapsedCycleCount
14667
14897
  };
14668
14898
  };
14669
14899
 
@@ -14776,18 +15006,14 @@ var SameNetJunctionAlignmentSolver = class extends BaseSolver {
14776
15006
  }
14777
15007
  _step() {
14778
15008
  const alignment = alignSameNetJunctions(this.input);
14779
- const cycleCollapse = collapseSameNetCycles({
14780
- traces: alignment.traces,
14781
- netLabelPlacements: alignment.netLabelPlacements
14782
- });
14783
- this.outputTraces = cycleCollapse.traces;
15009
+ this.outputTraces = alignment.traces;
14784
15010
  this.outputNetLabelPlacements = placeGroundRailLabelsAtOuterEnd({
14785
15011
  inputProblem: this.input.inputProblem,
14786
- traces: cycleCollapse.traces,
14787
- netLabelPlacements: cycleCollapse.netLabelPlacements
15012
+ traces: alignment.traces,
15013
+ netLabelPlacements: alignment.netLabelPlacements
14788
15014
  });
14789
15015
  this.stats.alignedJunctionCount = alignment.alignedJunctionCount;
14790
- this.stats.collapsedCycleCount = cycleCollapse.collapsedCycleCount;
15016
+ this.stats.collapsedCycleCount = alignment.collapsedCycleCount;
14791
15017
  this.solved = true;
14792
15018
  }
14793
15019
  getOutput() {
@@ -15159,10 +15385,10 @@ var getInlineLabelObstacles = (inputProblem, placements) => {
15159
15385
  };
15160
15386
 
15161
15387
  // lib/solvers/InlineNetLabelSolver/getLocalTraceLabelShifts.ts
15162
- var EPS15 = 1e-6;
15388
+ var EPS16 = 1e-6;
15163
15389
  var CLEARANCE = 0.05;
15164
15390
  var hits = (path, bounds) => path.slice(1).some((p, i) => segmentIntersectsRect(path[i], p, bounds));
15165
- var same = (a, b) => Math.hypot(a.x - b.x, a.y - b.y) < EPS15;
15391
+ var same = (a, b) => Math.hypot(a.x - b.x, a.y - b.y) < EPS16;
15166
15392
  var translate = (p, axis, distance7) => ({
15167
15393
  ...p,
15168
15394
  [axis]: p[axis] + distance7
@@ -15209,9 +15435,9 @@ function* getLocalTraceLabelShifts(inputProblem, output, pendingInlinePlacements
15209
15435
  const path = simplifyPath(trace.tracePath);
15210
15436
  for (let i = 0; i < path.length - 1; i++) {
15211
15437
  const a = path[i], b = path[i + 1];
15212
- const axis = Math.abs(a.x - b.x) < EPS15 ? "x" : "y";
15438
+ const axis = Math.abs(a.x - b.x) < EPS16 ? "x" : "y";
15213
15439
  const along = axis === "x" ? "y" : "x";
15214
- if (Math.abs(a[axis] - b[axis]) > EPS15 || same(a, b)) continue;
15440
+ if (Math.abs(a[axis] - b[axis]) > EPS16 || same(a, b)) continue;
15215
15441
  const blocking = [
15216
15442
  ...output.netLabelPlacements.filter((label) => label.globalConnNetId !== trace.globalConnNetId).map(getAnchoredNetLabelRenderedBounds),
15217
15443
  ...inlineBounds,
@@ -15229,17 +15455,17 @@ function* getLocalTraceLabelShifts(inputProblem, output, pendingInlinePlacements
15229
15455
  if (other.globalConnNetId !== trace.globalConnNetId) continue;
15230
15456
  for (let j = 1; j < other.tracePath.length; j++) {
15231
15457
  const p = other.tracePath[j - 1], q = other.tracePath[j];
15232
- if (Math.abs(p[axis] - a[axis]) > EPS15 || Math.abs(q[axis] - a[axis]) > EPS15)
15458
+ if (Math.abs(p[axis] - a[axis]) > EPS16 || Math.abs(q[axis] - a[axis]) > EPS16)
15233
15459
  continue;
15234
15460
  const min = Math.min(p[along], q[along]), max = Math.max(p[along], q[along]);
15235
- if (max < low - EPS15 || min > high + EPS15) continue;
15461
+ if (max < low - EPS16 || min > high + EPS16) continue;
15236
15462
  if (min < low || max > high) expanded = true;
15237
15463
  low = Math.min(low, min);
15238
15464
  high = Math.max(high, max);
15239
15465
  }
15240
15466
  }
15241
15467
  }
15242
- const onRail = (p) => Math.abs(p[axis] - a[axis]) < EPS15 && p[along] >= low - EPS15 && p[along] <= high + EPS15;
15468
+ const onRail = (p) => Math.abs(p[axis] - a[axis]) < EPS16 && p[along] >= low - EPS16 && p[along] <= high + EPS16;
15243
15469
  if (pinPositions.some(onRail)) continue;
15244
15470
  const minKey = axis === "x" ? "minX" : "minY";
15245
15471
  const maxKey = axis === "x" ? "maxX" : "maxY";
@@ -15321,7 +15547,7 @@ function* getLocalTraceLabelShifts(inputProblem, output, pendingInlinePlacements
15321
15547
  continue;
15322
15548
  }
15323
15549
  const [start, bend, end] = connector.tracePath;
15324
- if (Math.abs(bend[axis] - end[axis]) > EPS15 || Math.abs(start[along] - bend[along]) > EPS15) {
15550
+ if (Math.abs(bend[axis] - end[axis]) > EPS16 || Math.abs(start[along] - bend[along]) > EPS16) {
15325
15551
  blocked = true;
15326
15552
  break;
15327
15553
  }
@@ -15342,24 +15568,40 @@ function* getLocalTraceLabelShifts(inputProblem, output, pendingInlinePlacements
15342
15568
  };
15343
15569
  }
15344
15570
  if (blocked) continue;
15571
+ for (const [index, other] of traces.entries()) {
15572
+ if (other.globalConnNetId === trace.globalConnNetId || simplifyPath(other.tracePath).length <= 4 || !shiftedPaths.some(
15573
+ (path2) => doesPathCoincideWithTraces(path2, [other])
15574
+ ))
15575
+ continue;
15576
+ const simplified = minimizeTurnsWithFilteredLabels({
15577
+ inputProblem,
15578
+ traces: [...traces, ...terminalTraces],
15579
+ targetMspConnectionPairId: other.mspPairId,
15580
+ allLabelPlacements: [...labels, ...fixedLabels],
15581
+ mergedLabelNetIdMap: {},
15582
+ paddingBuffer: CLEARANCE
15583
+ });
15584
+ if (simplifyPath(simplified.tracePath).length < simplifyPath(other.tracePath).length && preservesLabelAnchors(labels, [other], [simplified]))
15585
+ traces[index] = simplified;
15586
+ }
15345
15587
  for (const [index, proposed] of traces.entries()) {
15346
15588
  const original = output.traces[index];
15347
15589
  if (proposed === original) continue;
15348
15590
  const candidate = proposed.tracePath;
15349
15591
  const previous = original.tracePath;
15350
15592
  if (candidate.slice(1).some(
15351
- (p, index2) => Math.abs(p.x - candidate[index2].x) > EPS15 && Math.abs(p.y - candidate[index2].y) > EPS15
15593
+ (p, index2) => Math.abs(p.x - candidate[index2].x) > EPS16 && Math.abs(p.y - candidate[index2].y) > EPS16
15352
15594
  )) {
15353
15595
  blocked = true;
15354
15596
  break;
15355
15597
  }
15356
15598
  const dot = (a2, b2, c, d) => (b2.x - a2.x) * (d.x - c.x) + (b2.y - a2.y) * (d.y - c.y);
15357
- if (dot(previous[0], previous[1], candidate[0], candidate[1]) <= EPS15 || dot(
15599
+ if (dot(previous[0], previous[1], candidate[0], candidate[1]) <= EPS16 || dot(
15358
15600
  previous.at(-1),
15359
15601
  previous.at(-2),
15360
15602
  candidate.at(-1),
15361
15603
  candidate.at(-2)
15362
- ) <= EPS15) {
15604
+ ) <= EPS16) {
15363
15605
  blocked = true;
15364
15606
  break;
15365
15607
  }
@@ -16315,15 +16557,15 @@ var pathIntersectsRenderedLabel = (path, label) => {
16315
16557
  };
16316
16558
 
16317
16559
  // lib/solvers/InlineNetLabelSolver/restoreReroutesAroundSupersededLabels.ts
16318
- var EPS16 = 1e-6;
16560
+ var EPS17 = 1e-6;
16319
16561
  var getPathLength7 = (path) => path.slice(1).reduce((length, point, pointIndex) => {
16320
16562
  const previousPoint = path[pointIndex];
16321
16563
  return length + Math.abs(point.x - previousPoint.x) + Math.abs(point.y - previousPoint.y);
16322
16564
  }, 0);
16323
16565
  var pathsEqual = (first, second) => first.length === second.length && first.every(
16324
- (point, index) => Math.abs(point.x - second[index].x) <= EPS16 && Math.abs(point.y - second[index].y) <= EPS16
16566
+ (point, index) => Math.abs(point.x - second[index].x) <= EPS17 && Math.abs(point.y - second[index].y) <= EPS17
16325
16567
  );
16326
- var isStrictlySimpler = (candidate, current) => candidate.length < current.length && getPathLength7(candidate) <= getPathLength7(current) + EPS16;
16568
+ var isStrictlySimpler = (candidate, current) => candidate.length < current.length && getPathLength7(candidate) <= getPathLength7(current) + EPS17;
16327
16569
  var getIntersectionKeys = (path, otherPath) => {
16328
16570
  const intersections2 = /* @__PURE__ */ new Set();
16329
16571
  for (let pathIndex = 0; pathIndex < path.length - 1; pathIndex++) {
@@ -17623,12 +17865,12 @@ var getTraceRecoveryConnectivityMaps = (inputProblem) => {
17623
17865
  };
17624
17866
 
17625
17867
  // lib/solvers/NetLabelToTraceSolver/reduceTraceCrossings.ts
17626
- var EPS17 = 1e-9;
17627
- var isHorizontal4 = (start, end) => Math.abs(start.y - end.y) < EPS17;
17628
- var isVertical5 = (start, end) => Math.abs(start.x - end.x) < EPS17;
17868
+ var EPS18 = 1e-9;
17869
+ var isHorizontal4 = (start, end) => Math.abs(start.y - end.y) < EPS18;
17870
+ var isVertical5 = (start, end) => Math.abs(start.x - end.x) < EPS18;
17629
17871
  var compactConsecutivePoints = (path) => path.filter((point, index) => {
17630
17872
  const previousPoint = path[index - 1];
17631
- return !previousPoint || Math.abs(point.x - previousPoint.x) >= EPS17 || Math.abs(point.y - previousPoint.y) >= EPS17;
17873
+ return !previousPoint || Math.abs(point.x - previousPoint.x) >= EPS18 || Math.abs(point.y - previousPoint.y) >= EPS18;
17632
17874
  });
17633
17875
  var isValidOrthogonalPath = (path) => {
17634
17876
  if (path.length < 2 || !path.every((point, index) => {
@@ -17666,7 +17908,7 @@ var getEndpointAlignedSegmentCandidates = (tracePath) => {
17666
17908
  }
17667
17909
  const alignedCoordinates = segmentIsVertical ? [previousPoint.x, nextPoint.x] : [previousPoint.y, nextPoint.y];
17668
17910
  for (const alignedCoordinate of alignedCoordinates) {
17669
- if (Math.abs(alignedCoordinate - (segmentIsVertical ? start.x : start.y)) < EPS17) {
17911
+ if (Math.abs(alignedCoordinate - (segmentIsVertical ? start.x : start.y)) < EPS18) {
17670
17912
  continue;
17671
17913
  }
17672
17914
  const candidate = tracePath.map((point) => ({ ...point }));
@@ -17755,6 +17997,13 @@ var NetLabelToTraceSolver = class extends BaseSolver {
17755
17997
  );
17756
17998
  this.chipMap = chipMap;
17757
17999
  this.pinMap = pinMap;
18000
+ this.canRecoverParallelPair = getParallelNetLabelPolicy(
18001
+ this.inputProblem,
18002
+ getConnectivityMapsFromInputProblem(this.inputProblem).netConnMap,
18003
+ new Set(
18004
+ input.traces.filter((trace) => trace.pinIds.length > 1).flatMap((trace) => trace.pinIds)
18005
+ )
18006
+ );
17758
18007
  this.queuedCandidates = this.buildCandidatePairs();
17759
18008
  this.stats.candidateCount = this.queuedCandidates.length;
17760
18009
  this.stats.recoveredTraceCount = 0;
@@ -17766,6 +18015,7 @@ var NetLabelToTraceSolver = class extends BaseSolver {
17766
18015
  recoveredTraceIds = /* @__PURE__ */ new Set();
17767
18016
  chipMap;
17768
18017
  pinMap;
18018
+ canRecoverParallelPair;
17769
18019
  queuedCandidates;
17770
18020
  currentCandidate = null;
17771
18021
  getConstructorParams() {
@@ -17816,6 +18066,7 @@ var NetLabelToTraceSolver = class extends BaseSolver {
17816
18066
  const firstPin = this.pinMap.get(firstLabel.pinIds[0]);
17817
18067
  const secondPin = this.pinMap.get(secondLabel.pinIds[0]);
17818
18068
  if (!firstPin || !secondPin) continue;
18069
+ if (!this.canRecoverParallelPair(firstPin, secondPin)) continue;
17819
18070
  const perpendicularOffset = getPerpendicularOffset(
17820
18071
  firstPin,
17821
18072
  secondPin
@@ -17934,6 +18185,7 @@ var NetLabelToTraceSolver = class extends BaseSolver {
17934
18185
  const firstPin = this.pinMap.get(firstPinId);
17935
18186
  const secondPin = this.pinMap.get(secondPinId);
17936
18187
  if (!firstPin || !secondPin) continue;
18188
+ if (!this.canRecoverParallelPair(firstPin, secondPin)) continue;
17937
18189
  if (isDirectConnection && firstPin.chipId === secondPin.chipId)
17938
18190
  continue;
17939
18191
  const perpendicularOffset = getPerpendicularOffset(