@tscircuit/schematic-trace-solver 0.0.107 → 0.0.109

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 (33) hide show
  1. package/dist/index.d.ts +31 -3
  2. package/dist/index.js +784 -247
  3. package/lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver.ts +3 -1
  4. package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2.ts +58 -27
  5. package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/generateEndpointCollisionDetours.ts +1 -10
  6. package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/generateInternalSegmentCollisionDetours.ts +94 -0
  7. package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/pathOps.ts +10 -0
  8. package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/rect.ts +5 -1
  9. package/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts +18 -3
  10. package/lib/solvers/UnroutedTraceRecoverySolver/UnroutedTraceRecoverySolver.ts +592 -0
  11. package/package.json +1 -1
  12. package/site/bug-reports/bug-report-20260724T175257Z.page.tsx +4 -0
  13. package/tests/bug-reports/bug-report-20260707T092615Z/__snapshots__/bug-report-20260707T092615Z.snap.svg +5 -9
  14. package/tests/bug-reports/bug-report-20260708T095725Z/__snapshots__/bug-report-20260708T095725Z.snap.svg +5 -5
  15. package/tests/bug-reports/bug-report-20260708T095725Z/bug-report-20260708T095725Z.test.ts +15 -0
  16. package/tests/bug-reports/bug-report-20260724T175257Z/__snapshots__/bug-report-20260724T175257Z.snap.svg +58 -0
  17. package/tests/bug-reports/bug-report-20260724T175257Z/bug-report-20260724T175257Z.json +138 -0
  18. package/tests/bug-reports/bug-report-20260724T175257Z/bug-report-20260724T175257Z.test.ts +12 -0
  19. package/tests/examples/__snapshots__/example04.snap.svg +7 -9
  20. package/tests/examples/__snapshots__/example08.snap.svg +16 -20
  21. package/tests/examples/__snapshots__/example13.snap.svg +2 -4
  22. package/tests/examples/__snapshots__/example32.snap.svg +64 -66
  23. package/tests/examples/__snapshots__/example46.snap.svg +1 -1
  24. package/tests/repros/__snapshots__/repro-netlabel-collision-687.snap.svg +81 -0
  25. package/tests/repros/__snapshots__/repro-netlabel-overlap-trace.snap.svg +16 -18
  26. package/tests/repros/__snapshots__/repro-tps61222-trace-intersection.snap.svg +63 -220
  27. package/tests/repros/assets/repro-netlabel-collision-687.input.json +212 -0
  28. package/tests/repros/repro-netlabel-collision-687.test.ts +59 -0
  29. package/tests/repros/repro-tps61222-trace-intersection.test.ts +27 -0
  30. package/tests/solvers/SchematicTraceSingleLineSolver2/generate-internal-segment-collision-detours.test.ts +49 -0
  31. package/tests/solvers/UnroutedTraceRecoverySolver/paired-junction-recovery.test.ts +49 -0
  32. package/tests/solvers/UnroutedTraceRecoverySolver/skip-ground.test.ts +30 -0
  33. package/tests/solvers/UnroutedTraceRecoverySolver/skip-long-connection.test.ts +24 -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 EPS12 = 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) < -EPS12) 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) < -EPS12) 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) < EPS12 || Math.abs(p1.y - p2.y) < EPS12) {
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 };
@@ -421,6 +421,7 @@ function getOrthogonalMinimumSpanningTree(pins, opts = {}) {
421
421
  }
422
422
 
423
423
  // lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver.ts
424
+ var DEFAULT_MAX_MSP_PAIR_DISTANCE = 1;
424
425
  var getPinPairKey = (pinIds) => [...pinIds].sort().join("::");
425
426
  var MspConnectionPairSolver = class extends BaseSolver {
426
427
  inputProblem;
@@ -436,7 +437,7 @@ var MspConnectionPairSolver = class extends BaseSolver {
436
437
  constructor({ inputProblem }) {
437
438
  super();
438
439
  this.inputProblem = inputProblem;
439
- this.maxMspPairDistance = inputProblem.maxMspPairDistance ?? 1;
440
+ this.maxMspPairDistance = inputProblem.maxMspPairDistance ?? DEFAULT_MAX_MSP_PAIR_DISTANCE;
440
441
  const { directConnMap, netConnMap } = getConnectivityMapsFromInputProblem(inputProblem);
441
442
  this.dcConnMap = directConnMap;
442
443
  this.globalConnMap = netConnMap;
@@ -630,6 +631,25 @@ function getRectBounds(center, w, h) {
630
631
  };
631
632
  }
632
633
 
634
+ // lib/utils/textBoxBounds.ts
635
+ function getTextBoxBounds(textBox, padding = {}) {
636
+ return {
637
+ minX: textBox.center.x - textBox.width / 2 - (padding.minX ?? 0),
638
+ minY: textBox.center.y - textBox.height / 2 - (padding.minY ?? 0),
639
+ maxX: textBox.center.x + textBox.width / 2 + (padding.maxX ?? 0),
640
+ maxY: textBox.center.y + textBox.height / 2 + (padding.maxY ?? 0)
641
+ };
642
+ }
643
+ function boundsOverlap(a, b, eps = 1e-9) {
644
+ return a.minX < b.maxX - eps && a.maxX > b.minX + eps && a.minY < b.maxY - eps && a.maxY > b.minY + eps;
645
+ }
646
+ function rectIntersectsAnyTextBox(bounds, inputProblem) {
647
+ for (const textBox of inputProblem.textBoxes ?? []) {
648
+ if (boundsOverlap(bounds, getTextBoxBounds(textBox))) return true;
649
+ }
650
+ return false;
651
+ }
652
+
633
653
  // lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/calculateDirectShortPath.ts
634
654
  import { calculateElbow } from "calculate-elbow";
635
655
  var MAX_SHORT_TRACE_DISTANCE = 0.15;
@@ -875,12 +895,8 @@ var candidateMidsFromSet = (axis, colliding, collisionRects, aabb, opts = {}) =>
875
895
  }
876
896
  };
877
897
 
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
- };
898
+ // lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/pathOps.ts
899
+ var EPS3 = 1e-9;
884
900
  var hasOnlyNonzeroOrthogonalSegments = (path) => path.every((point, index) => {
885
901
  const nextPoint = path[index + 1];
886
902
  if (!nextPoint) return true;
@@ -889,57 +905,6 @@ var hasOnlyNonzeroOrthogonalSegments = (path) => path.every((point, index) => {
889
905
  }
890
906
  return Math.abs(point.x - nextPoint.x) + Math.abs(point.y - nextPoint.y) > 0;
891
907
  });
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
908
  var shiftSegmentOrth = (pts, segIndex, axis, newCoord, eps = EPS3) => {
944
909
  if (segIndex < 0 || segIndex >= pts.length - 1) {
945
910
  return null;
@@ -1001,6 +966,121 @@ var pathKey = (pts, decimals = 6) => {
1001
966
  return key;
1002
967
  };
1003
968
 
969
+ // lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/generateEndpointCollisionDetours.ts
970
+ var getSegmentAxis = (start, end) => {
971
+ if (isVertical(start, end)) return "x";
972
+ if (isHorizontal(start, end)) return "y";
973
+ return null;
974
+ };
975
+ var generateEndpointCollisionDetours = ({
976
+ path,
977
+ collidingSegmentIndex,
978
+ obstacle
979
+ }) => {
980
+ if (path.length < 3 || path.length > 4) return [];
981
+ const lastSegmentIndex = path.length - 2;
982
+ if (collidingSegmentIndex !== 0 && collidingSegmentIndex !== lastSegmentIndex) {
983
+ return [];
984
+ }
985
+ const shouldReverse = collidingSegmentIndex === lastSegmentIndex;
986
+ const orderedPath = shouldReverse ? [...path].reverse() : path;
987
+ const [start, corner, end] = orderedPath;
988
+ const firstSegmentAxis = getSegmentAxis(start, corner);
989
+ const secondSegmentAxis = getSegmentAxis(corner, end);
990
+ if (!firstSegmentAxis || !secondSegmentAxis) return [];
991
+ if (firstSegmentAxis === secondSegmentAxis) return [];
992
+ const escapeCoordinates = [
993
+ ...midBetweenPointAndRect(secondSegmentAxis, start, obstacle),
994
+ ...midBetweenPointAndRect(secondSegmentAxis, end, obstacle)
995
+ ];
996
+ const detourCoordinates = [
997
+ ...midBetweenPointAndRect(firstSegmentAxis, start, obstacle),
998
+ ...midBetweenPointAndRect(firstSegmentAxis, end, obstacle)
999
+ ];
1000
+ const detours = [];
1001
+ for (const escapeCoordinate of [...new Set(escapeCoordinates)]) {
1002
+ for (const detourCoordinate of [...new Set(detourCoordinates)]) {
1003
+ const orderedDetourPrefix = firstSegmentAxis === "y" ? [
1004
+ start,
1005
+ { x: escapeCoordinate, y: start.y },
1006
+ { x: escapeCoordinate, y: detourCoordinate },
1007
+ { x: end.x, y: detourCoordinate },
1008
+ end
1009
+ ] : [
1010
+ start,
1011
+ { x: start.x, y: escapeCoordinate },
1012
+ { x: detourCoordinate, y: escapeCoordinate },
1013
+ { x: detourCoordinate, y: end.y },
1014
+ end
1015
+ ];
1016
+ const orderedDetour = [...orderedDetourPrefix, ...orderedPath.slice(3)];
1017
+ const detour = shouldReverse ? orderedDetour.reverse() : orderedDetour;
1018
+ if (hasOnlyNonzeroOrthogonalSegments(detour)) detours.push(detour);
1019
+ }
1020
+ }
1021
+ return detours;
1022
+ };
1023
+
1024
+ // lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/generateInternalSegmentCollisionDetours.ts
1025
+ var EPS4 = 1e-9;
1026
+ var DEFAULT_CLEARANCE = 0.2;
1027
+ var isStrictlyBetween = (value, a, b) => value > Math.min(a, b) + EPS4 && value < Math.max(a, b) - EPS4;
1028
+ var generateInternalSegmentCollisionDetours = ({
1029
+ path,
1030
+ collidingSegmentIndex,
1031
+ obstacle,
1032
+ clearance = DEFAULT_CLEARANCE
1033
+ }) => {
1034
+ if (collidingSegmentIndex <= 0 || collidingSegmentIndex >= path.length - 2) {
1035
+ return [];
1036
+ }
1037
+ const start = path[collidingSegmentIndex];
1038
+ const end = path[collidingSegmentIndex + 1];
1039
+ const detourPoints = [];
1040
+ if (isHorizontal(start, end)) {
1041
+ const movingRight = start.x < end.x;
1042
+ const entryX = movingRight ? obstacle.minX - clearance : obstacle.maxX + clearance;
1043
+ const exitX = movingRight ? obstacle.maxX + clearance : obstacle.minX - clearance;
1044
+ if (!isStrictlyBetween(entryX, start.x, end.x) || !isStrictlyBetween(exitX, start.x, end.x)) {
1045
+ return [];
1046
+ }
1047
+ for (const detourY of [
1048
+ obstacle.minY - clearance,
1049
+ obstacle.maxY + clearance
1050
+ ]) {
1051
+ detourPoints.push([
1052
+ { x: entryX, y: start.y },
1053
+ { x: entryX, y: detourY },
1054
+ { x: exitX, y: detourY },
1055
+ { x: exitX, y: end.y }
1056
+ ]);
1057
+ }
1058
+ } else if (isVertical(start, end)) {
1059
+ const movingUp = start.y < end.y;
1060
+ const entryY = movingUp ? obstacle.minY - clearance : obstacle.maxY + clearance;
1061
+ const exitY = movingUp ? obstacle.maxY + clearance : obstacle.minY - clearance;
1062
+ if (!isStrictlyBetween(entryY, start.y, end.y) || !isStrictlyBetween(exitY, start.y, end.y)) {
1063
+ return [];
1064
+ }
1065
+ for (const detourX of [
1066
+ obstacle.minX - clearance,
1067
+ obstacle.maxX + clearance
1068
+ ]) {
1069
+ detourPoints.push([
1070
+ { x: start.x, y: entryY },
1071
+ { x: detourX, y: entryY },
1072
+ { x: detourX, y: exitY },
1073
+ { x: end.x, y: exitY }
1074
+ ]);
1075
+ }
1076
+ }
1077
+ return detourPoints.map((points) => [
1078
+ ...path.slice(0, collidingSegmentIndex + 1),
1079
+ ...points,
1080
+ ...path.slice(collidingSegmentIndex + 1)
1081
+ ]).filter(hasOnlyNonzeroOrthogonalSegments);
1082
+ };
1083
+
1004
1084
  // lib/solvers/GuidelinesSolver/getInputChipBounds.ts
1005
1085
  function getInputChipBounds(chip) {
1006
1086
  const halfWidth = chip.width / 2;
@@ -1013,26 +1093,8 @@ function getInputChipBounds(chip) {
1013
1093
  };
1014
1094
  }
1015
1095
 
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
1096
  // lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/rect.ts
1097
+ var isTextBoxObstacle = (obstacle) => obstacle.kind === "text_box";
1036
1098
  var chipToRect = (chip) => {
1037
1099
  const b = getInputChipBounds(chip);
1038
1100
  return { kind: "chip", chipId: chip.chipId, ...b };
@@ -1096,13 +1158,11 @@ var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
1096
1158
  this.obstacles = getObstacleRects(this.inputProblem, {
1097
1159
  textBoxPadding: this.getTextBoxPaddingForConnectionPair()
1098
1160
  });
1099
- this.textObstacles = new Set(
1100
- this.obstacles.filter((r) => r.kind === "text_box")
1101
- );
1161
+ this.textObstacles = new Set(this.obstacles.filter(isTextBoxObstacle));
1102
1162
  const endpointChipIds = new Set(this.pins.map((pin) => pin.chipId));
1103
1163
  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)
1164
+ endpointChipIds.size > 1 ? this.obstacles.filter(isTextBoxObstacle).filter(
1165
+ (obstacle) => obstacle.textBox.chipId !== void 0 && endpointChipIds.has(obstacle.textBox.chipId)
1106
1166
  ) : []
1107
1167
  );
1108
1168
  const [pin1, pin2] = this.pins;
@@ -1290,7 +1350,10 @@ var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
1290
1350
  path[adjacentEndpointSegIndex + 1]
1291
1351
  )) {
1292
1352
  for (const textObstacle of this.endpointTextObstacles) {
1293
- excludedRects.add(textObstacle);
1353
+ const textBounds = getTextBoxBounds(textObstacle.textBox);
1354
+ if (!segmentIntersectsRect(segmentStart, segmentEnd, textBounds)) {
1355
+ excludedRects.add(textObstacle);
1356
+ }
1294
1357
  }
1295
1358
  }
