@tscircuit/schematic-trace-solver 0.0.108 → 0.0.109
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +29 -1
- package/dist/index.js +502 -38
- package/lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver.ts +3 -1
- package/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts +18 -3
- package/lib/solvers/UnroutedTraceRecoverySolver/UnroutedTraceRecoverySolver.ts +592 -0
- package/package.json +1 -1
- package/site/bug-reports/bug-report-20260724T175257Z.page.tsx +4 -0
- package/tests/bug-reports/bug-report-20260707T092615Z/__snapshots__/bug-report-20260707T092615Z.snap.svg +5 -9
- package/tests/bug-reports/bug-report-20260724T175257Z/__snapshots__/bug-report-20260724T175257Z.snap.svg +58 -0
- package/tests/bug-reports/bug-report-20260724T175257Z/bug-report-20260724T175257Z.json +138 -0
- package/tests/bug-reports/bug-report-20260724T175257Z/bug-report-20260724T175257Z.test.ts +12 -0
- package/tests/examples/__snapshots__/example04.snap.svg +7 -9
- package/tests/examples/__snapshots__/example08.snap.svg +16 -20
- package/tests/examples/__snapshots__/example13.snap.svg +2 -4
- package/tests/examples/__snapshots__/example32.snap.svg +64 -66
- package/tests/examples/__snapshots__/example46.snap.svg +1 -1
- package/tests/repros/__snapshots__/repro-netlabel-collision-687.snap.svg +81 -0
- package/tests/repros/__snapshots__/repro-netlabel-overlap-trace.snap.svg +16 -18
- package/tests/repros/assets/repro-netlabel-collision-687.input.json +212 -0
- package/tests/repros/repro-netlabel-collision-687.test.ts +59 -0
- package/tests/solvers/UnroutedTraceRecoverySolver/paired-junction-recovery.test.ts +49 -0
- package/tests/solvers/UnroutedTraceRecoverySolver/skip-ground.test.ts +30 -0
- package/tests/solvers/UnroutedTraceRecoverySolver/skip-long-connection.test.ts +24 -0
package/dist/index.js
CHANGED
|
@@ -421,6 +421,7 @@ function getOrthogonalMinimumSpanningTree(pins, opts = {}) {
|
|
|
421
421
|
}
|
|
422
422
|
|
|
423
423
|
// lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver.ts
|
|
424
|
+
var DEFAULT_MAX_MSP_PAIR_DISTANCE = 1;
|
|
424
425
|
var getPinPairKey = (pinIds) => [...pinIds].sort().join("::");
|
|
425
426
|
var MspConnectionPairSolver = class extends BaseSolver {
|
|
426
427
|
inputProblem;
|
|
@@ -436,7 +437,7 @@ var MspConnectionPairSolver = class extends BaseSolver {
|
|
|
436
437
|
constructor({ inputProblem }) {
|
|
437
438
|
super();
|
|
438
439
|
this.inputProblem = inputProblem;
|
|
439
|
-
this.maxMspPairDistance = inputProblem.maxMspPairDistance ??
|
|
440
|
+
this.maxMspPairDistance = inputProblem.maxMspPairDistance ?? DEFAULT_MAX_MSP_PAIR_DISTANCE;
|
|
440
441
|
const { directConnMap, netConnMap } = getConnectivityMapsFromInputProblem(inputProblem);
|
|
441
442
|
this.dcConnMap = directConnMap;
|
|
442
443
|
this.globalConnMap = netConnMap;
|
|
@@ -3893,7 +3894,7 @@ var SingleOverlapSolver = class extends BaseSolver {
|
|
|
3893
3894
|
paddingBuffer: effectivePadding
|
|
3894
3895
|
// Use the calculated, larger padding
|
|
3895
3896
|
});
|
|
3896
|
-
const
|
|
3897
|
+
const getPathLength3 = (pts) => {
|
|
3897
3898
|
let len = 0;
|
|
3898
3899
|
for (let i = 0; i < pts.length - 1; i++) {
|
|
3899
3900
|
const dx = pts[i + 1].x - pts[i].x;
|
|
@@ -3903,7 +3904,7 @@ var SingleOverlapSolver = class extends BaseSolver {
|
|
|
3903
3904
|
return len;
|
|
3904
3905
|
};
|
|
3905
3906
|
this.queuedCandidatePaths = candidates.sort(
|
|
3906
|
-
(a, b) =>
|
|
3907
|
+
(a, b) => getPathLength3(a) - getPathLength3(b)
|
|
3907
3908
|
);
|
|
3908
3909
|
this.obstacles = getObstacleRects(this.problem);
|
|
3909
3910
|
}
|
|
@@ -5343,10 +5344,10 @@ var projectPointToPath = (point, path) => {
|
|
|
5343
5344
|
let bestDistance = Number.POSITIVE_INFINITY;
|
|
5344
5345
|
for (let i = 0; i < path.length - 1; i++) {
|
|
5345
5346
|
const projectedPoint = projectPointToSegment(point, path[i], path[i + 1]);
|
|
5346
|
-
const
|
|
5347
|
-
if (
|
|
5347
|
+
const distance5 = getDistance2(point, projectedPoint);
|
|
5348
|
+
if (distance5 < bestDistance) {
|
|
5348
5349
|
bestPoint = projectedPoint;
|
|
5349
|
-
bestDistance =
|
|
5350
|
+
bestDistance = distance5;
|
|
5350
5351
|
}
|
|
5351
5352
|
}
|
|
5352
5353
|
return bestPoint;
|
|
@@ -7160,11 +7161,11 @@ var getLabelHugDistance = (tracePath, obstacleLabel) => {
|
|
|
7160
7161
|
obstacleLabel.width,
|
|
7161
7162
|
obstacleLabel.height
|
|
7162
7163
|
);
|
|
7163
|
-
let
|
|
7164
|
+
let distance5 = 0;
|
|
7164
7165
|
for (const point of tracePath) {
|
|
7165
|
-
|
|
7166
|
+
distance5 += getPointDistanceFromRect(point, bounds);
|
|
7166
7167
|
}
|
|
7167
|
-
return
|
|
7168
|
+
return distance5;
|
|
7168
7169
|
};
|
|
7169
7170
|
var getPointDistanceFromRect = (point, rect) => {
|
|
7170
7171
|
const dx = Math.max(rect.minX - point.x, 0, point.x - rect.maxX);
|
|
@@ -7441,16 +7442,16 @@ var Example28Solver = class extends BaseSolver {
|
|
|
7441
7442
|
const outward = dir(label.orientation);
|
|
7442
7443
|
if (outward.x === 0 && outward.y === 0) return null;
|
|
7443
7444
|
for (let step = 1; step <= LABEL_MAX_OUTWARD_STEPS; step++) {
|
|
7444
|
-
const
|
|
7445
|
+
const distance5 = step * LABEL_OUTWARD_STEP;
|
|
7445
7446
|
const candidate = {
|
|
7446
7447
|
...label,
|
|
7447
7448
|
anchorPoint: {
|
|
7448
|
-
x: label.anchorPoint.x + outward.x *
|
|
7449
|
-
y: label.anchorPoint.y + outward.y *
|
|
7449
|
+
x: label.anchorPoint.x + outward.x * distance5,
|
|
7450
|
+
y: label.anchorPoint.y + outward.y * distance5
|
|
7450
7451
|
},
|
|
7451
7452
|
center: {
|
|
7452
|
-
x: label.center.x + outward.x *
|
|
7453
|
-
y: label.center.y + outward.y *
|
|
7453
|
+
x: label.center.x + outward.x * distance5,
|
|
7454
|
+
y: label.center.y + outward.y * distance5
|
|
7454
7455
|
}
|
|
7455
7456
|
};
|
|
7456
7457
|
const candidateWithClearance = {
|
|
@@ -8024,10 +8025,10 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
8024
8025
|
phase = "shift",
|
|
8025
8026
|
stopOnTraceCollision = true
|
|
8026
8027
|
} = params;
|
|
8027
|
-
for (let
|
|
8028
|
+
for (let distance5 = LABEL_SEARCH_STEP; distance5 <= maxSearchDistance + EPS11; distance5 += LABEL_SEARCH_STEP) {
|
|
8028
8029
|
const anchorPoint = {
|
|
8029
|
-
x: baseAnchor.x + direction.x *
|
|
8030
|
-
y: baseAnchor.y + direction.y *
|
|
8030
|
+
x: baseAnchor.x + direction.x * distance5,
|
|
8031
|
+
y: baseAnchor.y + direction.y * distance5
|
|
8031
8032
|
};
|
|
8032
8033
|
const candidate = this.createCandidate(label, anchorPoint, orientation);
|
|
8033
8034
|
const result = this.evaluateCandidate(
|
|
@@ -8035,7 +8036,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
8035
8036
|
label,
|
|
8036
8037
|
labelIndex,
|
|
8037
8038
|
phase,
|
|
8038
|
-
|
|
8039
|
+
distance5,
|
|
8039
8040
|
outwardDistance
|
|
8040
8041
|
);
|
|
8041
8042
|
this.currentCandidateResults.push(result);
|
|
@@ -8047,11 +8048,11 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
8047
8048
|
}
|
|
8048
8049
|
return null;
|
|
8049
8050
|
}
|
|
8050
|
-
evaluateCandidate(candidate, label, labelIndex, phase,
|
|
8051
|
+
evaluateCandidate(candidate, label, labelIndex, phase, distance5, outwardDistance) {
|
|
8051
8052
|
return {
|
|
8052
8053
|
...candidate,
|
|
8053
8054
|
phase,
|
|
8054
|
-
distance:
|
|
8055
|
+
distance: distance5,
|
|
8055
8056
|
outwardDistance,
|
|
8056
8057
|
selected: false,
|
|
8057
8058
|
status: this.getCandidateStatus({
|
|
@@ -8389,10 +8390,10 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
8389
8390
|
if (point.x < bounds.minX - EPS11 || point.x > bounds.maxX + EPS11 || point.y < bounds.minY - EPS11 || point.y > bounds.maxY + EPS11) {
|
|
8390
8391
|
continue;
|
|
8391
8392
|
}
|
|
8392
|
-
for (const [side,
|
|
8393
|
-
if (
|
|
8393
|
+
for (const [side, distance5] of getSideDistances(point, bounds)) {
|
|
8394
|
+
if (distance5 < nearestDistance) {
|
|
8394
8395
|
nearestSide = side;
|
|
8395
|
-
nearestDistance =
|
|
8396
|
+
nearestDistance = distance5;
|
|
8396
8397
|
}
|
|
8397
8398
|
}
|
|
8398
8399
|
}
|
|
@@ -8421,10 +8422,10 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
8421
8422
|
let nearestSide = null;
|
|
8422
8423
|
let nearestDistance = Number.POSITIVE_INFINITY;
|
|
8423
8424
|
for (const chip of this.chipObstacleSpatialIndex.chips) {
|
|
8424
|
-
for (const [side,
|
|
8425
|
-
if (
|
|
8425
|
+
for (const [side, distance5] of getSideDistances(point, chip.bounds)) {
|
|
8426
|
+
if (distance5 < nearestDistance) {
|
|
8426
8427
|
nearestSide = side;
|
|
8427
|
-
nearestDistance =
|
|
8428
|
+
nearestDistance = distance5;
|
|
8428
8429
|
}
|
|
8429
8430
|
}
|
|
8430
8431
|
}
|
|
@@ -8825,17 +8826,17 @@ var getTraceLength = (trace) => {
|
|
|
8825
8826
|
}
|
|
8826
8827
|
return length;
|
|
8827
8828
|
};
|
|
8828
|
-
var getPointAtTraceDistance = (trace,
|
|
8829
|
+
var getPointAtTraceDistance = (trace, distance5) => {
|
|
8829
8830
|
let pathDistance = 0;
|
|
8830
8831
|
for (let i = 0; i < trace.tracePath.length - 1; i++) {
|
|
8831
8832
|
const start = trace.tracePath[i];
|
|
8832
8833
|
const end = trace.tracePath[i + 1];
|
|
8833
8834
|
const segmentLength = getManhattanDistance(start, end);
|
|
8834
8835
|
const nextDistance = pathDistance + segmentLength;
|
|
8835
|
-
if (
|
|
8836
|
+
if (distance5 <= nextDistance + EPS12) {
|
|
8836
8837
|
const offset = Math.max(
|
|
8837
8838
|
0,
|
|
8838
|
-
Math.min(segmentLength,
|
|
8839
|
+
Math.min(segmentLength, distance5 - pathDistance)
|
|
8839
8840
|
);
|
|
8840
8841
|
const direction = getSegmentDirection(start, end);
|
|
8841
8842
|
return {
|
|
@@ -8960,13 +8961,13 @@ var getCandidateDistances = (traceLength, vertexDistances) => {
|
|
|
8960
8961
|
const distances = /* @__PURE__ */ new Set();
|
|
8961
8962
|
const maxSteps = Math.ceil(traceLength / CANDIDATE_STEP);
|
|
8962
8963
|
for (let i = 0; i <= maxSteps; i++) {
|
|
8963
|
-
const
|
|
8964
|
-
distances.add(roundDistance(
|
|
8964
|
+
const distance5 = Math.min(traceLength, i * CANDIDATE_STEP);
|
|
8965
|
+
distances.add(roundDistance(distance5));
|
|
8965
8966
|
}
|
|
8966
|
-
for (const
|
|
8967
|
-
distances.add(roundDistance(
|
|
8967
|
+
for (const distance5 of vertexDistances) {
|
|
8968
|
+
distances.add(roundDistance(distance5));
|
|
8968
8969
|
}
|
|
8969
|
-
return [...distances].filter((
|
|
8970
|
+
return [...distances].filter((distance5) => distance5 >= -EPS12 && distance5 <= traceLength + EPS12).sort((a, b) => a - b);
|
|
8970
8971
|
};
|
|
8971
8972
|
var getOrientationsForPoint = (params) => {
|
|
8972
8973
|
const { inputProblem, label, point, orientationConstraint } = params;
|
|
@@ -9144,7 +9145,7 @@ var getNetLabelHeight = (inputProblem, label) => {
|
|
|
9144
9145
|
(nc) => nc.pinIds.some((pid) => label.pinIds.includes(pid))
|
|
9145
9146
|
)?.netLabelHeight;
|
|
9146
9147
|
};
|
|
9147
|
-
var roundDistance = (
|
|
9148
|
+
var roundDistance = (distance5) => Number(distance5.toFixed(6));
|
|
9148
9149
|
var isSamePlacement = (label, point, orientation) => Math.abs(point.x - label.anchorPoint.x) <= EPS12 && Math.abs(point.y - label.anchorPoint.y) <= EPS12 && orientation === label.orientation;
|
|
9149
9150
|
|
|
9150
9151
|
// lib/solvers/TraceAnchoredNetLabelOverlapSolver/visualize.ts
|
|
@@ -10110,6 +10111,457 @@ ${c.status}`
|
|
|
10110
10111
|
}
|
|
10111
10112
|
};
|
|
10112
10113
|
|
|
10114
|
+
// lib/solvers/UnroutedTraceRecoverySolver/UnroutedTraceRecoverySolver.ts
|
|
10115
|
+
import { distance as distance4, doSegmentsIntersect as doSegmentsIntersect3 } from "@tscircuit/math-utils";
|
|
10116
|
+
var ROUTE_CLEARANCE = 0.2;
|
|
10117
|
+
var COORDINATE_TOLERANCE = 1e-9;
|
|
10118
|
+
var GROUND_NET_ID = "GND";
|
|
10119
|
+
var pointsAreEqual = (firstPoint, secondPoint) => {
|
|
10120
|
+
return Math.abs(firstPoint.x - secondPoint.x) <= COORDINATE_TOLERANCE && Math.abs(firstPoint.y - secondPoint.y) <= COORDINATE_TOLERANCE;
|
|
10121
|
+
};
|
|
10122
|
+
var removeConsecutiveDuplicatePoints = (path) => {
|
|
10123
|
+
const filteredPath = [];
|
|
10124
|
+
for (const point of path) {
|
|
10125
|
+
const previousPoint = filteredPath.at(-1);
|
|
10126
|
+
if (!previousPoint || !pointsAreEqual(previousPoint, point)) {
|
|
10127
|
+
filteredPath.push(point);
|
|
10128
|
+
}
|
|
10129
|
+
}
|
|
10130
|
+
return filteredPath;
|
|
10131
|
+
};
|
|
10132
|
+
var getPathLength2 = (path) => {
|
|
10133
|
+
let pathLength = 0;
|
|
10134
|
+
for (let pointIndex = 0; pointIndex < path.length - 1; pointIndex++) {
|
|
10135
|
+
const startPoint = path[pointIndex];
|
|
10136
|
+
const endPoint = path[pointIndex + 1];
|
|
10137
|
+
pathLength += Math.abs(endPoint.x - startPoint.x) + Math.abs(endPoint.y - startPoint.y);
|
|
10138
|
+
}
|
|
10139
|
+
return pathLength;
|
|
10140
|
+
};
|
|
10141
|
+
var getEscapePoint = ({
|
|
10142
|
+
pin,
|
|
10143
|
+
facingDirection
|
|
10144
|
+
}) => {
|
|
10145
|
+
const escapePoint = { x: pin.x, y: pin.y };
|
|
10146
|
+
if (facingDirection === "x+") {
|
|
10147
|
+
escapePoint.x += ROUTE_CLEARANCE;
|
|
10148
|
+
}
|
|
10149
|
+
if (facingDirection === "x-") {
|
|
10150
|
+
escapePoint.x -= ROUTE_CLEARANCE;
|
|
10151
|
+
}
|
|
10152
|
+
if (facingDirection === "y+") {
|
|
10153
|
+
escapePoint.y += ROUTE_CLEARANCE;
|
|
10154
|
+
}
|
|
10155
|
+
if (facingDirection === "y-") {
|
|
10156
|
+
escapePoint.y -= ROUTE_CLEARANCE;
|
|
10157
|
+
}
|
|
10158
|
+
return escapePoint;
|
|
10159
|
+
};
|
|
10160
|
+
var getOuterBounds = (obstacles) => {
|
|
10161
|
+
return {
|
|
10162
|
+
minX: Math.min(...obstacles.map((obstacle) => obstacle.minX)),
|
|
10163
|
+
minY: Math.min(...obstacles.map((obstacle) => obstacle.minY)),
|
|
10164
|
+
maxX: Math.max(...obstacles.map((obstacle) => obstacle.maxX)),
|
|
10165
|
+
maxY: Math.max(...obstacles.map((obstacle) => obstacle.maxY))
|
|
10166
|
+
};
|
|
10167
|
+
};
|
|
10168
|
+
var getPerimeterCandidates = ({
|
|
10169
|
+
connectionPair,
|
|
10170
|
+
obstacles
|
|
10171
|
+
}) => {
|
|
10172
|
+
const [firstPin, secondPin] = connectionPair.pins;
|
|
10173
|
+
const firstEscapePoint = getEscapePoint({
|
|
10174
|
+
pin: firstPin,
|
|
10175
|
+
facingDirection: firstPin._facingDirection
|
|
10176
|
+
});
|
|
10177
|
+
const secondEscapePoint = getEscapePoint({
|
|
10178
|
+
pin: secondPin,
|
|
10179
|
+
facingDirection: secondPin._facingDirection
|
|
10180
|
+
});
|
|
10181
|
+
const outerBounds = getOuterBounds(obstacles);
|
|
10182
|
+
const horizontalChannels = [
|
|
10183
|
+
outerBounds.minY - ROUTE_CLEARANCE,
|
|
10184
|
+
outerBounds.maxY + ROUTE_CLEARANCE
|
|
10185
|
+
];
|
|
10186
|
+
const verticalChannels = [
|
|
10187
|
+
outerBounds.minX - ROUTE_CLEARANCE,
|
|
10188
|
+
outerBounds.maxX + ROUTE_CLEARANCE
|
|
10189
|
+
];
|
|
10190
|
+
const candidates = [];
|
|
10191
|
+
for (const channelY of horizontalChannels) {
|
|
10192
|
+
candidates.push(
|
|
10193
|
+
removeConsecutiveDuplicatePoints([
|
|
10194
|
+
firstPin,
|
|
10195
|
+
firstEscapePoint,
|
|
10196
|
+
{ x: firstEscapePoint.x, y: channelY },
|
|
10197
|
+
{ x: secondEscapePoint.x, y: channelY },
|
|
10198
|
+
secondEscapePoint,
|
|
10199
|
+
secondPin
|
|
10200
|
+
])
|
|
10201
|
+
);
|
|
10202
|
+
}
|
|
10203
|
+
for (const channelX of verticalChannels) {
|
|
10204
|
+
candidates.push(
|
|
10205
|
+
removeConsecutiveDuplicatePoints([
|
|
10206
|
+
firstPin,
|
|
10207
|
+
firstEscapePoint,
|
|
10208
|
+
{ x: channelX, y: firstEscapePoint.y },
|
|
10209
|
+
{ x: channelX, y: secondEscapePoint.y },
|
|
10210
|
+
secondEscapePoint,
|
|
10211
|
+
secondPin
|
|
10212
|
+
])
|
|
10213
|
+
);
|
|
10214
|
+
}
|
|
10215
|
+
return candidates.sort(
|
|
10216
|
+
(firstPath, secondPath) => getPathLength2(firstPath) - getPathLength2(secondPath)
|
|
10217
|
+
);
|
|
10218
|
+
};
|
|
10219
|
+
var getSegmentMidpoint = (startPoint, endPoint) => {
|
|
10220
|
+
return {
|
|
10221
|
+
x: (startPoint.x + endPoint.x) / 2,
|
|
10222
|
+
y: (startPoint.y + endPoint.y) / 2
|
|
10223
|
+
};
|
|
10224
|
+
};
|
|
10225
|
+
var getJunctionPoints = (sameNetTraces) => {
|
|
10226
|
+
const junctionPoints = [];
|
|
10227
|
+
for (const trace of sameNetTraces) {
|
|
10228
|
+
for (let pointIndex = 0; pointIndex < trace.tracePath.length - 1; pointIndex++) {
|
|
10229
|
+
const startPoint = trace.tracePath[pointIndex];
|
|
10230
|
+
const endPoint = trace.tracePath[pointIndex + 1];
|
|
10231
|
+
junctionPoints.push(startPoint);
|
|
10232
|
+
junctionPoints.push(getSegmentMidpoint(startPoint, endPoint));
|
|
10233
|
+
}
|
|
10234
|
+
const lastPoint = trace.tracePath.at(-1);
|
|
10235
|
+
if (lastPoint) {
|
|
10236
|
+
junctionPoints.push(lastPoint);
|
|
10237
|
+
}
|
|
10238
|
+
}
|
|
10239
|
+
return junctionPoints;
|
|
10240
|
+
};
|
|
10241
|
+
var getUnconnectedPins = ({
|
|
10242
|
+
connectionPair,
|
|
10243
|
+
sameNetTraces
|
|
10244
|
+
}) => {
|
|
10245
|
+
const connectedPinIds = new Set(
|
|
10246
|
+
sameNetTraces.flatMap((trace) => trace.pinIds)
|
|
10247
|
+
);
|
|
10248
|
+
return connectionPair.pins.filter((pin) => !connectedPinIds.has(pin.pinId));
|
|
10249
|
+
};
|
|
10250
|
+
var getJunctionCandidates = ({
|
|
10251
|
+
connectionPair,
|
|
10252
|
+
sameNetTraces,
|
|
10253
|
+
obstacles,
|
|
10254
|
+
maxConnectionDistance
|
|
10255
|
+
}) => {
|
|
10256
|
+
const outerBounds = getOuterBounds(obstacles);
|
|
10257
|
+
const horizontalChannels = [
|
|
10258
|
+
outerBounds.minY - ROUTE_CLEARANCE,
|
|
10259
|
+
outerBounds.maxY + ROUTE_CLEARANCE
|
|
10260
|
+
];
|
|
10261
|
+
const verticalChannels = [
|
|
10262
|
+
outerBounds.minX - ROUTE_CLEARANCE,
|
|
10263
|
+
outerBounds.maxX + ROUTE_CLEARANCE
|
|
10264
|
+
];
|
|
10265
|
+
const candidates = [];
|
|
10266
|
+
const junctionPoints = getJunctionPoints(sameNetTraces);
|
|
10267
|
+
const unconnectedPins = getUnconnectedPins({
|
|
10268
|
+
connectionPair,
|
|
10269
|
+
sameNetTraces
|
|
10270
|
+
});
|
|
10271
|
+
for (const pin of unconnectedPins) {
|
|
10272
|
+
const escapePoint = getEscapePoint({
|
|
10273
|
+
pin,
|
|
10274
|
+
facingDirection: pin._facingDirection
|
|
10275
|
+
});
|
|
10276
|
+
for (const junctionPoint of junctionPoints) {
|
|
10277
|
+
if (distance4(pin, junctionPoint) > maxConnectionDistance) {
|
|
10278
|
+
continue;
|
|
10279
|
+
}
|
|
10280
|
+
candidates.push(
|
|
10281
|
+
removeConsecutiveDuplicatePoints([
|
|
10282
|
+
pin,
|
|
10283
|
+
escapePoint,
|
|
10284
|
+
{ x: escapePoint.x, y: junctionPoint.y },
|
|
10285
|
+
junctionPoint
|
|
10286
|
+
])
|
|
10287
|
+
);
|
|
10288
|
+
candidates.push(
|
|
10289
|
+
removeConsecutiveDuplicatePoints([
|
|
10290
|
+
pin,
|
|
10291
|
+
escapePoint,
|
|
10292
|
+
{ x: junctionPoint.x, y: escapePoint.y },
|
|
10293
|
+
junctionPoint
|
|
10294
|
+
])
|
|
10295
|
+
);
|
|
10296
|
+
for (const channelY of horizontalChannels) {
|
|
10297
|
+
candidates.push(
|
|
10298
|
+
removeConsecutiveDuplicatePoints([
|
|
10299
|
+
pin,
|
|
10300
|
+
escapePoint,
|
|
10301
|
+
{ x: escapePoint.x, y: channelY },
|
|
10302
|
+
{ x: junctionPoint.x, y: channelY },
|
|
10303
|
+
junctionPoint
|
|
10304
|
+
])
|
|
10305
|
+
);
|
|
10306
|
+
}
|
|
10307
|
+
for (const channelX of verticalChannels) {
|
|
10308
|
+
candidates.push(
|
|
10309
|
+
removeConsecutiveDuplicatePoints([
|
|
10310
|
+
pin,
|
|
10311
|
+
escapePoint,
|
|
10312
|
+
{ x: channelX, y: escapePoint.y },
|
|
10313
|
+
{ x: channelX, y: junctionPoint.y },
|
|
10314
|
+
junctionPoint
|
|
10315
|
+
])
|
|
10316
|
+
);
|
|
10317
|
+
}
|
|
10318
|
+
}
|
|
10319
|
+
}
|
|
10320
|
+
return candidates.sort(
|
|
10321
|
+
(firstPath, secondPath) => getPathLength2(firstPath) - getPathLength2(secondPath)
|
|
10322
|
+
);
|
|
10323
|
+
};
|
|
10324
|
+
var pathCollidesWithObstacles = ({
|
|
10325
|
+
path,
|
|
10326
|
+
obstacles,
|
|
10327
|
+
connectionPair,
|
|
10328
|
+
rejectComponentBoundaryTravel
|
|
10329
|
+
}) => {
|
|
10330
|
+
const firstPathPoint = path[0];
|
|
10331
|
+
const lastPathPoint = path.at(-1);
|
|
10332
|
+
const firstPathPin = connectionPair.pins.find(
|
|
10333
|
+
(pin) => pointsAreEqual(pin, firstPathPoint)
|
|
10334
|
+
);
|
|
10335
|
+
const lastPathPin = connectionPair.pins.find(
|
|
10336
|
+
(pin) => pointsAreEqual(pin, lastPathPoint)
|
|
10337
|
+
);
|
|
10338
|
+
const pathConnectsPairPins = firstPathPin !== void 0 && lastPathPin !== void 0;
|
|
10339
|
+
const firstChipObstacle = obstacles.find(
|
|
10340
|
+
(obstacle) => obstacle.kind === "chip" && obstacle.chipId === firstPathPin?.chipId
|
|
10341
|
+
);
|
|
10342
|
+
const secondChipObstacle = obstacles.find(
|
|
10343
|
+
(obstacle) => obstacle.kind === "chip" && obstacle.chipId === lastPathPin?.chipId
|
|
10344
|
+
);
|
|
10345
|
+
if (rejectComponentBoundaryTravel && firstChipObstacle && segmentOverlapsRectBoundary(path[0], path[1], firstChipObstacle)) {
|
|
10346
|
+
return true;
|
|
10347
|
+
}
|
|
10348
|
+
if (rejectComponentBoundaryTravel && secondChipObstacle && segmentOverlapsRectBoundary(
|
|
10349
|
+
path[path.length - 2],
|
|
10350
|
+
path[path.length - 1],
|
|
10351
|
+
secondChipObstacle
|
|
10352
|
+
)) {
|
|
10353
|
+
return true;
|
|
10354
|
+
}
|
|
10355
|
+
const collision = findFirstCollision(path, obstacles, {
|
|
10356
|
+
excludeRectsForSegment: (segmentIndex) => {
|
|
10357
|
+
const excludedObstacles = /* @__PURE__ */ new Set();
|
|
10358
|
+
if (segmentIndex === 0 && firstChipObstacle) {
|
|
10359
|
+
excludedObstacles.add(firstChipObstacle);
|
|
10360
|
+
}
|
|
10361
|
+
if (pathConnectsPairPins && segmentIndex === path.length - 2 && secondChipObstacle) {
|
|
10362
|
+
excludedObstacles.add(secondChipObstacle);
|
|
10363
|
+
}
|
|
10364
|
+
return excludedObstacles;
|
|
10365
|
+
}
|
|
10366
|
+
});
|
|
10367
|
+
return collision !== null;
|
|
10368
|
+
};
|
|
10369
|
+
var hasParallelFailedConnection = ({
|
|
10370
|
+
connectionPair,
|
|
10371
|
+
failedConnectionPairs
|
|
10372
|
+
}) => {
|
|
10373
|
+
const connectionChipIds = new Set(
|
|
10374
|
+
connectionPair.pins.map((pin) => pin.chipId)
|
|
10375
|
+
);
|
|
10376
|
+
return failedConnectionPairs.some((otherConnectionPair) => {
|
|
10377
|
+
if (otherConnectionPair.mspPairId === connectionPair.mspPairId) {
|
|
10378
|
+
return false;
|
|
10379
|
+
}
|
|
10380
|
+
return otherConnectionPair.pins.every(
|
|
10381
|
+
(pin) => connectionChipIds.has(pin.chipId)
|
|
10382
|
+
);
|
|
10383
|
+
});
|
|
10384
|
+
};
|
|
10385
|
+
var segmentsOverlapBeyondEndpoint = ({
|
|
10386
|
+
firstStart,
|
|
10387
|
+
firstEnd,
|
|
10388
|
+
secondStart,
|
|
10389
|
+
secondEnd
|
|
10390
|
+
}) => {
|
|
10391
|
+
if (isHorizontal(firstStart, firstEnd) && isHorizontal(secondStart, secondEnd)) {
|
|
10392
|
+
const overlapLength = Math.min(
|
|
10393
|
+
Math.max(firstStart.x, firstEnd.x),
|
|
10394
|
+
Math.max(secondStart.x, secondEnd.x)
|
|
10395
|
+
) - Math.max(
|
|
10396
|
+
Math.min(firstStart.x, firstEnd.x),
|
|
10397
|
+
Math.min(secondStart.x, secondEnd.x)
|
|
10398
|
+
);
|
|
10399
|
+
return overlapLength > COORDINATE_TOLERANCE;
|
|
10400
|
+
}
|
|
10401
|
+
if (isVertical(firstStart, firstEnd) && isVertical(secondStart, secondEnd)) {
|
|
10402
|
+
const overlapLength = Math.min(
|
|
10403
|
+
Math.max(firstStart.y, firstEnd.y),
|
|
10404
|
+
Math.max(secondStart.y, secondEnd.y)
|
|
10405
|
+
) - Math.max(
|
|
10406
|
+
Math.min(firstStart.y, firstEnd.y),
|
|
10407
|
+
Math.min(secondStart.y, secondEnd.y)
|
|
10408
|
+
);
|
|
10409
|
+
return overlapLength > COORDINATE_TOLERANCE;
|
|
10410
|
+
}
|
|
10411
|
+
return false;
|
|
10412
|
+
};
|
|
10413
|
+
var getAllowedJunctionPoints = ({
|
|
10414
|
+
connectionPair,
|
|
10415
|
+
existingTrace
|
|
10416
|
+
}) => {
|
|
10417
|
+
const existingPinIds = new Set(existingTrace.pinIds);
|
|
10418
|
+
return connectionPair.pins.filter((pin) => existingPinIds.has(pin.pinId));
|
|
10419
|
+
};
|
|
10420
|
+
var pathCrossesExistingTraces = ({
|
|
10421
|
+
path,
|
|
10422
|
+
connectionPair,
|
|
10423
|
+
existingTraces
|
|
10424
|
+
}) => {
|
|
10425
|
+
for (const existingTrace of existingTraces) {
|
|
10426
|
+
if (existingTrace.globalConnNetId === connectionPair.globalConnNetId) {
|
|
10427
|
+
continue;
|
|
10428
|
+
}
|
|
10429
|
+
const allowedJunctionPoints = getAllowedJunctionPoints({
|
|
10430
|
+
connectionPair,
|
|
10431
|
+
existingTrace
|
|
10432
|
+
});
|
|
10433
|
+
for (let pathIndex = 0; pathIndex < path.length - 1; pathIndex++) {
|
|
10434
|
+
const pathStart = path[pathIndex];
|
|
10435
|
+
const pathEnd = path[pathIndex + 1];
|
|
10436
|
+
for (let traceIndex = 0; traceIndex < existingTrace.tracePath.length - 1; traceIndex++) {
|
|
10437
|
+
const traceStart = existingTrace.tracePath[traceIndex];
|
|
10438
|
+
const traceEnd = existingTrace.tracePath[traceIndex + 1];
|
|
10439
|
+
if (!doSegmentsIntersect3(pathStart, pathEnd, traceStart, traceEnd)) {
|
|
10440
|
+
continue;
|
|
10441
|
+
}
|
|
10442
|
+
const intersectionIsAllowedJunction = allowedJunctionPoints.some(
|
|
10443
|
+
(junctionPoint) => (pointsAreEqual(pathStart, junctionPoint) || pointsAreEqual(pathEnd, junctionPoint)) && (pointsAreEqual(traceStart, junctionPoint) || pointsAreEqual(traceEnd, junctionPoint))
|
|
10444
|
+
);
|
|
10445
|
+
if (intersectionIsAllowedJunction && !segmentsOverlapBeyondEndpoint({
|
|
10446
|
+
firstStart: pathStart,
|
|
10447
|
+
firstEnd: pathEnd,
|
|
10448
|
+
secondStart: traceStart,
|
|
10449
|
+
secondEnd: traceEnd
|
|
10450
|
+
})) {
|
|
10451
|
+
continue;
|
|
10452
|
+
}
|
|
10453
|
+
return true;
|
|
10454
|
+
}
|
|
10455
|
+
}
|
|
10456
|
+
}
|
|
10457
|
+
return false;
|
|
10458
|
+
};
|
|
10459
|
+
var UnroutedTraceRecoverySolver = class extends BaseSolver {
|
|
10460
|
+
constructor(params) {
|
|
10461
|
+
super();
|
|
10462
|
+
this.params = params;
|
|
10463
|
+
this.inputProblem = params.inputProblem;
|
|
10464
|
+
this.alreadySolvedTraces = params.alreadySolvedTraces;
|
|
10465
|
+
this.failedConnectionPairs = params.failedConnectionPairs;
|
|
10466
|
+
this.queuedConnectionPairs = [...params.failedConnectionPairs];
|
|
10467
|
+
this.maxConnectionDistance = this.inputProblem.maxMspPairDistance ?? DEFAULT_MAX_MSP_PAIR_DISTANCE;
|
|
10468
|
+
const { netConnMap } = getConnectivityMapsFromInputProblem(
|
|
10469
|
+
this.inputProblem
|
|
10470
|
+
);
|
|
10471
|
+
this.groundGlobalConnNetId = netConnMap.getNetConnectedToId(GROUND_NET_ID) ?? void 0;
|
|
10472
|
+
}
|
|
10473
|
+
params;
|
|
10474
|
+
inputProblem;
|
|
10475
|
+
alreadySolvedTraces;
|
|
10476
|
+
failedConnectionPairs;
|
|
10477
|
+
queuedConnectionPairs;
|
|
10478
|
+
maxConnectionDistance;
|
|
10479
|
+
groundGlobalConnNetId;
|
|
10480
|
+
solvedUnroutedTraces = [];
|
|
10481
|
+
getConstructorParams() {
|
|
10482
|
+
return this.params;
|
|
10483
|
+
}
|
|
10484
|
+
_step() {
|
|
10485
|
+
const connectionPair = this.queuedConnectionPairs.shift();
|
|
10486
|
+
if (!connectionPair) {
|
|
10487
|
+
this.solved = true;
|
|
10488
|
+
return;
|
|
10489
|
+
}
|
|
10490
|
+
if (connectionPair.globalConnNetId === this.groundGlobalConnNetId) {
|
|
10491
|
+
return;
|
|
10492
|
+
}
|
|
10493
|
+
if (distance4(connectionPair.pins[0], connectionPair.pins[1]) > this.maxConnectionDistance) {
|
|
10494
|
+
return;
|
|
10495
|
+
}
|
|
10496
|
+
const obstacles = getObstacleRects(this.inputProblem);
|
|
10497
|
+
const existingTraces = [
|
|
10498
|
+
...this.alreadySolvedTraces,
|
|
10499
|
+
...this.solvedUnroutedTraces
|
|
10500
|
+
];
|
|
10501
|
+
const sameNetTraces = existingTraces.filter(
|
|
10502
|
+
(trace) => trace.globalConnNetId === connectionPair.globalConnNetId
|
|
10503
|
+
);
|
|
10504
|
+
const junctionCandidates = getJunctionCandidates({
|
|
10505
|
+
connectionPair,
|
|
10506
|
+
sameNetTraces,
|
|
10507
|
+
obstacles,
|
|
10508
|
+
maxConnectionDistance: this.maxConnectionDistance
|
|
10509
|
+
});
|
|
10510
|
+
const perimeterCandidates = getPerimeterCandidates({
|
|
10511
|
+
connectionPair,
|
|
10512
|
+
obstacles
|
|
10513
|
+
});
|
|
10514
|
+
const candidates = [...junctionCandidates, ...perimeterCandidates];
|
|
10515
|
+
const rejectComponentBoundaryTravel = hasParallelFailedConnection({
|
|
10516
|
+
connectionPair,
|
|
10517
|
+
failedConnectionPairs: this.failedConnectionPairs
|
|
10518
|
+
});
|
|
10519
|
+
for (const tracePath of candidates) {
|
|
10520
|
+
if (pathCollidesWithObstacles({
|
|
10521
|
+
path: tracePath,
|
|
10522
|
+
obstacles,
|
|
10523
|
+
connectionPair,
|
|
10524
|
+
rejectComponentBoundaryTravel
|
|
10525
|
+
})) {
|
|
10526
|
+
continue;
|
|
10527
|
+
}
|
|
10528
|
+
if (pathCrossesExistingTraces({
|
|
10529
|
+
path: tracePath,
|
|
10530
|
+
connectionPair,
|
|
10531
|
+
existingTraces
|
|
10532
|
+
})) {
|
|
10533
|
+
continue;
|
|
10534
|
+
}
|
|
10535
|
+
this.solvedUnroutedTraces.push({
|
|
10536
|
+
...connectionPair,
|
|
10537
|
+
tracePath,
|
|
10538
|
+
mspConnectionPairIds: [connectionPair.mspPairId],
|
|
10539
|
+
pinIds: connectionPair.pins.map((pin) => pin.pinId)
|
|
10540
|
+
});
|
|
10541
|
+
return;
|
|
10542
|
+
}
|
|
10543
|
+
}
|
|
10544
|
+
getOutput() {
|
|
10545
|
+
return {
|
|
10546
|
+
newTraces: this.solvedUnroutedTraces,
|
|
10547
|
+
allTracesMerged: [
|
|
10548
|
+
...this.alreadySolvedTraces,
|
|
10549
|
+
...this.solvedUnroutedTraces
|
|
10550
|
+
]
|
|
10551
|
+
};
|
|
10552
|
+
}
|
|
10553
|
+
visualize() {
|
|
10554
|
+
const graphics = visualizeInputProblem(this.inputProblem);
|
|
10555
|
+
for (const trace of this.solvedUnroutedTraces) {
|
|
10556
|
+
graphics.lines.push({
|
|
10557
|
+
points: trace.tracePath,
|
|
10558
|
+
strokeColor: "blue"
|
|
10559
|
+
});
|
|
10560
|
+
}
|
|
10561
|
+
return graphics;
|
|
10562
|
+
}
|
|
10563
|
+
};
|
|
10564
|
+
|
|
10113
10565
|
// lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts
|
|
10114
10566
|
function definePipelineStep(solverName, solverClass, getConstructorParams, opts = {}) {
|
|
10115
10567
|
return {
|
|
@@ -10125,6 +10577,7 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
|
|
|
10125
10577
|
// guidelinesSolver?: GuidelinesSolver
|
|
10126
10578
|
schematicTraceLinesSolver;
|
|
10127
10579
|
longDistancePairSolver;
|
|
10580
|
+
unroutedTraceRecoverySolver;
|
|
10128
10581
|
traceOverlapShiftSolver;
|
|
10129
10582
|
netLabelPlacementSolver;
|
|
10130
10583
|
labelMergingSolver;
|
|
@@ -10195,13 +10648,24 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
|
|
|
10195
10648
|
}
|
|
10196
10649
|
}
|
|
10197
10650
|
),
|
|
10651
|
+
definePipelineStep(
|
|
10652
|
+
"unroutedTraceRecoverySolver",
|
|
10653
|
+
UnroutedTraceRecoverySolver,
|
|
10654
|
+
(instance) => [
|
|
10655
|
+
{
|
|
10656
|
+
inputProblem: instance.inputProblem,
|
|
10657
|
+
failedConnectionPairs: instance.schematicTraceLinesSolver.failedConnectionPairs,
|
|
10658
|
+
alreadySolvedTraces: instance.longDistancePairSolver.getOutput().allTracesMerged
|
|
10659
|
+
}
|
|
10660
|
+
]
|
|
10661
|
+
),
|
|
10198
10662
|
definePipelineStep(
|
|
10199
10663
|
"traceOverlapShiftSolver",
|
|
10200
10664
|
TraceOverlapShiftSolver,
|
|
10201
10665
|
() => [
|
|
10202
10666
|
{
|
|
10203
10667
|
inputProblem: this.inputProblem,
|
|
10204
|
-
inputTracePaths: this.
|
|
10668
|
+
inputTracePaths: this.unroutedTraceRecoverySolver?.getOutput().allTracesMerged,
|
|
10205
10669
|
globalConnMap: this.mspConnectionPairSolver.globalConnMap
|
|
10206
10670
|
}
|
|
10207
10671
|
],
|
|
@@ -10217,7 +10681,7 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
|
|
|
10217
10681
|
{
|
|
10218
10682
|
inputProblem: this.inputProblem,
|
|
10219
10683
|
inputTraceMap: this.traceOverlapShiftSolver?.correctedTraceMap ?? Object.fromEntries(
|
|
10220
|
-
this.
|
|
10684
|
+
this.unroutedTraceRecoverySolver.getOutput().allTracesMerged.map(
|
|
10221
10685
|
(p) => [p.mspPairId, p]
|
|
10222
10686
|
)
|
|
10223
10687
|
)
|
|
@@ -10233,7 +10697,7 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
|
|
|
10233
10697
|
TraceLabelOverlapAvoidanceSolver,
|
|
10234
10698
|
(instance) => {
|
|
10235
10699
|
const traceMap = instance.traceOverlapShiftSolver?.correctedTraceMap ?? Object.fromEntries(
|
|
10236
|
-
instance.
|
|
10700
|
+
instance.unroutedTraceRecoverySolver.getOutput().allTracesMerged.map((p) => [p.mspPairId, p])
|
|
10237
10701
|
);
|
|
10238
10702
|
const traces = Object.values(traceMap);
|
|
10239
10703
|
const netLabelPlacements = instance.netLabelPlacementSolver.netLabelPlacements;
|