@tscircuit/schematic-trace-solver 0.0.108 → 0.0.110
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 +510 -39
- package/lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver.ts +3 -1
- package/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts +18 -3
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/LabelMergingSolver/groupLabelsByChipAndOrientation.ts +12 -4
- 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;
|
|
@@ -3258,11 +3259,18 @@ var groupLabelsByChipAndOrientation = ({
|
|
|
3258
3259
|
chips
|
|
3259
3260
|
}) => {
|
|
3260
3261
|
const groupedLabels = {};
|
|
3262
|
+
const chipIdByPinId = new Map(
|
|
3263
|
+
chips.flatMap(
|
|
3264
|
+
(chip) => chip.pins.map((pin) => [pin.pinId, chip.chipId])
|
|
3265
|
+
)
|
|
3266
|
+
);
|
|
3261
3267
|
for (const label of labels) {
|
|
3262
3268
|
if (label.pinIds.length === 0) {
|
|
3263
3269
|
continue;
|
|
3264
3270
|
}
|
|
3265
|
-
const
|
|
3271
|
+
const pinId = label.pinIds[0];
|
|
3272
|
+
const legacyChipId = pinId.includes(".") ? pinId.split(".")[0] : void 0;
|
|
3273
|
+
const chipId = legacyChipId ?? chipIdByPinId.get(pinId);
|
|
3266
3274
|
if (!chipId) {
|
|
3267
3275
|
continue;
|
|
3268
3276
|
}
|
|
@@ -3893,7 +3901,7 @@ var SingleOverlapSolver = class extends BaseSolver {
|
|
|
3893
3901
|
paddingBuffer: effectivePadding
|
|
3894
3902
|
// Use the calculated, larger padding
|
|
3895
3903
|
});
|
|
3896
|
-
const
|
|
3904
|
+
const getPathLength3 = (pts) => {
|
|
3897
3905
|
let len = 0;
|
|
3898
3906
|
for (let i = 0; i < pts.length - 1; i++) {
|
|
3899
3907
|
const dx = pts[i + 1].x - pts[i].x;
|
|
@@ -3903,7 +3911,7 @@ var SingleOverlapSolver = class extends BaseSolver {
|
|
|
3903
3911
|
return len;
|
|
3904
3912
|
};
|
|
3905
3913
|
this.queuedCandidatePaths = candidates.sort(
|
|
3906
|
-
(a, b) =>
|
|
3914
|
+
(a, b) => getPathLength3(a) - getPathLength3(b)
|
|
3907
3915
|
);
|
|
3908
3916
|
this.obstacles = getObstacleRects(this.problem);
|
|
3909
3917
|
}
|
|
@@ -5343,10 +5351,10 @@ var projectPointToPath = (point, path) => {
|
|
|
5343
5351
|
let bestDistance = Number.POSITIVE_INFINITY;
|
|
5344
5352
|
for (let i = 0; i < path.length - 1; i++) {
|
|
5345
5353
|
const projectedPoint = projectPointToSegment(point, path[i], path[i + 1]);
|
|
5346
|
-
const
|
|
5347
|
-
if (
|
|
5354
|
+
const distance5 = getDistance2(point, projectedPoint);
|
|
5355
|
+
if (distance5 < bestDistance) {
|
|
5348
5356
|
bestPoint = projectedPoint;
|
|
5349
|
-
bestDistance =
|
|
5357
|
+
bestDistance = distance5;
|
|
5350
5358
|
}
|
|
5351
5359
|
}
|
|
5352
5360
|
return bestPoint;
|
|
@@ -7160,11 +7168,11 @@ var getLabelHugDistance = (tracePath, obstacleLabel) => {
|
|
|
7160
7168
|
obstacleLabel.width,
|
|
7161
7169
|
obstacleLabel.height
|
|
7162
7170
|
);
|
|
7163
|
-
let
|
|
7171
|
+
let distance5 = 0;
|
|
7164
7172
|
for (const point of tracePath) {
|
|
7165
|
-
|
|
7173
|
+
distance5 += getPointDistanceFromRect(point, bounds);
|
|
7166
7174
|
}
|
|
7167
|
-
return
|
|
7175
|
+
return distance5;
|
|
7168
7176
|
};
|
|
7169
7177
|
var getPointDistanceFromRect = (point, rect) => {
|
|
7170
7178
|
const dx = Math.max(rect.minX - point.x, 0, point.x - rect.maxX);
|
|
@@ -7441,16 +7449,16 @@ var Example28Solver = class extends BaseSolver {
|
|
|
7441
7449
|
const outward = dir(label.orientation);
|
|
7442
7450
|
if (outward.x === 0 && outward.y === 0) return null;
|
|
7443
7451
|
for (let step = 1; step <= LABEL_MAX_OUTWARD_STEPS; step++) {
|
|
7444
|
-
const
|
|
7452
|
+
const distance5 = step * LABEL_OUTWARD_STEP;
|
|
7445
7453
|
const candidate = {
|
|
7446
7454
|
...label,
|
|
7447
7455
|
anchorPoint: {
|
|
7448
|
-
x: label.anchorPoint.x + outward.x *
|
|
7449
|
-
y: label.anchorPoint.y + outward.y *
|
|
7456
|
+
x: label.anchorPoint.x + outward.x * distance5,
|
|
7457
|
+
y: label.anchorPoint.y + outward.y * distance5
|
|
7450
7458
|
},
|
|
7451
7459
|
center: {
|
|
7452
|
-
x: label.center.x + outward.x *
|
|
7453
|
-
y: label.center.y + outward.y *
|
|
7460
|
+
x: label.center.x + outward.x * distance5,
|
|
7461
|
+
y: label.center.y + outward.y * distance5
|
|
7454
7462
|
}
|
|
7455
7463
|
};
|
|
7456
7464
|
const candidateWithClearance = {
|
|
@@ -8024,10 +8032,10 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
8024
8032
|
phase = "shift",
|
|
8025
8033
|
stopOnTraceCollision = true
|
|
8026
8034
|
} = params;
|
|
8027
|
-
for (let
|
|
8035
|
+
for (let distance5 = LABEL_SEARCH_STEP; distance5 <= maxSearchDistance + EPS11; distance5 += LABEL_SEARCH_STEP) {
|
|
8028
8036
|
const anchorPoint = {
|
|
8029
|
-
x: baseAnchor.x + direction.x *
|
|
8030
|
-
y: baseAnchor.y + direction.y *
|
|
8037
|
+
x: baseAnchor.x + direction.x * distance5,
|
|
8038
|
+
y: baseAnchor.y + direction.y * distance5
|
|
8031
8039
|
};
|
|
8032
8040
|
const candidate = this.createCandidate(label, anchorPoint, orientation);
|
|
8033
8041
|
const result = this.evaluateCandidate(
|
|
@@ -8035,7 +8043,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
8035
8043
|
label,
|
|
8036
8044
|
labelIndex,
|
|
8037
8045
|
phase,
|
|
8038
|
-
|
|
8046
|
+
distance5,
|
|
8039
8047
|
outwardDistance
|
|
8040
8048
|
);
|
|
8041
8049
|
this.currentCandidateResults.push(result);
|
|
@@ -8047,11 +8055,11 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
8047
8055
|
}
|
|
8048
8056
|
return null;
|
|
8049
8057
|
}
|
|
8050
|
-
evaluateCandidate(candidate, label, labelIndex, phase,
|
|
8058
|
+
evaluateCandidate(candidate, label, labelIndex, phase, distance5, outwardDistance) {
|
|
8051
8059
|
return {
|
|
8052
8060
|
...candidate,
|
|
8053
8061
|
phase,
|
|
8054
|
-
distance:
|
|
8062
|
+
distance: distance5,
|
|
8055
8063
|
outwardDistance,
|
|
8056
8064
|
selected: false,
|
|
8057
8065
|
status: this.getCandidateStatus({
|
|
@@ -8389,10 +8397,10 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
8389
8397
|
if (point.x < bounds.minX - EPS11 || point.x > bounds.maxX + EPS11 || point.y < bounds.minY - EPS11 || point.y > bounds.maxY + EPS11) {
|
|
8390
8398
|
continue;
|
|
8391
8399
|
}
|
|
8392
|
-
for (const [side,
|
|
8393
|
-
if (
|
|
8400
|
+
for (const [side, distance5] of getSideDistances(point, bounds)) {
|
|
8401
|
+
if (distance5 < nearestDistance) {
|
|
8394
8402
|
nearestSide = side;
|
|
8395
|
-
nearestDistance =
|
|
8403
|
+
nearestDistance = distance5;
|
|
8396
8404
|
}
|
|
8397
8405
|
}
|
|
8398
8406
|
}
|
|
@@ -8421,10 +8429,10 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
8421
8429
|
let nearestSide = null;
|
|
8422
8430
|
let nearestDistance = Number.POSITIVE_INFINITY;
|
|
8423
8431
|
for (const chip of this.chipObstacleSpatialIndex.chips) {
|
|
8424
|
-
for (const [side,
|
|
8425
|
-
if (
|
|
8432
|
+
for (const [side, distance5] of getSideDistances(point, chip.bounds)) {
|
|
8433
|
+
if (distance5 < nearestDistance) {
|
|
8426
8434
|
nearestSide = side;
|
|
8427
|
-
nearestDistance =
|
|
8435
|
+
nearestDistance = distance5;
|
|
8428
8436
|
}
|
|
8429
8437
|
}
|
|
8430
8438
|
}
|
|
@@ -8825,17 +8833,17 @@ var getTraceLength = (trace) => {
|
|
|
8825
8833
|
}
|
|
8826
8834
|
return length;
|
|
8827
8835
|
};
|
|
8828
|
-
var getPointAtTraceDistance = (trace,
|
|
8836
|
+
var getPointAtTraceDistance = (trace, distance5) => {
|
|
8829
8837
|
let pathDistance = 0;
|
|
8830
8838
|
for (let i = 0; i < trace.tracePath.length - 1; i++) {
|
|
8831
8839
|
const start = trace.tracePath[i];
|
|
8832
8840
|
const end = trace.tracePath[i + 1];
|
|
8833
8841
|
const segmentLength = getManhattanDistance(start, end);
|
|
8834
8842
|
const nextDistance = pathDistance + segmentLength;
|
|
8835
|
-
if (
|
|
8843
|
+
if (distance5 <= nextDistance + EPS12) {
|
|
8836
8844
|
const offset = Math.max(
|
|
8837
8845
|
0,
|
|
8838
|
-
Math.min(segmentLength,
|
|
8846
|
+
Math.min(segmentLength, distance5 - pathDistance)
|
|
8839
8847
|
);
|
|
8840
8848
|
const direction = getSegmentDirection(start, end);
|
|
8841
8849
|
return {
|
|
@@ -8960,13 +8968,13 @@ var getCandidateDistances = (traceLength, vertexDistances) => {
|
|
|
8960
8968
|
const distances = /* @__PURE__ */ new Set();
|
|
8961
8969
|
const maxSteps = Math.ceil(traceLength / CANDIDATE_STEP);
|
|
8962
8970
|
for (let i = 0; i <= maxSteps; i++) {
|
|
8963
|
-
const
|
|
8964
|
-
distances.add(roundDistance(
|
|
8971
|
+
const distance5 = Math.min(traceLength, i * CANDIDATE_STEP);
|
|
8972
|
+
distances.add(roundDistance(distance5));
|
|
8965
8973
|
}
|
|
8966
|
-
for (const
|
|
8967
|
-
distances.add(roundDistance(
|
|
8974
|
+
for (const distance5 of vertexDistances) {
|
|
8975
|
+
distances.add(roundDistance(distance5));
|
|
8968
8976
|
}
|
|
8969
|
-
return [...distances].filter((
|
|
8977
|
+
return [...distances].filter((distance5) => distance5 >= -EPS12 && distance5 <= traceLength + EPS12).sort((a, b) => a - b);
|
|
8970
8978
|
};
|
|
8971
8979
|
var getOrientationsForPoint = (params) => {
|
|
8972
8980
|
const { inputProblem, label, point, orientationConstraint } = params;
|
|
@@ -9144,7 +9152,7 @@ var getNetLabelHeight = (inputProblem, label) => {
|
|
|
9144
9152
|
(nc) => nc.pinIds.some((pid) => label.pinIds.includes(pid))
|
|
9145
9153
|
)?.netLabelHeight;
|
|
9146
9154
|
};
|
|
9147
|
-
var roundDistance = (
|
|
9155
|
+
var roundDistance = (distance5) => Number(distance5.toFixed(6));
|
|
9148
9156
|
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
9157
|
|
|
9150
9158
|
// lib/solvers/TraceAnchoredNetLabelOverlapSolver/visualize.ts
|
|
@@ -10110,6 +10118,457 @@ ${c.status}`
|
|
|
10110
10118
|
}
|
|
10111
10119
|
};
|
|
10112
10120
|
|
|
10121
|
+
// lib/solvers/UnroutedTraceRecoverySolver/UnroutedTraceRecoverySolver.ts
|
|
10122
|
+
import { distance as distance4, doSegmentsIntersect as doSegmentsIntersect3 } from "@tscircuit/math-utils";
|
|
10123
|
+
var ROUTE_CLEARANCE = 0.2;
|
|
10124
|
+
var COORDINATE_TOLERANCE = 1e-9;
|
|
10125
|
+
var GROUND_NET_ID = "GND";
|
|
10126
|
+
var pointsAreEqual = (firstPoint, secondPoint) => {
|
|
10127
|
+
return Math.abs(firstPoint.x - secondPoint.x) <= COORDINATE_TOLERANCE && Math.abs(firstPoint.y - secondPoint.y) <= COORDINATE_TOLERANCE;
|
|
10128
|
+
};
|
|
10129
|
+
var removeConsecutiveDuplicatePoints = (path) => {
|
|
10130
|
+
const filteredPath = [];
|
|
10131
|
+
for (const point of path) {
|
|
10132
|
+
const previousPoint = filteredPath.at(-1);
|
|
10133
|
+
if (!previousPoint || !pointsAreEqual(previousPoint, point)) {
|
|
10134
|
+
filteredPath.push(point);
|
|
10135
|
+
}
|
|
10136
|
+
}
|
|
10137
|
+
return filteredPath;
|
|
10138
|
+
};
|
|
10139
|
+
var getPathLength2 = (path) => {
|
|
10140
|
+
let pathLength = 0;
|
|
10141
|
+
for (let pointIndex = 0; pointIndex < path.length - 1; pointIndex++) {
|
|
10142
|
+
const startPoint = path[pointIndex];
|
|
10143
|
+
const endPoint = path[pointIndex + 1];
|
|
10144
|
+
pathLength += Math.abs(endPoint.x - startPoint.x) + Math.abs(endPoint.y - startPoint.y);
|
|
10145
|
+
}
|
|
10146
|
+
return pathLength;
|
|
10147
|
+
};
|
|
10148
|
+
var getEscapePoint = ({
|
|
10149
|
+
pin,
|
|
10150
|
+
facingDirection
|
|
10151
|
+
}) => {
|
|
10152
|
+
const escapePoint = { x: pin.x, y: pin.y };
|
|
10153
|
+
if (facingDirection === "x+") {
|
|
10154
|
+
escapePoint.x += ROUTE_CLEARANCE;
|
|
10155
|
+
}
|
|
10156
|
+
if (facingDirection === "x-") {
|
|
10157
|
+
escapePoint.x -= ROUTE_CLEARANCE;
|
|
10158
|
+
}
|
|
10159
|
+
if (facingDirection === "y+") {
|
|
10160
|
+
escapePoint.y += ROUTE_CLEARANCE;
|
|
10161
|
+
}
|
|
10162
|
+
if (facingDirection === "y-") {
|
|
10163
|
+
escapePoint.y -= ROUTE_CLEARANCE;
|
|
10164
|
+
}
|
|
10165
|
+
return escapePoint;
|
|
10166
|
+
};
|
|
10167
|
+
var getOuterBounds = (obstacles) => {
|
|
10168
|
+
return {
|
|
10169
|
+
minX: Math.min(...obstacles.map((obstacle) => obstacle.minX)),
|
|
10170
|
+
minY: Math.min(...obstacles.map((obstacle) => obstacle.minY)),
|
|
10171
|
+
maxX: Math.max(...obstacles.map((obstacle) => obstacle.maxX)),
|
|
10172
|
+
maxY: Math.max(...obstacles.map((obstacle) => obstacle.maxY))
|
|
10173
|
+
};
|
|
10174
|
+
};
|
|
10175
|
+
var getPerimeterCandidates = ({
|
|
10176
|
+
connectionPair,
|
|
10177
|
+
obstacles
|
|
10178
|
+
}) => {
|
|
10179
|
+
const [firstPin, secondPin] = connectionPair.pins;
|
|
10180
|
+
const firstEscapePoint = getEscapePoint({
|
|
10181
|
+
pin: firstPin,
|
|
10182
|
+
facingDirection: firstPin._facingDirection
|
|
10183
|
+
});
|
|
10184
|
+
const secondEscapePoint = getEscapePoint({
|
|
10185
|
+
pin: secondPin,
|
|
10186
|
+
facingDirection: secondPin._facingDirection
|
|
10187
|
+
});
|
|
10188
|
+
const outerBounds = getOuterBounds(obstacles);
|
|
10189
|
+
const horizontalChannels = [
|
|
10190
|
+
outerBounds.minY - ROUTE_CLEARANCE,
|
|
10191
|
+
outerBounds.maxY + ROUTE_CLEARANCE
|
|
10192
|
+
];
|
|
10193
|
+
const verticalChannels = [
|
|
10194
|
+
outerBounds.minX - ROUTE_CLEARANCE,
|
|
10195
|
+
outerBounds.maxX + ROUTE_CLEARANCE
|
|
10196
|
+
];
|
|
10197
|
+
const candidates = [];
|
|
10198
|
+
for (const channelY of horizontalChannels) {
|
|
10199
|
+
candidates.push(
|
|
10200
|
+
removeConsecutiveDuplicatePoints([
|
|
10201
|
+
firstPin,
|
|
10202
|
+
firstEscapePoint,
|
|
10203
|
+
{ x: firstEscapePoint.x, y: channelY },
|
|
10204
|
+
{ x: secondEscapePoint.x, y: channelY },
|
|
10205
|
+
secondEscapePoint,
|
|
10206
|
+
secondPin
|
|
10207
|
+
])
|
|
10208
|
+
);
|
|
10209
|
+
}
|
|
10210
|
+
for (const channelX of verticalChannels) {
|
|
10211
|
+
candidates.push(
|
|
10212
|
+
removeConsecutiveDuplicatePoints([
|
|
10213
|
+
firstPin,
|
|
10214
|
+
firstEscapePoint,
|
|
10215
|
+
{ x: channelX, y: firstEscapePoint.y },
|
|
10216
|
+
{ x: channelX, y: secondEscapePoint.y },
|
|
10217
|
+
secondEscapePoint,
|
|
10218
|
+
secondPin
|
|
10219
|
+
])
|
|
10220
|
+
);
|
|
10221
|
+
}
|
|
10222
|
+
return candidates.sort(
|
|
10223
|
+
(firstPath, secondPath) => getPathLength2(firstPath) - getPathLength2(secondPath)
|
|
10224
|
+
);
|
|
10225
|
+
};
|
|
10226
|
+
var getSegmentMidpoint = (startPoint, endPoint) => {
|
|
10227
|
+
return {
|
|
10228
|
+
x: (startPoint.x + endPoint.x) / 2,
|
|
10229
|
+
y: (startPoint.y + endPoint.y) / 2
|
|
10230
|
+
};
|
|
10231
|
+
};
|
|
10232
|
+
var getJunctionPoints = (sameNetTraces) => {
|
|
10233
|
+
const junctionPoints = [];
|
|
10234
|
+
for (const trace of sameNetTraces) {
|
|
10235
|
+
for (let pointIndex = 0; pointIndex < trace.tracePath.length - 1; pointIndex++) {
|
|
10236
|
+
const startPoint = trace.tracePath[pointIndex];
|
|
10237
|
+
const endPoint = trace.tracePath[pointIndex + 1];
|
|
10238
|
+
junctionPoints.push(startPoint);
|
|
10239
|
+
junctionPoints.push(getSegmentMidpoint(startPoint, endPoint));
|
|
10240
|
+
}
|
|
10241
|
+
const lastPoint = trace.tracePath.at(-1);
|
|
10242
|
+
if (lastPoint) {
|
|
10243
|
+
junctionPoints.push(lastPoint);
|
|
10244
|
+
}
|
|
10245
|
+
}
|
|
10246
|
+
return junctionPoints;
|
|
10247
|
+
};
|
|
10248
|
+
var getUnconnectedPins = ({
|
|
10249
|
+
connectionPair,
|
|
10250
|
+
sameNetTraces
|
|
10251
|
+
}) => {
|
|
10252
|
+
const connectedPinIds = new Set(
|
|
10253
|
+
sameNetTraces.flatMap((trace) => trace.pinIds)
|
|
10254
|
+
);
|
|
10255
|
+
return connectionPair.pins.filter((pin) => !connectedPinIds.has(pin.pinId));
|
|
10256
|
+
};
|
|
10257
|
+
var getJunctionCandidates = ({
|
|
10258
|
+
connectionPair,
|
|
10259
|
+
sameNetTraces,
|
|
10260
|
+
obstacles,
|
|
10261
|
+
maxConnectionDistance
|
|
10262
|
+
}) => {
|
|
10263
|
+
const outerBounds = getOuterBounds(obstacles);
|
|
10264
|
+
const horizontalChannels = [
|
|
10265
|
+
outerBounds.minY - ROUTE_CLEARANCE,
|
|
10266
|
+
outerBounds.maxY + ROUTE_CLEARANCE
|
|
10267
|
+
];
|
|
10268
|
+
const verticalChannels = [
|
|
10269
|
+
outerBounds.minX - ROUTE_CLEARANCE,
|
|
10270
|
+
outerBounds.maxX + ROUTE_CLEARANCE
|
|
10271
|
+
];
|
|
10272
|
+
const candidates = [];
|
|
10273
|
+
const junctionPoints = getJunctionPoints(sameNetTraces);
|
|
10274
|
+
const unconnectedPins = getUnconnectedPins({
|
|
10275
|
+
connectionPair,
|
|
10276
|
+
sameNetTraces
|
|
10277
|
+
});
|
|
10278
|
+
for (const pin of unconnectedPins) {
|
|
10279
|
+
const escapePoint = getEscapePoint({
|
|
10280
|
+
pin,
|
|
10281
|
+
facingDirection: pin._facingDirection
|
|
10282
|
+
});
|
|
10283
|
+
for (const junctionPoint of junctionPoints) {
|
|
10284
|
+
if (distance4(pin, junctionPoint) > maxConnectionDistance) {
|
|
10285
|
+
continue;
|
|
10286
|
+
}
|
|
10287
|
+
candidates.push(
|
|
10288
|
+
removeConsecutiveDuplicatePoints([
|
|
10289
|
+
pin,
|
|
10290
|
+
escapePoint,
|
|
10291
|
+
{ x: escapePoint.x, y: junctionPoint.y },
|
|
10292
|
+
junctionPoint
|
|
10293
|
+
])
|
|
10294
|
+
);
|
|
10295
|
+
candidates.push(
|
|
10296
|
+
removeConsecutiveDuplicatePoints([
|
|
10297
|
+
pin,
|
|
10298
|
+
escapePoint,
|
|
10299
|
+
{ x: junctionPoint.x, y: escapePoint.y },
|
|
10300
|
+
junctionPoint
|
|
10301
|
+
])
|
|
10302
|
+
);
|
|
10303
|
+
for (const channelY of horizontalChannels) {
|
|
10304
|
+
candidates.push(
|
|
10305
|
+
removeConsecutiveDuplicatePoints([
|
|
10306
|
+
pin,
|
|
10307
|
+
escapePoint,
|
|
10308
|
+
{ x: escapePoint.x, y: channelY },
|
|
10309
|
+
{ x: junctionPoint.x, y: channelY },
|
|
10310
|
+
junctionPoint
|
|
10311
|
+
])
|
|
10312
|
+
);
|
|
10313
|
+
}
|
|
10314
|
+
for (const channelX of verticalChannels) {
|
|
10315
|
+
candidates.push(
|
|
10316
|
+
removeConsecutiveDuplicatePoints([
|
|
10317
|
+
pin,
|
|
10318
|
+
escapePoint,
|
|
10319
|
+
{ x: channelX, y: escapePoint.y },
|
|
10320
|
+
{ x: channelX, y: junctionPoint.y },
|
|
10321
|
+
junctionPoint
|
|
10322
|
+
])
|
|
10323
|
+
);
|
|
10324
|
+
}
|
|
10325
|
+
}
|
|
10326
|
+
}
|
|
10327
|
+
return candidates.sort(
|
|
10328
|
+
(firstPath, secondPath) => getPathLength2(firstPath) - getPathLength2(secondPath)
|
|
10329
|
+
);
|
|
10330
|
+
};
|
|
10331
|
+
var pathCollidesWithObstacles = ({
|
|
10332
|
+
path,
|
|
10333
|
+
obstacles,
|
|
10334
|
+
connectionPair,
|
|
10335
|
+
rejectComponentBoundaryTravel
|
|
10336
|
+
}) => {
|
|
10337
|
+
const firstPathPoint = path[0];
|
|
10338
|
+
const lastPathPoint = path.at(-1);
|
|
10339
|
+
const firstPathPin = connectionPair.pins.find(
|
|
10340
|
+
(pin) => pointsAreEqual(pin, firstPathPoint)
|
|
10341
|
+
);
|
|
10342
|
+
const lastPathPin = connectionPair.pins.find(
|
|
10343
|
+
(pin) => pointsAreEqual(pin, lastPathPoint)
|
|
10344
|
+
);
|
|
10345
|
+
const pathConnectsPairPins = firstPathPin !== void 0 && lastPathPin !== void 0;
|
|
10346
|
+
const firstChipObstacle = obstacles.find(
|
|
10347
|
+
(obstacle) => obstacle.kind === "chip" && obstacle.chipId === firstPathPin?.chipId
|
|
10348
|
+
);
|
|
10349
|
+
const secondChipObstacle = obstacles.find(
|
|
10350
|
+
(obstacle) => obstacle.kind === "chip" && obstacle.chipId === lastPathPin?.chipId
|
|
10351
|
+
);
|
|
10352
|
+
if (rejectComponentBoundaryTravel && firstChipObstacle && segmentOverlapsRectBoundary(path[0], path[1], firstChipObstacle)) {
|
|
10353
|
+
return true;
|
|
10354
|
+
}
|
|
10355
|
+
if (rejectComponentBoundaryTravel && secondChipObstacle && segmentOverlapsRectBoundary(
|
|
10356
|
+
path[path.length - 2],
|
|
10357
|
+
path[path.length - 1],
|
|
10358
|
+
secondChipObstacle
|
|
10359
|
+
)) {
|
|
10360
|
+
return true;
|
|
10361
|
+
}
|
|
10362
|
+
const collision = findFirstCollision(path, obstacles, {
|
|
10363
|
+
excludeRectsForSegment: (segmentIndex) => {
|
|
10364
|
+
const excludedObstacles = /* @__PURE__ */ new Set();
|
|
10365
|
+
if (segmentIndex === 0 && firstChipObstacle) {
|
|
10366
|
+
excludedObstacles.add(firstChipObstacle);
|
|
10367
|
+
}
|
|
10368
|
+
if (pathConnectsPairPins && segmentIndex === path.length - 2 && secondChipObstacle) {
|
|
10369
|
+
excludedObstacles.add(secondChipObstacle);
|
|
10370
|
+
}
|
|
10371
|
+
return excludedObstacles;
|
|
10372
|
+
}
|
|
10373
|
+
});
|
|
10374
|
+
return collision !== null;
|
|
10375
|
+
};
|
|
10376
|
+
var hasParallelFailedConnection = ({
|
|
10377
|
+
connectionPair,
|
|
10378
|
+
failedConnectionPairs
|
|
10379
|
+
}) => {
|
|
10380
|
+
const connectionChipIds = new Set(
|
|
10381
|
+
connectionPair.pins.map((pin) => pin.chipId)
|
|
10382
|
+
);
|
|
10383
|
+
return failedConnectionPairs.some((otherConnectionPair) => {
|
|
10384
|
+
if (otherConnectionPair.mspPairId === connectionPair.mspPairId) {
|
|
10385
|
+
return false;
|
|
10386
|
+
}
|
|
10387
|
+
return otherConnectionPair.pins.every(
|
|
10388
|
+
(pin) => connectionChipIds.has(pin.chipId)
|
|
10389
|
+
);
|
|
10390
|
+
});
|
|
10391
|
+
};
|
|
10392
|
+
var segmentsOverlapBeyondEndpoint = ({
|
|
10393
|
+
firstStart,
|
|
10394
|
+
firstEnd,
|
|
10395
|
+
secondStart,
|
|
10396
|
+
secondEnd
|
|
10397
|
+
}) => {
|
|
10398
|
+
if (isHorizontal(firstStart, firstEnd) && isHorizontal(secondStart, secondEnd)) {
|
|
10399
|
+
const overlapLength = Math.min(
|
|
10400
|
+
Math.max(firstStart.x, firstEnd.x),
|
|
10401
|
+
Math.max(secondStart.x, secondEnd.x)
|
|
10402
|
+
) - Math.max(
|
|
10403
|
+
Math.min(firstStart.x, firstEnd.x),
|
|
10404
|
+
Math.min(secondStart.x, secondEnd.x)
|
|
10405
|
+
);
|
|
10406
|
+
return overlapLength > COORDINATE_TOLERANCE;
|
|
10407
|
+
}
|
|
10408
|
+
if (isVertical(firstStart, firstEnd) && isVertical(secondStart, secondEnd)) {
|
|
10409
|
+
const overlapLength = Math.min(
|
|
10410
|
+
Math.max(firstStart.y, firstEnd.y),
|
|
10411
|
+
Math.max(secondStart.y, secondEnd.y)
|
|
10412
|
+
) - Math.max(
|
|
10413
|
+
Math.min(firstStart.y, firstEnd.y),
|
|
10414
|
+
Math.min(secondStart.y, secondEnd.y)
|
|
10415
|
+
);
|
|
10416
|
+
return overlapLength > COORDINATE_TOLERANCE;
|
|
10417
|
+
}
|
|
10418
|
+
return false;
|
|
10419
|
+
};
|
|
10420
|
+
var getAllowedJunctionPoints = ({
|
|
10421
|
+
connectionPair,
|
|
10422
|
+
existingTrace
|
|
10423
|
+
}) => {
|
|
10424
|
+
const existingPinIds = new Set(existingTrace.pinIds);
|
|
10425
|
+
return connectionPair.pins.filter((pin) => existingPinIds.has(pin.pinId));
|
|
10426
|
+
};
|
|
10427
|
+
var pathCrossesExistingTraces = ({
|
|
10428
|
+
path,
|
|
10429
|
+
connectionPair,
|
|
10430
|
+
existingTraces
|
|
10431
|
+
}) => {
|
|
10432
|
+
for (const existingTrace of existingTraces) {
|
|
10433
|
+
if (existingTrace.globalConnNetId === connectionPair.globalConnNetId) {
|
|
10434
|
+
continue;
|
|
10435
|
+
}
|
|
10436
|
+
const allowedJunctionPoints = getAllowedJunctionPoints({
|
|
10437
|
+
connectionPair,
|
|
10438
|
+
existingTrace
|
|
10439
|
+
});
|
|
10440
|
+
for (let pathIndex = 0; pathIndex < path.length - 1; pathIndex++) {
|
|
10441
|
+
const pathStart = path[pathIndex];
|
|
10442
|
+
const pathEnd = path[pathIndex + 1];
|
|
10443
|
+
for (let traceIndex = 0; traceIndex < existingTrace.tracePath.length - 1; traceIndex++) {
|
|
10444
|
+
const traceStart = existingTrace.tracePath[traceIndex];
|
|
10445
|
+
const traceEnd = existingTrace.tracePath[traceIndex + 1];
|
|
10446
|
+
if (!doSegmentsIntersect3(pathStart, pathEnd, traceStart, traceEnd)) {
|
|
10447
|
+
continue;
|
|
10448
|
+
}
|
|
10449
|
+
const intersectionIsAllowedJunction = allowedJunctionPoints.some(
|
|
10450
|
+
(junctionPoint) => (pointsAreEqual(pathStart, junctionPoint) || pointsAreEqual(pathEnd, junctionPoint)) && (pointsAreEqual(traceStart, junctionPoint) || pointsAreEqual(traceEnd, junctionPoint))
|
|
10451
|
+
);
|
|
10452
|
+
if (intersectionIsAllowedJunction && !segmentsOverlapBeyondEndpoint({
|
|
10453
|
+
firstStart: pathStart,
|
|
10454
|
+
firstEnd: pathEnd,
|
|
10455
|
+
secondStart: traceStart,
|
|
10456
|
+
secondEnd: traceEnd
|
|
10457
|
+
})) {
|
|
10458
|
+
continue;
|
|
10459
|
+
}
|
|
10460
|
+
return true;
|
|
10461
|
+
}
|
|
10462
|
+
}
|
|
10463
|
+
}
|
|
10464
|
+
return false;
|
|
10465
|
+
};
|
|
10466
|
+
var UnroutedTraceRecoverySolver = class extends BaseSolver {
|
|
10467
|
+
constructor(params) {
|
|
10468
|
+
super();
|
|
10469
|
+
this.params = params;
|
|
10470
|
+
this.inputProblem = params.inputProblem;
|
|
10471
|
+
this.alreadySolvedTraces = params.alreadySolvedTraces;
|
|
10472
|
+
this.failedConnectionPairs = params.failedConnectionPairs;
|
|
10473
|
+
this.queuedConnectionPairs = [...params.failedConnectionPairs];
|
|
10474
|
+
this.maxConnectionDistance = this.inputProblem.maxMspPairDistance ?? DEFAULT_MAX_MSP_PAIR_DISTANCE;
|
|
10475
|
+
const { netConnMap } = getConnectivityMapsFromInputProblem(
|
|
10476
|
+
this.inputProblem
|
|
10477
|
+
);
|
|
10478
|
+
this.groundGlobalConnNetId = netConnMap.getNetConnectedToId(GROUND_NET_ID) ?? void 0;
|
|
10479
|
+
}
|
|
10480
|
+
params;
|
|
10481
|
+
inputProblem;
|
|
10482
|
+
alreadySolvedTraces;
|
|
10483
|
+
failedConnectionPairs;
|
|
10484
|
+
queuedConnectionPairs;
|
|
10485
|
+
maxConnectionDistance;
|
|
10486
|
+
groundGlobalConnNetId;
|
|
10487
|
+
solvedUnroutedTraces = [];
|
|
10488
|
+
getConstructorParams() {
|
|
10489
|
+
return this.params;
|
|
10490
|
+
}
|
|
10491
|
+
_step() {
|
|
10492
|
+
const connectionPair = this.queuedConnectionPairs.shift();
|
|
10493
|
+
if (!connectionPair) {
|
|
10494
|
+
this.solved = true;
|
|
10495
|
+
return;
|
|
10496
|
+
}
|
|
10497
|
+
if (connectionPair.globalConnNetId === this.groundGlobalConnNetId) {
|
|
10498
|
+
return;
|
|
10499
|
+
}
|
|
10500
|
+
if (distance4(connectionPair.pins[0], connectionPair.pins[1]) > this.maxConnectionDistance) {
|
|
10501
|
+
return;
|
|
10502
|
+
}
|
|
10503
|
+
const obstacles = getObstacleRects(this.inputProblem);
|
|
10504
|
+
const existingTraces = [
|
|
10505
|
+
...this.alreadySolvedTraces,
|
|
10506
|
+
...this.solvedUnroutedTraces
|
|
10507
|
+
];
|
|
10508
|
+
const sameNetTraces = existingTraces.filter(
|
|
10509
|
+
(trace) => trace.globalConnNetId === connectionPair.globalConnNetId
|
|
10510
|
+
);
|
|
10511
|
+
const junctionCandidates = getJunctionCandidates({
|
|
10512
|
+
connectionPair,
|
|
10513
|
+
sameNetTraces,
|
|
10514
|
+
obstacles,
|
|
10515
|
+
maxConnectionDistance: this.maxConnectionDistance
|
|
10516
|
+
});
|
|
10517
|
+
const perimeterCandidates = getPerimeterCandidates({
|
|
10518
|
+
connectionPair,
|
|
10519
|
+
obstacles
|
|
10520
|
+
});
|
|
10521
|
+
const candidates = [...junctionCandidates, ...perimeterCandidates];
|
|
10522
|
+
const rejectComponentBoundaryTravel = hasParallelFailedConnection({
|
|
10523
|
+
connectionPair,
|
|
10524
|
+
failedConnectionPairs: this.failedConnectionPairs
|
|
10525
|
+
});
|
|
10526
|
+
for (const tracePath of candidates) {
|
|
10527
|
+
if (pathCollidesWithObstacles({
|
|
10528
|
+
path: tracePath,
|
|
10529
|
+
obstacles,
|
|
10530
|
+
connectionPair,
|
|
10531
|
+
rejectComponentBoundaryTravel
|
|
10532
|
+
})) {
|
|
10533
|
+
continue;
|
|
10534
|
+
}
|
|
10535
|
+
if (pathCrossesExistingTraces({
|
|
10536
|
+
path: tracePath,
|
|
10537
|
+
connectionPair,
|
|
10538
|
+
existingTraces
|
|
10539
|
+
})) {
|
|
10540
|
+
continue;
|
|
10541
|
+
}
|
|
10542
|
+
this.solvedUnroutedTraces.push({
|
|
10543
|
+
...connectionPair,
|
|
10544
|
+
tracePath,
|
|
10545
|
+
mspConnectionPairIds: [connectionPair.mspPairId],
|
|
10546
|
+
pinIds: connectionPair.pins.map((pin) => pin.pinId)
|
|
10547
|
+
});
|
|
10548
|
+
return;
|
|
10549
|
+
}
|
|
10550
|
+
}
|
|
10551
|
+
getOutput() {
|
|
10552
|
+
return {
|
|
10553
|
+
newTraces: this.solvedUnroutedTraces,
|
|
10554
|
+
allTracesMerged: [
|
|
10555
|
+
...this.alreadySolvedTraces,
|
|
10556
|
+
...this.solvedUnroutedTraces
|
|
10557
|
+
]
|
|
10558
|
+
};
|
|
10559
|
+
}
|
|
10560
|
+
visualize() {
|
|
10561
|
+
const graphics = visualizeInputProblem(this.inputProblem);
|
|
10562
|
+
for (const trace of this.solvedUnroutedTraces) {
|
|
10563
|
+
graphics.lines.push({
|
|
10564
|
+
points: trace.tracePath,
|
|
10565
|
+
strokeColor: "blue"
|
|
10566
|
+
});
|
|
10567
|
+
}
|
|
10568
|
+
return graphics;
|
|
10569
|
+
}
|
|
10570
|
+
};
|
|
10571
|
+
|
|
10113
10572
|
// lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts
|
|
10114
10573
|
function definePipelineStep(solverName, solverClass, getConstructorParams, opts = {}) {
|
|
10115
10574
|
return {
|
|
@@ -10125,6 +10584,7 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
|
|
|
10125
10584
|
// guidelinesSolver?: GuidelinesSolver
|
|
10126
10585
|
schematicTraceLinesSolver;
|
|
10127
10586
|
longDistancePairSolver;
|
|
10587
|
+
unroutedTraceRecoverySolver;
|
|
10128
10588
|
traceOverlapShiftSolver;
|
|
10129
10589
|
netLabelPlacementSolver;
|
|
10130
10590
|
labelMergingSolver;
|
|
@@ -10195,13 +10655,24 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
|
|
|
10195
10655
|
}
|
|
10196
10656
|
}
|
|
10197
10657
|
),
|
|
10658
|
+
definePipelineStep(
|
|
10659
|
+
"unroutedTraceRecoverySolver",
|
|
10660
|
+
UnroutedTraceRecoverySolver,
|
|
10661
|
+
(instance) => [
|
|
10662
|
+
{
|
|
10663
|
+
inputProblem: instance.inputProblem,
|
|
10664
|
+
failedConnectionPairs: instance.schematicTraceLinesSolver.failedConnectionPairs,
|
|
10665
|
+
alreadySolvedTraces: instance.longDistancePairSolver.getOutput().allTracesMerged
|
|
10666
|
+
}
|
|
10667
|
+
]
|
|
10668
|
+
),
|
|
10198
10669
|
definePipelineStep(
|
|
10199
10670
|
"traceOverlapShiftSolver",
|
|
10200
10671
|
TraceOverlapShiftSolver,
|
|
10201
10672
|
() => [
|
|
10202
10673
|
{
|
|
10203
10674
|
inputProblem: this.inputProblem,
|
|
10204
|
-
inputTracePaths: this.
|
|
10675
|
+
inputTracePaths: this.unroutedTraceRecoverySolver?.getOutput().allTracesMerged,
|
|
10205
10676
|
globalConnMap: this.mspConnectionPairSolver.globalConnMap
|
|
10206
10677
|
}
|
|
10207
10678
|
],
|
|
@@ -10217,7 +10688,7 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
|
|
|
10217
10688
|
{
|
|
10218
10689
|
inputProblem: this.inputProblem,
|
|
10219
10690
|
inputTraceMap: this.traceOverlapShiftSolver?.correctedTraceMap ?? Object.fromEntries(
|
|
10220
|
-
this.
|
|
10691
|
+
this.unroutedTraceRecoverySolver.getOutput().allTracesMerged.map(
|
|
10221
10692
|
(p) => [p.mspPairId, p]
|
|
10222
10693
|
)
|
|
10223
10694
|
)
|
|
@@ -10233,7 +10704,7 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
|
|
|
10233
10704
|
TraceLabelOverlapAvoidanceSolver,
|
|
10234
10705
|
(instance) => {
|
|
10235
10706
|
const traceMap = instance.traceOverlapShiftSolver?.correctedTraceMap ?? Object.fromEntries(
|
|
10236
|
-
instance.
|
|
10707
|
+
instance.unroutedTraceRecoverySolver.getOutput().allTracesMerged.map((p) => [p.mspPairId, p])
|
|
10237
10708
|
);
|
|
10238
10709
|
const traces = Object.values(traceMap);
|
|
10239
10710
|
const netLabelPlacements = instance.netLabelPlacementSolver.netLabelPlacements;
|