@tscircuit/schematic-trace-solver 0.0.42 → 0.0.44
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 +31 -29
- package/dist/index.js +981 -926
- package/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts +20 -1
- package/lib/solvers/TraceCleanupSolver/TraceCleanupSolver.ts +133 -0
- package/lib/solvers/TraceCleanupSolver/balanceZShapes.ts +200 -0
- package/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts +76 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/TraceLabelOverlapAvoidanceSolver.ts +1 -21
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/rerouteCollidingTrace.ts +1 -1
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/SingleOverlapSolver/SingleOverlapSolver.ts +1 -1
- package/lib/solvers/TraceOverlapShiftSolver/TraceOverlapShiftSolver.ts +83 -0
- package/package.json +1 -1
- package/site/examples/example26.page.tsx +4 -0
- package/tests/assets/example26.json +1206 -0
- package/tests/examples/__snapshots__/example02.snap.svg +1 -1
- package/tests/examples/__snapshots__/example09.snap.svg +34 -34
- package/tests/examples/__snapshots__/example11.snap.svg +13 -13
- package/tests/examples/__snapshots__/example12.snap.svg +3 -3
- package/tests/examples/__snapshots__/example13.snap.svg +12 -12
- package/tests/examples/__snapshots__/example16.snap.svg +4 -4
- package/tests/examples/__snapshots__/example19.snap.svg +9 -9
- package/tests/examples/__snapshots__/example25.snap.svg +3 -3
- package/tests/examples/__snapshots__/example26.snap.svg +3 -3
- package/tests/examples/__snapshots__/example28.snap.svg +3 -3
- package/tests/examples/__snapshots__/example29.snap.svg +1208 -0
- package/tests/examples/example29.test.ts +13 -0
- package/tests/solvers/{TraceLabelOverlapAvoidanceSolver/sub-solver → TraceCleanupSolver}/TraceCleanupSolver.test.ts +2 -2
- package/tests/solvers/{TraceLabelOverlapAvoidanceSolver/sub-solver → TraceCleanupSolver}/__snapshots__/TraceCleanupSolver.snap.svg +1 -1
- package/tests/solvers/TraceLabelOverlapAvoidanceSolver/renderComparisonView/__snapshots__/renderComparisonView03.snap.svg +1 -1
- package/tests/solvers/TraceLabelOverlapAvoidanceSolver/renderComparisonView/renderComparisonView03.test.ts +1 -1
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver/TraceCleanupSolver.ts +0 -132
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver/balanceLShapes.ts +0 -209
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts +0 -66
- /package/lib/solvers/{TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver → TraceCleanupSolver}/countTurns.ts +0 -0
- /package/lib/solvers/{TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver → TraceCleanupSolver}/hasCollisions.ts +0 -0
- /package/lib/solvers/{TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver → TraceCleanupSolver}/hasCollisionsWithLabels.ts +0 -0
- /package/lib/solvers/{TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver → TraceCleanupSolver}/simplifyPath.ts +0 -0
- /package/lib/solvers/{TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver → TraceCleanupSolver}/tryConnectPoints.ts +0 -0
- /package/lib/solvers/{TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver → TraceCleanupSolver}/turnMinimization.ts +0 -0
package/dist/index.js
CHANGED
|
@@ -1188,6 +1188,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
1188
1188
|
*/
|
|
1189
1189
|
traceNetIslands = {};
|
|
1190
1190
|
correctedTraceMap = {};
|
|
1191
|
+
cleanupPhase = null;
|
|
1191
1192
|
constructor(params) {
|
|
1192
1193
|
super();
|
|
1193
1194
|
this.inputProblem = params.inputProblem;
|
|
@@ -1314,6 +1315,47 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
1314
1315
|
}
|
|
1315
1316
|
return null;
|
|
1316
1317
|
}
|
|
1318
|
+
findNextDiagonalSegment() {
|
|
1319
|
+
const EPS4 = 2e-3;
|
|
1320
|
+
for (const mspPairId in this.correctedTraceMap) {
|
|
1321
|
+
const tracePath = this.correctedTraceMap[mspPairId].tracePath;
|
|
1322
|
+
for (let i = 0; i < tracePath.length - 1; i++) {
|
|
1323
|
+
const p1 = tracePath[i];
|
|
1324
|
+
const p2 = tracePath[i + 1];
|
|
1325
|
+
const isHorizontal2 = Math.abs(p1.y - p2.y) < EPS4;
|
|
1326
|
+
const isVertical2 = Math.abs(p1.x - p2.x) < EPS4;
|
|
1327
|
+
if (!isHorizontal2 && !isVertical2) {
|
|
1328
|
+
return { mspPairId, tracePath, i, p1, p2 };
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
return null;
|
|
1333
|
+
}
|
|
1334
|
+
findAndFixNextDiagonalSegment() {
|
|
1335
|
+
const diagonalInfo = this.findNextDiagonalSegment();
|
|
1336
|
+
if (!diagonalInfo) {
|
|
1337
|
+
return false;
|
|
1338
|
+
}
|
|
1339
|
+
const { mspPairId, tracePath, i, p1, p2 } = diagonalInfo;
|
|
1340
|
+
const EPS4 = 2e-3;
|
|
1341
|
+
const p0 = i > 0 ? tracePath[i - 1] : null;
|
|
1342
|
+
const p3 = i + 2 < tracePath.length ? tracePath[i + 2] : null;
|
|
1343
|
+
const prevIsVertical = p0 ? Math.abs(p0.x - p1.x) < EPS4 : false;
|
|
1344
|
+
const prevIsHorizontal = p0 ? Math.abs(p0.y - p1.y) < EPS4 : false;
|
|
1345
|
+
const nextIsVertical = p3 ? Math.abs(p2.x - p3.x) < EPS4 : false;
|
|
1346
|
+
const nextIsHorizontal = p3 ? Math.abs(p2.y - p3.y) < EPS4 : false;
|
|
1347
|
+
const elbow1 = { x: p1.x, y: p2.y };
|
|
1348
|
+
const elbow2 = { x: p2.x, y: p1.y };
|
|
1349
|
+
let score1 = 0;
|
|
1350
|
+
if (prevIsVertical) score1++;
|
|
1351
|
+
if (nextIsHorizontal) score1++;
|
|
1352
|
+
let score2 = 0;
|
|
1353
|
+
if (prevIsHorizontal) score2++;
|
|
1354
|
+
if (nextIsVertical) score2++;
|
|
1355
|
+
const elbowPoint = score1 < score2 ? elbow1 : elbow2;
|
|
1356
|
+
tracePath.splice(i + 1, 0, elbowPoint);
|
|
1357
|
+
return true;
|
|
1358
|
+
}
|
|
1317
1359
|
_step() {
|
|
1318
1360
|
if (this.activeSubSolver?.solved) {
|
|
1319
1361
|
for (const [mspPairId, newTrace] of Object.entries(
|
|
@@ -1330,6 +1372,17 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
1330
1372
|
}
|
|
1331
1373
|
const overlapIssue = this.findNextOverlapIssue();
|
|
1332
1374
|
if (overlapIssue === null) {
|
|
1375
|
+
if (this.cleanupPhase === null) {
|
|
1376
|
+
this.cleanupPhase = "diagonals";
|
|
1377
|
+
}
|
|
1378
|
+
if (this.cleanupPhase === "diagonals") {
|
|
1379
|
+
const fixedDiagonal = this.findAndFixNextDiagonalSegment();
|
|
1380
|
+
if (!fixedDiagonal) {
|
|
1381
|
+
this.cleanupPhase = "done";
|
|
1382
|
+
this.solved = true;
|
|
1383
|
+
}
|
|
1384
|
+
return;
|
|
1385
|
+
}
|
|
1333
1386
|
this.solved = true;
|
|
1334
1387
|
return;
|
|
1335
1388
|
}
|
|
@@ -1344,12 +1397,23 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
1344
1397
|
return this.activeSubSolver.visualize();
|
|
1345
1398
|
}
|
|
1346
1399
|
const graphics = visualizeInputProblem(this.inputProblem);
|
|
1400
|
+
graphics.circles = graphics.circles || [];
|
|
1347
1401
|
for (const trace of Object.values(this.correctedTraceMap)) {
|
|
1348
1402
|
graphics.lines.push({
|
|
1349
1403
|
points: trace.tracePath,
|
|
1350
1404
|
strokeColor: "purple"
|
|
1351
1405
|
});
|
|
1352
1406
|
}
|
|
1407
|
+
if (this.cleanupPhase === "diagonals") {
|
|
1408
|
+
const diagonalInfo = this.findNextDiagonalSegment();
|
|
1409
|
+
if (diagonalInfo) {
|
|
1410
|
+
graphics.lines.push({
|
|
1411
|
+
points: [diagonalInfo.p1, diagonalInfo.p2],
|
|
1412
|
+
strokeColor: "red",
|
|
1413
|
+
strokeWidth: 0.05
|
|
1414
|
+
});
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1353
1417
|
return graphics;
|
|
1354
1418
|
}
|
|
1355
1419
|
};
|
|
@@ -2352,37 +2416,182 @@ var MergedNetLabelObstacleSolver = class extends BaseSolver {
|
|
|
2352
2416
|
}
|
|
2353
2417
|
};
|
|
2354
2418
|
|
|
2355
|
-
// lib/solvers/TraceLabelOverlapAvoidanceSolver/
|
|
2356
|
-
var
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
const
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2419
|
+
// lib/solvers/TraceLabelOverlapAvoidanceSolver/detectTraceLabelOverlap.ts
|
|
2420
|
+
var detectTraceLabelOverlap = (traces, netLabels) => {
|
|
2421
|
+
const overlaps = [];
|
|
2422
|
+
for (const trace of traces) {
|
|
2423
|
+
for (const label of netLabels) {
|
|
2424
|
+
const labelBounds = getRectBounds(label.center, label.width, label.height);
|
|
2425
|
+
for (let i = 0; i < trace.tracePath.length - 1; i++) {
|
|
2426
|
+
const p1 = trace.tracePath[i];
|
|
2427
|
+
const p2 = trace.tracePath[i + 1];
|
|
2428
|
+
if (segmentIntersectsRect2(p1, p2, labelBounds)) {
|
|
2429
|
+
if (trace.globalConnNetId === label.globalConnNetId) {
|
|
2430
|
+
break;
|
|
2431
|
+
}
|
|
2432
|
+
overlaps.push({ trace, label });
|
|
2433
|
+
break;
|
|
2434
|
+
}
|
|
2363
2435
|
}
|
|
2364
2436
|
}
|
|
2365
2437
|
}
|
|
2366
|
-
return
|
|
2438
|
+
return overlaps;
|
|
2367
2439
|
};
|
|
2368
2440
|
|
|
2369
|
-
// lib/solvers/TraceLabelOverlapAvoidanceSolver/
|
|
2370
|
-
var
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2441
|
+
// lib/solvers/TraceLabelOverlapAvoidanceSolver/violation.ts
|
|
2442
|
+
var findTraceViolationZone = (path, labelBounds) => {
|
|
2443
|
+
const isPointInside = (p) => p.x > labelBounds.minX && p.x < labelBounds.maxX && p.y > labelBounds.minY && p.y < labelBounds.maxY;
|
|
2444
|
+
let firstInsideIndex = -1;
|
|
2445
|
+
let lastInsideIndex = -1;
|
|
2446
|
+
for (let i = 0; i < path.length; i++) {
|
|
2447
|
+
if (isPointInside(path[i])) {
|
|
2448
|
+
if (firstInsideIndex === -1) {
|
|
2449
|
+
firstInsideIndex = i;
|
|
2450
|
+
}
|
|
2451
|
+
lastInsideIndex = i;
|
|
2380
2452
|
}
|
|
2381
2453
|
}
|
|
2382
|
-
return
|
|
2454
|
+
return { firstInsideIndex, lastInsideIndex };
|
|
2455
|
+
};
|
|
2456
|
+
|
|
2457
|
+
// lib/solvers/TraceLabelOverlapAvoidanceSolver/trySnipAndReconnect.ts
|
|
2458
|
+
var generateSnipAndReconnectCandidates = ({
|
|
2459
|
+
initialTrace,
|
|
2460
|
+
firstInsideIndex,
|
|
2461
|
+
lastInsideIndex,
|
|
2462
|
+
labelBounds,
|
|
2463
|
+
paddingBuffer,
|
|
2464
|
+
detourCount
|
|
2465
|
+
}) => {
|
|
2466
|
+
if (firstInsideIndex <= 0 || lastInsideIndex >= initialTrace.tracePath.length - 1) {
|
|
2467
|
+
return [];
|
|
2468
|
+
}
|
|
2469
|
+
const entryPoint = initialTrace.tracePath[firstInsideIndex - 1];
|
|
2470
|
+
const exitPoint = initialTrace.tracePath[lastInsideIndex + 1];
|
|
2471
|
+
const pathToEntry = initialTrace.tracePath.slice(0, firstInsideIndex);
|
|
2472
|
+
const pathFromExit = initialTrace.tracePath.slice(lastInsideIndex + 1);
|
|
2473
|
+
const allCandidateDetours = [];
|
|
2474
|
+
if (entryPoint.x !== exitPoint.x && entryPoint.y !== exitPoint.y) {
|
|
2475
|
+
allCandidateDetours.push([{ x: exitPoint.x, y: entryPoint.y }]);
|
|
2476
|
+
allCandidateDetours.push([{ x: entryPoint.x, y: exitPoint.y }]);
|
|
2477
|
+
} else if (entryPoint.x === exitPoint.x || entryPoint.y === exitPoint.y) {
|
|
2478
|
+
allCandidateDetours.push([]);
|
|
2479
|
+
}
|
|
2480
|
+
const buffer = paddingBuffer + detourCount * paddingBuffer;
|
|
2481
|
+
const leftX = labelBounds.minX - buffer;
|
|
2482
|
+
const rightX = labelBounds.maxX + buffer;
|
|
2483
|
+
const topY = labelBounds.maxY + buffer;
|
|
2484
|
+
const bottomY = labelBounds.minY - buffer;
|
|
2485
|
+
if ((entryPoint.x <= labelBounds.minX || exitPoint.x <= labelBounds.minX) && entryPoint.x < labelBounds.maxX && exitPoint.x < labelBounds.maxX) {
|
|
2486
|
+
allCandidateDetours.push([
|
|
2487
|
+
{ x: leftX, y: entryPoint.y },
|
|
2488
|
+
{ x: leftX, y: exitPoint.y }
|
|
2489
|
+
]);
|
|
2490
|
+
}
|
|
2491
|
+
if ((entryPoint.x >= labelBounds.maxX || exitPoint.x >= labelBounds.maxX) && entryPoint.x > labelBounds.minX && exitPoint.x > labelBounds.minX) {
|
|
2492
|
+
allCandidateDetours.push([
|
|
2493
|
+
{ x: rightX, y: entryPoint.y },
|
|
2494
|
+
{ x: rightX, y: exitPoint.y }
|
|
2495
|
+
]);
|
|
2496
|
+
}
|
|
2497
|
+
if ((entryPoint.y >= labelBounds.maxY || exitPoint.y >= labelBounds.maxY) && entryPoint.y > labelBounds.minY && exitPoint.y > labelBounds.minY) {
|
|
2498
|
+
allCandidateDetours.push([
|
|
2499
|
+
{ x: entryPoint.x, y: topY },
|
|
2500
|
+
{ x: exitPoint.x, y: topY }
|
|
2501
|
+
]);
|
|
2502
|
+
}
|
|
2503
|
+
if ((entryPoint.y <= labelBounds.minY || exitPoint.y <= labelBounds.minY) && entryPoint.y < labelBounds.maxY && exitPoint.y < labelBounds.maxY) {
|
|
2504
|
+
allCandidateDetours.push([
|
|
2505
|
+
{ x: entryPoint.x, y: bottomY },
|
|
2506
|
+
{ x: exitPoint.x, y: bottomY }
|
|
2507
|
+
]);
|
|
2508
|
+
}
|
|
2509
|
+
return allCandidateDetours.map((detour) => [
|
|
2510
|
+
...pathToEntry,
|
|
2511
|
+
...detour,
|
|
2512
|
+
...pathFromExit
|
|
2513
|
+
]);
|
|
2514
|
+
};
|
|
2515
|
+
|
|
2516
|
+
// lib/solvers/TraceLabelOverlapAvoidanceSolver/tryFourPointDetour.ts
|
|
2517
|
+
var generateFourPointDetourCandidates = ({
|
|
2518
|
+
initialTrace,
|
|
2519
|
+
label,
|
|
2520
|
+
labelBounds,
|
|
2521
|
+
paddingBuffer,
|
|
2522
|
+
detourCount
|
|
2523
|
+
}) => {
|
|
2524
|
+
let collidingSegIndex = -1;
|
|
2525
|
+
for (let i = 0; i < initialTrace.tracePath.length - 1; i++) {
|
|
2526
|
+
if (segmentIntersectsRect(
|
|
2527
|
+
initialTrace.tracePath[i],
|
|
2528
|
+
initialTrace.tracePath[i + 1],
|
|
2529
|
+
labelBounds
|
|
2530
|
+
)) {
|
|
2531
|
+
collidingSegIndex = i;
|
|
2532
|
+
break;
|
|
2533
|
+
}
|
|
2534
|
+
}
|
|
2535
|
+
if (collidingSegIndex === -1) return [];
|
|
2536
|
+
const pA = initialTrace.tracePath[collidingSegIndex];
|
|
2537
|
+
const pB = initialTrace.tracePath[collidingSegIndex + 1];
|
|
2538
|
+
if (!pA || !pB) return [];
|
|
2539
|
+
const candidateDetours = [];
|
|
2540
|
+
const paddedLabelBounds = getRectBounds(
|
|
2541
|
+
label.center,
|
|
2542
|
+
label.width,
|
|
2543
|
+
label.height
|
|
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) {
|
|
2552
|
+
candidateDetours.push(
|
|
2553
|
+
pB.y > pA.y ? [
|
|
2554
|
+
{ x: pA.x, y: paddedLabelBounds.minY - effectivePadding },
|
|
2555
|
+
{ x: newX, y: paddedLabelBounds.minY - effectivePadding },
|
|
2556
|
+
{ x: newX, y: paddedLabelBounds.maxY + effectivePadding },
|
|
2557
|
+
{ x: pB.x, y: paddedLabelBounds.maxY + effectivePadding }
|
|
2558
|
+
] : [
|
|
2559
|
+
{ x: pA.x, y: paddedLabelBounds.maxY + effectivePadding },
|
|
2560
|
+
{ x: newX, y: paddedLabelBounds.maxY + effectivePadding },
|
|
2561
|
+
{ x: newX, y: paddedLabelBounds.minY - effectivePadding },
|
|
2562
|
+
{ x: pB.x, y: paddedLabelBounds.minY - effectivePadding }
|
|
2563
|
+
]
|
|
2564
|
+
);
|
|
2565
|
+
}
|
|
2566
|
+
} else {
|
|
2567
|
+
const yCandidates = [
|
|
2568
|
+
paddedLabelBounds.maxY + effectivePadding,
|
|
2569
|
+
paddedLabelBounds.minY - effectivePadding
|
|
2570
|
+
];
|
|
2571
|
+
for (const newY of yCandidates) {
|
|
2572
|
+
candidateDetours.push(
|
|
2573
|
+
pB.x > pA.x ? [
|
|
2574
|
+
{ x: paddedLabelBounds.minX - effectivePadding, y: pA.y },
|
|
2575
|
+
{ x: paddedLabelBounds.minX - effectivePadding, y: newY },
|
|
2576
|
+
{ x: paddedLabelBounds.maxX + effectivePadding, y: newY },
|
|
2577
|
+
{ x: paddedLabelBounds.maxX + effectivePadding, y: pB.y }
|
|
2578
|
+
] : [
|
|
2579
|
+
{ x: paddedLabelBounds.maxX + effectivePadding, y: pA.y },
|
|
2580
|
+
{ x: paddedLabelBounds.maxX + effectivePadding, y: newY },
|
|
2581
|
+
{ x: paddedLabelBounds.minX - effectivePadding, y: newY },
|
|
2582
|
+
{ x: paddedLabelBounds.minX - effectivePadding, y: pB.y }
|
|
2583
|
+
]
|
|
2584
|
+
);
|
|
2585
|
+
}
|
|
2586
|
+
}
|
|
2587
|
+
return candidateDetours.map((detourPoints) => [
|
|
2588
|
+
...initialTrace.tracePath.slice(0, collidingSegIndex + 1),
|
|
2589
|
+
...detourPoints,
|
|
2590
|
+
...initialTrace.tracePath.slice(collidingSegIndex + 1)
|
|
2591
|
+
]);
|
|
2383
2592
|
};
|
|
2384
2593
|
|
|
2385
|
-
// lib/solvers/
|
|
2594
|
+
// lib/solvers/TraceCleanupSolver/simplifyPath.ts
|
|
2386
2595
|
var simplifyPath = (path) => {
|
|
2387
2596
|
if (path.length < 3) return path;
|
|
2388
2597
|
const newPath = [path[0]];
|
|
@@ -2411,675 +2620,46 @@ var simplifyPath = (path) => {
|
|
|
2411
2620
|
return finalPath;
|
|
2412
2621
|
};
|
|
2413
2622
|
|
|
2414
|
-
// lib/solvers/TraceLabelOverlapAvoidanceSolver/
|
|
2415
|
-
var
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2623
|
+
// lib/solvers/TraceLabelOverlapAvoidanceSolver/rerouteCollidingTrace.ts
|
|
2624
|
+
var generateRerouteCandidates = ({
|
|
2625
|
+
trace,
|
|
2626
|
+
label,
|
|
2627
|
+
paddingBuffer,
|
|
2628
|
+
detourCount
|
|
2629
|
+
}) => {
|
|
2630
|
+
const initialTrace = { ...trace, tracePath: simplifyPath(trace.tracePath) };
|
|
2631
|
+
if (trace.globalConnNetId === label.globalConnNetId) {
|
|
2632
|
+
return [initialTrace.tracePath];
|
|
2422
2633
|
}
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
};
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
let isStairStep = true;
|
|
2453
|
-
for (let i = startIdx; i < pathToCheck.length - 2 && i < startIdx + 10; i++) {
|
|
2454
|
-
if (i + 2 >= pathToCheck.length) break;
|
|
2455
|
-
const p1 = pathToCheck[i];
|
|
2456
|
-
const p2 = pathToCheck[i + 1];
|
|
2457
|
-
const p3 = pathToCheck[i + 2];
|
|
2458
|
-
const seg1Vertical = p1.x === p2.x;
|
|
2459
|
-
const seg2Vertical = p2.x === p3.x;
|
|
2460
|
-
if (seg1Vertical === seg2Vertical) {
|
|
2461
|
-
break;
|
|
2462
|
-
}
|
|
2463
|
-
const seg1Direction = seg1Vertical ? Math.sign(p2.y - p1.y) : Math.sign(p2.x - p1.x);
|
|
2464
|
-
if (i > startIdx) {
|
|
2465
|
-
const prevP = pathToCheck[i - 1];
|
|
2466
|
-
const prevSegVertical = prevP.x === p1.x;
|
|
2467
|
-
const prevDirection = prevSegVertical ? Math.sign(p1.y - prevP.y) : Math.sign(p1.x - prevP.x);
|
|
2468
|
-
if (seg1Vertical && prevSegVertical && seg1Direction !== prevDirection || !seg1Vertical && !prevSegVertical && seg1Direction !== prevDirection) {
|
|
2469
|
-
isStairStep = false;
|
|
2470
|
-
break;
|
|
2471
|
-
}
|
|
2472
|
-
}
|
|
2473
|
-
endIdx = i + 2;
|
|
2474
|
-
}
|
|
2475
|
-
return isStairStep && endIdx - startIdx >= 3 ? endIdx : -1;
|
|
2476
|
-
};
|
|
2477
|
-
let optimizedPath = [...path];
|
|
2478
|
-
let currentTurns = countTurns(optimizedPath);
|
|
2479
|
-
let improved = true;
|
|
2480
|
-
while (improved) {
|
|
2481
|
-
improved = false;
|
|
2482
|
-
for (let startIdx = 0; startIdx < optimizedPath.length - 3; startIdx++) {
|
|
2483
|
-
const stairEndIdx = recognizeStairStepPattern(optimizedPath, startIdx);
|
|
2484
|
-
if (stairEndIdx > 0) {
|
|
2485
|
-
const startPoint = optimizedPath[startIdx];
|
|
2486
|
-
const endPoint = optimizedPath[stairEndIdx];
|
|
2487
|
-
const connectionOptions = tryConnectPoints(startPoint, endPoint);
|
|
2488
|
-
for (const connection of connectionOptions) {
|
|
2489
|
-
const testPath = [
|
|
2490
|
-
...optimizedPath.slice(0, startIdx + 1),
|
|
2491
|
-
...connection.slice(1, -1),
|
|
2492
|
-
...optimizedPath.slice(stairEndIdx)
|
|
2493
|
-
];
|
|
2494
|
-
const collidesWithObstacles = hasCollisions(connection, obstacles);
|
|
2495
|
-
const collidesWithLabels = hasCollisionsWithLabels(
|
|
2496
|
-
connection,
|
|
2497
|
-
labelBounds
|
|
2498
|
-
);
|
|
2499
|
-
if (!collidesWithObstacles && !collidesWithLabels) {
|
|
2500
|
-
const newTurns = countTurns(testPath);
|
|
2501
|
-
optimizedPath = testPath;
|
|
2502
|
-
currentTurns = newTurns;
|
|
2503
|
-
improved = true;
|
|
2504
|
-
break;
|
|
2505
|
-
}
|
|
2506
|
-
}
|
|
2507
|
-
if (improved) break;
|
|
2508
|
-
}
|
|
2509
|
-
}
|
|
2510
|
-
if (!improved) {
|
|
2511
|
-
for (let startIdx = 0; startIdx < optimizedPath.length - 2; startIdx++) {
|
|
2512
|
-
const maxRemove = Math.min(
|
|
2513
|
-
optimizedPath.length - startIdx - 2,
|
|
2514
|
-
optimizedPath.length - 2
|
|
2515
|
-
);
|
|
2516
|
-
for (let removeCount = 1; removeCount <= maxRemove; removeCount++) {
|
|
2517
|
-
const endIdx = startIdx + removeCount + 1;
|
|
2518
|
-
if (endIdx >= optimizedPath.length) continue;
|
|
2519
|
-
const startPoint = optimizedPath[startIdx];
|
|
2520
|
-
const endPoint = optimizedPath[endIdx];
|
|
2521
|
-
const connectionOptions = tryConnectPoints(startPoint, endPoint);
|
|
2522
|
-
for (const connection of connectionOptions) {
|
|
2523
|
-
const testPath = [
|
|
2524
|
-
...optimizedPath.slice(0, startIdx + 1),
|
|
2525
|
-
...connection.slice(1, -1),
|
|
2526
|
-
...optimizedPath.slice(endIdx)
|
|
2527
|
-
];
|
|
2528
|
-
const connectionSegments = connection;
|
|
2529
|
-
const collidesWithObstacles = hasCollisions(
|
|
2530
|
-
connectionSegments,
|
|
2531
|
-
obstacles
|
|
2532
|
-
);
|
|
2533
|
-
const collidesWithLabels = hasCollisionsWithLabels(
|
|
2534
|
-
connectionSegments,
|
|
2535
|
-
labelBounds
|
|
2536
|
-
);
|
|
2537
|
-
if (!collidesWithObstacles && !collidesWithLabels) {
|
|
2538
|
-
const newTurns = countTurns(testPath);
|
|
2539
|
-
if (newTurns < currentTurns || newTurns === currentTurns && testPath.length < optimizedPath.length) {
|
|
2540
|
-
optimizedPath = testPath;
|
|
2541
|
-
currentTurns = newTurns;
|
|
2542
|
-
improved = true;
|
|
2543
|
-
break;
|
|
2544
|
-
}
|
|
2545
|
-
}
|
|
2546
|
-
}
|
|
2547
|
-
if (improved) break;
|
|
2548
|
-
}
|
|
2549
|
-
if (improved) break;
|
|
2550
|
-
}
|
|
2551
|
-
}
|
|
2552
|
-
if (!improved) {
|
|
2553
|
-
for (let i = 0; i < optimizedPath.length - 2; i++) {
|
|
2554
|
-
const p1 = optimizedPath[i];
|
|
2555
|
-
const p2 = optimizedPath[i + 1];
|
|
2556
|
-
const p3 = optimizedPath[i + 2];
|
|
2557
|
-
const allVertical = p1.x === p2.x && p2.x === p3.x;
|
|
2558
|
-
const allHorizontal = p1.y === p2.y && p2.y === p3.y;
|
|
2559
|
-
if (allVertical || allHorizontal) {
|
|
2560
|
-
const testPath = [
|
|
2561
|
-
...optimizedPath.slice(0, i + 1),
|
|
2562
|
-
...optimizedPath.slice(i + 2)
|
|
2563
|
-
];
|
|
2564
|
-
const collidesWithObstacles = hasCollisions([p1, p3], obstacles);
|
|
2565
|
-
const collidesWithLabels = hasCollisionsWithLabels(
|
|
2566
|
-
[p1, p3],
|
|
2567
|
-
labelBounds
|
|
2568
|
-
);
|
|
2569
|
-
if (!collidesWithObstacles && !collidesWithLabels) {
|
|
2570
|
-
optimizedPath = testPath;
|
|
2571
|
-
improved = true;
|
|
2572
|
-
break;
|
|
2573
|
-
}
|
|
2574
|
-
}
|
|
2575
|
-
}
|
|
2576
|
-
}
|
|
2577
|
-
}
|
|
2578
|
-
const finalSimplifiedPath = simplifyPath(optimizedPath);
|
|
2579
|
-
return finalSimplifiedPath;
|
|
2580
|
-
};
|
|
2581
|
-
|
|
2582
|
-
// lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts
|
|
2583
|
-
var minimizeTurnsWithFilteredLabels = ({
|
|
2584
|
-
traces,
|
|
2585
|
-
inputProblem,
|
|
2586
|
-
allLabelPlacements,
|
|
2587
|
-
mergedLabelNetIdMap,
|
|
2588
|
-
paddingBuffer
|
|
2589
|
-
}) => {
|
|
2590
|
-
let changesMade = false;
|
|
2591
|
-
const obstacles = getObstacleRects(inputProblem);
|
|
2592
|
-
const newTraces = traces.map((trace) => {
|
|
2593
|
-
const originalPath = trace.tracePath;
|
|
2594
|
-
const filteredLabels = allLabelPlacements.filter((label) => {
|
|
2595
|
-
const originalNetIds = mergedLabelNetIdMap[label.globalConnNetId];
|
|
2596
|
-
if (originalNetIds) {
|
|
2597
|
-
return !originalNetIds.has(trace.globalConnNetId);
|
|
2598
|
-
}
|
|
2599
|
-
return label.globalConnNetId !== trace.globalConnNetId;
|
|
2600
|
-
});
|
|
2601
|
-
const labelBounds = filteredLabels.map((nl) => ({
|
|
2602
|
-
minX: nl.center.x - nl.width / 2 - paddingBuffer,
|
|
2603
|
-
maxX: nl.center.x + nl.width / 2 + paddingBuffer,
|
|
2604
|
-
minY: nl.center.y - nl.height / 2 - paddingBuffer,
|
|
2605
|
-
maxY: nl.center.y + nl.height / 2 + paddingBuffer
|
|
2606
|
-
}));
|
|
2607
|
-
const newPath = minimizeTurns({
|
|
2608
|
-
path: originalPath,
|
|
2609
|
-
obstacles,
|
|
2610
|
-
labelBounds
|
|
2611
|
-
});
|
|
2612
|
-
if (newPath.length !== originalPath.length || newPath.some(
|
|
2613
|
-
(p, i) => p.x !== originalPath[i].x || p.y !== originalPath[i].y
|
|
2614
|
-
)) {
|
|
2615
|
-
changesMade = true;
|
|
2616
|
-
}
|
|
2617
|
-
return {
|
|
2618
|
-
...trace,
|
|
2619
|
-
tracePath: newPath
|
|
2620
|
-
};
|
|
2621
|
-
});
|
|
2622
|
-
if (changesMade) {
|
|
2623
|
-
return newTraces;
|
|
2624
|
-
} else {
|
|
2625
|
-
return null;
|
|
2626
|
-
}
|
|
2627
|
-
};
|
|
2628
|
-
|
|
2629
|
-
// lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver/balanceLShapes.ts
|
|
2630
|
-
var balanceLShapes = ({
|
|
2631
|
-
traces,
|
|
2632
|
-
inputProblem,
|
|
2633
|
-
allLabelPlacements
|
|
2634
|
-
}) => {
|
|
2635
|
-
const TOLERANCE = 1e-5;
|
|
2636
|
-
let changesMade = false;
|
|
2637
|
-
for (const trace of traces) {
|
|
2638
|
-
if (trace.tracePath.length === 4) {
|
|
2639
|
-
const [p0, p1, p2, p3] = trace.tracePath;
|
|
2640
|
-
const isHVHShape = p0.y === p1.y && p1.x === p2.x && p2.y === p3.y;
|
|
2641
|
-
const isVHVShape = p0.x === p1.x && p1.y === p2.y && p2.x === p3.x;
|
|
2642
|
-
const isCollinearHorizontal = p0.y === p1.y && p1.y === p2.y && p2.y === p3.y;
|
|
2643
|
-
const isCollinearVertical = p0.x === p1.x && p1.x === p2.x && p2.x === p3.x;
|
|
2644
|
-
const isCollinear = isCollinearHorizontal || isCollinearVertical;
|
|
2645
|
-
let isSameDirection = false;
|
|
2646
|
-
if (isHVHShape) {
|
|
2647
|
-
isSameDirection = Math.sign(p1.x - p0.x) === Math.sign(p3.x - p2.x);
|
|
2648
|
-
} else if (isVHVShape) {
|
|
2649
|
-
isSameDirection = Math.sign(p1.y - p0.y) === Math.sign(p3.y - p2.y);
|
|
2650
|
-
}
|
|
2651
|
-
const isValidZShape = (isHVHShape || isVHVShape) && !isCollinear && isSameDirection;
|
|
2652
|
-
if (!isValidZShape) {
|
|
2653
|
-
return null;
|
|
2654
|
-
}
|
|
2655
|
-
}
|
|
2656
|
-
}
|
|
2657
|
-
const obstacles = getObstacleRects(inputProblem).map((obs) => ({
|
|
2658
|
-
...obs,
|
|
2659
|
-
minX: obs.minX + TOLERANCE,
|
|
2660
|
-
maxX: obs.maxX - TOLERANCE,
|
|
2661
|
-
minY: obs.minY + TOLERANCE,
|
|
2662
|
-
maxY: obs.maxY - TOLERANCE
|
|
2663
|
-
}));
|
|
2664
|
-
const segmentIntersectsAnyRect = (p1, p2, rects) => {
|
|
2665
|
-
for (const rect of rects) {
|
|
2666
|
-
if (segmentIntersectsRect(p1, p2, rect)) {
|
|
2667
|
-
return true;
|
|
2668
|
-
}
|
|
2669
|
-
}
|
|
2670
|
-
return false;
|
|
2671
|
-
};
|
|
2672
|
-
const getLabelBounds = (labels, traceNetId) => {
|
|
2673
|
-
const filteredLabels = labels.filter(
|
|
2674
|
-
(label) => label.globalConnNetId !== traceNetId
|
|
2675
|
-
);
|
|
2676
|
-
return filteredLabels.map((nl) => ({
|
|
2677
|
-
minX: nl.center.x - nl.width / 2 + TOLERANCE,
|
|
2678
|
-
maxX: nl.center.x + nl.width / 2 - TOLERANCE,
|
|
2679
|
-
minY: nl.center.y - nl.height / 2 + TOLERANCE,
|
|
2680
|
-
maxY: nl.center.y + nl.height / 2 - TOLERANCE
|
|
2681
|
-
}));
|
|
2682
|
-
};
|
|
2683
|
-
const newTraces = traces.map((trace) => {
|
|
2684
|
-
const newPath = [...trace.tracePath];
|
|
2685
|
-
if (newPath.length < 4) {
|
|
2686
|
-
return { ...trace };
|
|
2687
|
-
}
|
|
2688
|
-
const labelBounds = getLabelBounds(
|
|
2689
|
-
allLabelPlacements,
|
|
2690
|
-
trace.globalConnNetId
|
|
2691
|
-
);
|
|
2692
|
-
if (newPath.length === 4) {
|
|
2693
|
-
const [p0, p1, p2, p3] = newPath;
|
|
2694
|
-
let p1New;
|
|
2695
|
-
let p2New;
|
|
2696
|
-
const isHVHShape = p0.y === p1.y && p1.x === p2.x && p2.y === p3.y;
|
|
2697
|
-
if (isHVHShape) {
|
|
2698
|
-
const idealX = (p0.x + p3.x) / 2;
|
|
2699
|
-
p1New = { x: idealX, y: p1.y };
|
|
2700
|
-
p2New = { x: idealX, y: p2.y };
|
|
2701
|
-
} else {
|
|
2702
|
-
const idealY = (p0.y + p3.y) / 2;
|
|
2703
|
-
p1New = { x: p1.x, y: idealY };
|
|
2704
|
-
p2New = { x: p2.x, y: idealY };
|
|
2705
|
-
}
|
|
2706
|
-
const collides = segmentIntersectsAnyRect(p0, p1New, obstacles) || segmentIntersectsAnyRect(p1New, p2New, obstacles) || segmentIntersectsAnyRect(p2New, p3, obstacles) || segmentIntersectsAnyRect(p0, p1New, labelBounds) || segmentIntersectsAnyRect(p1New, p2New, labelBounds) || segmentIntersectsAnyRect(p2New, p3, labelBounds);
|
|
2707
|
-
if (!collides) {
|
|
2708
|
-
newPath[1] = p1New;
|
|
2709
|
-
newPath[2] = p2New;
|
|
2710
|
-
changesMade = true;
|
|
2711
|
-
}
|
|
2712
|
-
return { ...trace, tracePath: simplifyPath(newPath) };
|
|
2713
|
-
}
|
|
2714
|
-
for (let i = 1; i < newPath.length - 4; i++) {
|
|
2715
|
-
const p1 = newPath[i];
|
|
2716
|
-
const p2 = newPath[i + 1];
|
|
2717
|
-
const p3 = newPath[i + 2];
|
|
2718
|
-
const p4 = newPath[i + 3];
|
|
2719
|
-
const isHVHZShape = p1.y === p2.y && p2.x === p3.x && p3.y === p4.y;
|
|
2720
|
-
const isVHVZShape = p1.x === p2.x && p2.y === p3.y && p3.x === p4.x;
|
|
2721
|
-
const isCollinearHorizontal = p1.y === p2.y && p2.y === p3.y && p3.y === p4.y;
|
|
2722
|
-
const isCollinearVertical = p1.x === p2.x && p2.x === p3.x && p3.x === p4.x;
|
|
2723
|
-
const isCollinear = isCollinearHorizontal || isCollinearVertical;
|
|
2724
|
-
let isSameDirection = false;
|
|
2725
|
-
if (isHVHZShape) {
|
|
2726
|
-
isSameDirection = Math.sign(p2.x - p1.x) === Math.sign(p4.x - p3.x);
|
|
2727
|
-
} else if (isVHVZShape) {
|
|
2728
|
-
isSameDirection = Math.sign(p2.y - p1.y) === Math.sign(p4.y - p3.y);
|
|
2729
|
-
}
|
|
2730
|
-
const isValidZShape = (isHVHZShape || isVHVZShape) && !isCollinear && isSameDirection;
|
|
2731
|
-
if (!isValidZShape) {
|
|
2732
|
-
continue;
|
|
2733
|
-
}
|
|
2734
|
-
let p2New;
|
|
2735
|
-
let p3New;
|
|
2736
|
-
const len1Original = isHVHZShape ? Math.abs(p1.x - p2.x) : Math.abs(p1.y - p2.y);
|
|
2737
|
-
const len2Original = isHVHZShape ? Math.abs(p3.x - p4.x) : Math.abs(p3.y - p4.y);
|
|
2738
|
-
if (Math.abs(len1Original - len2Original) < 1e-3) {
|
|
2739
|
-
continue;
|
|
2740
|
-
}
|
|
2741
|
-
if (isHVHZShape) {
|
|
2742
|
-
const idealX = (p1.x + p4.x) / 2;
|
|
2743
|
-
p2New = { x: idealX, y: p2.y };
|
|
2744
|
-
p3New = { x: idealX, y: p3.y };
|
|
2745
|
-
} else {
|
|
2746
|
-
const idealY = (p1.y + p4.y) / 2;
|
|
2747
|
-
p2New = { x: p2.x, y: idealY };
|
|
2748
|
-
p3New = { x: p3.x, y: idealY };
|
|
2749
|
-
}
|
|
2750
|
-
const collides = segmentIntersectsAnyRect(p1, p2New, obstacles) || segmentIntersectsAnyRect(p2New, p3New, obstacles) || segmentIntersectsAnyRect(p3New, p4, obstacles) || segmentIntersectsAnyRect(p1, p2New, labelBounds) || segmentIntersectsAnyRect(p2New, p3New, labelBounds) || segmentIntersectsAnyRect(p3New, p4, labelBounds);
|
|
2751
|
-
if (!collides) {
|
|
2752
|
-
newPath[i + 1] = p2New;
|
|
2753
|
-
newPath[i + 2] = p3New;
|
|
2754
|
-
changesMade = true;
|
|
2755
|
-
i = 0;
|
|
2756
|
-
}
|
|
2757
|
-
}
|
|
2758
|
-
const finalSimplifiedPath = simplifyPath(newPath);
|
|
2759
|
-
return {
|
|
2760
|
-
...trace,
|
|
2761
|
-
tracePath: finalSimplifiedPath
|
|
2762
|
-
};
|
|
2763
|
-
});
|
|
2764
|
-
if (changesMade) {
|
|
2765
|
-
return newTraces;
|
|
2766
|
-
} else {
|
|
2767
|
-
return null;
|
|
2768
|
-
}
|
|
2769
|
-
};
|
|
2770
|
-
|
|
2771
|
-
// lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver/TraceCleanupSolver.ts
|
|
2772
|
-
var TraceCleanupSolver = class extends BaseSolver {
|
|
2773
|
-
input;
|
|
2774
|
-
outputTraces;
|
|
2775
|
-
pipelineStepIndex = 0;
|
|
2776
|
-
tracesToProcess;
|
|
2777
|
-
minimizedTraces = null;
|
|
2778
|
-
balancedTraces = null;
|
|
2779
|
-
constructor(solverInput) {
|
|
2780
|
-
super();
|
|
2781
|
-
this.input = solverInput;
|
|
2782
|
-
this.outputTraces = [...solverInput.allTraces];
|
|
2783
|
-
this.tracesToProcess = this.outputTraces.filter(
|
|
2784
|
-
(t) => this.input.targetTraceIds.has(t.mspPairId)
|
|
2785
|
-
);
|
|
2786
|
-
}
|
|
2787
|
-
_step() {
|
|
2788
|
-
const {
|
|
2789
|
-
targetTraceIds,
|
|
2790
|
-
inputProblem,
|
|
2791
|
-
allLabelPlacements,
|
|
2792
|
-
mergedLabelNetIdMap,
|
|
2793
|
-
paddingBuffer
|
|
2794
|
-
} = this.input;
|
|
2795
|
-
if (targetTraceIds.size === 0) {
|
|
2796
|
-
this.solved = true;
|
|
2797
|
-
return;
|
|
2798
|
-
}
|
|
2799
|
-
switch (this.pipelineStepIndex) {
|
|
2800
|
-
case 0: {
|
|
2801
|
-
const minimizedTracesResult = minimizeTurnsWithFilteredLabels({
|
|
2802
|
-
traces: this.tracesToProcess,
|
|
2803
|
-
inputProblem,
|
|
2804
|
-
allLabelPlacements,
|
|
2805
|
-
mergedLabelNetIdMap,
|
|
2806
|
-
paddingBuffer
|
|
2807
|
-
});
|
|
2808
|
-
this.minimizedTraces = minimizedTracesResult ?? this.tracesToProcess;
|
|
2809
|
-
const tracesMap = new Map(
|
|
2810
|
-
this.input.allTraces.map((t) => [t.mspPairId, t])
|
|
2811
|
-
);
|
|
2812
|
-
for (const trace of this.minimizedTraces) {
|
|
2813
|
-
tracesMap.set(trace.mspPairId, trace);
|
|
2814
|
-
}
|
|
2815
|
-
this.outputTraces = Array.from(tracesMap.values());
|
|
2816
|
-
this.pipelineStepIndex++;
|
|
2817
|
-
break;
|
|
2818
|
-
}
|
|
2819
|
-
case 1: {
|
|
2820
|
-
const balancedTracesResult = balanceLShapes({
|
|
2821
|
-
traces: this.minimizedTraces,
|
|
2822
|
-
inputProblem,
|
|
2823
|
-
allLabelPlacements
|
|
2824
|
-
});
|
|
2825
|
-
this.balancedTraces = balancedTracesResult ?? this.minimizedTraces;
|
|
2826
|
-
const tracesMap = new Map(
|
|
2827
|
-
this.input.allTraces.map((t) => [t.mspPairId, t])
|
|
2828
|
-
);
|
|
2829
|
-
for (const trace of this.balancedTraces) {
|
|
2830
|
-
tracesMap.set(trace.mspPairId, trace);
|
|
2831
|
-
}
|
|
2832
|
-
this.outputTraces = Array.from(tracesMap.values());
|
|
2833
|
-
this.pipelineStepIndex++;
|
|
2834
|
-
break;
|
|
2835
|
-
}
|
|
2836
|
-
case 2: {
|
|
2837
|
-
this.solved = true;
|
|
2838
|
-
break;
|
|
2839
|
-
}
|
|
2840
|
-
}
|
|
2841
|
-
}
|
|
2842
|
-
getOutput() {
|
|
2843
|
-
return {
|
|
2844
|
-
traces: this.outputTraces
|
|
2845
|
-
};
|
|
2846
|
-
}
|
|
2847
|
-
visualize() {
|
|
2848
|
-
const graphics = visualizeInputProblem(this.input.inputProblem, {
|
|
2849
|
-
chipAlpha: 0.1,
|
|
2850
|
-
connectionAlpha: 0.1
|
|
2851
|
-
});
|
|
2852
|
-
if (!graphics.lines) graphics.lines = [];
|
|
2853
|
-
if (!graphics.points) graphics.points = [];
|
|
2854
|
-
if (!graphics.rects) graphics.rects = [];
|
|
2855
|
-
if (!graphics.circles) graphics.circles = [];
|
|
2856
|
-
if (!graphics.texts) graphics.texts = [];
|
|
2857
|
-
for (const trace of this.outputTraces) {
|
|
2858
|
-
const line = {
|
|
2859
|
-
points: trace.tracePath.map((p) => ({ x: p.x, y: p.y })),
|
|
2860
|
-
strokeColor: "blue"
|
|
2861
|
-
};
|
|
2862
|
-
graphics.lines.push(line);
|
|
2863
|
-
}
|
|
2864
|
-
return graphics;
|
|
2865
|
-
}
|
|
2866
|
-
};
|
|
2867
|
-
|
|
2868
|
-
// lib/solvers/TraceLabelOverlapAvoidanceSolver/detectTraceLabelOverlap.ts
|
|
2869
|
-
var detectTraceLabelOverlap = (traces, netLabels) => {
|
|
2870
|
-
const overlaps = [];
|
|
2871
|
-
for (const trace of traces) {
|
|
2872
|
-
for (const label of netLabels) {
|
|
2873
|
-
const labelBounds = getRectBounds(label.center, label.width, label.height);
|
|
2874
|
-
for (let i = 0; i < trace.tracePath.length - 1; i++) {
|
|
2875
|
-
const p1 = trace.tracePath[i];
|
|
2876
|
-
const p2 = trace.tracePath[i + 1];
|
|
2877
|
-
if (segmentIntersectsRect2(p1, p2, labelBounds)) {
|
|
2878
|
-
if (trace.globalConnNetId === label.globalConnNetId) {
|
|
2879
|
-
break;
|
|
2880
|
-
}
|
|
2881
|
-
overlaps.push({ trace, label });
|
|
2882
|
-
break;
|
|
2883
|
-
}
|
|
2884
|
-
}
|
|
2885
|
-
}
|
|
2886
|
-
}
|
|
2887
|
-
return overlaps;
|
|
2888
|
-
};
|
|
2889
|
-
|
|
2890
|
-
// lib/solvers/TraceLabelOverlapAvoidanceSolver/violation.ts
|
|
2891
|
-
var findTraceViolationZone = (path, labelBounds) => {
|
|
2892
|
-
const isPointInside = (p) => p.x > labelBounds.minX && p.x < labelBounds.maxX && p.y > labelBounds.minY && p.y < labelBounds.maxY;
|
|
2893
|
-
let firstInsideIndex = -1;
|
|
2894
|
-
let lastInsideIndex = -1;
|
|
2895
|
-
for (let i = 0; i < path.length; i++) {
|
|
2896
|
-
if (isPointInside(path[i])) {
|
|
2897
|
-
if (firstInsideIndex === -1) {
|
|
2898
|
-
firstInsideIndex = i;
|
|
2899
|
-
}
|
|
2900
|
-
lastInsideIndex = i;
|
|
2901
|
-
}
|
|
2902
|
-
}
|
|
2903
|
-
return { firstInsideIndex, lastInsideIndex };
|
|
2904
|
-
};
|
|
2905
|
-
|
|
2906
|
-
// lib/solvers/TraceLabelOverlapAvoidanceSolver/trySnipAndReconnect.ts
|
|
2907
|
-
var generateSnipAndReconnectCandidates = ({
|
|
2908
|
-
initialTrace,
|
|
2909
|
-
firstInsideIndex,
|
|
2910
|
-
lastInsideIndex,
|
|
2911
|
-
labelBounds,
|
|
2912
|
-
paddingBuffer,
|
|
2913
|
-
detourCount
|
|
2914
|
-
}) => {
|
|
2915
|
-
if (firstInsideIndex <= 0 || lastInsideIndex >= initialTrace.tracePath.length - 1) {
|
|
2916
|
-
return [];
|
|
2917
|
-
}
|
|
2918
|
-
const entryPoint = initialTrace.tracePath[firstInsideIndex - 1];
|
|
2919
|
-
const exitPoint = initialTrace.tracePath[lastInsideIndex + 1];
|
|
2920
|
-
const pathToEntry = initialTrace.tracePath.slice(0, firstInsideIndex);
|
|
2921
|
-
const pathFromExit = initialTrace.tracePath.slice(lastInsideIndex + 1);
|
|
2922
|
-
const allCandidateDetours = [];
|
|
2923
|
-
if (entryPoint.x !== exitPoint.x && entryPoint.y !== exitPoint.y) {
|
|
2924
|
-
allCandidateDetours.push([{ x: exitPoint.x, y: entryPoint.y }]);
|
|
2925
|
-
allCandidateDetours.push([{ x: entryPoint.x, y: exitPoint.y }]);
|
|
2926
|
-
} else if (entryPoint.x === exitPoint.x || entryPoint.y === exitPoint.y) {
|
|
2927
|
-
allCandidateDetours.push([]);
|
|
2928
|
-
}
|
|
2929
|
-
const buffer = paddingBuffer + detourCount * paddingBuffer;
|
|
2930
|
-
const leftX = labelBounds.minX - buffer;
|
|
2931
|
-
const rightX = labelBounds.maxX + buffer;
|
|
2932
|
-
const topY = labelBounds.maxY + buffer;
|
|
2933
|
-
const bottomY = labelBounds.minY - buffer;
|
|
2934
|
-
if ((entryPoint.x <= labelBounds.minX || exitPoint.x <= labelBounds.minX) && entryPoint.x < labelBounds.maxX && exitPoint.x < labelBounds.maxX) {
|
|
2935
|
-
allCandidateDetours.push([
|
|
2936
|
-
{ x: leftX, y: entryPoint.y },
|
|
2937
|
-
{ x: leftX, y: exitPoint.y }
|
|
2938
|
-
]);
|
|
2939
|
-
}
|
|
2940
|
-
if ((entryPoint.x >= labelBounds.maxX || exitPoint.x >= labelBounds.maxX) && entryPoint.x > labelBounds.minX && exitPoint.x > labelBounds.minX) {
|
|
2941
|
-
allCandidateDetours.push([
|
|
2942
|
-
{ x: rightX, y: entryPoint.y },
|
|
2943
|
-
{ x: rightX, y: exitPoint.y }
|
|
2944
|
-
]);
|
|
2945
|
-
}
|
|
2946
|
-
if ((entryPoint.y >= labelBounds.maxY || exitPoint.y >= labelBounds.maxY) && entryPoint.y > labelBounds.minY && exitPoint.y > labelBounds.minY) {
|
|
2947
|
-
allCandidateDetours.push([
|
|
2948
|
-
{ x: entryPoint.x, y: topY },
|
|
2949
|
-
{ x: exitPoint.x, y: topY }
|
|
2950
|
-
]);
|
|
2951
|
-
}
|
|
2952
|
-
if ((entryPoint.y <= labelBounds.minY || exitPoint.y <= labelBounds.minY) && entryPoint.y < labelBounds.maxY && exitPoint.y < labelBounds.maxY) {
|
|
2953
|
-
allCandidateDetours.push([
|
|
2954
|
-
{ x: entryPoint.x, y: bottomY },
|
|
2955
|
-
{ x: exitPoint.x, y: bottomY }
|
|
2956
|
-
]);
|
|
2957
|
-
}
|
|
2958
|
-
return allCandidateDetours.map((detour) => [
|
|
2959
|
-
...pathToEntry,
|
|
2960
|
-
...detour,
|
|
2961
|
-
...pathFromExit
|
|
2962
|
-
]);
|
|
2963
|
-
};
|
|
2964
|
-
|
|
2965
|
-
// lib/solvers/TraceLabelOverlapAvoidanceSolver/tryFourPointDetour.ts
|
|
2966
|
-
var generateFourPointDetourCandidates = ({
|
|
2967
|
-
initialTrace,
|
|
2968
|
-
label,
|
|
2969
|
-
labelBounds,
|
|
2970
|
-
paddingBuffer,
|
|
2971
|
-
detourCount
|
|
2972
|
-
}) => {
|
|
2973
|
-
let collidingSegIndex = -1;
|
|
2974
|
-
for (let i = 0; i < initialTrace.tracePath.length - 1; i++) {
|
|
2975
|
-
if (segmentIntersectsRect(
|
|
2976
|
-
initialTrace.tracePath[i],
|
|
2977
|
-
initialTrace.tracePath[i + 1],
|
|
2978
|
-
labelBounds
|
|
2979
|
-
)) {
|
|
2980
|
-
collidingSegIndex = i;
|
|
2981
|
-
break;
|
|
2982
|
-
}
|
|
2983
|
-
}
|
|
2984
|
-
if (collidingSegIndex === -1) return [];
|
|
2985
|
-
const pA = initialTrace.tracePath[collidingSegIndex];
|
|
2986
|
-
const pB = initialTrace.tracePath[collidingSegIndex + 1];
|
|
2987
|
-
if (!pA || !pB) return [];
|
|
2988
|
-
const candidateDetours = [];
|
|
2989
|
-
const paddedLabelBounds = getRectBounds(
|
|
2990
|
-
label.center,
|
|
2991
|
-
label.width,
|
|
2992
|
-
label.height
|
|
2993
|
-
);
|
|
2994
|
-
const effectivePadding = paddingBuffer + detourCount * paddingBuffer;
|
|
2995
|
-
if (isVertical(pA, pB)) {
|
|
2996
|
-
const xCandidates = [
|
|
2997
|
-
paddedLabelBounds.maxX + effectivePadding,
|
|
2998
|
-
paddedLabelBounds.minX - effectivePadding
|
|
2999
|
-
];
|
|
3000
|
-
for (const newX of xCandidates) {
|
|
3001
|
-
candidateDetours.push(
|
|
3002
|
-
pB.y > pA.y ? [
|
|
3003
|
-
{ x: pA.x, y: paddedLabelBounds.minY - effectivePadding },
|
|
3004
|
-
{ x: newX, y: paddedLabelBounds.minY - effectivePadding },
|
|
3005
|
-
{ x: newX, y: paddedLabelBounds.maxY + effectivePadding },
|
|
3006
|
-
{ x: pB.x, y: paddedLabelBounds.maxY + effectivePadding }
|
|
3007
|
-
] : [
|
|
3008
|
-
{ x: pA.x, y: paddedLabelBounds.maxY + effectivePadding },
|
|
3009
|
-
{ x: newX, y: paddedLabelBounds.maxY + effectivePadding },
|
|
3010
|
-
{ x: newX, y: paddedLabelBounds.minY - effectivePadding },
|
|
3011
|
-
{ x: pB.x, y: paddedLabelBounds.minY - effectivePadding }
|
|
3012
|
-
]
|
|
3013
|
-
);
|
|
3014
|
-
}
|
|
3015
|
-
} else {
|
|
3016
|
-
const yCandidates = [
|
|
3017
|
-
paddedLabelBounds.maxY + effectivePadding,
|
|
3018
|
-
paddedLabelBounds.minY - effectivePadding
|
|
3019
|
-
];
|
|
3020
|
-
for (const newY of yCandidates) {
|
|
3021
|
-
candidateDetours.push(
|
|
3022
|
-
pB.x > pA.x ? [
|
|
3023
|
-
{ x: paddedLabelBounds.minX - effectivePadding, y: pA.y },
|
|
3024
|
-
{ x: paddedLabelBounds.minX - effectivePadding, y: newY },
|
|
3025
|
-
{ x: paddedLabelBounds.maxX + effectivePadding, y: newY },
|
|
3026
|
-
{ x: paddedLabelBounds.maxX + effectivePadding, y: pB.y }
|
|
3027
|
-
] : [
|
|
3028
|
-
{ x: paddedLabelBounds.maxX + effectivePadding, y: pA.y },
|
|
3029
|
-
{ x: paddedLabelBounds.maxX + effectivePadding, y: newY },
|
|
3030
|
-
{ x: paddedLabelBounds.minX - effectivePadding, y: newY },
|
|
3031
|
-
{ x: paddedLabelBounds.minX - effectivePadding, y: pB.y }
|
|
3032
|
-
]
|
|
3033
|
-
);
|
|
3034
|
-
}
|
|
3035
|
-
}
|
|
3036
|
-
return candidateDetours.map((detourPoints) => [
|
|
3037
|
-
...initialTrace.tracePath.slice(0, collidingSegIndex + 1),
|
|
3038
|
-
...detourPoints,
|
|
3039
|
-
...initialTrace.tracePath.slice(collidingSegIndex + 1)
|
|
3040
|
-
]);
|
|
3041
|
-
};
|
|
3042
|
-
|
|
3043
|
-
// lib/solvers/TraceLabelOverlapAvoidanceSolver/rerouteCollidingTrace.ts
|
|
3044
|
-
var generateRerouteCandidates = ({
|
|
3045
|
-
trace,
|
|
3046
|
-
label,
|
|
3047
|
-
paddingBuffer,
|
|
3048
|
-
detourCount
|
|
3049
|
-
}) => {
|
|
3050
|
-
const initialTrace = { ...trace, tracePath: simplifyPath(trace.tracePath) };
|
|
3051
|
-
if (trace.globalConnNetId === label.globalConnNetId) {
|
|
3052
|
-
return [initialTrace.tracePath];
|
|
3053
|
-
}
|
|
3054
|
-
const labelPadding = paddingBuffer;
|
|
3055
|
-
const labelBoundsRaw = getRectBounds(label.center, label.width, label.height);
|
|
3056
|
-
const labelBounds = {
|
|
3057
|
-
minX: labelBoundsRaw.minX - labelPadding,
|
|
3058
|
-
minY: labelBoundsRaw.minY - labelPadding,
|
|
3059
|
-
maxX: labelBoundsRaw.maxX + labelPadding,
|
|
3060
|
-
maxY: labelBoundsRaw.maxY + labelPadding,
|
|
3061
|
-
chipId: `netlabel-${label.netId}`
|
|
3062
|
-
};
|
|
3063
|
-
const fourPointCandidates = generateFourPointDetourCandidates({
|
|
3064
|
-
initialTrace,
|
|
3065
|
-
label,
|
|
3066
|
-
labelBounds,
|
|
3067
|
-
paddingBuffer,
|
|
3068
|
-
detourCount
|
|
3069
|
-
});
|
|
3070
|
-
const { firstInsideIndex, lastInsideIndex } = findTraceViolationZone(
|
|
3071
|
-
initialTrace.tracePath,
|
|
3072
|
-
labelBounds
|
|
3073
|
-
);
|
|
3074
|
-
const snipReconnectCandidates = generateSnipAndReconnectCandidates({
|
|
3075
|
-
initialTrace,
|
|
3076
|
-
firstInsideIndex,
|
|
3077
|
-
lastInsideIndex,
|
|
3078
|
-
labelBounds,
|
|
3079
|
-
paddingBuffer,
|
|
3080
|
-
detourCount
|
|
3081
|
-
});
|
|
3082
|
-
return [...fourPointCandidates, ...snipReconnectCandidates];
|
|
2634
|
+
const labelPadding = paddingBuffer;
|
|
2635
|
+
const labelBoundsRaw = getRectBounds(label.center, label.width, label.height);
|
|
2636
|
+
const labelBounds = {
|
|
2637
|
+
minX: labelBoundsRaw.minX - labelPadding,
|
|
2638
|
+
minY: labelBoundsRaw.minY - labelPadding,
|
|
2639
|
+
maxX: labelBoundsRaw.maxX + labelPadding,
|
|
2640
|
+
maxY: labelBoundsRaw.maxY + labelPadding,
|
|
2641
|
+
chipId: `netlabel-${label.netId}`
|
|
2642
|
+
};
|
|
2643
|
+
const fourPointCandidates = generateFourPointDetourCandidates({
|
|
2644
|
+
initialTrace,
|
|
2645
|
+
label,
|
|
2646
|
+
labelBounds,
|
|
2647
|
+
paddingBuffer,
|
|
2648
|
+
detourCount
|
|
2649
|
+
});
|
|
2650
|
+
const { firstInsideIndex, lastInsideIndex } = findTraceViolationZone(
|
|
2651
|
+
initialTrace.tracePath,
|
|
2652
|
+
labelBounds
|
|
2653
|
+
);
|
|
2654
|
+
const snipReconnectCandidates = generateSnipAndReconnectCandidates({
|
|
2655
|
+
initialTrace,
|
|
2656
|
+
firstInsideIndex,
|
|
2657
|
+
lastInsideIndex,
|
|
2658
|
+
labelBounds,
|
|
2659
|
+
paddingBuffer,
|
|
2660
|
+
detourCount
|
|
2661
|
+
});
|
|
2662
|
+
return [...fourPointCandidates, ...snipReconnectCandidates];
|
|
3083
2663
|
};
|
|
3084
2664
|
|
|
3085
2665
|
// lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/SingleOverlapSolver/SingleOverlapSolver.ts
|
|
@@ -3272,7 +2852,6 @@ var TraceLabelOverlapAvoidanceSolver = class extends BaseSolver {
|
|
|
3272
2852
|
// sub-solver instances
|
|
3273
2853
|
labelMergingSolver;
|
|
3274
2854
|
overlapAvoidanceSolver;
|
|
3275
|
-
traceCleanupSolver;
|
|
3276
2855
|
pipelineStepIndex = 0;
|
|
3277
2856
|
constructor(solverInput) {
|
|
3278
2857
|
super();
|
|
@@ -3310,292 +2889,753 @@ var TraceLabelOverlapAvoidanceSolver = class extends BaseSolver {
|
|
|
3310
2889
|
});
|
|
3311
2890
|
this.activeSubSolver = this.overlapAvoidanceSolver;
|
|
3312
2891
|
break;
|
|
3313
|
-
case 2:
|
|
3314
|
-
this.traceCleanupSolver = new TraceCleanupSolver({
|
|
3315
|
-
inputProblem: this.inputProblem,
|
|
3316
|
-
allTraces: this.overlapAvoidanceSolver.getOutput().allTraces,
|
|
3317
|
-
targetTraceIds: new Set(
|
|
3318
|
-
this.overlapAvoidanceSolver.getOutput().modifiedTraces.map(
|
|
3319
|
-
(t) => t.mspPairId
|
|
3320
|
-
)
|
|
3321
|
-
),
|
|
3322
|
-
allLabelPlacements: this.labelMergingSolver.getOutput().netLabelPlacements,
|
|
3323
|
-
mergedLabelNetIdMap: this.labelMergingSolver.getOutput().mergedLabelNetIdMap,
|
|
3324
|
-
paddingBuffer: 0.01
|
|
3325
|
-
});
|
|
3326
|
-
this.activeSubSolver = this.traceCleanupSolver;
|
|
3327
|
-
break;
|
|
3328
2892
|
default:
|
|
3329
2893
|
this.solved = true;
|
|
3330
2894
|
break;
|
|
3331
2895
|
}
|
|
3332
|
-
}
|
|
3333
|
-
getOutput() {
|
|
3334
|
-
return {
|
|
3335
|
-
traces: this.
|
|
3336
|
-
netLabelPlacements: this.labelMergingSolver?.getOutput().netLabelPlacements ?? this.netLabelPlacements
|
|
3337
|
-
};
|
|
2896
|
+
}
|
|
2897
|
+
getOutput() {
|
|
2898
|
+
return {
|
|
2899
|
+
traces: this.overlapAvoidanceSolver?.getOutput().allTraces ?? this.traces,
|
|
2900
|
+
netLabelPlacements: this.labelMergingSolver?.getOutput().netLabelPlacements ?? this.netLabelPlacements
|
|
2901
|
+
};
|
|
2902
|
+
}
|
|
2903
|
+
visualize() {
|
|
2904
|
+
if (this.activeSubSolver) {
|
|
2905
|
+
return this.activeSubSolver.visualize();
|
|
2906
|
+
}
|
|
2907
|
+
const graphics = visualizeInputProblem(this.inputProblem);
|
|
2908
|
+
if (!graphics.lines) graphics.lines = [];
|
|
2909
|
+
if (!graphics.rects) graphics.rects = [];
|
|
2910
|
+
const output = this.getOutput();
|
|
2911
|
+
for (const trace of output.traces) {
|
|
2912
|
+
graphics.lines.push({
|
|
2913
|
+
points: trace.tracePath,
|
|
2914
|
+
strokeColor: "purple"
|
|
2915
|
+
});
|
|
2916
|
+
}
|
|
2917
|
+
for (const label of output.netLabelPlacements) {
|
|
2918
|
+
const color = getColorFromString(label.globalConnNetId, 0.3);
|
|
2919
|
+
graphics.rects.push({
|
|
2920
|
+
center: label.center,
|
|
2921
|
+
width: label.width,
|
|
2922
|
+
height: label.height,
|
|
2923
|
+
fill: color,
|
|
2924
|
+
stroke: color.replace("0.3", "1"),
|
|
2925
|
+
label: label.globalConnNetId
|
|
2926
|
+
});
|
|
2927
|
+
}
|
|
2928
|
+
return graphics;
|
|
2929
|
+
}
|
|
2930
|
+
};
|
|
2931
|
+
|
|
2932
|
+
// lib/solvers/SchematicTracePipelineSolver/correctPinsInsideChip.ts
|
|
2933
|
+
var correctPinsInsideChips = (problem) => {
|
|
2934
|
+
for (const chip of problem.chips) {
|
|
2935
|
+
const bounds = getInputChipBounds(chip);
|
|
2936
|
+
for (const pin of chip.pins) {
|
|
2937
|
+
const isInside = pin.x > bounds.minX && pin.x < bounds.maxX && pin.y > bounds.minY && pin.y < bounds.maxY;
|
|
2938
|
+
if (!isInside) continue;
|
|
2939
|
+
const distLeft = pin.x - bounds.minX;
|
|
2940
|
+
const distRight = bounds.maxX - pin.x;
|
|
2941
|
+
const distBottom = pin.y - bounds.minY;
|
|
2942
|
+
const distTop = bounds.maxY - pin.y;
|
|
2943
|
+
const minDist = Math.min(distLeft, distRight, distBottom, distTop);
|
|
2944
|
+
if (minDist === distLeft) {
|
|
2945
|
+
pin.x = bounds.minX;
|
|
2946
|
+
} else if (minDist === distRight) {
|
|
2947
|
+
pin.x = bounds.maxX;
|
|
2948
|
+
} else if (minDist === distBottom) {
|
|
2949
|
+
pin.y = bounds.minY;
|
|
2950
|
+
} else {
|
|
2951
|
+
pin.y = bounds.maxY;
|
|
2952
|
+
}
|
|
2953
|
+
pin._facingDirection = void 0;
|
|
2954
|
+
}
|
|
2955
|
+
}
|
|
2956
|
+
};
|
|
2957
|
+
|
|
2958
|
+
// lib/solvers/SchematicTracePipelineSolver/expandChipsToFitPins.ts
|
|
2959
|
+
var expandChipsToFitPins = (problem) => {
|
|
2960
|
+
for (const chip of problem.chips) {
|
|
2961
|
+
const halfWidth = chip.width / 2;
|
|
2962
|
+
const halfHeight = chip.height / 2;
|
|
2963
|
+
let maxDx = 0;
|
|
2964
|
+
let maxDy = 0;
|
|
2965
|
+
for (const pin of chip.pins) {
|
|
2966
|
+
const dx = Math.abs(pin.x - chip.center.x);
|
|
2967
|
+
const dy = Math.abs(pin.y - chip.center.y);
|
|
2968
|
+
if (dx > maxDx) maxDx = dx;
|
|
2969
|
+
if (dy > maxDy) maxDy = dy;
|
|
2970
|
+
}
|
|
2971
|
+
const newHalfWidth = Math.max(halfWidth, maxDx);
|
|
2972
|
+
const newHalfHeight = Math.max(halfHeight, maxDy);
|
|
2973
|
+
if (newHalfWidth > halfWidth || newHalfHeight > halfHeight) {
|
|
2974
|
+
chip.width = newHalfWidth * 2;
|
|
2975
|
+
chip.height = newHalfHeight * 2;
|
|
2976
|
+
for (const pin of chip.pins) {
|
|
2977
|
+
pin._facingDirection = void 0;
|
|
2978
|
+
}
|
|
2979
|
+
}
|
|
2980
|
+
}
|
|
2981
|
+
};
|
|
2982
|
+
|
|
2983
|
+
// lib/utils/does-trace-overlap-with-existing-traces.ts
|
|
2984
|
+
import { doSegmentsIntersect } from "@tscircuit/math-utils";
|
|
2985
|
+
function doesTraceOverlapWithExistingTraces(newTracePath, existingTraces) {
|
|
2986
|
+
for (let i = 0; i < newTracePath.length - 1; i++) {
|
|
2987
|
+
const newSegmentP1 = newTracePath[i];
|
|
2988
|
+
const newSegmentP2 = newTracePath[i + 1];
|
|
2989
|
+
for (const existingTrace of existingTraces) {
|
|
2990
|
+
for (let j = 0; j < existingTrace.tracePath.length - 1; j++) {
|
|
2991
|
+
const existingSegmentP1 = existingTrace.tracePath[j];
|
|
2992
|
+
const existingSegmentP2 = existingTrace.tracePath[j + 1];
|
|
2993
|
+
if (doSegmentsIntersect(
|
|
2994
|
+
newSegmentP1,
|
|
2995
|
+
newSegmentP2,
|
|
2996
|
+
existingSegmentP1,
|
|
2997
|
+
existingSegmentP2
|
|
2998
|
+
)) {
|
|
2999
|
+
return true;
|
|
3000
|
+
}
|
|
3001
|
+
}
|
|
3002
|
+
}
|
|
3003
|
+
}
|
|
3004
|
+
return false;
|
|
3005
|
+
}
|
|
3006
|
+
|
|
3007
|
+
// lib/solvers/LongDistancePairSolver/LongDistancePairSolver.ts
|
|
3008
|
+
var NEAREST_NEIGHBOR_COUNT = 3;
|
|
3009
|
+
var distance = (p1, p2) => {
|
|
3010
|
+
return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));
|
|
3011
|
+
};
|
|
3012
|
+
var LongDistancePairSolver = class extends BaseSolver {
|
|
3013
|
+
constructor(params) {
|
|
3014
|
+
super();
|
|
3015
|
+
this.params = params;
|
|
3016
|
+
const { inputProblem, primaryMspConnectionPairs, alreadySolvedTraces } = this.params;
|
|
3017
|
+
this.inputProblem = inputProblem;
|
|
3018
|
+
this.allSolvedTraces = [...alreadySolvedTraces];
|
|
3019
|
+
const primaryConnectedPinIds = /* @__PURE__ */ new Set();
|
|
3020
|
+
for (const pair of primaryMspConnectionPairs) {
|
|
3021
|
+
primaryConnectedPinIds.add(pair.pins[0].pinId);
|
|
3022
|
+
primaryConnectedPinIds.add(pair.pins[1].pinId);
|
|
3023
|
+
}
|
|
3024
|
+
const { netConnMap } = getConnectivityMapsFromInputProblem(inputProblem);
|
|
3025
|
+
this.netConnMap = netConnMap;
|
|
3026
|
+
const pinMap = /* @__PURE__ */ new Map();
|
|
3027
|
+
for (const chip of inputProblem.chips) {
|
|
3028
|
+
this.chipMap[chip.chipId] = chip;
|
|
3029
|
+
for (const pin of chip.pins) {
|
|
3030
|
+
pinMap.set(pin.pinId, { ...pin, chipId: chip.chipId });
|
|
3031
|
+
}
|
|
3032
|
+
}
|
|
3033
|
+
const candidatePairs = [];
|
|
3034
|
+
const addedPairKeys = /* @__PURE__ */ new Set();
|
|
3035
|
+
for (const netId of Object.keys(netConnMap.netMap)) {
|
|
3036
|
+
const allPinIdsInNet = netConnMap.getIdsConnectedToNet(netId);
|
|
3037
|
+
if (allPinIdsInNet.length < 2) continue;
|
|
3038
|
+
const unconnectedPinIds = allPinIdsInNet.filter(
|
|
3039
|
+
(pinId) => !primaryConnectedPinIds.has(pinId)
|
|
3040
|
+
);
|
|
3041
|
+
for (const unconnectedPinId of unconnectedPinIds) {
|
|
3042
|
+
const sourcePin = pinMap.get(unconnectedPinId);
|
|
3043
|
+
if (!sourcePin) continue;
|
|
3044
|
+
const neighbors = allPinIdsInNet.filter((otherPinId) => otherPinId !== unconnectedPinId).flatMap((otherPinId) => {
|
|
3045
|
+
const targetPin = pinMap.get(otherPinId);
|
|
3046
|
+
if (!targetPin) return [];
|
|
3047
|
+
return [
|
|
3048
|
+
{
|
|
3049
|
+
pin: targetPin,
|
|
3050
|
+
distance: distance(sourcePin, targetPin)
|
|
3051
|
+
}
|
|
3052
|
+
];
|
|
3053
|
+
}).sort((a, b) => a.distance - b.distance).slice(0, NEAREST_NEIGHBOR_COUNT);
|
|
3054
|
+
for (const neighbor of neighbors) {
|
|
3055
|
+
const pair = [sourcePin, neighbor.pin];
|
|
3056
|
+
const pairKey = pair.map((p) => p.pinId).sort().join("--");
|
|
3057
|
+
if (!addedPairKeys.has(pairKey)) {
|
|
3058
|
+
candidatePairs.push(pair);
|
|
3059
|
+
addedPairKeys.add(pairKey);
|
|
3060
|
+
}
|
|
3061
|
+
}
|
|
3062
|
+
}
|
|
3063
|
+
}
|
|
3064
|
+
this.queuedCandidatePairs = candidatePairs;
|
|
3065
|
+
}
|
|
3066
|
+
solvedLongDistanceTraces = [];
|
|
3067
|
+
queuedCandidatePairs = [];
|
|
3068
|
+
currentCandidatePair = null;
|
|
3069
|
+
subSolver = null;
|
|
3070
|
+
chipMap = {};
|
|
3071
|
+
inputProblem;
|
|
3072
|
+
netConnMap;
|
|
3073
|
+
newlyConnectedPinIds = /* @__PURE__ */ new Set();
|
|
3074
|
+
allSolvedTraces = [];
|
|
3075
|
+
getConstructorParams() {
|
|
3076
|
+
return this.params;
|
|
3077
|
+
}
|
|
3078
|
+
_step() {
|
|
3079
|
+
if (this.subSolver?.solved) {
|
|
3080
|
+
const newTracePath = this.subSolver.solvedTracePath;
|
|
3081
|
+
if (newTracePath && this.currentCandidatePair) {
|
|
3082
|
+
const isTraceClear = !doesTraceOverlapWithExistingTraces(
|
|
3083
|
+
newTracePath,
|
|
3084
|
+
this.allSolvedTraces
|
|
3085
|
+
);
|
|
3086
|
+
if (isTraceClear) {
|
|
3087
|
+
const [p1, p2] = this.currentCandidatePair;
|
|
3088
|
+
const globalConnNetId = this.netConnMap.getNetConnectedToId(p1.pinId);
|
|
3089
|
+
const mspPairId = `${p1.pinId}-${p2.pinId}`;
|
|
3090
|
+
const newSolvedTrace = {
|
|
3091
|
+
mspPairId,
|
|
3092
|
+
dcConnNetId: globalConnNetId,
|
|
3093
|
+
globalConnNetId,
|
|
3094
|
+
pins: [p1, p2],
|
|
3095
|
+
tracePath: newTracePath,
|
|
3096
|
+
mspConnectionPairIds: [mspPairId],
|
|
3097
|
+
pinIds: [p1.pinId, p2.pinId]
|
|
3098
|
+
};
|
|
3099
|
+
this.solvedLongDistanceTraces.push(newSolvedTrace);
|
|
3100
|
+
this.allSolvedTraces.push(newSolvedTrace);
|
|
3101
|
+
this.newlyConnectedPinIds.add(p1.pinId);
|
|
3102
|
+
this.newlyConnectedPinIds.add(p2.pinId);
|
|
3103
|
+
}
|
|
3104
|
+
}
|
|
3105
|
+
this.subSolver = null;
|
|
3106
|
+
this.currentCandidatePair = null;
|
|
3107
|
+
} else if (this.subSolver?.failed) {
|
|
3108
|
+
this.subSolver = null;
|
|
3109
|
+
this.currentCandidatePair = null;
|
|
3110
|
+
}
|
|
3111
|
+
if (this.subSolver) {
|
|
3112
|
+
this.subSolver.step();
|
|
3113
|
+
return;
|
|
3114
|
+
}
|
|
3115
|
+
while (this.queuedCandidatePairs.length > 0) {
|
|
3116
|
+
const nextPair = this.queuedCandidatePairs.shift();
|
|
3117
|
+
const [p1, p2] = nextPair;
|
|
3118
|
+
if (this.newlyConnectedPinIds.has(p1.pinId) || this.newlyConnectedPinIds.has(p2.pinId)) {
|
|
3119
|
+
continue;
|
|
3120
|
+
}
|
|
3121
|
+
this.currentCandidatePair = nextPair;
|
|
3122
|
+
this.subSolver = new SchematicTraceSingleLineSolver2({
|
|
3123
|
+
inputProblem: this.params.inputProblem,
|
|
3124
|
+
pins: this.currentCandidatePair,
|
|
3125
|
+
chipMap: this.chipMap
|
|
3126
|
+
});
|
|
3127
|
+
return;
|
|
3128
|
+
}
|
|
3129
|
+
this.solved = true;
|
|
3338
3130
|
}
|
|
3339
3131
|
visualize() {
|
|
3340
|
-
if (this.
|
|
3341
|
-
return this.
|
|
3132
|
+
if (this.subSolver) {
|
|
3133
|
+
return this.subSolver.visualize();
|
|
3342
3134
|
}
|
|
3343
3135
|
const graphics = visualizeInputProblem(this.inputProblem);
|
|
3344
|
-
|
|
3345
|
-
if (!graphics.rects) graphics.rects = [];
|
|
3346
|
-
const output = this.getOutput();
|
|
3347
|
-
for (const trace of output.traces) {
|
|
3136
|
+
for (const trace of this.solvedLongDistanceTraces) {
|
|
3348
3137
|
graphics.lines.push({
|
|
3349
3138
|
points: trace.tracePath,
|
|
3350
3139
|
strokeColor: "purple"
|
|
3351
3140
|
});
|
|
3352
3141
|
}
|
|
3353
|
-
for (const
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
height: label.height,
|
|
3359
|
-
fill: color,
|
|
3360
|
-
stroke: color.replace("0.3", "1"),
|
|
3361
|
-
label: label.globalConnNetId
|
|
3142
|
+
for (const [p1, p2] of this.queuedCandidatePairs) {
|
|
3143
|
+
graphics.lines.push({
|
|
3144
|
+
points: [p1, p2],
|
|
3145
|
+
strokeColor: "gray",
|
|
3146
|
+
strokeDash: "4 4"
|
|
3362
3147
|
});
|
|
3363
3148
|
}
|
|
3364
3149
|
return graphics;
|
|
3365
3150
|
}
|
|
3151
|
+
getOutput() {
|
|
3152
|
+
if (!this.solved) {
|
|
3153
|
+
return { newTraces: [], allTracesMerged: this.params.alreadySolvedTraces };
|
|
3154
|
+
}
|
|
3155
|
+
return {
|
|
3156
|
+
newTraces: this.solvedLongDistanceTraces,
|
|
3157
|
+
allTracesMerged: [
|
|
3158
|
+
...this.params.alreadySolvedTraces,
|
|
3159
|
+
...this.solvedLongDistanceTraces
|
|
3160
|
+
]
|
|
3161
|
+
};
|
|
3162
|
+
}
|
|
3366
3163
|
};
|
|
3367
3164
|
|
|
3368
|
-
// lib/solvers/
|
|
3369
|
-
var
|
|
3370
|
-
for (
|
|
3371
|
-
const
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
if (
|
|
3375
|
-
|
|
3376
|
-
const distRight = bounds.maxX - pin.x;
|
|
3377
|
-
const distBottom = pin.y - bounds.minY;
|
|
3378
|
-
const distTop = bounds.maxY - pin.y;
|
|
3379
|
-
const minDist = Math.min(distLeft, distRight, distBottom, distTop);
|
|
3380
|
-
if (minDist === distLeft) {
|
|
3381
|
-
pin.x = bounds.minX;
|
|
3382
|
-
} else if (minDist === distRight) {
|
|
3383
|
-
pin.x = bounds.maxX;
|
|
3384
|
-
} else if (minDist === distBottom) {
|
|
3385
|
-
pin.y = bounds.minY;
|
|
3386
|
-
} else {
|
|
3387
|
-
pin.y = bounds.maxY;
|
|
3165
|
+
// lib/solvers/TraceCleanupSolver/hasCollisions.ts
|
|
3166
|
+
var hasCollisions = (pathSegments, obstacles) => {
|
|
3167
|
+
for (let i = 0; i < pathSegments.length - 1; i++) {
|
|
3168
|
+
const p1 = pathSegments[i];
|
|
3169
|
+
const p2 = pathSegments[i + 1];
|
|
3170
|
+
for (const obstacle of obstacles) {
|
|
3171
|
+
if (segmentIntersectsRect(p1, p2, obstacle)) {
|
|
3172
|
+
return true;
|
|
3388
3173
|
}
|
|
3389
|
-
pin._facingDirection = void 0;
|
|
3390
3174
|
}
|
|
3391
3175
|
}
|
|
3176
|
+
return false;
|
|
3392
3177
|
};
|
|
3393
3178
|
|
|
3394
|
-
// lib/solvers/
|
|
3395
|
-
var
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
const
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
if (dy > maxDy) maxDy = dy;
|
|
3179
|
+
// lib/solvers/TraceCleanupSolver/countTurns.ts
|
|
3180
|
+
var countTurns = (points) => {
|
|
3181
|
+
let turns = 0;
|
|
3182
|
+
for (let i = 1; i < points.length - 1; i++) {
|
|
3183
|
+
const prev = points[i - 1];
|
|
3184
|
+
const curr = points[i];
|
|
3185
|
+
const next = points[i + 1];
|
|
3186
|
+
const prevVertical = prev.x === curr.x;
|
|
3187
|
+
const nextVertical = curr.x === next.x;
|
|
3188
|
+
if (prevVertical !== nextVertical) {
|
|
3189
|
+
turns++;
|
|
3406
3190
|
}
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3191
|
+
}
|
|
3192
|
+
return turns;
|
|
3193
|
+
};
|
|
3194
|
+
|
|
3195
|
+
// lib/solvers/TraceCleanupSolver/tryConnectPoints.ts
|
|
3196
|
+
var tryConnectPoints = (start, end) => {
|
|
3197
|
+
const candidates = [];
|
|
3198
|
+
if (start.x === end.x || start.y === end.y) {
|
|
3199
|
+
candidates.push([start, end]);
|
|
3200
|
+
} else {
|
|
3201
|
+
candidates.push([start, { x: end.x, y: start.y }, end]);
|
|
3202
|
+
candidates.push([start, { x: start.x, y: end.y }, end]);
|
|
3203
|
+
}
|
|
3204
|
+
return candidates;
|
|
3205
|
+
};
|
|
3206
|
+
|
|
3207
|
+
// lib/solvers/TraceCleanupSolver/hasCollisionsWithLabels.ts
|
|
3208
|
+
var hasCollisionsWithLabels = (pathSegments, labels) => {
|
|
3209
|
+
for (let i = 0; i < pathSegments.length - 1; i++) {
|
|
3210
|
+
const p1 = pathSegments[i];
|
|
3211
|
+
const p2 = pathSegments[i + 1];
|
|
3212
|
+
for (const label of labels) {
|
|
3213
|
+
if (segmentIntersectsRect(p1, p2, label)) {
|
|
3214
|
+
return true;
|
|
3215
|
+
}
|
|
3216
|
+
}
|
|
3217
|
+
}
|
|
3218
|
+
return false;
|
|
3219
|
+
};
|
|
3220
|
+
|
|
3221
|
+
// lib/solvers/TraceCleanupSolver/turnMinimization.ts
|
|
3222
|
+
var minimizeTurns = ({
|
|
3223
|
+
path,
|
|
3224
|
+
obstacles,
|
|
3225
|
+
labelBounds
|
|
3226
|
+
}) => {
|
|
3227
|
+
if (path.length <= 2) {
|
|
3228
|
+
return path;
|
|
3229
|
+
}
|
|
3230
|
+
const recognizeStairStepPattern = (pathToCheck, startIdx) => {
|
|
3231
|
+
if (startIdx >= pathToCheck.length - 3) return -1;
|
|
3232
|
+
let endIdx = startIdx;
|
|
3233
|
+
let isStairStep = true;
|
|
3234
|
+
for (let i = startIdx; i < pathToCheck.length - 2 && i < startIdx + 10; i++) {
|
|
3235
|
+
if (i + 2 >= pathToCheck.length) break;
|
|
3236
|
+
const p1 = pathToCheck[i];
|
|
3237
|
+
const p2 = pathToCheck[i + 1];
|
|
3238
|
+
const p3 = pathToCheck[i + 2];
|
|
3239
|
+
const seg1Vertical = p1.x === p2.x;
|
|
3240
|
+
const seg2Vertical = p2.x === p3.x;
|
|
3241
|
+
if (seg1Vertical === seg2Vertical) {
|
|
3242
|
+
break;
|
|
3243
|
+
}
|
|
3244
|
+
const seg1Direction = seg1Vertical ? Math.sign(p2.y - p1.y) : Math.sign(p2.x - p1.x);
|
|
3245
|
+
if (i > startIdx) {
|
|
3246
|
+
const prevP = pathToCheck[i - 1];
|
|
3247
|
+
const prevSegVertical = prevP.x === p1.x;
|
|
3248
|
+
const prevDirection = prevSegVertical ? Math.sign(p1.y - prevP.y) : Math.sign(p1.x - prevP.x);
|
|
3249
|
+
if (seg1Vertical && prevSegVertical && seg1Direction !== prevDirection || !seg1Vertical && !prevSegVertical && seg1Direction !== prevDirection) {
|
|
3250
|
+
isStairStep = false;
|
|
3251
|
+
break;
|
|
3252
|
+
}
|
|
3253
|
+
}
|
|
3254
|
+
endIdx = i + 2;
|
|
3255
|
+
}
|
|
3256
|
+
return isStairStep && endIdx - startIdx >= 3 ? endIdx : -1;
|
|
3257
|
+
};
|
|
3258
|
+
let optimizedPath = [...path];
|
|
3259
|
+
let currentTurns = countTurns(optimizedPath);
|
|
3260
|
+
let improved = true;
|
|
3261
|
+
while (improved) {
|
|
3262
|
+
improved = false;
|
|
3263
|
+
for (let startIdx = 0; startIdx < optimizedPath.length - 3; startIdx++) {
|
|
3264
|
+
const stairEndIdx = recognizeStairStepPattern(optimizedPath, startIdx);
|
|
3265
|
+
if (stairEndIdx > 0) {
|
|
3266
|
+
const startPoint = optimizedPath[startIdx];
|
|
3267
|
+
const endPoint = optimizedPath[stairEndIdx];
|
|
3268
|
+
const connectionOptions = tryConnectPoints(startPoint, endPoint);
|
|
3269
|
+
for (const connection of connectionOptions) {
|
|
3270
|
+
const testPath = [
|
|
3271
|
+
...optimizedPath.slice(0, startIdx + 1),
|
|
3272
|
+
...connection.slice(1, -1),
|
|
3273
|
+
...optimizedPath.slice(stairEndIdx)
|
|
3274
|
+
];
|
|
3275
|
+
const collidesWithObstacles = hasCollisions(connection, obstacles);
|
|
3276
|
+
const collidesWithLabels = hasCollisionsWithLabels(
|
|
3277
|
+
connection,
|
|
3278
|
+
labelBounds
|
|
3279
|
+
);
|
|
3280
|
+
if (!collidesWithObstacles && !collidesWithLabels) {
|
|
3281
|
+
const newTurns = countTurns(testPath);
|
|
3282
|
+
optimizedPath = testPath;
|
|
3283
|
+
currentTurns = newTurns;
|
|
3284
|
+
improved = true;
|
|
3285
|
+
break;
|
|
3286
|
+
}
|
|
3287
|
+
}
|
|
3288
|
+
if (improved) break;
|
|
3289
|
+
}
|
|
3290
|
+
}
|
|
3291
|
+
if (!improved) {
|
|
3292
|
+
for (let startIdx = 0; startIdx < optimizedPath.length - 2; startIdx++) {
|
|
3293
|
+
const maxRemove = Math.min(
|
|
3294
|
+
optimizedPath.length - startIdx - 2,
|
|
3295
|
+
optimizedPath.length - 2
|
|
3296
|
+
);
|
|
3297
|
+
for (let removeCount = 1; removeCount <= maxRemove; removeCount++) {
|
|
3298
|
+
const endIdx = startIdx + removeCount + 1;
|
|
3299
|
+
if (endIdx >= optimizedPath.length) continue;
|
|
3300
|
+
const startPoint = optimizedPath[startIdx];
|
|
3301
|
+
const endPoint = optimizedPath[endIdx];
|
|
3302
|
+
const connectionOptions = tryConnectPoints(startPoint, endPoint);
|
|
3303
|
+
for (const connection of connectionOptions) {
|
|
3304
|
+
const testPath = [
|
|
3305
|
+
...optimizedPath.slice(0, startIdx + 1),
|
|
3306
|
+
...connection.slice(1, -1),
|
|
3307
|
+
...optimizedPath.slice(endIdx)
|
|
3308
|
+
];
|
|
3309
|
+
const connectionSegments = connection;
|
|
3310
|
+
const collidesWithObstacles = hasCollisions(
|
|
3311
|
+
connectionSegments,
|
|
3312
|
+
obstacles
|
|
3313
|
+
);
|
|
3314
|
+
const collidesWithLabels = hasCollisionsWithLabels(
|
|
3315
|
+
connectionSegments,
|
|
3316
|
+
labelBounds
|
|
3317
|
+
);
|
|
3318
|
+
if (!collidesWithObstacles && !collidesWithLabels) {
|
|
3319
|
+
const newTurns = countTurns(testPath);
|
|
3320
|
+
if (newTurns < currentTurns || newTurns === currentTurns && testPath.length < optimizedPath.length) {
|
|
3321
|
+
optimizedPath = testPath;
|
|
3322
|
+
currentTurns = newTurns;
|
|
3323
|
+
improved = true;
|
|
3324
|
+
break;
|
|
3325
|
+
}
|
|
3326
|
+
}
|
|
3327
|
+
}
|
|
3328
|
+
if (improved) break;
|
|
3329
|
+
}
|
|
3330
|
+
if (improved) break;
|
|
3331
|
+
}
|
|
3332
|
+
}
|
|
3333
|
+
if (!improved) {
|
|
3334
|
+
for (let i = 0; i < optimizedPath.length - 2; i++) {
|
|
3335
|
+
const p1 = optimizedPath[i];
|
|
3336
|
+
const p2 = optimizedPath[i + 1];
|
|
3337
|
+
const p3 = optimizedPath[i + 2];
|
|
3338
|
+
const allVertical = p1.x === p2.x && p2.x === p3.x;
|
|
3339
|
+
const allHorizontal = p1.y === p2.y && p2.y === p3.y;
|
|
3340
|
+
if (allVertical || allHorizontal) {
|
|
3341
|
+
const testPath = [
|
|
3342
|
+
...optimizedPath.slice(0, i + 1),
|
|
3343
|
+
...optimizedPath.slice(i + 2)
|
|
3344
|
+
];
|
|
3345
|
+
const collidesWithObstacles = hasCollisions([p1, p3], obstacles);
|
|
3346
|
+
const collidesWithLabels = hasCollisionsWithLabels(
|
|
3347
|
+
[p1, p3],
|
|
3348
|
+
labelBounds
|
|
3349
|
+
);
|
|
3350
|
+
if (!collidesWithObstacles && !collidesWithLabels) {
|
|
3351
|
+
optimizedPath = testPath;
|
|
3352
|
+
improved = true;
|
|
3353
|
+
break;
|
|
3354
|
+
}
|
|
3355
|
+
}
|
|
3414
3356
|
}
|
|
3415
3357
|
}
|
|
3416
3358
|
}
|
|
3359
|
+
const finalSimplifiedPath = simplifyPath(optimizedPath);
|
|
3360
|
+
return finalSimplifiedPath;
|
|
3417
3361
|
};
|
|
3418
3362
|
|
|
3419
|
-
// lib/
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
existingSegmentP2
|
|
3434
|
-
)) {
|
|
3435
|
-
return true;
|
|
3436
|
-
}
|
|
3437
|
-
}
|
|
3438
|
-
}
|
|
3363
|
+
// lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts
|
|
3364
|
+
var minimizeTurnsWithFilteredLabels = ({
|
|
3365
|
+
targetMspConnectionPairId,
|
|
3366
|
+
traces,
|
|
3367
|
+
inputProblem,
|
|
3368
|
+
allLabelPlacements,
|
|
3369
|
+
mergedLabelNetIdMap,
|
|
3370
|
+
paddingBuffer
|
|
3371
|
+
}) => {
|
|
3372
|
+
const targetTrace = traces.find(
|
|
3373
|
+
(t) => t.mspPairId === targetMspConnectionPairId
|
|
3374
|
+
);
|
|
3375
|
+
if (!targetTrace) {
|
|
3376
|
+
throw new Error(`Target trace ${targetMspConnectionPairId} not found`);
|
|
3439
3377
|
}
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3378
|
+
const obstacleTraces = traces.filter(
|
|
3379
|
+
(t) => t.mspPairId !== targetMspConnectionPairId
|
|
3380
|
+
);
|
|
3381
|
+
const TRACE_WIDTH = 0.01;
|
|
3382
|
+
const traceObstacles = obstacleTraces.flatMap(
|
|
3383
|
+
(trace, i) => trace.tracePath.slice(0, -1).map((p1, pi) => {
|
|
3384
|
+
const p2 = trace.tracePath[pi + 1];
|
|
3385
|
+
return {
|
|
3386
|
+
chipId: `trace-obstacle-${i}-${pi}`,
|
|
3387
|
+
minX: Math.min(p1.x, p2.x) - TRACE_WIDTH / 2,
|
|
3388
|
+
minY: Math.min(p1.y, p2.y) - TRACE_WIDTH / 2,
|
|
3389
|
+
maxX: Math.max(p1.x, p2.x) + TRACE_WIDTH / 2,
|
|
3390
|
+
maxY: Math.max(p1.y, p2.y) + TRACE_WIDTH / 2
|
|
3391
|
+
};
|
|
3392
|
+
})
|
|
3393
|
+
);
|
|
3394
|
+
const staticObstacles = getObstacleRects(inputProblem);
|
|
3395
|
+
const combinedObstacles = [...staticObstacles, ...traceObstacles];
|
|
3396
|
+
const originalPath = targetTrace.tracePath;
|
|
3397
|
+
const filteredLabels = allLabelPlacements.filter((label) => {
|
|
3398
|
+
const originalNetIds = mergedLabelNetIdMap[label.globalConnNetId];
|
|
3399
|
+
if (originalNetIds) {
|
|
3400
|
+
return !originalNetIds.has(targetTrace.globalConnNetId);
|
|
3401
|
+
}
|
|
3402
|
+
return label.globalConnNetId !== targetTrace.globalConnNetId;
|
|
3403
|
+
});
|
|
3404
|
+
const labelBounds = filteredLabels.map((nl) => ({
|
|
3405
|
+
minX: nl.center.x - nl.width / 2 - paddingBuffer,
|
|
3406
|
+
maxX: nl.center.x + nl.width / 2 + paddingBuffer,
|
|
3407
|
+
minY: nl.center.y - nl.height / 2 - paddingBuffer,
|
|
3408
|
+
maxY: nl.center.y + nl.height / 2 + paddingBuffer
|
|
3409
|
+
}));
|
|
3410
|
+
const newPath = minimizeTurns({
|
|
3411
|
+
path: originalPath,
|
|
3412
|
+
obstacles: combinedObstacles,
|
|
3413
|
+
labelBounds
|
|
3414
|
+
});
|
|
3415
|
+
return {
|
|
3416
|
+
...targetTrace,
|
|
3417
|
+
tracePath: newPath
|
|
3418
|
+
};
|
|
3447
3419
|
};
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3420
|
+
|
|
3421
|
+
// lib/solvers/TraceCleanupSolver/balanceZShapes.ts
|
|
3422
|
+
var balanceZShapes = ({
|
|
3423
|
+
targetMspConnectionPairId,
|
|
3424
|
+
traces,
|
|
3425
|
+
inputProblem,
|
|
3426
|
+
allLabelPlacements,
|
|
3427
|
+
mergedLabelNetIdMap,
|
|
3428
|
+
paddingBuffer
|
|
3429
|
+
}) => {
|
|
3430
|
+
const targetTrace = traces.find(
|
|
3431
|
+
(t) => t.mspPairId === targetMspConnectionPairId
|
|
3432
|
+
);
|
|
3433
|
+
if (!targetTrace) {
|
|
3434
|
+
throw new Error(`Target trace ${targetMspConnectionPairId} not found`);
|
|
3435
|
+
}
|
|
3436
|
+
const TOLERANCE = 1e-5;
|
|
3437
|
+
const obstacleTraces = traces.filter(
|
|
3438
|
+
(t) => t.mspPairId !== targetMspConnectionPairId
|
|
3439
|
+
);
|
|
3440
|
+
const TRACE_WIDTH = 0.01;
|
|
3441
|
+
const traceObstacles = obstacleTraces.flatMap(
|
|
3442
|
+
(trace, i) => trace.tracePath.slice(0, -1).map((p1, pi) => {
|
|
3443
|
+
const p2 = trace.tracePath[pi + 1];
|
|
3444
|
+
return {
|
|
3445
|
+
chipId: `trace-obstacle-${i}-${pi}`,
|
|
3446
|
+
minX: Math.min(p1.x, p2.x) - TRACE_WIDTH / 2,
|
|
3447
|
+
minY: Math.min(p1.y, p2.y) - TRACE_WIDTH / 2,
|
|
3448
|
+
maxX: Math.max(p1.x, p2.x) + TRACE_WIDTH / 2,
|
|
3449
|
+
maxY: Math.max(p1.y, p2.y) + TRACE_WIDTH / 2
|
|
3450
|
+
};
|
|
3451
|
+
})
|
|
3452
|
+
);
|
|
3453
|
+
const staticObstacles = getObstacleRects(inputProblem).map((obs) => ({
|
|
3454
|
+
...obs,
|
|
3455
|
+
minX: obs.minX + TOLERANCE,
|
|
3456
|
+
maxX: obs.maxX - TOLERANCE,
|
|
3457
|
+
minY: obs.minY + TOLERANCE,
|
|
3458
|
+
maxY: obs.maxY - TOLERANCE
|
|
3459
|
+
}));
|
|
3460
|
+
const combinedObstacles = [...staticObstacles, ...traceObstacles];
|
|
3461
|
+
const segmentIntersectsAnyRect = (p1, p2, rects) => {
|
|
3462
|
+
for (const rect of rects) {
|
|
3463
|
+
if (segmentIntersectsRect(p1, p2, rect)) {
|
|
3464
|
+
return true;
|
|
3467
3465
|
}
|
|
3468
3466
|
}
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3467
|
+
return false;
|
|
3468
|
+
};
|
|
3469
|
+
const filteredLabels = allLabelPlacements.filter((label) => {
|
|
3470
|
+
const originalNetIds = mergedLabelNetIdMap[label.globalConnNetId];
|
|
3471
|
+
if (originalNetIds) {
|
|
3472
|
+
return !originalNetIds.has(targetTrace.globalConnNetId);
|
|
3473
|
+
}
|
|
3474
|
+
return label.globalConnNetId !== targetTrace.globalConnNetId;
|
|
3475
|
+
});
|
|
3476
|
+
const labelBounds = filteredLabels.map((nl) => ({
|
|
3477
|
+
minX: nl.center.x - nl.width / 2 + TOLERANCE,
|
|
3478
|
+
maxX: nl.center.x + nl.width / 2 - TOLERANCE,
|
|
3479
|
+
minY: nl.center.y - nl.height / 2 + TOLERANCE,
|
|
3480
|
+
maxY: nl.center.y + nl.height / 2 - TOLERANCE
|
|
3481
|
+
}));
|
|
3482
|
+
const newPath = [...targetTrace.tracePath];
|
|
3483
|
+
if (newPath.length < 4) {
|
|
3484
|
+
return { ...targetTrace };
|
|
3485
|
+
}
|
|
3486
|
+
if (newPath.length === 4) {
|
|
3487
|
+
const [p0, p1, p2, p3] = newPath;
|
|
3488
|
+
let p1New;
|
|
3489
|
+
let p2New;
|
|
3490
|
+
const isHVHShape = p0.y === p1.y && p1.x === p2.x && p2.y === p3.y;
|
|
3491
|
+
if (isHVHShape) {
|
|
3492
|
+
const idealX = (p0.x + p3.x) / 2;
|
|
3493
|
+
p1New = { x: idealX, y: p1.y };
|
|
3494
|
+
p2New = { x: idealX, y: p2.y };
|
|
3495
|
+
} else {
|
|
3496
|
+
const idealY = (p0.y + p3.y) / 2;
|
|
3497
|
+
p1New = { x: p1.x, y: idealY };
|
|
3498
|
+
p2New = { x: p2.x, y: idealY };
|
|
3499
|
+
}
|
|
3500
|
+
const collides = segmentIntersectsAnyRect(p0, p1New, combinedObstacles) || segmentIntersectsAnyRect(p1New, p2New, combinedObstacles) || segmentIntersectsAnyRect(p2New, p3, combinedObstacles) || segmentIntersectsAnyRect(p0, p1New, labelBounds) || segmentIntersectsAnyRect(p1New, p2New, labelBounds) || segmentIntersectsAnyRect(p2New, p3, labelBounds);
|
|
3501
|
+
if (!collides) {
|
|
3502
|
+
newPath[1] = p1New;
|
|
3503
|
+
newPath[2] = p2New;
|
|
3504
|
+
}
|
|
3505
|
+
return { ...targetTrace, tracePath: simplifyPath(newPath) };
|
|
3506
|
+
}
|
|
3507
|
+
for (let i = 1; i < newPath.length - 4; i++) {
|
|
3508
|
+
const p1 = newPath[i];
|
|
3509
|
+
const p2 = newPath[i + 1];
|
|
3510
|
+
const p3 = newPath[i + 2];
|
|
3511
|
+
const p4 = newPath[i + 3];
|
|
3512
|
+
const isHVHZShape = p1.y === p2.y && p2.x === p3.x && p3.y === p4.y;
|
|
3513
|
+
const isVHVZShape = p1.x === p2.x && p2.y === p3.y && p3.x === p4.x;
|
|
3514
|
+
const isCollinearHorizontal = p1.y === p2.y && p2.y === p3.y && p3.y === p4.y;
|
|
3515
|
+
const isCollinearVertical = p1.x === p2.x && p2.x === p3.x && p3.x === p4.x;
|
|
3516
|
+
const isCollinear = isCollinearHorizontal || isCollinearVertical;
|
|
3517
|
+
let isSameDirection = false;
|
|
3518
|
+
if (isHVHZShape) {
|
|
3519
|
+
isSameDirection = Math.sign(p2.x - p1.x) === Math.sign(p4.x - p3.x);
|
|
3520
|
+
} else if (isVHVZShape) {
|
|
3521
|
+
isSameDirection = Math.sign(p2.y - p1.y) === Math.sign(p4.y - p3.y);
|
|
3522
|
+
}
|
|
3523
|
+
const isValidZShape = (isHVHZShape || isVHVZShape) && !isCollinear && isSameDirection;
|
|
3524
|
+
if (!isValidZShape) {
|
|
3525
|
+
continue;
|
|
3526
|
+
}
|
|
3527
|
+
let p2New;
|
|
3528
|
+
let p3New;
|
|
3529
|
+
const len1Original = isHVHZShape ? Math.abs(p1.x - p2.x) : Math.abs(p1.y - p2.y);
|
|
3530
|
+
const len2Original = isHVHZShape ? Math.abs(p3.x - p4.x) : Math.abs(p3.y - p4.y);
|
|
3531
|
+
if (Math.abs(len1Original - len2Original) < 1e-3) {
|
|
3532
|
+
continue;
|
|
3533
|
+
}
|
|
3534
|
+
if (isHVHZShape) {
|
|
3535
|
+
const idealX = (p1.x + p4.x) / 2;
|
|
3536
|
+
p2New = { x: idealX, y: p2.y };
|
|
3537
|
+
p3New = { x: idealX, y: p3.y };
|
|
3538
|
+
} else {
|
|
3539
|
+
const idealY = (p1.y + p4.y) / 2;
|
|
3540
|
+
p2New = { x: p2.x, y: idealY };
|
|
3541
|
+
p3New = { x: p3.x, y: idealY };
|
|
3542
|
+
}
|
|
3543
|
+
const collides = segmentIntersectsAnyRect(p1, p2New, combinedObstacles) || segmentIntersectsAnyRect(p2New, p3New, combinedObstacles) || segmentIntersectsAnyRect(p3New, p4, combinedObstacles) || segmentIntersectsAnyRect(p1, p2New, labelBounds) || segmentIntersectsAnyRect(p2New, p3New, labelBounds) || segmentIntersectsAnyRect(p3New, p4, labelBounds);
|
|
3544
|
+
if (!collides) {
|
|
3545
|
+
newPath[i + 1] = p2New;
|
|
3546
|
+
newPath[i + 2] = p3New;
|
|
3547
|
+
i = 0;
|
|
3499
3548
|
}
|
|
3500
|
-
this.queuedCandidatePairs = candidatePairs;
|
|
3501
3549
|
}
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3550
|
+
const finalSimplifiedPath = simplifyPath(newPath);
|
|
3551
|
+
return {
|
|
3552
|
+
...targetTrace,
|
|
3553
|
+
tracePath: finalSimplifiedPath
|
|
3554
|
+
};
|
|
3555
|
+
};
|
|
3556
|
+
|
|
3557
|
+
// lib/solvers/TraceCleanupSolver/TraceCleanupSolver.ts
|
|
3558
|
+
var TraceCleanupSolver = class extends BaseSolver {
|
|
3559
|
+
input;
|
|
3560
|
+
outputTraces;
|
|
3561
|
+
traceIdQueue;
|
|
3562
|
+
tracesMap;
|
|
3563
|
+
pipelineStep = "minimizing_turns";
|
|
3564
|
+
activeTraceId = null;
|
|
3565
|
+
// New property
|
|
3566
|
+
constructor(solverInput) {
|
|
3567
|
+
super();
|
|
3568
|
+
this.input = solverInput;
|
|
3569
|
+
this.outputTraces = [...solverInput.allTraces];
|
|
3570
|
+
this.tracesMap = new Map(this.outputTraces.map((t) => [t.mspPairId, t]));
|
|
3571
|
+
this.traceIdQueue = Array.from(solverInput.targetTraceIds);
|
|
3513
3572
|
}
|
|
3514
3573
|
_step() {
|
|
3515
|
-
if (this.
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
const isTraceClear = !doesTraceOverlapWithExistingTraces(
|
|
3519
|
-
newTracePath,
|
|
3520
|
-
this.allSolvedTraces
|
|
3521
|
-
);
|
|
3522
|
-
if (isTraceClear) {
|
|
3523
|
-
const [p1, p2] = this.currentCandidatePair;
|
|
3524
|
-
const globalConnNetId = this.netConnMap.getNetConnectedToId(p1.pinId);
|
|
3525
|
-
const mspPairId = `${p1.pinId}-${p2.pinId}`;
|
|
3526
|
-
const newSolvedTrace = {
|
|
3527
|
-
mspPairId,
|
|
3528
|
-
dcConnNetId: globalConnNetId,
|
|
3529
|
-
globalConnNetId,
|
|
3530
|
-
pins: [p1, p2],
|
|
3531
|
-
tracePath: newTracePath,
|
|
3532
|
-
mspConnectionPairIds: [mspPairId],
|
|
3533
|
-
pinIds: [p1.pinId, p2.pinId]
|
|
3534
|
-
};
|
|
3535
|
-
this.solvedLongDistanceTraces.push(newSolvedTrace);
|
|
3536
|
-
this.allSolvedTraces.push(newSolvedTrace);
|
|
3537
|
-
this.newlyConnectedPinIds.add(p1.pinId);
|
|
3538
|
-
this.newlyConnectedPinIds.add(p2.pinId);
|
|
3539
|
-
}
|
|
3540
|
-
}
|
|
3541
|
-
this.subSolver = null;
|
|
3542
|
-
this.currentCandidatePair = null;
|
|
3543
|
-
} else if (this.subSolver?.failed) {
|
|
3544
|
-
this.subSolver = null;
|
|
3545
|
-
this.currentCandidatePair = null;
|
|
3574
|
+
if (this.pipelineStep === "minimizing_turns" && this.traceIdQueue.length === 0) {
|
|
3575
|
+
this.pipelineStep = "balancing_l_shapes";
|
|
3576
|
+
this.traceIdQueue = Array.from(this.input.targetTraceIds);
|
|
3546
3577
|
}
|
|
3547
|
-
if (this.
|
|
3548
|
-
this.
|
|
3578
|
+
if (this.pipelineStep === "balancing_l_shapes" && this.traceIdQueue.length === 0) {
|
|
3579
|
+
this.solved = true;
|
|
3549
3580
|
return;
|
|
3550
3581
|
}
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3582
|
+
const targetMspConnectionPairId = this.traceIdQueue.shift();
|
|
3583
|
+
this.activeTraceId = targetMspConnectionPairId;
|
|
3584
|
+
const originalTrace = this.tracesMap.get(targetMspConnectionPairId);
|
|
3585
|
+
const { tracePath } = originalTrace;
|
|
3586
|
+
const is4PointRectangle = (path) => {
|
|
3587
|
+
if (path.length !== 4) return false;
|
|
3588
|
+
const [p0, p1, p2, p3] = path;
|
|
3589
|
+
const isHVHC = p0.y === p1.y && p1.x === p2.x && p2.y === p3.y && p0.x === p3.x;
|
|
3590
|
+
const isVHVC = p0.x === p1.x && p1.y === p2.y && p2.x === p3.x && p0.y === p3.y;
|
|
3591
|
+
return isHVHC || isVHVC;
|
|
3592
|
+
};
|
|
3593
|
+
if (is4PointRectangle(tracePath)) {
|
|
3563
3594
|
return;
|
|
3564
3595
|
}
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
for (const trace of this.solvedLongDistanceTraces) {
|
|
3573
|
-
graphics.lines.push({
|
|
3574
|
-
points: trace.tracePath,
|
|
3575
|
-
strokeColor: "purple"
|
|
3596
|
+
const allTraces = Array.from(this.tracesMap.values());
|
|
3597
|
+
let updatedTrace;
|
|
3598
|
+
if (this.pipelineStep === "minimizing_turns") {
|
|
3599
|
+
updatedTrace = minimizeTurnsWithFilteredLabels({
|
|
3600
|
+
...this.input,
|
|
3601
|
+
targetMspConnectionPairId,
|
|
3602
|
+
traces: allTraces
|
|
3576
3603
|
});
|
|
3577
|
-
}
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
strokeDash: "4 4"
|
|
3604
|
+
} else {
|
|
3605
|
+
updatedTrace = balanceZShapes({
|
|
3606
|
+
...this.input,
|
|
3607
|
+
targetMspConnectionPairId,
|
|
3608
|
+
traces: allTraces
|
|
3583
3609
|
});
|
|
3584
3610
|
}
|
|
3585
|
-
|
|
3611
|
+
this.tracesMap.set(targetMspConnectionPairId, updatedTrace);
|
|
3612
|
+
this.outputTraces = Array.from(this.tracesMap.values());
|
|
3586
3613
|
}
|
|
3587
3614
|
getOutput() {
|
|
3588
|
-
if (!this.solved) {
|
|
3589
|
-
return { newTraces: [], allTracesMerged: this.params.alreadySolvedTraces };
|
|
3590
|
-
}
|
|
3591
3615
|
return {
|
|
3592
|
-
|
|
3593
|
-
allTracesMerged: [
|
|
3594
|
-
...this.params.alreadySolvedTraces,
|
|
3595
|
-
...this.solvedLongDistanceTraces
|
|
3596
|
-
]
|
|
3616
|
+
traces: this.outputTraces
|
|
3597
3617
|
};
|
|
3598
3618
|
}
|
|
3619
|
+
visualize() {
|
|
3620
|
+
const graphics = visualizeInputProblem(this.input.inputProblem, {
|
|
3621
|
+
chipAlpha: 0.1,
|
|
3622
|
+
connectionAlpha: 0.1
|
|
3623
|
+
});
|
|
3624
|
+
if (!graphics.lines) graphics.lines = [];
|
|
3625
|
+
if (!graphics.points) graphics.points = [];
|
|
3626
|
+
if (!graphics.rects) graphics.rects = [];
|
|
3627
|
+
if (!graphics.circles) graphics.circles = [];
|
|
3628
|
+
if (!graphics.texts) graphics.texts = [];
|
|
3629
|
+
for (const trace of this.outputTraces) {
|
|
3630
|
+
const line = {
|
|
3631
|
+
points: trace.tracePath.map((p) => ({ x: p.x, y: p.y })),
|
|
3632
|
+
strokeColor: trace.mspPairId === this.activeTraceId ? "red" : "blue"
|
|
3633
|
+
// Highlight active trace
|
|
3634
|
+
};
|
|
3635
|
+
graphics.lines.push(line);
|
|
3636
|
+
}
|
|
3637
|
+
return graphics;
|
|
3638
|
+
}
|
|
3599
3639
|
};
|
|
3600
3640
|
|
|
3601
3641
|
// lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts
|
|
@@ -3725,6 +3765,21 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
|
|
|
3725
3765
|
];
|
|
3726
3766
|
}
|
|
3727
3767
|
),
|
|
3768
|
+
definePipelineStep("traceCleanupSolver", TraceCleanupSolver, (instance) => {
|
|
3769
|
+
const prevSolverOutput = instance.traceLabelOverlapAvoidanceSolver.getOutput();
|
|
3770
|
+
const traces = prevSolverOutput.traces;
|
|
3771
|
+
const labelMergingOutput = instance.traceLabelOverlapAvoidanceSolver.labelMergingSolver.getOutput();
|
|
3772
|
+
return [
|
|
3773
|
+
{
|
|
3774
|
+
inputProblem: instance.inputProblem,
|
|
3775
|
+
allTraces: traces,
|
|
3776
|
+
targetTraceIds: new Set(traces.map((t) => t.mspPairId)),
|
|
3777
|
+
allLabelPlacements: labelMergingOutput.netLabelPlacements,
|
|
3778
|
+
mergedLabelNetIdMap: labelMergingOutput.mergedLabelNetIdMap,
|
|
3779
|
+
paddingBuffer: 0.1
|
|
3780
|
+
}
|
|
3781
|
+
];
|
|
3782
|
+
}),
|
|
3728
3783
|
definePipelineStep(
|
|
3729
3784
|
"netLabelPlacementSolver",
|
|
3730
3785
|
NetLabelPlacementSolver,
|