@tscircuit/checks 0.0.126 → 0.0.128
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 +126 -31
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -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
|
|
@@ -811,14 +811,17 @@ var getCollidableBounds = (collidable) => {
|
|
|
811
811
|
};
|
|
812
812
|
|
|
813
813
|
// lib/check-each-pcb-trace-non-overlapping/getPcbPortIdsConnectedToTraces.ts
|
|
814
|
+
function getPcbPortIdsConnectedToRoutePoint(routePoint) {
|
|
815
|
+
if (routePoint.route_type !== "wire") return [];
|
|
816
|
+
return [routePoint.start_pcb_port_id, routePoint.end_pcb_port_id].filter(
|
|
817
|
+
(portId) => Boolean(portId)
|
|
818
|
+
);
|
|
819
|
+
}
|
|
814
820
|
function getPcbPortIdsConnectedToTrace(trace) {
|
|
815
821
|
const connectedPcbPorts = /* @__PURE__ */ new Set();
|
|
816
822
|
for (const segment of trace.route) {
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
connectedPcbPorts.add(segment.start_pcb_port_id);
|
|
820
|
-
if (segment.end_pcb_port_id)
|
|
821
|
-
connectedPcbPorts.add(segment.end_pcb_port_id);
|
|
823
|
+
for (const portId of getPcbPortIdsConnectedToRoutePoint(segment)) {
|
|
824
|
+
connectedPcbPorts.add(portId);
|
|
822
825
|
}
|
|
823
826
|
}
|
|
824
827
|
return Array.from(connectedPcbPorts);
|
|
@@ -966,12 +969,12 @@ function checkEachPcbTraceNonOverlapping(circuitJson, {
|
|
|
966
969
|
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";
|
|
967
970
|
if (isCircular) {
|
|
968
971
|
const radius = getRadiusOfCircuitJsonElement(obj);
|
|
969
|
-
const
|
|
972
|
+
const distance3 = segmentToCircleMinDistance(
|
|
970
973
|
{ x: segmentA.x1, y: segmentA.y1 },
|
|
971
974
|
{ x: segmentA.x2, y: segmentA.y2 },
|
|
972
975
|
{ x: obj.x, y: obj.y, radius }
|
|
973
976
|
);
|
|
974
|
-
const gap2 =
|
|
977
|
+
const gap2 = distance3 - segmentA.thickness / 2;
|
|
975
978
|
if (gap2 > minClearance - EPSILON) continue;
|
|
976
979
|
const pcb_trace_error_id = `overlap_${segmentA.pcb_trace_id}_${primaryObjId}`;
|
|
977
980
|
if (errorIds.has(pcb_trace_error_id)) continue;
|
|
@@ -1003,11 +1006,11 @@ function checkEachPcbTraceNonOverlapping(circuitJson, {
|
|
|
1003
1006
|
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
1007
|
if (isPolygon) {
|
|
1005
1008
|
const padOutline = getPolygonPointsForPad(obj);
|
|
1006
|
-
const { distance:
|
|
1009
|
+
const { distance: distance3, center } = getSegmentToPolygonClearance(
|
|
1007
1010
|
segmentA,
|
|
1008
1011
|
padOutline
|
|
1009
1012
|
);
|
|
1010
|
-
const gap2 =
|
|
1013
|
+
const gap2 = distance3 - segmentA.thickness / 2;
|
|
1011
1014
|
if (gap2 > minClearance - EPSILON) continue;
|
|
1012
1015
|
const pcb_trace_error_id = `overlap_${segmentA.pcb_trace_id}_${primaryObjId}`;
|
|
1013
1016
|
if (errorIds.has(pcb_trace_error_id)) continue;
|
|
@@ -1660,14 +1663,14 @@ function checkPcbTracesOutOfBoard(circuitJson, config = {}) {
|
|
|
1660
1663
|
for (let j = 0; j < boardPoints.length; j++) {
|
|
1661
1664
|
const edgeStart = boardPoints[j];
|
|
1662
1665
|
const edgeEnd = boardPoints[(j + 1) % boardPoints.length];
|
|
1663
|
-
const
|
|
1666
|
+
const distance3 = segmentToSegmentMinDistance3(
|
|
1664
1667
|
segmentStart,
|
|
1665
1668
|
segmentEnd,
|
|
1666
1669
|
edgeStart,
|
|
1667
1670
|
edgeEnd
|
|
1668
1671
|
);
|
|
1669
|
-
if (
|
|
1670
|
-
minDistance =
|
|
1672
|
+
if (distance3 < minDistance) {
|
|
1673
|
+
minDistance = distance3;
|
|
1671
1674
|
}
|
|
1672
1675
|
}
|
|
1673
1676
|
const minimumDistance = traceWidth / 2 + margin;
|
|
@@ -2545,13 +2548,22 @@ function checkCourtyardOverlap(circuitJson) {
|
|
|
2545
2548
|
}
|
|
2546
2549
|
|
|
2547
2550
|
// lib/check-traces-are-contiguous/is-point-in-pad.ts
|
|
2548
|
-
function
|
|
2549
|
-
return Math.sqrt((
|
|
2551
|
+
function getDistanceBetweenPoints(pointA, pointB) {
|
|
2552
|
+
return Math.sqrt((pointB.x - pointA.x) ** 2 + (pointB.y - pointA.y) ** 2);
|
|
2553
|
+
}
|
|
2554
|
+
var POINT_ON_SEGMENT_TOLERANCE_MM = 1e-9;
|
|
2555
|
+
function isPointOnSegment(point, segment) {
|
|
2556
|
+
const crossProduct = (point.y - segment.start.y) * (segment.end.x - segment.start.x) - (point.x - segment.start.x) * (segment.end.y - segment.start.y);
|
|
2557
|
+
if (Math.abs(crossProduct) > POINT_ON_SEGMENT_TOLERANCE_MM) return false;
|
|
2558
|
+
const dotProduct = (point.x - segment.start.x) * (segment.end.x - segment.start.x) + (point.y - segment.start.y) * (segment.end.y - segment.start.y);
|
|
2559
|
+
if (dotProduct < -POINT_ON_SEGMENT_TOLERANCE_MM) return false;
|
|
2560
|
+
const squaredLength = (segment.end.x - segment.start.x) ** 2 + (segment.end.y - segment.start.y) ** 2;
|
|
2561
|
+
return dotProduct <= squaredLength + POINT_ON_SEGMENT_TOLERANCE_MM;
|
|
2550
2562
|
}
|
|
2551
2563
|
function isPointInPad(point, pad) {
|
|
2552
2564
|
if (pad.type === "pcb_smtpad") {
|
|
2553
2565
|
if (pad.shape === "circle") {
|
|
2554
|
-
return
|
|
2566
|
+
return getDistanceBetweenPoints(point, pad) <= pad.radius;
|
|
2555
2567
|
}
|
|
2556
2568
|
if (pad.shape === "rect") {
|
|
2557
2569
|
const halfWidth = pad.width / 2;
|
|
@@ -2583,10 +2595,21 @@ function isPointInPad(point, pad) {
|
|
|
2583
2595
|
);
|
|
2584
2596
|
return cornerX * cornerX + cornerY * cornerY <= radius * radius;
|
|
2585
2597
|
}
|
|
2598
|
+
if (pad.shape === "polygon") {
|
|
2599
|
+
let inside = false;
|
|
2600
|
+
for (let i = 0, j = pad.points.length - 1; i < pad.points.length; j = i++) {
|
|
2601
|
+
const pi = pad.points[i];
|
|
2602
|
+
const pj = pad.points[j];
|
|
2603
|
+
if (isPointOnSegment(point, { start: pi, end: pj })) return true;
|
|
2604
|
+
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;
|
|
2605
|
+
if (intersects) inside = !inside;
|
|
2606
|
+
}
|
|
2607
|
+
return inside;
|
|
2608
|
+
}
|
|
2586
2609
|
}
|
|
2587
2610
|
if (pad.type === "pcb_plated_hole") {
|
|
2588
2611
|
if (pad.shape === "circle") {
|
|
2589
|
-
return
|
|
2612
|
+
return getDistanceBetweenPoints(point, pad) <= pad.outer_diameter / 2;
|
|
2590
2613
|
}
|
|
2591
2614
|
if (pad.shape === "oval" || pad.shape === "pill") {
|
|
2592
2615
|
return Math.abs(point.x - pad.x) <= pad.outer_width / 2 && Math.abs(point.y - pad.y) <= pad.outer_height / 2;
|
|
@@ -2606,6 +2629,64 @@ import {
|
|
|
2606
2629
|
getReadableNameForPcbPort as getReadableNameForPcbPort2,
|
|
2607
2630
|
getReadableNameForPcbTrace
|
|
2608
2631
|
} from "@tscircuit/circuit-json-util";
|
|
2632
|
+
function routePointConnectsToAnotherExpectedPort(point, expectedPorts, missingPcbPortId, padMap) {
|
|
2633
|
+
if (point.route_type !== "wire") return false;
|
|
2634
|
+
return expectedPorts.some((expectedPort) => {
|
|
2635
|
+
if (!expectedPort.pcb_port_id || expectedPort.pcb_port_id === missingPcbPortId) {
|
|
2636
|
+
return false;
|
|
2637
|
+
}
|
|
2638
|
+
const expectedPads = padMap.get(expectedPort.pcb_port_id);
|
|
2639
|
+
return expectedPads?.some(
|
|
2640
|
+
(pad) => isPointInPad({ x: point.x, y: point.y }, pad)
|
|
2641
|
+
) ?? false;
|
|
2642
|
+
});
|
|
2643
|
+
}
|
|
2644
|
+
function getMissingConnectionErrorCenter({
|
|
2645
|
+
firstPoint,
|
|
2646
|
+
lastPoint,
|
|
2647
|
+
port,
|
|
2648
|
+
expectedPorts,
|
|
2649
|
+
padMap
|
|
2650
|
+
}) {
|
|
2651
|
+
let errorLocation;
|
|
2652
|
+
const firstWirePoint = firstPoint.route_type === "wire" ? firstPoint : void 0;
|
|
2653
|
+
const lastWirePoint = lastPoint.route_type === "wire" ? lastPoint : void 0;
|
|
2654
|
+
const firstWirePointReferencesPort = getPcbPortIdsConnectedToRoutePoint(
|
|
2655
|
+
firstPoint
|
|
2656
|
+
).includes(port.pcb_port_id);
|
|
2657
|
+
const lastWirePointReferencesPort = getPcbPortIdsConnectedToRoutePoint(
|
|
2658
|
+
lastPoint
|
|
2659
|
+
).includes(port.pcb_port_id);
|
|
2660
|
+
if (firstWirePointReferencesPort && firstWirePoint) {
|
|
2661
|
+
errorLocation = firstWirePoint;
|
|
2662
|
+
} else if (lastWirePointReferencesPort && lastWirePoint) {
|
|
2663
|
+
errorLocation = lastWirePoint;
|
|
2664
|
+
} else if (routePointConnectsToAnotherExpectedPort(
|
|
2665
|
+
firstPoint,
|
|
2666
|
+
expectedPorts,
|
|
2667
|
+
port.pcb_port_id,
|
|
2668
|
+
padMap
|
|
2669
|
+
) && lastWirePoint) {
|
|
2670
|
+
errorLocation = lastWirePoint;
|
|
2671
|
+
} else if (routePointConnectsToAnotherExpectedPort(
|
|
2672
|
+
lastPoint,
|
|
2673
|
+
expectedPorts,
|
|
2674
|
+
port.pcb_port_id,
|
|
2675
|
+
padMap
|
|
2676
|
+
) && firstWirePoint) {
|
|
2677
|
+
errorLocation = firstWirePoint;
|
|
2678
|
+
} else if (firstWirePoint && lastWirePoint) {
|
|
2679
|
+
errorLocation = distance2(firstWirePoint, port) <= distance2(lastWirePoint, port) ? firstWirePoint : lastWirePoint;
|
|
2680
|
+
} else if (firstWirePoint) {
|
|
2681
|
+
errorLocation = firstWirePoint;
|
|
2682
|
+
} else if (lastWirePoint) {
|
|
2683
|
+
errorLocation = lastWirePoint;
|
|
2684
|
+
}
|
|
2685
|
+
return errorLocation ? { x: errorLocation.x, y: errorLocation.y } : {
|
|
2686
|
+
x: (firstPoint.x + lastPoint.x) / 2,
|
|
2687
|
+
y: (firstPoint.y + lastPoint.y) / 2
|
|
2688
|
+
};
|
|
2689
|
+
}
|
|
2609
2690
|
function checkTracesAreContiguous(circuitJson) {
|
|
2610
2691
|
const errors = [];
|
|
2611
2692
|
const pcbPorts = circuitJson.filter(
|
|
@@ -2626,12 +2707,15 @@ function checkTracesAreContiguous(circuitJson) {
|
|
|
2626
2707
|
const padMap = /* @__PURE__ */ new Map();
|
|
2627
2708
|
for (const pad of pcbSmtPads) {
|
|
2628
2709
|
if (pad.pcb_port_id) {
|
|
2629
|
-
padMap.set(pad.pcb_port_id, pad);
|
|
2710
|
+
padMap.set(pad.pcb_port_id, [...padMap.get(pad.pcb_port_id) ?? [], pad]);
|
|
2630
2711
|
}
|
|
2631
2712
|
}
|
|
2632
2713
|
for (const hole of pcbPlatedHoles) {
|
|
2633
2714
|
if (hole.pcb_port_id) {
|
|
2634
|
-
padMap.set(hole.pcb_port_id,
|
|
2715
|
+
padMap.set(hole.pcb_port_id, [
|
|
2716
|
+
...padMap.get(hole.pcb_port_id) ?? [],
|
|
2717
|
+
hole
|
|
2718
|
+
]);
|
|
2635
2719
|
}
|
|
2636
2720
|
}
|
|
2637
2721
|
for (const trace of pcbTraces) {
|
|
@@ -2679,20 +2763,27 @@ function checkTracesAreContiguous(circuitJson) {
|
|
|
2679
2763
|
);
|
|
2680
2764
|
for (const port of expectedPorts) {
|
|
2681
2765
|
if (!port.pcb_port_id) continue;
|
|
2682
|
-
const
|
|
2683
|
-
if (!
|
|
2684
|
-
const isFirstPointConnected = firstPoint.route_type === "wire" &&
|
|
2685
|
-
|
|
2766
|
+
const pads = padMap.get(port.pcb_port_id);
|
|
2767
|
+
if (!pads?.length) continue;
|
|
2768
|
+
const isFirstPointConnected = firstPoint.route_type === "wire" && pads.some(
|
|
2769
|
+
(pad) => isPointInPad({ x: firstPoint.x, y: firstPoint.y }, pad)
|
|
2770
|
+
);
|
|
2771
|
+
const isLastPointConnected = lastPoint.route_type === "wire" && pads.some(
|
|
2772
|
+
(pad) => isPointInPad({ x: lastPoint.x, y: lastPoint.y }, pad)
|
|
2773
|
+
);
|
|
2686
2774
|
if (!isFirstPointConnected && !isLastPointConnected) {
|
|
2687
2775
|
const portName = getReadableNameForPcbPort2(
|
|
2688
2776
|
circuitJson,
|
|
2689
2777
|
port.pcb_port_id
|
|
2690
2778
|
).replace("pcb_port", "");
|
|
2691
|
-
const padType =
|
|
2692
|
-
const errorCenter = {
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2779
|
+
const padType = pads[0].type.replace(/pcb_/, "");
|
|
2780
|
+
const errorCenter = getMissingConnectionErrorCenter({
|
|
2781
|
+
firstPoint,
|
|
2782
|
+
lastPoint,
|
|
2783
|
+
port,
|
|
2784
|
+
expectedPorts,
|
|
2785
|
+
padMap
|
|
2786
|
+
});
|
|
2696
2787
|
errors.push({
|
|
2697
2788
|
type: "pcb_trace_error",
|
|
2698
2789
|
message: `Trace [${traceName}] is missing a connection to ${padType}${portName}`,
|
|
@@ -2710,11 +2801,15 @@ function checkTracesAreContiguous(circuitJson) {
|
|
|
2710
2801
|
let firstConnectsToAnyPad = false;
|
|
2711
2802
|
let lastConnectsToAnyPad = false;
|
|
2712
2803
|
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;
|
|
2713
|
-
for (const
|
|
2714
|
-
if (firstPoint.route_type === "wire" &&
|
|
2804
|
+
for (const pads of padMap.values()) {
|
|
2805
|
+
if (firstPoint.route_type === "wire" && pads.some(
|
|
2806
|
+
(pad) => isPointInPad({ x: firstPoint.x, y: firstPoint.y }, pad)
|
|
2807
|
+
)) {
|
|
2715
2808
|
firstConnectsToAnyPad = true;
|
|
2716
2809
|
}
|
|
2717
|
-
if (lastPoint.route_type === "wire" &&
|
|
2810
|
+
if (lastPoint.route_type === "wire" && pads.some(
|
|
2811
|
+
(pad) => isPointInPad({ x: lastPoint.x, y: lastPoint.y }, pad)
|
|
2812
|
+
)) {
|
|
2718
2813
|
lastConnectsToAnyPad = true;
|
|
2719
2814
|
}
|
|
2720
2815
|
}
|