@tscircuit/schematic-trace-solver 0.0.42 → 0.0.43
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 +28 -29
- package/dist/index.js +917 -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/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
|
@@ -2352,37 +2352,182 @@ var MergedNetLabelObstacleSolver = class extends BaseSolver {
|
|
|
2352
2352
|
}
|
|
2353
2353
|
};
|
|
2354
2354
|
|
|
2355
|
-
// lib/solvers/TraceLabelOverlapAvoidanceSolver/
|
|
2356
|
-
var
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
const
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2355
|
+
// lib/solvers/TraceLabelOverlapAvoidanceSolver/detectTraceLabelOverlap.ts
|
|
2356
|
+
var detectTraceLabelOverlap = (traces, netLabels) => {
|
|
2357
|
+
const overlaps = [];
|
|
2358
|
+
for (const trace of traces) {
|
|
2359
|
+
for (const label of netLabels) {
|
|
2360
|
+
const labelBounds = getRectBounds(label.center, label.width, label.height);
|
|
2361
|
+
for (let i = 0; i < trace.tracePath.length - 1; i++) {
|
|
2362
|
+
const p1 = trace.tracePath[i];
|
|
2363
|
+
const p2 = trace.tracePath[i + 1];
|
|
2364
|
+
if (segmentIntersectsRect2(p1, p2, labelBounds)) {
|
|
2365
|
+
if (trace.globalConnNetId === label.globalConnNetId) {
|
|
2366
|
+
break;
|
|
2367
|
+
}
|
|
2368
|
+
overlaps.push({ trace, label });
|
|
2369
|
+
break;
|
|
2370
|
+
}
|
|
2363
2371
|
}
|
|
2364
2372
|
}
|
|
2365
2373
|
}
|
|
2366
|
-
return
|
|
2374
|
+
return overlaps;
|
|
2367
2375
|
};
|
|
2368
2376
|
|
|
2369
|
-
// lib/solvers/TraceLabelOverlapAvoidanceSolver/
|
|
2370
|
-
var
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2377
|
+
// lib/solvers/TraceLabelOverlapAvoidanceSolver/violation.ts
|
|
2378
|
+
var findTraceViolationZone = (path, labelBounds) => {
|
|
2379
|
+
const isPointInside = (p) => p.x > labelBounds.minX && p.x < labelBounds.maxX && p.y > labelBounds.minY && p.y < labelBounds.maxY;
|
|
2380
|
+
let firstInsideIndex = -1;
|
|
2381
|
+
let lastInsideIndex = -1;
|
|
2382
|
+
for (let i = 0; i < path.length; i++) {
|
|
2383
|
+
if (isPointInside(path[i])) {
|
|
2384
|
+
if (firstInsideIndex === -1) {
|
|
2385
|
+
firstInsideIndex = i;
|
|
2386
|
+
}
|
|
2387
|
+
lastInsideIndex = i;
|
|
2380
2388
|
}
|
|
2381
2389
|
}
|
|
2382
|
-
return
|
|
2390
|
+
return { firstInsideIndex, lastInsideIndex };
|
|
2391
|
+
};
|
|
2392
|
+
|
|
2393
|
+
// lib/solvers/TraceLabelOverlapAvoidanceSolver/trySnipAndReconnect.ts
|
|
2394
|
+
var generateSnipAndReconnectCandidates = ({
|
|
2395
|
+
initialTrace,
|
|
2396
|
+
firstInsideIndex,
|
|
2397
|
+
lastInsideIndex,
|
|
2398
|
+
labelBounds,
|
|
2399
|
+
paddingBuffer,
|
|
2400
|
+
detourCount
|
|
2401
|
+
}) => {
|
|
2402
|
+
if (firstInsideIndex <= 0 || lastInsideIndex >= initialTrace.tracePath.length - 1) {
|
|
2403
|
+
return [];
|
|
2404
|
+
}
|
|
2405
|
+
const entryPoint = initialTrace.tracePath[firstInsideIndex - 1];
|
|
2406
|
+
const exitPoint = initialTrace.tracePath[lastInsideIndex + 1];
|
|
2407
|
+
const pathToEntry = initialTrace.tracePath.slice(0, firstInsideIndex);
|
|
2408
|
+
const pathFromExit = initialTrace.tracePath.slice(lastInsideIndex + 1);
|
|
2409
|
+
const allCandidateDetours = [];
|
|
2410
|
+
if (entryPoint.x !== exitPoint.x && entryPoint.y !== exitPoint.y) {
|
|
2411
|
+
allCandidateDetours.push([{ x: exitPoint.x, y: entryPoint.y }]);
|
|
2412
|
+
allCandidateDetours.push([{ x: entryPoint.x, y: exitPoint.y }]);
|
|
2413
|
+
} else if (entryPoint.x === exitPoint.x || entryPoint.y === exitPoint.y) {
|
|
2414
|
+
allCandidateDetours.push([]);
|
|
2415
|
+
}
|
|
2416
|
+
const buffer = paddingBuffer + detourCount * paddingBuffer;
|
|
2417
|
+
const leftX = labelBounds.minX - buffer;
|
|
2418
|
+
const rightX = labelBounds.maxX + buffer;
|
|
2419
|
+
const topY = labelBounds.maxY + buffer;
|
|
2420
|
+
const bottomY = labelBounds.minY - buffer;
|
|
2421
|
+
if ((entryPoint.x <= labelBounds.minX || exitPoint.x <= labelBounds.minX) && entryPoint.x < labelBounds.maxX && exitPoint.x < labelBounds.maxX) {
|
|
2422
|
+
allCandidateDetours.push([
|
|
2423
|
+
{ x: leftX, y: entryPoint.y },
|
|
2424
|
+
{ x: leftX, y: exitPoint.y }
|
|
2425
|
+
]);
|
|
2426
|
+
}
|
|
2427
|
+
if ((entryPoint.x >= labelBounds.maxX || exitPoint.x >= labelBounds.maxX) && entryPoint.x > labelBounds.minX && exitPoint.x > labelBounds.minX) {
|
|
2428
|
+
allCandidateDetours.push([
|
|
2429
|
+
{ x: rightX, y: entryPoint.y },
|
|
2430
|
+
{ x: rightX, y: exitPoint.y }
|
|
2431
|
+
]);
|
|
2432
|
+
}
|
|
2433
|
+
if ((entryPoint.y >= labelBounds.maxY || exitPoint.y >= labelBounds.maxY) && entryPoint.y > labelBounds.minY && exitPoint.y > labelBounds.minY) {
|
|
2434
|
+
allCandidateDetours.push([
|
|
2435
|
+
{ x: entryPoint.x, y: topY },
|
|
2436
|
+
{ x: exitPoint.x, y: topY }
|
|
2437
|
+
]);
|
|
2438
|
+
}
|
|
2439
|
+
if ((entryPoint.y <= labelBounds.minY || exitPoint.y <= labelBounds.minY) && entryPoint.y < labelBounds.maxY && exitPoint.y < labelBounds.maxY) {
|
|
2440
|
+
allCandidateDetours.push([
|
|
2441
|
+
{ x: entryPoint.x, y: bottomY },
|
|
2442
|
+
{ x: exitPoint.x, y: bottomY }
|
|
2443
|
+
]);
|
|
2444
|
+
}
|
|
2445
|
+
return allCandidateDetours.map((detour) => [
|
|
2446
|
+
...pathToEntry,
|
|
2447
|
+
...detour,
|
|
2448
|
+
...pathFromExit
|
|
2449
|
+
]);
|
|
2450
|
+
};
|
|
2451
|
+
|
|
2452
|
+
// lib/solvers/TraceLabelOverlapAvoidanceSolver/tryFourPointDetour.ts
|
|
2453
|
+
var generateFourPointDetourCandidates = ({
|
|
2454
|
+
initialTrace,
|
|
2455
|
+
label,
|
|
2456
|
+
labelBounds,
|
|
2457
|
+
paddingBuffer,
|
|
2458
|
+
detourCount
|
|
2459
|
+
}) => {
|
|
2460
|
+
let collidingSegIndex = -1;
|
|
2461
|
+
for (let i = 0; i < initialTrace.tracePath.length - 1; i++) {
|
|
2462
|
+
if (segmentIntersectsRect(
|
|
2463
|
+
initialTrace.tracePath[i],
|
|
2464
|
+
initialTrace.tracePath[i + 1],
|
|
2465
|
+
labelBounds
|
|
2466
|
+
)) {
|
|
2467
|
+
collidingSegIndex = i;
|
|
2468
|
+
break;
|
|
2469
|
+
}
|
|
2470
|
+
}
|
|
2471
|
+
if (collidingSegIndex === -1) return [];
|
|
2472
|
+
const pA = initialTrace.tracePath[collidingSegIndex];
|
|
2473
|
+
const pB = initialTrace.tracePath[collidingSegIndex + 1];
|
|
2474
|
+
if (!pA || !pB) return [];
|
|
2475
|
+
const candidateDetours = [];
|
|
2476
|
+
const paddedLabelBounds = getRectBounds(
|
|
2477
|
+
label.center,
|
|
2478
|
+
label.width,
|
|
2479
|
+
label.height
|
|
2480
|
+
);
|
|
2481
|
+
const effectivePadding = paddingBuffer + detourCount * paddingBuffer;
|
|
2482
|
+
if (isVertical(pA, pB)) {
|
|
2483
|
+
const xCandidates = [
|
|
2484
|
+
paddedLabelBounds.maxX + effectivePadding,
|
|
2485
|
+
paddedLabelBounds.minX - effectivePadding
|
|
2486
|
+
];
|
|
2487
|
+
for (const newX of xCandidates) {
|
|
2488
|
+
candidateDetours.push(
|
|
2489
|
+
pB.y > pA.y ? [
|
|
2490
|
+
{ x: pA.x, y: paddedLabelBounds.minY - effectivePadding },
|
|
2491
|
+
{ x: newX, y: paddedLabelBounds.minY - effectivePadding },
|
|
2492
|
+
{ x: newX, y: paddedLabelBounds.maxY + effectivePadding },
|
|
2493
|
+
{ x: pB.x, y: paddedLabelBounds.maxY + effectivePadding }
|
|
2494
|
+
] : [
|
|
2495
|
+
{ x: pA.x, y: paddedLabelBounds.maxY + effectivePadding },
|
|
2496
|
+
{ x: newX, y: paddedLabelBounds.maxY + effectivePadding },
|
|
2497
|
+
{ x: newX, y: paddedLabelBounds.minY - effectivePadding },
|
|
2498
|
+
{ x: pB.x, y: paddedLabelBounds.minY - effectivePadding }
|
|
2499
|
+
]
|
|
2500
|
+
);
|
|
2501
|
+
}
|
|
2502
|
+
} else {
|
|
2503
|
+
const yCandidates = [
|
|
2504
|
+
paddedLabelBounds.maxY + effectivePadding,
|
|
2505
|
+
paddedLabelBounds.minY - effectivePadding
|
|
2506
|
+
];
|
|
2507
|
+
for (const newY of yCandidates) {
|
|
2508
|
+
candidateDetours.push(
|
|
2509
|
+
pB.x > pA.x ? [
|
|
2510
|
+
{ x: paddedLabelBounds.minX - effectivePadding, y: pA.y },
|
|
2511
|
+
{ x: paddedLabelBounds.minX - effectivePadding, y: newY },
|
|
2512
|
+
{ x: paddedLabelBounds.maxX + effectivePadding, y: newY },
|
|
2513
|
+
{ x: paddedLabelBounds.maxX + effectivePadding, y: pB.y }
|
|
2514
|
+
] : [
|
|
2515
|
+
{ x: paddedLabelBounds.maxX + effectivePadding, y: pA.y },
|
|
2516
|
+
{ x: paddedLabelBounds.maxX + effectivePadding, y: newY },
|
|
2517
|
+
{ x: paddedLabelBounds.minX - effectivePadding, y: newY },
|
|
2518
|
+
{ x: paddedLabelBounds.minX - effectivePadding, y: pB.y }
|
|
2519
|
+
]
|
|
2520
|
+
);
|
|
2521
|
+
}
|
|
2522
|
+
}
|
|
2523
|
+
return candidateDetours.map((detourPoints) => [
|
|
2524
|
+
...initialTrace.tracePath.slice(0, collidingSegIndex + 1),
|
|
2525
|
+
...detourPoints,
|
|
2526
|
+
...initialTrace.tracePath.slice(collidingSegIndex + 1)
|
|
2527
|
+
]);
|
|
2383
2528
|
};
|
|
2384
2529
|
|
|
2385
|
-
// lib/solvers/
|
|
2530
|
+
// lib/solvers/TraceCleanupSolver/simplifyPath.ts
|
|
2386
2531
|
var simplifyPath = (path) => {
|
|
2387
2532
|
if (path.length < 3) return path;
|
|
2388
2533
|
const newPath = [path[0]];
|
|
@@ -2411,675 +2556,46 @@ var simplifyPath = (path) => {
|
|
|
2411
2556
|
return finalPath;
|
|
2412
2557
|
};
|
|
2413
2558
|
|
|
2414
|
-
// lib/solvers/TraceLabelOverlapAvoidanceSolver/
|
|
2415
|
-
var
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
};
|
|
2425
|
-
|
|
2426
|
-
// lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver/hasCollisionsWithLabels.ts
|
|
2427
|
-
var hasCollisionsWithLabels = (pathSegments, labels) => {
|
|
2428
|
-
for (let i = 0; i < pathSegments.length - 1; i++) {
|
|
2429
|
-
const p1 = pathSegments[i];
|
|
2430
|
-
const p2 = pathSegments[i + 1];
|
|
2431
|
-
for (const label of labels) {
|
|
2432
|
-
if (segmentIntersectsRect(p1, p2, label)) {
|
|
2433
|
-
return true;
|
|
2434
|
-
}
|
|
2435
|
-
}
|
|
2559
|
+
// lib/solvers/TraceLabelOverlapAvoidanceSolver/rerouteCollidingTrace.ts
|
|
2560
|
+
var generateRerouteCandidates = ({
|
|
2561
|
+
trace,
|
|
2562
|
+
label,
|
|
2563
|
+
paddingBuffer,
|
|
2564
|
+
detourCount
|
|
2565
|
+
}) => {
|
|
2566
|
+
const initialTrace = { ...trace, tracePath: simplifyPath(trace.tracePath) };
|
|
2567
|
+
if (trace.globalConnNetId === label.globalConnNetId) {
|
|
2568
|
+
return [initialTrace.tracePath];
|
|
2436
2569
|
}
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
}
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
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];
|
|
2570
|
+
const labelPadding = paddingBuffer;
|
|
2571
|
+
const labelBoundsRaw = getRectBounds(label.center, label.width, label.height);
|
|
2572
|
+
const labelBounds = {
|
|
2573
|
+
minX: labelBoundsRaw.minX - labelPadding,
|
|
2574
|
+
minY: labelBoundsRaw.minY - labelPadding,
|
|
2575
|
+
maxX: labelBoundsRaw.maxX + labelPadding,
|
|
2576
|
+
maxY: labelBoundsRaw.maxY + labelPadding,
|
|
2577
|
+
chipId: `netlabel-${label.netId}`
|
|
2578
|
+
};
|
|
2579
|
+
const fourPointCandidates = generateFourPointDetourCandidates({
|
|
2580
|
+
initialTrace,
|
|
2581
|
+
label,
|
|
2582
|
+
labelBounds,
|
|
2583
|
+
paddingBuffer,
|
|
2584
|
+
detourCount
|
|
2585
|
+
});
|
|
2586
|
+
const { firstInsideIndex, lastInsideIndex } = findTraceViolationZone(
|
|
2587
|
+
initialTrace.tracePath,
|
|
2588
|
+
labelBounds
|
|
2589
|
+
);
|
|
2590
|
+
const snipReconnectCandidates = generateSnipAndReconnectCandidates({
|
|
2591
|
+
initialTrace,
|
|
2592
|
+
firstInsideIndex,
|
|
2593
|
+
lastInsideIndex,
|
|
2594
|
+
labelBounds,
|
|
2595
|
+
paddingBuffer,
|
|
2596
|
+
detourCount
|
|
2597
|
+
});
|
|
2598
|
+
return [...fourPointCandidates, ...snipReconnectCandidates];
|
|
3083
2599
|
};
|
|
3084
2600
|
|
|
3085
2601
|
// lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/SingleOverlapSolver/SingleOverlapSolver.ts
|
|
@@ -3272,7 +2788,6 @@ var TraceLabelOverlapAvoidanceSolver = class extends BaseSolver {
|
|
|
3272
2788
|
// sub-solver instances
|
|
3273
2789
|
labelMergingSolver;
|
|
3274
2790
|
overlapAvoidanceSolver;
|
|
3275
|
-
traceCleanupSolver;
|
|
3276
2791
|
pipelineStepIndex = 0;
|
|
3277
2792
|
constructor(solverInput) {
|
|
3278
2793
|
super();
|
|
@@ -3310,292 +2825,753 @@ var TraceLabelOverlapAvoidanceSolver = class extends BaseSolver {
|
|
|
3310
2825
|
});
|
|
3311
2826
|
this.activeSubSolver = this.overlapAvoidanceSolver;
|
|
3312
2827
|
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
2828
|
default:
|
|
3329
2829
|
this.solved = true;
|
|
3330
2830
|
break;
|
|
3331
2831
|
}
|
|
3332
|
-
}
|
|
3333
|
-
getOutput() {
|
|
3334
|
-
return {
|
|
3335
|
-
traces: this.
|
|
3336
|
-
netLabelPlacements: this.labelMergingSolver?.getOutput().netLabelPlacements ?? this.netLabelPlacements
|
|
3337
|
-
};
|
|
2832
|
+
}
|
|
2833
|
+
getOutput() {
|
|
2834
|
+
return {
|
|
2835
|
+
traces: this.overlapAvoidanceSolver?.getOutput().allTraces ?? this.traces,
|
|
2836
|
+
netLabelPlacements: this.labelMergingSolver?.getOutput().netLabelPlacements ?? this.netLabelPlacements
|
|
2837
|
+
};
|
|
2838
|
+
}
|
|
2839
|
+
visualize() {
|
|
2840
|
+
if (this.activeSubSolver) {
|
|
2841
|
+
return this.activeSubSolver.visualize();
|
|
2842
|
+
}
|
|
2843
|
+
const graphics = visualizeInputProblem(this.inputProblem);
|
|
2844
|
+
if (!graphics.lines) graphics.lines = [];
|
|
2845
|
+
if (!graphics.rects) graphics.rects = [];
|
|
2846
|
+
const output = this.getOutput();
|
|
2847
|
+
for (const trace of output.traces) {
|
|
2848
|
+
graphics.lines.push({
|
|
2849
|
+
points: trace.tracePath,
|
|
2850
|
+
strokeColor: "purple"
|
|
2851
|
+
});
|
|
2852
|
+
}
|
|
2853
|
+
for (const label of output.netLabelPlacements) {
|
|
2854
|
+
const color = getColorFromString(label.globalConnNetId, 0.3);
|
|
2855
|
+
graphics.rects.push({
|
|
2856
|
+
center: label.center,
|
|
2857
|
+
width: label.width,
|
|
2858
|
+
height: label.height,
|
|
2859
|
+
fill: color,
|
|
2860
|
+
stroke: color.replace("0.3", "1"),
|
|
2861
|
+
label: label.globalConnNetId
|
|
2862
|
+
});
|
|
2863
|
+
}
|
|
2864
|
+
return graphics;
|
|
2865
|
+
}
|
|
2866
|
+
};
|
|
2867
|
+
|
|
2868
|
+
// lib/solvers/SchematicTracePipelineSolver/correctPinsInsideChip.ts
|
|
2869
|
+
var correctPinsInsideChips = (problem) => {
|
|
2870
|
+
for (const chip of problem.chips) {
|
|
2871
|
+
const bounds = getInputChipBounds(chip);
|
|
2872
|
+
for (const pin of chip.pins) {
|
|
2873
|
+
const isInside = pin.x > bounds.minX && pin.x < bounds.maxX && pin.y > bounds.minY && pin.y < bounds.maxY;
|
|
2874
|
+
if (!isInside) continue;
|
|
2875
|
+
const distLeft = pin.x - bounds.minX;
|
|
2876
|
+
const distRight = bounds.maxX - pin.x;
|
|
2877
|
+
const distBottom = pin.y - bounds.minY;
|
|
2878
|
+
const distTop = bounds.maxY - pin.y;
|
|
2879
|
+
const minDist = Math.min(distLeft, distRight, distBottom, distTop);
|
|
2880
|
+
if (minDist === distLeft) {
|
|
2881
|
+
pin.x = bounds.minX;
|
|
2882
|
+
} else if (minDist === distRight) {
|
|
2883
|
+
pin.x = bounds.maxX;
|
|
2884
|
+
} else if (minDist === distBottom) {
|
|
2885
|
+
pin.y = bounds.minY;
|
|
2886
|
+
} else {
|
|
2887
|
+
pin.y = bounds.maxY;
|
|
2888
|
+
}
|
|
2889
|
+
pin._facingDirection = void 0;
|
|
2890
|
+
}
|
|
2891
|
+
}
|
|
2892
|
+
};
|
|
2893
|
+
|
|
2894
|
+
// lib/solvers/SchematicTracePipelineSolver/expandChipsToFitPins.ts
|
|
2895
|
+
var expandChipsToFitPins = (problem) => {
|
|
2896
|
+
for (const chip of problem.chips) {
|
|
2897
|
+
const halfWidth = chip.width / 2;
|
|
2898
|
+
const halfHeight = chip.height / 2;
|
|
2899
|
+
let maxDx = 0;
|
|
2900
|
+
let maxDy = 0;
|
|
2901
|
+
for (const pin of chip.pins) {
|
|
2902
|
+
const dx = Math.abs(pin.x - chip.center.x);
|
|
2903
|
+
const dy = Math.abs(pin.y - chip.center.y);
|
|
2904
|
+
if (dx > maxDx) maxDx = dx;
|
|
2905
|
+
if (dy > maxDy) maxDy = dy;
|
|
2906
|
+
}
|
|
2907
|
+
const newHalfWidth = Math.max(halfWidth, maxDx);
|
|
2908
|
+
const newHalfHeight = Math.max(halfHeight, maxDy);
|
|
2909
|
+
if (newHalfWidth > halfWidth || newHalfHeight > halfHeight) {
|
|
2910
|
+
chip.width = newHalfWidth * 2;
|
|
2911
|
+
chip.height = newHalfHeight * 2;
|
|
2912
|
+
for (const pin of chip.pins) {
|
|
2913
|
+
pin._facingDirection = void 0;
|
|
2914
|
+
}
|
|
2915
|
+
}
|
|
2916
|
+
}
|
|
2917
|
+
};
|
|
2918
|
+
|
|
2919
|
+
// lib/utils/does-trace-overlap-with-existing-traces.ts
|
|
2920
|
+
import { doSegmentsIntersect } from "@tscircuit/math-utils";
|
|
2921
|
+
function doesTraceOverlapWithExistingTraces(newTracePath, existingTraces) {
|
|
2922
|
+
for (let i = 0; i < newTracePath.length - 1; i++) {
|
|
2923
|
+
const newSegmentP1 = newTracePath[i];
|
|
2924
|
+
const newSegmentP2 = newTracePath[i + 1];
|
|
2925
|
+
for (const existingTrace of existingTraces) {
|
|
2926
|
+
for (let j = 0; j < existingTrace.tracePath.length - 1; j++) {
|
|
2927
|
+
const existingSegmentP1 = existingTrace.tracePath[j];
|
|
2928
|
+
const existingSegmentP2 = existingTrace.tracePath[j + 1];
|
|
2929
|
+
if (doSegmentsIntersect(
|
|
2930
|
+
newSegmentP1,
|
|
2931
|
+
newSegmentP2,
|
|
2932
|
+
existingSegmentP1,
|
|
2933
|
+
existingSegmentP2
|
|
2934
|
+
)) {
|
|
2935
|
+
return true;
|
|
2936
|
+
}
|
|
2937
|
+
}
|
|
2938
|
+
}
|
|
2939
|
+
}
|
|
2940
|
+
return false;
|
|
2941
|
+
}
|
|
2942
|
+
|
|
2943
|
+
// lib/solvers/LongDistancePairSolver/LongDistancePairSolver.ts
|
|
2944
|
+
var NEAREST_NEIGHBOR_COUNT = 3;
|
|
2945
|
+
var distance = (p1, p2) => {
|
|
2946
|
+
return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));
|
|
2947
|
+
};
|
|
2948
|
+
var LongDistancePairSolver = class extends BaseSolver {
|
|
2949
|
+
constructor(params) {
|
|
2950
|
+
super();
|
|
2951
|
+
this.params = params;
|
|
2952
|
+
const { inputProblem, primaryMspConnectionPairs, alreadySolvedTraces } = this.params;
|
|
2953
|
+
this.inputProblem = inputProblem;
|
|
2954
|
+
this.allSolvedTraces = [...alreadySolvedTraces];
|
|
2955
|
+
const primaryConnectedPinIds = /* @__PURE__ */ new Set();
|
|
2956
|
+
for (const pair of primaryMspConnectionPairs) {
|
|
2957
|
+
primaryConnectedPinIds.add(pair.pins[0].pinId);
|
|
2958
|
+
primaryConnectedPinIds.add(pair.pins[1].pinId);
|
|
2959
|
+
}
|
|
2960
|
+
const { netConnMap } = getConnectivityMapsFromInputProblem(inputProblem);
|
|
2961
|
+
this.netConnMap = netConnMap;
|
|
2962
|
+
const pinMap = /* @__PURE__ */ new Map();
|
|
2963
|
+
for (const chip of inputProblem.chips) {
|
|
2964
|
+
this.chipMap[chip.chipId] = chip;
|
|
2965
|
+
for (const pin of chip.pins) {
|
|
2966
|
+
pinMap.set(pin.pinId, { ...pin, chipId: chip.chipId });
|
|
2967
|
+
}
|
|
2968
|
+
}
|
|
2969
|
+
const candidatePairs = [];
|
|
2970
|
+
const addedPairKeys = /* @__PURE__ */ new Set();
|
|
2971
|
+
for (const netId of Object.keys(netConnMap.netMap)) {
|
|
2972
|
+
const allPinIdsInNet = netConnMap.getIdsConnectedToNet(netId);
|
|
2973
|
+
if (allPinIdsInNet.length < 2) continue;
|
|
2974
|
+
const unconnectedPinIds = allPinIdsInNet.filter(
|
|
2975
|
+
(pinId) => !primaryConnectedPinIds.has(pinId)
|
|
2976
|
+
);
|
|
2977
|
+
for (const unconnectedPinId of unconnectedPinIds) {
|
|
2978
|
+
const sourcePin = pinMap.get(unconnectedPinId);
|
|
2979
|
+
if (!sourcePin) continue;
|
|
2980
|
+
const neighbors = allPinIdsInNet.filter((otherPinId) => otherPinId !== unconnectedPinId).flatMap((otherPinId) => {
|
|
2981
|
+
const targetPin = pinMap.get(otherPinId);
|
|
2982
|
+
if (!targetPin) return [];
|
|
2983
|
+
return [
|
|
2984
|
+
{
|
|
2985
|
+
pin: targetPin,
|
|
2986
|
+
distance: distance(sourcePin, targetPin)
|
|
2987
|
+
}
|
|
2988
|
+
];
|
|
2989
|
+
}).sort((a, b) => a.distance - b.distance).slice(0, NEAREST_NEIGHBOR_COUNT);
|
|
2990
|
+
for (const neighbor of neighbors) {
|
|
2991
|
+
const pair = [sourcePin, neighbor.pin];
|
|
2992
|
+
const pairKey = pair.map((p) => p.pinId).sort().join("--");
|
|
2993
|
+
if (!addedPairKeys.has(pairKey)) {
|
|
2994
|
+
candidatePairs.push(pair);
|
|
2995
|
+
addedPairKeys.add(pairKey);
|
|
2996
|
+
}
|
|
2997
|
+
}
|
|
2998
|
+
}
|
|
2999
|
+
}
|
|
3000
|
+
this.queuedCandidatePairs = candidatePairs;
|
|
3001
|
+
}
|
|
3002
|
+
solvedLongDistanceTraces = [];
|
|
3003
|
+
queuedCandidatePairs = [];
|
|
3004
|
+
currentCandidatePair = null;
|
|
3005
|
+
subSolver = null;
|
|
3006
|
+
chipMap = {};
|
|
3007
|
+
inputProblem;
|
|
3008
|
+
netConnMap;
|
|
3009
|
+
newlyConnectedPinIds = /* @__PURE__ */ new Set();
|
|
3010
|
+
allSolvedTraces = [];
|
|
3011
|
+
getConstructorParams() {
|
|
3012
|
+
return this.params;
|
|
3013
|
+
}
|
|
3014
|
+
_step() {
|
|
3015
|
+
if (this.subSolver?.solved) {
|
|
3016
|
+
const newTracePath = this.subSolver.solvedTracePath;
|
|
3017
|
+
if (newTracePath && this.currentCandidatePair) {
|
|
3018
|
+
const isTraceClear = !doesTraceOverlapWithExistingTraces(
|
|
3019
|
+
newTracePath,
|
|
3020
|
+
this.allSolvedTraces
|
|
3021
|
+
);
|
|
3022
|
+
if (isTraceClear) {
|
|
3023
|
+
const [p1, p2] = this.currentCandidatePair;
|
|
3024
|
+
const globalConnNetId = this.netConnMap.getNetConnectedToId(p1.pinId);
|
|
3025
|
+
const mspPairId = `${p1.pinId}-${p2.pinId}`;
|
|
3026
|
+
const newSolvedTrace = {
|
|
3027
|
+
mspPairId,
|
|
3028
|
+
dcConnNetId: globalConnNetId,
|
|
3029
|
+
globalConnNetId,
|
|
3030
|
+
pins: [p1, p2],
|
|
3031
|
+
tracePath: newTracePath,
|
|
3032
|
+
mspConnectionPairIds: [mspPairId],
|
|
3033
|
+
pinIds: [p1.pinId, p2.pinId]
|
|
3034
|
+
};
|
|
3035
|
+
this.solvedLongDistanceTraces.push(newSolvedTrace);
|
|
3036
|
+
this.allSolvedTraces.push(newSolvedTrace);
|
|
3037
|
+
this.newlyConnectedPinIds.add(p1.pinId);
|
|
3038
|
+
this.newlyConnectedPinIds.add(p2.pinId);
|
|
3039
|
+
}
|
|
3040
|
+
}
|
|
3041
|
+
this.subSolver = null;
|
|
3042
|
+
this.currentCandidatePair = null;
|
|
3043
|
+
} else if (this.subSolver?.failed) {
|
|
3044
|
+
this.subSolver = null;
|
|
3045
|
+
this.currentCandidatePair = null;
|
|
3046
|
+
}
|
|
3047
|
+
if (this.subSolver) {
|
|
3048
|
+
this.subSolver.step();
|
|
3049
|
+
return;
|
|
3050
|
+
}
|
|
3051
|
+
while (this.queuedCandidatePairs.length > 0) {
|
|
3052
|
+
const nextPair = this.queuedCandidatePairs.shift();
|
|
3053
|
+
const [p1, p2] = nextPair;
|
|
3054
|
+
if (this.newlyConnectedPinIds.has(p1.pinId) || this.newlyConnectedPinIds.has(p2.pinId)) {
|
|
3055
|
+
continue;
|
|
3056
|
+
}
|
|
3057
|
+
this.currentCandidatePair = nextPair;
|
|
3058
|
+
this.subSolver = new SchematicTraceSingleLineSolver2({
|
|
3059
|
+
inputProblem: this.params.inputProblem,
|
|
3060
|
+
pins: this.currentCandidatePair,
|
|
3061
|
+
chipMap: this.chipMap
|
|
3062
|
+
});
|
|
3063
|
+
return;
|
|
3064
|
+
}
|
|
3065
|
+
this.solved = true;
|
|
3338
3066
|
}
|
|
3339
3067
|
visualize() {
|
|
3340
|
-
if (this.
|
|
3341
|
-
return this.
|
|
3068
|
+
if (this.subSolver) {
|
|
3069
|
+
return this.subSolver.visualize();
|
|
3342
3070
|
}
|
|
3343
3071
|
const graphics = visualizeInputProblem(this.inputProblem);
|
|
3344
|
-
|
|
3345
|
-
if (!graphics.rects) graphics.rects = [];
|
|
3346
|
-
const output = this.getOutput();
|
|
3347
|
-
for (const trace of output.traces) {
|
|
3072
|
+
for (const trace of this.solvedLongDistanceTraces) {
|
|
3348
3073
|
graphics.lines.push({
|
|
3349
3074
|
points: trace.tracePath,
|
|
3350
3075
|
strokeColor: "purple"
|
|
3351
3076
|
});
|
|
3352
3077
|
}
|
|
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
|
|
3078
|
+
for (const [p1, p2] of this.queuedCandidatePairs) {
|
|
3079
|
+
graphics.lines.push({
|
|
3080
|
+
points: [p1, p2],
|
|
3081
|
+
strokeColor: "gray",
|
|
3082
|
+
strokeDash: "4 4"
|
|
3362
3083
|
});
|
|
3363
3084
|
}
|
|
3364
3085
|
return graphics;
|
|
3365
3086
|
}
|
|
3087
|
+
getOutput() {
|
|
3088
|
+
if (!this.solved) {
|
|
3089
|
+
return { newTraces: [], allTracesMerged: this.params.alreadySolvedTraces };
|
|
3090
|
+
}
|
|
3091
|
+
return {
|
|
3092
|
+
newTraces: this.solvedLongDistanceTraces,
|
|
3093
|
+
allTracesMerged: [
|
|
3094
|
+
...this.params.alreadySolvedTraces,
|
|
3095
|
+
...this.solvedLongDistanceTraces
|
|
3096
|
+
]
|
|
3097
|
+
};
|
|
3098
|
+
}
|
|
3366
3099
|
};
|
|
3367
3100
|
|
|
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;
|
|
3101
|
+
// lib/solvers/TraceCleanupSolver/hasCollisions.ts
|
|
3102
|
+
var hasCollisions = (pathSegments, obstacles) => {
|
|
3103
|
+
for (let i = 0; i < pathSegments.length - 1; i++) {
|
|
3104
|
+
const p1 = pathSegments[i];
|
|
3105
|
+
const p2 = pathSegments[i + 1];
|
|
3106
|
+
for (const obstacle of obstacles) {
|
|
3107
|
+
if (segmentIntersectsRect(p1, p2, obstacle)) {
|
|
3108
|
+
return true;
|
|
3388
3109
|
}
|
|
3389
|
-
pin._facingDirection = void 0;
|
|
3390
3110
|
}
|
|
3391
3111
|
}
|
|
3112
|
+
return false;
|
|
3392
3113
|
};
|
|
3393
3114
|
|
|
3394
|
-
// lib/solvers/
|
|
3395
|
-
var
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
const
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
if (dy > maxDy) maxDy = dy;
|
|
3115
|
+
// lib/solvers/TraceCleanupSolver/countTurns.ts
|
|
3116
|
+
var countTurns = (points) => {
|
|
3117
|
+
let turns = 0;
|
|
3118
|
+
for (let i = 1; i < points.length - 1; i++) {
|
|
3119
|
+
const prev = points[i - 1];
|
|
3120
|
+
const curr = points[i];
|
|
3121
|
+
const next = points[i + 1];
|
|
3122
|
+
const prevVertical = prev.x === curr.x;
|
|
3123
|
+
const nextVertical = curr.x === next.x;
|
|
3124
|
+
if (prevVertical !== nextVertical) {
|
|
3125
|
+
turns++;
|
|
3406
3126
|
}
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3127
|
+
}
|
|
3128
|
+
return turns;
|
|
3129
|
+
};
|
|
3130
|
+
|
|
3131
|
+
// lib/solvers/TraceCleanupSolver/tryConnectPoints.ts
|
|
3132
|
+
var tryConnectPoints = (start, end) => {
|
|
3133
|
+
const candidates = [];
|
|
3134
|
+
if (start.x === end.x || start.y === end.y) {
|
|
3135
|
+
candidates.push([start, end]);
|
|
3136
|
+
} else {
|
|
3137
|
+
candidates.push([start, { x: end.x, y: start.y }, end]);
|
|
3138
|
+
candidates.push([start, { x: start.x, y: end.y }, end]);
|
|
3139
|
+
}
|
|
3140
|
+
return candidates;
|
|
3141
|
+
};
|
|
3142
|
+
|
|
3143
|
+
// lib/solvers/TraceCleanupSolver/hasCollisionsWithLabels.ts
|
|
3144
|
+
var hasCollisionsWithLabels = (pathSegments, labels) => {
|
|
3145
|
+
for (let i = 0; i < pathSegments.length - 1; i++) {
|
|
3146
|
+
const p1 = pathSegments[i];
|
|
3147
|
+
const p2 = pathSegments[i + 1];
|
|
3148
|
+
for (const label of labels) {
|
|
3149
|
+
if (segmentIntersectsRect(p1, p2, label)) {
|
|
3150
|
+
return true;
|
|
3151
|
+
}
|
|
3152
|
+
}
|
|
3153
|
+
}
|
|
3154
|
+
return false;
|
|
3155
|
+
};
|
|
3156
|
+
|
|
3157
|
+
// lib/solvers/TraceCleanupSolver/turnMinimization.ts
|
|
3158
|
+
var minimizeTurns = ({
|
|
3159
|
+
path,
|
|
3160
|
+
obstacles,
|
|
3161
|
+
labelBounds
|
|
3162
|
+
}) => {
|
|
3163
|
+
if (path.length <= 2) {
|
|
3164
|
+
return path;
|
|
3165
|
+
}
|
|
3166
|
+
const recognizeStairStepPattern = (pathToCheck, startIdx) => {
|
|
3167
|
+
if (startIdx >= pathToCheck.length - 3) return -1;
|
|
3168
|
+
let endIdx = startIdx;
|
|
3169
|
+
let isStairStep = true;
|
|
3170
|
+
for (let i = startIdx; i < pathToCheck.length - 2 && i < startIdx + 10; i++) {
|
|
3171
|
+
if (i + 2 >= pathToCheck.length) break;
|
|
3172
|
+
const p1 = pathToCheck[i];
|
|
3173
|
+
const p2 = pathToCheck[i + 1];
|
|
3174
|
+
const p3 = pathToCheck[i + 2];
|
|
3175
|
+
const seg1Vertical = p1.x === p2.x;
|
|
3176
|
+
const seg2Vertical = p2.x === p3.x;
|
|
3177
|
+
if (seg1Vertical === seg2Vertical) {
|
|
3178
|
+
break;
|
|
3179
|
+
}
|
|
3180
|
+
const seg1Direction = seg1Vertical ? Math.sign(p2.y - p1.y) : Math.sign(p2.x - p1.x);
|
|
3181
|
+
if (i > startIdx) {
|
|
3182
|
+
const prevP = pathToCheck[i - 1];
|
|
3183
|
+
const prevSegVertical = prevP.x === p1.x;
|
|
3184
|
+
const prevDirection = prevSegVertical ? Math.sign(p1.y - prevP.y) : Math.sign(p1.x - prevP.x);
|
|
3185
|
+
if (seg1Vertical && prevSegVertical && seg1Direction !== prevDirection || !seg1Vertical && !prevSegVertical && seg1Direction !== prevDirection) {
|
|
3186
|
+
isStairStep = false;
|
|
3187
|
+
break;
|
|
3188
|
+
}
|
|
3189
|
+
}
|
|
3190
|
+
endIdx = i + 2;
|
|
3191
|
+
}
|
|
3192
|
+
return isStairStep && endIdx - startIdx >= 3 ? endIdx : -1;
|
|
3193
|
+
};
|
|
3194
|
+
let optimizedPath = [...path];
|
|
3195
|
+
let currentTurns = countTurns(optimizedPath);
|
|
3196
|
+
let improved = true;
|
|
3197
|
+
while (improved) {
|
|
3198
|
+
improved = false;
|
|
3199
|
+
for (let startIdx = 0; startIdx < optimizedPath.length - 3; startIdx++) {
|
|
3200
|
+
const stairEndIdx = recognizeStairStepPattern(optimizedPath, startIdx);
|
|
3201
|
+
if (stairEndIdx > 0) {
|
|
3202
|
+
const startPoint = optimizedPath[startIdx];
|
|
3203
|
+
const endPoint = optimizedPath[stairEndIdx];
|
|
3204
|
+
const connectionOptions = tryConnectPoints(startPoint, endPoint);
|
|
3205
|
+
for (const connection of connectionOptions) {
|
|
3206
|
+
const testPath = [
|
|
3207
|
+
...optimizedPath.slice(0, startIdx + 1),
|
|
3208
|
+
...connection.slice(1, -1),
|
|
3209
|
+
...optimizedPath.slice(stairEndIdx)
|
|
3210
|
+
];
|
|
3211
|
+
const collidesWithObstacles = hasCollisions(connection, obstacles);
|
|
3212
|
+
const collidesWithLabels = hasCollisionsWithLabels(
|
|
3213
|
+
connection,
|
|
3214
|
+
labelBounds
|
|
3215
|
+
);
|
|
3216
|
+
if (!collidesWithObstacles && !collidesWithLabels) {
|
|
3217
|
+
const newTurns = countTurns(testPath);
|
|
3218
|
+
optimizedPath = testPath;
|
|
3219
|
+
currentTurns = newTurns;
|
|
3220
|
+
improved = true;
|
|
3221
|
+
break;
|
|
3222
|
+
}
|
|
3223
|
+
}
|
|
3224
|
+
if (improved) break;
|
|
3225
|
+
}
|
|
3226
|
+
}
|
|
3227
|
+
if (!improved) {
|
|
3228
|
+
for (let startIdx = 0; startIdx < optimizedPath.length - 2; startIdx++) {
|
|
3229
|
+
const maxRemove = Math.min(
|
|
3230
|
+
optimizedPath.length - startIdx - 2,
|
|
3231
|
+
optimizedPath.length - 2
|
|
3232
|
+
);
|
|
3233
|
+
for (let removeCount = 1; removeCount <= maxRemove; removeCount++) {
|
|
3234
|
+
const endIdx = startIdx + removeCount + 1;
|
|
3235
|
+
if (endIdx >= optimizedPath.length) continue;
|
|
3236
|
+
const startPoint = optimizedPath[startIdx];
|
|
3237
|
+
const endPoint = optimizedPath[endIdx];
|
|
3238
|
+
const connectionOptions = tryConnectPoints(startPoint, endPoint);
|
|
3239
|
+
for (const connection of connectionOptions) {
|
|
3240
|
+
const testPath = [
|
|
3241
|
+
...optimizedPath.slice(0, startIdx + 1),
|
|
3242
|
+
...connection.slice(1, -1),
|
|
3243
|
+
...optimizedPath.slice(endIdx)
|
|
3244
|
+
];
|
|
3245
|
+
const connectionSegments = connection;
|
|
3246
|
+
const collidesWithObstacles = hasCollisions(
|
|
3247
|
+
connectionSegments,
|
|
3248
|
+
obstacles
|
|
3249
|
+
);
|
|
3250
|
+
const collidesWithLabels = hasCollisionsWithLabels(
|
|
3251
|
+
connectionSegments,
|
|
3252
|
+
labelBounds
|
|
3253
|
+
);
|
|
3254
|
+
if (!collidesWithObstacles && !collidesWithLabels) {
|
|
3255
|
+
const newTurns = countTurns(testPath);
|
|
3256
|
+
if (newTurns < currentTurns || newTurns === currentTurns && testPath.length < optimizedPath.length) {
|
|
3257
|
+
optimizedPath = testPath;
|
|
3258
|
+
currentTurns = newTurns;
|
|
3259
|
+
improved = true;
|
|
3260
|
+
break;
|
|
3261
|
+
}
|
|
3262
|
+
}
|
|
3263
|
+
}
|
|
3264
|
+
if (improved) break;
|
|
3265
|
+
}
|
|
3266
|
+
if (improved) break;
|
|
3267
|
+
}
|
|
3268
|
+
}
|
|
3269
|
+
if (!improved) {
|
|
3270
|
+
for (let i = 0; i < optimizedPath.length - 2; i++) {
|
|
3271
|
+
const p1 = optimizedPath[i];
|
|
3272
|
+
const p2 = optimizedPath[i + 1];
|
|
3273
|
+
const p3 = optimizedPath[i + 2];
|
|
3274
|
+
const allVertical = p1.x === p2.x && p2.x === p3.x;
|
|
3275
|
+
const allHorizontal = p1.y === p2.y && p2.y === p3.y;
|
|
3276
|
+
if (allVertical || allHorizontal) {
|
|
3277
|
+
const testPath = [
|
|
3278
|
+
...optimizedPath.slice(0, i + 1),
|
|
3279
|
+
...optimizedPath.slice(i + 2)
|
|
3280
|
+
];
|
|
3281
|
+
const collidesWithObstacles = hasCollisions([p1, p3], obstacles);
|
|
3282
|
+
const collidesWithLabels = hasCollisionsWithLabels(
|
|
3283
|
+
[p1, p3],
|
|
3284
|
+
labelBounds
|
|
3285
|
+
);
|
|
3286
|
+
if (!collidesWithObstacles && !collidesWithLabels) {
|
|
3287
|
+
optimizedPath = testPath;
|
|
3288
|
+
improved = true;
|
|
3289
|
+
break;
|
|
3290
|
+
}
|
|
3291
|
+
}
|
|
3414
3292
|
}
|
|
3415
3293
|
}
|
|
3416
3294
|
}
|
|
3295
|
+
const finalSimplifiedPath = simplifyPath(optimizedPath);
|
|
3296
|
+
return finalSimplifiedPath;
|
|
3417
3297
|
};
|
|
3418
3298
|
|
|
3419
|
-
// lib/
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
existingSegmentP2
|
|
3434
|
-
)) {
|
|
3435
|
-
return true;
|
|
3436
|
-
}
|
|
3437
|
-
}
|
|
3438
|
-
}
|
|
3299
|
+
// lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts
|
|
3300
|
+
var minimizeTurnsWithFilteredLabels = ({
|
|
3301
|
+
targetMspConnectionPairId,
|
|
3302
|
+
traces,
|
|
3303
|
+
inputProblem,
|
|
3304
|
+
allLabelPlacements,
|
|
3305
|
+
mergedLabelNetIdMap,
|
|
3306
|
+
paddingBuffer
|
|
3307
|
+
}) => {
|
|
3308
|
+
const targetTrace = traces.find(
|
|
3309
|
+
(t) => t.mspPairId === targetMspConnectionPairId
|
|
3310
|
+
);
|
|
3311
|
+
if (!targetTrace) {
|
|
3312
|
+
throw new Error(`Target trace ${targetMspConnectionPairId} not found`);
|
|
3439
3313
|
}
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3314
|
+
const obstacleTraces = traces.filter(
|
|
3315
|
+
(t) => t.mspPairId !== targetMspConnectionPairId
|
|
3316
|
+
);
|
|
3317
|
+
const TRACE_WIDTH = 0.01;
|
|
3318
|
+
const traceObstacles = obstacleTraces.flatMap(
|
|
3319
|
+
(trace, i) => trace.tracePath.slice(0, -1).map((p1, pi) => {
|
|
3320
|
+
const p2 = trace.tracePath[pi + 1];
|
|
3321
|
+
return {
|
|
3322
|
+
chipId: `trace-obstacle-${i}-${pi}`,
|
|
3323
|
+
minX: Math.min(p1.x, p2.x) - TRACE_WIDTH / 2,
|
|
3324
|
+
minY: Math.min(p1.y, p2.y) - TRACE_WIDTH / 2,
|
|
3325
|
+
maxX: Math.max(p1.x, p2.x) + TRACE_WIDTH / 2,
|
|
3326
|
+
maxY: Math.max(p1.y, p2.y) + TRACE_WIDTH / 2
|
|
3327
|
+
};
|
|
3328
|
+
})
|
|
3329
|
+
);
|
|
3330
|
+
const staticObstacles = getObstacleRects(inputProblem);
|
|
3331
|
+
const combinedObstacles = [...staticObstacles, ...traceObstacles];
|
|
3332
|
+
const originalPath = targetTrace.tracePath;
|
|
3333
|
+
const filteredLabels = allLabelPlacements.filter((label) => {
|
|
3334
|
+
const originalNetIds = mergedLabelNetIdMap[label.globalConnNetId];
|
|
3335
|
+
if (originalNetIds) {
|
|
3336
|
+
return !originalNetIds.has(targetTrace.globalConnNetId);
|
|
3337
|
+
}
|
|
3338
|
+
return label.globalConnNetId !== targetTrace.globalConnNetId;
|
|
3339
|
+
});
|
|
3340
|
+
const labelBounds = filteredLabels.map((nl) => ({
|
|
3341
|
+
minX: nl.center.x - nl.width / 2 - paddingBuffer,
|
|
3342
|
+
maxX: nl.center.x + nl.width / 2 + paddingBuffer,
|
|
3343
|
+
minY: nl.center.y - nl.height / 2 - paddingBuffer,
|
|
3344
|
+
maxY: nl.center.y + nl.height / 2 + paddingBuffer
|
|
3345
|
+
}));
|
|
3346
|
+
const newPath = minimizeTurns({
|
|
3347
|
+
path: originalPath,
|
|
3348
|
+
obstacles: combinedObstacles,
|
|
3349
|
+
labelBounds
|
|
3350
|
+
});
|
|
3351
|
+
return {
|
|
3352
|
+
...targetTrace,
|
|
3353
|
+
tracePath: newPath
|
|
3354
|
+
};
|
|
3447
3355
|
};
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3356
|
+
|
|
3357
|
+
// lib/solvers/TraceCleanupSolver/balanceZShapes.ts
|
|
3358
|
+
var balanceZShapes = ({
|
|
3359
|
+
targetMspConnectionPairId,
|
|
3360
|
+
traces,
|
|
3361
|
+
inputProblem,
|
|
3362
|
+
allLabelPlacements,
|
|
3363
|
+
mergedLabelNetIdMap,
|
|
3364
|
+
paddingBuffer
|
|
3365
|
+
}) => {
|
|
3366
|
+
const targetTrace = traces.find(
|
|
3367
|
+
(t) => t.mspPairId === targetMspConnectionPairId
|
|
3368
|
+
);
|
|
3369
|
+
if (!targetTrace) {
|
|
3370
|
+
throw new Error(`Target trace ${targetMspConnectionPairId} not found`);
|
|
3371
|
+
}
|
|
3372
|
+
const TOLERANCE = 1e-5;
|
|
3373
|
+
const obstacleTraces = traces.filter(
|
|
3374
|
+
(t) => t.mspPairId !== targetMspConnectionPairId
|
|
3375
|
+
);
|
|
3376
|
+
const TRACE_WIDTH = 0.01;
|
|
3377
|
+
const traceObstacles = obstacleTraces.flatMap(
|
|
3378
|
+
(trace, i) => trace.tracePath.slice(0, -1).map((p1, pi) => {
|
|
3379
|
+
const p2 = trace.tracePath[pi + 1];
|
|
3380
|
+
return {
|
|
3381
|
+
chipId: `trace-obstacle-${i}-${pi}`,
|
|
3382
|
+
minX: Math.min(p1.x, p2.x) - TRACE_WIDTH / 2,
|
|
3383
|
+
minY: Math.min(p1.y, p2.y) - TRACE_WIDTH / 2,
|
|
3384
|
+
maxX: Math.max(p1.x, p2.x) + TRACE_WIDTH / 2,
|
|
3385
|
+
maxY: Math.max(p1.y, p2.y) + TRACE_WIDTH / 2
|
|
3386
|
+
};
|
|
3387
|
+
})
|
|
3388
|
+
);
|
|
3389
|
+
const staticObstacles = getObstacleRects(inputProblem).map((obs) => ({
|
|
3390
|
+
...obs,
|
|
3391
|
+
minX: obs.minX + TOLERANCE,
|
|
3392
|
+
maxX: obs.maxX - TOLERANCE,
|
|
3393
|
+
minY: obs.minY + TOLERANCE,
|
|
3394
|
+
maxY: obs.maxY - TOLERANCE
|
|
3395
|
+
}));
|
|
3396
|
+
const combinedObstacles = [...staticObstacles, ...traceObstacles];
|
|
3397
|
+
const segmentIntersectsAnyRect = (p1, p2, rects) => {
|
|
3398
|
+
for (const rect of rects) {
|
|
3399
|
+
if (segmentIntersectsRect(p1, p2, rect)) {
|
|
3400
|
+
return true;
|
|
3467
3401
|
}
|
|
3468
3402
|
}
|
|
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
|
-
|
|
3403
|
+
return false;
|
|
3404
|
+
};
|
|
3405
|
+
const filteredLabels = allLabelPlacements.filter((label) => {
|
|
3406
|
+
const originalNetIds = mergedLabelNetIdMap[label.globalConnNetId];
|
|
3407
|
+
if (originalNetIds) {
|
|
3408
|
+
return !originalNetIds.has(targetTrace.globalConnNetId);
|
|
3409
|
+
}
|
|
3410
|
+
return label.globalConnNetId !== targetTrace.globalConnNetId;
|
|
3411
|
+
});
|
|
3412
|
+
const labelBounds = filteredLabels.map((nl) => ({
|
|
3413
|
+
minX: nl.center.x - nl.width / 2 + TOLERANCE,
|
|
3414
|
+
maxX: nl.center.x + nl.width / 2 - TOLERANCE,
|
|
3415
|
+
minY: nl.center.y - nl.height / 2 + TOLERANCE,
|
|
3416
|
+
maxY: nl.center.y + nl.height / 2 - TOLERANCE
|
|
3417
|
+
}));
|
|
3418
|
+
const newPath = [...targetTrace.tracePath];
|
|
3419
|
+
if (newPath.length < 4) {
|
|
3420
|
+
return { ...targetTrace };
|
|
3421
|
+
}
|
|
3422
|
+
if (newPath.length === 4) {
|
|
3423
|
+
const [p0, p1, p2, p3] = newPath;
|
|
3424
|
+
let p1New;
|
|
3425
|
+
let p2New;
|
|
3426
|
+
const isHVHShape = p0.y === p1.y && p1.x === p2.x && p2.y === p3.y;
|
|
3427
|
+
if (isHVHShape) {
|
|
3428
|
+
const idealX = (p0.x + p3.x) / 2;
|
|
3429
|
+
p1New = { x: idealX, y: p1.y };
|
|
3430
|
+
p2New = { x: idealX, y: p2.y };
|
|
3431
|
+
} else {
|
|
3432
|
+
const idealY = (p0.y + p3.y) / 2;
|
|
3433
|
+
p1New = { x: p1.x, y: idealY };
|
|
3434
|
+
p2New = { x: p2.x, y: idealY };
|
|
3435
|
+
}
|
|
3436
|
+
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);
|
|
3437
|
+
if (!collides) {
|
|
3438
|
+
newPath[1] = p1New;
|
|
3439
|
+
newPath[2] = p2New;
|
|
3440
|
+
}
|
|
3441
|
+
return { ...targetTrace, tracePath: simplifyPath(newPath) };
|
|
3442
|
+
}
|
|
3443
|
+
for (let i = 1; i < newPath.length - 4; i++) {
|
|
3444
|
+
const p1 = newPath[i];
|
|
3445
|
+
const p2 = newPath[i + 1];
|
|
3446
|
+
const p3 = newPath[i + 2];
|
|
3447
|
+
const p4 = newPath[i + 3];
|
|
3448
|
+
const isHVHZShape = p1.y === p2.y && p2.x === p3.x && p3.y === p4.y;
|
|
3449
|
+
const isVHVZShape = p1.x === p2.x && p2.y === p3.y && p3.x === p4.x;
|
|
3450
|
+
const isCollinearHorizontal = p1.y === p2.y && p2.y === p3.y && p3.y === p4.y;
|
|
3451
|
+
const isCollinearVertical = p1.x === p2.x && p2.x === p3.x && p3.x === p4.x;
|
|
3452
|
+
const isCollinear = isCollinearHorizontal || isCollinearVertical;
|
|
3453
|
+
let isSameDirection = false;
|
|
3454
|
+
if (isHVHZShape) {
|
|
3455
|
+
isSameDirection = Math.sign(p2.x - p1.x) === Math.sign(p4.x - p3.x);
|
|
3456
|
+
} else if (isVHVZShape) {
|
|
3457
|
+
isSameDirection = Math.sign(p2.y - p1.y) === Math.sign(p4.y - p3.y);
|
|
3458
|
+
}
|
|
3459
|
+
const isValidZShape = (isHVHZShape || isVHVZShape) && !isCollinear && isSameDirection;
|
|
3460
|
+
if (!isValidZShape) {
|
|
3461
|
+
continue;
|
|
3462
|
+
}
|
|
3463
|
+
let p2New;
|
|
3464
|
+
let p3New;
|
|
3465
|
+
const len1Original = isHVHZShape ? Math.abs(p1.x - p2.x) : Math.abs(p1.y - p2.y);
|
|
3466
|
+
const len2Original = isHVHZShape ? Math.abs(p3.x - p4.x) : Math.abs(p3.y - p4.y);
|
|
3467
|
+
if (Math.abs(len1Original - len2Original) < 1e-3) {
|
|
3468
|
+
continue;
|
|
3469
|
+
}
|
|
3470
|
+
if (isHVHZShape) {
|
|
3471
|
+
const idealX = (p1.x + p4.x) / 2;
|
|
3472
|
+
p2New = { x: idealX, y: p2.y };
|
|
3473
|
+
p3New = { x: idealX, y: p3.y };
|
|
3474
|
+
} else {
|
|
3475
|
+
const idealY = (p1.y + p4.y) / 2;
|
|
3476
|
+
p2New = { x: p2.x, y: idealY };
|
|
3477
|
+
p3New = { x: p3.x, y: idealY };
|
|
3478
|
+
}
|
|
3479
|
+
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);
|
|
3480
|
+
if (!collides) {
|
|
3481
|
+
newPath[i + 1] = p2New;
|
|
3482
|
+
newPath[i + 2] = p3New;
|
|
3483
|
+
i = 0;
|
|
3499
3484
|
}
|
|
3500
|
-
this.queuedCandidatePairs = candidatePairs;
|
|
3501
3485
|
}
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3486
|
+
const finalSimplifiedPath = simplifyPath(newPath);
|
|
3487
|
+
return {
|
|
3488
|
+
...targetTrace,
|
|
3489
|
+
tracePath: finalSimplifiedPath
|
|
3490
|
+
};
|
|
3491
|
+
};
|
|
3492
|
+
|
|
3493
|
+
// lib/solvers/TraceCleanupSolver/TraceCleanupSolver.ts
|
|
3494
|
+
var TraceCleanupSolver = class extends BaseSolver {
|
|
3495
|
+
input;
|
|
3496
|
+
outputTraces;
|
|
3497
|
+
traceIdQueue;
|
|
3498
|
+
tracesMap;
|
|
3499
|
+
pipelineStep = "minimizing_turns";
|
|
3500
|
+
activeTraceId = null;
|
|
3501
|
+
// New property
|
|
3502
|
+
constructor(solverInput) {
|
|
3503
|
+
super();
|
|
3504
|
+
this.input = solverInput;
|
|
3505
|
+
this.outputTraces = [...solverInput.allTraces];
|
|
3506
|
+
this.tracesMap = new Map(this.outputTraces.map((t) => [t.mspPairId, t]));
|
|
3507
|
+
this.traceIdQueue = Array.from(solverInput.targetTraceIds);
|
|
3513
3508
|
}
|
|
3514
3509
|
_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;
|
|
3510
|
+
if (this.pipelineStep === "minimizing_turns" && this.traceIdQueue.length === 0) {
|
|
3511
|
+
this.pipelineStep = "balancing_l_shapes";
|
|
3512
|
+
this.traceIdQueue = Array.from(this.input.targetTraceIds);
|
|
3546
3513
|
}
|
|
3547
|
-
if (this.
|
|
3548
|
-
this.
|
|
3514
|
+
if (this.pipelineStep === "balancing_l_shapes" && this.traceIdQueue.length === 0) {
|
|
3515
|
+
this.solved = true;
|
|
3549
3516
|
return;
|
|
3550
3517
|
}
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3518
|
+
const targetMspConnectionPairId = this.traceIdQueue.shift();
|
|
3519
|
+
this.activeTraceId = targetMspConnectionPairId;
|
|
3520
|
+
const originalTrace = this.tracesMap.get(targetMspConnectionPairId);
|
|
3521
|
+
const { tracePath } = originalTrace;
|
|
3522
|
+
const is4PointRectangle = (path) => {
|
|
3523
|
+
if (path.length !== 4) return false;
|
|
3524
|
+
const [p0, p1, p2, p3] = path;
|
|
3525
|
+
const isHVHC = p0.y === p1.y && p1.x === p2.x && p2.y === p3.y && p0.x === p3.x;
|
|
3526
|
+
const isVHVC = p0.x === p1.x && p1.y === p2.y && p2.x === p3.x && p0.y === p3.y;
|
|
3527
|
+
return isHVHC || isVHVC;
|
|
3528
|
+
};
|
|
3529
|
+
if (is4PointRectangle(tracePath)) {
|
|
3563
3530
|
return;
|
|
3564
3531
|
}
|
|
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"
|
|
3532
|
+
const allTraces = Array.from(this.tracesMap.values());
|
|
3533
|
+
let updatedTrace;
|
|
3534
|
+
if (this.pipelineStep === "minimizing_turns") {
|
|
3535
|
+
updatedTrace = minimizeTurnsWithFilteredLabels({
|
|
3536
|
+
...this.input,
|
|
3537
|
+
targetMspConnectionPairId,
|
|
3538
|
+
traces: allTraces
|
|
3576
3539
|
});
|
|
3577
|
-
}
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
strokeDash: "4 4"
|
|
3540
|
+
} else {
|
|
3541
|
+
updatedTrace = balanceZShapes({
|
|
3542
|
+
...this.input,
|
|
3543
|
+
targetMspConnectionPairId,
|
|
3544
|
+
traces: allTraces
|
|
3583
3545
|
});
|
|
3584
3546
|
}
|
|
3585
|
-
|
|
3547
|
+
this.tracesMap.set(targetMspConnectionPairId, updatedTrace);
|
|
3548
|
+
this.outputTraces = Array.from(this.tracesMap.values());
|
|
3586
3549
|
}
|
|
3587
3550
|
getOutput() {
|
|
3588
|
-
if (!this.solved) {
|
|
3589
|
-
return { newTraces: [], allTracesMerged: this.params.alreadySolvedTraces };
|
|
3590
|
-
}
|
|
3591
3551
|
return {
|
|
3592
|
-
|
|
3593
|
-
allTracesMerged: [
|
|
3594
|
-
...this.params.alreadySolvedTraces,
|
|
3595
|
-
...this.solvedLongDistanceTraces
|
|
3596
|
-
]
|
|
3552
|
+
traces: this.outputTraces
|
|
3597
3553
|
};
|
|
3598
3554
|
}
|
|
3555
|
+
visualize() {
|
|
3556
|
+
const graphics = visualizeInputProblem(this.input.inputProblem, {
|
|
3557
|
+
chipAlpha: 0.1,
|
|
3558
|
+
connectionAlpha: 0.1
|
|
3559
|
+
});
|
|
3560
|
+
if (!graphics.lines) graphics.lines = [];
|
|
3561
|
+
if (!graphics.points) graphics.points = [];
|
|
3562
|
+
if (!graphics.rects) graphics.rects = [];
|
|
3563
|
+
if (!graphics.circles) graphics.circles = [];
|
|
3564
|
+
if (!graphics.texts) graphics.texts = [];
|
|
3565
|
+
for (const trace of this.outputTraces) {
|
|
3566
|
+
const line = {
|
|
3567
|
+
points: trace.tracePath.map((p) => ({ x: p.x, y: p.y })),
|
|
3568
|
+
strokeColor: trace.mspPairId === this.activeTraceId ? "red" : "blue"
|
|
3569
|
+
// Highlight active trace
|
|
3570
|
+
};
|
|
3571
|
+
graphics.lines.push(line);
|
|
3572
|
+
}
|
|
3573
|
+
return graphics;
|
|
3574
|
+
}
|
|
3599
3575
|
};
|
|
3600
3576
|
|
|
3601
3577
|
// lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts
|
|
@@ -3725,6 +3701,21 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
|
|
|
3725
3701
|
];
|
|
3726
3702
|
}
|
|
3727
3703
|
),
|
|
3704
|
+
definePipelineStep("traceCleanupSolver", TraceCleanupSolver, (instance) => {
|
|
3705
|
+
const prevSolverOutput = instance.traceLabelOverlapAvoidanceSolver.getOutput();
|
|
3706
|
+
const traces = prevSolverOutput.traces;
|
|
3707
|
+
const labelMergingOutput = instance.traceLabelOverlapAvoidanceSolver.labelMergingSolver.getOutput();
|
|
3708
|
+
return [
|
|
3709
|
+
{
|
|
3710
|
+
inputProblem: instance.inputProblem,
|
|
3711
|
+
allTraces: traces,
|
|
3712
|
+
targetTraceIds: new Set(traces.map((t) => t.mspPairId)),
|
|
3713
|
+
allLabelPlacements: labelMergingOutput.netLabelPlacements,
|
|
3714
|
+
mergedLabelNetIdMap: labelMergingOutput.mergedLabelNetIdMap,
|
|
3715
|
+
paddingBuffer: 0.1
|
|
3716
|
+
}
|
|
3717
|
+
];
|
|
3718
|
+
}),
|
|
3728
3719
|
definePipelineStep(
|
|
3729
3720
|
"netLabelPlacementSolver",
|
|
3730
3721
|
NetLabelPlacementSolver,
|