@tscircuit/schematic-trace-solver 0.0.45 → 0.0.46
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 +29 -5
- package/dist/index.js +355 -90
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/TraceLabelOverlapAvoidanceSolver.ts +17 -3
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/detectTraceLabelOverlap.ts +26 -11
- 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 +150 -27
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/OverlapAvoidanceStepSolver/isPointInsideLabel.ts +23 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/OverlapAvoidanceStepSolver/visualizeDecomposition.ts +54 -0
- package/package.json +1 -1
- package/tests/solvers/TraceLabelOverlapAvoidanceSolver/__snapshots__/TraceLabelOverlapAvoidanceSolver.snap.svg +8 -8
package/dist/index.d.ts
CHANGED
|
@@ -396,13 +396,20 @@ interface LabelMergingSolverOutput {
|
|
|
396
396
|
}
|
|
397
397
|
/**
|
|
398
398
|
* Merges multiple net labels into a single, larger label if they are on the
|
|
399
|
-
* same side of the same chip
|
|
399
|
+
* same side of the same chip and physically adjacent.
|
|
400
400
|
*/
|
|
401
401
|
declare class MergedNetLabelObstacleSolver extends BaseSolver {
|
|
402
402
|
private input;
|
|
403
403
|
private output;
|
|
404
404
|
private inputProblem;
|
|
405
405
|
private traces;
|
|
406
|
+
private pipelineStep;
|
|
407
|
+
private filteredLabels;
|
|
408
|
+
private labelGroups;
|
|
409
|
+
private groupKeysToProcess;
|
|
410
|
+
private finalPlacements;
|
|
411
|
+
private mergedLabelNetIdMap;
|
|
412
|
+
private activeMergingGroupKey;
|
|
406
413
|
constructor(solverInput: LabelMergingSolverInput);
|
|
407
414
|
_step(): void;
|
|
408
415
|
getOutput(): LabelMergingSolverOutput;
|
|
@@ -436,7 +443,8 @@ declare class SingleOverlapSolver extends BaseSolver {
|
|
|
436
443
|
interface OverlapCollectionSolverInput {
|
|
437
444
|
inputProblem: InputProblem;
|
|
438
445
|
traces: SolvedTracePath[];
|
|
439
|
-
|
|
446
|
+
initialNetLabelPlacements: NetLabelPlacement[];
|
|
447
|
+
mergedNetLabelPlacements: NetLabelPlacement[];
|
|
440
448
|
mergedLabelNetIdMap: Record<string, Set<string>>;
|
|
441
449
|
}
|
|
442
450
|
/**
|
|
@@ -445,7 +453,8 @@ interface OverlapCollectionSolverInput {
|
|
|
445
453
|
*/
|
|
446
454
|
declare class OverlapAvoidanceStepSolver extends BaseSolver {
|
|
447
455
|
inputProblem: InputProblem;
|
|
448
|
-
|
|
456
|
+
initialNetLabelPlacements: NetLabelPlacement[];
|
|
457
|
+
mergedNetLabelPlacements: NetLabelPlacement[];
|
|
449
458
|
mergedLabelNetIdMap: Record<string, Set<string>>;
|
|
450
459
|
allTraces: SolvedTracePath[];
|
|
451
460
|
modifiedTraces: SolvedTracePath[];
|
|
@@ -454,6 +463,8 @@ declare class OverlapAvoidanceStepSolver extends BaseSolver {
|
|
|
454
463
|
activeSubSolver: SingleOverlapSolver | null;
|
|
455
464
|
private overlapQueue;
|
|
456
465
|
private recentlyFailed;
|
|
466
|
+
private currentlyProcessingOverlap;
|
|
467
|
+
private decomposedChildLabels;
|
|
457
468
|
constructor(solverInput: OverlapCollectionSolverInput);
|
|
458
469
|
_step(): void;
|
|
459
470
|
getOutput(): {
|
|
@@ -469,8 +480,21 @@ interface TraceLabelOverlapAvoidanceSolverInput {
|
|
|
469
480
|
netLabelPlacements: NetLabelPlacement[];
|
|
470
481
|
}
|
|
471
482
|
/**
|
|
472
|
-
*
|
|
473
|
-
*
|
|
483
|
+
* A pipeline solver responsible for resolving overlaps between schematic traces and net labels.
|
|
484
|
+
*
|
|
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.
|
|
492
|
+
*
|
|
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.
|
|
495
|
+
*
|
|
496
|
+
* @param {TraceLabelOverlapAvoidanceSolverInput} solverInput - The input for the solver,
|
|
497
|
+
* containing the initial traces, label placements, and the input problem definition.
|
|
474
498
|
*/
|
|
475
499
|
declare class TraceLabelOverlapAvoidanceSolver extends BaseSolver {
|
|
476
500
|
inputProblem: InputProblem;
|
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;
|
|
@@ -2738,10 +2866,45 @@ var SingleOverlapSolver = class extends BaseSolver {
|
|
|
2738
2866
|
}
|
|
2739
2867
|
};
|
|
2740
2868
|
|
|
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;
|
|
2876
|
+
};
|
|
2877
|
+
|
|
2878
|
+
// lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/OverlapAvoidanceStepSolver/visualizeDecomposition.ts
|
|
2879
|
+
var visualizeDecomposition = (params) => {
|
|
2880
|
+
const { decomposedChildLabels, collidingTrace, mergedLabel, graphics } = params;
|
|
2881
|
+
if (!graphics.rects) graphics.rects = [];
|
|
2882
|
+
if (!graphics.texts) graphics.texts = [];
|
|
2883
|
+
for (const childLabel of decomposedChildLabels) {
|
|
2884
|
+
const isOwnLabel = childLabel.globalConnNetId === collidingTrace.globalConnNetId;
|
|
2885
|
+
graphics.rects.push({
|
|
2886
|
+
center: childLabel.center,
|
|
2887
|
+
width: childLabel.width,
|
|
2888
|
+
height: childLabel.height,
|
|
2889
|
+
fill: isOwnLabel ? "green" : "red"
|
|
2890
|
+
// Green for own label, red for others
|
|
2891
|
+
});
|
|
2892
|
+
}
|
|
2893
|
+
graphics.texts.push({
|
|
2894
|
+
x: mergedLabel.center.x,
|
|
2895
|
+
y: mergedLabel.center.y + mergedLabel.height / 2 + 0.5,
|
|
2896
|
+
text: `DECOMPOSITION: Trace ${collidingTrace.mspPairId} vs Merged Label ${mergedLabel.globalConnNetId}`,
|
|
2897
|
+
fontSize: 0.3,
|
|
2898
|
+
color: "blue"
|
|
2899
|
+
});
|
|
2900
|
+
return graphics;
|
|
2901
|
+
};
|
|
2902
|
+
|
|
2741
2903
|
// lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/OverlapAvoidanceStepSolver/OverlapAvoidanceStepSolver.ts
|
|
2742
2904
|
var OverlapAvoidanceStepSolver = class extends BaseSolver {
|
|
2743
2905
|
inputProblem;
|
|
2744
|
-
|
|
2906
|
+
initialNetLabelPlacements;
|
|
2907
|
+
mergedNetLabelPlacements;
|
|
2745
2908
|
mergedLabelNetIdMap;
|
|
2746
2909
|
allTraces;
|
|
2747
2910
|
modifiedTraces = [];
|
|
@@ -2750,14 +2913,19 @@ var OverlapAvoidanceStepSolver = class extends BaseSolver {
|
|
|
2750
2913
|
activeSubSolver = null;
|
|
2751
2914
|
overlapQueue = [];
|
|
2752
2915
|
recentlyFailed = /* @__PURE__ */ new Set();
|
|
2916
|
+
currentlyProcessingOverlap = null;
|
|
2917
|
+
decomposedChildLabels = null;
|
|
2753
2918
|
constructor(solverInput) {
|
|
2754
2919
|
super();
|
|
2755
2920
|
this.inputProblem = solverInput.inputProblem;
|
|
2756
|
-
this.
|
|
2921
|
+
this.initialNetLabelPlacements = solverInput.initialNetLabelPlacements;
|
|
2922
|
+
this.mergedNetLabelPlacements = solverInput.mergedNetLabelPlacements;
|
|
2757
2923
|
this.mergedLabelNetIdMap = solverInput.mergedLabelNetIdMap;
|
|
2758
2924
|
this.allTraces = [...solverInput.traces];
|
|
2759
2925
|
}
|
|
2760
2926
|
_step() {
|
|
2927
|
+
this.currentlyProcessingOverlap = null;
|
|
2928
|
+
this.decomposedChildLabels = null;
|
|
2761
2929
|
if (this.activeSubSolver) {
|
|
2762
2930
|
this.activeSubSolver.step();
|
|
2763
2931
|
if (this.activeSubSolver.solved) {
|
|
@@ -2777,18 +2945,13 @@ var OverlapAvoidanceStepSolver = class extends BaseSolver {
|
|
|
2777
2945
|
const overlapId = `${this.activeSubSolver.initialTrace.mspPairId}-${this.activeSubSolver.label.globalConnNetId}`;
|
|
2778
2946
|
this.recentlyFailed.add(overlapId);
|
|
2779
2947
|
this.activeSubSolver = null;
|
|
2948
|
+
} else {
|
|
2780
2949
|
}
|
|
2781
2950
|
return;
|
|
2782
2951
|
}
|
|
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;
|
|
2952
|
+
const overlaps = detectTraceLabelOverlap({
|
|
2953
|
+
traces: this.allTraces,
|
|
2954
|
+
netLabels: this.mergedNetLabelPlacements
|
|
2792
2955
|
});
|
|
2793
2956
|
if (overlaps.length === 0) {
|
|
2794
2957
|
this.solved = true;
|
|
@@ -2804,22 +2967,91 @@ var OverlapAvoidanceStepSolver = class extends BaseSolver {
|
|
|
2804
2967
|
}
|
|
2805
2968
|
this.overlapQueue = nonFailedOverlaps;
|
|
2806
2969
|
const nextOverlap = this.overlapQueue.shift();
|
|
2970
|
+
this.currentlyProcessingOverlap = nextOverlap ?? null;
|
|
2807
2971
|
if (nextOverlap) {
|
|
2808
2972
|
const traceToFix = this.allTraces.find(
|
|
2809
2973
|
(t) => t.mspPairId === nextOverlap.trace.mspPairId
|
|
2810
2974
|
);
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2975
|
+
const labelToAvoid = nextOverlap.label;
|
|
2976
|
+
const traceStartPoint = traceToFix.tracePath[0];
|
|
2977
|
+
const originalNetIds = this.mergedLabelNetIdMap[labelToAvoid.globalConnNetId];
|
|
2978
|
+
const isSelfOverlap = originalNetIds?.has(traceToFix.globalConnNetId);
|
|
2979
|
+
if (isSelfOverlap) {
|
|
2980
|
+
const childLabels = this.initialNetLabelPlacements.filter(
|
|
2981
|
+
(l) => originalNetIds.has(l.globalConnNetId)
|
|
2982
|
+
);
|
|
2983
|
+
this.decomposedChildLabels = childLabels;
|
|
2984
|
+
let actualOverlapLabel = null;
|
|
2985
|
+
for (const childLabel of childLabels) {
|
|
2986
|
+
const overlapsWithChild = detectTraceLabelOverlap({
|
|
2987
|
+
traces: [traceToFix],
|
|
2988
|
+
netLabels: [childLabel]
|
|
2989
|
+
});
|
|
2990
|
+
if (overlapsWithChild.length > 0) {
|
|
2991
|
+
actualOverlapLabel = childLabel;
|
|
2992
|
+
break;
|
|
2993
|
+
}
|
|
2994
|
+
}
|
|
2995
|
+
if (actualOverlapLabel) {
|
|
2996
|
+
const labelId2 = actualOverlapLabel.globalConnNetId;
|
|
2997
|
+
const detourCount2 = this.detourCountByLabel[labelId2] || 0;
|
|
2998
|
+
this.detourCountByLabel[labelId2] = detourCount2 + 1;
|
|
2999
|
+
this.activeSubSolver = new SingleOverlapSolver({
|
|
3000
|
+
trace: traceToFix,
|
|
3001
|
+
label: actualOverlapLabel,
|
|
3002
|
+
problem: this.inputProblem,
|
|
3003
|
+
paddingBuffer: this.PADDING_BUFFER,
|
|
3004
|
+
detourCount: detourCount2
|
|
3005
|
+
});
|
|
3006
|
+
} else {
|
|
3007
|
+
const overlapId = `${traceToFix.mspPairId}-${labelToAvoid.globalConnNetId}`;
|
|
3008
|
+
this.recentlyFailed.add(overlapId);
|
|
3009
|
+
}
|
|
3010
|
+
return;
|
|
2822
3011
|
}
|
|
3012
|
+
if (originalNetIds && isPointInsideLabel({ point: traceStartPoint, label: labelToAvoid })) {
|
|
3013
|
+
const childLabels = this.initialNetLabelPlacements.filter(
|
|
3014
|
+
(l) => originalNetIds.has(l.globalConnNetId)
|
|
3015
|
+
);
|
|
3016
|
+
this.decomposedChildLabels = childLabels;
|
|
3017
|
+
let actualOverlapLabel = null;
|
|
3018
|
+
for (const childLabel of childLabels) {
|
|
3019
|
+
const overlapsWithChild = detectTraceLabelOverlap({
|
|
3020
|
+
traces: [traceToFix],
|
|
3021
|
+
netLabels: [childLabel]
|
|
3022
|
+
});
|
|
3023
|
+
if (overlapsWithChild.length > 0) {
|
|
3024
|
+
actualOverlapLabel = childLabel;
|
|
3025
|
+
break;
|
|
3026
|
+
}
|
|
3027
|
+
}
|
|
3028
|
+
if (actualOverlapLabel) {
|
|
3029
|
+
const labelId2 = actualOverlapLabel.globalConnNetId;
|
|
3030
|
+
const detourCount2 = this.detourCountByLabel[labelId2] || 0;
|
|
3031
|
+
this.detourCountByLabel[labelId2] = detourCount2 + 1;
|
|
3032
|
+
this.activeSubSolver = new SingleOverlapSolver({
|
|
3033
|
+
trace: traceToFix,
|
|
3034
|
+
label: actualOverlapLabel,
|
|
3035
|
+
problem: this.inputProblem,
|
|
3036
|
+
paddingBuffer: this.PADDING_BUFFER,
|
|
3037
|
+
detourCount: detourCount2
|
|
3038
|
+
});
|
|
3039
|
+
} else {
|
|
3040
|
+
const overlapId = `${traceToFix.mspPairId}-${labelToAvoid.globalConnNetId}`;
|
|
3041
|
+
this.recentlyFailed.add(overlapId);
|
|
3042
|
+
}
|
|
3043
|
+
return;
|
|
3044
|
+
}
|
|
3045
|
+
const labelId = labelToAvoid.globalConnNetId;
|
|
3046
|
+
const detourCount = this.detourCountByLabel[labelId] || 0;
|
|
3047
|
+
this.detourCountByLabel[labelId] = detourCount + 1;
|
|
3048
|
+
this.activeSubSolver = new SingleOverlapSolver({
|
|
3049
|
+
trace: traceToFix,
|
|
3050
|
+
label: labelToAvoid,
|
|
3051
|
+
problem: this.inputProblem,
|
|
3052
|
+
paddingBuffer: this.PADDING_BUFFER,
|
|
3053
|
+
detourCount
|
|
3054
|
+
});
|
|
2823
3055
|
}
|
|
2824
3056
|
}
|
|
2825
3057
|
getOutput() {
|
|
@@ -2840,6 +3072,37 @@ var OverlapAvoidanceStepSolver = class extends BaseSolver {
|
|
|
2840
3072
|
strokeColor: "purple"
|
|
2841
3073
|
});
|
|
2842
3074
|
}
|
|
3075
|
+
if (this.currentlyProcessingOverlap) {
|
|
3076
|
+
const { trace, label } = this.currentlyProcessingOverlap;
|
|
3077
|
+
graphics.lines.push({
|
|
3078
|
+
points: trace.tracePath,
|
|
3079
|
+
strokeColor: "red"
|
|
3080
|
+
});
|
|
3081
|
+
if (this.decomposedChildLabels) {
|
|
3082
|
+
visualizeDecomposition({
|
|
3083
|
+
decomposedChildLabels: this.decomposedChildLabels,
|
|
3084
|
+
collidingTrace: trace,
|
|
3085
|
+
mergedLabel: label,
|
|
3086
|
+
graphics
|
|
3087
|
+
});
|
|
3088
|
+
} else {
|
|
3089
|
+
if (!graphics.rects) graphics.rects = [];
|
|
3090
|
+
graphics.rects.push({
|
|
3091
|
+
center: label.center,
|
|
3092
|
+
width: label.width,
|
|
3093
|
+
height: label.height,
|
|
3094
|
+
fill: "yellow"
|
|
3095
|
+
});
|
|
3096
|
+
if (!graphics.texts) graphics.texts = [];
|
|
3097
|
+
graphics.texts.push({
|
|
3098
|
+
x: label.center.x,
|
|
3099
|
+
y: label.center.y + label.height / 2 + 0.5,
|
|
3100
|
+
text: `COLLISION: Trace ${trace.mspPairId} vs Label ${label.globalConnNetId}`,
|
|
3101
|
+
fontSize: 0.3,
|
|
3102
|
+
color: "red"
|
|
3103
|
+
});
|
|
3104
|
+
}
|
|
3105
|
+
}
|
|
2843
3106
|
return graphics;
|
|
2844
3107
|
}
|
|
2845
3108
|
};
|
|
@@ -2884,7 +3147,9 @@ var TraceLabelOverlapAvoidanceSolver = class extends BaseSolver {
|
|
|
2884
3147
|
this.overlapAvoidanceSolver = new OverlapAvoidanceStepSolver({
|
|
2885
3148
|
inputProblem: this.inputProblem,
|
|
2886
3149
|
traces: this.traces,
|
|
2887
|
-
|
|
3150
|
+
initialNetLabelPlacements: this.netLabelPlacements,
|
|
3151
|
+
// The original, unfiltered list
|
|
3152
|
+
mergedNetLabelPlacements: this.labelMergingSolver.getOutput().netLabelPlacements,
|
|
2888
3153
|
mergedLabelNetIdMap: this.labelMergingSolver.getOutput().mergedLabelNetIdMap
|
|
2889
3154
|
});
|
|
2890
3155
|
this.activeSubSolver = this.overlapAvoidanceSolver;
|
|
@@ -3006,7 +3271,7 @@ function doesTraceOverlapWithExistingTraces(newTracePath, existingTraces) {
|
|
|
3006
3271
|
|
|
3007
3272
|
// lib/solvers/LongDistancePairSolver/LongDistancePairSolver.ts
|
|
3008
3273
|
var NEAREST_NEIGHBOR_COUNT = 3;
|
|
3009
|
-
var
|
|
3274
|
+
var distance2 = (p1, p2) => {
|
|
3010
3275
|
return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));
|
|
3011
3276
|
};
|
|
3012
3277
|
var LongDistancePairSolver = class extends BaseSolver {
|
|
@@ -3047,7 +3312,7 @@ var LongDistancePairSolver = class extends BaseSolver {
|
|
|
3047
3312
|
return [
|
|
3048
3313
|
{
|
|
3049
3314
|
pin: targetPin,
|
|
3050
|
-
distance:
|
|
3315
|
+
distance: distance2(sourcePin, targetPin)
|
|
3051
3316
|
}
|
|
3052
3317
|
];
|
|
3053
3318
|
}).sort((a, b) => a.distance - b.distance).slice(0, NEAREST_NEIGHBOR_COUNT);
|