@tscircuit/schematic-trace-solver 0.0.41 → 0.0.42
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 -7
- package/dist/index.js +760 -432
- package/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts +30 -17
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/TraceLabelOverlapAvoidanceSolver.ts +94 -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/sub-solvers/TraceCleanupSolver/TraceCleanupSolver.ts +132 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/{balanceLShapes.ts → sub-solvers/TraceCleanupSolver/balanceLShapes.ts} +12 -10
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/{hasCollisions.ts → sub-solvers/TraceCleanupSolver/hasCollisions.ts} +1 -1
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/{hasCollisionsWithLabels.ts → sub-solvers/TraceCleanupSolver/hasCollisionsWithLabels.ts} +1 -1
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/{minimizeTurnsWithFilteredLabels.ts → sub-solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts} +6 -6
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/{simplifyPath.ts → sub-solvers/TraceCleanupSolver/simplifyPath.ts} +1 -1
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/{turnMinimization.ts → sub-solvers/TraceCleanupSolver/turnMinimization.ts} +0 -2
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/tryFourPointDetour.ts +104 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/trySnipAndReconnect.ts +24 -154
- package/package.json +1 -1
- 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/examples/__snapshots__/example25.snap.svg +3 -3
- package/tests/examples/__snapshots__/example26.snap.svg +3 -3
- 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/TraceCleanupSolver.test.ts +19 -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/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/__snapshots__/TraceCleanupSolver.snap.svg +119 -0
- /package/lib/solvers/TraceLabelOverlapAvoidanceSolver/{countTurns.ts → sub-solvers/TraceCleanupSolver/countTurns.ts} +0 -0
- /package/lib/solvers/TraceLabelOverlapAvoidanceSolver/{tryConnectPoints.ts → sub-solvers/TraceCleanupSolver/tryConnectPoints.ts} +0 -0
package/dist/index.js
CHANGED
|
@@ -2196,48 +2196,193 @@ var NetLabelPlacementSolver = class extends BaseSolver {
|
|
|
2196
2196
|
}
|
|
2197
2197
|
};
|
|
2198
2198
|
|
|
2199
|
-
// lib/solvers/
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
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
|
+
}
|
|
2214
2339
|
}
|
|
2215
|
-
overlaps.push({ trace, label });
|
|
2216
|
-
break;
|
|
2217
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
|
+
});
|
|
2218
2349
|
}
|
|
2219
2350
|
}
|
|
2351
|
+
return graphics;
|
|
2220
2352
|
}
|
|
2221
|
-
return overlaps;
|
|
2222
2353
|
};
|
|
2223
2354
|
|
|
2224
|
-
// lib/solvers/TraceLabelOverlapAvoidanceSolver/
|
|
2225
|
-
var
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
firstInsideIndex = i;
|
|
2355
|
+
// lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver/hasCollisions.ts
|
|
2356
|
+
var hasCollisions = (pathSegments, obstacles) => {
|
|
2357
|
+
for (let i = 0; i < pathSegments.length - 1; i++) {
|
|
2358
|
+
const p1 = pathSegments[i];
|
|
2359
|
+
const p2 = pathSegments[i + 1];
|
|
2360
|
+
for (const obstacle of obstacles) {
|
|
2361
|
+
if (segmentIntersectsRect(p1, p2, obstacle)) {
|
|
2362
|
+
return true;
|
|
2233
2363
|
}
|
|
2234
|
-
lastInsideIndex = i;
|
|
2235
2364
|
}
|
|
2236
2365
|
}
|
|
2237
|
-
return
|
|
2366
|
+
return false;
|
|
2238
2367
|
};
|
|
2239
2368
|
|
|
2240
|
-
// lib/solvers/TraceLabelOverlapAvoidanceSolver/
|
|
2369
|
+
// lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver/countTurns.ts
|
|
2370
|
+
var countTurns = (points) => {
|
|
2371
|
+
let turns = 0;
|
|
2372
|
+
for (let i = 1; i < points.length - 1; i++) {
|
|
2373
|
+
const prev = points[i - 1];
|
|
2374
|
+
const curr = points[i];
|
|
2375
|
+
const next = points[i + 1];
|
|
2376
|
+
const prevVertical = prev.x === curr.x;
|
|
2377
|
+
const nextVertical = curr.x === next.x;
|
|
2378
|
+
if (prevVertical !== nextVertical) {
|
|
2379
|
+
turns++;
|
|
2380
|
+
}
|
|
2381
|
+
}
|
|
2382
|
+
return turns;
|
|
2383
|
+
};
|
|
2384
|
+
|
|
2385
|
+
// lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver/simplifyPath.ts
|
|
2241
2386
|
var simplifyPath = (path) => {
|
|
2242
2387
|
if (path.length < 3) return path;
|
|
2243
2388
|
const newPath = [path[0]];
|
|
@@ -2266,249 +2411,7 @@ var simplifyPath = (path) => {
|
|
|
2266
2411
|
return finalPath;
|
|
2267
2412
|
};
|
|
2268
2413
|
|
|
2269
|
-
// lib/solvers/TraceLabelOverlapAvoidanceSolver/
|
|
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
|
|
2414
|
+
// lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver/tryConnectPoints.ts
|
|
2512
2415
|
var tryConnectPoints = (start, end) => {
|
|
2513
2416
|
const candidates = [];
|
|
2514
2417
|
if (start.x === end.x || start.y === end.y) {
|
|
@@ -2520,7 +2423,7 @@ var tryConnectPoints = (start, end) => {
|
|
|
2520
2423
|
return candidates;
|
|
2521
2424
|
};
|
|
2522
2425
|
|
|
2523
|
-
// lib/solvers/TraceLabelOverlapAvoidanceSolver/hasCollisionsWithLabels.ts
|
|
2426
|
+
// lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver/hasCollisionsWithLabels.ts
|
|
2524
2427
|
var hasCollisionsWithLabels = (pathSegments, labels) => {
|
|
2525
2428
|
for (let i = 0; i < pathSegments.length - 1; i++) {
|
|
2526
2429
|
const p1 = pathSegments[i];
|
|
@@ -2534,7 +2437,7 @@ var hasCollisionsWithLabels = (pathSegments, labels) => {
|
|
|
2534
2437
|
return false;
|
|
2535
2438
|
};
|
|
2536
2439
|
|
|
2537
|
-
// lib/solvers/TraceLabelOverlapAvoidanceSolver/turnMinimization.ts
|
|
2440
|
+
// lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver/turnMinimization.ts
|
|
2538
2441
|
var minimizeTurns = ({
|
|
2539
2442
|
path,
|
|
2540
2443
|
obstacles,
|
|
@@ -2595,7 +2498,6 @@ var minimizeTurns = ({
|
|
|
2595
2498
|
);
|
|
2596
2499
|
if (!collidesWithObstacles && !collidesWithLabels) {
|
|
2597
2500
|
const newTurns = countTurns(testPath);
|
|
2598
|
-
const turnsRemoved = stairEndIdx - startIdx - 1;
|
|
2599
2501
|
optimizedPath = testPath;
|
|
2600
2502
|
currentTurns = newTurns;
|
|
2601
2503
|
improved = true;
|
|
@@ -2677,16 +2579,16 @@ var minimizeTurns = ({
|
|
|
2677
2579
|
return finalSimplifiedPath;
|
|
2678
2580
|
};
|
|
2679
2581
|
|
|
2680
|
-
// lib/solvers/TraceLabelOverlapAvoidanceSolver/minimizeTurnsWithFilteredLabels.ts
|
|
2582
|
+
// lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts
|
|
2681
2583
|
var minimizeTurnsWithFilteredLabels = ({
|
|
2682
2584
|
traces,
|
|
2683
|
-
|
|
2585
|
+
inputProblem,
|
|
2684
2586
|
allLabelPlacements,
|
|
2685
2587
|
mergedLabelNetIdMap,
|
|
2686
2588
|
paddingBuffer
|
|
2687
2589
|
}) => {
|
|
2688
2590
|
let changesMade = false;
|
|
2689
|
-
const obstacles = getObstacleRects(
|
|
2591
|
+
const obstacles = getObstacleRects(inputProblem);
|
|
2690
2592
|
const newTraces = traces.map((trace) => {
|
|
2691
2593
|
const originalPath = trace.tracePath;
|
|
2692
2594
|
const filteredLabels = allLabelPlacements.filter((label) => {
|
|
@@ -2724,10 +2626,10 @@ var minimizeTurnsWithFilteredLabels = ({
|
|
|
2724
2626
|
}
|
|
2725
2627
|
};
|
|
2726
2628
|
|
|
2727
|
-
// lib/solvers/TraceLabelOverlapAvoidanceSolver/balanceLShapes.ts
|
|
2629
|
+
// lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver/balanceLShapes.ts
|
|
2728
2630
|
var balanceLShapes = ({
|
|
2729
2631
|
traces,
|
|
2730
|
-
|
|
2632
|
+
inputProblem,
|
|
2731
2633
|
allLabelPlacements
|
|
2732
2634
|
}) => {
|
|
2733
2635
|
const TOLERANCE = 1e-5;
|
|
@@ -2752,7 +2654,7 @@ var balanceLShapes = ({
|
|
|
2752
2654
|
}
|
|
2753
2655
|
}
|
|
2754
2656
|
}
|
|
2755
|
-
const obstacles = getObstacleRects(
|
|
2657
|
+
const obstacles = getObstacleRects(inputProblem).map((obs) => ({
|
|
2756
2658
|
...obs,
|
|
2757
2659
|
minX: obs.minX + TOLERANCE,
|
|
2758
2660
|
maxX: obs.maxX - TOLERANCE,
|
|
@@ -2779,7 +2681,7 @@ var balanceLShapes = ({
|
|
|
2779
2681
|
}));
|
|
2780
2682
|
};
|
|
2781
2683
|
const newTraces = traces.map((trace) => {
|
|
2782
|
-
|
|
2684
|
+
const newPath = [...trace.tracePath];
|
|
2783
2685
|
if (newPath.length < 4) {
|
|
2784
2686
|
return { ...trace };
|
|
2785
2687
|
}
|
|
@@ -2789,7 +2691,8 @@ var balanceLShapes = ({
|
|
|
2789
2691
|
);
|
|
2790
2692
|
if (newPath.length === 4) {
|
|
2791
2693
|
const [p0, p1, p2, p3] = newPath;
|
|
2792
|
-
let p1New
|
|
2694
|
+
let p1New;
|
|
2695
|
+
let p2New;
|
|
2793
2696
|
const isHVHShape = p0.y === p1.y && p1.x === p2.x && p2.y === p3.y;
|
|
2794
2697
|
if (isHVHShape) {
|
|
2795
2698
|
const idealX = (p0.x + p3.x) / 2;
|
|
@@ -2828,7 +2731,8 @@ var balanceLShapes = ({
|
|
|
2828
2731
|
if (!isValidZShape) {
|
|
2829
2732
|
continue;
|
|
2830
2733
|
}
|
|
2831
|
-
let p2New
|
|
2734
|
+
let p2New;
|
|
2735
|
+
let p3New;
|
|
2832
2736
|
const len1Original = isHVHZShape ? Math.abs(p1.x - p2.x) : Math.abs(p1.y - p2.y);
|
|
2833
2737
|
const len2Original = isHVHZShape ? Math.abs(p3.x - p4.x) : Math.abs(p3.y - p4.y);
|
|
2834
2738
|
if (Math.abs(len1Original - len2Original) < 1e-3) {
|
|
@@ -2860,189 +2764,601 @@ var balanceLShapes = ({
|
|
|
2860
2764
|
if (changesMade) {
|
|
2861
2765
|
return newTraces;
|
|
2862
2766
|
} else {
|
|
2863
|
-
return null;
|
|
2767
|
+
return null;
|
|
2768
|
+
}
|
|
2769
|
+
};
|
|
2770
|
+
|
|
2771
|
+
// lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver/TraceCleanupSolver.ts
|
|
2772
|
+
var TraceCleanupSolver = class extends BaseSolver {
|
|
2773
|
+
input;
|
|
2774
|
+
outputTraces;
|
|
2775
|
+
pipelineStepIndex = 0;
|
|
2776
|
+
tracesToProcess;
|
|
2777
|
+
minimizedTraces = null;
|
|
2778
|
+
balancedTraces = null;
|
|
2779
|
+
constructor(solverInput) {
|
|
2780
|
+
super();
|
|
2781
|
+
this.input = solverInput;
|
|
2782
|
+
this.outputTraces = [...solverInput.allTraces];
|
|
2783
|
+
this.tracesToProcess = this.outputTraces.filter(
|
|
2784
|
+
(t) => this.input.targetTraceIds.has(t.mspPairId)
|
|
2785
|
+
);
|
|
2786
|
+
}
|
|
2787
|
+
_step() {
|
|
2788
|
+
const {
|
|
2789
|
+
targetTraceIds,
|
|
2790
|
+
inputProblem,
|
|
2791
|
+
allLabelPlacements,
|
|
2792
|
+
mergedLabelNetIdMap,
|
|
2793
|
+
paddingBuffer
|
|
2794
|
+
} = this.input;
|
|
2795
|
+
if (targetTraceIds.size === 0) {
|
|
2796
|
+
this.solved = true;
|
|
2797
|
+
return;
|
|
2798
|
+
}
|
|
2799
|
+
switch (this.pipelineStepIndex) {
|
|
2800
|
+
case 0: {
|
|
2801
|
+
const minimizedTracesResult = minimizeTurnsWithFilteredLabels({
|
|
2802
|
+
traces: this.tracesToProcess,
|
|
2803
|
+
inputProblem,
|
|
2804
|
+
allLabelPlacements,
|
|
2805
|
+
mergedLabelNetIdMap,
|
|
2806
|
+
paddingBuffer
|
|
2807
|
+
});
|
|
2808
|
+
this.minimizedTraces = minimizedTracesResult ?? this.tracesToProcess;
|
|
2809
|
+
const tracesMap = new Map(
|
|
2810
|
+
this.input.allTraces.map((t) => [t.mspPairId, t])
|
|
2811
|
+
);
|
|
2812
|
+
for (const trace of this.minimizedTraces) {
|
|
2813
|
+
tracesMap.set(trace.mspPairId, trace);
|
|
2814
|
+
}
|
|
2815
|
+
this.outputTraces = Array.from(tracesMap.values());
|
|
2816
|
+
this.pipelineStepIndex++;
|
|
2817
|
+
break;
|
|
2818
|
+
}
|
|
2819
|
+
case 1: {
|
|
2820
|
+
const balancedTracesResult = balanceLShapes({
|
|
2821
|
+
traces: this.minimizedTraces,
|
|
2822
|
+
inputProblem,
|
|
2823
|
+
allLabelPlacements
|
|
2824
|
+
});
|
|
2825
|
+
this.balancedTraces = balancedTracesResult ?? this.minimizedTraces;
|
|
2826
|
+
const tracesMap = new Map(
|
|
2827
|
+
this.input.allTraces.map((t) => [t.mspPairId, t])
|
|
2828
|
+
);
|
|
2829
|
+
for (const trace of this.balancedTraces) {
|
|
2830
|
+
tracesMap.set(trace.mspPairId, trace);
|
|
2831
|
+
}
|
|
2832
|
+
this.outputTraces = Array.from(tracesMap.values());
|
|
2833
|
+
this.pipelineStepIndex++;
|
|
2834
|
+
break;
|
|
2835
|
+
}
|
|
2836
|
+
case 2: {
|
|
2837
|
+
this.solved = true;
|
|
2838
|
+
break;
|
|
2839
|
+
}
|
|
2840
|
+
}
|
|
2841
|
+
}
|
|
2842
|
+
getOutput() {
|
|
2843
|
+
return {
|
|
2844
|
+
traces: this.outputTraces
|
|
2845
|
+
};
|
|
2846
|
+
}
|
|
2847
|
+
visualize() {
|
|
2848
|
+
const graphics = visualizeInputProblem(this.input.inputProblem, {
|
|
2849
|
+
chipAlpha: 0.1,
|
|
2850
|
+
connectionAlpha: 0.1
|
|
2851
|
+
});
|
|
2852
|
+
if (!graphics.lines) graphics.lines = [];
|
|
2853
|
+
if (!graphics.points) graphics.points = [];
|
|
2854
|
+
if (!graphics.rects) graphics.rects = [];
|
|
2855
|
+
if (!graphics.circles) graphics.circles = [];
|
|
2856
|
+
if (!graphics.texts) graphics.texts = [];
|
|
2857
|
+
for (const trace of this.outputTraces) {
|
|
2858
|
+
const line = {
|
|
2859
|
+
points: trace.tracePath.map((p) => ({ x: p.x, y: p.y })),
|
|
2860
|
+
strokeColor: "blue"
|
|
2861
|
+
};
|
|
2862
|
+
graphics.lines.push(line);
|
|
2863
|
+
}
|
|
2864
|
+
return graphics;
|
|
2865
|
+
}
|
|
2866
|
+
};
|
|
2867
|
+
|
|
2868
|
+
// lib/solvers/TraceLabelOverlapAvoidanceSolver/detectTraceLabelOverlap.ts
|
|
2869
|
+
var detectTraceLabelOverlap = (traces, netLabels) => {
|
|
2870
|
+
const overlaps = [];
|
|
2871
|
+
for (const trace of traces) {
|
|
2872
|
+
for (const label of netLabels) {
|
|
2873
|
+
const labelBounds = getRectBounds(label.center, label.width, label.height);
|
|
2874
|
+
for (let i = 0; i < trace.tracePath.length - 1; i++) {
|
|
2875
|
+
const p1 = trace.tracePath[i];
|
|
2876
|
+
const p2 = trace.tracePath[i + 1];
|
|
2877
|
+
if (segmentIntersectsRect2(p1, p2, labelBounds)) {
|
|
2878
|
+
if (trace.globalConnNetId === label.globalConnNetId) {
|
|
2879
|
+
break;
|
|
2880
|
+
}
|
|
2881
|
+
overlaps.push({ trace, label });
|
|
2882
|
+
break;
|
|
2883
|
+
}
|
|
2884
|
+
}
|
|
2885
|
+
}
|
|
2886
|
+
}
|
|
2887
|
+
return overlaps;
|
|
2888
|
+
};
|
|
2889
|
+
|
|
2890
|
+
// lib/solvers/TraceLabelOverlapAvoidanceSolver/violation.ts
|
|
2891
|
+
var findTraceViolationZone = (path, labelBounds) => {
|
|
2892
|
+
const isPointInside = (p) => p.x > labelBounds.minX && p.x < labelBounds.maxX && p.y > labelBounds.minY && p.y < labelBounds.maxY;
|
|
2893
|
+
let firstInsideIndex = -1;
|
|
2894
|
+
let lastInsideIndex = -1;
|
|
2895
|
+
for (let i = 0; i < path.length; i++) {
|
|
2896
|
+
if (isPointInside(path[i])) {
|
|
2897
|
+
if (firstInsideIndex === -1) {
|
|
2898
|
+
firstInsideIndex = i;
|
|
2899
|
+
}
|
|
2900
|
+
lastInsideIndex = i;
|
|
2901
|
+
}
|
|
2902
|
+
}
|
|
2903
|
+
return { firstInsideIndex, lastInsideIndex };
|
|
2904
|
+
};
|
|
2905
|
+
|
|
2906
|
+
// lib/solvers/TraceLabelOverlapAvoidanceSolver/trySnipAndReconnect.ts
|
|
2907
|
+
var generateSnipAndReconnectCandidates = ({
|
|
2908
|
+
initialTrace,
|
|
2909
|
+
firstInsideIndex,
|
|
2910
|
+
lastInsideIndex,
|
|
2911
|
+
labelBounds,
|
|
2912
|
+
paddingBuffer,
|
|
2913
|
+
detourCount
|
|
2914
|
+
}) => {
|
|
2915
|
+
if (firstInsideIndex <= 0 || lastInsideIndex >= initialTrace.tracePath.length - 1) {
|
|
2916
|
+
return [];
|
|
2917
|
+
}
|
|
2918
|
+
const entryPoint = initialTrace.tracePath[firstInsideIndex - 1];
|
|
2919
|
+
const exitPoint = initialTrace.tracePath[lastInsideIndex + 1];
|
|
2920
|
+
const pathToEntry = initialTrace.tracePath.slice(0, firstInsideIndex);
|
|
2921
|
+
const pathFromExit = initialTrace.tracePath.slice(lastInsideIndex + 1);
|
|
2922
|
+
const allCandidateDetours = [];
|
|
2923
|
+
if (entryPoint.x !== exitPoint.x && entryPoint.y !== exitPoint.y) {
|
|
2924
|
+
allCandidateDetours.push([{ x: exitPoint.x, y: entryPoint.y }]);
|
|
2925
|
+
allCandidateDetours.push([{ x: entryPoint.x, y: exitPoint.y }]);
|
|
2926
|
+
} else if (entryPoint.x === exitPoint.x || entryPoint.y === exitPoint.y) {
|
|
2927
|
+
allCandidateDetours.push([]);
|
|
2928
|
+
}
|
|
2929
|
+
const buffer = paddingBuffer + detourCount * paddingBuffer;
|
|
2930
|
+
const leftX = labelBounds.minX - buffer;
|
|
2931
|
+
const rightX = labelBounds.maxX + buffer;
|
|
2932
|
+
const topY = labelBounds.maxY + buffer;
|
|
2933
|
+
const bottomY = labelBounds.minY - buffer;
|
|
2934
|
+
if ((entryPoint.x <= labelBounds.minX || exitPoint.x <= labelBounds.minX) && entryPoint.x < labelBounds.maxX && exitPoint.x < labelBounds.maxX) {
|
|
2935
|
+
allCandidateDetours.push([
|
|
2936
|
+
{ x: leftX, y: entryPoint.y },
|
|
2937
|
+
{ x: leftX, y: exitPoint.y }
|
|
2938
|
+
]);
|
|
2939
|
+
}
|
|
2940
|
+
if ((entryPoint.x >= labelBounds.maxX || exitPoint.x >= labelBounds.maxX) && entryPoint.x > labelBounds.minX && exitPoint.x > labelBounds.minX) {
|
|
2941
|
+
allCandidateDetours.push([
|
|
2942
|
+
{ x: rightX, y: entryPoint.y },
|
|
2943
|
+
{ x: rightX, y: exitPoint.y }
|
|
2944
|
+
]);
|
|
2945
|
+
}
|
|
2946
|
+
if ((entryPoint.y >= labelBounds.maxY || exitPoint.y >= labelBounds.maxY) && entryPoint.y > labelBounds.minY && exitPoint.y > labelBounds.minY) {
|
|
2947
|
+
allCandidateDetours.push([
|
|
2948
|
+
{ x: entryPoint.x, y: topY },
|
|
2949
|
+
{ x: exitPoint.x, y: topY }
|
|
2950
|
+
]);
|
|
2951
|
+
}
|
|
2952
|
+
if ((entryPoint.y <= labelBounds.minY || exitPoint.y <= labelBounds.minY) && entryPoint.y < labelBounds.maxY && exitPoint.y < labelBounds.maxY) {
|
|
2953
|
+
allCandidateDetours.push([
|
|
2954
|
+
{ x: entryPoint.x, y: bottomY },
|
|
2955
|
+
{ x: exitPoint.x, y: bottomY }
|
|
2956
|
+
]);
|
|
2957
|
+
}
|
|
2958
|
+
return allCandidateDetours.map((detour) => [
|
|
2959
|
+
...pathToEntry,
|
|
2960
|
+
...detour,
|
|
2961
|
+
...pathFromExit
|
|
2962
|
+
]);
|
|
2963
|
+
};
|
|
2964
|
+
|
|
2965
|
+
// lib/solvers/TraceLabelOverlapAvoidanceSolver/tryFourPointDetour.ts
|
|
2966
|
+
var generateFourPointDetourCandidates = ({
|
|
2967
|
+
initialTrace,
|
|
2968
|
+
label,
|
|
2969
|
+
labelBounds,
|
|
2970
|
+
paddingBuffer,
|
|
2971
|
+
detourCount
|
|
2972
|
+
}) => {
|
|
2973
|
+
let collidingSegIndex = -1;
|
|
2974
|
+
for (let i = 0; i < initialTrace.tracePath.length - 1; i++) {
|
|
2975
|
+
if (segmentIntersectsRect(
|
|
2976
|
+
initialTrace.tracePath[i],
|
|
2977
|
+
initialTrace.tracePath[i + 1],
|
|
2978
|
+
labelBounds
|
|
2979
|
+
)) {
|
|
2980
|
+
collidingSegIndex = i;
|
|
2981
|
+
break;
|
|
2982
|
+
}
|
|
2983
|
+
}
|
|
2984
|
+
if (collidingSegIndex === -1) return [];
|
|
2985
|
+
const pA = initialTrace.tracePath[collidingSegIndex];
|
|
2986
|
+
const pB = initialTrace.tracePath[collidingSegIndex + 1];
|
|
2987
|
+
if (!pA || !pB) return [];
|
|
2988
|
+
const candidateDetours = [];
|
|
2989
|
+
const paddedLabelBounds = getRectBounds(
|
|
2990
|
+
label.center,
|
|
2991
|
+
label.width,
|
|
2992
|
+
label.height
|
|
2993
|
+
);
|
|
2994
|
+
const effectivePadding = paddingBuffer + detourCount * paddingBuffer;
|
|
2995
|
+
if (isVertical(pA, pB)) {
|
|
2996
|
+
const xCandidates = [
|
|
2997
|
+
paddedLabelBounds.maxX + effectivePadding,
|
|
2998
|
+
paddedLabelBounds.minX - effectivePadding
|
|
2999
|
+
];
|
|
3000
|
+
for (const newX of xCandidates) {
|
|
3001
|
+
candidateDetours.push(
|
|
3002
|
+
pB.y > pA.y ? [
|
|
3003
|
+
{ x: pA.x, y: paddedLabelBounds.minY - effectivePadding },
|
|
3004
|
+
{ x: newX, y: paddedLabelBounds.minY - effectivePadding },
|
|
3005
|
+
{ x: newX, y: paddedLabelBounds.maxY + effectivePadding },
|
|
3006
|
+
{ x: pB.x, y: paddedLabelBounds.maxY + effectivePadding }
|
|
3007
|
+
] : [
|
|
3008
|
+
{ x: pA.x, y: paddedLabelBounds.maxY + effectivePadding },
|
|
3009
|
+
{ x: newX, y: paddedLabelBounds.maxY + effectivePadding },
|
|
3010
|
+
{ x: newX, y: paddedLabelBounds.minY - effectivePadding },
|
|
3011
|
+
{ x: pB.x, y: paddedLabelBounds.minY - effectivePadding }
|
|
3012
|
+
]
|
|
3013
|
+
);
|
|
3014
|
+
}
|
|
3015
|
+
} else {
|
|
3016
|
+
const yCandidates = [
|
|
3017
|
+
paddedLabelBounds.maxY + effectivePadding,
|
|
3018
|
+
paddedLabelBounds.minY - effectivePadding
|
|
3019
|
+
];
|
|
3020
|
+
for (const newY of yCandidates) {
|
|
3021
|
+
candidateDetours.push(
|
|
3022
|
+
pB.x > pA.x ? [
|
|
3023
|
+
{ x: paddedLabelBounds.minX - effectivePadding, y: pA.y },
|
|
3024
|
+
{ x: paddedLabelBounds.minX - effectivePadding, y: newY },
|
|
3025
|
+
{ x: paddedLabelBounds.maxX + effectivePadding, y: newY },
|
|
3026
|
+
{ x: paddedLabelBounds.maxX + effectivePadding, y: pB.y }
|
|
3027
|
+
] : [
|
|
3028
|
+
{ x: paddedLabelBounds.maxX + effectivePadding, y: pA.y },
|
|
3029
|
+
{ x: paddedLabelBounds.maxX + effectivePadding, y: newY },
|
|
3030
|
+
{ x: paddedLabelBounds.minX - effectivePadding, y: newY },
|
|
3031
|
+
{ x: paddedLabelBounds.minX - effectivePadding, y: pB.y }
|
|
3032
|
+
]
|
|
3033
|
+
);
|
|
3034
|
+
}
|
|
2864
3035
|
}
|
|
3036
|
+
return candidateDetours.map((detourPoints) => [
|
|
3037
|
+
...initialTrace.tracePath.slice(0, collidingSegIndex + 1),
|
|
3038
|
+
...detourPoints,
|
|
3039
|
+
...initialTrace.tracePath.slice(collidingSegIndex + 1)
|
|
3040
|
+
]);
|
|
2865
3041
|
};
|
|
2866
3042
|
|
|
2867
|
-
// lib/solvers/TraceLabelOverlapAvoidanceSolver/
|
|
2868
|
-
var
|
|
3043
|
+
// lib/solvers/TraceLabelOverlapAvoidanceSolver/rerouteCollidingTrace.ts
|
|
3044
|
+
var generateRerouteCandidates = ({
|
|
3045
|
+
trace,
|
|
3046
|
+
label,
|
|
3047
|
+
paddingBuffer,
|
|
3048
|
+
detourCount
|
|
3049
|
+
}) => {
|
|
3050
|
+
const initialTrace = { ...trace, tracePath: simplifyPath(trace.tracePath) };
|
|
3051
|
+
if (trace.globalConnNetId === label.globalConnNetId) {
|
|
3052
|
+
return [initialTrace.tracePath];
|
|
3053
|
+
}
|
|
3054
|
+
const labelPadding = paddingBuffer;
|
|
3055
|
+
const labelBoundsRaw = getRectBounds(label.center, label.width, label.height);
|
|
3056
|
+
const labelBounds = {
|
|
3057
|
+
minX: labelBoundsRaw.minX - labelPadding,
|
|
3058
|
+
minY: labelBoundsRaw.minY - labelPadding,
|
|
3059
|
+
maxX: labelBoundsRaw.maxX + labelPadding,
|
|
3060
|
+
maxY: labelBoundsRaw.maxY + labelPadding,
|
|
3061
|
+
chipId: `netlabel-${label.netId}`
|
|
3062
|
+
};
|
|
3063
|
+
const fourPointCandidates = generateFourPointDetourCandidates({
|
|
3064
|
+
initialTrace,
|
|
3065
|
+
label,
|
|
3066
|
+
labelBounds,
|
|
3067
|
+
paddingBuffer,
|
|
3068
|
+
detourCount
|
|
3069
|
+
});
|
|
3070
|
+
const { firstInsideIndex, lastInsideIndex } = findTraceViolationZone(
|
|
3071
|
+
initialTrace.tracePath,
|
|
3072
|
+
labelBounds
|
|
3073
|
+
);
|
|
3074
|
+
const snipReconnectCandidates = generateSnipAndReconnectCandidates({
|
|
3075
|
+
initialTrace,
|
|
3076
|
+
firstInsideIndex,
|
|
3077
|
+
lastInsideIndex,
|
|
3078
|
+
labelBounds,
|
|
3079
|
+
paddingBuffer,
|
|
3080
|
+
detourCount
|
|
3081
|
+
});
|
|
3082
|
+
return [...fourPointCandidates, ...snipReconnectCandidates];
|
|
3083
|
+
};
|
|
3084
|
+
|
|
3085
|
+
// lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/SingleOverlapSolver/SingleOverlapSolver.ts
|
|
3086
|
+
var SingleOverlapSolver = class extends BaseSolver {
|
|
3087
|
+
queuedCandidatePaths;
|
|
3088
|
+
solvedTracePath = null;
|
|
3089
|
+
initialTrace;
|
|
2869
3090
|
problem;
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
netLabelPlacements;
|
|
2873
|
-
updatedTraces;
|
|
2874
|
-
mergedLabelNetIdMap;
|
|
2875
|
-
detourCountByLabel;
|
|
2876
|
-
PADDING_BUFFER = 0.1;
|
|
3091
|
+
obstacles;
|
|
3092
|
+
label;
|
|
2877
3093
|
constructor(solverInput) {
|
|
2878
3094
|
super();
|
|
2879
|
-
this.
|
|
2880
|
-
this.
|
|
2881
|
-
this.
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
3095
|
+
this.initialTrace = solverInput.trace;
|
|
3096
|
+
this.problem = solverInput.problem;
|
|
3097
|
+
this.label = solverInput.label;
|
|
3098
|
+
const candidates = generateRerouteCandidates({
|
|
3099
|
+
...solverInput
|
|
3100
|
+
});
|
|
3101
|
+
const getPathLength = (pts) => {
|
|
3102
|
+
let len = 0;
|
|
3103
|
+
for (let i = 0; i < pts.length - 1; i++) {
|
|
3104
|
+
const dx = pts[i + 1].x - pts[i].x;
|
|
3105
|
+
const dy = pts[i + 1].y - pts[i].y;
|
|
3106
|
+
len += Math.sqrt(dx * dx + dy * dy);
|
|
3107
|
+
}
|
|
3108
|
+
return len;
|
|
3109
|
+
};
|
|
3110
|
+
this.queuedCandidatePaths = candidates.sort(
|
|
3111
|
+
(a, b) => getPathLength(a) - getPathLength(b)
|
|
3112
|
+
);
|
|
3113
|
+
this.obstacles = getObstacleRects(this.problem);
|
|
3114
|
+
}
|
|
3115
|
+
_step() {
|
|
3116
|
+
if (this.queuedCandidatePaths.length === 0) {
|
|
3117
|
+
this.failed = true;
|
|
2888
3118
|
return;
|
|
2889
3119
|
}
|
|
2890
|
-
const
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
const key = `${chipId}-${p.orientation}`;
|
|
2896
|
-
if (!(key in labelGroups)) {
|
|
2897
|
-
labelGroups[key] = [];
|
|
2898
|
-
}
|
|
2899
|
-
labelGroups[key].push(p);
|
|
3120
|
+
const nextCandidatePath = this.queuedCandidatePaths.shift();
|
|
3121
|
+
const simplifiedPath = simplifyPath(nextCandidatePath);
|
|
3122
|
+
if (!isPathCollidingWithObstacles(simplifiedPath, this.obstacles)) {
|
|
3123
|
+
this.solvedTracePath = simplifiedPath;
|
|
3124
|
+
this.solved = true;
|
|
2900
3125
|
}
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
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
|
-
]
|
|
3126
|
+
}
|
|
3127
|
+
visualize() {
|
|
3128
|
+
const graphics = visualizeInputProblem(this.problem, {
|
|
3129
|
+
chipAlpha: 0.1,
|
|
3130
|
+
connectionAlpha: 0.1
|
|
3131
|
+
});
|
|
3132
|
+
if (!graphics.lines) graphics.lines = [];
|
|
3133
|
+
if (!graphics.rects) graphics.rects = [];
|
|
3134
|
+
graphics.lines.push({
|
|
3135
|
+
points: this.initialTrace.tracePath,
|
|
3136
|
+
strokeColor: "red",
|
|
3137
|
+
strokeDash: "4 4"
|
|
3138
|
+
});
|
|
3139
|
+
graphics.rects.push({
|
|
3140
|
+
center: this.label.center,
|
|
3141
|
+
width: this.label.width,
|
|
3142
|
+
height: this.label.height,
|
|
3143
|
+
fill: "rgba(255, 0, 0, 0.2)"
|
|
3144
|
+
});
|
|
3145
|
+
if (this.queuedCandidatePaths.length > 0) {
|
|
3146
|
+
graphics.lines.push({
|
|
3147
|
+
points: this.queuedCandidatePaths[0],
|
|
3148
|
+
strokeColor: "orange"
|
|
2931
3149
|
});
|
|
2932
3150
|
}
|
|
2933
|
-
this.
|
|
3151
|
+
if (this.solvedTracePath) {
|
|
3152
|
+
graphics.lines.push({
|
|
3153
|
+
points: this.solvedTracePath,
|
|
3154
|
+
strokeColor: "green"
|
|
3155
|
+
});
|
|
3156
|
+
}
|
|
3157
|
+
return graphics;
|
|
3158
|
+
}
|
|
3159
|
+
};
|
|
3160
|
+
|
|
3161
|
+
// lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/OverlapAvoidanceStepSolver/OverlapAvoidanceStepSolver.ts
|
|
3162
|
+
var OverlapAvoidanceStepSolver = class extends BaseSolver {
|
|
3163
|
+
inputProblem;
|
|
3164
|
+
netLabelPlacements;
|
|
3165
|
+
mergedLabelNetIdMap;
|
|
3166
|
+
allTraces;
|
|
3167
|
+
modifiedTraces = [];
|
|
3168
|
+
detourCountByLabel = {};
|
|
3169
|
+
PADDING_BUFFER = 0.1;
|
|
3170
|
+
activeSubSolver = null;
|
|
3171
|
+
overlapQueue = [];
|
|
3172
|
+
recentlyFailed = /* @__PURE__ */ new Set();
|
|
3173
|
+
constructor(solverInput) {
|
|
3174
|
+
super();
|
|
3175
|
+
this.inputProblem = solverInput.inputProblem;
|
|
3176
|
+
this.netLabelPlacements = solverInput.netLabelPlacements;
|
|
3177
|
+
this.mergedLabelNetIdMap = solverInput.mergedLabelNetIdMap;
|
|
3178
|
+
this.allTraces = [...solverInput.traces];
|
|
2934
3179
|
}
|
|
2935
3180
|
_step() {
|
|
2936
|
-
if (
|
|
2937
|
-
this.
|
|
3181
|
+
if (this.activeSubSolver) {
|
|
3182
|
+
this.activeSubSolver.step();
|
|
3183
|
+
if (this.activeSubSolver.solved) {
|
|
3184
|
+
const solvedPath = this.activeSubSolver.solvedTracePath;
|
|
3185
|
+
if (solvedPath) {
|
|
3186
|
+
const traceIndex = this.allTraces.findIndex(
|
|
3187
|
+
(t) => t.mspPairId === this.activeSubSolver.initialTrace.mspPairId
|
|
3188
|
+
);
|
|
3189
|
+
if (traceIndex !== -1) {
|
|
3190
|
+
this.allTraces[traceIndex].tracePath = solvedPath;
|
|
3191
|
+
this.modifiedTraces.push(this.allTraces[traceIndex]);
|
|
3192
|
+
}
|
|
3193
|
+
}
|
|
3194
|
+
this.activeSubSolver = null;
|
|
3195
|
+
this.recentlyFailed.clear();
|
|
3196
|
+
} else if (this.activeSubSolver.failed) {
|
|
3197
|
+
const overlapId = `${this.activeSubSolver.initialTrace.mspPairId}-${this.activeSubSolver.label.globalConnNetId}`;
|
|
3198
|
+
this.recentlyFailed.add(overlapId);
|
|
3199
|
+
this.activeSubSolver = null;
|
|
3200
|
+
}
|
|
2938
3201
|
return;
|
|
2939
3202
|
}
|
|
2940
|
-
this.detourCountByLabel = {};
|
|
2941
3203
|
const overlaps = detectTraceLabelOverlap(
|
|
2942
|
-
this.
|
|
2943
|
-
this.
|
|
2944
|
-
)
|
|
2945
|
-
if (overlaps.length === 0) {
|
|
2946
|
-
this.solved = true;
|
|
2947
|
-
return;
|
|
2948
|
-
}
|
|
2949
|
-
const unfriendlyOverlaps = overlaps.filter((o) => {
|
|
3204
|
+
this.allTraces,
|
|
3205
|
+
this.netLabelPlacements
|
|
3206
|
+
).filter((o) => {
|
|
2950
3207
|
const originalNetIds = this.mergedLabelNetIdMap[o.label.globalConnNetId];
|
|
2951
3208
|
if (originalNetIds) {
|
|
2952
3209
|
return !originalNetIds.has(o.trace.globalConnNetId);
|
|
2953
3210
|
}
|
|
2954
3211
|
return o.trace.globalConnNetId !== o.label.globalConnNetId;
|
|
2955
3212
|
});
|
|
2956
|
-
if (
|
|
3213
|
+
if (overlaps.length === 0) {
|
|
2957
3214
|
this.solved = true;
|
|
2958
3215
|
return;
|
|
2959
3216
|
}
|
|
2960
|
-
const
|
|
2961
|
-
|
|
2962
|
-
|
|
3217
|
+
const nonFailedOverlaps = overlaps.filter((o) => {
|
|
3218
|
+
const overlapId = `${o.trace.mspPairId}-${o.label.globalConnNetId}`;
|
|
3219
|
+
return !this.recentlyFailed.has(overlapId);
|
|
3220
|
+
});
|
|
3221
|
+
if (nonFailedOverlaps.length === 0) {
|
|
3222
|
+
this.solved = true;
|
|
3223
|
+
return;
|
|
2963
3224
|
}
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
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) {
|
|
3225
|
+
this.overlapQueue = nonFailedOverlaps;
|
|
3226
|
+
const nextOverlap = this.overlapQueue.shift();
|
|
3227
|
+
if (nextOverlap) {
|
|
3228
|
+
const traceToFix = this.allTraces.find(
|
|
3229
|
+
(t) => t.mspPairId === nextOverlap.trace.mspPairId
|
|
3230
|
+
);
|
|
3231
|
+
if (traceToFix) {
|
|
3232
|
+
const labelId = nextOverlap.label.globalConnNetId;
|
|
3233
|
+
const detourCount = this.detourCountByLabel[labelId] || 0;
|
|
2980
3234
|
this.detourCountByLabel[labelId] = detourCount + 1;
|
|
3235
|
+
this.activeSubSolver = new SingleOverlapSolver({
|
|
3236
|
+
trace: traceToFix,
|
|
3237
|
+
label: nextOverlap.label,
|
|
3238
|
+
problem: this.inputProblem,
|
|
3239
|
+
paddingBuffer: this.PADDING_BUFFER,
|
|
3240
|
+
detourCount
|
|
3241
|
+
});
|
|
2981
3242
|
}
|
|
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
3243
|
}
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3244
|
+
}
|
|
3245
|
+
getOutput() {
|
|
3246
|
+
return {
|
|
3247
|
+
allTraces: this.allTraces,
|
|
3248
|
+
modifiedTraces: this.modifiedTraces
|
|
3249
|
+
};
|
|
3250
|
+
}
|
|
3251
|
+
visualize() {
|
|
3252
|
+
if (this.activeSubSolver) {
|
|
3253
|
+
return this.activeSubSolver.visualize();
|
|
3004
3254
|
}
|
|
3005
|
-
const
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3255
|
+
const graphics = visualizeInputProblem(this.inputProblem);
|
|
3256
|
+
if (!graphics.lines) graphics.lines = [];
|
|
3257
|
+
for (const trace of this.allTraces) {
|
|
3258
|
+
graphics.lines.push({
|
|
3259
|
+
points: trace.tracePath,
|
|
3260
|
+
strokeColor: "purple"
|
|
3261
|
+
});
|
|
3262
|
+
}
|
|
3263
|
+
return graphics;
|
|
3264
|
+
}
|
|
3265
|
+
};
|
|
3266
|
+
|
|
3267
|
+
// lib/solvers/TraceLabelOverlapAvoidanceSolver/TraceLabelOverlapAvoidanceSolver.ts
|
|
3268
|
+
var TraceLabelOverlapAvoidanceSolver = class extends BaseSolver {
|
|
3269
|
+
inputProblem;
|
|
3270
|
+
traces;
|
|
3271
|
+
netLabelPlacements;
|
|
3272
|
+
// sub-solver instances
|
|
3273
|
+
labelMergingSolver;
|
|
3274
|
+
overlapAvoidanceSolver;
|
|
3275
|
+
traceCleanupSolver;
|
|
3276
|
+
pipelineStepIndex = 0;
|
|
3277
|
+
constructor(solverInput) {
|
|
3278
|
+
super();
|
|
3279
|
+
this.inputProblem = solverInput.inputProblem;
|
|
3280
|
+
this.traces = solverInput.traces;
|
|
3281
|
+
this.netLabelPlacements = solverInput.netLabelPlacements;
|
|
3282
|
+
}
|
|
3283
|
+
_step() {
|
|
3284
|
+
if (this.activeSubSolver) {
|
|
3285
|
+
this.activeSubSolver.step();
|
|
3286
|
+
if (this.activeSubSolver.solved) {
|
|
3287
|
+
this.activeSubSolver = null;
|
|
3288
|
+
this.pipelineStepIndex++;
|
|
3289
|
+
} else if (this.activeSubSolver.failed) {
|
|
3290
|
+
this.failed = true;
|
|
3291
|
+
this.activeSubSolver = null;
|
|
3292
|
+
}
|
|
3293
|
+
return;
|
|
3294
|
+
}
|
|
3295
|
+
switch (this.pipelineStepIndex) {
|
|
3296
|
+
case 0:
|
|
3297
|
+
this.labelMergingSolver = new MergedNetLabelObstacleSolver({
|
|
3298
|
+
netLabelPlacements: this.netLabelPlacements,
|
|
3299
|
+
inputProblem: this.inputProblem,
|
|
3300
|
+
traces: this.traces
|
|
3301
|
+
});
|
|
3302
|
+
this.activeSubSolver = this.labelMergingSolver;
|
|
3303
|
+
break;
|
|
3304
|
+
case 1:
|
|
3305
|
+
this.overlapAvoidanceSolver = new OverlapAvoidanceStepSolver({
|
|
3306
|
+
inputProblem: this.inputProblem,
|
|
3307
|
+
traces: this.traces,
|
|
3308
|
+
netLabelPlacements: this.labelMergingSolver.getOutput().netLabelPlacements,
|
|
3309
|
+
mergedLabelNetIdMap: this.labelMergingSolver.getOutput().mergedLabelNetIdMap
|
|
3310
|
+
});
|
|
3311
|
+
this.activeSubSolver = this.overlapAvoidanceSolver;
|
|
3312
|
+
break;
|
|
3313
|
+
case 2:
|
|
3314
|
+
this.traceCleanupSolver = new TraceCleanupSolver({
|
|
3315
|
+
inputProblem: this.inputProblem,
|
|
3316
|
+
allTraces: this.overlapAvoidanceSolver.getOutput().allTraces,
|
|
3317
|
+
targetTraceIds: new Set(
|
|
3318
|
+
this.overlapAvoidanceSolver.getOutput().modifiedTraces.map(
|
|
3319
|
+
(t) => t.mspPairId
|
|
3320
|
+
)
|
|
3321
|
+
),
|
|
3322
|
+
allLabelPlacements: this.labelMergingSolver.getOutput().netLabelPlacements,
|
|
3323
|
+
mergedLabelNetIdMap: this.labelMergingSolver.getOutput().mergedLabelNetIdMap,
|
|
3324
|
+
paddingBuffer: 0.01
|
|
3325
|
+
});
|
|
3326
|
+
this.activeSubSolver = this.traceCleanupSolver;
|
|
3327
|
+
break;
|
|
3328
|
+
default:
|
|
3329
|
+
this.solved = true;
|
|
3330
|
+
break;
|
|
3014
3331
|
}
|
|
3015
|
-
this.solved = true;
|
|
3016
3332
|
}
|
|
3017
3333
|
getOutput() {
|
|
3018
3334
|
return {
|
|
3019
|
-
traces: this.
|
|
3020
|
-
netLabelPlacements: this.netLabelPlacements
|
|
3335
|
+
traces: this.traceCleanupSolver?.getOutput().traces ?? this.traces,
|
|
3336
|
+
netLabelPlacements: this.labelMergingSolver?.getOutput().netLabelPlacements ?? this.netLabelPlacements
|
|
3021
3337
|
};
|
|
3022
3338
|
}
|
|
3023
3339
|
visualize() {
|
|
3024
|
-
|
|
3340
|
+
if (this.activeSubSolver) {
|
|
3341
|
+
return this.activeSubSolver.visualize();
|
|
3342
|
+
}
|
|
3343
|
+
const graphics = visualizeInputProblem(this.inputProblem);
|
|
3025
3344
|
if (!graphics.lines) graphics.lines = [];
|
|
3026
|
-
if (!graphics.circles) graphics.circles = [];
|
|
3027
|
-
if (!graphics.texts) graphics.texts = [];
|
|
3028
3345
|
if (!graphics.rects) graphics.rects = [];
|
|
3029
|
-
|
|
3346
|
+
const output = this.getOutput();
|
|
3347
|
+
for (const trace of output.traces) {
|
|
3030
3348
|
graphics.lines.push({
|
|
3031
3349
|
points: trace.tracePath,
|
|
3032
3350
|
strokeColor: "purple"
|
|
3033
3351
|
});
|
|
3034
3352
|
}
|
|
3035
|
-
for (const
|
|
3353
|
+
for (const label of output.netLabelPlacements) {
|
|
3354
|
+
const color = getColorFromString(label.globalConnNetId, 0.3);
|
|
3036
3355
|
graphics.rects.push({
|
|
3037
|
-
center:
|
|
3038
|
-
width:
|
|
3039
|
-
height:
|
|
3040
|
-
fill:
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
x: p.anchorPoint.x,
|
|
3044
|
-
y: p.anchorPoint.y,
|
|
3045
|
-
color: getColorFromString(p.globalConnNetId, 0.9)
|
|
3356
|
+
center: label.center,
|
|
3357
|
+
width: label.width,
|
|
3358
|
+
height: label.height,
|
|
3359
|
+
fill: color,
|
|
3360
|
+
stroke: color.replace("0.3", "1"),
|
|
3361
|
+
label: label.globalConnNetId
|
|
3046
3362
|
});
|
|
3047
3363
|
}
|
|
3048
3364
|
return graphics;
|
|
@@ -3299,7 +3615,9 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
|
|
|
3299
3615
|
longDistancePairSolver;
|
|
3300
3616
|
traceOverlapShiftSolver;
|
|
3301
3617
|
netLabelPlacementSolver;
|
|
3618
|
+
labelMergingSolver;
|
|
3302
3619
|
traceLabelOverlapAvoidanceSolver;
|
|
3620
|
+
traceCleanupSolver;
|
|
3303
3621
|
startTimeOfPhase;
|
|
3304
3622
|
endTimeOfPhase;
|
|
3305
3623
|
timeSpentOnPhase;
|
|
@@ -3405,14 +3723,21 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
|
|
|
3405
3723
|
netLabelPlacements
|
|
3406
3724
|
}
|
|
3407
3725
|
];
|
|
3408
|
-
}
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3726
|
+
}
|
|
3727
|
+
),
|
|
3728
|
+
definePipelineStep(
|
|
3729
|
+
"netLabelPlacementSolver",
|
|
3730
|
+
NetLabelPlacementSolver,
|
|
3731
|
+
(instance) => {
|
|
3732
|
+
const traces = instance.traceCleanupSolver?.getOutput().traces ?? instance.traceLabelOverlapAvoidanceSolver.getOutput().traces;
|
|
3733
|
+
return [
|
|
3734
|
+
{
|
|
3735
|
+
inputProblem: instance.inputProblem,
|
|
3736
|
+
inputTraceMap: Object.fromEntries(
|
|
3737
|
+
traces.map((trace) => [trace.mspPairId, trace])
|
|
3738
|
+
)
|
|
3414
3739
|
}
|
|
3415
|
-
|
|
3740
|
+
];
|
|
3416
3741
|
}
|
|
3417
3742
|
)
|
|
3418
3743
|
];
|
|
@@ -3498,14 +3823,17 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
|
|
|
3498
3823
|
return viz;
|
|
3499
3824
|
})
|
|
3500
3825
|
];
|
|
3501
|
-
if (visualizations.length === 1)
|
|
3502
|
-
|
|
3826
|
+
if (visualizations.length === 1) {
|
|
3827
|
+
return visualizations[0];
|
|
3828
|
+
}
|
|
3829
|
+
const finalGraphics = {
|
|
3503
3830
|
points: visualizations.flatMap((v) => v.points || []),
|
|
3504
3831
|
rects: visualizations.flatMap((v) => v.rects || []),
|
|
3505
3832
|
lines: visualizations.flatMap((v) => v.lines || []),
|
|
3506
3833
|
circles: visualizations.flatMap((v) => v.circles || []),
|
|
3507
3834
|
texts: visualizations.flatMap((v) => v.texts || [])
|
|
3508
3835
|
};
|
|
3836
|
+
return finalGraphics;
|
|
3509
3837
|
}
|
|
3510
3838
|
/**
|
|
3511
3839
|
* A lightweight version of the visualize method that can be used to stream
|