@tscircuit/schematic-trace-solver 0.0.48 → 0.0.50
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/README.md +1 -1
- package/dist/index.d.ts +128 -1
- package/dist/index.js +1450 -42
- package/lib/solvers/AvailableNetOrientationSolver/AvailableNetOrientationSolver.ts +682 -0
- package/lib/solvers/AvailableNetOrientationSolver/constants.ts +6 -0
- package/lib/solvers/AvailableNetOrientationSolver/geometry.ts +192 -0
- package/lib/solvers/AvailableNetOrientationSolver/traces.ts +43 -0
- package/lib/solvers/AvailableNetOrientationSolver/types.ts +44 -0
- package/lib/solvers/AvailableNetOrientationSolver/visualize.ts +123 -0
- package/lib/solvers/Example28Solver/Example28Solver.ts +182 -0
- package/lib/solvers/Example28Solver/geometry.ts +246 -0
- package/lib/solvers/Example28Solver/labelMovement.ts +82 -0
- package/lib/solvers/Example28Solver/reroute.ts +368 -0
- package/lib/solvers/Example28Solver/types.ts +47 -0
- package/lib/solvers/Example28Solver/visualize.ts +96 -0
- package/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts +30 -0
- package/package.json +1 -1
- package/site/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2_01-example17-d1_1-u1_1.page.tsx +2 -0
- package/site/TraceCleanupSolver/TraceCleanupSolver.page.tsx +23 -0
- package/site/TraceLabelOverlapAvoidanceSolver/MergedNetLabelObstacles.page.tsx +18 -0
- package/site/TraceLabelOverlapAvoidanceSolver/OverlapAvoidanceStepSolver.page.tsx +24 -0
- package/site/TraceLabelOverlapAvoidanceSolver/SingleOverlapSolver.page.tsx +10 -0
- package/site/TraceLabelOverlapAvoidanceSolver/TraceLabelOverlapAvoidanceSolver.page.tsx +146 -0
- package/site/TraceLabelOverlapAvoidanceSolver/renderComparisonView01.page.tsx +32 -0
- package/site/TraceLabelOverlapAvoidanceSolver/renderComparisonView02.page.tsx +32 -0
- package/site/TraceLabelOverlapAvoidanceSolver/renderComparisonView03.page.tsx +35 -0
- package/site/examples/example01.page.tsx +6 -0
- package/site/examples/example02.page.tsx +4 -180
- package/site/examples/example03.page.tsx +3 -212
- package/site/examples/example04.page.tsx +6 -0
- package/site/examples/example05.page.tsx +3 -179
- package/site/examples/example06.page.tsx +3 -57
- package/site/examples/example07.page.tsx +3 -132
- package/site/examples/example08.page.tsx +3 -91
- package/site/examples/example09.page.tsx +3 -414
- package/site/examples/example10.page.tsx +3 -75
- package/site/examples/example11.page.tsx +3 -116
- package/site/examples/example12.page.tsx +3 -94
- package/site/examples/example13.page.tsx +3 -216
- package/site/examples/example14.page.tsx +3 -207
- package/site/examples/example15.page.tsx +6 -0
- package/site/examples/example16.page.tsx +6 -0
- package/site/examples/example17.page.tsx +6 -0
- package/site/examples/example18.page.tsx +3 -178
- package/site/examples/example19.page.tsx +3 -166
- package/site/examples/example20.page.tsx +3 -100
- package/site/examples/example21.page.tsx +3 -174
- package/site/examples/example22.page.tsx +3 -105
- package/site/examples/example23.page.tsx +3 -134
- package/site/examples/example24.page.tsx +3 -125
- package/site/examples/example25.page.tsx +2 -0
- package/site/examples/example26.page.tsx +2 -0
- package/site/examples/example27.page.tsx +2 -0
- package/site/examples/example28.page.tsx +3 -58
- package/site/examples/example29.page.tsx +6 -0
- package/site/examples/example30.page.tsx +6 -0
- package/site/examples/example31.page.tsx +4 -0
- package/tests/assets/example01.json +109 -0
- package/tests/assets/example02.json +178 -0
- package/tests/assets/example03.json +210 -0
- package/tests/assets/example04.json +41 -0
- package/tests/assets/example05.json +175 -0
- package/tests/assets/example06.json +55 -0
- package/tests/assets/example07.json +130 -0
- package/tests/assets/example08.json +89 -0
- package/tests/assets/example09.json +412 -0
- package/tests/assets/example10.json +73 -0
- package/tests/assets/example11.json +114 -0
- package/tests/assets/example12.json +92 -0
- package/tests/assets/example13.json +214 -0
- package/tests/assets/example14.json +205 -0
- package/tests/assets/example15.json +618 -0
- package/tests/assets/example16.json +102 -0
- package/tests/assets/example17.json +160 -0
- package/tests/assets/example18.json +176 -0
- package/tests/assets/example19.json +164 -0
- package/tests/assets/example20.json +98 -0
- package/tests/assets/example21.json +172 -0
- package/tests/assets/example22.json +103 -0
- package/tests/assets/example23.json +132 -0
- package/tests/assets/example24.json +123 -0
- package/tests/assets/example25.json +146 -33
- package/tests/assets/example26.json +51 -1134
- package/tests/assets/example27.json +151 -91
- package/tests/assets/example28.json +56 -0
- package/tests/assets/example29.json +1206 -0
- package/tests/assets/example30.json +168 -0
- package/tests/assets/example31.json +46 -0
- package/tests/examples/__snapshots__/example01.snap.svg +12 -6
- package/tests/examples/__snapshots__/example02.snap.svg +16 -8
- package/tests/examples/__snapshots__/example03.snap.svg +36 -18
- package/tests/examples/__snapshots__/example04.snap.svg +8 -4
- package/tests/examples/__snapshots__/example05.snap.svg +4 -2
- package/tests/examples/__snapshots__/example06.snap.svg +4 -2
- package/tests/examples/__snapshots__/example07.snap.svg +20 -10
- package/tests/examples/__snapshots__/example08.snap.svg +16 -8
- package/tests/examples/__snapshots__/example09.snap.svg +80 -40
- package/tests/examples/__snapshots__/example10.snap.svg +16 -8
- package/tests/examples/__snapshots__/example11.snap.svg +24 -12
- package/tests/examples/__snapshots__/example12.snap.svg +41 -30
- package/tests/examples/__snapshots__/example13.snap.svg +38 -16
- package/tests/examples/__snapshots__/example14.snap.svg +121 -48
- package/tests/examples/__snapshots__/example15.snap.svg +36 -18
- package/tests/examples/__snapshots__/example16.snap.svg +41 -27
- package/tests/examples/__snapshots__/example17.snap.svg +16 -8
- package/tests/examples/__snapshots__/example18.snap.svg +20 -10
- package/tests/examples/__snapshots__/example19.snap.svg +16 -8
- package/tests/examples/__snapshots__/example20.snap.svg +45 -131
- package/tests/examples/__snapshots__/example21.snap.svg +93 -65
- package/tests/examples/__snapshots__/example22.snap.svg +43 -23
- package/tests/examples/__snapshots__/example23.snap.svg +62 -26
- package/tests/examples/__snapshots__/example24.snap.svg +50 -28
- package/tests/examples/__snapshots__/example25.snap.svg +175 -49
- package/tests/examples/__snapshots__/example26.snap.svg +16 -8
- package/tests/examples/__snapshots__/example27.snap.svg +24 -12
- package/tests/examples/__snapshots__/example28.snap.svg +24 -18
- package/tests/examples/__snapshots__/example29.snap.svg +184 -92
- package/tests/examples/__snapshots__/example30.snap.svg +48 -24
- package/tests/examples/__snapshots__/example31.snap.svg +88 -0
- package/tests/examples/example01.test.ts +2 -2
- package/tests/examples/example02.test.ts +2 -2
- package/tests/examples/example03.test.ts +2 -2
- package/tests/examples/example04.test.ts +2 -2
- package/tests/examples/example05.test.ts +2 -2
- package/tests/examples/example06.test.ts +2 -2
- package/tests/examples/example07.test.ts +2 -2
- package/tests/examples/example08.test.ts +2 -2
- package/tests/examples/example09.test.ts +2 -2
- package/tests/examples/example10.test.ts +2 -2
- package/tests/examples/{example11.test.tsx → example11.test.ts} +2 -2
- package/tests/examples/{example12.test.tsx → example12.test.ts} +2 -2
- package/tests/examples/{example13.test.tsx → example13.test.ts} +2 -2
- package/tests/examples/example14.test.ts +12 -0
- package/tests/examples/example15.test.ts +12 -0
- package/tests/examples/example16.test.ts +12 -0
- package/tests/examples/example17.test.ts +12 -0
- package/tests/examples/example18.test.ts +12 -0
- package/tests/examples/example19.test.ts +12 -0
- package/tests/examples/example20.test.ts +12 -0
- package/tests/examples/example21.test.ts +12 -0
- package/tests/examples/example22.test.ts +12 -0
- package/tests/examples/example23.test.ts +12 -0
- package/tests/examples/example24.test.ts +12 -0
- package/tests/examples/example25.test.ts +12 -0
- package/tests/examples/example26.test.ts +12 -0
- package/tests/examples/example27.test.ts +3 -232
- package/tests/examples/example28.test.ts +2 -2
- package/tests/examples/example29.test.ts +1 -2
- package/tests/examples/example30.test.ts +2 -2
- package/tests/examples/example31.test.ts +12 -0
- package/site/examples/example01-basic.page.tsx +0 -105
- package/site/examples/example04-single-symbol.page.tsx +0 -46
- package/site/examples/example15-rp2040-caps.page.tsx +0 -623
- package/site/examples/example16-core-repro51.page.tsx +0 -107
- package/site/examples/example17-straight-line-trace.page.tsx +0 -165
- package/tests/examples/example14.test.tsx +0 -11
- package/tests/examples/example15.test.tsx +0 -629
- package/tests/examples/example16.test.tsx +0 -113
- package/tests/examples/example17.test.tsx +0 -171
- package/tests/examples/example18.test.tsx +0 -187
- package/tests/examples/example19.test.tsx +0 -175
- package/tests/examples/example20.test.tsx +0 -190
- package/tests/examples/example21.test.tsx +0 -183
- package/tests/examples/example22.test.tsx +0 -109
- package/tests/examples/example23.test.tsx +0 -109
- package/tests/examples/example24.test.tsx +0 -114
- package/tests/examples/example25.test.tsx +0 -143
- package/tests/examples/example26.test.tsx +0 -134
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 EPS7 = 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) < -EPS7) 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) < -EPS7) 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) < EPS7 || Math.abs(p1.y - p2.y) < EPS7) {
|
|
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 EPS7 = 1e-9;
|
|
814
|
+
const samePoint = (p, q) => Math.abs(p.x - q.x) < EPS7 && Math.abs(p.y - q.y) < EPS7;
|
|
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,13 +1010,13 @@ var applyJogToTerminalSegment = ({
|
|
|
1010
1010
|
segmentIndex: si,
|
|
1011
1011
|
offset,
|
|
1012
1012
|
JOG_SIZE,
|
|
1013
|
-
EPS:
|
|
1013
|
+
EPS: EPS7 = 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 isVertical3 = Math.abs(start.x - end.x) <
|
|
1019
|
-
const isHorizontal2 = Math.abs(start.y - end.y) <
|
|
1018
|
+
const isVertical3 = Math.abs(start.x - end.x) < EPS7;
|
|
1019
|
+
const isHorizontal2 = Math.abs(start.y - end.y) < EPS7;
|
|
1020
1020
|
if (!isVertical3 && !isHorizontal2) return;
|
|
1021
1021
|
const segDir = isVertical3 ? end.y > start.y ? 1 : -1 : end.x > start.x ? 1 : -1;
|
|
1022
1022
|
if (si === 0) {
|
|
@@ -1083,13 +1083,13 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
|
|
|
1083
1083
|
}
|
|
1084
1084
|
}
|
|
1085
1085
|
_step() {
|
|
1086
|
-
const
|
|
1086
|
+
const EPS7 = 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) < EPS7;
|
|
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,13 +1115,13 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
|
|
|
1115
1115
|
segmentIndex: si,
|
|
1116
1116
|
offset,
|
|
1117
1117
|
JOG_SIZE,
|
|
1118
|
-
EPS:
|
|
1118
|
+
EPS: EPS7
|
|
1119
1119
|
});
|
|
1120
1120
|
} else {
|
|
1121
1121
|
const start = pts[si];
|
|
1122
1122
|
const end = pts[si + 1];
|
|
1123
|
-
const isVertical3 = Math.abs(start.x - end.x) <
|
|
1124
|
-
const isHorizontal2 = Math.abs(start.y - end.y) <
|
|
1123
|
+
const isVertical3 = Math.abs(start.x - end.x) < EPS7;
|
|
1124
|
+
const isHorizontal2 = Math.abs(start.y - end.y) < EPS7;
|
|
1125
1125
|
if (!isVertical3 && !isHorizontal2) continue;
|
|
1126
1126
|
if (isVertical3) {
|
|
1127
1127
|
start.x += offset;
|
|
@@ -1218,7 +1218,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
1218
1218
|
return islands;
|
|
1219
1219
|
}
|
|
1220
1220
|
findNextOverlapIssue() {
|
|
1221
|
-
const
|
|
1221
|
+
const EPS7 = 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 > EPS7;
|
|
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) < EPS7;
|
|
1248
|
+
const aHorz = Math.abs(a1.y - a2.y) < EPS7;
|
|
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) < EPS7;
|
|
1257
|
+
const bHorz = Math.abs(b1.y - b2.y) < EPS7;
|
|
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) < EPS7) {
|
|
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) < EPS7) {
|
|
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,14 +1316,14 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
1316
1316
|
return null;
|
|
1317
1317
|
}
|
|
1318
1318
|
findNextDiagonalSegment() {
|
|
1319
|
-
const
|
|
1319
|
+
const EPS7 = 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 isHorizontal2 = Math.abs(p1.y - p2.y) <
|
|
1326
|
-
const isVertical3 = Math.abs(p1.x - p2.x) <
|
|
1325
|
+
const isHorizontal2 = Math.abs(p1.y - p2.y) < EPS7;
|
|
1326
|
+
const isVertical3 = Math.abs(p1.x - p2.x) < EPS7;
|
|
1327
1327
|
if (!isHorizontal2 && !isVertical3) {
|
|
1328
1328
|
return { mspPairId, tracePath, i, p1, p2 };
|
|
1329
1329
|
}
|
|
@@ -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 EPS7 = 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) < EPS7 : false;
|
|
1344
|
+
const prevIsHorizontal = p0 ? Math.abs(p0.y - p1.y) < EPS7 : false;
|
|
1345
|
+
const nextIsVertical = p3 ? Math.abs(p2.x - p3.x) < EPS7 : false;
|
|
1346
|
+
const nextIsHorizontal = p3 ? Math.abs(p2.y - p3.y) < EPS7 : 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, EPS7 = 1e-9) {
|
|
1511
|
+
const isVert = Math.abs(p1.x - p2.x) < EPS7;
|
|
1512
|
+
const isHorz = Math.abs(p1.y - p2.y) < EPS7;
|
|
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 - EPS7 || x > rect.maxX + EPS7) 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 > EPS7;
|
|
1521
1521
|
} else {
|
|
1522
1522
|
const y = p1.y;
|
|
1523
|
-
if (y < rect.minY -
|
|
1523
|
+
if (y < rect.minY - EPS7 || y > rect.maxY + EPS7) 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 > EPS7;
|
|
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 EPS7 = 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) < EPS7;
|
|
1912
|
+
const isV = Math.abs(a.x - b.x) < EPS7;
|
|
1913
1913
|
if (!isH && !isV) continue;
|
|
1914
1914
|
const segmentAllowed = isH ? ["y+", "y-"] : ["x+", "x-"];
|
|
1915
1915
|
const candidateOrients = orientations.filter(
|
|
@@ -2856,7 +2856,7 @@ var SingleOverlapSolver = class extends BaseSolver {
|
|
|
2856
2856
|
paddingBuffer: effectivePadding
|
|
2857
2857
|
// Use the calculated, larger padding
|
|
2858
2858
|
});
|
|
2859
|
-
const
|
|
2859
|
+
const getPathLength2 = (pts) => {
|
|
2860
2860
|
let len = 0;
|
|
2861
2861
|
for (let i = 0; i < pts.length - 1; i++) {
|
|
2862
2862
|
const dx = pts[i + 1].x - pts[i].x;
|
|
@@ -2866,7 +2866,7 @@ var SingleOverlapSolver = class extends BaseSolver {
|
|
|
2866
2866
|
return len;
|
|
2867
2867
|
};
|
|
2868
2868
|
this.queuedCandidatePaths = candidates.sort(
|
|
2869
|
-
(a, b) =>
|
|
2869
|
+
(a, b) => getPathLength2(a) - getPathLength2(b)
|
|
2870
2870
|
);
|
|
2871
2871
|
this.obstacles = getObstacleRects(this.problem);
|
|
2872
2872
|
}
|
|
@@ -4669,6 +4669,1391 @@ var TraceCleanupSolver = class extends BaseSolver {
|
|
|
4669
4669
|
}
|
|
4670
4670
|
};
|
|
4671
4671
|
|
|
4672
|
+
// lib/solvers/Example28Solver/types.ts
|
|
4673
|
+
var EPS5 = 1e-9;
|
|
4674
|
+
|
|
4675
|
+
// lib/solvers/Example28Solver/geometry.ts
|
|
4676
|
+
var getPathKey = (path) => path.map((point) => `${point.x},${point.y}`).join(";");
|
|
4677
|
+
var getPathLength = (path) => {
|
|
4678
|
+
let length = 0;
|
|
4679
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
4680
|
+
length += Math.abs(path[i + 1].x - path[i].x) + Math.abs(path[i + 1].y - path[i].y);
|
|
4681
|
+
}
|
|
4682
|
+
return length;
|
|
4683
|
+
};
|
|
4684
|
+
var getDistance = (a, b) => Math.abs(a.x - b.x) + Math.abs(a.y - b.y);
|
|
4685
|
+
var isAxisAlignedSegment = (start, end) => Math.abs(start.x - end.x) < EPS5 || Math.abs(start.y - end.y) < EPS5;
|
|
4686
|
+
var getSegmentOrientation = (start, end) => Math.abs(start.y - end.y) < EPS5 ? "horizontal" : "vertical";
|
|
4687
|
+
var projectPointToSegment = (point, start, end) => {
|
|
4688
|
+
if (Math.abs(start.x - end.x) < EPS5) {
|
|
4689
|
+
return {
|
|
4690
|
+
x: start.x,
|
|
4691
|
+
y: Math.min(
|
|
4692
|
+
Math.max(point.y, Math.min(start.y, end.y)),
|
|
4693
|
+
Math.max(start.y, end.y)
|
|
4694
|
+
)
|
|
4695
|
+
};
|
|
4696
|
+
}
|
|
4697
|
+
if (Math.abs(start.y - end.y) < EPS5) {
|
|
4698
|
+
return {
|
|
4699
|
+
x: Math.min(
|
|
4700
|
+
Math.max(point.x, Math.min(start.x, end.x)),
|
|
4701
|
+
Math.max(start.x, end.x)
|
|
4702
|
+
),
|
|
4703
|
+
y: start.y
|
|
4704
|
+
};
|
|
4705
|
+
}
|
|
4706
|
+
return start;
|
|
4707
|
+
};
|
|
4708
|
+
var getPointToSegmentDistance = (point, start, end) => getDistance(point, projectPointToSegment(point, start, end));
|
|
4709
|
+
var getSegments = (path) => {
|
|
4710
|
+
const segments = [];
|
|
4711
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
4712
|
+
const start = path[i];
|
|
4713
|
+
const end = path[i + 1];
|
|
4714
|
+
if (!isAxisAlignedSegment(start, end)) continue;
|
|
4715
|
+
segments.push({
|
|
4716
|
+
start,
|
|
4717
|
+
end,
|
|
4718
|
+
orientation: getSegmentOrientation(start, end)
|
|
4719
|
+
});
|
|
4720
|
+
}
|
|
4721
|
+
return segments;
|
|
4722
|
+
};
|
|
4723
|
+
var findSegmentContainingPoint = (path, point) => {
|
|
4724
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
4725
|
+
const start = path[i];
|
|
4726
|
+
const end = path[i + 1];
|
|
4727
|
+
if (!isAxisAlignedSegment(start, end)) continue;
|
|
4728
|
+
if (getPointToSegmentDistance(point, start, end) > 1e-6) continue;
|
|
4729
|
+
return {
|
|
4730
|
+
index: i,
|
|
4731
|
+
start,
|
|
4732
|
+
end,
|
|
4733
|
+
orientation: getSegmentOrientation(start, end)
|
|
4734
|
+
};
|
|
4735
|
+
}
|
|
4736
|
+
return null;
|
|
4737
|
+
};
|
|
4738
|
+
var isPointWithinSegmentPrimaryRange = (point, segment) => {
|
|
4739
|
+
if (segment.orientation === "horizontal") {
|
|
4740
|
+
return point.x >= Math.min(segment.start.x, segment.end.x) - EPS5 && point.x <= Math.max(segment.start.x, segment.end.x) + EPS5;
|
|
4741
|
+
}
|
|
4742
|
+
return point.y >= Math.min(segment.start.y, segment.end.y) - EPS5 && point.y <= Math.max(segment.start.y, segment.end.y) + EPS5;
|
|
4743
|
+
};
|
|
4744
|
+
var findPreferredReroutedSegment = (path, originalSegmentIndex, originalSegmentCount, orientation, anchorPoint) => {
|
|
4745
|
+
const matchingSegments = getSegments(path).filter(
|
|
4746
|
+
(segment) => segment.orientation === orientation
|
|
4747
|
+
);
|
|
4748
|
+
if (matchingSegments.length === 0) return null;
|
|
4749
|
+
const containingSegments = matchingSegments.filter(
|
|
4750
|
+
(segment) => isPointWithinSegmentPrimaryRange(anchorPoint, segment)
|
|
4751
|
+
);
|
|
4752
|
+
const candidateSegments = containingSegments.length > 0 ? containingSegments : matchingSegments;
|
|
4753
|
+
const isNearStart = originalSegmentIndex < originalSegmentCount / 2;
|
|
4754
|
+
return isNearStart ? candidateSegments[0] : candidateSegments[candidateSegments.length - 1];
|
|
4755
|
+
};
|
|
4756
|
+
var projectPointToPath = (point, path) => {
|
|
4757
|
+
let bestPoint = null;
|
|
4758
|
+
let bestDistance = Number.POSITIVE_INFINITY;
|
|
4759
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
4760
|
+
const projectedPoint = projectPointToSegment(point, path[i], path[i + 1]);
|
|
4761
|
+
const distance3 = getDistance(point, projectedPoint);
|
|
4762
|
+
if (distance3 < bestDistance) {
|
|
4763
|
+
bestPoint = projectedPoint;
|
|
4764
|
+
bestDistance = distance3;
|
|
4765
|
+
}
|
|
4766
|
+
}
|
|
4767
|
+
return bestPoint;
|
|
4768
|
+
};
|
|
4769
|
+
var segmentsIntersect = (a1, a2, b1, b2) => {
|
|
4770
|
+
const aVertical = Math.abs(a1.x - a2.x) < EPS5;
|
|
4771
|
+
const bVertical = Math.abs(b1.x - b2.x) < EPS5;
|
|
4772
|
+
const between = (value, p1, p2) => value >= Math.min(p1, p2) - EPS5 && value <= Math.max(p1, p2) + EPS5;
|
|
4773
|
+
if (aVertical && bVertical) {
|
|
4774
|
+
if (Math.abs(a1.x - b1.x) > EPS5) return false;
|
|
4775
|
+
const overlap = Math.min(Math.max(a1.y, a2.y), Math.max(b1.y, b2.y)) - Math.max(Math.min(a1.y, a2.y), Math.min(b1.y, b2.y));
|
|
4776
|
+
return overlap > EPS5;
|
|
4777
|
+
}
|
|
4778
|
+
if (!aVertical && !bVertical) {
|
|
4779
|
+
if (Math.abs(a1.y - b1.y) > EPS5) return false;
|
|
4780
|
+
const overlap = Math.min(Math.max(a1.x, a2.x), Math.max(b1.x, b2.x)) - Math.max(Math.min(a1.x, a2.x), Math.min(b1.x, b2.x));
|
|
4781
|
+
return overlap > EPS5;
|
|
4782
|
+
}
|
|
4783
|
+
const verticalA = aVertical ? a1 : b1;
|
|
4784
|
+
const verticalB = aVertical ? a2 : b2;
|
|
4785
|
+
const horizontalA = aVertical ? b1 : a1;
|
|
4786
|
+
const horizontalB = aVertical ? b2 : a2;
|
|
4787
|
+
return between(verticalA.x, horizontalA.x, horizontalB.x) && between(horizontalA.y, verticalA.y, verticalB.y);
|
|
4788
|
+
};
|
|
4789
|
+
var countPathIntersections = (pathA, pathB) => {
|
|
4790
|
+
let count = 0;
|
|
4791
|
+
for (let i = 0; i < pathA.length - 1; i++) {
|
|
4792
|
+
for (let j = 0; j < pathB.length - 1; j++) {
|
|
4793
|
+
if (segmentsIntersect(pathA[i], pathA[i + 1], pathB[j], pathB[j + 1])) {
|
|
4794
|
+
count++;
|
|
4795
|
+
}
|
|
4796
|
+
}
|
|
4797
|
+
}
|
|
4798
|
+
return count;
|
|
4799
|
+
};
|
|
4800
|
+
var isPathCollidingWithChipInterior = (path, chipObstacles) => {
|
|
4801
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
4802
|
+
const start = path[i];
|
|
4803
|
+
const end = path[i + 1];
|
|
4804
|
+
for (const obstacle of chipObstacles) {
|
|
4805
|
+
if (!segmentIntersectsRect(start, end, obstacle)) continue;
|
|
4806
|
+
if (segmentRunsAlongRectBoundary(start, end, obstacle)) continue;
|
|
4807
|
+
return true;
|
|
4808
|
+
}
|
|
4809
|
+
}
|
|
4810
|
+
return false;
|
|
4811
|
+
};
|
|
4812
|
+
var segmentRunsAlongRectBoundary = (start, end, rect) => {
|
|
4813
|
+
const isVertical3 = Math.abs(start.x - end.x) < EPS5;
|
|
4814
|
+
const isHorizontal2 = Math.abs(start.y - end.y) < EPS5;
|
|
4815
|
+
if (isVertical3) {
|
|
4816
|
+
return Math.abs(start.x - rect.minX) < EPS5 || Math.abs(start.x - rect.maxX) < EPS5;
|
|
4817
|
+
}
|
|
4818
|
+
if (isHorizontal2) {
|
|
4819
|
+
return Math.abs(start.y - rect.minY) < EPS5 || Math.abs(start.y - rect.maxY) < EPS5;
|
|
4820
|
+
}
|
|
4821
|
+
return false;
|
|
4822
|
+
};
|
|
4823
|
+
|
|
4824
|
+
// lib/solvers/Example28Solver/labelMovement.ts
|
|
4825
|
+
var moveAttachedLabelsToReroutedTrace = ({
|
|
4826
|
+
trace,
|
|
4827
|
+
originalTracePath,
|
|
4828
|
+
reroutedTracePath,
|
|
4829
|
+
netLabelPlacements
|
|
4830
|
+
}) => netLabelPlacements.map((label) => {
|
|
4831
|
+
if (!isLabelAttachedToTrace(label, trace)) return label;
|
|
4832
|
+
const movedAnchorPoint = getMovedAnchorPointForReroute(
|
|
4833
|
+
label.anchorPoint,
|
|
4834
|
+
originalTracePath,
|
|
4835
|
+
reroutedTracePath
|
|
4836
|
+
);
|
|
4837
|
+
if (!movedAnchorPoint) return label;
|
|
4838
|
+
const delta = {
|
|
4839
|
+
x: movedAnchorPoint.x - label.anchorPoint.x,
|
|
4840
|
+
y: movedAnchorPoint.y - label.anchorPoint.y
|
|
4841
|
+
};
|
|
4842
|
+
return {
|
|
4843
|
+
...label,
|
|
4844
|
+
anchorPoint: movedAnchorPoint,
|
|
4845
|
+
center: {
|
|
4846
|
+
x: label.center.x + delta.x,
|
|
4847
|
+
y: label.center.y + delta.y
|
|
4848
|
+
}
|
|
4849
|
+
};
|
|
4850
|
+
});
|
|
4851
|
+
var isLabelAttachedToTrace = (label, trace) => label.globalConnNetId === trace.globalConnNetId || label.mspConnectionPairIds.includes(trace.mspPairId);
|
|
4852
|
+
var getMovedAnchorPointForReroute = (anchorPoint, originalTracePath, reroutedTracePath) => {
|
|
4853
|
+
const originalSegment = findSegmentContainingPoint(
|
|
4854
|
+
originalTracePath,
|
|
4855
|
+
anchorPoint
|
|
4856
|
+
);
|
|
4857
|
+
if (!originalSegment) return null;
|
|
4858
|
+
const preferredSegment = findPreferredReroutedSegment(
|
|
4859
|
+
reroutedTracePath,
|
|
4860
|
+
originalSegment.index,
|
|
4861
|
+
originalTracePath.length - 1,
|
|
4862
|
+
originalSegment.orientation,
|
|
4863
|
+
anchorPoint
|
|
4864
|
+
);
|
|
4865
|
+
if (!preferredSegment) {
|
|
4866
|
+
return projectPointToPath(anchorPoint, reroutedTracePath);
|
|
4867
|
+
}
|
|
4868
|
+
return projectPointToSegment(
|
|
4869
|
+
anchorPoint,
|
|
4870
|
+
preferredSegment.start,
|
|
4871
|
+
preferredSegment.end
|
|
4872
|
+
);
|
|
4873
|
+
};
|
|
4874
|
+
|
|
4875
|
+
// lib/utils/dir.ts
|
|
4876
|
+
var dir = (facingDirection) => {
|
|
4877
|
+
switch (facingDirection) {
|
|
4878
|
+
case "x+":
|
|
4879
|
+
return { x: 1, y: 0 };
|
|
4880
|
+
case "x-":
|
|
4881
|
+
return { x: -1, y: 0 };
|
|
4882
|
+
case "y+":
|
|
4883
|
+
return { x: 0, y: 1 };
|
|
4884
|
+
case "y-":
|
|
4885
|
+
return { x: 0, y: -1 };
|
|
4886
|
+
}
|
|
4887
|
+
throw new Error(`Invalid facing direction: ${facingDirection}`);
|
|
4888
|
+
};
|
|
4889
|
+
|
|
4890
|
+
// lib/solvers/Example28Solver/reroute.ts
|
|
4891
|
+
var LABEL_SIDE_CLEARANCE = 0.1;
|
|
4892
|
+
var LABEL_HUG_CLEARANCE = 1e-3;
|
|
4893
|
+
var findBestReroutePath = ({
|
|
4894
|
+
trace,
|
|
4895
|
+
obstacleLabel,
|
|
4896
|
+
inputProblem,
|
|
4897
|
+
outputTraces,
|
|
4898
|
+
outputNetLabelPlacements,
|
|
4899
|
+
chipObstacles
|
|
4900
|
+
}) => {
|
|
4901
|
+
const candidateResults = generateRerouteCandidateResults({
|
|
4902
|
+
trace,
|
|
4903
|
+
label: obstacleLabel,
|
|
4904
|
+
inputProblem,
|
|
4905
|
+
outputTraces,
|
|
4906
|
+
outputNetLabelPlacements,
|
|
4907
|
+
chipObstacles
|
|
4908
|
+
});
|
|
4909
|
+
const bestPath = selectBestReroutePath({
|
|
4910
|
+
trace,
|
|
4911
|
+
obstacleLabel,
|
|
4912
|
+
outputTraces,
|
|
4913
|
+
outputNetLabelPlacements,
|
|
4914
|
+
candidateResults
|
|
4915
|
+
});
|
|
4916
|
+
markSelectedCandidate(candidateResults, bestPath);
|
|
4917
|
+
return { bestPath, candidateResults };
|
|
4918
|
+
};
|
|
4919
|
+
var generateRerouteCandidateResults = ({
|
|
4920
|
+
trace,
|
|
4921
|
+
label,
|
|
4922
|
+
inputProblem,
|
|
4923
|
+
outputTraces,
|
|
4924
|
+
outputNetLabelPlacements,
|
|
4925
|
+
chipObstacles
|
|
4926
|
+
}) => {
|
|
4927
|
+
const rawCandidates = generateCandidatePaths(trace, label, inputProblem);
|
|
4928
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4929
|
+
const candidateResults = [];
|
|
4930
|
+
for (const rawCandidate of rawCandidates) {
|
|
4931
|
+
candidateResults.push(
|
|
4932
|
+
createCandidateResult({
|
|
4933
|
+
trace,
|
|
4934
|
+
obstacleLabel: label,
|
|
4935
|
+
path: simplifyPath(rawCandidate),
|
|
4936
|
+
seen,
|
|
4937
|
+
outputTraces,
|
|
4938
|
+
outputNetLabelPlacements,
|
|
4939
|
+
chipObstacles
|
|
4940
|
+
})
|
|
4941
|
+
);
|
|
4942
|
+
}
|
|
4943
|
+
return candidateResults;
|
|
4944
|
+
};
|
|
4945
|
+
var generateCandidatePaths = (trace, label, inputProblem) => [
|
|
4946
|
+
...generateLabelHugCandidates(trace, label),
|
|
4947
|
+
...generateRerouteCandidates({
|
|
4948
|
+
trace,
|
|
4949
|
+
label,
|
|
4950
|
+
problem: inputProblem,
|
|
4951
|
+
paddingBuffer: LABEL_SIDE_CLEARANCE,
|
|
4952
|
+
detourCount: 0
|
|
4953
|
+
}),
|
|
4954
|
+
...generateEndpointDetourCandidates(trace, label)
|
|
4955
|
+
];
|
|
4956
|
+
var createCandidateResult = ({
|
|
4957
|
+
trace,
|
|
4958
|
+
obstacleLabel,
|
|
4959
|
+
path,
|
|
4960
|
+
seen,
|
|
4961
|
+
outputTraces,
|
|
4962
|
+
outputNetLabelPlacements,
|
|
4963
|
+
chipObstacles
|
|
4964
|
+
}) => {
|
|
4965
|
+
const key = getPathKey(path);
|
|
4966
|
+
if (seen.has(key)) {
|
|
4967
|
+
return {
|
|
4968
|
+
path,
|
|
4969
|
+
status: "duplicate",
|
|
4970
|
+
selected: false
|
|
4971
|
+
};
|
|
4972
|
+
}
|
|
4973
|
+
seen.add(key);
|
|
4974
|
+
if (isPathCollidingWithChipInterior(path, chipObstacles)) {
|
|
4975
|
+
return {
|
|
4976
|
+
path,
|
|
4977
|
+
status: "chip-collision",
|
|
4978
|
+
selected: false
|
|
4979
|
+
};
|
|
4980
|
+
}
|
|
4981
|
+
return {
|
|
4982
|
+
path,
|
|
4983
|
+
score: scoreTracePath({
|
|
4984
|
+
trace,
|
|
4985
|
+
tracePath: path,
|
|
4986
|
+
obstacleLabel,
|
|
4987
|
+
outputTraces,
|
|
4988
|
+
outputNetLabelPlacements
|
|
4989
|
+
}),
|
|
4990
|
+
status: "valid",
|
|
4991
|
+
selected: false
|
|
4992
|
+
};
|
|
4993
|
+
};
|
|
4994
|
+
var generateEndpointDetourCandidates = (trace, label) => {
|
|
4995
|
+
const start = trace.tracePath[0];
|
|
4996
|
+
const end = trace.tracePath[trace.tracePath.length - 1];
|
|
4997
|
+
if (!start || !end) return [];
|
|
4998
|
+
const bounds = getRectBounds(label.center, label.width, label.height);
|
|
4999
|
+
const padding = LABEL_SIDE_CLEARANCE;
|
|
5000
|
+
const labelDirection = dir(label.orientation);
|
|
5001
|
+
const labelSideX = labelDirection.x < 0 ? bounds.minX - padding : bounds.maxX + padding;
|
|
5002
|
+
const oppositeSideX = labelDirection.x < 0 ? bounds.maxX + padding : bounds.minX - padding;
|
|
5003
|
+
const topY = bounds.maxY + padding;
|
|
5004
|
+
const bottomY = bounds.minY - padding;
|
|
5005
|
+
const candidates = [];
|
|
5006
|
+
for (const sideX of [labelSideX, oppositeSideX]) {
|
|
5007
|
+
candidates.push(
|
|
5008
|
+
[
|
|
5009
|
+
start,
|
|
5010
|
+
{ x: start.x, y: topY },
|
|
5011
|
+
{ x: sideX, y: topY },
|
|
5012
|
+
{ x: sideX, y: bottomY },
|
|
5013
|
+
{ x: end.x, y: bottomY },
|
|
5014
|
+
end
|
|
5015
|
+
],
|
|
5016
|
+
[
|
|
5017
|
+
start,
|
|
5018
|
+
{ x: start.x, y: bottomY },
|
|
5019
|
+
{ x: sideX, y: bottomY },
|
|
5020
|
+
{ x: sideX, y: topY },
|
|
5021
|
+
{ x: end.x, y: topY },
|
|
5022
|
+
end
|
|
5023
|
+
]
|
|
5024
|
+
);
|
|
5025
|
+
}
|
|
5026
|
+
return candidates;
|
|
5027
|
+
};
|
|
5028
|
+
var generateLabelHugCandidates = (trace, label) => {
|
|
5029
|
+
const start = trace.tracePath[0];
|
|
5030
|
+
const end = trace.tracePath[trace.tracePath.length - 1];
|
|
5031
|
+
if (!start || !end) return [];
|
|
5032
|
+
const labelDirection = dir(label.orientation);
|
|
5033
|
+
if (labelDirection.x === 0) return [];
|
|
5034
|
+
const bounds = getRectBounds(label.center, label.width, label.height);
|
|
5035
|
+
const sideX = labelDirection.x < 0 ? bounds.minX - LABEL_SIDE_CLEARANCE : bounds.maxX + LABEL_SIDE_CLEARANCE;
|
|
5036
|
+
const topY = bounds.maxY + LABEL_HUG_CLEARANCE;
|
|
5037
|
+
const bottomY = bounds.minY - LABEL_HUG_CLEARANCE;
|
|
5038
|
+
const startsAboveEnd = start.y >= end.y;
|
|
5039
|
+
const firstY = startsAboveEnd ? topY : bottomY;
|
|
5040
|
+
const secondY = startsAboveEnd ? bottomY : topY;
|
|
5041
|
+
return [
|
|
5042
|
+
[
|
|
5043
|
+
start,
|
|
5044
|
+
{ x: start.x, y: firstY },
|
|
5045
|
+
{ x: sideX, y: firstY },
|
|
5046
|
+
{ x: sideX, y: secondY },
|
|
5047
|
+
{ x: end.x, y: secondY },
|
|
5048
|
+
end
|
|
5049
|
+
],
|
|
5050
|
+
[
|
|
5051
|
+
start,
|
|
5052
|
+
{ x: start.x, y: secondY },
|
|
5053
|
+
{ x: sideX, y: secondY },
|
|
5054
|
+
{ x: sideX, y: firstY },
|
|
5055
|
+
{ x: end.x, y: firstY },
|
|
5056
|
+
end
|
|
5057
|
+
]
|
|
5058
|
+
];
|
|
5059
|
+
};
|
|
5060
|
+
var selectBestReroutePath = ({
|
|
5061
|
+
trace,
|
|
5062
|
+
obstacleLabel,
|
|
5063
|
+
outputTraces,
|
|
5064
|
+
outputNetLabelPlacements,
|
|
5065
|
+
candidateResults
|
|
5066
|
+
}) => {
|
|
5067
|
+
let bestPath = null;
|
|
5068
|
+
let bestScore = scoreTracePath({
|
|
5069
|
+
trace,
|
|
5070
|
+
tracePath: trace.tracePath,
|
|
5071
|
+
obstacleLabel,
|
|
5072
|
+
outputTraces,
|
|
5073
|
+
outputNetLabelPlacements
|
|
5074
|
+
});
|
|
5075
|
+
for (const candidate of candidateResults) {
|
|
5076
|
+
if (candidate.status !== "valid" || !candidate.score) continue;
|
|
5077
|
+
if (!isBetterScore(candidate.score, bestScore)) continue;
|
|
5078
|
+
bestScore = candidate.score;
|
|
5079
|
+
bestPath = candidate.path;
|
|
5080
|
+
}
|
|
5081
|
+
return bestPath;
|
|
5082
|
+
};
|
|
5083
|
+
var markSelectedCandidate = (candidateResults, bestPath) => {
|
|
5084
|
+
if (!bestPath) return;
|
|
5085
|
+
for (const candidate of candidateResults) {
|
|
5086
|
+
candidate.selected = candidate.path === bestPath;
|
|
5087
|
+
}
|
|
5088
|
+
};
|
|
5089
|
+
var scoreTracePath = ({
|
|
5090
|
+
trace,
|
|
5091
|
+
tracePath,
|
|
5092
|
+
obstacleLabel,
|
|
5093
|
+
outputTraces,
|
|
5094
|
+
outputNetLabelPlacements
|
|
5095
|
+
}) => {
|
|
5096
|
+
const candidateTrace = { ...trace, tracePath };
|
|
5097
|
+
return {
|
|
5098
|
+
labelIntersections: detectTraceLabelOverlap({
|
|
5099
|
+
traces: [candidateTrace],
|
|
5100
|
+
netLabels: outputNetLabelPlacements
|
|
5101
|
+
}).length,
|
|
5102
|
+
labelHugDistance: getLabelHugDistance(tracePath, obstacleLabel),
|
|
5103
|
+
traceIntersections: countTraceIntersections(candidateTrace, outputTraces),
|
|
5104
|
+
pathLength: getPathLength(tracePath)
|
|
5105
|
+
};
|
|
5106
|
+
};
|
|
5107
|
+
var countTraceIntersections = (trace, outputTraces) => {
|
|
5108
|
+
let count = 0;
|
|
5109
|
+
for (const otherTrace of outputTraces) {
|
|
5110
|
+
if (otherTrace.mspPairId === trace.mspPairId) continue;
|
|
5111
|
+
count += countPathIntersections(trace.tracePath, otherTrace.tracePath);
|
|
5112
|
+
}
|
|
5113
|
+
return count;
|
|
5114
|
+
};
|
|
5115
|
+
var isBetterScore = (score, bestScore) => {
|
|
5116
|
+
if (score.labelIntersections !== bestScore.labelIntersections) {
|
|
5117
|
+
return score.labelIntersections < bestScore.labelIntersections;
|
|
5118
|
+
}
|
|
5119
|
+
if (score.labelHugDistance !== bestScore.labelHugDistance) {
|
|
5120
|
+
return score.labelHugDistance < bestScore.labelHugDistance;
|
|
5121
|
+
}
|
|
5122
|
+
if (score.traceIntersections !== bestScore.traceIntersections) {
|
|
5123
|
+
return score.traceIntersections < bestScore.traceIntersections;
|
|
5124
|
+
}
|
|
5125
|
+
return score.pathLength < bestScore.pathLength;
|
|
5126
|
+
};
|
|
5127
|
+
var getLabelHugDistance = (tracePath, obstacleLabel) => {
|
|
5128
|
+
const bounds = getRectBounds(
|
|
5129
|
+
obstacleLabel.center,
|
|
5130
|
+
obstacleLabel.width,
|
|
5131
|
+
obstacleLabel.height
|
|
5132
|
+
);
|
|
5133
|
+
let distance3 = 0;
|
|
5134
|
+
for (const point of tracePath) {
|
|
5135
|
+
distance3 += getPointDistanceFromRect(point, bounds);
|
|
5136
|
+
}
|
|
5137
|
+
return distance3;
|
|
5138
|
+
};
|
|
5139
|
+
var getPointDistanceFromRect = (point, rect) => {
|
|
5140
|
+
const dx = Math.max(rect.minX - point.x, 0, point.x - rect.maxX);
|
|
5141
|
+
const dy = Math.max(rect.minY - point.y, 0, point.y - rect.maxY);
|
|
5142
|
+
return dx + dy;
|
|
5143
|
+
};
|
|
5144
|
+
|
|
5145
|
+
// lib/solvers/Example28Solver/visualize.ts
|
|
5146
|
+
var CANDIDATE_SELECTED_COLOR = "blue";
|
|
5147
|
+
var CANDIDATE_REJECTED_COLOR = "red";
|
|
5148
|
+
var visualizeExample28Solver = (state) => {
|
|
5149
|
+
const graphics = visualizeInputProblem(state.inputProblem);
|
|
5150
|
+
drawTraces(graphics, state);
|
|
5151
|
+
drawNetLabels(graphics, state);
|
|
5152
|
+
if (!state.solved) {
|
|
5153
|
+
drawCurrentCandidates(graphics, state.currentCandidateResults);
|
|
5154
|
+
drawCurrentOverlap(graphics, state);
|
|
5155
|
+
}
|
|
5156
|
+
return graphics;
|
|
5157
|
+
};
|
|
5158
|
+
var drawTraces = (graphics, state) => {
|
|
5159
|
+
for (const trace of state.outputTraces) {
|
|
5160
|
+
graphics.lines.push({
|
|
5161
|
+
points: trace.tracePath,
|
|
5162
|
+
strokeColor: "purple"
|
|
5163
|
+
});
|
|
5164
|
+
}
|
|
5165
|
+
};
|
|
5166
|
+
var drawNetLabels = (graphics, state) => {
|
|
5167
|
+
for (const label of state.outputNetLabelPlacements) {
|
|
5168
|
+
const color = getColorFromString(label.globalConnNetId, 0.35);
|
|
5169
|
+
graphics.rects.push({
|
|
5170
|
+
center: label.center,
|
|
5171
|
+
width: label.width,
|
|
5172
|
+
height: label.height,
|
|
5173
|
+
fill: color,
|
|
5174
|
+
strokeColor: getColorFromString(label.globalConnNetId, 0.9),
|
|
5175
|
+
label: `netId: ${label.netId}
|
|
5176
|
+
globalConnNetId: ${label.globalConnNetId}`
|
|
5177
|
+
});
|
|
5178
|
+
graphics.points.push({
|
|
5179
|
+
...label.anchorPoint,
|
|
5180
|
+
color: getColorFromString(label.globalConnNetId, 0.9),
|
|
5181
|
+
label: `anchorPoint
|
|
5182
|
+
orientation: ${label.orientation}`
|
|
5183
|
+
});
|
|
5184
|
+
}
|
|
5185
|
+
};
|
|
5186
|
+
var drawCurrentCandidates = (graphics, candidateResults) => {
|
|
5187
|
+
for (const candidate of candidateResults) {
|
|
5188
|
+
const scoreLabel = candidate.score ? `
|
|
5189
|
+
label intersections: ${candidate.score.labelIntersections}
|
|
5190
|
+
trace intersections: ${candidate.score.traceIntersections}` : "";
|
|
5191
|
+
graphics.lines.push({
|
|
5192
|
+
points: candidate.path,
|
|
5193
|
+
strokeColor: candidate.selected ? CANDIDATE_SELECTED_COLOR : CANDIDATE_REJECTED_COLOR,
|
|
5194
|
+
strokeDash: candidate.selected ? void 0 : "4 2",
|
|
5195
|
+
label: `${candidate.selected ? "selected" : candidate.status} reroute${scoreLabel}`
|
|
5196
|
+
});
|
|
5197
|
+
}
|
|
5198
|
+
};
|
|
5199
|
+
var drawCurrentOverlap = (graphics, state) => {
|
|
5200
|
+
if (!state.currentOverlap) return;
|
|
5201
|
+
const label = state.currentOverlap.label;
|
|
5202
|
+
graphics.rects.push({
|
|
5203
|
+
center: label.center,
|
|
5204
|
+
width: label.width,
|
|
5205
|
+
height: label.height,
|
|
5206
|
+
fill: "rgba(255, 0, 0, 0.2)",
|
|
5207
|
+
strokeColor: CANDIDATE_REJECTED_COLOR,
|
|
5208
|
+
label: `overlap target
|
|
5209
|
+
${label.netId ?? label.globalConnNetId}`
|
|
5210
|
+
});
|
|
5211
|
+
};
|
|
5212
|
+
|
|
5213
|
+
// lib/solvers/Example28Solver/Example28Solver.ts
|
|
5214
|
+
var Example28Solver = class extends BaseSolver {
|
|
5215
|
+
inputProblem;
|
|
5216
|
+
traces;
|
|
5217
|
+
netLabelPlacements;
|
|
5218
|
+
outputTraces;
|
|
5219
|
+
outputNetLabelPlacements;
|
|
5220
|
+
queuedOverlaps = [];
|
|
5221
|
+
currentOverlap = null;
|
|
5222
|
+
currentCandidateResults = [];
|
|
5223
|
+
chipObstacles;
|
|
5224
|
+
constructor(params) {
|
|
5225
|
+
super();
|
|
5226
|
+
this.inputProblem = params.inputProblem;
|
|
5227
|
+
this.traces = params.traces;
|
|
5228
|
+
this.netLabelPlacements = params.netLabelPlacements;
|
|
5229
|
+
this.outputTraces = [...params.traces];
|
|
5230
|
+
this.outputNetLabelPlacements = [...params.netLabelPlacements];
|
|
5231
|
+
this.chipObstacles = getObstacleRects(params.inputProblem);
|
|
5232
|
+
this.initializeQueuedOverlaps();
|
|
5233
|
+
this.currentOverlap = this.queuedOverlaps[0] ?? null;
|
|
5234
|
+
}
|
|
5235
|
+
getConstructorParams() {
|
|
5236
|
+
return {
|
|
5237
|
+
inputProblem: this.inputProblem,
|
|
5238
|
+
traces: this.traces,
|
|
5239
|
+
netLabelPlacements: this.netLabelPlacements
|
|
5240
|
+
};
|
|
5241
|
+
}
|
|
5242
|
+
_step() {
|
|
5243
|
+
const overlap = this.queuedOverlaps.shift();
|
|
5244
|
+
if (!overlap) {
|
|
5245
|
+
this.finish();
|
|
5246
|
+
return;
|
|
5247
|
+
}
|
|
5248
|
+
if (!this.isOverlapStillPresent(overlap)) {
|
|
5249
|
+
this.currentOverlap = overlap;
|
|
5250
|
+
this.currentCandidateResults = [];
|
|
5251
|
+
return;
|
|
5252
|
+
}
|
|
5253
|
+
this.rerouteOverlappingTrace(overlap);
|
|
5254
|
+
}
|
|
5255
|
+
finish() {
|
|
5256
|
+
this.currentOverlap = null;
|
|
5257
|
+
this.currentCandidateResults = [];
|
|
5258
|
+
this.solved = true;
|
|
5259
|
+
}
|
|
5260
|
+
initializeQueuedOverlaps() {
|
|
5261
|
+
const queuedOverlaps = [];
|
|
5262
|
+
const seenPairs = /* @__PURE__ */ new Set();
|
|
5263
|
+
for (const overlap of this.getTraceLabelOverlaps()) {
|
|
5264
|
+
if (!this.shouldRerouteOverlap(overlap)) continue;
|
|
5265
|
+
const key = this.getOverlapKey(overlap);
|
|
5266
|
+
if (seenPairs.has(key)) continue;
|
|
5267
|
+
seenPairs.add(key);
|
|
5268
|
+
queuedOverlaps.push(overlap);
|
|
5269
|
+
}
|
|
5270
|
+
this.queuedOverlaps = queuedOverlaps;
|
|
5271
|
+
}
|
|
5272
|
+
getTraceLabelOverlaps() {
|
|
5273
|
+
return detectTraceLabelOverlap({
|
|
5274
|
+
traces: this.outputTraces,
|
|
5275
|
+
netLabels: this.outputNetLabelPlacements
|
|
5276
|
+
});
|
|
5277
|
+
}
|
|
5278
|
+
shouldRerouteOverlap(overlap) {
|
|
5279
|
+
return this.isXFacingLabel(overlap.label) && !this.hasExplicitOrientationConstraint(overlap.label);
|
|
5280
|
+
}
|
|
5281
|
+
isXFacingLabel(label) {
|
|
5282
|
+
return label.orientation === "x+" || label.orientation === "x-";
|
|
5283
|
+
}
|
|
5284
|
+
hasExplicitOrientationConstraint(label) {
|
|
5285
|
+
const effectiveNetId = label.netId ?? label.globalConnNetId;
|
|
5286
|
+
return Object.hasOwn(
|
|
5287
|
+
this.inputProblem.availableNetLabelOrientations,
|
|
5288
|
+
effectiveNetId
|
|
5289
|
+
);
|
|
5290
|
+
}
|
|
5291
|
+
getOverlapKey(overlap) {
|
|
5292
|
+
const labelNetId = overlap.label.netId ?? overlap.label.globalConnNetId;
|
|
5293
|
+
return `${overlap.trace.mspPairId}:${labelNetId}:${overlap.label.anchorPoint.x}:${overlap.label.anchorPoint.y}`;
|
|
5294
|
+
}
|
|
5295
|
+
isOverlapStillPresent(overlap) {
|
|
5296
|
+
const currentTrace = this.getCurrentTrace(overlap.trace);
|
|
5297
|
+
if (!currentTrace) return false;
|
|
5298
|
+
return detectTraceLabelOverlap({
|
|
5299
|
+
traces: [currentTrace],
|
|
5300
|
+
netLabels: [overlap.label]
|
|
5301
|
+
}).length > 0;
|
|
5302
|
+
}
|
|
5303
|
+
getCurrentTrace(trace) {
|
|
5304
|
+
return this.outputTraces.find((t) => t.mspPairId === trace.mspPairId);
|
|
5305
|
+
}
|
|
5306
|
+
rerouteOverlappingTrace(overlap) {
|
|
5307
|
+
this.currentOverlap = overlap;
|
|
5308
|
+
this.currentCandidateResults = [];
|
|
5309
|
+
const traceIndex = this.outputTraces.findIndex(
|
|
5310
|
+
(trace) => trace.mspPairId === overlap.trace.mspPairId
|
|
5311
|
+
);
|
|
5312
|
+
if (traceIndex === -1) return;
|
|
5313
|
+
const currentTrace = this.outputTraces[traceIndex];
|
|
5314
|
+
const rerouteResult = findBestReroutePath({
|
|
5315
|
+
trace: currentTrace,
|
|
5316
|
+
obstacleLabel: overlap.label,
|
|
5317
|
+
inputProblem: this.inputProblem,
|
|
5318
|
+
outputTraces: this.outputTraces,
|
|
5319
|
+
outputNetLabelPlacements: this.outputNetLabelPlacements,
|
|
5320
|
+
chipObstacles: this.chipObstacles
|
|
5321
|
+
});
|
|
5322
|
+
this.currentCandidateResults = rerouteResult.candidateResults;
|
|
5323
|
+
if (!rerouteResult.bestPath) return;
|
|
5324
|
+
const originalTracePath = currentTrace.tracePath;
|
|
5325
|
+
this.outputTraces[traceIndex] = {
|
|
5326
|
+
...currentTrace,
|
|
5327
|
+
tracePath: rerouteResult.bestPath
|
|
5328
|
+
};
|
|
5329
|
+
this.outputNetLabelPlacements = moveAttachedLabelsToReroutedTrace({
|
|
5330
|
+
trace: currentTrace,
|
|
5331
|
+
originalTracePath,
|
|
5332
|
+
reroutedTracePath: rerouteResult.bestPath,
|
|
5333
|
+
netLabelPlacements: this.outputNetLabelPlacements
|
|
5334
|
+
});
|
|
5335
|
+
}
|
|
5336
|
+
visualize() {
|
|
5337
|
+
return visualizeExample28Solver({
|
|
5338
|
+
inputProblem: this.inputProblem,
|
|
5339
|
+
outputTraces: this.outputTraces,
|
|
5340
|
+
outputNetLabelPlacements: this.outputNetLabelPlacements,
|
|
5341
|
+
solved: this.solved,
|
|
5342
|
+
currentOverlap: this.currentOverlap,
|
|
5343
|
+
currentCandidateResults: this.currentCandidateResults
|
|
5344
|
+
});
|
|
5345
|
+
}
|
|
5346
|
+
};
|
|
5347
|
+
|
|
5348
|
+
// lib/solvers/AvailableNetOrientationSolver/constants.ts
|
|
5349
|
+
var LABEL_SEARCH_STEP = 0.05;
|
|
5350
|
+
var WICK_CLEARANCE = 1e-3;
|
|
5351
|
+
var EPS6 = 1e-9;
|
|
5352
|
+
var TRACE_BOUNDARY_TOLERANCE = WICK_CLEARANCE + EPS6;
|
|
5353
|
+
var CANDIDATE_SELECTED_COLOR2 = "blue";
|
|
5354
|
+
var CANDIDATE_REJECTED_COLOR2 = "red";
|
|
5355
|
+
|
|
5356
|
+
// lib/solvers/AvailableNetOrientationSolver/geometry.ts
|
|
5357
|
+
var isYOrientation = (orientation) => orientation === "y+" || orientation === "y-";
|
|
5358
|
+
var isXOrientation = (orientation) => orientation === "x+" || orientation === "x-";
|
|
5359
|
+
var rectsOverlap = (a, b) => Math.min(a.maxX, b.maxX) - Math.max(a.minX, b.minX) > EPS6 && Math.min(a.maxY, b.maxY) - Math.max(a.minY, b.minY) > EPS6;
|
|
5360
|
+
var rangesOverlap = (minA, maxA, minB, maxB) => Math.min(maxA, maxB) - Math.max(minA, minB) > EPS6;
|
|
5361
|
+
var traceCrossesBoundsInterior = (bounds, traceMap) => {
|
|
5362
|
+
for (const trace of Object.values(traceMap)) {
|
|
5363
|
+
const points = trace.tracePath;
|
|
5364
|
+
for (let i = 0; i < points.length - 1; i++) {
|
|
5365
|
+
if (segmentCrossesBoundsInterior(points[i], points[i + 1], bounds)) {
|
|
5366
|
+
return true;
|
|
5367
|
+
}
|
|
5368
|
+
}
|
|
5369
|
+
}
|
|
5370
|
+
return false;
|
|
5371
|
+
};
|
|
5372
|
+
var segmentCrossesBoundsInterior = (p1, p2, bounds) => {
|
|
5373
|
+
const interiorBounds = {
|
|
5374
|
+
minX: bounds.minX + TRACE_BOUNDARY_TOLERANCE,
|
|
5375
|
+
minY: bounds.minY + TRACE_BOUNDARY_TOLERANCE,
|
|
5376
|
+
maxX: bounds.maxX - TRACE_BOUNDARY_TOLERANCE,
|
|
5377
|
+
maxY: bounds.maxY - TRACE_BOUNDARY_TOLERANCE
|
|
5378
|
+
};
|
|
5379
|
+
if (interiorBounds.minX >= interiorBounds.maxX || interiorBounds.minY >= interiorBounds.maxY) {
|
|
5380
|
+
return false;
|
|
5381
|
+
}
|
|
5382
|
+
if (sameX(p1, p2)) {
|
|
5383
|
+
if (p1.x <= interiorBounds.minX + EPS6 || p1.x >= interiorBounds.maxX - EPS6) {
|
|
5384
|
+
return false;
|
|
5385
|
+
}
|
|
5386
|
+
return rangesOverlap(
|
|
5387
|
+
Math.min(p1.y, p2.y),
|
|
5388
|
+
Math.max(p1.y, p2.y),
|
|
5389
|
+
interiorBounds.minY,
|
|
5390
|
+
interiorBounds.maxY
|
|
5391
|
+
);
|
|
5392
|
+
}
|
|
5393
|
+
if (sameY(p1, p2)) {
|
|
5394
|
+
if (p1.y <= interiorBounds.minY + EPS6 || p1.y >= interiorBounds.maxY - EPS6) {
|
|
5395
|
+
return false;
|
|
5396
|
+
}
|
|
5397
|
+
return rangesOverlap(
|
|
5398
|
+
Math.min(p1.x, p2.x),
|
|
5399
|
+
Math.max(p1.x, p2.x),
|
|
5400
|
+
interiorBounds.minX,
|
|
5401
|
+
interiorBounds.maxX
|
|
5402
|
+
);
|
|
5403
|
+
}
|
|
5404
|
+
return false;
|
|
5405
|
+
};
|
|
5406
|
+
var tracePathCrossesAnyTrace = (tracePath, traceMap) => {
|
|
5407
|
+
for (const trace of Object.values(traceMap)) {
|
|
5408
|
+
const points = trace.tracePath;
|
|
5409
|
+
for (let i = 0; i < tracePath.length - 1; i++) {
|
|
5410
|
+
for (let j = 0; j < points.length - 1; j++) {
|
|
5411
|
+
if (segmentsStrictlyCross(
|
|
5412
|
+
tracePath[i],
|
|
5413
|
+
tracePath[i + 1],
|
|
5414
|
+
points[j],
|
|
5415
|
+
points[j + 1]
|
|
5416
|
+
)) {
|
|
5417
|
+
return true;
|
|
5418
|
+
}
|
|
5419
|
+
}
|
|
5420
|
+
}
|
|
5421
|
+
}
|
|
5422
|
+
return false;
|
|
5423
|
+
};
|
|
5424
|
+
var segmentsStrictlyCross = (a1, a2, b1, b2) => {
|
|
5425
|
+
if (sameX(a1, a2) && sameY(b1, b2)) {
|
|
5426
|
+
return a1.x > Math.min(b1.x, b2.x) + EPS6 && a1.x < Math.max(b1.x, b2.x) - EPS6 && b1.y > Math.min(a1.y, a2.y) + EPS6 && b1.y < Math.max(a1.y, a2.y) - EPS6;
|
|
5427
|
+
}
|
|
5428
|
+
if (sameY(a1, a2) && sameX(b1, b2)) {
|
|
5429
|
+
return b1.x > Math.min(a1.x, a2.x) + EPS6 && b1.x < Math.max(a1.x, a2.x) - EPS6 && a1.y > Math.min(b1.y, b2.y) + EPS6 && a1.y < Math.max(b1.y, b2.y) - EPS6;
|
|
5430
|
+
}
|
|
5431
|
+
return false;
|
|
5432
|
+
};
|
|
5433
|
+
var getSideDistances = (point, bounds) => [
|
|
5434
|
+
["left", Math.abs(point.x - bounds.minX)],
|
|
5435
|
+
["right", Math.abs(point.x - bounds.maxX)],
|
|
5436
|
+
["bottom", Math.abs(point.y - bounds.minY)],
|
|
5437
|
+
["top", Math.abs(point.y - bounds.maxY)]
|
|
5438
|
+
];
|
|
5439
|
+
var getConnectorTracePath = (source, target, orientation) => simplifyOrthogonalPath(
|
|
5440
|
+
isYOrientation(orientation) ? [source, { x: target.x, y: source.y }, target] : [source, { x: source.x, y: target.y }, target]
|
|
5441
|
+
);
|
|
5442
|
+
var simplifyOrthogonalPath = (path) => {
|
|
5443
|
+
const deduped = path.filter(
|
|
5444
|
+
(point, index) => index === 0 || !pointsEqual(point, path[index - 1])
|
|
5445
|
+
);
|
|
5446
|
+
if (deduped.length < 3) return deduped;
|
|
5447
|
+
const simplified = [deduped[0]];
|
|
5448
|
+
for (let i = 1; i < deduped.length - 1; i++) {
|
|
5449
|
+
const prev = simplified[simplified.length - 1];
|
|
5450
|
+
const point = deduped[i];
|
|
5451
|
+
const next = deduped[i + 1];
|
|
5452
|
+
if (sameX(prev, point) && sameX(point, next) || sameY(prev, point) && sameY(point, next)) {
|
|
5453
|
+
continue;
|
|
5454
|
+
}
|
|
5455
|
+
simplified.push(point);
|
|
5456
|
+
}
|
|
5457
|
+
simplified.push(deduped[deduped.length - 1]);
|
|
5458
|
+
return simplified;
|
|
5459
|
+
};
|
|
5460
|
+
var pointsEqual = (a, b) => sameX(a, b) && sameY(a, b);
|
|
5461
|
+
var sameX = (a, b) => Math.abs(a.x - b.x) <= EPS6;
|
|
5462
|
+
var sameY = (a, b) => Math.abs(a.y - b.y) <= EPS6;
|
|
5463
|
+
var getMaxSearchDistance = (inputProblem) => {
|
|
5464
|
+
const maxChipWidth = Math.max(
|
|
5465
|
+
...inputProblem.chips.map((chip) => chip.width),
|
|
5466
|
+
1
|
|
5467
|
+
);
|
|
5468
|
+
return maxChipWidth * 3;
|
|
5469
|
+
};
|
|
5470
|
+
|
|
5471
|
+
// lib/solvers/AvailableNetOrientationSolver/traces.ts
|
|
5472
|
+
var getPinMap = (inputProblem) => {
|
|
5473
|
+
const pinMap = {};
|
|
5474
|
+
for (const chip of inputProblem.chips) {
|
|
5475
|
+
for (const pin of chip.pins) {
|
|
5476
|
+
pinMap[pin.pinId] = { ...pin, chipId: chip.chipId };
|
|
5477
|
+
}
|
|
5478
|
+
}
|
|
5479
|
+
return pinMap;
|
|
5480
|
+
};
|
|
5481
|
+
var getTracePins = (label, pinMap) => {
|
|
5482
|
+
const pins = label.pinIds.flatMap((pinId) => {
|
|
5483
|
+
const pin = pinMap[pinId];
|
|
5484
|
+
return pin ? [pin] : [];
|
|
5485
|
+
});
|
|
5486
|
+
if (pins.length >= 2) return [pins[0], pins[1]];
|
|
5487
|
+
if (pins.length === 1) return [pins[0], pins[0]];
|
|
5488
|
+
const syntheticPin = {
|
|
5489
|
+
pinId: `${label.globalConnNetId}-netlabel-anchor`,
|
|
5490
|
+
x: label.anchorPoint.x,
|
|
5491
|
+
y: label.anchorPoint.y,
|
|
5492
|
+
chipId: "available-net-orientation"
|
|
5493
|
+
};
|
|
5494
|
+
return [syntheticPin, syntheticPin];
|
|
5495
|
+
};
|
|
5496
|
+
var toNetLabelPlacementPatch = (candidate) => ({
|
|
5497
|
+
orientation: candidate.orientation,
|
|
5498
|
+
anchorPoint: candidate.anchorPoint,
|
|
5499
|
+
center: candidate.center,
|
|
5500
|
+
width: candidate.width,
|
|
5501
|
+
height: candidate.height
|
|
5502
|
+
});
|
|
5503
|
+
|
|
5504
|
+
// lib/solvers/AvailableNetOrientationSolver/visualize.ts
|
|
5505
|
+
var visualizeAvailableNetOrientationSolver = (params) => {
|
|
5506
|
+
const graphics = visualizeInputProblem(params.inputProblem);
|
|
5507
|
+
ensureGraphicsArrays(graphics);
|
|
5508
|
+
drawTraces2(graphics, params.traces);
|
|
5509
|
+
drawNetLabels2(graphics, params.outputNetLabelPlacements);
|
|
5510
|
+
drawCurrentLabel(graphics, params.currentLabel, params.solved);
|
|
5511
|
+
drawCurrentCandidates2(graphics, params.currentCandidateResults, params.solved);
|
|
5512
|
+
return graphics;
|
|
5513
|
+
};
|
|
5514
|
+
var drawTraces2 = (graphics, traces) => {
|
|
5515
|
+
for (const trace of traces) {
|
|
5516
|
+
graphics.lines.push({
|
|
5517
|
+
points: trace.tracePath,
|
|
5518
|
+
strokeColor: "purple"
|
|
5519
|
+
});
|
|
5520
|
+
}
|
|
5521
|
+
};
|
|
5522
|
+
var drawNetLabels2 = (graphics, labels) => {
|
|
5523
|
+
for (const label of labels) {
|
|
5524
|
+
graphics.rects.push({
|
|
5525
|
+
center: label.center,
|
|
5526
|
+
width: label.width,
|
|
5527
|
+
height: label.height,
|
|
5528
|
+
fill: getColorFromString(label.globalConnNetId, 0.35),
|
|
5529
|
+
strokeColor: getColorFromString(label.globalConnNetId, 0.9),
|
|
5530
|
+
label: `netId: ${label.netId}
|
|
5531
|
+
globalConnNetId: ${label.globalConnNetId}`
|
|
5532
|
+
});
|
|
5533
|
+
graphics.points.push({
|
|
5534
|
+
x: label.anchorPoint.x,
|
|
5535
|
+
y: label.anchorPoint.y,
|
|
5536
|
+
color: getColorFromString(label.globalConnNetId, 0.9),
|
|
5537
|
+
label: `anchorPoint
|
|
5538
|
+
orientation: ${label.orientation}`
|
|
5539
|
+
});
|
|
5540
|
+
}
|
|
5541
|
+
};
|
|
5542
|
+
var drawCurrentLabel = (graphics, currentLabel, solved) => {
|
|
5543
|
+
if (!currentLabel || solved) return;
|
|
5544
|
+
graphics.rects.push({
|
|
5545
|
+
center: currentLabel.center,
|
|
5546
|
+
width: currentLabel.width,
|
|
5547
|
+
height: currentLabel.height,
|
|
5548
|
+
fill: "rgba(255, 0, 0, 0.2)",
|
|
5549
|
+
strokeColor: CANDIDATE_REJECTED_COLOR2,
|
|
5550
|
+
label: `available orientation target
|
|
5551
|
+
${currentLabel.netId ?? currentLabel.globalConnNetId}`
|
|
5552
|
+
});
|
|
5553
|
+
};
|
|
5554
|
+
var drawCurrentCandidates2 = (graphics, candidates, solved) => {
|
|
5555
|
+
if (solved) return;
|
|
5556
|
+
for (const candidate of candidates) {
|
|
5557
|
+
const color = candidate.selected ? CANDIDATE_SELECTED_COLOR2 : CANDIDATE_REJECTED_COLOR2;
|
|
5558
|
+
const distanceLabel = candidate.distance === void 0 ? "" : `
|
|
5559
|
+
distance: ${candidate.distance.toFixed(3)}`;
|
|
5560
|
+
const outwardDistanceLabel = candidate.outwardDistance === void 0 || candidate.outwardDistance === 0 ? "" : `
|
|
5561
|
+
outward distance: ${candidate.outwardDistance.toFixed(3)}`;
|
|
5562
|
+
graphics.rects.push({
|
|
5563
|
+
center: candidate.center,
|
|
5564
|
+
width: candidate.width,
|
|
5565
|
+
height: candidate.height,
|
|
5566
|
+
fill: candidate.selected ? "rgba(0, 0, 255, 0.2)" : "rgba(255, 0, 0, 0.15)",
|
|
5567
|
+
strokeColor: color,
|
|
5568
|
+
strokeDash: candidate.selected ? void 0 : "4 2",
|
|
5569
|
+
label: `${candidate.selected ? "selected" : candidate.status} available orientation
|
|
5570
|
+
phase: ${candidate.phase}
|
|
5571
|
+
orientation: ${candidate.orientation}${distanceLabel}${outwardDistanceLabel}`
|
|
5572
|
+
});
|
|
5573
|
+
graphics.points.push({
|
|
5574
|
+
x: candidate.anchorPoint.x,
|
|
5575
|
+
y: candidate.anchorPoint.y,
|
|
5576
|
+
color,
|
|
5577
|
+
label: `candidate anchor
|
|
5578
|
+
${candidate.phase}
|
|
5579
|
+
${candidate.orientation}`
|
|
5580
|
+
});
|
|
5581
|
+
}
|
|
5582
|
+
};
|
|
5583
|
+
var ensureGraphicsArrays = (graphics) => {
|
|
5584
|
+
if (!graphics.lines) graphics.lines = [];
|
|
5585
|
+
if (!graphics.points) graphics.points = [];
|
|
5586
|
+
if (!graphics.rects) graphics.rects = [];
|
|
5587
|
+
if (!graphics.circles) graphics.circles = [];
|
|
5588
|
+
if (!graphics.texts) graphics.texts = [];
|
|
5589
|
+
};
|
|
5590
|
+
|
|
5591
|
+
// lib/solvers/AvailableNetOrientationSolver/AvailableNetOrientationSolver.ts
|
|
5592
|
+
var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
5593
|
+
inputProblem;
|
|
5594
|
+
traces;
|
|
5595
|
+
netLabelPlacements;
|
|
5596
|
+
outputNetLabelPlacements;
|
|
5597
|
+
queuedLabelIndices = [];
|
|
5598
|
+
currentLabelIndex = null;
|
|
5599
|
+
currentLabel = null;
|
|
5600
|
+
currentCandidateResults = [];
|
|
5601
|
+
traceMap;
|
|
5602
|
+
chipObstacleSpatialIndex;
|
|
5603
|
+
maxSearchDistance;
|
|
5604
|
+
pinMap;
|
|
5605
|
+
constructor(params) {
|
|
5606
|
+
super();
|
|
5607
|
+
this.inputProblem = params.inputProblem;
|
|
5608
|
+
this.traces = [...params.traces];
|
|
5609
|
+
this.netLabelPlacements = params.netLabelPlacements;
|
|
5610
|
+
this.outputNetLabelPlacements = [...params.netLabelPlacements];
|
|
5611
|
+
this.traceMap = Object.fromEntries(
|
|
5612
|
+
this.traces.map((trace) => [trace.mspPairId, trace])
|
|
5613
|
+
);
|
|
5614
|
+
this.pinMap = getPinMap(params.inputProblem);
|
|
5615
|
+
this.chipObstacleSpatialIndex = params.inputProblem._chipObstacleSpatialIndex ?? new ChipObstacleSpatialIndex(params.inputProblem.chips);
|
|
5616
|
+
this.maxSearchDistance = getMaxSearchDistance(params.inputProblem);
|
|
5617
|
+
this.queuedLabelIndices = this.getProcessableLabelIndices();
|
|
5618
|
+
this.setCurrentLabel(this.queuedLabelIndices[0] ?? null);
|
|
5619
|
+
}
|
|
5620
|
+
getConstructorParams() {
|
|
5621
|
+
return {
|
|
5622
|
+
inputProblem: this.inputProblem,
|
|
5623
|
+
traces: this.traces,
|
|
5624
|
+
netLabelPlacements: this.netLabelPlacements
|
|
5625
|
+
};
|
|
5626
|
+
}
|
|
5627
|
+
_step() {
|
|
5628
|
+
const labelIndex = this.queuedLabelIndices.shift();
|
|
5629
|
+
if (labelIndex === void 0) {
|
|
5630
|
+
this.finish();
|
|
5631
|
+
return;
|
|
5632
|
+
}
|
|
5633
|
+
this.processLabel(labelIndex);
|
|
5634
|
+
}
|
|
5635
|
+
getOutput() {
|
|
5636
|
+
return {
|
|
5637
|
+
traces: this.traces,
|
|
5638
|
+
netLabelPlacements: this.outputNetLabelPlacements
|
|
5639
|
+
};
|
|
5640
|
+
}
|
|
5641
|
+
getProcessableLabelIndices() {
|
|
5642
|
+
const indices = [];
|
|
5643
|
+
for (let i = 0; i < this.outputNetLabelPlacements.length; i++) {
|
|
5644
|
+
if (this.shouldProcessLabel(this.outputNetLabelPlacements[i], i)) {
|
|
5645
|
+
indices.push(i);
|
|
5646
|
+
}
|
|
5647
|
+
}
|
|
5648
|
+
return indices;
|
|
5649
|
+
}
|
|
5650
|
+
shouldProcessLabel(label, labelIndex) {
|
|
5651
|
+
const orientations = this.getAvailableOrientations(label);
|
|
5652
|
+
if (orientations.length === 0) return false;
|
|
5653
|
+
return !(orientations.includes(label.orientation) && this.isLabelPlacementValid(label, labelIndex));
|
|
5654
|
+
}
|
|
5655
|
+
processLabel(labelIndex) {
|
|
5656
|
+
const label = this.outputNetLabelPlacements[labelIndex];
|
|
5657
|
+
this.setCurrentLabel(labelIndex);
|
|
5658
|
+
this.currentCandidateResults = [];
|
|
5659
|
+
const candidate = this.findCorrectedCandidate(label, labelIndex);
|
|
5660
|
+
if (!candidate) return;
|
|
5661
|
+
this.applyCandidate(label, candidate, labelIndex);
|
|
5662
|
+
}
|
|
5663
|
+
applyCandidate(label, candidate, labelIndex) {
|
|
5664
|
+
this.outputNetLabelPlacements[labelIndex] = {
|
|
5665
|
+
...label,
|
|
5666
|
+
...toNetLabelPlacementPatch(candidate)
|
|
5667
|
+
};
|
|
5668
|
+
this.addConnectorTrace(label, candidate, labelIndex);
|
|
5669
|
+
}
|
|
5670
|
+
addConnectorTrace(label, candidate, labelIndex) {
|
|
5671
|
+
const tracePath = getConnectorTracePath(
|
|
5672
|
+
label.anchorPoint,
|
|
5673
|
+
candidate.anchorPoint,
|
|
5674
|
+
candidate.orientation
|
|
5675
|
+
);
|
|
5676
|
+
if (tracePath.length < 2) return;
|
|
5677
|
+
const mspPairId = `available-net-orientation-${labelIndex}-${label.netId ?? label.globalConnNetId}`;
|
|
5678
|
+
const connectorTrace = {
|
|
5679
|
+
mspPairId,
|
|
5680
|
+
dcConnNetId: label.dcConnNetId ?? label.globalConnNetId,
|
|
5681
|
+
globalConnNetId: label.globalConnNetId,
|
|
5682
|
+
userNetId: label.netId,
|
|
5683
|
+
pins: getTracePins(label, this.pinMap),
|
|
5684
|
+
tracePath,
|
|
5685
|
+
mspConnectionPairIds: [mspPairId],
|
|
5686
|
+
pinIds: label.pinIds
|
|
5687
|
+
};
|
|
5688
|
+
this.traces.push(connectorTrace);
|
|
5689
|
+
this.traceMap[mspPairId] = connectorTrace;
|
|
5690
|
+
}
|
|
5691
|
+
finish() {
|
|
5692
|
+
this.currentLabelIndex = null;
|
|
5693
|
+
this.currentLabel = null;
|
|
5694
|
+
this.currentCandidateResults = [];
|
|
5695
|
+
this.solved = true;
|
|
5696
|
+
}
|
|
5697
|
+
setCurrentLabel(labelIndex) {
|
|
5698
|
+
this.currentLabelIndex = labelIndex;
|
|
5699
|
+
this.currentLabel = labelIndex === null ? null : this.outputNetLabelPlacements[labelIndex];
|
|
5700
|
+
}
|
|
5701
|
+
findCorrectedCandidate(label, labelIndex) {
|
|
5702
|
+
const orientations = this.getAvailableOrientations(label);
|
|
5703
|
+
const rotatedCandidate = this.findValidRotatedCandidate(
|
|
5704
|
+
label,
|
|
5705
|
+
labelIndex,
|
|
5706
|
+
orientations
|
|
5707
|
+
);
|
|
5708
|
+
return rotatedCandidate ?? this.findValidShiftedCandidate(label, orientations[0], labelIndex);
|
|
5709
|
+
}
|
|
5710
|
+
findValidRotatedCandidate(label, labelIndex, orientations) {
|
|
5711
|
+
for (const orientation of orientations) {
|
|
5712
|
+
const candidate = this.createCandidate(
|
|
5713
|
+
label,
|
|
5714
|
+
this.getSearchStartAnchor(label, orientation),
|
|
5715
|
+
orientation
|
|
5716
|
+
);
|
|
5717
|
+
const result = this.evaluateCandidate(
|
|
5718
|
+
candidate,
|
|
5719
|
+
label,
|
|
5720
|
+
labelIndex,
|
|
5721
|
+
"rotate"
|
|
5722
|
+
);
|
|
5723
|
+
this.currentCandidateResults.push(result);
|
|
5724
|
+
if (result.status === "valid") {
|
|
5725
|
+
result.selected = true;
|
|
5726
|
+
return result;
|
|
5727
|
+
}
|
|
5728
|
+
}
|
|
5729
|
+
return null;
|
|
5730
|
+
}
|
|
5731
|
+
getAvailableOrientations(label) {
|
|
5732
|
+
const effectiveNetId = label.netId ?? label.globalConnNetId;
|
|
5733
|
+
return this.inputProblem.availableNetLabelOrientations[effectiveNetId] ?? [];
|
|
5734
|
+
}
|
|
5735
|
+
findValidShiftedCandidate(label, orientation, labelIndex) {
|
|
5736
|
+
const direction = dir(orientation);
|
|
5737
|
+
const initialBaseAnchor = this.getSearchStartAnchor(label, orientation);
|
|
5738
|
+
const outwardDirection = this.getPerpendicularOutwardDirection(
|
|
5739
|
+
label.anchorPoint,
|
|
5740
|
+
orientation
|
|
5741
|
+
);
|
|
5742
|
+
const maxSearchDistance = this.getSearchDistanceLimit(label, orientation);
|
|
5743
|
+
const maxOutwardDistance = outwardDirection.x === 0 && outwardDirection.y === 0 ? 0 : this.maxSearchDistance;
|
|
5744
|
+
for (let outwardDistance = 0; outwardDistance <= maxOutwardDistance + EPS6; outwardDistance += LABEL_SEARCH_STEP) {
|
|
5745
|
+
const baseAnchor = {
|
|
5746
|
+
x: initialBaseAnchor.x + outwardDirection.x * outwardDistance,
|
|
5747
|
+
y: initialBaseAnchor.y + outwardDirection.y * outwardDistance
|
|
5748
|
+
};
|
|
5749
|
+
const candidate = this.findValidCandidateInShiftColumn({
|
|
5750
|
+
label,
|
|
5751
|
+
labelIndex,
|
|
5752
|
+
orientation,
|
|
5753
|
+
direction,
|
|
5754
|
+
baseAnchor,
|
|
5755
|
+
maxSearchDistance,
|
|
5756
|
+
outwardDistance
|
|
5757
|
+
});
|
|
5758
|
+
if (candidate) return candidate;
|
|
5759
|
+
}
|
|
5760
|
+
return null;
|
|
5761
|
+
}
|
|
5762
|
+
findValidCandidateInShiftColumn(params) {
|
|
5763
|
+
const {
|
|
5764
|
+
label,
|
|
5765
|
+
labelIndex,
|
|
5766
|
+
orientation,
|
|
5767
|
+
direction,
|
|
5768
|
+
baseAnchor,
|
|
5769
|
+
maxSearchDistance,
|
|
5770
|
+
outwardDistance
|
|
5771
|
+
} = params;
|
|
5772
|
+
for (let distance3 = LABEL_SEARCH_STEP; distance3 <= maxSearchDistance + EPS6; distance3 += LABEL_SEARCH_STEP) {
|
|
5773
|
+
const anchorPoint = {
|
|
5774
|
+
x: baseAnchor.x + direction.x * distance3,
|
|
5775
|
+
y: baseAnchor.y + direction.y * distance3
|
|
5776
|
+
};
|
|
5777
|
+
const candidate = this.createCandidate(label, anchorPoint, orientation);
|
|
5778
|
+
const result = this.evaluateCandidate(
|
|
5779
|
+
candidate,
|
|
5780
|
+
label,
|
|
5781
|
+
labelIndex,
|
|
5782
|
+
"shift",
|
|
5783
|
+
distance3,
|
|
5784
|
+
outwardDistance
|
|
5785
|
+
);
|
|
5786
|
+
this.currentCandidateResults.push(result);
|
|
5787
|
+
if (result.status === "valid") {
|
|
5788
|
+
result.selected = true;
|
|
5789
|
+
return result;
|
|
5790
|
+
}
|
|
5791
|
+
if (result.status === "trace-collision") break;
|
|
5792
|
+
}
|
|
5793
|
+
return null;
|
|
5794
|
+
}
|
|
5795
|
+
evaluateCandidate(candidate, label, labelIndex, phase, distance3, outwardDistance) {
|
|
5796
|
+
return {
|
|
5797
|
+
...candidate,
|
|
5798
|
+
phase,
|
|
5799
|
+
distance: distance3,
|
|
5800
|
+
outwardDistance,
|
|
5801
|
+
selected: false,
|
|
5802
|
+
status: this.getCandidateStatus(candidate, label, labelIndex)
|
|
5803
|
+
};
|
|
5804
|
+
}
|
|
5805
|
+
getSearchStartAnchor(label, orientation) {
|
|
5806
|
+
const anchorPoint = this.getWickOffsetAnchor(label.anchorPoint, orientation);
|
|
5807
|
+
const { width, height } = getDimsForOrientation({
|
|
5808
|
+
orientation,
|
|
5809
|
+
netLabelWidth: this.getNetLabelWidth(label)
|
|
5810
|
+
});
|
|
5811
|
+
return this.getSideOffsetAnchor({
|
|
5812
|
+
anchorPoint,
|
|
5813
|
+
labelAnchorPoint: label.anchorPoint,
|
|
5814
|
+
orientation,
|
|
5815
|
+
width,
|
|
5816
|
+
height
|
|
5817
|
+
});
|
|
5818
|
+
}
|
|
5819
|
+
getWickOffsetAnchor(anchorPoint, orientation) {
|
|
5820
|
+
const direction = dir(orientation);
|
|
5821
|
+
return {
|
|
5822
|
+
x: anchorPoint.x + direction.x * WICK_CLEARANCE,
|
|
5823
|
+
y: anchorPoint.y + direction.y * WICK_CLEARANCE
|
|
5824
|
+
};
|
|
5825
|
+
}
|
|
5826
|
+
getSideOffsetAnchor(params) {
|
|
5827
|
+
const { anchorPoint, labelAnchorPoint, orientation, width, height } = params;
|
|
5828
|
+
const chipSide = this.getChipSideForPoint(labelAnchorPoint);
|
|
5829
|
+
if (isYOrientation(orientation) && chipSide === "left") {
|
|
5830
|
+
return {
|
|
5831
|
+
x: anchorPoint.x - width / 2 - WICK_CLEARANCE,
|
|
5832
|
+
y: anchorPoint.y
|
|
5833
|
+
};
|
|
5834
|
+
}
|
|
5835
|
+
if (isYOrientation(orientation) && chipSide === "right") {
|
|
5836
|
+
return {
|
|
5837
|
+
x: anchorPoint.x + width / 2 + WICK_CLEARANCE,
|
|
5838
|
+
y: anchorPoint.y
|
|
5839
|
+
};
|
|
5840
|
+
}
|
|
5841
|
+
if (isXOrientation(orientation) && chipSide === "bottom") {
|
|
5842
|
+
return {
|
|
5843
|
+
x: anchorPoint.x,
|
|
5844
|
+
y: anchorPoint.y - height / 2 - WICK_CLEARANCE
|
|
5845
|
+
};
|
|
5846
|
+
}
|
|
5847
|
+
if (isXOrientation(orientation) && chipSide === "top") {
|
|
5848
|
+
return {
|
|
5849
|
+
x: anchorPoint.x,
|
|
5850
|
+
y: anchorPoint.y + height / 2 + WICK_CLEARANCE
|
|
5851
|
+
};
|
|
5852
|
+
}
|
|
5853
|
+
return anchorPoint;
|
|
5854
|
+
}
|
|
5855
|
+
getSearchDistanceLimit(label, orientation) {
|
|
5856
|
+
const { width, height } = getDimsForOrientation({
|
|
5857
|
+
orientation,
|
|
5858
|
+
netLabelWidth: this.getNetLabelWidth(label)
|
|
5859
|
+
});
|
|
5860
|
+
const labelLength = orientation === "y+" || orientation === "y-" ? height : width;
|
|
5861
|
+
return Math.min(this.maxSearchDistance, labelLength * 2);
|
|
5862
|
+
}
|
|
5863
|
+
createCandidate(label, anchorPoint, orientation) {
|
|
5864
|
+
const { width, height } = getDimsForOrientation({
|
|
5865
|
+
orientation,
|
|
5866
|
+
netLabelWidth: this.getNetLabelWidth(label)
|
|
5867
|
+
});
|
|
5868
|
+
return {
|
|
5869
|
+
orientation,
|
|
5870
|
+
anchorPoint,
|
|
5871
|
+
width,
|
|
5872
|
+
height,
|
|
5873
|
+
center: getCenterFromAnchor(anchorPoint, orientation, width, height)
|
|
5874
|
+
};
|
|
5875
|
+
}
|
|
5876
|
+
getNetLabelWidth(label) {
|
|
5877
|
+
if (!label.netId) return void 0;
|
|
5878
|
+
return this.inputProblem.netConnections.find(
|
|
5879
|
+
(connection) => connection.netId === label.netId
|
|
5880
|
+
)?.netLabelWidth;
|
|
5881
|
+
}
|
|
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
|
+
getCandidateStatus(candidate, label, labelIndex) {
|
|
5899
|
+
const boundsStatus = this.getBoundsStatus(
|
|
5900
|
+
getRectBounds(candidate.center, candidate.width, candidate.height),
|
|
5901
|
+
labelIndex
|
|
5902
|
+
);
|
|
5903
|
+
if (boundsStatus !== "valid") return boundsStatus;
|
|
5904
|
+
if (tracePathCrossesAnyTrace(
|
|
5905
|
+
getConnectorTracePath(
|
|
5906
|
+
label.anchorPoint,
|
|
5907
|
+
candidate.anchorPoint,
|
|
5908
|
+
candidate.orientation
|
|
5909
|
+
),
|
|
5910
|
+
this.traceMap
|
|
5911
|
+
)) {
|
|
5912
|
+
return "trace-collision";
|
|
5913
|
+
}
|
|
5914
|
+
return "valid";
|
|
5915
|
+
}
|
|
5916
|
+
getBoundsStatus(bounds, labelIndex) {
|
|
5917
|
+
if (this.chipObstacleSpatialIndex.getChipsInBounds(bounds).length > 0) {
|
|
5918
|
+
return "chip-collision";
|
|
5919
|
+
}
|
|
5920
|
+
if (this.sharesChipBoundary(bounds)) {
|
|
5921
|
+
return "chip-collision";
|
|
5922
|
+
}
|
|
5923
|
+
if (traceCrossesBoundsInterior(bounds, this.traceMap)) {
|
|
5924
|
+
return "trace-collision";
|
|
5925
|
+
}
|
|
5926
|
+
if (this.intersectsAnyOtherNetLabel(bounds, labelIndex)) {
|
|
5927
|
+
return "netlabel-collision";
|
|
5928
|
+
}
|
|
5929
|
+
return "valid";
|
|
5930
|
+
}
|
|
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
|
+
intersectsAnyOtherNetLabel(bounds, labelIndex) {
|
|
5941
|
+
for (let i = 0; i < this.outputNetLabelPlacements.length; i++) {
|
|
5942
|
+
if (i === labelIndex) continue;
|
|
5943
|
+
const label = this.outputNetLabelPlacements[i];
|
|
5944
|
+
const otherBounds = getRectBounds(label.center, label.width, label.height);
|
|
5945
|
+
if (rectsOverlap(bounds, otherBounds)) return true;
|
|
5946
|
+
}
|
|
5947
|
+
return false;
|
|
5948
|
+
}
|
|
5949
|
+
sharesChipBoundary(bounds) {
|
|
5950
|
+
for (const chip of this.chipObstacleSpatialIndex.chips) {
|
|
5951
|
+
const chipBounds = chip.bounds;
|
|
5952
|
+
const adjacentToVerticalSide = Math.abs(bounds.minX - chipBounds.maxX) <= WICK_CLEARANCE + EPS6 || Math.abs(bounds.maxX - chipBounds.minX) <= WICK_CLEARANCE + EPS6;
|
|
5953
|
+
const adjacentToHorizontalSide = Math.abs(bounds.minY - chipBounds.maxY) <= WICK_CLEARANCE + EPS6 || Math.abs(bounds.maxY - chipBounds.minY) <= WICK_CLEARANCE + EPS6;
|
|
5954
|
+
if (adjacentToVerticalSide && rangesOverlap(
|
|
5955
|
+
bounds.minY,
|
|
5956
|
+
bounds.maxY,
|
|
5957
|
+
chipBounds.minY,
|
|
5958
|
+
chipBounds.maxY
|
|
5959
|
+
)) {
|
|
5960
|
+
return true;
|
|
5961
|
+
}
|
|
5962
|
+
if (adjacentToHorizontalSide && rangesOverlap(
|
|
5963
|
+
bounds.minX,
|
|
5964
|
+
bounds.maxX,
|
|
5965
|
+
chipBounds.minX,
|
|
5966
|
+
chipBounds.maxX
|
|
5967
|
+
)) {
|
|
5968
|
+
return true;
|
|
5969
|
+
}
|
|
5970
|
+
}
|
|
5971
|
+
return false;
|
|
5972
|
+
}
|
|
5973
|
+
getChipSideForPoint(point) {
|
|
5974
|
+
const containingSide = this.getContainingChipSide(point);
|
|
5975
|
+
if (containingSide) return containingSide;
|
|
5976
|
+
const side = this.getOutsideChipSide(point);
|
|
5977
|
+
if (side) return side;
|
|
5978
|
+
return this.getNearestChipSide(point);
|
|
5979
|
+
}
|
|
5980
|
+
getPerpendicularOutwardDirection(point, orientation) {
|
|
5981
|
+
const chipSide = this.getChipSideForPoint(point);
|
|
5982
|
+
if (isYOrientation(orientation) && chipSide === "left") {
|
|
5983
|
+
return { x: -1, y: 0 };
|
|
5984
|
+
}
|
|
5985
|
+
if (isYOrientation(orientation) && chipSide === "right") {
|
|
5986
|
+
return { x: 1, y: 0 };
|
|
5987
|
+
}
|
|
5988
|
+
if (isXOrientation(orientation) && chipSide === "bottom") {
|
|
5989
|
+
return { x: 0, y: -1 };
|
|
5990
|
+
}
|
|
5991
|
+
if (isXOrientation(orientation) && chipSide === "top") {
|
|
5992
|
+
return { x: 0, y: 1 };
|
|
5993
|
+
}
|
|
5994
|
+
return { x: 0, y: 0 };
|
|
5995
|
+
}
|
|
5996
|
+
getContainingChipSide(point) {
|
|
5997
|
+
let nearestSide = null;
|
|
5998
|
+
let nearestDistance = Number.POSITIVE_INFINITY;
|
|
5999
|
+
for (const chip of this.chipObstacleSpatialIndex.chips) {
|
|
6000
|
+
const bounds = chip.bounds;
|
|
6001
|
+
if (point.x < bounds.minX - EPS6 || point.x > bounds.maxX + EPS6 || point.y < bounds.minY - EPS6 || point.y > bounds.maxY + EPS6) {
|
|
6002
|
+
continue;
|
|
6003
|
+
}
|
|
6004
|
+
for (const [side, distance3] of getSideDistances(point, bounds)) {
|
|
6005
|
+
if (distance3 < nearestDistance) {
|
|
6006
|
+
nearestSide = side;
|
|
6007
|
+
nearestDistance = distance3;
|
|
6008
|
+
}
|
|
6009
|
+
}
|
|
6010
|
+
}
|
|
6011
|
+
return nearestSide;
|
|
6012
|
+
}
|
|
6013
|
+
getOutsideChipSide(point) {
|
|
6014
|
+
let nearestSide = null;
|
|
6015
|
+
let nearestDistanceSq = Number.POSITIVE_INFINITY;
|
|
6016
|
+
for (const chip of this.chipObstacleSpatialIndex.chips) {
|
|
6017
|
+
const bounds = chip.bounds;
|
|
6018
|
+
const dx = point.x < bounds.minX - EPS6 ? bounds.minX - point.x : point.x > bounds.maxX + EPS6 ? point.x - bounds.maxX : 0;
|
|
6019
|
+
const dy = point.y < bounds.minY - EPS6 ? bounds.minY - point.y : point.y > bounds.maxY + EPS6 ? point.y - bounds.maxY : 0;
|
|
6020
|
+
if (dx === 0 && dy === 0) continue;
|
|
6021
|
+
const distanceSq = dx ** 2 + dy ** 2;
|
|
6022
|
+
if (distanceSq >= nearestDistanceSq) continue;
|
|
6023
|
+
nearestDistanceSq = distanceSq;
|
|
6024
|
+
if (dx > 0) {
|
|
6025
|
+
nearestSide = point.x < bounds.minX ? "left" : "right";
|
|
6026
|
+
} else {
|
|
6027
|
+
nearestSide = point.y < bounds.minY ? "bottom" : "top";
|
|
6028
|
+
}
|
|
6029
|
+
}
|
|
6030
|
+
return nearestSide;
|
|
6031
|
+
}
|
|
6032
|
+
getNearestChipSide(point) {
|
|
6033
|
+
let nearestSide = null;
|
|
6034
|
+
let nearestDistance = Number.POSITIVE_INFINITY;
|
|
6035
|
+
for (const chip of this.chipObstacleSpatialIndex.chips) {
|
|
6036
|
+
for (const [side, distance3] of getSideDistances(point, chip.bounds)) {
|
|
6037
|
+
if (distance3 < nearestDistance) {
|
|
6038
|
+
nearestSide = side;
|
|
6039
|
+
nearestDistance = distance3;
|
|
6040
|
+
}
|
|
6041
|
+
}
|
|
6042
|
+
}
|
|
6043
|
+
return nearestSide;
|
|
6044
|
+
}
|
|
6045
|
+
visualize() {
|
|
6046
|
+
return visualizeAvailableNetOrientationSolver({
|
|
6047
|
+
inputProblem: this.inputProblem,
|
|
6048
|
+
traces: this.traces,
|
|
6049
|
+
outputNetLabelPlacements: this.outputNetLabelPlacements,
|
|
6050
|
+
currentLabel: this.currentLabel,
|
|
6051
|
+
currentCandidateResults: this.currentCandidateResults,
|
|
6052
|
+
solved: this.solved
|
|
6053
|
+
});
|
|
6054
|
+
}
|
|
6055
|
+
};
|
|
6056
|
+
|
|
4672
6057
|
// lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts
|
|
4673
6058
|
function definePipelineStep(solverName, solverClass, getConstructorParams, opts = {}) {
|
|
4674
6059
|
return {
|
|
@@ -4689,6 +6074,8 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
|
|
|
4689
6074
|
labelMergingSolver;
|
|
4690
6075
|
traceLabelOverlapAvoidanceSolver;
|
|
4691
6076
|
traceCleanupSolver;
|
|
6077
|
+
example28Solver;
|
|
6078
|
+
availableNetOrientationSolver;
|
|
4692
6079
|
startTimeOfPhase;
|
|
4693
6080
|
endTimeOfPhase;
|
|
4694
6081
|
timeSpentOnPhase;
|
|
@@ -4824,6 +6211,27 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
|
|
|
4824
6211
|
}
|
|
4825
6212
|
];
|
|
4826
6213
|
}
|
|
6214
|
+
),
|
|
6215
|
+
definePipelineStep("example28Solver", Example28Solver, (instance) => {
|
|
6216
|
+
const traces = instance.traceCleanupSolver?.getOutput().traces ?? instance.traceLabelOverlapAvoidanceSolver.getOutput().traces;
|
|
6217
|
+
return [
|
|
6218
|
+
{
|
|
6219
|
+
inputProblem: instance.inputProblem,
|
|
6220
|
+
traces,
|
|
6221
|
+
netLabelPlacements: instance.netLabelPlacementSolver.netLabelPlacements
|
|
6222
|
+
}
|
|
6223
|
+
];
|
|
6224
|
+
}),
|
|
6225
|
+
definePipelineStep(
|
|
6226
|
+
"availableNetOrientationSolver",
|
|
6227
|
+
AvailableNetOrientationSolver,
|
|
6228
|
+
(instance) => [
|
|
6229
|
+
{
|
|
6230
|
+
inputProblem: instance.inputProblem,
|
|
6231
|
+
traces: instance.example28Solver.outputTraces,
|
|
6232
|
+
netLabelPlacements: instance.example28Solver.outputNetLabelPlacements
|
|
6233
|
+
}
|
|
6234
|
+
]
|
|
4827
6235
|
)
|
|
4828
6236
|
];
|
|
4829
6237
|
constructor(inputProblem) {
|