@tscircuit/schematic-trace-solver 0.0.46 → 0.0.47

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.
package/dist/index.d.ts CHANGED
@@ -435,6 +435,7 @@ declare class SingleOverlapSolver extends BaseSolver {
435
435
  problem: InputProblem;
436
436
  obstacles: ReturnType<typeof getObstacleRects>;
437
437
  label: NetLabelPlacement;
438
+ _tried: number;
438
439
  constructor(solverInput: SingleOverlapSolverInput);
439
440
  _step(): void;
440
441
  visualize(): GraphicsObject;
@@ -446,6 +447,7 @@ interface OverlapCollectionSolverInput {
446
447
  initialNetLabelPlacements: NetLabelPlacement[];
447
448
  mergedNetLabelPlacements: NetLabelPlacement[];
448
449
  mergedLabelNetIdMap: Record<string, Set<string>>;
450
+ detourCounts: Map<string, number>;
449
451
  }
450
452
  /**
451
453
  * This is an internal solver that manages the step-by-step process of avoiding
@@ -458,8 +460,8 @@ declare class OverlapAvoidanceStepSolver extends BaseSolver {
458
460
  mergedLabelNetIdMap: Record<string, Set<string>>;
459
461
  allTraces: SolvedTracePath[];
460
462
  modifiedTraces: SolvedTracePath[];
461
- private detourCountByLabel;
462
463
  private readonly PADDING_BUFFER;
464
+ private detourCounts;
463
465
  activeSubSolver: SingleOverlapSolver | null;
464
466
  private overlapQueue;
465
467
  private recentlyFailed;
@@ -470,6 +472,7 @@ declare class OverlapAvoidanceStepSolver extends BaseSolver {
470
472
  getOutput(): {
471
473
  allTraces: SolvedTracePath[];
472
474
  modifiedTraces: SolvedTracePath[];
475
+ detourCounts: Map<string, number>;
473
476
  };
474
477
  visualize(): GraphicsObject;
475
478
  }
@@ -480,29 +483,28 @@ interface TraceLabelOverlapAvoidanceSolverInput {
480
483
  netLabelPlacements: NetLabelPlacement[];
481
484
  }
482
485
  /**
483
- * A pipeline solver responsible for resolving overlaps between schematic traces and net labels.
486
+ * Resolves overlaps between schematic traces and net labels using a two-phase,
487
+ * "fire-and-forget" dispatching strategy.
484
488
  *
485
- * This solver orchestrates a sequence of sub-solvers to achieve its goal:
486
- * 1. **MergedNetLabelObstacleSolver**: This solver first merges labels that are
487
- * close to each other to form larger "obstacle" groups. This simplifies the
488
- * problem by reducing the number of individual obstacles the traces need to avoid.
489
- * 2. **OverlapAvoidanceStepSolver**: This solver then takes the output of the merging
490
- * step and iteratively attempts to reroute traces to avoid the merged label obstacles.
491
- * It handles one overlap at a time, making it a step-by-step process.
489
+ * This solver operates in two distinct phases:
492
490
  *
493
- * The final output is a set of modified traces that have been rerouted to avoid
494
- * labels, and the set of merged labels that were used as obstacles.
491
+ * 1. **Dispatch Phase**: Iterates through traces, identifying clean ones and dispatching
492
+ * colliding ones to dedicated `OverlapAvoidanceStepSolver` instances.
495
493
  *
496
- * @param {TraceLabelOverlapAvoidanceSolverInput} solverInput - The input for the solver,
497
- * containing the initial traces, label placements, and the input problem definition.
494
+ * 2. **Execution Phase**: Steps through all dispatched sub-solvers until they complete.
495
+ *
496
+ * The final output combines clean traces with results from sub-solvers. A final
497
+ * `MergedNetLabelObstacleSolver` ensures pipeline compatibility.
498
498
  */
499
499
  declare class TraceLabelOverlapAvoidanceSolver extends BaseSolver {
500
500
  inputProblem: InputProblem;
501
- traces: SolvedTracePath[];
502
501
  netLabelPlacements: NetLabelPlacement[];
502
+ unprocessedTraces: SolvedTracePath[];
503
+ cleanTraces: SolvedTracePath[];
504
+ subSolvers: OverlapAvoidanceStepSolver[];
505
+ private phase;
506
+ detourCounts: Map<string, number>;
503
507
  labelMergingSolver?: MergedNetLabelObstacleSolver;
504
- overlapAvoidanceSolver?: OverlapAvoidanceStepSolver;
505
- pipelineStepIndex: number;
506
508
  constructor(solverInput: TraceLabelOverlapAvoidanceSolverInput);
507
509
  _step(): void;
508
510
  getOutput(): {
package/dist/index.js CHANGED
@@ -2649,73 +2649,106 @@ var generateFourPointDetourCandidates = ({
2649
2649
  paddingBuffer,
2650
2650
  detourCount
2651
2651
  }) => {
2652
- let collidingSegIndex = -1;
2653
- for (let i = 0; i < initialTrace.tracePath.length - 1; i++) {
2654
- if (segmentIntersectsRect(
2655
- initialTrace.tracePath[i],
2656
- initialTrace.tracePath[i + 1],
2657
- labelBounds
2658
- )) {
2659
- collidingSegIndex = i;
2660
- break;
2652
+ const isMergedLabel = label.globalConnNetId.startsWith("merged-group-");
2653
+ const effectivePadding = paddingBuffer + detourCount * paddingBuffer;
2654
+ const paddedLabelBounds = {
2655
+ minX: labelBounds.minX - effectivePadding,
2656
+ maxX: labelBounds.maxX + effectivePadding,
2657
+ minY: labelBounds.minY - effectivePadding,
2658
+ maxY: labelBounds.maxY + effectivePadding
2659
+ };
2660
+ let entryPoint, exitPoint;
2661
+ let entryIndex, exitIndex;
2662
+ const isPointInRect = (p) => p.x >= paddedLabelBounds.minX && p.x <= paddedLabelBounds.maxX && p.y >= paddedLabelBounds.minY && p.y <= paddedLabelBounds.maxY;
2663
+ if (isMergedLabel) {
2664
+ let firstInsideIndex = -1;
2665
+ for (let i = 0; i < initialTrace.tracePath.length; i++) {
2666
+ if (isPointInRect(initialTrace.tracePath[i])) {
2667
+ firstInsideIndex = i;
2668
+ break;
2669
+ }
2670
+ }
2671
+ if (firstInsideIndex === -1) return [];
2672
+ entryIndex = Math.max(0, firstInsideIndex - 1);
2673
+ entryPoint = initialTrace.tracePath[entryIndex];
2674
+ let firstOutsideIndex = -1;
2675
+ for (let i = firstInsideIndex; i < initialTrace.tracePath.length; i++) {
2676
+ if (!isPointInRect(initialTrace.tracePath[i])) {
2677
+ firstOutsideIndex = i;
2678
+ break;
2679
+ }
2661
2680
  }
2681
+ if (firstOutsideIndex === -1) {
2682
+ exitIndex = initialTrace.tracePath.length - 1;
2683
+ } else {
2684
+ exitIndex = firstOutsideIndex;
2685
+ }
2686
+ exitPoint = initialTrace.tracePath[exitIndex];
2687
+ } else {
2688
+ let collidingSegIndex = -1;
2689
+ for (let i = 0; i < initialTrace.tracePath.length - 1; i++) {
2690
+ if (segmentIntersectsRect(
2691
+ initialTrace.tracePath[i],
2692
+ initialTrace.tracePath[i + 1],
2693
+ { ...paddedLabelBounds, chipId: "temp-obstacle" }
2694
+ )) {
2695
+ collidingSegIndex = i;
2696
+ break;
2697
+ }
2698
+ }
2699
+ if (collidingSegIndex === -1) return [];
2700
+ entryIndex = collidingSegIndex;
2701
+ exitIndex = collidingSegIndex + 1;
2702
+ entryPoint = initialTrace.tracePath[entryIndex];
2703
+ exitPoint = initialTrace.tracePath[exitIndex];
2662
2704
  }
2663
- if (collidingSegIndex === -1) return [];
2664
- const pA = initialTrace.tracePath[collidingSegIndex];
2665
- const pB = initialTrace.tracePath[collidingSegIndex + 1];
2666
- if (!pA || !pB) return [];
2705
+ if (!entryPoint || !exitPoint || entryIndex >= exitIndex) return [];
2667
2706
  const candidateDetours = [];
2668
- const paddedLabelBounds = getRectBounds(
2669
- label.center,
2670
- label.width,
2671
- label.height
2672
- );
2673
- const effectivePadding = paddingBuffer + detourCount * paddingBuffer;
2674
- if (isVertical(pA, pB)) {
2675
- const xCandidates = [
2676
- paddedLabelBounds.maxX + effectivePadding,
2677
- paddedLabelBounds.minX - effectivePadding
2678
- ];
2679
- for (const newX of xCandidates) {
2707
+ const dx = exitPoint.x - entryPoint.x;
2708
+ const dy = exitPoint.y - entryPoint.y;
2709
+ if (Math.abs(dx) > Math.abs(dy)) {
2710
+ const yCandidates = [paddedLabelBounds.maxY, paddedLabelBounds.minY];
2711
+ for (const newY of yCandidates) {
2680
2712
  candidateDetours.push(
2681
- pB.y > pA.y ? [
2682
- { x: pA.x, y: paddedLabelBounds.minY - effectivePadding },
2683
- { x: newX, y: paddedLabelBounds.minY - effectivePadding },
2684
- { x: newX, y: paddedLabelBounds.maxY + effectivePadding },
2685
- { x: pB.x, y: paddedLabelBounds.maxY + effectivePadding }
2713
+ dx > 0 ? [
2714
+ { x: paddedLabelBounds.minX, y: entryPoint.y },
2715
+ { x: paddedLabelBounds.minX, y: newY },
2716
+ { x: paddedLabelBounds.maxX, y: newY },
2717
+ { x: paddedLabelBounds.maxX, y: exitPoint.y }
2686
2718
  ] : [
2687
- { x: pA.x, y: paddedLabelBounds.maxY + effectivePadding },
2688
- { x: newX, y: paddedLabelBounds.maxY + effectivePadding },
2689
- { x: newX, y: paddedLabelBounds.minY - effectivePadding },
2690
- { x: pB.x, y: paddedLabelBounds.minY - effectivePadding }
2719
+ // Right-to-left
2720
+ { x: paddedLabelBounds.maxX, y: entryPoint.y },
2721
+ { x: paddedLabelBounds.maxX, y: newY },
2722
+ { x: paddedLabelBounds.minX, y: newY },
2723
+ { x: paddedLabelBounds.minX, y: exitPoint.y }
2691
2724
  ]
2692
2725
  );
2693
2726
  }
2694
2727
  } else {
2695
- const yCandidates = [
2696
- paddedLabelBounds.maxY + effectivePadding,
2697
- paddedLabelBounds.minY - effectivePadding
2698
- ];
2699
- for (const newY of yCandidates) {
2728
+ const xCandidates = [paddedLabelBounds.maxX, paddedLabelBounds.minX];
2729
+ for (const newX of xCandidates) {
2700
2730
  candidateDetours.push(
2701
- pB.x > pA.x ? [
2702
- { x: paddedLabelBounds.minX - effectivePadding, y: pA.y },
2703
- { x: paddedLabelBounds.minX - effectivePadding, y: newY },
2704
- { x: paddedLabelBounds.maxX + effectivePadding, y: newY },
2705
- { x: paddedLabelBounds.maxX + effectivePadding, y: pB.y }
2731
+ dy > 0 ? [
2732
+ { x: entryPoint.x, y: paddedLabelBounds.minY },
2733
+ { x: newX, y: paddedLabelBounds.minY },
2734
+ { x: newX, y: paddedLabelBounds.maxY },
2735
+ { x: exitPoint.x, y: paddedLabelBounds.maxY }
2706
2736
  ] : [
2707
- { x: paddedLabelBounds.maxX + effectivePadding, y: pA.y },
2708
- { x: paddedLabelBounds.maxX + effectivePadding, y: newY },
2709
- { x: paddedLabelBounds.minX - effectivePadding, y: newY },
2710
- { x: paddedLabelBounds.minX - effectivePadding, y: pB.y }
2737
+ // Bottom-to-top
2738
+ { x: entryPoint.x, y: paddedLabelBounds.maxY },
2739
+ { x: newX, y: paddedLabelBounds.maxY },
2740
+ { x: newX, y: paddedLabelBounds.minY },
2741
+ { x: exitPoint.x, y: paddedLabelBounds.minY }
2711
2742
  ]
2712
2743
  );
2713
2744
  }
2714
2745
  }
2715
2746
  return candidateDetours.map((detourPoints) => [
2716
- ...initialTrace.tracePath.slice(0, collidingSegIndex + 1),
2747
+ ...initialTrace.tracePath.slice(0, entryIndex),
2748
+ entryPoint,
2717
2749
  ...detourPoints,
2718
- ...initialTrace.tracePath.slice(collidingSegIndex + 1)
2750
+ exitPoint,
2751
+ ...initialTrace.tracePath.slice(exitIndex + 1)
2719
2752
  ]);
2720
2753
  };
2721
2754
 
@@ -2759,13 +2792,12 @@ var generateRerouteCandidates = ({
2759
2792
  if (trace.globalConnNetId === label.globalConnNetId) {
2760
2793
  return [initialTrace.tracePath];
2761
2794
  }
2762
- const labelPadding = paddingBuffer;
2763
2795
  const labelBoundsRaw = getRectBounds(label.center, label.width, label.height);
2764
2796
  const labelBounds = {
2765
- minX: labelBoundsRaw.minX - labelPadding,
2766
- minY: labelBoundsRaw.minY - labelPadding,
2767
- maxX: labelBoundsRaw.maxX + labelPadding,
2768
- maxY: labelBoundsRaw.maxY + labelPadding,
2797
+ minX: labelBoundsRaw.minX,
2798
+ minY: labelBoundsRaw.minY,
2799
+ maxX: labelBoundsRaw.maxX,
2800
+ maxY: labelBoundsRaw.maxY,
2769
2801
  chipId: `netlabel-${label.netId}`
2770
2802
  };
2771
2803
  const fourPointCandidates = generateFourPointDetourCandidates({
@@ -2791,6 +2823,7 @@ var generateRerouteCandidates = ({
2791
2823
  };
2792
2824
 
2793
2825
  // lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/SingleOverlapSolver/SingleOverlapSolver.ts
2826
+ var MAX_TRIES = 5;
2794
2827
  var SingleOverlapSolver = class extends BaseSolver {
2795
2828
  queuedCandidatePaths;
2796
2829
  solvedTracePath = null;
@@ -2798,13 +2831,17 @@ var SingleOverlapSolver = class extends BaseSolver {
2798
2831
  problem;
2799
2832
  obstacles;
2800
2833
  label;
2834
+ _tried = 0;
2801
2835
  constructor(solverInput) {
2802
2836
  super();
2803
2837
  this.initialTrace = solverInput.trace;
2804
2838
  this.problem = solverInput.problem;
2805
2839
  this.label = solverInput.label;
2840
+ const effectivePadding = solverInput.paddingBuffer + solverInput.detourCount * solverInput.paddingBuffer;
2806
2841
  const candidates = generateRerouteCandidates({
2807
- ...solverInput
2842
+ ...solverInput,
2843
+ paddingBuffer: effectivePadding
2844
+ // Use the calculated, larger padding
2808
2845
  });
2809
2846
  const getPathLength = (pts) => {
2810
2847
  let len = 0;
@@ -2821,10 +2858,11 @@ var SingleOverlapSolver = class extends BaseSolver {
2821
2858
  this.obstacles = getObstacleRects(this.problem);
2822
2859
  }
2823
2860
  _step() {
2824
- if (this.queuedCandidatePaths.length === 0) {
2861
+ if (this.queuedCandidatePaths.length === 0 || this._tried >= MAX_TRIES) {
2825
2862
  this.failed = true;
2826
2863
  return;
2827
2864
  }
2865
+ this._tried++;
2828
2866
  const nextCandidatePath = this.queuedCandidatePaths.shift();
2829
2867
  const simplifiedPath = simplifyPath(nextCandidatePath);
2830
2868
  if (!isPathCollidingWithObstacles(simplifiedPath, this.obstacles)) {
@@ -2866,13 +2904,38 @@ var SingleOverlapSolver = class extends BaseSolver {
2866
2904
  }
2867
2905
  };
2868
2906
 
2869
- // lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/OverlapAvoidanceStepSolver/isPointInsideLabel.ts
2870
- var isPointInsideLabel = ({
2871
- point,
2872
- label
2873
- }) => {
2874
- const bounds = getRectBounds(label.center, label.width, label.height);
2875
- return point.x >= bounds.minX && point.x <= bounds.maxX && point.y >= bounds.minY && point.y <= bounds.maxY;
2907
+ // lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/OverlapAvoidanceStepSolver/doesTraceStartOrEndInLabel.ts
2908
+ var doesTraceStartOrEndInLabel = ({ trace, label }) => {
2909
+ if (trace.tracePath.length < 2) {
2910
+ return false;
2911
+ }
2912
+ const firstSegmentTrace = {
2913
+ ...trace,
2914
+ tracePath: [trace.tracePath[0], trace.tracePath[1]]
2915
+ };
2916
+ const firstSegmentOverlap = detectTraceLabelOverlap({
2917
+ traces: [firstSegmentTrace],
2918
+ netLabels: [label]
2919
+ });
2920
+ if (firstSegmentOverlap.length > 0) {
2921
+ return true;
2922
+ }
2923
+ if (trace.tracePath.length > 2) {
2924
+ const lastPoint = trace.tracePath[trace.tracePath.length - 1];
2925
+ const secondToLastPoint = trace.tracePath[trace.tracePath.length - 2];
2926
+ const lastSegmentTrace = {
2927
+ ...trace,
2928
+ tracePath: [secondToLastPoint, lastPoint]
2929
+ };
2930
+ const lastSegmentOverlap = detectTraceLabelOverlap({
2931
+ traces: [lastSegmentTrace],
2932
+ netLabels: [label]
2933
+ });
2934
+ if (lastSegmentOverlap.length > 0) {
2935
+ return true;
2936
+ }
2937
+ }
2938
+ return false;
2876
2939
  };
2877
2940
 
2878
2941
  // lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/OverlapAvoidanceStepSolver/visualizeDecomposition.ts
@@ -2908,8 +2971,8 @@ var OverlapAvoidanceStepSolver = class extends BaseSolver {
2908
2971
  mergedLabelNetIdMap;
2909
2972
  allTraces;
2910
2973
  modifiedTraces = [];
2911
- detourCountByLabel = {};
2912
2974
  PADDING_BUFFER = 0.1;
2975
+ detourCounts = /* @__PURE__ */ new Map();
2913
2976
  activeSubSolver = null;
2914
2977
  overlapQueue = [];
2915
2978
  recentlyFailed = /* @__PURE__ */ new Set();
@@ -2922,6 +2985,7 @@ var OverlapAvoidanceStepSolver = class extends BaseSolver {
2922
2985
  this.mergedNetLabelPlacements = solverInput.mergedNetLabelPlacements;
2923
2986
  this.mergedLabelNetIdMap = solverInput.mergedLabelNetIdMap;
2924
2987
  this.allTraces = [...solverInput.traces];
2988
+ this.detourCounts = solverInput.detourCounts;
2925
2989
  }
2926
2990
  _step() {
2927
2991
  this.currentlyProcessingOverlap = null;
@@ -2973,7 +3037,6 @@ var OverlapAvoidanceStepSolver = class extends BaseSolver {
2973
3037
  (t) => t.mspPairId === nextOverlap.trace.mspPairId
2974
3038
  );
2975
3039
  const labelToAvoid = nextOverlap.label;
2976
- const traceStartPoint = traceToFix.tracePath[0];
2977
3040
  const originalNetIds = this.mergedLabelNetIdMap[labelToAvoid.globalConnNetId];
2978
3041
  const isSelfOverlap = originalNetIds?.has(traceToFix.globalConnNetId);
2979
3042
  if (isSelfOverlap) {
@@ -2993,9 +3056,11 @@ var OverlapAvoidanceStepSolver = class extends BaseSolver {
2993
3056
  }
2994
3057
  }
2995
3058
  if (actualOverlapLabel) {
2996
- const labelId2 = actualOverlapLabel.globalConnNetId;
2997
- const detourCount2 = this.detourCountByLabel[labelId2] || 0;
2998
- this.detourCountByLabel[labelId2] = detourCount2 + 1;
3059
+ const detourCount2 = this.detourCounts.get(actualOverlapLabel.globalConnNetId) ?? 0;
3060
+ this.detourCounts.set(
3061
+ actualOverlapLabel.globalConnNetId,
3062
+ detourCount2 + 1
3063
+ );
2999
3064
  this.activeSubSolver = new SingleOverlapSolver({
3000
3065
  trace: traceToFix,
3001
3066
  label: actualOverlapLabel,
@@ -3009,7 +3074,7 @@ var OverlapAvoidanceStepSolver = class extends BaseSolver {
3009
3074
  }
3010
3075
  return;
3011
3076
  }
3012
- if (originalNetIds && isPointInsideLabel({ point: traceStartPoint, label: labelToAvoid })) {
3077
+ if (originalNetIds && doesTraceStartOrEndInLabel({ trace: traceToFix, label: labelToAvoid })) {
3013
3078
  const childLabels = this.initialNetLabelPlacements.filter(
3014
3079
  (l) => originalNetIds.has(l.globalConnNetId)
3015
3080
  );
@@ -3026,9 +3091,11 @@ var OverlapAvoidanceStepSolver = class extends BaseSolver {
3026
3091
  }
3027
3092
  }
3028
3093
  if (actualOverlapLabel) {
3029
- const labelId2 = actualOverlapLabel.globalConnNetId;
3030
- const detourCount2 = this.detourCountByLabel[labelId2] || 0;
3031
- this.detourCountByLabel[labelId2] = detourCount2 + 1;
3094
+ const detourCount2 = this.detourCounts.get(actualOverlapLabel.globalConnNetId) ?? 0;
3095
+ this.detourCounts.set(
3096
+ actualOverlapLabel.globalConnNetId,
3097
+ detourCount2 + 1
3098
+ );
3032
3099
  this.activeSubSolver = new SingleOverlapSolver({
3033
3100
  trace: traceToFix,
3034
3101
  label: actualOverlapLabel,
@@ -3042,9 +3109,8 @@ var OverlapAvoidanceStepSolver = class extends BaseSolver {
3042
3109
  }
3043
3110
  return;
3044
3111
  }
3045
- const labelId = labelToAvoid.globalConnNetId;
3046
- const detourCount = this.detourCountByLabel[labelId] || 0;
3047
- this.detourCountByLabel[labelId] = detourCount + 1;
3112
+ const detourCount = this.detourCounts.get(labelToAvoid.globalConnNetId) ?? 0;
3113
+ this.detourCounts.set(labelToAvoid.globalConnNetId, detourCount + 1);
3048
3114
  this.activeSubSolver = new SingleOverlapSolver({
3049
3115
  trace: traceToFix,
3050
3116
  label: labelToAvoid,
@@ -3057,7 +3123,8 @@ var OverlapAvoidanceStepSolver = class extends BaseSolver {
3057
3123
  getOutput() {
3058
3124
  return {
3059
3125
  allTraces: this.allTraces,
3060
- modifiedTraces: this.modifiedTraces
3126
+ modifiedTraces: this.modifiedTraces,
3127
+ detourCounts: this.detourCounts
3061
3128
  };
3062
3129
  }
3063
3130
  visualize() {
@@ -3110,76 +3177,101 @@ var OverlapAvoidanceStepSolver = class extends BaseSolver {
3110
3177
  // lib/solvers/TraceLabelOverlapAvoidanceSolver/TraceLabelOverlapAvoidanceSolver.ts
3111
3178
  var TraceLabelOverlapAvoidanceSolver = class extends BaseSolver {
3112
3179
  inputProblem;
3113
- traces;
3114
3180
  netLabelPlacements;
3115
- // sub-solver instances
3181
+ unprocessedTraces = [];
3182
+ cleanTraces = [];
3183
+ subSolvers = [];
3184
+ phase = "searching_for_overlaps";
3185
+ detourCounts = /* @__PURE__ */ new Map();
3116
3186
  labelMergingSolver;
3117
- overlapAvoidanceSolver;
3118
- pipelineStepIndex = 0;
3119
3187
  constructor(solverInput) {
3120
3188
  super();
3121
3189
  this.inputProblem = solverInput.inputProblem;
3122
- this.traces = solverInput.traces;
3190
+ this.unprocessedTraces = [...solverInput.traces];
3123
3191
  this.netLabelPlacements = solverInput.netLabelPlacements;
3192
+ this.cleanTraces = [];
3193
+ this.subSolvers = [];
3124
3194
  }
3125
3195
  _step() {
3126
- if (this.activeSubSolver) {
3127
- this.activeSubSolver.step();
3128
- if (this.activeSubSolver.solved) {
3129
- this.activeSubSolver = null;
3130
- this.pipelineStepIndex++;
3131
- } else if (this.activeSubSolver.failed) {
3132
- this.failed = true;
3133
- this.activeSubSolver = null;
3196
+ if (this.phase === "searching_for_overlaps") {
3197
+ if (this.unprocessedTraces.length === 0) {
3198
+ console.log(
3199
+ `Dispatch phase complete. Created ${this.subSolvers.length} sub-solvers.`
3200
+ );
3201
+ this.phase = "fixing_overlaps";
3202
+ return;
3134
3203
  }
3135
- return;
3136
- }
3137
- switch (this.pipelineStepIndex) {
3138
- case 0:
3139
- this.labelMergingSolver = new MergedNetLabelObstacleSolver({
3140
- netLabelPlacements: this.netLabelPlacements,
3204
+ const currentTargetTrace = this.unprocessedTraces.shift();
3205
+ const localOverlaps = detectTraceLabelOverlap({
3206
+ traces: [currentTargetTrace],
3207
+ netLabels: this.netLabelPlacements
3208
+ });
3209
+ if (localOverlaps.length === 0) {
3210
+ this.cleanTraces.push(currentTargetTrace);
3211
+ } else {
3212
+ const collidingLabels = localOverlaps.map((o) => o.label);
3213
+ const labelMerger = new MergedNetLabelObstacleSolver({
3214
+ netLabelPlacements: collidingLabels,
3141
3215
  inputProblem: this.inputProblem,
3142
- traces: this.traces
3216
+ traces: [currentTargetTrace]
3143
3217
  });
3144
- this.activeSubSolver = this.labelMergingSolver;
3145
- break;
3146
- case 1:
3147
- this.overlapAvoidanceSolver = new OverlapAvoidanceStepSolver({
3218
+ labelMerger.solve();
3219
+ const mergingOutput = labelMerger.getOutput();
3220
+ const subSolver = new OverlapAvoidanceStepSolver({
3148
3221
  inputProblem: this.inputProblem,
3149
- traces: this.traces,
3222
+ traces: [currentTargetTrace],
3150
3223
  initialNetLabelPlacements: this.netLabelPlacements,
3151
- // The original, unfiltered list
3152
- mergedNetLabelPlacements: this.labelMergingSolver.getOutput().netLabelPlacements,
3153
- mergedLabelNetIdMap: this.labelMergingSolver.getOutput().mergedLabelNetIdMap
3224
+ mergedNetLabelPlacements: mergingOutput.netLabelPlacements,
3225
+ mergedLabelNetIdMap: mergingOutput.mergedLabelNetIdMap,
3226
+ detourCounts: this.detourCounts
3154
3227
  });
3155
- this.activeSubSolver = this.overlapAvoidanceSolver;
3156
- break;
3157
- default:
3228
+ this.subSolvers.push(subSolver);
3229
+ }
3230
+ } else if (this.phase === "fixing_overlaps") {
3231
+ if (this.subSolvers.every((s) => s.solved || s.failed)) {
3232
+ console.log("All sub-solvers finished.");
3233
+ if (!this.labelMergingSolver) {
3234
+ this.labelMergingSolver = new MergedNetLabelObstacleSolver({
3235
+ netLabelPlacements: this.netLabelPlacements,
3236
+ inputProblem: this.inputProblem,
3237
+ traces: this.getOutput().traces
3238
+ });
3239
+ this.labelMergingSolver.solve();
3240
+ }
3158
3241
  this.solved = true;
3159
- break;
3242
+ return;
3243
+ }
3244
+ for (const solver of this.subSolvers) {
3245
+ if (!solver.solved && !solver.failed) {
3246
+ solver.step();
3247
+ }
3248
+ }
3160
3249
  }
3161
3250
  }
3162
3251
  getOutput() {
3252
+ const solvedTraces = this.subSolvers.flatMap((s) => s.getOutput().allTraces);
3163
3253
  return {
3164
- traces: this.overlapAvoidanceSolver?.getOutput().allTraces ?? this.traces,
3254
+ traces: [...this.cleanTraces, ...solvedTraces],
3165
3255
  netLabelPlacements: this.labelMergingSolver?.getOutput().netLabelPlacements ?? this.netLabelPlacements
3166
3256
  };
3167
3257
  }
3168
3258
  visualize() {
3169
- if (this.activeSubSolver) {
3170
- return this.activeSubSolver.visualize();
3171
- }
3172
3259
  const graphics = visualizeInputProblem(this.inputProblem);
3173
3260
  if (!graphics.lines) graphics.lines = [];
3174
3261
  if (!graphics.rects) graphics.rects = [];
3175
- const output = this.getOutput();
3176
- for (const trace of output.traces) {
3262
+ for (const trace of this.cleanTraces) {
3177
3263
  graphics.lines.push({
3178
3264
  points: trace.tracePath,
3179
3265
  strokeColor: "purple"
3180
3266
  });
3181
3267
  }
3182
- for (const label of output.netLabelPlacements) {
3268
+ for (const solver of this.subSolvers) {
3269
+ const solverGraphics = solver.visualize();
3270
+ graphics.lines.push(...solverGraphics.lines ?? []);
3271
+ graphics.rects.push(...solverGraphics.rects ?? []);
3272
+ graphics.points.push(...solverGraphics.points ?? []);
3273
+ }
3274
+ for (const label of this.netLabelPlacements) {
3183
3275
  const color = getColorFromString(label.globalConnNetId, 0.3);
3184
3276
  graphics.rects.push({
3185
3277
  center: label.center,