@tscircuit/schematic-trace-solver 0.0.190 → 0.0.192

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 (58) hide show
  1. package/dist/index.d.ts +2 -0
  2. package/dist/index.js +437 -211
  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/collapseRedundantSharedEndpointStubs.ts +12 -6
  10. package/package.json +1 -1
  11. package/tests/bug-reports/bug-report-20260706T213649Z/__snapshots__/bug-report-20260706T213649Z.snap.svg +837 -799
  12. package/tests/bug-reports/bug-report-20260706T220324Z/__snapshots__/bug-report-20260706T220324Z.snap.svg +904 -866
  13. package/tests/bug-reports/bug-report-20260707T092615Z/__snapshots__/bug-report-20260707T092615Z.snap.svg +533 -524
  14. package/tests/bug-reports/bug-report-20260721T221026Z/__snapshots__/bug-report-20260721T221026Z.snap.svg +2 -2
  15. package/tests/bug-reports/bug-report-20260724T175257Z/__snapshots__/bug-report-20260724T175257Z.snap.svg +2 -2
  16. package/tests/bug-reports/bug-report-20260728T144234Z/__snapshots__/bug-report-20260728T144234Z.snap.svg +2 -2
  17. package/tests/bug-reports/bug-report-20260804T171919Z/__snapshots__/bug-report-20260804T171919Z.snap.svg +2 -2
  18. package/tests/bug-reports/bug-report-20260806T093501Z/__snapshots__/bug-report-20260806T093501Z.snap.svg +94 -94
  19. package/tests/bug-reports/bug-report-20260815T073240Z/__snapshots__/bug-report-20260815T073240Z.snap.svg +2 -2
  20. package/tests/bug-reports/bug-report-20260901T055358Z/__snapshots__/bug-report-20260901T055358Z.snap.svg +2 -2
  21. package/tests/bug-reports/bug-report-20260901T064117Z/bug-report-20260901T064117Z.test.ts +13 -2
  22. package/tests/bug-reports/bug-report-20260902T092425Z/__snapshots__/bug-report-20260902T092425Z.snap.svg +2 -2
  23. package/tests/bug-reports/bug-report-20260902T092425Z/bug-report-20260902T092425Z.test.ts +16 -2
  24. package/tests/bug-reports/bug-report-20260905T041712Z/__snapshots__/bug-report-20260905T041712Z.snap.svg +2 -2
  25. package/tests/bug-reports/bug-report-20260907T083336Z/__snapshots__/bug-report-20260907T083336Z.snap.svg +2 -2
  26. package/tests/bug-reports/bug-report-20260907T110144Z/__snapshots__/bug-report-20260907T110144Z.snap.svg +19 -58
  27. package/tests/bug-reports/bug-report-20260907T110144Z/bug-report-20260907T110144Z.test.ts +2 -2
  28. package/tests/bug-reports/bug-report-20260907T145640Z/__snapshots__/bug-report-20260907T145640Z.snap.svg +917 -933
  29. package/tests/examples/__snapshots__/example13.snap.svg +2 -2
  30. package/tests/examples/__snapshots__/example15.snap.svg +2 -2
  31. package/tests/examples/__snapshots__/example51.snap.svg +2 -2
  32. package/tests/repros/__snapshots__/board-1273-trace-overlap-cycle.snap.svg +2 -2
  33. package/tests/repros/__snapshots__/board-15984-same-net-junction.snap.svg +2 -2
  34. package/tests/repros/__snapshots__/repro-bq25895-cross-net-trace-overlap.snap.svg +2 -2
  35. package/tests/repros/__snapshots__/repro-cc2340r5.snap.svg +2 -2
  36. package/tests/repros/__snapshots__/repro-esp12f-section.snap.svg +2 -2
  37. package/tests/repros/__snapshots__/repro-example35-minimize-trace-crossing.snap.svg +2 -2
  38. package/tests/repros/__snapshots__/repro-hub-usb-sheet.snap.svg +2 -2
  39. package/tests/repros/__snapshots__/repro-isolated-rs485-isow7841.snap.svg +2 -2
  40. package/tests/repros/__snapshots__/repro-pga300-redundant-ground-traces.snap.svg +2 -2
  41. package/tests/repros/__snapshots__/repro-pmp11282-isolated-dcdc.snap.svg +2 -2
  42. package/tests/repros/__snapshots__/repro-powerbank-3v-system-power.snap.svg +2 -2
  43. package/tests/repros/__snapshots__/repro-repeated-power-rail-junctions.snap.svg +2 -2
  44. package/tests/repros/__snapshots__/repro-rp2040-gamepad-trace-alignment.snap.svg +2 -2
  45. package/tests/repros/__snapshots__/repro-smartwatch-power-sheet.snap.svg +111 -75
  46. package/tests/repros/__snapshots__/repro-tida00553-j4-cell-divider.snap.svg +2 -2
  47. package/tests/repros/__snapshots__/repro-tida010076-page02.snap.svg +2 -2
  48. package/tests/repros/__snapshots__/repro-type-c-vbus-led-ground-label.snap.svg +2 -2
  49. package/tests/repros/__snapshots__/repro-usb-power-vbus-label-detour.snap.svg +2 -2
  50. package/tests/repros/__snapshots__/repro130-bq27441-fuel-gauge-trace-through-c1.snap.svg +2 -2
  51. package/tests/repros/__snapshots__/repro161-power-section-autolayout.snap.svg +2 -2
  52. package/tests/repros/repro-trellis-core-decoupling-snake-traces.test.ts +66 -0
  53. package/tests/solvers/InlineNetLabelSolver/local-trace-label-shifts.test.ts +62 -0
  54. package/tests/solvers/LongDistancePairSolver/__snapshots__/decoupling-mixed-supplies.snap.svg +112 -0
  55. package/tests/solvers/LongDistancePairSolver/__snapshots__/horizontal-rails.snap.svg +79 -0
  56. package/tests/solvers/LongDistancePairSolver/__snapshots__/vertical-rails.snap.svg +86 -0
  57. package/tests/solvers/LongDistancePairSolver/parallel-decoupling-labels.test.ts +505 -0
  58. package/tests/solvers/SameNetJunctionAlignmentSolver/collapse-redundant-shared-endpoint-stubs.test.ts +45 -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";
