@tscircuit/schematic-trace-solver 0.0.37 → 0.0.39

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 (35) hide show
  1. package/dist/index.d.ts +25 -1
  2. package/dist/index.js +917 -10
  3. package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/collisions.ts +17 -0
  4. package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/pathOps.ts +31 -12
  5. package/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts +40 -0
  6. package/lib/solvers/TraceLabelOverlapAvoidanceSolver/TraceLabelOverlapAvoidanceSolver.ts +240 -0
  7. package/lib/solvers/TraceLabelOverlapAvoidanceSolver/balanceLShapes.ts +207 -0
  8. package/lib/solvers/TraceLabelOverlapAvoidanceSolver/countTurns.ts +18 -0
  9. package/lib/solvers/TraceLabelOverlapAvoidanceSolver/detectTraceLabelOverlap.ts +38 -0
  10. package/lib/solvers/TraceLabelOverlapAvoidanceSolver/hasCollisions.ts +22 -0
  11. package/lib/solvers/TraceLabelOverlapAvoidanceSolver/hasCollisionsWithLabels.ts +19 -0
  12. package/lib/solvers/TraceLabelOverlapAvoidanceSolver/index.ts +1 -0
  13. package/lib/solvers/TraceLabelOverlapAvoidanceSolver/minimizeTurnsWithFilteredLabels.ts +66 -0
  14. package/lib/solvers/TraceLabelOverlapAvoidanceSolver/rerouteCollidingTrace.ts +73 -0
  15. package/lib/solvers/TraceLabelOverlapAvoidanceSolver/simplifyPath.ts +41 -0
  16. package/lib/solvers/TraceLabelOverlapAvoidanceSolver/tryConnectPoints.ts +14 -0
  17. package/lib/solvers/TraceLabelOverlapAvoidanceSolver/trySnipAndReconnect.ts +229 -0
  18. package/lib/solvers/TraceLabelOverlapAvoidanceSolver/turnMinimization.ts +211 -0
  19. package/lib/solvers/TraceLabelOverlapAvoidanceSolver/violation.ts +25 -0
  20. package/package.json +1 -1
  21. package/site/examples/example20.page.tsx +103 -0
  22. package/site/examples/example22.page.tsx +108 -0
  23. package/site/examples/example23.page.tsx +137 -0
  24. package/site/examples/example24.page.tsx +128 -0
  25. package/tests/examples/__snapshots__/example16.snap.svg +27 -27
  26. package/tests/examples/__snapshots__/example22.snap.svg +137 -0
  27. package/tests/examples/__snapshots__/example23.snap.svg +137 -0
  28. package/tests/examples/__snapshots__/example24.snap.svg +143 -0
  29. package/tests/examples/__snapshots__/example25.snap.svg +165 -0
  30. package/tests/examples/__snapshots__/example26.snap.svg +157 -0
  31. package/tests/examples/example22.test.tsx +109 -0
  32. package/tests/examples/example23.test.tsx +109 -0
  33. package/tests/examples/example24.test.tsx +114 -0
  34. package/tests/examples/example25.test.tsx +143 -0
  35. package/tests/examples/example26.test.tsx +134 -0
package/dist/index.js CHANGED
@@ -588,6 +588,16 @@ var findFirstCollision = (pts, rects, opts = {}) => {
588
588
  }
589
589
  return null;
590
590
  };
591
+ var isPathCollidingWithObstacles = (path, obstacles) => {
592
+ for (let i = 0; i < path.length - 1; i++) {
593
+ for (const obstacle of obstacles) {
594
+ if (segmentIntersectsRect(path[i], path[i + 1], obstacle)) {
595
+ return true;
596
+ }
597
+ }
598
+ }
599
+ return false;
600
+ };
591
601
 
592
602
  // lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/mid.ts
593
603
  var EPS2 = 1e-9;
