@tscircuit/schematic-trace-solver 0.0.105 → 0.0.107

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 (20) hide show
  1. package/dist/index.js +403 -166
  2. package/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts +78 -27
  3. package/lib/solvers/TraceCleanupSolver/sameNetRailAlignment/geometry.ts +40 -20
  4. package/lib/solvers/TraceCleanupSolver/sub-solver/UntangleTraceSubsolver.ts +178 -9
  5. package/lib/solvers/TraceCleanupSolver/sub-solver/findIntersectionsWithObstacles.ts +59 -0
  6. package/lib/solvers/TraceCleanupSolver/sub-solver/generateLShapeRerouteCandidates.ts +64 -1
  7. package/package.json +1 -1
  8. package/tests/bug-reports/bug-report-20260707T140410Z/__snapshots__/bug-report-20260707T140410Z.snap.svg +3 -3
  9. package/tests/bug-reports/bug-report-20260721T221026Z/__snapshots__/bug-report-20260721T221026Z.snap.svg +1 -1
  10. package/tests/examples/__snapshots__/example13.snap.svg +3 -3
  11. package/tests/examples/__snapshots__/example29.snap.svg +23 -23
  12. package/tests/examples/example19.test.ts +4 -0
  13. package/tests/repros/__snapshots__/repro-example35-minimize-trace-crossing.snap.svg +256 -0
  14. package/tests/repros/__snapshots__/repro-tps61222-trace-intersection.snap.svg +221 -0
  15. package/tests/repros/__snapshots__/repro47-endpoint-obstacle-detour.snap.svg +1 -1
  16. package/tests/repros/assets/repro-example35-minimize-trace-crossing.input.json +1561 -0
  17. package/tests/repros/assets/repro-tps61222-trace-intersection.input.json +200 -0
  18. package/tests/repros/repro-example35-minimize-trace-crossing.test.ts +22 -0
  19. package/tests/repros/repro-tps61222-trace-intersection.test.ts +14 -0
  20. package/tests/repros/repro47-endpoint-obstacle-detour.test.ts +6 -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 EPS12 = 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) < -EPS12) 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) < -EPS12) 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) < EPS12 || Math.abs(p1.y - p2.y) < EPS12) {
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 };
@@ -1299,8 +1299,8 @@ var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
1299
1299
  if (!collision) {
1300
1300
  const first = path[0];
1301
1301
  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;
1302
+ const EPS12 = 1e-9;
1303
+ const samePoint = (p, q) => Math.abs(p.x - q.x) < EPS12 && Math.abs(p.y - q.y) < EPS12;
1304
1304
  if (samePoint(first, { x: PA.x, y: PA.y }) && samePoint(last, { x: PB.x, y: PB.y })) {
1305
1305
  this.solvedTracePath = path;
1306
1306
  this.solved = true;
@@ -1573,13 +1573,13 @@ var applyJogToTerminalSegment = ({
1573
1573
  segmentIndex: si,
1574
1574
  offset,
1575
1575
  JOG_SIZE,
1576
- EPS: EPS11 = 1e-6
1576
+ EPS: EPS12 = 1e-6
1577
1577
  }) => {
1578
1578
  if (si !== 0 && si !== pts.length - 2) return;
1579
1579
  const start = pts[si];
1580
1580
  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;
1581
+ const isVertical5 = Math.abs(start.x - end.x) < EPS12;
1582
+ const isHorizontal4 = Math.abs(start.y - end.y) < EPS12;
1583
1583
  if (!isVertical5 && !isHorizontal4) return;
1584
1584
  const segDir = isVertical5 ? end.y > start.y ? 1 : -1 : end.x > start.x ? 1 : -1;
1585
1585
  if (pts.length === 2) {
@@ -1997,7 +1997,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
1997
1997
  return islands;
1998
1998
  }
1999
1999
  findNextOverlapIssue() {
2000
- const EPS11 = 2e-3;
2000
+ const EPS12 = 2e-3;
2001
2001
  const netIds = Object.keys(this.traceNetIslands);
2002
2002
  for (let i = 0; i < netIds.length; i++) {
2003
2003
  for (let j = i + 1; j < netIds.length; j++) {
@@ -2024,7 +2024,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
2024
2024
  segmentBIndex,
2025
2025
  rangeOverlap
2026
2026
  }) => {
2027
- if (rangeOverlap > EPS11) {
2027
+ if (rangeOverlap > EPS12) {
2028
2028
  const keyA = `${pathAIndex}:${segmentAIndex}`;
2029
2029
  const keyB = `${pathBIndex}:${segmentBIndex}`;
2030
2030
  if (!seenA.has(keyA)) {
@@ -2043,7 +2043,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
2043
2043
  }
2044
2044
  return;
2045
2045
  }
2046
- if (rangeOverlap < -EPS11) return;
2046
+ if (rangeOverlap < -EPS12) return;
2047
2047
  const pathA = pathsA[pathAIndex].tracePath;
2048
2048
  const pathB = pathsB[pathBIndex].tracePath;
2049
2049
  const segmentAStart = pathA[segmentAIndex];
@@ -2066,8 +2066,8 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
2066
2066
  for (let sa = 0; sa < ptsA.length - 1; sa++) {
2067
2067
  const a1 = ptsA[sa];
2068
2068
  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;
2069
+ const aVert = Math.abs(a1.x - a2.x) < EPS12;
2070
+ const aHorz = Math.abs(a1.y - a2.y) < EPS12;
2071
2071
  if (!aVert && !aHorz) continue;
2072
2072
  for (let pb = 0; pb < pathsB.length; pb++) {
2073
2073
  const pathB = pathsB[pb];
@@ -2075,11 +2075,11 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
2075
2075
  for (let sb = 0; sb < ptsB.length - 1; sb++) {
2076
2076
  const b1 = ptsB[sb];
2077
2077
  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;
2078
+ const bVert = Math.abs(b1.x - b2.x) < EPS12;
2079
+ const bHorz = Math.abs(b1.y - b2.y) < EPS12;
2080
2080
  if (!bVert && !bHorz) continue;
2081
2081
  if (aVert && bVert) {
2082
- if (Math.abs(a1.x - b1.x) < EPS11) {
2082
+ if (Math.abs(a1.x - b1.x) < EPS12) {
2083
2083
  recordCollinearInteraction({
2084
2084
  pathAIndex: pa,
2085
2085
  segmentAIndex: sa,
@@ -2089,7 +2089,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
2089
2089
  });
2090
2090
  }
2091
2091
  } else if (aHorz && bHorz) {
2092
- if (Math.abs(a1.y - b1.y) < EPS11) {
2092
+ if (Math.abs(a1.y - b1.y) < EPS12) {
2093
2093
  recordCollinearInteraction({
2094
2094
  pathAIndex: pa,
2095
2095
  segmentAIndex: sa,
@@ -2145,14 +2145,14 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
2145
2145
  return null;
2146
2146
  }
2147
2147
  findNextDiagonalSegment() {
2148
- const EPS11 = 2e-3;
2148
+ const EPS12 = 2e-3;
2149
2149
  for (const mspPairId in this.correctedTraceMap) {
2150
2150
  const tracePath = this.correctedTraceMap[mspPairId].tracePath;
2151
2151
  for (let i = 0; i < tracePath.length - 1; i++) {
2152
2152
  const p1 = tracePath[i];
2153
2153
  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;
2154
+ const isHorizontal4 = Math.abs(p1.y - p2.y) < EPS12;
2155
+ const isVertical5 = Math.abs(p1.x - p2.x) < EPS12;
2156
2156
  if (!isHorizontal4 && !isVertical5) {
2157
2157
  return { mspPairId, tracePath, i, p1, p2 };
2158
2158
  }
@@ -2166,13 +2166,13 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
2166
2166
  return false;
2167
2167
  }
2168
2168
  const { mspPairId, tracePath, i, p1, p2 } = diagonalInfo;
2169
- const EPS11 = 2e-3;
2169
+ const EPS12 = 2e-3;
2170
2170
  const p0 = i > 0 ? tracePath[i - 1] : null;
2171
2171
  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;
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;
2176
2176
  const elbow1 = { x: p1.x, y: p2.y };
2177
2177
  const elbow2 = { x: p2.x, y: p1.y };
2178
2178
  let score1 = 0;
@@ -2299,24 +2299,24 @@ var ChipObstacleSpatialIndex = class {
2299
2299
  };
2300
2300
 
2301
2301
  // 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;
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;
2305
2305
  if (!isVert && !isHorz) return false;
2306
2306
  if (isVert) {
2307
2307
  const x = p1.x;
2308
- if (x < rect.minX - EPS11 || x > rect.maxX + EPS11) return false;
2308
+ if (x < rect.minX - EPS12 || x > rect.maxX + EPS12) return false;
2309
2309
  const segMinY = Math.min(p1.y, p2.y);
2310
2310
  const segMaxY = Math.max(p1.y, p2.y);
2311
2311
  const overlap = Math.min(segMaxY, rect.maxY) - Math.max(segMinY, rect.minY);
2312
- return overlap > EPS11;
2312
+ return overlap > EPS12;
2313
2313
  } else {
2314
2314
  const y = p1.y;
2315
- if (y < rect.minY - EPS11 || y > rect.maxY + EPS11) return false;
2315
+ if (y < rect.minY - EPS12 || y > rect.maxY + EPS12) return false;
2316
2316
  const segMinX = Math.min(p1.x, p2.x);
2317
2317
  const segMaxX = Math.max(p1.x, p2.x);
2318
2318
  const overlap = Math.min(segMaxX, rect.maxX) - Math.max(segMinX, rect.minX);
2319
- return overlap > EPS11;
2319
+ return overlap > EPS12;
2320
2320
  }
2321
2321
  }
2322
2322
  function rectIntersectsAnyTrace(bounds, inputTraceMap, hostPathId, hostSegIndex) {
@@ -2713,14 +2713,14 @@ var SingleNetLabelPlacementSolver = class extends BaseSolver {
2713
2713
  };
2714
2714
  let bestCandidate = null;
2715
2715
  let bestScore = -Infinity;
2716
- const EPS11 = 1e-6;
2716
+ const EPS12 = 1e-6;
2717
2717
  for (const curr of tracesToScan) {
2718
2718
  const pts = curr.tracePath.slice();
2719
2719
  for (let si = 0; si < pts.length - 1; si++) {
2720
2720
  const a = pts[si];
2721
2721
  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;
2722
+ const isH = Math.abs(a.y - b.y) < EPS12;
2723
+ const isV = Math.abs(a.x - b.x) < EPS12;
2724
2724
  if (!isH && !isV) continue;
2725
2725
  const segmentAllowed = isH ? ["y+", "y-"] : ["x+", "x-"];
2726
2726
  const candidateOrients = orientations.filter(
@@ -4774,6 +4774,95 @@ var minimizeTurns = ({
4774
4774
  return finalSimplifiedPath;
4775
4775
  };
4776
4776
 
4777
+ // lib/solvers/TraceCleanupSolver/sameNetRailAlignment/geometry.ts
4778
+ var RAIL_ALIGNMENT_EPSILON = 2e-3;
4779
+ var nearlyEqual = (a, b) => Math.abs(a - b) < RAIL_ALIGNMENT_EPSILON;
4780
+ var isHorizontal2 = (a, b) => nearlyEqual(a.y, b.y);
4781
+ var isVertical2 = (a, b) => nearlyEqual(a.x, b.x);
4782
+ var isPositiveLength = (a, b) => Math.abs(a.x - b.x) >= RAIL_ALIGNMENT_EPSILON || Math.abs(a.y - b.y) >= RAIL_ALIGNMENT_EPSILON;
4783
+ var getRailOrientation = (a, b) => {
4784
+ if (!isPositiveLength(a, b)) return null;
4785
+ if (isVertical2(a, b)) return "vertical";
4786
+ if (isHorizontal2(a, b)) return "horizontal";
4787
+ return null;
4788
+ };
4789
+ var rangesTouchOrOverlap = (a, b) => Math.min(a.maxAlong, b.maxAlong) - Math.max(a.minAlong, b.minAlong) >= -RAIL_ALIGNMENT_EPSILON;
4790
+ var pointsEqual = (a, b) => nearlyEqual(a.x, b.x) && nearlyEqual(a.y, b.y);
4791
+ var getDistinctCoordinates = (coordinates) => {
4792
+ const distinct = [];
4793
+ for (const coordinate of coordinates) {
4794
+ if (!distinct.some((item) => nearlyEqual(item, coordinate))) {
4795
+ distinct.push(coordinate);
4796
+ }
4797
+ }
4798
+ return distinct;
4799
+ };
4800
+ var getMergedIntervalMetrics = (intervals) => {
4801
+ const groups = [];
4802
+ for (const interval of intervals) {
4803
+ const group = groups.find(
4804
+ (items) => nearlyEqual(items[0].coordinate, interval.coordinate)
4805
+ );
4806
+ if (group) group.push(interval);
4807
+ else groups.push([interval]);
4808
+ }
4809
+ return groups.reduce(
4810
+ (total, group) => {
4811
+ const sorted = [...group].sort((a, b) => a.min - b.min);
4812
+ let groupLength = 0;
4813
+ let segmentCount = 1;
4814
+ let currentMin = sorted[0].min;
4815
+ let currentMax = sorted[0].max;
4816
+ for (const interval of sorted.slice(1)) {
4817
+ if (interval.min <= currentMax + RAIL_ALIGNMENT_EPSILON) {
4818
+ currentMax = Math.max(currentMax, interval.max);
4819
+ } else {
4820
+ groupLength += currentMax - currentMin;
4821
+ segmentCount++;
4822
+ currentMin = interval.min;
4823
+ currentMax = interval.max;
4824
+ }
4825
+ }
4826
+ return {
4827
+ length: total.length + groupLength + currentMax - currentMin,
4828
+ segmentCount: total.segmentCount + segmentCount
4829
+ };
4830
+ },
4831
+ { length: 0, segmentCount: 0 }
4832
+ );
4833
+ };
4834
+ var getVisibleTraceMetrics = (traces) => {
4835
+ const horizontal = [];
4836
+ const vertical = [];
4837
+ for (const trace of traces) {
4838
+ for (let index = 0; index < trace.tracePath.length - 1; index++) {
4839
+ const start = trace.tracePath[index];
4840
+ const end = trace.tracePath[index + 1];
4841
+ if (isHorizontal2(start, end)) {
4842
+ horizontal.push({
4843
+ coordinate: start.y,
4844
+ min: Math.min(start.x, end.x),
4845
+ max: Math.max(start.x, end.x)
4846
+ });
4847
+ } else if (isVertical2(start, end)) {
4848
+ vertical.push({
4849
+ coordinate: start.x,
4850
+ min: Math.min(start.y, end.y),
4851
+ max: Math.max(start.y, end.y)
4852
+ });
4853
+ }
4854
+ }
4855
+ }
4856
+ const horizontalMetrics = getMergedIntervalMetrics(horizontal);
4857
+ const verticalMetrics = getMergedIntervalMetrics(vertical);
4858
+ return {
4859
+ length: horizontalMetrics.length + verticalMetrics.length,
4860
+ segmentCount: horizontalMetrics.segmentCount + verticalMetrics.segmentCount
4861
+ };
4862
+ };
4863
+ var getVisibleTraceLength = (traces) => getVisibleTraceMetrics(traces).length;
4864
+ var getVisibleTraceSegmentCount = (traces) => getVisibleTraceMetrics(traces).segmentCount;
4865
+
4777
4866
  // lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts
4778
4867
  var minimizeTurnsWithFilteredLabels = ({
4779
4868
  targetMspConnectionPairId,
@@ -4789,11 +4878,16 @@ var minimizeTurnsWithFilteredLabels = ({
4789
4878
  if (!targetTrace) {
4790
4879
  throw new Error(`Target trace ${targetMspConnectionPairId} not found`);
4791
4880
  }
4792
- const obstacleTraces = traces.filter(
4793
- (t) => t.mspPairId !== targetMspConnectionPairId && t.globalConnNetId !== targetTrace.globalConnNetId
4881
+ const targetPinIds = new Set(targetTrace.pinIds);
4882
+ const otherTraces = traces.filter(
4883
+ (trace) => trace.mspPairId !== targetMspConnectionPairId
4794
4884
  );
4885
+ const relaxedObstacleTraces = otherTraces.filter((trace) => {
4886
+ const sharesEndpoint = trace.pinIds.some((pinId) => targetPinIds.has(pinId));
4887
+ return trace.globalConnNetId !== targetTrace.globalConnNetId || !sharesEndpoint;
4888
+ });
4795
4889
  const TRACE_WIDTH = 0.01;
4796
- const traceObstacles = obstacleTraces.flatMap(
4890
+ const getTraceObstacles2 = (obstacleTraces) => obstacleTraces.flatMap(
4797
4891
  (trace, i) => trace.tracePath.slice(0, -1).map((p1, pi) => {
4798
4892
  const p2 = trace.tracePath[pi + 1];
4799
4893
  return {
@@ -4814,7 +4908,6 @@ var minimizeTurnsWithFilteredLabels = ({
4814
4908
  maxX: obs.maxX + PADDING,
4815
4909
  maxY: obs.maxY + PADDING
4816
4910
  }));
4817
- const combinedObstacles = [...staticObstacles, ...traceObstacles];
4818
4911
  const originalPath = targetTrace.tracePath;
4819
4912
  const filteredLabels = allLabelPlacements.filter((label) => {
4820
4913
  const originalNetIds = mergedLabelNetIdMap[label.globalConnNetId];
@@ -4829,12 +4922,46 @@ var minimizeTurnsWithFilteredLabels = ({
4829
4922
  minY: nl.center.y - nl.height / 2 - paddingBuffer,
4830
4923
  maxY: nl.center.y + nl.height / 2 + paddingBuffer
4831
4924
  }));
4832
- const newPath = minimizeTurns({
4925
+ const strictPath = minimizeTurns({
4926
+ path: originalPath,
4927
+ obstacles: [...staticObstacles, ...getTraceObstacles2(otherTraces)],
4928
+ labelBounds,
4929
+ originalPath
4930
+ });
4931
+ const relaxedPath = minimizeTurns({
4833
4932
  path: originalPath,
4834
- obstacles: combinedObstacles,
4933
+ obstacles: [
4934
+ ...staticObstacles,
4935
+ ...getTraceObstacles2(relaxedObstacleTraces)
4936
+ ],
4835
4937
  labelBounds,
4836
4938
  originalPath
4837
4939
  });
4940
+ const sameNetTraces = otherTraces.filter(
4941
+ (trace) => trace.globalConnNetId === targetTrace.globalConnNetId
4942
+ );
4943
+ const getSameNetReadability = (tracePath) => {
4944
+ const tracesWithCandidate = [
4945
+ ...sameNetTraces,
4946
+ { ...targetTrace, tracePath }
4947
+ ];
4948
+ return {
4949
+ segmentCount: getVisibleTraceSegmentCount(tracesWithCandidate),
4950
+ visibleLength: getVisibleTraceLength(tracesWithCandidate)
4951
+ };
4952
+ };
4953
+ const strictTurns = countTurns(strictPath);
4954
+ const relaxedTurns = countTurns(relaxedPath);
4955
+ let newPath = strictPath;
4956
+ if (relaxedTurns < strictTurns) {
4957
+ newPath = relaxedPath;
4958
+ } else if (relaxedTurns === strictTurns) {
4959
+ const strictReadability = getSameNetReadability(strictPath);
4960
+ const relaxedReadability = getSameNetReadability(relaxedPath);
4961
+ if (relaxedReadability.segmentCount < strictReadability.segmentCount || relaxedReadability.segmentCount === strictReadability.segmentCount && relaxedReadability.visibleLength < strictReadability.visibleLength - RAIL_ALIGNMENT_EPSILON) {
4962
+ newPath = relaxedPath;
4963
+ }
4964
+ }
4838
4965
  return {
4839
4966
  ...targetTrace,
4840
4967
  tracePath: newPath
@@ -4978,80 +5105,6 @@ var balanceZShapes = ({
4978
5105
  };
4979
5106
  };
4980
5107
 
4981
- // lib/solvers/TraceCleanupSolver/sameNetRailAlignment/geometry.ts
4982
- var RAIL_ALIGNMENT_EPSILON = 2e-3;
4983
- var nearlyEqual = (a, b) => Math.abs(a - b) < RAIL_ALIGNMENT_EPSILON;
4984
- var isHorizontal2 = (a, b) => nearlyEqual(a.y, b.y);
4985
- var isVertical2 = (a, b) => nearlyEqual(a.x, b.x);
4986
- var isPositiveLength = (a, b) => Math.abs(a.x - b.x) >= RAIL_ALIGNMENT_EPSILON || Math.abs(a.y - b.y) >= RAIL_ALIGNMENT_EPSILON;
4987
- var getRailOrientation = (a, b) => {
4988
- if (!isPositiveLength(a, b)) return null;
4989
- if (isVertical2(a, b)) return "vertical";
4990
- if (isHorizontal2(a, b)) return "horizontal";
4991
- return null;
4992
- };
4993
- var rangesTouchOrOverlap = (a, b) => Math.min(a.maxAlong, b.maxAlong) - Math.max(a.minAlong, b.minAlong) >= -RAIL_ALIGNMENT_EPSILON;
4994
- var pointsEqual = (a, b) => nearlyEqual(a.x, b.x) && nearlyEqual(a.y, b.y);
4995
- var getDistinctCoordinates = (coordinates) => {
4996
- const distinct = [];
4997
- for (const coordinate of coordinates) {
4998
- if (!distinct.some((item) => nearlyEqual(item, coordinate))) {
4999
- distinct.push(coordinate);
5000
- }
5001
- }
5002
- return distinct;
5003
- };
5004
- var getMergedIntervalLength = (intervals) => {
5005
- const groups = [];
5006
- for (const interval of intervals) {
5007
- const group = groups.find(
5008
- (items) => nearlyEqual(items[0].coordinate, interval.coordinate)
5009
- );
5010
- if (group) group.push(interval);
5011
- else groups.push([interval]);
5012
- }
5013
- return groups.reduce((total, group) => {
5014
- const sorted = [...group].sort((a, b) => a.min - b.min);
5015
- let groupLength = 0;
5016
- let currentMin = sorted[0].min;
5017
- let currentMax = sorted[0].max;
5018
- for (const interval of sorted.slice(1)) {
5019
- if (interval.min <= currentMax + RAIL_ALIGNMENT_EPSILON) {
5020
- currentMax = Math.max(currentMax, interval.max);
5021
- } else {
5022
- groupLength += currentMax - currentMin;
5023
- currentMin = interval.min;
5024
- currentMax = interval.max;
5025
- }
5026
- }
5027
- return total + groupLength + currentMax - currentMin;
5028
- }, 0);
5029
- };
5030
- var getVisibleTraceLength = (traces) => {
5031
- const horizontal = [];
5032
- const vertical = [];
5033
- for (const trace of traces) {
5034
- for (let index = 0; index < trace.tracePath.length - 1; index++) {
5035
- const start = trace.tracePath[index];
5036
- const end = trace.tracePath[index + 1];
5037
- if (isHorizontal2(start, end)) {
5038
- horizontal.push({
5039
- coordinate: start.y,
5040
- min: Math.min(start.x, end.x),
5041
- max: Math.max(start.x, end.x)
5042
- });
5043
- } else if (isVertical2(start, end)) {
5044
- vertical.push({
5045
- coordinate: start.x,
5046
- min: Math.min(start.y, end.y),
5047
- max: Math.max(start.y, end.y)
5048
- });
5049
- }
5050
- }
5051
- }
5052
- return getMergedIntervalLength(horizontal) + getMergedIntervalLength(vertical);
5053
- };
5054
-
5055
5108
  // lib/solvers/TraceCleanupSolver/sameNetRailAlignment/moveRailSegments.ts
5056
5109
  var moveRailSegments = (trace, segments, coordinate) => {
5057
5110
  const pointsToMove = /* @__PURE__ */ new Set();
@@ -5599,6 +5652,7 @@ var getTraceObstacles = (allTraces, excludeTraceId) => {
5599
5652
 
5600
5653
  // lib/solvers/TraceCleanupSolver/sub-solver/findIntersectionsWithObstacles.ts
5601
5654
  import { getSegmentIntersection } from "@tscircuit/math-utils/line-intersections";
5655
+ var EPS7 = 1e-6;
5602
5656
  var findIntersectionsWithObstacles = (p1, p2, obstacles) => {
5603
5657
  const intersections = [];
5604
5658
  for (const obstacle of obstacles) {
@@ -5617,10 +5671,38 @@ var findIntersectionsWithObstacles = (p1, p2, obstacles) => {
5617
5671
  }
5618
5672
  return intersections;
5619
5673
  };
5674
+ var isSamePoint = (first, second) => Math.abs(first.x - second.x) < EPS7 && Math.abs(first.y - second.y) < EPS7;
5675
+ var findPerpendicularPathCrossings = (path, otherPath) => {
5676
+ const crossings = [];
5677
+ for (let pathSegmentIndex = 1; pathSegmentIndex < path.length - 2; pathSegmentIndex++) {
5678
+ const start = path[pathSegmentIndex];
5679
+ const end = path[pathSegmentIndex + 1];
5680
+ const isVertical5 = Math.abs(start.x - end.x) < EPS7;
5681
+ for (let otherPathSegmentIndex = 1; otherPathSegmentIndex < otherPath.length - 2; otherPathSegmentIndex++) {
5682
+ const otherStart = otherPath[otherPathSegmentIndex];
5683
+ const otherEnd = otherPath[otherPathSegmentIndex + 1];
5684
+ const otherIsVertical = Math.abs(otherStart.x - otherEnd.x) < EPS7;
5685
+ if (isVertical5 === otherIsVertical) continue;
5686
+ const intersection = getSegmentIntersection(
5687
+ start,
5688
+ end,
5689
+ otherStart,
5690
+ otherEnd
5691
+ );
5692
+ if (!intersection || [start, end, otherStart, otherEnd].some(
5693
+ (point) => isSamePoint(point, intersection)
5694
+ )) {
5695
+ continue;
5696
+ }
5697
+ crossings.push({ pathSegmentIndex, otherPathSegmentIndex });
5698
+ }
5699
+ }
5700
+ return crossings;
5701
+ };
5620
5702
 
5621
5703
  // lib/solvers/TraceCleanupSolver/sub-solver/generateLShapeRerouteCandidates.ts
5622
- var EPS7 = 1e-6;
5623
- var isVertical3 = (a, b, eps = EPS7) => Math.abs(a.x - b.x) < eps;
5704
+ var EPS8 = 1e-6;
5705
+ var isVertical3 = (a, b, eps = EPS8) => Math.abs(a.x - b.x) < eps;
5624
5706
  var generateLShapeRerouteCandidates = ({
5625
5707
  lShape,
5626
5708
  rectangle,
@@ -5633,7 +5715,7 @@ var generateLShapeRerouteCandidates = ({
5633
5715
  let c2;
5634
5716
  let i1_padded = interactionPoint1;
5635
5717
  let i2_padded = interactionPoint2;
5636
- if (Math.abs(p2.x - x) < EPS7 && Math.abs(p2.y - (y + height)) < EPS7) {
5718
+ if (Math.abs(p2.x - x) < EPS8 && Math.abs(p2.y - (y + height)) < EPS8) {
5637
5719
  c2 = { x: x + width + padding, y: y - padding };
5638
5720
  if (isVertical3(p1, p2)) {
5639
5721
  i1_padded = { x: interactionPoint1.x, y: interactionPoint1.y - padding };
@@ -5645,7 +5727,7 @@ var generateLShapeRerouteCandidates = ({
5645
5727
  } else {
5646
5728
  i2_padded = { x: interactionPoint2.x + padding, y: interactionPoint2.y };
5647
5729
  }
5648
- } else if (Math.abs(p2.x - (x + width)) < EPS7 && Math.abs(p2.y - (y + height)) < EPS7) {
5730
+ } else if (Math.abs(p2.x - (x + width)) < EPS8 && Math.abs(p2.y - (y + height)) < EPS8) {
5649
5731
  c2 = { x: x - padding, y: y - padding };
5650
5732
  if (isVertical3(p1, p2)) {
5651
5733
  i1_padded = { x: interactionPoint1.x, y: interactionPoint1.y - padding };
@@ -5657,7 +5739,7 @@ var generateLShapeRerouteCandidates = ({
5657
5739
  } else {
5658
5740
  i2_padded = { x: interactionPoint2.x - padding, y: interactionPoint2.y };
5659
5741
  }
5660
- } else if (Math.abs(p2.x - x) < EPS7 && Math.abs(p2.y - y) < EPS7) {
5742
+ } else if (Math.abs(p2.x - x) < EPS8 && Math.abs(p2.y - y) < EPS8) {
5661
5743
  c2 = { x: x + width + padding, y: y + height + padding };
5662
5744
  if (isVertical3(p1, p2)) {
5663
5745
  i1_padded = { x: interactionPoint1.x, y: interactionPoint1.y + padding };
@@ -5669,7 +5751,7 @@ var generateLShapeRerouteCandidates = ({
5669
5751
  } else {
5670
5752
  i2_padded = { x: interactionPoint2.x + padding, y: interactionPoint2.y };
5671
5753
  }
5672
- } else if (Math.abs(p2.x - (x + width)) < EPS7 && Math.abs(p2.y - y) < EPS7) {
5754
+ } else if (Math.abs(p2.x - (x + width)) < EPS8 && Math.abs(p2.y - y) < EPS8) {
5673
5755
  c2 = { x: x - padding, y: y + height + padding };
5674
5756
  if (isVertical3(p1, p2)) {
5675
5757
  i1_padded = { x: interactionPoint1.x, y: interactionPoint1.y + padding };
@@ -5686,6 +5768,48 @@ var generateLShapeRerouteCandidates = ({
5686
5768
  }
5687
5769
  return [[i1_padded, c2, i2_padded]];
5688
5770
  };
5771
+ var generatePerpendicularTraceDetours = ({
5772
+ trace,
5773
+ segmentIndex,
5774
+ obstacleStart,
5775
+ obstacleEnd,
5776
+ chipBounds,
5777
+ clearance
5778
+ }) => {
5779
+ const buildDetours = (path, index) => {
5780
+ const start = path[index];
5781
+ const end = path[index + 1];
5782
+ const movingAxis = Math.abs(start.x - end.x) < EPS8 ? "y" : "x";
5783
+ const detourAxis = movingAxis === "x" ? "y" : "x";
5784
+ const gate = obstacleStart[movingAxis] + Math.sign(start[movingAxis] - obstacleStart[movingAxis]) * clearance;
5785
+ const obstacleRange = [obstacleStart[detourAxis], obstacleEnd[detourAxis]];
5786
+ const lowBound = detourAxis === "x" ? "minX" : "minY";
5787
+ const highBound = detourAxis === "x" ? "maxX" : "maxY";
5788
+ const detourCoordinates = [
5789
+ Math.min(...obstacleRange) - clearance,
5790
+ Math.max(...obstacleRange) + clearance,
5791
+ ...chipBounds.flatMap((bounds) => [
5792
+ bounds[lowBound] - clearance,
5793
+ bounds[highBound] + clearance
5794
+ ])
5795
+ ];
5796
+ return [...new Set(detourCoordinates)].map(
5797
+ (detour) => simplifyPath([
5798
+ ...path.slice(0, index + 1),
5799
+ { ...start, [movingAxis]: gate },
5800
+ { ...start, [movingAxis]: gate, [detourAxis]: detour },
5801
+ { ...end, [detourAxis]: detour },
5802
+ ...path.slice(index + 2)
5803
+ ])
5804
+ );
5805
+ };
5806
+ const reversedPath = [...trace.tracePath].reverse();
5807
+ const reversedIndex = trace.tracePath.length - 2 - segmentIndex;
5808
+ return [
5809
+ ...buildDetours(trace.tracePath, segmentIndex),
5810
+ ...buildDetours(reversedPath, reversedIndex).map((path) => path.reverse())
5811
+ ].map((path) => ({ traceId: trace.mspPairId, path }));
5812
+ };
5689
5813
 
5690
5814
  // lib/solvers/TraceCleanupSolver/sub-solver/isPathColliding.ts
5691
5815
  import { getSegmentIntersection as getSegmentIntersection2 } from "@tscircuit/math-utils/line-intersections";
@@ -5860,6 +5984,9 @@ var UntangleTraceSubsolver = class extends BaseSolver {
5860
5984
  input;
5861
5985
  chipObstacleSpatialIndex;
5862
5986
  lShapesToProcess = [];
5987
+ ignoredCrossings = /* @__PURE__ */ new Set();
5988
+ reroutedTraceIds = /* @__PURE__ */ new Set();
5989
+ processingCrossings = true;
5863
5990
  visualizationMode = "l_shapes";
5864
5991
  currentLShape = null;
5865
5992
  intersectionPoints = [];
@@ -5883,18 +6010,24 @@ var UntangleTraceSubsolver = class extends BaseSolver {
5883
6010
  if (!this.input.inputProblem._chipObstacleSpatialIndex) {
5884
6011
  this.input.inputProblem._chipObstacleSpatialIndex = this.chipObstacleSpatialIndex;
5885
6012
  }
5886
- for (const trace of this.input.allTraces) {
5887
- const lShapes = findAllLShapedTurns(trace.tracePath);
5888
- this.lShapesToProcess.push(
5889
- ...lShapes.map((l) => ({ ...l, traceId: trace.mspPairId }))
5890
- );
5891
- }
5892
6013
  }
5893
6014
  _step() {
5894
6015
  if (this.isInitialStep) {
5895
6016
  this.isInitialStep = false;
5896
6017
  return;
5897
6018
  }
6019
+ if (this.processingCrossings) {
6020
+ const crossing = this._findCrossing();
6021
+ if (crossing) {
6022
+ if (!this._resolveCrossing(crossing)) {
6023
+ this.ignoredCrossings.add(this._crossingKey(crossing));
6024
+ }
6025
+ return;
6026
+ }
6027
+ this.processingCrossings = false;
6028
+ this._initializeLShapes();
6029
+ return;
6030
+ }
5898
6031
  if (this.lShapeJustProcessed) {
5899
6032
  this._resetAfterLShapProcessing();
5900
6033
  return;
@@ -5918,6 +6051,110 @@ var UntangleTraceSubsolver = class extends BaseSolver {
5918
6051
  break;
5919
6052
  }
5920
6053
  }
6054
+ _initializeLShapes() {
6055
+ for (const trace of this.input.allTraces) {
6056
+ this.lShapesToProcess.push(
6057
+ ...findAllLShapedTurns(trace.tracePath).map((lShape) => ({
6058
+ ...lShape,
6059
+ traceId: trace.mspPairId
6060
+ }))
6061
+ );
6062
+ }
6063
+ }
6064
+ _crossingKey(crossing) {
6065
+ return `${crossing.trace.mspPairId}:${crossing.segmentIndex}:${crossing.otherTrace.mspPairId}:${crossing.otherSegmentIndex}`;
6066
+ }
6067
+ _isTraceBundle(first, second) {
6068
+ const componentPair = (trace) => trace.pins.map((pin) => pin.chipId).sort().join(":");
6069
+ if (componentPair(first) !== componentPair(second)) return false;
6070
+ return Object.values(this.input.mergedLabelNetIdMap).some(
6071
+ (netIds) => netIds.has(first.globalConnNetId) && netIds.has(second.globalConnNetId)
6072
+ );
6073
+ }
6074
+ _findCrossing() {
6075
+ const traces = this.input.allTraces;
6076
+ for (let firstIndex = 0; firstIndex < traces.length; firstIndex++) {
6077
+ const trace = traces[firstIndex];
6078
+ for (let secondIndex = firstIndex + 1; secondIndex < traces.length; secondIndex++) {
6079
+ const otherTrace = traces[secondIndex];
6080
+ if (trace.globalConnNetId === otherTrace.globalConnNetId) continue;
6081
+ const isInitialBundleCrossing = this._isTraceBundle(trace, otherTrace);
6082
+ if (!isInitialBundleCrossing && !this.reroutedTraceIds.has(trace.mspPairId) && !this.reroutedTraceIds.has(otherTrace.mspPairId)) {
6083
+ continue;
6084
+ }
6085
+ const crossings = findPerpendicularPathCrossings(
6086
+ trace.tracePath,
6087
+ otherTrace.tracePath
6088
+ );
6089
+ for (const { pathSegmentIndex, otherPathSegmentIndex } of crossings) {
6090
+ const crossing = {
6091
+ trace,
6092
+ segmentIndex: pathSegmentIndex,
6093
+ otherTrace,
6094
+ otherSegmentIndex: otherPathSegmentIndex,
6095
+ isInitialBundleCrossing
6096
+ };
6097
+ if (!this.ignoredCrossings.has(this._crossingKey(crossing))) {
6098
+ return crossing;
6099
+ }
6100
+ }
6101
+ }
6102
+ }
6103
+ return null;
6104
+ }
6105
+ _resolveCrossing(crossing) {
6106
+ const chipBounds = this.chipObstacleSpatialIndex.chips.map(
6107
+ (chip) => chip.bounds
6108
+ );
6109
+ const candidates = [
6110
+ ...generatePerpendicularTraceDetours({
6111
+ trace: crossing.trace,
6112
+ segmentIndex: crossing.segmentIndex,
6113
+ obstacleStart: crossing.otherTrace.tracePath[crossing.otherSegmentIndex],
6114
+ obstacleEnd: crossing.otherTrace.tracePath[crossing.otherSegmentIndex + 1],
6115
+ chipBounds,
6116
+ clearance: this.input.paddingBuffer
6117
+ }),
6118
+ ...generatePerpendicularTraceDetours({
6119
+ trace: crossing.otherTrace,
6120
+ segmentIndex: crossing.otherSegmentIndex,
6121
+ obstacleStart: crossing.trace.tracePath[crossing.segmentIndex],
6122
+ obstacleEnd: crossing.trace.tracePath[crossing.segmentIndex + 1],
6123
+ chipBounds,
6124
+ clearance: this.input.paddingBuffer
6125
+ })
6126
+ ];
6127
+ const chipObstacles = getObstacleRects(this.input.inputProblem).filter(
6128
+ (obstacle) => obstacle.kind === "chip"
6129
+ );
6130
+ const validCandidates = candidates.map((candidate) => ({
6131
+ ...candidate,
6132
+ collision: isPathColliding(
6133
+ candidate.path,
6134
+ this.input.allTraces,
6135
+ candidate.traceId
6136
+ )
6137
+ })).filter(
6138
+ (candidate) => !isPathCollidingWithChipInterior(candidate.path, chipObstacles) && !isPathColliding(
6139
+ candidate.path,
6140
+ [crossing.trace, crossing.otherTrace],
6141
+ candidate.traceId
6142
+ ).isColliding && (crossing.isInitialBundleCrossing || !candidate.collision.isColliding)
6143
+ ).sort(
6144
+ (first, second) => Number(first.collision.isColliding) - Number(second.collision.isColliding) || getPathLength(first.path) - getPathLength(second.path)
6145
+ );
6146
+ const bestCandidate = validCandidates[0];
6147
+ if (!bestCandidate) return false;
6148
+ const traceIndex = this.input.allTraces.findIndex(
6149
+ (trace) => trace.mspPairId === bestCandidate.traceId
6150
+ );
6151
+ this.input.allTraces[traceIndex] = {
6152
+ ...this.input.allTraces[traceIndex],
6153
+ tracePath: bestCandidate.path
6154
+ };
6155
+ this.reroutedTraceIds.add(bestCandidate.traceId);
6156
+ return true;
6157
+ }
5921
6158
  _resetAfterLShapProcessing() {
5922
6159
  this.lShapeProcessingStep = "idle";
5923
6160
  this.currentLShape = null;
@@ -6140,9 +6377,9 @@ var UntangleTraceSubsolver = class extends BaseSolver {
6140
6377
  };
6141
6378
 
6142
6379
  // lib/solvers/TraceCleanupSolver/is4PointRectangle.ts
6143
- var EPS8 = 1e-6;
6144
- var sameX = (a, b) => Math.abs(a.x - b.x) <= EPS8;
6145
- var sameY = (a, b) => Math.abs(a.y - b.y) <= EPS8;
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;
6146
6383
  var is4PointRectangle = (path) => {
6147
6384
  if (path.length !== 4) return false;
6148
6385
  const [p0, p1, p2, p3] = path;
@@ -7202,16 +7439,16 @@ var createPortOnlyLabelConnectorTrace = ({
7202
7439
  // lib/solvers/AvailableNetOrientationSolver/constants.ts
7203
7440
  var LABEL_SEARCH_STEP = 0.05;
7204
7441
  var WICK_CLEARANCE = 1e-3;
7205
- var EPS9 = 1e-9;
7206
- var TRACE_BOUNDARY_TOLERANCE = WICK_CLEARANCE + EPS9;
7442
+ var EPS10 = 1e-9;
7443
+ var TRACE_BOUNDARY_TOLERANCE = WICK_CLEARANCE + EPS10;
7207
7444
  var CANDIDATE_SELECTED_COLOR2 = "blue";
7208
7445
  var CANDIDATE_REJECTED_COLOR2 = "red";
7209
7446
 
7210
7447
  // lib/solvers/AvailableNetOrientationSolver/geometry.ts
7211
7448
  var isYOrientation = (orientation) => orientation === "y+" || orientation === "y-";
7212
7449
  var isXOrientation = (orientation) => orientation === "x+" || orientation === "x-";
7213
- 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;
7214
- var rangesOverlap = (minA, maxA, minB, maxB) => Math.min(maxA, maxB) - Math.max(minA, minB) > EPS9;
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;
7215
7452
  var traceCrossesBoundsInterior = (bounds, traceMap) => {
7216
7453
  for (const trace of Object.values(traceMap)) {
7217
7454
  const points = trace.tracePath;
@@ -7234,7 +7471,7 @@ var segmentCrossesBoundsInterior = (p1, p2, bounds) => {
7234
7471
  return false;
7235
7472
  }
7236
7473
  if (sameX2(p1, p2)) {
7237
- if (p1.x <= interiorBounds.minX + EPS9 || p1.x >= interiorBounds.maxX - EPS9) {
7474
+ if (p1.x <= interiorBounds.minX + EPS10 || p1.x >= interiorBounds.maxX - EPS10) {
7238
7475
  return false;
7239
7476
  }
7240
7477
  return rangesOverlap(
@@ -7245,7 +7482,7 @@ var segmentCrossesBoundsInterior = (p1, p2, bounds) => {
7245
7482
  );
7246
7483
  }
7247
7484
  if (sameY2(p1, p2)) {
7248
- if (p1.y <= interiorBounds.minY + EPS9 || p1.y >= interiorBounds.maxY - EPS9) {
7485
+ if (p1.y <= interiorBounds.minY + EPS10 || p1.y >= interiorBounds.maxY - EPS10) {
7249
7486
  return false;
7250
7487
  }
7251
7488
  return rangesOverlap(
@@ -7301,8 +7538,8 @@ var simplifyOrthogonalPath = (path) => {
7301
7538
  return simplified;
7302
7539
  };
7303
7540
  var pointsEqual2 = (a, b) => sameX2(a, b) && sameY2(a, b);
7304
- var sameX2 = (a, b) => Math.abs(a.x - b.x) <= EPS9;
7305
- var sameY2 = (a, b) => Math.abs(a.y - b.y) <= EPS9;
7541
+ var sameX2 = (a, b) => Math.abs(a.x - b.x) <= EPS10;
7542
+ var sameY2 = (a, b) => Math.abs(a.y - b.y) <= EPS10;
7306
7543
  var getMaxSearchDistance = (inputProblem) => {
7307
7544
  const maxChipWidth = Math.max(
7308
7545
  ...inputProblem.chips.map((chip) => chip.width),
@@ -7557,11 +7794,11 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
7557
7794
  if (trace.globalConnNetId !== label.globalConnNetId) return false;
7558
7795
  const path = trace.tracePath;
7559
7796
  return path.some((point, pointIndex) => {
7560
- if (Math.abs(point.x - label.anchorPoint.x) > EPS9 || Math.abs(point.y - label.anchorPoint.y) > EPS9) {
7797
+ if (Math.abs(point.x - label.anchorPoint.x) > EPS10 || Math.abs(point.y - label.anchorPoint.y) > EPS10) {
7561
7798
  return false;
7562
7799
  }
7563
7800
  return [path[pointIndex - 1], path[pointIndex + 1]].some(
7564
- (neighbor) => neighbor && Math.abs(neighbor.x - point.x) <= EPS9 && (neighbor.y - point.y) * direction.y > EPS9
7801
+ (neighbor) => neighbor && Math.abs(neighbor.x - point.x) <= EPS10 && (neighbor.y - point.y) * direction.y > EPS10
7565
7802
  );
7566
7803
  });
7567
7804
  });
@@ -7640,7 +7877,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
7640
7877
  );
7641
7878
  const maxSearchDistance = this.getSearchDistanceLimit(label, orientation);
7642
7879
  const maxOutwardDistance = outwardDirection.x === 0 && outwardDirection.y === 0 ? 0 : this.maxSearchDistance;
7643
- for (let outwardDistance = 0; outwardDistance <= maxOutwardDistance + EPS9; outwardDistance += LABEL_SEARCH_STEP) {
7880
+ for (let outwardDistance = 0; outwardDistance <= maxOutwardDistance + EPS10; outwardDistance += LABEL_SEARCH_STEP) {
7644
7881
  const baseAnchor = {
7645
7882
  x: initialBaseAnchor.x + outwardDirection.x * outwardDistance,
7646
7883
  y: initialBaseAnchor.y + outwardDirection.y * outwardDistance
@@ -7714,7 +7951,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
7714
7951
  phase = "shift",
7715
7952
  stopOnTraceCollision = true
7716
7953
  } = params;
7717
- for (let distance4 = LABEL_SEARCH_STEP; distance4 <= maxSearchDistance + EPS9; distance4 += LABEL_SEARCH_STEP) {
7954
+ for (let distance4 = LABEL_SEARCH_STEP; distance4 <= maxSearchDistance + EPS10; distance4 += LABEL_SEARCH_STEP) {
7718
7955
  const anchorPoint = {
7719
7956
  x: baseAnchor.x + direction.x * distance4,
7720
7957
  y: baseAnchor.y + direction.y * distance4
@@ -7951,9 +8188,9 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
7951
8188
  const { anchorPoint, orientation } = candidate;
7952
8189
  const chipBounds = chip.bounds;
7953
8190
  if (isYOrientation(orientation)) {
7954
- return anchorPoint.x < chipBounds.minX - EPS9 || anchorPoint.x > chipBounds.maxX + EPS9;
8191
+ return anchorPoint.x < chipBounds.minX - EPS10 || anchorPoint.x > chipBounds.maxX + EPS10;
7955
8192
  }
7956
- return anchorPoint.y < chipBounds.minY - EPS9 || anchorPoint.y > chipBounds.maxY + EPS9;
8193
+ return anchorPoint.y < chipBounds.minY - EPS10 || anchorPoint.y > chipBounds.maxY + EPS10;
7957
8194
  });
7958
8195
  }
7959
8196
  getBoundsStatus(candidateBoundsCheck) {
@@ -8027,8 +8264,8 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
8027
8264
  sharesChipBoundary(bounds) {
8028
8265
  for (const chip of this.chipObstacleSpatialIndex.chips) {
8029
8266
  const chipBounds = chip.bounds;
8030
- const adjacentToVerticalSide = Math.abs(bounds.minX - chipBounds.maxX) <= WICK_CLEARANCE + EPS9 || Math.abs(bounds.maxX - chipBounds.minX) <= WICK_CLEARANCE + EPS9;
8031
- const adjacentToHorizontalSide = Math.abs(bounds.minY - chipBounds.maxY) <= WICK_CLEARANCE + EPS9 || Math.abs(bounds.maxY - chipBounds.minY) <= WICK_CLEARANCE + EPS9;
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;
8032
8269
  if (adjacentToVerticalSide && rangesOverlap(
8033
8270
  bounds.minY,
8034
8271
  bounds.maxY,
@@ -8076,7 +8313,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
8076
8313
  let nearestDistance = Number.POSITIVE_INFINITY;
8077
8314
  for (const chip of this.chipObstacleSpatialIndex.chips) {
8078
8315
  const bounds = chip.bounds;
8079
- if (point.x < bounds.minX - EPS9 || point.x > bounds.maxX + EPS9 || point.y < bounds.minY - EPS9 || point.y > bounds.maxY + EPS9) {
8316
+ if (point.x < bounds.minX - EPS10 || point.x > bounds.maxX + EPS10 || point.y < bounds.minY - EPS10 || point.y > bounds.maxY + EPS10) {
8080
8317
  continue;
8081
8318
  }
8082
8319
  for (const [side, distance4] of getSideDistances(point, bounds)) {
@@ -8093,8 +8330,8 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
8093
8330
  let nearestDistanceSq = Number.POSITIVE_INFINITY;
8094
8331
  for (const chip of this.chipObstacleSpatialIndex.chips) {
8095
8332
  const bounds = chip.bounds;
8096
- const dx = point.x < bounds.minX - EPS9 ? bounds.minX - point.x : point.x > bounds.maxX + EPS9 ? point.x - bounds.maxX : 0;
8097
- const dy = point.y < bounds.minY - EPS9 ? bounds.minY - point.y : point.y > bounds.maxY + EPS9 ? point.y - bounds.maxY : 0;
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;
8098
8335
  if (dx === 0 && dy === 0) continue;
8099
8336
  const distanceSq = dx ** 2 + dy ** 2;
8100
8337
  if (distanceSq >= nearestDistanceSq) continue;
@@ -8472,11 +8709,11 @@ var getOrientationConstraint = (inputProblem, label) => {
8472
8709
  };
8473
8710
 
8474
8711
  // lib/solvers/TraceAnchoredNetLabelOverlapSolver/geometry.ts
8475
- var EPS10 = 1e-6;
8712
+ var EPS11 = 1e-6;
8476
8713
  var TRACE_BOUNDARY_TOLERANCE2 = 1e-6;
8477
8714
  var getLabelBounds = (label) => getRectBounds(label.center, label.width, label.height);
8478
- 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;
8479
- 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;
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;
8480
8717
  var traceCrossesBoundsInterior2 = (bounds, traces) => {
8481
8718
  for (const trace of traces) {
8482
8719
  const points = trace.tracePath;
@@ -8522,7 +8759,7 @@ var getPointAtTraceDistance = (trace, distance4) => {
8522
8759
  const end = trace.tracePath[i + 1];
8523
8760
  const segmentLength = getManhattanDistance(start, end);
8524
8761
  const nextDistance = pathDistance + segmentLength;
8525
- if (distance4 <= nextDistance + EPS10) {
8762
+ if (distance4 <= nextDistance + EPS11) {
8526
8763
  const offset = Math.max(
8527
8764
  0,
8528
8765
  Math.min(segmentLength, distance4 - pathDistance)
@@ -8561,7 +8798,7 @@ var segmentCrossesBoundsInterior2 = (start, end, bounds) => {
8561
8798
  return false;
8562
8799
  }
8563
8800
  if (isVertical4(start, end)) {
8564
- if (start.x <= interior.minX + EPS10 || start.x >= interior.maxX - EPS10) {
8801
+ if (start.x <= interior.minX + EPS11 || start.x >= interior.maxX - EPS11) {
8565
8802
  return false;
8566
8803
  }
8567
8804
  return rangesOverlap2(
@@ -8572,7 +8809,7 @@ var segmentCrossesBoundsInterior2 = (start, end, bounds) => {
8572
8809
  );
8573
8810
  }
8574
8811
  if (isHorizontal3(start, end)) {
8575
- if (start.y <= interior.minY + EPS10 || start.y >= interior.maxY - EPS10) {
8812
+ if (start.y <= interior.minY + EPS11 || start.y >= interior.maxY - EPS11) {
8576
8813
  return false;
8577
8814
  }
8578
8815
  return rangesOverlap2(
@@ -8586,10 +8823,10 @@ var segmentCrossesBoundsInterior2 = (start, end, bounds) => {
8586
8823
  };
8587
8824
  var isPointOnSegment2 = (point, start, end) => {
8588
8825
  if (isHorizontal3(start, end)) {
8589
- 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;
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;
8590
8827
  }
8591
8828
  if (isVertical4(start, end)) {
8592
- 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;
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;
8593
8830
  }
8594
8831
  return false;
8595
8832
  };
@@ -8597,9 +8834,9 @@ var getSegmentDirection = (start, end) => ({
8597
8834
  x: isVertical4(start, end) ? 0 : Math.sign(end.x - start.x),
8598
8835
  y: isHorizontal3(start, end) ? 0 : Math.sign(end.y - start.y)
8599
8836
  });
8600
- var isHorizontal3 = (start, end) => Math.abs(start.y - end.y) <= EPS10;
8601
- var isVertical4 = (start, end) => Math.abs(start.x - end.x) <= EPS10;
8602
- var rangesOverlap2 = (minA, maxA, minB, maxB) => Math.min(maxA, maxB) - Math.max(minA, minB) > EPS10;
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;
8603
8840
  var getUniquePinCount2 = (pinIds) => new Set(pinIds).size;
8604
8841
 
8605
8842
  // lib/solvers/TraceAnchoredNetLabelOverlapSolver/candidates.ts
@@ -8656,7 +8893,7 @@ var getCandidateDistances = (traceLength, vertexDistances) => {
8656
8893
  for (const distance4 of vertexDistances) {
8657
8894
  distances.add(roundDistance(distance4));
8658
8895
  }
8659
- return [...distances].filter((distance4) => distance4 >= -EPS10 && distance4 <= traceLength + EPS10).sort((a, b) => a - b);
8896
+ return [...distances].filter((distance4) => distance4 >= -EPS11 && distance4 <= traceLength + EPS11).sort((a, b) => a - b);
8660
8897
  };
8661
8898
  var getOrientationsForPoint = (params) => {
8662
8899
  const { inputProblem, label, point, orientationConstraint } = params;
@@ -8745,13 +8982,13 @@ var getOutwardOrientationOptions = (point, chipBounds) => {
8745
8982
  };
8746
8983
  var getOutwardAxisOption = (params) => {
8747
8984
  const { value, min, max, center, positiveOrientation, negativeOrientation } = params;
8748
- if (value > max + EPS10) {
8985
+ if (value > max + EPS11) {
8749
8986
  return {
8750
8987
  orientation: positiveOrientation,
8751
8988
  outwardScore: 1 + value - max
8752
8989
  };
8753
8990
  }
8754
- if (value < min - EPS10) {
8991
+ if (value < min - EPS11) {
8755
8992
  return {
8756
8993
  orientation: negativeOrientation,
8757
8994
  outwardScore: 1 + min - value
@@ -8762,7 +8999,7 @@ var getOutwardAxisOption = (params) => {
8762
8999
  outwardScore: getNormalizedCenterDistance(value, center, max - min)
8763
9000
  };
8764
9001
  };
8765
- var getNormalizedCenterDistance = (value, center, span) => Math.abs(value - center) / Math.max(EPS10, span / 2);
9002
+ var getNormalizedCenterDistance = (value, center, span) => Math.abs(value - center) / Math.max(EPS11, span / 2);
8766
9003
  var ALL_ORIENTATIONS = ["x+", "x-", "y+", "y-"];
8767
9004
  var getFlippedOrientation = (orientation) => {
8768
9005
  switch (orientation) {
@@ -8835,7 +9072,7 @@ var getNetLabelHeight = (inputProblem, label) => {
8835
9072
  )?.netLabelHeight;
8836
9073
  };
8837
9074
  var roundDistance = (distance4) => Number(distance4.toFixed(6));
8838
- var isSamePlacement = (label, point, orientation) => Math.abs(point.x - label.anchorPoint.x) <= EPS10 && Math.abs(point.y - label.anchorPoint.y) <= EPS10 && orientation === label.orientation;
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;
8839
9076
 
8840
9077
  // lib/solvers/TraceAnchoredNetLabelOverlapSolver/visualize.ts
8841
9078
  var CANDIDATE_SELECTED_COLOR4 = "blue";