@tscircuit/schematic-trace-solver 0.0.169 → 0.0.171
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.js +216 -107
- package/lib/solvers/AvailableNetOrientationSolver/AvailableNetOrientationSolver.ts +6 -1
- package/lib/solvers/NetLabelToTraceSolver/NetLabelToTraceSolver.ts +17 -4
- package/lib/solvers/NetLabelToTraceSolver/reduceTraceCrossings.ts +180 -0
- package/package.json +1 -1
- package/tests/bug-reports/bug-report-20260707T140410Z/__snapshots__/bug-report-20260707T140410Z.snap.svg +10 -10
- package/tests/bug-reports/bug-report-20260825T103621Z/__snapshots__/bug-report-20260825T103621Z.snap.svg +10 -10
- package/tests/bug-reports/bug-report-20260826T072956Z/__snapshots__/bug-report-20260826T072956Z.snap.svg +10 -10
- package/tests/examples/__snapshots__/example37.snap.svg +2 -2
- package/tests/examples/example37.test.ts +13 -1
- package/tests/repros/__snapshots__/repro-hdc3020-ground-label-alignment.snap.svg +110 -0
- package/tests/repros/assets/repro-hdc3020-ground-label-alignment.input.json +282 -0
- package/tests/repros/repro-hdc3020-ground-label-alignment.test.ts +25 -0
package/dist/index.js
CHANGED
|
@@ -325,19 +325,19 @@ var doesPairCrossRestrictedCenterLines = (params) => {
|
|
|
325
325
|
chipMap
|
|
326
326
|
});
|
|
327
327
|
if (restrictedCenterLines.size === 0) return false;
|
|
328
|
-
const
|
|
328
|
+
const EPS15 = 1e-9;
|
|
329
329
|
const crossesSegment = (a, b) => {
|
|
330
330
|
for (const [, rcl] of restrictedCenterLines) {
|
|
331
331
|
if (rcl.axes.has("x") && typeof rcl.x === "number") {
|
|
332
|
-
if ((a.x - rcl.x) * (b.x - rcl.x) < -
|
|
332
|
+
if ((a.x - rcl.x) * (b.x - rcl.x) < -EPS15) return true;
|
|
333
333
|
}
|
|
334
334
|
if (rcl.axes.has("y") && typeof rcl.y === "number") {
|
|
335
|
-
if ((a.y - rcl.y) * (b.y - rcl.y) < -
|
|
335
|
+
if ((a.y - rcl.y) * (b.y - rcl.y) < -EPS15) return true;
|
|
336
336
|
}
|
|
337
337
|
}
|
|
338
338
|
return false;
|
|
339
339
|
};
|
|
340
|
-
if (Math.abs(p1.x - p2.x) <
|
|
340
|
+
if (Math.abs(p1.x - p2.x) < EPS15 || Math.abs(p1.y - p2.y) < EPS15) {
|
|
341
341
|
return crossesSegment({ x: p1.x, y: p1.y }, { x: p2.x, y: p2.y });
|
|
342
342
|
}
|
|
343
343
|
const elbowHV = { x: p2.x, y: p1.y };
|
|
@@ -1575,8 +1575,8 @@ var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
|
|
|
1575
1575
|
if (!collision) {
|
|
1576
1576
|
const first = path[0];
|
|
1577
1577
|
const last = path[path.length - 1];
|
|
1578
|
-
const
|
|
1579
|
-
const samePoint = (p, q) => Math.abs(p.x - q.x) <
|
|
1578
|
+
const EPS15 = 1e-9;
|
|
1579
|
+
const samePoint = (p, q) => Math.abs(p.x - q.x) < EPS15 && Math.abs(p.y - q.y) < EPS15;
|
|
1580
1580
|
if (samePoint(first, { x: PA.x, y: PA.y }) && samePoint(last, { x: PB.x, y: PB.y })) {
|
|
1581
1581
|
this.solvedTracePath = path;
|
|
1582
1582
|
this.solved = true;
|
|
@@ -1887,17 +1887,17 @@ var applyJogToTerminalSegment = ({
|
|
|
1887
1887
|
segmentIndex: si,
|
|
1888
1888
|
offset,
|
|
1889
1889
|
JOG_SIZE,
|
|
1890
|
-
EPS:
|
|
1890
|
+
EPS: EPS15 = 1e-6
|
|
1891
1891
|
}) => {
|
|
1892
1892
|
if (si !== 0 && si !== pts.length - 2) return;
|
|
1893
1893
|
const start = pts[si];
|
|
1894
1894
|
const end = pts[si + 1];
|
|
1895
|
-
const
|
|
1896
|
-
const
|
|
1897
|
-
if (!
|
|
1898
|
-
const segDir =
|
|
1895
|
+
const isVertical6 = Math.abs(start.x - end.x) < EPS15;
|
|
1896
|
+
const isHorizontal5 = Math.abs(start.y - end.y) < EPS15;
|
|
1897
|
+
if (!isVertical6 && !isHorizontal5) return;
|
|
1898
|
+
const segDir = isVertical6 ? end.y > start.y ? 1 : -1 : end.x > start.x ? 1 : -1;
|
|
1899
1899
|
if (pts.length === 2) {
|
|
1900
|
-
if (
|
|
1900
|
+
if (isVertical6) {
|
|
1901
1901
|
const jogYNearStart = start.y + segDir * JOG_SIZE;
|
|
1902
1902
|
const jogYNearEnd = end.y - segDir * JOG_SIZE;
|
|
1903
1903
|
pts.splice(
|
|
@@ -1923,7 +1923,7 @@ var applyJogToTerminalSegment = ({
|
|
|
1923
1923
|
return;
|
|
1924
1924
|
}
|
|
1925
1925
|
if (si === 0) {
|
|
1926
|
-
if (
|
|
1926
|
+
if (isVertical6) {
|
|
1927
1927
|
const jogY = start.y + segDir * JOG_SIZE;
|
|
1928
1928
|
pts.splice(
|
|
1929
1929
|
1,
|
|
@@ -1943,7 +1943,7 @@ var applyJogToTerminalSegment = ({
|
|
|
1943
1943
|
);
|
|
1944
1944
|
}
|
|
1945
1945
|
} else {
|
|
1946
|
-
if (
|
|
1946
|
+
if (isVertical6) {
|
|
1947
1947
|
const jogY = end.y - segDir * JOG_SIZE;
|
|
1948
1948
|
pts.splice(
|
|
1949
1949
|
si,
|
|
@@ -2175,11 +2175,11 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
|
|
|
2175
2175
|
}) {
|
|
2176
2176
|
const blockingCollisions = this.getBlockingCollisions({ group, offset });
|
|
2177
2177
|
if (blockingCollisions.length === 0) return offset;
|
|
2178
|
-
const crossesThroughObstacle = (candidateOffset) => blockingCollisions.some(({ start, isVertical:
|
|
2178
|
+
const crossesThroughObstacle = (candidateOffset) => blockingCollisions.some(({ start, isVertical: isVertical6, obstacle }) => {
|
|
2179
2179
|
let orig;
|
|
2180
2180
|
let min;
|
|
2181
2181
|
let max;
|
|
2182
|
-
if (
|
|
2182
|
+
if (isVertical6) {
|
|
2183
2183
|
orig = start.x;
|
|
2184
2184
|
min = obstacle.minX;
|
|
2185
2185
|
max = obstacle.maxX;
|
|
@@ -2191,8 +2191,8 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
|
|
|
2191
2191
|
const next = orig + candidateOffset;
|
|
2192
2192
|
return orig <= min + EPS5 && next >= max - EPS5 || orig >= max - EPS5 && next <= min + EPS5;
|
|
2193
2193
|
});
|
|
2194
|
-
const edges = blockingCollisions.map(({ start, isVertical:
|
|
2195
|
-
if (
|
|
2194
|
+
const edges = blockingCollisions.map(({ start, isVertical: isVertical6, obstacle }) => {
|
|
2195
|
+
if (isVertical6) {
|
|
2196
2196
|
return {
|
|
2197
2197
|
coord: start.x,
|
|
2198
2198
|
lowEdge: obstacle.minX,
|
|
@@ -2226,10 +2226,10 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
|
|
|
2226
2226
|
const start = trace.tracePath[traceSegmentIndex];
|
|
2227
2227
|
const end = trace.tracePath[traceSegmentIndex + 1];
|
|
2228
2228
|
if (!start || !end) continue;
|
|
2229
|
-
const
|
|
2230
|
-
const
|
|
2231
|
-
if (!
|
|
2232
|
-
const shiftedSegment =
|
|
2229
|
+
const isVertical6 = Math.abs(start.x - end.x) < EPS5;
|
|
2230
|
+
const isHorizontal5 = Math.abs(start.y - end.y) < EPS5;
|
|
2231
|
+
if (!isVertical6 && !isHorizontal5) continue;
|
|
2232
|
+
const shiftedSegment = isVertical6 ? [
|
|
2233
2233
|
{ ...start, x: start.x + offset },
|
|
2234
2234
|
{ ...end, x: end.x + offset }
|
|
2235
2235
|
] : [
|
|
@@ -2240,7 +2240,7 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
|
|
|
2240
2240
|
if (collision) {
|
|
2241
2241
|
blockingCollisions.push({
|
|
2242
2242
|
start,
|
|
2243
|
-
isVertical:
|
|
2243
|
+
isVertical: isVertical6,
|
|
2244
2244
|
obstacle: collision.rect
|
|
2245
2245
|
});
|
|
2246
2246
|
}
|
|
@@ -2327,7 +2327,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
2327
2327
|
return islands;
|
|
2328
2328
|
}
|
|
2329
2329
|
findNextOverlapIssue() {
|
|
2330
|
-
const
|
|
2330
|
+
const EPS15 = 2e-3;
|
|
2331
2331
|
const netIds = Object.keys(this.traceNetIslands);
|
|
2332
2332
|
for (let i = 0; i < netIds.length; i++) {
|
|
2333
2333
|
for (let j = i + 1; j < netIds.length; j++) {
|
|
@@ -2354,7 +2354,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
2354
2354
|
segmentBIndex,
|
|
2355
2355
|
rangeOverlap
|
|
2356
2356
|
}) => {
|
|
2357
|
-
if (rangeOverlap >
|
|
2357
|
+
if (rangeOverlap > EPS15) {
|
|
2358
2358
|
const keyA = `${pathAIndex}:${segmentAIndex}`;
|
|
2359
2359
|
const keyB = `${pathBIndex}:${segmentBIndex}`;
|
|
2360
2360
|
if (!seenA.has(keyA)) {
|
|
@@ -2373,7 +2373,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
2373
2373
|
}
|
|
2374
2374
|
return;
|
|
2375
2375
|
}
|
|
2376
|
-
if (rangeOverlap < -
|
|
2376
|
+
if (rangeOverlap < -EPS15) return;
|
|
2377
2377
|
const pathA = pathsA[pathAIndex].tracePath;
|
|
2378
2378
|
const pathB = pathsB[pathBIndex].tracePath;
|
|
2379
2379
|
const segmentAStart = pathA[segmentAIndex];
|
|
@@ -2396,8 +2396,8 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
2396
2396
|
for (let sa = 0; sa < ptsA.length - 1; sa++) {
|
|
2397
2397
|
const a1 = ptsA[sa];
|
|
2398
2398
|
const a2 = ptsA[sa + 1];
|
|
2399
|
-
const aVert = Math.abs(a1.x - a2.x) <
|
|
2400
|
-
const aHorz = Math.abs(a1.y - a2.y) <
|
|
2399
|
+
const aVert = Math.abs(a1.x - a2.x) < EPS15;
|
|
2400
|
+
const aHorz = Math.abs(a1.y - a2.y) < EPS15;
|
|
2401
2401
|
if (!aVert && !aHorz) continue;
|
|
2402
2402
|
for (let pb = 0; pb < pathsB.length; pb++) {
|
|
2403
2403
|
const pathB = pathsB[pb];
|
|
@@ -2408,11 +2408,11 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
2408
2408
|
for (let sb = 0; sb < ptsB.length - 1; sb++) {
|
|
2409
2409
|
const b1 = ptsB[sb];
|
|
2410
2410
|
const b2 = ptsB[sb + 1];
|
|
2411
|
-
const bVert = Math.abs(b1.x - b2.x) <
|
|
2412
|
-
const bHorz = Math.abs(b1.y - b2.y) <
|
|
2411
|
+
const bVert = Math.abs(b1.x - b2.x) < EPS15;
|
|
2412
|
+
const bHorz = Math.abs(b1.y - b2.y) < EPS15;
|
|
2413
2413
|
if (!bVert && !bHorz) continue;
|
|
2414
2414
|
if (aVert && bVert) {
|
|
2415
|
-
if (Math.abs(a1.x - b1.x) <
|
|
2415
|
+
if (Math.abs(a1.x - b1.x) < EPS15) {
|
|
2416
2416
|
recordCollinearInteraction({
|
|
2417
2417
|
pathAIndex: pa,
|
|
2418
2418
|
segmentAIndex: sa,
|
|
@@ -2422,7 +2422,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
2422
2422
|
});
|
|
2423
2423
|
}
|
|
2424
2424
|
} else if (aHorz && bHorz) {
|
|
2425
|
-
if (Math.abs(a1.y - b1.y) <
|
|
2425
|
+
if (Math.abs(a1.y - b1.y) < EPS15) {
|
|
2426
2426
|
recordCollinearInteraction({
|
|
2427
2427
|
pathAIndex: pa,
|
|
2428
2428
|
segmentAIndex: sa,
|
|
@@ -2478,15 +2478,15 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
2478
2478
|
return null;
|
|
2479
2479
|
}
|
|
2480
2480
|
findNextDiagonalSegment() {
|
|
2481
|
-
const
|
|
2481
|
+
const EPS15 = 2e-3;
|
|
2482
2482
|
for (const mspPairId in this.correctedTraceMap) {
|
|
2483
2483
|
const tracePath = this.correctedTraceMap[mspPairId].tracePath;
|
|
2484
2484
|
for (let i = 0; i < tracePath.length - 1; i++) {
|
|
2485
2485
|
const p1 = tracePath[i];
|
|
2486
2486
|
const p2 = tracePath[i + 1];
|
|
2487
|
-
const
|
|
2488
|
-
const
|
|
2489
|
-
if (!
|
|
2487
|
+
const isHorizontal5 = Math.abs(p1.y - p2.y) < EPS15;
|
|
2488
|
+
const isVertical6 = Math.abs(p1.x - p2.x) < EPS15;
|
|
2489
|
+
if (!isHorizontal5 && !isVertical6) {
|
|
2490
2490
|
return { mspPairId, tracePath, i, p1, p2 };
|
|
2491
2491
|
}
|
|
2492
2492
|
}
|
|
@@ -2499,13 +2499,13 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
2499
2499
|
return false;
|
|
2500
2500
|
}
|
|
2501
2501
|
const { mspPairId, tracePath, i, p1, p2 } = diagonalInfo;
|
|
2502
|
-
const
|
|
2502
|
+
const EPS15 = 2e-3;
|
|
2503
2503
|
const p0 = i > 0 ? tracePath[i - 1] : null;
|
|
2504
2504
|
const p3 = i + 2 < tracePath.length ? tracePath[i + 2] : null;
|
|
2505
|
-
const prevIsVertical = p0 ? Math.abs(p0.x - p1.x) <
|
|
2506
|
-
const prevIsHorizontal = p0 ? Math.abs(p0.y - p1.y) <
|
|
2507
|
-
const nextIsVertical = p3 ? Math.abs(p2.x - p3.x) <
|
|
2508
|
-
const nextIsHorizontal = p3 ? Math.abs(p2.y - p3.y) <
|
|
2505
|
+
const prevIsVertical = p0 ? Math.abs(p0.x - p1.x) < EPS15 : false;
|
|
2506
|
+
const prevIsHorizontal = p0 ? Math.abs(p0.y - p1.y) < EPS15 : false;
|
|
2507
|
+
const nextIsVertical = p3 ? Math.abs(p2.x - p3.x) < EPS15 : false;
|
|
2508
|
+
const nextIsHorizontal = p3 ? Math.abs(p2.y - p3.y) < EPS15 : false;
|
|
2509
2509
|
const elbow1 = { x: p1.x, y: p2.y };
|
|
2510
2510
|
const elbow2 = { x: p2.x, y: p1.y };
|
|
2511
2511
|
let score1 = 0;
|
|
@@ -2667,24 +2667,24 @@ var ChipObstacleSpatialIndex = class {
|
|
|
2667
2667
|
};
|
|
2668
2668
|
|
|
2669
2669
|
// lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/collisions.ts
|
|
2670
|
-
function segmentIntersectsRect2(p1, p2, rect,
|
|
2671
|
-
const isVert = Math.abs(p1.x - p2.x) <
|
|
2672
|
-
const isHorz = Math.abs(p1.y - p2.y) <
|
|
2670
|
+
function segmentIntersectsRect2(p1, p2, rect, EPS15 = 1e-9) {
|
|
2671
|
+
const isVert = Math.abs(p1.x - p2.x) < EPS15;
|
|
2672
|
+
const isHorz = Math.abs(p1.y - p2.y) < EPS15;
|
|
2673
2673
|
if (!isVert && !isHorz) return false;
|
|
2674
2674
|
if (isVert) {
|
|
2675
2675
|
const x = p1.x;
|
|
2676
|
-
if (x < rect.minX -
|
|
2676
|
+
if (x < rect.minX - EPS15 || x > rect.maxX + EPS15) return false;
|
|
2677
2677
|
const segMinY = Math.min(p1.y, p2.y);
|
|
2678
2678
|
const segMaxY = Math.max(p1.y, p2.y);
|
|
2679
2679
|
const overlap = Math.min(segMaxY, rect.maxY) - Math.max(segMinY, rect.minY);
|
|
2680
|
-
return overlap >
|
|
2680
|
+
return overlap > EPS15;
|
|
2681
2681
|
} else {
|
|
2682
2682
|
const y = p1.y;
|
|
2683
|
-
if (y < rect.minY -
|
|
2683
|
+
if (y < rect.minY - EPS15 || y > rect.maxY + EPS15) return false;
|
|
2684
2684
|
const segMinX = Math.min(p1.x, p2.x);
|
|
2685
2685
|
const segMaxX = Math.max(p1.x, p2.x);
|
|
2686
2686
|
const overlap = Math.min(segMaxX, rect.maxX) - Math.max(segMinX, rect.minX);
|
|
2687
|
-
return overlap >
|
|
2687
|
+
return overlap > EPS15;
|
|
2688
2688
|
}
|
|
2689
2689
|
}
|
|
2690
2690
|
function rectIntersectsAnyTrace(bounds, inputTraceMap, hostPathId, hostSegIndex) {
|
|
@@ -3083,14 +3083,14 @@ var SingleNetLabelPlacementSolver = class extends BaseSolver {
|
|
|
3083
3083
|
};
|
|
3084
3084
|
let bestCandidate = null;
|
|
3085
3085
|
let bestScore = -Infinity;
|
|
3086
|
-
const
|
|
3086
|
+
const EPS15 = 1e-6;
|
|
3087
3087
|
for (const curr of tracesToScan) {
|
|
3088
3088
|
const pts = curr.tracePath.slice();
|
|
3089
3089
|
for (let si = 0; si < pts.length - 1; si++) {
|
|
3090
3090
|
const a = pts[si];
|
|
3091
3091
|
const b = pts[si + 1];
|
|
3092
|
-
const isH = Math.abs(a.y - b.y) <
|
|
3093
|
-
const isV = Math.abs(a.x - b.x) <
|
|
3092
|
+
const isH = Math.abs(a.y - b.y) < EPS15;
|
|
3093
|
+
const isV = Math.abs(a.x - b.x) < EPS15;
|
|
3094
3094
|
if (!isH && !isV) continue;
|
|
3095
3095
|
const segmentAllowed = isH ? ["y+", "y-"] : ["x+", "x-"];
|
|
3096
3096
|
const candidateOrients = orientations.filter(
|
|
@@ -4176,11 +4176,11 @@ var doesPathCoincideWithTraces = (path, traces) => {
|
|
|
4176
4176
|
for (let i = 0; i < path.length - 1; i++) {
|
|
4177
4177
|
const pathSegStart = path[i];
|
|
4178
4178
|
const pathSegEnd = path[i + 1];
|
|
4179
|
-
const
|
|
4180
|
-
const
|
|
4181
|
-
if (!
|
|
4182
|
-
const crossAxis =
|
|
4183
|
-
const alongAxis =
|
|
4179
|
+
const isVertical6 = Math.abs(pathSegStart.x - pathSegEnd.x) < COINCIDENT_EPS;
|
|
4180
|
+
const isHorizontal5 = Math.abs(pathSegStart.y - pathSegEnd.y) < COINCIDENT_EPS;
|
|
4181
|
+
if (!isVertical6 && !isHorizontal5) continue;
|
|
4182
|
+
const crossAxis = isVertical6 ? "x" : "y";
|
|
4183
|
+
const alongAxis = isVertical6 ? "y" : "x";
|
|
4184
4184
|
for (const trace of traces) {
|
|
4185
4185
|
for (let j = 0; j < trace.tracePath.length - 1; j++) {
|
|
4186
4186
|
const traceSegStart = trace.tracePath[j];
|
|
@@ -4204,7 +4204,7 @@ var doesPathCoincideWithTraces = (path, traces) => {
|
|
|
4204
4204
|
};
|
|
4205
4205
|
var doesPathOverlapTraceStrokes = (path, traces) => {
|
|
4206
4206
|
const traceStrokeRadius = SCHEMATIC_TRACE_MIN_CENTERLINE_CLEARANCE / 2;
|
|
4207
|
-
const getStrokeBounds = (start, end,
|
|
4207
|
+
const getStrokeBounds = (start, end, isVertical6) => isVertical6 ? {
|
|
4208
4208
|
minX: start.x - traceStrokeRadius,
|
|
4209
4209
|
maxX: start.x + traceStrokeRadius,
|
|
4210
4210
|
minY: Math.min(start.y, end.y),
|
|
@@ -4218,22 +4218,22 @@ var doesPathOverlapTraceStrokes = (path, traces) => {
|
|
|
4218
4218
|
for (let pathIndex = 0; pathIndex < path.length - 1; pathIndex++) {
|
|
4219
4219
|
const pathStart = path[pathIndex];
|
|
4220
4220
|
const pathEnd = path[pathIndex + 1];
|
|
4221
|
-
const
|
|
4222
|
-
const
|
|
4223
|
-
if (!
|
|
4224
|
-
const pathBounds = getStrokeBounds(pathStart, pathEnd,
|
|
4221
|
+
const isVertical6 = Math.abs(pathStart.x - pathEnd.x) < COINCIDENT_EPS;
|
|
4222
|
+
const isHorizontal5 = Math.abs(pathStart.y - pathEnd.y) < COINCIDENT_EPS;
|
|
4223
|
+
if (!isVertical6 && !isHorizontal5) continue;
|
|
4224
|
+
const pathBounds = getStrokeBounds(pathStart, pathEnd, isVertical6);
|
|
4225
4225
|
for (const trace of traces) {
|
|
4226
4226
|
for (let traceIndex = 0; traceIndex < trace.tracePath.length - 1; traceIndex++) {
|
|
4227
4227
|
const traceStart = trace.tracePath[traceIndex];
|
|
4228
4228
|
const traceEnd = trace.tracePath[traceIndex + 1];
|
|
4229
|
-
const isParallel =
|
|
4229
|
+
const isParallel = isVertical6 ? Math.abs(traceStart.x - traceEnd.x) < COINCIDENT_EPS : Math.abs(traceStart.y - traceEnd.y) < COINCIDENT_EPS;
|
|
4230
4230
|
if (!isParallel) continue;
|
|
4231
4231
|
const overlap = boundsIntersection(
|
|
4232
4232
|
pathBounds,
|
|
4233
|
-
getStrokeBounds(traceStart, traceEnd,
|
|
4233
|
+
getStrokeBounds(traceStart, traceEnd, isVertical6)
|
|
4234
4234
|
);
|
|
4235
4235
|
if (!overlap) continue;
|
|
4236
|
-
const overlapLength =
|
|
4236
|
+
const overlapLength = isVertical6 ? overlap.maxY - overlap.minY : overlap.maxX - overlap.minX;
|
|
4237
4237
|
if (overlapLength > COINCIDENT_EPS) return true;
|
|
4238
4238
|
}
|
|
4239
4239
|
}
|
|
@@ -6015,12 +6015,12 @@ var EPS8 = 1e-9;
|
|
|
6015
6015
|
|
|
6016
6016
|
// lib/solvers/Example28Solver/segmentRunsAlongRectBoundary.ts
|
|
6017
6017
|
var segmentRunsAlongRectBoundary = (start, end, rect) => {
|
|
6018
|
-
const
|
|
6019
|
-
const
|
|
6020
|
-
if (
|
|
6018
|
+
const isVertical6 = Math.abs(start.x - end.x) < EPS8;
|
|
6019
|
+
const isHorizontal5 = Math.abs(start.y - end.y) < EPS8;
|
|
6020
|
+
if (isVertical6) {
|
|
6021
6021
|
return Math.abs(start.x - rect.minX) < EPS8 || Math.abs(start.x - rect.maxX) < EPS8;
|
|
6022
6022
|
}
|
|
6023
|
-
if (
|
|
6023
|
+
if (isHorizontal5) {
|
|
6024
6024
|
return Math.abs(start.y - rect.minY) < EPS8 || Math.abs(start.y - rect.maxY) < EPS8;
|
|
6025
6025
|
}
|
|
6026
6026
|
return false;
|
|
@@ -6614,12 +6614,12 @@ var findPerpendicularPathCrossings = (path, otherPath) => {
|
|
|
6614
6614
|
for (let pathSegmentIndex = 1; pathSegmentIndex < path.length - 2; pathSegmentIndex++) {
|
|
6615
6615
|
const start = path[pathSegmentIndex];
|
|
6616
6616
|
const end = path[pathSegmentIndex + 1];
|
|
6617
|
-
const
|
|
6617
|
+
const isVertical6 = Math.abs(start.x - end.x) < EPS9;
|
|
6618
6618
|
for (let otherPathSegmentIndex = 1; otherPathSegmentIndex < otherPath.length - 2; otherPathSegmentIndex++) {
|
|
6619
6619
|
const otherStart = otherPath[otherPathSegmentIndex];
|
|
6620
6620
|
const otherEnd = otherPath[otherPathSegmentIndex + 1];
|
|
6621
6621
|
const otherIsVertical = Math.abs(otherStart.x - otherEnd.x) < EPS9;
|
|
6622
|
-
if (
|
|
6622
|
+
if (isVertical6 === otherIsVertical) continue;
|
|
6623
6623
|
const intersection = getSegmentIntersection(
|
|
6624
6624
|
start,
|
|
6625
6625
|
end,
|
|
@@ -7727,8 +7727,8 @@ var getChipBoundaryOverlap = (path, chipObstacles) => {
|
|
|
7727
7727
|
return total;
|
|
7728
7728
|
};
|
|
7729
7729
|
var getSegmentOverlapWithRectSpan = (start, end, rect) => {
|
|
7730
|
-
const
|
|
7731
|
-
if (
|
|
7730
|
+
const isVertical6 = Math.abs(start.x - end.x) < EPS8;
|
|
7731
|
+
if (isVertical6) {
|
|
7732
7732
|
return Math.min(Math.max(start.y, end.y), rect.maxY) - Math.max(Math.min(start.y, end.y), rect.minY);
|
|
7733
7733
|
}
|
|
7734
7734
|
return Math.min(Math.max(start.x, end.x), rect.maxX) - Math.max(Math.min(start.x, end.x), rect.minX);
|
|
@@ -10049,7 +10049,9 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
10049
10049
|
changed = true;
|
|
10050
10050
|
}
|
|
10051
10051
|
}
|
|
10052
|
-
if (connectedPinIds.size
|
|
10052
|
+
if (connectedPinIds.size < 3 || connectedPinIds.size === 3 && !this.hasPortOnlyLabelOnSameNet(label)) {
|
|
10053
|
+
return null;
|
|
10054
|
+
}
|
|
10053
10055
|
const pins = [...connectedPinIds].map((pinId) => this.pinMap[pinId]);
|
|
10054
10056
|
const direction = dir(orientation);
|
|
10055
10057
|
const pinX = pins[0].x;
|
|
@@ -11978,11 +11980,11 @@ var NetLabelNetLabelCollisionSolver = class extends BaseSolver {
|
|
|
11978
11980
|
for (let si = 0; si < pts.length - 1; si++) {
|
|
11979
11981
|
const segStart = pts[si];
|
|
11980
11982
|
const segEnd = pts[si + 1];
|
|
11981
|
-
const
|
|
11982
|
-
const
|
|
11983
|
-
if (!
|
|
11983
|
+
const isHorizontal5 = Math.abs(segStart.y - segEnd.y) < SEGMENT_PARALLEL_EPS;
|
|
11984
|
+
const isVertical6 = Math.abs(segStart.x - segEnd.x) < SEGMENT_PARALLEL_EPS;
|
|
11985
|
+
if (!isHorizontal5 && !isVertical6) continue;
|
|
11984
11986
|
let perpendicularOrientations;
|
|
11985
|
-
if (
|
|
11987
|
+
if (isHorizontal5) {
|
|
11986
11988
|
perpendicularOrientations = ["y+", "y-"];
|
|
11987
11989
|
} else {
|
|
11988
11990
|
perpendicularOrientations = ["x+", "x-"];
|
|
@@ -13660,9 +13662,9 @@ var getStubLength = (path) => {
|
|
|
13660
13662
|
return Math.abs(end.x - start.x) + Math.abs(end.y - start.y);
|
|
13661
13663
|
};
|
|
13662
13664
|
var getLabelBounds2 = (placement) => {
|
|
13663
|
-
const
|
|
13664
|
-
const width =
|
|
13665
|
-
const height =
|
|
13665
|
+
const isVertical6 = placement.axis === "y";
|
|
13666
|
+
const width = isVertical6 ? placement.height : placement.width;
|
|
13667
|
+
const height = isVertical6 ? placement.width : placement.height;
|
|
13666
13668
|
return {
|
|
13667
13669
|
minX: placement.center.x - width / 2,
|
|
13668
13670
|
maxX: placement.center.x + width / 2,
|
|
@@ -13890,9 +13892,9 @@ var isPointOnPath2 = (point, path) => {
|
|
|
13890
13892
|
const maxX = Math.max(start.x, end.x) + POINT_EPSILON;
|
|
13891
13893
|
const minY = Math.min(start.y, end.y) - POINT_EPSILON;
|
|
13892
13894
|
const maxY = Math.max(start.y, end.y) + POINT_EPSILON;
|
|
13893
|
-
const
|
|
13894
|
-
const
|
|
13895
|
-
if ((
|
|
13895
|
+
const isHorizontal5 = Math.abs(start.y - end.y) <= POINT_EPSILON;
|
|
13896
|
+
const isVertical6 = Math.abs(start.x - end.x) <= POINT_EPSILON;
|
|
13897
|
+
if ((isHorizontal5 && Math.abs(point.y - start.y) <= POINT_EPSILON || isVertical6 && Math.abs(point.x - start.x) <= POINT_EPSILON) && point.x >= minX && point.x <= maxX && point.y >= minY && point.y <= maxY) {
|
|
13896
13898
|
return true;
|
|
13897
13899
|
}
|
|
13898
13900
|
}
|
|
@@ -15127,10 +15129,10 @@ orientation: ${label.orientation}`
|
|
|
15127
15129
|
strokeColor: "purple"
|
|
15128
15130
|
});
|
|
15129
15131
|
}
|
|
15130
|
-
const
|
|
15132
|
+
const isHorizontal5 = inlineLabel.axis === "x";
|
|
15131
15133
|
let renderedWidth = inlineLabel.width;
|
|
15132
15134
|
let renderedHeight = inlineLabel.height;
|
|
15133
|
-
if (!
|
|
15135
|
+
if (!isHorizontal5) {
|
|
15134
15136
|
renderedWidth = inlineLabel.height;
|
|
15135
15137
|
renderedHeight = inlineLabel.width;
|
|
15136
15138
|
}
|
|
@@ -15230,9 +15232,9 @@ var getCollisionLimitedTerminalStubEnd = ({
|
|
|
15230
15232
|
ownGlobalConnNetId,
|
|
15231
15233
|
ignoredTraceIds
|
|
15232
15234
|
}) => {
|
|
15233
|
-
const
|
|
15234
|
-
const alongAxis =
|
|
15235
|
-
const acrossAxis =
|
|
15235
|
+
const isHorizontal5 = Math.abs(intendedEnd.x - start.x) >= Math.abs(intendedEnd.y - start.y);
|
|
15236
|
+
const alongAxis = isHorizontal5 ? "x" : "y";
|
|
15237
|
+
const acrossAxis = isHorizontal5 ? "y" : "x";
|
|
15236
15238
|
const direction = Math.sign(intendedEnd[alongAxis] - start[alongAxis]) || 1;
|
|
15237
15239
|
const intendedLength = Math.abs(intendedEnd[alongAxis] - start[alongAxis]);
|
|
15238
15240
|
let availableLength = intendedLength;
|
|
@@ -15270,7 +15272,7 @@ var getCollisionLimitedTerminalStubEnd = ({
|
|
|
15270
15272
|
}
|
|
15271
15273
|
}
|
|
15272
15274
|
if (availableLength + GEOMETRY_EPSILON < minimumLength) return null;
|
|
15273
|
-
return
|
|
15275
|
+
return isHorizontal5 ? { x: start.x + direction * availableLength, y: start.y } : { x: start.x, y: start.y + direction * availableLength };
|
|
15274
15276
|
};
|
|
15275
15277
|
|
|
15276
15278
|
// lib/solvers/NetLabelToTraceSolver/NetLabelToTraceSolver.ts
|
|
@@ -15279,23 +15281,6 @@ import {
|
|
|
15279
15281
|
getBoundFromCenteredRect
|
|
15280
15282
|
} from "@tscircuit/math-utils";
|
|
15281
15283
|
|
|
15282
|
-
// lib/solvers/NetLabelTraceRecovery/getTraceRecoveryConnectivityMaps.ts
|
|
15283
|
-
var getTraceRecoveryConnectivityMaps = (inputProblem) => {
|
|
15284
|
-
const chipMap = {};
|
|
15285
|
-
const pinMap = /* @__PURE__ */ new Map();
|
|
15286
|
-
for (const chip of inputProblem.chips) {
|
|
15287
|
-
chipMap[chip.chipId] = chip;
|
|
15288
|
-
for (const pin of chip.pins) {
|
|
15289
|
-
pinMap.set(pin.pinId, {
|
|
15290
|
-
...pin,
|
|
15291
|
-
chipId: chip.chipId,
|
|
15292
|
-
_facingDirection: pin._facingDirection ?? getPinDirection(pin, chip)
|
|
15293
|
-
});
|
|
15294
|
-
}
|
|
15295
|
-
}
|
|
15296
|
-
return { chipMap, pinMap };
|
|
15297
|
-
};
|
|
15298
|
-
|
|
15299
15284
|
// lib/solvers/NetLabelTraceRecovery/doesTraceRecoveryPathConflict.ts
|
|
15300
15285
|
import { doSegmentsIntersect as doSegmentsIntersect4 } from "@tscircuit/math-utils";
|
|
15301
15286
|
var EPSILON = 1e-6;
|
|
@@ -15372,6 +15357,124 @@ var doesTraceRecoveryPathConflict = (tracePath, collisionTraces) => {
|
|
|
15372
15357
|
return false;
|
|
15373
15358
|
};
|
|
15374
15359
|
|
|
15360
|
+
// lib/solvers/NetLabelTraceRecovery/getTraceRecoveryConnectivityMaps.ts
|
|
15361
|
+
var getTraceRecoveryConnectivityMaps = (inputProblem) => {
|
|
15362
|
+
const chipMap = {};
|
|
15363
|
+
const pinMap = /* @__PURE__ */ new Map();
|
|
15364
|
+
for (const chip of inputProblem.chips) {
|
|
15365
|
+
chipMap[chip.chipId] = chip;
|
|
15366
|
+
for (const pin of chip.pins) {
|
|
15367
|
+
pinMap.set(pin.pinId, {
|
|
15368
|
+
...pin,
|
|
15369
|
+
chipId: chip.chipId,
|
|
15370
|
+
_facingDirection: pin._facingDirection ?? getPinDirection(pin, chip)
|
|
15371
|
+
});
|
|
15372
|
+
}
|
|
15373
|
+
}
|
|
15374
|
+
return { chipMap, pinMap };
|
|
15375
|
+
};
|
|
15376
|
+
|
|
15377
|
+
// lib/solvers/NetLabelToTraceSolver/reduceTraceCrossings.ts
|
|
15378
|
+
var EPS14 = 1e-9;
|
|
15379
|
+
var isHorizontal4 = (start, end) => Math.abs(start.y - end.y) < EPS14;
|
|
15380
|
+
var isVertical5 = (start, end) => Math.abs(start.x - end.x) < EPS14;
|
|
15381
|
+
var compactConsecutivePoints = (path) => path.filter((point, index) => {
|
|
15382
|
+
const previousPoint = path[index - 1];
|
|
15383
|
+
return !previousPoint || Math.abs(point.x - previousPoint.x) >= EPS14 || Math.abs(point.y - previousPoint.y) >= EPS14;
|
|
15384
|
+
});
|
|
15385
|
+
var isValidOrthogonalPath = (path) => {
|
|
15386
|
+
if (path.length < 2 || !path.every((point, index) => {
|
|
15387
|
+
const nextPoint = path[index + 1];
|
|
15388
|
+
if (!nextPoint) return true;
|
|
15389
|
+
return isHorizontal4(point, nextPoint) || isVertical5(point, nextPoint);
|
|
15390
|
+
})) {
|
|
15391
|
+
return false;
|
|
15392
|
+
}
|
|
15393
|
+
for (let firstSegmentIndex = 0; firstSegmentIndex < path.length - 1; firstSegmentIndex++) {
|
|
15394
|
+
for (let secondSegmentIndex = firstSegmentIndex + 2; secondSegmentIndex < path.length - 1; secondSegmentIndex++) {
|
|
15395
|
+
if (segmentsIntersect(
|
|
15396
|
+
path[firstSegmentIndex],
|
|
15397
|
+
path[firstSegmentIndex + 1],
|
|
15398
|
+
path[secondSegmentIndex],
|
|
15399
|
+
path[secondSegmentIndex + 1]
|
|
15400
|
+
)) {
|
|
15401
|
+
return false;
|
|
15402
|
+
}
|
|
15403
|
+
}
|
|
15404
|
+
}
|
|
15405
|
+
return true;
|
|
15406
|
+
};
|
|
15407
|
+
var getEndpointAlignedSegmentCandidates = (tracePath) => {
|
|
15408
|
+
const candidates = [];
|
|
15409
|
+
for (let segmentIndex = 2; segmentIndex <= tracePath.length - 4; segmentIndex++) {
|
|
15410
|
+
const previousPoint = tracePath[segmentIndex - 1];
|
|
15411
|
+
const start = tracePath[segmentIndex];
|
|
15412
|
+
const end = tracePath[segmentIndex + 1];
|
|
15413
|
+
const nextPoint = tracePath[segmentIndex + 2];
|
|
15414
|
+
const segmentIsVertical = isVertical5(start, end);
|
|
15415
|
+
const segmentIsHorizontal = isHorizontal4(start, end);
|
|
15416
|
+
if (!segmentIsVertical && !segmentIsHorizontal || segmentIsVertical && (!isHorizontal4(previousPoint, start) || !isHorizontal4(end, nextPoint)) || segmentIsHorizontal && (!isVertical5(previousPoint, start) || !isVertical5(end, nextPoint))) {
|
|
15417
|
+
continue;
|
|
15418
|
+
}
|
|
15419
|
+
const alignedCoordinates = segmentIsVertical ? [previousPoint.x, nextPoint.x] : [previousPoint.y, nextPoint.y];
|
|
15420
|
+
for (const alignedCoordinate of alignedCoordinates) {
|
|
15421
|
+
if (Math.abs(alignedCoordinate - (segmentIsVertical ? start.x : start.y)) < EPS14) {
|
|
15422
|
+
continue;
|
|
15423
|
+
}
|
|
15424
|
+
const candidate = tracePath.map((point) => ({ ...point }));
|
|
15425
|
+
if (segmentIsVertical) {
|
|
15426
|
+
candidate[segmentIndex].x = alignedCoordinate;
|
|
15427
|
+
candidate[segmentIndex + 1].x = alignedCoordinate;
|
|
15428
|
+
} else {
|
|
15429
|
+
candidate[segmentIndex].y = alignedCoordinate;
|
|
15430
|
+
candidate[segmentIndex + 1].y = alignedCoordinate;
|
|
15431
|
+
}
|
|
15432
|
+
const compactedCandidate = compactConsecutivePoints(candidate);
|
|
15433
|
+
if (isValidOrthogonalPath(compactedCandidate)) {
|
|
15434
|
+
candidates.push(compactedCandidate);
|
|
15435
|
+
}
|
|
15436
|
+
}
|
|
15437
|
+
}
|
|
15438
|
+
return candidates;
|
|
15439
|
+
};
|
|
15440
|
+
var countOtherNetCrossings2 = ({
|
|
15441
|
+
tracePath,
|
|
15442
|
+
globalConnNetId,
|
|
15443
|
+
otherTraces
|
|
15444
|
+
}) => otherTraces.reduce(
|
|
15445
|
+
(crossingCount, otherTrace) => crossingCount + (otherTrace.globalConnNetId === globalConnNetId ? 0 : countPathIntersections(tracePath, otherTrace.tracePath)),
|
|
15446
|
+
0
|
|
15447
|
+
);
|
|
15448
|
+
var reduceTraceCrossings = ({
|
|
15449
|
+
tracePath,
|
|
15450
|
+
globalConnNetId,
|
|
15451
|
+
otherTraces,
|
|
15452
|
+
isCandidateValid
|
|
15453
|
+
}) => {
|
|
15454
|
+
let bestPath = tracePath;
|
|
15455
|
+
let bestCrossingCount = countOtherNetCrossings2({
|
|
15456
|
+
tracePath,
|
|
15457
|
+
globalConnNetId,
|
|
15458
|
+
otherTraces
|
|
15459
|
+
});
|
|
15460
|
+
let bestPathLength = getPathLength2(tracePath);
|
|
15461
|
+
for (const candidate of getEndpointAlignedSegmentCandidates(tracePath)) {
|
|
15462
|
+
if (!isCandidateValid(candidate)) continue;
|
|
15463
|
+
const crossingCount = countOtherNetCrossings2({
|
|
15464
|
+
tracePath: candidate,
|
|
15465
|
+
globalConnNetId,
|
|
15466
|
+
otherTraces
|
|
15467
|
+
});
|
|
15468
|
+
const pathLength = getPathLength2(candidate);
|
|
15469
|
+
if (crossingCount < bestCrossingCount || crossingCount === bestCrossingCount && pathLength < bestPathLength) {
|
|
15470
|
+
bestPath = candidate;
|
|
15471
|
+
bestCrossingCount = crossingCount;
|
|
15472
|
+
bestPathLength = pathLength;
|
|
15473
|
+
}
|
|
15474
|
+
}
|
|
15475
|
+
return bestPath;
|
|
15476
|
+
};
|
|
15477
|
+
|
|
15375
15478
|
// lib/solvers/NetLabelToTraceSolver/NetLabelToTraceSolver.ts
|
|
15376
15479
|
var AVAILABLE_NET_ORIENTATION_PREFIX = "available-net-orientation-";
|
|
15377
15480
|
var RECOVERED_TRACE_PREFIX = "net-label-to-trace-";
|
|
@@ -15631,7 +15734,7 @@ var NetLabelToTraceSolver = class extends BaseSolver {
|
|
|
15631
15734
|
}
|
|
15632
15735
|
tryAcceptCurrentRoute() {
|
|
15633
15736
|
const candidate = this.currentCandidate;
|
|
15634
|
-
|
|
15737
|
+
let tracePath = this.activeSubSolver?.solvedTracePath;
|
|
15635
15738
|
if (!candidate || !tracePath) return;
|
|
15636
15739
|
const retainedTraces = this.outputTraces.filter(
|
|
15637
15740
|
(trace) => !this.isSupersededConnectorTrace(trace, candidate)
|
|
@@ -15645,6 +15748,12 @@ var NetLabelToTraceSolver = class extends BaseSolver {
|
|
|
15645
15748
|
if (doesTraceRecoveryPathConflict(tracePath, collisionTraces) || this.routeIntersectsRemainingLabels(tracePath, candidate)) {
|
|
15646
15749
|
return;
|
|
15647
15750
|
}
|
|
15751
|
+
tracePath = reduceTraceCrossings({
|
|
15752
|
+
tracePath,
|
|
15753
|
+
globalConnNetId: candidate.firstLabel.globalConnNetId,
|
|
15754
|
+
otherTraces: collisionTraces,
|
|
15755
|
+
isCandidateValid: (candidatePath) => findFirstCollision(candidatePath, this.activeSubSolver.obstacles) === null && !doesTraceRecoveryPathConflict(candidatePath, collisionTraces) && !this.routeIntersectsRemainingLabels(candidatePath, candidate)
|
|
15756
|
+
});
|
|
15648
15757
|
const [firstPin, secondPin] = candidate.pins;
|
|
15649
15758
|
const mspPairId = `${RECOVERED_TRACE_PREFIX}${candidate.key}`;
|
|
15650
15759
|
const recoveredTrace = {
|
|
@@ -1695,7 +1695,12 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
1695
1695
|
changed = true
|
|
1696
1696
|
}
|
|
1697
1697
|
}
|
|
1698
|
-
if (
|
|
1698
|
+
if (
|
|
1699
|
+
connectedPinIds.size < 3 ||
|
|
1700
|
+
(connectedPinIds.size === 3 && !this.hasPortOnlyLabelOnSameNet(label))
|
|
1701
|
+
) {
|
|
1702
|
+
return null
|
|
1703
|
+
}
|
|
1699
1704
|
|
|
1700
1705
|
const pins = [...connectedPinIds].map((pinId) => this.pinMap[pinId]!)
|
|
1701
1706
|
const direction = dir(orientation)
|