@tscircuit/checks 0.0.142 → 0.0.144
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 +10 -1
- package/dist/index.js +58 -32
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -78,6 +78,15 @@ declare function checkViaTraceClearance(circuitJson: AnyCircuitElement[], { conn
|
|
|
78
78
|
minClearance?: number;
|
|
79
79
|
}): PcbViaTraceClearanceError[];
|
|
80
80
|
|
|
81
|
+
/**
|
|
82
|
+
* Removes generic pcb_trace_error records when a more specific pad-trace or
|
|
83
|
+
* via-trace clearance error describes the same physical pair.
|
|
84
|
+
*
|
|
85
|
+
* The specific record is retained because it includes the measured and
|
|
86
|
+
* required clearance. Trace-trace and all other generic errors are untouched.
|
|
87
|
+
*/
|
|
88
|
+
declare const dedupePcbDrcErrors: <T extends AnyCircuitElement>(errors: T[]) => T[];
|
|
89
|
+
|
|
81
90
|
type SourcePinMustBeConnectedError = {
|
|
82
91
|
type: "source_pin_must_be_connected_error";
|
|
83
92
|
source_pin_must_be_connected_error_id: string;
|
|
@@ -137,4 +146,4 @@ declare function runAllChecks(circuitJson: AnyCircuitElement[]): Promise<(circui
|
|
|
137
146
|
|
|
138
147
|
declare function checkConnectorAccessibleOrientation(circuitJson: AnyCircuitElement[]): PcbConnectorNotInAccessibleOrientationWarning[];
|
|
139
148
|
|
|
140
|
-
export { NetManager, checkAllPinsInComponentAreUnderspecified, checkConnectorAccessibleOrientation, checkDifferentNetViaSpacing, checkEachPcbPortConnectedToPcbTraces, checkEachPcbTraceNonOverlapping, checkNoGroundPinDefined, checkNoPowerPinDefined, checkPadPadClearance, checkPadTraceClearance, checkPcbComponentOverlap, checkPcbComponentsOutOfBoard, checkPcbTracesOutOfBoard, checkPinMustBeConnected, checkSameNetViaSpacing, checkSourceTracesHavePcbTraces, checkSourceTracesMatchPcbTraceThickness, checkTracesAreContiguous, checkViaTraceClearance, checkViasOffBoard, runAllChecks, runAllNetlistChecks, runAllPinSpecificationChecks, runAllPlacementChecks, runAllRoutingChecks };
|
|
149
|
+
export { NetManager, checkAllPinsInComponentAreUnderspecified, checkConnectorAccessibleOrientation, checkDifferentNetViaSpacing, checkEachPcbPortConnectedToPcbTraces, checkEachPcbTraceNonOverlapping, checkNoGroundPinDefined, checkNoPowerPinDefined, checkPadPadClearance, checkPadTraceClearance, checkPcbComponentOverlap, checkPcbComponentsOutOfBoard, checkPcbTracesOutOfBoard, checkPinMustBeConnected, checkSameNetViaSpacing, checkSourceTracesHavePcbTraces, checkSourceTracesMatchPcbTraceThickness, checkTracesAreContiguous, checkViaTraceClearance, checkViasOffBoard, dedupePcbDrcErrors, runAllChecks, runAllNetlistChecks, runAllPinSpecificationChecks, runAllPlacementChecks, runAllRoutingChecks };
|
package/dist/index.js
CHANGED
|
@@ -193,6 +193,17 @@ function isPointOnSegment(point, segment) {
|
|
|
193
193
|
const squaredLength = (segment.end.x - segment.start.x) ** 2 + (segment.end.y - segment.start.y) ** 2;
|
|
194
194
|
return dotProduct <= squaredLength + POINT_ON_SEGMENT_TOLERANCE_MM;
|
|
195
195
|
}
|
|
196
|
+
function isPointInPolygon(point, polygon) {
|
|
197
|
+
let inside = false;
|
|
198
|
+
for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
|
|
199
|
+
const pi = polygon[i];
|
|
200
|
+
const pj = polygon[j];
|
|
201
|
+
if (isPointOnSegment(point, { start: pi, end: pj })) return true;
|
|
202
|
+
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;
|
|
203
|
+
if (intersects) inside = !inside;
|
|
204
|
+
}
|
|
205
|
+
return inside;
|
|
206
|
+
}
|
|
196
207
|
function isPointInPad(point, pad) {
|
|
197
208
|
if (pad.type === "pcb_smtpad") {
|
|
198
209
|
if (pad.shape === "circle") {
|
|
@@ -204,12 +215,7 @@ function isPointInPad(point, pad) {
|
|
|
204
215
|
return Math.abs(point.x - pad.x) <= halfWidth && Math.abs(point.y - pad.y) <= halfHeight;
|
|
205
216
|
}
|
|
206
217
|
if (pad.shape === "rotated_rect") {
|
|
207
|
-
|
|
208
|
-
const dy = point.y - pad.y;
|
|
209
|
-
const angle = -pad.ccw_rotation;
|
|
210
|
-
const rotatedX = dx * Math.cos(angle) - dy * Math.sin(angle);
|
|
211
|
-
const rotatedY = dx * Math.sin(angle) + dy * Math.cos(angle);
|
|
212
|
-
return Math.abs(rotatedX) <= pad.width / 2 && Math.abs(rotatedY) <= pad.height / 2;
|
|
218
|
+
return isPointInPolygon(point, getPolygonPointsForPad(pad));
|
|
213
219
|
}
|
|
214
220
|
if (pad.shape === "pill" || pad.shape === "rotated_pill") {
|
|
215
221
|
if (pad.shape === "rotated_pill") {
|
|
@@ -233,30 +239,19 @@ function isPointInPad(point, pad) {
|
|
|
233
239
|
return cornerX * cornerX + cornerY * cornerY <= radius * radius;
|
|
234
240
|
}
|
|
235
241
|
if (pad.shape === "polygon") {
|
|
236
|
-
|
|
237
|
-
for (let i = 0, j = pad.points.length - 1; i < pad.points.length; j = i++) {
|
|
238
|
-
const pi = pad.points[i];
|
|
239
|
-
const pj = pad.points[j];
|
|
240
|
-
if (isPointOnSegment(point, { start: pi, end: pj })) return true;
|
|
241
|
-
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;
|
|
242
|
-
if (intersects) inside = !inside;
|
|
243
|
-
}
|
|
244
|
-
return inside;
|
|
242
|
+
return isPointInPolygon(point, pad.points);
|
|
245
243
|
}
|
|
246
244
|
}
|
|
247
245
|
if (pad.type === "pcb_plated_hole") {
|
|
248
246
|
if (pad.shape === "circle") {
|
|
249
247
|
return getDistanceBetweenPoints(point, pad) <= pad.outer_diameter / 2;
|
|
250
248
|
}
|
|
249
|
+
if ("rect_pad_width" in pad && "rect_pad_height" in pad) {
|
|
250
|
+
return isPointInPolygon(point, getPolygonPointsForPad(pad));
|
|
251
|
+
}
|
|
251
252
|
if (pad.shape === "oval" || pad.shape === "pill") {
|
|
252
253
|
return Math.abs(point.x - pad.x) <= pad.outer_width / 2 && Math.abs(point.y - pad.y) <= pad.outer_height / 2;
|
|
253
254
|
}
|
|
254
|
-
if (pad.shape === "circular_hole_with_rect_pad") {
|
|
255
|
-
return Math.abs(point.x - pad.x) <= pad.rect_pad_width / 2 && Math.abs(point.y - pad.y) <= pad.rect_pad_height / 2;
|
|
256
|
-
}
|
|
257
|
-
if (pad.shape === "pill_hole_with_rect_pad") {
|
|
258
|
-
return Math.abs(point.x - pad.x) <= pad.rect_pad_width / 2 && Math.abs(point.y - pad.y) <= pad.rect_pad_height / 2;
|
|
259
|
-
}
|
|
260
255
|
}
|
|
261
256
|
return false;
|
|
262
257
|
}
|
|
@@ -1871,6 +1866,7 @@ function checkTracesAreContiguous(circuitJson) {
|
|
|
1871
1866
|
);
|
|
1872
1867
|
const padMap = /* @__PURE__ */ new Map();
|
|
1873
1868
|
const pcbConnectivityMap = new PcbConnectivityMap(circuitJson);
|
|
1869
|
+
const checkedSourceTraceIds = /* @__PURE__ */ new Set();
|
|
1874
1870
|
for (const pad of pcbSmtPads) {
|
|
1875
1871
|
if (pad.pcb_port_id) {
|
|
1876
1872
|
padMap.set(pad.pcb_port_id, [...padMap.get(pad.pcb_port_id) ?? [], pad]);
|
|
@@ -1927,14 +1923,22 @@ function checkTracesAreContiguous(circuitJson) {
|
|
|
1927
1923
|
circuitJson,
|
|
1928
1924
|
trace.pcb_trace_id
|
|
1929
1925
|
);
|
|
1926
|
+
if (sourceTrace && expectedPorts.length > 0) {
|
|
1927
|
+
if (checkedSourceTraceIds.has(sourceTrace.source_trace_id)) continue;
|
|
1928
|
+
checkedSourceTraceIds.add(sourceTrace.source_trace_id);
|
|
1929
|
+
}
|
|
1930
1930
|
for (const port of expectedPorts) {
|
|
1931
1931
|
if (!port.pcb_port_id) continue;
|
|
1932
1932
|
const pads = padMap.get(port.pcb_port_id);
|
|
1933
1933
|
if (!pads?.length) continue;
|
|
1934
1934
|
let isConnectedByRoutedSourceTrace = false;
|
|
1935
|
-
|
|
1936
|
-
trace.pcb_trace_id
|
|
1937
|
-
|
|
1935
|
+
const candidateTraces = [
|
|
1936
|
+
...pcbConnectivityMap.getAllTracesConnectedToTrace(trace.pcb_trace_id),
|
|
1937
|
+
...pcbTraces.filter(
|
|
1938
|
+
(candidateTrace) => candidateTrace.source_trace_id === sourceTrace?.source_trace_id
|
|
1939
|
+
)
|
|
1940
|
+
];
|
|
1941
|
+
for (const candidateTrace of candidateTraces) {
|
|
1938
1942
|
if (candidateTrace.pcb_trace_id === trace.pcb_trace_id) continue;
|
|
1939
1943
|
if (!candidateTrace.source_trace_id) continue;
|
|
1940
1944
|
const candidateSourceTrace = sourceTraces.find(
|
|
@@ -1945,16 +1949,16 @@ function checkTracesAreContiguous(circuitJson) {
|
|
|
1945
1949
|
)) {
|
|
1946
1950
|
continue;
|
|
1947
1951
|
}
|
|
1952
|
+
const candidateFirstPoint = candidateTrace.route[0];
|
|
1953
|
+
const candidateLastPoint = candidateTrace.route.at(-1);
|
|
1948
1954
|
if (getPcbPortIdsConnectedToTrace(candidateTrace).includes(
|
|
1949
1955
|
port.pcb_port_id
|
|
1950
|
-
)) {
|
|
1956
|
+
) || candidateFirstPoint?.route_type === "wire" && pads.some((pad) => isPointInPad(candidateFirstPoint, pad)) || candidateLastPoint?.route_type === "wire" && pads.some((pad) => isPointInPad(candidateLastPoint, pad))) {
|
|
1951
1957
|
isConnectedByRoutedSourceTrace = true;
|
|
1952
1958
|
break;
|
|
1953
1959
|
}
|
|
1954
1960
|
}
|
|
1955
|
-
if (isConnectedByRoutedSourceTrace)
|
|
1956
|
-
continue;
|
|
1957
|
-
}
|
|
1961
|
+
if (isConnectedByRoutedSourceTrace) continue;
|
|
1958
1962
|
const isFirstPointConnected = firstPoint.route_type === "wire" && pads.some(
|
|
1959
1963
|
(pad) => isPointInPad({ x: firstPoint.x, y: firstPoint.y }, pad)
|
|
1960
1964
|
);
|
|
@@ -2643,6 +2647,27 @@ function checkViaTraceClearance(circuitJson, {
|
|
|
2643
2647
|
return Array.from(errors.values()).map(({ error }) => error);
|
|
2644
2648
|
}
|
|
2645
2649
|
|
|
2650
|
+
// lib/dedupe-pcb-drc-errors.ts
|
|
2651
|
+
var dedupePcbDrcErrors = (errors) => {
|
|
2652
|
+
const specificallyReportedPairIds = /* @__PURE__ */ new Set();
|
|
2653
|
+
for (const error of errors) {
|
|
2654
|
+
if (error.type === "pcb_pad_trace_clearance_error" && typeof error.pcb_trace_id === "string" && typeof error.pcb_pad_id === "string") {
|
|
2655
|
+
specificallyReportedPairIds.add(
|
|
2656
|
+
`overlap_${error.pcb_trace_id}_${error.pcb_pad_id}`
|
|
2657
|
+
);
|
|
2658
|
+
}
|
|
2659
|
+
if (error.type === "pcb_via_trace_clearance_error" && typeof error.pcb_trace_id === "string" && typeof error.pcb_via_id === "string") {
|
|
2660
|
+
specificallyReportedPairIds.add(
|
|
2661
|
+
`overlap_${error.pcb_trace_id}_${error.pcb_via_id}`
|
|
2662
|
+
);
|
|
2663
|
+
}
|
|
2664
|
+
}
|
|
2665
|
+
return errors.filter((element) => {
|
|
2666
|
+
const error = element;
|
|
2667
|
+
return !(error.type === "pcb_trace_error" && typeof error.pcb_trace_error_id === "string" && specificallyReportedPairIds.has(error.pcb_trace_error_id));
|
|
2668
|
+
});
|
|
2669
|
+
};
|
|
2670
|
+
|
|
2646
2671
|
// lib/check-pin-must-be-connected.ts
|
|
2647
2672
|
function checkPinMustBeConnected(circuitJson) {
|
|
2648
2673
|
const errors = [];
|
|
@@ -3064,7 +3089,7 @@ async function runAllPinSpecificationChecks(circuitJson) {
|
|
|
3064
3089
|
];
|
|
3065
3090
|
}
|
|
3066
3091
|
async function runAllRoutingChecks(circuitJson) {
|
|
3067
|
-
return [
|
|
3092
|
+
return dedupePcbDrcErrors([
|
|
3068
3093
|
...checkEachPcbPortConnectedToPcbTraces(circuitJson),
|
|
3069
3094
|
...checkSourceTracesHavePcbTraces(circuitJson),
|
|
3070
3095
|
...checkEachPcbTraceNonOverlapping(circuitJson),
|
|
@@ -3073,15 +3098,15 @@ async function runAllRoutingChecks(circuitJson) {
|
|
|
3073
3098
|
...checkDifferentNetViaSpacing(circuitJson),
|
|
3074
3099
|
...checkTracesAreContiguous(circuitJson),
|
|
3075
3100
|
...checkPcbTracesOutOfBoard(circuitJson)
|
|
3076
|
-
];
|
|
3101
|
+
]);
|
|
3077
3102
|
}
|
|
3078
3103
|
async function runAllChecks(circuitJson) {
|
|
3079
|
-
return [
|
|
3104
|
+
return dedupePcbDrcErrors([
|
|
3080
3105
|
...await runAllPlacementChecks(circuitJson),
|
|
3081
3106
|
...await runAllNetlistChecks(circuitJson),
|
|
3082
3107
|
...await runAllPinSpecificationChecks(circuitJson),
|
|
3083
3108
|
...await runAllRoutingChecks(circuitJson)
|
|
3084
|
-
];
|
|
3109
|
+
]);
|
|
3085
3110
|
}
|
|
3086
3111
|
export {
|
|
3087
3112
|
NetManager,
|
|
@@ -3104,6 +3129,7 @@ export {
|
|
|
3104
3129
|
checkTracesAreContiguous,
|
|
3105
3130
|
checkViaTraceClearance,
|
|
3106
3131
|
checkViasOffBoard,
|
|
3132
|
+
dedupePcbDrcErrors,
|
|
3107
3133
|
runAllChecks,
|
|
3108
3134
|
runAllNetlistChecks,
|
|
3109
3135
|
runAllPinSpecificationChecks,
|