@tscircuit/schematic-trace-solver 0.0.174 → 0.0.175

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 (28) hide show
  1. package/dist/index.d.ts +2 -0
  2. package/dist/index.js +195 -74
  3. package/lib/solvers/InlineNetLabelSolver/InlineNetLabelSolver.ts +16 -1
  4. package/lib/solvers/InlineNetLabelSolver/restoreReroutesAroundSupersededLabels.ts +174 -0
  5. package/lib/solvers/NetLabelToTraceSolver/NetLabelToTraceSolver.ts +2 -30
  6. package/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts +9 -0
  7. package/lib/utils/doesPathCoincideWithTraces.ts +17 -4
  8. package/lib/utils/pathIntersectsRenderedLabel.ts +33 -0
  9. package/package.json +1 -1
  10. package/site/bug-reports/bug-report-20260901T055358Z.page.tsx +4 -0
  11. package/site/bug-reports/bug-report-20260901T064117Z.page.tsx +4 -0
  12. package/site/bug-reports/bug-report-20260901T134241Z.page.tsx +4 -0
  13. package/tests/bug-reports/bug-report-20260901T055358Z/__snapshots__/bug-report-20260901T055358Z.snap.svg +676 -0
  14. package/tests/bug-reports/bug-report-20260901T055358Z/bug-report-20260901T055358Z.json +2467 -0
  15. package/tests/bug-reports/bug-report-20260901T055358Z/bug-report-20260901T055358Z.test.ts +59 -0
  16. package/tests/bug-reports/bug-report-20260901T064117Z/__snapshots__/bug-report-20260901T064117Z.snap.svg +134 -0
  17. package/tests/bug-reports/bug-report-20260901T064117Z/bug-report-20260901T064117Z.json +215 -0
  18. package/tests/bug-reports/bug-report-20260901T064117Z/bug-report-20260901T064117Z.test.ts +12 -0
  19. package/tests/bug-reports/bug-report-20260901T134241Z/__snapshots__/bug-report-20260901T134241Z.snap.svg +115 -0
  20. package/tests/bug-reports/bug-report-20260901T134241Z/bug-report-20260901T134241Z.json +359 -0
  21. package/tests/bug-reports/bug-report-20260901T134241Z/bug-report-20260901T134241Z.test.ts +12 -0
  22. package/tests/repros/__snapshots__/repro-trellis-core-gnd-net-label-overlap.snap.svg +134 -0
  23. package/tests/repros/__snapshots__/repro-wireless-mouse-charger-section.snap.svg +164 -0
  24. package/tests/repros/assets/repro-trellis-core-gnd-net-label-overlap.input.json +178 -0
  25. package/tests/repros/assets/repro-wireless-mouse-charger-section.input.json +591 -0
  26. package/tests/repros/repro-trellis-core-gnd-net-label-overlap.test.ts +73 -0
  27. package/tests/repros/repro-wireless-mouse-charger-section.test.ts +16 -0
  28. package/tests/solvers/InlineNetLabelSolver/restore-reroutes-around-superseded-labels.test.ts +186 -0
package/dist/index.d.ts CHANGED
@@ -1286,6 +1286,7 @@ interface InlineNetLabelSolverInput {
1286
1286
  inputProblem: InputProblem;
1287
1287
  traces: SolvedTracePath[];
1288
1288
  netLabelPlacements: NetLabelPlacement[];
1289
+ completedReroutes?: CompletedTraceReroute[];
1289
1290
  }
1290
1291
  interface InlineNetLabelOutput {
1291
1292
  inputProblem: InputProblem;
@@ -1311,6 +1312,7 @@ declare class InlineNetLabelSolver extends BaseSolver {
1311
1312
  inputProblem: InputProblem;
1312
1313
  traces: SolvedTracePath[];
1313
1314
  inputNetLabelPlacements: NetLabelPlacement[];
1315
+ completedReroutes: CompletedTraceReroute[];
1314
1316
  inlineNetLabelPlacements: InlineNetLabelPlacement[];
1315
1317
  /** Eligible connections still waiting to be processed. */
1316
1318
  queuedConnections: QueuedInlineConnection[];
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 EPS15 = 1e-9;
328
+ const EPS16 = 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) < -EPS15) return true;
332
+ if ((a.x - rcl.x) * (b.x - rcl.x) < -EPS16) 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) < -EPS15) return true;
335
+ if ((a.y - rcl.y) * (b.y - rcl.y) < -EPS16) return true;
336
336
  }
337
337
  }
338
338
  return false;
339
339
  };
