@tscircuit/schematic-trace-solver 0.0.51 → 0.0.53
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 +65 -6
- package/dist/index.js +835 -80
- package/lib/solvers/AvailableNetOrientationSolver/AvailableNetOrientationSolver.ts +3 -39
- package/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts +16 -0
- package/lib/solvers/SchematicTracePipelineSolver/colorAvailableNetOrientationLabels.ts +54 -0
- package/lib/solvers/TraceAnchoredNetLabelOverlapSolver/TraceAnchoredNetLabelOverlapSolver.ts +331 -0
- package/lib/solvers/TraceAnchoredNetLabelOverlapSolver/candidates.ts +387 -0
- package/lib/solvers/TraceAnchoredNetLabelOverlapSolver/geometry.ts +254 -0
- package/lib/solvers/TraceAnchoredNetLabelOverlapSolver/types.ts +47 -0
- package/lib/solvers/TraceAnchoredNetLabelOverlapSolver/visualize.ts +159 -0
- package/package.json +1 -1
- package/tests/assets/example30.json +4 -1
- package/tests/examples/__snapshots__/example01.snap.svg +6 -3
- package/tests/examples/__snapshots__/example02.snap.svg +8 -4
- package/tests/examples/__snapshots__/example04.snap.svg +4 -2
- package/tests/examples/__snapshots__/example07.snap.svg +10 -5
- package/tests/examples/__snapshots__/example12.snap.svg +8 -4
- package/tests/examples/__snapshots__/example13.snap.svg +16 -8
- package/tests/examples/__snapshots__/example14.snap.svg +16 -8
- package/tests/examples/__snapshots__/example15.snap.svg +18 -9
- package/tests/examples/__snapshots__/example16.snap.svg +8 -4
- package/tests/examples/__snapshots__/example17.snap.svg +9 -5
- package/tests/examples/__snapshots__/example18.snap.svg +10 -5
- package/tests/examples/__snapshots__/example20.snap.svg +6 -3
- package/tests/examples/__snapshots__/example21.snap.svg +17 -12
- package/tests/examples/__snapshots__/example22.snap.svg +8 -4
- package/tests/examples/__snapshots__/example25.snap.svg +18 -9
- package/tests/examples/__snapshots__/example30.snap.svg +74 -62
- package/tests/examples/__snapshots__/example31.snap.svg +2 -1
- package/tests/fixtures/matcher.ts +21 -0
package/dist/index.js
CHANGED
|
@@ -271,19 +271,19 @@ var doesPairCrossRestrictedCenterLines = (params) => {
|
|
|
271
271
|
chipMap
|
|
272
272
|
});
|
|
273
273
|
if (restrictedCenterLines.size === 0) return false;
|
|
274
|
-
const
|
|
274
|
+
const EPS9 = 1e-9;
|
|
275
275
|
const crossesSegment = (a, b) => {
|
|
276
276
|
for (const [, rcl] of restrictedCenterLines) {
|
|
277
277
|
if (rcl.axes.has("x") && typeof rcl.x === "number") {
|
|
278
|
-
if ((a.x - rcl.x) * (b.x - rcl.x) < -
|
|
278
|
+
if ((a.x - rcl.x) * (b.x - rcl.x) < -EPS9) return true;
|
|
279
279
|
}
|
|
280
280
|
if (rcl.axes.has("y") && typeof rcl.y === "number") {
|
|
281
|
-
if ((a.y - rcl.y) * (b.y - rcl.y) < -
|
|
281
|
+
if ((a.y - rcl.y) * (b.y - rcl.y) < -EPS9) return true;
|
|
282
282
|
}
|
|
283
283
|
}
|
|
284
284
|
return false;
|
|
285
285
|
};
|
|
286
|
-
if (Math.abs(p1.x - p2.x) <
|
|
286
|
+
if (Math.abs(p1.x - p2.x) < EPS9 || Math.abs(p1.y - p2.y) < EPS9) {
|
|
287
287
|
return crossesSegment({ x: p1.x, y: p1.y }, { x: p2.x, y: p2.y });
|
|
288
288
|
}
|
|
289
289
|
const elbowHV = { x: p2.x, y: p1.y };
|
|
@@ -810,8 +810,8 @@ var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
|
|
|
810
810
|
if (!collision) {
|
|
811
811
|
const first = path[0];
|
|
812
812
|
const last = path[path.length - 1];
|
|
813
|
-
const
|
|
814
|
-
const samePoint = (p, q) => Math.abs(p.x - q.x) <
|
|
813
|
+
const EPS9 = 1e-9;
|
|
814
|
+
const samePoint = (p, q) => Math.abs(p.x - q.x) < EPS9 && Math.abs(p.y - q.y) < EPS9;
|
|
815
815
|
if (samePoint(first, { x: PA.x, y: PA.y }) && samePoint(last, { x: PB.x, y: PB.y })) {
|
|
816
816
|
this.solvedTracePath = path;
|
|
817
817
|
this.solved = true;
|
|
@@ -1010,17 +1010,17 @@ var applyJogToTerminalSegment = ({
|
|
|
1010
1010
|
segmentIndex: si,
|
|
1011
1011
|
offset,
|
|
1012
1012
|
JOG_SIZE,
|
|
1013
|
-
EPS:
|
|
1013
|
+
EPS: EPS9 = 1e-6
|
|
1014
1014
|
}) => {
|
|
1015
1015
|
if (si !== 0 && si !== pts.length - 2) return;
|
|
1016
1016
|
const start = pts[si];
|
|
1017
1017
|
const end = pts[si + 1];
|
|
1018
|
-
const
|
|
1019
|
-
const
|
|
1020
|
-
if (!
|
|
1021
|
-
const segDir =
|
|
1018
|
+
const isVertical4 = Math.abs(start.x - end.x) < EPS9;
|
|
1019
|
+
const isHorizontal3 = Math.abs(start.y - end.y) < EPS9;
|
|
1020
|
+
if (!isVertical4 && !isHorizontal3) return;
|
|
1021
|
+
const segDir = isVertical4 ? end.y > start.y ? 1 : -1 : end.x > start.x ? 1 : -1;
|
|
1022
1022
|
if (si === 0) {
|
|
1023
|
-
if (
|
|
1023
|
+
if (isVertical4) {
|
|
1024
1024
|
const jogY = start.y + segDir * JOG_SIZE;
|
|
1025
1025
|
pts.splice(
|
|
1026
1026
|
1,
|
|
@@ -1040,7 +1040,7 @@ var applyJogToTerminalSegment = ({
|
|
|
1040
1040
|
);
|
|
1041
1041
|
}
|
|
1042
1042
|
} else {
|
|
1043
|
-
if (
|
|
1043
|
+
if (isVertical4) {
|
|
1044
1044
|
const jogY = end.y - segDir * JOG_SIZE;
|
|
1045
1045
|
pts.splice(
|
|
1046
1046
|
si,
|
|
@@ -1083,13 +1083,13 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
|
|
|
1083
1083
|
}
|
|
1084
1084
|
}
|
|
1085
1085
|
_step() {
|
|
1086
|
-
const
|
|
1086
|
+
const EPS9 = 1e-6;
|
|
1087
1087
|
const offsets = this.overlappingTraceSegments.map((_, idx) => {
|
|
1088
1088
|
const n = Math.floor(idx / 2) + 1;
|
|
1089
1089
|
const signed = idx % 2 === 0 ? -n : n;
|
|
1090
1090
|
return signed * this.SHIFT_DISTANCE;
|
|
1091
1091
|
});
|
|
1092
|
-
const eq = (a, b) => Math.abs(a - b) <
|
|
1092
|
+
const eq = (a, b) => Math.abs(a - b) < EPS9;
|
|
1093
1093
|
const samePoint = (p, q) => !!p && !!q && eq(p.x, q.x) && eq(p.y, q.y);
|
|
1094
1094
|
this.overlappingTraceSegments.forEach((group, gidx) => {
|
|
1095
1095
|
const offset = offsets[gidx];
|
|
@@ -1115,15 +1115,15 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
|
|
|
1115
1115
|
segmentIndex: si,
|
|
1116
1116
|
offset,
|
|
1117
1117
|
JOG_SIZE,
|
|
1118
|
-
EPS:
|
|
1118
|
+
EPS: EPS9
|
|
1119
1119
|
});
|
|
1120
1120
|
} else {
|
|
1121
1121
|
const start = pts[si];
|
|
1122
1122
|
const end = pts[si + 1];
|
|
1123
|
-
const
|
|
1124
|
-
const
|
|
1125
|
-
if (!
|
|
1126
|
-
if (
|
|
1123
|
+
const isVertical4 = Math.abs(start.x - end.x) < EPS9;
|
|
1124
|
+
const isHorizontal3 = Math.abs(start.y - end.y) < EPS9;
|
|
1125
|
+
if (!isVertical4 && !isHorizontal3) continue;
|
|
1126
|
+
if (isVertical4) {
|
|
1127
1127
|
start.x += offset;
|
|
1128
1128
|
end.x += offset;
|
|
1129
1129
|
} else {
|
|
@@ -1218,7 +1218,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
1218
1218
|
return islands;
|
|
1219
1219
|
}
|
|
1220
1220
|
findNextOverlapIssue() {
|
|
1221
|
-
const
|
|
1221
|
+
const EPS9 = 2e-3;
|
|
1222
1222
|
const netIds = Object.keys(this.traceNetIslands);
|
|
1223
1223
|
for (let i = 0; i < netIds.length; i++) {
|
|
1224
1224
|
for (let j = i + 1; j < netIds.length; j++) {
|
|
@@ -1236,7 +1236,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
1236
1236
|
const minB = Math.min(b1, b2);
|
|
1237
1237
|
const maxB = Math.max(b1, b2);
|
|
1238
1238
|
const overlap = Math.min(maxA, maxB) - Math.max(minA, minB);
|
|
1239
|
-
return overlap >
|
|
1239
|
+
return overlap > EPS9;
|
|
1240
1240
|
};
|
|
1241
1241
|
for (let pa = 0; pa < pathsA.length; pa++) {
|
|
1242
1242
|
const pathA = pathsA[pa];
|
|
@@ -1244,8 +1244,8 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
1244
1244
|
for (let sa = 0; sa < ptsA.length - 1; sa++) {
|
|
1245
1245
|
const a1 = ptsA[sa];
|
|
1246
1246
|
const a2 = ptsA[sa + 1];
|
|
1247
|
-
const aVert = Math.abs(a1.x - a2.x) <
|
|
1248
|
-
const aHorz = Math.abs(a1.y - a2.y) <
|
|
1247
|
+
const aVert = Math.abs(a1.x - a2.x) < EPS9;
|
|
1248
|
+
const aHorz = Math.abs(a1.y - a2.y) < EPS9;
|
|
1249
1249
|
if (!aVert && !aHorz) continue;
|
|
1250
1250
|
for (let pb = 0; pb < pathsB.length; pb++) {
|
|
1251
1251
|
const pathB = pathsB[pb];
|
|
@@ -1253,11 +1253,11 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
1253
1253
|
for (let sb = 0; sb < ptsB.length - 1; sb++) {
|
|
1254
1254
|
const b1 = ptsB[sb];
|
|
1255
1255
|
const b2 = ptsB[sb + 1];
|
|
1256
|
-
const bVert = Math.abs(b1.x - b2.x) <
|
|
1257
|
-
const bHorz = Math.abs(b1.y - b2.y) <
|
|
1256
|
+
const bVert = Math.abs(b1.x - b2.x) < EPS9;
|
|
1257
|
+
const bHorz = Math.abs(b1.y - b2.y) < EPS9;
|
|
1258
1258
|
if (!bVert && !bHorz) continue;
|
|
1259
1259
|
if (aVert && bVert) {
|
|
1260
|
-
if (Math.abs(a1.x - b1.x) <
|
|
1260
|
+
if (Math.abs(a1.x - b1.x) < EPS9) {
|
|
1261
1261
|
if (overlaps1D(a1.y, a2.y, b1.y, b2.y)) {
|
|
1262
1262
|
const keyA = `${pa}:${sa}`;
|
|
1263
1263
|
const keyB = `${pb}:${sb}`;
|
|
@@ -1278,7 +1278,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
1278
1278
|
}
|
|
1279
1279
|
}
|
|
1280
1280
|
} else if (aHorz && bHorz) {
|
|
1281
|
-
if (Math.abs(a1.y - b1.y) <
|
|
1281
|
+
if (Math.abs(a1.y - b1.y) < EPS9) {
|
|
1282
1282
|
if (overlaps1D(a1.x, a2.x, b1.x, b2.x)) {
|
|
1283
1283
|
const keyA = `${pa}:${sa}`;
|
|
1284
1284
|
const keyB = `${pb}:${sb}`;
|
|
@@ -1316,15 +1316,15 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
1316
1316
|
return null;
|
|
1317
1317
|
}
|
|
1318
1318
|
findNextDiagonalSegment() {
|
|
1319
|
-
const
|
|
1319
|
+
const EPS9 = 2e-3;
|
|
1320
1320
|
for (const mspPairId in this.correctedTraceMap) {
|
|
1321
1321
|
const tracePath = this.correctedTraceMap[mspPairId].tracePath;
|
|
1322
1322
|
for (let i = 0; i < tracePath.length - 1; i++) {
|
|
1323
1323
|
const p1 = tracePath[i];
|
|
1324
1324
|
const p2 = tracePath[i + 1];
|
|
1325
|
-
const
|
|
1326
|
-
const
|
|
1327
|
-
if (!
|
|
1325
|
+
const isHorizontal3 = Math.abs(p1.y - p2.y) < EPS9;
|
|
1326
|
+
const isVertical4 = Math.abs(p1.x - p2.x) < EPS9;
|
|
1327
|
+
if (!isHorizontal3 && !isVertical4) {
|
|
1328
1328
|
return { mspPairId, tracePath, i, p1, p2 };
|
|
1329
1329
|
}
|
|
1330
1330
|
}
|
|
@@ -1337,13 +1337,13 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
1337
1337
|
return false;
|
|
1338
1338
|
}
|
|
1339
1339
|
const { mspPairId, tracePath, i, p1, p2 } = diagonalInfo;
|
|
1340
|
-
const
|
|
1340
|
+
const EPS9 = 2e-3;
|
|
1341
1341
|
const p0 = i > 0 ? tracePath[i - 1] : null;
|
|
1342
1342
|
const p3 = i + 2 < tracePath.length ? tracePath[i + 2] : null;
|
|
1343
|
-
const prevIsVertical = p0 ? Math.abs(p0.x - p1.x) <
|
|
1344
|
-
const prevIsHorizontal = p0 ? Math.abs(p0.y - p1.y) <
|
|
1345
|
-
const nextIsVertical = p3 ? Math.abs(p2.x - p3.x) <
|
|
1346
|
-
const nextIsHorizontal = p3 ? Math.abs(p2.y - p3.y) <
|
|
1343
|
+
const prevIsVertical = p0 ? Math.abs(p0.x - p1.x) < EPS9 : false;
|
|
1344
|
+
const prevIsHorizontal = p0 ? Math.abs(p0.y - p1.y) < EPS9 : false;
|
|
1345
|
+
const nextIsVertical = p3 ? Math.abs(p2.x - p3.x) < EPS9 : false;
|
|
1346
|
+
const nextIsHorizontal = p3 ? Math.abs(p2.y - p3.y) < EPS9 : false;
|
|
1347
1347
|
const elbow1 = { x: p1.x, y: p2.y };
|
|
1348
1348
|
const elbow2 = { x: p2.x, y: p1.y };
|
|
1349
1349
|
let score1 = 0;
|
|
@@ -1507,24 +1507,24 @@ function getRectBounds(center, w, h) {
|
|
|
1507
1507
|
}
|
|
1508
1508
|
|
|
1509
1509
|
// lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/collisions.ts
|
|
1510
|
-
function segmentIntersectsRect2(p1, p2, rect,
|
|
1511
|
-
const isVert = Math.abs(p1.x - p2.x) <
|
|
1512
|
-
const isHorz = Math.abs(p1.y - p2.y) <
|
|
1510
|
+
function segmentIntersectsRect2(p1, p2, rect, EPS9 = 1e-9) {
|
|
1511
|
+
const isVert = Math.abs(p1.x - p2.x) < EPS9;
|
|
1512
|
+
const isHorz = Math.abs(p1.y - p2.y) < EPS9;
|
|
1513
1513
|
if (!isVert && !isHorz) return false;
|
|
1514
1514
|
if (isVert) {
|
|
1515
1515
|
const x = p1.x;
|
|
1516
|
-
if (x < rect.minX -
|
|
1516
|
+
if (x < rect.minX - EPS9 || x > rect.maxX + EPS9) return false;
|
|
1517
1517
|
const segMinY = Math.min(p1.y, p2.y);
|
|
1518
1518
|
const segMaxY = Math.max(p1.y, p2.y);
|
|
1519
1519
|
const overlap = Math.min(segMaxY, rect.maxY) - Math.max(segMinY, rect.minY);
|
|
1520
|
-
return overlap >
|
|
1520
|
+
return overlap > EPS9;
|
|
1521
1521
|
} else {
|
|
1522
1522
|
const y = p1.y;
|
|
1523
|
-
if (y < rect.minY -
|
|
1523
|
+
if (y < rect.minY - EPS9 || y > rect.maxY + EPS9) return false;
|
|
1524
1524
|
const segMinX = Math.min(p1.x, p2.x);
|
|
1525
1525
|
const segMaxX = Math.max(p1.x, p2.x);
|
|
1526
1526
|
const overlap = Math.min(segMaxX, rect.maxX) - Math.max(segMinX, rect.minX);
|
|
1527
|
-
return overlap >
|
|
1527
|
+
return overlap > EPS9;
|
|
1528
1528
|
}
|
|
1529
1529
|
}
|
|
1530
1530
|
function rectIntersectsAnyTrace(bounds, inputTraceMap, hostPathId, hostSegIndex) {
|
|
@@ -1902,14 +1902,14 @@ var SingleNetLabelPlacementSolver = class extends BaseSolver {
|
|
|
1902
1902
|
};
|
|
1903
1903
|
let bestCandidate = null;
|
|
1904
1904
|
let bestScore = -Infinity;
|
|
1905
|
-
const
|
|
1905
|
+
const EPS9 = 1e-6;
|
|
1906
1906
|
for (const curr of tracesToScan) {
|
|
1907
1907
|
const pts = curr.tracePath.slice();
|
|
1908
1908
|
for (let si = 0; si < pts.length - 1; si++) {
|
|
1909
1909
|
const a = pts[si];
|
|
1910
1910
|
const b = pts[si + 1];
|
|
1911
|
-
const isH = Math.abs(a.y - b.y) <
|
|
1912
|
-
const isV = Math.abs(a.x - b.x) <
|
|
1911
|
+
const isH = Math.abs(a.y - b.y) < EPS9;
|
|
1912
|
+
const isV = Math.abs(a.x - b.x) < EPS9;
|
|
1913
1913
|
if (!isH && !isV) continue;
|
|
1914
1914
|
const segmentAllowed = isH ? ["y+", "y-"] : ["x+", "x-"];
|
|
1915
1915
|
const candidateOrients = orientations.filter(
|
|
@@ -2273,6 +2273,44 @@ orientation: ${p.orientation}`
|
|
|
2273
2273
|
}
|
|
2274
2274
|
};
|
|
2275
2275
|
|
|
2276
|
+
// lib/solvers/SchematicTracePipelineSolver/colorAvailableNetOrientationLabels.ts
|
|
2277
|
+
var AVAILABLE_Y_PLUS_COLOR = "#93c5fd";
|
|
2278
|
+
var AVAILABLE_Y_MINUS_COLOR = "#fca5a5";
|
|
2279
|
+
var AVAILABLE_ORIENTATION_FILL_ALPHA = "66";
|
|
2280
|
+
var colorAvailableNetOrientationLabels = (graphicsObject, inputProblem) => {
|
|
2281
|
+
const availableOrientations = inputProblem.availableNetLabelOrientations;
|
|
2282
|
+
if (!availableOrientations) return;
|
|
2283
|
+
for (const rect of graphicsObject.rects ?? []) {
|
|
2284
|
+
const orientations = getAvailableOrientationsForRect(
|
|
2285
|
+
rect.label,
|
|
2286
|
+
availableOrientations
|
|
2287
|
+
);
|
|
2288
|
+
if (!orientations) continue;
|
|
2289
|
+
const hasYPlus = orientations.includes("y+");
|
|
2290
|
+
const hasYMinus = orientations.includes("y-");
|
|
2291
|
+
if (hasYPlus === hasYMinus) continue;
|
|
2292
|
+
const color = hasYPlus ? AVAILABLE_Y_PLUS_COLOR : AVAILABLE_Y_MINUS_COLOR;
|
|
2293
|
+
rect.fill = `${color}${AVAILABLE_ORIENTATION_FILL_ALPHA}`;
|
|
2294
|
+
rect.stroke = color;
|
|
2295
|
+
rect.color = color;
|
|
2296
|
+
}
|
|
2297
|
+
};
|
|
2298
|
+
var getAvailableOrientationsForRect = (label, availableOrientations) => {
|
|
2299
|
+
for (const netId of getNetIdsFromRectLabel(label)) {
|
|
2300
|
+
if (Object.hasOwn(availableOrientations, netId)) {
|
|
2301
|
+
return availableOrientations[netId];
|
|
2302
|
+
}
|
|
2303
|
+
}
|
|
2304
|
+
return void 0;
|
|
2305
|
+
};
|
|
2306
|
+
var getNetIdsFromRectLabel = (label) => {
|
|
2307
|
+
if (!label) return [];
|
|
2308
|
+
return [
|
|
2309
|
+
label.match(/^netId: (.+)$/m)?.[1],
|
|
2310
|
+
label.match(/^globalConnNetId: (.+)$/m)?.[1]
|
|
2311
|
+
].filter((netId) => Boolean(netId && netId !== "undefined"));
|
|
2312
|
+
};
|
|
2313
|
+
|
|
2276
2314
|
// lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/LabelMergingSolver/groupLabelsByChipAndOrientation.ts
|
|
2277
2315
|
var groupLabelsByChipAndOrientation = ({
|
|
2278
2316
|
labels,
|
|
@@ -4810,12 +4848,12 @@ var isPathCollidingWithChipInterior = (path, chipObstacles) => {
|
|
|
4810
4848
|
return false;
|
|
4811
4849
|
};
|
|
4812
4850
|
var segmentRunsAlongRectBoundary = (start, end, rect) => {
|
|
4813
|
-
const
|
|
4814
|
-
const
|
|
4815
|
-
if (
|
|
4851
|
+
const isVertical4 = Math.abs(start.x - end.x) < EPS5;
|
|
4852
|
+
const isHorizontal3 = Math.abs(start.y - end.y) < EPS5;
|
|
4853
|
+
if (isVertical4) {
|
|
4816
4854
|
return Math.abs(start.x - rect.minX) < EPS5 || Math.abs(start.x - rect.maxX) < EPS5;
|
|
4817
4855
|
}
|
|
4818
|
-
if (
|
|
4856
|
+
if (isHorizontal3) {
|
|
4819
4857
|
return Math.abs(start.y - rect.minY) < EPS5 || Math.abs(start.y - rect.maxY) < EPS5;
|
|
4820
4858
|
}
|
|
4821
4859
|
return false;
|
|
@@ -5641,16 +5679,15 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
5641
5679
|
getProcessableLabelIndices() {
|
|
5642
5680
|
const indices = [];
|
|
5643
5681
|
for (let i = 0; i < this.outputNetLabelPlacements.length; i++) {
|
|
5644
|
-
if (this.shouldProcessLabel(this.outputNetLabelPlacements[i]
|
|
5682
|
+
if (this.shouldProcessLabel(this.outputNetLabelPlacements[i])) {
|
|
5645
5683
|
indices.push(i);
|
|
5646
5684
|
}
|
|
5647
5685
|
}
|
|
5648
5686
|
return indices;
|
|
5649
5687
|
}
|
|
5650
|
-
shouldProcessLabel(label
|
|
5688
|
+
shouldProcessLabel(label) {
|
|
5651
5689
|
const orientations = this.getAvailableOrientations(label);
|
|
5652
|
-
|
|
5653
|
-
return !(orientations.includes(label.orientation) && this.isLabelPlacementValid(label, labelIndex));
|
|
5690
|
+
return orientations.length > 0 && !orientations.includes(label.orientation);
|
|
5654
5691
|
}
|
|
5655
5692
|
processLabel(labelIndex) {
|
|
5656
5693
|
const label = this.outputNetLabelPlacements[labelIndex];
|
|
@@ -5879,22 +5916,6 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
5879
5916
|
(connection) => connection.netId === label.netId
|
|
5880
5917
|
)?.netLabelWidth;
|
|
5881
5918
|
}
|
|
5882
|
-
isLabelPlacementValid(label, labelIndex) {
|
|
5883
|
-
return this.isCandidateValidWithOffset(
|
|
5884
|
-
{
|
|
5885
|
-
orientation: label.orientation,
|
|
5886
|
-
anchorPoint: label.anchorPoint,
|
|
5887
|
-
center: label.center,
|
|
5888
|
-
width: label.width,
|
|
5889
|
-
height: label.height
|
|
5890
|
-
},
|
|
5891
|
-
labelIndex
|
|
5892
|
-
);
|
|
5893
|
-
}
|
|
5894
|
-
isCandidateValidWithOffset(candidate, labelIndex) {
|
|
5895
|
-
const bounds = this.getOffsetCollisionBounds(candidate);
|
|
5896
|
-
return this.getBoundsStatus(bounds, labelIndex) === "valid";
|
|
5897
|
-
}
|
|
5898
5919
|
getCandidateStatus(candidate, label, labelIndex) {
|
|
5899
5920
|
const boundsStatus = this.getBoundsStatus(
|
|
5900
5921
|
getRectBounds(candidate.center, candidate.width, candidate.height),
|
|
@@ -5928,15 +5949,6 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
5928
5949
|
}
|
|
5929
5950
|
return "valid";
|
|
5930
5951
|
}
|
|
5931
|
-
getOffsetCollisionBounds(candidate) {
|
|
5932
|
-
const direction = dir(candidate.orientation);
|
|
5933
|
-
const collisionTestOffset = 1e-4;
|
|
5934
|
-
const testCenter = {
|
|
5935
|
-
x: candidate.center.x + direction.x * collisionTestOffset,
|
|
5936
|
-
y: candidate.center.y + direction.y * collisionTestOffset
|
|
5937
|
-
};
|
|
5938
|
-
return getRectBounds(testCenter, candidate.width, candidate.height);
|
|
5939
|
-
}
|
|
5940
5952
|
intersectsAnyOtherNetLabel(bounds, labelIndex) {
|
|
5941
5953
|
for (let i = 0; i < this.outputNetLabelPlacements.length; i++) {
|
|
5942
5954
|
if (i === labelIndex) continue;
|
|
@@ -6362,6 +6374,736 @@ var VccNetLabelCornerPlacementSolver = class extends BaseSolver {
|
|
|
6362
6374
|
}
|
|
6363
6375
|
};
|
|
6364
6376
|
|
|
6377
|
+
// lib/solvers/TraceAnchoredNetLabelOverlapSolver/geometry.ts
|
|
6378
|
+
var EPS8 = 1e-6;
|
|
6379
|
+
var TRACE_BOUNDARY_TOLERANCE2 = 1e-6;
|
|
6380
|
+
var getLabelBounds = (label) => getRectBounds(label.center, label.width, label.height);
|
|
6381
|
+
var rectsOverlap3 = (a, b) => Math.min(a.maxX, b.maxX) - Math.max(a.minX, b.minX) > EPS8 && Math.min(a.maxY, b.maxY) - Math.max(a.minY, b.minY) > EPS8;
|
|
6382
|
+
var rectsTouchOrOverlap = (a, b) => Math.min(a.maxX, b.maxX) - Math.max(a.minX, b.minX) >= -EPS8 && Math.min(a.maxY, b.maxY) - Math.max(a.minY, b.minY) >= -EPS8;
|
|
6383
|
+
var traceCrossesBoundsInterior2 = (bounds, traces) => {
|
|
6384
|
+
for (const trace of traces) {
|
|
6385
|
+
const points = trace.tracePath;
|
|
6386
|
+
for (let i = 0; i < points.length - 1; i++) {
|
|
6387
|
+
if (segmentCrossesBoundsInterior2(points[i], points[i + 1], bounds)) {
|
|
6388
|
+
return true;
|
|
6389
|
+
}
|
|
6390
|
+
}
|
|
6391
|
+
}
|
|
6392
|
+
return false;
|
|
6393
|
+
};
|
|
6394
|
+
var getTraceLocationsForPoint = (point, traces) => {
|
|
6395
|
+
const locations = [];
|
|
6396
|
+
for (const trace of traces) {
|
|
6397
|
+
if (getUniquePinCount2(trace.pinIds) < 2) continue;
|
|
6398
|
+
let pathDistance = 0;
|
|
6399
|
+
for (let i = 0; i < trace.tracePath.length - 1; i++) {
|
|
6400
|
+
const start = trace.tracePath[i];
|
|
6401
|
+
const end = trace.tracePath[i + 1];
|
|
6402
|
+
const segmentLength = getManhattanDistance(start, end);
|
|
6403
|
+
if (isPointOnSegment2(point, start, end)) {
|
|
6404
|
+
locations.push({
|
|
6405
|
+
trace,
|
|
6406
|
+
distance: pathDistance + getManhattanDistance(start, point)
|
|
6407
|
+
});
|
|
6408
|
+
}
|
|
6409
|
+
pathDistance += segmentLength;
|
|
6410
|
+
}
|
|
6411
|
+
}
|
|
6412
|
+
return locations;
|
|
6413
|
+
};
|
|
6414
|
+
var getTraceLength = (trace) => {
|
|
6415
|
+
let length = 0;
|
|
6416
|
+
for (let i = 0; i < trace.tracePath.length - 1; i++) {
|
|
6417
|
+
length += getManhattanDistance(trace.tracePath[i], trace.tracePath[i + 1]);
|
|
6418
|
+
}
|
|
6419
|
+
return length;
|
|
6420
|
+
};
|
|
6421
|
+
var getPointAtTraceDistance = (trace, distance3) => {
|
|
6422
|
+
let pathDistance = 0;
|
|
6423
|
+
for (let i = 0; i < trace.tracePath.length - 1; i++) {
|
|
6424
|
+
const start = trace.tracePath[i];
|
|
6425
|
+
const end = trace.tracePath[i + 1];
|
|
6426
|
+
const segmentLength = getManhattanDistance(start, end);
|
|
6427
|
+
const nextDistance = pathDistance + segmentLength;
|
|
6428
|
+
if (distance3 <= nextDistance + EPS8) {
|
|
6429
|
+
const offset = Math.max(
|
|
6430
|
+
0,
|
|
6431
|
+
Math.min(segmentLength, distance3 - pathDistance)
|
|
6432
|
+
);
|
|
6433
|
+
const direction = getSegmentDirection(start, end);
|
|
6434
|
+
return {
|
|
6435
|
+
x: start.x + direction.x * offset,
|
|
6436
|
+
y: start.y + direction.y * offset
|
|
6437
|
+
};
|
|
6438
|
+
}
|
|
6439
|
+
pathDistance = nextDistance;
|
|
6440
|
+
}
|
|
6441
|
+
return trace.tracePath[trace.tracePath.length - 1];
|
|
6442
|
+
};
|
|
6443
|
+
var getTraceVertexDistances = (trace) => {
|
|
6444
|
+
const distances = [];
|
|
6445
|
+
let pathDistance = 0;
|
|
6446
|
+
for (let i = 0; i < trace.tracePath.length; i++) {
|
|
6447
|
+
distances.push(pathDistance);
|
|
6448
|
+
const nextPoint = trace.tracePath[i + 1];
|
|
6449
|
+
if (nextPoint) {
|
|
6450
|
+
pathDistance += getManhattanDistance(trace.tracePath[i], nextPoint);
|
|
6451
|
+
}
|
|
6452
|
+
}
|
|
6453
|
+
return distances;
|
|
6454
|
+
};
|
|
6455
|
+
var getManhattanDistance = (a, b) => Math.abs(a.x - b.x) + Math.abs(a.y - b.y);
|
|
6456
|
+
var segmentCrossesBoundsInterior2 = (start, end, bounds) => {
|
|
6457
|
+
const interior = {
|
|
6458
|
+
minX: bounds.minX + TRACE_BOUNDARY_TOLERANCE2,
|
|
6459
|
+
minY: bounds.minY + TRACE_BOUNDARY_TOLERANCE2,
|
|
6460
|
+
maxX: bounds.maxX - TRACE_BOUNDARY_TOLERANCE2,
|
|
6461
|
+
maxY: bounds.maxY - TRACE_BOUNDARY_TOLERANCE2
|
|
6462
|
+
};
|
|
6463
|
+
if (interior.minX >= interior.maxX || interior.minY >= interior.maxY) {
|
|
6464
|
+
return false;
|
|
6465
|
+
}
|
|
6466
|
+
if (isVertical3(start, end)) {
|
|
6467
|
+
if (start.x <= interior.minX + EPS8 || start.x >= interior.maxX - EPS8) {
|
|
6468
|
+
return false;
|
|
6469
|
+
}
|
|
6470
|
+
return rangesOverlap2(
|
|
6471
|
+
Math.min(start.y, end.y),
|
|
6472
|
+
Math.max(start.y, end.y),
|
|
6473
|
+
interior.minY,
|
|
6474
|
+
interior.maxY
|
|
6475
|
+
);
|
|
6476
|
+
}
|
|
6477
|
+
if (isHorizontal2(start, end)) {
|
|
6478
|
+
if (start.y <= interior.minY + EPS8 || start.y >= interior.maxY - EPS8) {
|
|
6479
|
+
return false;
|
|
6480
|
+
}
|
|
6481
|
+
return rangesOverlap2(
|
|
6482
|
+
Math.min(start.x, end.x),
|
|
6483
|
+
Math.max(start.x, end.x),
|
|
6484
|
+
interior.minX,
|
|
6485
|
+
interior.maxX
|
|
6486
|
+
);
|
|
6487
|
+
}
|
|
6488
|
+
return false;
|
|
6489
|
+
};
|
|
6490
|
+
var isPointOnSegment2 = (point, start, end) => {
|
|
6491
|
+
if (isHorizontal2(start, end)) {
|
|
6492
|
+
return Math.abs(point.y - start.y) <= EPS8 && point.x >= Math.min(start.x, end.x) - EPS8 && point.x <= Math.max(start.x, end.x) + EPS8;
|
|
6493
|
+
}
|
|
6494
|
+
if (isVertical3(start, end)) {
|
|
6495
|
+
return Math.abs(point.x - start.x) <= EPS8 && point.y >= Math.min(start.y, end.y) - EPS8 && point.y <= Math.max(start.y, end.y) + EPS8;
|
|
6496
|
+
}
|
|
6497
|
+
return false;
|
|
6498
|
+
};
|
|
6499
|
+
var getSegmentDirection = (start, end) => ({
|
|
6500
|
+
x: isVertical3(start, end) ? 0 : Math.sign(end.x - start.x),
|
|
6501
|
+
y: isHorizontal2(start, end) ? 0 : Math.sign(end.y - start.y)
|
|
6502
|
+
});
|
|
6503
|
+
var isHorizontal2 = (start, end) => Math.abs(start.y - end.y) <= EPS8;
|
|
6504
|
+
var isVertical3 = (start, end) => Math.abs(start.x - end.x) <= EPS8;
|
|
6505
|
+
var rangesOverlap2 = (minA, maxA, minB, maxB) => Math.min(maxA, maxB) - Math.max(minA, minB) > EPS8;
|
|
6506
|
+
var getUniquePinCount2 = (pinIds) => new Set(pinIds).size;
|
|
6507
|
+
|
|
6508
|
+
// lib/solvers/TraceAnchoredNetLabelOverlapSolver/candidates.ts
|
|
6509
|
+
var CANDIDATE_STEP = 0.1;
|
|
6510
|
+
var generateCandidatesAlongTrace = (params) => {
|
|
6511
|
+
const { inputProblem, label, traceLocation } = params;
|
|
6512
|
+
const traceLength = getTraceLength(traceLocation.trace);
|
|
6513
|
+
const orientationConstraint = getOrientationConstraint(inputProblem, label);
|
|
6514
|
+
const candidateDistances = getCandidateDistances(
|
|
6515
|
+
traceLength,
|
|
6516
|
+
getTraceVertexDistances(traceLocation.trace)
|
|
6517
|
+
);
|
|
6518
|
+
const netLabelWidth = getNetLabelWidth(inputProblem, label);
|
|
6519
|
+
const candidates = [];
|
|
6520
|
+
const seenCandidateKeys = /* @__PURE__ */ new Set();
|
|
6521
|
+
for (const pathDistance of candidateDistances) {
|
|
6522
|
+
const point = getPointAtTraceDistance(traceLocation.trace, pathDistance);
|
|
6523
|
+
const orientations = getOrientationsForPoint({
|
|
6524
|
+
inputProblem,
|
|
6525
|
+
label,
|
|
6526
|
+
point,
|
|
6527
|
+
orientationConstraint
|
|
6528
|
+
});
|
|
6529
|
+
for (const orientation of orientations) {
|
|
6530
|
+
if (isSamePlacement(label, point, orientation)) continue;
|
|
6531
|
+
const key = [point.x.toFixed(6), point.y.toFixed(6), orientation].join(
|
|
6532
|
+
":"
|
|
6533
|
+
);
|
|
6534
|
+
if (seenCandidateKeys.has(key)) continue;
|
|
6535
|
+
seenCandidateKeys.add(key);
|
|
6536
|
+
candidates.push(
|
|
6537
|
+
createCandidate({
|
|
6538
|
+
point,
|
|
6539
|
+
orientation,
|
|
6540
|
+
netLabelWidth,
|
|
6541
|
+
traceLocation,
|
|
6542
|
+
pathDistance,
|
|
6543
|
+
label
|
|
6544
|
+
})
|
|
6545
|
+
);
|
|
6546
|
+
}
|
|
6547
|
+
}
|
|
6548
|
+
return candidates;
|
|
6549
|
+
};
|
|
6550
|
+
var getCandidateDistances = (traceLength, vertexDistances) => {
|
|
6551
|
+
const distances = /* @__PURE__ */ new Set();
|
|
6552
|
+
const maxSteps = Math.ceil(traceLength / CANDIDATE_STEP);
|
|
6553
|
+
for (let i = 0; i <= maxSteps; i++) {
|
|
6554
|
+
const distance3 = Math.min(traceLength, i * CANDIDATE_STEP);
|
|
6555
|
+
distances.add(roundDistance(distance3));
|
|
6556
|
+
}
|
|
6557
|
+
for (const distance3 of vertexDistances) {
|
|
6558
|
+
distances.add(roundDistance(distance3));
|
|
6559
|
+
}
|
|
6560
|
+
return [...distances].filter((distance3) => distance3 >= -EPS8 && distance3 <= traceLength + EPS8).sort((a, b) => a - b);
|
|
6561
|
+
};
|
|
6562
|
+
var getOrientationsForPoint = (params) => {
|
|
6563
|
+
const { inputProblem, label, point, orientationConstraint } = params;
|
|
6564
|
+
if (orientationConstraint) {
|
|
6565
|
+
return orientationConstraint;
|
|
6566
|
+
}
|
|
6567
|
+
return getUnconstrainedOrientations({
|
|
6568
|
+
label,
|
|
6569
|
+
inputProblem,
|
|
6570
|
+
point
|
|
6571
|
+
});
|
|
6572
|
+
};
|
|
6573
|
+
var createCandidate = (params) => {
|
|
6574
|
+
const {
|
|
6575
|
+
point,
|
|
6576
|
+
orientation,
|
|
6577
|
+
netLabelWidth,
|
|
6578
|
+
traceLocation,
|
|
6579
|
+
pathDistance,
|
|
6580
|
+
label
|
|
6581
|
+
} = params;
|
|
6582
|
+
const { width, height } = getDimsForOrientation({
|
|
6583
|
+
orientation,
|
|
6584
|
+
netLabelWidth
|
|
6585
|
+
});
|
|
6586
|
+
return {
|
|
6587
|
+
anchorPoint: point,
|
|
6588
|
+
center: getCenterFromAnchor(point, orientation, width, height),
|
|
6589
|
+
width,
|
|
6590
|
+
height,
|
|
6591
|
+
orientation,
|
|
6592
|
+
traceId: traceLocation.trace.mspPairId,
|
|
6593
|
+
pathDistance,
|
|
6594
|
+
distanceFromOriginal: getManhattanDistance(point, label.anchorPoint),
|
|
6595
|
+
status: "valid",
|
|
6596
|
+
selected: false
|
|
6597
|
+
};
|
|
6598
|
+
};
|
|
6599
|
+
var getOrientationConstraint = (inputProblem, label) => {
|
|
6600
|
+
const availableOrientations = inputProblem.availableNetLabelOrientations ?? {};
|
|
6601
|
+
for (const netId of getOrientationConstraintKeys(inputProblem, label)) {
|
|
6602
|
+
if (Object.hasOwn(availableOrientations, netId)) {
|
|
6603
|
+
return dedupeOrientations(
|
|
6604
|
+
(availableOrientations[netId] ?? []).map(normalizeFacingDirection).filter(
|
|
6605
|
+
(orientation) => orientation !== void 0
|
|
6606
|
+
)
|
|
6607
|
+
);
|
|
6608
|
+
}
|
|
6609
|
+
}
|
|
6610
|
+
return null;
|
|
6611
|
+
};
|
|
6612
|
+
var getOrientationConstraintKeys = (inputProblem, label) => dedupeStrings([
|
|
6613
|
+
label.netId,
|
|
6614
|
+
label.globalConnNetId,
|
|
6615
|
+
...inputProblem.netConnections.filter(
|
|
6616
|
+
(connection) => label.pinIds.some((pinId) => connection.pinIds.includes(pinId))
|
|
6617
|
+
).map((connection) => connection.netId)
|
|
6618
|
+
]);
|
|
6619
|
+
var getUnconstrainedOrientations = (params) => {
|
|
6620
|
+
const { label, inputProblem, point } = params;
|
|
6621
|
+
return dedupeOrientations([
|
|
6622
|
+
...getInitialOrientations(label),
|
|
6623
|
+
...getOutwardOrientations({
|
|
6624
|
+
inputProblem,
|
|
6625
|
+
point
|
|
6626
|
+
}),
|
|
6627
|
+
...ALL_ORIENTATIONS
|
|
6628
|
+
]);
|
|
6629
|
+
};
|
|
6630
|
+
var getInitialOrientations = (label) => [
|
|
6631
|
+
label.orientation,
|
|
6632
|
+
getFlippedOrientation(label.orientation)
|
|
6633
|
+
];
|
|
6634
|
+
var getOutwardOrientations = (params) => {
|
|
6635
|
+
const { inputProblem, point } = params;
|
|
6636
|
+
const chipBounds = getChipBounds(inputProblem);
|
|
6637
|
+
return dedupeOrientations(
|
|
6638
|
+
getOutwardOrientationOptions(point, chipBounds).sort((a, b) => b.outwardScore - a.outwardScore).map(({ orientation }) => orientation)
|
|
6639
|
+
);
|
|
6640
|
+
};
|
|
6641
|
+
var getOutwardOrientationOptions = (point, chipBounds) => {
|
|
6642
|
+
const options = [];
|
|
6643
|
+
options.push(
|
|
6644
|
+
getOutwardAxisOption({
|
|
6645
|
+
value: point.y,
|
|
6646
|
+
min: chipBounds.minY,
|
|
6647
|
+
max: chipBounds.maxY,
|
|
6648
|
+
center: chipBounds.centerY,
|
|
6649
|
+
positiveOrientation: "y+",
|
|
6650
|
+
negativeOrientation: "y-"
|
|
6651
|
+
})
|
|
6652
|
+
);
|
|
6653
|
+
options.push(
|
|
6654
|
+
getOutwardAxisOption({
|
|
6655
|
+
value: point.x,
|
|
6656
|
+
min: chipBounds.minX,
|
|
6657
|
+
max: chipBounds.maxX,
|
|
6658
|
+
center: chipBounds.centerX,
|
|
6659
|
+
positiveOrientation: "x+",
|
|
6660
|
+
negativeOrientation: "x-"
|
|
6661
|
+
})
|
|
6662
|
+
);
|
|
6663
|
+
return options;
|
|
6664
|
+
};
|
|
6665
|
+
var getOutwardAxisOption = (params) => {
|
|
6666
|
+
const { value, min, max, center, positiveOrientation, negativeOrientation } = params;
|
|
6667
|
+
if (value > max + EPS8) {
|
|
6668
|
+
return {
|
|
6669
|
+
orientation: positiveOrientation,
|
|
6670
|
+
outwardScore: 1 + value - max
|
|
6671
|
+
};
|
|
6672
|
+
}
|
|
6673
|
+
if (value < min - EPS8) {
|
|
6674
|
+
return {
|
|
6675
|
+
orientation: negativeOrientation,
|
|
6676
|
+
outwardScore: 1 + min - value
|
|
6677
|
+
};
|
|
6678
|
+
}
|
|
6679
|
+
return {
|
|
6680
|
+
orientation: value >= center ? positiveOrientation : negativeOrientation,
|
|
6681
|
+
outwardScore: getNormalizedCenterDistance(value, center, max - min)
|
|
6682
|
+
};
|
|
6683
|
+
};
|
|
6684
|
+
var getNormalizedCenterDistance = (value, center, span) => Math.abs(value - center) / Math.max(EPS8, span / 2);
|
|
6685
|
+
var ALL_ORIENTATIONS = ["x+", "x-", "y+", "y-"];
|
|
6686
|
+
var getFlippedOrientation = (orientation) => {
|
|
6687
|
+
switch (orientation) {
|
|
6688
|
+
case "x+":
|
|
6689
|
+
return "x-";
|
|
6690
|
+
case "x-":
|
|
6691
|
+
return "x+";
|
|
6692
|
+
case "y+":
|
|
6693
|
+
return "y-";
|
|
6694
|
+
case "y-":
|
|
6695
|
+
return "y+";
|
|
6696
|
+
}
|
|
6697
|
+
};
|
|
6698
|
+
var getChipBounds = (inputProblem) => {
|
|
6699
|
+
if (inputProblem.chips.length === 0) {
|
|
6700
|
+
return {
|
|
6701
|
+
minX: 0,
|
|
6702
|
+
minY: 0,
|
|
6703
|
+
maxX: 0,
|
|
6704
|
+
maxY: 0,
|
|
6705
|
+
centerX: 0,
|
|
6706
|
+
centerY: 0
|
|
6707
|
+
};
|
|
6708
|
+
}
|
|
6709
|
+
const bounds = inputProblem.chips.reduce(
|
|
6710
|
+
(acc, chip) => ({
|
|
6711
|
+
minX: Math.min(acc.minX, chip.center.x - chip.width / 2),
|
|
6712
|
+
minY: Math.min(acc.minY, chip.center.y - chip.height / 2),
|
|
6713
|
+
maxX: Math.max(acc.maxX, chip.center.x + chip.width / 2),
|
|
6714
|
+
maxY: Math.max(acc.maxY, chip.center.y + chip.height / 2)
|
|
6715
|
+
}),
|
|
6716
|
+
{
|
|
6717
|
+
minX: Number.POSITIVE_INFINITY,
|
|
6718
|
+
minY: Number.POSITIVE_INFINITY,
|
|
6719
|
+
maxX: Number.NEGATIVE_INFINITY,
|
|
6720
|
+
maxY: Number.NEGATIVE_INFINITY
|
|
6721
|
+
}
|
|
6722
|
+
);
|
|
6723
|
+
return {
|
|
6724
|
+
...bounds,
|
|
6725
|
+
centerX: (bounds.minX + bounds.maxX) / 2,
|
|
6726
|
+
centerY: (bounds.minY + bounds.maxY) / 2
|
|
6727
|
+
};
|
|
6728
|
+
};
|
|
6729
|
+
var getNetLabelWidth = (inputProblem, label) => {
|
|
6730
|
+
const configuredWidth = inputProblem.netConnections.find(
|
|
6731
|
+
(connection) => connection.netId === label.netId
|
|
6732
|
+
)?.netLabelWidth;
|
|
6733
|
+
if (configuredWidth !== void 0) return configuredWidth;
|
|
6734
|
+
return label.orientation === "y+" || label.orientation === "y-" ? label.height : label.width;
|
|
6735
|
+
};
|
|
6736
|
+
var roundDistance = (distance3) => Number(distance3.toFixed(6));
|
|
6737
|
+
var dedupeOrientations = (orientations) => [
|
|
6738
|
+
...new Set(orientations)
|
|
6739
|
+
];
|
|
6740
|
+
var normalizeFacingDirection = (value) => {
|
|
6741
|
+
switch (value) {
|
|
6742
|
+
case "x+":
|
|
6743
|
+
case "+x":
|
|
6744
|
+
return "x+";
|
|
6745
|
+
case "x-":
|
|
6746
|
+
case "-x":
|
|
6747
|
+
return "x-";
|
|
6748
|
+
case "y+":
|
|
6749
|
+
case "+y":
|
|
6750
|
+
return "y+";
|
|
6751
|
+
case "y-":
|
|
6752
|
+
case "-y":
|
|
6753
|
+
return "y-";
|
|
6754
|
+
}
|
|
6755
|
+
};
|
|
6756
|
+
var dedupeStrings = (values) => [
|
|
6757
|
+
...new Set(values.filter((value) => value !== void 0))
|
|
6758
|
+
];
|
|
6759
|
+
var isSamePlacement = (label, point, orientation) => Math.abs(point.x - label.anchorPoint.x) <= EPS8 && Math.abs(point.y - label.anchorPoint.y) <= EPS8 && orientation === label.orientation;
|
|
6760
|
+
|
|
6761
|
+
// lib/solvers/TraceAnchoredNetLabelOverlapSolver/visualize.ts
|
|
6762
|
+
var CANDIDATE_SELECTED_COLOR4 = "blue";
|
|
6763
|
+
var CANDIDATE_REJECTED_COLOR4 = "red";
|
|
6764
|
+
var visualizeTraceAnchoredNetLabelOverlapSolver = (state) => {
|
|
6765
|
+
const graphics = visualizeInputProblem(state.inputProblem);
|
|
6766
|
+
ensureGraphicsArrays3(graphics);
|
|
6767
|
+
drawTraces4(graphics, state.traces);
|
|
6768
|
+
drawNetLabels4(graphics, state.inputProblem, state.outputNetLabelPlacements);
|
|
6769
|
+
if (!state.solved) {
|
|
6770
|
+
drawCurrentOverlap2(graphics, state);
|
|
6771
|
+
drawCurrentCandidates4(graphics, state.currentCandidateResults);
|
|
6772
|
+
}
|
|
6773
|
+
return graphics;
|
|
6774
|
+
};
|
|
6775
|
+
var drawTraces4 = (graphics, traces) => {
|
|
6776
|
+
for (const trace of traces) {
|
|
6777
|
+
graphics.lines.push({
|
|
6778
|
+
points: trace.tracePath,
|
|
6779
|
+
strokeColor: "purple"
|
|
6780
|
+
});
|
|
6781
|
+
}
|
|
6782
|
+
};
|
|
6783
|
+
var drawNetLabels4 = (graphics, inputProblem, labels) => {
|
|
6784
|
+
for (const label of labels) {
|
|
6785
|
+
graphics.rects.push({
|
|
6786
|
+
center: label.center,
|
|
6787
|
+
width: label.width,
|
|
6788
|
+
height: label.height,
|
|
6789
|
+
fill: getColorFromString(label.globalConnNetId, 0.35),
|
|
6790
|
+
strokeColor: getColorFromString(label.globalConnNetId, 0.9),
|
|
6791
|
+
label: getNetLabelVisualizationLabel(inputProblem, label)
|
|
6792
|
+
});
|
|
6793
|
+
graphics.points.push({
|
|
6794
|
+
x: label.anchorPoint.x,
|
|
6795
|
+
y: label.anchorPoint.y,
|
|
6796
|
+
color: getColorFromString(label.globalConnNetId, 0.9),
|
|
6797
|
+
label: `anchorPoint
|
|
6798
|
+
orientation: ${label.orientation}`
|
|
6799
|
+
});
|
|
6800
|
+
}
|
|
6801
|
+
};
|
|
6802
|
+
var drawCurrentOverlap2 = (graphics, state) => {
|
|
6803
|
+
if (!state.currentOverlap) return;
|
|
6804
|
+
for (const labelIndex of [
|
|
6805
|
+
state.currentOverlap.firstLabelIndex,
|
|
6806
|
+
state.currentOverlap.secondLabelIndex
|
|
6807
|
+
]) {
|
|
6808
|
+
const label = state.outputNetLabelPlacements[labelIndex];
|
|
6809
|
+
if (!label) continue;
|
|
6810
|
+
graphics.rects.push({
|
|
6811
|
+
center: label.center,
|
|
6812
|
+
width: label.width,
|
|
6813
|
+
height: label.height,
|
|
6814
|
+
fill: "rgba(255, 0, 0, 0.2)",
|
|
6815
|
+
strokeColor: CANDIDATE_REJECTED_COLOR4,
|
|
6816
|
+
label: `netlabel overlap target
|
|
6817
|
+
${label.netId ?? label.globalConnNetId}
|
|
6818
|
+
${getAvailableOrientationText(state.inputProblem, label)}`
|
|
6819
|
+
});
|
|
6820
|
+
}
|
|
6821
|
+
};
|
|
6822
|
+
var drawCurrentCandidates4 = (graphics, candidates) => {
|
|
6823
|
+
for (const candidate of candidates) {
|
|
6824
|
+
const color = candidate.selected ? CANDIDATE_SELECTED_COLOR4 : CANDIDATE_REJECTED_COLOR4;
|
|
6825
|
+
graphics.rects.push({
|
|
6826
|
+
center: candidate.center,
|
|
6827
|
+
width: candidate.width,
|
|
6828
|
+
height: candidate.height,
|
|
6829
|
+
fill: candidate.selected ? "rgba(0, 0, 255, 0.2)" : "rgba(255, 0, 0, 0.15)",
|
|
6830
|
+
strokeColor: color,
|
|
6831
|
+
strokeDash: candidate.selected ? void 0 : "4 2",
|
|
6832
|
+
label: `${candidate.selected ? "selected" : candidate.status} netlabel overlap candidate
|
|
6833
|
+
trace: ${candidate.traceId}
|
|
6834
|
+
path distance: ${candidate.pathDistance.toFixed(3)}
|
|
6835
|
+
orientation: ${candidate.orientation}
|
|
6836
|
+
distance from original: ${candidate.distanceFromOriginal.toFixed(3)}`
|
|
6837
|
+
});
|
|
6838
|
+
graphics.points.push({
|
|
6839
|
+
...candidate.anchorPoint,
|
|
6840
|
+
color,
|
|
6841
|
+
label: `candidate anchor
|
|
6842
|
+
${candidate.status}`
|
|
6843
|
+
});
|
|
6844
|
+
}
|
|
6845
|
+
};
|
|
6846
|
+
var ensureGraphicsArrays3 = (graphics) => {
|
|
6847
|
+
if (!graphics.lines) graphics.lines = [];
|
|
6848
|
+
if (!graphics.points) graphics.points = [];
|
|
6849
|
+
if (!graphics.rects) graphics.rects = [];
|
|
6850
|
+
if (!graphics.circles) graphics.circles = [];
|
|
6851
|
+
if (!graphics.texts) graphics.texts = [];
|
|
6852
|
+
};
|
|
6853
|
+
var getAvailableOrientationText = (inputProblem, label) => {
|
|
6854
|
+
const orientations = getAvailableOrientationsForLabel(inputProblem, label);
|
|
6855
|
+
return `available orientations: ${orientations?.join(", ") ?? "any"}`;
|
|
6856
|
+
};
|
|
6857
|
+
var getNetLabelVisualizationLabel = (inputProblem, label) => [
|
|
6858
|
+
`netId: ${label.netId}`,
|
|
6859
|
+
`globalConnNetId: ${label.globalConnNetId}`,
|
|
6860
|
+
getAvailableOrientationText(inputProblem, label)
|
|
6861
|
+
].join("\n");
|
|
6862
|
+
var getAvailableOrientationsForLabel = (inputProblem, label) => {
|
|
6863
|
+
const availableOrientations = inputProblem.availableNetLabelOrientations ?? {};
|
|
6864
|
+
for (const netId of [label.netId, label.globalConnNetId]) {
|
|
6865
|
+
if (netId && Object.hasOwn(availableOrientations, netId)) {
|
|
6866
|
+
return availableOrientations[netId];
|
|
6867
|
+
}
|
|
6868
|
+
}
|
|
6869
|
+
return void 0;
|
|
6870
|
+
};
|
|
6871
|
+
|
|
6872
|
+
// lib/solvers/TraceAnchoredNetLabelOverlapSolver/TraceAnchoredNetLabelOverlapSolver.ts
|
|
6873
|
+
var TraceAnchoredNetLabelOverlapSolver = class extends BaseSolver {
|
|
6874
|
+
inputProblem;
|
|
6875
|
+
traces;
|
|
6876
|
+
netLabelPlacements;
|
|
6877
|
+
outputNetLabelPlacements;
|
|
6878
|
+
currentOverlap = null;
|
|
6879
|
+
currentCandidateResults = [];
|
|
6880
|
+
activeSearch = null;
|
|
6881
|
+
skippedOverlapKeys = /* @__PURE__ */ new Set();
|
|
6882
|
+
constructor(params) {
|
|
6883
|
+
super();
|
|
6884
|
+
this.inputProblem = params.inputProblem;
|
|
6885
|
+
this.traces = params.traces;
|
|
6886
|
+
this.netLabelPlacements = params.netLabelPlacements;
|
|
6887
|
+
this.outputNetLabelPlacements = [...params.netLabelPlacements];
|
|
6888
|
+
}
|
|
6889
|
+
getConstructorParams() {
|
|
6890
|
+
return {
|
|
6891
|
+
inputProblem: this.inputProblem,
|
|
6892
|
+
traces: this.traces,
|
|
6893
|
+
netLabelPlacements: this.netLabelPlacements
|
|
6894
|
+
};
|
|
6895
|
+
}
|
|
6896
|
+
_step() {
|
|
6897
|
+
if (this.activeSearch?.completed) this.clearActiveSearch();
|
|
6898
|
+
const search = this.getNextSearchWithCandidates();
|
|
6899
|
+
if (!search) {
|
|
6900
|
+
this.finish();
|
|
6901
|
+
return;
|
|
6902
|
+
}
|
|
6903
|
+
this.evaluateNextCandidate(search);
|
|
6904
|
+
}
|
|
6905
|
+
getOutput() {
|
|
6906
|
+
return {
|
|
6907
|
+
netLabelPlacements: this.outputNetLabelPlacements
|
|
6908
|
+
};
|
|
6909
|
+
}
|
|
6910
|
+
findNextOverlap() {
|
|
6911
|
+
for (let i = 0; i < this.outputNetLabelPlacements.length; i++) {
|
|
6912
|
+
for (let j = i + 1; j < this.outputNetLabelPlacements.length; j++) {
|
|
6913
|
+
if (!this.isLabelEligible(i) && !this.isLabelEligible(j)) continue;
|
|
6914
|
+
const overlap = {
|
|
6915
|
+
firstLabelIndex: i,
|
|
6916
|
+
secondLabelIndex: j
|
|
6917
|
+
};
|
|
6918
|
+
if (this.skippedOverlapKeys.has(this.getOverlapKey(overlap))) continue;
|
|
6919
|
+
if (this.labelsOverlap(overlap)) return overlap;
|
|
6920
|
+
}
|
|
6921
|
+
}
|
|
6922
|
+
return null;
|
|
6923
|
+
}
|
|
6924
|
+
isLabelEligible(labelIndex) {
|
|
6925
|
+
return this.getTraceLocationsForLabel(labelIndex).length > 0;
|
|
6926
|
+
}
|
|
6927
|
+
labelsOverlap(overlap) {
|
|
6928
|
+
const first = this.outputNetLabelPlacements[overlap.firstLabelIndex];
|
|
6929
|
+
const second = this.outputNetLabelPlacements[overlap.secondLabelIndex];
|
|
6930
|
+
if (!first || !second) return false;
|
|
6931
|
+
return rectsOverlap3(getLabelBounds(first), getLabelBounds(second));
|
|
6932
|
+
}
|
|
6933
|
+
startNextOverlapSearch() {
|
|
6934
|
+
const overlap = this.findNextOverlap();
|
|
6935
|
+
if (!overlap) return null;
|
|
6936
|
+
const search = {
|
|
6937
|
+
overlap,
|
|
6938
|
+
queuedLabelIndices: this.getMoveLabelIndices(overlap),
|
|
6939
|
+
labelIndex: null,
|
|
6940
|
+
candidates: [],
|
|
6941
|
+
candidateIndex: 0,
|
|
6942
|
+
candidateResults: [],
|
|
6943
|
+
completed: false
|
|
6944
|
+
};
|
|
6945
|
+
this.setActiveSearch(search);
|
|
6946
|
+
return search;
|
|
6947
|
+
}
|
|
6948
|
+
getMoveLabelIndices(overlap) {
|
|
6949
|
+
const labelIndices = [];
|
|
6950
|
+
if (this.isLabelEligible(overlap.secondLabelIndex)) {
|
|
6951
|
+
labelIndices.push(overlap.secondLabelIndex);
|
|
6952
|
+
}
|
|
6953
|
+
if (this.isLabelEligible(overlap.firstLabelIndex)) {
|
|
6954
|
+
labelIndices.push(overlap.firstLabelIndex);
|
|
6955
|
+
}
|
|
6956
|
+
return labelIndices;
|
|
6957
|
+
}
|
|
6958
|
+
getNextSearchWithCandidates() {
|
|
6959
|
+
while (true) {
|
|
6960
|
+
const search = this.activeSearch ?? this.startNextOverlapSearch();
|
|
6961
|
+
if (!search) return null;
|
|
6962
|
+
if (this.activateNextLabelSearch(search)) return search;
|
|
6963
|
+
this.skipSearch(search);
|
|
6964
|
+
}
|
|
6965
|
+
}
|
|
6966
|
+
activateNextLabelSearch(search) {
|
|
6967
|
+
if (search.labelIndex !== null && search.candidateIndex < search.candidates.length) {
|
|
6968
|
+
return true;
|
|
6969
|
+
}
|
|
6970
|
+
while (search.queuedLabelIndices.length > 0) {
|
|
6971
|
+
const labelIndex = search.queuedLabelIndices.shift();
|
|
6972
|
+
const label = this.outputNetLabelPlacements[labelIndex];
|
|
6973
|
+
if (!label) continue;
|
|
6974
|
+
search.labelIndex = labelIndex;
|
|
6975
|
+
search.candidates = this.getCandidatesForLabel(label);
|
|
6976
|
+
search.candidateIndex = 0;
|
|
6977
|
+
if (search.candidates.length > 0) return true;
|
|
6978
|
+
}
|
|
6979
|
+
search.labelIndex = null;
|
|
6980
|
+
search.candidates = [];
|
|
6981
|
+
search.candidateIndex = 0;
|
|
6982
|
+
return false;
|
|
6983
|
+
}
|
|
6984
|
+
skipSearch(search) {
|
|
6985
|
+
this.skippedOverlapKeys.add(this.getOverlapKey(search.overlap));
|
|
6986
|
+
this.clearActiveSearch();
|
|
6987
|
+
}
|
|
6988
|
+
evaluateNextCandidate(search) {
|
|
6989
|
+
const labelIndex = search.labelIndex;
|
|
6990
|
+
if (labelIndex === null) return;
|
|
6991
|
+
const label = this.outputNetLabelPlacements[labelIndex];
|
|
6992
|
+
const candidate = search.candidates[search.candidateIndex];
|
|
6993
|
+
if (!label || !candidate) return;
|
|
6994
|
+
search.candidateIndex += 1;
|
|
6995
|
+
const evaluatedCandidate = {
|
|
6996
|
+
...candidate,
|
|
6997
|
+
status: this.getCandidateStatus(candidate, labelIndex)
|
|
6998
|
+
};
|
|
6999
|
+
search.candidateResults.push(evaluatedCandidate);
|
|
7000
|
+
if (evaluatedCandidate.status !== "valid") return;
|
|
7001
|
+
evaluatedCandidate.selected = true;
|
|
7002
|
+
this.applyCandidate(labelIndex, label, evaluatedCandidate);
|
|
7003
|
+
search.completed = true;
|
|
7004
|
+
}
|
|
7005
|
+
getCandidatesForLabel(label) {
|
|
7006
|
+
const candidates = [];
|
|
7007
|
+
for (const traceLocation of getTraceLocationsForPoint(
|
|
7008
|
+
label.anchorPoint,
|
|
7009
|
+
this.traces
|
|
7010
|
+
).filter(({ trace }) => this.isTraceAssociatedWithLabel(trace, label))) {
|
|
7011
|
+
candidates.push(
|
|
7012
|
+
...generateCandidatesAlongTrace({
|
|
7013
|
+
inputProblem: this.inputProblem,
|
|
7014
|
+
label,
|
|
7015
|
+
traceLocation
|
|
7016
|
+
})
|
|
7017
|
+
);
|
|
7018
|
+
}
|
|
7019
|
+
return candidates;
|
|
7020
|
+
}
|
|
7021
|
+
getCandidateStatus(candidate, labelIndex) {
|
|
7022
|
+
const bounds = getLabelBounds(candidate);
|
|
7023
|
+
if (this.intersectsAnyChip(bounds)) return "chip-collision";
|
|
7024
|
+
if (traceCrossesBoundsInterior2(bounds, this.traces))
|
|
7025
|
+
return "trace-collision";
|
|
7026
|
+
if (this.intersectsAnyOtherLabel(bounds, labelIndex)) {
|
|
7027
|
+
return "netlabel-collision";
|
|
7028
|
+
}
|
|
7029
|
+
return "valid";
|
|
7030
|
+
}
|
|
7031
|
+
applyCandidate(labelIndex, label, candidate) {
|
|
7032
|
+
this.outputNetLabelPlacements[labelIndex] = {
|
|
7033
|
+
...label,
|
|
7034
|
+
anchorPoint: candidate.anchorPoint,
|
|
7035
|
+
center: candidate.center,
|
|
7036
|
+
width: candidate.width,
|
|
7037
|
+
height: candidate.height,
|
|
7038
|
+
orientation: candidate.orientation
|
|
7039
|
+
};
|
|
7040
|
+
}
|
|
7041
|
+
intersectsAnyChip(bounds) {
|
|
7042
|
+
return this.inputProblem.chips.some(
|
|
7043
|
+
(chip) => rectsTouchOrOverlap(
|
|
7044
|
+
bounds,
|
|
7045
|
+
getRectBounds(chip.center, chip.width, chip.height)
|
|
7046
|
+
)
|
|
7047
|
+
);
|
|
7048
|
+
}
|
|
7049
|
+
intersectsAnyOtherLabel(bounds, labelIndex) {
|
|
7050
|
+
return this.outputNetLabelPlacements.some((label, index) => {
|
|
7051
|
+
if (index === labelIndex) return false;
|
|
7052
|
+
return rectsOverlap3(bounds, getLabelBounds(label));
|
|
7053
|
+
});
|
|
7054
|
+
}
|
|
7055
|
+
getTraceLocationsForLabel(labelIndex) {
|
|
7056
|
+
const label = this.outputNetLabelPlacements[labelIndex];
|
|
7057
|
+
if (!label) return [];
|
|
7058
|
+
return getTraceLocationsForPoint(label.anchorPoint, this.traces).filter(
|
|
7059
|
+
({ trace }) => this.isTraceAssociatedWithLabel(trace, label)
|
|
7060
|
+
);
|
|
7061
|
+
}
|
|
7062
|
+
isTraceAssociatedWithLabel(trace, label) {
|
|
7063
|
+
if (label.mspConnectionPairIds.includes(trace.mspPairId)) return true;
|
|
7064
|
+
if (label.netId !== void 0 && label.netId === trace.userNetId) {
|
|
7065
|
+
return true;
|
|
7066
|
+
}
|
|
7067
|
+
return label.globalConnNetId === trace.globalConnNetId;
|
|
7068
|
+
}
|
|
7069
|
+
getOverlapKey(overlap) {
|
|
7070
|
+
const first = this.outputNetLabelPlacements[overlap.firstLabelIndex];
|
|
7071
|
+
const second = this.outputNetLabelPlacements[overlap.secondLabelIndex];
|
|
7072
|
+
return [
|
|
7073
|
+
overlap.firstLabelIndex,
|
|
7074
|
+
overlap.secondLabelIndex,
|
|
7075
|
+
first?.anchorPoint.x,
|
|
7076
|
+
first?.anchorPoint.y,
|
|
7077
|
+
second?.anchorPoint.x,
|
|
7078
|
+
second?.anchorPoint.y
|
|
7079
|
+
].join(":");
|
|
7080
|
+
}
|
|
7081
|
+
finish() {
|
|
7082
|
+
this.clearActiveSearch();
|
|
7083
|
+
this.solved = true;
|
|
7084
|
+
}
|
|
7085
|
+
setActiveSearch(search) {
|
|
7086
|
+
this.activeSearch = search;
|
|
7087
|
+
this.currentOverlap = search.overlap;
|
|
7088
|
+
this.currentCandidateResults = search.candidateResults;
|
|
7089
|
+
}
|
|
7090
|
+
clearActiveSearch() {
|
|
7091
|
+
this.activeSearch = null;
|
|
7092
|
+
this.currentOverlap = null;
|
|
7093
|
+
this.currentCandidateResults = [];
|
|
7094
|
+
}
|
|
7095
|
+
visualize() {
|
|
7096
|
+
return visualizeTraceAnchoredNetLabelOverlapSolver({
|
|
7097
|
+
inputProblem: this.inputProblem,
|
|
7098
|
+
traces: this.traces,
|
|
7099
|
+
outputNetLabelPlacements: this.outputNetLabelPlacements,
|
|
7100
|
+
currentOverlap: this.currentOverlap,
|
|
7101
|
+
currentCandidateResults: this.currentCandidateResults,
|
|
7102
|
+
solved: this.solved
|
|
7103
|
+
});
|
|
7104
|
+
}
|
|
7105
|
+
};
|
|
7106
|
+
|
|
6365
7107
|
// lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts
|
|
6366
7108
|
function definePipelineStep(solverName, solverClass, getConstructorParams, opts = {}) {
|
|
6367
7109
|
return {
|
|
@@ -6385,6 +7127,7 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
|
|
|
6385
7127
|
example28Solver;
|
|
6386
7128
|
availableNetOrientationSolver;
|
|
6387
7129
|
vccNetLabelCornerPlacementSolver;
|
|
7130
|
+
traceAnchoredNetLabelOverlapSolver;
|
|
6388
7131
|
startTimeOfPhase;
|
|
6389
7132
|
endTimeOfPhase;
|
|
6390
7133
|
timeSpentOnPhase;
|
|
@@ -6554,6 +7297,17 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
|
|
|
6554
7297
|
}
|
|
6555
7298
|
];
|
|
6556
7299
|
}
|
|
7300
|
+
),
|
|
7301
|
+
definePipelineStep(
|
|
7302
|
+
"traceAnchoredNetLabelOverlapSolver",
|
|
7303
|
+
TraceAnchoredNetLabelOverlapSolver,
|
|
7304
|
+
(instance) => [
|
|
7305
|
+
{
|
|
7306
|
+
inputProblem: instance.inputProblem,
|
|
7307
|
+
traces: instance.availableNetOrientationSolver.traces,
|
|
7308
|
+
netLabelPlacements: instance.vccNetLabelCornerPlacementSolver.outputNetLabelPlacements
|
|
7309
|
+
}
|
|
7310
|
+
]
|
|
6557
7311
|
)
|
|
6558
7312
|
];
|
|
6559
7313
|
constructor(inputProblem) {
|
|
@@ -6648,6 +7402,7 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
|
|
|
6648
7402
|
circles: visualizations.flatMap((v) => v.circles || []),
|
|
6649
7403
|
texts: visualizations.flatMap((v) => v.texts || [])
|
|
6650
7404
|
};
|
|
7405
|
+
colorAvailableNetOrientationLabels(finalGraphics, this.inputProblem);
|
|
6651
7406
|
return finalGraphics;
|
|
6652
7407
|
}
|
|
6653
7408
|
/**
|