1296
1359
  return excludedRects;
@@ -1299,8 +1362,8 @@ var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
1299
1362
  if (!collision) {
1300
1363
  const first = path[0];
1301
1364
  const last = path[path.length - 1];
1302
- const EPS12 = 1e-9;
1303
- const samePoint = (p, q) => Math.abs(p.x - q.x) < EPS12 && Math.abs(p.y - q.y) < EPS12;
1365
+ const EPS13 = 1e-9;
1366
+ const samePoint = (p, q) => Math.abs(p.x - q.x) < EPS13 && Math.abs(p.y - q.y) < EPS13;
1304
1367
  if (samePoint(first, { x: PA.x, y: PA.y }) && samePoint(last, { x: PB.x, y: PB.y })) {
1305
1368
  this.solvedTracePath = path;
1306
1369
  this.solved = true;
@@ -1373,14 +1436,7 @@ var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
1373
1436
  candidates.push(...mids);
1374
1437
  }
1375
1438
  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;
1439
+ const addPathCandidate = (newPath) => {
1384
1440
  const key = pathKey(newPath);
1385
1441
  if (this.visited.has(key)) return;
1386
1442
  this.visited.add(key);
@@ -1393,10 +1449,28 @@ var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
1393
1449
  pinBandPenalty: this.getPinBandPenalty(newPath)
1394
1450
  });
1395
1451
  };
1452
+ const addShiftedCandidate = (candidateSegIndex, candidateAxis, coord) => {
1453
+ const newPath = shiftSegmentOrth(
1454
+ path,
1455
+ candidateSegIndex,
1456
+ candidateAxis,
1457
+ coord
1458
+ );
1459
+ if (newPath) addPathCandidate(newPath);
1460
+ };
1461
+ const lastSegIndex = path.length - 2;
1462
+ if (this.connectionPair === void 0 && rect.kind === "text_box" && this.endpointTextObstacles.has(rect) && !collisionRects.has(rect) && originalSegIndex > 0 && originalSegIndex < lastSegIndex) {
1463
+ for (const detour of generateInternalSegmentCollisionDetours({
1464
+ path,
1465
+ collidingSegmentIndex: originalSegIndex,
1466
+ obstacle: rect
1467
+ })) {
1468
+ addPathCandidate(detour);
1469
+ }
1470
+ }
1396
1471
  for (const coord of candidates) {
1397
1472
  addShiftedCandidate(segIndex, axis, coord);
1398
1473
  }
1399
- const lastSegIndex = path.length - 2;
1400
1474
  const adjacentSegmentIndexes = originalSegIndex === 1 ? [2] : originalSegIndex === lastSegIndex - 1 ? [lastSegIndex - 2] : [];
