@tscircuit/schematic-trace-solver 0.0.37 → 0.0.39
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 +25 -1
- package/dist/index.js +917 -10
- package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/collisions.ts +17 -0
- package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/pathOps.ts +31 -12
- package/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts +40 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/TraceLabelOverlapAvoidanceSolver.ts +240 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/balanceLShapes.ts +207 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/countTurns.ts +18 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/detectTraceLabelOverlap.ts +38 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/hasCollisions.ts +22 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/hasCollisionsWithLabels.ts +19 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/index.ts +1 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/minimizeTurnsWithFilteredLabels.ts +66 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/rerouteCollidingTrace.ts +73 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/simplifyPath.ts +41 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/tryConnectPoints.ts +14 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/trySnipAndReconnect.ts +229 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/turnMinimization.ts +211 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/violation.ts +25 -0
- package/package.json +1 -1
- package/site/examples/example20.page.tsx +103 -0
- package/site/examples/example22.page.tsx +108 -0
- package/site/examples/example23.page.tsx +137 -0
- package/site/examples/example24.page.tsx +128 -0
- package/tests/examples/__snapshots__/example16.snap.svg +27 -27
- package/tests/examples/__snapshots__/example22.snap.svg +137 -0
- package/tests/examples/__snapshots__/example23.snap.svg +137 -0
- package/tests/examples/__snapshots__/example24.snap.svg +143 -0
- package/tests/examples/__snapshots__/example25.snap.svg +165 -0
- package/tests/examples/__snapshots__/example26.snap.svg +157 -0
- package/tests/examples/example22.test.tsx +109 -0
- package/tests/examples/example23.test.tsx +109 -0
- package/tests/examples/example24.test.tsx +114 -0
- package/tests/examples/example25.test.tsx +143 -0
- package/tests/examples/example26.test.tsx +134 -0
package/dist/index.js
CHANGED
|
@@ -588,6 +588,16 @@ var findFirstCollision = (pts, rects, opts = {}) => {
|
|
|
588
588
|
}
|
|
589
589
|
return null;
|
|
590
590
|
};
|
|
591
|
+
var isPathCollidingWithObstacles = (path, obstacles) => {
|
|
592
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
593
|
+
for (const obstacle of obstacles) {
|
|
594
|
+
if (segmentIntersectsRect(path[i], path[i + 1], obstacle)) {
|
|
595
|
+
return true;
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
return false;
|
|
600
|
+
};
|
|
591
601
|
|
|
592
602
|
// lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/mid.ts
|
|
593
603
|
var EPS2 = 1e-9;
|
|
@@ -658,23 +668,33 @@ var candidateMidsFromSet = (axis, colliding, rectsById, collisionRectIds, aabb,
|
|
|
658
668
|
// lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/pathOps.ts
|
|
659
669
|
var EPS3 = 1e-9;
|
|
660
670
|
var shiftSegmentOrth = (pts, segIndex, axis, newCoord, eps = EPS3) => {
|
|
661
|
-
if (segIndex < 0 || segIndex >= pts.length - 1)
|
|
671
|
+
if (segIndex < 0 || segIndex >= pts.length - 1) {
|
|
672
|
+
return null;
|
|
673
|
+
}
|
|
662
674
|
const a = pts[segIndex];
|
|
663
675
|
const b = pts[segIndex + 1];
|
|
664
676
|
const vert = isVertical(a, b, eps);
|
|
665
677
|
const horz = isHorizontal(a, b, eps);
|
|
666
|
-
if (!vert && !horz)
|
|
667
|
-
|
|
668
|
-
|
|
678
|
+
if (!vert && !horz) {
|
|
679
|
+
return null;
|
|
680
|
+
}
|
|
681
|
+
if (vert && axis !== "x") {
|
|
682
|
+
return null;
|
|
683
|
+
}
|
|
684
|
+
if (horz && axis !== "y") {
|
|
685
|
+
return null;
|
|
686
|
+
}
|
|
669
687
|
const out = pts.map((p) => ({ ...p }));
|
|
670
688
|
if (axis === "x") {
|
|
671
|
-
if (Math.abs(a.x - newCoord) < eps && Math.abs(b.x - newCoord) < eps)
|
|
689
|
+
if (Math.abs(a.x - newCoord) < eps && Math.abs(b.x - newCoord) < eps) {
|
|
672
690
|
return null;
|
|
691
|
+
}
|
|
673
692
|
out[segIndex] = { ...out[segIndex], x: newCoord };
|
|
674
693
|
out[segIndex + 1] = { ...out[segIndex + 1], x: newCoord };
|
|
675
694
|
} else {
|
|
676
|
-
if (Math.abs(a.y - newCoord) < eps && Math.abs(b.y - newCoord) < eps)
|
|
695
|
+
if (Math.abs(a.y - newCoord) < eps && Math.abs(b.y - newCoord) < eps) {
|
|
677
696
|
return null;
|
|
697
|
+
}
|
|
678
698
|
out[segIndex] = { ...out[segIndex], y: newCoord };
|
|
679
699
|
out[segIndex + 1] = { ...out[segIndex + 1], y: newCoord };
|
|
680
700
|
}
|
|
@@ -682,22 +702,31 @@ var shiftSegmentOrth = (pts, segIndex, axis, newCoord, eps = EPS3) => {
|
|
|
682
702
|
const p = out[segIndex - 1];
|
|
683
703
|
const q = out[segIndex];
|
|
684
704
|
const manhattan = Math.abs(p.x - q.x) + Math.abs(p.y - q.y);
|
|
685
|
-
if (manhattan < eps)
|
|
705
|
+
if (manhattan < eps) {
|
|
706
|
+
return null;
|
|
707
|
+
}
|
|
686
708
|
}
|
|
687
709
|
if (segIndex + 2 <= out.length - 1) {
|
|
688
710
|
const p = out[segIndex + 1];
|
|
689
711
|
const q = out[segIndex + 2];
|
|
690
712
|
const manhattan = Math.abs(p.x - q.x) + Math.abs(p.y - q.y);
|
|
691
|
-
if (manhattan < eps)
|
|
713
|
+
if (manhattan < eps) {
|
|
714
|
+
return null;
|
|
715
|
+
}
|
|
692
716
|
}
|
|
693
717
|
for (let i = 0; i < out.length - 1; i++) {
|
|
694
718
|
const u = out[i];
|
|
695
719
|
const v = out[i + 1];
|
|
696
|
-
if (!isHorizontal(u, v, eps) && !isVertical(u, v, eps))
|
|
720
|
+
if (!isHorizontal(u, v, eps) && !isVertical(u, v, eps)) {
|
|
721
|
+
return null;
|
|
722
|
+
}
|
|
697
723
|
}
|
|
698
724
|
return out;
|
|
699
725
|
};
|
|
700
|
-
var pathKey = (pts, decimals = 6) =>
|
|
726
|
+
var pathKey = (pts, decimals = 6) => {
|
|
727
|
+
const key = pts.map((p) => `${p.x.toFixed(decimals)},${p.y.toFixed(decimals)}`).join("|");
|
|
728
|
+
return key;
|
|
729
|
+
};
|
|
701
730
|
|
|
702
731
|
// lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2.ts
|
|
703
732
|
var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
|
|
@@ -2170,6 +2199,854 @@ var NetLabelPlacementSolver = class extends BaseSolver {
|
|
|
2170
2199
|
// lib/solvers/GuidelinesSolver/GuidelinesSolver.ts
|
|
2171
2200
|
import { getBounds as getBounds3 } from "graphics-debug";
|
|
2172
2201
|
|
|
2202
|
+
// lib/solvers/TraceLabelOverlapAvoidanceSolver/detectTraceLabelOverlap.ts
|
|
2203
|
+
var detectTraceLabelOverlap = (traces, netLabels) => {
|
|
2204
|
+
const overlaps = [];
|
|
2205
|
+
for (const trace of traces) {
|
|
2206
|
+
for (const label of netLabels) {
|
|
2207
|
+
const labelBounds = getRectBounds(label.center, label.width, label.height);
|
|
2208
|
+
for (let i = 0; i < trace.tracePath.length - 1; i++) {
|
|
2209
|
+
const p1 = trace.tracePath[i];
|
|
2210
|
+
const p2 = trace.tracePath[i + 1];
|
|
2211
|
+
if (segmentIntersectsRect2(p1, p2, labelBounds)) {
|
|
2212
|
+
if (trace.globalConnNetId === label.globalConnNetId) {
|
|
2213
|
+
break;
|
|
2214
|
+
}
|
|
2215
|
+
overlaps.push({ trace, label });
|
|
2216
|
+
break;
|
|
2217
|
+
}
|
|
2218
|
+
}
|
|
2219
|
+
}
|
|
2220
|
+
}
|
|
2221
|
+
return overlaps;
|
|
2222
|
+
};
|
|
2223
|
+
|
|
2224
|
+
// lib/solvers/TraceLabelOverlapAvoidanceSolver/violation.ts
|
|
2225
|
+
var findTraceViolationZone = (path, labelBounds) => {
|
|
2226
|
+
const isPointInside = (p) => p.x > labelBounds.minX && p.x < labelBounds.maxX && p.y > labelBounds.minY && p.y < labelBounds.maxY;
|
|
2227
|
+
let firstInsideIndex = -1;
|
|
2228
|
+
let lastInsideIndex = -1;
|
|
2229
|
+
for (let i = 0; i < path.length; i++) {
|
|
2230
|
+
if (isPointInside(path[i])) {
|
|
2231
|
+
if (firstInsideIndex === -1) {
|
|
2232
|
+
firstInsideIndex = i;
|
|
2233
|
+
}
|
|
2234
|
+
lastInsideIndex = i;
|
|
2235
|
+
}
|
|
2236
|
+
}
|
|
2237
|
+
return { firstInsideIndex, lastInsideIndex };
|
|
2238
|
+
};
|
|
2239
|
+
|
|
2240
|
+
// lib/solvers/TraceLabelOverlapAvoidanceSolver/simplifyPath.ts
|
|
2241
|
+
var simplifyPath = (path) => {
|
|
2242
|
+
if (path.length < 3) return path;
|
|
2243
|
+
const newPath = [path[0]];
|
|
2244
|
+
for (let i = 1; i < path.length - 1; i++) {
|
|
2245
|
+
const p1 = newPath[newPath.length - 1];
|
|
2246
|
+
const p2 = path[i];
|
|
2247
|
+
const p3 = path[i + 1];
|
|
2248
|
+
if (isVertical(p1, p2) && isVertical(p2, p3) || isHorizontal(p1, p2) && isHorizontal(p2, p3)) {
|
|
2249
|
+
continue;
|
|
2250
|
+
}
|
|
2251
|
+
newPath.push(p2);
|
|
2252
|
+
}
|
|
2253
|
+
newPath.push(path[path.length - 1]);
|
|
2254
|
+
if (newPath.length < 3) return newPath;
|
|
2255
|
+
const finalPath = [newPath[0]];
|
|
2256
|
+
for (let i = 1; i < newPath.length - 1; i++) {
|
|
2257
|
+
const p1 = finalPath[finalPath.length - 1];
|
|
2258
|
+
const p2 = newPath[i];
|
|
2259
|
+
const p3 = newPath[i + 1];
|
|
2260
|
+
if (isVertical(p1, p2) && isVertical(p2, p3) || isHorizontal(p1, p2) && isHorizontal(p2, p3)) {
|
|
2261
|
+
continue;
|
|
2262
|
+
}
|
|
2263
|
+
finalPath.push(p2);
|
|
2264
|
+
}
|
|
2265
|
+
finalPath.push(newPath[newPath.length - 1]);
|
|
2266
|
+
return finalPath;
|
|
2267
|
+
};
|
|
2268
|
+
|
|
2269
|
+
// lib/solvers/TraceLabelOverlapAvoidanceSolver/trySnipAndReconnect.ts
|
|
2270
|
+
var trySnipAndReconnect = ({
|
|
2271
|
+
initialTrace,
|
|
2272
|
+
firstInsideIndex,
|
|
2273
|
+
lastInsideIndex,
|
|
2274
|
+
labelBounds,
|
|
2275
|
+
obstacles
|
|
2276
|
+
}) => {
|
|
2277
|
+
if (firstInsideIndex <= 0 || lastInsideIndex >= initialTrace.tracePath.length - 1) {
|
|
2278
|
+
return null;
|
|
2279
|
+
}
|
|
2280
|
+
const entryPoint = initialTrace.tracePath[firstInsideIndex - 1];
|
|
2281
|
+
const exitPoint = initialTrace.tracePath[lastInsideIndex + 1];
|
|
2282
|
+
const pathToEntry = initialTrace.tracePath.slice(0, firstInsideIndex);
|
|
2283
|
+
const pathFromExit = initialTrace.tracePath.slice(lastInsideIndex + 1);
|
|
2284
|
+
const candidateDetours = [];
|
|
2285
|
+
if (entryPoint.x !== exitPoint.x && entryPoint.y !== exitPoint.y) {
|
|
2286
|
+
candidateDetours.push([{ x: exitPoint.x, y: entryPoint.y }]);
|
|
2287
|
+
candidateDetours.push([{ x: entryPoint.x, y: exitPoint.y }]);
|
|
2288
|
+
} else if (entryPoint.x === exitPoint.x || entryPoint.y === exitPoint.y) {
|
|
2289
|
+
const newPath = [...pathToEntry, ...pathFromExit];
|
|
2290
|
+
const simplified = simplifyPath(newPath);
|
|
2291
|
+
if (!isPathCollidingWithObstacles(simplified, obstacles)) {
|
|
2292
|
+
return { ...initialTrace, tracePath: simplified };
|
|
2293
|
+
}
|
|
2294
|
+
}
|
|
2295
|
+
for (let i = 0; i < candidateDetours.length; i++) {
|
|
2296
|
+
const detour = candidateDetours[i];
|
|
2297
|
+
const newFullPath = [...pathToEntry, ...detour, ...pathFromExit];
|
|
2298
|
+
const simplified = simplifyPath(newFullPath);
|
|
2299
|
+
if (!isPathCollidingWithObstacles(simplified, obstacles)) {
|
|
2300
|
+
return { ...initialTrace, tracePath: simplified };
|
|
2301
|
+
}
|
|
2302
|
+
}
|
|
2303
|
+
candidateDetours.length = 0;
|
|
2304
|
+
const buffer = 0.1;
|
|
2305
|
+
const leftX = labelBounds.minX - buffer;
|
|
2306
|
+
const rightX = labelBounds.maxX + buffer;
|
|
2307
|
+
const topY = labelBounds.maxY + buffer;
|
|
2308
|
+
const bottomY = labelBounds.minY - buffer;
|
|
2309
|
+
if ((entryPoint.x <= labelBounds.minX || exitPoint.x <= labelBounds.minX) && entryPoint.x < labelBounds.maxX && exitPoint.x < labelBounds.maxX) {
|
|
2310
|
+
candidateDetours.push([
|
|
2311
|
+
{ x: leftX, y: entryPoint.y },
|
|
2312
|
+
{ x: leftX, y: exitPoint.y }
|
|
2313
|
+
]);
|
|
2314
|
+
}
|
|
2315
|
+
if ((entryPoint.x >= labelBounds.maxX || exitPoint.x >= labelBounds.maxX) && entryPoint.x > labelBounds.minX && exitPoint.x > labelBounds.minX) {
|
|
2316
|
+
candidateDetours.push([
|
|
2317
|
+
{ x: rightX, y: entryPoint.y },
|
|
2318
|
+
{ x: rightX, y: exitPoint.y }
|
|
2319
|
+
]);
|
|
2320
|
+
}
|
|
2321
|
+
if ((entryPoint.y >= labelBounds.maxY || exitPoint.y >= labelBounds.maxY) && entryPoint.y > labelBounds.minY && exitPoint.y > labelBounds.minY) {
|
|
2322
|
+
candidateDetours.push([
|
|
2323
|
+
{ x: entryPoint.x, y: topY },
|
|
2324
|
+
{ x: exitPoint.x, y: topY }
|
|
2325
|
+
]);
|
|
2326
|
+
}
|
|
2327
|
+
if ((entryPoint.y <= labelBounds.minY || exitPoint.y <= labelBounds.minY) && entryPoint.y < labelBounds.maxY && exitPoint.y < labelBounds.maxY) {
|
|
2328
|
+
candidateDetours.push([
|
|
2329
|
+
{ x: entryPoint.x, y: bottomY },
|
|
2330
|
+
{ x: exitPoint.x, y: bottomY }
|
|
2331
|
+
]);
|
|
2332
|
+
}
|
|
2333
|
+
for (let i = 0; i < candidateDetours.length; i++) {
|
|
2334
|
+
const detour = candidateDetours[i];
|
|
2335
|
+
const newFullPath = [...pathToEntry, ...detour, ...pathFromExit];
|
|
2336
|
+
const simplified = simplifyPath(newFullPath);
|
|
2337
|
+
if (!isPathCollidingWithObstacles(simplified, obstacles)) {
|
|
2338
|
+
return { ...initialTrace, tracePath: simplified };
|
|
2339
|
+
}
|
|
2340
|
+
}
|
|
2341
|
+
return null;
|
|
2342
|
+
};
|
|
2343
|
+
var tryFourPointDetour = ({
|
|
2344
|
+
initialTrace,
|
|
2345
|
+
label,
|
|
2346
|
+
labelBounds,
|
|
2347
|
+
obstacles,
|
|
2348
|
+
paddingBuffer,
|
|
2349
|
+
detourCount
|
|
2350
|
+
}) => {
|
|
2351
|
+
let collidingSegIndex = -1;
|
|
2352
|
+
for (let i = 0; i < initialTrace.tracePath.length - 1; i++) {
|
|
2353
|
+
if (segmentIntersectsRect(
|
|
2354
|
+
initialTrace.tracePath[i],
|
|
2355
|
+
initialTrace.tracePath[i + 1],
|
|
2356
|
+
labelBounds
|
|
2357
|
+
)) {
|
|
2358
|
+
collidingSegIndex = i;
|
|
2359
|
+
break;
|
|
2360
|
+
}
|
|
2361
|
+
}
|
|
2362
|
+
if (collidingSegIndex === -1) return initialTrace;
|
|
2363
|
+
const pA = initialTrace.tracePath[collidingSegIndex];
|
|
2364
|
+
const pB = initialTrace.tracePath[collidingSegIndex + 1];
|
|
2365
|
+
if (!pA || !pB) return null;
|
|
2366
|
+
const candidateDetours = [];
|
|
2367
|
+
const paddedLabelBounds = getRectBounds(
|
|
2368
|
+
label.center,
|
|
2369
|
+
label.width,
|
|
2370
|
+
label.height
|
|
2371
|
+
);
|
|
2372
|
+
const effectivePadding = paddingBuffer + detourCount * paddingBuffer;
|
|
2373
|
+
if (isVertical(pA, pB)) {
|
|
2374
|
+
const xCandidates = [
|
|
2375
|
+
paddedLabelBounds.maxX + effectivePadding,
|
|
2376
|
+
paddedLabelBounds.minX - effectivePadding
|
|
2377
|
+
];
|
|
2378
|
+
for (const newX of xCandidates) {
|
|
2379
|
+
candidateDetours.push(
|
|
2380
|
+
pB.y > pA.y ? [
|
|
2381
|
+
{ x: pA.x, y: paddedLabelBounds.minY - effectivePadding },
|
|
2382
|
+
{ x: newX, y: paddedLabelBounds.minY - effectivePadding },
|
|
2383
|
+
{ x: newX, y: paddedLabelBounds.maxY + effectivePadding },
|
|
2384
|
+
{ x: pB.x, y: paddedLabelBounds.maxY + effectivePadding }
|
|
2385
|
+
] : [
|
|
2386
|
+
{ x: pA.x, y: paddedLabelBounds.maxY + effectivePadding },
|
|
2387
|
+
{ x: newX, y: paddedLabelBounds.maxY + effectivePadding },
|
|
2388
|
+
{ x: newX, y: paddedLabelBounds.minY - effectivePadding },
|
|
2389
|
+
{ x: pB.x, y: paddedLabelBounds.minY - effectivePadding }
|
|
2390
|
+
]
|
|
2391
|
+
);
|
|
2392
|
+
}
|
|
2393
|
+
} else {
|
|
2394
|
+
const yCandidates = [
|
|
2395
|
+
paddedLabelBounds.maxY + effectivePadding,
|
|
2396
|
+
paddedLabelBounds.minY - effectivePadding
|
|
2397
|
+
];
|
|
2398
|
+
for (const newY of yCandidates) {
|
|
2399
|
+
candidateDetours.push(
|
|
2400
|
+
pB.x > pA.x ? [
|
|
2401
|
+
{ x: paddedLabelBounds.minX - effectivePadding, y: pA.y },
|
|
2402
|
+
{ x: paddedLabelBounds.minX - effectivePadding, y: newY },
|
|
2403
|
+
{ x: paddedLabelBounds.maxX + effectivePadding, y: newY },
|
|
2404
|
+
{ x: paddedLabelBounds.maxX + effectivePadding, y: pB.y }
|
|
2405
|
+
] : [
|
|
2406
|
+
{ x: paddedLabelBounds.maxX + effectivePadding, y: pA.y },
|
|
2407
|
+
{ x: paddedLabelBounds.maxX + effectivePadding, y: newY },
|
|
2408
|
+
{ x: paddedLabelBounds.minX - effectivePadding, y: newY },
|
|
2409
|
+
{ x: paddedLabelBounds.minX - effectivePadding, y: pB.y }
|
|
2410
|
+
]
|
|
2411
|
+
);
|
|
2412
|
+
}
|
|
2413
|
+
}
|
|
2414
|
+
for (const detourPoints of candidateDetours) {
|
|
2415
|
+
const finalPath = [
|
|
2416
|
+
...initialTrace.tracePath.slice(0, collidingSegIndex + 1),
|
|
2417
|
+
...detourPoints,
|
|
2418
|
+
...initialTrace.tracePath.slice(collidingSegIndex + 1)
|
|
2419
|
+
];
|
|
2420
|
+
const simplifiedFinalPath = simplifyPath(finalPath);
|
|
2421
|
+
if (!isPathCollidingWithObstacles(simplifiedFinalPath, obstacles)) {
|
|
2422
|
+
return { ...initialTrace, tracePath: simplifiedFinalPath };
|
|
2423
|
+
}
|
|
2424
|
+
}
|
|
2425
|
+
return null;
|
|
2426
|
+
};
|
|
2427
|
+
|
|
2428
|
+
// lib/solvers/TraceLabelOverlapAvoidanceSolver/rerouteCollidingTrace.ts
|
|
2429
|
+
var rerouteCollidingTrace = ({
|
|
2430
|
+
trace,
|
|
2431
|
+
label,
|
|
2432
|
+
problem,
|
|
2433
|
+
paddingBuffer,
|
|
2434
|
+
detourCount
|
|
2435
|
+
}) => {
|
|
2436
|
+
const initialTrace = { ...trace, tracePath: simplifyPath(trace.tracePath) };
|
|
2437
|
+
if (trace.globalConnNetId === label.globalConnNetId) {
|
|
2438
|
+
return initialTrace;
|
|
2439
|
+
}
|
|
2440
|
+
const obstacles = getObstacleRects(problem);
|
|
2441
|
+
const labelPadding = paddingBuffer;
|
|
2442
|
+
const labelBoundsRaw = getRectBounds(label.center, label.width, label.height);
|
|
2443
|
+
const labelBounds = {
|
|
2444
|
+
minX: labelBoundsRaw.minX - labelPadding,
|
|
2445
|
+
minY: labelBoundsRaw.minY - labelPadding,
|
|
2446
|
+
maxX: labelBoundsRaw.maxX + labelPadding,
|
|
2447
|
+
maxY: labelBoundsRaw.maxY + labelPadding,
|
|
2448
|
+
chipId: `netlabel-${label.netId}`
|
|
2449
|
+
};
|
|
2450
|
+
const fourPointResult = tryFourPointDetour({
|
|
2451
|
+
initialTrace,
|
|
2452
|
+
label,
|
|
2453
|
+
labelBounds,
|
|
2454
|
+
obstacles,
|
|
2455
|
+
paddingBuffer,
|
|
2456
|
+
detourCount
|
|
2457
|
+
});
|
|
2458
|
+
if (fourPointResult) {
|
|
2459
|
+
initialTrace.tracePath = fourPointResult.tracePath;
|
|
2460
|
+
}
|
|
2461
|
+
const { firstInsideIndex, lastInsideIndex } = findTraceViolationZone(
|
|
2462
|
+
initialTrace.tracePath,
|
|
2463
|
+
labelBounds
|
|
2464
|
+
);
|
|
2465
|
+
const snipReconnectResult = trySnipAndReconnect({
|
|
2466
|
+
initialTrace,
|
|
2467
|
+
firstInsideIndex,
|
|
2468
|
+
lastInsideIndex,
|
|
2469
|
+
labelBounds,
|
|
2470
|
+
obstacles
|
|
2471
|
+
});
|
|
2472
|
+
if (snipReconnectResult) {
|
|
2473
|
+
return snipReconnectResult;
|
|
2474
|
+
}
|
|
2475
|
+
if (fourPointResult) {
|
|
2476
|
+
return fourPointResult;
|
|
2477
|
+
}
|
|
2478
|
+
return initialTrace;
|
|
2479
|
+
};
|
|
2480
|
+
|
|
2481
|
+
// lib/solvers/TraceLabelOverlapAvoidanceSolver/hasCollisions.ts
|
|
2482
|
+
var hasCollisions = (pathSegments, obstacles) => {
|
|
2483
|
+
for (let i = 0; i < pathSegments.length - 1; i++) {
|
|
2484
|
+
const p1 = pathSegments[i];
|
|
2485
|
+
const p2 = pathSegments[i + 1];
|
|
2486
|
+
for (const obstacle of obstacles) {
|
|
2487
|
+
if (segmentIntersectsRect(p1, p2, obstacle)) {
|
|
2488
|
+
return true;
|
|
2489
|
+
}
|
|
2490
|
+
}
|
|
2491
|
+
}
|
|
2492
|
+
return false;
|
|
2493
|
+
};
|
|
2494
|
+
|
|
2495
|
+
// lib/solvers/TraceLabelOverlapAvoidanceSolver/countTurns.ts
|
|
2496
|
+
var countTurns = (points) => {
|
|
2497
|
+
let turns = 0;
|
|
2498
|
+
for (let i = 1; i < points.length - 1; i++) {
|
|
2499
|
+
const prev = points[i - 1];
|
|
2500
|
+
const curr = points[i];
|
|
2501
|
+
const next = points[i + 1];
|
|
2502
|
+
const prevVertical = prev.x === curr.x;
|
|
2503
|
+
const nextVertical = curr.x === next.x;
|
|
2504
|
+
if (prevVertical !== nextVertical) {
|
|
2505
|
+
turns++;
|
|
2506
|
+
}
|
|
2507
|
+
}
|
|
2508
|
+
return turns;
|
|
2509
|
+
};
|
|
2510
|
+
|
|
2511
|
+
// lib/solvers/TraceLabelOverlapAvoidanceSolver/tryConnectPoints.ts
|
|
2512
|
+
var tryConnectPoints = (start, end) => {
|
|
2513
|
+
const candidates = [];
|
|
2514
|
+
if (start.x === end.x || start.y === end.y) {
|
|
2515
|
+
candidates.push([start, end]);
|
|
2516
|
+
} else {
|
|
2517
|
+
candidates.push([start, { x: end.x, y: start.y }, end]);
|
|
2518
|
+
candidates.push([start, { x: start.x, y: end.y }, end]);
|
|
2519
|
+
}
|
|
2520
|
+
return candidates;
|
|
2521
|
+
};
|
|
2522
|
+
|
|
2523
|
+
// lib/solvers/TraceLabelOverlapAvoidanceSolver/hasCollisionsWithLabels.ts
|
|
2524
|
+
var hasCollisionsWithLabels = (pathSegments, labels) => {
|
|
2525
|
+
for (let i = 0; i < pathSegments.length - 1; i++) {
|
|
2526
|
+
const p1 = pathSegments[i];
|
|
2527
|
+
const p2 = pathSegments[i + 1];
|
|
2528
|
+
for (const label of labels) {
|
|
2529
|
+
if (segmentIntersectsRect(p1, p2, label)) {
|
|
2530
|
+
return true;
|
|
2531
|
+
}
|
|
2532
|
+
}
|
|
2533
|
+
}
|
|
2534
|
+
return false;
|
|
2535
|
+
};
|
|
2536
|
+
|
|
2537
|
+
// lib/solvers/TraceLabelOverlapAvoidanceSolver/turnMinimization.ts
|
|
2538
|
+
var minimizeTurns = ({
|
|
2539
|
+
path,
|
|
2540
|
+
obstacles,
|
|
2541
|
+
labelBounds
|
|
2542
|
+
}) => {
|
|
2543
|
+
if (path.length <= 2) {
|
|
2544
|
+
return path;
|
|
2545
|
+
}
|
|
2546
|
+
const recognizeStairStepPattern = (pathToCheck, startIdx) => {
|
|
2547
|
+
if (startIdx >= pathToCheck.length - 3) return -1;
|
|
2548
|
+
let endIdx = startIdx;
|
|
2549
|
+
let isStairStep = true;
|
|
2550
|
+
for (let i = startIdx; i < pathToCheck.length - 2 && i < startIdx + 10; i++) {
|
|
2551
|
+
if (i + 2 >= pathToCheck.length) break;
|
|
2552
|
+
const p1 = pathToCheck[i];
|
|
2553
|
+
const p2 = pathToCheck[i + 1];
|
|
2554
|
+
const p3 = pathToCheck[i + 2];
|
|
2555
|
+
const seg1Vertical = p1.x === p2.x;
|
|
2556
|
+
const seg2Vertical = p2.x === p3.x;
|
|
2557
|
+
if (seg1Vertical === seg2Vertical) {
|
|
2558
|
+
break;
|
|
2559
|
+
}
|
|
2560
|
+
const seg1Direction = seg1Vertical ? Math.sign(p2.y - p1.y) : Math.sign(p2.x - p1.x);
|
|
2561
|
+
if (i > startIdx) {
|
|
2562
|
+
const prevP = pathToCheck[i - 1];
|
|
2563
|
+
const prevSegVertical = prevP.x === p1.x;
|
|
2564
|
+
const prevDirection = prevSegVertical ? Math.sign(p1.y - prevP.y) : Math.sign(p1.x - prevP.x);
|
|
2565
|
+
if (seg1Vertical && prevSegVertical && seg1Direction !== prevDirection || !seg1Vertical && !prevSegVertical && seg1Direction !== prevDirection) {
|
|
2566
|
+
isStairStep = false;
|
|
2567
|
+
break;
|
|
2568
|
+
}
|
|
2569
|
+
}
|
|
2570
|
+
endIdx = i + 2;
|
|
2571
|
+
}
|
|
2572
|
+
return isStairStep && endIdx - startIdx >= 3 ? endIdx : -1;
|
|
2573
|
+
};
|
|
2574
|
+
let optimizedPath = [...path];
|
|
2575
|
+
let currentTurns = countTurns(optimizedPath);
|
|
2576
|
+
let improved = true;
|
|
2577
|
+
while (improved) {
|
|
2578
|
+
improved = false;
|
|
2579
|
+
for (let startIdx = 0; startIdx < optimizedPath.length - 3; startIdx++) {
|
|
2580
|
+
const stairEndIdx = recognizeStairStepPattern(optimizedPath, startIdx);
|
|
2581
|
+
if (stairEndIdx > 0) {
|
|
2582
|
+
const startPoint = optimizedPath[startIdx];
|
|
2583
|
+
const endPoint = optimizedPath[stairEndIdx];
|
|
2584
|
+
const connectionOptions = tryConnectPoints(startPoint, endPoint);
|
|
2585
|
+
for (const connection of connectionOptions) {
|
|
2586
|
+
const testPath = [
|
|
2587
|
+
...optimizedPath.slice(0, startIdx + 1),
|
|
2588
|
+
...connection.slice(1, -1),
|
|
2589
|
+
...optimizedPath.slice(stairEndIdx)
|
|
2590
|
+
];
|
|
2591
|
+
const collidesWithObstacles = hasCollisions(connection, obstacles);
|
|
2592
|
+
const collidesWithLabels = hasCollisionsWithLabels(
|
|
2593
|
+
connection,
|
|
2594
|
+
labelBounds
|
|
2595
|
+
);
|
|
2596
|
+
if (!collidesWithObstacles && !collidesWithLabels) {
|
|
2597
|
+
const newTurns = countTurns(testPath);
|
|
2598
|
+
const turnsRemoved = stairEndIdx - startIdx - 1;
|
|
2599
|
+
optimizedPath = testPath;
|
|
2600
|
+
currentTurns = newTurns;
|
|
2601
|
+
improved = true;
|
|
2602
|
+
break;
|
|
2603
|
+
}
|
|
2604
|
+
}
|
|
2605
|
+
if (improved) break;
|
|
2606
|
+
}
|
|
2607
|
+
}
|
|
2608
|
+
if (!improved) {
|
|
2609
|
+
for (let startIdx = 0; startIdx < optimizedPath.length - 2; startIdx++) {
|
|
2610
|
+
const maxRemove = Math.min(
|
|
2611
|
+
optimizedPath.length - startIdx - 2,
|
|
2612
|
+
optimizedPath.length - 2
|
|
2613
|
+
);
|
|
2614
|
+
for (let removeCount = 1; removeCount <= maxRemove; removeCount++) {
|
|
2615
|
+
const endIdx = startIdx + removeCount + 1;
|
|
2616
|
+
if (endIdx >= optimizedPath.length) continue;
|
|
2617
|
+
const startPoint = optimizedPath[startIdx];
|
|
2618
|
+
const endPoint = optimizedPath[endIdx];
|
|
2619
|
+
const connectionOptions = tryConnectPoints(startPoint, endPoint);
|
|
2620
|
+
for (const connection of connectionOptions) {
|
|
2621
|
+
const testPath = [
|
|
2622
|
+
...optimizedPath.slice(0, startIdx + 1),
|
|
2623
|
+
...connection.slice(1, -1),
|
|
2624
|
+
...optimizedPath.slice(endIdx)
|
|
2625
|
+
];
|
|
2626
|
+
const connectionSegments = connection;
|
|
2627
|
+
const collidesWithObstacles = hasCollisions(
|
|
2628
|
+
connectionSegments,
|
|
2629
|
+
obstacles
|
|
2630
|
+
);
|
|
2631
|
+
const collidesWithLabels = hasCollisionsWithLabels(
|
|
2632
|
+
connectionSegments,
|
|
2633
|
+
labelBounds
|
|
2634
|
+
);
|
|
2635
|
+
if (!collidesWithObstacles && !collidesWithLabels) {
|
|
2636
|
+
const newTurns = countTurns(testPath);
|
|
2637
|
+
if (newTurns < currentTurns || newTurns === currentTurns && testPath.length < optimizedPath.length) {
|
|
2638
|
+
optimizedPath = testPath;
|
|
2639
|
+
currentTurns = newTurns;
|
|
2640
|
+
improved = true;
|
|
2641
|
+
break;
|
|
2642
|
+
}
|
|
2643
|
+
}
|
|
2644
|
+
}
|
|
2645
|
+
if (improved) break;
|
|
2646
|
+
}
|
|
2647
|
+
if (improved) break;
|
|
2648
|
+
}
|
|
2649
|
+
}
|
|
2650
|
+
if (!improved) {
|
|
2651
|
+
for (let i = 0; i < optimizedPath.length - 2; i++) {
|
|
2652
|
+
const p1 = optimizedPath[i];
|
|
2653
|
+
const p2 = optimizedPath[i + 1];
|
|
2654
|
+
const p3 = optimizedPath[i + 2];
|
|
2655
|
+
const allVertical = p1.x === p2.x && p2.x === p3.x;
|
|
2656
|
+
const allHorizontal = p1.y === p2.y && p2.y === p3.y;
|
|
2657
|
+
if (allVertical || allHorizontal) {
|
|
2658
|
+
const testPath = [
|
|
2659
|
+
...optimizedPath.slice(0, i + 1),
|
|
2660
|
+
...optimizedPath.slice(i + 2)
|
|
2661
|
+
];
|
|
2662
|
+
const collidesWithObstacles = hasCollisions([p1, p3], obstacles);
|
|
2663
|
+
const collidesWithLabels = hasCollisionsWithLabels(
|
|
2664
|
+
[p1, p3],
|
|
2665
|
+
labelBounds
|
|
2666
|
+
);
|
|
2667
|
+
if (!collidesWithObstacles && !collidesWithLabels) {
|
|
2668
|
+
optimizedPath = testPath;
|
|
2669
|
+
improved = true;
|
|
2670
|
+
break;
|
|
2671
|
+
}
|
|
2672
|
+
}
|
|
2673
|
+
}
|
|
2674
|
+
}
|
|
2675
|
+
}
|
|
2676
|
+
const finalSimplifiedPath = simplifyPath(optimizedPath);
|
|
2677
|
+
return finalSimplifiedPath;
|
|
2678
|
+
};
|
|
2679
|
+
|
|
2680
|
+
// lib/solvers/TraceLabelOverlapAvoidanceSolver/minimizeTurnsWithFilteredLabels.ts
|
|
2681
|
+
var minimizeTurnsWithFilteredLabels = ({
|
|
2682
|
+
traces,
|
|
2683
|
+
problem,
|
|
2684
|
+
allLabelPlacements,
|
|
2685
|
+
mergedLabelNetIdMap,
|
|
2686
|
+
paddingBuffer
|
|
2687
|
+
}) => {
|
|
2688
|
+
let changesMade = false;
|
|
2689
|
+
const obstacles = getObstacleRects(problem);
|
|
2690
|
+
const newTraces = traces.map((trace) => {
|
|
2691
|
+
const originalPath = trace.tracePath;
|
|
2692
|
+
const filteredLabels = allLabelPlacements.filter((label) => {
|
|
2693
|
+
const originalNetIds = mergedLabelNetIdMap[label.globalConnNetId];
|
|
2694
|
+
if (originalNetIds) {
|
|
2695
|
+
return !originalNetIds.has(trace.globalConnNetId);
|
|
2696
|
+
}
|
|
2697
|
+
return label.globalConnNetId !== trace.globalConnNetId;
|
|
2698
|
+
});
|
|
2699
|
+
const labelBounds = filteredLabels.map((nl) => ({
|
|
2700
|
+
minX: nl.center.x - nl.width / 2 - paddingBuffer,
|
|
2701
|
+
maxX: nl.center.x + nl.width / 2 + paddingBuffer,
|
|
2702
|
+
minY: nl.center.y - nl.height / 2 - paddingBuffer,
|
|
2703
|
+
maxY: nl.center.y + nl.height / 2 + paddingBuffer
|
|
2704
|
+
}));
|
|
2705
|
+
const newPath = minimizeTurns({
|
|
2706
|
+
path: originalPath,
|
|
2707
|
+
obstacles,
|
|
2708
|
+
labelBounds
|
|
2709
|
+
});
|
|
2710
|
+
if (newPath.length !== originalPath.length || newPath.some(
|
|
2711
|
+
(p, i) => p.x !== originalPath[i].x || p.y !== originalPath[i].y
|
|
2712
|
+
)) {
|
|
2713
|
+
changesMade = true;
|
|
2714
|
+
}
|
|
2715
|
+
return {
|
|
2716
|
+
...trace,
|
|
2717
|
+
tracePath: newPath
|
|
2718
|
+
};
|
|
2719
|
+
});
|
|
2720
|
+
if (changesMade) {
|
|
2721
|
+
return newTraces;
|
|
2722
|
+
} else {
|
|
2723
|
+
return null;
|
|
2724
|
+
}
|
|
2725
|
+
};
|
|
2726
|
+
|
|
2727
|
+
// lib/solvers/TraceLabelOverlapAvoidanceSolver/balanceLShapes.ts
|
|
2728
|
+
var balanceLShapes = ({
|
|
2729
|
+
traces,
|
|
2730
|
+
problem,
|
|
2731
|
+
allLabelPlacements
|
|
2732
|
+
}) => {
|
|
2733
|
+
const TOLERANCE = 1e-5;
|
|
2734
|
+
let changesMade = false;
|
|
2735
|
+
for (const trace of traces) {
|
|
2736
|
+
if (trace.tracePath.length === 4) {
|
|
2737
|
+
const [p0, p1, p2, p3] = trace.tracePath;
|
|
2738
|
+
const isHVHShape = p0.y === p1.y && p1.x === p2.x && p2.y === p3.y;
|
|
2739
|
+
const isVHVShape = p0.x === p1.x && p1.y === p2.y && p2.x === p3.x;
|
|
2740
|
+
const isCollinearHorizontal = p0.y === p1.y && p1.y === p2.y && p2.y === p3.y;
|
|
2741
|
+
const isCollinearVertical = p0.x === p1.x && p1.x === p2.x && p2.x === p3.x;
|
|
2742
|
+
const isCollinear = isCollinearHorizontal || isCollinearVertical;
|
|
2743
|
+
let isSameDirection = false;
|
|
2744
|
+
if (isHVHShape) {
|
|
2745
|
+
isSameDirection = Math.sign(p1.x - p0.x) === Math.sign(p3.x - p2.x);
|
|
2746
|
+
} else if (isVHVShape) {
|
|
2747
|
+
isSameDirection = Math.sign(p1.y - p0.y) === Math.sign(p3.y - p2.y);
|
|
2748
|
+
}
|
|
2749
|
+
const isValidZShape = (isHVHShape || isVHVShape) && !isCollinear && isSameDirection;
|
|
2750
|
+
if (!isValidZShape) {
|
|
2751
|
+
return null;
|
|
2752
|
+
}
|
|
2753
|
+
}
|
|
2754
|
+
}
|
|
2755
|
+
const obstacles = getObstacleRects(problem).map((obs) => ({
|
|
2756
|
+
...obs,
|
|
2757
|
+
minX: obs.minX + TOLERANCE,
|
|
2758
|
+
maxX: obs.maxX - TOLERANCE,
|
|
2759
|
+
minY: obs.minY + TOLERANCE,
|
|
2760
|
+
maxY: obs.maxY - TOLERANCE
|
|
2761
|
+
}));
|
|
2762
|
+
const segmentIntersectsAnyRect = (p1, p2, rects) => {
|
|
2763
|
+
for (const rect of rects) {
|
|
2764
|
+
if (segmentIntersectsRect(p1, p2, rect)) {
|
|
2765
|
+
return true;
|
|
2766
|
+
}
|
|
2767
|
+
}
|
|
2768
|
+
return false;
|
|
2769
|
+
};
|
|
2770
|
+
const getLabelBounds = (labels, traceNetId) => {
|
|
2771
|
+
const filteredLabels = labels.filter(
|
|
2772
|
+
(label) => label.globalConnNetId !== traceNetId
|
|
2773
|
+
);
|
|
2774
|
+
return filteredLabels.map((nl) => ({
|
|
2775
|
+
minX: nl.center.x - nl.width / 2 + TOLERANCE,
|
|
2776
|
+
maxX: nl.center.x + nl.width / 2 - TOLERANCE,
|
|
2777
|
+
minY: nl.center.y - nl.height / 2 + TOLERANCE,
|
|
2778
|
+
maxY: nl.center.y + nl.height / 2 - TOLERANCE
|
|
2779
|
+
}));
|
|
2780
|
+
};
|
|
2781
|
+
const newTraces = traces.map((trace) => {
|
|
2782
|
+
let newPath = [...trace.tracePath];
|
|
2783
|
+
if (newPath.length < 4) {
|
|
2784
|
+
return { ...trace };
|
|
2785
|
+
}
|
|
2786
|
+
const labelBounds = getLabelBounds(
|
|
2787
|
+
allLabelPlacements,
|
|
2788
|
+
trace.globalConnNetId
|
|
2789
|
+
);
|
|
2790
|
+
if (newPath.length === 4) {
|
|
2791
|
+
const [p0, p1, p2, p3] = newPath;
|
|
2792
|
+
let p1New, p2New;
|
|
2793
|
+
const isHVHShape = p0.y === p1.y && p1.x === p2.x && p2.y === p3.y;
|
|
2794
|
+
if (isHVHShape) {
|
|
2795
|
+
const idealX = (p0.x + p3.x) / 2;
|
|
2796
|
+
p1New = { x: idealX, y: p1.y };
|
|
2797
|
+
p2New = { x: idealX, y: p2.y };
|
|
2798
|
+
} else {
|
|
2799
|
+
const idealY = (p0.y + p3.y) / 2;
|
|
2800
|
+
p1New = { x: p1.x, y: idealY };
|
|
2801
|
+
p2New = { x: p2.x, y: idealY };
|
|
2802
|
+
}
|
|
2803
|
+
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);
|
|
2804
|
+
if (!collides) {
|
|
2805
|
+
newPath[1] = p1New;
|
|
2806
|
+
newPath[2] = p2New;
|
|
2807
|
+
changesMade = true;
|
|
2808
|
+
}
|
|
2809
|
+
return { ...trace, tracePath: simplifyPath(newPath) };
|
|
2810
|
+
}
|
|
2811
|
+
for (let i = 1; i < newPath.length - 4; i++) {
|
|
2812
|
+
const p1 = newPath[i];
|
|
2813
|
+
const p2 = newPath[i + 1];
|
|
2814
|
+
const p3 = newPath[i + 2];
|
|
2815
|
+
const p4 = newPath[i + 3];
|
|
2816
|
+
const isHVHZShape = p1.y === p2.y && p2.x === p3.x && p3.y === p4.y;
|
|
2817
|
+
const isVHVZShape = p1.x === p2.x && p2.y === p3.y && p3.x === p4.x;
|
|
2818
|
+
const isCollinearHorizontal = p1.y === p2.y && p2.y === p3.y && p3.y === p4.y;
|
|
2819
|
+
const isCollinearVertical = p1.x === p2.x && p2.x === p3.x && p3.x === p4.x;
|
|
2820
|
+
const isCollinear = isCollinearHorizontal || isCollinearVertical;
|
|
2821
|
+
let isSameDirection = false;
|
|
2822
|
+
if (isHVHZShape) {
|
|
2823
|
+
isSameDirection = Math.sign(p2.x - p1.x) === Math.sign(p4.x - p3.x);
|
|
2824
|
+
} else if (isVHVZShape) {
|
|
2825
|
+
isSameDirection = Math.sign(p2.y - p1.y) === Math.sign(p4.y - p3.y);
|
|
2826
|
+
}
|
|
2827
|
+
const isValidZShape = (isHVHZShape || isVHVZShape) && !isCollinear && isSameDirection;
|
|
2828
|
+
if (!isValidZShape) {
|
|
2829
|
+
continue;
|
|
2830
|
+
}
|
|
2831
|
+
let p2New, p3New;
|
|
2832
|
+
const len1Original = isHVHZShape ? Math.abs(p1.x - p2.x) : Math.abs(p1.y - p2.y);
|
|
2833
|
+
const len2Original = isHVHZShape ? Math.abs(p3.x - p4.x) : Math.abs(p3.y - p4.y);
|
|
2834
|
+
if (Math.abs(len1Original - len2Original) < 1e-3) {
|
|
2835
|
+
continue;
|
|
2836
|
+
}
|
|
2837
|
+
if (isHVHZShape) {
|
|
2838
|
+
const idealX = (p1.x + p4.x) / 2;
|
|
2839
|
+
p2New = { x: idealX, y: p2.y };
|
|
2840
|
+
p3New = { x: idealX, y: p3.y };
|
|
2841
|
+
} else {
|
|
2842
|
+
const idealY = (p1.y + p4.y) / 2;
|
|
2843
|
+
p2New = { x: p2.x, y: idealY };
|
|
2844
|
+
p3New = { x: p3.x, y: idealY };
|
|
2845
|
+
}
|
|
2846
|
+
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);
|
|
2847
|
+
if (!collides) {
|
|
2848
|
+
newPath[i + 1] = p2New;
|
|
2849
|
+
newPath[i + 2] = p3New;
|
|
2850
|
+
changesMade = true;
|
|
2851
|
+
i = 0;
|
|
2852
|
+
}
|
|
2853
|
+
}
|
|
2854
|
+
const finalSimplifiedPath = simplifyPath(newPath);
|
|
2855
|
+
return {
|
|
2856
|
+
...trace,
|
|
2857
|
+
tracePath: finalSimplifiedPath
|
|
2858
|
+
};
|
|
2859
|
+
});
|
|
2860
|
+
if (changesMade) {
|
|
2861
|
+
return newTraces;
|
|
2862
|
+
} else {
|
|
2863
|
+
return null;
|
|
2864
|
+
}
|
|
2865
|
+
};
|
|
2866
|
+
|
|
2867
|
+
// lib/solvers/TraceLabelOverlapAvoidanceSolver/TraceLabelOverlapAvoidanceSolver.ts
|
|
2868
|
+
var TraceLabelOverlapAvoidanceSolver = class extends BaseSolver {
|
|
2869
|
+
problem;
|
|
2870
|
+
traces;
|
|
2871
|
+
netTempLabelPlacements;
|
|
2872
|
+
netLabelPlacements;
|
|
2873
|
+
updatedTraces;
|
|
2874
|
+
mergedLabelNetIdMap;
|
|
2875
|
+
detourCountByLabel;
|
|
2876
|
+
PADDING_BUFFER = 0.1;
|
|
2877
|
+
constructor(solverInput) {
|
|
2878
|
+
super();
|
|
2879
|
+
this.problem = solverInput.inputProblem;
|
|
2880
|
+
this.traces = solverInput.traces;
|
|
2881
|
+
this.updatedTraces = [...solverInput.traces];
|
|
2882
|
+
this.mergedLabelNetIdMap = {};
|
|
2883
|
+
this.detourCountByLabel = {};
|
|
2884
|
+
const originalLabels = solverInput.netLabelPlacements;
|
|
2885
|
+
this.netLabelPlacements = originalLabels;
|
|
2886
|
+
if (!originalLabels || originalLabels.length === 0) {
|
|
2887
|
+
this.netTempLabelPlacements = [];
|
|
2888
|
+
return;
|
|
2889
|
+
}
|
|
2890
|
+
const labelGroups = {};
|
|
2891
|
+
for (const p of originalLabels) {
|
|
2892
|
+
if (p.pinIds.length === 0) continue;
|
|
2893
|
+
const chipId = p.pinIds[0].split(".")[0];
|
|
2894
|
+
if (!chipId) continue;
|
|
2895
|
+
const key = `${chipId}-${p.orientation}`;
|
|
2896
|
+
if (!(key in labelGroups)) {
|
|
2897
|
+
labelGroups[key] = [];
|
|
2898
|
+
}
|
|
2899
|
+
labelGroups[key].push(p);
|
|
2900
|
+
}
|
|
2901
|
+
const finalPlacements = [];
|
|
2902
|
+
for (const [key, group] of Object.entries(labelGroups)) {
|
|
2903
|
+
if (group.length <= 1) {
|
|
2904
|
+
finalPlacements.push(...group);
|
|
2905
|
+
continue;
|
|
2906
|
+
}
|
|
2907
|
+
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
2908
|
+
for (const p of group) {
|
|
2909
|
+
const bounds = getRectBounds(p.center, p.width, p.height);
|
|
2910
|
+
minX = Math.min(minX, bounds.minX);
|
|
2911
|
+
minY = Math.min(minY, bounds.minY);
|
|
2912
|
+
maxX = Math.max(maxX, bounds.maxX);
|
|
2913
|
+
maxY = Math.max(maxY, bounds.maxY);
|
|
2914
|
+
}
|
|
2915
|
+
const newWidth = maxX - minX;
|
|
2916
|
+
const newHeight = maxY - minY;
|
|
2917
|
+
const template = group[0];
|
|
2918
|
+
const syntheticId = `merged-group-${key}`;
|
|
2919
|
+
const originalNetIds = new Set(group.map((p) => p.globalConnNetId));
|
|
2920
|
+
this.mergedLabelNetIdMap[syntheticId] = originalNetIds;
|
|
2921
|
+
finalPlacements.push({
|
|
2922
|
+
...template,
|
|
2923
|
+
globalConnNetId: syntheticId,
|
|
2924
|
+
width: newWidth,
|
|
2925
|
+
height: newHeight,
|
|
2926
|
+
center: { x: minX + newWidth / 2, y: minY + newHeight / 2 },
|
|
2927
|
+
pinIds: [...new Set(group.flatMap((p) => p.pinIds))],
|
|
2928
|
+
mspConnectionPairIds: [
|
|
2929
|
+
...new Set(group.flatMap((p) => p.mspConnectionPairIds))
|
|
2930
|
+
]
|
|
2931
|
+
});
|
|
2932
|
+
}
|
|
2933
|
+
this.netTempLabelPlacements = finalPlacements;
|
|
2934
|
+
}
|
|
2935
|
+
_step() {
|
|
2936
|
+
if (!this.traces || this.traces.length === 0 || !this.netTempLabelPlacements || this.netTempLabelPlacements.length === 0) {
|
|
2937
|
+
this.solved = true;
|
|
2938
|
+
return;
|
|
2939
|
+
}
|
|
2940
|
+
this.detourCountByLabel = {};
|
|
2941
|
+
const overlaps = detectTraceLabelOverlap(
|
|
2942
|
+
this.traces,
|
|
2943
|
+
this.netTempLabelPlacements
|
|
2944
|
+
);
|
|
2945
|
+
if (overlaps.length === 0) {
|
|
2946
|
+
this.solved = true;
|
|
2947
|
+
return;
|
|
2948
|
+
}
|
|
2949
|
+
const unfriendlyOverlaps = overlaps.filter((o) => {
|
|
2950
|
+
const originalNetIds = this.mergedLabelNetIdMap[o.label.globalConnNetId];
|
|
2951
|
+
if (originalNetIds) {
|
|
2952
|
+
return !originalNetIds.has(o.trace.globalConnNetId);
|
|
2953
|
+
}
|
|
2954
|
+
return o.trace.globalConnNetId !== o.label.globalConnNetId;
|
|
2955
|
+
});
|
|
2956
|
+
if (unfriendlyOverlaps.length === 0) {
|
|
2957
|
+
this.solved = true;
|
|
2958
|
+
return;
|
|
2959
|
+
}
|
|
2960
|
+
const updatedTracesMap = {};
|
|
2961
|
+
for (const trace of this.traces) {
|
|
2962
|
+
updatedTracesMap[trace.mspPairId] = trace;
|
|
2963
|
+
}
|
|
2964
|
+
const processedTraceIds = /* @__PURE__ */ new Set();
|
|
2965
|
+
for (const overlap of unfriendlyOverlaps) {
|
|
2966
|
+
if (processedTraceIds.has(overlap.trace.mspPairId)) {
|
|
2967
|
+
continue;
|
|
2968
|
+
}
|
|
2969
|
+
const currentTraceState = updatedTracesMap[overlap.trace.mspPairId];
|
|
2970
|
+
const labelId = overlap.label.globalConnNetId;
|
|
2971
|
+
const detourCount = this.detourCountByLabel[labelId] || 0;
|
|
2972
|
+
const newTrace = rerouteCollidingTrace({
|
|
2973
|
+
trace: currentTraceState,
|
|
2974
|
+
label: overlap.label,
|
|
2975
|
+
problem: this.problem,
|
|
2976
|
+
paddingBuffer: this.PADDING_BUFFER,
|
|
2977
|
+
detourCount
|
|
2978
|
+
});
|
|
2979
|
+
if (newTrace.tracePath !== currentTraceState.tracePath) {
|
|
2980
|
+
this.detourCountByLabel[labelId] = detourCount + 1;
|
|
2981
|
+
}
|
|
2982
|
+
updatedTracesMap[currentTraceState.mspPairId] = newTrace;
|
|
2983
|
+
processedTraceIds.add(currentTraceState.mspPairId);
|
|
2984
|
+
}
|
|
2985
|
+
this.updatedTraces = Object.values(updatedTracesMap);
|
|
2986
|
+
const minimizedTraces = minimizeTurnsWithFilteredLabels({
|
|
2987
|
+
traces: this.updatedTraces,
|
|
2988
|
+
problem: this.problem,
|
|
2989
|
+
allLabelPlacements: this.netTempLabelPlacements,
|
|
2990
|
+
// Use temp labels which include merged ones
|
|
2991
|
+
mergedLabelNetIdMap: this.mergedLabelNetIdMap,
|
|
2992
|
+
paddingBuffer: this.PADDING_BUFFER
|
|
2993
|
+
});
|
|
2994
|
+
if (minimizedTraces) {
|
|
2995
|
+
this.updatedTraces = minimizedTraces;
|
|
2996
|
+
}
|
|
2997
|
+
const balancedTraces = balanceLShapes({
|
|
2998
|
+
traces: this.updatedTraces,
|
|
2999
|
+
problem: this.problem,
|
|
3000
|
+
allLabelPlacements: this.netLabelPlacements
|
|
3001
|
+
});
|
|
3002
|
+
if (balancedTraces) {
|
|
3003
|
+
this.updatedTraces = balancedTraces;
|
|
3004
|
+
}
|
|
3005
|
+
const finalLabelPlacementSolver = new NetLabelPlacementSolver({
|
|
3006
|
+
inputProblem: this.problem,
|
|
3007
|
+
inputTraceMap: Object.fromEntries(
|
|
3008
|
+
this.updatedTraces.map((trace) => [trace.mspPairId, trace])
|
|
3009
|
+
)
|
|
3010
|
+
});
|
|
3011
|
+
finalLabelPlacementSolver.solve();
|
|
3012
|
+
this.netLabelPlacements = finalLabelPlacementSolver.netLabelPlacements;
|
|
3013
|
+
this.solved = true;
|
|
3014
|
+
}
|
|
3015
|
+
getOutput() {
|
|
3016
|
+
return {
|
|
3017
|
+
traces: this.updatedTraces,
|
|
3018
|
+
netLabelPlacements: this.netLabelPlacements
|
|
3019
|
+
};
|
|
3020
|
+
}
|
|
3021
|
+
visualize() {
|
|
3022
|
+
const graphics = visualizeInputProblem(this.problem);
|
|
3023
|
+
if (!graphics.lines) graphics.lines = [];
|
|
3024
|
+
if (!graphics.circles) graphics.circles = [];
|
|
3025
|
+
if (!graphics.texts) graphics.texts = [];
|
|
3026
|
+
if (!graphics.rects) graphics.rects = [];
|
|
3027
|
+
for (const trace of this.updatedTraces) {
|
|
3028
|
+
graphics.lines.push({
|
|
3029
|
+
points: trace.tracePath,
|
|
3030
|
+
strokeColor: "purple"
|
|
3031
|
+
});
|
|
3032
|
+
}
|
|
3033
|
+
for (const p of this.netLabelPlacements) {
|
|
3034
|
+
graphics.rects.push({
|
|
3035
|
+
center: p.center,
|
|
3036
|
+
width: p.width,
|
|
3037
|
+
height: p.height,
|
|
3038
|
+
fill: getColorFromString(p.globalConnNetId, 0.35)
|
|
3039
|
+
});
|
|
3040
|
+
graphics.points.push({
|
|
3041
|
+
x: p.anchorPoint.x,
|
|
3042
|
+
y: p.anchorPoint.y,
|
|
3043
|
+
color: getColorFromString(p.globalConnNetId, 0.9)
|
|
3044
|
+
});
|
|
3045
|
+
}
|
|
3046
|
+
return graphics;
|
|
3047
|
+
}
|
|
3048
|
+
};
|
|
3049
|
+
|
|
2173
3050
|
// lib/solvers/SchematicTracePipelineSolver/correctPinsInsideChip.ts
|
|
2174
3051
|
var correctPinsInsideChips = (problem) => {
|
|
2175
3052
|
for (const chip of problem.chips) {
|
|
@@ -2236,6 +3113,7 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
|
|
|
2236
3113
|
schematicTraceLinesSolver;
|
|
2237
3114
|
traceOverlapShiftSolver;
|
|
2238
3115
|
netLabelPlacementSolver;
|
|
3116
|
+
traceLabelOverlapAvoidanceSolver;
|
|
2239
3117
|
startTimeOfPhase;
|
|
2240
3118
|
endTimeOfPhase;
|
|
2241
3119
|
timeSpentOnPhase;
|
|
@@ -2314,6 +3192,35 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
|
|
|
2314
3192
|
onSolved: (_solver) => {
|
|
2315
3193
|
}
|
|
2316
3194
|
}
|
|
3195
|
+
),
|
|
3196
|
+
definePipelineStep(
|
|
3197
|
+
"traceLabelOverlapAvoidanceSolver",
|
|
3198
|
+
TraceLabelOverlapAvoidanceSolver,
|
|
3199
|
+
(instance) => {
|
|
3200
|
+
const traceMap = instance.traceOverlapShiftSolver?.correctedTraceMap ?? Object.fromEntries(
|
|
3201
|
+
instance.schematicTraceLinesSolver.solvedTracePaths.map((p) => [
|
|
3202
|
+
p.mspPairId,
|
|
3203
|
+
p
|
|
3204
|
+
])
|
|
3205
|
+
);
|
|
3206
|
+
const traces = Object.values(traceMap);
|
|
3207
|
+
const netLabelPlacements = instance.netLabelPlacementSolver.netLabelPlacements;
|
|
3208
|
+
return [
|
|
3209
|
+
{
|
|
3210
|
+
inputProblem: instance.inputProblem,
|
|
3211
|
+
traces,
|
|
3212
|
+
netLabelPlacements
|
|
3213
|
+
}
|
|
3214
|
+
];
|
|
3215
|
+
},
|
|
3216
|
+
{
|
|
3217
|
+
onSolved: (instance) => {
|
|
3218
|
+
if (instance.traceLabelOverlapAvoidanceSolver && instance.netLabelPlacementSolver) {
|
|
3219
|
+
const { netLabelPlacements } = instance.traceLabelOverlapAvoidanceSolver.getOutput();
|
|
3220
|
+
instance.netLabelPlacementSolver.netLabelPlacements = netLabelPlacements;
|
|
3221
|
+
}
|
|
3222
|
+
}
|
|
3223
|
+
}
|
|
2317
3224
|
)
|
|
2318
3225
|
];
|
|
2319
3226
|
constructor(inputProblem) {
|