@tscircuit/schematic-trace-solver 0.0.106 → 0.0.108

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 (21) hide show
  1. package/dist/index.d.ts +2 -2
  2. package/dist/index.js +470 -213
  3. package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2.ts +58 -27
  4. package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/generateEndpointCollisionDetours.ts +1 -10
  5. package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/generateInternalSegmentCollisionDetours.ts +94 -0
  6. package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/pathOps.ts +10 -0
  7. package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/rect.ts +5 -1
  8. package/lib/solvers/TraceCleanupSolver/sub-solver/UntangleTraceSubsolver.ts +178 -9
  9. package/lib/solvers/TraceCleanupSolver/sub-solver/findIntersectionsWithObstacles.ts +59 -0
  10. package/lib/solvers/TraceCleanupSolver/sub-solver/generateLShapeRerouteCandidates.ts +64 -1
  11. package/package.json +1 -1
  12. package/tests/bug-reports/bug-report-20260707T140410Z/__snapshots__/bug-report-20260707T140410Z.snap.svg +3 -3
  13. package/tests/bug-reports/bug-report-20260708T095725Z/__snapshots__/bug-report-20260708T095725Z.snap.svg +5 -5
  14. package/tests/bug-reports/bug-report-20260708T095725Z/bug-report-20260708T095725Z.test.ts +15 -0
  15. package/tests/bug-reports/bug-report-20260721T221026Z/__snapshots__/bug-report-20260721T221026Z.snap.svg +1 -1
  16. package/tests/repros/__snapshots__/repro-tps61222-trace-intersection.snap.svg +64 -0
  17. package/tests/repros/__snapshots__/repro47-endpoint-obstacle-detour.snap.svg +1 -1
  18. package/tests/repros/assets/repro-tps61222-trace-intersection.input.json +200 -0
  19. package/tests/repros/repro-tps61222-trace-intersection.test.ts +41 -0
  20. package/tests/repros/repro47-endpoint-obstacle-detour.test.ts +6 -0
  21. package/tests/solvers/SchematicTraceSingleLineSolver2/generate-internal-segment-collision-detours.test.ts +49 -0
package/dist/index.js CHANGED
@@ -325,19 +325,19 @@ var doesPairCrossRestrictedCenterLines = (params) => {
325
325
  chipMap
326
326
  });
327
327
  if (restrictedCenterLines.size === 0) return false;
328
- const EPS11 = 1e-9;
328
+ const EPS13 = 1e-9;
329
329
  const crossesSegment = (a, b) => {
330
330
  for (const [, rcl] of restrictedCenterLines) {
331
331
  if (rcl.axes.has("x") && typeof rcl.x === "number") {
332
- if ((a.x - rcl.x) * (b.x - rcl.x) < -EPS11) return true;
332
+ if ((a.x - rcl.x) * (b.x - rcl.x) < -EPS13) return true;
333
333
  }
334
334
  if (rcl.axes.has("y") && typeof rcl.y === "number") {
335
- if ((a.y - rcl.y) * (b.y - rcl.y) < -EPS11) return true;
335
+ if ((a.y - rcl.y) * (b.y - rcl.y) < -EPS13) return true;
336
336
  }
337
337
  }
338
338
  return false;
339
339
  };
340
- if (Math.abs(p1.x - p2.x) < EPS11 || Math.abs(p1.y - p2.y) < EPS11) {
340
+ if (Math.abs(p1.x - p2.x) < EPS13 || Math.abs(p1.y - p2.y) < EPS13) {
341
341
  return crossesSegment({ x: p1.x, y: p1.y }, { x: p2.x, y: p2.y });
342
342
  }
343
343
  const elbowHV = { x: p2.x, y: p1.y };
@@ -630,6 +630,25 @@ function getRectBounds(center, w, h) {
630
630
  };
631
631
  }
632
632
 
633
+ // lib/utils/textBoxBounds.ts
634
+ function getTextBoxBounds(textBox, padding = {}) {
635
+ return {
636
+ minX: textBox.center.x - textBox.width / 2 - (padding.minX ?? 0),
637
+ minY: textBox.center.y - textBox.height / 2 - (padding.minY ?? 0),
638
+ maxX: textBox.center.x + textBox.width / 2 + (padding.maxX ?? 0),
639
+ maxY: textBox.center.y + textBox.height / 2 + (padding.maxY ?? 0)
640
+ };
641
+ }
642
+ function boundsOverlap(a, b, eps = 1e-9) {
643
+ return a.minX < b.maxX - eps && a.maxX > b.minX + eps && a.minY < b.maxY - eps && a.maxY > b.minY + eps;
644
+ }
645
+ function rectIntersectsAnyTextBox(bounds, inputProblem) {
646
+ for (const textBox of inputProblem.textBoxes ?? []) {
647
+ if (boundsOverlap(bounds, getTextBoxBounds(textBox))) return true;
648
+ }
649
+ return false;
650
+ }
651
+
633
652
  // lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/calculateDirectShortPath.ts
634
653
  import { calculateElbow } from "calculate-elbow";
635
654
  var MAX_SHORT_TRACE_DISTANCE = 0.15;
@@ -875,12 +894,8 @@ var candidateMidsFromSet = (axis, colliding, collisionRects, aabb, opts = {}) =>
875
894
  }
876
895
  };
877
896
 
