@tscircuit/schematic-trace-solver 0.0.45 → 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/.github/workflows/bun-formatcheck.yml +1 -1
- package/.github/workflows/bun-pver-release.yml +1 -1
- package/.github/workflows/bun-test.yml +1 -1
- package/.github/workflows/bun-typecheck.yml +1 -1
- package/dist/index.d.ts +35 -9
- package/dist/index.js +543 -186
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/TraceLabelOverlapAvoidanceSolver.ts +92 -50
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/detectTraceLabelOverlap.ts +26 -11
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/rerouteCollidingTrace.ts +16 -5
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/LabelMergingSolver/LabelMergingSolver.ts +95 -75
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/LabelMergingSolver/filterLabelsAtTraceEdges.ts +74 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/LabelMergingSolver/groupLabelsByChipAndOrientation.ts +45 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/LabelMergingSolver/mergeLabelGroup.ts +71 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/OverlapAvoidanceStepSolver/OverlapAvoidanceStepSolver.ts +162 -28
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/OverlapAvoidanceStepSolver/doesTraceStartOrEndInLabel.ts +58 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/OverlapAvoidanceStepSolver/isPointInsideLabel.ts +23 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/OverlapAvoidanceStepSolver/visualizeDecomposition.ts +54 -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/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__/example30.snap.svg +258 -0
- package/tests/examples/example30.test.ts +12 -0
- package/tests/solvers/TraceLabelOverlapAvoidanceSolver/__snapshots__/TraceLabelOverlapAvoidanceSolver.snap.svg +4 -1
package/dist/index.js
CHANGED
|
@@ -2260,12 +2260,134 @@ var NetLabelPlacementSolver = class extends BaseSolver {
|
|
|
2260
2260
|
}
|
|
2261
2261
|
};
|
|
2262
2262
|
|
|
2263
|
+
// lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/LabelMergingSolver/groupLabelsByChipAndOrientation.ts
|
|
2264
|
+
var groupLabelsByChipAndOrientation = ({
|
|
2265
|
+
labels,
|
|
2266
|
+
chips
|
|
2267
|
+
}) => {
|
|
2268
|
+
const groupedLabels = {};
|
|
2269
|
+
for (const label of labels) {
|
|
2270
|
+
if (label.pinIds.length === 0) {
|
|
2271
|
+
continue;
|
|
2272
|
+
}
|
|
2273
|
+
const chipId = label.pinIds[0].split(".")[0];
|
|
2274
|
+
if (!chipId) {
|
|
2275
|
+
continue;
|
|
2276
|
+
}
|
|
2277
|
+
const key = `${chipId}-${label.orientation}`;
|
|
2278
|
+
if (!groupedLabels[key]) {
|
|
2279
|
+
groupedLabels[key] = [];
|
|
2280
|
+
}
|
|
2281
|
+
groupedLabels[key].push(label);
|
|
2282
|
+
}
|
|
2283
|
+
return groupedLabels;
|
|
2284
|
+
};
|
|
2285
|
+
|
|
2286
|
+
// lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/LabelMergingSolver/mergeLabelGroup.ts
|
|
2287
|
+
var mergeLabelGroup = (group, groupKey) => {
|
|
2288
|
+
if (group.length === 0) {
|
|
2289
|
+
throw new Error("Cannot merge an empty group of labels.");
|
|
2290
|
+
}
|
|
2291
|
+
let minX = Infinity;
|
|
2292
|
+
let minY = Infinity;
|
|
2293
|
+
let maxX = -Infinity;
|
|
2294
|
+
let maxY = -Infinity;
|
|
2295
|
+
const allPinIds = /* @__PURE__ */ new Set();
|
|
2296
|
+
const allMspConnectionPairIds = /* @__PURE__ */ new Set();
|
|
2297
|
+
const originalNetIds = /* @__PURE__ */ new Set();
|
|
2298
|
+
for (const label of group) {
|
|
2299
|
+
const bounds = getRectBounds(label.center, label.width, label.height);
|
|
2300
|
+
minX = Math.min(minX, bounds.minX);
|
|
2301
|
+
minY = Math.min(minY, bounds.minY);
|
|
2302
|
+
maxX = Math.max(maxX, bounds.maxX);
|
|
2303
|
+
maxY = Math.max(maxY, bounds.maxY);
|
|
2304
|
+
label.pinIds.forEach((id) => allPinIds.add(id));
|
|
2305
|
+
label.mspConnectionPairIds.forEach((id) => allMspConnectionPairIds.add(id));
|
|
2306
|
+
originalNetIds.add(label.globalConnNetId);
|
|
2307
|
+
}
|
|
2308
|
+
const newWidth = maxX - minX;
|
|
2309
|
+
const newHeight = maxY - minY;
|
|
2310
|
+
const newCenter = { x: minX + newWidth / 2, y: minY + newHeight / 2 };
|
|
2311
|
+
const template = group[0];
|
|
2312
|
+
const syntheticId = `merged-group-${groupKey}`;
|
|
2313
|
+
const mergedLabel = {
|
|
2314
|
+
...template,
|
|
2315
|
+
// Copy common properties
|
|
2316
|
+
globalConnNetId: syntheticId,
|
|
2317
|
+
center: newCenter,
|
|
2318
|
+
width: newWidth,
|
|
2319
|
+
height: newHeight,
|
|
2320
|
+
pinIds: Array.from(allPinIds),
|
|
2321
|
+
mspConnectionPairIds: Array.from(allMspConnectionPairIds)
|
|
2322
|
+
};
|
|
2323
|
+
return {
|
|
2324
|
+
mergedLabel,
|
|
2325
|
+
originalNetIds
|
|
2326
|
+
};
|
|
2327
|
+
};
|
|
2328
|
+
|
|
2329
|
+
// lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/LabelMergingSolver/filterLabelsAtTraceEdges.ts
|
|
2330
|
+
import { distance } from "@tscircuit/math-utils";
|
|
2331
|
+
var filterLabelsAtTraceEdges = ({
|
|
2332
|
+
labels,
|
|
2333
|
+
traces,
|
|
2334
|
+
distanceThreshold = 0.5
|
|
2335
|
+
// Example threshold
|
|
2336
|
+
}) => {
|
|
2337
|
+
const tracesByNetId = /* @__PURE__ */ new Map();
|
|
2338
|
+
if (!traces) {
|
|
2339
|
+
throw new Error("No traces provided to filterLabelsAtTraceEdges");
|
|
2340
|
+
}
|
|
2341
|
+
for (const trace of traces) {
|
|
2342
|
+
if (!trace.globalConnNetId) continue;
|
|
2343
|
+
if (!tracesByNetId.has(trace.globalConnNetId)) {
|
|
2344
|
+
tracesByNetId.set(trace.globalConnNetId, []);
|
|
2345
|
+
}
|
|
2346
|
+
tracesByNetId.get(trace.globalConnNetId).push(trace);
|
|
2347
|
+
}
|
|
2348
|
+
const filteredLabels = [];
|
|
2349
|
+
for (const label of labels) {
|
|
2350
|
+
if (label.mspConnectionPairIds.length === 0) {
|
|
2351
|
+
filteredLabels.push(label);
|
|
2352
|
+
continue;
|
|
2353
|
+
}
|
|
2354
|
+
const relevantTraces = tracesByNetId.get(label.globalConnNetId);
|
|
2355
|
+
let isNearTraceEdge = false;
|
|
2356
|
+
if (!relevantTraces || relevantTraces.length === 0) {
|
|
2357
|
+
continue;
|
|
2358
|
+
}
|
|
2359
|
+
for (const trace of relevantTraces) {
|
|
2360
|
+
if (trace.tracePath.length === 0) continue;
|
|
2361
|
+
const startPoint = trace.tracePath[0];
|
|
2362
|
+
const endPoint = trace.tracePath[trace.tracePath.length - 1];
|
|
2363
|
+
const startDist = distance(label.center, startPoint);
|
|
2364
|
+
const endDist = distance(label.center, endPoint);
|
|
2365
|
+
if (startDist <= distanceThreshold || endDist <= distanceThreshold) {
|
|
2366
|
+
isNearTraceEdge = true;
|
|
2367
|
+
break;
|
|
2368
|
+
}
|
|
2369
|
+
}
|
|
2370
|
+
if (isNearTraceEdge) {
|
|
2371
|
+
filteredLabels.push(label);
|
|
2372
|
+
}
|
|
2373
|
+
}
|
|
2374
|
+
return filteredLabels;
|
|
2375
|
+
};
|
|
2376
|
+
|
|
2263
2377
|
// lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/LabelMergingSolver/LabelMergingSolver.ts
|
|
2264
2378
|
var MergedNetLabelObstacleSolver = class extends BaseSolver {
|
|
2265
2379
|
input;
|
|
2266
2380
|
output;
|
|
2267
2381
|
inputProblem;
|
|
2268
2382
|
traces;
|
|
2383
|
+
// State for the new pipeline
|
|
2384
|
+
pipelineStep = "filtering_labels";
|
|
2385
|
+
filteredLabels = [];
|
|
2386
|
+
labelGroups = {};
|
|
2387
|
+
groupKeysToProcess = [];
|
|
2388
|
+
finalPlacements = [];
|
|
2389
|
+
mergedLabelNetIdMap = {};
|
|
2390
|
+
activeMergingGroupKey = null;
|
|
2269
2391
|
constructor(solverInput) {
|
|
2270
2392
|
super();
|
|
2271
2393
|
this.input = solverInput;
|
|
@@ -2277,67 +2399,58 @@ var MergedNetLabelObstacleSolver = class extends BaseSolver {
|
|
|
2277
2399
|
};
|
|
2278
2400
|
}
|
|
2279
2401
|
_step() {
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
mspConnectionPairIds: [
|
|
2332
|
-
...new Set(group.flatMap((p) => p.mspConnectionPairIds))
|
|
2333
|
-
]
|
|
2334
|
-
});
|
|
2402
|
+
switch (this.pipelineStep) {
|
|
2403
|
+
case "filtering_labels":
|
|
2404
|
+
this.filteredLabels = filterLabelsAtTraceEdges({
|
|
2405
|
+
labels: this.input.netLabelPlacements,
|
|
2406
|
+
traces: this.traces
|
|
2407
|
+
});
|
|
2408
|
+
this.pipelineStep = "grouping_labels";
|
|
2409
|
+
break;
|
|
2410
|
+
case "grouping_labels":
|
|
2411
|
+
this.labelGroups = groupLabelsByChipAndOrientation({
|
|
2412
|
+
labels: this.filteredLabels,
|
|
2413
|
+
chips: this.inputProblem.chips
|
|
2414
|
+
});
|
|
2415
|
+
this.groupKeysToProcess = Object.keys(this.labelGroups);
|
|
2416
|
+
this.pipelineStep = "merging_groups";
|
|
2417
|
+
break;
|
|
2418
|
+
case "merging_groups":
|
|
2419
|
+
if (this.groupKeysToProcess.length === 0) {
|
|
2420
|
+
this.pipelineStep = "finalizing";
|
|
2421
|
+
this.activeMergingGroupKey = null;
|
|
2422
|
+
break;
|
|
2423
|
+
}
|
|
2424
|
+
const groupKey = this.groupKeysToProcess.pop();
|
|
2425
|
+
this.activeMergingGroupKey = groupKey;
|
|
2426
|
+
const group = this.labelGroups[groupKey];
|
|
2427
|
+
if (group.length > 1) {
|
|
2428
|
+
const { mergedLabel, originalNetIds } = mergeLabelGroup(
|
|
2429
|
+
group,
|
|
2430
|
+
groupKey
|
|
2431
|
+
);
|
|
2432
|
+
this.finalPlacements.push(mergedLabel);
|
|
2433
|
+
this.mergedLabelNetIdMap[mergedLabel.globalConnNetId] = originalNetIds;
|
|
2434
|
+
} else {
|
|
2435
|
+
this.finalPlacements.push(...group);
|
|
2436
|
+
}
|
|
2437
|
+
break;
|
|
2438
|
+
case "finalizing":
|
|
2439
|
+
const processedOriginalIds = new Set(
|
|
2440
|
+
this.finalPlacements.flatMap(
|
|
2441
|
+
(p) => this.mergedLabelNetIdMap[p.globalConnNetId] ? [...this.mergedLabelNetIdMap[p.globalConnNetId]] : [p.globalConnNetId]
|
|
2442
|
+
)
|
|
2443
|
+
);
|
|
2444
|
+
const unprocessedLabels = this.input.netLabelPlacements.filter(
|
|
2445
|
+
(l) => !processedOriginalIds.has(l.globalConnNetId)
|
|
2446
|
+
);
|
|
2447
|
+
this.output = {
|
|
2448
|
+
netLabelPlacements: [...this.finalPlacements, ...unprocessedLabels],
|
|
2449
|
+
mergedLabelNetIdMap: this.mergedLabelNetIdMap
|
|
2450
|
+
};
|
|
2451
|
+
this.solved = true;
|
|
2452
|
+
break;
|
|
2335
2453
|
}
|
|
2336
|
-
this.output = {
|
|
2337
|
-
netLabelPlacements: finalPlacements,
|
|
2338
|
-
mergedLabelNetIdMap
|
|
2339
|
-
};
|
|
2340
|
-
this.solved = true;
|
|
2341
2454
|
}
|
|
2342
2455
|
getOutput() {
|
|
2343
2456
|
return this.output;
|
|
@@ -2362,6 +2475,19 @@ var MergedNetLabelObstacleSolver = class extends BaseSolver {
|
|
|
2362
2475
|
};
|
|
2363
2476
|
graphics.lines.push(line);
|
|
2364
2477
|
}
|
|
2478
|
+
if (this.activeMergingGroupKey && this.labelGroups[this.activeMergingGroupKey]) {
|
|
2479
|
+
const activeGroup = this.labelGroups[this.activeMergingGroupKey];
|
|
2480
|
+
for (const label of activeGroup) {
|
|
2481
|
+
graphics.rects.push({
|
|
2482
|
+
center: label.center,
|
|
2483
|
+
width: label.width,
|
|
2484
|
+
height: label.height,
|
|
2485
|
+
fill: "rgba(255, 165, 0, 0.5)",
|
|
2486
|
+
// Orange highlight
|
|
2487
|
+
stroke: "orange"
|
|
2488
|
+
});
|
|
2489
|
+
}
|
|
2490
|
+
}
|
|
2365
2491
|
for (const finalLabel of this.output.netLabelPlacements) {
|
|
2366
2492
|
const isMerged = finalLabel.globalConnNetId.startsWith("merged-group-");
|
|
2367
2493
|
const color = getColorFromString(finalLabel.globalConnNetId);
|
|
@@ -2371,7 +2497,6 @@ var MergedNetLabelObstacleSolver = class extends BaseSolver {
|
|
|
2371
2497
|
width: finalLabel.width,
|
|
2372
2498
|
height: finalLabel.height,
|
|
2373
2499
|
fill: color.replace(/, 1\)/, ", 0.2)"),
|
|
2374
|
-
// semi-transparent
|
|
2375
2500
|
stroke: color,
|
|
2376
2501
|
label: finalLabel.globalConnNetId
|
|
2377
2502
|
});
|
|
@@ -2417,14 +2542,17 @@ var MergedNetLabelObstacleSolver = class extends BaseSolver {
|
|
|
2417
2542
|
};
|
|
2418
2543
|
|
|
2419
2544
|
// lib/solvers/TraceLabelOverlapAvoidanceSolver/detectTraceLabelOverlap.ts
|
|
2420
|
-
var detectTraceLabelOverlap = (
|
|
2545
|
+
var detectTraceLabelOverlap = ({
|
|
2546
|
+
traces,
|
|
2547
|
+
netLabels = []
|
|
2548
|
+
}) => {
|
|
2421
2549
|
const overlaps = [];
|
|
2422
2550
|
for (const trace of traces) {
|
|
2423
2551
|
for (const label of netLabels) {
|
|
2424
2552
|
const labelBounds = getRectBounds(label.center, label.width, label.height);
|
|
2425
|
-
for (let
|
|
2426
|
-
const p1 = trace.tracePath[
|
|
2427
|
-
const p2 = trace.tracePath[
|
|
2553
|
+
for (let j = 0; j < trace.tracePath.length - 1; j++) {
|
|
2554
|
+
const p1 = trace.tracePath[j];
|
|
2555
|
+
const p2 = trace.tracePath[j + 1];
|
|
2428
2556
|
if (segmentIntersectsRect2(p1, p2, labelBounds)) {
|
|
2429
2557
|
if (trace.globalConnNetId === label.globalConnNetId) {
|
|
2430
2558
|
break;
|
|
@@ -2521,73 +2649,106 @@ var generateFourPointDetourCandidates = ({
|
|
|
2521
2649
|
paddingBuffer,
|
|
2522
2650
|
detourCount
|
|
2523
2651
|
}) => {
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
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
|
+
}
|
|
2680
|
+
}
|
|
2681
|
+
if (firstOutsideIndex === -1) {
|
|
2682
|
+
exitIndex = initialTrace.tracePath.length - 1;
|
|
2683
|
+
} else {
|
|
2684
|
+
exitIndex = firstOutsideIndex;
|
|
2533
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];
|
|
2534
2704
|
}
|
|
2535
|
-
if (
|
|
2536
|
-
const pA = initialTrace.tracePath[collidingSegIndex];
|
|
2537
|
-
const pB = initialTrace.tracePath[collidingSegIndex + 1];
|
|
2538
|
-
if (!pA || !pB) return [];
|
|
2705
|
+
if (!entryPoint || !exitPoint || entryIndex >= exitIndex) return [];
|
|
2539
2706
|
const candidateDetours = [];
|
|
2540
|
-
const
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
const effectivePadding = paddingBuffer + detourCount * paddingBuffer;
|
|
2546
|
-
if (isVertical(pA, pB)) {
|
|
2547
|
-
const xCandidates = [
|
|
2548
|
-
paddedLabelBounds.maxX + effectivePadding,
|
|
2549
|
-
paddedLabelBounds.minX - effectivePadding
|
|
2550
|
-
];
|
|
2551
|
-
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) {
|
|
2552
2712
|
candidateDetours.push(
|
|
2553
|
-
|
|
2554
|
-
{ x:
|
|
2555
|
-
{ x:
|
|
2556
|
-
{ x:
|
|
2557
|
-
{ x:
|
|
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 }
|
|
2558
2718
|
] : [
|
|
2559
|
-
|
|
2560
|
-
{ x:
|
|
2561
|
-
{ x:
|
|
2562
|
-
{ x:
|
|
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 }
|
|
2563
2724
|
]
|
|
2564
2725
|
);
|
|
2565
2726
|
}
|
|
2566
2727
|
} else {
|
|
2567
|
-
const
|
|
2568
|
-
|
|
2569
|
-
paddedLabelBounds.minY - effectivePadding
|
|
2570
|
-
];
|
|
2571
|
-
for (const newY of yCandidates) {
|
|
2728
|
+
const xCandidates = [paddedLabelBounds.maxX, paddedLabelBounds.minX];
|
|
2729
|
+
for (const newX of xCandidates) {
|
|
2572
2730
|
candidateDetours.push(
|
|
2573
|
-
|
|
2574
|
-
{ x:
|
|
2575
|
-
{ x:
|
|
2576
|
-
{ x:
|
|
2577
|
-
{ x:
|
|
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 }
|
|
2578
2736
|
] : [
|
|
2579
|
-
|
|
2580
|
-
{ x:
|
|
2581
|
-
{ x:
|
|
2582
|
-
{ x:
|
|
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 }
|
|
2583
2742
|
]
|
|
2584
2743
|
);
|
|
2585
2744
|
}
|
|
2586
2745
|
}
|
|
2587
2746
|
return candidateDetours.map((detourPoints) => [
|
|
2588
|
-
...initialTrace.tracePath.slice(0,
|
|
2747
|
+
...initialTrace.tracePath.slice(0, entryIndex),
|
|
2748
|
+
entryPoint,
|
|
2589
2749
|
...detourPoints,
|
|
2590
|
-
|
|
2750
|
+
exitPoint,
|
|
2751
|
+
...initialTrace.tracePath.slice(exitIndex + 1)
|
|
2591
2752
|
]);
|
|
2592
2753
|
};
|
|
2593
2754
|
|
|
@@ -2631,13 +2792,12 @@ var generateRerouteCandidates = ({
|
|
|
2631
2792
|
if (trace.globalConnNetId === label.globalConnNetId) {
|
|
2632
2793
|
return [initialTrace.tracePath];
|
|
2633
2794
|
}
|
|
2634
|
-
const labelPadding = paddingBuffer;
|
|
2635
2795
|
const labelBoundsRaw = getRectBounds(label.center, label.width, label.height);
|
|
2636
2796
|
const labelBounds = {
|
|
2637
|
-
minX: labelBoundsRaw.minX
|
|
2638
|
-
minY: labelBoundsRaw.minY
|
|
2639
|
-
maxX: labelBoundsRaw.maxX
|
|
2640
|
-
maxY: labelBoundsRaw.maxY
|
|
2797
|
+
minX: labelBoundsRaw.minX,
|
|
2798
|
+
minY: labelBoundsRaw.minY,
|
|
2799
|
+
maxX: labelBoundsRaw.maxX,
|
|
2800
|
+
maxY: labelBoundsRaw.maxY,
|
|
2641
2801
|
chipId: `netlabel-${label.netId}`
|
|
2642
2802
|
};
|
|
2643
2803
|
const fourPointCandidates = generateFourPointDetourCandidates({
|
|
@@ -2663,6 +2823,7 @@ var generateRerouteCandidates = ({
|
|
|
2663
2823
|
};
|
|
2664
2824
|
|
|
2665
2825
|
// lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/SingleOverlapSolver/SingleOverlapSolver.ts
|
|
2826
|
+
var MAX_TRIES = 5;
|
|
2666
2827
|
var SingleOverlapSolver = class extends BaseSolver {
|
|
2667
2828
|
queuedCandidatePaths;
|
|
2668
2829
|
solvedTracePath = null;
|
|
@@ -2670,13 +2831,17 @@ var SingleOverlapSolver = class extends BaseSolver {
|
|
|
2670
2831
|
problem;
|
|
2671
2832
|
obstacles;
|
|
2672
2833
|
label;
|
|
2834
|
+
_tried = 0;
|
|
2673
2835
|
constructor(solverInput) {
|
|
2674
2836
|
super();
|
|
2675
2837
|
this.initialTrace = solverInput.trace;
|
|
2676
2838
|
this.problem = solverInput.problem;
|
|
2677
2839
|
this.label = solverInput.label;
|
|
2840
|
+
const effectivePadding = solverInput.paddingBuffer + solverInput.detourCount * solverInput.paddingBuffer;
|
|
2678
2841
|
const candidates = generateRerouteCandidates({
|
|
2679
|
-
...solverInput
|
|
2842
|
+
...solverInput,
|
|
2843
|
+
paddingBuffer: effectivePadding
|
|
2844
|
+
// Use the calculated, larger padding
|
|
2680
2845
|
});
|
|
2681
2846
|
const getPathLength = (pts) => {
|
|
2682
2847
|
let len = 0;
|
|
@@ -2693,10 +2858,11 @@ var SingleOverlapSolver = class extends BaseSolver {
|
|
|
2693
2858
|
this.obstacles = getObstacleRects(this.problem);
|
|
2694
2859
|
}
|
|
2695
2860
|
_step() {
|
|
2696
|
-
if (this.queuedCandidatePaths.length === 0) {
|
|
2861
|
+
if (this.queuedCandidatePaths.length === 0 || this._tried >= MAX_TRIES) {
|
|
2697
2862
|
this.failed = true;
|
|
2698
2863
|
return;
|
|
2699
2864
|
}
|
|
2865
|
+
this._tried++;
|
|
2700
2866
|
const nextCandidatePath = this.queuedCandidatePaths.shift();
|
|
2701
2867
|
const simplifiedPath = simplifyPath(nextCandidatePath);
|
|
2702
2868
|
if (!isPathCollidingWithObstacles(simplifiedPath, this.obstacles)) {
|
|
@@ -2738,26 +2904,92 @@ var SingleOverlapSolver = class extends BaseSolver {
|
|
|
2738
2904
|
}
|
|
2739
2905
|
};
|
|
2740
2906
|
|
|
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;
|
|
2939
|
+
};
|
|
2940
|
+
|
|
2941
|
+
// lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/OverlapAvoidanceStepSolver/visualizeDecomposition.ts
|
|
2942
|
+
var visualizeDecomposition = (params) => {
|
|
2943
|
+
const { decomposedChildLabels, collidingTrace, mergedLabel, graphics } = params;
|
|
2944
|
+
if (!graphics.rects) graphics.rects = [];
|
|
2945
|
+
if (!graphics.texts) graphics.texts = [];
|
|
2946
|
+
for (const childLabel of decomposedChildLabels) {
|
|
2947
|
+
const isOwnLabel = childLabel.globalConnNetId === collidingTrace.globalConnNetId;
|
|
2948
|
+
graphics.rects.push({
|
|
2949
|
+
center: childLabel.center,
|
|
2950
|
+
width: childLabel.width,
|
|
2951
|
+
height: childLabel.height,
|
|
2952
|
+
fill: isOwnLabel ? "green" : "red"
|
|
2953
|
+
// Green for own label, red for others
|
|
2954
|
+
});
|
|
2955
|
+
}
|
|
2956
|
+
graphics.texts.push({
|
|
2957
|
+
x: mergedLabel.center.x,
|
|
2958
|
+
y: mergedLabel.center.y + mergedLabel.height / 2 + 0.5,
|
|
2959
|
+
text: `DECOMPOSITION: Trace ${collidingTrace.mspPairId} vs Merged Label ${mergedLabel.globalConnNetId}`,
|
|
2960
|
+
fontSize: 0.3,
|
|
2961
|
+
color: "blue"
|
|
2962
|
+
});
|
|
2963
|
+
return graphics;
|
|
2964
|
+
};
|
|
2965
|
+
|
|
2741
2966
|
// lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/OverlapAvoidanceStepSolver/OverlapAvoidanceStepSolver.ts
|
|
2742
2967
|
var OverlapAvoidanceStepSolver = class extends BaseSolver {
|
|
2743
2968
|
inputProblem;
|
|
2744
|
-
|
|
2969
|
+
initialNetLabelPlacements;
|
|
2970
|
+
mergedNetLabelPlacements;
|
|
2745
2971
|
mergedLabelNetIdMap;
|
|
2746
2972
|
allTraces;
|
|
2747
2973
|
modifiedTraces = [];
|
|
2748
|
-
detourCountByLabel = {};
|
|
2749
2974
|
PADDING_BUFFER = 0.1;
|
|
2975
|
+
detourCounts = /* @__PURE__ */ new Map();
|
|
2750
2976
|
activeSubSolver = null;
|
|
2751
2977
|
overlapQueue = [];
|
|
2752
2978
|
recentlyFailed = /* @__PURE__ */ new Set();
|
|
2979
|
+
currentlyProcessingOverlap = null;
|
|
2980
|
+
decomposedChildLabels = null;
|
|
2753
2981
|
constructor(solverInput) {
|
|
2754
2982
|
super();
|
|
2755
2983
|
this.inputProblem = solverInput.inputProblem;
|
|
2756
|
-
this.
|
|
2984
|
+
this.initialNetLabelPlacements = solverInput.initialNetLabelPlacements;
|
|
2985
|
+
this.mergedNetLabelPlacements = solverInput.mergedNetLabelPlacements;
|
|
2757
2986
|
this.mergedLabelNetIdMap = solverInput.mergedLabelNetIdMap;
|
|
2758
2987
|
this.allTraces = [...solverInput.traces];
|
|
2988
|
+
this.detourCounts = solverInput.detourCounts;
|
|
2759
2989
|
}
|
|
2760
2990
|
_step() {
|
|
2991
|
+
this.currentlyProcessingOverlap = null;
|
|
2992
|
+
this.decomposedChildLabels = null;
|
|
2761
2993
|
if (this.activeSubSolver) {
|
|
2762
2994
|
this.activeSubSolver.step();
|
|
2763
2995
|
if (this.activeSubSolver.solved) {
|
|
@@ -2777,18 +3009,13 @@ var OverlapAvoidanceStepSolver = class extends BaseSolver {
|
|
|
2777
3009
|
const overlapId = `${this.activeSubSolver.initialTrace.mspPairId}-${this.activeSubSolver.label.globalConnNetId}`;
|
|
2778
3010
|
this.recentlyFailed.add(overlapId);
|
|
2779
3011
|
this.activeSubSolver = null;
|
|
3012
|
+
} else {
|
|
2780
3013
|
}
|
|
2781
3014
|
return;
|
|
2782
3015
|
}
|
|
2783
|
-
const overlaps = detectTraceLabelOverlap(
|
|
2784
|
-
this.allTraces,
|
|
2785
|
-
this.
|
|
2786
|
-
).filter((o) => {
|
|
2787
|
-
const originalNetIds = this.mergedLabelNetIdMap[o.label.globalConnNetId];
|
|
2788
|
-
if (originalNetIds) {
|
|
2789
|
-
return !originalNetIds.has(o.trace.globalConnNetId);
|
|
2790
|
-
}
|
|
2791
|
-
return o.trace.globalConnNetId !== o.label.globalConnNetId;
|
|
3016
|
+
const overlaps = detectTraceLabelOverlap({
|
|
3017
|
+
traces: this.allTraces,
|
|
3018
|
+
netLabels: this.mergedNetLabelPlacements
|
|
2792
3019
|
});
|
|
2793
3020
|
if (overlaps.length === 0) {
|
|
2794
3021
|
this.solved = true;
|
|
@@ -2804,28 +3031,100 @@ var OverlapAvoidanceStepSolver = class extends BaseSolver {
|
|
|
2804
3031
|
}
|
|
2805
3032
|
this.overlapQueue = nonFailedOverlaps;
|
|
2806
3033
|
const nextOverlap = this.overlapQueue.shift();
|
|
3034
|
+
this.currentlyProcessingOverlap = nextOverlap ?? null;
|
|
2807
3035
|
if (nextOverlap) {
|
|
2808
3036
|
const traceToFix = this.allTraces.find(
|
|
2809
3037
|
(t) => t.mspPairId === nextOverlap.trace.mspPairId
|
|
2810
3038
|
);
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
3039
|
+
const labelToAvoid = nextOverlap.label;
|
|
3040
|
+
const originalNetIds = this.mergedLabelNetIdMap[labelToAvoid.globalConnNetId];
|
|
3041
|
+
const isSelfOverlap = originalNetIds?.has(traceToFix.globalConnNetId);
|
|
3042
|
+
if (isSelfOverlap) {
|
|
3043
|
+
const childLabels = this.initialNetLabelPlacements.filter(
|
|
3044
|
+
(l) => originalNetIds.has(l.globalConnNetId)
|
|
3045
|
+
);
|
|
3046
|
+
this.decomposedChildLabels = childLabels;
|
|
3047
|
+
let actualOverlapLabel = null;
|
|
3048
|
+
for (const childLabel of childLabels) {
|
|
3049
|
+
const overlapsWithChild = detectTraceLabelOverlap({
|
|
3050
|
+
traces: [traceToFix],
|
|
3051
|
+
netLabels: [childLabel]
|
|
3052
|
+
});
|
|
3053
|
+
if (overlapsWithChild.length > 0) {
|
|
3054
|
+
actualOverlapLabel = childLabel;
|
|
3055
|
+
break;
|
|
3056
|
+
}
|
|
3057
|
+
}
|
|
3058
|
+
if (actualOverlapLabel) {
|
|
3059
|
+
const detourCount2 = this.detourCounts.get(actualOverlapLabel.globalConnNetId) ?? 0;
|
|
3060
|
+
this.detourCounts.set(
|
|
3061
|
+
actualOverlapLabel.globalConnNetId,
|
|
3062
|
+
detourCount2 + 1
|
|
3063
|
+
);
|
|
3064
|
+
this.activeSubSolver = new SingleOverlapSolver({
|
|
3065
|
+
trace: traceToFix,
|
|
3066
|
+
label: actualOverlapLabel,
|
|
3067
|
+
problem: this.inputProblem,
|
|
3068
|
+
paddingBuffer: this.PADDING_BUFFER,
|
|
3069
|
+
detourCount: detourCount2
|
|
3070
|
+
});
|
|
3071
|
+
} else {
|
|
3072
|
+
const overlapId = `${traceToFix.mspPairId}-${labelToAvoid.globalConnNetId}`;
|
|
3073
|
+
this.recentlyFailed.add(overlapId);
|
|
3074
|
+
}
|
|
3075
|
+
return;
|
|
3076
|
+
}
|
|
3077
|
+
if (originalNetIds && doesTraceStartOrEndInLabel({ trace: traceToFix, label: labelToAvoid })) {
|
|
3078
|
+
const childLabels = this.initialNetLabelPlacements.filter(
|
|
3079
|
+
(l) => originalNetIds.has(l.globalConnNetId)
|
|
3080
|
+
);
|
|
3081
|
+
this.decomposedChildLabels = childLabels;
|
|
3082
|
+
let actualOverlapLabel = null;
|
|
3083
|
+
for (const childLabel of childLabels) {
|
|
3084
|
+
const overlapsWithChild = detectTraceLabelOverlap({
|
|
3085
|
+
traces: [traceToFix],
|
|
3086
|
+
netLabels: [childLabel]
|
|
3087
|
+
});
|
|
3088
|
+
if (overlapsWithChild.length > 0) {
|
|
3089
|
+
actualOverlapLabel = childLabel;
|
|
3090
|
+
break;
|
|
3091
|
+
}
|
|
3092
|
+
}
|
|
3093
|
+
if (actualOverlapLabel) {
|
|
3094
|
+
const detourCount2 = this.detourCounts.get(actualOverlapLabel.globalConnNetId) ?? 0;
|
|
3095
|
+
this.detourCounts.set(
|
|
3096
|
+
actualOverlapLabel.globalConnNetId,
|
|
3097
|
+
detourCount2 + 1
|
|
3098
|
+
);
|
|
3099
|
+
this.activeSubSolver = new SingleOverlapSolver({
|
|
3100
|
+
trace: traceToFix,
|
|
3101
|
+
label: actualOverlapLabel,
|
|
3102
|
+
problem: this.inputProblem,
|
|
3103
|
+
paddingBuffer: this.PADDING_BUFFER,
|
|
3104
|
+
detourCount: detourCount2
|
|
3105
|
+
});
|
|
3106
|
+
} else {
|
|
3107
|
+
const overlapId = `${traceToFix.mspPairId}-${labelToAvoid.globalConnNetId}`;
|
|
3108
|
+
this.recentlyFailed.add(overlapId);
|
|
3109
|
+
}
|
|
3110
|
+
return;
|
|
2822
3111
|
}
|
|
3112
|
+
const detourCount = this.detourCounts.get(labelToAvoid.globalConnNetId) ?? 0;
|
|
3113
|
+
this.detourCounts.set(labelToAvoid.globalConnNetId, detourCount + 1);
|
|
3114
|
+
this.activeSubSolver = new SingleOverlapSolver({
|
|
3115
|
+
trace: traceToFix,
|
|
3116
|
+
label: labelToAvoid,
|
|
3117
|
+
problem: this.inputProblem,
|
|
3118
|
+
paddingBuffer: this.PADDING_BUFFER,
|
|
3119
|
+
detourCount
|
|
3120
|
+
});
|
|
2823
3121
|
}
|
|
2824
3122
|
}
|
|
2825
3123
|
getOutput() {
|
|
2826
3124
|
return {
|
|
2827
3125
|
allTraces: this.allTraces,
|
|
2828
|
-
modifiedTraces: this.modifiedTraces
|
|
3126
|
+
modifiedTraces: this.modifiedTraces,
|
|
3127
|
+
detourCounts: this.detourCounts
|
|
2829
3128
|
};
|
|
2830
3129
|
}
|
|
2831
3130
|
visualize() {
|
|
@@ -2840,6 +3139,37 @@ var OverlapAvoidanceStepSolver = class extends BaseSolver {
|
|
|
2840
3139
|
strokeColor: "purple"
|
|
2841
3140
|
});
|
|
2842
3141
|
}
|
|
3142
|
+
if (this.currentlyProcessingOverlap) {
|
|
3143
|
+
const { trace, label } = this.currentlyProcessingOverlap;
|
|
3144
|
+
graphics.lines.push({
|
|
3145
|
+
points: trace.tracePath,
|
|
3146
|
+
strokeColor: "red"
|
|
3147
|
+
});
|
|
3148
|
+
if (this.decomposedChildLabels) {
|
|
3149
|
+
visualizeDecomposition({
|
|
3150
|
+
decomposedChildLabels: this.decomposedChildLabels,
|
|
3151
|
+
collidingTrace: trace,
|
|
3152
|
+
mergedLabel: label,
|
|
3153
|
+
graphics
|
|
3154
|
+
});
|
|
3155
|
+
} else {
|
|
3156
|
+
if (!graphics.rects) graphics.rects = [];
|
|
3157
|
+
graphics.rects.push({
|
|
3158
|
+
center: label.center,
|
|
3159
|
+
width: label.width,
|
|
3160
|
+
height: label.height,
|
|
3161
|
+
fill: "yellow"
|
|
3162
|
+
});
|
|
3163
|
+
if (!graphics.texts) graphics.texts = [];
|
|
3164
|
+
graphics.texts.push({
|
|
3165
|
+
x: label.center.x,
|
|
3166
|
+
y: label.center.y + label.height / 2 + 0.5,
|
|
3167
|
+
text: `COLLISION: Trace ${trace.mspPairId} vs Label ${label.globalConnNetId}`,
|
|
3168
|
+
fontSize: 0.3,
|
|
3169
|
+
color: "red"
|
|
3170
|
+
});
|
|
3171
|
+
}
|
|
3172
|
+
}
|
|
2843
3173
|
return graphics;
|
|
2844
3174
|
}
|
|
2845
3175
|
};
|
|
@@ -2847,74 +3177,101 @@ var OverlapAvoidanceStepSolver = class extends BaseSolver {
|
|
|
2847
3177
|
// lib/solvers/TraceLabelOverlapAvoidanceSolver/TraceLabelOverlapAvoidanceSolver.ts
|
|
2848
3178
|
var TraceLabelOverlapAvoidanceSolver = class extends BaseSolver {
|
|
2849
3179
|
inputProblem;
|
|
2850
|
-
traces;
|
|
2851
3180
|
netLabelPlacements;
|
|
2852
|
-
|
|
3181
|
+
unprocessedTraces = [];
|
|
3182
|
+
cleanTraces = [];
|
|
3183
|
+
subSolvers = [];
|
|
3184
|
+
phase = "searching_for_overlaps";
|
|
3185
|
+
detourCounts = /* @__PURE__ */ new Map();
|
|
2853
3186
|
labelMergingSolver;
|
|
2854
|
-
overlapAvoidanceSolver;
|
|
2855
|
-
pipelineStepIndex = 0;
|
|
2856
3187
|
constructor(solverInput) {
|
|
2857
3188
|
super();
|
|
2858
3189
|
this.inputProblem = solverInput.inputProblem;
|
|
2859
|
-
this.
|
|
3190
|
+
this.unprocessedTraces = [...solverInput.traces];
|
|
2860
3191
|
this.netLabelPlacements = solverInput.netLabelPlacements;
|
|
3192
|
+
this.cleanTraces = [];
|
|
3193
|
+
this.subSolvers = [];
|
|
2861
3194
|
}
|
|
2862
3195
|
_step() {
|
|
2863
|
-
if (this.
|
|
2864
|
-
this.
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
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;
|
|
2871
3203
|
}
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
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,
|
|
2878
3215
|
inputProblem: this.inputProblem,
|
|
2879
|
-
traces:
|
|
3216
|
+
traces: [currentTargetTrace]
|
|
2880
3217
|
});
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
this.overlapAvoidanceSolver = new OverlapAvoidanceStepSolver({
|
|
3218
|
+
labelMerger.solve();
|
|
3219
|
+
const mergingOutput = labelMerger.getOutput();
|
|
3220
|
+
const subSolver = new OverlapAvoidanceStepSolver({
|
|
2885
3221
|
inputProblem: this.inputProblem,
|
|
2886
|
-
traces:
|
|
2887
|
-
|
|
2888
|
-
|
|
3222
|
+
traces: [currentTargetTrace],
|
|
3223
|
+
initialNetLabelPlacements: this.netLabelPlacements,
|
|
3224
|
+
mergedNetLabelPlacements: mergingOutput.netLabelPlacements,
|
|
3225
|
+
mergedLabelNetIdMap: mergingOutput.mergedLabelNetIdMap,
|
|
3226
|
+
detourCounts: this.detourCounts
|
|
2889
3227
|
});
|
|
2890
|
-
this.
|
|
2891
|
-
|
|
2892
|
-
|
|
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
|
+
}
|
|
2893
3241
|
this.solved = true;
|
|
2894
|
-
|
|
3242
|
+
return;
|
|
3243
|
+
}
|
|
3244
|
+
for (const solver of this.subSolvers) {
|
|
3245
|
+
if (!solver.solved && !solver.failed) {
|
|
3246
|
+
solver.step();
|
|
3247
|
+
}
|
|
3248
|
+
}
|
|
2895
3249
|
}
|
|
2896
3250
|
}
|
|
2897
3251
|
getOutput() {
|
|
3252
|
+
const solvedTraces = this.subSolvers.flatMap((s) => s.getOutput().allTraces);
|
|
2898
3253
|
return {
|
|
2899
|
-
traces: this.
|
|
3254
|
+
traces: [...this.cleanTraces, ...solvedTraces],
|
|
2900
3255
|
netLabelPlacements: this.labelMergingSolver?.getOutput().netLabelPlacements ?? this.netLabelPlacements
|
|
2901
3256
|
};
|
|
2902
3257
|
}
|
|
2903
3258
|
visualize() {
|
|
2904
|
-
if (this.activeSubSolver) {
|
|
2905
|
-
return this.activeSubSolver.visualize();
|
|
2906
|
-
}
|
|
2907
3259
|
const graphics = visualizeInputProblem(this.inputProblem);
|
|
2908
3260
|
if (!graphics.lines) graphics.lines = [];
|
|
2909
3261
|
if (!graphics.rects) graphics.rects = [];
|
|
2910
|
-
const
|
|
2911
|
-
for (const trace of output.traces) {
|
|
3262
|
+
for (const trace of this.cleanTraces) {
|
|
2912
3263
|
graphics.lines.push({
|
|
2913
3264
|
points: trace.tracePath,
|
|
2914
3265
|
strokeColor: "purple"
|
|
2915
3266
|
});
|
|
2916
3267
|
}
|
|
2917
|
-
for (const
|
|
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) {
|
|
2918
3275
|
const color = getColorFromString(label.globalConnNetId, 0.3);
|
|
2919
3276
|
graphics.rects.push({
|
|
2920
3277
|
center: label.center,
|
|
@@ -3006,7 +3363,7 @@ function doesTraceOverlapWithExistingTraces(newTracePath, existingTraces) {
|
|
|
3006
3363
|
|
|
3007
3364
|
// lib/solvers/LongDistancePairSolver/LongDistancePairSolver.ts
|
|
3008
3365
|
var NEAREST_NEIGHBOR_COUNT = 3;
|
|
3009
|
-
var
|
|
3366
|
+
var distance2 = (p1, p2) => {
|
|
3010
3367
|
return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));
|
|
3011
3368
|
};
|
|
3012
3369
|
var LongDistancePairSolver = class extends BaseSolver {
|
|
@@ -3047,7 +3404,7 @@ var LongDistancePairSolver = class extends BaseSolver {
|
|
|
3047
3404
|
return [
|
|
3048
3405
|
{
|
|
3049
3406
|
pin: targetPin,
|
|
3050
|
-
distance:
|
|
3407
|
+
distance: distance2(sourcePin, targetPin)
|
|
3051
3408
|
}
|
|
3052
3409
|
];
|
|
3053
3410
|
}).sort((a, b) => a.distance - b.distance).slice(0, NEAREST_NEIGHBOR_COUNT);
|