@tscircuit/schematic-trace-solver 0.0.43 → 0.0.45
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 +17 -3
- package/dist/index.js +809 -86
- package/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts +0 -1
- package/lib/solvers/TraceCleanupSolver/TraceCleanupSolver.ts +83 -36
- package/lib/solvers/TraceCleanupSolver/hasCollisions.ts +16 -4
- package/lib/solvers/TraceCleanupSolver/is4PointRectangle.ts +17 -0
- package/lib/solvers/TraceCleanupSolver/isSegmentAnEndpointSegment.ts +36 -0
- package/lib/solvers/TraceCleanupSolver/mergeGraphicsObjects.ts +28 -0
- package/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts +18 -1
- package/lib/solvers/TraceCleanupSolver/recognizeStairStepPattern.ts +56 -0
- package/lib/solvers/TraceCleanupSolver/sub-solver/UntangleTraceSubsolver.ts +370 -0
- package/lib/solvers/TraceCleanupSolver/sub-solver/findAllLShapedTurns.ts +48 -0
- package/lib/solvers/TraceCleanupSolver/sub-solver/findIntersectionsWithObstacles.ts +36 -0
- package/lib/solvers/TraceCleanupSolver/sub-solver/generateLShapeRerouteCandidates.ts +107 -0
- package/lib/solvers/TraceCleanupSolver/sub-solver/generateRectangleCandidates.ts +55 -0
- package/lib/solvers/TraceCleanupSolver/sub-solver/getTraceObstacles.ts +25 -0
- package/lib/solvers/TraceCleanupSolver/sub-solver/isPathColliding.ts +58 -0
- package/lib/solvers/TraceCleanupSolver/sub-solver/visualizeCandidates.ts +33 -0
- package/lib/solvers/TraceCleanupSolver/sub-solver/visualizeCollision.ts +20 -0
- package/lib/solvers/TraceCleanupSolver/sub-solver/visualizeIntersectionPoints.ts +26 -0
- package/lib/solvers/TraceCleanupSolver/sub-solver/visualizeLSapes.ts +33 -0
- package/lib/solvers/TraceCleanupSolver/turnMinimization.ts +50 -56
- package/lib/solvers/TraceCleanupSolver/visualizeTightRectangle.ts +24 -0
- package/lib/solvers/TraceOverlapShiftSolver/TraceOverlapShiftSolver.ts +83 -0
- package/package.json +1 -1
- package/tests/examples/__snapshots__/example16.snap.svg +4 -4
- package/tests/examples/__snapshots__/example29.snap.svg +7 -7
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 EPS5 = 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) < -EPS5) 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) < -EPS5) 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) < EPS5 || Math.abs(p1.y - p2.y) < EPS5) {
|
|
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 EPS5 = 1e-9;
|
|
814
|
+
const samePoint = (p, q) => Math.abs(p.x - q.x) < EPS5 && Math.abs(p.y - q.y) < EPS5;
|
|
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: EPS5 = 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 isHorizontal2 = Math.abs(start.y - end.y) <
|
|
1020
|
-
if (!
|
|
1021
|
-
const segDir =
|
|
1018
|
+
const isVertical3 = Math.abs(start.x - end.x) < EPS5;
|
|
1019
|
+
const isHorizontal2 = Math.abs(start.y - end.y) < EPS5;
|
|
1020
|
+
if (!isVertical3 && !isHorizontal2) return;
|
|
1021
|
+
const segDir = isVertical3 ? end.y > start.y ? 1 : -1 : end.x > start.x ? 1 : -1;
|
|
1022
1022
|
if (si === 0) {
|
|
1023
|
-
if (
|
|
1023
|
+
if (isVertical3) {
|
|
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 (isVertical3) {
|
|
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 EPS5 = 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) < EPS5;
|
|
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: EPS5
|
|
1119
1119
|
});
|
|
1120
1120
|
} else {
|
|
1121
1121
|
const start = pts[si];
|
|
1122
1122
|
const end = pts[si + 1];
|
|
1123
|
-
const
|
|
1124
|
-
const isHorizontal2 = Math.abs(start.y - end.y) <
|
|
1125
|
-
if (!
|
|
1126
|
-
if (
|
|
1123
|
+
const isVertical3 = Math.abs(start.x - end.x) < EPS5;
|
|
1124
|
+
const isHorizontal2 = Math.abs(start.y - end.y) < EPS5;
|
|
1125
|
+
if (!isVertical3 && !isHorizontal2) continue;
|
|
1126
|
+
if (isVertical3) {
|
|
1127
1127
|
start.x += offset;
|
|
1128
1128
|
end.x += offset;
|
|
1129
1129
|
} else {
|
|
@@ -1188,6 +1188,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
1188
1188
|
*/
|
|
1189
1189
|
traceNetIslands = {};
|
|
1190
1190
|
correctedTraceMap = {};
|
|
1191
|
+
cleanupPhase = null;
|
|
1191
1192
|
constructor(params) {
|
|
1192
1193
|
super();
|
|
1193
1194
|
this.inputProblem = params.inputProblem;
|
|
@@ -1217,7 +1218,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
1217
1218
|
return islands;
|
|
1218
1219
|
}
|
|
1219
1220
|
findNextOverlapIssue() {
|
|
1220
|
-
const
|
|
1221
|
+
const EPS5 = 2e-3;
|
|
1221
1222
|
const netIds = Object.keys(this.traceNetIslands);
|
|
1222
1223
|
for (let i = 0; i < netIds.length; i++) {
|
|
1223
1224
|
for (let j = i + 1; j < netIds.length; j++) {
|
|
@@ -1235,7 +1236,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
1235
1236
|
const minB = Math.min(b1, b2);
|
|
1236
1237
|
const maxB = Math.max(b1, b2);
|
|
1237
1238
|
const overlap = Math.min(maxA, maxB) - Math.max(minA, minB);
|
|
1238
|
-
return overlap >
|
|
1239
|
+
return overlap > EPS5;
|
|
1239
1240
|
};
|
|
1240
1241
|
for (let pa = 0; pa < pathsA.length; pa++) {
|
|
1241
1242
|
const pathA = pathsA[pa];
|
|
@@ -1243,8 +1244,8 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
1243
1244
|
for (let sa = 0; sa < ptsA.length - 1; sa++) {
|
|
1244
1245
|
const a1 = ptsA[sa];
|
|
1245
1246
|
const a2 = ptsA[sa + 1];
|
|
1246
|
-
const aVert = Math.abs(a1.x - a2.x) <
|
|
1247
|
-
const aHorz = Math.abs(a1.y - a2.y) <
|
|
1247
|
+
const aVert = Math.abs(a1.x - a2.x) < EPS5;
|
|
1248
|
+
const aHorz = Math.abs(a1.y - a2.y) < EPS5;
|
|
1248
1249
|
if (!aVert && !aHorz) continue;
|
|
1249
1250
|
for (let pb = 0; pb < pathsB.length; pb++) {
|
|
1250
1251
|
const pathB = pathsB[pb];
|
|
@@ -1252,11 +1253,11 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
1252
1253
|
for (let sb = 0; sb < ptsB.length - 1; sb++) {
|
|
1253
1254
|
const b1 = ptsB[sb];
|
|
1254
1255
|
const b2 = ptsB[sb + 1];
|
|
1255
|
-
const bVert = Math.abs(b1.x - b2.x) <
|
|
1256
|
-
const bHorz = Math.abs(b1.y - b2.y) <
|
|
1256
|
+
const bVert = Math.abs(b1.x - b2.x) < EPS5;
|
|
1257
|
+
const bHorz = Math.abs(b1.y - b2.y) < EPS5;
|
|
1257
1258
|
if (!bVert && !bHorz) continue;
|
|
1258
1259
|
if (aVert && bVert) {
|
|
1259
|
-
if (Math.abs(a1.x - b1.x) <
|
|
1260
|
+
if (Math.abs(a1.x - b1.x) < EPS5) {
|
|
1260
1261
|
if (overlaps1D(a1.y, a2.y, b1.y, b2.y)) {
|
|
1261
1262
|
const keyA = `${pa}:${sa}`;
|
|
1262
1263
|
const keyB = `${pb}:${sb}`;
|
|
@@ -1277,7 +1278,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
1277
1278
|
}
|
|
1278
1279
|
}
|
|
1279
1280
|
} else if (aHorz && bHorz) {
|
|
1280
|
-
if (Math.abs(a1.y - b1.y) <
|
|
1281
|
+
if (Math.abs(a1.y - b1.y) < EPS5) {
|
|
1281
1282
|
if (overlaps1D(a1.x, a2.x, b1.x, b2.x)) {
|
|
1282
1283
|
const keyA = `${pa}:${sa}`;
|
|
1283
1284
|
const keyB = `${pb}:${sb}`;
|
|
@@ -1314,6 +1315,47 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
1314
1315
|
}
|
|
1315
1316
|
return null;
|
|
1316
1317
|
}
|
|
1318
|
+
findNextDiagonalSegment() {
|
|
1319
|
+
const EPS5 = 2e-3;
|
|
1320
|
+
for (const mspPairId in this.correctedTraceMap) {
|
|
1321
|
+
const tracePath = this.correctedTraceMap[mspPairId].tracePath;
|
|
1322
|
+
for (let i = 0; i < tracePath.length - 1; i++) {
|
|
1323
|
+
const p1 = tracePath[i];
|
|
1324
|
+
const p2 = tracePath[i + 1];
|
|
1325
|
+
const isHorizontal2 = Math.abs(p1.y - p2.y) < EPS5;
|
|
1326
|
+
const isVertical3 = Math.abs(p1.x - p2.x) < EPS5;
|
|
1327
|
+
if (!isHorizontal2 && !isVertical3) {
|
|
1328
|
+
return { mspPairId, tracePath, i, p1, p2 };
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
return null;
|
|
1333
|
+
}
|
|
1334
|
+
findAndFixNextDiagonalSegment() {
|
|
1335
|
+
const diagonalInfo = this.findNextDiagonalSegment();
|
|
1336
|
+
if (!diagonalInfo) {
|
|
1337
|
+
return false;
|
|
1338
|
+
}
|
|
1339
|
+
const { mspPairId, tracePath, i, p1, p2 } = diagonalInfo;
|
|
1340
|
+
const EPS5 = 2e-3;
|
|
1341
|
+
const p0 = i > 0 ? tracePath[i - 1] : null;
|
|
1342
|
+
const p3 = i + 2 < tracePath.length ? tracePath[i + 2] : null;
|
|
1343
|
+
const prevIsVertical = p0 ? Math.abs(p0.x - p1.x) < EPS5 : false;
|
|
1344
|
+
const prevIsHorizontal = p0 ? Math.abs(p0.y - p1.y) < EPS5 : false;
|
|
1345
|
+
const nextIsVertical = p3 ? Math.abs(p2.x - p3.x) < EPS5 : false;
|
|
1346
|
+
const nextIsHorizontal = p3 ? Math.abs(p2.y - p3.y) < EPS5 : false;
|
|
1347
|
+
const elbow1 = { x: p1.x, y: p2.y };
|
|
1348
|
+
const elbow2 = { x: p2.x, y: p1.y };
|
|
1349
|
+
let score1 = 0;
|
|
1350
|
+
if (prevIsVertical) score1++;
|
|
1351
|
+
if (nextIsHorizontal) score1++;
|
|
1352
|
+
let score2 = 0;
|
|
1353
|
+
if (prevIsHorizontal) score2++;
|
|
1354
|
+
if (nextIsVertical) score2++;
|
|
1355
|
+
const elbowPoint = score1 < score2 ? elbow1 : elbow2;
|
|
1356
|
+
tracePath.splice(i + 1, 0, elbowPoint);
|
|
1357
|
+
return true;
|
|
1358
|
+
}
|
|
1317
1359
|
_step() {
|
|
1318
1360
|
if (this.activeSubSolver?.solved) {
|
|
1319
1361
|
for (const [mspPairId, newTrace] of Object.entries(
|
|
@@ -1330,6 +1372,17 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
1330
1372
|
}
|
|
1331
1373
|
const overlapIssue = this.findNextOverlapIssue();
|
|
1332
1374
|
if (overlapIssue === null) {
|
|
1375
|
+
if (this.cleanupPhase === null) {
|
|
1376
|
+
this.cleanupPhase = "diagonals";
|
|
1377
|
+
}
|
|
1378
|
+
if (this.cleanupPhase === "diagonals") {
|
|
1379
|
+
const fixedDiagonal = this.findAndFixNextDiagonalSegment();
|
|
1380
|
+
if (!fixedDiagonal) {
|
|
1381
|
+
this.cleanupPhase = "done";
|
|
1382
|
+
this.solved = true;
|
|
1383
|
+
}
|
|
1384
|
+
return;
|
|
1385
|
+
}
|
|
1333
1386
|
this.solved = true;
|
|
1334
1387
|
return;
|
|
1335
1388
|
}
|
|
@@ -1344,12 +1397,23 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
1344
1397
|
return this.activeSubSolver.visualize();
|
|
1345
1398
|
}
|
|
1346
1399
|
const graphics = visualizeInputProblem(this.inputProblem);
|
|
1400
|
+
graphics.circles = graphics.circles || [];
|
|
1347
1401
|
for (const trace of Object.values(this.correctedTraceMap)) {
|
|
1348
1402
|
graphics.lines.push({
|
|
1349
1403
|
points: trace.tracePath,
|
|
1350
1404
|
strokeColor: "purple"
|
|
1351
1405
|
});
|
|
1352
1406
|
}
|
|
1407
|
+
if (this.cleanupPhase === "diagonals") {
|
|
1408
|
+
const diagonalInfo = this.findNextDiagonalSegment();
|
|
1409
|
+
if (diagonalInfo) {
|
|
1410
|
+
graphics.lines.push({
|
|
1411
|
+
points: [diagonalInfo.p1, diagonalInfo.p2],
|
|
1412
|
+
strokeColor: "red",
|
|
1413
|
+
strokeWidth: 0.05
|
|
1414
|
+
});
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1353
1417
|
return graphics;
|
|
1354
1418
|
}
|
|
1355
1419
|
};
|
|
@@ -1443,24 +1507,24 @@ function getRectBounds(center, w, h) {
|
|
|
1443
1507
|
}
|
|
1444
1508
|
|
|
1445
1509
|
// lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/collisions.ts
|
|
1446
|
-
function segmentIntersectsRect2(p1, p2, rect,
|
|
1447
|
-
const isVert = Math.abs(p1.x - p2.x) <
|
|
1448
|
-
const isHorz = Math.abs(p1.y - p2.y) <
|
|
1510
|
+
function segmentIntersectsRect2(p1, p2, rect, EPS5 = 1e-9) {
|
|
1511
|
+
const isVert = Math.abs(p1.x - p2.x) < EPS5;
|
|
1512
|
+
const isHorz = Math.abs(p1.y - p2.y) < EPS5;
|
|
1449
1513
|
if (!isVert && !isHorz) return false;
|
|
1450
1514
|
if (isVert) {
|
|
1451
1515
|
const x = p1.x;
|
|
1452
|
-
if (x < rect.minX -
|
|
1516
|
+
if (x < rect.minX - EPS5 || x > rect.maxX + EPS5) return false;
|
|
1453
1517
|
const segMinY = Math.min(p1.y, p2.y);
|
|
1454
1518
|
const segMaxY = Math.max(p1.y, p2.y);
|
|
1455
1519
|
const overlap = Math.min(segMaxY, rect.maxY) - Math.max(segMinY, rect.minY);
|
|
1456
|
-
return overlap >
|
|
1520
|
+
return overlap > EPS5;
|
|
1457
1521
|
} else {
|
|
1458
1522
|
const y = p1.y;
|
|
1459
|
-
if (y < rect.minY -
|
|
1523
|
+
if (y < rect.minY - EPS5 || y > rect.maxY + EPS5) return false;
|
|
1460
1524
|
const segMinX = Math.min(p1.x, p2.x);
|
|
1461
1525
|
const segMaxX = Math.max(p1.x, p2.x);
|
|
1462
1526
|
const overlap = Math.min(segMaxX, rect.maxX) - Math.max(segMinX, rect.minX);
|
|
1463
|
-
return overlap >
|
|
1527
|
+
return overlap > EPS5;
|
|
1464
1528
|
}
|
|
1465
1529
|
}
|
|
1466
1530
|
function rectIntersectsAnyTrace(bounds, inputTraceMap, hostPathId, hostSegIndex) {
|
|
@@ -1829,14 +1893,14 @@ var SingleNetLabelPlacementSolver = class extends BaseSolver {
|
|
|
1829
1893
|
};
|
|
1830
1894
|
let bestCandidate = null;
|
|
1831
1895
|
let bestScore = -Infinity;
|
|
1832
|
-
const
|
|
1896
|
+
const EPS5 = 1e-6;
|
|
1833
1897
|
for (const curr of tracesToScan) {
|
|
1834
1898
|
const pts = curr.tracePath.slice();
|
|
1835
1899
|
for (let si = 0; si < pts.length - 1; si++) {
|
|
1836
1900
|
const a = pts[si];
|
|
1837
1901
|
const b = pts[si + 1];
|
|
1838
|
-
const isH = Math.abs(a.y - b.y) <
|
|
1839
|
-
const isV = Math.abs(a.x - b.x) <
|
|
1902
|
+
const isH = Math.abs(a.y - b.y) < EPS5;
|
|
1903
|
+
const isV = Math.abs(a.x - b.x) < EPS5;
|
|
1840
1904
|
if (!isH && !isV) continue;
|
|
1841
1905
|
const segmentAllowed = isH ? ["y+", "y-"] : ["x+", "x-"];
|
|
1842
1906
|
const candidateOrients = orientations.filter(
|
|
@@ -3099,12 +3163,21 @@ var LongDistancePairSolver = class extends BaseSolver {
|
|
|
3099
3163
|
};
|
|
3100
3164
|
|
|
3101
3165
|
// lib/solvers/TraceCleanupSolver/hasCollisions.ts
|
|
3166
|
+
import { segmentToBoxMinDistance } from "@tscircuit/math-utils";
|
|
3102
3167
|
var hasCollisions = (pathSegments, obstacles) => {
|
|
3103
3168
|
for (let i = 0; i < pathSegments.length - 1; i++) {
|
|
3104
3169
|
const p1 = pathSegments[i];
|
|
3105
3170
|
const p2 = pathSegments[i + 1];
|
|
3106
3171
|
for (const obstacle of obstacles) {
|
|
3107
|
-
|
|
3172
|
+
const box = {
|
|
3173
|
+
center: {
|
|
3174
|
+
x: obstacle.minX + (obstacle.maxX - obstacle.minX) / 2,
|
|
3175
|
+
y: obstacle.minY + (obstacle.maxY - obstacle.minY) / 2
|
|
3176
|
+
},
|
|
3177
|
+
width: obstacle.maxX - obstacle.minX,
|
|
3178
|
+
height: obstacle.maxY - obstacle.minY
|
|
3179
|
+
};
|
|
3180
|
+
if (segmentToBoxMinDistance(p1, p2, box) <= 0) {
|
|
3108
3181
|
return true;
|
|
3109
3182
|
}
|
|
3110
3183
|
}
|
|
@@ -3154,43 +3227,60 @@ var hasCollisionsWithLabels = (pathSegments, labels) => {
|
|
|
3154
3227
|
return false;
|
|
3155
3228
|
};
|
|
3156
3229
|
|
|
3230
|
+
// lib/solvers/TraceCleanupSolver/recognizeStairStepPattern.ts
|
|
3231
|
+
var recognizeStairStepPattern = (pathToCheck, startIdx) => {
|
|
3232
|
+
if (startIdx >= pathToCheck.length - 3) return -1;
|
|
3233
|
+
let endIdx = startIdx;
|
|
3234
|
+
let isStairStep = true;
|
|
3235
|
+
for (let i = startIdx; i < pathToCheck.length - 2 && i < startIdx + 10; i++) {
|
|
3236
|
+
if (i + 2 >= pathToCheck.length) break;
|
|
3237
|
+
const p1 = pathToCheck[i];
|
|
3238
|
+
const p2 = pathToCheck[i + 1];
|
|
3239
|
+
const p3 = pathToCheck[i + 2];
|
|
3240
|
+
const seg1Vertical = p1.x === p2.x;
|
|
3241
|
+
const seg2Vertical = p2.x === p3.x;
|
|
3242
|
+
if (seg1Vertical === seg2Vertical) {
|
|
3243
|
+
break;
|
|
3244
|
+
}
|
|
3245
|
+
const seg1Direction = seg1Vertical ? Math.sign(p2.y - p1.y) : Math.sign(p2.x - p1.x);
|
|
3246
|
+
if (i > startIdx) {
|
|
3247
|
+
const prevP = pathToCheck[i - 1];
|
|
3248
|
+
const prevSegVertical = prevP.x === p1.x;
|
|
3249
|
+
const prevDirection = prevSegVertical ? Math.sign(p1.y - prevP.y) : Math.sign(p1.x - prevP.x);
|
|
3250
|
+
if (seg1Vertical && prevSegVertical && seg1Direction !== prevDirection || !seg1Vertical && !prevSegVertical && seg1Direction !== prevDirection) {
|
|
3251
|
+
isStairStep = false;
|
|
3252
|
+
break;
|
|
3253
|
+
}
|
|
3254
|
+
}
|
|
3255
|
+
endIdx = i + 2;
|
|
3256
|
+
}
|
|
3257
|
+
return isStairStep && endIdx - startIdx >= 3 ? endIdx : -1;
|
|
3258
|
+
};
|
|
3259
|
+
|
|
3260
|
+
// lib/solvers/TraceCleanupSolver/isSegmentAnEndpointSegment.ts
|
|
3261
|
+
var isSegmentAnEndpointSegment = (p1, p2, originalPath) => {
|
|
3262
|
+
if (originalPath.length < 2) return false;
|
|
3263
|
+
const originalStart = originalPath[0];
|
|
3264
|
+
const originalEnd = originalPath[originalPath.length - 1];
|
|
3265
|
+
if (p1.x === originalStart.x && p1.y === originalStart.y && p2.x === originalPath[1].x && p2.y === originalPath[1].y) {
|
|
3266
|
+
return true;
|
|
3267
|
+
}
|
|
3268
|
+
if (p1.x === originalPath[originalPath.length - 2].x && p1.y === originalPath[originalPath.length - 2].y && p2.x === originalEnd.x && p2.y === originalEnd.y) {
|
|
3269
|
+
return true;
|
|
3270
|
+
}
|
|
3271
|
+
return false;
|
|
3272
|
+
};
|
|
3273
|
+
|
|
3157
3274
|
// lib/solvers/TraceCleanupSolver/turnMinimization.ts
|
|
3158
3275
|
var minimizeTurns = ({
|
|
3159
3276
|
path,
|
|
3160
3277
|
obstacles,
|
|
3161
|
-
labelBounds
|
|
3278
|
+
labelBounds,
|
|
3279
|
+
originalPath
|
|
3162
3280
|
}) => {
|
|
3163
3281
|
if (path.length <= 2) {
|
|
3164
3282
|
return path;
|
|
3165
3283
|
}
|
|
3166
|
-
const recognizeStairStepPattern = (pathToCheck, startIdx) => {
|
|
3167
|
-
if (startIdx >= pathToCheck.length - 3) return -1;
|
|
3168
|
-
let endIdx = startIdx;
|
|
3169
|
-
let isStairStep = true;
|
|
3170
|
-
for (let i = startIdx; i < pathToCheck.length - 2 && i < startIdx + 10; i++) {
|
|
3171
|
-
if (i + 2 >= pathToCheck.length) break;
|
|
3172
|
-
const p1 = pathToCheck[i];
|
|
3173
|
-
const p2 = pathToCheck[i + 1];
|
|
3174
|
-
const p3 = pathToCheck[i + 2];
|
|
3175
|
-
const seg1Vertical = p1.x === p2.x;
|
|
3176
|
-
const seg2Vertical = p2.x === p3.x;
|
|
3177
|
-
if (seg1Vertical === seg2Vertical) {
|
|
3178
|
-
break;
|
|
3179
|
-
}
|
|
3180
|
-
const seg1Direction = seg1Vertical ? Math.sign(p2.y - p1.y) : Math.sign(p2.x - p1.x);
|
|
3181
|
-
if (i > startIdx) {
|
|
3182
|
-
const prevP = pathToCheck[i - 1];
|
|
3183
|
-
const prevSegVertical = prevP.x === p1.x;
|
|
3184
|
-
const prevDirection = prevSegVertical ? Math.sign(p1.y - prevP.y) : Math.sign(p1.x - prevP.x);
|
|
3185
|
-
if (seg1Vertical && prevSegVertical && seg1Direction !== prevDirection || !seg1Vertical && !prevSegVertical && seg1Direction !== prevDirection) {
|
|
3186
|
-
isStairStep = false;
|
|
3187
|
-
break;
|
|
3188
|
-
}
|
|
3189
|
-
}
|
|
3190
|
-
endIdx = i + 2;
|
|
3191
|
-
}
|
|
3192
|
-
return isStairStep && endIdx - startIdx >= 3 ? endIdx : -1;
|
|
3193
|
-
};
|
|
3194
3284
|
let optimizedPath = [...path];
|
|
3195
3285
|
let currentTurns = countTurns(optimizedPath);
|
|
3196
3286
|
let improved = true;
|
|
@@ -3199,6 +3289,17 @@ var minimizeTurns = ({
|
|
|
3199
3289
|
for (let startIdx = 0; startIdx < optimizedPath.length - 3; startIdx++) {
|
|
3200
3290
|
const stairEndIdx = recognizeStairStepPattern(optimizedPath, startIdx);
|
|
3201
3291
|
if (stairEndIdx > 0) {
|
|
3292
|
+
if (isSegmentAnEndpointSegment(
|
|
3293
|
+
optimizedPath[startIdx],
|
|
3294
|
+
optimizedPath[startIdx + 1],
|
|
3295
|
+
originalPath
|
|
3296
|
+
) || isSegmentAnEndpointSegment(
|
|
3297
|
+
optimizedPath[stairEndIdx - 1],
|
|
3298
|
+
optimizedPath[stairEndIdx],
|
|
3299
|
+
originalPath
|
|
3300
|
+
)) {
|
|
3301
|
+
continue;
|
|
3302
|
+
}
|
|
3202
3303
|
const startPoint = optimizedPath[startIdx];
|
|
3203
3304
|
const endPoint = optimizedPath[stairEndIdx];
|
|
3204
3305
|
const connectionOptions = tryConnectPoints(startPoint, endPoint);
|
|
@@ -3233,6 +3334,17 @@ var minimizeTurns = ({
|
|
|
3233
3334
|
for (let removeCount = 1; removeCount <= maxRemove; removeCount++) {
|
|
3234
3335
|
const endIdx = startIdx + removeCount + 1;
|
|
3235
3336
|
if (endIdx >= optimizedPath.length) continue;
|
|
3337
|
+
if (isSegmentAnEndpointSegment(
|
|
3338
|
+
optimizedPath[startIdx],
|
|
3339
|
+
optimizedPath[startIdx + 1],
|
|
3340
|
+
originalPath
|
|
3341
|
+
) || isSegmentAnEndpointSegment(
|
|
3342
|
+
optimizedPath[endIdx - 1],
|
|
3343
|
+
optimizedPath[endIdx],
|
|
3344
|
+
originalPath
|
|
3345
|
+
)) {
|
|
3346
|
+
continue;
|
|
3347
|
+
}
|
|
3236
3348
|
const startPoint = optimizedPath[startIdx];
|
|
3237
3349
|
const endPoint = optimizedPath[endIdx];
|
|
3238
3350
|
const connectionOptions = tryConnectPoints(startPoint, endPoint);
|
|
@@ -3271,6 +3383,9 @@ var minimizeTurns = ({
|
|
|
3271
3383
|
const p1 = optimizedPath[i];
|
|
3272
3384
|
const p2 = optimizedPath[i + 1];
|
|
3273
3385
|
const p3 = optimizedPath[i + 2];
|
|
3386
|
+
if (isSegmentAnEndpointSegment(p1, p2, originalPath) || isSegmentAnEndpointSegment(p2, p3, originalPath)) {
|
|
3387
|
+
continue;
|
|
3388
|
+
}
|
|
3274
3389
|
const allVertical = p1.x === p2.x && p2.x === p3.x;
|
|
3275
3390
|
const allHorizontal = p1.y === p2.y && p2.y === p3.y;
|
|
3276
3391
|
if (allVertical || allHorizontal) {
|
|
@@ -3327,7 +3442,15 @@ var minimizeTurnsWithFilteredLabels = ({
|
|
|
3327
3442
|
};
|
|
3328
3443
|
})
|
|
3329
3444
|
);
|
|
3330
|
-
const
|
|
3445
|
+
const staticObstaclesRaw = getObstacleRects(inputProblem);
|
|
3446
|
+
const PADDING = 0.01;
|
|
3447
|
+
const staticObstacles = staticObstaclesRaw.map((obs) => ({
|
|
3448
|
+
...obs,
|
|
3449
|
+
minX: obs.minX - PADDING,
|
|
3450
|
+
minY: obs.minY - PADDING,
|
|
3451
|
+
maxX: obs.maxX + PADDING,
|
|
3452
|
+
maxY: obs.maxY + PADDING
|
|
3453
|
+
}));
|
|
3331
3454
|
const combinedObstacles = [...staticObstacles, ...traceObstacles];
|
|
3332
3455
|
const originalPath = targetTrace.tracePath;
|
|
3333
3456
|
const filteredLabels = allLabelPlacements.filter((label) => {
|
|
@@ -3346,7 +3469,8 @@ var minimizeTurnsWithFilteredLabels = ({
|
|
|
3346
3469
|
const newPath = minimizeTurns({
|
|
3347
3470
|
path: originalPath,
|
|
3348
3471
|
obstacles: combinedObstacles,
|
|
3349
|
-
labelBounds
|
|
3472
|
+
labelBounds,
|
|
3473
|
+
originalPath
|
|
3350
3474
|
});
|
|
3351
3475
|
return {
|
|
3352
3476
|
...targetTrace,
|
|
@@ -3490,48 +3614,645 @@ var balanceZShapes = ({
|
|
|
3490
3614
|
};
|
|
3491
3615
|
};
|
|
3492
3616
|
|
|
3617
|
+
// lib/solvers/TraceCleanupSolver/sub-solver/findAllLShapedTurns.ts
|
|
3618
|
+
var findAllLShapedTurns = (tracePath) => {
|
|
3619
|
+
const lShapes = [];
|
|
3620
|
+
if (tracePath.length < 3) {
|
|
3621
|
+
return lShapes;
|
|
3622
|
+
}
|
|
3623
|
+
for (let i = 0; i < tracePath.length - 2; i++) {
|
|
3624
|
+
const p1 = tracePath[i];
|
|
3625
|
+
const p2 = tracePath[i + 1];
|
|
3626
|
+
const p3 = tracePath[i + 2];
|
|
3627
|
+
const dx1 = p2.x - p1.x;
|
|
3628
|
+
const dy1 = p2.y - p1.y;
|
|
3629
|
+
const dx2 = p3.x - p2.x;
|
|
3630
|
+
const dy2 = p3.y - p2.y;
|
|
3631
|
+
if ((dx1 === 0 && dy2 === 0 && dy1 !== 0 && dx2 !== 0 || // Vertical then Horizontal
|
|
3632
|
+
dy1 === 0 && dx2 === 0 && dx1 !== 0 && dy2 !== 0) && // Horizontal then Vertical
|
|
3633
|
+
dx1 * dx1 + dy1 * dy1 >= 0.25 && // p1-p2 arm length >= 0.5
|
|
3634
|
+
dx2 * dx2 + dy2 * dy2 >= 0.25) {
|
|
3635
|
+
lShapes.push({ p1, p2, p3 });
|
|
3636
|
+
}
|
|
3637
|
+
}
|
|
3638
|
+
return lShapes;
|
|
3639
|
+
};
|
|
3640
|
+
|
|
3641
|
+
// lib/solvers/TraceCleanupSolver/sub-solver/getTraceObstacles.ts
|
|
3642
|
+
var getTraceObstacles = (allTraces, excludeTraceId) => {
|
|
3643
|
+
const obstacles = [];
|
|
3644
|
+
for (const trace of allTraces) {
|
|
3645
|
+
if (trace.mspPairId !== excludeTraceId) {
|
|
3646
|
+
obstacles.push({ points: trace.tracePath });
|
|
3647
|
+
}
|
|
3648
|
+
}
|
|
3649
|
+
return obstacles;
|
|
3650
|
+
};
|
|
3651
|
+
|
|
3652
|
+
// lib/solvers/TraceCleanupSolver/sub-solver/findIntersectionsWithObstacles.ts
|
|
3653
|
+
import { getSegmentIntersection } from "@tscircuit/math-utils/line-intersections";
|
|
3654
|
+
var findIntersectionsWithObstacles = (p1, p2, obstacles) => {
|
|
3655
|
+
const intersections = [];
|
|
3656
|
+
for (const obstacle of obstacles) {
|
|
3657
|
+
const obstaclePath = obstacle.points;
|
|
3658
|
+
for (let i = 0; i < obstaclePath.length - 1; i++) {
|
|
3659
|
+
const o1 = obstaclePath[i];
|
|
3660
|
+
const o2 = obstaclePath[i + 1];
|
|
3661
|
+
if (!o1 || !o2) {
|
|
3662
|
+
continue;
|
|
3663
|
+
}
|
|
3664
|
+
const intersection = getSegmentIntersection(p1, p2, o1, o2);
|
|
3665
|
+
if (intersection) {
|
|
3666
|
+
intersections.push(intersection);
|
|
3667
|
+
}
|
|
3668
|
+
}
|
|
3669
|
+
}
|
|
3670
|
+
return intersections;
|
|
3671
|
+
};
|
|
3672
|
+
|
|
3673
|
+
// lib/solvers/TraceCleanupSolver/sub-solver/generateLShapeRerouteCandidates.ts
|
|
3674
|
+
var EPS4 = 1e-6;
|
|
3675
|
+
var isVertical2 = (a, b, eps = EPS4) => Math.abs(a.x - b.x) < eps;
|
|
3676
|
+
var generateLShapeRerouteCandidates = ({
|
|
3677
|
+
lShape,
|
|
3678
|
+
rectangle,
|
|
3679
|
+
padding = 0.5,
|
|
3680
|
+
interactionPoint1,
|
|
3681
|
+
interactionPoint2
|
|
3682
|
+
}) => {
|
|
3683
|
+
const { p1, p2, p3 } = lShape;
|
|
3684
|
+
const { x, y, width, height } = rectangle;
|
|
3685
|
+
let c2;
|
|
3686
|
+
let i1_padded = interactionPoint1;
|
|
3687
|
+
let i2_padded = interactionPoint2;
|
|
3688
|
+
if (Math.abs(p2.x - x) < EPS4 && Math.abs(p2.y - (y + height)) < EPS4) {
|
|
3689
|
+
c2 = { x: x + width + padding, y: y - padding };
|
|
3690
|
+
if (isVertical2(p1, p2)) {
|
|
3691
|
+
i1_padded = { x: interactionPoint1.x, y: interactionPoint1.y - padding };
|
|
3692
|
+
} else {
|
|
3693
|
+
i1_padded = { x: interactionPoint1.x + padding, y: interactionPoint1.y };
|
|
3694
|
+
}
|
|
3695
|
+
if (isVertical2(p2, p3)) {
|
|
3696
|
+
i2_padded = { x: interactionPoint2.x, y: interactionPoint2.y - padding };
|
|
3697
|
+
} else {
|
|
3698
|
+
i2_padded = { x: interactionPoint2.x + padding, y: interactionPoint2.y };
|
|
3699
|
+
}
|
|
3700
|
+
} else if (Math.abs(p2.x - (x + width)) < EPS4 && Math.abs(p2.y - (y + height)) < EPS4) {
|
|
3701
|
+
c2 = { x: x - padding, y: y - padding };
|
|
3702
|
+
if (isVertical2(p1, p2)) {
|
|
3703
|
+
i1_padded = { x: interactionPoint1.x, y: interactionPoint1.y - padding };
|
|
3704
|
+
} else {
|
|
3705
|
+
i1_padded = { x: interactionPoint1.x - padding, y: interactionPoint1.y };
|
|
3706
|
+
}
|
|
3707
|
+
if (isVertical2(p2, p3)) {
|
|
3708
|
+
i2_padded = { x: interactionPoint2.x, y: interactionPoint2.y - padding };
|
|
3709
|
+
} else {
|
|
3710
|
+
i2_padded = { x: interactionPoint2.x - padding, y: interactionPoint2.y };
|
|
3711
|
+
}
|
|
3712
|
+
} else if (Math.abs(p2.x - x) < EPS4 && Math.abs(p2.y - y) < EPS4) {
|
|
3713
|
+
c2 = { x: x + width + padding, y: y + height + padding };
|
|
3714
|
+
if (isVertical2(p1, p2)) {
|
|
3715
|
+
i1_padded = { x: interactionPoint1.x, y: interactionPoint1.y + padding };
|
|
3716
|
+
} else {
|
|
3717
|
+
i1_padded = { x: interactionPoint1.x + padding, y: interactionPoint1.y };
|
|
3718
|
+
}
|
|
3719
|
+
if (isVertical2(p2, p3)) {
|
|
3720
|
+
i2_padded = { x: interactionPoint2.x, y: interactionPoint2.y + padding };
|
|
3721
|
+
} else {
|
|
3722
|
+
i2_padded = { x: interactionPoint2.x + padding, y: interactionPoint2.y };
|
|
3723
|
+
}
|
|
3724
|
+
} else if (Math.abs(p2.x - (x + width)) < EPS4 && Math.abs(p2.y - y) < EPS4) {
|
|
3725
|
+
c2 = { x: x - padding, y: y + height + padding };
|
|
3726
|
+
if (isVertical2(p1, p2)) {
|
|
3727
|
+
i1_padded = { x: interactionPoint1.x, y: interactionPoint1.y + padding };
|
|
3728
|
+
} else {
|
|
3729
|
+
i1_padded = { x: interactionPoint1.x - padding, y: interactionPoint1.y };
|
|
3730
|
+
}
|
|
3731
|
+
if (isVertical2(p2, p3)) {
|
|
3732
|
+
i2_padded = { x: interactionPoint2.x, y: interactionPoint2.y + padding };
|
|
3733
|
+
} else {
|
|
3734
|
+
i2_padded = { x: interactionPoint2.x - padding, y: interactionPoint2.y };
|
|
3735
|
+
}
|
|
3736
|
+
} else {
|
|
3737
|
+
return [];
|
|
3738
|
+
}
|
|
3739
|
+
return [[i1_padded, c2, i2_padded]];
|
|
3740
|
+
};
|
|
3741
|
+
|
|
3742
|
+
// lib/solvers/TraceCleanupSolver/sub-solver/isPathColliding.ts
|
|
3743
|
+
import { getSegmentIntersection as getSegmentIntersection2 } from "@tscircuit/math-utils/line-intersections";
|
|
3744
|
+
var isPathColliding = (path, allTraces, traceIdToExclude) => {
|
|
3745
|
+
if (path.length < 2) {
|
|
3746
|
+
return { isColliding: false };
|
|
3747
|
+
}
|
|
3748
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
3749
|
+
const pathSegP1 = path[i];
|
|
3750
|
+
const pathSegQ1 = path[i + 1];
|
|
3751
|
+
for (const existingTrace of allTraces) {
|
|
3752
|
+
if (existingTrace.mspPairId === traceIdToExclude) {
|
|
3753
|
+
continue;
|
|
3754
|
+
}
|
|
3755
|
+
for (let j = 0; j < existingTrace.tracePath.length - 1; j++) {
|
|
3756
|
+
const existingSegP2 = existingTrace.tracePath[j];
|
|
3757
|
+
const existingSegQ2 = existingTrace.tracePath[j + 1];
|
|
3758
|
+
const intersectionPoint = getSegmentIntersection2(
|
|
3759
|
+
pathSegP1,
|
|
3760
|
+
pathSegQ1,
|
|
3761
|
+
existingSegP2,
|
|
3762
|
+
existingSegQ2
|
|
3763
|
+
);
|
|
3764
|
+
if (intersectionPoint) {
|
|
3765
|
+
return {
|
|
3766
|
+
isColliding: true,
|
|
3767
|
+
collidingTraceId: existingTrace.mspPairId,
|
|
3768
|
+
collisionPoint: intersectionPoint
|
|
3769
|
+
};
|
|
3770
|
+
}
|
|
3771
|
+
}
|
|
3772
|
+
}
|
|
3773
|
+
}
|
|
3774
|
+
return { isColliding: false };
|
|
3775
|
+
};
|
|
3776
|
+
|
|
3777
|
+
// lib/solvers/TraceCleanupSolver/sub-solver/generateRectangleCandidates.ts
|
|
3778
|
+
var generateRectangleCandidates = (intersections1, intersections2) => {
|
|
3779
|
+
const rectangleCandidates = [];
|
|
3780
|
+
for (const p1 of intersections1) {
|
|
3781
|
+
for (const p2 of intersections2) {
|
|
3782
|
+
const minX = Math.min(p1.x, p2.x);
|
|
3783
|
+
const minY = Math.min(p1.y, p2.y);
|
|
3784
|
+
const maxX = Math.max(p1.x, p2.x);
|
|
3785
|
+
const maxY = Math.max(p1.y, p2.y);
|
|
3786
|
+
const width = maxX - minX;
|
|
3787
|
+
const height = maxY - minY;
|
|
3788
|
+
if (width > 1e-6 && height > 1e-6) {
|
|
3789
|
+
rectangleCandidates.push({
|
|
3790
|
+
rect: {
|
|
3791
|
+
x: minX,
|
|
3792
|
+
y: minY,
|
|
3793
|
+
width,
|
|
3794
|
+
height
|
|
3795
|
+
},
|
|
3796
|
+
i1: p1,
|
|
3797
|
+
i2: p2
|
|
3798
|
+
});
|
|
3799
|
+
}
|
|
3800
|
+
}
|
|
3801
|
+
}
|
|
3802
|
+
return rectangleCandidates;
|
|
3803
|
+
};
|
|
3804
|
+
|
|
3805
|
+
// lib/solvers/TraceCleanupSolver/sub-solver/visualizeLSapes.ts
|
|
3806
|
+
var visualizeLSapes = (lShapes) => {
|
|
3807
|
+
const graphics = { circles: [], lines: [] };
|
|
3808
|
+
const lShapesArray = Array.isArray(lShapes) ? lShapes : [lShapes];
|
|
3809
|
+
for (const lShape of lShapesArray) {
|
|
3810
|
+
graphics.circles.push({
|
|
3811
|
+
center: {
|
|
3812
|
+
x: lShape.p2.x,
|
|
3813
|
+
y: lShape.p2.y
|
|
3814
|
+
},
|
|
3815
|
+
radius: 0.01,
|
|
3816
|
+
fill: "blue"
|
|
3817
|
+
});
|
|
3818
|
+
graphics.lines.push({
|
|
3819
|
+
points: [lShape.p1, lShape.p2, lShape.p3],
|
|
3820
|
+
strokeColor: "lightblue"
|
|
3821
|
+
});
|
|
3822
|
+
}
|
|
3823
|
+
return graphics;
|
|
3824
|
+
};
|
|
3825
|
+
|
|
3826
|
+
// lib/solvers/TraceCleanupSolver/sub-solver/visualizeIntersectionPoints.ts
|
|
3827
|
+
var visualizeIntersectionPoints = (points, color = "red") => {
|
|
3828
|
+
const graphics = { circles: [] };
|
|
3829
|
+
for (const point of points) {
|
|
3830
|
+
graphics.circles.push({
|
|
3831
|
+
center: {
|
|
3832
|
+
x: point.x,
|
|
3833
|
+
y: point.y
|
|
3834
|
+
},
|
|
3835
|
+
radius: 0.01,
|
|
3836
|
+
fill: color
|
|
3837
|
+
});
|
|
3838
|
+
}
|
|
3839
|
+
return graphics;
|
|
3840
|
+
};
|
|
3841
|
+
|
|
3842
|
+
// lib/solvers/TraceCleanupSolver/visualizeTightRectangle.ts
|
|
3843
|
+
var visualizeTightRectangle = (rectangle) => {
|
|
3844
|
+
const graphics = { rects: [] };
|
|
3845
|
+
graphics.rects.push({
|
|
3846
|
+
center: {
|
|
3847
|
+
x: rectangle.x + rectangle.width / 2,
|
|
3848
|
+
y: rectangle.y + rectangle.height / 2
|
|
3849
|
+
},
|
|
3850
|
+
width: rectangle.width,
|
|
3851
|
+
height: rectangle.height,
|
|
3852
|
+
stroke: "green"
|
|
3853
|
+
});
|
|
3854
|
+
return graphics;
|
|
3855
|
+
};
|
|
3856
|
+
|
|
3857
|
+
// lib/solvers/TraceCleanupSolver/sub-solver/visualizeCandidates.ts
|
|
3858
|
+
var visualizeCandidates = (candidates, color = "gray", intersectionPoints = []) => {
|
|
3859
|
+
const graphics = { lines: [], circles: [] };
|
|
3860
|
+
for (const candidate of candidates) {
|
|
3861
|
+
graphics.lines.push({
|
|
3862
|
+
points: candidate,
|
|
3863
|
+
strokeColor: color
|
|
3864
|
+
});
|
|
3865
|
+
}
|
|
3866
|
+
for (const point of intersectionPoints) {
|
|
3867
|
+
graphics.circles.push({
|
|
3868
|
+
center: point,
|
|
3869
|
+
radius: 0.01,
|
|
3870
|
+
// Larger radius for intersection points
|
|
3871
|
+
fill: "green"
|
|
3872
|
+
});
|
|
3873
|
+
}
|
|
3874
|
+
return graphics;
|
|
3875
|
+
};
|
|
3876
|
+
|
|
3877
|
+
// lib/solvers/TraceCleanupSolver/mergeGraphicsObjects.ts
|
|
3878
|
+
var mergeGraphicsObjects = (objects) => {
|
|
3879
|
+
const merged = {
|
|
3880
|
+
lines: [],
|
|
3881
|
+
points: [],
|
|
3882
|
+
rects: [],
|
|
3883
|
+
circles: [],
|
|
3884
|
+
texts: []
|
|
3885
|
+
};
|
|
3886
|
+
for (const obj of objects) {
|
|
3887
|
+
if (!obj) continue;
|
|
3888
|
+
if (obj.lines) merged.lines.push(...obj.lines);
|
|
3889
|
+
if (obj.points) merged.points.push(...obj.points);
|
|
3890
|
+
if (obj.rects) merged.rects.push(...obj.rects);
|
|
3891
|
+
if (obj.circles) merged.circles.push(...obj.circles);
|
|
3892
|
+
if (obj.texts) merged.texts.push(...obj.texts);
|
|
3893
|
+
}
|
|
3894
|
+
return merged;
|
|
3895
|
+
};
|
|
3896
|
+
|
|
3897
|
+
// lib/solvers/TraceCleanupSolver/sub-solver/visualizeCollision.ts
|
|
3898
|
+
var visualizeCollision = (collisionInfo) => {
|
|
3899
|
+
const collisionGraphics = { circles: [] };
|
|
3900
|
+
if (collisionInfo?.isColliding && collisionInfo.collisionPoint) {
|
|
3901
|
+
collisionGraphics.circles.push({
|
|
3902
|
+
center: collisionInfo.collisionPoint,
|
|
3903
|
+
radius: 0.01,
|
|
3904
|
+
fill: "red"
|
|
3905
|
+
});
|
|
3906
|
+
}
|
|
3907
|
+
return collisionGraphics;
|
|
3908
|
+
};
|
|
3909
|
+
|
|
3910
|
+
// lib/solvers/TraceCleanupSolver/sub-solver/UntangleTraceSubsolver.ts
|
|
3911
|
+
var UntangleTraceSubsolver = class extends BaseSolver {
|
|
3912
|
+
input;
|
|
3913
|
+
lShapesToProcess = [];
|
|
3914
|
+
visualizationMode = "l_shapes";
|
|
3915
|
+
currentLShape = null;
|
|
3916
|
+
intersectionPoints = [];
|
|
3917
|
+
tightRectangle = null;
|
|
3918
|
+
candidates = [];
|
|
3919
|
+
bestRoute = null;
|
|
3920
|
+
lastCollision = null;
|
|
3921
|
+
collidingCandidate = null;
|
|
3922
|
+
rectangleCandidates = [];
|
|
3923
|
+
currentRectangleIndex = 0;
|
|
3924
|
+
isInitialStep = true;
|
|
3925
|
+
currentCandidateIndex = 0;
|
|
3926
|
+
lShapeProcessingStep = "idle";
|
|
3927
|
+
lShapeJustProcessed = false;
|
|
3928
|
+
bestRouteFound = null;
|
|
3929
|
+
constructor(solverInput) {
|
|
3930
|
+
super();
|
|
3931
|
+
this.input = solverInput;
|
|
3932
|
+
this.visualizationMode = "l_shapes";
|
|
3933
|
+
for (const trace of this.input.allTraces) {
|
|
3934
|
+
const lShapes = findAllLShapedTurns(trace.tracePath);
|
|
3935
|
+
this.lShapesToProcess.push(
|
|
3936
|
+
...lShapes.map((l) => ({ ...l, traceId: trace.mspPairId }))
|
|
3937
|
+
);
|
|
3938
|
+
}
|
|
3939
|
+
}
|
|
3940
|
+
_step() {
|
|
3941
|
+
if (this.isInitialStep) {
|
|
3942
|
+
this.isInitialStep = false;
|
|
3943
|
+
return;
|
|
3944
|
+
}
|
|
3945
|
+
if (this.lShapeJustProcessed) {
|
|
3946
|
+
this._resetAfterLShapProcessing();
|
|
3947
|
+
return;
|
|
3948
|
+
}
|
|
3949
|
+
if (this.lShapesToProcess.length === 0 && this.currentLShape === null) {
|
|
3950
|
+
this.solved = true;
|
|
3951
|
+
return;
|
|
3952
|
+
}
|
|
3953
|
+
switch (this.lShapeProcessingStep) {
|
|
3954
|
+
case "idle":
|
|
3955
|
+
this._handleIdleStep();
|
|
3956
|
+
break;
|
|
3957
|
+
case "intersections":
|
|
3958
|
+
this._handleIntersectionsStep();
|
|
3959
|
+
break;
|
|
3960
|
+
case "rectangle_selection":
|
|
3961
|
+
this._handleRectangleSelectionStep();
|
|
3962
|
+
break;
|
|
3963
|
+
case "candidate_evaluation":
|
|
3964
|
+
this._handleCandidateEvaluationStep();
|
|
3965
|
+
break;
|
|
3966
|
+
}
|
|
3967
|
+
}
|
|
3968
|
+
_resetAfterLShapProcessing() {
|
|
3969
|
+
this.lShapeProcessingStep = "idle";
|
|
3970
|
+
this.currentLShape = null;
|
|
3971
|
+
this.currentCandidateIndex = 0;
|
|
3972
|
+
this.lShapeJustProcessed = false;
|
|
3973
|
+
this.visualizationMode = "l_shapes";
|
|
3974
|
+
this.intersectionPoints = [];
|
|
3975
|
+
this.tightRectangle = null;
|
|
3976
|
+
this.candidates = [];
|
|
3977
|
+
this.bestRoute = null;
|
|
3978
|
+
this.lastCollision = null;
|
|
3979
|
+
this.collidingCandidate = null;
|
|
3980
|
+
}
|
|
3981
|
+
_handleIdleStep() {
|
|
3982
|
+
this.currentLShape = this.lShapesToProcess.shift();
|
|
3983
|
+
if (!this.currentLShape) {
|
|
3984
|
+
this.solved = true;
|
|
3985
|
+
return;
|
|
3986
|
+
}
|
|
3987
|
+
this.lShapeProcessingStep = "intersections";
|
|
3988
|
+
this.visualizationMode = "l_shapes";
|
|
3989
|
+
}
|
|
3990
|
+
_handleIntersectionsStep() {
|
|
3991
|
+
if (!this.currentLShape.traceId) {
|
|
3992
|
+
this.lShapeProcessingStep = "idle";
|
|
3993
|
+
return;
|
|
3994
|
+
}
|
|
3995
|
+
const allObstacles = getTraceObstacles(
|
|
3996
|
+
this.input.allTraces,
|
|
3997
|
+
this.currentLShape.traceId
|
|
3998
|
+
);
|
|
3999
|
+
const intersections1 = findIntersectionsWithObstacles(
|
|
4000
|
+
this.currentLShape.p1,
|
|
4001
|
+
this.currentLShape.p2,
|
|
4002
|
+
allObstacles
|
|
4003
|
+
);
|
|
4004
|
+
const intersections2 = findIntersectionsWithObstacles(
|
|
4005
|
+
this.currentLShape.p2,
|
|
4006
|
+
this.currentLShape.p3,
|
|
4007
|
+
allObstacles
|
|
4008
|
+
);
|
|
4009
|
+
this.intersectionPoints = [...intersections1, ...intersections2];
|
|
4010
|
+
if (intersections1.length === 0 || intersections2.length === 0) {
|
|
4011
|
+
this.lShapeProcessingStep = "idle";
|
|
4012
|
+
return;
|
|
4013
|
+
}
|
|
4014
|
+
this.rectangleCandidates = generateRectangleCandidates(
|
|
4015
|
+
intersections1,
|
|
4016
|
+
intersections2
|
|
4017
|
+
);
|
|
4018
|
+
this.currentRectangleIndex = 0;
|
|
4019
|
+
this.lShapeProcessingStep = "rectangle_selection";
|
|
4020
|
+
}
|
|
4021
|
+
_handleRectangleSelectionStep() {
|
|
4022
|
+
if (this.currentRectangleIndex >= this.rectangleCandidates.length) {
|
|
4023
|
+
this.lShapeProcessingStep = "idle";
|
|
4024
|
+
return;
|
|
4025
|
+
}
|
|
4026
|
+
const { rect, i1, i2 } = this.rectangleCandidates[this.currentRectangleIndex];
|
|
4027
|
+
this.tightRectangle = rect;
|
|
4028
|
+
this.candidates = generateLShapeRerouteCandidates({
|
|
4029
|
+
lShape: this.currentLShape,
|
|
4030
|
+
rectangle: this.tightRectangle,
|
|
4031
|
+
padding: 2 * this.input.paddingBuffer,
|
|
4032
|
+
interactionPoint1: i1,
|
|
4033
|
+
interactionPoint2: i2
|
|
4034
|
+
});
|
|
4035
|
+
this.currentCandidateIndex = 0;
|
|
4036
|
+
this.lastCollision = null;
|
|
4037
|
+
this.collidingCandidate = null;
|
|
4038
|
+
this.visualizationMode = "candidates";
|
|
4039
|
+
this.lShapeProcessingStep = "candidate_evaluation";
|
|
4040
|
+
}
|
|
4041
|
+
_handleCandidateEvaluationStep() {
|
|
4042
|
+
this.visualizationMode = "candidates";
|
|
4043
|
+
if (this.bestRouteFound) {
|
|
4044
|
+
this._applyBestRoute(this.bestRouteFound);
|
|
4045
|
+
this.bestRouteFound = null;
|
|
4046
|
+
return;
|
|
4047
|
+
}
|
|
4048
|
+
if (this.currentCandidateIndex >= this.candidates.length) {
|
|
4049
|
+
this.currentRectangleIndex++;
|
|
4050
|
+
this.lShapeProcessingStep = "rectangle_selection";
|
|
4051
|
+
return;
|
|
4052
|
+
}
|
|
4053
|
+
const currentCandidate = this.candidates[this.currentCandidateIndex];
|
|
4054
|
+
const collisionResult = isPathColliding(
|
|
4055
|
+
currentCandidate,
|
|
4056
|
+
this.input.allTraces,
|
|
4057
|
+
this.currentLShape.traceId
|
|
4058
|
+
);
|
|
4059
|
+
if (!collisionResult?.isColliding) {
|
|
4060
|
+
this.bestRouteFound = currentCandidate;
|
|
4061
|
+
this.lastCollision = null;
|
|
4062
|
+
this.collidingCandidate = null;
|
|
4063
|
+
} else {
|
|
4064
|
+
this.lastCollision = collisionResult;
|
|
4065
|
+
this.collidingCandidate = currentCandidate;
|
|
4066
|
+
this.currentCandidateIndex++;
|
|
4067
|
+
}
|
|
4068
|
+
}
|
|
4069
|
+
_applyBestRoute(bestRoute) {
|
|
4070
|
+
this.bestRoute = bestRoute;
|
|
4071
|
+
this.collidingCandidate = null;
|
|
4072
|
+
this.lastCollision = null;
|
|
4073
|
+
const traceIndex = this.input.allTraces.findIndex(
|
|
4074
|
+
(trace) => trace.mspPairId === this.currentLShape.traceId
|
|
4075
|
+
);
|
|
4076
|
+
if (traceIndex !== -1) {
|
|
4077
|
+
const originalTrace = this.input.allTraces[traceIndex];
|
|
4078
|
+
const p2Index = originalTrace.tracePath.findIndex(
|
|
4079
|
+
(p) => p.x === this.currentLShape.p2.x && p.y === this.currentLShape.p2.y
|
|
4080
|
+
);
|
|
4081
|
+
if (p2Index !== -1) {
|
|
4082
|
+
const newTracePath = [
|
|
4083
|
+
...originalTrace.tracePath.slice(0, p2Index),
|
|
4084
|
+
...bestRoute,
|
|
4085
|
+
...originalTrace.tracePath.slice(p2Index + 1)
|
|
4086
|
+
];
|
|
4087
|
+
this.input.allTraces[traceIndex] = {
|
|
4088
|
+
...originalTrace,
|
|
4089
|
+
tracePath: newTracePath
|
|
4090
|
+
};
|
|
4091
|
+
this.lShapesToProcess = this.lShapesToProcess.filter(
|
|
4092
|
+
(l) => l.traceId !== this.currentLShape.traceId
|
|
4093
|
+
);
|
|
4094
|
+
}
|
|
4095
|
+
}
|
|
4096
|
+
this.lShapeJustProcessed = true;
|
|
4097
|
+
}
|
|
4098
|
+
getOutput() {
|
|
4099
|
+
return { traces: this.input.allTraces };
|
|
4100
|
+
}
|
|
4101
|
+
visualize() {
|
|
4102
|
+
switch (this.visualizationMode) {
|
|
4103
|
+
case "l_shapes":
|
|
4104
|
+
return visualizeLSapes(this.lShapesToProcess);
|
|
4105
|
+
case "intersection_points":
|
|
4106
|
+
return mergeGraphicsObjects([
|
|
4107
|
+
this.currentLShape ? visualizeLSapes(this.currentLShape) : void 0,
|
|
4108
|
+
visualizeIntersectionPoints(this.intersectionPoints)
|
|
4109
|
+
]);
|
|
4110
|
+
case "tight_rectangle":
|
|
4111
|
+
return mergeGraphicsObjects([
|
|
4112
|
+
this.currentLShape ? visualizeLSapes(this.currentLShape) : void 0,
|
|
4113
|
+
visualizeIntersectionPoints(this.intersectionPoints),
|
|
4114
|
+
this.tightRectangle ? visualizeTightRectangle(this.tightRectangle) : void 0
|
|
4115
|
+
]);
|
|
4116
|
+
case "candidates": {
|
|
4117
|
+
if (this.lShapeJustProcessed) {
|
|
4118
|
+
const allTracesGraphics2 = { lines: [] };
|
|
4119
|
+
for (const trace of this.input.allTraces) {
|
|
4120
|
+
const isUpdatedTrace = trace.mspPairId === this.currentLShape?.traceId;
|
|
4121
|
+
for (let i = 0; i < trace.tracePath.length - 1; i++) {
|
|
4122
|
+
allTracesGraphics2.lines.push({
|
|
4123
|
+
points: [trace.tracePath[i], trace.tracePath[i + 1]],
|
|
4124
|
+
strokeColor: isUpdatedTrace ? "green" : "#ccc"
|
|
4125
|
+
});
|
|
4126
|
+
}
|
|
4127
|
+
}
|
|
4128
|
+
return allTracesGraphics2;
|
|
4129
|
+
}
|
|
4130
|
+
const allTracesGraphics = { lines: [] };
|
|
4131
|
+
for (const trace of this.input.allTraces) {
|
|
4132
|
+
for (let i = 0; i < trace.tracePath.length - 1; i++) {
|
|
4133
|
+
allTracesGraphics.lines.push({
|
|
4134
|
+
points: [trace.tracePath[i], trace.tracePath[i + 1]],
|
|
4135
|
+
strokeColor: "#ccc"
|
|
4136
|
+
// Light gray for other traces
|
|
4137
|
+
});
|
|
4138
|
+
}
|
|
4139
|
+
}
|
|
4140
|
+
let candidateToDraw;
|
|
4141
|
+
if (this.bestRouteFound) {
|
|
4142
|
+
candidateToDraw = this.bestRouteFound;
|
|
4143
|
+
} else if (this.lastCollision?.isColliding) {
|
|
4144
|
+
candidateToDraw = this.collidingCandidate ?? void 0;
|
|
4145
|
+
} else {
|
|
4146
|
+
if (this.currentCandidateIndex < this.candidates.length) {
|
|
4147
|
+
candidateToDraw = this.candidates[this.currentCandidateIndex];
|
|
4148
|
+
}
|
|
4149
|
+
}
|
|
4150
|
+
return mergeGraphicsObjects([
|
|
4151
|
+
allTracesGraphics,
|
|
4152
|
+
this.currentLShape ? visualizeLSapes(this.currentLShape) : void 0,
|
|
4153
|
+
this.tightRectangle ? visualizeTightRectangle(this.tightRectangle) : void 0,
|
|
4154
|
+
candidateToDraw ? visualizeCandidates(
|
|
4155
|
+
[candidateToDraw],
|
|
4156
|
+
this.bestRouteFound ? "green" : "blue",
|
|
4157
|
+
this.intersectionPoints
|
|
4158
|
+
) : void 0,
|
|
4159
|
+
this.lastCollision ? visualizeCollision(this.lastCollision) : void 0
|
|
4160
|
+
]);
|
|
4161
|
+
}
|
|
4162
|
+
default:
|
|
4163
|
+
return {};
|
|
4164
|
+
}
|
|
4165
|
+
}
|
|
4166
|
+
};
|
|
4167
|
+
|
|
4168
|
+
// lib/solvers/TraceCleanupSolver/is4PointRectangle.ts
|
|
4169
|
+
var is4PointRectangle = (path) => {
|
|
4170
|
+
if (path.length !== 4) return false;
|
|
4171
|
+
const [p0, p1, p2, p3] = path;
|
|
4172
|
+
const isHVHC = p0.y === p1.y && p1.x === p2.x && p2.y === p3.y && p0.x === p3.x;
|
|
4173
|
+
const isVHVC = p0.x === p1.x && p1.y === p2.y && p2.x === p3.x && p0.y === p3.y;
|
|
4174
|
+
return isHVHC || isVHVC;
|
|
4175
|
+
};
|
|
4176
|
+
|
|
3493
4177
|
// lib/solvers/TraceCleanupSolver/TraceCleanupSolver.ts
|
|
3494
4178
|
var TraceCleanupSolver = class extends BaseSolver {
|
|
3495
4179
|
input;
|
|
3496
4180
|
outputTraces;
|
|
3497
4181
|
traceIdQueue;
|
|
3498
4182
|
tracesMap;
|
|
3499
|
-
pipelineStep = "
|
|
4183
|
+
pipelineStep = "untangling_traces";
|
|
3500
4184
|
activeTraceId = null;
|
|
3501
4185
|
// New property
|
|
4186
|
+
activeSubSolver = null;
|
|
3502
4187
|
constructor(solverInput) {
|
|
3503
4188
|
super();
|
|
3504
4189
|
this.input = solverInput;
|
|
3505
4190
|
this.outputTraces = [...solverInput.allTraces];
|
|
3506
4191
|
this.tracesMap = new Map(this.outputTraces.map((t) => [t.mspPairId, t]));
|
|
3507
|
-
this.traceIdQueue = Array.from(
|
|
4192
|
+
this.traceIdQueue = Array.from(
|
|
4193
|
+
solverInput.allTraces.map((e) => e.mspPairId)
|
|
4194
|
+
);
|
|
3508
4195
|
}
|
|
3509
4196
|
_step() {
|
|
3510
|
-
if (this.
|
|
4197
|
+
if (this.activeSubSolver) {
|
|
4198
|
+
this.activeSubSolver.step();
|
|
4199
|
+
if (this.activeSubSolver.solved) {
|
|
4200
|
+
const output = this.activeSubSolver.getOutput();
|
|
4201
|
+
this.outputTraces = output.traces;
|
|
4202
|
+
this.tracesMap = new Map(this.outputTraces.map((t) => [t.mspPairId, t]));
|
|
4203
|
+
this.activeSubSolver = null;
|
|
4204
|
+
this.pipelineStep = "minimizing_turns";
|
|
4205
|
+
} else if (this.activeSubSolver.failed) {
|
|
4206
|
+
this.activeSubSolver = null;
|
|
4207
|
+
this.pipelineStep = "minimizing_turns";
|
|
4208
|
+
}
|
|
4209
|
+
return;
|
|
4210
|
+
}
|
|
4211
|
+
switch (this.pipelineStep) {
|
|
4212
|
+
case "untangling_traces":
|
|
4213
|
+
this._runUntangleTracesStep();
|
|
4214
|
+
break;
|
|
4215
|
+
case "minimizing_turns":
|
|
4216
|
+
this._runMinimizeTurnsStep();
|
|
4217
|
+
break;
|
|
4218
|
+
case "balancing_l_shapes":
|
|
4219
|
+
this._runBalanceLShapesStep();
|
|
4220
|
+
break;
|
|
4221
|
+
}
|
|
4222
|
+
}
|
|
4223
|
+
_runUntangleTracesStep() {
|
|
4224
|
+
this.activeSubSolver = new UntangleTraceSubsolver({
|
|
4225
|
+
...this.input,
|
|
4226
|
+
allTraces: Array.from(this.tracesMap.values())
|
|
4227
|
+
});
|
|
4228
|
+
}
|
|
4229
|
+
_runMinimizeTurnsStep() {
|
|
4230
|
+
if (this.traceIdQueue.length === 0) {
|
|
3511
4231
|
this.pipelineStep = "balancing_l_shapes";
|
|
3512
|
-
this.traceIdQueue = Array.from(
|
|
4232
|
+
this.traceIdQueue = Array.from(
|
|
4233
|
+
this.input.allTraces.map((e) => e.mspPairId)
|
|
4234
|
+
);
|
|
4235
|
+
return;
|
|
3513
4236
|
}
|
|
3514
|
-
|
|
4237
|
+
this._processTrace("minimizing_turns");
|
|
4238
|
+
}
|
|
4239
|
+
_runBalanceLShapesStep() {
|
|
4240
|
+
if (this.traceIdQueue.length === 0) {
|
|
3515
4241
|
this.solved = true;
|
|
3516
4242
|
return;
|
|
3517
4243
|
}
|
|
4244
|
+
this._processTrace("balancing_l_shapes");
|
|
4245
|
+
}
|
|
4246
|
+
_processTrace(step) {
|
|
3518
4247
|
const targetMspConnectionPairId = this.traceIdQueue.shift();
|
|
3519
4248
|
this.activeTraceId = targetMspConnectionPairId;
|
|
3520
4249
|
const originalTrace = this.tracesMap.get(targetMspConnectionPairId);
|
|
3521
|
-
|
|
3522
|
-
const is4PointRectangle = (path) => {
|
|
3523
|
-
if (path.length !== 4) return false;
|
|
3524
|
-
const [p0, p1, p2, p3] = path;
|
|
3525
|
-
const isHVHC = p0.y === p1.y && p1.x === p2.x && p2.y === p3.y && p0.x === p3.x;
|
|
3526
|
-
const isVHVC = p0.x === p1.x && p1.y === p2.y && p2.x === p3.x && p0.y === p3.y;
|
|
3527
|
-
return isHVHC || isVHVC;
|
|
3528
|
-
};
|
|
3529
|
-
if (is4PointRectangle(tracePath)) {
|
|
4250
|
+
if (is4PointRectangle(originalTrace.tracePath)) {
|
|
3530
4251
|
return;
|
|
3531
4252
|
}
|
|
3532
4253
|
const allTraces = Array.from(this.tracesMap.values());
|
|
3533
4254
|
let updatedTrace;
|
|
3534
|
-
if (
|
|
4255
|
+
if (step === "minimizing_turns") {
|
|
3535
4256
|
updatedTrace = minimizeTurnsWithFilteredLabels({
|
|
3536
4257
|
...this.input,
|
|
3537
4258
|
targetMspConnectionPairId,
|
|
@@ -3553,6 +4274,9 @@ var TraceCleanupSolver = class extends BaseSolver {
|
|
|
3553
4274
|
};
|
|
3554
4275
|
}
|
|
3555
4276
|
visualize() {
|
|
4277
|
+
if (this.activeSubSolver) {
|
|
4278
|
+
return this.activeSubSolver.visualize();
|
|
4279
|
+
}
|
|
3556
4280
|
const graphics = visualizeInputProblem(this.input.inputProblem, {
|
|
3557
4281
|
chipAlpha: 0.1,
|
|
3558
4282
|
connectionAlpha: 0.1
|
|
@@ -3709,7 +4433,6 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
|
|
|
3709
4433
|
{
|
|
3710
4434
|
inputProblem: instance.inputProblem,
|
|
3711
4435
|
allTraces: traces,
|
|
3712
|
-
targetTraceIds: new Set(traces.map((t) => t.mspPairId)),
|
|
3713
4436
|
allLabelPlacements: labelMergingOutput.netLabelPlacements,
|
|
3714
4437
|
mergedLabelNetIdMap: labelMergingOutput.mergedLabelNetIdMap,
|
|
3715
4438
|
paddingBuffer: 0.1
|