340
- if (Math.abs(p1.x - p2.x) < EPS15 || Math.abs(p1.y - p2.y) < EPS15) {
340
+ if (Math.abs(p1.x - p2.x) < EPS16 || Math.abs(p1.y - p2.y) < EPS16) {
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 };
@@ -1575,8 +1575,8 @@ var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
1575
1575
  if (!collision) {
1576
1576
  const first = path[0];
1577
1577
  const last = path[path.length - 1];
1578
- const EPS15 = 1e-9;
1579
- const samePoint = (p, q) => Math.abs(p.x - q.x) < EPS15 && Math.abs(p.y - q.y) < EPS15;
1578
+ const EPS16 = 1e-9;
1579
+ const samePoint = (p, q) => Math.abs(p.x - q.x) < EPS16 && Math.abs(p.y - q.y) < EPS16;
1580
1580
  if (samePoint(first, { x: PA.x, y: PA.y }) && samePoint(last, { x: PB.x, y: PB.y })) {
1581
1581
  this.solvedTracePath = path;
1582
1582
  this.solved = true;
@@ -1887,13 +1887,13 @@ var applyJogToTerminalSegment = ({
1887
1887
  segmentIndex: si,
1888
1888
  offset,
1889
1889
  JOG_SIZE,
1890
- EPS: EPS15 = 1e-6
1890
+ EPS: EPS16 = 1e-6
1891
1891
  }) => {
1892
1892
  if (si !== 0 && si !== pts.length - 2) return;
1893
1893
  const start = pts[si];
1894
1894
  const end = pts[si + 1];
1895
- const isVertical6 = Math.abs(start.x - end.x) < EPS15;
1896
- const isHorizontal5 = Math.abs(start.y - end.y) < EPS15;
1895
+ const isVertical6 = Math.abs(start.x - end.x) < EPS16;
1896
+ const isHorizontal5 = Math.abs(start.y - end.y) < EPS16;
1897
1897
  if (!isVertical6 && !isHorizontal5) return;
1898
1898
  const segDir = isVertical6 ? end.y > start.y ? 1 : -1 : end.x > start.x ? 1 : -1;
1899
1899
  if (pts.length === 2) {
@@ -2327,7 +2327,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
2327
2327
  return islands;
2328
2328
  }
2329
2329
  findNextOverlapIssue() {
2330
- const EPS15 = 2e-3;
2330
+ const EPS16 = 2e-3;
2331
2331
  const netIds = Object.keys(this.traceNetIslands);
2332
2332
  for (let i = 0; i < netIds.length; i++) {
2333
2333
  for (let j = i + 1; j < netIds.length; j++) {
@@ -2354,7 +2354,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
2354
2354
  segmentBIndex,
2355
2355
  rangeOverlap
2356
2356
  }) => {
2357
- if (rangeOverlap > EPS15) {
2357
+ if (rangeOverlap > EPS16) {
2358
2358
  const keyA = `${pathAIndex}:${segmentAIndex}`;
2359
2359
  const keyB = `${pathBIndex}:${segmentBIndex}`;
2360
2360
  if (!seenA.has(keyA)) {
@@ -2373,7 +2373,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
2373
2373
  }
2374
2374
  return;
2375
2375
  }
2376
- if (rangeOverlap < -EPS15) return;
2376
+ if (rangeOverlap < -EPS16) return;
2377
2377
  const pathA = pathsA[pathAIndex].tracePath;
2378
2378
  const pathB = pathsB[pathBIndex].tracePath;
2379
2379
  const segmentAStart = pathA[segmentAIndex];
@@ -2396,8 +2396,8 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
2396
2396
  for (let sa = 0; sa < ptsA.length - 1; sa++) {
2397
2397
  const a1 = ptsA[sa];
2398
2398
  const a2 = ptsA[sa + 1];
2399
- const aVert = Math.abs(a1.x - a2.x) < EPS15;
2400
- const aHorz = Math.abs(a1.y - a2.y) < EPS15;
2399
+ const aVert = Math.abs(a1.x - a2.x) < EPS16;
2400
+ const aHorz = Math.abs(a1.y - a2.y) < EPS16;
2401
2401
  if (!aVert && !aHorz) continue;
2402
2402
  for (let pb = 0; pb < pathsB.length; pb++) {
2403
2403
  const pathB = pathsB[pb];
@@ -2408,11 +2408,11 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
2408
2408
  for (let sb = 0; sb < ptsB.length - 1; sb++) {
2409
2409
  const b1 = ptsB[sb];
2410
2410
  const b2 = ptsB[sb + 1];
2411
- const bVert = Math.abs(b1.x - b2.x) < EPS15;
2412
- const bHorz = Math.abs(b1.y - b2.y) < EPS15;
2411
+ const bVert = Math.abs(b1.x - b2.x) < EPS16;
2412
+ const bHorz = Math.abs(b1.y - b2.y) < EPS16;
2413
2413
  if (!bVert && !bHorz) continue;
2414
2414
  if (aVert && bVert) {
2415
- if (Math.abs(a1.x - b1.x) < EPS15) {
2415
+ if (Math.abs(a1.x - b1.x) < EPS16) {
2416
2416
  recordCollinearInteraction({
2417
2417
  pathAIndex: pa,
2418
2418
  segmentAIndex: sa,
@@ -2422,7 +2422,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
2422
2422
  });
2423
2423
  }
2424
2424
  } else if (aHorz && bHorz) {
2425
- if (Math.abs(a1.y - b1.y) < EPS15) {
2425
+ if (Math.abs(a1.y - b1.y) < EPS16) {
2426
2426
  recordCollinearInteraction({
2427
2427
  pathAIndex: pa,
2428
2428
  segmentAIndex: sa,
@@ -2478,14 +2478,14 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
2478
2478
  return null;
2479
2479
  }
2480
2480
  findNextDiagonalSegment() {
2481
- const EPS15 = 2e-3;
2481
+ const EPS16 = 2e-3;
2482
2482
  for (const mspPairId in this.correctedTraceMap) {
2483
2483
  const tracePath = this.correctedTraceMap[mspPairId].tracePath;
2484
2484
  for (let i = 0; i < tracePath.length - 1; i++) {
2485
2485
  const p1 = tracePath[i];
2486
2486
  const p2 = tracePath[i + 1];
2487
- const isHorizontal5 = Math.abs(p1.y - p2.y) < EPS15;
2488
- const isVertical6 = Math.abs(p1.x - p2.x) < EPS15;
2487
+ const isHorizontal5 = Math.abs(p1.y - p2.y) < EPS16;
2488
+ const isVertical6 = Math.abs(p1.x - p2.x) < EPS16;
2489
2489
  if (!isHorizontal5 && !isVertical6) {
2490
2490
  return { mspPairId, tracePath, i, p1, p2 };
2491
2491
  }
@@ -2499,13 +2499,13 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
2499
2499
  return false;
2500
2500
  }
2501
2501
  const { mspPairId, tracePath, i, p1, p2 } = diagonalInfo;
2502
- const EPS15 = 2e-3;
2502
+ const EPS16 = 2e-3;
2503
2503
  const p0 = i > 0 ? tracePath[i - 1] : null;
2504
2504
  const p3 = i + 2 < tracePath.length ? tracePath[i + 2] : null;
2505
- const prevIsVertical = p0 ? Math.abs(p0.x - p1.x) < EPS15 : false;
2506
- const prevIsHorizontal = p0 ? Math.abs(p0.y - p1.y) < EPS15 : false;
2507
- const nextIsVertical = p3 ? Math.abs(p2.x - p3.x) < EPS15 : false;
2508
- const nextIsHorizontal = p3 ? Math.abs(p2.y - p3.y) < EPS15 : false;
2505
+ const prevIsVertical = p0 ? Math.abs(p0.x - p1.x) < EPS16 : false;
2506
+ const prevIsHorizontal = p0 ? Math.abs(p0.y - p1.y) < EPS16 : false;
2507
+ const nextIsVertical = p3 ? Math.abs(p2.x - p3.x) < EPS16 : false;
2508
+ const nextIsHorizontal = p3 ? Math.abs(p2.y - p3.y) < EPS16 : false;
2509
2509
  const elbow1 = { x: p1.x, y: p2.y };
2510
2510
  const elbow2 = { x: p2.x, y: p1.y };
2511
2511
  let score1 = 0;
@@ -2667,24 +2667,24 @@ var ChipObstacleSpatialIndex = class {
2667
2667
  };
2668
2668
 
2669
2669
  // lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/collisions.ts
2670
- function segmentIntersectsRect2(p1, p2, rect, EPS15 = 1e-9) {
2671
- const isVert = Math.abs(p1.x - p2.x) < EPS15;
2672
- const isHorz = Math.abs(p1.y - p2.y) < EPS15;
2670
+ function segmentIntersectsRect2(p1, p2, rect, EPS16 = 1e-9) {
2671
+ const isVert = Math.abs(p1.x - p2.x) < EPS16;
2672
+ const isHorz = Math.abs(p1.y - p2.y) < EPS16;
2673
2673
  if (!isVert && !isHorz) return false;
2674
2674
  if (isVert) {
2675
2675
  const x = p1.x;
2676
- if (x < rect.minX - EPS15 || x > rect.maxX + EPS15) return false;
2676
+ if (x < rect.minX - EPS16 || x > rect.maxX + EPS16) return false;
2677
2677
  const segMinY = Math.min(p1.y, p2.y);
2678
2678
  const segMaxY = Math.max(p1.y, p2.y);
2679
2679
  const overlap = Math.min(segMaxY, rect.maxY) - Math.max(segMinY, rect.minY);
2680
- return overlap > EPS15;
2680
+ return overlap > EPS16;
2681
2681
  } else {
2682
2682
  const y = p1.y;
2683
- if (y < rect.minY - EPS15 || y > rect.maxY + EPS15) return false;
2683
+ if (y < rect.minY - EPS16 || y > rect.maxY + EPS16) return false;
2684
2684
  const segMinX = Math.min(p1.x, p2.x);
2685
2685
  const segMaxX = Math.max(p1.x, p2.x);
2686
2686
  const overlap = Math.min(segMaxX, rect.maxX) - Math.max(segMinX, rect.minX);
2687
- return overlap > EPS15;
2687
+ return overlap > EPS16;
2688
2688
  }
2689
2689
  }
2690
2690
  function rectIntersectsAnyTrace(bounds, inputTraceMap, hostPathId, hostSegIndex) {
@@ -3083,14 +3083,14 @@ var SingleNetLabelPlacementSolver = class extends BaseSolver {
3083
3083
  };
3084
3084
  let bestCandidate = null;
3085
3085
  let bestScore = -Infinity;
3086
- const EPS15 = 1e-6;
3086
+ const EPS16 = 1e-6;
3087
3087
  for (const curr of tracesToScan) {
3088
3088
  const pts = curr.tracePath.slice();
3089
3089
  for (let si = 0; si < pts.length - 1; si++) {
3090
3090
  const a = pts[si];
3091
3091
  const b = pts[si + 1];
3092
- const isH = Math.abs(a.y - b.y) < EPS15;
3093
- const isV = Math.abs(a.x - b.x) < EPS15;
3092
+ const isH = Math.abs(a.y - b.y) < EPS16;
3093
+ const isV = Math.abs(a.x - b.x) < EPS16;
3094
3094
  if (!isH && !isV) continue;
3095
3095
  const segmentAllowed = isH ? ["y+", "y-"] : ["x+", "x-"];
3096
3096
  const candidateOrients = orientations.filter(
@@ -4171,7 +4171,11 @@ var GEOMETRY_EPS = 1e-6;
4171
4171
  var SCHEMATIC_TRACE_STROKE_WIDTH = 0.02;
4172
4172
  var SCHEMATIC_TRACE_MIN_CENTERLINE_CLEARANCE = SCHEMATIC_TRACE_STROKE_WIDTH + GEOMETRY_EPS;
4173
4173
  var SCHEMATIC_TRACE_MIN_VISUAL_CENTERLINE_CLEARANCE = SCHEMATIC_TRACE_STROKE_WIDTH * 3;
4174
- var doesPathCoincideWithTraces = (path, traces) => {
4174
+ var doesPathCoincideWithTraces = (path, traces) => doesPathCoincideWithPaths(
4175
+ path,
4176
+ traces.map((trace) => trace.tracePath)
4177
+ );
4178
+ var doesPathCoincideWithPaths = (path, otherPaths) => {
4175
4179
  const rangesOverlap1D = (a1, a2, b1, b2) => Math.min(Math.max(a1, a2), Math.max(b1, b2)) - Math.max(Math.min(a1, a2), Math.min(b1, b2)) > COINCIDENT_EPS;
4176
4180
  for (let i = 0; i < path.length - 1; i++) {
4177
4181
  const pathSegStart = path[i];
@@ -4181,10 +4185,10 @@ var doesPathCoincideWithTraces = (path, traces) => {
4181
4185
  if (!isVertical6 && !isHorizontal5) continue;
4182
4186
  const crossAxis = isVertical6 ? "x" : "y";
4183
4187
  const alongAxis = isVertical6 ? "y" : "x";
4184
- for (const trace of traces) {
4185
- for (let j = 0; j < trace.tracePath.length - 1; j++) {
4186
- const traceSegStart = trace.tracePath[j];
4187
- const traceSegEnd = trace.tracePath[j + 1];
4188
+ for (const otherPath of otherPaths) {
4189
+ for (let j = 0; j < otherPath.length - 1; j++) {
4190
+ const traceSegStart = otherPath[j];
4191
+ const traceSegEnd = otherPath[j + 1];
4188
4192
  const isParallel = Math.abs(traceSegStart[crossAxis] - traceSegEnd[crossAxis]) < COINCIDENT_EPS;
4189
4193
  if (!isParallel) continue;
4190
4194
  const isCoincident = Math.abs(pathSegStart[crossAxis] - traceSegStart[crossAxis]) < COINCIDENT_EPS;
@@ -14445,6 +14449,131 @@ var pushInlineTerminalLabelsAwayFromAnchoredLabels = ({
14445
14449
  };
14446
14450
  };
14447
14451
 
14452
+ // lib/solvers/InlineNetLabelSolver/restoreReroutesAroundSupersededLabels.ts
14453
+ import { getSegmentIntersection as getSegmentIntersection5 } from "@tscircuit/math-utils/line-intersections";
14454
+
14455
+ // lib/utils/pathIntersectsRenderedLabel.ts
14456
+ import {
14457
+ doesSegmentIntersectRect,
14458
+ getBoundFromCenteredRect
14459
+ } from "@tscircuit/math-utils";
14460
+ var pathIntersectsRenderedLabel = (path, label) => {
14461
+ let width = label.width;
14462
+ let height = label.height;
14463
+ if ("axis" in label && label.axis === "y") {
14464
+ width = label.height;
14465
+ height = label.width;
14466
+ }
14467
+ const bounds = getBoundFromCenteredRect({
14468
+ center: label.center,
14469
+ width,
14470
+ height
14471
+ });
14472
+ for (let pathIndex = 0; pathIndex < path.length - 1; pathIndex++) {
14473
+ if (doesSegmentIntersectRect(path[pathIndex], path[pathIndex + 1], bounds)) {
14474
+ return true;
14475
+ }
14476
+ }
14477
+ return false;
14478
+ };
14479
+
14480
+ // lib/solvers/InlineNetLabelSolver/restoreReroutesAroundSupersededLabels.ts
14481
+ var EPS14 = 1e-6;
14482
+ var getPathLength5 = (path) => path.slice(1).reduce((length, point, pointIndex) => {
14483
+ const previousPoint = path[pointIndex];
14484
+ return length + Math.abs(point.x - previousPoint.x) + Math.abs(point.y - previousPoint.y);
14485
+ }, 0);
14486
+ var pathsEqual = (first, second) => first.length === second.length && first.every(
14487
+ (point, index) => Math.abs(point.x - second[index].x) <= EPS14 && Math.abs(point.y - second[index].y) <= EPS14
14488
+ );
14489
+ var isStrictlySimpler = (candidate, current) => candidate.length < current.length && getPathLength5(candidate) <= getPathLength5(current) + EPS14;
14490
+ var getIntersectionKeys = (path, otherPath) => {
14491
+ const intersections = /* @__PURE__ */ new Set();
14492
+ for (let pathIndex = 0; pathIndex < path.length - 1; pathIndex++) {
14493
+ for (let otherPathIndex = 0; otherPathIndex < otherPath.length - 1; otherPathIndex++) {
14494
+ const point = getSegmentIntersection5(
14495
+ path[pathIndex],
14496
+ path[pathIndex + 1],
14497
+ otherPath[otherPathIndex],
14498
+ otherPath[otherPathIndex + 1]
14499
+ );
14500
+ if (point) {
14501
+ intersections.add(`${point.x.toFixed(6)},${point.y.toFixed(6)}`);
14502
+ }
14503
+ }
14504
+ }
14505
+ return intersections;
14506
+ };
14507
+ var introducesNewCrossings = (candidatePath, currentPath, otherPaths) => otherPaths.some((otherPath) => {
14508
+ const currentIntersections = getIntersectionKeys(currentPath, otherPath);
14509
+ const candidateIntersections = getIntersectionKeys(candidatePath, otherPath);
14510
+ return [...candidateIntersections].some(
14511
+ (intersection) => !currentIntersections.has(intersection)
14512
+ );
14513
+ });
14514
+ var restoreReroutesAroundSupersededLabels = ({
14515
+ inputProblem,
14516
+ traces,
14517
+ netLabelPlacements,
14518
+ inlineNetLabelPlacements,
14519
+ completedReroutes
14520
+ }) => {
14521
+ const outputTraces = [...traces];
14522
+ const supersededLabelGlobalConnNetIds = new Set(
14523
+ inlineNetLabelPlacements.map((placement) => placement.globalConnNetId)
14524
+ );
14525
+ const obstacles = getObstacleRects(inputProblem);
14526
+ let restoredTraceCount = 0;
14527
+ for (const reroute of [...completedReroutes].reverse()) {
14528
+ if (!supersededLabelGlobalConnNetIds.has(reroute.label.globalConnNetId)) {
14529
+ continue;
14530
+ }
14531
+ const traceIndex = outputTraces.findIndex(
14532
+ (trace) => trace.mspPairId === reroute.initialTrace.mspPairId
14533
+ );
14534
+ if (traceIndex < 0) continue;
14535
+ const currentTrace = outputTraces[traceIndex];
14536
+ const currentPath = simplifyPath(currentTrace.tracePath);
14537
+ const recordedReroutePath = simplifyPath(reroute.reroutedTracePath);
14538
+ if (!pathsEqual(currentPath, recordedReroutePath)) continue;
14539
+ const candidatePath = simplifyPath(reroute.initialTrace.tracePath);
14540
+ if (!isStrictlySimpler(candidatePath, currentPath)) continue;
14541
+ const candidateTrace = { ...currentTrace, tracePath: candidatePath };
14542
+ const otherNetTraces = outputTraces.filter(
14543
+ (trace) => trace.mspPairId !== currentTrace.mspPairId && trace.globalConnNetId !== currentTrace.globalConnNetId
14544
+ );
14545
+ const otherNetInlineLabels = inlineNetLabelPlacements.filter(
14546
+ (label) => label.globalConnNetId !== currentTrace.globalConnNetId
14547
+ );
14548
+ const otherNetInlineStubPaths = otherNetInlineLabels.flatMap(
14549
+ (label) => label.stubTracePath ? [[...label.stubTracePath]] : []
14550
+ );
14551
+ if (isPathCollidingWithObstacles(candidatePath, obstacles) || detectTraceLabelOverlap({
14552
+ traces: [candidateTrace],
14553
+ netLabels: netLabelPlacements
14554
+ }).length > 0 || otherNetInlineLabels.some(
14555
+ (label) => pathIntersectsRenderedLabel(candidatePath, label)
14556
+ ) || doesPathCoincideWithTraces(candidatePath, otherNetTraces) || doesPathCoincideWithPaths(candidatePath, otherNetInlineStubPaths) || introducesNewCrossings(
14557
+ candidatePath,
14558
+ currentPath,
14559
+ otherNetTraces.map((trace) => trace.tracePath)
14560
+ ) || introducesNewCrossings(
14561
+ candidatePath,
14562
+ currentPath,
14563
+ otherNetInlineStubPaths
14564
+ ) || !preservesLabelAnchors(
14565
+ netLabelPlacements,
14566
+ [currentTrace],
14567
+ [candidateTrace]
14568
+ )) {
14569
+ continue;
14570
+ }
14571
+ outputTraces[traceIndex] = candidateTrace;
14572
+ restoredTraceCount++;
14573
+ }
14574
+ return { traces: outputTraces, restoredTraceCount };
14575
+ };
14576
+
14448
14577
  // lib/solvers/InlineNetLabelSolver/InlineNetLabelSolver.ts
14449
14578
  var DEFAULT_INLINE_NET_LABEL_HEIGHT = 0.18;
14450
14579
  var INLINE_NET_LABEL_TRACE_MARGIN = 0.05;
@@ -14477,6 +14606,7 @@ var InlineNetLabelSolver = class extends BaseSolver {
14477
14606
  inputProblem;
14478
14607
  traces;
14479
14608
  inputNetLabelPlacements;
14609
+ completedReroutes;
14480
14610
  inlineNetLabelPlacements = [];
14481
14611
  /** Eligible connections still waiting to be processed. */
14482
14612
  queuedConnections;
@@ -14490,6 +14620,7 @@ var InlineNetLabelSolver = class extends BaseSolver {
14490
14620
  this.inputProblem = input.inputProblem;
14491
14621
  this.traces = input.traces;
14492
14622
  this.inputNetLabelPlacements = input.netLabelPlacements;
14623
+ this.completedReroutes = input.completedReroutes ?? [];
14493
14624
  this.globalConnMap = getConnectivityMapsFromInputProblem(
14494
14625
  this.inputProblem
14495
14626
  ).netConnMap;
@@ -14524,7 +14655,8 @@ var InlineNetLabelSolver = class extends BaseSolver {
14524
14655
  {
14525
14656
  inputProblem: this.inputProblem,
14526
14657
  traces: this.traces,
14527
- netLabelPlacements: this.inputNetLabelPlacements
14658
+ netLabelPlacements: this.inputNetLabelPlacements,
14659
+ completedReroutes: this.completedReroutes
14528
14660
  }
14529
14661
  ];
14530
14662
  }
@@ -15097,9 +15229,17 @@ var InlineNetLabelSolver = class extends BaseSolver {
15097
15229
  if (blockedGlobalConnNetIds.size === 0) {
15098
15230
  this.inlineNetLabelPlacements = activeInlinePlacements;
15099
15231
  this.stats.pushedAnchoredNetLabelCount = pushed.movedLabelCount;
15100
- return {
15232
+ const restored = restoreReroutesAroundSupersededLabels({
15233
+ inputProblem: this.inputProblem,
15101
15234
  traces: pushed.traces,
15102
15235
  netLabelPlacements: pushed.netLabelPlacements,
15236
+ inlineNetLabelPlacements: activeInlinePlacements,
15237
+ completedReroutes: this.completedReroutes
15238
+ });
15239
+ this.stats.restoredSupersededLabelRerouteCount = restored.restoredTraceCount;
15240
+ return {
15241
+ traces: restored.traces,
15242
+ netLabelPlacements: pushed.netLabelPlacements,
15103
15243
  inlineNetLabelPlacements: activeInlinePlacements
15104
15244
  };
15105
15245
  }
@@ -15307,12 +15447,6 @@ var getCollisionLimitedTerminalStubEnd = ({
15307
15447
  return isHorizontal5 ? { x: start.x + direction * availableLength, y: start.y } : { x: start.x, y: start.y + direction * availableLength };
15308
15448
  };
15309
15449
 
15310
- // lib/solvers/NetLabelToTraceSolver/NetLabelToTraceSolver.ts
15311
- import {
15312
- doesSegmentIntersectRect,
15313
- getBoundFromCenteredRect
15314
- } from "@tscircuit/math-utils";
15315
-
15316
15450
  // lib/solvers/NetLabelTraceRecovery/doesTraceRecoveryPathConflict.ts
15317
15451
  import { doSegmentsIntersect as doSegmentsIntersect4 } from "@tscircuit/math-utils";
15318
15452
  var EPSILON = 1e-6;
@@ -15407,12 +15541,12 @@ var getTraceRecoveryConnectivityMaps = (inputProblem) => {
15407
15541
  };
15408
15542
 
15409
15543
  // lib/solvers/NetLabelToTraceSolver/reduceTraceCrossings.ts
15410
- var EPS14 = 1e-9;
15411
- var isHorizontal4 = (start, end) => Math.abs(start.y - end.y) < EPS14;
15412
- var isVertical5 = (start, end) => Math.abs(start.x - end.x) < EPS14;
15544
+ var EPS15 = 1e-9;
15545
+ var isHorizontal4 = (start, end) => Math.abs(start.y - end.y) < EPS15;
15546
+ var isVertical5 = (start, end) => Math.abs(start.x - end.x) < EPS15;
15413
15547
  var compactConsecutivePoints = (path) => path.filter((point, index) => {
15414
15548
  const previousPoint = path[index - 1];
15415
- return !previousPoint || Math.abs(point.x - previousPoint.x) >= EPS14 || Math.abs(point.y - previousPoint.y) >= EPS14;
15549
+ return !previousPoint || Math.abs(point.x - previousPoint.x) >= EPS15 || Math.abs(point.y - previousPoint.y) >= EPS15;
15416
15550
  });
15417
15551
  var isValidOrthogonalPath = (path) => {
15418
15552
  if (path.length < 2 || !path.every((point, index) => {
@@ -15450,7 +15584,7 @@ var getEndpointAlignedSegmentCandidates = (tracePath) => {
15450
15584
  }
15451
15585
  const alignedCoordinates = segmentIsVertical ? [previousPoint.x, nextPoint.x] : [previousPoint.y, nextPoint.y];
15452
15586
  for (const alignedCoordinate of alignedCoordinates) {
15453
- if (Math.abs(alignedCoordinate - (segmentIsVertical ? start.x : start.y)) < EPS14) {
15587
+ if (Math.abs(alignedCoordinate - (segmentIsVertical ? start.x : start.y)) < EPS15) {
15454
15588
  continue;
15455
15589
  }
15456
15590
  const candidate = tracePath.map((point) => ({ ...point }));
@@ -15513,25 +15647,6 @@ var RECOVERED_TRACE_PREFIX = "net-label-to-trace-";
15513
15647
  var MAX_NAMED_NET_RECOVERY_PERPENDICULAR_OFFSET = 0.05;
15514
15648
  var MAX_ROUTED_COMPONENT_RECOVERY_PERPENDICULAR_OFFSET = 0.25;
15515
15649
  var getCanonicalPairKey = (firstPinId, secondPinId) => [firstPinId, secondPinId].sort().join("--");
15516
- var pathIntersectsRenderedLabel = (path, label) => {
15517
- let width = label.width;
15518
- let height = label.height;
15519
- if ("axis" in label && label.axis === "y") {
15520
- width = label.height;
15521
- height = label.width;
15522
- }
15523
- const bounds = getBoundFromCenteredRect({
15524
- center: label.center,
15525
- width,
15526
- height
15527
- });
15528
- for (let pathIndex = 0; pathIndex < path.length - 1; pathIndex++) {
15529
- if (doesSegmentIntersectRect(path[pathIndex], path[pathIndex + 1], bounds)) {
15530
- return true;
15531
- }
15532
- }
15533
- return false;
15534
- };
15535
15650
  var getPerpendicularOffset = (firstPoint, secondPoint) => {
15536
15651
  const xDistance = Math.abs(firstPoint.x - secondPoint.x);
15537
15652
  const yDistance = Math.abs(firstPoint.y - secondPoint.y);
@@ -16345,11 +16460,17 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
16345
16460
  InlineNetLabelSolver,
16346
16461
  (instance) => {
16347
16462
  const junctionOutput = instance.sameNetJunctionAlignmentSolver.getOutput();
16463
+ const completedReroutes = [
16464
+ ...instance.traceLabelOverlapAvoidanceSolver.getOutput().completedReroutes,
16465
+ ...instance.preAlignmentNetLabelTraceCollisionSolver.getOutput().completedReroutes,
16466
+ ...instance.netLabelTraceCollisionSolver.getOutput().completedReroutes
16467
+ ];
16348
16468
  return [
16349
16469
  {
16350
16470
  inputProblem: instance.inputProblem,
16351
16471
  traces: junctionOutput.traces,
16352
- netLabelPlacements: junctionOutput.netLabelPlacements
16472
+ netLabelPlacements: junctionOutput.netLabelPlacements,
16473
+ completedReroutes
16353
16474
  }
16354
16475
  ];
16355
16476
  }
@@ -8,6 +8,7 @@ import { getTraceConnectedPinComponents } from "lib/solvers/SchematicTraceLinesS
8
8
  import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
9
9
  import { getPinDirection } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/getPinDirection"
10
10
  import { visualizeInputProblem } from "lib/solvers/SchematicTracePipelineSolver/visualizeInputProblem"
11
+ import type { CompletedTraceReroute } from "lib/solvers/TraceElbowTransitionSimplificationSolver/types"
11
12
  import type {
12
13
  InputDirectConnection,
13
14
  InputNetConnection,
@@ -24,6 +25,7 @@ import {
24
25
  import { getAnchoredNetLabelRenderedBounds } from "./getAnchoredNetLabelRenderedBounds"
25
26
  import { pushAnchoredNetLabelsAwayFromInlineLabels } from "./pushAnchoredNetLabelsAwayFromInlineLabels"
26
27
  import { pushInlineTerminalLabelsAwayFromAnchoredLabels } from "./pushInlineTerminalLabelsAwayFromAnchoredLabels"
28
+ import { restoreReroutesAroundSupersededLabels } from "./restoreReroutesAroundSupersededLabels"
27
29
 
28
30
  export const DEFAULT_INLINE_NET_LABEL_HEIGHT = 0.18
29
31
 
@@ -95,6 +97,7 @@ interface InlineNetLabelSolverInput {
95
97
  inputProblem: InputProblem
96
98
  traces: SolvedTracePath[]
97
99
  netLabelPlacements: NetLabelPlacement[]
100
+ completedReroutes?: CompletedTraceReroute[]
98
101
  }
99
102
 
100
103
  export interface InlineNetLabelOutput {
@@ -169,6 +172,7 @@ export class InlineNetLabelSolver extends BaseSolver {
169
172
  inputProblem: InputProblem
170
173
  traces: SolvedTracePath[]
171
174
  inputNetLabelPlacements: NetLabelPlacement[]
175
+ completedReroutes: CompletedTraceReroute[]
172
176
 
173
177
  inlineNetLabelPlacements: InlineNetLabelPlacement[] = []
174
178
 
@@ -190,6 +194,7 @@ export class InlineNetLabelSolver extends BaseSolver {
190
194
  this.inputProblem = input.inputProblem
191
195
  this.traces = input.traces
192
196
  this.inputNetLabelPlacements = input.netLabelPlacements
197
+ this.completedReroutes = input.completedReroutes ?? []
193
198
  this.globalConnMap = getConnectivityMapsFromInputProblem(
194
199
  this.inputProblem,
195
200
  ).netConnMap
@@ -235,6 +240,7 @@ export class InlineNetLabelSolver extends BaseSolver {
235
240
  inputProblem: this.inputProblem,
236
241
  traces: this.traces,
237
242
  netLabelPlacements: this.inputNetLabelPlacements,
243
+ completedReroutes: this.completedReroutes,
238
244
  },
239
245
  ]
240
246
  }
@@ -1066,10 +1072,19 @@ export class InlineNetLabelSolver extends BaseSolver {
1066
1072
  if (blockedGlobalConnNetIds.size === 0) {
1067
1073
  this.inlineNetLabelPlacements = activeInlinePlacements
1068
1074
  this.stats.pushedAnchoredNetLabelCount = pushed.movedLabelCount
1069
- return {
1075
+ const restored = restoreReroutesAroundSupersededLabels({
1076
+ inputProblem: this.inputProblem,
1070
1077
  traces: pushed.traces,
1071
1078
  netLabelPlacements: pushed.netLabelPlacements,
1072
1079
  inlineNetLabelPlacements: activeInlinePlacements,
1080
+ completedReroutes: this.completedReroutes,
1081
+ })
1082
+ this.stats.restoredSupersededLabelRerouteCount =
1083
+ restored.restoredTraceCount
1084
+ return {
1085
+ traces: restored.traces,
1086
+ netLabelPlacements: pushed.netLabelPlacements,
1087
+ inlineNetLabelPlacements: activeInlinePlacements,
1073
1088
  }
1074
1089
  }
1075
1090
  activeInlinePlacements = activeInlinePlacements.filter(