@tscircuit/schematic-trace-solver 0.0.46 → 0.0.48
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 +18 -16
- package/dist/index.js +228 -122
- package/lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver.ts +2 -0
- package/lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/SingleNetLabelPlacementSolver_visualize.ts +12 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/TraceLabelOverlapAvoidanceSolver.ts +89 -61
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/rerouteCollidingTrace.ts +16 -5
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/OverlapAvoidanceStepSolver/OverlapAvoidanceStepSolver.ts +27 -16
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/OverlapAvoidanceStepSolver/doesTraceStartOrEndInLabel.ts +58 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/SingleOverlapSolver/SingleOverlapSolver.ts +13 -1
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/tryFourPointDetour.ts +110 -60
- package/package.json +1 -1
- package/site/examples/example27.page.tsx +4 -0
- package/site/examples/example28.page.tsx +61 -0
- package/tests/assets/example27.json +168 -0
- package/tests/examples/__snapshots__/example03.snap.svg +67 -67
- package/tests/examples/__snapshots__/example16.snap.svg +3 -3
- package/tests/examples/__snapshots__/example28.snap.svg +23 -189
- package/tests/examples/__snapshots__/example30.snap.svg +258 -0
- package/tests/examples/example28.test.ts +2 -3
- package/tests/examples/example30.test.ts +12 -0
- package/tests/solvers/TraceLabelOverlapAvoidanceSolver/__snapshots__/TraceLabelOverlapAvoidanceSolver.snap.svg +9 -6
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
|
-
*
|
|
486
|
+
* Resolves overlaps between schematic traces and net labels using a two-phase,
|
|
487
|
+
* "fire-and-forget" dispatching strategy.
|
|
484
488
|
*
|
|
485
|
-
* This solver
|
|
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
|
-
*
|
|
494
|
-
*
|
|
491
|
+
* 1. **Dispatch Phase**: Iterates through traces, identifying clean ones and dispatching
|
|
492
|
+
* colliding ones to dedicated `OverlapAvoidanceStepSolver` instances.
|
|
495
493
|
*
|
|
496
|
-
*
|
|
497
|
-
*
|
|
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
|
@@ -1767,6 +1767,7 @@ function visualizeSingleNetLabelPlacementSolver(solver) {
|
|
|
1767
1767
|
for (const c of solver.testedCandidates) {
|
|
1768
1768
|
const fill = c.status === "ok" ? "rgba(0, 180, 0, 0.25)" : c.status === "chip-collision" ? "rgba(220, 0, 0, 0.25)" : c.status === "trace-collision" ? "rgba(220, 140, 0, 0.25)" : "rgba(120, 120, 120, 0.15)";
|
|
1769
1769
|
const stroke = c.status === "ok" ? "green" : c.status === "chip-collision" ? "red" : c.status === "trace-collision" ? "orange" : "gray";
|
|
1770
|
+
const candidateLabel = c.status === "ok" ? "status: ok(valid net label candidate)" : c.status === "chip-collision" ? "status: chip-collision" : c.status === "trace-collision" ? "status: trace-collision" : "status: parallel-to-segment";
|
|
1770
1771
|
graphics.rects.push({
|
|
1771
1772
|
center: {
|
|
1772
1773
|
x: (c.bounds.minX + c.bounds.maxX) / 2,
|
|
@@ -1775,12 +1776,16 @@ function visualizeSingleNetLabelPlacementSolver(solver) {
|
|
|
1775
1776
|
width: c.width,
|
|
1776
1777
|
height: c.height,
|
|
1777
1778
|
fill,
|
|
1778
|
-
strokeColor: stroke
|
|
1779
|
+
strokeColor: stroke,
|
|
1780
|
+
label: `${candidateLabel}
|
|
1781
|
+
orientation: ${c.orientation}`
|
|
1779
1782
|
});
|
|
1780
1783
|
graphics.points.push({
|
|
1781
1784
|
x: c.anchor.x,
|
|
1782
1785
|
y: c.anchor.y,
|
|
1783
|
-
color: stroke
|
|
1786
|
+
color: stroke,
|
|
1787
|
+
label: `anchor
|
|
1788
|
+
orientation: ${c.orientation}`
|
|
1784
1789
|
});
|
|
1785
1790
|
}
|
|
1786
1791
|
if (solver.netLabelPlacement) {
|
|
@@ -1790,12 +1795,16 @@ function visualizeSingleNetLabelPlacementSolver(solver) {
|
|
|
1790
1795
|
width: p.width,
|
|
1791
1796
|
height: p.height,
|
|
1792
1797
|
fill: "rgba(0, 128, 255, 0.35)",
|
|
1793
|
-
strokeColor: "blue"
|
|
1798
|
+
strokeColor: "blue",
|
|
1799
|
+
label: `netId: ${p.netId}
|
|
1800
|
+
globalConnNetId: ${p.globalConnNetId}`
|
|
1794
1801
|
});
|
|
1795
1802
|
graphics.points.push({
|
|
1796
1803
|
x: p.anchorPoint.x,
|
|
1797
1804
|
y: p.anchorPoint.y,
|
|
1798
|
-
color: "blue"
|
|
1805
|
+
color: "blue",
|
|
1806
|
+
label: `anchor
|
|
1807
|
+
orientation: ${p.orientation}`
|
|
1799
1808
|
});
|
|
1800
1809
|
}
|
|
1801
1810
|
return graphics;
|
|
@@ -2248,12 +2257,16 @@ var NetLabelPlacementSolver = class extends BaseSolver {
|
|
|
2248
2257
|
width: p.width,
|
|
2249
2258
|
height: p.height,
|
|
2250
2259
|
fill: getColorFromString(p.globalConnNetId, 0.35),
|
|
2251
|
-
strokeColor: getColorFromString(p.globalConnNetId, 0.9)
|
|
2260
|
+
strokeColor: getColorFromString(p.globalConnNetId, 0.9),
|
|
2261
|
+
label: `netId: ${p.netId}
|
|
2262
|
+
globalConnNetId: ${p.globalConnNetId}`
|
|
2252
2263
|
});
|
|
2253
2264
|
graphics.points.push({
|
|
2254
2265
|
x: p.anchorPoint.x,
|
|
2255
2266
|
y: p.anchorPoint.y,
|
|
2256
|
-
color: getColorFromString(p.globalConnNetId, 0.9)
|
|
2267
|
+
color: getColorFromString(p.globalConnNetId, 0.9),
|
|
2268
|
+
label: `anchorPoint
|
|
2269
|
+
orientation: ${p.orientation}`
|
|
2257
2270
|
});
|
|
2258
2271
|
}
|
|
2259
2272
|
return graphics;
|
|
@@ -2649,73 +2662,106 @@ var generateFourPointDetourCandidates = ({
|
|
|
2649
2662
|
paddingBuffer,
|
|
2650
2663
|
detourCount
|
|
2651
2664
|
}) => {
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2665
|
+
const isMergedLabel = label.globalConnNetId.startsWith("merged-group-");
|
|
2666
|
+
const effectivePadding = paddingBuffer + detourCount * paddingBuffer;
|
|
2667
|
+
const paddedLabelBounds = {
|
|
2668
|
+
minX: labelBounds.minX - effectivePadding,
|
|
2669
|
+
maxX: labelBounds.maxX + effectivePadding,
|
|
2670
|
+
minY: labelBounds.minY - effectivePadding,
|
|
2671
|
+
maxY: labelBounds.maxY + effectivePadding
|
|
2672
|
+
};
|
|
2673
|
+
let entryPoint, exitPoint;
|
|
2674
|
+
let entryIndex, exitIndex;
|
|
2675
|
+
const isPointInRect = (p) => p.x >= paddedLabelBounds.minX && p.x <= paddedLabelBounds.maxX && p.y >= paddedLabelBounds.minY && p.y <= paddedLabelBounds.maxY;
|
|
2676
|
+
if (isMergedLabel) {
|
|
2677
|
+
let firstInsideIndex = -1;
|
|
2678
|
+
for (let i = 0; i < initialTrace.tracePath.length; i++) {
|
|
2679
|
+
if (isPointInRect(initialTrace.tracePath[i])) {
|
|
2680
|
+
firstInsideIndex = i;
|
|
2681
|
+
break;
|
|
2682
|
+
}
|
|
2683
|
+
}
|
|
2684
|
+
if (firstInsideIndex === -1) return [];
|
|
2685
|
+
entryIndex = Math.max(0, firstInsideIndex - 1);
|
|
2686
|
+
entryPoint = initialTrace.tracePath[entryIndex];
|
|
2687
|
+
let firstOutsideIndex = -1;
|
|
2688
|
+
for (let i = firstInsideIndex; i < initialTrace.tracePath.length; i++) {
|
|
2689
|
+
if (!isPointInRect(initialTrace.tracePath[i])) {
|
|
2690
|
+
firstOutsideIndex = i;
|
|
2691
|
+
break;
|
|
2692
|
+
}
|
|
2693
|
+
}
|
|
2694
|
+
if (firstOutsideIndex === -1) {
|
|
2695
|
+
exitIndex = initialTrace.tracePath.length - 1;
|
|
2696
|
+
} else {
|
|
2697
|
+
exitIndex = firstOutsideIndex;
|
|
2661
2698
|
}
|
|
2699
|
+
exitPoint = initialTrace.tracePath[exitIndex];
|
|
2700
|
+
} else {
|
|
2701
|
+
let collidingSegIndex = -1;
|
|
2702
|
+
for (let i = 0; i < initialTrace.tracePath.length - 1; i++) {
|
|
2703
|
+
if (segmentIntersectsRect(
|
|
2704
|
+
initialTrace.tracePath[i],
|
|
2705
|
+
initialTrace.tracePath[i + 1],
|
|
2706
|
+
{ ...paddedLabelBounds, chipId: "temp-obstacle" }
|
|
2707
|
+
)) {
|
|
2708
|
+
collidingSegIndex = i;
|
|
2709
|
+
break;
|
|
2710
|
+
}
|
|
2711
|
+
}
|
|
2712
|
+
if (collidingSegIndex === -1) return [];
|
|
2713
|
+
entryIndex = collidingSegIndex;
|
|
2714
|
+
exitIndex = collidingSegIndex + 1;
|
|
2715
|
+
entryPoint = initialTrace.tracePath[entryIndex];
|
|
2716
|
+
exitPoint = initialTrace.tracePath[exitIndex];
|
|
2662
2717
|
}
|
|
2663
|
-
if (
|
|
2664
|
-
const pA = initialTrace.tracePath[collidingSegIndex];
|
|
2665
|
-
const pB = initialTrace.tracePath[collidingSegIndex + 1];
|
|
2666
|
-
if (!pA || !pB) return [];
|
|
2718
|
+
if (!entryPoint || !exitPoint || entryIndex >= exitIndex) return [];
|
|
2667
2719
|
const candidateDetours = [];
|
|
2668
|
-
const
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
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) {
|
|
2720
|
+
const dx = exitPoint.x - entryPoint.x;
|
|
2721
|
+
const dy = exitPoint.y - entryPoint.y;
|
|
2722
|
+
if (Math.abs(dx) > Math.abs(dy)) {
|
|
2723
|
+
const yCandidates = [paddedLabelBounds.maxY, paddedLabelBounds.minY];
|
|
2724
|
+
for (const newY of yCandidates) {
|
|
2680
2725
|
candidateDetours.push(
|
|
2681
|
-
|
|
2682
|
-
{ x:
|
|
2683
|
-
{ x:
|
|
2684
|
-
{ x:
|
|
2685
|
-
{ x:
|
|
2726
|
+
dx > 0 ? [
|
|
2727
|
+
{ x: paddedLabelBounds.minX, y: entryPoint.y },
|
|
2728
|
+
{ x: paddedLabelBounds.minX, y: newY },
|
|
2729
|
+
{ x: paddedLabelBounds.maxX, y: newY },
|
|
2730
|
+
{ x: paddedLabelBounds.maxX, y: exitPoint.y }
|
|
2686
2731
|
] : [
|
|
2687
|
-
|
|
2688
|
-
{ x:
|
|
2689
|
-
{ x:
|
|
2690
|
-
{ x:
|
|
2732
|
+
// Right-to-left
|
|
2733
|
+
{ x: paddedLabelBounds.maxX, y: entryPoint.y },
|
|
2734
|
+
{ x: paddedLabelBounds.maxX, y: newY },
|
|
2735
|
+
{ x: paddedLabelBounds.minX, y: newY },
|
|
2736
|
+
{ x: paddedLabelBounds.minX, y: exitPoint.y }
|
|
2691
2737
|
]
|
|
2692
2738
|
);
|
|
2693
2739
|
}
|
|
2694
2740
|
} else {
|
|
2695
|
-
const
|
|
2696
|
-
|
|
2697
|
-
paddedLabelBounds.minY - effectivePadding
|
|
2698
|
-
];
|
|
2699
|
-
for (const newY of yCandidates) {
|
|
2741
|
+
const xCandidates = [paddedLabelBounds.maxX, paddedLabelBounds.minX];
|
|
2742
|
+
for (const newX of xCandidates) {
|
|
2700
2743
|
candidateDetours.push(
|
|
2701
|
-
|
|
2702
|
-
{ x:
|
|
2703
|
-
{ x:
|
|
2704
|
-
{ x:
|
|
2705
|
-
{ x:
|
|
2744
|
+
dy > 0 ? [
|
|
2745
|
+
{ x: entryPoint.x, y: paddedLabelBounds.minY },
|
|
2746
|
+
{ x: newX, y: paddedLabelBounds.minY },
|
|
2747
|
+
{ x: newX, y: paddedLabelBounds.maxY },
|
|
2748
|
+
{ x: exitPoint.x, y: paddedLabelBounds.maxY }
|
|
2706
2749
|
] : [
|
|
2707
|
-
|
|
2708
|
-
{ x:
|
|
2709
|
-
{ x:
|
|
2710
|
-
{ x:
|
|
2750
|
+
// Bottom-to-top
|
|
2751
|
+
{ x: entryPoint.x, y: paddedLabelBounds.maxY },
|
|
2752
|
+
{ x: newX, y: paddedLabelBounds.maxY },
|
|
2753
|
+
{ x: newX, y: paddedLabelBounds.minY },
|
|
2754
|
+
{ x: exitPoint.x, y: paddedLabelBounds.minY }
|
|
2711
2755
|
]
|
|
2712
2756
|
);
|
|
2713
2757
|
}
|
|
2714
2758
|
}
|
|
2715
2759
|
return candidateDetours.map((detourPoints) => [
|
|
2716
|
-
...initialTrace.tracePath.slice(0,
|
|
2760
|
+
...initialTrace.tracePath.slice(0, entryIndex),
|
|
2761
|
+
entryPoint,
|
|
2717
2762
|
...detourPoints,
|
|
2718
|
-
|
|
2763
|
+
exitPoint,
|
|
2764
|
+
...initialTrace.tracePath.slice(exitIndex + 1)
|
|
2719
2765
|
]);
|
|
2720
2766
|
};
|
|
2721
2767
|
|
|
@@ -2759,13 +2805,12 @@ var generateRerouteCandidates = ({
|
|
|
2759
2805
|
if (trace.globalConnNetId === label.globalConnNetId) {
|
|
2760
2806
|
return [initialTrace.tracePath];
|
|
2761
2807
|
}
|
|
2762
|
-
const labelPadding = paddingBuffer;
|
|
2763
2808
|
const labelBoundsRaw = getRectBounds(label.center, label.width, label.height);
|
|
2764
2809
|
const labelBounds = {
|
|
2765
|
-
minX: labelBoundsRaw.minX
|
|
2766
|
-
minY: labelBoundsRaw.minY
|
|
2767
|
-
maxX: labelBoundsRaw.maxX
|
|
2768
|
-
maxY: labelBoundsRaw.maxY
|
|
2810
|
+
minX: labelBoundsRaw.minX,
|
|
2811
|
+
minY: labelBoundsRaw.minY,
|
|
2812
|
+
maxX: labelBoundsRaw.maxX,
|
|
2813
|
+
maxY: labelBoundsRaw.maxY,
|
|
2769
2814
|
chipId: `netlabel-${label.netId}`
|
|
2770
2815
|
};
|
|
2771
2816
|
const fourPointCandidates = generateFourPointDetourCandidates({
|
|
@@ -2791,6 +2836,7 @@ var generateRerouteCandidates = ({
|
|
|
2791
2836
|
};
|
|
2792
2837
|
|
|
2793
2838
|
// lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/SingleOverlapSolver/SingleOverlapSolver.ts
|
|
2839
|
+
var MAX_TRIES = 5;
|
|
2794
2840
|
var SingleOverlapSolver = class extends BaseSolver {
|
|
2795
2841
|
queuedCandidatePaths;
|
|
2796
2842
|
solvedTracePath = null;
|
|
@@ -2798,13 +2844,17 @@ var SingleOverlapSolver = class extends BaseSolver {
|
|
|
2798
2844
|
problem;
|
|
2799
2845
|
obstacles;
|
|
2800
2846
|
label;
|
|
2847
|
+
_tried = 0;
|
|
2801
2848
|
constructor(solverInput) {
|
|
2802
2849
|
super();
|
|
2803
2850
|
this.initialTrace = solverInput.trace;
|
|
2804
2851
|
this.problem = solverInput.problem;
|
|
2805
2852
|
this.label = solverInput.label;
|
|
2853
|
+
const effectivePadding = solverInput.paddingBuffer + solverInput.detourCount * solverInput.paddingBuffer;
|
|
2806
2854
|
const candidates = generateRerouteCandidates({
|
|
2807
|
-
...solverInput
|
|
2855
|
+
...solverInput,
|
|
2856
|
+
paddingBuffer: effectivePadding
|
|
2857
|
+
// Use the calculated, larger padding
|
|
2808
2858
|
});
|
|
2809
2859
|
const getPathLength = (pts) => {
|
|
2810
2860
|
let len = 0;
|
|
@@ -2821,10 +2871,11 @@ var SingleOverlapSolver = class extends BaseSolver {
|
|
|
2821
2871
|
this.obstacles = getObstacleRects(this.problem);
|
|
2822
2872
|
}
|
|
2823
2873
|
_step() {
|
|
2824
|
-
if (this.queuedCandidatePaths.length === 0) {
|
|
2874
|
+
if (this.queuedCandidatePaths.length === 0 || this._tried >= MAX_TRIES) {
|
|
2825
2875
|
this.failed = true;
|
|
2826
2876
|
return;
|
|
2827
2877
|
}
|
|
2878
|
+
this._tried++;
|
|
2828
2879
|
const nextCandidatePath = this.queuedCandidatePaths.shift();
|
|
2829
2880
|
const simplifiedPath = simplifyPath(nextCandidatePath);
|
|
2830
2881
|
if (!isPathCollidingWithObstacles(simplifiedPath, this.obstacles)) {
|
|
@@ -2866,13 +2917,38 @@ var SingleOverlapSolver = class extends BaseSolver {
|
|
|
2866
2917
|
}
|
|
2867
2918
|
};
|
|
2868
2919
|
|
|
2869
|
-
// lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/OverlapAvoidanceStepSolver/
|
|
2870
|
-
var
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
}
|
|
2874
|
-
const
|
|
2875
|
-
|
|
2920
|
+
// lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/OverlapAvoidanceStepSolver/doesTraceStartOrEndInLabel.ts
|
|
2921
|
+
var doesTraceStartOrEndInLabel = ({ trace, label }) => {
|
|
2922
|
+
if (trace.tracePath.length < 2) {
|
|
2923
|
+
return false;
|
|
2924
|
+
}
|
|
2925
|
+
const firstSegmentTrace = {
|
|
2926
|
+
...trace,
|
|
2927
|
+
tracePath: [trace.tracePath[0], trace.tracePath[1]]
|
|
2928
|
+
};
|
|
2929
|
+
const firstSegmentOverlap = detectTraceLabelOverlap({
|
|
2930
|
+
traces: [firstSegmentTrace],
|
|
2931
|
+
netLabels: [label]
|
|
2932
|
+
});
|
|
2933
|
+
if (firstSegmentOverlap.length > 0) {
|
|
2934
|
+
return true;
|
|
2935
|
+
}
|
|
2936
|
+
if (trace.tracePath.length > 2) {
|
|
2937
|
+
const lastPoint = trace.tracePath[trace.tracePath.length - 1];
|
|
2938
|
+
const secondToLastPoint = trace.tracePath[trace.tracePath.length - 2];
|
|
2939
|
+
const lastSegmentTrace = {
|
|
2940
|
+
...trace,
|
|
2941
|
+
tracePath: [secondToLastPoint, lastPoint]
|
|
2942
|
+
};
|
|
2943
|
+
const lastSegmentOverlap = detectTraceLabelOverlap({
|
|
2944
|
+
traces: [lastSegmentTrace],
|
|
2945
|
+
netLabels: [label]
|
|
2946
|
+
});
|
|
2947
|
+
if (lastSegmentOverlap.length > 0) {
|
|
2948
|
+
return true;
|
|
2949
|
+
}
|
|
2950
|
+
}
|
|
2951
|
+
return false;
|
|
2876
2952
|
};
|
|
2877
2953
|
|
|
2878
2954
|
// lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/OverlapAvoidanceStepSolver/visualizeDecomposition.ts
|
|
@@ -2908,8 +2984,8 @@ var OverlapAvoidanceStepSolver = class extends BaseSolver {
|
|
|
2908
2984
|
mergedLabelNetIdMap;
|
|
2909
2985
|
allTraces;
|
|
2910
2986
|
modifiedTraces = [];
|
|
2911
|
-
detourCountByLabel = {};
|
|
2912
2987
|
PADDING_BUFFER = 0.1;
|
|
2988
|
+
detourCounts = /* @__PURE__ */ new Map();
|
|
2913
2989
|
activeSubSolver = null;
|
|
2914
2990
|
overlapQueue = [];
|
|
2915
2991
|
recentlyFailed = /* @__PURE__ */ new Set();
|
|
@@ -2922,6 +2998,7 @@ var OverlapAvoidanceStepSolver = class extends BaseSolver {
|
|
|
2922
2998
|
this.mergedNetLabelPlacements = solverInput.mergedNetLabelPlacements;
|
|
2923
2999
|
this.mergedLabelNetIdMap = solverInput.mergedLabelNetIdMap;
|
|
2924
3000
|
this.allTraces = [...solverInput.traces];
|
|
3001
|
+
this.detourCounts = solverInput.detourCounts;
|
|
2925
3002
|
}
|
|
2926
3003
|
_step() {
|
|
2927
3004
|
this.currentlyProcessingOverlap = null;
|
|
@@ -2973,7 +3050,6 @@ var OverlapAvoidanceStepSolver = class extends BaseSolver {
|
|
|
2973
3050
|
(t) => t.mspPairId === nextOverlap.trace.mspPairId
|
|
2974
3051
|
);
|
|
2975
3052
|
const labelToAvoid = nextOverlap.label;
|
|
2976
|
-
const traceStartPoint = traceToFix.tracePath[0];
|
|
2977
3053
|
const originalNetIds = this.mergedLabelNetIdMap[labelToAvoid.globalConnNetId];
|
|
2978
3054
|
const isSelfOverlap = originalNetIds?.has(traceToFix.globalConnNetId);
|
|
2979
3055
|
if (isSelfOverlap) {
|
|
@@ -2993,9 +3069,11 @@ var OverlapAvoidanceStepSolver = class extends BaseSolver {
|
|
|
2993
3069
|
}
|
|
2994
3070
|
}
|
|
2995
3071
|
if (actualOverlapLabel) {
|
|
2996
|
-
const
|
|
2997
|
-
|
|
2998
|
-
|
|
3072
|
+
const detourCount2 = this.detourCounts.get(actualOverlapLabel.globalConnNetId) ?? 0;
|
|
3073
|
+
this.detourCounts.set(
|
|
3074
|
+
actualOverlapLabel.globalConnNetId,
|
|
3075
|
+
detourCount2 + 1
|
|
3076
|
+
);
|
|
2999
3077
|
this.activeSubSolver = new SingleOverlapSolver({
|
|
3000
3078
|
trace: traceToFix,
|
|
3001
3079
|
label: actualOverlapLabel,
|
|
@@ -3009,7 +3087,7 @@ var OverlapAvoidanceStepSolver = class extends BaseSolver {
|
|
|
3009
3087
|
}
|
|
3010
3088
|
return;
|
|
3011
3089
|
}
|
|
3012
|
-
if (originalNetIds &&
|
|
3090
|
+
if (originalNetIds && doesTraceStartOrEndInLabel({ trace: traceToFix, label: labelToAvoid })) {
|
|
3013
3091
|
const childLabels = this.initialNetLabelPlacements.filter(
|
|
3014
3092
|
(l) => originalNetIds.has(l.globalConnNetId)
|
|
3015
3093
|
);
|
|
@@ -3026,9 +3104,11 @@ var OverlapAvoidanceStepSolver = class extends BaseSolver {
|
|
|
3026
3104
|
}
|
|
3027
3105
|
}
|
|
3028
3106
|
if (actualOverlapLabel) {
|
|
3029
|
-
const
|
|
3030
|
-
|
|
3031
|
-
|
|
3107
|
+
const detourCount2 = this.detourCounts.get(actualOverlapLabel.globalConnNetId) ?? 0;
|
|
3108
|
+
this.detourCounts.set(
|
|
3109
|
+
actualOverlapLabel.globalConnNetId,
|
|
3110
|
+
detourCount2 + 1
|
|
3111
|
+
);
|
|
3032
3112
|
this.activeSubSolver = new SingleOverlapSolver({
|
|
3033
3113
|
trace: traceToFix,
|
|
3034
3114
|
label: actualOverlapLabel,
|
|
@@ -3042,9 +3122,8 @@ var OverlapAvoidanceStepSolver = class extends BaseSolver {
|
|
|
3042
3122
|
}
|
|
3043
3123
|
return;
|
|
3044
3124
|
}
|
|
3045
|
-
const
|
|
3046
|
-
|
|
3047
|
-
this.detourCountByLabel[labelId] = detourCount + 1;
|
|
3125
|
+
const detourCount = this.detourCounts.get(labelToAvoid.globalConnNetId) ?? 0;
|
|
3126
|
+
this.detourCounts.set(labelToAvoid.globalConnNetId, detourCount + 1);
|
|
3048
3127
|
this.activeSubSolver = new SingleOverlapSolver({
|
|
3049
3128
|
trace: traceToFix,
|
|
3050
3129
|
label: labelToAvoid,
|
|
@@ -3057,7 +3136,8 @@ var OverlapAvoidanceStepSolver = class extends BaseSolver {
|
|
|
3057
3136
|
getOutput() {
|
|
3058
3137
|
return {
|
|
3059
3138
|
allTraces: this.allTraces,
|
|
3060
|
-
modifiedTraces: this.modifiedTraces
|
|
3139
|
+
modifiedTraces: this.modifiedTraces,
|
|
3140
|
+
detourCounts: this.detourCounts
|
|
3061
3141
|
};
|
|
3062
3142
|
}
|
|
3063
3143
|
visualize() {
|
|
@@ -3110,76 +3190,101 @@ var OverlapAvoidanceStepSolver = class extends BaseSolver {
|
|
|
3110
3190
|
// lib/solvers/TraceLabelOverlapAvoidanceSolver/TraceLabelOverlapAvoidanceSolver.ts
|
|
3111
3191
|
var TraceLabelOverlapAvoidanceSolver = class extends BaseSolver {
|
|
3112
3192
|
inputProblem;
|
|
3113
|
-
traces;
|
|
3114
3193
|
netLabelPlacements;
|
|
3115
|
-
|
|
3194
|
+
unprocessedTraces = [];
|
|
3195
|
+
cleanTraces = [];
|
|
3196
|
+
subSolvers = [];
|
|
3197
|
+
phase = "searching_for_overlaps";
|
|
3198
|
+
detourCounts = /* @__PURE__ */ new Map();
|
|
3116
3199
|
labelMergingSolver;
|
|
3117
|
-
overlapAvoidanceSolver;
|
|
3118
|
-
pipelineStepIndex = 0;
|
|
3119
3200
|
constructor(solverInput) {
|
|
3120
3201
|
super();
|
|
3121
3202
|
this.inputProblem = solverInput.inputProblem;
|
|
3122
|
-
this.
|
|
3203
|
+
this.unprocessedTraces = [...solverInput.traces];
|
|
3123
3204
|
this.netLabelPlacements = solverInput.netLabelPlacements;
|
|
3205
|
+
this.cleanTraces = [];
|
|
3206
|
+
this.subSolvers = [];
|
|
3124
3207
|
}
|
|
3125
3208
|
_step() {
|
|
3126
|
-
if (this.
|
|
3127
|
-
this.
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
this.activeSubSolver = null;
|
|
3209
|
+
if (this.phase === "searching_for_overlaps") {
|
|
3210
|
+
if (this.unprocessedTraces.length === 0) {
|
|
3211
|
+
console.log(
|
|
3212
|
+
`Dispatch phase complete. Created ${this.subSolvers.length} sub-solvers.`
|
|
3213
|
+
);
|
|
3214
|
+
this.phase = "fixing_overlaps";
|
|
3215
|
+
return;
|
|
3134
3216
|
}
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3217
|
+
const currentTargetTrace = this.unprocessedTraces.shift();
|
|
3218
|
+
const localOverlaps = detectTraceLabelOverlap({
|
|
3219
|
+
traces: [currentTargetTrace],
|
|
3220
|
+
netLabels: this.netLabelPlacements
|
|
3221
|
+
});
|
|
3222
|
+
if (localOverlaps.length === 0) {
|
|
3223
|
+
this.cleanTraces.push(currentTargetTrace);
|
|
3224
|
+
} else {
|
|
3225
|
+
const collidingLabels = localOverlaps.map((o) => o.label);
|
|
3226
|
+
const labelMerger = new MergedNetLabelObstacleSolver({
|
|
3227
|
+
netLabelPlacements: collidingLabels,
|
|
3141
3228
|
inputProblem: this.inputProblem,
|
|
3142
|
-
traces:
|
|
3229
|
+
traces: [currentTargetTrace]
|
|
3143
3230
|
});
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
this.overlapAvoidanceSolver = new OverlapAvoidanceStepSolver({
|
|
3231
|
+
labelMerger.solve();
|
|
3232
|
+
const mergingOutput = labelMerger.getOutput();
|
|
3233
|
+
const subSolver = new OverlapAvoidanceStepSolver({
|
|
3148
3234
|
inputProblem: this.inputProblem,
|
|
3149
|
-
traces:
|
|
3235
|
+
traces: [currentTargetTrace],
|
|
3150
3236
|
initialNetLabelPlacements: this.netLabelPlacements,
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3237
|
+
mergedNetLabelPlacements: mergingOutput.netLabelPlacements,
|
|
3238
|
+
mergedLabelNetIdMap: mergingOutput.mergedLabelNetIdMap,
|
|
3239
|
+
detourCounts: this.detourCounts
|
|
3154
3240
|
});
|
|
3155
|
-
this.
|
|
3156
|
-
|
|
3157
|
-
|
|
3241
|
+
this.subSolvers.push(subSolver);
|
|
3242
|
+
}
|
|
3243
|
+
} else if (this.phase === "fixing_overlaps") {
|
|
3244
|
+
if (this.subSolvers.every((s) => s.solved || s.failed)) {
|
|
3245
|
+
console.log("All sub-solvers finished.");
|
|
3246
|
+
if (!this.labelMergingSolver) {
|
|
3247
|
+
this.labelMergingSolver = new MergedNetLabelObstacleSolver({
|
|
3248
|
+
netLabelPlacements: this.netLabelPlacements,
|
|
3249
|
+
inputProblem: this.inputProblem,
|
|
3250
|
+
traces: this.getOutput().traces
|
|
3251
|
+
});
|
|
3252
|
+
this.labelMergingSolver.solve();
|
|
3253
|
+
}
|
|
3158
3254
|
this.solved = true;
|
|
3159
|
-
|
|
3255
|
+
return;
|
|
3256
|
+
}
|
|
3257
|
+
for (const solver of this.subSolvers) {
|
|
3258
|
+
if (!solver.solved && !solver.failed) {
|
|
3259
|
+
solver.step();
|
|
3260
|
+
}
|
|
3261
|
+
}
|
|
3160
3262
|
}
|
|
3161
3263
|
}
|
|
3162
3264
|
getOutput() {
|
|
3265
|
+
const solvedTraces = this.subSolvers.flatMap((s) => s.getOutput().allTraces);
|
|
3163
3266
|
return {
|
|
3164
|
-
traces: this.
|
|
3267
|
+
traces: [...this.cleanTraces, ...solvedTraces],
|
|
3165
3268
|
netLabelPlacements: this.labelMergingSolver?.getOutput().netLabelPlacements ?? this.netLabelPlacements
|
|
3166
3269
|
};
|
|
3167
3270
|
}
|
|
3168
3271
|
visualize() {
|
|
3169
|
-
if (this.activeSubSolver) {
|
|
3170
|
-
return this.activeSubSolver.visualize();
|
|
3171
|
-
}
|
|
3172
3272
|
const graphics = visualizeInputProblem(this.inputProblem);
|
|
3173
3273
|
if (!graphics.lines) graphics.lines = [];
|
|
3174
3274
|
if (!graphics.rects) graphics.rects = [];
|
|
3175
|
-
const
|
|
3176
|
-
for (const trace of output.traces) {
|
|
3275
|
+
for (const trace of this.cleanTraces) {
|
|
3177
3276
|
graphics.lines.push({
|
|
3178
3277
|
points: trace.tracePath,
|
|
3179
3278
|
strokeColor: "purple"
|
|
3180
3279
|
});
|
|
3181
3280
|
}
|
|
3182
|
-
for (const
|
|
3281
|
+
for (const solver of this.subSolvers) {
|
|
3282
|
+
const solverGraphics = solver.visualize();
|
|
3283
|
+
graphics.lines.push(...solverGraphics.lines ?? []);
|
|
3284
|
+
graphics.rects.push(...solverGraphics.rects ?? []);
|
|
3285
|
+
graphics.points.push(...solverGraphics.points ?? []);
|
|
3286
|
+
}
|
|
3287
|
+
for (const label of this.netLabelPlacements) {
|
|
3183
3288
|
const color = getColorFromString(label.globalConnNetId, 0.3);
|
|
3184
3289
|
graphics.rects.push({
|
|
3185
3290
|
center: label.center,
|
|
@@ -3328,6 +3433,7 @@ var LongDistancePairSolver = class extends BaseSolver {
|
|
|
3328
3433
|
}
|
|
3329
3434
|
this.queuedCandidatePairs = candidatePairs;
|
|
3330
3435
|
}
|
|
3436
|
+
params;
|
|
3331
3437
|
solvedLongDistanceTraces = [];
|
|
3332
3438
|
queuedCandidatePairs = [];
|
|
3333
3439
|
currentCandidatePair = null;
|
|
@@ -350,11 +350,13 @@ export class NetLabelPlacementSolver extends BaseSolver {
|
|
|
350
350
|
height: p.height,
|
|
351
351
|
fill: getColorFromString(p.globalConnNetId, 0.35),
|
|
352
352
|
strokeColor: getColorFromString(p.globalConnNetId, 0.9),
|
|
353
|
+
label: `netId: ${p.netId}\nglobalConnNetId: ${p.globalConnNetId}`,
|
|
353
354
|
} as any)
|
|
354
355
|
graphics.points!.push({
|
|
355
356
|
x: p.anchorPoint.x,
|
|
356
357
|
y: p.anchorPoint.y,
|
|
357
358
|
color: getColorFromString(p.globalConnNetId, 0.9),
|
|
359
|
+
label: `anchorPoint\norientation: ${p.orientation}`,
|
|
358
360
|
} as any)
|
|
359
361
|
}
|
|
360
362
|
|