@@ -13416,8 +13613,10 @@ var getPathFromSharedPin = ({
13416
13613
  sharedPinId
13417
13614
  }) => {
13418
13615
  const tracePath = simplifyPath(trace.tracePath);
13419
- if (trace.pins[0]?.pinId === sharedPinId) return tracePath;
13420
- return tracePath.reverse();
13616
+ const sharedPin = trace.pins.find((pin) => pin.pinId === sharedPinId);
13617
+ if (!sharedPin) return null;
13618
+ const pathFromSharedPin = trace.pins[0]?.pinId === sharedPinId ? tracePath : tracePath.reverse();
13619
+ return pointsEqual2(pathFromSharedPin[0], sharedPin) ? pathFromSharedPin : null;
13421
13620
  };
13422
13621
  var traceCanLoseSharedPrefix = ({
13423
13622
  trace,
@@ -13458,9 +13657,10 @@ var collapseRedundantSharedEndpointStubs = ({
13458
13657
  }) => {
13459
13658
  const outputTraces = [...traces];
13460
13659
  for (let firstIndex = 0; firstIndex < outputTraces.length; firstIndex++) {
13461
- const firstTrace = outputTraces[firstIndex];
13462
- if (netLabelConnectorTraceIds.has(firstTrace.mspPairId)) continue;
13660
+ if (netLabelConnectorTraceIds.has(outputTraces[firstIndex].mspPairId))
13661
+ continue;
13463
13662
  for (let secondIndex = firstIndex + 1; secondIndex < outputTraces.length; secondIndex++) {
13663
+ const firstTrace = outputTraces[firstIndex];
13464
13664
  const secondTrace = outputTraces[secondIndex];
13465
13665
  if (netLabelConnectorTraceIds.has(secondTrace.mspPairId) || firstTrace.globalConnNetId !== secondTrace.globalConnNetId) {
13466
13666
  continue;
@@ -13478,6 +13678,7 @@ var collapseRedundantSharedEndpointStubs = ({
13478
13678
  trace: secondTrace,
13479
13679
  sharedPinId: sharedPin.pinId
13480
13680
  });
13681
+ if (!firstPath || !secondPath) continue;
13481
13682
  if (firstPath.length < 2 || secondPath.length < 2) continue;
13482
13683
  const departureAxis = getSegmentAxis3(firstPath[0], firstPath[1]);
13483
13684
  const secondDepartureAxis = getSegmentAxis3(secondPath[0], secondPath[1]);
@@ -13524,7 +13725,7 @@ var collapseRedundantSharedEndpointStubs = ({
13524
13725
  });
13525
13726
  if (!collapsedTrace2) continue;
13526
13727
  outputTraces[traceIndexToCollapse2] = collapsedTrace2;
13527
- break;
13728
+ continue;
13528
13729
  }
13529
13730
  if (firstPath.length < 3 || secondPath.length < 3) continue;
13530
13731
  if (!pointsEqual2(firstPath[1], secondPath[1])) continue;
@@ -13588,7 +13789,6 @@ var collapseRedundantSharedEndpointStubs = ({
13588
13789
  });
13589
13790
  if (!collapsedTrace) continue;
13590
13791
  outputTraces[traceIndexToCollapse] = collapsedTrace;
13591
- break;
13592
13792
  }
13593
13793
  }
13594
13794
  return outputTraces;
@@ -15188,10 +15388,10 @@ var getInlineLabelObstacles = (inputProblem, placements) => {
15188
15388
  };
15189
15389
 
15190
15390
  // lib/solvers/InlineNetLabelSolver/getLocalTraceLabelShifts.ts
15191
- var EPS15 = 1e-6;
15391
+ var EPS16 = 1e-6;
15192
15392
  var CLEARANCE = 0.05;
15193
15393
  var hits = (path, bounds) => path.slice(1).some((p, i) => segmentIntersectsRect(path[i], p, bounds));
15194
- var same = (a, b) => Math.hypot(a.x - b.x, a.y - b.y) < EPS15;
15394
+ var same = (a, b) => Math.hypot(a.x - b.x, a.y - b.y) < EPS16;
15195
15395
  var translate = (p, axis, distance7) => ({
15196
15396
  ...p,
15197
15397
  [axis]: p[axis] + distance7
@@ -15238,9 +15438,9 @@ function* getLocalTraceLabelShifts(inputProblem, output, pendingInlinePlacements
15238
15438
  const path = simplifyPath(trace.tracePath);
15239
15439
  for (let i = 0; i < path.length - 1; i++) {
15240
15440
  const a = path[i], b = path[i + 1];
15241
- const axis = Math.abs(a.x - b.x) < EPS15 ? "x" : "y";
15441
+ const axis = Math.abs(a.x - b.x) < EPS16 ? "x" : "y";
15242
15442
  const along = axis === "x" ? "y" : "x";
15243
- if (Math.abs(a[axis] - b[axis]) > EPS15 || same(a, b)) continue;
15443
+ if (Math.abs(a[axis] - b[axis]) > EPS16 || same(a, b)) continue;
15244
15444
  const blocking = [
15245
15445
  ...output.netLabelPlacements.filter((label) => label.globalConnNetId !== trace.globalConnNetId).map(getAnchoredNetLabelRenderedBounds),
15246
15446
  ...inlineBounds,
@@ -15258,17 +15458,17 @@ function* getLocalTraceLabelShifts(inputProblem, output, pendingInlinePlacements
15258
15458
  if (other.globalConnNetId !== trace.globalConnNetId) continue;
15259
15459
  for (let j = 1; j < other.tracePath.length; j++) {
15260
15460
  const p = other.tracePath[j - 1], q = other.tracePath[j];
15261
- if (Math.abs(p[axis] - a[axis]) > EPS15 || Math.abs(q[axis] - a[axis]) > EPS15)
15461
+ if (Math.abs(p[axis] - a[axis]) > EPS16 || Math.abs(q[axis] - a[axis]) > EPS16)
15262
15462
  continue;
15263
15463
  const min = Math.min(p[along], q[along]), max = Math.max(p[along], q[along]);
15264
- if (max < low - EPS15 || min > high + EPS15) continue;
15464
+ if (max < low - EPS16 || min > high + EPS16) continue;
15265
15465
  if (min < low || max > high) expanded = true;
15266
15466
  low = Math.min(low, min);
15267
15467
  high = Math.max(high, max);
15268
15468
  }
15269
15469
  }
15270
15470
  }
15271
- const onRail = (p) => Math.abs(p[axis] - a[axis]) < EPS15 && p[along] >= low - EPS15 && p[along] <= high + EPS15;
15471
+ const onRail = (p) => Math.abs(p[axis] - a[axis]) < EPS16 && p[along] >= low - EPS16 && p[along] <= high + EPS16;
15272
15472
  if (pinPositions.some(onRail)) continue;
15273
15473
  const minKey = axis === "x" ? "minX" : "minY";
15274
15474
  const maxKey = axis === "x" ? "maxX" : "maxY";
@@ -15350,7 +15550,7 @@ function* getLocalTraceLabelShifts(inputProblem, output, pendingInlinePlacements
15350
15550
  continue;
15351
15551
  }
15352
15552
  const [start, bend, end] = connector.tracePath;
15353
- if (Math.abs(bend[axis] - end[axis]) > EPS15 || Math.abs(start[along] - bend[along]) > EPS15) {
15553
+ if (Math.abs(bend[axis] - end[axis]) > EPS16 || Math.abs(start[along] - bend[along]) > EPS16) {
15354
15554
  blocked = true;
15355
15555
  break;
15356
15556
  }
@@ -15371,24 +15571,40 @@ function* getLocalTraceLabelShifts(inputProblem, output, pendingInlinePlacements
15371
15571
  };
15372
15572
  }
15373
15573
  if (blocked) continue;
15574
+ for (const [index, other] of traces.entries()) {
15575
+ if (other.globalConnNetId === trace.globalConnNetId || simplifyPath(other.tracePath).length <= 4 || !shiftedPaths.some(
15576
+ (path2) => doesPathCoincideWithTraces(path2, [other])
15577
+ ))
15578
+ continue;
15579
+ const simplified = minimizeTurnsWithFilteredLabels({
15580
+ inputProblem,
15581
+ traces: [...traces, ...terminalTraces],
15582
+ targetMspConnectionPairId: other.mspPairId,
15583
+ allLabelPlacements: [...labels, ...fixedLabels],
15584
+ mergedLabelNetIdMap: {},
15585
+ paddingBuffer: CLEARANCE
15586
+ });
15587
+ if (simplifyPath(simplified.tracePath).length < simplifyPath(other.tracePath).length && preservesLabelAnchors(labels, [other], [simplified]))
15588
+ traces[index] = simplified;
15589
+ }
15374
15590
  for (const [index, proposed] of traces.entries()) {
15375
15591
  const original = output.traces[index];
15376
15592
  if (proposed === original) continue;
15377
15593
  const candidate = proposed.tracePath;
15378
15594
  const previous = original.tracePath;
15379
15595
  if (candidate.slice(1).some(
15380
- (p, index2) => Math.abs(p.x - candidate[index2].x) > EPS15 && Math.abs(p.y - candidate[index2].y) > EPS15
15596
+ (p, index2) => Math.abs(p.x - candidate[index2].x) > EPS16 && Math.abs(p.y - candidate[index2].y) > EPS16
15381
15597
  )) {
15382
15598
  blocked = true;
15383
15599
  break;
15384
15600
  }
15385
15601
  const dot = (a2, b2, c, d) => (b2.x - a2.x) * (d.x - c.x) + (b2.y - a2.y) * (d.y - c.y);
15386
- if (dot(previous[0], previous[1], candidate[0], candidate[1]) <= EPS15 || dot(
15602
+ if (dot(previous[0], previous[1], candidate[0], candidate[1]) <= EPS16 || dot(
15387
15603
  previous.at(-1),
15388
15604
  previous.at(-2),
15389
15605
  candidate.at(-1),
15390
15606
  candidate.at(-2)
15391
- ) <= EPS15) {
15607
+ ) <= EPS16) {
15392
15608
  blocked = true;
15393
15609
  break;
15394
15610
  }
@@ -16344,15 +16560,15 @@ var pathIntersectsRenderedLabel = (path, label) => {
16344
16560
  };
16345
16561
 
16346
16562
  // lib/solvers/InlineNetLabelSolver/restoreReroutesAroundSupersededLabels.ts
16347
- var EPS16 = 1e-6;
16563
+ var EPS17 = 1e-6;
16348
16564
  var getPathLength7 = (path) => path.slice(1).reduce((length, point, pointIndex) => {
16349
16565
  const previousPoint = path[pointIndex];
16350
16566
  return length + Math.abs(point.x - previousPoint.x) + Math.abs(point.y - previousPoint.y);
16351
16567
  }, 0);
16352
16568
  var pathsEqual = (first, second) => first.length === second.length && first.every(
16353
- (point, index) => Math.abs(point.x - second[index].x) <= EPS16 && Math.abs(point.y - second[index].y) <= EPS16
16569
+ (point, index) => Math.abs(point.x - second[index].x) <= EPS17 && Math.abs(point.y - second[index].y) <= EPS17
16354
16570
  );
16355
- var isStrictlySimpler = (candidate, current) => candidate.length < current.length && getPathLength7(candidate) <= getPathLength7(current) + EPS16;
16571
+ var isStrictlySimpler = (candidate, current) => candidate.length < current.length && getPathLength7(candidate) <= getPathLength7(current) + EPS17;
16356
16572
  var getIntersectionKeys = (path, otherPath) => {
16357
16573
  const intersections2 = /* @__PURE__ */ new Set();
16358
16574
  for (let pathIndex = 0; pathIndex < path.length - 1; pathIndex++) {
@@ -17652,12 +17868,12 @@ var getTraceRecoveryConnectivityMaps = (inputProblem) => {
17652
17868
  };
17653
17869
 
17654
17870
  // lib/solvers/NetLabelToTraceSolver/reduceTraceCrossings.ts
17655
- var EPS17 = 1e-9;
17656
- var isHorizontal4 = (start, end) => Math.abs(start.y - end.y) < EPS17;
17657
- var isVertical5 = (start, end) => Math.abs(start.x - end.x) < EPS17;
17871
+ var EPS18 = 1e-9;
17872
+ var isHorizontal4 = (start, end) => Math.abs(start.y - end.y) < EPS18;
17873
+ var isVertical5 = (start, end) => Math.abs(start.x - end.x) < EPS18;
17658
17874
  var compactConsecutivePoints = (path) => path.filter((point, index) => {
17659
17875
  const previousPoint = path[index - 1];
17660
- return !previousPoint || Math.abs(point.x - previousPoint.x) >= EPS17 || Math.abs(point.y - previousPoint.y) >= EPS17;
17876
+ return !previousPoint || Math.abs(point.x - previousPoint.x) >= EPS18 || Math.abs(point.y - previousPoint.y) >= EPS18;
17661
17877
  });
17662
17878
  var isValidOrthogonalPath = (path) => {
17663
17879
  if (path.length < 2 || !path.every((point, index) => {
@@ -17695,7 +17911,7 @@ var getEndpointAlignedSegmentCandidates = (tracePath) => {
17695
17911
  }
17696
17912
  const alignedCoordinates = segmentIsVertical ? [previousPoint.x, nextPoint.x] : [previousPoint.y, nextPoint.y];
17697
17913
  for (const alignedCoordinate of alignedCoordinates) {
17698
- if (Math.abs(alignedCoordinate - (segmentIsVertical ? start.x : start.y)) < EPS17) {
17914
+ if (Math.abs(alignedCoordinate - (segmentIsVertical ? start.x : start.y)) < EPS18) {
17699
17915
  continue;
17700
17916
  }
17701
17917
  const candidate = tracePath.map((point) => ({ ...point }));
@@ -17784,6 +18000,13 @@ var NetLabelToTraceSolver = class extends BaseSolver {
17784
18000
  );
17785
18001
  this.chipMap = chipMap;
17786
18002
  this.pinMap = pinMap;
18003
+ this.canRecoverParallelPair = getParallelNetLabelPolicy(
18004
+ this.inputProblem,
18005
+ getConnectivityMapsFromInputProblem(this.inputProblem).netConnMap,
18006
+ new Set(
18007
+ input.traces.filter((trace) => trace.pinIds.length > 1).flatMap((trace) => trace.pinIds)
18008
+ )
18009
+ );
17787
18010
  this.queuedCandidates = this.buildCandidatePairs();
17788
18011
  this.stats.candidateCount = this.queuedCandidates.length;
17789
18012
  this.stats.recoveredTraceCount = 0;
@@ -17795,6 +18018,7 @@ var NetLabelToTraceSolver = class extends BaseSolver {
17795
18018
  recoveredTraceIds = /* @__PURE__ */ new Set();
17796
18019
  chipMap;
17797
18020
  pinMap;
18021
+ canRecoverParallelPair;
17798
18022
  queuedCandidates;
17799
18023
  currentCandidate = null;
17800
18024
  getConstructorParams() {
@@ -17845,6 +18069,7 @@ var NetLabelToTraceSolver = class extends BaseSolver {
17845
18069
  const firstPin = this.pinMap.get(firstLabel.pinIds[0]);
17846
18070
  const secondPin = this.pinMap.get(secondLabel.pinIds[0]);
17847
18071
  if (!firstPin || !secondPin) continue;
18072
+ if (!this.canRecoverParallelPair(firstPin, secondPin)) continue;
17848
18073
  const perpendicularOffset = getPerpendicularOffset(
17849
18074
  firstPin,
17850
18075
  secondPin
@@ -17963,6 +18188,7 @@ var NetLabelToTraceSolver = class extends BaseSolver {
17963
18188
  const firstPin = this.pinMap.get(firstPinId);
17964
18189
  const secondPin = this.pinMap.get(secondPinId);
17965
18190
  if (!firstPin || !secondPin) continue;
18191
+ if (!this.canRecoverParallelPair(firstPin, secondPin)) continue;
17966
18192
  if (isDirectConnection && firstPin.chipId === secondPin.chipId)
17967
18193
  continue;
17968
18194
  const perpendicularOffset = getPerpendicularOffset(