@tscircuit/schematic-trace-solver 0.0.106 → 0.0.108
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 +2 -2
- package/dist/index.js +470 -213
- package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2.ts +58 -27
- package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/generateEndpointCollisionDetours.ts +1 -10
- package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/generateInternalSegmentCollisionDetours.ts +94 -0
- package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/pathOps.ts +10 -0
- package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/rect.ts +5 -1
- package/lib/solvers/TraceCleanupSolver/sub-solver/UntangleTraceSubsolver.ts +178 -9
- package/lib/solvers/TraceCleanupSolver/sub-solver/findIntersectionsWithObstacles.ts +59 -0
- package/lib/solvers/TraceCleanupSolver/sub-solver/generateLShapeRerouteCandidates.ts +64 -1
- package/package.json +1 -1
- package/tests/bug-reports/bug-report-20260707T140410Z/__snapshots__/bug-report-20260707T140410Z.snap.svg +3 -3
- package/tests/bug-reports/bug-report-20260708T095725Z/__snapshots__/bug-report-20260708T095725Z.snap.svg +5 -5
- package/tests/bug-reports/bug-report-20260708T095725Z/bug-report-20260708T095725Z.test.ts +15 -0
- package/tests/bug-reports/bug-report-20260721T221026Z/__snapshots__/bug-report-20260721T221026Z.snap.svg +1 -1
- package/tests/repros/__snapshots__/repro-tps61222-trace-intersection.snap.svg +64 -0
- package/tests/repros/__snapshots__/repro47-endpoint-obstacle-detour.snap.svg +1 -1
- package/tests/repros/assets/repro-tps61222-trace-intersection.input.json +200 -0
- package/tests/repros/repro-tps61222-trace-intersection.test.ts +41 -0
- package/tests/repros/repro47-endpoint-obstacle-detour.test.ts +6 -0
- package/tests/solvers/SchematicTraceSingleLineSolver2/generate-internal-segment-collision-detours.test.ts +49 -0
package/dist/index.js
CHANGED
|
@@ -325,19 +325,19 @@ var doesPairCrossRestrictedCenterLines = (params) => {
|
|
|
325
325
|
chipMap
|
|
326
326
|
});
|
|
327
327
|
if (restrictedCenterLines.size === 0) return false;
|
|
328
|
-
const
|
|
328
|
+
const EPS13 = 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) < -EPS13) 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) < -EPS13) 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) < EPS13 || Math.abs(p1.y - p2.y) < EPS13) {
|
|
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 };
|
|
@@ -630,6 +630,25 @@ function getRectBounds(center, w, h) {
|
|
|
630
630
|
};
|
|
631
631
|
}
|
|
632
632
|
|
|
633
|
+
// lib/utils/textBoxBounds.ts
|
|
634
|
+
function getTextBoxBounds(textBox, padding = {}) {
|
|
635
|
+
return {
|
|
636
|
+
minX: textBox.center.x - textBox.width / 2 - (padding.minX ?? 0),
|
|
637
|
+
minY: textBox.center.y - textBox.height / 2 - (padding.minY ?? 0),
|
|
638
|
+
maxX: textBox.center.x + textBox.width / 2 + (padding.maxX ?? 0),
|
|
639
|
+
maxY: textBox.center.y + textBox.height / 2 + (padding.maxY ?? 0)
|
|
640
|
+
};
|
|
641
|
+
}
|
|
642
|
+
function boundsOverlap(a, b, eps = 1e-9) {
|
|
643
|
+
return a.minX < b.maxX - eps && a.maxX > b.minX + eps && a.minY < b.maxY - eps && a.maxY > b.minY + eps;
|
|
644
|
+
}
|
|
645
|
+
function rectIntersectsAnyTextBox(bounds, inputProblem) {
|
|
646
|
+
for (const textBox of inputProblem.textBoxes ?? []) {
|
|
647
|
+
if (boundsOverlap(bounds, getTextBoxBounds(textBox))) return true;
|
|
648
|
+
}
|
|
649
|
+
return false;
|
|
650
|
+
}
|
|
651
|
+
|
|
633
652
|
// lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/calculateDirectShortPath.ts
|
|
634
653
|
import { calculateElbow } from "calculate-elbow";
|
|
635
654
|
var MAX_SHORT_TRACE_DISTANCE = 0.15;
|
|
@@ -875,12 +894,8 @@ var candidateMidsFromSet = (axis, colliding, collisionRects, aabb, opts = {}) =>
|
|
|
875
894
|
}
|
|
876
895
|
};
|
|
877
896
|
|
|
878
|
-
// lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/
|
|
879
|
-
var
|
|
880
|
-
if (isVertical(start, end)) return "x";
|
|
881
|
-
if (isHorizontal(start, end)) return "y";
|
|
882
|
-
return null;
|
|
883
|
-
};
|
|
897
|
+
// lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/pathOps.ts
|
|
898
|
+
var EPS3 = 1e-9;
|
|
884
899
|
var hasOnlyNonzeroOrthogonalSegments = (path) => path.every((point, index) => {
|
|
885
900
|
const nextPoint = path[index + 1];
|
|
886
901
|
if (!nextPoint) return true;
|
|
@@ -889,57 +904,6 @@ var hasOnlyNonzeroOrthogonalSegments = (path) => path.every((point, index) => {
|
|
|
889
904
|
}
|
|
890
905
|
return Math.abs(point.x - nextPoint.x) + Math.abs(point.y - nextPoint.y) > 0;
|
|
891
906
|
});
|
|
892
|
-
var generateEndpointCollisionDetours = ({
|
|
893
|
-
path,
|
|
894
|
-
collidingSegmentIndex,
|
|
895
|
-
obstacle
|
|
896
|
-
}) => {
|
|
897
|
-
if (path.length < 3 || path.length > 4) return [];
|
|
898
|
-
const lastSegmentIndex = path.length - 2;
|
|
899
|
-
if (collidingSegmentIndex !== 0 && collidingSegmentIndex !== lastSegmentIndex) {
|
|
900
|
-
return [];
|
|
901
|
-
}
|
|
902
|
-
const shouldReverse = collidingSegmentIndex === lastSegmentIndex;
|
|
903
|
-
const orderedPath = shouldReverse ? [...path].reverse() : path;
|
|
904
|
-
const [start, corner, end] = orderedPath;
|
|
905
|
-
const firstSegmentAxis = getSegmentAxis(start, corner);
|
|
906
|
-
const secondSegmentAxis = getSegmentAxis(corner, end);
|
|
907
|
-
if (!firstSegmentAxis || !secondSegmentAxis) return [];
|
|
908
|
-
if (firstSegmentAxis === secondSegmentAxis) return [];
|
|
909
|
-
const escapeCoordinates = [
|
|
910
|
-
...midBetweenPointAndRect(secondSegmentAxis, start, obstacle),
|
|
911
|
-
...midBetweenPointAndRect(secondSegmentAxis, end, obstacle)
|
|
912
|
-
];
|
|
913
|
-
const detourCoordinates = [
|
|
914
|
-
...midBetweenPointAndRect(firstSegmentAxis, start, obstacle),
|
|
915
|
-
...midBetweenPointAndRect(firstSegmentAxis, end, obstacle)
|
|
916
|
-
];
|
|
917
|
-
const detours = [];
|
|
918
|
-
for (const escapeCoordinate of [...new Set(escapeCoordinates)]) {
|
|
919
|
-
for (const detourCoordinate of [...new Set(detourCoordinates)]) {
|
|
920
|
-
const orderedDetourPrefix = firstSegmentAxis === "y" ? [
|
|
921
|
-
start,
|
|
922
|
-
{ x: escapeCoordinate, y: start.y },
|
|
923
|
-
{ x: escapeCoordinate, y: detourCoordinate },
|
|
924
|
-
{ x: end.x, y: detourCoordinate },
|
|
925
|
-
end
|
|
926
|
-
] : [
|
|
927
|
-
start,
|
|
928
|
-
{ x: start.x, y: escapeCoordinate },
|
|
929
|
-
{ x: detourCoordinate, y: escapeCoordinate },
|
|
930
|
-
{ x: detourCoordinate, y: end.y },
|
|
931
|
-
end
|
|
932
|
-
];
|
|
933
|
-
const orderedDetour = [...orderedDetourPrefix, ...orderedPath.slice(3)];
|
|
934
|
-
const detour = shouldReverse ? orderedDetour.reverse() : orderedDetour;
|
|
935
|
-
if (hasOnlyNonzeroOrthogonalSegments(detour)) detours.push(detour);
|
|
936
|
-
}
|
|
937
|
-
}
|
|
938
|
-
return detours;
|
|
939
|
-
};
|
|
940
|
-
|
|
941
|
-
// lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/pathOps.ts
|
|
942
|
-
var EPS3 = 1e-9;
|
|
943
907
|
var shiftSegmentOrth = (pts, segIndex, axis, newCoord, eps = EPS3) => {
|
|
944
908
|
if (segIndex < 0 || segIndex >= pts.length - 1) {
|
|
945
909
|
return null;
|
|
@@ -1001,6 +965,121 @@ var pathKey = (pts, decimals = 6) => {
|
|
|
1001
965
|
return key;
|
|
1002
966
|
};
|
|
1003
967
|
|
|
968
|
+
// lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/generateEndpointCollisionDetours.ts
|
|
969
|
+
var getSegmentAxis = (start, end) => {
|
|
970
|
+
if (isVertical(start, end)) return "x";
|
|
971
|
+
if (isHorizontal(start, end)) return "y";
|
|
972
|
+
return null;
|
|
973
|
+
};
|
|
974
|
+
var generateEndpointCollisionDetours = ({
|
|
975
|
+
path,
|
|
976
|
+
collidingSegmentIndex,
|
|
977
|
+
obstacle
|
|
978
|
+
}) => {
|
|
979
|
+
if (path.length < 3 || path.length > 4) return [];
|
|
980
|
+
const lastSegmentIndex = path.length - 2;
|
|
981
|
+
if (collidingSegmentIndex !== 0 && collidingSegmentIndex !== lastSegmentIndex) {
|
|
982
|
+
return [];
|
|
983
|
+
}
|
|
984
|
+
const shouldReverse = collidingSegmentIndex === lastSegmentIndex;
|
|
985
|
+
const orderedPath = shouldReverse ? [...path].reverse() : path;
|
|
986
|
+
const [start, corner, end] = orderedPath;
|
|
987
|
+
const firstSegmentAxis = getSegmentAxis(start, corner);
|
|
988
|
+
const secondSegmentAxis = getSegmentAxis(corner, end);
|
|
989
|
+
if (!firstSegmentAxis || !secondSegmentAxis) return [];
|
|
990
|
+
if (firstSegmentAxis === secondSegmentAxis) return [];
|
|
991
|
+
const escapeCoordinates = [
|
|
992
|
+
...midBetweenPointAndRect(secondSegmentAxis, start, obstacle),
|
|
993
|
+
...midBetweenPointAndRect(secondSegmentAxis, end, obstacle)
|
|
994
|
+
];
|
|
995
|
+
const detourCoordinates = [
|
|
996
|
+
...midBetweenPointAndRect(firstSegmentAxis, start, obstacle),
|
|
997
|
+
...midBetweenPointAndRect(firstSegmentAxis, end, obstacle)
|
|
998
|
+
];
|
|
999
|
+
const detours = [];
|
|
1000
|
+
for (const escapeCoordinate of [...new Set(escapeCoordinates)]) {
|
|
1001
|
+
for (const detourCoordinate of [...new Set(detourCoordinates)]) {
|
|
1002
|
+
const orderedDetourPrefix = firstSegmentAxis === "y" ? [
|
|
1003
|
+
start,
|
|
1004
|
+
{ x: escapeCoordinate, y: start.y },
|
|
1005
|
+
{ x: escapeCoordinate, y: detourCoordinate },
|
|
1006
|
+
{ x: end.x, y: detourCoordinate },
|
|
1007
|
+
end
|
|
1008
|
+
] : [
|
|
1009
|
+
start,
|
|
1010
|
+
{ x: start.x, y: escapeCoordinate },
|
|
1011
|
+
{ x: detourCoordinate, y: escapeCoordinate },
|
|
1012
|
+
{ x: detourCoordinate, y: end.y },
|
|
1013
|
+
end
|
|
1014
|
+
];
|
|
1015
|
+
const orderedDetour = [...orderedDetourPrefix, ...orderedPath.slice(3)];
|
|
1016
|
+
const detour = shouldReverse ? orderedDetour.reverse() : orderedDetour;
|
|
1017
|
+
if (hasOnlyNonzeroOrthogonalSegments(detour)) detours.push(detour);
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
return detours;
|
|
1021
|
+
};
|
|
1022
|
+
|
|
1023
|
+
// lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/generateInternalSegmentCollisionDetours.ts
|
|
1024
|
+
var EPS4 = 1e-9;
|
|
1025
|
+
var DEFAULT_CLEARANCE = 0.2;
|
|
1026
|
+
var isStrictlyBetween = (value, a, b) => value > Math.min(a, b) + EPS4 && value < Math.max(a, b) - EPS4;
|
|
1027
|
+
var generateInternalSegmentCollisionDetours = ({
|
|
1028
|
+
path,
|
|
1029
|
+
collidingSegmentIndex,
|
|
1030
|
+
obstacle,
|
|
1031
|
+
clearance = DEFAULT_CLEARANCE
|
|
1032
|
+
}) => {
|
|
1033
|
+
if (collidingSegmentIndex <= 0 || collidingSegmentIndex >= path.length - 2) {
|
|
1034
|
+
return [];
|
|
1035
|
+
}
|
|
1036
|
+
const start = path[collidingSegmentIndex];
|
|
1037
|
+
const end = path[collidingSegmentIndex + 1];
|
|
1038
|
+
const detourPoints = [];
|
|
1039
|
+
if (isHorizontal(start, end)) {
|
|
1040
|
+
const movingRight = start.x < end.x;
|
|
1041
|
+
const entryX = movingRight ? obstacle.minX - clearance : obstacle.maxX + clearance;
|
|
1042
|
+
const exitX = movingRight ? obstacle.maxX + clearance : obstacle.minX - clearance;
|
|
1043
|
+
if (!isStrictlyBetween(entryX, start.x, end.x) || !isStrictlyBetween(exitX, start.x, end.x)) {
|
|
1044
|
+
return [];
|
|
1045
|
+
}
|
|
1046
|
+
for (const detourY of [
|
|
1047
|
+
obstacle.minY - clearance,
|
|
1048
|
+
obstacle.maxY + clearance
|
|
1049
|
+
]) {
|
|
1050
|
+
detourPoints.push([
|
|
1051
|
+
{ x: entryX, y: start.y },
|
|
1052
|
+
{ x: entryX, y: detourY },
|
|
1053
|
+
{ x: exitX, y: detourY },
|
|
1054
|
+
{ x: exitX, y: end.y }
|
|
1055
|
+
]);
|
|
1056
|
+
}
|
|
1057
|
+
} else if (isVertical(start, end)) {
|
|
1058
|
+
const movingUp = start.y < end.y;
|
|
1059
|
+
const entryY = movingUp ? obstacle.minY - clearance : obstacle.maxY + clearance;
|
|
1060
|
+
const exitY = movingUp ? obstacle.maxY + clearance : obstacle.minY - clearance;
|
|
1061
|
+
if (!isStrictlyBetween(entryY, start.y, end.y) || !isStrictlyBetween(exitY, start.y, end.y)) {
|
|
1062
|
+
return [];
|
|
1063
|
+
}
|
|
1064
|
+
for (const detourX of [
|
|
1065
|
+
obstacle.minX - clearance,
|
|
1066
|
+
obstacle.maxX + clearance
|
|
1067
|
+
]) {
|
|
1068
|
+
detourPoints.push([
|
|
1069
|
+
{ x: start.x, y: entryY },
|
|
1070
|
+
{ x: detourX, y: entryY },
|
|
1071
|
+
{ x: detourX, y: exitY },
|
|
1072
|
+
{ x: end.x, y: exitY }
|
|
1073
|
+
]);
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
return detourPoints.map((points) => [
|
|
1077
|
+
...path.slice(0, collidingSegmentIndex + 1),
|
|
1078
|
+
...points,
|
|
1079
|
+
...path.slice(collidingSegmentIndex + 1)
|
|
1080
|
+
]).filter(hasOnlyNonzeroOrthogonalSegments);
|
|
1081
|
+
};
|
|
1082
|
+
|
|
1004
1083
|
// lib/solvers/GuidelinesSolver/getInputChipBounds.ts
|
|
1005
1084
|
function getInputChipBounds(chip) {
|
|
1006
1085
|
const halfWidth = chip.width / 2;
|
|
@@ -1013,26 +1092,8 @@ function getInputChipBounds(chip) {
|
|
|
1013
1092
|
};
|
|
1014
1093
|
}
|
|
1015
1094
|
|
|
1016
|
-
// lib/utils/textBoxBounds.ts
|
|
1017
|
-
function getTextBoxBounds(textBox, padding = {}) {
|
|
1018
|
-
return {
|
|
1019
|
-
minX: textBox.center.x - textBox.width / 2 - (padding.minX ?? 0),
|
|
1020
|
-
minY: textBox.center.y - textBox.height / 2 - (padding.minY ?? 0),
|
|
1021
|
-
maxX: textBox.center.x + textBox.width / 2 + (padding.maxX ?? 0),
|
|
1022
|
-
maxY: textBox.center.y + textBox.height / 2 + (padding.maxY ?? 0)
|
|
1023
|
-
};
|
|
1024
|
-
}
|
|
1025
|
-
function boundsOverlap(a, b, eps = 1e-9) {
|
|
1026
|
-
return a.minX < b.maxX - eps && a.maxX > b.minX + eps && a.minY < b.maxY - eps && a.maxY > b.minY + eps;
|
|
1027
|
-
}
|
|
1028
|
-
function rectIntersectsAnyTextBox(bounds, inputProblem) {
|
|
1029
|
-
for (const textBox of inputProblem.textBoxes ?? []) {
|
|
1030
|
-
if (boundsOverlap(bounds, getTextBoxBounds(textBox))) return true;
|
|
1031
|
-
}
|
|
1032
|
-
return false;
|
|
1033
|
-
}
|
|
1034
|
-
|
|
1035
1095
|
// lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/rect.ts
|
|
1096
|
+
var isTextBoxObstacle = (obstacle) => obstacle.kind === "text_box";
|
|
1036
1097
|
var chipToRect = (chip) => {
|
|
1037
1098
|
const b = getInputChipBounds(chip);
|
|
1038
1099
|
return { kind: "chip", chipId: chip.chipId, ...b };
|
|
@@ -1096,13 +1157,11 @@ var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
|
|
|
1096
1157
|
this.obstacles = getObstacleRects(this.inputProblem, {
|
|
1097
1158
|
textBoxPadding: this.getTextBoxPaddingForConnectionPair()
|
|
1098
1159
|
});
|
|
1099
|
-
this.textObstacles = new Set(
|
|
1100
|
-
this.obstacles.filter((r) => r.kind === "text_box")
|
|
1101
|
-
);
|
|
1160
|
+
this.textObstacles = new Set(this.obstacles.filter(isTextBoxObstacle));
|
|
1102
1161
|
const endpointChipIds = new Set(this.pins.map((pin) => pin.chipId));
|
|
1103
1162
|
this.endpointTextObstacles = new Set(
|
|
1104
|
-
endpointChipIds.size > 1 ? this.obstacles.filter(
|
|
1105
|
-
(
|
|
1163
|
+
endpointChipIds.size > 1 ? this.obstacles.filter(isTextBoxObstacle).filter(
|
|
1164
|
+
(obstacle) => obstacle.textBox.chipId !== void 0 && endpointChipIds.has(obstacle.textBox.chipId)
|
|
1106
1165
|
) : []
|
|
1107
1166
|
);
|
|
1108
1167
|
const [pin1, pin2] = this.pins;
|
|
@@ -1290,7 +1349,10 @@ var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
|
|
|
1290
1349
|
path[adjacentEndpointSegIndex + 1]
|
|
1291
1350
|
)) {
|
|
1292
1351
|
for (const textObstacle of this.endpointTextObstacles) {
|
|
1293
|
-
|
|
1352
|
+
const textBounds = getTextBoxBounds(textObstacle.textBox);
|
|
1353
|
+
if (!segmentIntersectsRect(segmentStart, segmentEnd, textBounds)) {
|
|
1354
|
+
excludedRects.add(textObstacle);
|
|
1355
|
+
}
|
|
1294
1356
|
}
|
|
1295
1357
|
}
|
|
1296
1358
|
return excludedRects;
|
|
@@ -1299,8 +1361,8 @@ var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
|
|
|
1299
1361
|
if (!collision) {
|
|
1300
1362
|
const first = path[0];
|
|
1301
1363
|
const last = path[path.length - 1];
|
|
1302
|
-
const
|
|
1303
|
-
const samePoint = (p, q) => Math.abs(p.x - q.x) <
|
|
1364
|
+
const EPS13 = 1e-9;
|
|
1365
|
+
const samePoint = (p, q) => Math.abs(p.x - q.x) < EPS13 && Math.abs(p.y - q.y) < EPS13;
|
|
1304
1366
|
if (samePoint(first, { x: PA.x, y: PA.y }) && samePoint(last, { x: PB.x, y: PB.y })) {
|
|
1305
1367
|
this.solvedTracePath = path;
|
|
1306
1368
|
this.solved = true;
|
|
@@ -1373,14 +1435,7 @@ var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
|
|
|
1373
1435
|
candidates.push(...mids);
|
|
1374
1436
|
}
|
|
1375
1437
|
const newStates = [];
|
|
1376
|
-
const
|
|
1377
|
-
const newPath = shiftSegmentOrth(
|
|
1378
|
-
path,
|
|
1379
|
-
candidateSegIndex,
|
|
1380
|
-
candidateAxis,
|
|
1381
|
-
coord
|
|
1382
|
-
);
|
|
1383
|
-
if (!newPath) return;
|
|
1438
|
+
const addPathCandidate = (newPath) => {
|
|
1384
1439
|
const key = pathKey(newPath);
|
|
1385
1440
|
if (this.visited.has(key)) return;
|
|
1386
1441
|
this.visited.add(key);
|
|
@@ -1393,10 +1448,28 @@ var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
|
|
|
1393
1448
|
pinBandPenalty: this.getPinBandPenalty(newPath)
|
|
1394
1449
|
});
|
|
1395
1450
|
};
|
|
1451
|
+
const addShiftedCandidate = (candidateSegIndex, candidateAxis, coord) => {
|
|
1452
|
+
const newPath = shiftSegmentOrth(
|
|
1453
|
+
path,
|
|
1454
|
+
candidateSegIndex,
|
|
1455
|
+
candidateAxis,
|
|
1456
|
+
coord
|
|
1457
|
+
);
|
|
1458
|
+
if (newPath) addPathCandidate(newPath);
|
|
1459
|
+
};
|
|
1460
|
+
const lastSegIndex = path.length - 2;
|
|
1461
|
+
if (this.connectionPair === void 0 && rect.kind === "text_box" && this.endpointTextObstacles.has(rect) && !collisionRects.has(rect) && originalSegIndex > 0 && originalSegIndex < lastSegIndex) {
|
|
1462
|
+
for (const detour of generateInternalSegmentCollisionDetours({
|
|
1463
|
+
path,
|
|
1464
|
+
collidingSegmentIndex: originalSegIndex,
|
|
1465
|
+
obstacle: rect
|
|
1466
|
+
})) {
|
|
1467
|
+
addPathCandidate(detour);
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1396
1470
|
for (const coord of candidates) {
|
|
1397
1471
|
addShiftedCandidate(segIndex, axis, coord);
|
|
1398
1472
|
}
|
|
1399
|
-
const lastSegIndex = path.length - 2;
|
|
1400
1473
|
const adjacentSegmentIndexes = originalSegIndex === 1 ? [2] : originalSegIndex === lastSegIndex - 1 ? [lastSegIndex - 2] : [];
|
|
1401
1474
|
for (const adjacentSegIndex of adjacentSegmentIndexes) {
|
|
1402
1475
|
if (adjacentSegIndex < 0 || adjacentSegIndex >= lastSegIndex) continue;
|
|
@@ -1573,13 +1646,13 @@ var applyJogToTerminalSegment = ({
|
|
|
1573
1646
|
segmentIndex: si,
|
|
1574
1647
|
offset,
|
|
1575
1648
|
JOG_SIZE,
|
|
1576
|
-
EPS:
|
|
1649
|
+
EPS: EPS13 = 1e-6
|
|
1577
1650
|
}) => {
|
|
1578
1651
|
if (si !== 0 && si !== pts.length - 2) return;
|
|
1579
1652
|
const start = pts[si];
|
|
1580
1653
|
const end = pts[si + 1];
|
|
1581
|
-
const isVertical5 = Math.abs(start.x - end.x) <
|
|
1582
|
-
const isHorizontal4 = Math.abs(start.y - end.y) <
|
|
1654
|
+
const isVertical5 = Math.abs(start.x - end.x) < EPS13;
|
|
1655
|
+
const isHorizontal4 = Math.abs(start.y - end.y) < EPS13;
|
|
1583
1656
|
if (!isVertical5 && !isHorizontal4) return;
|
|
1584
1657
|
const segDir = isVertical5 ? end.y > start.y ? 1 : -1 : end.x > start.x ? 1 : -1;
|
|
1585
1658
|
if (pts.length === 2) {
|
|
@@ -1652,7 +1725,7 @@ var applyJogToTerminalSegment = ({
|
|
|
1652
1725
|
};
|
|
1653
1726
|
|
|
1654
1727
|
// lib/solvers/TraceOverlapShiftSolver/TraceOverlapIssueSolver/TraceOverlapIssueSolver.ts
|
|
1655
|
-
var
|
|
1728
|
+
var EPS5 = 1e-6;
|
|
1656
1729
|
var TraceOverlapIssueSolver = class extends BaseSolver {
|
|
1657
1730
|
overlappingTraceSegments;
|
|
1658
1731
|
interactionKind;
|
|
@@ -1741,7 +1814,7 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
|
|
|
1741
1814
|
}
|
|
1742
1815
|
applyOffsets(offsets) {
|
|
1743
1816
|
const correctedTraceMap = { ...this.correctedTraceMap };
|
|
1744
|
-
const eq = (a, b) => Math.abs(a - b) <
|
|
1817
|
+
const eq = (a, b) => Math.abs(a - b) < EPS5;
|
|
1745
1818
|
const samePoint = (p, q) => !!p && !!q && eq(p.x, q.x) && eq(p.y, q.y);
|
|
1746
1819
|
this.overlappingTraceSegments.forEach((group, groupIndex) => {
|
|
1747
1820
|
const offset = offsets[groupIndex];
|
|
@@ -1763,9 +1836,9 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
|
|
|
1763
1836
|
const start = pts[si];
|
|
1764
1837
|
const end = pts[si + 1];
|
|
1765
1838
|
if (!start || !end) continue;
|
|
1766
|
-
if (Math.abs(start.x - end.x) <
|
|
1839
|
+
if (Math.abs(start.x - end.x) < EPS5) {
|
|
1767
1840
|
segmentAxes.set(si, "vertical");
|
|
1768
|
-
} else if (Math.abs(start.y - end.y) <
|
|
1841
|
+
} else if (Math.abs(start.y - end.y) < EPS5) {
|
|
1769
1842
|
segmentAxes.set(si, "horizontal");
|
|
1770
1843
|
}
|
|
1771
1844
|
}
|
|
@@ -1780,7 +1853,7 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
|
|
|
1780
1853
|
segmentIndex: si,
|
|
1781
1854
|
offset,
|
|
1782
1855
|
JOG_SIZE,
|
|
1783
|
-
EPS:
|
|
1856
|
+
EPS: EPS5
|
|
1784
1857
|
});
|
|
1785
1858
|
} else {
|
|
1786
1859
|
const start = pts[si];
|
|
@@ -1867,7 +1940,7 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
|
|
|
1867
1940
|
max = obstacle.maxY;
|
|
1868
1941
|
}
|
|
1869
1942
|
const next = orig + candidateOffset;
|
|
1870
|
-
return orig <= min +
|
|
1943
|
+
return orig <= min + EPS5 && next >= max - EPS5 || orig >= max - EPS5 && next <= min + EPS5;
|
|
1871
1944
|
});
|
|
1872
1945
|
const edges = blockingCollisions.map(({ start, isVertical: isVertical5, obstacle }) => {
|
|
1873
1946
|
if (isVertical5) {
|
|
@@ -1904,8 +1977,8 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
|
|
|
1904
1977
|
const start = trace.tracePath[traceSegmentIndex];
|
|
1905
1978
|
const end = trace.tracePath[traceSegmentIndex + 1];
|
|
1906
1979
|
if (!start || !end) continue;
|
|
1907
|
-
const isVertical5 = Math.abs(start.x - end.x) <
|
|
1908
|
-
const isHorizontal4 = Math.abs(start.y - end.y) <
|
|
1980
|
+
const isVertical5 = Math.abs(start.x - end.x) < EPS5;
|
|
1981
|
+
const isHorizontal4 = Math.abs(start.y - end.y) < EPS5;
|
|
1909
1982
|
if (!isVertical5 && !isHorizontal4) continue;
|
|
1910
1983
|
const shiftedSegment = isVertical5 ? [
|
|
1911
1984
|
{ ...start, x: start.x + offset },
|
|
@@ -1997,7 +2070,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
1997
2070
|
return islands;
|
|
1998
2071
|
}
|
|
1999
2072
|
findNextOverlapIssue() {
|
|
2000
|
-
const
|
|
2073
|
+
const EPS13 = 2e-3;
|
|
2001
2074
|
const netIds = Object.keys(this.traceNetIslands);
|
|
2002
2075
|
for (let i = 0; i < netIds.length; i++) {
|
|
2003
2076
|
for (let j = i + 1; j < netIds.length; j++) {
|
|
@@ -2024,7 +2097,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
2024
2097
|
segmentBIndex,
|
|
2025
2098
|
rangeOverlap
|
|
2026
2099
|
}) => {
|
|
2027
|
-
if (rangeOverlap >
|
|
2100
|
+
if (rangeOverlap > EPS13) {
|
|
2028
2101
|
const keyA = `${pathAIndex}:${segmentAIndex}`;
|
|
2029
2102
|
const keyB = `${pathBIndex}:${segmentBIndex}`;
|
|
2030
2103
|
if (!seenA.has(keyA)) {
|
|
@@ -2043,7 +2116,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
2043
2116
|
}
|
|
2044
2117
|
return;
|
|
2045
2118
|
}
|
|
2046
|
-
if (rangeOverlap < -
|
|
2119
|
+
if (rangeOverlap < -EPS13) return;
|
|
2047
2120
|
const pathA = pathsA[pathAIndex].tracePath;
|
|
2048
2121
|
const pathB = pathsB[pathBIndex].tracePath;
|
|
2049
2122
|
const segmentAStart = pathA[segmentAIndex];
|
|
@@ -2066,8 +2139,8 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
2066
2139
|
for (let sa = 0; sa < ptsA.length - 1; sa++) {
|
|
2067
2140
|
const a1 = ptsA[sa];
|
|
2068
2141
|
const a2 = ptsA[sa + 1];
|
|
2069
|
-
const aVert = Math.abs(a1.x - a2.x) <
|
|
2070
|
-
const aHorz = Math.abs(a1.y - a2.y) <
|
|
2142
|
+
const aVert = Math.abs(a1.x - a2.x) < EPS13;
|
|
2143
|
+
const aHorz = Math.abs(a1.y - a2.y) < EPS13;
|
|
2071
2144
|
if (!aVert && !aHorz) continue;
|
|
2072
2145
|
for (let pb = 0; pb < pathsB.length; pb++) {
|
|
2073
2146
|
const pathB = pathsB[pb];
|
|
@@ -2075,11 +2148,11 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
2075
2148
|
for (let sb = 0; sb < ptsB.length - 1; sb++) {
|
|
2076
2149
|
const b1 = ptsB[sb];
|
|
2077
2150
|
const b2 = ptsB[sb + 1];
|
|
2078
|
-
const bVert = Math.abs(b1.x - b2.x) <
|
|
2079
|
-
const bHorz = Math.abs(b1.y - b2.y) <
|
|
2151
|
+
const bVert = Math.abs(b1.x - b2.x) < EPS13;
|
|
2152
|
+
const bHorz = Math.abs(b1.y - b2.y) < EPS13;
|
|
2080
2153
|
if (!bVert && !bHorz) continue;
|
|
2081
2154
|
if (aVert && bVert) {
|
|
2082
|
-
if (Math.abs(a1.x - b1.x) <
|
|
2155
|
+
if (Math.abs(a1.x - b1.x) < EPS13) {
|
|
2083
2156
|
recordCollinearInteraction({
|
|
2084
2157
|
pathAIndex: pa,
|
|
2085
2158
|
segmentAIndex: sa,
|
|
@@ -2089,7 +2162,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
2089
2162
|
});
|
|
2090
2163
|
}
|
|
2091
2164
|
} else if (aHorz && bHorz) {
|
|
2092
|
-
if (Math.abs(a1.y - b1.y) <
|
|
2165
|
+
if (Math.abs(a1.y - b1.y) < EPS13) {
|
|
2093
2166
|
recordCollinearInteraction({
|
|
2094
2167
|
pathAIndex: pa,
|
|
2095
2168
|
segmentAIndex: sa,
|
|
@@ -2145,14 +2218,14 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
2145
2218
|
return null;
|
|
2146
2219
|
}
|
|
2147
2220
|
findNextDiagonalSegment() {
|
|
2148
|
-
const
|
|
2221
|
+
const EPS13 = 2e-3;
|
|
2149
2222
|
for (const mspPairId in this.correctedTraceMap) {
|
|
2150
2223
|
const tracePath = this.correctedTraceMap[mspPairId].tracePath;
|
|
2151
2224
|
for (let i = 0; i < tracePath.length - 1; i++) {
|
|
2152
2225
|
const p1 = tracePath[i];
|
|
2153
2226
|
const p2 = tracePath[i + 1];
|
|
2154
|
-
const isHorizontal4 = Math.abs(p1.y - p2.y) <
|
|
2155
|
-
const isVertical5 = Math.abs(p1.x - p2.x) <
|
|
2227
|
+
const isHorizontal4 = Math.abs(p1.y - p2.y) < EPS13;
|
|
2228
|
+
const isVertical5 = Math.abs(p1.x - p2.x) < EPS13;
|
|
2156
2229
|
if (!isHorizontal4 && !isVertical5) {
|
|
2157
2230
|
return { mspPairId, tracePath, i, p1, p2 };
|
|
2158
2231
|
}
|
|
@@ -2166,13 +2239,13 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
2166
2239
|
return false;
|
|
2167
2240
|
}
|
|
2168
2241
|
const { mspPairId, tracePath, i, p1, p2 } = diagonalInfo;
|
|
2169
|
-
const
|
|
2242
|
+
const EPS13 = 2e-3;
|
|
2170
2243
|
const p0 = i > 0 ? tracePath[i - 1] : null;
|
|
2171
2244
|
const p3 = i + 2 < tracePath.length ? tracePath[i + 2] : null;
|
|
2172
|
-
const prevIsVertical = p0 ? Math.abs(p0.x - p1.x) <
|
|
2173
|
-
const prevIsHorizontal = p0 ? Math.abs(p0.y - p1.y) <
|
|
2174
|
-
const nextIsVertical = p3 ? Math.abs(p2.x - p3.x) <
|
|
2175
|
-
const nextIsHorizontal = p3 ? Math.abs(p2.y - p3.y) <
|
|
2245
|
+
const prevIsVertical = p0 ? Math.abs(p0.x - p1.x) < EPS13 : false;
|
|
2246
|
+
const prevIsHorizontal = p0 ? Math.abs(p0.y - p1.y) < EPS13 : false;
|
|
2247
|
+
const nextIsVertical = p3 ? Math.abs(p2.x - p3.x) < EPS13 : false;
|
|
2248
|
+
const nextIsHorizontal = p3 ? Math.abs(p2.y - p3.y) < EPS13 : false;
|
|
2176
2249
|
const elbow1 = { x: p1.x, y: p2.y };
|
|
2177
2250
|
const elbow2 = { x: p2.x, y: p1.y };
|
|
2178
2251
|
let score1 = 0;
|
|
@@ -2299,24 +2372,24 @@ var ChipObstacleSpatialIndex = class {
|
|
|
2299
2372
|
};
|
|
2300
2373
|
|
|
2301
2374
|
// lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/collisions.ts
|
|
2302
|
-
function segmentIntersectsRect2(p1, p2, rect,
|
|
2303
|
-
const isVert = Math.abs(p1.x - p2.x) <
|
|
2304
|
-
const isHorz = Math.abs(p1.y - p2.y) <
|
|
2375
|
+
function segmentIntersectsRect2(p1, p2, rect, EPS13 = 1e-9) {
|
|
2376
|
+
const isVert = Math.abs(p1.x - p2.x) < EPS13;
|
|
2377
|
+
const isHorz = Math.abs(p1.y - p2.y) < EPS13;
|
|
2305
2378
|
if (!isVert && !isHorz) return false;
|
|
2306
2379
|
if (isVert) {
|
|
2307
2380
|
const x = p1.x;
|
|
2308
|
-
if (x < rect.minX -
|
|
2381
|
+
if (x < rect.minX - EPS13 || x > rect.maxX + EPS13) return false;
|
|
2309
2382
|
const segMinY = Math.min(p1.y, p2.y);
|
|
2310
2383
|
const segMaxY = Math.max(p1.y, p2.y);
|
|
2311
2384
|
const overlap = Math.min(segMaxY, rect.maxY) - Math.max(segMinY, rect.minY);
|
|
2312
|
-
return overlap >
|
|
2385
|
+
return overlap > EPS13;
|
|
2313
2386
|
} else {
|
|
2314
2387
|
const y = p1.y;
|
|
2315
|
-
if (y < rect.minY -
|
|
2388
|
+
if (y < rect.minY - EPS13 || y > rect.maxY + EPS13) return false;
|
|
2316
2389
|
const segMinX = Math.min(p1.x, p2.x);
|
|
2317
2390
|
const segMaxX = Math.max(p1.x, p2.x);
|
|
2318
2391
|
const overlap = Math.min(segMaxX, rect.maxX) - Math.max(segMinX, rect.minX);
|
|
2319
|
-
return overlap >
|
|
2392
|
+
return overlap > EPS13;
|
|
2320
2393
|
}
|
|
2321
2394
|
}
|
|
2322
2395
|
function rectIntersectsAnyTrace(bounds, inputTraceMap, hostPathId, hostSegIndex) {
|
|
@@ -2713,14 +2786,14 @@ var SingleNetLabelPlacementSolver = class extends BaseSolver {
|
|
|
2713
2786
|
};
|
|
2714
2787
|
let bestCandidate = null;
|
|
2715
2788
|
let bestScore = -Infinity;
|
|
2716
|
-
const
|
|
2789
|
+
const EPS13 = 1e-6;
|
|
2717
2790
|
for (const curr of tracesToScan) {
|
|
2718
2791
|
const pts = curr.tracePath.slice();
|
|
2719
2792
|
for (let si = 0; si < pts.length - 1; si++) {
|
|
2720
2793
|
const a = pts[si];
|
|
2721
2794
|
const b = pts[si + 1];
|
|
2722
|
-
const isH = Math.abs(a.y - b.y) <
|
|
2723
|
-
const isV = Math.abs(a.x - b.x) <
|
|
2795
|
+
const isH = Math.abs(a.y - b.y) < EPS13;
|
|
2796
|
+
const isV = Math.abs(a.x - b.x) < EPS13;
|
|
2724
2797
|
if (!isH && !isV) continue;
|
|
2725
2798
|
const segmentAllowed = isH ? ["y+", "y-"] : ["x+", "x-"];
|
|
2726
2799
|
const candidateOrients = orientations.filter(
|
|
@@ -5123,9 +5196,9 @@ var moveRailSegments = (trace, segments, coordinate) => {
|
|
|
5123
5196
|
};
|
|
5124
5197
|
|
|
5125
5198
|
// lib/solvers/RailNetLabelCornerPlacementSolver/geometry.ts
|
|
5126
|
-
var
|
|
5199
|
+
var EPS6 = 1e-6;
|
|
5127
5200
|
var isTraceLine = (trace) => getUniquePinCount(trace.pinIds) >= 2;
|
|
5128
|
-
var rectsOverlap = (a, b) => Math.min(a.maxX, b.maxX) - Math.max(a.minX, b.minX) >
|
|
5201
|
+
var rectsOverlap = (a, b) => Math.min(a.maxX, b.maxX) - Math.max(a.minX, b.minX) > EPS6 && Math.min(a.maxY, b.maxY) - Math.max(a.minY, b.minY) > EPS6;
|
|
5129
5202
|
var getTraceCorners = (path) => {
|
|
5130
5203
|
const corners = [];
|
|
5131
5204
|
for (let i = 1; i < path.length - 1; i++) {
|
|
@@ -5149,11 +5222,11 @@ var getUniquePinCount = (pinIds) => new Set(pinIds).size;
|
|
|
5149
5222
|
var isTraceCorner = (a, b, c) => getSegmentOrientation(a, b) !== getSegmentOrientation(b, c);
|
|
5150
5223
|
var isPointOnSegment = (point, start, end) => {
|
|
5151
5224
|
if (getSegmentOrientation(start, end) === "horizontal") {
|
|
5152
|
-
return Math.abs(point.y - start.y) <=
|
|
5225
|
+
return Math.abs(point.y - start.y) <= EPS6 && point.x >= Math.min(start.x, end.x) - EPS6 && point.x <= Math.max(start.x, end.x) + EPS6;
|
|
5153
5226
|
}
|
|
5154
|
-
return Math.abs(point.x - start.x) <=
|
|
5227
|
+
return Math.abs(point.x - start.x) <= EPS6 && point.y >= Math.min(start.y, end.y) - EPS6 && point.y <= Math.max(start.y, end.y) + EPS6;
|
|
5155
5228
|
};
|
|
5156
|
-
var getSegmentOrientation = (a, b) => Math.abs(a.y - b.y) <=
|
|
5229
|
+
var getSegmentOrientation = (a, b) => Math.abs(a.y - b.y) <= EPS6 ? "horizontal" : "vertical";
|
|
5157
5230
|
|
|
5158
5231
|
// lib/solvers/TraceCleanupSolver/sameNetRailAlignment/preservesLabelAnchors.ts
|
|
5159
5232
|
var getAnchoredTraceIds = (label, traces) => new Set(
|
|
@@ -5169,17 +5242,17 @@ var preservesLabelAnchors = (labels, before, after) => labels.every((label) => {
|
|
|
5169
5242
|
});
|
|
5170
5243
|
|
|
5171
5244
|
// lib/solvers/Example28Solver/types.ts
|
|
5172
|
-
var
|
|
5245
|
+
var EPS7 = 1e-9;
|
|
5173
5246
|
|
|
5174
5247
|
// lib/solvers/Example28Solver/segmentRunsAlongRectBoundary.ts
|
|
5175
5248
|
var segmentRunsAlongRectBoundary = (start, end, rect) => {
|
|
5176
|
-
const isVertical5 = Math.abs(start.x - end.x) <
|
|
5177
|
-
const isHorizontal4 = Math.abs(start.y - end.y) <
|
|
5249
|
+
const isVertical5 = Math.abs(start.x - end.x) < EPS7;
|
|
5250
|
+
const isHorizontal4 = Math.abs(start.y - end.y) < EPS7;
|
|
5178
5251
|
if (isVertical5) {
|
|
5179
|
-
return Math.abs(start.x - rect.minX) <
|
|
5252
|
+
return Math.abs(start.x - rect.minX) < EPS7 || Math.abs(start.x - rect.maxX) < EPS7;
|
|
5180
5253
|
}
|
|
5181
5254
|
if (isHorizontal4) {
|
|
5182
|
-
return Math.abs(start.y - rect.minY) <
|
|
5255
|
+
return Math.abs(start.y - rect.minY) < EPS7 || Math.abs(start.y - rect.maxY) < EPS7;
|
|
5183
5256
|
}
|
|
5184
5257
|
return false;
|
|
5185
5258
|
};
|
|
@@ -5194,10 +5267,10 @@ var getPathLength = (path) => {
|
|
|
5194
5267
|
return length;
|
|
5195
5268
|
};
|
|
5196
5269
|
var getDistance2 = (a, b) => Math.abs(a.x - b.x) + Math.abs(a.y - b.y);
|
|
5197
|
-
var isAxisAlignedSegment = (start, end) => Math.abs(start.x - end.x) <
|
|
5198
|
-
var getSegmentOrientation2 = (start, end) => Math.abs(start.y - end.y) <
|
|
5270
|
+
var isAxisAlignedSegment = (start, end) => Math.abs(start.x - end.x) < EPS7 || Math.abs(start.y - end.y) < EPS7;
|
|
5271
|
+
var getSegmentOrientation2 = (start, end) => Math.abs(start.y - end.y) < EPS7 ? "horizontal" : "vertical";
|
|
5199
5272
|
var projectPointToSegment = (point, start, end) => {
|
|
5200
|
-
if (Math.abs(start.x - end.x) <
|
|
5273
|
+
if (Math.abs(start.x - end.x) < EPS7) {
|
|
5201
5274
|
return {
|
|
5202
5275
|
x: start.x,
|
|
5203
5276
|
y: Math.min(
|
|
@@ -5206,7 +5279,7 @@ var projectPointToSegment = (point, start, end) => {
|
|
|
5206
5279
|
)
|
|
5207
5280
|
};
|
|
5208
5281
|
}
|
|
5209
|
-
if (Math.abs(start.y - end.y) <
|
|
5282
|
+
if (Math.abs(start.y - end.y) < EPS7) {
|
|
5210
5283
|
return {
|
|
5211
5284
|
x: Math.min(
|
|
5212
5285
|
Math.max(point.x, Math.min(start.x, end.x)),
|
|
@@ -5249,9 +5322,9 @@ var findSegmentContainingPoint = (path, point) => {
|
|
|
5249
5322
|
};
|
|
5250
5323
|
var isPointWithinSegmentPrimaryRange = (point, segment) => {
|
|
5251
5324
|
if (segment.orientation === "horizontal") {
|
|
5252
|
-
return point.x >= Math.min(segment.start.x, segment.end.x) -
|
|
5325
|
+
return point.x >= Math.min(segment.start.x, segment.end.x) - EPS7 && point.x <= Math.max(segment.start.x, segment.end.x) + EPS7;
|
|
5253
5326
|
}
|
|
5254
|
-
return point.y >= Math.min(segment.start.y, segment.end.y) -
|
|
5327
|
+
return point.y >= Math.min(segment.start.y, segment.end.y) - EPS7 && point.y <= Math.max(segment.start.y, segment.end.y) + EPS7;
|
|
5255
5328
|
};
|
|
5256
5329
|
var findPreferredReroutedSegment = (path, originalSegmentIndex, originalSegmentCount, orientation, anchorPoint) => {
|
|
5257
5330
|
const matchingSegments = getSegments(path).filter(
|
|
@@ -5279,18 +5352,18 @@ var projectPointToPath = (point, path) => {
|
|
|
5279
5352
|
return bestPoint;
|
|
5280
5353
|
};
|
|
5281
5354
|
var segmentsIntersect = (a1, a2, b1, b2) => {
|
|
5282
|
-
const aVertical = Math.abs(a1.x - a2.x) <
|
|
5283
|
-
const bVertical = Math.abs(b1.x - b2.x) <
|
|
5284
|
-
const between = (value, p1, p2) => value >= Math.min(p1, p2) -
|
|
5355
|
+
const aVertical = Math.abs(a1.x - a2.x) < EPS7;
|
|
5356
|
+
const bVertical = Math.abs(b1.x - b2.x) < EPS7;
|
|
5357
|
+
const between = (value, p1, p2) => value >= Math.min(p1, p2) - EPS7 && value <= Math.max(p1, p2) + EPS7;
|
|
5285
5358
|
if (aVertical && bVertical) {
|
|
5286
|
-
if (Math.abs(a1.x - b1.x) >
|
|
5359
|
+
if (Math.abs(a1.x - b1.x) > EPS7) return false;
|
|
5287
5360
|
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));
|
|
5288
|
-
return overlap >
|
|
5361
|
+
return overlap > EPS7;
|
|
5289
5362
|
}
|
|
5290
5363
|
if (!aVertical && !bVertical) {
|
|
5291
|
-
if (Math.abs(a1.y - b1.y) >
|
|
5364
|
+
if (Math.abs(a1.y - b1.y) > EPS7) return false;
|
|
5292
5365
|
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));
|
|
5293
|
-
return overlap >
|
|
5366
|
+
return overlap > EPS7;
|
|
5294
5367
|
}
|
|
5295
5368
|
const verticalA = aVertical ? a1 : b1;
|
|
5296
5369
|
const verticalB = aVertical ? a2 : b2;
|
|
@@ -5652,6 +5725,7 @@ var getTraceObstacles = (allTraces, excludeTraceId) => {
|
|
|
5652
5725
|
|
|
5653
5726
|
// lib/solvers/TraceCleanupSolver/sub-solver/findIntersectionsWithObstacles.ts
|
|
5654
5727
|
import { getSegmentIntersection } from "@tscircuit/math-utils/line-intersections";
|
|
5728
|
+
var EPS8 = 1e-6;
|
|
5655
5729
|
var findIntersectionsWithObstacles = (p1, p2, obstacles) => {
|
|
5656
5730
|
const intersections = [];
|
|
5657
5731
|
for (const obstacle of obstacles) {
|
|
@@ -5670,10 +5744,38 @@ var findIntersectionsWithObstacles = (p1, p2, obstacles) => {
|
|
|
5670
5744
|
}
|
|
5671
5745
|
return intersections;
|
|
5672
5746
|
};
|
|
5747
|
+
var isSamePoint = (first, second) => Math.abs(first.x - second.x) < EPS8 && Math.abs(first.y - second.y) < EPS8;
|
|
5748
|
+
var findPerpendicularPathCrossings = (path, otherPath) => {
|
|
5749
|
+
const crossings = [];
|
|
5750
|
+
for (let pathSegmentIndex = 1; pathSegmentIndex < path.length - 2; pathSegmentIndex++) {
|
|
5751
|
+
const start = path[pathSegmentIndex];
|
|
5752
|
+
const end = path[pathSegmentIndex + 1];
|
|
5753
|
+
const isVertical5 = Math.abs(start.x - end.x) < EPS8;
|
|
5754
|
+
for (let otherPathSegmentIndex = 1; otherPathSegmentIndex < otherPath.length - 2; otherPathSegmentIndex++) {
|
|
5755
|
+
const otherStart = otherPath[otherPathSegmentIndex];
|
|
5756
|
+
const otherEnd = otherPath[otherPathSegmentIndex + 1];
|
|
5757
|
+
const otherIsVertical = Math.abs(otherStart.x - otherEnd.x) < EPS8;
|
|
5758
|
+
if (isVertical5 === otherIsVertical) continue;
|
|
5759
|
+
const intersection = getSegmentIntersection(
|
|
5760
|
+
start,
|
|
5761
|
+
end,
|
|
5762
|
+
otherStart,
|
|
5763
|
+
otherEnd
|
|
5764
|
+
);
|
|
5765
|
+
if (!intersection || [start, end, otherStart, otherEnd].some(
|
|
5766
|
+
(point) => isSamePoint(point, intersection)
|
|
5767
|
+
)) {
|
|
5768
|
+
continue;
|
|
5769
|
+
}
|
|
5770
|
+
crossings.push({ pathSegmentIndex, otherPathSegmentIndex });
|
|
5771
|
+
}
|
|
5772
|
+
}
|
|
5773
|
+
return crossings;
|
|
5774
|
+
};
|
|
5673
5775
|
|
|
5674
5776
|
// lib/solvers/TraceCleanupSolver/sub-solver/generateLShapeRerouteCandidates.ts
|
|
5675
|
-
var
|
|
5676
|
-
var isVertical3 = (a, b, eps =
|
|
5777
|
+
var EPS9 = 1e-6;
|
|
5778
|
+
var isVertical3 = (a, b, eps = EPS9) => Math.abs(a.x - b.x) < eps;
|
|
5677
5779
|
var generateLShapeRerouteCandidates = ({
|
|
5678
5780
|
lShape,
|
|
5679
5781
|
rectangle,
|
|
@@ -5686,7 +5788,7 @@ var generateLShapeRerouteCandidates = ({
|
|
|
5686
5788
|
let c2;
|
|
5687
5789
|
let i1_padded = interactionPoint1;
|
|
5688
5790
|
let i2_padded = interactionPoint2;
|
|
5689
|
-
if (Math.abs(p2.x - x) <
|
|
5791
|
+
if (Math.abs(p2.x - x) < EPS9 && Math.abs(p2.y - (y + height)) < EPS9) {
|
|
5690
5792
|
c2 = { x: x + width + padding, y: y - padding };
|
|
5691
5793
|
if (isVertical3(p1, p2)) {
|
|
5692
5794
|
i1_padded = { x: interactionPoint1.x, y: interactionPoint1.y - padding };
|
|
@@ -5698,7 +5800,7 @@ var generateLShapeRerouteCandidates = ({
|
|
|
5698
5800
|
} else {
|
|
5699
5801
|
i2_padded = { x: interactionPoint2.x + padding, y: interactionPoint2.y };
|
|
5700
5802
|
}
|
|
5701
|
-
} else if (Math.abs(p2.x - (x + width)) <
|
|
5803
|
+
} else if (Math.abs(p2.x - (x + width)) < EPS9 && Math.abs(p2.y - (y + height)) < EPS9) {
|
|
5702
5804
|
c2 = { x: x - padding, y: y - padding };
|
|
5703
5805
|
if (isVertical3(p1, p2)) {
|
|
5704
5806
|
i1_padded = { x: interactionPoint1.x, y: interactionPoint1.y - padding };
|
|
@@ -5710,7 +5812,7 @@ var generateLShapeRerouteCandidates = ({
|
|
|
5710
5812
|
} else {
|
|
5711
5813
|
i2_padded = { x: interactionPoint2.x - padding, y: interactionPoint2.y };
|
|
5712
5814
|
}
|
|
5713
|
-
} else if (Math.abs(p2.x - x) <
|
|
5815
|
+
} else if (Math.abs(p2.x - x) < EPS9 && Math.abs(p2.y - y) < EPS9) {
|
|
5714
5816
|
c2 = { x: x + width + padding, y: y + height + padding };
|
|
5715
5817
|
if (isVertical3(p1, p2)) {
|
|
5716
5818
|
i1_padded = { x: interactionPoint1.x, y: interactionPoint1.y + padding };
|
|
@@ -5722,7 +5824,7 @@ var generateLShapeRerouteCandidates = ({
|
|
|
5722
5824
|
} else {
|
|
5723
5825
|
i2_padded = { x: interactionPoint2.x + padding, y: interactionPoint2.y };
|
|
5724
5826
|
}
|
|
5725
|
-
} else if (Math.abs(p2.x - (x + width)) <
|
|
5827
|
+
} else if (Math.abs(p2.x - (x + width)) < EPS9 && Math.abs(p2.y - y) < EPS9) {
|
|
5726
5828
|
c2 = { x: x - padding, y: y + height + padding };
|
|
5727
5829
|
if (isVertical3(p1, p2)) {
|
|
5728
5830
|
i1_padded = { x: interactionPoint1.x, y: interactionPoint1.y + padding };
|
|
@@ -5739,6 +5841,48 @@ var generateLShapeRerouteCandidates = ({
|
|
|
5739
5841
|
}
|
|
5740
5842
|
return [[i1_padded, c2, i2_padded]];
|
|
5741
5843
|
};
|
|
5844
|
+
var generatePerpendicularTraceDetours = ({
|
|
5845
|
+
trace,
|
|
5846
|
+
segmentIndex,
|
|
5847
|
+
obstacleStart,
|
|
5848
|
+
obstacleEnd,
|
|
5849
|
+
chipBounds,
|
|
5850
|
+
clearance
|
|
5851
|
+
}) => {
|
|
5852
|
+
const buildDetours = (path, index) => {
|
|
5853
|
+
const start = path[index];
|
|
5854
|
+
const end = path[index + 1];
|
|
5855
|
+
const movingAxis = Math.abs(start.x - end.x) < EPS9 ? "y" : "x";
|
|
5856
|
+
const detourAxis = movingAxis === "x" ? "y" : "x";
|
|
5857
|
+
const gate = obstacleStart[movingAxis] + Math.sign(start[movingAxis] - obstacleStart[movingAxis]) * clearance;
|
|
5858
|
+
const obstacleRange = [obstacleStart[detourAxis], obstacleEnd[detourAxis]];
|
|
5859
|
+
const lowBound = detourAxis === "x" ? "minX" : "minY";
|
|
5860
|
+
const highBound = detourAxis === "x" ? "maxX" : "maxY";
|
|
5861
|
+
const detourCoordinates = [
|
|
5862
|
+
Math.min(...obstacleRange) - clearance,
|
|
5863
|
+
Math.max(...obstacleRange) + clearance,
|
|
5864
|
+
...chipBounds.flatMap((bounds) => [
|
|
5865
|
+
bounds[lowBound] - clearance,
|
|
5866
|
+
bounds[highBound] + clearance
|
|
5867
|
+
])
|
|
5868
|
+
];
|
|
5869
|
+
return [...new Set(detourCoordinates)].map(
|
|
5870
|
+
(detour) => simplifyPath([
|
|
5871
|
+
...path.slice(0, index + 1),
|
|
5872
|
+
{ ...start, [movingAxis]: gate },
|
|
5873
|
+
{ ...start, [movingAxis]: gate, [detourAxis]: detour },
|
|
5874
|
+
{ ...end, [detourAxis]: detour },
|
|
5875
|
+
...path.slice(index + 2)
|
|
5876
|
+
])
|
|
5877
|
+
);
|
|
5878
|
+
};
|
|
5879
|
+
const reversedPath = [...trace.tracePath].reverse();
|
|
5880
|
+
const reversedIndex = trace.tracePath.length - 2 - segmentIndex;
|
|
5881
|
+
return [
|
|
5882
|
+
...buildDetours(trace.tracePath, segmentIndex),
|
|
5883
|
+
...buildDetours(reversedPath, reversedIndex).map((path) => path.reverse())
|
|
5884
|
+
].map((path) => ({ traceId: trace.mspPairId, path }));
|
|
5885
|
+
};
|
|
5742
5886
|
|
|
5743
5887
|
// lib/solvers/TraceCleanupSolver/sub-solver/isPathColliding.ts
|
|
5744
5888
|
import { getSegmentIntersection as getSegmentIntersection2 } from "@tscircuit/math-utils/line-intersections";
|
|
@@ -5913,6 +6057,9 @@ var UntangleTraceSubsolver = class extends BaseSolver {
|
|
|
5913
6057
|
input;
|
|
5914
6058
|
chipObstacleSpatialIndex;
|
|
5915
6059
|
lShapesToProcess = [];
|
|
6060
|
+
ignoredCrossings = /* @__PURE__ */ new Set();
|
|
6061
|
+
reroutedTraceIds = /* @__PURE__ */ new Set();
|
|
6062
|
+
processingCrossings = true;
|
|
5916
6063
|
visualizationMode = "l_shapes";
|
|
5917
6064
|
currentLShape = null;
|
|
5918
6065
|
intersectionPoints = [];
|
|
@@ -5936,18 +6083,24 @@ var UntangleTraceSubsolver = class extends BaseSolver {
|
|
|
5936
6083
|
if (!this.input.inputProblem._chipObstacleSpatialIndex) {
|
|
5937
6084
|
this.input.inputProblem._chipObstacleSpatialIndex = this.chipObstacleSpatialIndex;
|
|
5938
6085
|
}
|
|
5939
|
-
for (const trace of this.input.allTraces) {
|
|
5940
|
-
const lShapes = findAllLShapedTurns(trace.tracePath);
|
|
5941
|
-
this.lShapesToProcess.push(
|
|
5942
|
-
...lShapes.map((l) => ({ ...l, traceId: trace.mspPairId }))
|
|
5943
|
-
);
|
|
5944
|
-
}
|
|
5945
6086
|
}
|
|
5946
6087
|
_step() {
|
|
5947
6088
|
if (this.isInitialStep) {
|
|
5948
6089
|
this.isInitialStep = false;
|
|
5949
6090
|
return;
|
|
5950
6091
|
}
|
|
6092
|
+
if (this.processingCrossings) {
|
|
6093
|
+
const crossing = this._findCrossing();
|
|
6094
|
+
if (crossing) {
|
|
6095
|
+
if (!this._resolveCrossing(crossing)) {
|
|
6096
|
+
this.ignoredCrossings.add(this._crossingKey(crossing));
|
|
6097
|
+
}
|
|
6098
|
+
return;
|
|
6099
|
+
}
|
|
6100
|
+
this.processingCrossings = false;
|
|
6101
|
+
this._initializeLShapes();
|
|
6102
|
+
return;
|
|
6103
|
+
}
|
|
5951
6104
|
if (this.lShapeJustProcessed) {
|
|
5952
6105
|
this._resetAfterLShapProcessing();
|
|
5953
6106
|
return;
|
|
@@ -5971,6 +6124,110 @@ var UntangleTraceSubsolver = class extends BaseSolver {
|
|
|
5971
6124
|
break;
|
|
5972
6125
|
}
|
|
5973
6126
|
}
|
|
6127
|
+
_initializeLShapes() {
|
|
6128
|
+
for (const trace of this.input.allTraces) {
|
|
6129
|
+
this.lShapesToProcess.push(
|
|
6130
|
+
...findAllLShapedTurns(trace.tracePath).map((lShape) => ({
|
|
6131
|
+
...lShape,
|
|
6132
|
+
traceId: trace.mspPairId
|
|
6133
|
+
}))
|
|
6134
|
+
);
|
|
6135
|
+
}
|
|
6136
|
+
}
|
|
6137
|
+
_crossingKey(crossing) {
|
|
6138
|
+
return `${crossing.trace.mspPairId}:${crossing.segmentIndex}:${crossing.otherTrace.mspPairId}:${crossing.otherSegmentIndex}`;
|
|
6139
|
+
}
|
|
6140
|
+
_isTraceBundle(first, second) {
|
|
6141
|
+
const componentPair = (trace) => trace.pins.map((pin) => pin.chipId).sort().join(":");
|
|
6142
|
+
if (componentPair(first) !== componentPair(second)) return false;
|
|
6143
|
+
return Object.values(this.input.mergedLabelNetIdMap).some(
|
|
6144
|
+
(netIds) => netIds.has(first.globalConnNetId) && netIds.has(second.globalConnNetId)
|
|
6145
|
+
);
|
|
6146
|
+
}
|
|
6147
|
+
_findCrossing() {
|
|
6148
|
+
const traces = this.input.allTraces;
|
|
6149
|
+
for (let firstIndex = 0; firstIndex < traces.length; firstIndex++) {
|
|
6150
|
+
const trace = traces[firstIndex];
|
|
6151
|
+
for (let secondIndex = firstIndex + 1; secondIndex < traces.length; secondIndex++) {
|
|
6152
|
+
const otherTrace = traces[secondIndex];
|
|
6153
|
+
if (trace.globalConnNetId === otherTrace.globalConnNetId) continue;
|
|
6154
|
+
const isInitialBundleCrossing = this._isTraceBundle(trace, otherTrace);
|
|
6155
|
+
if (!isInitialBundleCrossing && !this.reroutedTraceIds.has(trace.mspPairId) && !this.reroutedTraceIds.has(otherTrace.mspPairId)) {
|
|
6156
|
+
continue;
|
|
6157
|
+
}
|
|
6158
|
+
const crossings = findPerpendicularPathCrossings(
|
|
6159
|
+
trace.tracePath,
|
|
6160
|
+
otherTrace.tracePath
|
|
6161
|
+
);
|
|
6162
|
+
for (const { pathSegmentIndex, otherPathSegmentIndex } of crossings) {
|
|
6163
|
+
const crossing = {
|
|
6164
|
+
trace,
|
|
6165
|
+
segmentIndex: pathSegmentIndex,
|
|
6166
|
+
otherTrace,
|
|
6167
|
+
otherSegmentIndex: otherPathSegmentIndex,
|
|
6168
|
+
isInitialBundleCrossing
|
|
6169
|
+
};
|
|
6170
|
+
if (!this.ignoredCrossings.has(this._crossingKey(crossing))) {
|
|
6171
|
+
return crossing;
|
|
6172
|
+
}
|
|
6173
|
+
}
|
|
6174
|
+
}
|
|
6175
|
+
}
|
|
6176
|
+
return null;
|
|
6177
|
+
}
|
|
6178
|
+
_resolveCrossing(crossing) {
|
|
6179
|
+
const chipBounds = this.chipObstacleSpatialIndex.chips.map(
|
|
6180
|
+
(chip) => chip.bounds
|
|
6181
|
+
);
|
|
6182
|
+
const candidates = [
|
|
6183
|
+
...generatePerpendicularTraceDetours({
|
|
6184
|
+
trace: crossing.trace,
|
|
6185
|
+
segmentIndex: crossing.segmentIndex,
|
|
6186
|
+
obstacleStart: crossing.otherTrace.tracePath[crossing.otherSegmentIndex],
|
|
6187
|
+
obstacleEnd: crossing.otherTrace.tracePath[crossing.otherSegmentIndex + 1],
|
|
6188
|
+
chipBounds,
|
|
6189
|
+
clearance: this.input.paddingBuffer
|
|
6190
|
+
}),
|
|
6191
|
+
...generatePerpendicularTraceDetours({
|
|
6192
|
+
trace: crossing.otherTrace,
|
|
6193
|
+
segmentIndex: crossing.otherSegmentIndex,
|
|
6194
|
+
obstacleStart: crossing.trace.tracePath[crossing.segmentIndex],
|
|
6195
|
+
obstacleEnd: crossing.trace.tracePath[crossing.segmentIndex + 1],
|
|
6196
|
+
chipBounds,
|
|
6197
|
+
clearance: this.input.paddingBuffer
|
|
6198
|
+
})
|
|
6199
|
+
];
|
|
6200
|
+
const chipObstacles = getObstacleRects(this.input.inputProblem).filter(
|
|
6201
|
+
(obstacle) => obstacle.kind === "chip"
|
|
6202
|
+
);
|
|
6203
|
+
const validCandidates = candidates.map((candidate) => ({
|
|
6204
|
+
...candidate,
|
|
6205
|
+
collision: isPathColliding(
|
|
6206
|
+
candidate.path,
|
|
6207
|
+
this.input.allTraces,
|
|
6208
|
+
candidate.traceId
|
|
6209
|
+
)
|
|
6210
|
+
})).filter(
|
|
6211
|
+
(candidate) => !isPathCollidingWithChipInterior(candidate.path, chipObstacles) && !isPathColliding(
|
|
6212
|
+
candidate.path,
|
|
6213
|
+
[crossing.trace, crossing.otherTrace],
|
|
6214
|
+
candidate.traceId
|
|
6215
|
+
).isColliding && (crossing.isInitialBundleCrossing || !candidate.collision.isColliding)
|
|
6216
|
+
).sort(
|
|
6217
|
+
(first, second) => Number(first.collision.isColliding) - Number(second.collision.isColliding) || getPathLength(first.path) - getPathLength(second.path)
|
|
6218
|
+
);
|
|
6219
|
+
const bestCandidate = validCandidates[0];
|
|
6220
|
+
if (!bestCandidate) return false;
|
|
6221
|
+
const traceIndex = this.input.allTraces.findIndex(
|
|
6222
|
+
(trace) => trace.mspPairId === bestCandidate.traceId
|
|
6223
|
+
);
|
|
6224
|
+
this.input.allTraces[traceIndex] = {
|
|
6225
|
+
...this.input.allTraces[traceIndex],
|
|
6226
|
+
tracePath: bestCandidate.path
|
|
6227
|
+
};
|
|
6228
|
+
this.reroutedTraceIds.add(bestCandidate.traceId);
|
|
6229
|
+
return true;
|
|
6230
|
+
}
|
|
5974
6231
|
_resetAfterLShapProcessing() {
|
|
5975
6232
|
this.lShapeProcessingStep = "idle";
|
|
5976
6233
|
this.currentLShape = null;
|
|
@@ -6193,9 +6450,9 @@ var UntangleTraceSubsolver = class extends BaseSolver {
|
|
|
6193
6450
|
};
|
|
6194
6451
|
|
|
6195
6452
|
// lib/solvers/TraceCleanupSolver/is4PointRectangle.ts
|
|
6196
|
-
var
|
|
6197
|
-
var sameX = (a, b) => Math.abs(a.x - b.x) <=
|
|
6198
|
-
var sameY = (a, b) => Math.abs(a.y - b.y) <=
|
|
6453
|
+
var EPS10 = 1e-6;
|
|
6454
|
+
var sameX = (a, b) => Math.abs(a.x - b.x) <= EPS10;
|
|
6455
|
+
var sameY = (a, b) => Math.abs(a.y - b.y) <= EPS10;
|
|
6199
6456
|
var is4PointRectangle = (path) => {
|
|
6200
6457
|
if (path.length !== 4) return false;
|
|
6201
6458
|
const [p0, p1, p2, p3] = path;
|
|
@@ -6470,7 +6727,7 @@ var doesPathRunAlongChipBoundary = (path, chipObstacles) => {
|
|
|
6470
6727
|
const end = path[i + 1];
|
|
6471
6728
|
for (const obstacle of chipObstacles) {
|
|
6472
6729
|
if (!segmentRunsAlongRectBoundary(start, end, obstacle)) continue;
|
|
6473
|
-
if (getSegmentOverlapWithRectSpan(start, end, obstacle) >
|
|
6730
|
+
if (getSegmentOverlapWithRectSpan(start, end, obstacle) > EPS7) {
|
|
6474
6731
|
return true;
|
|
6475
6732
|
}
|
|
6476
6733
|
}
|
|
@@ -6485,13 +6742,13 @@ var getChipBoundaryOverlap = (path, chipObstacles) => {
|
|
|
6485
6742
|
for (const obstacle of chipObstacles) {
|
|
6486
6743
|
if (!segmentRunsAlongRectBoundary(start, end, obstacle)) continue;
|
|
6487
6744
|
const overlap = getSegmentOverlapWithRectSpan(start, end, obstacle);
|
|
6488
|
-
if (overlap >
|
|
6745
|
+
if (overlap > EPS7) total += overlap;
|
|
6489
6746
|
}
|
|
6490
6747
|
}
|
|
6491
6748
|
return total;
|
|
6492
6749
|
};
|
|
6493
6750
|
var getSegmentOverlapWithRectSpan = (start, end, rect) => {
|
|
6494
|
-
const isVertical5 = Math.abs(start.x - end.x) <
|
|
6751
|
+
const isVertical5 = Math.abs(start.x - end.x) < EPS7;
|
|
6495
6752
|
if (isVertical5) {
|
|
6496
6753
|
return Math.min(Math.max(start.y, end.y), rect.maxY) - Math.max(Math.min(start.y, end.y), rect.minY);
|
|
6497
6754
|
}
|
|
@@ -7255,16 +7512,16 @@ var createPortOnlyLabelConnectorTrace = ({
|
|
|
7255
7512
|
// lib/solvers/AvailableNetOrientationSolver/constants.ts
|
|
7256
7513
|
var LABEL_SEARCH_STEP = 0.05;
|
|
7257
7514
|
var WICK_CLEARANCE = 1e-3;
|
|
7258
|
-
var
|
|
7259
|
-
var TRACE_BOUNDARY_TOLERANCE = WICK_CLEARANCE +
|
|
7515
|
+
var EPS11 = 1e-9;
|
|
7516
|
+
var TRACE_BOUNDARY_TOLERANCE = WICK_CLEARANCE + EPS11;
|
|
7260
7517
|
var CANDIDATE_SELECTED_COLOR2 = "blue";
|
|
7261
7518
|
var CANDIDATE_REJECTED_COLOR2 = "red";
|
|
7262
7519
|
|
|
7263
7520
|
// lib/solvers/AvailableNetOrientationSolver/geometry.ts
|
|
7264
7521
|
var isYOrientation = (orientation) => orientation === "y+" || orientation === "y-";
|
|
7265
7522
|
var isXOrientation = (orientation) => orientation === "x+" || orientation === "x-";
|
|
7266
|
-
var rectsOverlap2 = (a, b) => Math.min(a.maxX, b.maxX) - Math.max(a.minX, b.minX) >
|
|
7267
|
-
var rangesOverlap = (minA, maxA, minB, maxB) => Math.min(maxA, maxB) - Math.max(minA, minB) >
|
|
7523
|
+
var rectsOverlap2 = (a, b) => Math.min(a.maxX, b.maxX) - Math.max(a.minX, b.minX) > EPS11 && Math.min(a.maxY, b.maxY) - Math.max(a.minY, b.minY) > EPS11;
|
|
7524
|
+
var rangesOverlap = (minA, maxA, minB, maxB) => Math.min(maxA, maxB) - Math.max(minA, minB) > EPS11;
|
|
7268
7525
|
var traceCrossesBoundsInterior = (bounds, traceMap) => {
|
|
7269
7526
|
for (const trace of Object.values(traceMap)) {
|
|
7270
7527
|
const points = trace.tracePath;
|
|
@@ -7287,7 +7544,7 @@ var segmentCrossesBoundsInterior = (p1, p2, bounds) => {
|
|
|
7287
7544
|
return false;
|
|
7288
7545
|
}
|
|
7289
7546
|
if (sameX2(p1, p2)) {
|
|
7290
|
-
if (p1.x <= interiorBounds.minX +
|
|
7547
|
+
if (p1.x <= interiorBounds.minX + EPS11 || p1.x >= interiorBounds.maxX - EPS11) {
|
|
7291
7548
|
return false;
|
|
7292
7549
|
}
|
|
7293
7550
|
return rangesOverlap(
|
|
@@ -7298,7 +7555,7 @@ var segmentCrossesBoundsInterior = (p1, p2, bounds) => {
|
|
|
7298
7555
|
);
|
|
7299
7556
|
}
|
|
7300
7557
|
if (sameY2(p1, p2)) {
|
|
7301
|
-
if (p1.y <= interiorBounds.minY +
|
|
7558
|
+
if (p1.y <= interiorBounds.minY + EPS11 || p1.y >= interiorBounds.maxY - EPS11) {
|
|
7302
7559
|
return false;
|
|
7303
7560
|
}
|
|
7304
7561
|
return rangesOverlap(
|
|
@@ -7354,8 +7611,8 @@ var simplifyOrthogonalPath = (path) => {
|
|
|
7354
7611
|
return simplified;
|
|
7355
7612
|
};
|
|
7356
7613
|
var pointsEqual2 = (a, b) => sameX2(a, b) && sameY2(a, b);
|
|
7357
|
-
var sameX2 = (a, b) => Math.abs(a.x - b.x) <=
|
|
7358
|
-
var sameY2 = (a, b) => Math.abs(a.y - b.y) <=
|
|
7614
|
+
var sameX2 = (a, b) => Math.abs(a.x - b.x) <= EPS11;
|
|
7615
|
+
var sameY2 = (a, b) => Math.abs(a.y - b.y) <= EPS11;
|
|
7359
7616
|
var getMaxSearchDistance = (inputProblem) => {
|
|
7360
7617
|
const maxChipWidth = Math.max(
|
|
7361
7618
|
...inputProblem.chips.map((chip) => chip.width),
|
|
@@ -7610,11 +7867,11 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
7610
7867
|
if (trace.globalConnNetId !== label.globalConnNetId) return false;
|
|
7611
7868
|
const path = trace.tracePath;
|
|
7612
7869
|
return path.some((point, pointIndex) => {
|
|
7613
|
-
if (Math.abs(point.x - label.anchorPoint.x) >
|
|
7870
|
+
if (Math.abs(point.x - label.anchorPoint.x) > EPS11 || Math.abs(point.y - label.anchorPoint.y) > EPS11) {
|
|
7614
7871
|
return false;
|
|
7615
7872
|
}
|
|
7616
7873
|
return [path[pointIndex - 1], path[pointIndex + 1]].some(
|
|
7617
|
-
(neighbor) => neighbor && Math.abs(neighbor.x - point.x) <=
|
|
7874
|
+
(neighbor) => neighbor && Math.abs(neighbor.x - point.x) <= EPS11 && (neighbor.y - point.y) * direction.y > EPS11
|
|
7618
7875
|
);
|
|
7619
7876
|
});
|
|
7620
7877
|
});
|
|
@@ -7693,7 +7950,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
7693
7950
|
);
|
|
7694
7951
|
const maxSearchDistance = this.getSearchDistanceLimit(label, orientation);
|
|
7695
7952
|
const maxOutwardDistance = outwardDirection.x === 0 && outwardDirection.y === 0 ? 0 : this.maxSearchDistance;
|
|
7696
|
-
for (let outwardDistance = 0; outwardDistance <= maxOutwardDistance +
|
|
7953
|
+
for (let outwardDistance = 0; outwardDistance <= maxOutwardDistance + EPS11; outwardDistance += LABEL_SEARCH_STEP) {
|
|
7697
7954
|
const baseAnchor = {
|
|
7698
7955
|
x: initialBaseAnchor.x + outwardDirection.x * outwardDistance,
|
|
7699
7956
|
y: initialBaseAnchor.y + outwardDirection.y * outwardDistance
|
|
@@ -7767,7 +8024,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
7767
8024
|
phase = "shift",
|
|
7768
8025
|
stopOnTraceCollision = true
|
|
7769
8026
|
} = params;
|
|
7770
|
-
for (let distance4 = LABEL_SEARCH_STEP; distance4 <= maxSearchDistance +
|
|
8027
|
+
for (let distance4 = LABEL_SEARCH_STEP; distance4 <= maxSearchDistance + EPS11; distance4 += LABEL_SEARCH_STEP) {
|
|
7771
8028
|
const anchorPoint = {
|
|
7772
8029
|
x: baseAnchor.x + direction.x * distance4,
|
|
7773
8030
|
y: baseAnchor.y + direction.y * distance4
|
|
@@ -8004,9 +8261,9 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
8004
8261
|
const { anchorPoint, orientation } = candidate;
|
|
8005
8262
|
const chipBounds = chip.bounds;
|
|
8006
8263
|
if (isYOrientation(orientation)) {
|
|
8007
|
-
return anchorPoint.x < chipBounds.minX -
|
|
8264
|
+
return anchorPoint.x < chipBounds.minX - EPS11 || anchorPoint.x > chipBounds.maxX + EPS11;
|
|
8008
8265
|
}
|
|
8009
|
-
return anchorPoint.y < chipBounds.minY -
|
|
8266
|
+
return anchorPoint.y < chipBounds.minY - EPS11 || anchorPoint.y > chipBounds.maxY + EPS11;
|
|
8010
8267
|
});
|
|
8011
8268
|
}
|
|
8012
8269
|
getBoundsStatus(candidateBoundsCheck) {
|
|
@@ -8080,8 +8337,8 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
8080
8337
|
sharesChipBoundary(bounds) {
|
|
8081
8338
|
for (const chip of this.chipObstacleSpatialIndex.chips) {
|
|
8082
8339
|
const chipBounds = chip.bounds;
|
|
8083
|
-
const adjacentToVerticalSide = Math.abs(bounds.minX - chipBounds.maxX) <= WICK_CLEARANCE +
|
|
8084
|
-
const adjacentToHorizontalSide = Math.abs(bounds.minY - chipBounds.maxY) <= WICK_CLEARANCE +
|
|
8340
|
+
const adjacentToVerticalSide = Math.abs(bounds.minX - chipBounds.maxX) <= WICK_CLEARANCE + EPS11 || Math.abs(bounds.maxX - chipBounds.minX) <= WICK_CLEARANCE + EPS11;
|
|
8341
|
+
const adjacentToHorizontalSide = Math.abs(bounds.minY - chipBounds.maxY) <= WICK_CLEARANCE + EPS11 || Math.abs(bounds.maxY - chipBounds.minY) <= WICK_CLEARANCE + EPS11;
|
|
8085
8342
|
if (adjacentToVerticalSide && rangesOverlap(
|
|
8086
8343
|
bounds.minY,
|
|
8087
8344
|
bounds.maxY,
|
|
@@ -8129,7 +8386,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
8129
8386
|
let nearestDistance = Number.POSITIVE_INFINITY;
|
|
8130
8387
|
for (const chip of this.chipObstacleSpatialIndex.chips) {
|
|
8131
8388
|
const bounds = chip.bounds;
|
|
8132
|
-
if (point.x < bounds.minX -
|
|
8389
|
+
if (point.x < bounds.minX - EPS11 || point.x > bounds.maxX + EPS11 || point.y < bounds.minY - EPS11 || point.y > bounds.maxY + EPS11) {
|
|
8133
8390
|
continue;
|
|
8134
8391
|
}
|
|
8135
8392
|
for (const [side, distance4] of getSideDistances(point, bounds)) {
|
|
@@ -8146,8 +8403,8 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
8146
8403
|
let nearestDistanceSq = Number.POSITIVE_INFINITY;
|
|
8147
8404
|
for (const chip of this.chipObstacleSpatialIndex.chips) {
|
|
8148
8405
|
const bounds = chip.bounds;
|
|
8149
|
-
const dx = point.x < bounds.minX -
|
|
8150
|
-
const dy = point.y < bounds.minY -
|
|
8406
|
+
const dx = point.x < bounds.minX - EPS11 ? bounds.minX - point.x : point.x > bounds.maxX + EPS11 ? point.x - bounds.maxX : 0;
|
|
8407
|
+
const dy = point.y < bounds.minY - EPS11 ? bounds.minY - point.y : point.y > bounds.maxY + EPS11 ? point.y - bounds.maxY : 0;
|
|
8151
8408
|
if (dx === 0 && dy === 0) continue;
|
|
8152
8409
|
const distanceSq = dx ** 2 + dy ** 2;
|
|
8153
8410
|
if (distanceSq >= nearestDistanceSq) continue;
|
|
@@ -8431,8 +8688,8 @@ var RailNetLabelCornerPlacementSolver = class extends BaseSolver {
|
|
|
8431
8688
|
if (seenCornerKeys.has(key)) continue;
|
|
8432
8689
|
seenCornerKeys.add(key);
|
|
8433
8690
|
const pinAligned = pins.some((pin) => {
|
|
8434
|
-
if (isVertical5) return Math.abs(pin.x - anchorPoint.x) <
|
|
8435
|
-
return Math.abs(pin.y - anchorPoint.y) <
|
|
8691
|
+
if (isVertical5) return Math.abs(pin.x - anchorPoint.x) < EPS6;
|
|
8692
|
+
return Math.abs(pin.y - anchorPoint.y) < EPS6;
|
|
8436
8693
|
});
|
|
8437
8694
|
if (!pinAligned) continue;
|
|
8438
8695
|
candidates.push({
|
|
@@ -8525,11 +8782,11 @@ var getOrientationConstraint = (inputProblem, label) => {
|
|
|
8525
8782
|
};
|
|
8526
8783
|
|
|
8527
8784
|
// lib/solvers/TraceAnchoredNetLabelOverlapSolver/geometry.ts
|
|
8528
|
-
var
|
|
8785
|
+
var EPS12 = 1e-6;
|
|
8529
8786
|
var TRACE_BOUNDARY_TOLERANCE2 = 1e-6;
|
|
8530
8787
|
var getLabelBounds = (label) => getRectBounds(label.center, label.width, label.height);
|
|
8531
|
-
var rectsOverlap3 = (a, b) => Math.min(a.maxX, b.maxX) - Math.max(a.minX, b.minX) >
|
|
8532
|
-
var rectsTouchOrOverlap = (a, b) => Math.min(a.maxX, b.maxX) - Math.max(a.minX, b.minX) >= -
|
|
8788
|
+
var rectsOverlap3 = (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;
|
|
8789
|
+
var rectsTouchOrOverlap = (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;
|
|
8533
8790
|
var traceCrossesBoundsInterior2 = (bounds, traces) => {
|
|
8534
8791
|
for (const trace of traces) {
|
|
8535
8792
|
const points = trace.tracePath;
|
|
@@ -8575,7 +8832,7 @@ var getPointAtTraceDistance = (trace, distance4) => {
|
|
|
8575
8832
|
const end = trace.tracePath[i + 1];
|
|
8576
8833
|
const segmentLength = getManhattanDistance(start, end);
|
|
8577
8834
|
const nextDistance = pathDistance + segmentLength;
|
|
8578
|
-
if (distance4 <= nextDistance +
|
|
8835
|
+
if (distance4 <= nextDistance + EPS12) {
|
|
8579
8836
|
const offset = Math.max(
|
|
8580
8837
|
0,
|
|
8581
8838
|
Math.min(segmentLength, distance4 - pathDistance)
|
|
@@ -8614,7 +8871,7 @@ var segmentCrossesBoundsInterior2 = (start, end, bounds) => {
|
|
|
8614
8871
|
return false;
|
|
8615
8872
|
}
|
|
8616
8873
|
if (isVertical4(start, end)) {
|
|
8617
|
-
if (start.x <= interior.minX +
|
|
8874
|
+
if (start.x <= interior.minX + EPS12 || start.x >= interior.maxX - EPS12) {
|
|
8618
8875
|
return false;
|
|
8619
8876
|
}
|
|
8620
8877
|
return rangesOverlap2(
|
|
@@ -8625,7 +8882,7 @@ var segmentCrossesBoundsInterior2 = (start, end, bounds) => {
|
|
|
8625
8882
|
);
|
|
8626
8883
|
}
|
|
8627
8884
|
if (isHorizontal3(start, end)) {
|
|
8628
|
-
if (start.y <= interior.minY +
|
|
8885
|
+
if (start.y <= interior.minY + EPS12 || start.y >= interior.maxY - EPS12) {
|
|
8629
8886
|
return false;
|
|
8630
8887
|
}
|
|
8631
8888
|
return rangesOverlap2(
|
|
@@ -8639,10 +8896,10 @@ var segmentCrossesBoundsInterior2 = (start, end, bounds) => {
|
|
|
8639
8896
|
};
|
|
8640
8897
|
var isPointOnSegment2 = (point, start, end) => {
|
|
8641
8898
|
if (isHorizontal3(start, end)) {
|
|
8642
|
-
return Math.abs(point.y - start.y) <=
|
|
8899
|
+
return Math.abs(point.y - start.y) <= EPS12 && point.x >= Math.min(start.x, end.x) - EPS12 && point.x <= Math.max(start.x, end.x) + EPS12;
|
|
8643
8900
|
}
|
|
8644
8901
|
if (isVertical4(start, end)) {
|
|
8645
|
-
return Math.abs(point.x - start.x) <=
|
|
8902
|
+
return Math.abs(point.x - start.x) <= EPS12 && point.y >= Math.min(start.y, end.y) - EPS12 && point.y <= Math.max(start.y, end.y) + EPS12;
|
|
8646
8903
|
}
|
|
8647
8904
|
return false;
|
|
8648
8905
|
};
|
|
@@ -8650,9 +8907,9 @@ var getSegmentDirection = (start, end) => ({
|
|
|
8650
8907
|
x: isVertical4(start, end) ? 0 : Math.sign(end.x - start.x),
|
|
8651
8908
|
y: isHorizontal3(start, end) ? 0 : Math.sign(end.y - start.y)
|
|
8652
8909
|
});
|
|
8653
|
-
var isHorizontal3 = (start, end) => Math.abs(start.y - end.y) <=
|
|
8654
|
-
var isVertical4 = (start, end) => Math.abs(start.x - end.x) <=
|
|
8655
|
-
var rangesOverlap2 = (minA, maxA, minB, maxB) => Math.min(maxA, maxB) - Math.max(minA, minB) >
|
|
8910
|
+
var isHorizontal3 = (start, end) => Math.abs(start.y - end.y) <= EPS12;
|
|
8911
|
+
var isVertical4 = (start, end) => Math.abs(start.x - end.x) <= EPS12;
|
|
8912
|
+
var rangesOverlap2 = (minA, maxA, minB, maxB) => Math.min(maxA, maxB) - Math.max(minA, minB) > EPS12;
|
|
8656
8913
|
var getUniquePinCount2 = (pinIds) => new Set(pinIds).size;
|
|
8657
8914
|
|
|
8658
8915
|
// lib/solvers/TraceAnchoredNetLabelOverlapSolver/candidates.ts
|
|
@@ -8709,7 +8966,7 @@ var getCandidateDistances = (traceLength, vertexDistances) => {
|
|
|
8709
8966
|
for (const distance4 of vertexDistances) {
|
|
8710
8967
|
distances.add(roundDistance(distance4));
|
|
8711
8968
|
}
|
|
8712
|
-
return [...distances].filter((distance4) => distance4 >= -
|
|
8969
|
+
return [...distances].filter((distance4) => distance4 >= -EPS12 && distance4 <= traceLength + EPS12).sort((a, b) => a - b);
|
|
8713
8970
|
};
|
|
8714
8971
|
var getOrientationsForPoint = (params) => {
|
|
8715
8972
|
const { inputProblem, label, point, orientationConstraint } = params;
|
|
@@ -8798,13 +9055,13 @@ var getOutwardOrientationOptions = (point, chipBounds) => {
|
|
|
8798
9055
|
};
|
|
8799
9056
|
var getOutwardAxisOption = (params) => {
|
|
8800
9057
|
const { value, min, max, center, positiveOrientation, negativeOrientation } = params;
|
|
8801
|
-
if (value > max +
|
|
9058
|
+
if (value > max + EPS12) {
|
|
8802
9059
|
return {
|
|
8803
9060
|
orientation: positiveOrientation,
|
|
8804
9061
|
outwardScore: 1 + value - max
|
|
8805
9062
|
};
|
|
8806
9063
|
}
|
|
8807
|
-
if (value < min -
|
|
9064
|
+
if (value < min - EPS12) {
|
|
8808
9065
|
return {
|
|
8809
9066
|
orientation: negativeOrientation,
|
|
8810
9067
|
outwardScore: 1 + min - value
|
|
@@ -8815,7 +9072,7 @@ var getOutwardAxisOption = (params) => {
|
|
|
8815
9072
|
outwardScore: getNormalizedCenterDistance(value, center, max - min)
|
|
8816
9073
|
};
|
|
8817
9074
|
};
|
|
8818
|
-
var getNormalizedCenterDistance = (value, center, span) => Math.abs(value - center) / Math.max(
|
|
9075
|
+
var getNormalizedCenterDistance = (value, center, span) => Math.abs(value - center) / Math.max(EPS12, span / 2);
|
|
8819
9076
|
var ALL_ORIENTATIONS = ["x+", "x-", "y+", "y-"];
|
|
8820
9077
|
var getFlippedOrientation = (orientation) => {
|
|
8821
9078
|
switch (orientation) {
|
|
@@ -8888,7 +9145,7 @@ var getNetLabelHeight = (inputProblem, label) => {
|
|
|
8888
9145
|
)?.netLabelHeight;
|
|
8889
9146
|
};
|
|
8890
9147
|
var roundDistance = (distance4) => Number(distance4.toFixed(6));
|
|
8891
|
-
var isSamePlacement = (label, point, orientation) => Math.abs(point.x - label.anchorPoint.x) <=
|
|
9148
|
+
var isSamePlacement = (label, point, orientation) => Math.abs(point.x - label.anchorPoint.x) <= EPS12 && Math.abs(point.y - label.anchorPoint.y) <= EPS12 && orientation === label.orientation;
|
|
8892
9149
|
|
|
8893
9150
|
// lib/solvers/TraceAnchoredNetLabelOverlapSolver/visualize.ts
|
|
8894
9151
|
var CANDIDATE_SELECTED_COLOR4 = "blue";
|