@tscircuit/schematic-trace-solver 0.0.133 → 0.0.134
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
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 EPS14 = 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) < -EPS14) 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) < -EPS14) 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) < EPS14 || Math.abs(p1.y - p2.y) < EPS14) {
|
|
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 };
|
|
@@ -1424,8 +1424,8 @@ var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
|
|
|
1424
1424
|
if (!collision) {
|
|
1425
1425
|
const first = path[0];
|
|
1426
1426
|
const last = path[path.length - 1];
|
|
1427
|
-
const
|
|
1428
|
-
const samePoint = (p, q) => Math.abs(p.x - q.x) <
|
|
1427
|
+
const EPS14 = 1e-9;
|
|
1428
|
+
const samePoint = (p, q) => Math.abs(p.x - q.x) < EPS14 && Math.abs(p.y - q.y) < EPS14;
|
|
1429
1429
|
if (samePoint(first, { x: PA.x, y: PA.y }) && samePoint(last, { x: PB.x, y: PB.y })) {
|
|
1430
1430
|
this.solvedTracePath = path;
|
|
1431
1431
|
this.solved = true;
|
|
@@ -1709,13 +1709,13 @@ var applyJogToTerminalSegment = ({
|
|
|
1709
1709
|
segmentIndex: si,
|
|
1710
1710
|
offset,
|
|
1711
1711
|
JOG_SIZE,
|
|
1712
|
-
EPS:
|
|
1712
|
+
EPS: EPS14 = 1e-6
|
|
1713
1713
|
}) => {
|
|
1714
1714
|
if (si !== 0 && si !== pts.length - 2) return;
|
|
1715
1715
|
const start = pts[si];
|
|
1716
1716
|
const end = pts[si + 1];
|
|
1717
|
-
const isVertical5 = Math.abs(start.x - end.x) <
|
|
1718
|
-
const isHorizontal4 = Math.abs(start.y - end.y) <
|
|
1717
|
+
const isVertical5 = Math.abs(start.x - end.x) < EPS14;
|
|
1718
|
+
const isHorizontal4 = Math.abs(start.y - end.y) < EPS14;
|
|
1719
1719
|
if (!isVertical5 && !isHorizontal4) return;
|
|
1720
1720
|
const segDir = isVertical5 ? end.y > start.y ? 1 : -1 : end.x > start.x ? 1 : -1;
|
|
1721
1721
|
if (pts.length === 2) {
|
|
@@ -2144,7 +2144,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
2144
2144
|
return islands;
|
|
2145
2145
|
}
|
|
2146
2146
|
findNextOverlapIssue() {
|
|
2147
|
-
const
|
|
2147
|
+
const EPS14 = 2e-3;
|
|
2148
2148
|
const netIds = Object.keys(this.traceNetIslands);
|
|
2149
2149
|
for (let i = 0; i < netIds.length; i++) {
|
|
2150
2150
|
for (let j = i + 1; j < netIds.length; j++) {
|
|
@@ -2171,7 +2171,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
2171
2171
|
segmentBIndex,
|
|
2172
2172
|
rangeOverlap
|
|
2173
2173
|
}) => {
|
|
2174
|
-
if (rangeOverlap >
|
|
2174
|
+
if (rangeOverlap > EPS14) {
|
|
2175
2175
|
const keyA = `${pathAIndex}:${segmentAIndex}`;
|
|
2176
2176
|
const keyB = `${pathBIndex}:${segmentBIndex}`;
|
|
2177
2177
|
if (!seenA.has(keyA)) {
|
|
@@ -2190,7 +2190,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
2190
2190
|
}
|
|
2191
2191
|
return;
|
|
2192
2192
|
}
|
|
2193
|
-
if (rangeOverlap < -
|
|
2193
|
+
if (rangeOverlap < -EPS14) return;
|
|
2194
2194
|
const pathA = pathsA[pathAIndex].tracePath;
|
|
2195
2195
|
const pathB = pathsB[pathBIndex].tracePath;
|
|
2196
2196
|
const segmentAStart = pathA[segmentAIndex];
|
|
@@ -2213,8 +2213,8 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
2213
2213
|
for (let sa = 0; sa < ptsA.length - 1; sa++) {
|
|
2214
2214
|
const a1 = ptsA[sa];
|
|
2215
2215
|
const a2 = ptsA[sa + 1];
|
|
2216
|
-
const aVert = Math.abs(a1.x - a2.x) <
|
|
2217
|
-
const aHorz = Math.abs(a1.y - a2.y) <
|
|
2216
|
+
const aVert = Math.abs(a1.x - a2.x) < EPS14;
|
|
2217
|
+
const aHorz = Math.abs(a1.y - a2.y) < EPS14;
|
|
2218
2218
|
if (!aVert && !aHorz) continue;
|
|
2219
2219
|
for (let pb = 0; pb < pathsB.length; pb++) {
|
|
2220
2220
|
const pathB = pathsB[pb];
|
|
@@ -2225,11 +2225,11 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
2225
2225
|
for (let sb = 0; sb < ptsB.length - 1; sb++) {
|
|
2226
2226
|
const b1 = ptsB[sb];
|
|
2227
2227
|
const b2 = ptsB[sb + 1];
|
|
2228
|
-
const bVert = Math.abs(b1.x - b2.x) <
|
|
2229
|
-
const bHorz = Math.abs(b1.y - b2.y) <
|
|
2228
|
+
const bVert = Math.abs(b1.x - b2.x) < EPS14;
|
|
2229
|
+
const bHorz = Math.abs(b1.y - b2.y) < EPS14;
|
|
2230
2230
|
if (!bVert && !bHorz) continue;
|
|
2231
2231
|
if (aVert && bVert) {
|
|
2232
|
-
if (Math.abs(a1.x - b1.x) <
|
|
2232
|
+
if (Math.abs(a1.x - b1.x) < EPS14) {
|
|
2233
2233
|
recordCollinearInteraction({
|
|
2234
2234
|
pathAIndex: pa,
|
|
2235
2235
|
segmentAIndex: sa,
|
|
@@ -2239,7 +2239,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
2239
2239
|
});
|
|
2240
2240
|
}
|
|
2241
2241
|
} else if (aHorz && bHorz) {
|
|
2242
|
-
if (Math.abs(a1.y - b1.y) <
|
|
2242
|
+
if (Math.abs(a1.y - b1.y) < EPS14) {
|
|
2243
2243
|
recordCollinearInteraction({
|
|
2244
2244
|
pathAIndex: pa,
|
|
2245
2245
|
segmentAIndex: sa,
|
|
@@ -2295,14 +2295,14 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
2295
2295
|
return null;
|
|
2296
2296
|
}
|
|
2297
2297
|
findNextDiagonalSegment() {
|
|
2298
|
-
const
|
|
2298
|
+
const EPS14 = 2e-3;
|
|
2299
2299
|
for (const mspPairId in this.correctedTraceMap) {
|
|
2300
2300
|
const tracePath = this.correctedTraceMap[mspPairId].tracePath;
|
|
2301
2301
|
for (let i = 0; i < tracePath.length - 1; i++) {
|
|
2302
2302
|
const p1 = tracePath[i];
|
|
2303
2303
|
const p2 = tracePath[i + 1];
|
|
2304
|
-
const isHorizontal4 = Math.abs(p1.y - p2.y) <
|
|
2305
|
-
const isVertical5 = Math.abs(p1.x - p2.x) <
|
|
2304
|
+
const isHorizontal4 = Math.abs(p1.y - p2.y) < EPS14;
|
|
2305
|
+
const isVertical5 = Math.abs(p1.x - p2.x) < EPS14;
|
|
2306
2306
|
if (!isHorizontal4 && !isVertical5) {
|
|
2307
2307
|
return { mspPairId, tracePath, i, p1, p2 };
|
|
2308
2308
|
}
|
|
@@ -2316,13 +2316,13 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
2316
2316
|
return false;
|
|
2317
2317
|
}
|
|
2318
2318
|
const { mspPairId, tracePath, i, p1, p2 } = diagonalInfo;
|
|
2319
|
-
const
|
|
2319
|
+
const EPS14 = 2e-3;
|
|
2320
2320
|
const p0 = i > 0 ? tracePath[i - 1] : null;
|
|
2321
2321
|
const p3 = i + 2 < tracePath.length ? tracePath[i + 2] : null;
|
|
2322
|
-
const prevIsVertical = p0 ? Math.abs(p0.x - p1.x) <
|
|
2323
|
-
const prevIsHorizontal = p0 ? Math.abs(p0.y - p1.y) <
|
|
2324
|
-
const nextIsVertical = p3 ? Math.abs(p2.x - p3.x) <
|
|
2325
|
-
const nextIsHorizontal = p3 ? Math.abs(p2.y - p3.y) <
|
|
2322
|
+
const prevIsVertical = p0 ? Math.abs(p0.x - p1.x) < EPS14 : false;
|
|
2323
|
+
const prevIsHorizontal = p0 ? Math.abs(p0.y - p1.y) < EPS14 : false;
|
|
2324
|
+
const nextIsVertical = p3 ? Math.abs(p2.x - p3.x) < EPS14 : false;
|
|
2325
|
+
const nextIsHorizontal = p3 ? Math.abs(p2.y - p3.y) < EPS14 : false;
|
|
2326
2326
|
const elbow1 = { x: p1.x, y: p2.y };
|
|
2327
2327
|
const elbow2 = { x: p2.x, y: p1.y };
|
|
2328
2328
|
let score1 = 0;
|
|
@@ -2450,24 +2450,24 @@ var ChipObstacleSpatialIndex = class {
|
|
|
2450
2450
|
};
|
|
2451
2451
|
|
|
2452
2452
|
// lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/collisions.ts
|
|
2453
|
-
function segmentIntersectsRect2(p1, p2, rect,
|
|
2454
|
-
const isVert = Math.abs(p1.x - p2.x) <
|
|
2455
|
-
const isHorz = Math.abs(p1.y - p2.y) <
|
|
2453
|
+
function segmentIntersectsRect2(p1, p2, rect, EPS14 = 1e-9) {
|
|
2454
|
+
const isVert = Math.abs(p1.x - p2.x) < EPS14;
|
|
2455
|
+
const isHorz = Math.abs(p1.y - p2.y) < EPS14;
|
|
2456
2456
|
if (!isVert && !isHorz) return false;
|
|
2457
2457
|
if (isVert) {
|
|
2458
2458
|
const x = p1.x;
|
|
2459
|
-
if (x < rect.minX -
|
|
2459
|
+
if (x < rect.minX - EPS14 || x > rect.maxX + EPS14) return false;
|
|
2460
2460
|
const segMinY = Math.min(p1.y, p2.y);
|
|
2461
2461
|
const segMaxY = Math.max(p1.y, p2.y);
|
|
2462
2462
|
const overlap = Math.min(segMaxY, rect.maxY) - Math.max(segMinY, rect.minY);
|
|
2463
|
-
return overlap >
|
|
2463
|
+
return overlap > EPS14;
|
|
2464
2464
|
} else {
|
|
2465
2465
|
const y = p1.y;
|
|
2466
|
-
if (y < rect.minY -
|
|
2466
|
+
if (y < rect.minY - EPS14 || y > rect.maxY + EPS14) return false;
|
|
2467
2467
|
const segMinX = Math.min(p1.x, p2.x);
|
|
2468
2468
|
const segMaxX = Math.max(p1.x, p2.x);
|
|
2469
2469
|
const overlap = Math.min(segMaxX, rect.maxX) - Math.max(segMinX, rect.minX);
|
|
2470
|
-
return overlap >
|
|
2470
|
+
return overlap > EPS14;
|
|
2471
2471
|
}
|
|
2472
2472
|
}
|
|
2473
2473
|
function rectIntersectsAnyTrace(bounds, inputTraceMap, hostPathId, hostSegIndex) {
|
|
@@ -2864,14 +2864,14 @@ var SingleNetLabelPlacementSolver = class extends BaseSolver {
|
|
|
2864
2864
|
};
|
|
2865
2865
|
let bestCandidate = null;
|
|
2866
2866
|
let bestScore = -Infinity;
|
|
2867
|
-
const
|
|
2867
|
+
const EPS14 = 1e-6;
|
|
2868
2868
|
for (const curr of tracesToScan) {
|
|
2869
2869
|
const pts = curr.tracePath.slice();
|
|
2870
2870
|
for (let si = 0; si < pts.length - 1; si++) {
|
|
2871
2871
|
const a = pts[si];
|
|
2872
2872
|
const b = pts[si + 1];
|
|
2873
|
-
const isH = Math.abs(a.y - b.y) <
|
|
2874
|
-
const isV = Math.abs(a.x - b.x) <
|
|
2873
|
+
const isH = Math.abs(a.y - b.y) < EPS14;
|
|
2874
|
+
const isV = Math.abs(a.x - b.x) < EPS14;
|
|
2875
2875
|
if (!isH && !isV) continue;
|
|
2876
2876
|
const segmentAllowed = isH ? ["y+", "y-"] : ["x+", "x-"];
|
|
2877
2877
|
const candidateOrients = orientations.filter(
|
|
@@ -4619,6 +4619,81 @@ var distance3 = (p1, p2) => {
|
|
|
4619
4619
|
return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));
|
|
4620
4620
|
};
|
|
4621
4621
|
var manhattanDistance = (p1, p2) => Math.abs(p1.x - p2.x) + Math.abs(p1.y - p2.y);
|
|
4622
|
+
var EPS6 = 1e-9;
|
|
4623
|
+
var between = (value, first, second) => value >= Math.min(first, second) - EPS6 && value <= Math.max(first, second) + EPS6;
|
|
4624
|
+
var getAxisAlignedIntersection = (firstStart, firstEnd, secondStart, secondEnd) => {
|
|
4625
|
+
const firstIsHorizontal = Math.abs(firstStart.y - firstEnd.y) <= EPS6;
|
|
4626
|
+
const firstIsVertical = Math.abs(firstStart.x - firstEnd.x) <= EPS6;
|
|
4627
|
+
const secondIsHorizontal = Math.abs(secondStart.y - secondEnd.y) <= EPS6;
|
|
4628
|
+
const secondIsVertical = Math.abs(secondStart.x - secondEnd.x) <= EPS6;
|
|
4629
|
+
if (firstIsHorizontal && secondIsVertical) {
|
|
4630
|
+
const point = { x: secondStart.x, y: firstStart.y };
|
|
4631
|
+
return between(point.x, firstStart.x, firstEnd.x) && between(point.y, secondStart.y, secondEnd.y) ? point : null;
|
|
4632
|
+
}
|
|
4633
|
+
if (firstIsVertical && secondIsHorizontal) {
|
|
4634
|
+
const point = { x: firstStart.x, y: secondStart.y };
|
|
4635
|
+
return between(point.y, firstStart.y, firstEnd.y) && between(point.x, secondStart.x, secondEnd.x) ? point : null;
|
|
4636
|
+
}
|
|
4637
|
+
if (firstIsHorizontal && secondIsHorizontal && Math.abs(firstStart.y - secondStart.y) <= EPS6) {
|
|
4638
|
+
const overlapMin = Math.max(
|
|
4639
|
+
Math.min(firstStart.x, firstEnd.x),
|
|
4640
|
+
Math.min(secondStart.x, secondEnd.x)
|
|
4641
|
+
);
|
|
4642
|
+
const overlapMax = Math.min(
|
|
4643
|
+
Math.max(firstStart.x, firstEnd.x),
|
|
4644
|
+
Math.max(secondStart.x, secondEnd.x)
|
|
4645
|
+
);
|
|
4646
|
+
if (overlapMin > overlapMax + EPS6) return null;
|
|
4647
|
+
return {
|
|
4648
|
+
x: Math.max(overlapMin, Math.min(firstStart.x, overlapMax)),
|
|
4649
|
+
y: firstStart.y
|
|
4650
|
+
};
|
|
4651
|
+
}
|
|
4652
|
+
if (firstIsVertical && secondIsVertical && Math.abs(firstStart.x - secondStart.x) <= EPS6) {
|
|
4653
|
+
const overlapMin = Math.max(
|
|
4654
|
+
Math.min(firstStart.y, firstEnd.y),
|
|
4655
|
+
Math.min(secondStart.y, secondEnd.y)
|
|
4656
|
+
);
|
|
4657
|
+
const overlapMax = Math.min(
|
|
4658
|
+
Math.max(firstStart.y, firstEnd.y),
|
|
4659
|
+
Math.max(secondStart.y, secondEnd.y)
|
|
4660
|
+
);
|
|
4661
|
+
if (overlapMin > overlapMax + EPS6) return null;
|
|
4662
|
+
return {
|
|
4663
|
+
x: firstStart.x,
|
|
4664
|
+
y: Math.max(overlapMin, Math.min(firstStart.y, overlapMax))
|
|
4665
|
+
};
|
|
4666
|
+
}
|
|
4667
|
+
return null;
|
|
4668
|
+
};
|
|
4669
|
+
var clipPathAtFirstIntersection = (tracePath, existingTraces) => {
|
|
4670
|
+
for (let pathIndex = 0; pathIndex < tracePath.length - 1; pathIndex++) {
|
|
4671
|
+
const pathStart = tracePath[pathIndex];
|
|
4672
|
+
const pathEnd = tracePath[pathIndex + 1];
|
|
4673
|
+
const intersections = existingTraces.flatMap(
|
|
4674
|
+
(trace) => trace.tracePath.slice(0, -1).flatMap((traceStart, traceIndex) => {
|
|
4675
|
+
const intersection2 = getAxisAlignedIntersection(
|
|
4676
|
+
pathStart,
|
|
4677
|
+
pathEnd,
|
|
4678
|
+
traceStart,
|
|
4679
|
+
trace.tracePath[traceIndex + 1]
|
|
4680
|
+
);
|
|
4681
|
+
return intersection2 ? [intersection2] : [];
|
|
4682
|
+
})
|
|
4683
|
+
);
|
|
4684
|
+
intersections.sort(
|
|
4685
|
+
(first, second) => distance3(pathStart, first) - distance3(pathStart, second)
|
|
4686
|
+
);
|
|
4687
|
+
const intersection = intersections[0];
|
|
4688
|
+
if (!intersection) continue;
|
|
4689
|
+
const clippedPath = tracePath.slice(0, pathIndex + 1);
|
|
4690
|
+
if (distance3(clippedPath.at(-1), intersection) > EPS6) {
|
|
4691
|
+
clippedPath.push(intersection);
|
|
4692
|
+
}
|
|
4693
|
+
return clippedPath;
|
|
4694
|
+
}
|
|
4695
|
+
return null;
|
|
4696
|
+
};
|
|
4622
4697
|
var LongDistancePairSolver = class extends BaseSolver {
|
|
4623
4698
|
constructor(params) {
|
|
4624
4699
|
super();
|
|
@@ -4733,20 +4808,36 @@ var LongDistancePairSolver = class extends BaseSolver {
|
|
|
4733
4808
|
} else if (this.subSolver?.solved) {
|
|
4734
4809
|
const newTracePath = this.subSolver.solvedTracePath;
|
|
4735
4810
|
if (newTracePath && this.currentCandidatePair) {
|
|
4811
|
+
const [p1, p2] = this.currentCandidatePair;
|
|
4812
|
+
const globalConnNetId = this.netConnMap.getNetConnectedToId(p1.pinId);
|
|
4813
|
+
const sameNetTraces = this.allSolvedTraces.filter(
|
|
4814
|
+
(trace) => trace.globalConnNetId === globalConnNetId
|
|
4815
|
+
);
|
|
4816
|
+
const inputNetConnection = this.inputProblem.netConnections.find(
|
|
4817
|
+
(connection) => connection.pinIds.length === 3 && connection.pinIds.includes(p1.pinId) && connection.pinIds.includes(p2.pinId)
|
|
4818
|
+
);
|
|
4819
|
+
const sourceChip = this.chipMap[p1.chipId];
|
|
4820
|
+
const targetRailTrace = sameNetTraces.find(
|
|
4821
|
+
(trace) => trace.pinIds.includes(p2.pinId) && trace.pins.every((pin) => pin.chipId === p2.chipId)
|
|
4822
|
+
);
|
|
4823
|
+
const canJoinThreePinNet = inputNetConnection !== void 0 && sourceChip?.pins.length === 2 && targetRailTrace !== void 0;
|
|
4824
|
+
const junctionTracePath = canJoinThreePinNet ? clipPathAtFirstIntersection(newTracePath, sameNetTraces) : null;
|
|
4825
|
+
const acceptedTracePath = junctionTracePath ?? newTracePath;
|
|
4826
|
+
const tracesToCheck = junctionTracePath ? this.allSolvedTraces.filter(
|
|
4827
|
+
(trace) => trace.globalConnNetId !== globalConnNetId
|
|
4828
|
+
) : this.allSolvedTraces;
|
|
4736
4829
|
const isTraceClear = !doesTraceOverlapWithExistingTraces(
|
|
4737
|
-
|
|
4738
|
-
|
|
4830
|
+
acceptedTracePath,
|
|
4831
|
+
tracesToCheck
|
|
4739
4832
|
);
|
|
4740
4833
|
if (isTraceClear) {
|
|
4741
|
-
const [p1, p2] = this.currentCandidatePair;
|
|
4742
|
-
const globalConnNetId = this.netConnMap.getNetConnectedToId(p1.pinId);
|
|
4743
4834
|
const mspPairId = `${p1.pinId}-${p2.pinId}`;
|
|
4744
4835
|
const newSolvedTrace = {
|
|
4745
4836
|
mspPairId,
|
|
4746
4837
|
dcConnNetId: globalConnNetId,
|
|
4747
4838
|
globalConnNetId,
|
|
4748
4839
|
pins: [p1, p2],
|
|
4749
|
-
tracePath:
|
|
4840
|
+
tracePath: acceptedTracePath,
|
|
4750
4841
|
mspConnectionPairIds: [mspPairId],
|
|
4751
4842
|
pinIds: [p1.pinId, p2.pinId]
|
|
4752
4843
|
};
|
|
@@ -5525,9 +5616,9 @@ var moveRailSegments = (trace, segments, coordinate) => {
|
|
|
5525
5616
|
};
|
|
5526
5617
|
|
|
5527
5618
|
// lib/solvers/RailNetLabelCornerPlacementSolver/geometry.ts
|
|
5528
|
-
var
|
|
5619
|
+
var EPS7 = 1e-6;
|
|
5529
5620
|
var isTraceLine = (trace) => getUniquePinCount(trace.pinIds) >= 2;
|
|
5530
|
-
var rectsOverlap = (a, b) => Math.min(a.maxX, b.maxX) - Math.max(a.minX, b.minX) >
|
|
5621
|
+
var rectsOverlap = (a, b) => Math.min(a.maxX, b.maxX) - Math.max(a.minX, b.minX) > EPS7 && Math.min(a.maxY, b.maxY) - Math.max(a.minY, b.minY) > EPS7;
|
|
5531
5622
|
var getTraceCorners = (path) => {
|
|
5532
5623
|
const corners = [];
|
|
5533
5624
|
for (let i = 1; i < path.length - 1; i++) {
|
|
@@ -5551,11 +5642,11 @@ var getUniquePinCount = (pinIds) => new Set(pinIds).size;
|
|
|
5551
5642
|
var isTraceCorner = (a, b, c) => getSegmentOrientation(a, b) !== getSegmentOrientation(b, c);
|
|
5552
5643
|
var isPointOnSegment = (point, start, end) => {
|
|
5553
5644
|
if (getSegmentOrientation(start, end) === "horizontal") {
|
|
5554
|
-
return Math.abs(point.y - start.y) <=
|
|
5645
|
+
return Math.abs(point.y - start.y) <= EPS7 && point.x >= Math.min(start.x, end.x) - EPS7 && point.x <= Math.max(start.x, end.x) + EPS7;
|
|
5555
5646
|
}
|
|
5556
|
-
return Math.abs(point.x - start.x) <=
|
|
5647
|
+
return Math.abs(point.x - start.x) <= EPS7 && point.y >= Math.min(start.y, end.y) - EPS7 && point.y <= Math.max(start.y, end.y) + EPS7;
|
|
5557
5648
|
};
|
|
5558
|
-
var getSegmentOrientation = (a, b) => Math.abs(a.y - b.y) <=
|
|
5649
|
+
var getSegmentOrientation = (a, b) => Math.abs(a.y - b.y) <= EPS7 ? "horizontal" : "vertical";
|
|
5559
5650
|
|
|
5560
5651
|
// lib/solvers/TraceCleanupSolver/sameNetRailAlignment/preservesLabelAnchors.ts
|
|
5561
5652
|
var getAnchoredTraceIds = (label, traces) => new Set(
|
|
@@ -5571,17 +5662,17 @@ var preservesLabelAnchors = (labels, before, after) => labels.every((label) => {
|
|
|
5571
5662
|
});
|
|
5572
5663
|
|
|
5573
5664
|
// lib/solvers/Example28Solver/types.ts
|
|
5574
|
-
var
|
|
5665
|
+
var EPS8 = 1e-9;
|
|
5575
5666
|
|
|
5576
5667
|
// lib/solvers/Example28Solver/segmentRunsAlongRectBoundary.ts
|
|
5577
5668
|
var segmentRunsAlongRectBoundary = (start, end, rect) => {
|
|
5578
|
-
const isVertical5 = Math.abs(start.x - end.x) <
|
|
5579
|
-
const isHorizontal4 = Math.abs(start.y - end.y) <
|
|
5669
|
+
const isVertical5 = Math.abs(start.x - end.x) < EPS8;
|
|
5670
|
+
const isHorizontal4 = Math.abs(start.y - end.y) < EPS8;
|
|
5580
5671
|
if (isVertical5) {
|
|
5581
|
-
return Math.abs(start.x - rect.minX) <
|
|
5672
|
+
return Math.abs(start.x - rect.minX) < EPS8 || Math.abs(start.x - rect.maxX) < EPS8;
|
|
5582
5673
|
}
|
|
5583
5674
|
if (isHorizontal4) {
|
|
5584
|
-
return Math.abs(start.y - rect.minY) <
|
|
5675
|
+
return Math.abs(start.y - rect.minY) < EPS8 || Math.abs(start.y - rect.maxY) < EPS8;
|
|
5585
5676
|
}
|
|
5586
5677
|
return false;
|
|
5587
5678
|
};
|
|
@@ -5596,10 +5687,10 @@ var getPathLength2 = (path) => {
|
|
|
5596
5687
|
return length;
|
|
5597
5688
|
};
|
|
5598
5689
|
var getDistance2 = (a, b) => Math.abs(a.x - b.x) + Math.abs(a.y - b.y);
|
|
5599
|
-
var isAxisAlignedSegment = (start, end) => Math.abs(start.x - end.x) <
|
|
5600
|
-
var getSegmentOrientation2 = (start, end) => Math.abs(start.y - end.y) <
|
|
5690
|
+
var isAxisAlignedSegment = (start, end) => Math.abs(start.x - end.x) < EPS8 || Math.abs(start.y - end.y) < EPS8;
|
|
5691
|
+
var getSegmentOrientation2 = (start, end) => Math.abs(start.y - end.y) < EPS8 ? "horizontal" : "vertical";
|
|
5601
5692
|
var projectPointToSegment = (point, start, end) => {
|
|
5602
|
-
if (Math.abs(start.x - end.x) <
|
|
5693
|
+
if (Math.abs(start.x - end.x) < EPS8) {
|
|
5603
5694
|
return {
|
|
5604
5695
|
x: start.x,
|
|
5605
5696
|
y: Math.min(
|
|
@@ -5608,7 +5699,7 @@ var projectPointToSegment = (point, start, end) => {
|
|
|
5608
5699
|
)
|
|
5609
5700
|
};
|
|
5610
5701
|
}
|
|
5611
|
-
if (Math.abs(start.y - end.y) <
|
|
5702
|
+
if (Math.abs(start.y - end.y) < EPS8) {
|
|
5612
5703
|
return {
|
|
5613
5704
|
x: Math.min(
|
|
5614
5705
|
Math.max(point.x, Math.min(start.x, end.x)),
|
|
@@ -5651,9 +5742,9 @@ var findSegmentContainingPoint = (path, point) => {
|
|
|
5651
5742
|
};
|
|
5652
5743
|
var isPointWithinSegmentPrimaryRange = (point, segment) => {
|
|
5653
5744
|
if (segment.orientation === "horizontal") {
|
|
5654
|
-
return point.x >= Math.min(segment.start.x, segment.end.x) -
|
|
5745
|
+
return point.x >= Math.min(segment.start.x, segment.end.x) - EPS8 && point.x <= Math.max(segment.start.x, segment.end.x) + EPS8;
|
|
5655
5746
|
}
|
|
5656
|
-
return point.y >= Math.min(segment.start.y, segment.end.y) -
|
|
5747
|
+
return point.y >= Math.min(segment.start.y, segment.end.y) - EPS8 && point.y <= Math.max(segment.start.y, segment.end.y) + EPS8;
|
|
5657
5748
|
};
|
|
5658
5749
|
var findPreferredReroutedSegment = (path, originalSegmentIndex, originalSegmentCount, orientation, anchorPoint) => {
|
|
5659
5750
|
const matchingSegments = getSegments(path).filter(
|
|
@@ -5681,24 +5772,24 @@ var projectPointToPath = (point, path) => {
|
|
|
5681
5772
|
return bestPoint;
|
|
5682
5773
|
};
|
|
5683
5774
|
var segmentsIntersect = (a1, a2, b1, b2) => {
|
|
5684
|
-
const aVertical = Math.abs(a1.x - a2.x) <
|
|
5685
|
-
const bVertical = Math.abs(b1.x - b2.x) <
|
|
5686
|
-
const
|
|
5775
|
+
const aVertical = Math.abs(a1.x - a2.x) < EPS8;
|
|
5776
|
+
const bVertical = Math.abs(b1.x - b2.x) < EPS8;
|
|
5777
|
+
const between2 = (value, p1, p2) => value >= Math.min(p1, p2) - EPS8 && value <= Math.max(p1, p2) + EPS8;
|
|
5687
5778
|
if (aVertical && bVertical) {
|
|
5688
|
-
if (Math.abs(a1.x - b1.x) >
|
|
5779
|
+
if (Math.abs(a1.x - b1.x) > EPS8) return false;
|
|
5689
5780
|
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));
|
|
5690
|
-
return overlap >
|
|
5781
|
+
return overlap > EPS8;
|
|
5691
5782
|
}
|
|
5692
5783
|
if (!aVertical && !bVertical) {
|
|
5693
|
-
if (Math.abs(a1.y - b1.y) >
|
|
5784
|
+
if (Math.abs(a1.y - b1.y) > EPS8) return false;
|
|
5694
5785
|
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));
|
|
5695
|
-
return overlap >
|
|
5786
|
+
return overlap > EPS8;
|
|
5696
5787
|
}
|
|
5697
5788
|
const verticalA = aVertical ? a1 : b1;
|
|
5698
5789
|
const verticalB = aVertical ? a2 : b2;
|
|
5699
5790
|
const horizontalA = aVertical ? b1 : a1;
|
|
5700
5791
|
const horizontalB = aVertical ? b2 : a2;
|
|
5701
|
-
return
|
|
5792
|
+
return between2(verticalA.x, horizontalA.x, horizontalB.x) && between2(horizontalA.y, verticalA.y, verticalB.y);
|
|
5702
5793
|
};
|
|
5703
5794
|
var countPathIntersections = (pathA, pathB) => {
|
|
5704
5795
|
let count = 0;
|
|
@@ -6068,7 +6159,7 @@ var getTraceObstacles = (allTraces, excludeTraceId) => {
|
|
|
6068
6159
|
|
|
6069
6160
|
// lib/solvers/TraceCleanupSolver/sub-solver/findIntersectionsWithObstacles.ts
|
|
6070
6161
|
import { getSegmentIntersection } from "@tscircuit/math-utils/line-intersections";
|
|
6071
|
-
var
|
|
6162
|
+
var EPS9 = 1e-6;
|
|
6072
6163
|
var findIntersectionsWithObstacles = (p1, p2, obstacles) => {
|
|
6073
6164
|
const intersections = [];
|
|
6074
6165
|
for (const obstacle of obstacles) {
|
|
@@ -6087,17 +6178,17 @@ var findIntersectionsWithObstacles = (p1, p2, obstacles) => {
|
|
|
6087
6178
|
}
|
|
6088
6179
|
return intersections;
|
|
6089
6180
|
};
|
|
6090
|
-
var isSamePoint = (first, second) => Math.abs(first.x - second.x) <
|
|
6181
|
+
var isSamePoint = (first, second) => Math.abs(first.x - second.x) < EPS9 && Math.abs(first.y - second.y) < EPS9;
|
|
6091
6182
|
var findPerpendicularPathCrossings = (path, otherPath) => {
|
|
6092
6183
|
const crossings = [];
|
|
6093
6184
|
for (let pathSegmentIndex = 1; pathSegmentIndex < path.length - 2; pathSegmentIndex++) {
|
|
6094
6185
|
const start = path[pathSegmentIndex];
|
|
6095
6186
|
const end = path[pathSegmentIndex + 1];
|
|
6096
|
-
const isVertical5 = Math.abs(start.x - end.x) <
|
|
6187
|
+
const isVertical5 = Math.abs(start.x - end.x) < EPS9;
|
|
6097
6188
|
for (let otherPathSegmentIndex = 1; otherPathSegmentIndex < otherPath.length - 2; otherPathSegmentIndex++) {
|
|
6098
6189
|
const otherStart = otherPath[otherPathSegmentIndex];
|
|
6099
6190
|
const otherEnd = otherPath[otherPathSegmentIndex + 1];
|
|
6100
|
-
const otherIsVertical = Math.abs(otherStart.x - otherEnd.x) <
|
|
6191
|
+
const otherIsVertical = Math.abs(otherStart.x - otherEnd.x) < EPS9;
|
|
6101
6192
|
if (isVertical5 === otherIsVertical) continue;
|
|
6102
6193
|
const intersection = getSegmentIntersection(
|
|
6103
6194
|
start,
|
|
@@ -6117,8 +6208,8 @@ var findPerpendicularPathCrossings = (path, otherPath) => {
|
|
|
6117
6208
|
};
|
|
6118
6209
|
|
|
6119
6210
|
// lib/solvers/TraceCleanupSolver/sub-solver/generateLShapeRerouteCandidates.ts
|
|
6120
|
-
var
|
|
6121
|
-
var isVertical3 = (a, b, eps =
|
|
6211
|
+
var EPS10 = 1e-6;
|
|
6212
|
+
var isVertical3 = (a, b, eps = EPS10) => Math.abs(a.x - b.x) < eps;
|
|
6122
6213
|
var generateLShapeRerouteCandidates = ({
|
|
6123
6214
|
lShape,
|
|
6124
6215
|
rectangle,
|
|
@@ -6131,7 +6222,7 @@ var generateLShapeRerouteCandidates = ({
|
|
|
6131
6222
|
let c2;
|
|
6132
6223
|
let i1_padded = interactionPoint1;
|
|
6133
6224
|
let i2_padded = interactionPoint2;
|
|
6134
|
-
if (Math.abs(p2.x - x) <
|
|
6225
|
+
if (Math.abs(p2.x - x) < EPS10 && Math.abs(p2.y - (y + height)) < EPS10) {
|
|
6135
6226
|
c2 = { x: x + width + padding, y: y - padding };
|
|
6136
6227
|
if (isVertical3(p1, p2)) {
|
|
6137
6228
|
i1_padded = { x: interactionPoint1.x, y: interactionPoint1.y - padding };
|
|
@@ -6143,7 +6234,7 @@ var generateLShapeRerouteCandidates = ({
|
|
|
6143
6234
|
} else {
|
|
6144
6235
|
i2_padded = { x: interactionPoint2.x + padding, y: interactionPoint2.y };
|
|
6145
6236
|
}
|
|
6146
|
-
} else if (Math.abs(p2.x - (x + width)) <
|
|
6237
|
+
} else if (Math.abs(p2.x - (x + width)) < EPS10 && Math.abs(p2.y - (y + height)) < EPS10) {
|
|
6147
6238
|
c2 = { x: x - padding, y: y - padding };
|
|
6148
6239
|
if (isVertical3(p1, p2)) {
|
|
6149
6240
|
i1_padded = { x: interactionPoint1.x, y: interactionPoint1.y - padding };
|
|
@@ -6155,7 +6246,7 @@ var generateLShapeRerouteCandidates = ({
|
|
|
6155
6246
|
} else {
|
|
6156
6247
|
i2_padded = { x: interactionPoint2.x - padding, y: interactionPoint2.y };
|
|
6157
6248
|
}
|
|
6158
|
-
} else if (Math.abs(p2.x - x) <
|
|
6249
|
+
} else if (Math.abs(p2.x - x) < EPS10 && Math.abs(p2.y - y) < EPS10) {
|
|
6159
6250
|
c2 = { x: x + width + padding, y: y + height + padding };
|
|
6160
6251
|
if (isVertical3(p1, p2)) {
|
|
6161
6252
|
i1_padded = { x: interactionPoint1.x, y: interactionPoint1.y + padding };
|
|
@@ -6167,7 +6258,7 @@ var generateLShapeRerouteCandidates = ({
|
|
|
6167
6258
|
} else {
|
|
6168
6259
|
i2_padded = { x: interactionPoint2.x + padding, y: interactionPoint2.y };
|
|
6169
6260
|
}
|
|
6170
|
-
} else if (Math.abs(p2.x - (x + width)) <
|
|
6261
|
+
} else if (Math.abs(p2.x - (x + width)) < EPS10 && Math.abs(p2.y - y) < EPS10) {
|
|
6171
6262
|
c2 = { x: x - padding, y: y + height + padding };
|
|
6172
6263
|
if (isVertical3(p1, p2)) {
|
|
6173
6264
|
i1_padded = { x: interactionPoint1.x, y: interactionPoint1.y + padding };
|
|
@@ -6195,7 +6286,7 @@ var generatePerpendicularTraceDetours = ({
|
|
|
6195
6286
|
const buildDetours = (path, index) => {
|
|
6196
6287
|
const start = path[index];
|
|
6197
6288
|
const end = path[index + 1];
|
|
6198
|
-
const movingAxis = Math.abs(start.x - end.x) <
|
|
6289
|
+
const movingAxis = Math.abs(start.x - end.x) < EPS10 ? "y" : "x";
|
|
6199
6290
|
const detourAxis = movingAxis === "x" ? "y" : "x";
|
|
6200
6291
|
const gate = obstacleStart[movingAxis] + Math.sign(start[movingAxis] - obstacleStart[movingAxis]) * clearance;
|
|
6201
6292
|
const obstacleRange = [obstacleStart[detourAxis], obstacleEnd[detourAxis]];
|
|
@@ -6793,9 +6884,9 @@ var UntangleTraceSubsolver = class extends BaseSolver {
|
|
|
6793
6884
|
};
|
|
6794
6885
|
|
|
6795
6886
|
// lib/solvers/TraceCleanupSolver/is4PointRectangle.ts
|
|
6796
|
-
var
|
|
6797
|
-
var sameX = (a, b) => Math.abs(a.x - b.x) <=
|
|
6798
|
-
var sameY = (a, b) => Math.abs(a.y - b.y) <=
|
|
6887
|
+
var EPS11 = 1e-6;
|
|
6888
|
+
var sameX = (a, b) => Math.abs(a.x - b.x) <= EPS11;
|
|
6889
|
+
var sameY = (a, b) => Math.abs(a.y - b.y) <= EPS11;
|
|
6799
6890
|
var is4PointRectangle = (path) => {
|
|
6800
6891
|
if (path.length !== 4) return false;
|
|
6801
6892
|
const [p0, p1, p2, p3] = path;
|
|
@@ -7070,7 +7161,7 @@ var doesPathRunAlongChipBoundary = (path, chipObstacles) => {
|
|
|
7070
7161
|
const end = path[i + 1];
|
|
7071
7162
|
for (const obstacle of chipObstacles) {
|
|
7072
7163
|
if (!segmentRunsAlongRectBoundary(start, end, obstacle)) continue;
|
|
7073
|
-
if (getSegmentOverlapWithRectSpan(start, end, obstacle) >
|
|
7164
|
+
if (getSegmentOverlapWithRectSpan(start, end, obstacle) > EPS8) {
|
|
7074
7165
|
return true;
|
|
7075
7166
|
}
|
|
7076
7167
|
}
|
|
@@ -7085,13 +7176,13 @@ var getChipBoundaryOverlap = (path, chipObstacles) => {
|
|
|
7085
7176
|
for (const obstacle of chipObstacles) {
|
|
7086
7177
|
if (!segmentRunsAlongRectBoundary(start, end, obstacle)) continue;
|
|
7087
7178
|
const overlap = getSegmentOverlapWithRectSpan(start, end, obstacle);
|
|
7088
|
-
if (overlap >
|
|
7179
|
+
if (overlap > EPS8) total += overlap;
|
|
7089
7180
|
}
|
|
7090
7181
|
}
|
|
7091
7182
|
return total;
|
|
7092
7183
|
};
|
|
7093
7184
|
var getSegmentOverlapWithRectSpan = (start, end, rect) => {
|
|
7094
|
-
const isVertical5 = Math.abs(start.x - end.x) <
|
|
7185
|
+
const isVertical5 = Math.abs(start.x - end.x) < EPS8;
|
|
7095
7186
|
if (isVertical5) {
|
|
7096
7187
|
return Math.min(Math.max(start.y, end.y), rect.maxY) - Math.max(Math.min(start.y, end.y), rect.minY);
|
|
7097
7188
|
}
|
|
@@ -7855,16 +7946,16 @@ var createPortOnlyLabelConnectorTrace = ({
|
|
|
7855
7946
|
// lib/solvers/AvailableNetOrientationSolver/constants.ts
|
|
7856
7947
|
var LABEL_SEARCH_STEP = 0.05;
|
|
7857
7948
|
var WICK_CLEARANCE = 1e-3;
|
|
7858
|
-
var
|
|
7859
|
-
var TRACE_BOUNDARY_TOLERANCE = WICK_CLEARANCE +
|
|
7949
|
+
var EPS12 = 1e-9;
|
|
7950
|
+
var TRACE_BOUNDARY_TOLERANCE = WICK_CLEARANCE + EPS12;
|
|
7860
7951
|
var CANDIDATE_SELECTED_COLOR2 = "blue";
|
|
7861
7952
|
var CANDIDATE_REJECTED_COLOR2 = "red";
|
|
7862
7953
|
|
|
7863
7954
|
// lib/solvers/AvailableNetOrientationSolver/geometry.ts
|
|
7864
7955
|
var isYOrientation = (orientation) => orientation === "y+" || orientation === "y-";
|
|
7865
7956
|
var isXOrientation = (orientation) => orientation === "x+" || orientation === "x-";
|
|
7866
|
-
var rectsOverlap2 = (a, b) => Math.min(a.maxX, b.maxX) - Math.max(a.minX, b.minX) >
|
|
7867
|
-
var rangesOverlap = (minA, maxA, minB, maxB) => Math.min(maxA, maxB) - Math.max(minA, minB) >
|
|
7957
|
+
var rectsOverlap2 = (a, b) => Math.min(a.maxX, b.maxX) - Math.max(a.minX, b.minX) > EPS12 && Math.min(a.maxY, b.maxY) - Math.max(a.minY, b.minY) > EPS12;
|
|
7958
|
+
var rangesOverlap = (minA, maxA, minB, maxB) => Math.min(maxA, maxB) - Math.max(minA, minB) > EPS12;
|
|
7868
7959
|
var traceCrossesBoundsInterior = (bounds, traceMap) => {
|
|
7869
7960
|
for (const trace of Object.values(traceMap)) {
|
|
7870
7961
|
const points = trace.tracePath;
|
|
@@ -7887,7 +7978,7 @@ var segmentCrossesBoundsInterior = (p1, p2, bounds) => {
|
|
|
7887
7978
|
return false;
|
|
7888
7979
|
}
|
|
7889
7980
|
if (sameX2(p1, p2)) {
|
|
7890
|
-
if (p1.x <= interiorBounds.minX +
|
|
7981
|
+
if (p1.x <= interiorBounds.minX + EPS12 || p1.x >= interiorBounds.maxX - EPS12) {
|
|
7891
7982
|
return false;
|
|
7892
7983
|
}
|
|
7893
7984
|
return rangesOverlap(
|
|
@@ -7898,7 +7989,7 @@ var segmentCrossesBoundsInterior = (p1, p2, bounds) => {
|
|
|
7898
7989
|
);
|
|
7899
7990
|
}
|
|
7900
7991
|
if (sameY2(p1, p2)) {
|
|
7901
|
-
if (p1.y <= interiorBounds.minY +
|
|
7992
|
+
if (p1.y <= interiorBounds.minY + EPS12 || p1.y >= interiorBounds.maxY - EPS12) {
|
|
7902
7993
|
return false;
|
|
7903
7994
|
}
|
|
7904
7995
|
return rangesOverlap(
|
|
@@ -7954,8 +8045,8 @@ var simplifyOrthogonalPath = (path) => {
|
|
|
7954
8045
|
return simplified;
|
|
7955
8046
|
};
|
|
7956
8047
|
var pointsEqual2 = (a, b) => sameX2(a, b) && sameY2(a, b);
|
|
7957
|
-
var sameX2 = (a, b) => Math.abs(a.x - b.x) <=
|
|
7958
|
-
var sameY2 = (a, b) => Math.abs(a.y - b.y) <=
|
|
8048
|
+
var sameX2 = (a, b) => Math.abs(a.x - b.x) <= EPS12;
|
|
8049
|
+
var sameY2 = (a, b) => Math.abs(a.y - b.y) <= EPS12;
|
|
7959
8050
|
var getMaxSearchDistance = (inputProblem) => {
|
|
7960
8051
|
const maxChipWidth = Math.max(
|
|
7961
8052
|
...inputProblem.chips.map((chip) => chip.width),
|
|
@@ -8411,7 +8502,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
8411
8502
|
index,
|
|
8412
8503
|
pin: this.pinMap[otherLabel.pinIds[0]]
|
|
8413
8504
|
})).filter(
|
|
8414
|
-
({ index, pin: otherPin }) => this.crowdedPortOnlyLabelIndices.has(index) && otherPin?.chipId === pin.chipId && otherPin.x > pin.x +
|
|
8505
|
+
({ index, pin: otherPin }) => this.crowdedPortOnlyLabelIndices.has(index) && otherPin?.chipId === pin.chipId && otherPin.x > pin.x + EPS12
|
|
8415
8506
|
).sort((a, b) => a.pin.x - b.pin.x)[0]?.pin;
|
|
8416
8507
|
if (orientation === "y-" && nextPinToRight) return null;
|
|
8417
8508
|
const anchorX = orientation === "y-" ? Math.max(
|
|
@@ -8444,7 +8535,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
8444
8535
|
const centeredAnchorY = hasUpFacingStack ? (lowestUpFacingLabelY + highestUpFacingLabelY) / 2 : stackedAnchorY;
|
|
8445
8536
|
const anchorYs = orientation === "y-" ? [centeredAnchorY, stackedAnchorY] : [stackedAnchorY];
|
|
8446
8537
|
for (const anchorY of anchorYs) {
|
|
8447
|
-
if (anchorY - label.anchorPoint.y > this.maxSearchDistance +
|
|
8538
|
+
if (anchorY - label.anchorPoint.y > this.maxSearchDistance + EPS12) {
|
|
8448
8539
|
continue;
|
|
8449
8540
|
}
|
|
8450
8541
|
const candidate = this.createCandidate(
|
|
@@ -8473,11 +8564,11 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
8473
8564
|
if (trace.globalConnNetId !== label.globalConnNetId) return false;
|
|
8474
8565
|
const path = trace.tracePath;
|
|
8475
8566
|
return path.some((point, pointIndex) => {
|
|
8476
|
-
if (Math.abs(point.x - label.anchorPoint.x) >
|
|
8567
|
+
if (Math.abs(point.x - label.anchorPoint.x) > EPS12 || Math.abs(point.y - label.anchorPoint.y) > EPS12) {
|
|
8477
8568
|
return false;
|
|
8478
8569
|
}
|
|
8479
8570
|
return [path[pointIndex - 1], path[pointIndex + 1]].some(
|
|
8480
|
-
(neighbor) => neighbor && Math.abs(neighbor.x - point.x) <=
|
|
8571
|
+
(neighbor) => neighbor && Math.abs(neighbor.x - point.x) <= EPS12 && (neighbor.y - point.y) * direction.y > EPS12
|
|
8481
8572
|
);
|
|
8482
8573
|
});
|
|
8483
8574
|
});
|
|
@@ -8567,7 +8658,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
8567
8658
|
orientation
|
|
8568
8659
|
);
|
|
8569
8660
|
if (outwardDirection.x === 0 && outwardDirection.y === 0) continue;
|
|
8570
|
-
for (let distance5 = LABEL_SEARCH_STEP; distance5 <= this.maxSearchDistance +
|
|
8661
|
+
for (let distance5 = LABEL_SEARCH_STEP; distance5 <= this.maxSearchDistance + EPS12; distance5 += LABEL_SEARCH_STEP) {
|
|
8571
8662
|
const anchorPoint = {
|
|
8572
8663
|
x: connectorSource.x + outwardDirection.x * distance5,
|
|
8573
8664
|
y: connectorSource.y + outwardDirection.y * distance5
|
|
@@ -8622,7 +8713,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
8622
8713
|
);
|
|
8623
8714
|
const maxSearchDistance = this.getSearchDistanceLimit(label, orientation);
|
|
8624
8715
|
const maxOutwardDistance = outwardDirection.x === 0 && outwardDirection.y === 0 ? 0 : this.maxSearchDistance;
|
|
8625
|
-
for (let outwardDistance = 0; outwardDistance <= maxOutwardDistance +
|
|
8716
|
+
for (let outwardDistance = 0; outwardDistance <= maxOutwardDistance + EPS12; outwardDistance += LABEL_SEARCH_STEP) {
|
|
8626
8717
|
const baseAnchor = {
|
|
8627
8718
|
x: initialBaseAnchor.x + outwardDirection.x * outwardDistance,
|
|
8628
8719
|
y: initialBaseAnchor.y + outwardDirection.y * outwardDistance
|
|
@@ -8696,7 +8787,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
8696
8787
|
phase = "shift",
|
|
8697
8788
|
stopOnTraceCollision = true
|
|
8698
8789
|
} = params;
|
|
8699
|
-
for (let distance5 = LABEL_SEARCH_STEP; distance5 <= maxSearchDistance +
|
|
8790
|
+
for (let distance5 = LABEL_SEARCH_STEP; distance5 <= maxSearchDistance + EPS12; distance5 += LABEL_SEARCH_STEP) {
|
|
8700
8791
|
const anchorPoint = {
|
|
8701
8792
|
x: baseAnchor.x + direction.x * distance5,
|
|
8702
8793
|
y: baseAnchor.y + direction.y * distance5
|
|
@@ -9002,9 +9093,9 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
9002
9093
|
const { anchorPoint, orientation } = candidate;
|
|
9003
9094
|
const chipBounds = chip.bounds;
|
|
9004
9095
|
if (isYOrientation(orientation)) {
|
|
9005
|
-
return anchorPoint.x < chipBounds.minX -
|
|
9096
|
+
return anchorPoint.x < chipBounds.minX - EPS12 || anchorPoint.x > chipBounds.maxX + EPS12;
|
|
9006
9097
|
}
|
|
9007
|
-
return anchorPoint.y < chipBounds.minY -
|
|
9098
|
+
return anchorPoint.y < chipBounds.minY - EPS12 || anchorPoint.y > chipBounds.maxY + EPS12;
|
|
9008
9099
|
});
|
|
9009
9100
|
}
|
|
9010
9101
|
getBoundsStatus(candidateBoundsCheck) {
|
|
@@ -9105,8 +9196,8 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
9105
9196
|
if (!chip) return null;
|
|
9106
9197
|
const sides = new Set(
|
|
9107
9198
|
pins.map((pin) => {
|
|
9108
|
-
if (Math.abs(pin.x - chip.bounds.minX) <=
|
|
9109
|
-
if (Math.abs(pin.x - chip.bounds.maxX) <=
|
|
9199
|
+
if (Math.abs(pin.x - chip.bounds.minX) <= EPS12) return "left";
|
|
9200
|
+
if (Math.abs(pin.x - chip.bounds.maxX) <= EPS12) return "right";
|
|
9110
9201
|
return null;
|
|
9111
9202
|
})
|
|
9112
9203
|
);
|
|
@@ -9139,8 +9230,8 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
9139
9230
|
sharesChipBoundary(bounds) {
|
|
9140
9231
|
for (const chip of this.chipObstacleSpatialIndex.chips) {
|
|
9141
9232
|
const chipBounds = chip.bounds;
|
|
9142
|
-
const adjacentToVerticalSide = Math.abs(bounds.minX - chipBounds.maxX) <= WICK_CLEARANCE +
|
|
9143
|
-
const adjacentToHorizontalSide = Math.abs(bounds.minY - chipBounds.maxY) <= WICK_CLEARANCE +
|
|
9233
|
+
const adjacentToVerticalSide = Math.abs(bounds.minX - chipBounds.maxX) <= WICK_CLEARANCE + EPS12 || Math.abs(bounds.maxX - chipBounds.minX) <= WICK_CLEARANCE + EPS12;
|
|
9234
|
+
const adjacentToHorizontalSide = Math.abs(bounds.minY - chipBounds.maxY) <= WICK_CLEARANCE + EPS12 || Math.abs(bounds.maxY - chipBounds.minY) <= WICK_CLEARANCE + EPS12;
|
|
9144
9235
|
if (adjacentToVerticalSide && rangesOverlap(
|
|
9145
9236
|
bounds.minY,
|
|
9146
9237
|
bounds.maxY,
|
|
@@ -9202,7 +9293,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
9202
9293
|
let nearestDistance = Number.POSITIVE_INFINITY;
|
|
9203
9294
|
for (const chip of this.chipObstacleSpatialIndex.chips) {
|
|
9204
9295
|
const bounds = chip.bounds;
|
|
9205
|
-
if (point.x < bounds.minX -
|
|
9296
|
+
if (point.x < bounds.minX - EPS12 || point.x > bounds.maxX + EPS12 || point.y < bounds.minY - EPS12 || point.y > bounds.maxY + EPS12) {
|
|
9206
9297
|
continue;
|
|
9207
9298
|
}
|
|
9208
9299
|
for (const [side, distance5] of getSideDistances(point, bounds)) {
|
|
@@ -9219,8 +9310,8 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
9219
9310
|
let nearestDistanceSq = Number.POSITIVE_INFINITY;
|
|
9220
9311
|
for (const chip of this.chipObstacleSpatialIndex.chips) {
|
|
9221
9312
|
const bounds = chip.bounds;
|
|
9222
|
-
const dx = point.x < bounds.minX -
|
|
9223
|
-
const dy = point.y < bounds.minY -
|
|
9313
|
+
const dx = point.x < bounds.minX - EPS12 ? bounds.minX - point.x : point.x > bounds.maxX + EPS12 ? point.x - bounds.maxX : 0;
|
|
9314
|
+
const dy = point.y < bounds.minY - EPS12 ? bounds.minY - point.y : point.y > bounds.maxY + EPS12 ? point.y - bounds.maxY : 0;
|
|
9224
9315
|
if (dx === 0 && dy === 0) continue;
|
|
9225
9316
|
const distanceSq = dx ** 2 + dy ** 2;
|
|
9226
9317
|
if (distanceSq >= nearestDistanceSq) continue;
|
|
@@ -9536,7 +9627,7 @@ var RailNetLabelCornerPlacementSolver = class extends BaseSolver {
|
|
|
9536
9627
|
for (const anchorPoint of getTraceCorners(path)) {
|
|
9537
9628
|
const cornerIndex = path.indexOf(anchorPoint);
|
|
9538
9629
|
const anchorAlignedToPin = pins.some(
|
|
9539
|
-
(pin) => Math.abs(pin.x - anchorPoint.x) <=
|
|
9630
|
+
(pin) => Math.abs(pin.x - anchorPoint.x) <= EPS7
|
|
9540
9631
|
);
|
|
9541
9632
|
const pinAligned = anchorAlignedToPin || allowRailAlignedFallback && this.isPinAlignedCorner(path, cornerIndex);
|
|
9542
9633
|
if (!pinAligned) continue;
|
|
@@ -9576,12 +9667,12 @@ var RailNetLabelCornerPlacementSolver = class extends BaseSolver {
|
|
|
9576
9667
|
return traceCrossesBoundsInterior(bounds, otherNetTraceMap);
|
|
9577
9668
|
}
|
|
9578
9669
|
pointsEqual(a, b) {
|
|
9579
|
-
return Math.abs(a.x - b.x) <=
|
|
9670
|
+
return Math.abs(a.x - b.x) <= EPS7 && Math.abs(a.y - b.y) <= EPS7;
|
|
9580
9671
|
}
|
|
9581
9672
|
getVerticalSegmentPointIndices(path, cornerIndex) {
|
|
9582
9673
|
const corner = path[cornerIndex];
|
|
9583
|
-
const previousIsVertical = Math.abs(path[cornerIndex - 1].x - corner.x) <=
|
|
9584
|
-
const nextIsVertical = Math.abs(path[cornerIndex + 1].x - corner.x) <=
|
|
9674
|
+
const previousIsVertical = Math.abs(path[cornerIndex - 1].x - corner.x) <= EPS7;
|
|
9675
|
+
const nextIsVertical = Math.abs(path[cornerIndex + 1].x - corner.x) <= EPS7;
|
|
9585
9676
|
if (previousIsVertical === nextIsVertical) return null;
|
|
9586
9677
|
const indices = previousIsVertical ? [cornerIndex - 1, cornerIndex] : [cornerIndex, cornerIndex + 1];
|
|
9587
9678
|
return indices;
|
|
@@ -9631,7 +9722,7 @@ var RailNetLabelCornerPlacementSolver = class extends BaseSolver {
|
|
|
9631
9722
|
const end = otherTrace.tracePath[i + 1];
|
|
9632
9723
|
if (!segmentCrossesBoundsInterior(start, end, labelBounds)) continue;
|
|
9633
9724
|
const halfLabelWidth = label.width / 2;
|
|
9634
|
-
if (Math.abs(start.x - end.x) <=
|
|
9725
|
+
if (Math.abs(start.x - end.x) <= EPS7) {
|
|
9635
9726
|
shiftedCoordinates.add(
|
|
9636
9727
|
start.x - halfLabelWidth - LABEL_TRACE_CLEARANCE3
|
|
9637
9728
|
);
|
|
@@ -9649,7 +9740,7 @@ var RailNetLabelCornerPlacementSolver = class extends BaseSolver {
|
|
|
9649
9740
|
}
|
|
9650
9741
|
}
|
|
9651
9742
|
return [...shiftedCoordinates].flatMap((x) => {
|
|
9652
|
-
if (Math.abs(x - corner.x) <=
|
|
9743
|
+
if (Math.abs(x - corner.x) <= EPS7) return [];
|
|
9653
9744
|
const reroutedTracePath = simplifyPath(
|
|
9654
9745
|
path.map(
|
|
9655
9746
|
(point, pointIndex) => verticalPointIndices.includes(pointIndex) ? { ...point, x } : point
|
|
@@ -9780,11 +9871,11 @@ var getOrientationConstraint = (inputProblem, label) => {
|
|
|
9780
9871
|
};
|
|
9781
9872
|
|
|
9782
9873
|
// lib/solvers/TraceAnchoredNetLabelOverlapSolver/geometry.ts
|
|
9783
|
-
var
|
|
9874
|
+
var EPS13 = 1e-6;
|
|
9784
9875
|
var TRACE_BOUNDARY_TOLERANCE2 = 1e-6;
|
|
9785
9876
|
var getLabelBounds = (label) => getRectBounds(label.center, label.width, label.height);
|
|
9786
|
-
var rectsOverlap3 = (a, b) => Math.min(a.maxX, b.maxX) - Math.max(a.minX, b.minX) >
|
|
9787
|
-
var rectsTouchOrOverlap = (a, b) => Math.min(a.maxX, b.maxX) - Math.max(a.minX, b.minX) >= -
|
|
9877
|
+
var rectsOverlap3 = (a, b) => Math.min(a.maxX, b.maxX) - Math.max(a.minX, b.minX) > EPS13 && Math.min(a.maxY, b.maxY) - Math.max(a.minY, b.minY) > EPS13;
|
|
9878
|
+
var rectsTouchOrOverlap = (a, b) => Math.min(a.maxX, b.maxX) - Math.max(a.minX, b.minX) >= -EPS13 && Math.min(a.maxY, b.maxY) - Math.max(a.minY, b.minY) >= -EPS13;
|
|
9788
9879
|
var traceCrossesBoundsInterior2 = (bounds, traces) => {
|
|
9789
9880
|
for (const trace of traces) {
|
|
9790
9881
|
const points = trace.tracePath;
|
|
@@ -9830,7 +9921,7 @@ var getPointAtTraceDistance = (trace, distance5) => {
|
|
|
9830
9921
|
const end = trace.tracePath[i + 1];
|
|
9831
9922
|
const segmentLength = getManhattanDistance(start, end);
|
|
9832
9923
|
const nextDistance = pathDistance + segmentLength;
|
|
9833
|
-
if (distance5 <= nextDistance +
|
|
9924
|
+
if (distance5 <= nextDistance + EPS13) {
|
|
9834
9925
|
const offset = Math.max(
|
|
9835
9926
|
0,
|
|
9836
9927
|
Math.min(segmentLength, distance5 - pathDistance)
|
|
@@ -9869,7 +9960,7 @@ var segmentCrossesBoundsInterior2 = (start, end, bounds) => {
|
|
|
9869
9960
|
return false;
|
|
9870
9961
|
}
|
|
9871
9962
|
if (isVertical4(start, end)) {
|
|
9872
|
-
if (start.x <= interior.minX +
|
|
9963
|
+
if (start.x <= interior.minX + EPS13 || start.x >= interior.maxX - EPS13) {
|
|
9873
9964
|
return false;
|
|
9874
9965
|
}
|
|
9875
9966
|
return rangesOverlap2(
|
|
@@ -9880,7 +9971,7 @@ var segmentCrossesBoundsInterior2 = (start, end, bounds) => {
|
|
|
9880
9971
|
);
|
|
9881
9972
|
}
|
|
9882
9973
|
if (isHorizontal3(start, end)) {
|
|
9883
|
-
if (start.y <= interior.minY +
|
|
9974
|
+
if (start.y <= interior.minY + EPS13 || start.y >= interior.maxY - EPS13) {
|
|
9884
9975
|
return false;
|
|
9885
9976
|
}
|
|
9886
9977
|
return rangesOverlap2(
|
|
@@ -9894,10 +9985,10 @@ var segmentCrossesBoundsInterior2 = (start, end, bounds) => {
|
|
|
9894
9985
|
};
|
|
9895
9986
|
var isPointOnSegment2 = (point, start, end) => {
|
|
9896
9987
|
if (isHorizontal3(start, end)) {
|
|
9897
|
-
return Math.abs(point.y - start.y) <=
|
|
9988
|
+
return Math.abs(point.y - start.y) <= EPS13 && point.x >= Math.min(start.x, end.x) - EPS13 && point.x <= Math.max(start.x, end.x) + EPS13;
|
|
9898
9989
|
}
|
|
9899
9990
|
if (isVertical4(start, end)) {
|
|
9900
|
-
return Math.abs(point.x - start.x) <=
|
|
9991
|
+
return Math.abs(point.x - start.x) <= EPS13 && point.y >= Math.min(start.y, end.y) - EPS13 && point.y <= Math.max(start.y, end.y) + EPS13;
|
|
9901
9992
|
}
|
|
9902
9993
|
return false;
|
|
9903
9994
|
};
|
|
@@ -9905,9 +9996,9 @@ var getSegmentDirection = (start, end) => ({
|
|
|
9905
9996
|
x: isVertical4(start, end) ? 0 : Math.sign(end.x - start.x),
|
|
9906
9997
|
y: isHorizontal3(start, end) ? 0 : Math.sign(end.y - start.y)
|
|
9907
9998
|
});
|
|
9908
|
-
var isHorizontal3 = (start, end) => Math.abs(start.y - end.y) <=
|
|
9909
|
-
var isVertical4 = (start, end) => Math.abs(start.x - end.x) <=
|
|
9910
|
-
var rangesOverlap2 = (minA, maxA, minB, maxB) => Math.min(maxA, maxB) - Math.max(minA, minB) >
|
|
9999
|
+
var isHorizontal3 = (start, end) => Math.abs(start.y - end.y) <= EPS13;
|
|
10000
|
+
var isVertical4 = (start, end) => Math.abs(start.x - end.x) <= EPS13;
|
|
10001
|
+
var rangesOverlap2 = (minA, maxA, minB, maxB) => Math.min(maxA, maxB) - Math.max(minA, minB) > EPS13;
|
|
9911
10002
|
var getUniquePinCount2 = (pinIds) => new Set(pinIds).size;
|
|
9912
10003
|
|
|
9913
10004
|
// lib/solvers/TraceAnchoredNetLabelOverlapSolver/candidates.ts
|
|
@@ -9964,7 +10055,7 @@ var getCandidateDistances = (traceLength, vertexDistances) => {
|
|
|
9964
10055
|
for (const distance5 of vertexDistances) {
|
|
9965
10056
|
distances.add(roundDistance(distance5));
|
|
9966
10057
|
}
|
|
9967
|
-
return [...distances].filter((distance5) => distance5 >= -
|
|
10058
|
+
return [...distances].filter((distance5) => distance5 >= -EPS13 && distance5 <= traceLength + EPS13).sort((a, b) => a - b);
|
|
9968
10059
|
};
|
|
9969
10060
|
var getOrientationsForPoint = (params) => {
|
|
9970
10061
|
const { inputProblem, label, point, orientationConstraint } = params;
|
|
@@ -10053,13 +10144,13 @@ var getOutwardOrientationOptions = (point, chipBounds) => {
|
|
|
10053
10144
|
};
|
|
10054
10145
|
var getOutwardAxisOption = (params) => {
|
|
10055
10146
|
const { value, min, max, center, positiveOrientation, negativeOrientation } = params;
|
|
10056
|
-
if (value > max +
|
|
10147
|
+
if (value > max + EPS13) {
|
|
10057
10148
|
return {
|
|
10058
10149
|
orientation: positiveOrientation,
|
|
10059
10150
|
outwardScore: 1 + value - max
|
|
10060
10151
|
};
|
|
10061
10152
|
}
|
|
10062
|
-
if (value < min -
|
|
10153
|
+
if (value < min - EPS13) {
|
|
10063
10154
|
return {
|
|
10064
10155
|
orientation: negativeOrientation,
|
|
10065
10156
|
outwardScore: 1 + min - value
|
|
@@ -10070,7 +10161,7 @@ var getOutwardAxisOption = (params) => {
|
|
|
10070
10161
|
outwardScore: getNormalizedCenterDistance(value, center, max - min)
|
|
10071
10162
|
};
|
|
10072
10163
|
};
|
|
10073
|
-
var getNormalizedCenterDistance = (value, center, span) => Math.abs(value - center) / Math.max(
|
|
10164
|
+
var getNormalizedCenterDistance = (value, center, span) => Math.abs(value - center) / Math.max(EPS13, span / 2);
|
|
10074
10165
|
var ALL_ORIENTATIONS = ["x+", "x-", "y+", "y-"];
|
|
10075
10166
|
var getFlippedOrientation = (orientation) => {
|
|
10076
10167
|
switch (orientation) {
|
|
@@ -10143,7 +10234,7 @@ var getNetLabelHeight = (inputProblem, label) => {
|
|
|
10143
10234
|
)?.netLabelHeight;
|
|
10144
10235
|
};
|
|
10145
10236
|
var roundDistance = (distance5) => Number(distance5.toFixed(6));
|
|
10146
|
-
var isSamePlacement = (label, point, orientation) => Math.abs(point.x - label.anchorPoint.x) <=
|
|
10237
|
+
var isSamePlacement = (label, point, orientation) => Math.abs(point.x - label.anchorPoint.x) <= EPS13 && Math.abs(point.y - label.anchorPoint.y) <= EPS13 && orientation === label.orientation;
|
|
10147
10238
|
|
|
10148
10239
|
// lib/solvers/TraceAnchoredNetLabelOverlapSolver/visualize.ts
|
|
10149
10240
|
var CANDIDATE_SELECTED_COLOR4 = "blue";
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getConnectivityMapsFromInputProblem } from "lib/solvers/MspConnectionPairSolver/getConnectivityMapFromInputProblem"
|
|
2
|
+
import type { Point } from "@tscircuit/math-utils"
|
|
2
3
|
import {
|
|
3
4
|
DEFAULT_MAX_MSP_PAIR_DISTANCE,
|
|
4
5
|
type MspConnectionPair,
|
|
@@ -20,13 +21,118 @@ import { isLabeledPeripheralConnection } from "../MspConnectionPairSolver/isLabe
|
|
|
20
21
|
|
|
21
22
|
const NEAREST_NEIGHBOR_COUNT = 3
|
|
22
23
|
|
|
23
|
-
const distance = (p1:
|
|
24
|
+
const distance = (p1: Point, p2: Point) => {
|
|
24
25
|
return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2))
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
const manhattanDistance = (p1: InputPin, p2: InputPin) =>
|
|
28
29
|
Math.abs(p1.x - p2.x) + Math.abs(p1.y - p2.y)
|
|
29
30
|
|
|
31
|
+
const EPS = 1e-9
|
|
32
|
+
|
|
33
|
+
const between = (value: number, first: number, second: number) =>
|
|
34
|
+
value >= Math.min(first, second) - EPS &&
|
|
35
|
+
value <= Math.max(first, second) + EPS
|
|
36
|
+
|
|
37
|
+
const getAxisAlignedIntersection = (
|
|
38
|
+
firstStart: Point,
|
|
39
|
+
firstEnd: Point,
|
|
40
|
+
secondStart: Point,
|
|
41
|
+
secondEnd: Point,
|
|
42
|
+
) => {
|
|
43
|
+
const firstIsHorizontal = Math.abs(firstStart.y - firstEnd.y) <= EPS
|
|
44
|
+
const firstIsVertical = Math.abs(firstStart.x - firstEnd.x) <= EPS
|
|
45
|
+
const secondIsHorizontal = Math.abs(secondStart.y - secondEnd.y) <= EPS
|
|
46
|
+
const secondIsVertical = Math.abs(secondStart.x - secondEnd.x) <= EPS
|
|
47
|
+
|
|
48
|
+
if (firstIsHorizontal && secondIsVertical) {
|
|
49
|
+
const point = { x: secondStart.x, y: firstStart.y }
|
|
50
|
+
return between(point.x, firstStart.x, firstEnd.x) &&
|
|
51
|
+
between(point.y, secondStart.y, secondEnd.y)
|
|
52
|
+
? point
|
|
53
|
+
: null
|
|
54
|
+
}
|
|
55
|
+
if (firstIsVertical && secondIsHorizontal) {
|
|
56
|
+
const point = { x: firstStart.x, y: secondStart.y }
|
|
57
|
+
return between(point.y, firstStart.y, firstEnd.y) &&
|
|
58
|
+
between(point.x, secondStart.x, secondEnd.x)
|
|
59
|
+
? point
|
|
60
|
+
: null
|
|
61
|
+
}
|
|
62
|
+
if (
|
|
63
|
+
firstIsHorizontal &&
|
|
64
|
+
secondIsHorizontal &&
|
|
65
|
+
Math.abs(firstStart.y - secondStart.y) <= EPS
|
|
66
|
+
) {
|
|
67
|
+
const overlapMin = Math.max(
|
|
68
|
+
Math.min(firstStart.x, firstEnd.x),
|
|
69
|
+
Math.min(secondStart.x, secondEnd.x),
|
|
70
|
+
)
|
|
71
|
+
const overlapMax = Math.min(
|
|
72
|
+
Math.max(firstStart.x, firstEnd.x),
|
|
73
|
+
Math.max(secondStart.x, secondEnd.x),
|
|
74
|
+
)
|
|
75
|
+
if (overlapMin > overlapMax + EPS) return null
|
|
76
|
+
return {
|
|
77
|
+
x: Math.max(overlapMin, Math.min(firstStart.x, overlapMax)),
|
|
78
|
+
y: firstStart.y,
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (
|
|
82
|
+
firstIsVertical &&
|
|
83
|
+
secondIsVertical &&
|
|
84
|
+
Math.abs(firstStart.x - secondStart.x) <= EPS
|
|
85
|
+
) {
|
|
86
|
+
const overlapMin = Math.max(
|
|
87
|
+
Math.min(firstStart.y, firstEnd.y),
|
|
88
|
+
Math.min(secondStart.y, secondEnd.y),
|
|
89
|
+
)
|
|
90
|
+
const overlapMax = Math.min(
|
|
91
|
+
Math.max(firstStart.y, firstEnd.y),
|
|
92
|
+
Math.max(secondStart.y, secondEnd.y),
|
|
93
|
+
)
|
|
94
|
+
if (overlapMin > overlapMax + EPS) return null
|
|
95
|
+
return {
|
|
96
|
+
x: firstStart.x,
|
|
97
|
+
y: Math.max(overlapMin, Math.min(firstStart.y, overlapMax)),
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return null
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const clipPathAtFirstIntersection = (
|
|
104
|
+
tracePath: SolvedTracePath["tracePath"],
|
|
105
|
+
existingTraces: SolvedTracePath[],
|
|
106
|
+
) => {
|
|
107
|
+
for (let pathIndex = 0; pathIndex < tracePath.length - 1; pathIndex++) {
|
|
108
|
+
const pathStart = tracePath[pathIndex]!
|
|
109
|
+
const pathEnd = tracePath[pathIndex + 1]!
|
|
110
|
+
const intersections = existingTraces.flatMap((trace) =>
|
|
111
|
+
trace.tracePath.slice(0, -1).flatMap((traceStart, traceIndex) => {
|
|
112
|
+
const intersection = getAxisAlignedIntersection(
|
|
113
|
+
pathStart,
|
|
114
|
+
pathEnd,
|
|
115
|
+
traceStart,
|
|
116
|
+
trace.tracePath[traceIndex + 1]!,
|
|
117
|
+
)
|
|
118
|
+
return intersection ? [intersection] : []
|
|
119
|
+
}),
|
|
120
|
+
)
|
|
121
|
+
intersections.sort(
|
|
122
|
+
(first, second) =>
|
|
123
|
+
distance(pathStart, first) - distance(pathStart, second),
|
|
124
|
+
)
|
|
125
|
+
const intersection = intersections[0]
|
|
126
|
+
if (!intersection) continue
|
|
127
|
+
const clippedPath = tracePath.slice(0, pathIndex + 1)
|
|
128
|
+
if (distance(clippedPath.at(-1)!, intersection) > EPS) {
|
|
129
|
+
clippedPath.push(intersection)
|
|
130
|
+
}
|
|
131
|
+
return clippedPath
|
|
132
|
+
}
|
|
133
|
+
return null
|
|
134
|
+
}
|
|
135
|
+
|
|
30
136
|
export class LongDistancePairSolver extends BaseSolver {
|
|
31
137
|
public solvedLongDistanceTraces: SolvedTracePath[] = []
|
|
32
138
|
private queuedCandidatePairs: Array<
|
|
@@ -196,14 +302,42 @@ export class LongDistancePairSolver extends BaseSolver {
|
|
|
196
302
|
} else if (this.subSolver?.solved) {
|
|
197
303
|
const newTracePath = this.subSolver.solvedTracePath
|
|
198
304
|
if (newTracePath && this.currentCandidatePair) {
|
|
305
|
+
const [p1, p2] = this.currentCandidatePair
|
|
306
|
+
const globalConnNetId = this.netConnMap.getNetConnectedToId(p1.pinId)!
|
|
307
|
+
const sameNetTraces = this.allSolvedTraces.filter(
|
|
308
|
+
(trace) => trace.globalConnNetId === globalConnNetId,
|
|
309
|
+
)
|
|
310
|
+
const inputNetConnection = this.inputProblem.netConnections.find(
|
|
311
|
+
(connection) =>
|
|
312
|
+
connection.pinIds.length === 3 &&
|
|
313
|
+
connection.pinIds.includes(p1.pinId) &&
|
|
314
|
+
connection.pinIds.includes(p2.pinId),
|
|
315
|
+
)
|
|
316
|
+
const sourceChip = this.chipMap[p1.chipId]
|
|
317
|
+
const targetRailTrace = sameNetTraces.find(
|
|
318
|
+
(trace) =>
|
|
319
|
+
trace.pinIds.includes(p2.pinId) &&
|
|
320
|
+
trace.pins.every((pin) => pin.chipId === p2.chipId),
|
|
321
|
+
)
|
|
322
|
+
const canJoinThreePinNet =
|
|
323
|
+
inputNetConnection !== undefined &&
|
|
324
|
+
sourceChip?.pins.length === 2 &&
|
|
325
|
+
targetRailTrace !== undefined
|
|
326
|
+
const junctionTracePath = canJoinThreePinNet
|
|
327
|
+
? clipPathAtFirstIntersection(newTracePath, sameNetTraces)
|
|
328
|
+
: null
|
|
329
|
+
const acceptedTracePath = junctionTracePath ?? newTracePath
|
|
330
|
+
const tracesToCheck = junctionTracePath
|
|
331
|
+
? this.allSolvedTraces.filter(
|
|
332
|
+
(trace) => trace.globalConnNetId !== globalConnNetId,
|
|
333
|
+
)
|
|
334
|
+
: this.allSolvedTraces
|
|
199
335
|
const isTraceClear = !doesTraceOverlapWithExistingTraces(
|
|
200
|
-
|
|
201
|
-
|
|
336
|
+
acceptedTracePath,
|
|
337
|
+
tracesToCheck,
|
|
202
338
|
)
|
|
203
339
|
|
|
204
340
|
if (isTraceClear) {
|
|
205
|
-
const [p1, p2] = this.currentCandidatePair
|
|
206
|
-
const globalConnNetId = this.netConnMap.getNetConnectedToId(p1.pinId)!
|
|
207
341
|
const mspPairId = `${p1.pinId}-${p2.pinId}`
|
|
208
342
|
|
|
209
343
|
const newSolvedTrace: SolvedTracePath = {
|
|
@@ -211,7 +345,7 @@ export class LongDistancePairSolver extends BaseSolver {
|
|
|
211
345
|
dcConnNetId: globalConnNetId,
|
|
212
346
|
globalConnNetId,
|
|
213
347
|
pins: [p1, p2],
|
|
214
|
-
tracePath:
|
|
348
|
+
tracePath: acceptedTracePath,
|
|
215
349
|
mspConnectionPairIds: [mspPairId],
|
|
216
350
|
pinIds: [p1.pinId, p2.pinId],
|
|
217
351
|
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
<svg width="640" height="640" viewBox="0 0 640 640" xmlns="http://www.w3.org/2000/svg"><rect width="100%" height="100%" fill="white"/><g><polyline data-points="0.4,1.5 2.5,1.5" data-type="line" data-label="" points="339.77220956719816,237.08428246013665 473.71298405466973,237.08428246013665" fill="none" stroke="hsl(238, 100%, 50%, 0.8)" stroke-width="1px"/></g><g><polyline data-points="-1.7,1.65 -1.7,1.3499999999999999" data-type="line" data-label="" points="205.83143507972665,227.51708428246013 205.83143507972665,246.65148063781322" fill="none" stroke="hsl(170, 100%, 50%, 0.8)" stroke-width="1px" stroke-dasharray="4 2"/></g><g><polyline data-points="-1.7,1.65 -0.4,1.5" data-type="line" data-label="" points="205.83143507972665,227.51708428246013 288.74715261959,237.08428246013665" fill="none" stroke="hsl(170, 100%, 50%, 0.8)" stroke-width="1px" stroke-dasharray="4 2"/></g><g><polyline data-points="-1.7,1.3499999999999999 -0.4,1.5" data-type="line" data-label="" points="205.83143507972665,246.65148063781322 288.74715261959,237.08428246013665" fill="none" stroke="hsl(170, 100%, 50%, 0.8)" stroke-width="1px" stroke-dasharray="4 2"/></g><g><polyline data-points="-1.7,-1.35 -1.7,-1.65" data-type="line" data-label="" points="205.83143507972665,418.8610478359909 205.83143507972665,437.99544419134395" fill="none" stroke="hsl(124, 100%, 50%, 0.8)" stroke-width="1px" stroke-dasharray="4 2"/></g><g><polyline data-points="-1.7,-1.35 3.5,1.5" data-type="line" data-label="" points="205.83143507972665,418.8610478359909 537.49430523918,237.08428246013665" fill="none" stroke="hsl(124, 100%, 50%, 0.8)" stroke-width="1px" stroke-dasharray="4 2"/></g><g><polyline data-points="-1.7,-1.65 3.5,1.5" data-type="line" data-label="" points="205.83143507972665,437.99544419134395 537.49430523918,237.08428246013665" fill="none" stroke="hsl(124, 100%, 50%, 0.8)" stroke-width="1px" stroke-dasharray="4 2"/></g><g><polyline data-points="0.4,1.5 2.5,1.5" data-type="line" data-label="" points="339.77220956719816,237.08428246013665 473.71298405466973,237.08428246013665" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="-1.7,1.3499999999999999 -1.5,1.3499999999999999 -1.5,1.65 -1.7,1.65" data-type="line" data-label="" points="205.83143507972665,246.65148063781322 218.5876993166287,246.65148063781322 218.5876993166287,227.51708428246013 205.83143507972665,227.51708428246013" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="-1.7,-1.65 -1.5,-1.65 -1.5,-1.35 -1.7,-1.35" data-type="line" data-label="" points="205.83143507972665,437.99544419134395 218.5876993166287,437.99544419134395 218.5876993166287,418.8610478359909 205.83143507972665,418.8610478359909" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="3.5,1.5 3.7,1.5 3.7,-1.35 -1.5,-1.35" data-type="line" data-label="" points="537.49430523918,237.08428246013665 550.2505694760821,237.08428246013665 550.2505694760821,418.8610478359909 218.5876993166287,418.8610478359909" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="-0.4,1.5 -1.5,1.5" data-type="line" data-label="" points="288.74715261959,237.08428246013665 218.5876993166287,237.08428246013665" fill="none" stroke="purple" stroke-width="1px"/></g><g><rect data-type="rect" data-label="J1" data-x="-3" data-y="0" x="40" y="205.19362186788152" width="165.83143507972665" height="255.12528473804105" fill="hsl(183, 100%, 50%, 0.8)" stroke="black" stroke-width="0.015678571428571427"/></g><g><rect data-type="rect" data-label="R1" data-x="0" data-y="1.5" x="288.74715261959" y="221.13895216400908" width="51.025056947608164" height="31.89066059225513" fill="hsl(71, 100%, 50%, 0.8)" stroke="black" stroke-width="0.015678571428571427"/></g><g><rect data-type="rect" data-label="D1" data-x="3" data-y="1.5" x="473.71298405466973" y="211.57175398633254" width="63.78132118451026" height="51.02505694760822" fill="hsl(357, 100%, 50%, 0.8)" stroke="black" stroke-width="0.015678571428571427"/></g><g><rect data-type="rect" data-label="J1 TYPE-C-31-M-12" data-x="-3" data-y="2.3" x="71.89066059225513" y="179.68109339407744" width="102.05011389521638" height="12.75626423690207" fill="rgba(160, 0, 220, 0.14)" stroke="black" stroke-width="0.015678571428571427"/></g><g><rect data-type="rect" data-label="R1 1kΩ" data-x="0" data-y="1.95" x="301.50341685649204" y="202.004555808656" width="25.512528473804082" height="12.75626423690207" fill="rgba(160, 0, 220, 0.14)" stroke="black" stroke-width="0.015678571428571427"/></g><g><rect data-type="rect" data-label="D1 white" data-x="3" data-y="2.15" x="489.6583143507973" y="189.24829157175398" width="31.89066059225513" height="12.756264236902041" fill="rgba(160, 0, 220, 0.14)" stroke="black" stroke-width="0.015678571428571427"/></g><g><rect data-type="rect" data-label="netId: VBUS
|
|
2
|
+
globalConnNetId: connectivity_net1" data-x="-0.95" data-y="1.74" x="238.3599088838269" y="206.46924829157177" width="30.615034168564904" height="30.615034168564904" fill="#ef444466" stroke="#ef4444" stroke-width="0.015678571428571427"/></g><g><rect data-type="rect" data-label="netId: J1_GND1
|
|
3
|
+
globalConnNetId: connectivity_net2" data-x="4.09" data-y="1.5" x="550.2505694760821" y="221.7767653758542" width="49.74943052391802" height="30.615034168564904" fill="#00000066" stroke="#000000" stroke-width="0.015678571428571427"/></g><g><rect data-type="rect" data-label="netId: VBUS_LED
|
|
4
|
+
globalConnNetId: connectivity_net0" data-x="1.45" data-y="1.725" x="400.3644646924829" y="208.38268792710704" width="12.756264236902098" height="28.701594533029606" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.015678571428571427"/></g><g><circle data-type="point" data-label="J1.VBUS1
|
|
5
|
+
x+" data-x="-1.7" data-y="1.65" cx="205.83143507972665" cy="227.51708428246013" r="3" fill="hsl(208, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="J1.VBUS2
|
|
6
|
+
x+" data-x="-1.7" data-y="1.3499999999999999" cx="205.83143507972665" cy="246.65148063781322" r="3" fill="hsl(209, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="J1.CC1
|
|
7
|
+
x+" data-x="-1.7" data-y="1.0499999999999998" cx="205.83143507972665" cy="265.7858769931663" r="3" fill="hsl(322, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="J1.CC2
|
|
8
|
+
x+" data-x="-1.7" data-y="0.75" cx="205.83143507972665" cy="284.92027334851934" r="3" fill="hsl(323, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="J1.DP1
|
|
9
|
+
x+" data-x="-1.7" data-y="0.44999999999999996" cx="205.83143507972665" cy="304.05466970387243" r="3" fill="hsl(246, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="J1.DP2
|
|
10
|
+
x+" data-x="-1.7" data-y="0.1499999999999999" cx="205.83143507972665" cy="323.1890660592255" r="3" fill="hsl(247, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="J1.DM1
|
|
11
|
+
x+" data-x="-1.7" data-y="-0.1499999999999999" cx="205.83143507972665" cy="342.32346241457856" r="3" fill="hsl(153, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="J1.DM2
|
|
12
|
+
x+" data-x="-1.7" data-y="-0.4500000000000002" cx="205.83143507972665" cy="361.45785876993165" r="3" fill="hsl(154, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="J1.SBU1
|
|
13
|
+
x+" data-x="-1.7" data-y="-0.75" cx="205.83143507972665" cy="380.59225512528474" r="3" fill="hsl(122, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="J1.SBU2
|
|
14
|
+
x+" data-x="-1.7" data-y="-1.0499999999999998" cx="205.83143507972665" cy="399.72665148063777" r="3" fill="hsl(123, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="J1.GND1
|
|
15
|
+
x+" data-x="-1.7" data-y="-1.35" cx="205.83143507972665" cy="418.8610478359909" r="3" fill="hsl(315, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="J1.GND2
|
|
16
|
+
x+" data-x="-1.7" data-y="-1.65" cx="205.83143507972665" cy="437.99544419134395" r="3" fill="hsl(316, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="R1.1
|
|
17
|
+
x-" data-x="-0.4" data-y="1.5" cx="288.74715261959" cy="237.08428246013665" r="3" fill="hsl(226, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="R1.2
|
|
18
|
+
x+" data-x="0.4" data-y="1.5" cx="339.77220956719816" cy="237.08428246013665" r="3" fill="hsl(227, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="D1.1
|
|
19
|
+
x-" data-x="2.5" data-y="1.5" cx="473.71298405466973" cy="237.08428246013665" r="3" fill="hsl(32, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="D1.2
|
|
20
|
+
x+" data-x="3.5" data-y="1.5" cx="537.49430523918" cy="237.08428246013665" r="3" fill="hsl(33, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="anchorPoint
|
|
21
|
+
orientation: y+" data-x="-0.95" data-y="1.5" cx="253.66742596810934" cy="237.08428246013665" r="3" fill="hsl(40, 100%, 50%, 0.9)"/></g><g><circle data-type="point" data-label="anchorPoint
|
|
22
|
+
orientation: x+" data-x="3.7" data-y="1.5" cx="550.2505694760821" cy="237.08428246013665" r="3" fill="hsl(40, 100%, 50%, 0.9)"/></g><g><circle data-type="point" data-label="anchorPoint
|
|
23
|
+
orientation: y+" data-x="1.45" data-y="1.5" cx="406.74259681093395" cy="237.08428246013665" r="3" fill="hsl(40, 100%, 50%, 0.9)"/></g><g id="crosshair" style="display: none"><line id="crosshair-h" y1="0" y2="640" stroke="#666" stroke-width="0.5"/><line id="crosshair-v" x1="0" x2="640" stroke="#666" stroke-width="0.5"/><text id="coordinates" font-family="monospace" font-size="12" fill="#666"></text></g><script><![CDATA[
|
|
24
|
+
document.currentScript.parentElement.addEventListener('mousemove', (e) => {
|
|
25
|
+
const svg = e.currentTarget;
|
|
26
|
+
const rect = svg.getBoundingClientRect();
|
|
27
|
+
const x = e.clientX - rect.left;
|
|
28
|
+
const y = e.clientY - rect.top;
|
|
29
|
+
const crosshair = svg.getElementById('crosshair');
|
|
30
|
+
const h = svg.getElementById('crosshair-h');
|
|
31
|
+
const v = svg.getElementById('crosshair-v');
|
|
32
|
+
const coords = svg.getElementById('coordinates');
|
|
33
|
+
|
|
34
|
+
crosshair.style.display = 'block';
|
|
35
|
+
h.setAttribute('x1', '0');
|
|
36
|
+
h.setAttribute('x2', '640');
|
|
37
|
+
h.setAttribute('y1', y);
|
|
38
|
+
h.setAttribute('y2', y);
|
|
39
|
+
v.setAttribute('x1', x);
|
|
40
|
+
v.setAttribute('x2', x);
|
|
41
|
+
v.setAttribute('y1', '0');
|
|
42
|
+
v.setAttribute('y2', '640');
|
|
43
|
+
|
|
44
|
+
// Calculate real coordinates using inverse transformation
|
|
45
|
+
const matrix = {"a":63.781321184510254,"c":0,"e":314.2596810933941,"b":0,"d":-63.781321184510254,"f":332.75626423690204};
|
|
46
|
+
// Manually invert and apply the affine transform
|
|
47
|
+
// Since we only use translate and scale, we can directly compute:
|
|
48
|
+
// x' = (x - tx) / sx
|
|
49
|
+
// y' = (y - ty) / sy
|
|
50
|
+
const sx = matrix.a;
|
|
51
|
+
const sy = matrix.d;
|
|
52
|
+
const tx = matrix.e;
|
|
53
|
+
const ty = matrix.f;
|
|
54
|
+
const realPoint = {
|
|
55
|
+
x: (x - tx) / sx,
|
|
56
|
+
y: (y - ty) / sy // Flip y back since we used negative scale
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
coords.textContent = `(${realPoint.x.toFixed(2)}, ${realPoint.y.toFixed(2)})`;
|
|
60
|
+
coords.setAttribute('x', (x + 5).toString());
|
|
61
|
+
coords.setAttribute('y', (y - 5).toString());
|
|
62
|
+
});
|
|
63
|
+
document.currentScript.parentElement.addEventListener('mouseleave', () => {
|
|
64
|
+
document.currentScript.parentElement.getElementById('crosshair').style.display = 'none';
|
|
65
|
+
});
|
|
66
|
+
]]></script></svg>
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { expect, test } from "bun:test"
|
|
2
|
+
import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver"
|
|
3
|
+
import type { InputProblem } from "lib/types/InputProblem"
|
|
4
|
+
import "tests/fixtures/matcher"
|
|
5
|
+
|
|
6
|
+
const connectorPins = [
|
|
7
|
+
"VBUS1",
|
|
8
|
+
"VBUS2",
|
|
9
|
+
"CC1",
|
|
10
|
+
"CC2",
|
|
11
|
+
"DP1",
|
|
12
|
+
"DP2",
|
|
13
|
+
"DM1",
|
|
14
|
+
"DM2",
|
|
15
|
+
"SBU1",
|
|
16
|
+
"SBU2",
|
|
17
|
+
"GND1",
|
|
18
|
+
"GND2",
|
|
19
|
+
].map((name, index) => ({
|
|
20
|
+
pinId: `J1.${name}`,
|
|
21
|
+
x: -1.7,
|
|
22
|
+
y: 1.65 - index * 0.3,
|
|
23
|
+
_facingDirection: "x+" as const,
|
|
24
|
+
}))
|
|
25
|
+
|
|
26
|
+
// Reproduces a TYPE-C-31-M-12 connector with its two VBUS pins joined to a
|
|
27
|
+
// resistor/LED chain while the LED cathode shares the connector's GND net.
|
|
28
|
+
// The distant GND endpoints should be represented by separate net labels.
|
|
29
|
+
const inputProblem: InputProblem = {
|
|
30
|
+
chips: [
|
|
31
|
+
{
|
|
32
|
+
chipId: "J1",
|
|
33
|
+
center: { x: -3, y: 0 },
|
|
34
|
+
width: 2.6,
|
|
35
|
+
height: 4,
|
|
36
|
+
pins: connectorPins,
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
chipId: "R1",
|
|
40
|
+
center: { x: 0, y: 1.5 },
|
|
41
|
+
width: 0.8,
|
|
42
|
+
height: 0.5,
|
|
43
|
+
pins: [
|
|
44
|
+
{
|
|
45
|
+
pinId: "R1.1",
|
|
46
|
+
x: -0.4,
|
|
47
|
+
y: 1.5,
|
|
48
|
+
_facingDirection: "x-",
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
pinId: "R1.2",
|
|
52
|
+
x: 0.4,
|
|
53
|
+
y: 1.5,
|
|
54
|
+
_facingDirection: "x+",
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
chipId: "D1",
|
|
60
|
+
center: { x: 3, y: 1.5 },
|
|
61
|
+
width: 1,
|
|
62
|
+
height: 0.8,
|
|
63
|
+
pins: [
|
|
64
|
+
{
|
|
65
|
+
pinId: "D1.1",
|
|
66
|
+
x: 2.5,
|
|
67
|
+
y: 1.5,
|
|
68
|
+
_facingDirection: "x-",
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
pinId: "D1.2",
|
|
72
|
+
x: 3.5,
|
|
73
|
+
y: 1.5,
|
|
74
|
+
_facingDirection: "x+",
|
|
75
|
+
},
|
|
76
|
+
],
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
directConnections: [
|
|
80
|
+
{
|
|
81
|
+
netId: "VBUS_LED",
|
|
82
|
+
pinIds: ["R1.2", "D1.1"],
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
netConnections: [
|
|
86
|
+
{
|
|
87
|
+
netId: "VBUS",
|
|
88
|
+
pinIds: ["J1.VBUS1", "J1.VBUS2", "R1.1"],
|
|
89
|
+
netLabelWidth: 0.48,
|
|
90
|
+
netLabelHeight: 0.48,
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
netId: "J1_GND1",
|
|
94
|
+
pinIds: ["J1.GND1", "J1.GND2", "D1.2"],
|
|
95
|
+
netLabelWidth: 0.78,
|
|
96
|
+
netLabelHeight: 0.48,
|
|
97
|
+
},
|
|
98
|
+
],
|
|
99
|
+
textBoxes: [
|
|
100
|
+
{
|
|
101
|
+
chipId: "J1",
|
|
102
|
+
center: { x: -3, y: 2.3 },
|
|
103
|
+
width: 1.6,
|
|
104
|
+
height: 0.2,
|
|
105
|
+
text: "J1 TYPE-C-31-M-12",
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
chipId: "R1",
|
|
109
|
+
center: { x: 0, y: 1.95 },
|
|
110
|
+
width: 0.4,
|
|
111
|
+
height: 0.2,
|
|
112
|
+
text: "R1 1kΩ",
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
chipId: "D1",
|
|
116
|
+
center: { x: 3, y: 2.15 },
|
|
117
|
+
width: 0.5,
|
|
118
|
+
height: 0.2,
|
|
119
|
+
text: "D1 white",
|
|
120
|
+
},
|
|
121
|
+
],
|
|
122
|
+
availableNetLabelOrientations: {
|
|
123
|
+
VBUS: ["x+", "y+"],
|
|
124
|
+
J1_GND1: ["y-", "x+"],
|
|
125
|
+
},
|
|
126
|
+
maxMspPairDistance: 2.4,
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
test("routes TYPE-C-31-M-12 VBUS through an LED with shared GND labels", () => {
|
|
130
|
+
const solver = new SchematicTracePipelineSolver(inputProblem)
|
|
131
|
+
|
|
132
|
+
solver.solve()
|
|
133
|
+
|
|
134
|
+
const output = solver.netLabelNetLabelCollisionSolver!.getOutput()
|
|
135
|
+
const gndLabels = output.netLabelPlacements.filter(
|
|
136
|
+
(label) => label.netId === "J1_GND1",
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
const recoveredGroundTrace =
|
|
140
|
+
solver.longDistancePairSolver!.solvedLongDistanceTraces.find(
|
|
141
|
+
(trace) =>
|
|
142
|
+
trace.pinIds.includes("D1.2") &&
|
|
143
|
+
trace.pinIds.some((pinId) => pinId.startsWith("J1.GND")),
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
expect(recoveredGroundTrace).toBeDefined()
|
|
147
|
+
expect(gndLabels).toHaveLength(1)
|
|
148
|
+
expect(solver).toMatchSolverSnapshot(import.meta.path)
|
|
149
|
+
})
|