1401
1475
  for (const adjacentSegIndex of adjacentSegmentIndexes) {
1402
1476
  if (adjacentSegIndex < 0 || adjacentSegIndex >= lastSegIndex) continue;
@@ -1573,13 +1647,13 @@ var applyJogToTerminalSegment = ({
1573
1647
  segmentIndex: si,
1574
1648
  offset,
1575
1649
  JOG_SIZE,
1576
- EPS: EPS12 = 1e-6
1650
+ EPS: EPS13 = 1e-6
1577
1651
  }) => {
1578
1652
  if (si !== 0 && si !== pts.length - 2) return;
1579
1653
  const start = pts[si];
1580
1654
  const end = pts[si + 1];
1581
- const isVertical5 = Math.abs(start.x - end.x) < EPS12;
1582
- const isHorizontal4 = Math.abs(start.y - end.y) < EPS12;
1655
+ const isVertical5 = Math.abs(start.x - end.x) < EPS13;
1656
+ const isHorizontal4 = Math.abs(start.y - end.y) < EPS13;
1583
1657
  if (!isVertical5 && !isHorizontal4) return;
1584
1658
  const segDir = isVertical5 ? end.y > start.y ? 1 : -1 : end.x > start.x ? 1 : -1;
1585
1659
  if (pts.length === 2) {
@@ -1652,7 +1726,7 @@ var applyJogToTerminalSegment = ({
1652
1726
  };
1653
1727
 
1654
1728
  // lib/solvers/TraceOverlapShiftSolver/TraceOverlapIssueSolver/TraceOverlapIssueSolver.ts
1655
- var EPS4 = 1e-6;
1729
+ var EPS5 = 1e-6;
1656
1730
  var TraceOverlapIssueSolver = class extends BaseSolver {
1657
1731
  overlappingTraceSegments;
1658
1732
  interactionKind;
@@ -1741,7 +1815,7 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
1741
1815
  }
1742
1816
  applyOffsets(offsets) {
1743
1817
  const correctedTraceMap = { ...this.correctedTraceMap };
1744
- const eq = (a, b) => Math.abs(a - b) < EPS4;
1818
+ const eq = (a, b) => Math.abs(a - b) < EPS5;
1745
1819
  const samePoint = (p, q) => !!p && !!q && eq(p.x, q.x) && eq(p.y, q.y);
1746
1820
  this.overlappingTraceSegments.forEach((group, groupIndex) => {
1747
1821
  const offset = offsets[groupIndex];
@@ -1763,9 +1837,9 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
1763
1837
  const start = pts[si];
1764
1838
  const end = pts[si + 1];
1765
1839
  if (!start || !end) continue;
1766
- if (Math.abs(start.x - end.x) < EPS4) {
1840
+ if (Math.abs(start.x - end.x) < EPS5) {
1767
1841
  segmentAxes.set(si, "vertical");
1768
- } else if (Math.abs(start.y - end.y) < EPS4) {
1842
+ } else if (Math.abs(start.y - end.y) < EPS5) {
1769
1843
  segmentAxes.set(si, "horizontal");
1770
1844
  }
1771
1845
  }
@@ -1780,7 +1854,7 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
1780
1854
  segmentIndex: si,
1781
1855
  offset,
1782
1856
  JOG_SIZE,
1783
- EPS: EPS4
1857
+ EPS: EPS5
1784
1858
  });
1785
1859
  } else {
1786
1860
  const start = pts[si];
@@ -1867,7 +1941,7 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
1867
1941
  max = obstacle.maxY;
1868
1942
  }
1869
1943
  const next = orig + candidateOffset;
1870
- return orig <= min + EPS4 && next >= max - EPS4 || orig >= max - EPS4 && next <= min + EPS4;
1944
+ return orig <= min + EPS5 && next >= max - EPS5 || orig >= max - EPS5 && next <= min + EPS5;
1871
1945
  });
1872
1946
  const edges = blockingCollisions.map(({ start, isVertical: isVertical5, obstacle }) => {
1873
1947
  if (isVertical5) {
@@ -1904,8 +1978,8 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
1904
1978
  const start = trace.tracePath[traceSegmentIndex];
1905
1979
  const end = trace.tracePath[traceSegmentIndex + 1];
1906
1980
  if (!start || !end) continue;
1907
- const isVertical5 = Math.abs(start.x - end.x) < EPS4;
1908
- const isHorizontal4 = Math.abs(start.y - end.y) < EPS4;
1981
+ const isVertical5 = Math.abs(start.x - end.x) < EPS5;
1982
+ const isHorizontal4 = Math.abs(start.y - end.y) < EPS5;
1909
1983
  if (!isVertical5 && !isHorizontal4) continue;
1910
1984
  const shiftedSegment = isVertical5 ? [
1911
1985
  { ...start, x: start.x + offset },
@@ -1997,7 +2071,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
1997
2071
  return islands;
1998
2072
  }
1999
2073
  findNextOverlapIssue() {
2000
- const EPS12 = 2e-3;
2074
+ const EPS13 = 2e-3;
2001
2075
  const netIds = Object.keys(this.traceNetIslands);
2002
2076
  for (let i = 0; i < netIds.length; i++) {
2003
2077
  for (let j = i + 1; j < netIds.length; j++) {
@@ -2024,7 +2098,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
2024
2098
  segmentBIndex,
2025
2099
  rangeOverlap
2026
2100
  }) => {
2027
- if (rangeOverlap > EPS12) {
2101
+ if (rangeOverlap > EPS13) {
2028
2102
  const keyA = `${pathAIndex}:${segmentAIndex}`;
2029
2103
  const keyB = `${pathBIndex}:${segmentBIndex}`;
2030
2104
  if (!seenA.has(keyA)) {
@@ -2043,7 +2117,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
2043
2117
  }
2044
2118
  return;
2045
2119
  }
2046
- if (rangeOverlap < -EPS12) return;
2120
+ if (rangeOverlap < -EPS13) return;
2047
2121
  const pathA = pathsA[pathAIndex].tracePath;
2048
2122
  const pathB = pathsB[pathBIndex].tracePath;
2049
2123
  const segmentAStart = pathA[segmentAIndex];
@@ -2066,8 +2140,8 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
2066
2140
  for (let sa = 0; sa < ptsA.length - 1; sa++) {
2067
2141
  const a1 = ptsA[sa];
2068
2142
  const a2 = ptsA[sa + 1];
2069
- const aVert = Math.abs(a1.x - a2.x) < EPS12;
2070
- const aHorz = Math.abs(a1.y - a2.y) < EPS12;
2143
+ const aVert = Math.abs(a1.x - a2.x) < EPS13;
2144
+ const aHorz = Math.abs(a1.y - a2.y) < EPS13;
2071
2145
  if (!aVert && !aHorz) continue;
2072
2146
  for (let pb = 0; pb < pathsB.length; pb++) {
2073
2147
  const pathB = pathsB[pb];
@@ -2075,11 +2149,11 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
2075
2149
  for (let sb = 0; sb < ptsB.length - 1; sb++) {
2076
2150
  const b1 = ptsB[sb];
2077
2151
  const b2 = ptsB[sb + 1];
2078
- const bVert = Math.abs(b1.x - b2.x) < EPS12;
2079
- const bHorz = Math.abs(b1.y - b2.y) < EPS12;
2152
+ const bVert = Math.abs(b1.x - b2.x) < EPS13;
2153
+ const bHorz = Math.abs(b1.y - b2.y) < EPS13;
2080
2154
  if (!bVert && !bHorz) continue;
2081
2155
  if (aVert && bVert) {
2082
- if (Math.abs(a1.x - b1.x) < EPS12) {
2156
+ if (Math.abs(a1.x - b1.x) < EPS13) {
2083
2157
  recordCollinearInteraction({
2084
2158
  pathAIndex: pa,
2085
2159
  segmentAIndex: sa,
@@ -2089,7 +2163,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
2089
2163
  });
2090
2164
  }
2091
2165
  } else if (aHorz && bHorz) {
2092
- if (Math.abs(a1.y - b1.y) < EPS12) {
2166
+ if (Math.abs(a1.y - b1.y) < EPS13) {
2093
2167
  recordCollinearInteraction({
2094
2168
  pathAIndex: pa,
2095
2169
  segmentAIndex: sa,
@@ -2145,14 +2219,14 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
2145
2219
  return null;
2146
2220
  }
2147
2221
  findNextDiagonalSegment() {
2148
- const EPS12 = 2e-3;
2222
+ const EPS13 = 2e-3;
2149
2223
  for (const mspPairId in this.correctedTraceMap) {
2150
2224
  const tracePath = this.correctedTraceMap[mspPairId].tracePath;
2151
2225
  for (let i = 0; i < tracePath.length - 1; i++) {
2152
2226
  const p1 = tracePath[i];
2153
2227
  const p2 = tracePath[i + 1];
2154
- const isHorizontal4 = Math.abs(p1.y - p2.y) < EPS12;
2155
- const isVertical5 = Math.abs(p1.x - p2.x) < EPS12;
2228
+ const isHorizontal4 = Math.abs(p1.y - p2.y) < EPS13;
2229
+ const isVertical5 = Math.abs(p1.x - p2.x) < EPS13;
2156
2230
  if (!isHorizontal4 && !isVertical5) {
2157
2231
  return { mspPairId, tracePath, i, p1, p2 };
2158
2232
  }
@@ -2166,13 +2240,13 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
2166
2240
  return false;
2167
2241
  }
2168
2242
  const { mspPairId, tracePath, i, p1, p2 } = diagonalInfo;
2169
- const EPS12 = 2e-3;
2243
+ const EPS13 = 2e-3;
2170
2244
  const p0 = i > 0 ? tracePath[i - 1] : null;
2171
2245
  const p3 = i + 2 < tracePath.length ? tracePath[i + 2] : null;
2172
- const prevIsVertical = p0 ? Math.abs(p0.x - p1.x) < EPS12 : false;
2173
- const prevIsHorizontal = p0 ? Math.abs(p0.y - p1.y) < EPS12 : false;
2174
- const nextIsVertical = p3 ? Math.abs(p2.x - p3.x) < EPS12 : false;
2175
- const nextIsHorizontal = p3 ? Math.abs(p2.y - p3.y) < EPS12 : false;
2246
+ const prevIsVertical = p0 ? Math.abs(p0.x - p1.x) < EPS13 : false;
2247
+ const prevIsHorizontal = p0 ? Math.abs(p0.y - p1.y) < EPS13 : false;
2248
+ const nextIsVertical = p3 ? Math.abs(p2.x - p3.x) < EPS13 : false;
2249
+ const nextIsHorizontal = p3 ? Math.abs(p2.y - p3.y) < EPS13 : false;
2176
2250
  const elbow1 = { x: p1.x, y: p2.y };
2177
2251
  const elbow2 = { x: p2.x, y: p1.y };
2178
2252
  let score1 = 0;
@@ -2299,24 +2373,24 @@ var ChipObstacleSpatialIndex = class {
2299
2373
  };
2300
2374
 
2301
2375
  // lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/collisions.ts
2302
- function segmentIntersectsRect2(p1, p2, rect, EPS12 = 1e-9) {
2303
- const isVert = Math.abs(p1.x - p2.x) < EPS12;
2304
- const isHorz = Math.abs(p1.y - p2.y) < EPS12;
2376
+ function segmentIntersectsRect2(p1, p2, rect, EPS13 = 1e-9) {
2377
+ const isVert = Math.abs(p1.x - p2.x) < EPS13;
2378
+ const isHorz = Math.abs(p1.y - p2.y) < EPS13;
2305
2379
  if (!isVert && !isHorz) return false;
2306
2380
  if (isVert) {
2307
2381
  const x = p1.x;
2308
- if (x < rect.minX - EPS12 || x > rect.maxX + EPS12) return false;
2382
+ if (x < rect.minX - EPS13 || x > rect.maxX + EPS13) return false;
2309
2383
  const segMinY = Math.min(p1.y, p2.y);
2310
2384
  const segMaxY = Math.max(p1.y, p2.y);
2311
2385
  const overlap = Math.min(segMaxY, rect.maxY) - Math.max(segMinY, rect.minY);
2312
- return overlap > EPS12;
2386
+ return overlap > EPS13;
2313
2387
  } else {
2314
2388
  const y = p1.y;
2315
- if (y < rect.minY - EPS12 || y > rect.maxY + EPS12) return false;
2389
+ if (y < rect.minY - EPS13 || y > rect.maxY + EPS13) return false;
2316
2390
  const segMinX = Math.min(p1.x, p2.x);
2317
2391
  const segMaxX = Math.max(p1.x, p2.x);
2318
2392
  const overlap = Math.min(segMaxX, rect.maxX) - Math.max(segMinX, rect.minX);
2319
- return overlap > EPS12;
2393
+ return overlap > EPS13;
2320
2394
  }
2321
2395
  }
2322
2396
  function rectIntersectsAnyTrace(bounds, inputTraceMap, hostPathId, hostSegIndex) {
@@ -2713,14 +2787,14 @@ var SingleNetLabelPlacementSolver = class extends BaseSolver {
2713
2787
  };
2714
2788
  let bestCandidate = null;
2715
2789
  let bestScore = -Infinity;
2716
- const EPS12 = 1e-6;
2790
+ const EPS13 = 1e-6;
2717
2791
  for (const curr of tracesToScan) {
2718
2792
  const pts = curr.tracePath.slice();
2719
2793
  for (let si = 0; si < pts.length - 1; si++) {
2720
2794
  const a = pts[si];
2721
2795
  const b = pts[si + 1];
2722
- const isH = Math.abs(a.y - b.y) < EPS12;
2723
- const isV = Math.abs(a.x - b.x) < EPS12;
2796
+ const isH = Math.abs(a.y - b.y) < EPS13;
2797
+ const isV = Math.abs(a.x - b.x) < EPS13;
2724
2798
  if (!isH && !isV) continue;
2725
2799
  const segmentAllowed = isH ? ["y+", "y-"] : ["x+", "x-"];
2726
2800
  const candidateOrients = orientations.filter(
@@ -3820,7 +3894,7 @@ var SingleOverlapSolver = class extends BaseSolver {
3820
3894
  paddingBuffer: effectivePadding
3821
3895
  // Use the calculated, larger padding
3822
3896
  });
3823
- const getPathLength2 = (pts) => {
3897
+ const getPathLength3 = (pts) => {
3824
3898
  let len = 0;
3825
3899
  for (let i = 0; i < pts.length - 1; i++) {
3826
3900
  const dx = pts[i + 1].x - pts[i].x;
@@ -3830,7 +3904,7 @@ var SingleOverlapSolver = class extends BaseSolver {
3830
3904
  return len;
3831
3905
  };
3832
3906
  this.queuedCandidatePaths = candidates.sort(
3833
- (a, b) => getPathLength2(a) - getPathLength2(b)
3907
+ (a, b) => getPathLength3(a) - getPathLength3(b)
3834
3908
  );
3835
3909
  this.obstacles = getObstacleRects(this.problem);
3836
3910
  }
@@ -5123,9 +5197,9 @@ var moveRailSegments = (trace, segments, coordinate) => {
5123
5197
  };
5124
5198
 
5125
5199
  // lib/solvers/RailNetLabelCornerPlacementSolver/geometry.ts
5126
- var EPS5 = 1e-6;
5200
+ var EPS6 = 1e-6;
5127
5201
  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;
5202
+ 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
5203
  var getTraceCorners = (path) => {
5130
5204
  const corners = [];
5131
5205
  for (let i = 1; i < path.length - 1; i++) {
@@ -5149,11 +5223,11 @@ var getUniquePinCount = (pinIds) => new Set(pinIds).size;
5149
5223
  var isTraceCorner = (a, b, c) => getSegmentOrientation(a, b) !== getSegmentOrientation(b, c);
5150
5224
  var isPointOnSegment = (point, start, end) => {
5151
5225
  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;
5226
+ 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
5227
  }
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;
5228
+ 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
5229
  };
5156
- var getSegmentOrientation = (a, b) => Math.abs(a.y - b.y) <= EPS5 ? "horizontal" : "vertical";
5230
+ var getSegmentOrientation = (a, b) => Math.abs(a.y - b.y) <= EPS6 ? "horizontal" : "vertical";
5157
5231
 
5158
5232
  // lib/solvers/TraceCleanupSolver/sameNetRailAlignment/preservesLabelAnchors.ts
5159
5233
  var getAnchoredTraceIds = (label, traces) => new Set(
@@ -5169,17 +5243,17 @@ var preservesLabelAnchors = (labels, before, after) => labels.every((label) => {
5169
5243
  });
5170
5244
 
5171
5245
  // lib/solvers/Example28Solver/types.ts
5172
- var EPS6 = 1e-9;
5246
+ var EPS7 = 1e-9;
5173
5247
 
5174
5248
  // lib/solvers/Example28Solver/segmentRunsAlongRectBoundary.ts
5175
5249
  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;
5250
+ const isVertical5 = Math.abs(start.x - end.x) < EPS7;
5251
+ const isHorizontal4 = Math.abs(start.y - end.y) < EPS7;
5178
5252
  if (isVertical5) {
5179
- return Math.abs(start.x - rect.minX) < EPS6 || Math.abs(start.x - rect.maxX) < EPS6;
5253
+ return Math.abs(start.x - rect.minX) < EPS7 || Math.abs(start.x - rect.maxX) < EPS7;
5180
5254
  }
5181
5255
  if (isHorizontal4) {
5182
- return Math.abs(start.y - rect.minY) < EPS6 || Math.abs(start.y - rect.maxY) < EPS6;
5256
+ return Math.abs(start.y - rect.minY) < EPS7 || Math.abs(start.y - rect.maxY) < EPS7;
5183
5257
  }
5184
5258
  return false;
5185
5259
  };
@@ -5194,10 +5268,10 @@ var getPathLength = (path) => {
5194
5268
  return length;
5195
5269
  };
5196
5270
  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";
5271
+ var isAxisAlignedSegment = (start, end) => Math.abs(start.x - end.x) < EPS7 || Math.abs(start.y - end.y) < EPS7;
5272
+ var getSegmentOrientation2 = (start, end) => Math.abs(start.y - end.y) < EPS7 ? "horizontal" : "vertical";
5199
5273
  var projectPointToSegment = (point, start, end) => {
5200
- if (Math.abs(start.x - end.x) < EPS6) {
5274
+ if (Math.abs(start.x - end.x) < EPS7) {
5201
5275
  return {
5202
5276
  x: start.x,
5203
5277
  y: Math.min(
@@ -5206,7 +5280,7 @@ var projectPointToSegment = (point, start, end) => {
5206
5280
  )
5207
5281
  };
5208
5282
  }
5209
- if (Math.abs(start.y - end.y) < EPS6) {
5283
+ if (Math.abs(start.y - end.y) < EPS7) {
5210
5284
  return {
5211
5285
  x: Math.min(
5212
5286
  Math.max(point.x, Math.min(start.x, end.x)),
@@ -5249,9 +5323,9 @@ var findSegmentContainingPoint = (path, point) => {
5249
5323
  };
5250
5324
  var isPointWithinSegmentPrimaryRange = (point, segment) => {
5251
5325
  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;
5326
+ return point.x >= Math.min(segment.start.x, segment.end.x) - EPS7 && point.x <= Math.max(segment.start.x, segment.end.x) + EPS7;
5253
5327
  }
5254
- return point.y >= Math.min(segment.start.y, segment.end.y) - EPS6 && point.y <= Math.max(segment.start.y, segment.end.y) + EPS6;
5328
+ return point.y >= Math.min(segment.start.y, segment.end.y) - EPS7 && point.y <= Math.max(segment.start.y, segment.end.y) + EPS7;
5255
5329
  };
5256
5330
  var findPreferredReroutedSegment = (path, originalSegmentIndex, originalSegmentCount, orientation, anchorPoint) => {
5257
5331
  const matchingSegments = getSegments(path).filter(
@@ -5270,27 +5344,27 @@ var projectPointToPath = (point, path) => {
5270
5344
  let bestDistance = Number.POSITIVE_INFINITY;
5271
5345
  for (let i = 0; i < path.length - 1; i++) {
5272
5346
  const projectedPoint = projectPointToSegment(point, path[i], path[i + 1]);
5273
- const distance4 = getDistance2(point, projectedPoint);
5274
- if (distance4 < bestDistance) {
5347
+ const distance5 = getDistance2(point, projectedPoint);
5348
+ if (distance5 < bestDistance) {
5275
5349
  bestPoint = projectedPoint;
5276
- bestDistance = distance4;
5350
+ bestDistance = distance5;
5277
5351
  }
5278
5352
  }
5279
5353
  return bestPoint;
5280
5354
  };
5281
5355
  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;
5356
+ const aVertical = Math.abs(a1.x - a2.x) < EPS7;
5357
+ const bVertical = Math.abs(b1.x - b2.x) < EPS7;
5358
+ const between = (value, p1, p2) => value >= Math.min(p1, p2) - EPS7 && value <= Math.max(p1, p2) + EPS7;
5285
5359
  if (aVertical && bVertical) {
5286
- if (Math.abs(a1.x - b1.x) > EPS6) return false;
5360
+ if (Math.abs(a1.x - b1.x) > EPS7) return false;
5287
5361
  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;
5362
+ return overlap > EPS7;
5289
5363
  }
5290
5364
  if (!aVertical && !bVertical) {
5291
- if (Math.abs(a1.y - b1.y) > EPS6) return false;
5365
+ if (Math.abs(a1.y - b1.y) > EPS7) return false;
5292
5366
  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;
5367
+ return overlap > EPS7;
5294
5368
  }
5295
5369
  const verticalA = aVertical ? a1 : b1;
5296
5370
  const verticalB = aVertical ? a2 : b2;
@@ -5652,7 +5726,7 @@ var getTraceObstacles = (allTraces, excludeTraceId) => {
5652
5726
 
5653
5727
  // lib/solvers/TraceCleanupSolver/sub-solver/findIntersectionsWithObstacles.ts
5654
5728
  import { getSegmentIntersection } from "@tscircuit/math-utils/line-intersections";
5655
- var EPS7 = 1e-6;
5729
+ var EPS8 = 1e-6;
5656
5730
  var findIntersectionsWithObstacles = (p1, p2, obstacles) => {
5657
5731
  const intersections = [];
5658
5732
  for (const obstacle of obstacles) {
@@ -5671,17 +5745,17 @@ var findIntersectionsWithObstacles = (p1, p2, obstacles) => {
5671
5745
  }
5672
5746
  return intersections;
5673
5747
  };
5674
- var isSamePoint = (first, second) => Math.abs(first.x - second.x) < EPS7 && Math.abs(first.y - second.y) < EPS7;
5748
+ var isSamePoint = (first, second) => Math.abs(first.x - second.x) < EPS8 && Math.abs(first.y - second.y) < EPS8;
5675
5749
  var findPerpendicularPathCrossings = (path, otherPath) => {
5676
5750
  const crossings = [];
5677
5751
  for (let pathSegmentIndex = 1; pathSegmentIndex < path.length - 2; pathSegmentIndex++) {
5678
5752
  const start = path[pathSegmentIndex];
5679
5753
  const end = path[pathSegmentIndex + 1];
5680
- const isVertical5 = Math.abs(start.x - end.x) < EPS7;
5754
+ const isVertical5 = Math.abs(start.x - end.x) < EPS8;
5681
5755
  for (let otherPathSegmentIndex = 1; otherPathSegmentIndex < otherPath.length - 2; otherPathSegmentIndex++) {
5682
5756
  const otherStart = otherPath[otherPathSegmentIndex];
5683
5757
  const otherEnd = otherPath[otherPathSegmentIndex + 1];
5684
- const otherIsVertical = Math.abs(otherStart.x - otherEnd.x) < EPS7;
5758
+ const otherIsVertical = Math.abs(otherStart.x - otherEnd.x) < EPS8;
5685
5759
  if (isVertical5 === otherIsVertical) continue;
5686
5760
  const intersection = getSegmentIntersection(
5687
5761
  start,
@@ -5701,8 +5775,8 @@ var findPerpendicularPathCrossings = (path, otherPath) => {
5701
5775
  };
5702
5776
 
5703
5777
  // lib/solvers/TraceCleanupSolver/sub-solver/generateLShapeRerouteCandidates.ts
5704
- var EPS8 = 1e-6;
5705
- var isVertical3 = (a, b, eps = EPS8) => Math.abs(a.x - b.x) < eps;
5778
+ var EPS9 = 1e-6;
5779
+ var isVertical3 = (a, b, eps = EPS9) => Math.abs(a.x - b.x) < eps;
5706
5780
  var generateLShapeRerouteCandidates = ({
5707
5781
  lShape,
5708
5782
  rectangle,
@@ -5715,7 +5789,7 @@ var generateLShapeRerouteCandidates = ({
5715
5789
  let c2;
5716
5790
  let i1_padded = interactionPoint1;
5717
5791
  let i2_padded = interactionPoint2;
5718
- if (Math.abs(p2.x - x) < EPS8 && Math.abs(p2.y - (y + height)) < EPS8) {
5792
+ if (Math.abs(p2.x - x) < EPS9 && Math.abs(p2.y - (y + height)) < EPS9) {
5719
5793
  c2 = { x: x + width + padding, y: y - padding };
5720
5794
  if (isVertical3(p1, p2)) {
5721
5795
  i1_padded = { x: interactionPoint1.x, y: interactionPoint1.y - padding };
@@ -5727,7 +5801,7 @@ var generateLShapeRerouteCandidates = ({
5727
5801
  } else {
5728
5802
  i2_padded = { x: interactionPoint2.x + padding, y: interactionPoint2.y };
5729
5803
  }
5730
- } else if (Math.abs(p2.x - (x + width)) < EPS8 && Math.abs(p2.y - (y + height)) < EPS8) {
5804
+ } else if (Math.abs(p2.x - (x + width)) < EPS9 && Math.abs(p2.y - (y + height)) < EPS9) {
5731
5805
  c2 = { x: x - padding, y: y - padding };
5732
5806
  if (isVertical3(p1, p2)) {
5733
5807
  i1_padded = { x: interactionPoint1.x, y: interactionPoint1.y - padding };
@@ -5739,7 +5813,7 @@ var generateLShapeRerouteCandidates = ({
5739
5813
  } else {
5740
5814
  i2_padded = { x: interactionPoint2.x - padding, y: interactionPoint2.y };
5741
5815
  }
5742
- } else if (Math.abs(p2.x - x) < EPS8 && Math.abs(p2.y - y) < EPS8) {
5816
+ } else if (Math.abs(p2.x - x) < EPS9 && Math.abs(p2.y - y) < EPS9) {
5743
5817
  c2 = { x: x + width + padding, y: y + height + padding };
5744
5818
  if (isVertical3(p1, p2)) {
5745
5819
  i1_padded = { x: interactionPoint1.x, y: interactionPoint1.y + padding };
@@ -5751,7 +5825,7 @@ var generateLShapeRerouteCandidates = ({
5751
5825
  } else {
5752
5826
  i2_padded = { x: interactionPoint2.x + padding, y: interactionPoint2.y };
5753
5827
  }
5754
- } else if (Math.abs(p2.x - (x + width)) < EPS8 && Math.abs(p2.y - y) < EPS8) {
5828
+ } else if (Math.abs(p2.x - (x + width)) < EPS9 && Math.abs(p2.y - y) < EPS9) {
5755
5829
  c2 = { x: x - padding, y: y + height + padding };
5756
5830
  if (isVertical3(p1, p2)) {
5757
5831
  i1_padded = { x: interactionPoint1.x, y: interactionPoint1.y + padding };
@@ -5779,7 +5853,7 @@ var generatePerpendicularTraceDetours = ({
5779
5853
  const buildDetours = (path, index) => {
5780
5854
  const start = path[index];
5781
5855
  const end = path[index + 1];
5782
- const movingAxis = Math.abs(start.x - end.x) < EPS8 ? "y" : "x";
5856
+ const movingAxis = Math.abs(start.x - end.x) < EPS9 ? "y" : "x";
5783
5857
  const detourAxis = movingAxis === "x" ? "y" : "x";
5784
5858
  const gate = obstacleStart[movingAxis] + Math.sign(start[movingAxis] - obstacleStart[movingAxis]) * clearance;
5785
5859
  const obstacleRange = [obstacleStart[detourAxis], obstacleEnd[detourAxis]];
@@ -6377,9 +6451,9 @@ var UntangleTraceSubsolver = class extends BaseSolver {
6377
6451
  };
6378
6452
 
6379
6453
  // lib/solvers/TraceCleanupSolver/is4PointRectangle.ts
6380
- var EPS9 = 1e-6;
6381
- var sameX = (a, b) => Math.abs(a.x - b.x) <= EPS9;
6382
- var sameY = (a, b) => Math.abs(a.y - b.y) <= EPS9;
6454
+ var EPS10 = 1e-6;
6455
+ var sameX = (a, b) => Math.abs(a.x - b.x) <= EPS10;
6456
+ var sameY = (a, b) => Math.abs(a.y - b.y) <= EPS10;
6383
6457
  var is4PointRectangle = (path) => {
6384
6458
  if (path.length !== 4) return false;
6385
6459
  const [p0, p1, p2, p3] = path;
@@ -6654,7 +6728,7 @@ var doesPathRunAlongChipBoundary = (path, chipObstacles) => {
6654
6728
  const end = path[i + 1];
6655
6729
  for (const obstacle of chipObstacles) {
6656
6730
  if (!segmentRunsAlongRectBoundary(start, end, obstacle)) continue;
6657
- if (getSegmentOverlapWithRectSpan(start, end, obstacle) > EPS6) {
6731
+ if (getSegmentOverlapWithRectSpan(start, end, obstacle) > EPS7) {
6658
6732
  return true;
6659
6733
  }
6660
6734
  }
@@ -6669,13 +6743,13 @@ var getChipBoundaryOverlap = (path, chipObstacles) => {
6669
6743
  for (const obstacle of chipObstacles) {
6670
6744
  if (!segmentRunsAlongRectBoundary(start, end, obstacle)) continue;
6671
6745
  const overlap = getSegmentOverlapWithRectSpan(start, end, obstacle);
6672
- if (overlap > EPS6) total += overlap;
6746
+ if (overlap > EPS7) total += overlap;
6673
6747
  }
6674
6748
  }
6675
6749
  return total;
6676
6750
  };
6677
6751
  var getSegmentOverlapWithRectSpan = (start, end, rect) => {
6678
- const isVertical5 = Math.abs(start.x - end.x) < EPS6;
6752
+ const isVertical5 = Math.abs(start.x - end.x) < EPS7;
6679
6753
  if (isVertical5) {
6680
6754
  return Math.min(Math.max(start.y, end.y), rect.maxY) - Math.max(Math.min(start.y, end.y), rect.minY);
6681
6755
  }
@@ -7087,11 +7161,11 @@ var getLabelHugDistance = (tracePath, obstacleLabel) => {
7087
7161
  obstacleLabel.width,
7088
7162
  obstacleLabel.height
7089
7163
  );
7090
- let distance4 = 0;
7164
+ let distance5 = 0;
7091
7165
  for (const point of tracePath) {
7092
- distance4 += getPointDistanceFromRect(point, bounds);
7166
+ distance5 += getPointDistanceFromRect(point, bounds);
7093
7167
  }
7094
- return distance4;
7168
+ return distance5;
7095
7169
  };
7096
7170
  var getPointDistanceFromRect = (point, rect) => {
7097
7171
  const dx = Math.max(rect.minX - point.x, 0, point.x - rect.maxX);
@@ -7368,16 +7442,16 @@ var Example28Solver = class extends BaseSolver {
7368
7442
  const outward = dir(label.orientation);
7369
7443
  if (outward.x === 0 && outward.y === 0) return null;
7370
7444
  for (let step = 1; step <= LABEL_MAX_OUTWARD_STEPS; step++) {
7371
- const distance4 = step * LABEL_OUTWARD_STEP;
7445
+ const distance5 = step * LABEL_OUTWARD_STEP;
7372
7446
  const candidate = {
7373
7447
  ...label,
7374
7448
  anchorPoint: {
7375
- x: label.anchorPoint.x + outward.x * distance4,
7376
- y: label.anchorPoint.y + outward.y * distance4
7449
+ x: label.anchorPoint.x + outward.x * distance5,
7450
+ y: label.anchorPoint.y + outward.y * distance5
7377
7451
  },
7378
7452
  center: {
7379
- x: label.center.x + outward.x * distance4,
7380
- y: label.center.y + outward.y * distance4
7453
+ x: label.center.x + outward.x * distance5,
7454
+ y: label.center.y + outward.y * distance5
7381
7455
  }
7382
7456
  };
7383
7457
  const candidateWithClearance = {
@@ -7439,16 +7513,16 @@ var createPortOnlyLabelConnectorTrace = ({
7439
7513
  // lib/solvers/AvailableNetOrientationSolver/constants.ts
7440
7514
  var LABEL_SEARCH_STEP = 0.05;
7441
7515
  var WICK_CLEARANCE = 1e-3;
7442
- var EPS10 = 1e-9;
7443
- var TRACE_BOUNDARY_TOLERANCE = WICK_CLEARANCE + EPS10;
7516
+ var EPS11 = 1e-9;
7517
+ var TRACE_BOUNDARY_TOLERANCE = WICK_CLEARANCE + EPS11;
7444
7518
  var CANDIDATE_SELECTED_COLOR2 = "blue";
7445
7519
  var CANDIDATE_REJECTED_COLOR2 = "red";
7446
7520
 
7447
7521
  // lib/solvers/AvailableNetOrientationSolver/geometry.ts
7448
7522
  var isYOrientation = (orientation) => orientation === "y+" || orientation === "y-";
7449
7523
  var isXOrientation = (orientation) => orientation === "x+" || orientation === "x-";
7450
- var rectsOverlap2 = (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;
7451
- var rangesOverlap = (minA, maxA, minB, maxB) => Math.min(maxA, maxB) - Math.max(minA, minB) > EPS10;
7524
+ 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;
7525
+ var rangesOverlap = (minA, maxA, minB, maxB) => Math.min(maxA, maxB) - Math.max(minA, minB) > EPS11;
7452
7526
  var traceCrossesBoundsInterior = (bounds, traceMap) => {
7453
7527
  for (const trace of Object.values(traceMap)) {
7454
7528
  const points = trace.tracePath;
@@ -7471,7 +7545,7 @@ var segmentCrossesBoundsInterior = (p1, p2, bounds) => {
7471
7545
  return false;
7472
7546
  }
7473
7547
  if (sameX2(p1, p2)) {
7474
- if (p1.x <= interiorBounds.minX + EPS10 || p1.x >= interiorBounds.maxX - EPS10) {
7548
+ if (p1.x <= interiorBounds.minX + EPS11 || p1.x >= interiorBounds.maxX - EPS11) {
7475
7549
  return false;
7476
7550
  }
7477
7551
  return rangesOverlap(
@@ -7482,7 +7556,7 @@ var segmentCrossesBoundsInterior = (p1, p2, bounds) => {
7482
7556
  );
7483
7557
  }
7484
7558
  if (sameY2(p1, p2)) {
7485
- if (p1.y <= interiorBounds.minY + EPS10 || p1.y >= interiorBounds.maxY - EPS10) {
7559
+ if (p1.y <= interiorBounds.minY + EPS11 || p1.y >= interiorBounds.maxY - EPS11) {
7486
7560
  return false;
7487
7561
  }
7488
7562
  return rangesOverlap(
@@ -7538,8 +7612,8 @@ var simplifyOrthogonalPath = (path) => {
7538
7612
  return simplified;
7539
7613
  };
7540
7614
  var pointsEqual2 = (a, b) => sameX2(a, b) && sameY2(a, b);
7541
- var sameX2 = (a, b) => Math.abs(a.x - b.x) <= EPS10;
7542
- var sameY2 = (a, b) => Math.abs(a.y - b.y) <= EPS10;
7615
+ var sameX2 = (a, b) => Math.abs(a.x - b.x) <= EPS11;
7616
+ var sameY2 = (a, b) => Math.abs(a.y - b.y) <= EPS11;
7543
7617
  var getMaxSearchDistance = (inputProblem) => {
7544
7618
  const maxChipWidth = Math.max(
7545
7619
  ...inputProblem.chips.map((chip) => chip.width),
@@ -7794,11 +7868,11 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
7794
7868
  if (trace.globalConnNetId !== label.globalConnNetId) return false;
7795
7869
  const path = trace.tracePath;
7796
7870
  return path.some((point, pointIndex) => {
7797
- if (Math.abs(point.x - label.anchorPoint.x) > EPS10 || Math.abs(point.y - label.anchorPoint.y) > EPS10) {
7871
+ if (Math.abs(point.x - label.anchorPoint.x) > EPS11 || Math.abs(point.y - label.anchorPoint.y) > EPS11) {
7798
7872
  return false;
7799
7873
  }
7800
7874
  return [path[pointIndex - 1], path[pointIndex + 1]].some(
7801
- (neighbor) => neighbor && Math.abs(neighbor.x - point.x) <= EPS10 && (neighbor.y - point.y) * direction.y > EPS10
7875
+ (neighbor) => neighbor && Math.abs(neighbor.x - point.x) <= EPS11 && (neighbor.y - point.y) * direction.y > EPS11
7802
7876
  );
7803
7877
  });
7804
7878
  });
@@ -7877,7 +7951,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
7877
7951
  );
7878
7952
  const maxSearchDistance = this.getSearchDistanceLimit(label, orientation);
7879
7953
  const maxOutwardDistance = outwardDirection.x === 0 && outwardDirection.y === 0 ? 0 : this.maxSearchDistance;
7880
- for (let outwardDistance = 0; outwardDistance <= maxOutwardDistance + EPS10; outwardDistance += LABEL_SEARCH_STEP) {
7954
+ for (let outwardDistance = 0; outwardDistance <= maxOutwardDistance + EPS11; outwardDistance += LABEL_SEARCH_STEP) {
7881
7955
  const baseAnchor = {
7882
7956
  x: initialBaseAnchor.x + outwardDirection.x * outwardDistance,
7883
7957
  y: initialBaseAnchor.y + outwardDirection.y * outwardDistance
@@ -7951,10 +8025,10 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
7951
8025
  phase = "shift",
7952
8026
  stopOnTraceCollision = true
7953
8027
  } = params;
7954
- for (let distance4 = LABEL_SEARCH_STEP; distance4 <= maxSearchDistance + EPS10; distance4 += LABEL_SEARCH_STEP) {
8028
+ for (let distance5 = LABEL_SEARCH_STEP; distance5 <= maxSearchDistance + EPS11; distance5 += LABEL_SEARCH_STEP) {
7955
8029
  const anchorPoint = {
7956
- x: baseAnchor.x + direction.x * distance4,
7957
- y: baseAnchor.y + direction.y * distance4
8030
+ x: baseAnchor.x + direction.x * distance5,
8031
+ y: baseAnchor.y + direction.y * distance5
7958
8032
  };
7959
8033
  const candidate = this.createCandidate(label, anchorPoint, orientation);
7960
8034
  const result = this.evaluateCandidate(
@@ -7962,7 +8036,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
7962
8036
  label,
7963
8037
  labelIndex,
7964
8038
  phase,
7965
- distance4,
8039
+ distance5,
7966
8040
  outwardDistance
7967
8041
  );
7968
8042
  this.currentCandidateResults.push(result);
@@ -7974,11 +8048,11 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
7974
8048
  }
7975
8049
  return null;
7976
8050
  }
7977
- evaluateCandidate(candidate, label, labelIndex, phase, distance4, outwardDistance) {
8051
+ evaluateCandidate(candidate, label, labelIndex, phase, distance5, outwardDistance) {
7978
8052
  return {
7979
8053
  ...candidate,
7980
8054
  phase,
7981
- distance: distance4,
8055
+ distance: distance5,
7982
8056
  outwardDistance,
7983
8057
  selected: false,
7984
8058
  status: this.getCandidateStatus({
@@ -8188,9 +8262,9 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
8188
8262
  const { anchorPoint, orientation } = candidate;
8189
8263
  const chipBounds = chip.bounds;
8190
8264
  if (isYOrientation(orientation)) {
8191
- return anchorPoint.x < chipBounds.minX - EPS10 || anchorPoint.x > chipBounds.maxX + EPS10;
8265
+ return anchorPoint.x < chipBounds.minX - EPS11 || anchorPoint.x > chipBounds.maxX + EPS11;
8192
8266
  }
8193
- return anchorPoint.y < chipBounds.minY - EPS10 || anchorPoint.y > chipBounds.maxY + EPS10;
8267
+ return anchorPoint.y < chipBounds.minY - EPS11 || anchorPoint.y > chipBounds.maxY + EPS11;
8194
8268
  });
8195
8269
  }
8196
8270
  getBoundsStatus(candidateBoundsCheck) {
@@ -8264,8 +8338,8 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
8264
8338
  sharesChipBoundary(bounds) {
8265
8339
  for (const chip of this.chipObstacleSpatialIndex.chips) {
8266
8340
  const chipBounds = chip.bounds;
8267
- const adjacentToVerticalSide = Math.abs(bounds.minX - chipBounds.maxX) <= WICK_CLEARANCE + EPS10 || Math.abs(bounds.maxX - chipBounds.minX) <= WICK_CLEARANCE + EPS10;
8268
- const adjacentToHorizontalSide = Math.abs(bounds.minY - chipBounds.maxY) <= WICK_CLEARANCE + EPS10 || Math.abs(bounds.maxY - chipBounds.minY) <= WICK_CLEARANCE + EPS10;
8341
+ const adjacentToVerticalSide = Math.abs(bounds.minX - chipBounds.maxX) <= WICK_CLEARANCE + EPS11 || Math.abs(bounds.maxX - chipBounds.minX) <= WICK_CLEARANCE + EPS11;
8342
+ const adjacentToHorizontalSide = Math.abs(bounds.minY - chipBounds.maxY) <= WICK_CLEARANCE + EPS11 || Math.abs(bounds.maxY - chipBounds.minY) <= WICK_CLEARANCE + EPS11;
8269
8343
  if (adjacentToVerticalSide && rangesOverlap(
8270
8344
  bounds.minY,
8271
8345
  bounds.maxY,
@@ -8313,13 +8387,13 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
8313
8387
  let nearestDistance = Number.POSITIVE_INFINITY;
8314
8388
  for (const chip of this.chipObstacleSpatialIndex.chips) {
8315
8389
  const bounds = chip.bounds;
8316
- if (point.x < bounds.minX - EPS10 || point.x > bounds.maxX + EPS10 || point.y < bounds.minY - EPS10 || point.y > bounds.maxY + EPS10) {
8390
+ if (point.x < bounds.minX - EPS11 || point.x > bounds.maxX + EPS11 || point.y < bounds.minY - EPS11 || point.y > bounds.maxY + EPS11) {
8317
8391
  continue;
8318
8392
  }
8319
- for (const [side, distance4] of getSideDistances(point, bounds)) {
8320
- if (distance4 < nearestDistance) {
8393
+ for (const [side, distance5] of getSideDistances(point, bounds)) {
8394
+ if (distance5 < nearestDistance) {
8321
8395
  nearestSide = side;
8322
- nearestDistance = distance4;
8396
+ nearestDistance = distance5;
8323
8397
  }
8324
8398
  }
8325
8399
  }
@@ -8330,8 +8404,8 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
8330
8404
  let nearestDistanceSq = Number.POSITIVE_INFINITY;
8331
8405
  for (const chip of this.chipObstacleSpatialIndex.chips) {
8332
8406
  const bounds = chip.bounds;
8333
- const dx = point.x < bounds.minX - EPS10 ? bounds.minX - point.x : point.x > bounds.maxX + EPS10 ? point.x - bounds.maxX : 0;
8334
- const dy = point.y < bounds.minY - EPS10 ? bounds.minY - point.y : point.y > bounds.maxY + EPS10 ? point.y - bounds.maxY : 0;
8407
+ const dx = point.x < bounds.minX - EPS11 ? bounds.minX - point.x : point.x > bounds.maxX + EPS11 ? point.x - bounds.maxX : 0;
8408
+ const dy = point.y < bounds.minY - EPS11 ? bounds.minY - point.y : point.y > bounds.maxY + EPS11 ? point.y - bounds.maxY : 0;
8335
8409
  if (dx === 0 && dy === 0) continue;
8336
8410
  const distanceSq = dx ** 2 + dy ** 2;
8337
8411
  if (distanceSq >= nearestDistanceSq) continue;
@@ -8348,10 +8422,10 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
8348
8422
  let nearestSide = null;
8349
8423
  let nearestDistance = Number.POSITIVE_INFINITY;
8350
8424
  for (const chip of this.chipObstacleSpatialIndex.chips) {
8351
- for (const [side, distance4] of getSideDistances(point, chip.bounds)) {
8352
- if (distance4 < nearestDistance) {
8425
+ for (const [side, distance5] of getSideDistances(point, chip.bounds)) {
8426
+ if (distance5 < nearestDistance) {
8353
8427
  nearestSide = side;
8354
- nearestDistance = distance4;
8428
+ nearestDistance = distance5;
8355
8429
  }
8356
8430
  }
8357
8431
  }
@@ -8615,8 +8689,8 @@ var RailNetLabelCornerPlacementSolver = class extends BaseSolver {
8615
8689
  if (seenCornerKeys.has(key)) continue;
8616
8690
  seenCornerKeys.add(key);
8617
8691
  const pinAligned = pins.some((pin) => {
8618
- if (isVertical5) return Math.abs(pin.x - anchorPoint.x) < EPS5;
8619
- return Math.abs(pin.y - anchorPoint.y) < EPS5;
8692
+ if (isVertical5) return Math.abs(pin.x - anchorPoint.x) < EPS6;
8693
+ return Math.abs(pin.y - anchorPoint.y) < EPS6;
8620
8694
  });
8621
8695
  if (!pinAligned) continue;
8622
8696
  candidates.push({
@@ -8709,11 +8783,11 @@ var getOrientationConstraint = (inputProblem, label) => {
8709
8783
  };
8710
8784
 
8711
8785
  // lib/solvers/TraceAnchoredNetLabelOverlapSolver/geometry.ts
8712
- var EPS11 = 1e-6;
8786
+ var EPS12 = 1e-6;
8713
8787
  var TRACE_BOUNDARY_TOLERANCE2 = 1e-6;
8714
8788
  var getLabelBounds = (label) => getRectBounds(label.center, label.width, label.height);
8715
- var rectsOverlap3 = (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;
8716
- var rectsTouchOrOverlap = (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;
8789
+ 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;
8790
+ 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;
8717
8791
  var traceCrossesBoundsInterior2 = (bounds, traces) => {
8718
8792
  for (const trace of traces) {
8719
8793
  const points = trace.tracePath;
@@ -8752,17 +8826,17 @@ var getTraceLength = (trace) => {
8752
8826
  }
8753
8827
  return length;
8754
8828
  };
8755
- var getPointAtTraceDistance = (trace, distance4) => {
8829
+ var getPointAtTraceDistance = (trace, distance5) => {
8756
8830
  let pathDistance = 0;
8757
8831
  for (let i = 0; i < trace.tracePath.length - 1; i++) {
8758
8832
  const start = trace.tracePath[i];
8759
8833
  const end = trace.tracePath[i + 1];
8760
8834
  const segmentLength = getManhattanDistance(start, end);
8761
8835
  const nextDistance = pathDistance + segmentLength;
8762
- if (distance4 <= nextDistance + EPS11) {
8836
+ if (distance5 <= nextDistance + EPS12) {
8763
8837
  const offset = Math.max(
8764
8838
  0,
8765
- Math.min(segmentLength, distance4 - pathDistance)
8839
+ Math.min(segmentLength, distance5 - pathDistance)
8766
8840
  );
8767
8841
  const direction = getSegmentDirection(start, end);
8768
8842
  return {
@@ -8798,7 +8872,7 @@ var segmentCrossesBoundsInterior2 = (start, end, bounds) => {
8798
8872
  return false;
8799
8873
  }
8800
8874
  if (isVertical4(start, end)) {
8801
- if (start.x <= interior.minX + EPS11 || start.x >= interior.maxX - EPS11) {
8875
+ if (start.x <= interior.minX + EPS12 || start.x >= interior.maxX - EPS12) {
8802
8876
  return false;
8803
8877
  }
8804
8878
  return rangesOverlap2(
@@ -8809,7 +8883,7 @@ var segmentCrossesBoundsInterior2 = (start, end, bounds) => {
8809
8883
  );
8810
8884
  }
8811
8885
  if (isHorizontal3(start, end)) {
8812
- if (start.y <= interior.minY + EPS11 || start.y >= interior.maxY - EPS11) {
8886
+ if (start.y <= interior.minY + EPS12 || start.y >= interior.maxY - EPS12) {
8813
8887
  return false;
8814
8888
  }
8815
8889
  return rangesOverlap2(
@@ -8823,10 +8897,10 @@ var segmentCrossesBoundsInterior2 = (start, end, bounds) => {
8823
8897
  };
8824
8898
  var isPointOnSegment2 = (point, start, end) => {
8825
8899
  if (isHorizontal3(start, end)) {
8826
- return Math.abs(point.y - start.y) <= EPS11 && point.x >= Math.min(start.x, end.x) - EPS11 && point.x <= Math.max(start.x, end.x) + EPS11;
8900
+ 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;
8827
8901
  }
8828
8902
  if (isVertical4(start, end)) {
8829
- return Math.abs(point.x - start.x) <= EPS11 && point.y >= Math.min(start.y, end.y) - EPS11 && point.y <= Math.max(start.y, end.y) + EPS11;
8903
+ 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;
8830
8904
  }
8831
8905
  return false;
8832
8906
  };
@@ -8834,9 +8908,9 @@ var getSegmentDirection = (start, end) => ({
8834
8908
  x: isVertical4(start, end) ? 0 : Math.sign(end.x - start.x),
8835
8909
  y: isHorizontal3(start, end) ? 0 : Math.sign(end.y - start.y)
8836
8910
  });
8837
- var isHorizontal3 = (start, end) => Math.abs(start.y - end.y) <= EPS11;
8838
- var isVertical4 = (start, end) => Math.abs(start.x - end.x) <= EPS11;
8839
- var rangesOverlap2 = (minA, maxA, minB, maxB) => Math.min(maxA, maxB) - Math.max(minA, minB) > EPS11;
8911
+ var isHorizontal3 = (start, end) => Math.abs(start.y - end.y) <= EPS12;
8912
+ var isVertical4 = (start, end) => Math.abs(start.x - end.x) <= EPS12;
8913
+ var rangesOverlap2 = (minA, maxA, minB, maxB) => Math.min(maxA, maxB) - Math.max(minA, minB) > EPS12;
8840
8914
  var getUniquePinCount2 = (pinIds) => new Set(pinIds).size;
8841
8915
 
8842
8916
  // lib/solvers/TraceAnchoredNetLabelOverlapSolver/candidates.ts
@@ -8887,13 +8961,13 @@ var getCandidateDistances = (traceLength, vertexDistances) => {
8887
8961
  const distances = /* @__PURE__ */ new Set();
8888
8962
  const maxSteps = Math.ceil(traceLength / CANDIDATE_STEP);
8889
8963
  for (let i = 0; i <= maxSteps; i++) {
8890
- const distance4 = Math.min(traceLength, i * CANDIDATE_STEP);
8891
- distances.add(roundDistance(distance4));
8964
+ const distance5 = Math.min(traceLength, i * CANDIDATE_STEP);
8965
+ distances.add(roundDistance(distance5));
8892
8966
  }
8893
- for (const distance4 of vertexDistances) {
8894
- distances.add(roundDistance(distance4));
8967
+ for (const distance5 of vertexDistances) {
8968
+ distances.add(roundDistance(distance5));
8895
8969
  }
8896
- return [...distances].filter((distance4) => distance4 >= -EPS11 && distance4 <= traceLength + EPS11).sort((a, b) => a - b);
8970
+ return [...distances].filter((distance5) => distance5 >= -EPS12 && distance5 <= traceLength + EPS12).sort((a, b) => a - b);
8897
8971
  };
8898
8972
  var getOrientationsForPoint = (params) => {
8899
8973
  const { inputProblem, label, point, orientationConstraint } = params;
@@ -8982,13 +9056,13 @@ var getOutwardOrientationOptions = (point, chipBounds) => {
8982
9056
  };
8983
9057
  var getOutwardAxisOption = (params) => {
8984
9058
  const { value, min, max, center, positiveOrientation, negativeOrientation } = params;
8985
- if (value > max + EPS11) {
9059
+ if (value > max + EPS12) {
8986
9060
  return {
8987
9061
  orientation: positiveOrientation,
8988
9062
  outwardScore: 1 + value - max
8989
9063
  };
8990
9064
  }
8991
- if (value < min - EPS11) {
9065
+ if (value < min - EPS12) {
8992
9066
  return {
8993
9067
  orientation: negativeOrientation,
8994
9068
  outwardScore: 1 + min - value
@@ -8999,7 +9073,7 @@ var getOutwardAxisOption = (params) => {
8999
9073
  outwardScore: getNormalizedCenterDistance(value, center, max - min)
9000
9074
  };
9001
9075
  };
9002
- var getNormalizedCenterDistance = (value, center, span) => Math.abs(value - center) / Math.max(EPS11, span / 2);
9076
+ var getNormalizedCenterDistance = (value, center, span) => Math.abs(value - center) / Math.max(EPS12, span / 2);
9003
9077
  var ALL_ORIENTATIONS = ["x+", "x-", "y+", "y-"];
9004
9078
  var getFlippedOrientation = (orientation) => {
9005
9079
  switch (orientation) {
@@ -9071,8 +9145,8 @@ var getNetLabelHeight = (inputProblem, label) => {
9071
9145
  (nc) => nc.pinIds.some((pid) => label.pinIds.includes(pid))
9072
9146
  )?.netLabelHeight;
9073
9147
  };
9074
- var roundDistance = (distance4) => Number(distance4.toFixed(6));
9075
- var isSamePlacement = (label, point, orientation) => Math.abs(point.x - label.anchorPoint.x) <= EPS11 && Math.abs(point.y - label.anchorPoint.y) <= EPS11 && orientation === label.orientation;
9148
+ var roundDistance = (distance5) => Number(distance5.toFixed(6));
9149
+ var isSamePlacement = (label, point, orientation) => Math.abs(point.x - label.anchorPoint.x) <= EPS12 && Math.abs(point.y - label.anchorPoint.y) <= EPS12 && orientation === label.orientation;
9076
9150
 
9077
9151
  // lib/solvers/TraceAnchoredNetLabelOverlapSolver/visualize.ts
9078
9152
  var CANDIDATE_SELECTED_COLOR4 = "blue";
@@ -10037,6 +10111,457 @@ ${c.status}`
10037
10111
  }
10038
10112
  };
10039
10113
 
10114
+ // lib/solvers/UnroutedTraceRecoverySolver/UnroutedTraceRecoverySolver.ts
10115
+ import { distance as distance4, doSegmentsIntersect as doSegmentsIntersect3 } from "@tscircuit/math-utils";
10116
+ var ROUTE_CLEARANCE = 0.2;
10117
+ var COORDINATE_TOLERANCE = 1e-9;
10118
+ var GROUND_NET_ID = "GND";
10119
+ var pointsAreEqual = (firstPoint, secondPoint) => {
10120
+ return Math.abs(firstPoint.x - secondPoint.x) <= COORDINATE_TOLERANCE && Math.abs(firstPoint.y - secondPoint.y) <= COORDINATE_TOLERANCE;
10121
+ };
10122
+ var removeConsecutiveDuplicatePoints = (path) => {
10123
+ const filteredPath = [];
10124
+ for (const point of path) {
10125
+ const previousPoint = filteredPath.at(-1);
10126
+ if (!previousPoint || !pointsAreEqual(previousPoint, point)) {
10127
+ filteredPath.push(point);
10128
+ }
10129
+ }
10130
+ return filteredPath;
10131
+ };
10132
+ var getPathLength2 = (path) => {
10133
+ let pathLength = 0;
10134
+ for (let pointIndex = 0; pointIndex < path.length - 1; pointIndex++) {
10135
+ const startPoint = path[pointIndex];
10136
+ const endPoint = path[pointIndex + 1];
10137
+ pathLength += Math.abs(endPoint.x - startPoint.x) + Math.abs(endPoint.y - startPoint.y);
10138
+ }
10139
+ return pathLength;
10140
+ };
10141
+ var getEscapePoint = ({
10142
+ pin,
10143
+ facingDirection
10144
+ }) => {
10145
+ const escapePoint = { x: pin.x, y: pin.y };
10146
+ if (facingDirection === "x+") {
10147
+ escapePoint.x += ROUTE_CLEARANCE;
10148
+ }
10149
+ if (facingDirection === "x-") {
10150
+ escapePoint.x -= ROUTE_CLEARANCE;
10151
+ }
10152
+ if (facingDirection === "y+") {
10153
+ escapePoint.y += ROUTE_CLEARANCE;
10154
+ }
10155
+ if (facingDirection === "y-") {
10156
+ escapePoint.y -= ROUTE_CLEARANCE;
10157
+ }
10158
+ return escapePoint;
10159
+ };
10160
+ var getOuterBounds = (obstacles) => {
10161
+ return {
10162
+ minX: Math.min(...obstacles.map((obstacle) => obstacle.minX)),
10163
+ minY: Math.min(...obstacles.map((obstacle) => obstacle.minY)),
10164
+ maxX: Math.max(...obstacles.map((obstacle) => obstacle.maxX)),
10165
+ maxY: Math.max(...obstacles.map((obstacle) => obstacle.maxY))
10166
+ };
10167
+ };
10168
+ var getPerimeterCandidates = ({
10169
+ connectionPair,
10170
+ obstacles
10171
+ }) => {
10172
+ const [firstPin, secondPin] = connectionPair.pins;
10173
+ const firstEscapePoint = getEscapePoint({
10174
+ pin: firstPin,
10175
+ facingDirection: firstPin._facingDirection
10176
+ });
10177
+ const secondEscapePoint = getEscapePoint({
10178
+ pin: secondPin,
10179
+ facingDirection: secondPin._facingDirection
10180
+ });
10181
+ const outerBounds = getOuterBounds(obstacles);
10182
+ const horizontalChannels = [
10183
+ outerBounds.minY - ROUTE_CLEARANCE,
10184
+ outerBounds.maxY + ROUTE_CLEARANCE
10185
+ ];
10186
+ const verticalChannels = [
10187
+ outerBounds.minX - ROUTE_CLEARANCE,
10188
+ outerBounds.maxX + ROUTE_CLEARANCE
10189
+ ];
10190
+ const candidates = [];
10191
+ for (const channelY of horizontalChannels) {
10192
+ candidates.push(
10193
+ removeConsecutiveDuplicatePoints([
10194
+ firstPin,
10195
+ firstEscapePoint,
10196
+ { x: firstEscapePoint.x, y: channelY },
10197
+ { x: secondEscapePoint.x, y: channelY },
10198
+ secondEscapePoint,
10199
+ secondPin
10200
+ ])
10201
+ );
10202
+ }
10203
+ for (const channelX of verticalChannels) {
10204
+ candidates.push(
10205
+ removeConsecutiveDuplicatePoints([
10206
+ firstPin,
10207
+ firstEscapePoint,
10208
+ { x: channelX, y: firstEscapePoint.y },
10209
+ { x: channelX, y: secondEscapePoint.y },
10210
+ secondEscapePoint,
10211
+ secondPin
10212
+ ])
10213
+ );
10214
+ }
10215
+ return candidates.sort(
10216
+ (firstPath, secondPath) => getPathLength2(firstPath) - getPathLength2(secondPath)
10217
+ );
10218
+ };
10219
+ var getSegmentMidpoint = (startPoint, endPoint) => {
10220
+ return {
10221
+ x: (startPoint.x + endPoint.x) / 2,
10222
+ y: (startPoint.y + endPoint.y) / 2
10223
+ };
10224
+ };
10225
+ var getJunctionPoints = (sameNetTraces) => {
10226
+ const junctionPoints = [];
10227
+ for (const trace of sameNetTraces) {
10228
+ for (let pointIndex = 0; pointIndex < trace.tracePath.length - 1; pointIndex++) {
10229
+ const startPoint = trace.tracePath[pointIndex];
10230
+ const endPoint = trace.tracePath[pointIndex + 1];
10231
+ junctionPoints.push(startPoint);
10232
+ junctionPoints.push(getSegmentMidpoint(startPoint, endPoint));
10233
+ }
10234
+ const lastPoint = trace.tracePath.at(-1);
10235
+ if (lastPoint) {
10236
+ junctionPoints.push(lastPoint);
10237
+ }
10238
+ }
10239
+ return junctionPoints;
10240
+ };
10241
+ var getUnconnectedPins = ({
10242
+ connectionPair,
10243
+ sameNetTraces
10244
+ }) => {
10245
+ const connectedPinIds = new Set(
10246
+ sameNetTraces.flatMap((trace) => trace.pinIds)
10247
+ );
10248
+ return connectionPair.pins.filter((pin) => !connectedPinIds.has(pin.pinId));
10249
+ };
10250
+ var getJunctionCandidates = ({
10251
+ connectionPair,
10252
+ sameNetTraces,
10253
+ obstacles,
10254
+ maxConnectionDistance
10255
+ }) => {
10256
+ const outerBounds = getOuterBounds(obstacles);
10257
+ const horizontalChannels = [
10258
+ outerBounds.minY - ROUTE_CLEARANCE,
10259
+ outerBounds.maxY + ROUTE_CLEARANCE
10260
+ ];
10261
+ const verticalChannels = [
10262
+ outerBounds.minX - ROUTE_CLEARANCE,
10263
+ outerBounds.maxX + ROUTE_CLEARANCE
10264
+ ];
10265
+ const candidates = [];
10266
+ const junctionPoints = getJunctionPoints(sameNetTraces);
10267
+ const unconnectedPins = getUnconnectedPins({
10268
+ connectionPair,
10269
+ sameNetTraces
10270
+ });
10271
+ for (const pin of unconnectedPins) {
10272
+ const escapePoint = getEscapePoint({
10273
+ pin,
10274
+ facingDirection: pin._facingDirection
10275
+ });
10276
+ for (const junctionPoint of junctionPoints) {
10277
+ if (distance4(pin, junctionPoint) > maxConnectionDistance) {
10278
+ continue;
10279
+ }
10280
+ candidates.push(
10281
+ removeConsecutiveDuplicatePoints([
10282
+ pin,
10283
+ escapePoint,
10284
+ { x: escapePoint.x, y: junctionPoint.y },
10285
+ junctionPoint
10286
+ ])
10287
+ );
10288
+ candidates.push(
10289
+ removeConsecutiveDuplicatePoints([
10290
+ pin,
10291
+ escapePoint,
10292
+ { x: junctionPoint.x, y: escapePoint.y },
10293
+ junctionPoint
10294
+ ])
10295
+ );
10296
+ for (const channelY of horizontalChannels) {
10297
+ candidates.push(
10298
+ removeConsecutiveDuplicatePoints([
10299
+ pin,
10300
+ escapePoint,
10301
+ { x: escapePoint.x, y: channelY },
10302
+ { x: junctionPoint.x, y: channelY },
10303
+ junctionPoint
10304
+ ])
10305
+ );
10306
+ }
10307
+ for (const channelX of verticalChannels) {
10308
+ candidates.push(
10309
+ removeConsecutiveDuplicatePoints([
10310
+ pin,
10311
+ escapePoint,
10312
+ { x: channelX, y: escapePoint.y },
10313
+ { x: channelX, y: junctionPoint.y },
10314
+ junctionPoint
10315
+ ])
10316
+ );
10317
+ }
10318
+ }
10319
+ }
10320
+ return candidates.sort(
10321
+ (firstPath, secondPath) => getPathLength2(firstPath) - getPathLength2(secondPath)
10322
+ );
10323
+ };
10324
+ var pathCollidesWithObstacles = ({
10325
+ path,
10326
+ obstacles,
10327
+ connectionPair,
10328
+ rejectComponentBoundaryTravel
10329
+ }) => {
10330
+ const firstPathPoint = path[0];
10331
+ const lastPathPoint = path.at(-1);
10332
+ const firstPathPin = connectionPair.pins.find(
10333
+ (pin) => pointsAreEqual(pin, firstPathPoint)
10334
+ );
10335
+ const lastPathPin = connectionPair.pins.find(
10336
+ (pin) => pointsAreEqual(pin, lastPathPoint)
10337
+ );
10338
+ const pathConnectsPairPins = firstPathPin !== void 0 && lastPathPin !== void 0;
10339
+ const firstChipObstacle = obstacles.find(
10340
+ (obstacle) => obstacle.kind === "chip" && obstacle.chipId === firstPathPin?.chipId
10341
+ );
10342
+ const secondChipObstacle = obstacles.find(
10343
+ (obstacle) => obstacle.kind === "chip" && obstacle.chipId === lastPathPin?.chipId
10344
+ );
10345
+ if (rejectComponentBoundaryTravel && firstChipObstacle && segmentOverlapsRectBoundary(path[0], path[1], firstChipObstacle)) {
10346
+ return true;
10347
+ }
10348
+ if (rejectComponentBoundaryTravel && secondChipObstacle && segmentOverlapsRectBoundary(
10349
+ path[path.length - 2],
10350
+ path[path.length - 1],
10351
+ secondChipObstacle
10352
+ )) {
10353
+ return true;
10354
+ }
10355
+ const collision = findFirstCollision(path, obstacles, {
10356
+ excludeRectsForSegment: (segmentIndex) => {
10357
+ const excludedObstacles = /* @__PURE__ */ new Set();
10358
+ if (segmentIndex === 0 && firstChipObstacle) {
10359
+ excludedObstacles.add(firstChipObstacle);
10360
+ }
10361
+ if (pathConnectsPairPins && segmentIndex === path.length - 2 && secondChipObstacle) {
10362
+ excludedObstacles.add(secondChipObstacle);
10363
+ }
10364
+ return excludedObstacles;
10365
+ }
10366
+ });
10367
+ return collision !== null;
10368
+ };
10369
+ var hasParallelFailedConnection = ({
10370
+ connectionPair,
10371
+ failedConnectionPairs
10372
+ }) => {
10373
+ const connectionChipIds = new Set(
10374
+ connectionPair.pins.map((pin) => pin.chipId)
10375
+ );
10376
+ return failedConnectionPairs.some((otherConnectionPair) => {
10377
+ if (otherConnectionPair.mspPairId === connectionPair.mspPairId) {
10378
+ return false;
10379
+ }
10380
+ return otherConnectionPair.pins.every(
10381
+ (pin) => connectionChipIds.has(pin.chipId)
10382
+ );
10383
+ });
10384
+ };
10385
+ var segmentsOverlapBeyondEndpoint = ({
10386
+ firstStart,
10387
+ firstEnd,
10388
+ secondStart,
10389
+ secondEnd
10390
+ }) => {
10391
+ if (isHorizontal(firstStart, firstEnd) && isHorizontal(secondStart, secondEnd)) {
10392
+ const overlapLength = Math.min(
10393
+ Math.max(firstStart.x, firstEnd.x),
10394
+ Math.max(secondStart.x, secondEnd.x)
10395
+ ) - Math.max(
10396
+ Math.min(firstStart.x, firstEnd.x),
10397
+ Math.min(secondStart.x, secondEnd.x)
10398
+ );
10399
+ return overlapLength > COORDINATE_TOLERANCE;
10400
+ }
10401
+ if (isVertical(firstStart, firstEnd) && isVertical(secondStart, secondEnd)) {
10402
+ const overlapLength = Math.min(
10403
+ Math.max(firstStart.y, firstEnd.y),
10404
+ Math.max(secondStart.y, secondEnd.y)
10405
+ ) - Math.max(
10406
+ Math.min(firstStart.y, firstEnd.y),
10407
+ Math.min(secondStart.y, secondEnd.y)
10408
+ );
10409
+ return overlapLength > COORDINATE_TOLERANCE;
10410
+ }
10411
+ return false;
10412
+ };
10413
+ var getAllowedJunctionPoints = ({
10414
+ connectionPair,
10415
+ existingTrace
10416
+ }) => {
10417
+ const existingPinIds = new Set(existingTrace.pinIds);
10418
+ return connectionPair.pins.filter((pin) => existingPinIds.has(pin.pinId));
10419
+ };
10420
+ var pathCrossesExistingTraces = ({
10421
+ path,
10422
+ connectionPair,
10423
+ existingTraces
10424
+ }) => {
10425
+ for (const existingTrace of existingTraces) {
10426
+ if (existingTrace.globalConnNetId === connectionPair.globalConnNetId) {
10427
+ continue;
10428
+ }
10429
+ const allowedJunctionPoints = getAllowedJunctionPoints({
10430
+ connectionPair,
10431
+ existingTrace
10432
+ });
10433
+ for (let pathIndex = 0; pathIndex < path.length - 1; pathIndex++) {
10434
+ const pathStart = path[pathIndex];
10435
+ const pathEnd = path[pathIndex + 1];
10436
+ for (let traceIndex = 0; traceIndex < existingTrace.tracePath.length - 1; traceIndex++) {
10437
+ const traceStart = existingTrace.tracePath[traceIndex];
10438
+ const traceEnd = existingTrace.tracePath[traceIndex + 1];
10439
+ if (!doSegmentsIntersect3(pathStart, pathEnd, traceStart, traceEnd)) {
10440
+ continue;
10441
+ }
10442
+ const intersectionIsAllowedJunction = allowedJunctionPoints.some(
10443
+ (junctionPoint) => (pointsAreEqual(pathStart, junctionPoint) || pointsAreEqual(pathEnd, junctionPoint)) && (pointsAreEqual(traceStart, junctionPoint) || pointsAreEqual(traceEnd, junctionPoint))
10444
+ );
10445
+ if (intersectionIsAllowedJunction && !segmentsOverlapBeyondEndpoint({
10446
+ firstStart: pathStart,
10447
+ firstEnd: pathEnd,
10448
+ secondStart: traceStart,
10449
+ secondEnd: traceEnd
10450
+ })) {
10451
+ continue;
10452
+ }
10453
+ return true;
10454
+ }
10455
+ }
10456
+ }
10457
+ return false;
10458
+ };
10459
+ var UnroutedTraceRecoverySolver = class extends BaseSolver {
10460
+ constructor(params) {
10461
+ super();
10462
+ this.params = params;
10463
+ this.inputProblem = params.inputProblem;
10464
+ this.alreadySolvedTraces = params.alreadySolvedTraces;
10465
+ this.failedConnectionPairs = params.failedConnectionPairs;
10466
+ this.queuedConnectionPairs = [...params.failedConnectionPairs];
10467
+ this.maxConnectionDistance = this.inputProblem.maxMspPairDistance ?? DEFAULT_MAX_MSP_PAIR_DISTANCE;
10468
+ const { netConnMap } = getConnectivityMapsFromInputProblem(
10469
+ this.inputProblem
10470
+ );
10471
+ this.groundGlobalConnNetId = netConnMap.getNetConnectedToId(GROUND_NET_ID) ?? void 0;
10472
+ }
10473
+ params;
10474
+ inputProblem;
10475
+ alreadySolvedTraces;
10476
+ failedConnectionPairs;
10477
+ queuedConnectionPairs;
10478
+ maxConnectionDistance;
10479
+ groundGlobalConnNetId;
10480
+ solvedUnroutedTraces = [];
10481
+ getConstructorParams() {
10482
+ return this.params;
10483
+ }
10484
+ _step() {
10485
+ const connectionPair = this.queuedConnectionPairs.shift();
10486
+ if (!connectionPair) {
10487
+ this.solved = true;
10488
+ return;
10489
+ }
10490
+ if (connectionPair.globalConnNetId === this.groundGlobalConnNetId) {
10491
+ return;
10492
+ }
10493
+ if (distance4(connectionPair.pins[0], connectionPair.pins[1]) > this.maxConnectionDistance) {
10494
+ return;
10495
+ }
10496
+ const obstacles = getObstacleRects(this.inputProblem);
10497
+ const existingTraces = [
10498
+ ...this.alreadySolvedTraces,
10499
+ ...this.solvedUnroutedTraces
10500
+ ];
10501
+ const sameNetTraces = existingTraces.filter(
10502
+ (trace) => trace.globalConnNetId === connectionPair.globalConnNetId
10503
+ );
10504
+ const junctionCandidates = getJunctionCandidates({
10505
+ connectionPair,
10506
+ sameNetTraces,
10507
+ obstacles,
10508
+ maxConnectionDistance: this.maxConnectionDistance
10509
+ });
10510
+ const perimeterCandidates = getPerimeterCandidates({
10511
+ connectionPair,
10512
+ obstacles
10513
+ });
10514
+ const candidates = [...junctionCandidates, ...perimeterCandidates];
10515
+ const rejectComponentBoundaryTravel = hasParallelFailedConnection({
10516
+ connectionPair,
10517
+ failedConnectionPairs: this.failedConnectionPairs
10518
+ });
10519
+ for (const tracePath of candidates) {
10520
+ if (pathCollidesWithObstacles({
10521
+ path: tracePath,
10522
+ obstacles,
10523
+ connectionPair,
10524
+ rejectComponentBoundaryTravel
10525
+ })) {
10526
+ continue;
10527
+ }
10528
+ if (pathCrossesExistingTraces({
10529
+ path: tracePath,
10530
+ connectionPair,
10531
+ existingTraces
10532
+ })) {
10533
+ continue;
10534
+ }
10535
+ this.solvedUnroutedTraces.push({
10536
+ ...connectionPair,
10537
+ tracePath,
10538
+ mspConnectionPairIds: [connectionPair.mspPairId],
10539
+ pinIds: connectionPair.pins.map((pin) => pin.pinId)
10540
+ });
10541
+ return;
10542
+ }
10543
+ }
10544
+ getOutput() {
10545
+ return {
10546
+ newTraces: this.solvedUnroutedTraces,
10547
+ allTracesMerged: [
10548
+ ...this.alreadySolvedTraces,
10549
+ ...this.solvedUnroutedTraces
10550
+ ]
10551
+ };
10552
+ }
10553
+ visualize() {
10554
+ const graphics = visualizeInputProblem(this.inputProblem);
10555
+ for (const trace of this.solvedUnroutedTraces) {
10556
+ graphics.lines.push({
10557
+ points: trace.tracePath,
10558
+ strokeColor: "blue"
10559
+ });
10560
+ }
10561
+ return graphics;
10562
+ }
10563
+ };
10564
+
10040
10565
  // lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts
10041
10566
  function definePipelineStep(solverName, solverClass, getConstructorParams, opts = {}) {
10042
10567
  return {
@@ -10052,6 +10577,7 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
10052
10577
  // guidelinesSolver?: GuidelinesSolver
10053
10578
  schematicTraceLinesSolver;
10054
10579
  longDistancePairSolver;
10580
+ unroutedTraceRecoverySolver;
10055
10581
  traceOverlapShiftSolver;
10056
10582
  netLabelPlacementSolver;
10057
10583
  labelMergingSolver;
@@ -10122,13 +10648,24 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
10122
10648
  }
10123
10649
  }
10124
10650
  ),
10651
+ definePipelineStep(
10652
+ "unroutedTraceRecoverySolver",
10653
+ UnroutedTraceRecoverySolver,
10654
+ (instance) => [
10655
+ {
10656
+ inputProblem: instance.inputProblem,
10657
+ failedConnectionPairs: instance.schematicTraceLinesSolver.failedConnectionPairs,
10658
+ alreadySolvedTraces: instance.longDistancePairSolver.getOutput().allTracesMerged
10659
+ }
10660
+ ]
10661
+ ),
10125
10662
  definePipelineStep(
10126
10663
  "traceOverlapShiftSolver",
10127
10664
  TraceOverlapShiftSolver,
10128
10665
  () => [
10129
10666
  {
10130
10667
  inputProblem: this.inputProblem,
10131
- inputTracePaths: this.longDistancePairSolver?.getOutput().allTracesMerged,
10668
+ inputTracePaths: this.unroutedTraceRecoverySolver?.getOutput().allTracesMerged,
10132
10669
  globalConnMap: this.mspConnectionPairSolver.globalConnMap
10133
10670
  }
10134
10671
  ],
@@ -10144,7 +10681,7 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
10144
10681
  {
10145
10682
  inputProblem: this.inputProblem,
10146
10683
  inputTraceMap: this.traceOverlapShiftSolver?.correctedTraceMap ?? Object.fromEntries(
10147
- this.longDistancePairSolver.getOutput().allTracesMerged.map(
10684
+ this.unroutedTraceRecoverySolver.getOutput().allTracesMerged.map(
10148
10685
  (p) => [p.mspPairId, p]
10149
10686
  )
10150
10687
  )
@@ -10160,7 +10697,7 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
10160
10697
  TraceLabelOverlapAvoidanceSolver,
10161
10698
  (instance) => {
10162
10699
  const traceMap = instance.traceOverlapShiftSolver?.correctedTraceMap ?? Object.fromEntries(
10163
- instance.longDistancePairSolver.getOutput().allTracesMerged.map((p) => [p.mspPairId, p])
10700
+ instance.unroutedTraceRecoverySolver.getOutput().allTracesMerged.map((p) => [p.mspPairId, p])
10164
10701
  );
10165
10702
  const traces = Object.values(traceMap);
10166
10703
  const netLabelPlacements = instance.netLabelPlacementSolver.netLabelPlacements;