@tscircuit/checks 0.0.125 → 0.0.127
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +255 -28
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -243,7 +243,7 @@ import {
|
|
|
243
243
|
segmentToBoundsMinDistance,
|
|
244
244
|
segmentToCircleMinDistance
|
|
245
245
|
} from "@tscircuit/math-utils";
|
|
246
|
-
import { segmentToSegmentMinDistance } from "@tscircuit/math-utils";
|
|
246
|
+
import { segmentToSegmentMinDistance as segmentToSegmentMinDistance2 } from "@tscircuit/math-utils";
|
|
247
247
|
import {
|
|
248
248
|
getFullConnectivityMapFromCircuitJson as getFullConnectivityMapFromCircuitJson2
|
|
249
249
|
} from "circuit-json-to-connectivity-map";
|
|
@@ -572,7 +572,7 @@ var getClosestPointBetweenSegments = (segmentA, segmentB) => {
|
|
|
572
572
|
};
|
|
573
573
|
const dx = closestOnA.x - closestOnB.x;
|
|
574
574
|
const dy = closestOnA.y - closestOnB.y;
|
|
575
|
-
const
|
|
575
|
+
const distance3 = Math.sqrt(dx * dx + dy * dy);
|
|
576
576
|
const averagePoint = {
|
|
577
577
|
x: (closestOnA.x + closestOnB.x) / 2,
|
|
578
578
|
y: (closestOnA.y + closestOnB.y) / 2
|
|
@@ -636,6 +636,156 @@ var clamp = (value, min, max) => {
|
|
|
636
636
|
|
|
637
637
|
// lib/check-each-pcb-trace-non-overlapping/getCollidableBounds.ts
|
|
638
638
|
import { getBoundsOfPcbElements as getBoundsOfPcbElements2 } from "@tscircuit/circuit-json-util";
|
|
639
|
+
|
|
640
|
+
// lib/check-each-pcb-trace-non-overlapping/segment-to-polygon-clearance.ts
|
|
641
|
+
import {
|
|
642
|
+
distSq,
|
|
643
|
+
getSegmentIntersection,
|
|
644
|
+
isPointInsidePolygon,
|
|
645
|
+
pointToSegmentClosestPoint,
|
|
646
|
+
segmentToSegmentMinDistance
|
|
647
|
+
} from "@tscircuit/math-utils";
|
|
648
|
+
var rotatePoint = (point, angleDegrees) => {
|
|
649
|
+
const angle = angleDegrees * Math.PI / 180;
|
|
650
|
+
return {
|
|
651
|
+
x: point.x * Math.cos(angle) - point.y * Math.sin(angle),
|
|
652
|
+
y: point.x * Math.sin(angle) + point.y * Math.cos(angle)
|
|
653
|
+
};
|
|
654
|
+
};
|
|
655
|
+
var getRotatedRectPoints = ({
|
|
656
|
+
x,
|
|
657
|
+
y,
|
|
658
|
+
width,
|
|
659
|
+
height,
|
|
660
|
+
ccwRotation
|
|
661
|
+
}) => {
|
|
662
|
+
const halfWidth = width / 2;
|
|
663
|
+
const halfHeight = height / 2;
|
|
664
|
+
return [
|
|
665
|
+
{ x: -halfWidth, y: -halfHeight },
|
|
666
|
+
{ x: halfWidth, y: -halfHeight },
|
|
667
|
+
{ x: halfWidth, y: halfHeight },
|
|
668
|
+
{ x: -halfWidth, y: halfHeight }
|
|
669
|
+
].map((point) => {
|
|
670
|
+
const rotated = rotatePoint(point, ccwRotation);
|
|
671
|
+
return { x: x + rotated.x, y: y + rotated.y };
|
|
672
|
+
});
|
|
673
|
+
};
|
|
674
|
+
var getPolygonPointsForPad = (pad) => {
|
|
675
|
+
if (pad.type === "pcb_smtpad") {
|
|
676
|
+
if (pad.shape === "polygon") return pad.points;
|
|
677
|
+
if (pad.shape === "rotated_rect") {
|
|
678
|
+
return getRotatedRectPoints({
|
|
679
|
+
x: pad.x,
|
|
680
|
+
y: pad.y,
|
|
681
|
+
width: pad.width,
|
|
682
|
+
height: pad.height,
|
|
683
|
+
ccwRotation: pad.ccw_rotation
|
|
684
|
+
});
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
if (pad.type === "pcb_plated_hole" && "rect_pad_width" in pad && "rect_pad_height" in pad) {
|
|
688
|
+
return getRotatedRectPoints({
|
|
689
|
+
x: pad.x,
|
|
690
|
+
y: pad.y,
|
|
691
|
+
width: pad.rect_pad_width,
|
|
692
|
+
height: pad.rect_pad_height,
|
|
693
|
+
ccwRotation: "rect_ccw_rotation" in pad && typeof pad.rect_ccw_rotation === "number" ? pad.rect_ccw_rotation : 0
|
|
694
|
+
});
|
|
695
|
+
}
|
|
696
|
+
throw new Error(
|
|
697
|
+
`Expected polygonal pad geometry, got ${pad.type} with shape "${pad.shape}"`
|
|
698
|
+
);
|
|
699
|
+
};
|
|
700
|
+
var getPolygonEdges = (points) => points.map(
|
|
701
|
+
(point, index) => [point, points[(index + 1) % points.length]]
|
|
702
|
+
);
|
|
703
|
+
var getClosestPointsBetweenSegments = (a1, a2, b1, b2) => {
|
|
704
|
+
const intersection = getSegmentIntersection(a1, a2, b1, b2);
|
|
705
|
+
if (intersection) {
|
|
706
|
+
return {
|
|
707
|
+
distance: 0,
|
|
708
|
+
pointOnA: intersection,
|
|
709
|
+
pointOnB: intersection,
|
|
710
|
+
center: intersection
|
|
711
|
+
};
|
|
712
|
+
}
|
|
713
|
+
const candidates = [
|
|
714
|
+
{ pointOnA: a1, pointOnB: pointToSegmentClosestPoint(a1, b1, b2) },
|
|
715
|
+
{ pointOnA: a2, pointOnB: pointToSegmentClosestPoint(a2, b1, b2) },
|
|
716
|
+
{ pointOnA: pointToSegmentClosestPoint(b1, a1, a2), pointOnB: b1 },
|
|
717
|
+
{ pointOnA: pointToSegmentClosestPoint(b2, a1, a2), pointOnB: b2 }
|
|
718
|
+
];
|
|
719
|
+
let best = candidates[0];
|
|
720
|
+
let bestDistanceSquared = distSq(best.pointOnA, best.pointOnB);
|
|
721
|
+
for (const candidate of candidates.slice(1)) {
|
|
722
|
+
const candidateDistanceSquared = distSq(
|
|
723
|
+
candidate.pointOnA,
|
|
724
|
+
candidate.pointOnB
|
|
725
|
+
);
|
|
726
|
+
if (candidateDistanceSquared < bestDistanceSquared) {
|
|
727
|
+
best = candidate;
|
|
728
|
+
bestDistanceSquared = candidateDistanceSquared;
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
return {
|
|
732
|
+
distance: segmentToSegmentMinDistance(a1, a2, b1, b2),
|
|
733
|
+
pointOnA: best.pointOnA,
|
|
734
|
+
pointOnB: best.pointOnB,
|
|
735
|
+
center: {
|
|
736
|
+
x: (best.pointOnA.x + best.pointOnB.x) / 2,
|
|
737
|
+
y: (best.pointOnA.y + best.pointOnB.y) / 2
|
|
738
|
+
}
|
|
739
|
+
};
|
|
740
|
+
};
|
|
741
|
+
var getSegmentToPolygonClearance = (segment, polygon) => {
|
|
742
|
+
const start = { x: segment.x1, y: segment.y1 };
|
|
743
|
+
const end = { x: segment.x2, y: segment.y2 };
|
|
744
|
+
if (polygon.length < 3) {
|
|
745
|
+
return { distance: Number.POSITIVE_INFINITY, center: start };
|
|
746
|
+
}
|
|
747
|
+
const intersections = getPolygonEdges(polygon).map(
|
|
748
|
+
([edgeStart, edgeEnd]) => getSegmentIntersection(start, end, edgeStart, edgeEnd)
|
|
749
|
+
).filter((point) => point !== null);
|
|
750
|
+
if (intersections.length > 0) {
|
|
751
|
+
const dx = end.x - start.x;
|
|
752
|
+
const dy = end.y - start.y;
|
|
753
|
+
const lengthSquared = dx * dx + dy * dy;
|
|
754
|
+
intersections.sort((a, b) => {
|
|
755
|
+
const ta = ((a.x - start.x) * dx + (a.y - start.y) * dy) / lengthSquared;
|
|
756
|
+
const tb = ((b.x - start.x) * dx + (b.y - start.y) * dy) / lengthSquared;
|
|
757
|
+
return ta - tb;
|
|
758
|
+
});
|
|
759
|
+
return { distance: 0, center: intersections[0] };
|
|
760
|
+
}
|
|
761
|
+
if (isPointInsidePolygon(start, polygon) || isPointInsidePolygon(end, polygon)) {
|
|
762
|
+
return {
|
|
763
|
+
distance: 0,
|
|
764
|
+
center: {
|
|
765
|
+
x: (start.x + end.x) / 2,
|
|
766
|
+
y: (start.y + end.y) / 2
|
|
767
|
+
}
|
|
768
|
+
};
|
|
769
|
+
}
|
|
770
|
+
let best = getClosestPointsBetweenSegments(
|
|
771
|
+
start,
|
|
772
|
+
end,
|
|
773
|
+
polygon[0],
|
|
774
|
+
polygon[1]
|
|
775
|
+
);
|
|
776
|
+
for (const [edgeStart, edgeEnd] of getPolygonEdges(polygon).slice(1)) {
|
|
777
|
+
const candidate = getClosestPointsBetweenSegments(
|
|
778
|
+
start,
|
|
779
|
+
end,
|
|
780
|
+
edgeStart,
|
|
781
|
+
edgeEnd
|
|
782
|
+
);
|
|
783
|
+
if (candidate.distance < best.distance) best = candidate;
|
|
784
|
+
}
|
|
785
|
+
return { distance: best.distance, center: best.center };
|
|
786
|
+
};
|
|
787
|
+
|
|
788
|
+
// lib/check-each-pcb-trace-non-overlapping/getCollidableBounds.ts
|
|
639
789
|
var getCollidableBounds = (collidable) => {
|
|
640
790
|
if (collidable.type === "pcb_trace_segment") {
|
|
641
791
|
return {
|
|
@@ -645,6 +795,18 @@ var getCollidableBounds = (collidable) => {
|
|
|
645
795
|
maxY: Math.max(collidable.y1, collidable.y2)
|
|
646
796
|
};
|
|
647
797
|
}
|
|
798
|
+
if (collidable.type === "pcb_smtpad" || collidable.type === "pcb_plated_hole") {
|
|
799
|
+
const isPolygon = collidable.type === "pcb_smtpad" && (collidable.shape === "rotated_rect" || collidable.shape === "polygon") || collidable.type === "pcb_plated_hole" && "rect_pad_width" in collidable && "rect_pad_height" in collidable;
|
|
800
|
+
if (isPolygon) {
|
|
801
|
+
const polygonPoints = getPolygonPointsForPad(collidable);
|
|
802
|
+
return {
|
|
803
|
+
minX: Math.min(...polygonPoints.map((point) => point.x)),
|
|
804
|
+
minY: Math.min(...polygonPoints.map((point) => point.y)),
|
|
805
|
+
maxX: Math.max(...polygonPoints.map((point) => point.x)),
|
|
806
|
+
maxY: Math.max(...polygonPoints.map((point) => point.y))
|
|
807
|
+
};
|
|
808
|
+
}
|
|
809
|
+
}
|
|
648
810
|
return getBoundsOfPcbElements2([collidable]);
|
|
649
811
|
};
|
|
650
812
|
|
|
@@ -763,7 +925,7 @@ function checkEachPcbTraceNonOverlapping(circuitJson, {
|
|
|
763
925
|
if (segmentA.layer !== segmentB.layer) continue;
|
|
764
926
|
if (connMap.areIdsConnected(segmentA.pcb_trace_id, segmentB.pcb_trace_id))
|
|
765
927
|
continue;
|
|
766
|
-
const gap2 =
|
|
928
|
+
const gap2 = segmentToSegmentMinDistance2(
|
|
767
929
|
{ x: segmentA.x1, y: segmentA.y1 },
|
|
768
930
|
{ x: segmentA.x2, y: segmentA.y2 },
|
|
769
931
|
{ x: segmentB.x1, y: segmentB.y1 },
|
|
@@ -804,12 +966,12 @@ function checkEachPcbTraceNonOverlapping(circuitJson, {
|
|
|
804
966
|
const isCircular = obj.type === "pcb_via" || obj.type === "pcb_plated_hole" && obj.shape === "circle" || obj.type === "pcb_hole" || obj.type === "pcb_smtpad" && obj.shape === "circle";
|
|
805
967
|
if (isCircular) {
|
|
806
968
|
const radius = getRadiusOfCircuitJsonElement(obj);
|
|
807
|
-
const
|
|
969
|
+
const distance3 = segmentToCircleMinDistance(
|
|
808
970
|
{ x: segmentA.x1, y: segmentA.y1 },
|
|
809
971
|
{ x: segmentA.x2, y: segmentA.y2 },
|
|
810
972
|
{ x: obj.x, y: obj.y, radius }
|
|
811
973
|
);
|
|
812
|
-
const gap2 =
|
|
974
|
+
const gap2 = distance3 - segmentA.thickness / 2;
|
|
813
975
|
if (gap2 > minClearance - EPSILON) continue;
|
|
814
976
|
const pcb_trace_error_id = `overlap_${segmentA.pcb_trace_id}_${primaryObjId}`;
|
|
815
977
|
if (errorIds.has(pcb_trace_error_id)) continue;
|
|
@@ -838,6 +1000,40 @@ function checkEachPcbTraceNonOverlapping(circuitJson, {
|
|
|
838
1000
|
].filter(Boolean)
|
|
839
1001
|
});
|
|
840
1002
|
}
|
|
1003
|
+
const isPolygon = obj.type === "pcb_smtpad" && (obj.shape === "rotated_rect" || obj.shape === "polygon") || obj.type === "pcb_plated_hole" && "rect_pad_width" in obj && "rect_pad_height" in obj;
|
|
1004
|
+
if (isPolygon) {
|
|
1005
|
+
const padOutline = getPolygonPointsForPad(obj);
|
|
1006
|
+
const { distance: distance3, center } = getSegmentToPolygonClearance(
|
|
1007
|
+
segmentA,
|
|
1008
|
+
padOutline
|
|
1009
|
+
);
|
|
1010
|
+
const gap2 = distance3 - segmentA.thickness / 2;
|
|
1011
|
+
if (gap2 > minClearance - EPSILON) continue;
|
|
1012
|
+
const pcb_trace_error_id = `overlap_${segmentA.pcb_trace_id}_${primaryObjId}`;
|
|
1013
|
+
if (errorIds.has(pcb_trace_error_id)) continue;
|
|
1014
|
+
errorIds.add(pcb_trace_error_id);
|
|
1015
|
+
errors.push({
|
|
1016
|
+
type: "pcb_trace_error",
|
|
1017
|
+
error_type: "pcb_trace_error",
|
|
1018
|
+
message: constructErrorMessage(
|
|
1019
|
+
getReadableName(segmentA.pcb_trace_id),
|
|
1020
|
+
`${obj.type} "${getReadableName(getPrimaryId(obj))}"`,
|
|
1021
|
+
gap2
|
|
1022
|
+
),
|
|
1023
|
+
pcb_trace_id: segmentA.pcb_trace_id,
|
|
1024
|
+
center,
|
|
1025
|
+
source_trace_id: "",
|
|
1026
|
+
pcb_trace_error_id,
|
|
1027
|
+
pcb_component_ids: [
|
|
1028
|
+
"pcb_component_id" in obj ? obj.pcb_component_id : void 0
|
|
1029
|
+
].filter(Boolean),
|
|
1030
|
+
pcb_port_ids: [
|
|
1031
|
+
...getPcbPortIdsConnectedToTraces([segmentA._pcbTrace]),
|
|
1032
|
+
"pcb_port_id" in obj ? obj.pcb_port_id : void 0
|
|
1033
|
+
].filter(Boolean)
|
|
1034
|
+
});
|
|
1035
|
+
continue;
|
|
1036
|
+
}
|
|
841
1037
|
const gap = segmentToBoundsMinDistance(
|
|
842
1038
|
{ x: segmentA.x1, y: segmentA.y1 },
|
|
843
1039
|
{ x: segmentA.x2, y: segmentA.y2 },
|
|
@@ -1041,11 +1237,11 @@ function computeOverlapDistance(compPoly, boardPoly, componentCenter, componentW
|
|
|
1041
1237
|
});
|
|
1042
1238
|
}
|
|
1043
1239
|
const matrix = rotateDEG(rotationDeg, componentCenter.x, componentCenter.y);
|
|
1044
|
-
const
|
|
1240
|
+
const rotatePoint2 = (pt) => {
|
|
1045
1241
|
const p = applyToPoint(matrix, pt);
|
|
1046
1242
|
return new Flatten.Point(p.x, p.y);
|
|
1047
1243
|
};
|
|
1048
|
-
const rotatedPoints = corners.concat(midpoints).map(
|
|
1244
|
+
const rotatedPoints = corners.concat(midpoints).map(rotatePoint2);
|
|
1049
1245
|
let maxDistance = 0;
|
|
1050
1246
|
for (const pt of rotatedPoints) {
|
|
1051
1247
|
if (!boardPoly.contains(pt)) {
|
|
@@ -1420,7 +1616,7 @@ function checkSourceTracesHavePcbTraces(circuitJson) {
|
|
|
1420
1616
|
|
|
1421
1617
|
// lib/check-trace-out-of-board/checkTraceOutOfBoard.ts
|
|
1422
1618
|
import { cju as cju3 } from "@tscircuit/circuit-json-util";
|
|
1423
|
-
import { segmentToSegmentMinDistance as
|
|
1619
|
+
import { segmentToSegmentMinDistance as segmentToSegmentMinDistance3 } from "@tscircuit/math-utils";
|
|
1424
1620
|
function getBoardPolygonPoints(board) {
|
|
1425
1621
|
if (board.outline && board.outline.length > 0) {
|
|
1426
1622
|
return board.outline.map((p) => ({ x: p.x, y: p.y }));
|
|
@@ -1464,14 +1660,14 @@ function checkPcbTracesOutOfBoard(circuitJson, config = {}) {
|
|
|
1464
1660
|
for (let j = 0; j < boardPoints.length; j++) {
|
|
1465
1661
|
const edgeStart = boardPoints[j];
|
|
1466
1662
|
const edgeEnd = boardPoints[(j + 1) % boardPoints.length];
|
|
1467
|
-
const
|
|
1663
|
+
const distance3 = segmentToSegmentMinDistance3(
|
|
1468
1664
|
segmentStart,
|
|
1469
1665
|
segmentEnd,
|
|
1470
1666
|
edgeStart,
|
|
1471
1667
|
edgeEnd
|
|
1472
1668
|
);
|
|
1473
|
-
if (
|
|
1474
|
-
minDistance =
|
|
1669
|
+
if (distance3 < minDistance) {
|
|
1670
|
+
minDistance = distance3;
|
|
1475
1671
|
}
|
|
1476
1672
|
}
|
|
1477
1673
|
const minimumDistance = traceWidth / 2 + margin;
|
|
@@ -2245,7 +2441,7 @@ function checkConnectorAccessibleOrientation(circuitJson) {
|
|
|
2245
2441
|
// lib/check-courtyard-overlap/checkCourtyardOverlap.ts
|
|
2246
2442
|
import {
|
|
2247
2443
|
doSegmentsIntersect,
|
|
2248
|
-
isPointInsidePolygon
|
|
2444
|
+
isPointInsidePolygon as isPointInsidePolygon2
|
|
2249
2445
|
} from "@tscircuit/math-utils";
|
|
2250
2446
|
function getCourtyardPolygon(el) {
|
|
2251
2447
|
if (el.type === "pcb_courtyard_rect") {
|
|
@@ -2291,8 +2487,8 @@ function getComponentName2(circuitJson, pcbComponentId) {
|
|
|
2291
2487
|
return pcbComponentId;
|
|
2292
2488
|
}
|
|
2293
2489
|
function polygonsOverlap(polyA, polyB) {
|
|
2294
|
-
if (polyA.some((p) =>
|
|
2295
|
-
if (polyB.some((p) =>
|
|
2490
|
+
if (polyA.some((p) => isPointInsidePolygon2(p, polyB))) return true;
|
|
2491
|
+
if (polyB.some((p) => isPointInsidePolygon2(p, polyA))) return true;
|
|
2296
2492
|
for (let i = 0; i < polyA.length; i++) {
|
|
2297
2493
|
const a1 = polyA[i];
|
|
2298
2494
|
const a2 = polyA[(i + 1) % polyA.length];
|
|
@@ -2349,13 +2545,22 @@ function checkCourtyardOverlap(circuitJson) {
|
|
|
2349
2545
|
}
|
|
2350
2546
|
|
|
2351
2547
|
// lib/check-traces-are-contiguous/is-point-in-pad.ts
|
|
2352
|
-
function
|
|
2353
|
-
return Math.sqrt((
|
|
2548
|
+
function getDistanceBetweenPoints(pointA, pointB) {
|
|
2549
|
+
return Math.sqrt((pointB.x - pointA.x) ** 2 + (pointB.y - pointA.y) ** 2);
|
|
2550
|
+
}
|
|
2551
|
+
var POINT_ON_SEGMENT_TOLERANCE_MM = 1e-9;
|
|
2552
|
+
function isPointOnSegment(point, segment) {
|
|
2553
|
+
const crossProduct = (point.y - segment.start.y) * (segment.end.x - segment.start.x) - (point.x - segment.start.x) * (segment.end.y - segment.start.y);
|
|
2554
|
+
if (Math.abs(crossProduct) > POINT_ON_SEGMENT_TOLERANCE_MM) return false;
|
|
2555
|
+
const dotProduct = (point.x - segment.start.x) * (segment.end.x - segment.start.x) + (point.y - segment.start.y) * (segment.end.y - segment.start.y);
|
|
2556
|
+
if (dotProduct < -POINT_ON_SEGMENT_TOLERANCE_MM) return false;
|
|
2557
|
+
const squaredLength = (segment.end.x - segment.start.x) ** 2 + (segment.end.y - segment.start.y) ** 2;
|
|
2558
|
+
return dotProduct <= squaredLength + POINT_ON_SEGMENT_TOLERANCE_MM;
|
|
2354
2559
|
}
|
|
2355
2560
|
function isPointInPad(point, pad) {
|
|
2356
2561
|
if (pad.type === "pcb_smtpad") {
|
|
2357
2562
|
if (pad.shape === "circle") {
|
|
2358
|
-
return
|
|
2563
|
+
return getDistanceBetweenPoints(point, pad) <= pad.radius;
|
|
2359
2564
|
}
|
|
2360
2565
|
if (pad.shape === "rect") {
|
|
2361
2566
|
const halfWidth = pad.width / 2;
|
|
@@ -2387,10 +2592,21 @@ function isPointInPad(point, pad) {
|
|
|
2387
2592
|
);
|
|
2388
2593
|
return cornerX * cornerX + cornerY * cornerY <= radius * radius;
|
|
2389
2594
|
}
|
|
2595
|
+
if (pad.shape === "polygon") {
|
|
2596
|
+
let inside = false;
|
|
2597
|
+
for (let i = 0, j = pad.points.length - 1; i < pad.points.length; j = i++) {
|
|
2598
|
+
const pi = pad.points[i];
|
|
2599
|
+
const pj = pad.points[j];
|
|
2600
|
+
if (isPointOnSegment(point, { start: pi, end: pj })) return true;
|
|
2601
|
+
const intersects = pi.y > point.y !== pj.y > point.y && point.x < (pj.x - pi.x) * (point.y - pi.y) / (pj.y - pi.y) + pi.x;
|
|
2602
|
+
if (intersects) inside = !inside;
|
|
2603
|
+
}
|
|
2604
|
+
return inside;
|
|
2605
|
+
}
|
|
2390
2606
|
}
|
|
2391
2607
|
if (pad.type === "pcb_plated_hole") {
|
|
2392
2608
|
if (pad.shape === "circle") {
|
|
2393
|
-
return
|
|
2609
|
+
return getDistanceBetweenPoints(point, pad) <= pad.outer_diameter / 2;
|
|
2394
2610
|
}
|
|
2395
2611
|
if (pad.shape === "oval" || pad.shape === "pill") {
|
|
2396
2612
|
return Math.abs(point.x - pad.x) <= pad.outer_width / 2 && Math.abs(point.y - pad.y) <= pad.outer_height / 2;
|
|
@@ -2430,12 +2646,15 @@ function checkTracesAreContiguous(circuitJson) {
|
|
|
2430
2646
|
const padMap = /* @__PURE__ */ new Map();
|
|
2431
2647
|
for (const pad of pcbSmtPads) {
|
|
2432
2648
|
if (pad.pcb_port_id) {
|
|
2433
|
-
padMap.set(pad.pcb_port_id, pad);
|
|
2649
|
+
padMap.set(pad.pcb_port_id, [...padMap.get(pad.pcb_port_id) ?? [], pad]);
|
|
2434
2650
|
}
|
|
2435
2651
|
}
|
|
2436
2652
|
for (const hole of pcbPlatedHoles) {
|
|
2437
2653
|
if (hole.pcb_port_id) {
|
|
2438
|
-
padMap.set(hole.pcb_port_id,
|
|
2654
|
+
padMap.set(hole.pcb_port_id, [
|
|
2655
|
+
...padMap.get(hole.pcb_port_id) ?? [],
|
|
2656
|
+
hole
|
|
2657
|
+
]);
|
|
2439
2658
|
}
|
|
2440
2659
|
}
|
|
2441
2660
|
for (const trace of pcbTraces) {
|
|
@@ -2483,16 +2702,20 @@ function checkTracesAreContiguous(circuitJson) {
|
|
|
2483
2702
|
);
|
|
2484
2703
|
for (const port of expectedPorts) {
|
|
2485
2704
|
if (!port.pcb_port_id) continue;
|
|
2486
|
-
const
|
|
2487
|
-
if (!
|
|
2488
|
-
const isFirstPointConnected = firstPoint.route_type === "wire" &&
|
|
2489
|
-
|
|
2705
|
+
const pads = padMap.get(port.pcb_port_id);
|
|
2706
|
+
if (!pads?.length) continue;
|
|
2707
|
+
const isFirstPointConnected = firstPoint.route_type === "wire" && pads.some(
|
|
2708
|
+
(pad) => isPointInPad({ x: firstPoint.x, y: firstPoint.y }, pad)
|
|
2709
|
+
);
|
|
2710
|
+
const isLastPointConnected = lastPoint.route_type === "wire" && pads.some(
|
|
2711
|
+
(pad) => isPointInPad({ x: lastPoint.x, y: lastPoint.y }, pad)
|
|
2712
|
+
);
|
|
2490
2713
|
if (!isFirstPointConnected && !isLastPointConnected) {
|
|
2491
2714
|
const portName = getReadableNameForPcbPort2(
|
|
2492
2715
|
circuitJson,
|
|
2493
2716
|
port.pcb_port_id
|
|
2494
2717
|
).replace("pcb_port", "");
|
|
2495
|
-
const padType =
|
|
2718
|
+
const padType = pads[0].type.replace(/pcb_/, "");
|
|
2496
2719
|
const errorCenter = {
|
|
2497
2720
|
x: (firstPoint.x + lastPoint.x) / 2,
|
|
2498
2721
|
y: (firstPoint.y + lastPoint.y) / 2
|
|
@@ -2514,11 +2737,15 @@ function checkTracesAreContiguous(circuitJson) {
|
|
|
2514
2737
|
let firstConnectsToAnyPad = false;
|
|
2515
2738
|
let lastConnectsToAnyPad = false;
|
|
2516
2739
|
const endpointsAreSame = firstPoint.route_type === "wire" && lastPoint.route_type === "wire" && Math.abs(firstPoint.x - lastPoint.x) < 0.01 && Math.abs(firstPoint.y - lastPoint.y) < 0.01;
|
|
2517
|
-
for (const
|
|
2518
|
-
if (firstPoint.route_type === "wire" &&
|
|
2740
|
+
for (const pads of padMap.values()) {
|
|
2741
|
+
if (firstPoint.route_type === "wire" && pads.some(
|
|
2742
|
+
(pad) => isPointInPad({ x: firstPoint.x, y: firstPoint.y }, pad)
|
|
2743
|
+
)) {
|
|
2519
2744
|
firstConnectsToAnyPad = true;
|
|
2520
2745
|
}
|
|
2521
|
-
if (lastPoint.route_type === "wire" &&
|
|
2746
|
+
if (lastPoint.route_type === "wire" && pads.some(
|
|
2747
|
+
(pad) => isPointInPad({ x: lastPoint.x, y: lastPoint.y }, pad)
|
|
2748
|
+
)) {
|
|
2522
2749
|
lastConnectsToAnyPad = true;
|
|
2523
2750
|
}
|
|
2524
2751
|
}
|