@@ -658,23 +668,33 @@ var candidateMidsFromSet = (axis, colliding, rectsById, collisionRectIds, aabb,
658
668
  // lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/pathOps.ts
659
669
  var EPS3 = 1e-9;
660
670
  var shiftSegmentOrth = (pts, segIndex, axis, newCoord, eps = EPS3) => {
661
- if (segIndex < 0 || segIndex >= pts.length - 1) return null;
671
+ if (segIndex < 0 || segIndex >= pts.length - 1) {
672
+ return null;
673
+ }
662
674
  const a = pts[segIndex];
663
675
  const b = pts[segIndex + 1];
664
676
  const vert = isVertical(a, b, eps);
665
677
  const horz = isHorizontal(a, b, eps);
666
- if (!vert && !horz) return null;
667
- if (vert && axis !== "x") return null;
668
- if (horz && axis !== "y") return null;
678
+ if (!vert && !horz) {
679
+ return null;
680
+ }
681
+ if (vert && axis !== "x") {
682
+ return null;
683
+ }
684
+ if (horz && axis !== "y") {
685
+ return null;
686
+ }
669
687
  const out = pts.map((p) => ({ ...p }));
670
688
  if (axis === "x") {
671
- if (Math.abs(a.x - newCoord) < eps && Math.abs(b.x - newCoord) < eps)
689
+ if (Math.abs(a.x - newCoord) < eps && Math.abs(b.x - newCoord) < eps) {
672
690
  return null;
691
+ }
673
692
  out[segIndex] = { ...out[segIndex], x: newCoord };
674
693
  out[segIndex + 1] = { ...out[segIndex + 1], x: newCoord };
675
694
  } else {
676
- if (Math.abs(a.y - newCoord) < eps && Math.abs(b.y - newCoord) < eps)
695
+ if (Math.abs(a.y - newCoord) < eps && Math.abs(b.y - newCoord) < eps) {
677
696
  return null;
697
+ }
678
698
  out[segIndex] = { ...out[segIndex], y: newCoord };
679
699
  out[segIndex + 1] = { ...out[segIndex + 1], y: newCoord };
680
700
  }
@@ -682,22 +702,31 @@ var shiftSegmentOrth = (pts, segIndex, axis, newCoord, eps = EPS3) => {
682
702
  const p = out[segIndex - 1];
683
703
  const q = out[segIndex];
684
704
  const manhattan = Math.abs(p.x - q.x) + Math.abs(p.y - q.y);
685
- if (manhattan < eps) return null;
705
+ if (manhattan < eps) {
706
+ return null;
707
+ }
686
708
  }
687
709
  if (segIndex + 2 <= out.length - 1) {
688
710
  const p = out[segIndex + 1];
689
711
  const q = out[segIndex + 2];
690
712
  const manhattan = Math.abs(p.x - q.x) + Math.abs(p.y - q.y);
691
- if (manhattan < eps) return null;
713
+ if (manhattan < eps) {
714
+ return null;
715
+ }
692
716
  }
693
717
  for (let i = 0; i < out.length - 1; i++) {
694
718
  const u = out[i];
695
719
  const v = out[i + 1];
696
- if (!isHorizontal(u, v, eps) && !isVertical(u, v, eps)) return null;
720
+ if (!isHorizontal(u, v, eps) && !isVertical(u, v, eps)) {
721
+ return null;
722
+ }
697
723
  }
698
724
  return out;
699
725
  };
700
- var pathKey = (pts, decimals = 6) => pts.map((p) => `${p.x.toFixed(decimals)},${p.y.toFixed(decimals)}`).join("|");
726
+ var pathKey = (pts, decimals = 6) => {
727
+ const key = pts.map((p) => `${p.x.toFixed(decimals)},${p.y.toFixed(decimals)}`).join("|");
728
+ return key;
729
+ };
701
730
 
702
731
  // lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2.ts
703
732
  var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
@@ -2170,6 +2199,854 @@ var NetLabelPlacementSolver = class extends BaseSolver {
2170
2199
  // lib/solvers/GuidelinesSolver/GuidelinesSolver.ts
2171
2200
  import { getBounds as getBounds3 } from "graphics-debug";
2172
2201
 
2202
+ // lib/solvers/TraceLabelOverlapAvoidanceSolver/detectTraceLabelOverlap.ts
2203
+ var detectTraceLabelOverlap = (traces, netLabels) => {
2204
+ const overlaps = [];
2205
+ for (const trace of traces) {
2206
+ for (const label of netLabels) {
2207
+ const labelBounds = getRectBounds(label.center, label.width, label.height);
2208
+ for (let i = 0; i < trace.tracePath.length - 1; i++) {
2209
+ const p1 = trace.tracePath[i];
2210
+ const p2 = trace.tracePath[i + 1];
2211
+ if (segmentIntersectsRect2(p1, p2, labelBounds)) {
2212
+ if (trace.globalConnNetId === label.globalConnNetId) {
2213
+ break;
2214
+ }
2215
+ overlaps.push({ trace, label });
2216
+ break;
2217
+ }
2218
+ }
2219
+ }
2220
+ }
2221
+ return overlaps;
2222
+ };
2223
+
2224
+ // lib/solvers/TraceLabelOverlapAvoidanceSolver/violation.ts
2225
+ var findTraceViolationZone = (path, labelBounds) => {
2226
+ const isPointInside = (p) => p.x > labelBounds.minX && p.x < labelBounds.maxX && p.y > labelBounds.minY && p.y < labelBounds.maxY;
2227
+ let firstInsideIndex = -1;
2228
+ let lastInsideIndex = -1;
2229
+ for (let i = 0; i < path.length; i++) {
2230
+ if (isPointInside(path[i])) {
2231
+ if (firstInsideIndex === -1) {
2232
+ firstInsideIndex = i;
2233
+ }
2234
+ lastInsideIndex = i;
2235
+ }
2236
+ }
2237
+ return { firstInsideIndex, lastInsideIndex };
2238
+ };
2239
+
2240
+ // lib/solvers/TraceLabelOverlapAvoidanceSolver/simplifyPath.ts
2241
+ var simplifyPath = (path) => {
2242
+ if (path.length < 3) return path;
2243
+ const newPath = [path[0]];
2244
+ for (let i = 1; i < path.length - 1; i++) {
2245
+ const p1 = newPath[newPath.length - 1];
2246
+ const p2 = path[i];
2247
+ const p3 = path[i + 1];
2248
+ if (isVertical(p1, p2) && isVertical(p2, p3) || isHorizontal(p1, p2) && isHorizontal(p2, p3)) {
2249
+ continue;
2250
+ }
2251
+ newPath.push(p2);
2252
+ }
2253
+ newPath.push(path[path.length - 1]);
2254
+ if (newPath.length < 3) return newPath;
2255
+ const finalPath = [newPath[0]];
2256
+ for (let i = 1; i < newPath.length - 1; i++) {
2257
+ const p1 = finalPath[finalPath.length - 1];
2258
+ const p2 = newPath[i];
2259
+ const p3 = newPath[i + 1];
2260
+ if (isVertical(p1, p2) && isVertical(p2, p3) || isHorizontal(p1, p2) && isHorizontal(p2, p3)) {
2261
+ continue;
2262
+ }
2263
+ finalPath.push(p2);
2264
+ }
2265
+ finalPath.push(newPath[newPath.length - 1]);
2266
+ return finalPath;
2267
+ };
2268
+
2269
+ // lib/solvers/TraceLabelOverlapAvoidanceSolver/trySnipAndReconnect.ts
2270
+ var trySnipAndReconnect = ({
2271
+ initialTrace,
2272
+ firstInsideIndex,
2273
+ lastInsideIndex,
2274
+ labelBounds,
2275
+ obstacles
2276
+ }) => {
2277
+ if (firstInsideIndex <= 0 || lastInsideIndex >= initialTrace.tracePath.length - 1) {
2278
+ return null;
2279
+ }
2280
+ const entryPoint = initialTrace.tracePath[firstInsideIndex - 1];
2281
+ const exitPoint = initialTrace.tracePath[lastInsideIndex + 1];
2282
+ const pathToEntry = initialTrace.tracePath.slice(0, firstInsideIndex);
2283
+ const pathFromExit = initialTrace.tracePath.slice(lastInsideIndex + 1);
2284
+ const candidateDetours = [];
2285
+ if (entryPoint.x !== exitPoint.x && entryPoint.y !== exitPoint.y) {
2286
+ candidateDetours.push([{ x: exitPoint.x, y: entryPoint.y }]);
2287
+ candidateDetours.push([{ x: entryPoint.x, y: exitPoint.y }]);
2288
+ } else if (entryPoint.x === exitPoint.x || entryPoint.y === exitPoint.y) {
2289
+ const newPath = [...pathToEntry, ...pathFromExit];
2290
+ const simplified = simplifyPath(newPath);
2291
+ if (!isPathCollidingWithObstacles(simplified, obstacles)) {
2292
+ return { ...initialTrace, tracePath: simplified };
2293
+ }
2294
+ }
2295
+ for (let i = 0; i < candidateDetours.length; i++) {
2296
+ const detour = candidateDetours[i];
2297
+ const newFullPath = [...pathToEntry, ...detour, ...pathFromExit];
2298
+ const simplified = simplifyPath(newFullPath);
2299
+ if (!isPathCollidingWithObstacles(simplified, obstacles)) {
2300
+ return { ...initialTrace, tracePath: simplified };
2301
+ }
2302
+ }
2303
+ candidateDetours.length = 0;
2304
+ const buffer = 0.1;
2305
+ const leftX = labelBounds.minX - buffer;
2306
+ const rightX = labelBounds.maxX + buffer;
2307
+ const topY = labelBounds.maxY + buffer;
2308
+ const bottomY = labelBounds.minY - buffer;
2309
+ if ((entryPoint.x <= labelBounds.minX || exitPoint.x <= labelBounds.minX) && entryPoint.x < labelBounds.maxX && exitPoint.x < labelBounds.maxX) {
2310
+ candidateDetours.push([
2311
+ { x: leftX, y: entryPoint.y },
2312
+ { x: leftX, y: exitPoint.y }
2313
+ ]);
2314
+ }
2315
+ if ((entryPoint.x >= labelBounds.maxX || exitPoint.x >= labelBounds.maxX) && entryPoint.x > labelBounds.minX && exitPoint.x > labelBounds.minX) {
2316
+ candidateDetours.push([
2317
+ { x: rightX, y: entryPoint.y },
2318
+ { x: rightX, y: exitPoint.y }
2319
+ ]);
2320
+ }
2321
+ if ((entryPoint.y >= labelBounds.maxY || exitPoint.y >= labelBounds.maxY) && entryPoint.y > labelBounds.minY && exitPoint.y > labelBounds.minY) {
2322
+ candidateDetours.push([
2323
+ { x: entryPoint.x, y: topY },
2324
+ { x: exitPoint.x, y: topY }
2325
+ ]);
2326
+ }
2327
+ if ((entryPoint.y <= labelBounds.minY || exitPoint.y <= labelBounds.minY) && entryPoint.y < labelBounds.maxY && exitPoint.y < labelBounds.maxY) {
2328
+ candidateDetours.push([
2329
+ { x: entryPoint.x, y: bottomY },
2330
+ { x: exitPoint.x, y: bottomY }
2331
+ ]);
2332
+ }
2333
+ for (let i = 0; i < candidateDetours.length; i++) {
2334
+ const detour = candidateDetours[i];
2335
+ const newFullPath = [...pathToEntry, ...detour, ...pathFromExit];
2336
+ const simplified = simplifyPath(newFullPath);
2337
+ if (!isPathCollidingWithObstacles(simplified, obstacles)) {
2338
+ return { ...initialTrace, tracePath: simplified };
2339
+ }
2340
+ }
2341
+ return null;
2342
+ };
2343
+ var tryFourPointDetour = ({
2344
+ initialTrace,
2345
+ label,
2346
+ labelBounds,
2347
+ obstacles,
2348
+ paddingBuffer,
2349
+ detourCount
2350
+ }) => {
2351
+ let collidingSegIndex = -1;
2352
+ for (let i = 0; i < initialTrace.tracePath.length - 1; i++) {
2353
+ if (segmentIntersectsRect(
2354
+ initialTrace.tracePath[i],
2355
+ initialTrace.tracePath[i + 1],
2356
+ labelBounds
2357
+ )) {
2358
+ collidingSegIndex = i;
2359
+ break;
2360
+ }
2361
+ }
2362
+ if (collidingSegIndex === -1) return initialTrace;
2363
+ const pA = initialTrace.tracePath[collidingSegIndex];
2364
+ const pB = initialTrace.tracePath[collidingSegIndex + 1];
2365
+ if (!pA || !pB) return null;
2366
+ const candidateDetours = [];
2367
+ const paddedLabelBounds = getRectBounds(
2368
+ label.center,
2369
+ label.width,
2370
+ label.height
2371
+ );
2372
+ const effectivePadding = paddingBuffer + detourCount * paddingBuffer;
2373
+ if (isVertical(pA, pB)) {
2374
+ const xCandidates = [
2375
+ paddedLabelBounds.maxX + effectivePadding,
2376
+ paddedLabelBounds.minX - effectivePadding
2377
+ ];
2378
+ for (const newX of xCandidates) {
2379
+ candidateDetours.push(
2380
+ pB.y > pA.y ? [
2381
+ { x: pA.x, y: paddedLabelBounds.minY - effectivePadding },
2382
+ { x: newX, y: paddedLabelBounds.minY - effectivePadding },
2383
+ { x: newX, y: paddedLabelBounds.maxY + effectivePadding },
2384
+ { x: pB.x, y: paddedLabelBounds.maxY + effectivePadding }
2385
+ ] : [
2386
+ { x: pA.x, y: paddedLabelBounds.maxY + effectivePadding },
2387
+ { x: newX, y: paddedLabelBounds.maxY + effectivePadding },
2388
+ { x: newX, y: paddedLabelBounds.minY - effectivePadding },
2389
+ { x: pB.x, y: paddedLabelBounds.minY - effectivePadding }
2390
+ ]
2391
+ );
2392
+ }
2393
+ } else {
2394
+ const yCandidates = [
2395
+ paddedLabelBounds.maxY + effectivePadding,
2396
+ paddedLabelBounds.minY - effectivePadding
2397
+ ];
2398
+ for (const newY of yCandidates) {
2399
+ candidateDetours.push(
2400
+ pB.x > pA.x ? [
2401
+ { x: paddedLabelBounds.minX - effectivePadding, y: pA.y },
2402
+ { x: paddedLabelBounds.minX - effectivePadding, y: newY },
2403
+ { x: paddedLabelBounds.maxX + effectivePadding, y: newY },
2404
+ { x: paddedLabelBounds.maxX + effectivePadding, y: pB.y }
2405
+ ] : [
2406
+ { x: paddedLabelBounds.maxX + effectivePadding, y: pA.y },
2407
+ { x: paddedLabelBounds.maxX + effectivePadding, y: newY },
2408
+ { x: paddedLabelBounds.minX - effectivePadding, y: newY },
2409
+ { x: paddedLabelBounds.minX - effectivePadding, y: pB.y }
2410
+ ]
2411
+ );
2412
+ }
2413
+ }
2414
+ for (const detourPoints of candidateDetours) {
2415
+ const finalPath = [
2416
+ ...initialTrace.tracePath.slice(0, collidingSegIndex + 1),
2417
+ ...detourPoints,
2418
+ ...initialTrace.tracePath.slice(collidingSegIndex + 1)
2419
+ ];
2420
+ const simplifiedFinalPath = simplifyPath(finalPath);
2421
+ if (!isPathCollidingWithObstacles(simplifiedFinalPath, obstacles)) {
2422
+ return { ...initialTrace, tracePath: simplifiedFinalPath };
2423
+ }
2424
+ }
2425
+ return null;
2426
+ };
2427
+
2428
+ // lib/solvers/TraceLabelOverlapAvoidanceSolver/rerouteCollidingTrace.ts
2429
+ var rerouteCollidingTrace = ({
2430
+ trace,
2431
+ label,
2432
+ problem,
2433
+ paddingBuffer,
2434
+ detourCount
2435
+ }) => {
2436
+ const initialTrace = { ...trace, tracePath: simplifyPath(trace.tracePath) };
2437
+ if (trace.globalConnNetId === label.globalConnNetId) {
2438
+ return initialTrace;
2439
+ }
2440
+ const obstacles = getObstacleRects(problem);
2441
+ const labelPadding = paddingBuffer;
2442
+ const labelBoundsRaw = getRectBounds(label.center, label.width, label.height);
2443
+ const labelBounds = {
2444
+ minX: labelBoundsRaw.minX - labelPadding,
2445
+ minY: labelBoundsRaw.minY - labelPadding,
2446
+ maxX: labelBoundsRaw.maxX + labelPadding,
2447
+ maxY: labelBoundsRaw.maxY + labelPadding,
2448
+ chipId: `netlabel-${label.netId}`
2449
+ };
2450
+ const fourPointResult = tryFourPointDetour({
2451
+ initialTrace,
2452
+ label,
2453
+ labelBounds,
2454
+ obstacles,
2455
+ paddingBuffer,
2456
+ detourCount
2457
+ });
2458
+ if (fourPointResult) {
2459
+ initialTrace.tracePath = fourPointResult.tracePath;
2460
+ }
2461
+ const { firstInsideIndex, lastInsideIndex } = findTraceViolationZone(
2462
+ initialTrace.tracePath,
2463
+ labelBounds
2464
+ );
2465
+ const snipReconnectResult = trySnipAndReconnect({
2466
+ initialTrace,
2467
+ firstInsideIndex,
2468
+ lastInsideIndex,
2469
+ labelBounds,
2470
+ obstacles
2471
+ });
2472
+ if (snipReconnectResult) {
2473
+ return snipReconnectResult;
2474
+ }
2475
+ if (fourPointResult) {
2476
+ return fourPointResult;
2477
+ }
2478
+ return initialTrace;
2479
+ };
2480
+
2481
+ // lib/solvers/TraceLabelOverlapAvoidanceSolver/hasCollisions.ts
2482
+ var hasCollisions = (pathSegments, obstacles) => {
2483
+ for (let i = 0; i < pathSegments.length - 1; i++) {
2484
+ const p1 = pathSegments[i];
2485
+ const p2 = pathSegments[i + 1];
2486
+ for (const obstacle of obstacles) {
2487
+ if (segmentIntersectsRect(p1, p2, obstacle)) {
2488
+ return true;
2489
+ }
2490
+ }
2491
+ }
2492
+ return false;
2493
+ };
2494
+
2495
+ // lib/solvers/TraceLabelOverlapAvoidanceSolver/countTurns.ts
2496
+ var countTurns = (points) => {
2497
+ let turns = 0;
2498
+ for (let i = 1; i < points.length - 1; i++) {
2499
+ const prev = points[i - 1];
2500
+ const curr = points[i];
2501
+ const next = points[i + 1];
2502
+ const prevVertical = prev.x === curr.x;
2503
+ const nextVertical = curr.x === next.x;
2504
+ if (prevVertical !== nextVertical) {
2505
+ turns++;
2506
+ }
2507
+ }
2508
+ return turns;
2509
+ };
2510
+
2511
+ // lib/solvers/TraceLabelOverlapAvoidanceSolver/tryConnectPoints.ts
2512
+ var tryConnectPoints = (start, end) => {
2513
+ const candidates = [];
2514
+ if (start.x === end.x || start.y === end.y) {
2515
+ candidates.push([start, end]);
2516
+ } else {
2517
+ candidates.push([start, { x: end.x, y: start.y }, end]);
2518
+ candidates.push([start, { x: start.x, y: end.y }, end]);
2519
+ }
2520
+ return candidates;
2521
+ };
2522
+
2523
+ // lib/solvers/TraceLabelOverlapAvoidanceSolver/hasCollisionsWithLabels.ts
2524
+ var hasCollisionsWithLabels = (pathSegments, labels) => {
2525
+ for (let i = 0; i < pathSegments.length - 1; i++) {
2526
+ const p1 = pathSegments[i];
2527
+ const p2 = pathSegments[i + 1];
2528
+ for (const label of labels) {
2529
+ if (segmentIntersectsRect(p1, p2, label)) {
2530
+ return true;
2531
+ }
2532
+ }
2533
+ }
2534
+ return false;
2535
+ };
2536
+
2537
+ // lib/solvers/TraceLabelOverlapAvoidanceSolver/turnMinimization.ts
2538
+ var minimizeTurns = ({
2539
+ path,
2540
+ obstacles,
2541
+ labelBounds
2542
+ }) => {
2543
+ if (path.length <= 2) {
2544
+ return path;
2545
+ }
2546
+ const recognizeStairStepPattern = (pathToCheck, startIdx) => {
2547
+ if (startIdx >= pathToCheck.length - 3) return -1;
2548
+ let endIdx = startIdx;
2549
+ let isStairStep = true;
2550
+ for (let i = startIdx; i < pathToCheck.length - 2 && i < startIdx + 10; i++) {
2551
+ if (i + 2 >= pathToCheck.length) break;
2552
+ const p1 = pathToCheck[i];
2553
+ const p2 = pathToCheck[i + 1];
2554
+ const p3 = pathToCheck[i + 2];
2555
+ const seg1Vertical = p1.x === p2.x;
2556
+ const seg2Vertical = p2.x === p3.x;
2557
+ if (seg1Vertical === seg2Vertical) {
2558
+ break;
2559
+ }
2560
+ const seg1Direction = seg1Vertical ? Math.sign(p2.y - p1.y) : Math.sign(p2.x - p1.x);
2561
+ if (i > startIdx) {
2562
+ const prevP = pathToCheck[i - 1];
2563
+ const prevSegVertical = prevP.x === p1.x;
2564
+ const prevDirection = prevSegVertical ? Math.sign(p1.y - prevP.y) : Math.sign(p1.x - prevP.x);
2565
+ if (seg1Vertical && prevSegVertical && seg1Direction !== prevDirection || !seg1Vertical && !prevSegVertical && seg1Direction !== prevDirection) {
2566
+ isStairStep = false;
2567
+ break;
2568
+ }
2569
+ }
2570
+ endIdx = i + 2;
2571
+ }
2572
+ return isStairStep && endIdx - startIdx >= 3 ? endIdx : -1;
2573
+ };
2574
+ let optimizedPath = [...path];
2575
+ let currentTurns = countTurns(optimizedPath);
2576
+ let improved = true;
2577
+ while (improved) {
2578
+ improved = false;
2579
+ for (let startIdx = 0; startIdx < optimizedPath.length - 3; startIdx++) {
2580
+ const stairEndIdx = recognizeStairStepPattern(optimizedPath, startIdx);
2581
+ if (stairEndIdx > 0) {
2582
+ const startPoint = optimizedPath[startIdx];
2583
+ const endPoint = optimizedPath[stairEndIdx];
2584
+ const connectionOptions = tryConnectPoints(startPoint, endPoint);
2585
+ for (const connection of connectionOptions) {
2586
+ const testPath = [
2587
+ ...optimizedPath.slice(0, startIdx + 1),
2588
+ ...connection.slice(1, -1),
2589
+ ...optimizedPath.slice(stairEndIdx)
2590
+ ];
2591
+ const collidesWithObstacles = hasCollisions(connection, obstacles);
2592
+ const collidesWithLabels = hasCollisionsWithLabels(
2593
+ connection,
2594
+ labelBounds
2595
+ );
2596
+ if (!collidesWithObstacles && !collidesWithLabels) {
2597
+ const newTurns = countTurns(testPath);
2598
+ const turnsRemoved = stairEndIdx - startIdx - 1;
2599
+ optimizedPath = testPath;
2600
+ currentTurns = newTurns;
2601
+ improved = true;
2602
+ break;
2603
+ }
2604
+ }
2605
+ if (improved) break;
2606
+ }
2607
+ }
2608
+ if (!improved) {
2609
+ for (let startIdx = 0; startIdx < optimizedPath.length - 2; startIdx++) {
2610
+ const maxRemove = Math.min(
2611
+ optimizedPath.length - startIdx - 2,
2612
+ optimizedPath.length - 2
2613
+ );
2614
+ for (let removeCount = 1; removeCount <= maxRemove; removeCount++) {
2615
+ const endIdx = startIdx + removeCount + 1;
2616
+ if (endIdx >= optimizedPath.length) continue;
2617
+ const startPoint = optimizedPath[startIdx];
2618
+ const endPoint = optimizedPath[endIdx];
2619
+ const connectionOptions = tryConnectPoints(startPoint, endPoint);
2620
+ for (const connection of connectionOptions) {
2621
+ const testPath = [
2622
+ ...optimizedPath.slice(0, startIdx + 1),
2623
+ ...connection.slice(1, -1),
2624
+ ...optimizedPath.slice(endIdx)
2625
+ ];
2626
+ const connectionSegments = connection;
2627
+ const collidesWithObstacles = hasCollisions(
2628
+ connectionSegments,
2629
+ obstacles
2630
+ );
2631
+ const collidesWithLabels = hasCollisionsWithLabels(
2632
+ connectionSegments,
2633
+ labelBounds
2634
+ );
2635
+ if (!collidesWithObstacles && !collidesWithLabels) {
2636
+ const newTurns = countTurns(testPath);
2637
+ if (newTurns < currentTurns || newTurns === currentTurns && testPath.length < optimizedPath.length) {
2638
+ optimizedPath = testPath;
2639
+ currentTurns = newTurns;
2640
+ improved = true;
2641
+ break;
2642
+ }
2643
+ }
2644
+ }
2645
+ if (improved) break;
2646
+ }
2647
+ if (improved) break;
2648
+ }
2649
+ }
2650
+ if (!improved) {
2651
+ for (let i = 0; i < optimizedPath.length - 2; i++) {
2652
+ const p1 = optimizedPath[i];
2653
+ const p2 = optimizedPath[i + 1];
2654
+ const p3 = optimizedPath[i + 2];
2655
+ const allVertical = p1.x === p2.x && p2.x === p3.x;
2656
+ const allHorizontal = p1.y === p2.y && p2.y === p3.y;
2657
+ if (allVertical || allHorizontal) {
2658
+ const testPath = [
2659
+ ...optimizedPath.slice(0, i + 1),
2660
+ ...optimizedPath.slice(i + 2)
2661
+ ];
2662
+ const collidesWithObstacles = hasCollisions([p1, p3], obstacles);
2663
+ const collidesWithLabels = hasCollisionsWithLabels(
2664
+ [p1, p3],
2665
+ labelBounds
2666
+ );
2667
+ if (!collidesWithObstacles && !collidesWithLabels) {
2668
+ optimizedPath = testPath;
2669
+ improved = true;
2670
+ break;
2671
+ }
2672
+ }
2673
+ }
2674
+ }
2675
+ }
2676
+ const finalSimplifiedPath = simplifyPath(optimizedPath);
2677
+ return finalSimplifiedPath;
2678
+ };
2679
+
2680
+ // lib/solvers/TraceLabelOverlapAvoidanceSolver/minimizeTurnsWithFilteredLabels.ts
2681
+ var minimizeTurnsWithFilteredLabels = ({
2682
+ traces,
2683
+ problem,
2684
+ allLabelPlacements,
2685
+ mergedLabelNetIdMap,
2686
+ paddingBuffer
2687
+ }) => {
2688
+ let changesMade = false;
2689
+ const obstacles = getObstacleRects(problem);
2690
+ const newTraces = traces.map((trace) => {
2691
+ const originalPath = trace.tracePath;
2692
+ const filteredLabels = allLabelPlacements.filter((label) => {
2693
+ const originalNetIds = mergedLabelNetIdMap[label.globalConnNetId];
2694
+ if (originalNetIds) {
2695
+ return !originalNetIds.has(trace.globalConnNetId);
2696
+ }
2697
+ return label.globalConnNetId !== trace.globalConnNetId;
2698
+ });
2699
+ const labelBounds = filteredLabels.map((nl) => ({
2700
+ minX: nl.center.x - nl.width / 2 - paddingBuffer,
2701
+ maxX: nl.center.x + nl.width / 2 + paddingBuffer,
2702
+ minY: nl.center.y - nl.height / 2 - paddingBuffer,
2703
+ maxY: nl.center.y + nl.height / 2 + paddingBuffer
2704
+ }));
2705
+ const newPath = minimizeTurns({
2706
+ path: originalPath,
2707
+ obstacles,
2708
+ labelBounds
2709
+ });
2710
+ if (newPath.length !== originalPath.length || newPath.some(
2711
+ (p, i) => p.x !== originalPath[i].x || p.y !== originalPath[i].y
2712
+ )) {
2713
+ changesMade = true;
2714
+ }
2715
+ return {
2716
+ ...trace,
2717
+ tracePath: newPath
2718
+ };
2719
+ });
2720
+ if (changesMade) {
2721
+ return newTraces;
2722
+ } else {
2723
+ return null;
2724
+ }
2725
+ };
2726
+
2727
+ // lib/solvers/TraceLabelOverlapAvoidanceSolver/balanceLShapes.ts
2728
+ var balanceLShapes = ({
2729
+ traces,
2730
+ problem,
2731
+ allLabelPlacements
2732
+ }) => {
2733
+ const TOLERANCE = 1e-5;
2734
+ let changesMade = false;
2735
+ for (const trace of traces) {
2736
+ if (trace.tracePath.length === 4) {
2737
+ const [p0, p1, p2, p3] = trace.tracePath;
2738
+ const isHVHShape = p0.y === p1.y && p1.x === p2.x && p2.y === p3.y;
2739
+ const isVHVShape = p0.x === p1.x && p1.y === p2.y && p2.x === p3.x;
2740
+ const isCollinearHorizontal = p0.y === p1.y && p1.y === p2.y && p2.y === p3.y;
2741
+ const isCollinearVertical = p0.x === p1.x && p1.x === p2.x && p2.x === p3.x;
2742
+ const isCollinear = isCollinearHorizontal || isCollinearVertical;
2743
+ let isSameDirection = false;
2744
+ if (isHVHShape) {
2745
+ isSameDirection = Math.sign(p1.x - p0.x) === Math.sign(p3.x - p2.x);
2746
+ } else if (isVHVShape) {
2747
+ isSameDirection = Math.sign(p1.y - p0.y) === Math.sign(p3.y - p2.y);
2748
+ }
2749
+ const isValidZShape = (isHVHShape || isVHVShape) && !isCollinear && isSameDirection;
2750
+ if (!isValidZShape) {
2751
+ return null;
2752
+ }
2753
+ }
2754
+ }
2755
+ const obstacles = getObstacleRects(problem).map((obs) => ({
2756
+ ...obs,
2757
+ minX: obs.minX + TOLERANCE,
2758
+ maxX: obs.maxX - TOLERANCE,
2759
+ minY: obs.minY + TOLERANCE,
2760
+ maxY: obs.maxY - TOLERANCE
2761
+ }));
2762
+ const segmentIntersectsAnyRect = (p1, p2, rects) => {
2763
+ for (const rect of rects) {
2764
+ if (segmentIntersectsRect(p1, p2, rect)) {
2765
+ return true;
2766
+ }
2767
+ }
2768
+ return false;
2769
+ };
2770
+ const getLabelBounds = (labels, traceNetId) => {
2771
+ const filteredLabels = labels.filter(
2772
+ (label) => label.globalConnNetId !== traceNetId
2773
+ );
2774
+ return filteredLabels.map((nl) => ({
2775
+ minX: nl.center.x - nl.width / 2 + TOLERANCE,
2776
+ maxX: nl.center.x + nl.width / 2 - TOLERANCE,
2777
+ minY: nl.center.y - nl.height / 2 + TOLERANCE,
2778
+ maxY: nl.center.y + nl.height / 2 - TOLERANCE
2779
+ }));
2780
+ };
2781
+ const newTraces = traces.map((trace) => {
2782
+ let newPath = [...trace.tracePath];
2783
+ if (newPath.length < 4) {
2784
+ return { ...trace };
2785
+ }
2786
+ const labelBounds = getLabelBounds(
2787
+ allLabelPlacements,
2788
+ trace.globalConnNetId
2789
+ );
2790
+ if (newPath.length === 4) {
2791
+ const [p0, p1, p2, p3] = newPath;
2792
+ let p1New, p2New;
2793
+ const isHVHShape = p0.y === p1.y && p1.x === p2.x && p2.y === p3.y;
2794
+ if (isHVHShape) {
2795
+ const idealX = (p0.x + p3.x) / 2;
2796
+ p1New = { x: idealX, y: p1.y };
2797
+ p2New = { x: idealX, y: p2.y };
2798
+ } else {
2799
+ const idealY = (p0.y + p3.y) / 2;
2800
+ p1New = { x: p1.x, y: idealY };
2801
+ p2New = { x: p2.x, y: idealY };
2802
+ }
2803
+ const collides = segmentIntersectsAnyRect(p0, p1New, obstacles) || segmentIntersectsAnyRect(p1New, p2New, obstacles) || segmentIntersectsAnyRect(p2New, p3, obstacles) || segmentIntersectsAnyRect(p0, p1New, labelBounds) || segmentIntersectsAnyRect(p1New, p2New, labelBounds) || segmentIntersectsAnyRect(p2New, p3, labelBounds);
2804
+ if (!collides) {
2805
+ newPath[1] = p1New;
2806
+ newPath[2] = p2New;
2807
+ changesMade = true;
2808
+ }
2809
+ return { ...trace, tracePath: simplifyPath(newPath) };
2810
+ }
2811
+ for (let i = 1; i < newPath.length - 4; i++) {
2812
+ const p1 = newPath[i];
2813
+ const p2 = newPath[i + 1];
2814
+ const p3 = newPath[i + 2];
2815
+ const p4 = newPath[i + 3];
2816
+ const isHVHZShape = p1.y === p2.y && p2.x === p3.x && p3.y === p4.y;
2817
+ const isVHVZShape = p1.x === p2.x && p2.y === p3.y && p3.x === p4.x;
2818
+ const isCollinearHorizontal = p1.y === p2.y && p2.y === p3.y && p3.y === p4.y;
2819
+ const isCollinearVertical = p1.x === p2.x && p2.x === p3.x && p3.x === p4.x;
2820
+ const isCollinear = isCollinearHorizontal || isCollinearVertical;
2821
+ let isSameDirection = false;
2822
+ if (isHVHZShape) {
2823
+ isSameDirection = Math.sign(p2.x - p1.x) === Math.sign(p4.x - p3.x);
2824
+ } else if (isVHVZShape) {
2825
+ isSameDirection = Math.sign(p2.y - p1.y) === Math.sign(p4.y - p3.y);
2826
+ }
2827
+ const isValidZShape = (isHVHZShape || isVHVZShape) && !isCollinear && isSameDirection;
2828
+ if (!isValidZShape) {
2829
+ continue;
2830
+ }
2831
+ let p2New, p3New;
2832
+ const len1Original = isHVHZShape ? Math.abs(p1.x - p2.x) : Math.abs(p1.y - p2.y);
2833
+ const len2Original = isHVHZShape ? Math.abs(p3.x - p4.x) : Math.abs(p3.y - p4.y);
2834
+ if (Math.abs(len1Original - len2Original) < 1e-3) {
2835
+ continue;
2836
+ }
2837
+ if (isHVHZShape) {
2838
+ const idealX = (p1.x + p4.x) / 2;
2839
+ p2New = { x: idealX, y: p2.y };
2840
+ p3New = { x: idealX, y: p3.y };
2841
+ } else {
2842
+ const idealY = (p1.y + p4.y) / 2;
2843
+ p2New = { x: p2.x, y: idealY };
2844
+ p3New = { x: p3.x, y: idealY };
2845
+ }
2846
+ const collides = segmentIntersectsAnyRect(p1, p2New, obstacles) || segmentIntersectsAnyRect(p2New, p3New, obstacles) || segmentIntersectsAnyRect(p3New, p4, obstacles) || segmentIntersectsAnyRect(p1, p2New, labelBounds) || segmentIntersectsAnyRect(p2New, p3New, labelBounds) || segmentIntersectsAnyRect(p3New, p4, labelBounds);
2847
+ if (!collides) {
2848
+ newPath[i + 1] = p2New;
2849
+ newPath[i + 2] = p3New;
2850
+ changesMade = true;
2851
+ i = 0;
2852
+ }
2853
+ }
2854
+ const finalSimplifiedPath = simplifyPath(newPath);
2855
+ return {
2856
+ ...trace,
2857
+ tracePath: finalSimplifiedPath
2858
+ };
2859
+ });
2860
+ if (changesMade) {
2861
+ return newTraces;
2862
+ } else {
2863
+ return null;
2864
+ }
2865
+ };
2866
+
2867
+ // lib/solvers/TraceLabelOverlapAvoidanceSolver/TraceLabelOverlapAvoidanceSolver.ts
2868
+ var TraceLabelOverlapAvoidanceSolver = class extends BaseSolver {
2869
+ problem;
2870
+ traces;
2871
+ netTempLabelPlacements;
2872
+ netLabelPlacements;
2873
+ updatedTraces;
2874
+ mergedLabelNetIdMap;
2875
+ detourCountByLabel;
2876
+ PADDING_BUFFER = 0.1;
2877
+ constructor(solverInput) {
2878
+ super();
2879
+ this.problem = solverInput.inputProblem;
2880
+ this.traces = solverInput.traces;
2881
+ this.updatedTraces = [...solverInput.traces];
2882
+ this.mergedLabelNetIdMap = {};
2883
+ this.detourCountByLabel = {};
2884
+ const originalLabels = solverInput.netLabelPlacements;
2885
+ this.netLabelPlacements = originalLabels;
2886
+ if (!originalLabels || originalLabels.length === 0) {
2887
+ this.netTempLabelPlacements = [];
2888
+ return;
2889
+ }
2890
+ const labelGroups = {};
2891
+ for (const p of originalLabels) {
2892
+ if (p.pinIds.length === 0) continue;
2893
+ const chipId = p.pinIds[0].split(".")[0];
2894
+ if (!chipId) continue;
2895
+ const key = `${chipId}-${p.orientation}`;
2896
+ if (!(key in labelGroups)) {
2897
+ labelGroups[key] = [];
2898
+ }
2899
+ labelGroups[key].push(p);
2900
+ }
2901
+ const finalPlacements = [];
2902
+ for (const [key, group] of Object.entries(labelGroups)) {
2903
+ if (group.length <= 1) {
2904
+ finalPlacements.push(...group);
2905
+ continue;
2906
+ }
2907
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
2908
+ for (const p of group) {
2909
+ const bounds = getRectBounds(p.center, p.width, p.height);
2910
+ minX = Math.min(minX, bounds.minX);
2911
+ minY = Math.min(minY, bounds.minY);
2912
+ maxX = Math.max(maxX, bounds.maxX);
2913
+ maxY = Math.max(maxY, bounds.maxY);
2914
+ }
2915
+ const newWidth = maxX - minX;
2916
+ const newHeight = maxY - minY;
2917
+ const template = group[0];
2918
+ const syntheticId = `merged-group-${key}`;
2919
+ const originalNetIds = new Set(group.map((p) => p.globalConnNetId));
2920
+ this.mergedLabelNetIdMap[syntheticId] = originalNetIds;
2921
+ finalPlacements.push({
2922
+ ...template,
2923
+ globalConnNetId: syntheticId,
2924
+ width: newWidth,
2925
+ height: newHeight,
2926
+ center: { x: minX + newWidth / 2, y: minY + newHeight / 2 },
2927
+ pinIds: [...new Set(group.flatMap((p) => p.pinIds))],
2928
+ mspConnectionPairIds: [
2929
+ ...new Set(group.flatMap((p) => p.mspConnectionPairIds))
2930
+ ]
2931
+ });
2932
+ }
2933
+ this.netTempLabelPlacements = finalPlacements;
2934
+ }
2935
+ _step() {
2936
+ if (!this.traces || this.traces.length === 0 || !this.netTempLabelPlacements || this.netTempLabelPlacements.length === 0) {
2937
+ this.solved = true;
2938
+ return;
2939
+ }
2940
+ this.detourCountByLabel = {};
2941
+ const overlaps = detectTraceLabelOverlap(
2942
+ this.traces,
2943
+ this.netTempLabelPlacements
2944
+ );
2945
+ if (overlaps.length === 0) {
2946
+ this.solved = true;
2947
+ return;
2948
+ }
2949
+ const unfriendlyOverlaps = overlaps.filter((o) => {
2950
+ const originalNetIds = this.mergedLabelNetIdMap[o.label.globalConnNetId];
2951
+ if (originalNetIds) {
2952
+ return !originalNetIds.has(o.trace.globalConnNetId);
2953
+ }
2954
+ return o.trace.globalConnNetId !== o.label.globalConnNetId;
2955
+ });
2956
+ if (unfriendlyOverlaps.length === 0) {
2957
+ this.solved = true;
2958
+ return;
2959
+ }
2960
+ const updatedTracesMap = {};
2961
+ for (const trace of this.traces) {
2962
+ updatedTracesMap[trace.mspPairId] = trace;
2963
+ }
2964
+ const processedTraceIds = /* @__PURE__ */ new Set();
2965
+ for (const overlap of unfriendlyOverlaps) {
2966
+ if (processedTraceIds.has(overlap.trace.mspPairId)) {
2967
+ continue;
2968
+ }
2969
+ const currentTraceState = updatedTracesMap[overlap.trace.mspPairId];
2970
+ const labelId = overlap.label.globalConnNetId;
2971
+ const detourCount = this.detourCountByLabel[labelId] || 0;
2972
+ const newTrace = rerouteCollidingTrace({
2973
+ trace: currentTraceState,
2974
+ label: overlap.label,
2975
+ problem: this.problem,
2976
+ paddingBuffer: this.PADDING_BUFFER,
2977
+ detourCount
2978
+ });
2979
+ if (newTrace.tracePath !== currentTraceState.tracePath) {
2980
+ this.detourCountByLabel[labelId] = detourCount + 1;
2981
+ }
2982
+ updatedTracesMap[currentTraceState.mspPairId] = newTrace;
2983
+ processedTraceIds.add(currentTraceState.mspPairId);
2984
+ }
2985
+ this.updatedTraces = Object.values(updatedTracesMap);
2986
+ const minimizedTraces = minimizeTurnsWithFilteredLabels({
2987
+ traces: this.updatedTraces,
2988
+ problem: this.problem,
2989
+ allLabelPlacements: this.netTempLabelPlacements,
2990
+ // Use temp labels which include merged ones
2991
+ mergedLabelNetIdMap: this.mergedLabelNetIdMap,
2992
+ paddingBuffer: this.PADDING_BUFFER
2993
+ });
2994
+ if (minimizedTraces) {
2995
+ this.updatedTraces = minimizedTraces;
2996
+ }
2997
+ const balancedTraces = balanceLShapes({
2998
+ traces: this.updatedTraces,
2999
+ problem: this.problem,
3000
+ allLabelPlacements: this.netLabelPlacements
3001
+ });
3002
+ if (balancedTraces) {
3003
+ this.updatedTraces = balancedTraces;
3004
+ }
3005
+ const finalLabelPlacementSolver = new NetLabelPlacementSolver({
3006
+ inputProblem: this.problem,
3007
+ inputTraceMap: Object.fromEntries(
3008
+ this.updatedTraces.map((trace) => [trace.mspPairId, trace])
3009
+ )
3010
+ });
3011
+ finalLabelPlacementSolver.solve();
3012
+ this.netLabelPlacements = finalLabelPlacementSolver.netLabelPlacements;
3013
+ this.solved = true;
3014
+ }
3015
+ getOutput() {
3016
+ return {
3017
+ traces: this.updatedTraces,
3018
+ netLabelPlacements: this.netLabelPlacements
3019
+ };
3020
+ }
3021
+ visualize() {
3022
+ const graphics = visualizeInputProblem(this.problem);
3023
+ if (!graphics.lines) graphics.lines = [];
3024
+ if (!graphics.circles) graphics.circles = [];
3025
+ if (!graphics.texts) graphics.texts = [];
3026
+ if (!graphics.rects) graphics.rects = [];
3027
+ for (const trace of this.updatedTraces) {
3028
+ graphics.lines.push({
3029
+ points: trace.tracePath,
3030
+ strokeColor: "purple"
3031
+ });
3032
+ }
3033
+ for (const p of this.netLabelPlacements) {
3034
+ graphics.rects.push({
3035
+ center: p.center,
3036
+ width: p.width,
3037
+ height: p.height,
3038
+ fill: getColorFromString(p.globalConnNetId, 0.35)
3039
+ });
3040
+ graphics.points.push({
3041
+ x: p.anchorPoint.x,
3042
+ y: p.anchorPoint.y,
3043
+ color: getColorFromString(p.globalConnNetId, 0.9)
3044
+ });
3045
+ }
3046
+ return graphics;
3047
+ }
3048
+ };
3049
+
2173
3050
  // lib/solvers/SchematicTracePipelineSolver/correctPinsInsideChip.ts
2174
3051
  var correctPinsInsideChips = (problem) => {
2175
3052
  for (const chip of problem.chips) {
@@ -2236,6 +3113,7 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
2236
3113
  schematicTraceLinesSolver;
2237
3114
  traceOverlapShiftSolver;
2238
3115
  netLabelPlacementSolver;
3116
+ traceLabelOverlapAvoidanceSolver;
2239
3117
  startTimeOfPhase;
2240
3118
  endTimeOfPhase;
2241
3119
  timeSpentOnPhase;
@@ -2314,6 +3192,35 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
2314
3192
  onSolved: (_solver) => {
2315
3193
  }
2316
3194
  }
3195
+ ),
3196
+ definePipelineStep(
3197
+ "traceLabelOverlapAvoidanceSolver",
3198
+ TraceLabelOverlapAvoidanceSolver,
3199
+ (instance) => {
3200
+ const traceMap = instance.traceOverlapShiftSolver?.correctedTraceMap ?? Object.fromEntries(
3201
+ instance.schematicTraceLinesSolver.solvedTracePaths.map((p) => [
3202
+ p.mspPairId,
3203
+ p
3204
+ ])
3205
+ );
3206
+ const traces = Object.values(traceMap);
3207
+ const netLabelPlacements = instance.netLabelPlacementSolver.netLabelPlacements;
3208
+ return [
3209
+ {
3210
+ inputProblem: instance.inputProblem,
3211
+ traces,
3212
+ netLabelPlacements
3213
+ }
3214
+ ];
3215
+ },
3216
+ {
3217
+ onSolved: (instance) => {
3218
+ if (instance.traceLabelOverlapAvoidanceSolver && instance.netLabelPlacementSolver) {
3219
+ const { netLabelPlacements } = instance.traceLabelOverlapAvoidanceSolver.getOutput();
3220
+ instance.netLabelPlacementSolver.netLabelPlacements = netLabelPlacements;
3221
+ }
3222
+ }
3223
+ }
2317
3224
  )
2318
3225
  ];
2319
3226
  constructor(inputProblem) {