878
- // lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/generateEndpointCollisionDetours.ts
879
- var getSegmentAxis = (start, end) => {
880
- if (isVertical(start, end)) return "x";
881
- if (isHorizontal(start, end)) return "y";
882
- return null;
883
- };
897
+ // lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/pathOps.ts
898
+ var EPS3 = 1e-9;
884
899
  var hasOnlyNonzeroOrthogonalSegments = (path) => path.every((point, index) => {
885
900
  const nextPoint = path[index + 1];
886
901
  if (!nextPoint) return true;
@@ -889,57 +904,6 @@ var hasOnlyNonzeroOrthogonalSegments = (path) => path.every((point, index) => {
889
904
  }
890
905
  return Math.abs(point.x - nextPoint.x) + Math.abs(point.y - nextPoint.y) > 0;
891
906
  });
892
- var generateEndpointCollisionDetours = ({
893
- path,
894
- collidingSegmentIndex,
895
- obstacle
896
- }) => {
897
- if (path.length < 3 || path.length > 4) return [];
898
- const lastSegmentIndex = path.length - 2;
899
- if (collidingSegmentIndex !== 0 && collidingSegmentIndex !== lastSegmentIndex) {
900
- return [];
901
- }
902
- const shouldReverse = collidingSegmentIndex === lastSegmentIndex;
903
- const orderedPath = shouldReverse ? [...path].reverse() : path;
904
- const [start, corner, end] = orderedPath;
905
- const firstSegmentAxis = getSegmentAxis(start, corner);
906
- const secondSegmentAxis = getSegmentAxis(corner, end);
907
- if (!firstSegmentAxis || !secondSegmentAxis) return [];
908
- if (firstSegmentAxis === secondSegmentAxis) return [];
909
- const escapeCoordinates = [
910
- ...midBetweenPointAndRect(secondSegmentAxis, start, obstacle),
911
- ...midBetweenPointAndRect(secondSegmentAxis, end, obstacle)
912
- ];
913
- const detourCoordinates = [
914
- ...midBetweenPointAndRect(firstSegmentAxis, start, obstacle),
915
- ...midBetweenPointAndRect(firstSegmentAxis, end, obstacle)
916
- ];
917
- const detours = [];
918
- for (const escapeCoordinate of [...new Set(escapeCoordinates)]) {
919
- for (const detourCoordinate of [...new Set(detourCoordinates)]) {
920
- const orderedDetourPrefix = firstSegmentAxis === "y" ? [
921
- start,
922
- { x: escapeCoordinate, y: start.y },
923
- { x: escapeCoordinate, y: detourCoordinate },
924
- { x: end.x, y: detourCoordinate },
925
- end
926
- ] : [
927
- start,
928
- { x: start.x, y: escapeCoordinate },
929
- { x: detourCoordinate, y: escapeCoordinate },
930
- { x: detourCoordinate, y: end.y },
931
- end
932
- ];
933
- const orderedDetour = [...orderedDetourPrefix, ...orderedPath.slice(3)];
934
- const detour = shouldReverse ? orderedDetour.reverse() : orderedDetour;
935
- if (hasOnlyNonzeroOrthogonalSegments(detour)) detours.push(detour);
936
- }
937
- }
938
- return detours;
939
- };
940
-
941
- // lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/pathOps.ts
942
- var EPS3 = 1e-9;
943
907
  var shiftSegmentOrth = (pts, segIndex, axis, newCoord, eps = EPS3) => {
944
908
  if (segIndex < 0 || segIndex >= pts.length - 1) {
945
909
  return null;
@@ -1001,6 +965,121 @@ var pathKey = (pts, decimals = 6) => {
1001
965
  return key;
1002
966
  };
1003
967
 
968
+ // lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/generateEndpointCollisionDetours.ts
969
+ var getSegmentAxis = (start, end) => {
970
+ if (isVertical(start, end)) return "x";
971
+ if (isHorizontal(start, end)) return "y";
972
+ return null;
973
+ };
974
+ var generateEndpointCollisionDetours = ({
975
+ path,
976
+ collidingSegmentIndex,
977
+ obstacle
978
+ }) => {
979
+ if (path.length < 3 || path.length > 4) return [];
980
+ const lastSegmentIndex = path.length - 2;
981
+ if (collidingSegmentIndex !== 0 && collidingSegmentIndex !== lastSegmentIndex) {
982
+ return [];
983
+ }
984
+ const shouldReverse = collidingSegmentIndex === lastSegmentIndex;
985
+ const orderedPath = shouldReverse ? [...path].reverse() : path;
986
+ const [start, corner, end] = orderedPath;
987
+ const firstSegmentAxis = getSegmentAxis(start, corner);
988
+ const secondSegmentAxis = getSegmentAxis(corner, end);
989
+ if (!firstSegmentAxis || !secondSegmentAxis) return [];
990
+ if (firstSegmentAxis === secondSegmentAxis) return [];
991
+ const escapeCoordinates = [
992
+ ...midBetweenPointAndRect(secondSegmentAxis, start, obstacle),
993
+ ...midBetweenPointAndRect(secondSegmentAxis, end, obstacle)
994
+ ];
995
+ const detourCoordinates = [
996
+ ...midBetweenPointAndRect(firstSegmentAxis, start, obstacle),
997
+ ...midBetweenPointAndRect(firstSegmentAxis, end, obstacle)
998
+ ];
999
+ const detours = [];
1000
+ for (const escapeCoordinate of [...new Set(escapeCoordinates)]) {
1001
+ for (const detourCoordinate of [...new Set(detourCoordinates)]) {
1002
+ const orderedDetourPrefix = firstSegmentAxis === "y" ? [
1003
+ start,
1004
+ { x: escapeCoordinate, y: start.y },
1005
+ { x: escapeCoordinate, y: detourCoordinate },
1006
+ { x: end.x, y: detourCoordinate },
1007
+ end
1008
+ ] : [
1009
+ start,
1010
+ { x: start.x, y: escapeCoordinate },
1011
+ { x: detourCoordinate, y: escapeCoordinate },
1012
+ { x: detourCoordinate, y: end.y },
1013
+ end
1014
+ ];
1015
+ const orderedDetour = [...orderedDetourPrefix, ...orderedPath.slice(3)];
1016
+ const detour = shouldReverse ? orderedDetour.reverse() : orderedDetour;
1017
+ if (hasOnlyNonzeroOrthogonalSegments(detour)) detours.push(detour);
1018
+ }
1019
+ }
1020
+ return detours;
1021
+ };
1022
+
1023
+ // lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/generateInternalSegmentCollisionDetours.ts
1024
+ var EPS4 = 1e-9;
1025
+ var DEFAULT_CLEARANCE = 0.2;
1026
+ var isStrictlyBetween = (value, a, b) => value > Math.min(a, b) + EPS4 && value < Math.max(a, b) - EPS4;
1027
+ var generateInternalSegmentCollisionDetours = ({
1028
+ path,
1029
+ collidingSegmentIndex,
1030
+ obstacle,
1031
+ clearance = DEFAULT_CLEARANCE
1032
+ }) => {
1033
+ if (collidingSegmentIndex <= 0 || collidingSegmentIndex >= path.length - 2) {
1034
+ return [];
1035
+ }
1036
+ const start = path[collidingSegmentIndex];
1037
+ const end = path[collidingSegmentIndex + 1];
1038
+ const detourPoints = [];
1039
+ if (isHorizontal(start, end)) {
1040
+ const movingRight = start.x < end.x;
1041
+ const entryX = movingRight ? obstacle.minX - clearance : obstacle.maxX + clearance;
1042
+ const exitX = movingRight ? obstacle.maxX + clearance : obstacle.minX - clearance;
1043
+ if (!isStrictlyBetween(entryX, start.x, end.x) || !isStrictlyBetween(exitX, start.x, end.x)) {
1044
+ return [];
1045
+ }
1046
+ for (const detourY of [
1047
+ obstacle.minY - clearance,
1048
+ obstacle.maxY + clearance
1049
+ ]) {
1050
+ detourPoints.push([
1051
+ { x: entryX, y: start.y },
1052
+ { x: entryX, y: detourY },
1053
+ { x: exitX, y: detourY },
1054
+ { x: exitX, y: end.y }
1055
+ ]);
1056
+ }
1057
+ } else if (isVertical(start, end)) {
1058
+ const movingUp = start.y < end.y;
1059
+ const entryY = movingUp ? obstacle.minY - clearance : obstacle.maxY + clearance;
1060
+ const exitY = movingUp ? obstacle.maxY + clearance : obstacle.minY - clearance;
1061
+ if (!isStrictlyBetween(entryY, start.y, end.y) || !isStrictlyBetween(exitY, start.y, end.y)) {
1062
+ return [];
1063
+ }
1064
+ for (const detourX of [
1065
+ obstacle.minX - clearance,
1066
+ obstacle.maxX + clearance
1067
+ ]) {
1068
+ detourPoints.push([
1069
+ { x: start.x, y: entryY },
1070
+ { x: detourX, y: entryY },
1071
+ { x: detourX, y: exitY },
1072
+ { x: end.x, y: exitY }
1073
+ ]);
1074
+ }
1075
+ }
1076
+ return detourPoints.map((points) => [
1077
+ ...path.slice(0, collidingSegmentIndex + 1),
1078
+ ...points,
1079
+ ...path.slice(collidingSegmentIndex + 1)
1080
+ ]).filter(hasOnlyNonzeroOrthogonalSegments);
1081
+ };
1082
+
1004
1083
  // lib/solvers/GuidelinesSolver/getInputChipBounds.ts
1005
1084
  function getInputChipBounds(chip) {
1006
1085
  const halfWidth = chip.width / 2;
@@ -1013,26 +1092,8 @@ function getInputChipBounds(chip) {
1013
1092
  };
1014
1093
  }
1015
1094
 
1016
- // lib/utils/textBoxBounds.ts
1017
- function getTextBoxBounds(textBox, padding = {}) {
1018
- return {
1019
- minX: textBox.center.x - textBox.width / 2 - (padding.minX ?? 0),
1020
- minY: textBox.center.y - textBox.height / 2 - (padding.minY ?? 0),
1021
- maxX: textBox.center.x + textBox.width / 2 + (padding.maxX ?? 0),
1022
- maxY: textBox.center.y + textBox.height / 2 + (padding.maxY ?? 0)
1023
- };
1024
- }
1025
- function boundsOverlap(a, b, eps = 1e-9) {
1026
- return a.minX < b.maxX - eps && a.maxX > b.minX + eps && a.minY < b.maxY - eps && a.maxY > b.minY + eps;
1027
- }
1028
- function rectIntersectsAnyTextBox(bounds, inputProblem) {
1029
- for (const textBox of inputProblem.textBoxes ?? []) {
1030
- if (boundsOverlap(bounds, getTextBoxBounds(textBox))) return true;
1031
- }
1032
- return false;
1033
- }
1034
-
1035
1095
  // lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/rect.ts
1096
+ var isTextBoxObstacle = (obstacle) => obstacle.kind === "text_box";
1036
1097
  var chipToRect = (chip) => {
1037
1098
  const b = getInputChipBounds(chip);
1038
1099
  return { kind: "chip", chipId: chip.chipId, ...b };
@@ -1096,13 +1157,11 @@ var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
1096
1157
  this.obstacles = getObstacleRects(this.inputProblem, {
1097
1158
  textBoxPadding: this.getTextBoxPaddingForConnectionPair()
1098
1159
  });
1099
- this.textObstacles = new Set(
1100
- this.obstacles.filter((r) => r.kind === "text_box")
1101
- );
1160
+ this.textObstacles = new Set(this.obstacles.filter(isTextBoxObstacle));
1102
1161
  const endpointChipIds = new Set(this.pins.map((pin) => pin.chipId));
1103
1162
  this.endpointTextObstacles = new Set(
1104
- endpointChipIds.size > 1 ? this.obstacles.filter(
1105
- (r) => r.kind === "text_box" && r.textBox.chipId !== void 0 && endpointChipIds.has(r.textBox.chipId)
1163
+ endpointChipIds.size > 1 ? this.obstacles.filter(isTextBoxObstacle).filter(
1164
+ (obstacle) => obstacle.textBox.chipId !== void 0 && endpointChipIds.has(obstacle.textBox.chipId)
1106
1165
  ) : []
1107
1166
  );
1108
1167
  const [pin1, pin2] = this.pins;
@@ -1290,7 +1349,10 @@ var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
1290
1349
  path[adjacentEndpointSegIndex + 1]
1291
1350
  )) {
1292
1351
  for (const textObstacle of this.endpointTextObstacles) {
1293
- excludedRects.add(textObstacle);
1352
+ const textBounds = getTextBoxBounds(textObstacle.textBox);
1353
+ if (!segmentIntersectsRect(segmentStart, segmentEnd, textBounds)) {
1354
+ excludedRects.add(textObstacle);
1355
+ }
1294
1356
  }
1295
1357
  }
1296
1358
  return excludedRects;
@@ -1299,8 +1361,8 @@ var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
1299
1361
  if (!collision) {
1300
1362
  const first = path[0];
1301
1363
  const last = path[path.length - 1];
1302
- const EPS11 = 1e-9;
1303
- const samePoint = (p, q) => Math.abs(p.x - q.x) < EPS11 && Math.abs(p.y - q.y) < EPS11;
1364
+ const EPS13 = 1e-9;
1365
+ const samePoint = (p, q) => Math.abs(p.x - q.x) < EPS13 && Math.abs(p.y - q.y) < EPS13;
1304
1366
  if (samePoint(first, { x: PA.x, y: PA.y }) && samePoint(last, { x: PB.x, y: PB.y })) {
1305
1367
  this.solvedTracePath = path;
1306
1368
  this.solved = true;
@@ -1373,14 +1435,7 @@ var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
1373
1435
  candidates.push(...mids);
1374
1436
  }
1375
1437
  const newStates = [];
1376
- const addShiftedCandidate = (candidateSegIndex, candidateAxis, coord) => {
1377
- const newPath = shiftSegmentOrth(
1378
- path,
1379
- candidateSegIndex,
1380
- candidateAxis,
1381
- coord
1382
- );
1383
- if (!newPath) return;
1438
+ const addPathCandidate = (newPath) => {
1384
1439
  const key = pathKey(newPath);
1385
1440
  if (this.visited.has(key)) return;
1386
1441
  this.visited.add(key);
@@ -1393,10 +1448,28 @@ var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
1393
1448
  pinBandPenalty: this.getPinBandPenalty(newPath)
1394
1449
  });
1395
1450
  };
1451
+ const addShiftedCandidate = (candidateSegIndex, candidateAxis, coord) => {
1452
+ const newPath = shiftSegmentOrth(
1453
+ path,
1454
+ candidateSegIndex,
1455
+ candidateAxis,
1456
+ coord
1457
+ );
1458
+ if (newPath) addPathCandidate(newPath);
1459
+ };
1460
+ const lastSegIndex = path.length - 2;
1461
+ if (this.connectionPair === void 0 && rect.kind === "text_box" && this.endpointTextObstacles.has(rect) && !collisionRects.has(rect) && originalSegIndex > 0 && originalSegIndex < lastSegIndex) {
1462
+ for (const detour of generateInternalSegmentCollisionDetours({
1463
+ path,
1464
+ collidingSegmentIndex: originalSegIndex,
1465
+ obstacle: rect
1466
+ })) {
1467
+ addPathCandidate(detour);
1468
+ }
1469
+ }
1396
1470
  for (const coord of candidates) {
1397
1471
  addShiftedCandidate(segIndex, axis, coord);
1398
1472
  }
1399
- const lastSegIndex = path.length - 2;
1400
1473
  const adjacentSegmentIndexes = originalSegIndex === 1 ? [2] : originalSegIndex === lastSegIndex - 1 ? [lastSegIndex - 2] : [];
1401
1474
  for (const adjacentSegIndex of adjacentSegmentIndexes) {
1402
1475
  if (adjacentSegIndex < 0 || adjacentSegIndex >= lastSegIndex) continue;
@@ -1573,13 +1646,13 @@ var applyJogToTerminalSegment = ({
1573
1646
  segmentIndex: si,
1574
1647
  offset,
1575
1648
  JOG_SIZE,
1576
- EPS: EPS11 = 1e-6
1649
+ EPS: EPS13 = 1e-6
1577
1650
  }) => {
1578
1651
  if (si !== 0 && si !== pts.length - 2) return;
1579
1652
  const start = pts[si];
1580
1653
  const end = pts[si + 1];
1581
- const isVertical5 = Math.abs(start.x - end.x) < EPS11;
1582
- const isHorizontal4 = Math.abs(start.y - end.y) < EPS11;
1654
+ const isVertical5 = Math.abs(start.x - end.x) < EPS13;
1655
+ const isHorizontal4 = Math.abs(start.y - end.y) < EPS13;
1583
1656
  if (!isVertical5 && !isHorizontal4) return;
1584
1657
  const segDir = isVertical5 ? end.y > start.y ? 1 : -1 : end.x > start.x ? 1 : -1;
1585
1658
  if (pts.length === 2) {
@@ -1652,7 +1725,7 @@ var applyJogToTerminalSegment = ({
1652
1725
  };
1653
1726
 
1654
1727
  // lib/solvers/TraceOverlapShiftSolver/TraceOverlapIssueSolver/TraceOverlapIssueSolver.ts
1655
- var EPS4 = 1e-6;
1728
+ var EPS5 = 1e-6;
1656
1729
  var TraceOverlapIssueSolver = class extends BaseSolver {
1657
1730
  overlappingTraceSegments;
1658
1731
  interactionKind;
@@ -1741,7 +1814,7 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
1741
1814
  }
1742
1815
  applyOffsets(offsets) {
1743
1816
  const correctedTraceMap = { ...this.correctedTraceMap };
1744
- const eq = (a, b) => Math.abs(a - b) < EPS4;
1817
+ const eq = (a, b) => Math.abs(a - b) < EPS5;
1745
1818
  const samePoint = (p, q) => !!p && !!q && eq(p.x, q.x) && eq(p.y, q.y);
1746
1819
  this.overlappingTraceSegments.forEach((group, groupIndex) => {
1747
1820
  const offset = offsets[groupIndex];
@@ -1763,9 +1836,9 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
1763
1836
  const start = pts[si];
1764
1837
  const end = pts[si + 1];
1765
1838
  if (!start || !end) continue;
1766
- if (Math.abs(start.x - end.x) < EPS4) {
1839
+ if (Math.abs(start.x - end.x) < EPS5) {
1767
1840
  segmentAxes.set(si, "vertical");
1768
- } else if (Math.abs(start.y - end.y) < EPS4) {
1841
+ } else if (Math.abs(start.y - end.y) < EPS5) {
1769
1842
  segmentAxes.set(si, "horizontal");
1770
1843
  }
1771
1844
  }
@@ -1780,7 +1853,7 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
1780
1853
  segmentIndex: si,
1781
1854
  offset,
1782
1855
  JOG_SIZE,
1783
- EPS: EPS4
1856
+ EPS: EPS5
1784
1857
  });
1785
1858
  } else {
1786
1859
  const start = pts[si];
@@ -1867,7 +1940,7 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
1867
1940
  max = obstacle.maxY;
1868
1941
  }
1869
1942
  const next = orig + candidateOffset;
1870
- return orig <= min + EPS4 && next >= max - EPS4 || orig >= max - EPS4 && next <= min + EPS4;
1943
+ return orig <= min + EPS5 && next >= max - EPS5 || orig >= max - EPS5 && next <= min + EPS5;
1871
1944
  });
1872
1945
  const edges = blockingCollisions.map(({ start, isVertical: isVertical5, obstacle }) => {
1873
1946
  if (isVertical5) {
@@ -1904,8 +1977,8 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
1904
1977
  const start = trace.tracePath[traceSegmentIndex];
1905
1978
  const end = trace.tracePath[traceSegmentIndex + 1];
1906
1979
  if (!start || !end) continue;
1907
- const isVertical5 = Math.abs(start.x - end.x) < EPS4;
1908
- const isHorizontal4 = Math.abs(start.y - end.y) < EPS4;
1980
+ const isVertical5 = Math.abs(start.x - end.x) < EPS5;
1981
+ const isHorizontal4 = Math.abs(start.y - end.y) < EPS5;
1909
1982
  if (!isVertical5 && !isHorizontal4) continue;
1910
1983
  const shiftedSegment = isVertical5 ? [
1911
1984
  { ...start, x: start.x + offset },
@@ -1997,7 +2070,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
1997
2070
  return islands;
1998
2071
  }
1999
2072
  findNextOverlapIssue() {
2000
- const EPS11 = 2e-3;
2073
+ const EPS13 = 2e-3;
2001
2074
  const netIds = Object.keys(this.traceNetIslands);
2002
2075
  for (let i = 0; i < netIds.length; i++) {
2003
2076
  for (let j = i + 1; j < netIds.length; j++) {
@@ -2024,7 +2097,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
2024
2097
  segmentBIndex,
2025
2098
  rangeOverlap
2026
2099
  }) => {
2027
- if (rangeOverlap > EPS11) {
2100
+ if (rangeOverlap > EPS13) {
2028
2101
  const keyA = `${pathAIndex}:${segmentAIndex}`;
2029
2102
  const keyB = `${pathBIndex}:${segmentBIndex}`;
2030
2103
  if (!seenA.has(keyA)) {
@@ -2043,7 +2116,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
2043
2116
  }
2044
2117
  return;
2045
2118
  }
2046
- if (rangeOverlap < -EPS11) return;
2119
+ if (rangeOverlap < -EPS13) return;
2047
2120
  const pathA = pathsA[pathAIndex].tracePath;
2048
2121
  const pathB = pathsB[pathBIndex].tracePath;
2049
2122
  const segmentAStart = pathA[segmentAIndex];
@@ -2066,8 +2139,8 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
2066
2139
  for (let sa = 0; sa < ptsA.length - 1; sa++) {
2067
2140
  const a1 = ptsA[sa];
2068
2141
  const a2 = ptsA[sa + 1];
2069
- const aVert = Math.abs(a1.x - a2.x) < EPS11;
2070
- const aHorz = Math.abs(a1.y - a2.y) < EPS11;
2142
+ const aVert = Math.abs(a1.x - a2.x) < EPS13;
2143
+ const aHorz = Math.abs(a1.y - a2.y) < EPS13;
2071
2144
  if (!aVert && !aHorz) continue;
2072
2145
  for (let pb = 0; pb < pathsB.length; pb++) {
2073
2146
  const pathB = pathsB[pb];
@@ -2075,11 +2148,11 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
2075
2148
  for (let sb = 0; sb < ptsB.length - 1; sb++) {
2076
2149
  const b1 = ptsB[sb];
2077
2150
  const b2 = ptsB[sb + 1];
2078
- const bVert = Math.abs(b1.x - b2.x) < EPS11;
2079
- const bHorz = Math.abs(b1.y - b2.y) < EPS11;
2151
+ const bVert = Math.abs(b1.x - b2.x) < EPS13;
2152
+ const bHorz = Math.abs(b1.y - b2.y) < EPS13;
2080
2153
  if (!bVert && !bHorz) continue;
2081
2154
  if (aVert && bVert) {
2082
- if (Math.abs(a1.x - b1.x) < EPS11) {
2155
+ if (Math.abs(a1.x - b1.x) < EPS13) {
2083
2156
  recordCollinearInteraction({
2084
2157
  pathAIndex: pa,
2085
2158
  segmentAIndex: sa,
@@ -2089,7 +2162,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
2089
2162
  });
2090
2163
  }
2091
2164
  } else if (aHorz && bHorz) {
2092
- if (Math.abs(a1.y - b1.y) < EPS11) {
2165
+ if (Math.abs(a1.y - b1.y) < EPS13) {
2093
2166
  recordCollinearInteraction({
2094
2167
  pathAIndex: pa,
2095
2168
  segmentAIndex: sa,
@@ -2145,14 +2218,14 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
2145
2218
  return null;
2146
2219
  }
2147
2220
  findNextDiagonalSegment() {
2148
- const EPS11 = 2e-3;
2221
+ const EPS13 = 2e-3;
2149
2222
  for (const mspPairId in this.correctedTraceMap) {
2150
2223
  const tracePath = this.correctedTraceMap[mspPairId].tracePath;
2151
2224
  for (let i = 0; i < tracePath.length - 1; i++) {
2152
2225
  const p1 = tracePath[i];
2153
2226
  const p2 = tracePath[i + 1];
2154
- const isHorizontal4 = Math.abs(p1.y - p2.y) < EPS11;
2155
- const isVertical5 = Math.abs(p1.x - p2.x) < EPS11;
2227
+ const isHorizontal4 = Math.abs(p1.y - p2.y) < EPS13;
2228
+ const isVertical5 = Math.abs(p1.x - p2.x) < EPS13;
2156
2229
  if (!isHorizontal4 && !isVertical5) {
2157
2230
  return { mspPairId, tracePath, i, p1, p2 };
2158
2231
  }
@@ -2166,13 +2239,13 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
2166
2239
  return false;
2167
2240
  }
2168
2241
  const { mspPairId, tracePath, i, p1, p2 } = diagonalInfo;
2169
- const EPS11 = 2e-3;
2242
+ const EPS13 = 2e-3;
2170
2243
  const p0 = i > 0 ? tracePath[i - 1] : null;
2171
2244
  const p3 = i + 2 < tracePath.length ? tracePath[i + 2] : null;
2172
- const prevIsVertical = p0 ? Math.abs(p0.x - p1.x) < EPS11 : false;
2173
- const prevIsHorizontal = p0 ? Math.abs(p0.y - p1.y) < EPS11 : false;
2174
- const nextIsVertical = p3 ? Math.abs(p2.x - p3.x) < EPS11 : false;
2175
- const nextIsHorizontal = p3 ? Math.abs(p2.y - p3.y) < EPS11 : false;
2245
+ const prevIsVertical = p0 ? Math.abs(p0.x - p1.x) < EPS13 : false;
2246
+ const prevIsHorizontal = p0 ? Math.abs(p0.y - p1.y) < EPS13 : false;
2247
+ const nextIsVertical = p3 ? Math.abs(p2.x - p3.x) < EPS13 : false;
2248
+ const nextIsHorizontal = p3 ? Math.abs(p2.y - p3.y) < EPS13 : false;
2176
2249
  const elbow1 = { x: p1.x, y: p2.y };
2177
2250
  const elbow2 = { x: p2.x, y: p1.y };
2178
2251
  let score1 = 0;
@@ -2299,24 +2372,24 @@ var ChipObstacleSpatialIndex = class {
2299
2372
  };
2300
2373
 
2301
2374
  // lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/collisions.ts
2302
- function segmentIntersectsRect2(p1, p2, rect, EPS11 = 1e-9) {
2303
- const isVert = Math.abs(p1.x - p2.x) < EPS11;
2304
- const isHorz = Math.abs(p1.y - p2.y) < EPS11;
2375
+ function segmentIntersectsRect2(p1, p2, rect, EPS13 = 1e-9) {
2376
+ const isVert = Math.abs(p1.x - p2.x) < EPS13;
2377
+ const isHorz = Math.abs(p1.y - p2.y) < EPS13;
2305
2378
  if (!isVert && !isHorz) return false;
2306
2379
  if (isVert) {
2307
2380
  const x = p1.x;
2308
- if (x < rect.minX - EPS11 || x > rect.maxX + EPS11) return false;
2381
+ if (x < rect.minX - EPS13 || x > rect.maxX + EPS13) return false;
2309
2382
  const segMinY = Math.min(p1.y, p2.y);
2310
2383
  const segMaxY = Math.max(p1.y, p2.y);
2311
2384
  const overlap = Math.min(segMaxY, rect.maxY) - Math.max(segMinY, rect.minY);
2312
- return overlap > EPS11;
2385
+ return overlap > EPS13;
2313
2386
  } else {
2314
2387
  const y = p1.y;
2315
- if (y < rect.minY - EPS11 || y > rect.maxY + EPS11) return false;
2388
+ if (y < rect.minY - EPS13 || y > rect.maxY + EPS13) return false;
2316
2389
  const segMinX = Math.min(p1.x, p2.x);
2317
2390
  const segMaxX = Math.max(p1.x, p2.x);
2318
2391
  const overlap = Math.min(segMaxX, rect.maxX) - Math.max(segMinX, rect.minX);
2319
- return overlap > EPS11;
2392
+ return overlap > EPS13;
2320
2393
  }
2321
2394
  }
2322
2395
  function rectIntersectsAnyTrace(bounds, inputTraceMap, hostPathId, hostSegIndex) {
@@ -2713,14 +2786,14 @@ var SingleNetLabelPlacementSolver = class extends BaseSolver {
2713
2786
  };
2714
2787
  let bestCandidate = null;
2715
2788
  let bestScore = -Infinity;
2716
- const EPS11 = 1e-6;
2789
+ const EPS13 = 1e-6;
2717
2790
  for (const curr of tracesToScan) {
2718
2791
  const pts = curr.tracePath.slice();
2719
2792
  for (let si = 0; si < pts.length - 1; si++) {
2720
2793
  const a = pts[si];
2721
2794
  const b = pts[si + 1];
2722
- const isH = Math.abs(a.y - b.y) < EPS11;
2723
- const isV = Math.abs(a.x - b.x) < EPS11;
2795
+ const isH = Math.abs(a.y - b.y) < EPS13;
2796
+ const isV = Math.abs(a.x - b.x) < EPS13;
2724
2797
  if (!isH && !isV) continue;
2725
2798
  const segmentAllowed = isH ? ["y+", "y-"] : ["x+", "x-"];
2726
2799
  const candidateOrients = orientations.filter(
@@ -5123,9 +5196,9 @@ var moveRailSegments = (trace, segments, coordinate) => {
5123
5196
  };
5124
5197
 
5125
5198
  // lib/solvers/RailNetLabelCornerPlacementSolver/geometry.ts
5126
- var EPS5 = 1e-6;
5199
+ var EPS6 = 1e-6;
5127
5200
  var isTraceLine = (trace) => getUniquePinCount(trace.pinIds) >= 2;
5128
- var rectsOverlap = (a, b) => Math.min(a.maxX, b.maxX) - Math.max(a.minX, b.minX) > EPS5 && Math.min(a.maxY, b.maxY) - Math.max(a.minY, b.minY) > EPS5;
5201
+ var rectsOverlap = (a, b) => Math.min(a.maxX, b.maxX) - Math.max(a.minX, b.minX) > EPS6 && Math.min(a.maxY, b.maxY) - Math.max(a.minY, b.minY) > EPS6;
5129
5202
  var getTraceCorners = (path) => {
5130
5203
  const corners = [];
5131
5204
  for (let i = 1; i < path.length - 1; i++) {
@@ -5149,11 +5222,11 @@ var getUniquePinCount = (pinIds) => new Set(pinIds).size;
5149
5222
  var isTraceCorner = (a, b, c) => getSegmentOrientation(a, b) !== getSegmentOrientation(b, c);
5150
5223
  var isPointOnSegment = (point, start, end) => {
5151
5224
  if (getSegmentOrientation(start, end) === "horizontal") {
5152
- return Math.abs(point.y - start.y) <= EPS5 && point.x >= Math.min(start.x, end.x) - EPS5 && point.x <= Math.max(start.x, end.x) + EPS5;
5225
+ return Math.abs(point.y - start.y) <= EPS6 && point.x >= Math.min(start.x, end.x) - EPS6 && point.x <= Math.max(start.x, end.x) + EPS6;
5153
5226
  }
5154
- return Math.abs(point.x - start.x) <= EPS5 && point.y >= Math.min(start.y, end.y) - EPS5 && point.y <= Math.max(start.y, end.y) + EPS5;
5227
+ return Math.abs(point.x - start.x) <= EPS6 && point.y >= Math.min(start.y, end.y) - EPS6 && point.y <= Math.max(start.y, end.y) + EPS6;
5155
5228
  };
5156
- var getSegmentOrientation = (a, b) => Math.abs(a.y - b.y) <= EPS5 ? "horizontal" : "vertical";
5229
+ var getSegmentOrientation = (a, b) => Math.abs(a.y - b.y) <= EPS6 ? "horizontal" : "vertical";
5157
5230
 
5158
5231
  // lib/solvers/TraceCleanupSolver/sameNetRailAlignment/preservesLabelAnchors.ts
5159
5232
  var getAnchoredTraceIds = (label, traces) => new Set(
@@ -5169,17 +5242,17 @@ var preservesLabelAnchors = (labels, before, after) => labels.every((label) => {
5169
5242
  });
5170
5243
 
5171
5244
  // lib/solvers/Example28Solver/types.ts
5172
- var EPS6 = 1e-9;
5245
+ var EPS7 = 1e-9;
5173
5246
 
5174
5247
  // lib/solvers/Example28Solver/segmentRunsAlongRectBoundary.ts
5175
5248
  var segmentRunsAlongRectBoundary = (start, end, rect) => {
5176
- const isVertical5 = Math.abs(start.x - end.x) < EPS6;
5177
- const isHorizontal4 = Math.abs(start.y - end.y) < EPS6;
5249
+ const isVertical5 = Math.abs(start.x - end.x) < EPS7;
5250
+ const isHorizontal4 = Math.abs(start.y - end.y) < EPS7;
5178
5251
  if (isVertical5) {
5179
- return Math.abs(start.x - rect.minX) < EPS6 || Math.abs(start.x - rect.maxX) < EPS6;
5252
+ return Math.abs(start.x - rect.minX) < EPS7 || Math.abs(start.x - rect.maxX) < EPS7;
5180
5253
  }
5181
5254
  if (isHorizontal4) {
5182
- return Math.abs(start.y - rect.minY) < EPS6 || Math.abs(start.y - rect.maxY) < EPS6;
5255
+ return Math.abs(start.y - rect.minY) < EPS7 || Math.abs(start.y - rect.maxY) < EPS7;
5183
5256
  }
5184
5257
  return false;
5185
5258
  };
@@ -5194,10 +5267,10 @@ var getPathLength = (path) => {
5194
5267
  return length;
5195
5268
  };
5196
5269
  var getDistance2 = (a, b) => Math.abs(a.x - b.x) + Math.abs(a.y - b.y);
5197
- var isAxisAlignedSegment = (start, end) => Math.abs(start.x - end.x) < EPS6 || Math.abs(start.y - end.y) < EPS6;
5198
- var getSegmentOrientation2 = (start, end) => Math.abs(start.y - end.y) < EPS6 ? "horizontal" : "vertical";
5270
+ var isAxisAlignedSegment = (start, end) => Math.abs(start.x - end.x) < EPS7 || Math.abs(start.y - end.y) < EPS7;
5271
+ var getSegmentOrientation2 = (start, end) => Math.abs(start.y - end.y) < EPS7 ? "horizontal" : "vertical";
5199
5272
  var projectPointToSegment = (point, start, end) => {
5200
- if (Math.abs(start.x - end.x) < EPS6) {
5273
+ if (Math.abs(start.x - end.x) < EPS7) {
5201
5274
  return {
5202
5275
  x: start.x,
5203
5276
  y: Math.min(
@@ -5206,7 +5279,7 @@ var projectPointToSegment = (point, start, end) => {
5206
5279
  )
5207
5280
  };
5208
5281
  }
5209
- if (Math.abs(start.y - end.y) < EPS6) {
5282
+ if (Math.abs(start.y - end.y) < EPS7) {
5210
5283
  return {
5211
5284
  x: Math.min(
5212
5285
  Math.max(point.x, Math.min(start.x, end.x)),
@@ -5249,9 +5322,9 @@ var findSegmentContainingPoint = (path, point) => {
5249
5322
  };
5250
5323
  var isPointWithinSegmentPrimaryRange = (point, segment) => {
5251
5324
  if (segment.orientation === "horizontal") {
5252
- return point.x >= Math.min(segment.start.x, segment.end.x) - EPS6 && point.x <= Math.max(segment.start.x, segment.end.x) + EPS6;
5325
+ return point.x >= Math.min(segment.start.x, segment.end.x) - EPS7 && point.x <= Math.max(segment.start.x, segment.end.x) + EPS7;
5253
5326
  }
5254
- return point.y >= Math.min(segment.start.y, segment.end.y) - EPS6 && point.y <= Math.max(segment.start.y, segment.end.y) + EPS6;
5327
+ return point.y >= Math.min(segment.start.y, segment.end.y) - EPS7 && point.y <= Math.max(segment.start.y, segment.end.y) + EPS7;
5255
5328
  };
5256
5329
  var findPreferredReroutedSegment = (path, originalSegmentIndex, originalSegmentCount, orientation, anchorPoint) => {
5257
5330
  const matchingSegments = getSegments(path).filter(
@@ -5279,18 +5352,18 @@ var projectPointToPath = (point, path) => {
5279
5352
  return bestPoint;
5280
5353
  };
5281
5354
  var segmentsIntersect = (a1, a2, b1, b2) => {
5282
- const aVertical = Math.abs(a1.x - a2.x) < EPS6;
5283
- const bVertical = Math.abs(b1.x - b2.x) < EPS6;
5284
- const between = (value, p1, p2) => value >= Math.min(p1, p2) - EPS6 && value <= Math.max(p1, p2) + EPS6;
5355
+ const aVertical = Math.abs(a1.x - a2.x) < EPS7;
5356
+ const bVertical = Math.abs(b1.x - b2.x) < EPS7;
5357
+ const between = (value, p1, p2) => value >= Math.min(p1, p2) - EPS7 && value <= Math.max(p1, p2) + EPS7;
5285
5358
  if (aVertical && bVertical) {
5286
- if (Math.abs(a1.x - b1.x) > EPS6) return false;
5359
+ if (Math.abs(a1.x - b1.x) > EPS7) return false;
5287
5360
  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));
5288
- return overlap > EPS6;
5361
+ return overlap > EPS7;
5289
5362
  }
5290
5363
  if (!aVertical && !bVertical) {
5291
- if (Math.abs(a1.y - b1.y) > EPS6) return false;
5364
+ if (Math.abs(a1.y - b1.y) > EPS7) return false;
5292
5365
  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));
5293
- return overlap > EPS6;
5366
+ return overlap > EPS7;
5294
5367
  }
5295
5368
  const verticalA = aVertical ? a1 : b1;
5296
5369
  const verticalB = aVertical ? a2 : b2;
@@ -5652,6 +5725,7 @@ var getTraceObstacles = (allTraces, excludeTraceId) => {
5652
5725
 
5653
5726
  // lib/solvers/TraceCleanupSolver/sub-solver/findIntersectionsWithObstacles.ts
5654
5727
  import { getSegmentIntersection } from "@tscircuit/math-utils/line-intersections";
5728
+ var EPS8 = 1e-6;
5655
5729
  var findIntersectionsWithObstacles = (p1, p2, obstacles) => {
5656
5730
  const intersections = [];
5657
5731
  for (const obstacle of obstacles) {
@@ -5670,10 +5744,38 @@ var findIntersectionsWithObstacles = (p1, p2, obstacles) => {
5670
5744
  }
5671
5745
  return intersections;
5672
5746
  };
5747
+ var isSamePoint = (first, second) => Math.abs(first.x - second.x) < EPS8 && Math.abs(first.y - second.y) < EPS8;
5748
+ var findPerpendicularPathCrossings = (path, otherPath) => {
5749
+ const crossings = [];
5750
+ for (let pathSegmentIndex = 1; pathSegmentIndex < path.length - 2; pathSegmentIndex++) {
5751
+ const start = path[pathSegmentIndex];
5752
+ const end = path[pathSegmentIndex + 1];
5753
+ const isVertical5 = Math.abs(start.x - end.x) < EPS8;
5754
+ for (let otherPathSegmentIndex = 1; otherPathSegmentIndex < otherPath.length - 2; otherPathSegmentIndex++) {
5755
+ const otherStart = otherPath[otherPathSegmentIndex];
5756
+ const otherEnd = otherPath[otherPathSegmentIndex + 1];
5757
+ const otherIsVertical = Math.abs(otherStart.x - otherEnd.x) < EPS8;
5758
+ if (isVertical5 === otherIsVertical) continue;
5759
+ const intersection = getSegmentIntersection(
5760
+ start,
5761
+ end,
5762
+ otherStart,
5763
+ otherEnd
5764
+ );
5765
+ if (!intersection || [start, end, otherStart, otherEnd].some(
5766
+ (point) => isSamePoint(point, intersection)
5767
+ )) {
5768
+ continue;
5769
+ }
5770
+ crossings.push({ pathSegmentIndex, otherPathSegmentIndex });
5771
+ }
5772
+ }
5773
+ return crossings;
5774
+ };
5673
5775
 
5674
5776
  // lib/solvers/TraceCleanupSolver/sub-solver/generateLShapeRerouteCandidates.ts
5675
- var EPS7 = 1e-6;
5676
- var isVertical3 = (a, b, eps = EPS7) => Math.abs(a.x - b.x) < eps;
5777
+ var EPS9 = 1e-6;
5778
+ var isVertical3 = (a, b, eps = EPS9) => Math.abs(a.x - b.x) < eps;
5677
5779
  var generateLShapeRerouteCandidates = ({
5678
5780
  lShape,
5679
5781
  rectangle,
@@ -5686,7 +5788,7 @@ var generateLShapeRerouteCandidates = ({
5686
5788
  let c2;
5687
5789
  let i1_padded = interactionPoint1;
5688
5790
  let i2_padded = interactionPoint2;
5689
- if (Math.abs(p2.x - x) < EPS7 && Math.abs(p2.y - (y + height)) < EPS7) {
5791
+ if (Math.abs(p2.x - x) < EPS9 && Math.abs(p2.y - (y + height)) < EPS9) {
5690
5792
  c2 = { x: x + width + padding, y: y - padding };
5691
5793
  if (isVertical3(p1, p2)) {
5692
5794
  i1_padded = { x: interactionPoint1.x, y: interactionPoint1.y - padding };
@@ -5698,7 +5800,7 @@ var generateLShapeRerouteCandidates = ({
5698
5800
  } else {
5699
5801
  i2_padded = { x: interactionPoint2.x + padding, y: interactionPoint2.y };
5700
5802
  }
5701
- } else if (Math.abs(p2.x - (x + width)) < EPS7 && Math.abs(p2.y - (y + height)) < EPS7) {
5803
+ } else if (Math.abs(p2.x - (x + width)) < EPS9 && Math.abs(p2.y - (y + height)) < EPS9) {
5702
5804
  c2 = { x: x - padding, y: y - padding };
5703
5805
  if (isVertical3(p1, p2)) {
5704
5806
  i1_padded = { x: interactionPoint1.x, y: interactionPoint1.y - padding };
@@ -5710,7 +5812,7 @@ var generateLShapeRerouteCandidates = ({
5710
5812
  } else {
5711
5813
  i2_padded = { x: interactionPoint2.x - padding, y: interactionPoint2.y };
5712
5814
  }
5713
- } else if (Math.abs(p2.x - x) < EPS7 && Math.abs(p2.y - y) < EPS7) {
5815
+ } else if (Math.abs(p2.x - x) < EPS9 && Math.abs(p2.y - y) < EPS9) {
5714
5816
  c2 = { x: x + width + padding, y: y + height + padding };
5715
5817
  if (isVertical3(p1, p2)) {
5716
5818
  i1_padded = { x: interactionPoint1.x, y: interactionPoint1.y + padding };
@@ -5722,7 +5824,7 @@ var generateLShapeRerouteCandidates = ({
5722
5824
  } else {
5723
5825
  i2_padded = { x: interactionPoint2.x + padding, y: interactionPoint2.y };
5724
5826
  }
5725
- } else if (Math.abs(p2.x - (x + width)) < EPS7 && Math.abs(p2.y - y) < EPS7) {
5827
+ } else if (Math.abs(p2.x - (x + width)) < EPS9 && Math.abs(p2.y - y) < EPS9) {
5726
5828
  c2 = { x: x - padding, y: y + height + padding };
5727
5829
  if (isVertical3(p1, p2)) {
5728
5830
  i1_padded = { x: interactionPoint1.x, y: interactionPoint1.y + padding };
@@ -5739,6 +5841,48 @@ var generateLShapeRerouteCandidates = ({
5739
5841
  }
5740
5842
  return [[i1_padded, c2, i2_padded]];
5741
5843
  };
5844
+ var generatePerpendicularTraceDetours = ({
5845
+ trace,
5846
+ segmentIndex,
5847
+ obstacleStart,
5848
+ obstacleEnd,
5849
+ chipBounds,
5850
+ clearance
5851
+ }) => {
5852
+ const buildDetours = (path, index) => {
5853
+ const start = path[index];
5854
+ const end = path[index + 1];
5855
+ const movingAxis = Math.abs(start.x - end.x) < EPS9 ? "y" : "x";
5856
+ const detourAxis = movingAxis === "x" ? "y" : "x";
5857
+ const gate = obstacleStart[movingAxis] + Math.sign(start[movingAxis] - obstacleStart[movingAxis]) * clearance;
5858
+ const obstacleRange = [obstacleStart[detourAxis], obstacleEnd[detourAxis]];
5859
+ const lowBound = detourAxis === "x" ? "minX" : "minY";
5860
+ const highBound = detourAxis === "x" ? "maxX" : "maxY";
5861
+ const detourCoordinates = [
5862
+ Math.min(...obstacleRange) - clearance,
5863
+ Math.max(...obstacleRange) + clearance,
5864
+ ...chipBounds.flatMap((bounds) => [
5865
+ bounds[lowBound] - clearance,
5866
+ bounds[highBound] + clearance
5867
+ ])
5868
+ ];
5869
+ return [...new Set(detourCoordinates)].map(
5870
+ (detour) => simplifyPath([
5871
+ ...path.slice(0, index + 1),
5872
+ { ...start, [movingAxis]: gate },
5873
+ { ...start, [movingAxis]: gate, [detourAxis]: detour },
5874
+ { ...end, [detourAxis]: detour },
5875
+ ...path.slice(index + 2)
5876
+ ])
5877
+ );
5878
+ };
5879
+ const reversedPath = [...trace.tracePath].reverse();
5880
+ const reversedIndex = trace.tracePath.length - 2 - segmentIndex;
5881
+ return [
5882
+ ...buildDetours(trace.tracePath, segmentIndex),
5883
+ ...buildDetours(reversedPath, reversedIndex).map((path) => path.reverse())
5884
+ ].map((path) => ({ traceId: trace.mspPairId, path }));
5885
+ };
5742
5886
 
5743
5887
  // lib/solvers/TraceCleanupSolver/sub-solver/isPathColliding.ts
5744
5888
  import { getSegmentIntersection as getSegmentIntersection2 } from "@tscircuit/math-utils/line-intersections";
@@ -5913,6 +6057,9 @@ var UntangleTraceSubsolver = class extends BaseSolver {
5913
6057
  input;
5914
6058
  chipObstacleSpatialIndex;
5915
6059
  lShapesToProcess = [];
6060
+ ignoredCrossings = /* @__PURE__ */ new Set();
6061
+ reroutedTraceIds = /* @__PURE__ */ new Set();
6062
+ processingCrossings = true;
5916
6063
  visualizationMode = "l_shapes";
5917
6064
  currentLShape = null;
5918
6065
  intersectionPoints = [];
@@ -5936,18 +6083,24 @@ var UntangleTraceSubsolver = class extends BaseSolver {
5936
6083
  if (!this.input.inputProblem._chipObstacleSpatialIndex) {
5937
6084
  this.input.inputProblem._chipObstacleSpatialIndex = this.chipObstacleSpatialIndex;
5938
6085
  }
5939
- for (const trace of this.input.allTraces) {
5940
- const lShapes = findAllLShapedTurns(trace.tracePath);
5941
- this.lShapesToProcess.push(
5942
- ...lShapes.map((l) => ({ ...l, traceId: trace.mspPairId }))
5943
- );
5944
- }
5945
6086
  }
5946
6087
  _step() {
5947
6088
  if (this.isInitialStep) {
5948
6089
  this.isInitialStep = false;
5949
6090
  return;
5950
6091
  }
6092
+ if (this.processingCrossings) {
6093
+ const crossing = this._findCrossing();
6094
+ if (crossing) {
6095
+ if (!this._resolveCrossing(crossing)) {
6096
+ this.ignoredCrossings.add(this._crossingKey(crossing));
6097
+ }
6098
+ return;
6099
+ }
6100
+ this.processingCrossings = false;
6101
+ this._initializeLShapes();
6102
+ return;
6103
+ }
5951
6104
  if (this.lShapeJustProcessed) {
5952
6105
  this._resetAfterLShapProcessing();
5953
6106
  return;
@@ -5971,6 +6124,110 @@ var UntangleTraceSubsolver = class extends BaseSolver {
5971
6124
  break;
5972
6125
  }
5973
6126
  }
6127
+ _initializeLShapes() {
6128
+ for (const trace of this.input.allTraces) {
6129
+ this.lShapesToProcess.push(
6130
+ ...findAllLShapedTurns(trace.tracePath).map((lShape) => ({
6131
+ ...lShape,
6132
+ traceId: trace.mspPairId
6133
+ }))
6134
+ );
6135
+ }
6136
+ }
6137
+ _crossingKey(crossing) {
6138
+ return `${crossing.trace.mspPairId}:${crossing.segmentIndex}:${crossing.otherTrace.mspPairId}:${crossing.otherSegmentIndex}`;
6139
+ }
6140
+ _isTraceBundle(first, second) {
6141
+ const componentPair = (trace) => trace.pins.map((pin) => pin.chipId).sort().join(":");
6142
+ if (componentPair(first) !== componentPair(second)) return false;
6143
+ return Object.values(this.input.mergedLabelNetIdMap).some(
6144
+ (netIds) => netIds.has(first.globalConnNetId) && netIds.has(second.globalConnNetId)
6145
+ );
6146
+ }
6147
+ _findCrossing() {
6148
+ const traces = this.input.allTraces;
6149
+ for (let firstIndex = 0; firstIndex < traces.length; firstIndex++) {
6150
+ const trace = traces[firstIndex];
6151
+ for (let secondIndex = firstIndex + 1; secondIndex < traces.length; secondIndex++) {
6152
+ const otherTrace = traces[secondIndex];
6153
+ if (trace.globalConnNetId === otherTrace.globalConnNetId) continue;
6154
+ const isInitialBundleCrossing = this._isTraceBundle(trace, otherTrace);
6155
+ if (!isInitialBundleCrossing && !this.reroutedTraceIds.has(trace.mspPairId) && !this.reroutedTraceIds.has(otherTrace.mspPairId)) {
6156
+ continue;
6157
+ }
6158
+ const crossings = findPerpendicularPathCrossings(
6159
+ trace.tracePath,
6160
+ otherTrace.tracePath
6161
+ );
6162
+ for (const { pathSegmentIndex, otherPathSegmentIndex } of crossings) {
6163
+ const crossing = {
6164
+ trace,
6165
+ segmentIndex: pathSegmentIndex,
6166
+ otherTrace,
6167
+ otherSegmentIndex: otherPathSegmentIndex,
6168
+ isInitialBundleCrossing
6169
+ };
6170
+ if (!this.ignoredCrossings.has(this._crossingKey(crossing))) {
6171
+ return crossing;
6172
+ }
6173
+ }
6174
+ }
6175
+ }
6176
+ return null;
6177
+ }
6178
+ _resolveCrossing(crossing) {
6179
+ const chipBounds = this.chipObstacleSpatialIndex.chips.map(
6180
+ (chip) => chip.bounds
6181
+ );
6182
+ const candidates = [
6183
+ ...generatePerpendicularTraceDetours({
6184
+ trace: crossing.trace,
6185
+ segmentIndex: crossing.segmentIndex,
6186
+ obstacleStart: crossing.otherTrace.tracePath[crossing.otherSegmentIndex],
6187
+ obstacleEnd: crossing.otherTrace.tracePath[crossing.otherSegmentIndex + 1],
6188
+ chipBounds,
6189
+ clearance: this.input.paddingBuffer
6190
+ }),
6191
+ ...generatePerpendicularTraceDetours({
6192
+ trace: crossing.otherTrace,
6193
+ segmentIndex: crossing.otherSegmentIndex,
6194
+ obstacleStart: crossing.trace.tracePath[crossing.segmentIndex],
6195
+ obstacleEnd: crossing.trace.tracePath[crossing.segmentIndex + 1],
6196
+ chipBounds,
6197
+ clearance: this.input.paddingBuffer
6198
+ })
6199
+ ];
6200
+ const chipObstacles = getObstacleRects(this.input.inputProblem).filter(
6201
+ (obstacle) => obstacle.kind === "chip"
6202
+ );
6203
+ const validCandidates = candidates.map((candidate) => ({
6204
+ ...candidate,
6205
+ collision: isPathColliding(
6206
+ candidate.path,
6207
+ this.input.allTraces,
6208
+ candidate.traceId
6209
+ )
6210
+ })).filter(
6211
+ (candidate) => !isPathCollidingWithChipInterior(candidate.path, chipObstacles) && !isPathColliding(
6212
+ candidate.path,
6213
+ [crossing.trace, crossing.otherTrace],
6214
+ candidate.traceId
6215
+ ).isColliding && (crossing.isInitialBundleCrossing || !candidate.collision.isColliding)
6216
+ ).sort(
6217
+ (first, second) => Number(first.collision.isColliding) - Number(second.collision.isColliding) || getPathLength(first.path) - getPathLength(second.path)
6218
+ );
6219
+ const bestCandidate = validCandidates[0];
6220
+ if (!bestCandidate) return false;
6221
+ const traceIndex = this.input.allTraces.findIndex(
6222
+ (trace) => trace.mspPairId === bestCandidate.traceId
6223
+ );
6224
+ this.input.allTraces[traceIndex] = {
6225
+ ...this.input.allTraces[traceIndex],
6226
+ tracePath: bestCandidate.path
6227
+ };
6228
+ this.reroutedTraceIds.add(bestCandidate.traceId);
6229
+ return true;
6230
+ }
5974
6231
  _resetAfterLShapProcessing() {
5975
6232
  this.lShapeProcessingStep = "idle";
5976
6233
  this.currentLShape = null;
@@ -6193,9 +6450,9 @@ var UntangleTraceSubsolver = class extends BaseSolver {
6193
6450
  };
6194
6451
 
6195
6452
  // lib/solvers/TraceCleanupSolver/is4PointRectangle.ts
6196
- var EPS8 = 1e-6;
6197
- var sameX = (a, b) => Math.abs(a.x - b.x) <= EPS8;
6198
- var sameY = (a, b) => Math.abs(a.y - b.y) <= EPS8;
6453
+ var EPS10 = 1e-6;
6454
+ var sameX = (a, b) => Math.abs(a.x - b.x) <= EPS10;
6455
+ var sameY = (a, b) => Math.abs(a.y - b.y) <= EPS10;
6199
6456
  var is4PointRectangle = (path) => {
6200
6457
  if (path.length !== 4) return false;
6201
6458
  const [p0, p1, p2, p3] = path;
@@ -6470,7 +6727,7 @@ var doesPathRunAlongChipBoundary = (path, chipObstacles) => {
6470
6727
  const end = path[i + 1];
6471
6728
  for (const obstacle of chipObstacles) {
6472
6729
  if (!segmentRunsAlongRectBoundary(start, end, obstacle)) continue;
6473
- if (getSegmentOverlapWithRectSpan(start, end, obstacle) > EPS6) {
6730
+ if (getSegmentOverlapWithRectSpan(start, end, obstacle) > EPS7) {
6474
6731
  return true;
6475
6732
  }
6476
6733
  }
@@ -6485,13 +6742,13 @@ var getChipBoundaryOverlap = (path, chipObstacles) => {
6485
6742
  for (const obstacle of chipObstacles) {
6486
6743
  if (!segmentRunsAlongRectBoundary(start, end, obstacle)) continue;
6487
6744
  const overlap = getSegmentOverlapWithRectSpan(start, end, obstacle);
6488
- if (overlap > EPS6) total += overlap;
6745
+ if (overlap > EPS7) total += overlap;
6489
6746
  }
6490
6747
  }
6491
6748
  return total;
6492
6749
  };
6493
6750
  var getSegmentOverlapWithRectSpan = (start, end, rect) => {
6494
- const isVertical5 = Math.abs(start.x - end.x) < EPS6;
6751
+ const isVertical5 = Math.abs(start.x - end.x) < EPS7;
6495
6752
  if (isVertical5) {
6496
6753
  return Math.min(Math.max(start.y, end.y), rect.maxY) - Math.max(Math.min(start.y, end.y), rect.minY);
6497
6754
  }
@@ -7255,16 +7512,16 @@ var createPortOnlyLabelConnectorTrace = ({
7255
7512
  // lib/solvers/AvailableNetOrientationSolver/constants.ts
7256
7513
  var LABEL_SEARCH_STEP = 0.05;
7257
7514
  var WICK_CLEARANCE = 1e-3;
7258
- var EPS9 = 1e-9;
7259
- var TRACE_BOUNDARY_TOLERANCE = WICK_CLEARANCE + EPS9;
7515
+ var EPS11 = 1e-9;
7516
+ var TRACE_BOUNDARY_TOLERANCE = WICK_CLEARANCE + EPS11;
7260
7517
  var CANDIDATE_SELECTED_COLOR2 = "blue";
7261
7518
  var CANDIDATE_REJECTED_COLOR2 = "red";
7262
7519
 
7263
7520
  // lib/solvers/AvailableNetOrientationSolver/geometry.ts
7264
7521
  var isYOrientation = (orientation) => orientation === "y+" || orientation === "y-";
7265
7522
  var isXOrientation = (orientation) => orientation === "x+" || orientation === "x-";
7266
- var rectsOverlap2 = (a, b) => Math.min(a.maxX, b.maxX) - Math.max(a.minX, b.minX) > EPS9 && Math.min(a.maxY, b.maxY) - Math.max(a.minY, b.minY) > EPS9;
7267
- var rangesOverlap = (minA, maxA, minB, maxB) => Math.min(maxA, maxB) - Math.max(minA, minB) > EPS9;
7523
+ var rectsOverlap2 = (a, b) => Math.min(a.maxX, b.maxX) - Math.max(a.minX, b.minX) > EPS11 && Math.min(a.maxY, b.maxY) - Math.max(a.minY, b.minY) > EPS11;
7524
+ var rangesOverlap = (minA, maxA, minB, maxB) => Math.min(maxA, maxB) - Math.max(minA, minB) > EPS11;
7268
7525
  var traceCrossesBoundsInterior = (bounds, traceMap) => {
7269
7526
  for (const trace of Object.values(traceMap)) {
7270
7527
  const points = trace.tracePath;
@@ -7287,7 +7544,7 @@ var segmentCrossesBoundsInterior = (p1, p2, bounds) => {
7287
7544
  return false;
7288
7545
  }
7289
7546
  if (sameX2(p1, p2)) {
7290
- if (p1.x <= interiorBounds.minX + EPS9 || p1.x >= interiorBounds.maxX - EPS9) {
7547
+ if (p1.x <= interiorBounds.minX + EPS11 || p1.x >= interiorBounds.maxX - EPS11) {
7291
7548
  return false;
7292
7549
  }
7293
7550
  return rangesOverlap(
@@ -7298,7 +7555,7 @@ var segmentCrossesBoundsInterior = (p1, p2, bounds) => {
7298
7555
  );
7299
7556
  }
7300
7557
  if (sameY2(p1, p2)) {
7301
- if (p1.y <= interiorBounds.minY + EPS9 || p1.y >= interiorBounds.maxY - EPS9) {
7558
+ if (p1.y <= interiorBounds.minY + EPS11 || p1.y >= interiorBounds.maxY - EPS11) {
7302
7559
  return false;
7303
7560
  }
7304
7561
  return rangesOverlap(
@@ -7354,8 +7611,8 @@ var simplifyOrthogonalPath = (path) => {
7354
7611
  return simplified;
7355
7612
  };
7356
7613
  var pointsEqual2 = (a, b) => sameX2(a, b) && sameY2(a, b);
7357
- var sameX2 = (a, b) => Math.abs(a.x - b.x) <= EPS9;
7358
- var sameY2 = (a, b) => Math.abs(a.y - b.y) <= EPS9;
7614
+ var sameX2 = (a, b) => Math.abs(a.x - b.x) <= EPS11;
7615
+ var sameY2 = (a, b) => Math.abs(a.y - b.y) <= EPS11;
7359
7616
  var getMaxSearchDistance = (inputProblem) => {
7360
7617
  const maxChipWidth = Math.max(
7361
7618
  ...inputProblem.chips.map((chip) => chip.width),
@@ -7610,11 +7867,11 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
7610
7867
  if (trace.globalConnNetId !== label.globalConnNetId) return false;
7611
7868
  const path = trace.tracePath;
7612
7869
  return path.some((point, pointIndex) => {
7613
- if (Math.abs(point.x - label.anchorPoint.x) > EPS9 || Math.abs(point.y - label.anchorPoint.y) > EPS9) {
7870
+ if (Math.abs(point.x - label.anchorPoint.x) > EPS11 || Math.abs(point.y - label.anchorPoint.y) > EPS11) {
7614
7871
  return false;
7615
7872
  }
7616
7873
  return [path[pointIndex - 1], path[pointIndex + 1]].some(
7617
- (neighbor) => neighbor && Math.abs(neighbor.x - point.x) <= EPS9 && (neighbor.y - point.y) * direction.y > EPS9
7874
+ (neighbor) => neighbor && Math.abs(neighbor.x - point.x) <= EPS11 && (neighbor.y - point.y) * direction.y > EPS11
7618
7875
  );
7619
7876
  });
7620
7877
  });
@@ -7693,7 +7950,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
7693
7950
  );
7694
7951
  const maxSearchDistance = this.getSearchDistanceLimit(label, orientation);
7695
7952
  const maxOutwardDistance = outwardDirection.x === 0 && outwardDirection.y === 0 ? 0 : this.maxSearchDistance;
7696
- for (let outwardDistance = 0; outwardDistance <= maxOutwardDistance + EPS9; outwardDistance += LABEL_SEARCH_STEP) {
7953
+ for (let outwardDistance = 0; outwardDistance <= maxOutwardDistance + EPS11; outwardDistance += LABEL_SEARCH_STEP) {
7697
7954
  const baseAnchor = {
7698
7955
  x: initialBaseAnchor.x + outwardDirection.x * outwardDistance,
7699
7956
  y: initialBaseAnchor.y + outwardDirection.y * outwardDistance
@@ -7767,7 +8024,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
7767
8024
  phase = "shift",
7768
8025
  stopOnTraceCollision = true
7769
8026
  } = params;
7770
- for (let distance4 = LABEL_SEARCH_STEP; distance4 <= maxSearchDistance + EPS9; distance4 += LABEL_SEARCH_STEP) {
8027
+ for (let distance4 = LABEL_SEARCH_STEP; distance4 <= maxSearchDistance + EPS11; distance4 += LABEL_SEARCH_STEP) {
7771
8028
  const anchorPoint = {
7772
8029
  x: baseAnchor.x + direction.x * distance4,
7773
8030
  y: baseAnchor.y + direction.y * distance4
@@ -8004,9 +8261,9 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
8004
8261
  const { anchorPoint, orientation } = candidate;
8005
8262
  const chipBounds = chip.bounds;
8006
8263
  if (isYOrientation(orientation)) {
8007
- return anchorPoint.x < chipBounds.minX - EPS9 || anchorPoint.x > chipBounds.maxX + EPS9;
8264
+ return anchorPoint.x < chipBounds.minX - EPS11 || anchorPoint.x > chipBounds.maxX + EPS11;
8008
8265
  }
8009
- return anchorPoint.y < chipBounds.minY - EPS9 || anchorPoint.y > chipBounds.maxY + EPS9;
8266
+ return anchorPoint.y < chipBounds.minY - EPS11 || anchorPoint.y > chipBounds.maxY + EPS11;
8010
8267
  });
8011
8268
  }
8012
8269
  getBoundsStatus(candidateBoundsCheck) {
@@ -8080,8 +8337,8 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
8080
8337
  sharesChipBoundary(bounds) {
8081
8338
  for (const chip of this.chipObstacleSpatialIndex.chips) {
8082
8339
  const chipBounds = chip.bounds;
8083
- const adjacentToVerticalSide = Math.abs(bounds.minX - chipBounds.maxX) <= WICK_CLEARANCE + EPS9 || Math.abs(bounds.maxX - chipBounds.minX) <= WICK_CLEARANCE + EPS9;
8084
- const adjacentToHorizontalSide = Math.abs(bounds.minY - chipBounds.maxY) <= WICK_CLEARANCE + EPS9 || Math.abs(bounds.maxY - chipBounds.minY) <= WICK_CLEARANCE + EPS9;
8340
+ const adjacentToVerticalSide = Math.abs(bounds.minX - chipBounds.maxX) <= WICK_CLEARANCE + EPS11 || Math.abs(bounds.maxX - chipBounds.minX) <= WICK_CLEARANCE + EPS11;
8341
+ const adjacentToHorizontalSide = Math.abs(bounds.minY - chipBounds.maxY) <= WICK_CLEARANCE + EPS11 || Math.abs(bounds.maxY - chipBounds.minY) <= WICK_CLEARANCE + EPS11;
8085
8342
  if (adjacentToVerticalSide && rangesOverlap(
8086
8343
  bounds.minY,
8087
8344
  bounds.maxY,
@@ -8129,7 +8386,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
8129
8386
  let nearestDistance = Number.POSITIVE_INFINITY;
8130
8387
  for (const chip of this.chipObstacleSpatialIndex.chips) {
8131
8388
  const bounds = chip.bounds;
8132
- if (point.x < bounds.minX - EPS9 || point.x > bounds.maxX + EPS9 || point.y < bounds.minY - EPS9 || point.y > bounds.maxY + EPS9) {
8389
+ if (point.x < bounds.minX - EPS11 || point.x > bounds.maxX + EPS11 || point.y < bounds.minY - EPS11 || point.y > bounds.maxY + EPS11) {
8133
8390
  continue;
8134
8391
  }
8135
8392
  for (const [side, distance4] of getSideDistances(point, bounds)) {
@@ -8146,8 +8403,8 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
8146
8403
  let nearestDistanceSq = Number.POSITIVE_INFINITY;
8147
8404
  for (const chip of this.chipObstacleSpatialIndex.chips) {
8148
8405
  const bounds = chip.bounds;
8149
- const dx = point.x < bounds.minX - EPS9 ? bounds.minX - point.x : point.x > bounds.maxX + EPS9 ? point.x - bounds.maxX : 0;
8150
- const dy = point.y < bounds.minY - EPS9 ? bounds.minY - point.y : point.y > bounds.maxY + EPS9 ? point.y - bounds.maxY : 0;
8406
+ const dx = point.x < bounds.minX - EPS11 ? bounds.minX - point.x : point.x > bounds.maxX + EPS11 ? point.x - bounds.maxX : 0;
8407
+ const dy = point.y < bounds.minY - EPS11 ? bounds.minY - point.y : point.y > bounds.maxY + EPS11 ? point.y - bounds.maxY : 0;
8151
8408
  if (dx === 0 && dy === 0) continue;
8152
8409
  const distanceSq = dx ** 2 + dy ** 2;
8153
8410
  if (distanceSq >= nearestDistanceSq) continue;
@@ -8431,8 +8688,8 @@ var RailNetLabelCornerPlacementSolver = class extends BaseSolver {
8431
8688
  if (seenCornerKeys.has(key)) continue;
8432
8689
  seenCornerKeys.add(key);
8433
8690
  const pinAligned = pins.some((pin) => {
8434
- if (isVertical5) return Math.abs(pin.x - anchorPoint.x) < EPS5;
8435
- return Math.abs(pin.y - anchorPoint.y) < EPS5;
8691
+ if (isVertical5) return Math.abs(pin.x - anchorPoint.x) < EPS6;
8692
+ return Math.abs(pin.y - anchorPoint.y) < EPS6;
8436
8693
  });
8437
8694
  if (!pinAligned) continue;
8438
8695
  candidates.push({
@@ -8525,11 +8782,11 @@ var getOrientationConstraint = (inputProblem, label) => {
8525
8782
  };
8526
8783
 
8527
8784
  // lib/solvers/TraceAnchoredNetLabelOverlapSolver/geometry.ts
8528
- var EPS10 = 1e-6;
8785
+ var EPS12 = 1e-6;
8529
8786
  var TRACE_BOUNDARY_TOLERANCE2 = 1e-6;
8530
8787
  var getLabelBounds = (label) => getRectBounds(label.center, label.width, label.height);
8531
- var rectsOverlap3 = (a, b) => Math.min(a.maxX, b.maxX) - Math.max(a.minX, b.minX) > EPS10 && Math.min(a.maxY, b.maxY) - Math.max(a.minY, b.minY) > EPS10;
8532
- var rectsTouchOrOverlap = (a, b) => Math.min(a.maxX, b.maxX) - Math.max(a.minX, b.minX) >= -EPS10 && Math.min(a.maxY, b.maxY) - Math.max(a.minY, b.minY) >= -EPS10;
8788
+ var rectsOverlap3 = (a, b) => Math.min(a.maxX, b.maxX) - Math.max(a.minX, b.minX) > EPS12 && Math.min(a.maxY, b.maxY) - Math.max(a.minY, b.minY) > EPS12;
8789
+ var rectsTouchOrOverlap = (a, b) => Math.min(a.maxX, b.maxX) - Math.max(a.minX, b.minX) >= -EPS12 && Math.min(a.maxY, b.maxY) - Math.max(a.minY, b.minY) >= -EPS12;
8533
8790
  var traceCrossesBoundsInterior2 = (bounds, traces) => {
8534
8791
  for (const trace of traces) {
8535
8792
  const points = trace.tracePath;
@@ -8575,7 +8832,7 @@ var getPointAtTraceDistance = (trace, distance4) => {
8575
8832
  const end = trace.tracePath[i + 1];
8576
8833
  const segmentLength = getManhattanDistance(start, end);
8577
8834
  const nextDistance = pathDistance + segmentLength;
8578
- if (distance4 <= nextDistance + EPS10) {
8835
+ if (distance4 <= nextDistance + EPS12) {
8579
8836
  const offset = Math.max(
8580
8837
  0,
8581
8838
  Math.min(segmentLength, distance4 - pathDistance)
@@ -8614,7 +8871,7 @@ var segmentCrossesBoundsInterior2 = (start, end, bounds) => {
8614
8871
  return false;
8615
8872
  }
8616
8873
  if (isVertical4(start, end)) {
8617
- if (start.x <= interior.minX + EPS10 || start.x >= interior.maxX - EPS10) {
8874
+ if (start.x <= interior.minX + EPS12 || start.x >= interior.maxX - EPS12) {
8618
8875
  return false;
8619
8876
  }
8620
8877
  return rangesOverlap2(
@@ -8625,7 +8882,7 @@ var segmentCrossesBoundsInterior2 = (start, end, bounds) => {
8625
8882
  );
8626
8883
  }
8627
8884
  if (isHorizontal3(start, end)) {
8628
- if (start.y <= interior.minY + EPS10 || start.y >= interior.maxY - EPS10) {
8885
+ if (start.y <= interior.minY + EPS12 || start.y >= interior.maxY - EPS12) {
8629
8886
  return false;
8630
8887
  }
8631
8888
  return rangesOverlap2(
@@ -8639,10 +8896,10 @@ var segmentCrossesBoundsInterior2 = (start, end, bounds) => {
8639
8896
  };
8640
8897
  var isPointOnSegment2 = (point, start, end) => {
8641
8898
  if (isHorizontal3(start, end)) {
8642
- return Math.abs(point.y - start.y) <= EPS10 && point.x >= Math.min(start.x, end.x) - EPS10 && point.x <= Math.max(start.x, end.x) + EPS10;
8899
+ return Math.abs(point.y - start.y) <= EPS12 && point.x >= Math.min(start.x, end.x) - EPS12 && point.x <= Math.max(start.x, end.x) + EPS12;
8643
8900
  }
8644
8901
  if (isVertical4(start, end)) {
8645
- return Math.abs(point.x - start.x) <= EPS10 && point.y >= Math.min(start.y, end.y) - EPS10 && point.y <= Math.max(start.y, end.y) + EPS10;
8902
+ return Math.abs(point.x - start.x) <= EPS12 && point.y >= Math.min(start.y, end.y) - EPS12 && point.y <= Math.max(start.y, end.y) + EPS12;
8646
8903
  }
8647
8904
  return false;
8648
8905
  };
@@ -8650,9 +8907,9 @@ var getSegmentDirection = (start, end) => ({
8650
8907
  x: isVertical4(start, end) ? 0 : Math.sign(end.x - start.x),
8651
8908
  y: isHorizontal3(start, end) ? 0 : Math.sign(end.y - start.y)
8652
8909
  });
8653
- var isHorizontal3 = (start, end) => Math.abs(start.y - end.y) <= EPS10;
8654
- var isVertical4 = (start, end) => Math.abs(start.x - end.x) <= EPS10;
8655
- var rangesOverlap2 = (minA, maxA, minB, maxB) => Math.min(maxA, maxB) - Math.max(minA, minB) > EPS10;
8910
+ var isHorizontal3 = (start, end) => Math.abs(start.y - end.y) <= EPS12;
8911
+ var isVertical4 = (start, end) => Math.abs(start.x - end.x) <= EPS12;
8912
+ var rangesOverlap2 = (minA, maxA, minB, maxB) => Math.min(maxA, maxB) - Math.max(minA, minB) > EPS12;
8656
8913
  var getUniquePinCount2 = (pinIds) => new Set(pinIds).size;
8657
8914
 
8658
8915
  // lib/solvers/TraceAnchoredNetLabelOverlapSolver/candidates.ts
@@ -8709,7 +8966,7 @@ var getCandidateDistances = (traceLength, vertexDistances) => {
8709
8966
  for (const distance4 of vertexDistances) {
8710
8967
  distances.add(roundDistance(distance4));
8711
8968
  }
8712
- return [...distances].filter((distance4) => distance4 >= -EPS10 && distance4 <= traceLength + EPS10).sort((a, b) => a - b);
8969
+ return [...distances].filter((distance4) => distance4 >= -EPS12 && distance4 <= traceLength + EPS12).sort((a, b) => a - b);
8713
8970
  };
8714
8971
  var getOrientationsForPoint = (params) => {
8715
8972
  const { inputProblem, label, point, orientationConstraint } = params;
@@ -8798,13 +9055,13 @@ var getOutwardOrientationOptions = (point, chipBounds) => {
8798
9055
  };
8799
9056
  var getOutwardAxisOption = (params) => {
8800
9057
  const { value, min, max, center, positiveOrientation, negativeOrientation } = params;
8801
- if (value > max + EPS10) {
9058
+ if (value > max + EPS12) {
8802
9059
  return {
8803
9060
  orientation: positiveOrientation,
8804
9061
  outwardScore: 1 + value - max
8805
9062
  };
8806
9063
  }
8807
- if (value < min - EPS10) {
9064
+ if (value < min - EPS12) {
8808
9065
  return {
8809
9066
  orientation: negativeOrientation,
8810
9067
  outwardScore: 1 + min - value
@@ -8815,7 +9072,7 @@ var getOutwardAxisOption = (params) => {
8815
9072
  outwardScore: getNormalizedCenterDistance(value, center, max - min)
8816
9073
  };
8817
9074
  };
8818
- var getNormalizedCenterDistance = (value, center, span) => Math.abs(value - center) / Math.max(EPS10, span / 2);
9075
+ var getNormalizedCenterDistance = (value, center, span) => Math.abs(value - center) / Math.max(EPS12, span / 2);
8819
9076
  var ALL_ORIENTATIONS = ["x+", "x-", "y+", "y-"];
8820
9077
  var getFlippedOrientation = (orientation) => {
8821
9078
  switch (orientation) {
@@ -8888,7 +9145,7 @@ var getNetLabelHeight = (inputProblem, label) => {
8888
9145
  )?.netLabelHeight;
8889
9146
  };
8890
9147
  var roundDistance = (distance4) => Number(distance4.toFixed(6));
8891
- var isSamePlacement = (label, point, orientation) => Math.abs(point.x - label.anchorPoint.x) <= EPS10 && Math.abs(point.y - label.anchorPoint.y) <= EPS10 && orientation === label.orientation;
9148
+ var isSamePlacement = (label, point, orientation) => Math.abs(point.x - label.anchorPoint.x) <= EPS12 && Math.abs(point.y - label.anchorPoint.y) <= EPS12 && orientation === label.orientation;
8892
9149
 
8893
9150
  // lib/solvers/TraceAnchoredNetLabelOverlapSolver/visualize.ts
8894
9151
  var CANDIDATE_SELECTED_COLOR4 = "blue";