@tscircuit/schematic-trace-solver 0.0.41 → 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 +118 -8
- package/dist/index.js +1067 -748
- package/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts +48 -16
- package/lib/solvers/TraceCleanupSolver/TraceCleanupSolver.ts +133 -0
- package/lib/solvers/TraceCleanupSolver/balanceZShapes.ts +200 -0
- package/lib/solvers/{TraceLabelOverlapAvoidanceSolver → TraceCleanupSolver}/hasCollisions.ts +1 -1
- package/lib/solvers/{TraceLabelOverlapAvoidanceSolver → TraceCleanupSolver}/hasCollisionsWithLabels.ts +1 -1
- package/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts +76 -0
- package/lib/solvers/{TraceLabelOverlapAvoidanceSolver → TraceCleanupSolver}/simplifyPath.ts +1 -1
- package/lib/solvers/{TraceLabelOverlapAvoidanceSolver → TraceCleanupSolver}/turnMinimization.ts +0 -2
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/TraceLabelOverlapAvoidanceSolver.ts +74 -192
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/rerouteCollidingTrace.ts +13 -24
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/LabelMergingSolver/LabelMergingSolver.ts +205 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/OverlapAvoidanceStepSolver/OverlapAvoidanceStepSolver.ts +144 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/SingleOverlapSolver/SingleOverlapSolver.ts +117 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/tryFourPointDetour.ts +104 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/trySnipAndReconnect.ts +24 -154
- package/package.json +1 -1
- package/site/examples/example26.page.tsx +4 -0
- package/tests/assets/1.input.json +379 -0
- package/tests/assets/2.input.json +330 -0
- package/tests/assets/3.input.json +388 -0
- package/tests/assets/MergedNetLabelObstacles.test.input.json +154 -0
- package/tests/assets/OverlapAvoidanceStepSolver.test.input.json +682 -0
- package/tests/assets/SingleOverlapSolver.test.input.json +170 -0
- package/tests/assets/TraceCleanupSolver.test.input.json +216 -0
- package/tests/assets/TraceLabelOverlapAvoidanceSolver.test.input.json +424 -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__/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/TraceCleanupSolver/TraceCleanupSolver.test.ts +19 -0
- package/tests/solvers/TraceCleanupSolver/__snapshots__/TraceCleanupSolver.snap.svg +119 -0
- package/tests/solvers/TraceLabelOverlapAvoidanceSolver/TraceLabelOverlapAvoidanceSolver.test.ts +18 -0
- package/tests/solvers/TraceLabelOverlapAvoidanceSolver/__snapshots__/TraceLabelOverlapAvoidanceSolver.snap.svg +243 -0
- package/tests/solvers/TraceLabelOverlapAvoidanceSolver/renderComparisonView/__snapshots__/renderComparisonView01.snap.svg +223 -0
- package/tests/solvers/TraceLabelOverlapAvoidanceSolver/renderComparisonView/__snapshots__/renderComparisonView02.snap.svg +205 -0
- package/tests/solvers/TraceLabelOverlapAvoidanceSolver/renderComparisonView/__snapshots__/renderComparisonView03.snap.svg +184 -0
- package/tests/solvers/TraceLabelOverlapAvoidanceSolver/renderComparisonView/renderComparisonView01.test.ts +29 -0
- package/tests/solvers/TraceLabelOverlapAvoidanceSolver/renderComparisonView/renderComparisonView02.test.ts +27 -0
- package/tests/solvers/TraceLabelOverlapAvoidanceSolver/renderComparisonView/renderComparisonView03.test.ts +39 -0
- package/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/MergedNetLabelObstacles.test.ts +14 -0
- package/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/OverlapAvoidanceStepSolver.test.ts +20 -0
- package/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/SingleOverlapSolver.test.ts +10 -0
- package/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/__snapshots__/MergedNetLabelObstacles.snap.svg +140 -0
- package/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/__snapshots__/OverlapAvoidanceStepSolver.snap.svg +216 -0
- package/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/__snapshots__/SingleOverlapSolver.snap.svg +125 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/balanceLShapes.ts +0 -207
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/minimizeTurnsWithFilteredLabels.ts +0 -66
- /package/lib/solvers/{TraceLabelOverlapAvoidanceSolver → TraceCleanupSolver}/countTurns.ts +0 -0
- /package/lib/solvers/{TraceLabelOverlapAvoidanceSolver → TraceCleanupSolver}/tryConnectPoints.ts +0 -0
package/dist/index.js
CHANGED
|
@@ -2196,8 +2196,161 @@ var NetLabelPlacementSolver = class extends BaseSolver {
|
|
|
2196
2196
|
}
|
|
2197
2197
|
};
|
|
2198
2198
|
|
|
2199
|
-
// lib/solvers/
|
|
2200
|
-
|
|
2199
|
+
// lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/LabelMergingSolver/LabelMergingSolver.ts
|
|
2200
|
+
var MergedNetLabelObstacleSolver = class extends BaseSolver {
|
|
2201
|
+
input;
|
|
2202
|
+
output;
|
|
2203
|
+
inputProblem;
|
|
2204
|
+
traces;
|
|
2205
|
+
constructor(solverInput) {
|
|
2206
|
+
super();
|
|
2207
|
+
this.input = solverInput;
|
|
2208
|
+
this.inputProblem = solverInput.inputProblem;
|
|
2209
|
+
this.traces = solverInput.traces;
|
|
2210
|
+
this.output = {
|
|
2211
|
+
netLabelPlacements: solverInput.netLabelPlacements,
|
|
2212
|
+
mergedLabelNetIdMap: {}
|
|
2213
|
+
};
|
|
2214
|
+
}
|
|
2215
|
+
_step() {
|
|
2216
|
+
const originalLabels = this.input.netLabelPlacements;
|
|
2217
|
+
const mergedLabelNetIdMap = {};
|
|
2218
|
+
if (!originalLabels || originalLabels.length === 0) {
|
|
2219
|
+
this.output = {
|
|
2220
|
+
netLabelPlacements: [],
|
|
2221
|
+
mergedLabelNetIdMap: {}
|
|
2222
|
+
};
|
|
2223
|
+
this.solved = true;
|
|
2224
|
+
return;
|
|
2225
|
+
}
|
|
2226
|
+
const labelGroups = {};
|
|
2227
|
+
for (const p of originalLabels) {
|
|
2228
|
+
if (p.pinIds.length === 0) continue;
|
|
2229
|
+
const chipId = p.pinIds[0].split(".")[0];
|
|
2230
|
+
if (!chipId) continue;
|
|
2231
|
+
const key = `${chipId}-${p.orientation}`;
|
|
2232
|
+
if (!(key in labelGroups)) {
|
|
2233
|
+
labelGroups[key] = [];
|
|
2234
|
+
}
|
|
2235
|
+
labelGroups[key].push(p);
|
|
2236
|
+
}
|
|
2237
|
+
const finalPlacements = [];
|
|
2238
|
+
for (const [key, group] of Object.entries(labelGroups)) {
|
|
2239
|
+
if (group.length <= 1) {
|
|
2240
|
+
finalPlacements.push(...group);
|
|
2241
|
+
continue;
|
|
2242
|
+
}
|
|
2243
|
+
let minX = Infinity;
|
|
2244
|
+
let minY = Infinity;
|
|
2245
|
+
let maxX = -Infinity;
|
|
2246
|
+
let maxY = -Infinity;
|
|
2247
|
+
for (const p of group) {
|
|
2248
|
+
const bounds = getRectBounds(p.center, p.width, p.height);
|
|
2249
|
+
minX = Math.min(minX, bounds.minX);
|
|
2250
|
+
minY = Math.min(minY, bounds.minY);
|
|
2251
|
+
maxX = Math.max(maxX, bounds.maxX);
|
|
2252
|
+
maxY = Math.max(maxY, bounds.maxY);
|
|
2253
|
+
}
|
|
2254
|
+
const newWidth = maxX - minX;
|
|
2255
|
+
const newHeight = maxY - minY;
|
|
2256
|
+
const template = group[0];
|
|
2257
|
+
const syntheticId = `merged-group-${key}`;
|
|
2258
|
+
const originalNetIds = new Set(group.map((p) => p.globalConnNetId));
|
|
2259
|
+
mergedLabelNetIdMap[syntheticId] = originalNetIds;
|
|
2260
|
+
finalPlacements.push({
|
|
2261
|
+
...template,
|
|
2262
|
+
globalConnNetId: syntheticId,
|
|
2263
|
+
width: newWidth,
|
|
2264
|
+
height: newHeight,
|
|
2265
|
+
center: { x: minX + newWidth / 2, y: minY + newHeight / 2 },
|
|
2266
|
+
pinIds: [...new Set(group.flatMap((p) => p.pinIds))],
|
|
2267
|
+
mspConnectionPairIds: [
|
|
2268
|
+
...new Set(group.flatMap((p) => p.mspConnectionPairIds))
|
|
2269
|
+
]
|
|
2270
|
+
});
|
|
2271
|
+
}
|
|
2272
|
+
this.output = {
|
|
2273
|
+
netLabelPlacements: finalPlacements,
|
|
2274
|
+
mergedLabelNetIdMap
|
|
2275
|
+
};
|
|
2276
|
+
this.solved = true;
|
|
2277
|
+
}
|
|
2278
|
+
getOutput() {
|
|
2279
|
+
return this.output;
|
|
2280
|
+
}
|
|
2281
|
+
visualize() {
|
|
2282
|
+
const graphics = visualizeInputProblem(this.inputProblem, {
|
|
2283
|
+
chipAlpha: 0.1,
|
|
2284
|
+
connectionAlpha: 0.1
|
|
2285
|
+
});
|
|
2286
|
+
if (!graphics.rects) graphics.rects = [];
|
|
2287
|
+
if (!graphics.lines) graphics.lines = [];
|
|
2288
|
+
if (!graphics.points) graphics.points = [];
|
|
2289
|
+
if (!graphics.texts) graphics.texts = [];
|
|
2290
|
+
const originalLabelsById = /* @__PURE__ */ new Map();
|
|
2291
|
+
for (const label of this.input.netLabelPlacements) {
|
|
2292
|
+
originalLabelsById.set(label.globalConnNetId, label);
|
|
2293
|
+
}
|
|
2294
|
+
for (const trace of this.traces) {
|
|
2295
|
+
const line = {
|
|
2296
|
+
points: trace.tracePath.map((p) => ({ x: p.x, y: p.y })),
|
|
2297
|
+
strokeColor: "blue"
|
|
2298
|
+
};
|
|
2299
|
+
graphics.lines.push(line);
|
|
2300
|
+
}
|
|
2301
|
+
for (const finalLabel of this.output.netLabelPlacements) {
|
|
2302
|
+
const isMerged = finalLabel.globalConnNetId.startsWith("merged-group-");
|
|
2303
|
+
const color = getColorFromString(finalLabel.globalConnNetId);
|
|
2304
|
+
if (isMerged) {
|
|
2305
|
+
graphics.rects.push({
|
|
2306
|
+
center: finalLabel.center,
|
|
2307
|
+
width: finalLabel.width,
|
|
2308
|
+
height: finalLabel.height,
|
|
2309
|
+
fill: color.replace(/, 1\)/, ", 0.2)"),
|
|
2310
|
+
// semi-transparent
|
|
2311
|
+
stroke: color,
|
|
2312
|
+
label: finalLabel.globalConnNetId
|
|
2313
|
+
});
|
|
2314
|
+
const originalNetIds = this.output.mergedLabelNetIdMap[finalLabel.globalConnNetId];
|
|
2315
|
+
if (originalNetIds) {
|
|
2316
|
+
for (const originalNetId of originalNetIds) {
|
|
2317
|
+
const originalLabel = originalLabelsById.get(originalNetId);
|
|
2318
|
+
if (originalLabel) {
|
|
2319
|
+
const bounds = getRectBounds(
|
|
2320
|
+
originalLabel.center,
|
|
2321
|
+
originalLabel.width,
|
|
2322
|
+
originalLabel.height
|
|
2323
|
+
);
|
|
2324
|
+
const p1 = { x: bounds.minX, y: bounds.minY };
|
|
2325
|
+
const p2 = { x: bounds.maxX, y: bounds.minY };
|
|
2326
|
+
const p3 = { x: bounds.maxX, y: bounds.maxY };
|
|
2327
|
+
const p4 = { x: bounds.minX, y: bounds.maxY };
|
|
2328
|
+
graphics.lines.push({
|
|
2329
|
+
points: [p1, p2, p3, p4, p1],
|
|
2330
|
+
strokeColor: color,
|
|
2331
|
+
strokeDash: "4 4"
|
|
2332
|
+
});
|
|
2333
|
+
graphics.lines.push({
|
|
2334
|
+
points: [originalLabel.center, finalLabel.center],
|
|
2335
|
+
strokeColor: color,
|
|
2336
|
+
strokeDash: "2 2"
|
|
2337
|
+
});
|
|
2338
|
+
}
|
|
2339
|
+
}
|
|
2340
|
+
}
|
|
2341
|
+
} else {
|
|
2342
|
+
graphics.rects.push({
|
|
2343
|
+
center: finalLabel.center,
|
|
2344
|
+
width: finalLabel.width,
|
|
2345
|
+
height: finalLabel.height,
|
|
2346
|
+
stroke: color,
|
|
2347
|
+
label: finalLabel.globalConnNetId
|
|
2348
|
+
});
|
|
2349
|
+
}
|
|
2350
|
+
}
|
|
2351
|
+
return graphics;
|
|
2352
|
+
}
|
|
2353
|
+
};
|
|
2201
2354
|
|
|
2202
2355
|
// lib/solvers/TraceLabelOverlapAvoidanceSolver/detectTraceLabelOverlap.ts
|
|
2203
2356
|
var detectTraceLabelOverlap = (traces, netLabels) => {
|
|
@@ -2237,114 +2390,70 @@ var findTraceViolationZone = (path, labelBounds) => {
|
|
|
2237
2390
|
return { firstInsideIndex, lastInsideIndex };
|
|
2238
2391
|
};
|
|
2239
2392
|
|
|
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
2393
|
// lib/solvers/TraceLabelOverlapAvoidanceSolver/trySnipAndReconnect.ts
|
|
2270
|
-
var
|
|
2394
|
+
var generateSnipAndReconnectCandidates = ({
|
|
2271
2395
|
initialTrace,
|
|
2272
2396
|
firstInsideIndex,
|
|
2273
2397
|
lastInsideIndex,
|
|
2274
2398
|
labelBounds,
|
|
2275
|
-
|
|
2399
|
+
paddingBuffer,
|
|
2400
|
+
detourCount
|
|
2276
2401
|
}) => {
|
|
2277
2402
|
if (firstInsideIndex <= 0 || lastInsideIndex >= initialTrace.tracePath.length - 1) {
|
|
2278
|
-
return
|
|
2403
|
+
return [];
|
|
2279
2404
|
}
|
|
2280
2405
|
const entryPoint = initialTrace.tracePath[firstInsideIndex - 1];
|
|
2281
2406
|
const exitPoint = initialTrace.tracePath[lastInsideIndex + 1];
|
|
2282
2407
|
const pathToEntry = initialTrace.tracePath.slice(0, firstInsideIndex);
|
|
2283
2408
|
const pathFromExit = initialTrace.tracePath.slice(lastInsideIndex + 1);
|
|
2284
|
-
const
|
|
2409
|
+
const allCandidateDetours = [];
|
|
2285
2410
|
if (entryPoint.x !== exitPoint.x && entryPoint.y !== exitPoint.y) {
|
|
2286
|
-
|
|
2287
|
-
|
|
2411
|
+
allCandidateDetours.push([{ x: exitPoint.x, y: entryPoint.y }]);
|
|
2412
|
+
allCandidateDetours.push([{ x: entryPoint.x, y: exitPoint.y }]);
|
|
2288
2413
|
} else if (entryPoint.x === exitPoint.x || entryPoint.y === exitPoint.y) {
|
|
2289
|
-
|
|
2290
|
-
const simplified = simplifyPath(newPath);
|
|
2291
|
-
if (!isPathCollidingWithObstacles(simplified, obstacles)) {
|
|
2292
|
-
return { ...initialTrace, tracePath: simplified };
|
|
2293
|
-
}
|
|
2414
|
+
allCandidateDetours.push([]);
|
|
2294
2415
|
}
|
|
2295
|
-
|
|
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;
|
|
2416
|
+
const buffer = paddingBuffer + detourCount * paddingBuffer;
|
|
2305
2417
|
const leftX = labelBounds.minX - buffer;
|
|
2306
2418
|
const rightX = labelBounds.maxX + buffer;
|
|
2307
2419
|
const topY = labelBounds.maxY + buffer;
|
|
2308
2420
|
const bottomY = labelBounds.minY - buffer;
|
|
2309
2421
|
if ((entryPoint.x <= labelBounds.minX || exitPoint.x <= labelBounds.minX) && entryPoint.x < labelBounds.maxX && exitPoint.x < labelBounds.maxX) {
|
|
2310
|
-
|
|
2422
|
+
allCandidateDetours.push([
|
|
2311
2423
|
{ x: leftX, y: entryPoint.y },
|
|
2312
2424
|
{ x: leftX, y: exitPoint.y }
|
|
2313
2425
|
]);
|
|
2314
2426
|
}
|
|
2315
2427
|
if ((entryPoint.x >= labelBounds.maxX || exitPoint.x >= labelBounds.maxX) && entryPoint.x > labelBounds.minX && exitPoint.x > labelBounds.minX) {
|
|
2316
|
-
|
|
2428
|
+
allCandidateDetours.push([
|
|
2317
2429
|
{ x: rightX, y: entryPoint.y },
|
|
2318
2430
|
{ x: rightX, y: exitPoint.y }
|
|
2319
2431
|
]);
|
|
2320
2432
|
}
|
|
2321
2433
|
if ((entryPoint.y >= labelBounds.maxY || exitPoint.y >= labelBounds.maxY) && entryPoint.y > labelBounds.minY && exitPoint.y > labelBounds.minY) {
|
|
2322
|
-
|
|
2434
|
+
allCandidateDetours.push([
|
|
2323
2435
|
{ x: entryPoint.x, y: topY },
|
|
2324
2436
|
{ x: exitPoint.x, y: topY }
|
|
2325
2437
|
]);
|
|
2326
2438
|
}
|
|
2327
2439
|
if ((entryPoint.y <= labelBounds.minY || exitPoint.y <= labelBounds.minY) && entryPoint.y < labelBounds.maxY && exitPoint.y < labelBounds.maxY) {
|
|
2328
|
-
|
|
2440
|
+
allCandidateDetours.push([
|
|
2329
2441
|
{ x: entryPoint.x, y: bottomY },
|
|
2330
2442
|
{ x: exitPoint.x, y: bottomY }
|
|
2331
2443
|
]);
|
|
2332
2444
|
}
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
return { ...initialTrace, tracePath: simplified };
|
|
2339
|
-
}
|
|
2340
|
-
}
|
|
2341
|
-
return null;
|
|
2445
|
+
return allCandidateDetours.map((detour) => [
|
|
2446
|
+
...pathToEntry,
|
|
2447
|
+
...detour,
|
|
2448
|
+
...pathFromExit
|
|
2449
|
+
]);
|
|
2342
2450
|
};
|
|
2343
|
-
|
|
2451
|
+
|
|
2452
|
+
// lib/solvers/TraceLabelOverlapAvoidanceSolver/tryFourPointDetour.ts
|
|
2453
|
+
var generateFourPointDetourCandidates = ({
|
|
2344
2454
|
initialTrace,
|
|
2345
2455
|
label,
|
|
2346
2456
|
labelBounds,
|
|
2347
|
-
obstacles,
|
|
2348
2457
|
paddingBuffer,
|
|
2349
2458
|
detourCount
|
|
2350
2459
|
}) => {
|
|
@@ -2359,10 +2468,10 @@ var tryFourPointDetour = ({
|
|
|
2359
2468
|
break;
|
|
2360
2469
|
}
|
|
2361
2470
|
}
|
|
2362
|
-
if (collidingSegIndex === -1) return
|
|
2471
|
+
if (collidingSegIndex === -1) return [];
|
|
2363
2472
|
const pA = initialTrace.tracePath[collidingSegIndex];
|
|
2364
2473
|
const pB = initialTrace.tracePath[collidingSegIndex + 1];
|
|
2365
|
-
if (!pA || !pB) return
|
|
2474
|
+
if (!pA || !pB) return [];
|
|
2366
2475
|
const candidateDetours = [];
|
|
2367
2476
|
const paddedLabelBounds = getRectBounds(
|
|
2368
2477
|
label.center,
|
|
@@ -2411,33 +2520,53 @@ var tryFourPointDetour = ({
|
|
|
2411
2520
|
);
|
|
2412
2521
|
}
|
|
2413
2522
|
}
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2523
|
+
return candidateDetours.map((detourPoints) => [
|
|
2524
|
+
...initialTrace.tracePath.slice(0, collidingSegIndex + 1),
|
|
2525
|
+
...detourPoints,
|
|
2526
|
+
...initialTrace.tracePath.slice(collidingSegIndex + 1)
|
|
2527
|
+
]);
|
|
2528
|
+
};
|
|
2529
|
+
|
|
2530
|
+
// lib/solvers/TraceCleanupSolver/simplifyPath.ts
|
|
2531
|
+
var simplifyPath = (path) => {
|
|
2532
|
+
if (path.length < 3) return path;
|
|
2533
|
+
const newPath = [path[0]];
|
|
2534
|
+
for (let i = 1; i < path.length - 1; i++) {
|
|
2535
|
+
const p1 = newPath[newPath.length - 1];
|
|
2536
|
+
const p2 = path[i];
|
|
2537
|
+
const p3 = path[i + 1];
|
|
2538
|
+
if (isVertical(p1, p2) && isVertical(p2, p3) || isHorizontal(p1, p2) && isHorizontal(p2, p3)) {
|
|
2539
|
+
continue;
|
|
2423
2540
|
}
|
|
2541
|
+
newPath.push(p2);
|
|
2424
2542
|
}
|
|
2425
|
-
|
|
2543
|
+
newPath.push(path[path.length - 1]);
|
|
2544
|
+
if (newPath.length < 3) return newPath;
|
|
2545
|
+
const finalPath = [newPath[0]];
|
|
2546
|
+
for (let i = 1; i < newPath.length - 1; i++) {
|
|
2547
|
+
const p1 = finalPath[finalPath.length - 1];
|
|
2548
|
+
const p2 = newPath[i];
|
|
2549
|
+
const p3 = newPath[i + 1];
|
|
2550
|
+
if (isVertical(p1, p2) && isVertical(p2, p3) || isHorizontal(p1, p2) && isHorizontal(p2, p3)) {
|
|
2551
|
+
continue;
|
|
2552
|
+
}
|
|
2553
|
+
finalPath.push(p2);
|
|
2554
|
+
}
|
|
2555
|
+
finalPath.push(newPath[newPath.length - 1]);
|
|
2556
|
+
return finalPath;
|
|
2426
2557
|
};
|
|
2427
2558
|
|
|
2428
2559
|
// lib/solvers/TraceLabelOverlapAvoidanceSolver/rerouteCollidingTrace.ts
|
|
2429
|
-
var
|
|
2560
|
+
var generateRerouteCandidates = ({
|
|
2430
2561
|
trace,
|
|
2431
2562
|
label,
|
|
2432
|
-
problem,
|
|
2433
2563
|
paddingBuffer,
|
|
2434
2564
|
detourCount
|
|
2435
2565
|
}) => {
|
|
2436
2566
|
const initialTrace = { ...trace, tracePath: simplifyPath(trace.tracePath) };
|
|
2437
2567
|
if (trace.globalConnNetId === label.globalConnNetId) {
|
|
2438
|
-
return initialTrace;
|
|
2568
|
+
return [initialTrace.tracePath];
|
|
2439
2569
|
}
|
|
2440
|
-
const obstacles = getObstacleRects(problem);
|
|
2441
2570
|
const labelPadding = paddingBuffer;
|
|
2442
2571
|
const labelBoundsRaw = getRectBounds(label.center, label.width, label.height);
|
|
2443
2572
|
const labelBounds = {
|
|
@@ -2447,98 +2576,589 @@ var rerouteCollidingTrace = ({
|
|
|
2447
2576
|
maxY: labelBoundsRaw.maxY + labelPadding,
|
|
2448
2577
|
chipId: `netlabel-${label.netId}`
|
|
2449
2578
|
};
|
|
2450
|
-
const
|
|
2579
|
+
const fourPointCandidates = generateFourPointDetourCandidates({
|
|
2451
2580
|
initialTrace,
|
|
2452
2581
|
label,
|
|
2453
2582
|
labelBounds,
|
|
2454
|
-
obstacles,
|
|
2455
2583
|
paddingBuffer,
|
|
2456
2584
|
detourCount
|
|
2457
2585
|
});
|
|
2458
|
-
if (fourPointResult) {
|
|
2459
|
-
initialTrace.tracePath = fourPointResult.tracePath;
|
|
2460
|
-
}
|
|
2461
2586
|
const { firstInsideIndex, lastInsideIndex } = findTraceViolationZone(
|
|
2462
2587
|
initialTrace.tracePath,
|
|
2463
2588
|
labelBounds
|
|
2464
2589
|
);
|
|
2465
|
-
const
|
|
2590
|
+
const snipReconnectCandidates = generateSnipAndReconnectCandidates({
|
|
2466
2591
|
initialTrace,
|
|
2467
2592
|
firstInsideIndex,
|
|
2468
2593
|
lastInsideIndex,
|
|
2469
2594
|
labelBounds,
|
|
2470
|
-
|
|
2595
|
+
paddingBuffer,
|
|
2596
|
+
detourCount
|
|
2471
2597
|
});
|
|
2472
|
-
|
|
2473
|
-
return snipReconnectResult;
|
|
2474
|
-
}
|
|
2475
|
-
if (fourPointResult) {
|
|
2476
|
-
return fourPointResult;
|
|
2477
|
-
}
|
|
2478
|
-
return initialTrace;
|
|
2598
|
+
return [...fourPointCandidates, ...snipReconnectCandidates];
|
|
2479
2599
|
};
|
|
2480
2600
|
|
|
2481
|
-
// lib/solvers/TraceLabelOverlapAvoidanceSolver/
|
|
2482
|
-
var
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2601
|
+
// lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/SingleOverlapSolver/SingleOverlapSolver.ts
|
|
2602
|
+
var SingleOverlapSolver = class extends BaseSolver {
|
|
2603
|
+
queuedCandidatePaths;
|
|
2604
|
+
solvedTracePath = null;
|
|
2605
|
+
initialTrace;
|
|
2606
|
+
problem;
|
|
2607
|
+
obstacles;
|
|
2608
|
+
label;
|
|
2609
|
+
constructor(solverInput) {
|
|
2610
|
+
super();
|
|
2611
|
+
this.initialTrace = solverInput.trace;
|
|
2612
|
+
this.problem = solverInput.problem;
|
|
2613
|
+
this.label = solverInput.label;
|
|
2614
|
+
const candidates = generateRerouteCandidates({
|
|
2615
|
+
...solverInput
|
|
2616
|
+
});
|
|
2617
|
+
const getPathLength = (pts) => {
|
|
2618
|
+
let len = 0;
|
|
2619
|
+
for (let i = 0; i < pts.length - 1; i++) {
|
|
2620
|
+
const dx = pts[i + 1].x - pts[i].x;
|
|
2621
|
+
const dy = pts[i + 1].y - pts[i].y;
|
|
2622
|
+
len += Math.sqrt(dx * dx + dy * dy);
|
|
2623
|
+
}
|
|
2624
|
+
return len;
|
|
2625
|
+
};
|
|
2626
|
+
this.queuedCandidatePaths = candidates.sort(
|
|
2627
|
+
(a, b) => getPathLength(a) - getPathLength(b)
|
|
2628
|
+
);
|
|
2629
|
+
this.obstacles = getObstacleRects(this.problem);
|
|
2630
|
+
}
|
|
2631
|
+
_step() {
|
|
2632
|
+
if (this.queuedCandidatePaths.length === 0) {
|
|
2633
|
+
this.failed = true;
|
|
2634
|
+
return;
|
|
2635
|
+
}
|
|
2636
|
+
const nextCandidatePath = this.queuedCandidatePaths.shift();
|
|
2637
|
+
const simplifiedPath = simplifyPath(nextCandidatePath);
|
|
2638
|
+
if (!isPathCollidingWithObstacles(simplifiedPath, this.obstacles)) {
|
|
2639
|
+
this.solvedTracePath = simplifiedPath;
|
|
2640
|
+
this.solved = true;
|
|
2490
2641
|
}
|
|
2491
2642
|
}
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2643
|
+
visualize() {
|
|
2644
|
+
const graphics = visualizeInputProblem(this.problem, {
|
|
2645
|
+
chipAlpha: 0.1,
|
|
2646
|
+
connectionAlpha: 0.1
|
|
2647
|
+
});
|
|
2648
|
+
if (!graphics.lines) graphics.lines = [];
|
|
2649
|
+
if (!graphics.rects) graphics.rects = [];
|
|
2650
|
+
graphics.lines.push({
|
|
2651
|
+
points: this.initialTrace.tracePath,
|
|
2652
|
+
strokeColor: "red",
|
|
2653
|
+
strokeDash: "4 4"
|
|
2654
|
+
});
|
|
2655
|
+
graphics.rects.push({
|
|
2656
|
+
center: this.label.center,
|
|
2657
|
+
width: this.label.width,
|
|
2658
|
+
height: this.label.height,
|
|
2659
|
+
fill: "rgba(255, 0, 0, 0.2)"
|
|
2660
|
+
});
|
|
2661
|
+
if (this.queuedCandidatePaths.length > 0) {
|
|
2662
|
+
graphics.lines.push({
|
|
2663
|
+
points: this.queuedCandidatePaths[0],
|
|
2664
|
+
strokeColor: "orange"
|
|
2665
|
+
});
|
|
2666
|
+
}
|
|
2667
|
+
if (this.solvedTracePath) {
|
|
2668
|
+
graphics.lines.push({
|
|
2669
|
+
points: this.solvedTracePath,
|
|
2670
|
+
strokeColor: "green"
|
|
2671
|
+
});
|
|
2506
2672
|
}
|
|
2673
|
+
return graphics;
|
|
2507
2674
|
}
|
|
2508
|
-
return turns;
|
|
2509
2675
|
};
|
|
2510
2676
|
|
|
2511
|
-
// lib/solvers/TraceLabelOverlapAvoidanceSolver/
|
|
2512
|
-
var
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2677
|
+
// lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/OverlapAvoidanceStepSolver/OverlapAvoidanceStepSolver.ts
|
|
2678
|
+
var OverlapAvoidanceStepSolver = class extends BaseSolver {
|
|
2679
|
+
inputProblem;
|
|
2680
|
+
netLabelPlacements;
|
|
2681
|
+
mergedLabelNetIdMap;
|
|
2682
|
+
allTraces;
|
|
2683
|
+
modifiedTraces = [];
|
|
2684
|
+
detourCountByLabel = {};
|
|
2685
|
+
PADDING_BUFFER = 0.1;
|
|
2686
|
+
activeSubSolver = null;
|
|
2687
|
+
overlapQueue = [];
|
|
2688
|
+
recentlyFailed = /* @__PURE__ */ new Set();
|
|
2689
|
+
constructor(solverInput) {
|
|
2690
|
+
super();
|
|
2691
|
+
this.inputProblem = solverInput.inputProblem;
|
|
2692
|
+
this.netLabelPlacements = solverInput.netLabelPlacements;
|
|
2693
|
+
this.mergedLabelNetIdMap = solverInput.mergedLabelNetIdMap;
|
|
2694
|
+
this.allTraces = [...solverInput.traces];
|
|
2519
2695
|
}
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2696
|
+
_step() {
|
|
2697
|
+
if (this.activeSubSolver) {
|
|
2698
|
+
this.activeSubSolver.step();
|
|
2699
|
+
if (this.activeSubSolver.solved) {
|
|
2700
|
+
const solvedPath = this.activeSubSolver.solvedTracePath;
|
|
2701
|
+
if (solvedPath) {
|
|
2702
|
+
const traceIndex = this.allTraces.findIndex(
|
|
2703
|
+
(t) => t.mspPairId === this.activeSubSolver.initialTrace.mspPairId
|
|
2704
|
+
);
|
|
2705
|
+
if (traceIndex !== -1) {
|
|
2706
|
+
this.allTraces[traceIndex].tracePath = solvedPath;
|
|
2707
|
+
this.modifiedTraces.push(this.allTraces[traceIndex]);
|
|
2708
|
+
}
|
|
2709
|
+
}
|
|
2710
|
+
this.activeSubSolver = null;
|
|
2711
|
+
this.recentlyFailed.clear();
|
|
2712
|
+
} else if (this.activeSubSolver.failed) {
|
|
2713
|
+
const overlapId = `${this.activeSubSolver.initialTrace.mspPairId}-${this.activeSubSolver.label.globalConnNetId}`;
|
|
2714
|
+
this.recentlyFailed.add(overlapId);
|
|
2715
|
+
this.activeSubSolver = null;
|
|
2531
2716
|
}
|
|
2717
|
+
return;
|
|
2532
2718
|
}
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2719
|
+
const overlaps = detectTraceLabelOverlap(
|
|
2720
|
+
this.allTraces,
|
|
2721
|
+
this.netLabelPlacements
|
|
2722
|
+
).filter((o) => {
|
|
2723
|
+
const originalNetIds = this.mergedLabelNetIdMap[o.label.globalConnNetId];
|
|
2724
|
+
if (originalNetIds) {
|
|
2725
|
+
return !originalNetIds.has(o.trace.globalConnNetId);
|
|
2726
|
+
}
|
|
2727
|
+
return o.trace.globalConnNetId !== o.label.globalConnNetId;
|
|
2728
|
+
});
|
|
2729
|
+
if (overlaps.length === 0) {
|
|
2730
|
+
this.solved = true;
|
|
2731
|
+
return;
|
|
2732
|
+
}
|
|
2733
|
+
const nonFailedOverlaps = overlaps.filter((o) => {
|
|
2734
|
+
const overlapId = `${o.trace.mspPairId}-${o.label.globalConnNetId}`;
|
|
2735
|
+
return !this.recentlyFailed.has(overlapId);
|
|
2736
|
+
});
|
|
2737
|
+
if (nonFailedOverlaps.length === 0) {
|
|
2738
|
+
this.solved = true;
|
|
2739
|
+
return;
|
|
2740
|
+
}
|
|
2741
|
+
this.overlapQueue = nonFailedOverlaps;
|
|
2742
|
+
const nextOverlap = this.overlapQueue.shift();
|
|
2743
|
+
if (nextOverlap) {
|
|
2744
|
+
const traceToFix = this.allTraces.find(
|
|
2745
|
+
(t) => t.mspPairId === nextOverlap.trace.mspPairId
|
|
2746
|
+
);
|
|
2747
|
+
if (traceToFix) {
|
|
2748
|
+
const labelId = nextOverlap.label.globalConnNetId;
|
|
2749
|
+
const detourCount = this.detourCountByLabel[labelId] || 0;
|
|
2750
|
+
this.detourCountByLabel[labelId] = detourCount + 1;
|
|
2751
|
+
this.activeSubSolver = new SingleOverlapSolver({
|
|
2752
|
+
trace: traceToFix,
|
|
2753
|
+
label: nextOverlap.label,
|
|
2754
|
+
problem: this.inputProblem,
|
|
2755
|
+
paddingBuffer: this.PADDING_BUFFER,
|
|
2756
|
+
detourCount
|
|
2757
|
+
});
|
|
2758
|
+
}
|
|
2759
|
+
}
|
|
2760
|
+
}
|
|
2761
|
+
getOutput() {
|
|
2762
|
+
return {
|
|
2763
|
+
allTraces: this.allTraces,
|
|
2764
|
+
modifiedTraces: this.modifiedTraces
|
|
2765
|
+
};
|
|
2766
|
+
}
|
|
2767
|
+
visualize() {
|
|
2768
|
+
if (this.activeSubSolver) {
|
|
2769
|
+
return this.activeSubSolver.visualize();
|
|
2770
|
+
}
|
|
2771
|
+
const graphics = visualizeInputProblem(this.inputProblem);
|
|
2772
|
+
if (!graphics.lines) graphics.lines = [];
|
|
2773
|
+
for (const trace of this.allTraces) {
|
|
2774
|
+
graphics.lines.push({
|
|
2775
|
+
points: trace.tracePath,
|
|
2776
|
+
strokeColor: "purple"
|
|
2777
|
+
});
|
|
2778
|
+
}
|
|
2779
|
+
return graphics;
|
|
2780
|
+
}
|
|
2781
|
+
};
|
|
2782
|
+
|
|
2783
|
+
// lib/solvers/TraceLabelOverlapAvoidanceSolver/TraceLabelOverlapAvoidanceSolver.ts
|
|
2784
|
+
var TraceLabelOverlapAvoidanceSolver = class extends BaseSolver {
|
|
2785
|
+
inputProblem;
|
|
2786
|
+
traces;
|
|
2787
|
+
netLabelPlacements;
|
|
2788
|
+
// sub-solver instances
|
|
2789
|
+
labelMergingSolver;
|
|
2790
|
+
overlapAvoidanceSolver;
|
|
2791
|
+
pipelineStepIndex = 0;
|
|
2792
|
+
constructor(solverInput) {
|
|
2793
|
+
super();
|
|
2794
|
+
this.inputProblem = solverInput.inputProblem;
|
|
2795
|
+
this.traces = solverInput.traces;
|
|
2796
|
+
this.netLabelPlacements = solverInput.netLabelPlacements;
|
|
2797
|
+
}
|
|
2798
|
+
_step() {
|
|
2799
|
+
if (this.activeSubSolver) {
|
|
2800
|
+
this.activeSubSolver.step();
|
|
2801
|
+
if (this.activeSubSolver.solved) {
|
|
2802
|
+
this.activeSubSolver = null;
|
|
2803
|
+
this.pipelineStepIndex++;
|
|
2804
|
+
} else if (this.activeSubSolver.failed) {
|
|
2805
|
+
this.failed = true;
|
|
2806
|
+
this.activeSubSolver = null;
|
|
2807
|
+
}
|
|
2808
|
+
return;
|
|
2809
|
+
}
|
|
2810
|
+
switch (this.pipelineStepIndex) {
|
|
2811
|
+
case 0:
|
|
2812
|
+
this.labelMergingSolver = new MergedNetLabelObstacleSolver({
|
|
2813
|
+
netLabelPlacements: this.netLabelPlacements,
|
|
2814
|
+
inputProblem: this.inputProblem,
|
|
2815
|
+
traces: this.traces
|
|
2816
|
+
});
|
|
2817
|
+
this.activeSubSolver = this.labelMergingSolver;
|
|
2818
|
+
break;
|
|
2819
|
+
case 1:
|
|
2820
|
+
this.overlapAvoidanceSolver = new OverlapAvoidanceStepSolver({
|
|
2821
|
+
inputProblem: this.inputProblem,
|
|
2822
|
+
traces: this.traces,
|
|
2823
|
+
netLabelPlacements: this.labelMergingSolver.getOutput().netLabelPlacements,
|
|
2824
|
+
mergedLabelNetIdMap: this.labelMergingSolver.getOutput().mergedLabelNetIdMap
|
|
2825
|
+
});
|
|
2826
|
+
this.activeSubSolver = this.overlapAvoidanceSolver;
|
|
2827
|
+
break;
|
|
2828
|
+
default:
|
|
2829
|
+
this.solved = true;
|
|
2830
|
+
break;
|
|
2831
|
+
}
|
|
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;
|
|
3066
|
+
}
|
|
3067
|
+
visualize() {
|
|
3068
|
+
if (this.subSolver) {
|
|
3069
|
+
return this.subSolver.visualize();
|
|
3070
|
+
}
|
|
3071
|
+
const graphics = visualizeInputProblem(this.inputProblem);
|
|
3072
|
+
for (const trace of this.solvedLongDistanceTraces) {
|
|
3073
|
+
graphics.lines.push({
|
|
3074
|
+
points: trace.tracePath,
|
|
3075
|
+
strokeColor: "purple"
|
|
3076
|
+
});
|
|
3077
|
+
}
|
|
3078
|
+
for (const [p1, p2] of this.queuedCandidatePairs) {
|
|
3079
|
+
graphics.lines.push({
|
|
3080
|
+
points: [p1, p2],
|
|
3081
|
+
strokeColor: "gray",
|
|
3082
|
+
strokeDash: "4 4"
|
|
3083
|
+
});
|
|
3084
|
+
}
|
|
3085
|
+
return graphics;
|
|
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
|
+
}
|
|
3099
|
+
};
|
|
3100
|
+
|
|
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;
|
|
3109
|
+
}
|
|
3110
|
+
}
|
|
3111
|
+
}
|
|
3112
|
+
return false;
|
|
3113
|
+
};
|
|
3114
|
+
|
|
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++;
|
|
3126
|
+
}
|
|
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
|
|
2542
3162
|
}) => {
|
|
2543
3163
|
if (path.length <= 2) {
|
|
2544
3164
|
return path;
|
|
@@ -2595,7 +3215,6 @@ var minimizeTurns = ({
|
|
|
2595
3215
|
);
|
|
2596
3216
|
if (!collidesWithObstacles && !collidesWithLabels) {
|
|
2597
3217
|
const newTurns = countTurns(testPath);
|
|
2598
|
-
const turnsRemoved = stairEndIdx - startIdx - 1;
|
|
2599
3218
|
optimizedPath = testPath;
|
|
2600
3219
|
currentTurns = newTurns;
|
|
2601
3220
|
improved = true;
|
|
@@ -2665,621 +3284,294 @@ var minimizeTurns = ({
|
|
|
2665
3284
|
labelBounds
|
|
2666
3285
|
);
|
|
2667
3286
|
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
|
-
if (!finalLabelPlacementSolver.failed) {
|
|
3013
|
-
this.netLabelPlacements = finalLabelPlacementSolver.netLabelPlacements;
|
|
3014
|
-
}
|
|
3015
|
-
this.solved = true;
|
|
3016
|
-
}
|
|
3017
|
-
getOutput() {
|
|
3018
|
-
return {
|
|
3019
|
-
traces: this.updatedTraces,
|
|
3020
|
-
netLabelPlacements: this.netLabelPlacements
|
|
3021
|
-
};
|
|
3022
|
-
}
|
|
3023
|
-
visualize() {
|
|
3024
|
-
const graphics = visualizeInputProblem(this.problem);
|
|
3025
|
-
if (!graphics.lines) graphics.lines = [];
|
|
3026
|
-
if (!graphics.circles) graphics.circles = [];
|
|
3027
|
-
if (!graphics.texts) graphics.texts = [];
|
|
3028
|
-
if (!graphics.rects) graphics.rects = [];
|
|
3029
|
-
for (const trace of this.updatedTraces) {
|
|
3030
|
-
graphics.lines.push({
|
|
3031
|
-
points: trace.tracePath,
|
|
3032
|
-
strokeColor: "purple"
|
|
3033
|
-
});
|
|
3034
|
-
}
|
|
3035
|
-
for (const p of this.netLabelPlacements) {
|
|
3036
|
-
graphics.rects.push({
|
|
3037
|
-
center: p.center,
|
|
3038
|
-
width: p.width,
|
|
3039
|
-
height: p.height,
|
|
3040
|
-
fill: getColorFromString(p.globalConnNetId, 0.35)
|
|
3041
|
-
});
|
|
3042
|
-
graphics.points.push({
|
|
3043
|
-
x: p.anchorPoint.x,
|
|
3044
|
-
y: p.anchorPoint.y,
|
|
3045
|
-
color: getColorFromString(p.globalConnNetId, 0.9)
|
|
3046
|
-
});
|
|
3047
|
-
}
|
|
3048
|
-
return graphics;
|
|
3049
|
-
}
|
|
3050
|
-
};
|
|
3051
|
-
|
|
3052
|
-
// lib/solvers/SchematicTracePipelineSolver/correctPinsInsideChip.ts
|
|
3053
|
-
var correctPinsInsideChips = (problem) => {
|
|
3054
|
-
for (const chip of problem.chips) {
|
|
3055
|
-
const bounds = getInputChipBounds(chip);
|
|
3056
|
-
for (const pin of chip.pins) {
|
|
3057
|
-
const isInside = pin.x > bounds.minX && pin.x < bounds.maxX && pin.y > bounds.minY && pin.y < bounds.maxY;
|
|
3058
|
-
if (!isInside) continue;
|
|
3059
|
-
const distLeft = pin.x - bounds.minX;
|
|
3060
|
-
const distRight = bounds.maxX - pin.x;
|
|
3061
|
-
const distBottom = pin.y - bounds.minY;
|
|
3062
|
-
const distTop = bounds.maxY - pin.y;
|
|
3063
|
-
const minDist = Math.min(distLeft, distRight, distBottom, distTop);
|
|
3064
|
-
if (minDist === distLeft) {
|
|
3065
|
-
pin.x = bounds.minX;
|
|
3066
|
-
} else if (minDist === distRight) {
|
|
3067
|
-
pin.x = bounds.maxX;
|
|
3068
|
-
} else if (minDist === distBottom) {
|
|
3069
|
-
pin.y = bounds.minY;
|
|
3070
|
-
} else {
|
|
3071
|
-
pin.y = bounds.maxY;
|
|
3072
|
-
}
|
|
3073
|
-
pin._facingDirection = void 0;
|
|
3074
|
-
}
|
|
3075
|
-
}
|
|
3076
|
-
};
|
|
3077
|
-
|
|
3078
|
-
// lib/solvers/SchematicTracePipelineSolver/expandChipsToFitPins.ts
|
|
3079
|
-
var expandChipsToFitPins = (problem) => {
|
|
3080
|
-
for (const chip of problem.chips) {
|
|
3081
|
-
const halfWidth = chip.width / 2;
|
|
3082
|
-
const halfHeight = chip.height / 2;
|
|
3083
|
-
let maxDx = 0;
|
|
3084
|
-
let maxDy = 0;
|
|
3085
|
-
for (const pin of chip.pins) {
|
|
3086
|
-
const dx = Math.abs(pin.x - chip.center.x);
|
|
3087
|
-
const dy = Math.abs(pin.y - chip.center.y);
|
|
3088
|
-
if (dx > maxDx) maxDx = dx;
|
|
3089
|
-
if (dy > maxDy) maxDy = dy;
|
|
3090
|
-
}
|
|
3091
|
-
const newHalfWidth = Math.max(halfWidth, maxDx);
|
|
3092
|
-
const newHalfHeight = Math.max(halfHeight, maxDy);
|
|
3093
|
-
if (newHalfWidth > halfWidth || newHalfHeight > halfHeight) {
|
|
3094
|
-
chip.width = newHalfWidth * 2;
|
|
3095
|
-
chip.height = newHalfHeight * 2;
|
|
3096
|
-
for (const pin of chip.pins) {
|
|
3097
|
-
pin._facingDirection = void 0;
|
|
3287
|
+
optimizedPath = testPath;
|
|
3288
|
+
improved = true;
|
|
3289
|
+
break;
|
|
3290
|
+
}
|
|
3291
|
+
}
|
|
3098
3292
|
}
|
|
3099
3293
|
}
|
|
3100
3294
|
}
|
|
3295
|
+
const finalSimplifiedPath = simplifyPath(optimizedPath);
|
|
3296
|
+
return finalSimplifiedPath;
|
|
3101
3297
|
};
|
|
3102
3298
|
|
|
3103
|
-
// lib/
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
existingSegmentP2
|
|
3118
|
-
)) {
|
|
3119
|
-
return true;
|
|
3120
|
-
}
|
|
3121
|
-
}
|
|
3122
|
-
}
|
|
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`);
|
|
3123
3313
|
}
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
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
|
+
};
|
|
3131
3355
|
};
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
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;
|
|
3151
3401
|
}
|
|
3152
3402
|
}
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
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;
|
|
3183
3484
|
}
|
|
3184
|
-
this.queuedCandidatePairs = candidatePairs;
|
|
3185
3485
|
}
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
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);
|
|
3197
3508
|
}
|
|
3198
3509
|
_step() {
|
|
3199
|
-
if (this.
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
const isTraceClear = !doesTraceOverlapWithExistingTraces(
|
|
3203
|
-
newTracePath,
|
|
3204
|
-
this.allSolvedTraces
|
|
3205
|
-
);
|
|
3206
|
-
if (isTraceClear) {
|
|
3207
|
-
const [p1, p2] = this.currentCandidatePair;
|
|
3208
|
-
const globalConnNetId = this.netConnMap.getNetConnectedToId(p1.pinId);
|
|
3209
|
-
const mspPairId = `${p1.pinId}-${p2.pinId}`;
|
|
3210
|
-
const newSolvedTrace = {
|
|
3211
|
-
mspPairId,
|
|
3212
|
-
dcConnNetId: globalConnNetId,
|
|
3213
|
-
globalConnNetId,
|
|
3214
|
-
pins: [p1, p2],
|
|
3215
|
-
tracePath: newTracePath,
|
|
3216
|
-
mspConnectionPairIds: [mspPairId],
|
|
3217
|
-
pinIds: [p1.pinId, p2.pinId]
|
|
3218
|
-
};
|
|
3219
|
-
this.solvedLongDistanceTraces.push(newSolvedTrace);
|
|
3220
|
-
this.allSolvedTraces.push(newSolvedTrace);
|
|
3221
|
-
this.newlyConnectedPinIds.add(p1.pinId);
|
|
3222
|
-
this.newlyConnectedPinIds.add(p2.pinId);
|
|
3223
|
-
}
|
|
3224
|
-
}
|
|
3225
|
-
this.subSolver = null;
|
|
3226
|
-
this.currentCandidatePair = null;
|
|
3227
|
-
} else if (this.subSolver?.failed) {
|
|
3228
|
-
this.subSolver = null;
|
|
3229
|
-
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);
|
|
3230
3513
|
}
|
|
3231
|
-
if (this.
|
|
3232
|
-
this.
|
|
3514
|
+
if (this.pipelineStep === "balancing_l_shapes" && this.traceIdQueue.length === 0) {
|
|
3515
|
+
this.solved = true;
|
|
3233
3516
|
return;
|
|
3234
3517
|
}
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
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)) {
|
|
3247
3530
|
return;
|
|
3248
3531
|
}
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
for (const trace of this.solvedLongDistanceTraces) {
|
|
3257
|
-
graphics.lines.push({
|
|
3258
|
-
points: trace.tracePath,
|
|
3259
|
-
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
|
|
3260
3539
|
});
|
|
3261
|
-
}
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
strokeDash: "4 4"
|
|
3540
|
+
} else {
|
|
3541
|
+
updatedTrace = balanceZShapes({
|
|
3542
|
+
...this.input,
|
|
3543
|
+
targetMspConnectionPairId,
|
|
3544
|
+
traces: allTraces
|
|
3267
3545
|
});
|
|
3268
3546
|
}
|
|
3269
|
-
|
|
3547
|
+
this.tracesMap.set(targetMspConnectionPairId, updatedTrace);
|
|
3548
|
+
this.outputTraces = Array.from(this.tracesMap.values());
|
|
3270
3549
|
}
|
|
3271
3550
|
getOutput() {
|
|
3272
|
-
if (!this.solved) {
|
|
3273
|
-
return { newTraces: [], allTracesMerged: this.params.alreadySolvedTraces };
|
|
3274
|
-
}
|
|
3275
3551
|
return {
|
|
3276
|
-
|
|
3277
|
-
allTracesMerged: [
|
|
3278
|
-
...this.params.alreadySolvedTraces,
|
|
3279
|
-
...this.solvedLongDistanceTraces
|
|
3280
|
-
]
|
|
3552
|
+
traces: this.outputTraces
|
|
3281
3553
|
};
|
|
3282
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
|
+
}
|
|
3283
3575
|
};
|
|
3284
3576
|
|
|
3285
3577
|
// lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts
|
|
@@ -3299,7 +3591,9 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
|
|
|
3299
3591
|
longDistancePairSolver;
|
|
3300
3592
|
traceOverlapShiftSolver;
|
|
3301
3593
|
netLabelPlacementSolver;
|
|
3594
|
+
labelMergingSolver;
|
|
3302
3595
|
traceLabelOverlapAvoidanceSolver;
|
|
3596
|
+
traceCleanupSolver;
|
|
3303
3597
|
startTimeOfPhase;
|
|
3304
3598
|
endTimeOfPhase;
|
|
3305
3599
|
timeSpentOnPhase;
|
|
@@ -3405,14 +3699,36 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
|
|
|
3405
3699
|
netLabelPlacements
|
|
3406
3700
|
}
|
|
3407
3701
|
];
|
|
3408
|
-
}
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3702
|
+
}
|
|
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
|
|
3415
3716
|
}
|
|
3717
|
+
];
|
|
3718
|
+
}),
|
|
3719
|
+
definePipelineStep(
|
|
3720
|
+
"netLabelPlacementSolver",
|
|
3721
|
+
NetLabelPlacementSolver,
|
|
3722
|
+
(instance) => {
|
|
3723
|
+
const traces = instance.traceCleanupSolver?.getOutput().traces ?? instance.traceLabelOverlapAvoidanceSolver.getOutput().traces;
|
|
3724
|
+
return [
|
|
3725
|
+
{
|
|
3726
|
+
inputProblem: instance.inputProblem,
|
|
3727
|
+
inputTraceMap: Object.fromEntries(
|
|
3728
|
+
traces.map((trace) => [trace.mspPairId, trace])
|
|
3729
|
+
)
|
|
3730
|
+
}
|
|
3731
|
+
];
|
|
3416
3732
|
}
|
|
3417
3733
|
)
|
|
3418
3734
|
];
|
|
@@ -3498,14 +3814,17 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
|
|
|
3498
3814
|
return viz;
|
|
3499
3815
|
})
|
|
3500
3816
|
];
|
|
3501
|
-
if (visualizations.length === 1)
|
|
3502
|
-
|
|
3817
|
+
if (visualizations.length === 1) {
|
|
3818
|
+
return visualizations[0];
|
|
3819
|
+
}
|
|
3820
|
+
const finalGraphics = {
|
|
3503
3821
|
points: visualizations.flatMap((v) => v.points || []),
|
|
3504
3822
|
rects: visualizations.flatMap((v) => v.rects || []),
|
|
3505
3823
|
lines: visualizations.flatMap((v) => v.lines || []),
|
|
3506
3824
|
circles: visualizations.flatMap((v) => v.circles || []),
|
|
3507
3825
|
texts: visualizations.flatMap((v) => v.texts || [])
|
|
3508
3826
|
};
|
|
3827
|
+
return finalGraphics;
|
|
3509
3828
|
}
|
|
3510
3829
|
/**
|
|
3511
3830
|
* A lightweight version of the visualize method that can be used to stream
|