@tscircuit/checks 0.0.169 → 0.0.171

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/README.md CHANGED
@@ -20,6 +20,7 @@ and output an array of arrays for any issues found.
20
20
  | [`checkEachPcbTraceNonOverlapping`](./lib/check-each-pcb-trace-non-overlapping/check-each-pcb-trace-non-overlapping.ts) | Returns `pcb_trace_error` when `pcb_trace` segments physically overlap incompatible geometry on the same layer. Pad/via near-misses are reported by the typed clearance checks instead. |
21
21
  | [`checkPcbComponentOverlap`](./lib/check-pcb-components-overlap/checkPcbComponentOverlap.ts) | Returns `pcb_footprint_overlap_error` when footprint elements from different components overlap in disallowed ways. |
22
22
  | [`checkPcbComponentsOutOfBoard`](./lib/check-pcb-components-out-of-board/checkPcbComponentsOutOfBoard.ts) | Returns `pcb_placement_error` when PCB components do not fit inside the board area. |
23
+ | [`checkPcbCopperOverKeepout`](./lib/check-pcb-copper-over-keepout.ts) | Returns one `pcb_placement_error` per non-excluded component or via whose copper overlaps a keepout on a shared layer. |
23
24
  | [`checkPcbTracesOutOfBoard`](./lib/check-trace-out-of-board/checkTraceOutOfBoard.ts) | Returns `pcb_trace_error` when any trace segment or via extends beyond the board boundary. |
24
25
  | [`checkPadTraceClearance`](./lib/check-pad-trace-clearance.ts) | Returns `pcb_pad_trace_clearance_error` when a pad and unrelated trace have a positive gap below the minimum clearance. Physical overlaps are reported by `checkEachPcbTraceNonOverlapping`. |
25
26
  | [`checkViaTraceClearance`](./lib/check-via-trace-clearance.ts) | Returns `pcb_via_trace_clearance_error` when a via and unrelated trace have a positive gap below the minimum clearance. Physical overlaps are reported by `checkEachPcbTraceNonOverlapping`. |
@@ -34,7 +35,7 @@ and output an array of arrays for any issues found.
34
35
 
35
36
  | Function | Description |
36
37
  | --- | --- |
37
- | [`runAllPlacementChecks`](./lib/run-all-checks.ts) | Runs placement checks (`checkCopperToBoardEdgeClearance`, `checkPcbComponentsOutOfBoard`, `checkPcbComponentOverlap`, `checkPadPadClearance`, `checkCourtyardOverlap`, `checkConnectorAccessibleOrientation`, and `checkTestPointAccessibility`). |
38
+ | [`runAllPlacementChecks`](./lib/run-all-checks.ts) | Runs placement checks (`checkCopperToBoardEdgeClearance`, `checkPcbComponentsOutOfBoard`, `checkPcbCopperOverKeepout`, `checkPcbComponentOverlap`, `checkPadPadClearance`, `checkCourtyardOverlap`, `checkConnectorAccessibleOrientation`, and `checkTestPointAccessibility`). |
38
39
  | [`runAllNetlistChecks`](./lib/run-all-checks.ts) | Runs netlist connectivity checks (currently `checkPinMustBeConnected`). |
39
40
  | [`runAllPinSpecificationChecks`](./lib/run-all-checks.ts) | Runs pin specification checks (e.g. `checkAllPinsInComponentAreUnderspecified`, `checkNoPowerPinDefined`, and `checkNoGroundPinDefined`). |
40
41
  | [`runAllSchematicChecks`](./lib/run-all-checks.ts) | Runs schematic-layout checks (currently `checkSchematicComponentExcessiveVerticalPadding`). |
package/dist/index.d.ts CHANGED
@@ -41,6 +41,8 @@ declare function checkPcbComponentsOutOfBoard(circuitJson: AnyCircuitElement[]):
41
41
  */
42
42
  declare function checkPcbComponentOverCutout(circuitJson: AnyCircuitElement[]): PcbPlacementError[];
43
43
 
44
+ declare function checkPcbCopperOverKeepout(circuitJson: AnyCircuitElement[]): PcbPlacementError[];
45
+
44
46
  declare function checkSameNetViaSpacing(circuitJson: AnyCircuitElement[], { connMap, minClearance, }?: {
45
47
  connMap?: ConnectivityMap;
46
48
  minClearance?: number;
@@ -198,4 +200,4 @@ declare function checkConnectorAccessibleOrientation(circuitJson: AnyCircuitElem
198
200
  */
199
201
  declare function checkTestPointAccessibility(circuitJson: AnyCircuitElement[]): PcbPlacementError[];
200
202
 
201
- export { NetManager, checkAllPinsInComponentAreUnderspecified, checkConnectorAccessibleOrientation, checkCopperToBoardEdgeClearance, checkDifferentNetViaSpacing, checkEachPcbPortConnectedToPcbTraces, checkEachPcbTraceNonOverlapping, checkNoGroundPinDefined, checkNoPowerPinDefined, checkPadPadClearance, checkPadTraceClearance, checkPcbComponentOverCutout, checkPcbComponentOverlap, checkPcbComponentsMissingCourtyard, checkPcbComponentsOutOfBoard, checkPcbTraceLengths, checkPcbTraceViaCounts, checkPcbTracesOutOfBoard, checkPinMustBeConnected, checkSameNetViaSpacing, checkSchematicComponentExcessiveVerticalPadding, checkSourceTracesHavePcbTraces, checkSourceTracesMatchPcbTraceThickness, checkTestPointAccessibility, checkTracesAreContiguous, checkViaPadClearance, checkViaTraceClearance, checkViasInPads, checkViasOffBoard, dedupePcbDrcErrors, runAllChecks, runAllNetlistChecks, runAllPinSpecificationChecks, runAllPlacementChecks, runAllRoutingChecks, runAllSchematicChecks };
203
+ export { NetManager, checkAllPinsInComponentAreUnderspecified, checkConnectorAccessibleOrientation, checkCopperToBoardEdgeClearance, checkDifferentNetViaSpacing, checkEachPcbPortConnectedToPcbTraces, checkEachPcbTraceNonOverlapping, checkNoGroundPinDefined, checkNoPowerPinDefined, checkPadPadClearance, checkPadTraceClearance, checkPcbComponentOverCutout, checkPcbComponentOverlap, checkPcbComponentsMissingCourtyard, checkPcbComponentsOutOfBoard, checkPcbCopperOverKeepout, checkPcbTraceLengths, checkPcbTraceViaCounts, checkPcbTracesOutOfBoard, checkPinMustBeConnected, checkSameNetViaSpacing, checkSchematicComponentExcessiveVerticalPadding, checkSourceTracesHavePcbTraces, checkSourceTracesMatchPcbTraceThickness, checkTestPointAccessibility, checkTracesAreContiguous, checkViaPadClearance, checkViaTraceClearance, checkViasInPads, checkViasOffBoard, dedupePcbDrcErrors, runAllChecks, runAllNetlistChecks, runAllPinSpecificationChecks, runAllPlacementChecks, runAllRoutingChecks, runAllSchematicChecks };
package/dist/index.js CHANGED
@@ -605,8 +605,27 @@ var getPcbBoard = (circuitJson) => circuitJson.find((el) => el.type === "pcb_boa
605
605
  var getBoardDrcValue = (board, key) => board?.[key];
606
606
 
607
607
  // lib/check-pad-clearance/common.ts
608
- var getPadBounds = (pad) => getBoundsOfPcbElements2([pad]);
608
+ var getPadBounds = (pad) => {
609
+ if (pad.type === "pcb_keepout") {
610
+ if (pad.shape === "circle") {
611
+ return {
612
+ minX: pad.center.x - pad.radius,
613
+ minY: pad.center.y - pad.radius,
614
+ maxX: pad.center.x + pad.radius,
615
+ maxY: pad.center.y + pad.radius
616
+ };
617
+ }
618
+ return {
619
+ minX: pad.center.x - pad.width / 2,
620
+ minY: pad.center.y - pad.height / 2,
621
+ maxX: pad.center.x + pad.width / 2,
622
+ maxY: pad.center.y + pad.height / 2
623
+ };
624
+ }
625
+ return getBoundsOfPcbElements2([pad]);
626
+ };
609
627
  var getPadCenter = (pad) => {
628
+ if (pad.type === "pcb_keepout") return pad.center;
610
629
  const bounds = getPadBounds(pad);
611
630
  return midpoint(
612
631
  { x: bounds.minX, y: bounds.minY },
@@ -614,6 +633,7 @@ var getPadCenter = (pad) => {
614
633
  );
615
634
  };
616
635
  var getPadRadius = (pad) => {
636
+ if (pad.type === "pcb_keepout" && pad.shape === "circle") return pad.radius;
617
637
  const bounds = getPadBounds(pad);
618
638
  return Math.min(bounds.maxX - bounds.minX, bounds.maxY - bounds.minY) / 2;
619
639
  };
@@ -629,6 +649,32 @@ var getCircleShape = (pad) => {
629
649
  };
630
650
  };
631
651
  var getPolygonShape = (pad) => {
652
+ if (pad.type === "pcb_keepout") {
653
+ if (pad.shape !== "rect") {
654
+ throw new Error(`Expected rectangular keepout, got ${pad.shape}`);
655
+ }
656
+ return {
657
+ kind: "polygon",
658
+ points: [
659
+ {
660
+ x: pad.center.x - pad.width / 2,
661
+ y: pad.center.y - pad.height / 2
662
+ },
663
+ {
664
+ x: pad.center.x + pad.width / 2,
665
+ y: pad.center.y - pad.height / 2
666
+ },
667
+ {
668
+ x: pad.center.x + pad.width / 2,
669
+ y: pad.center.y + pad.height / 2
670
+ },
671
+ {
672
+ x: pad.center.x - pad.width / 2,
673
+ y: pad.center.y + pad.height / 2
674
+ }
675
+ ]
676
+ };
677
+ }
632
678
  if (pad.type === "pcb_smtpad" && (pad.shape === "polygon" || pad.shape === "rotated_rect")) {
633
679
  return {
634
680
  kind: "polygon",
@@ -2340,6 +2386,62 @@ function checkPcbComponentOverCutout(circuitJson) {
2340
2386
  return errors;
2341
2387
  }
2342
2388
 
2389
+ // lib/check-pcb-copper-over-keepout.ts
2390
+ import { cju as cju3, getPrimaryId as getPrimaryId2 } from "@tscircuit/circuit-json-util";
2391
+ var getErrorOwnerId = (copper) => "pcb_component_id" in copper && copper.pcb_component_id ? copper.pcb_component_id : getPrimaryId2(copper);
2392
+ var getReadableCopperName = (circuitJson, copper) => {
2393
+ if ("pcb_component_id" in copper && copper.pcb_component_id) {
2394
+ const pcbComponent = circuitJson.find(
2395
+ (element) => element.type === "pcb_component" && element.pcb_component_id === copper.pcb_component_id
2396
+ );
2397
+ const sourceComponent = pcbComponent?.type === "pcb_component" ? circuitJson.find(
2398
+ (element) => element.type === "source_component" && element.source_component_id === pcbComponent.source_component_id
2399
+ ) : void 0;
2400
+ const componentName = sourceComponent?.type === "source_component" && sourceComponent.name ? sourceComponent.name : getReadableNameForComponent(circuitJson, copper.pcb_component_id);
2401
+ return `component ${componentName}`;
2402
+ }
2403
+ return copper.type === "pcb_via" ? `via ${copper.pcb_via_id}` : `${copper.type} ${getPrimaryId2(copper)}`;
2404
+ };
2405
+ function checkPcbCopperOverKeepout(circuitJson) {
2406
+ const keepouts = cju3(circuitJson).pcb_keepout.list();
2407
+ if (keepouts.length === 0) return [];
2408
+ const copper = [
2409
+ ...getPads(circuitJson),
2410
+ ...cju3(circuitJson).pcb_via.list()
2411
+ ];
2412
+ const errors = /* @__PURE__ */ new Map();
2413
+ for (const keepout of keepouts) {
2414
+ const excludedComponentIds = new Set(
2415
+ keepout.excluded_pcb_component_ids ?? []
2416
+ );
2417
+ for (const copperElement of copper) {
2418
+ const copperComponentId = "pcb_component_id" in copperElement ? copperElement.pcb_component_id : void 0;
2419
+ if (copperComponentId && excludedComponentIds.has(copperComponentId)) {
2420
+ continue;
2421
+ }
2422
+ const copperLayers = getLayersOfPcbElement(copperElement);
2423
+ if (!copperLayers.some((layer) => keepout.layers.includes(layer))) {
2424
+ continue;
2425
+ }
2426
+ if (getPadToPadGap(copperElement, keepout) > EPSILON) continue;
2427
+ const ownerId = getErrorOwnerId(copperElement);
2428
+ const errorId = `copper_over_keepout_${ownerId}_${keepout.pcb_keepout_id}`;
2429
+ if (errors.has(errorId)) continue;
2430
+ errors.set(errorId, {
2431
+ type: "pcb_placement_error",
2432
+ pcb_placement_error_id: errorId,
2433
+ error_type: "pcb_placement_error",
2434
+ message: `Copper for ${getReadableCopperName(
2435
+ circuitJson,
2436
+ copperElement
2437
+ )} overlaps ${keepout.description ? `PCB keepout "${keepout.description}"` : "a PCB keepout"}`,
2438
+ subcircuit_id: copperElement.subcircuit_id ?? keepout.subcircuit_id
2439
+ });
2440
+ }
2441
+ }
2442
+ return [...errors.values()];
2443
+ }
2444
+
2343
2445
  // lib/check-same-net-via-spacing.ts
2344
2446
  import { getReadableNameForElement as getReadableNameForElement4 } from "@tscircuit/circuit-json-util";
2345
2447
  import {
@@ -2457,11 +2559,11 @@ function checkDifferentNetViaSpacing(circuitJson, {
2457
2559
  }
2458
2560
 
2459
2561
  // lib/check-source-traces-match-pcb-trace-thickness.ts
2460
- import { cju as cju3 } from "@tscircuit/circuit-json-util";
2562
+ import { cju as cju4 } from "@tscircuit/circuit-json-util";
2461
2563
  import { getFullConnectivityMapFromCircuitJson as getFullConnectivityMapFromCircuitJson5 } from "circuit-json-to-connectivity-map";
2462
2564
  function checkSourceTracesMatchPcbTraceThickness(circuitJson) {
2463
2565
  const warnings = [];
2464
- const db = cju3(circuitJson);
2566
+ const db = cju4(circuitJson);
2465
2567
  const sourceTraces = db.source_trace.list();
2466
2568
  const pcbTraces = db.pcb_trace.list();
2467
2569
  const pcbPorts = db.pcb_port.list();
@@ -2583,6 +2685,9 @@ import {
2583
2685
  getReadableNameForPcbTrace
2584
2686
  } from "@tscircuit/circuit-json-util";
2585
2687
  import { PcbConnectivityMap as PcbConnectivityMap2 } from "circuit-json-to-connectivity-map";
2688
+ function routePointTouchesPad(point, pad) {
2689
+ return point.route_type === "wire" && getLayersOfPcbElement(pad).includes(point.layer) && isPointInPad(point, pad);
2690
+ }
2586
2691
  function getRoutePointCenter(point) {
2587
2692
  if (point.route_type === "through_pad") {
2588
2693
  return {
@@ -2593,15 +2698,12 @@ function getRoutePointCenter(point) {
2593
2698
  return { x: point.x, y: point.y };
2594
2699
  }
2595
2700
  function routePointConnectsToAnotherExpectedPort(point, expectedPorts, missingPcbPortId, padMap) {
2596
- if (point.route_type !== "wire") return false;
2597
2701
  return expectedPorts.some((expectedPort) => {
2598
2702
  if (!expectedPort.pcb_port_id || expectedPort.pcb_port_id === missingPcbPortId) {
2599
2703
  return false;
2600
2704
  }
2601
2705
  const expectedPads = padMap.get(expectedPort.pcb_port_id);
2602
- return expectedPads?.some(
2603
- (pad) => isPointInPad({ x: point.x, y: point.y }, pad)
2604
- ) ?? false;
2706
+ return expectedPads?.some((pad) => routePointTouchesPad(point, pad)) ?? false;
2605
2707
  });
2606
2708
  }
2607
2709
  function getMissingConnectionErrorCenter({
@@ -2685,6 +2787,65 @@ function checkTracesAreContiguous(circuitJson) {
2685
2787
  ]);
2686
2788
  }
2687
2789
  }
2790
+ const touchedPortIdsByTraceId = /* @__PURE__ */ new Map();
2791
+ const traceIdsByTouchedPortId = /* @__PURE__ */ new Map();
2792
+ for (const trace of pcbTraces) {
2793
+ const touchedPortIds = /* @__PURE__ */ new Set();
2794
+ const firstPoint = trace.route[0];
2795
+ const lastPoint = trace.route.at(-1);
2796
+ for (const point of [firstPoint, lastPoint]) {
2797
+ if (!point) continue;
2798
+ for (const [pcbPortId, pads] of padMap) {
2799
+ if (pads.some((pad) => routePointTouchesPad(point, pad))) {
2800
+ touchedPortIds.add(pcbPortId);
2801
+ }
2802
+ }
2803
+ }
2804
+ touchedPortIdsByTraceId.set(trace.pcb_trace_id, touchedPortIds);
2805
+ for (const pcbPortId of touchedPortIds) {
2806
+ const traceIds = traceIdsByTouchedPortId.get(pcbPortId) ?? /* @__PURE__ */ new Set();
2807
+ traceIds.add(trace.pcb_trace_id);
2808
+ traceIdsByTouchedPortId.set(pcbPortId, traceIds);
2809
+ }
2810
+ }
2811
+ const physicallyConnectedTracesByTraceId = /* @__PURE__ */ new Map();
2812
+ const getPhysicallyConnectedTraces = (startTrace) => {
2813
+ const cached = physicallyConnectedTracesByTraceId.get(
2814
+ startTrace.pcb_trace_id
2815
+ );
2816
+ if (cached) return cached;
2817
+ const connectedTraceIds = /* @__PURE__ */ new Set();
2818
+ const pendingTraceIds = [startTrace.pcb_trace_id];
2819
+ while (pendingTraceIds.length > 0) {
2820
+ const traceId = pendingTraceIds.pop();
2821
+ if (connectedTraceIds.has(traceId)) continue;
2822
+ connectedTraceIds.add(traceId);
2823
+ for (const connectedTrace of pcbConnectivityMap.getAllTracesConnectedToTrace(
2824
+ traceId
2825
+ )) {
2826
+ if (!connectedTraceIds.has(connectedTrace.pcb_trace_id)) {
2827
+ pendingTraceIds.push(connectedTrace.pcb_trace_id);
2828
+ }
2829
+ }
2830
+ for (const pcbPortId of touchedPortIdsByTraceId.get(traceId) ?? []) {
2831
+ for (const touchingTraceId of traceIdsByTouchedPortId.get(pcbPortId) ?? []) {
2832
+ if (!connectedTraceIds.has(touchingTraceId)) {
2833
+ pendingTraceIds.push(touchingTraceId);
2834
+ }
2835
+ }
2836
+ }
2837
+ }
2838
+ const connectedTraces = pcbTraces.filter(
2839
+ (trace) => connectedTraceIds.has(trace.pcb_trace_id)
2840
+ );
2841
+ for (const trace of connectedTraces) {
2842
+ physicallyConnectedTracesByTraceId.set(
2843
+ trace.pcb_trace_id,
2844
+ connectedTraces
2845
+ );
2846
+ }
2847
+ return connectedTraces;
2848
+ };
2688
2849
  for (const trace of pcbTraces) {
2689
2850
  if (trace.route.length === 0) continue;
2690
2851
  const firstPoint = trace.route[0];
@@ -2736,39 +2897,17 @@ function checkTracesAreContiguous(circuitJson) {
2736
2897
  if (!port.pcb_port_id) continue;
2737
2898
  const pads = padMap.get(port.pcb_port_id);
2738
2899
  if (!pads?.length) continue;
2739
- let isConnectedByRoutedSourceTrace = false;
2740
- const candidateTraces = [
2741
- ...pcbConnectivityMap.getAllTracesConnectedToTrace(trace.pcb_trace_id),
2742
- ...pcbTraces.filter(
2743
- (candidateTrace) => candidateTrace.source_trace_id === sourceTrace?.source_trace_id
2744
- )
2745
- ];
2746
- for (const candidateTrace of candidateTraces) {
2747
- if (candidateTrace.pcb_trace_id === trace.pcb_trace_id) continue;
2748
- if (!candidateTrace.source_trace_id) continue;
2749
- const candidateSourceTrace = sourceTraces.find(
2750
- (st) => st.source_trace_id === candidateTrace.source_trace_id
2751
- );
2752
- if (!sourceTrace || candidateTrace.source_trace_id !== sourceTrace.source_trace_id && !candidateSourceTrace?.connected_source_port_ids.includes(
2753
- port.source_port_id
2754
- )) {
2755
- continue;
2756
- }
2757
- const candidateFirstPoint = candidateTrace.route[0];
2758
- const candidateLastPoint = candidateTrace.route.at(-1);
2759
- if (getPcbPortIdsConnectedToTrace(candidateTrace).includes(
2760
- port.pcb_port_id
2761
- ) || candidateFirstPoint?.route_type === "wire" && pads.some((pad) => isPointInPad(candidateFirstPoint, pad)) || candidateLastPoint?.route_type === "wire" && pads.some((pad) => isPointInPad(candidateLastPoint, pad))) {
2762
- isConnectedByRoutedSourceTrace = true;
2763
- break;
2764
- }
2765
- }
2900
+ const isConnectedByRoutedSourceTrace = getPhysicallyConnectedTraces(
2901
+ trace
2902
+ ).some(
2903
+ (candidateTrace) => touchedPortIdsByTraceId.get(candidateTrace.pcb_trace_id)?.has(port.pcb_port_id)
2904
+ );
2766
2905
  if (isConnectedByRoutedSourceTrace) continue;
2767
- const isFirstPointConnected = firstPoint.route_type === "wire" && pads.some(
2768
- (pad) => isPointInPad({ x: firstPoint.x, y: firstPoint.y }, pad)
2906
+ const isFirstPointConnected = pads.some(
2907
+ (pad) => routePointTouchesPad(firstPoint, pad)
2769
2908
  );
2770
- const isLastPointConnected = lastPoint.route_type === "wire" && pads.some(
2771
- (pad) => isPointInPad({ x: lastPoint.x, y: lastPoint.y }, pad)
2909
+ const isLastPointConnected = pads.some(
2910
+ (pad) => routePointTouchesPad(lastPoint, pad)
2772
2911
  );
2773
2912
  if (!isFirstPointConnected && !isLastPointConnected) {
2774
2913
  const portName = getReadableNameForPcbPort2(
@@ -2801,14 +2940,10 @@ function checkTracesAreContiguous(circuitJson) {
2801
2940
  let lastConnectsToAnyPad = false;
2802
2941
  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;
2803
2942
  for (const pads of padMap.values()) {
2804
- if (firstPoint.route_type === "wire" && pads.some(
2805
- (pad) => isPointInPad({ x: firstPoint.x, y: firstPoint.y }, pad)
2806
- )) {
2943
+ if (pads.some((pad) => routePointTouchesPad(firstPoint, pad))) {
2807
2944
  firstConnectsToAnyPad = true;
2808
2945
  }
2809
- if (lastPoint.route_type === "wire" && pads.some(
2810
- (pad) => isPointInPad({ x: lastPoint.x, y: lastPoint.y }, pad)
2811
- )) {
2946
+ if (pads.some((pad) => routePointTouchesPad(lastPoint, pad))) {
2812
2947
  lastConnectsToAnyPad = true;
2813
2948
  }
2814
2949
  }
@@ -2844,7 +2979,7 @@ function checkTracesAreContiguous(circuitJson) {
2844
2979
  }
2845
2980
 
2846
2981
  // lib/check-trace-out-of-board/checkTraceOutOfBoard.ts
2847
- import { cju as cju4 } from "@tscircuit/circuit-json-util";
2982
+ import { cju as cju5 } from "@tscircuit/circuit-json-util";
2848
2983
  import { segmentToSegmentMinDistance as segmentToSegmentMinDistance4 } from "@tscircuit/math-utils";
2849
2984
  function getBoardPolygonPoints(board) {
2850
2985
  if (board.outline && board.outline.length > 0) {
@@ -2875,7 +3010,7 @@ function checkPcbTracesOutOfBoard(circuitJson, config = {}) {
2875
3010
  const margin = config.margin ?? getBoardDrcValue(board, "min_board_edge_clearance") ?? jlcMinTolerances.min_board_edge_clearance;
2876
3011
  const boardPoints = getBoardPolygonPoints(board);
2877
3012
  if (!boardPoints) return errors;
2878
- const pcbTraces = cju4(circuitJson).pcb_trace.list();
3013
+ const pcbTraces = cju5(circuitJson).pcb_trace.list();
2879
3014
  for (const trace of pcbTraces) {
2880
3015
  if (trace.route.length < 2) continue;
2881
3016
  for (let i = 0; i < trace.route.length - 1; i++) {
@@ -2924,9 +3059,9 @@ function checkPcbTracesOutOfBoard(circuitJson, config = {}) {
2924
3059
 
2925
3060
  // lib/check-pcb-components-overlap/checkPcbComponentOverlap.ts
2926
3061
  import {
2927
- cju as cju5,
3062
+ cju as cju6,
2928
3063
  getBoundsOfPcbElements as getBoundsOfPcbElements5,
2929
- getPrimaryId as getPrimaryId2
3064
+ getPrimaryId as getPrimaryId3
2930
3065
  } from "@tscircuit/circuit-json-util";
2931
3066
  import { doBoundsOverlap as doBoundsOverlap3 } from "@tscircuit/math-utils";
2932
3067
  import { getFullConnectivityMapFromCircuitJson as getFullConnectivityMapFromCircuitJson7 } from "circuit-json-to-connectivity-map";
@@ -2962,20 +3097,20 @@ var formatOverlapElementDescription = (circuitJson, element) => {
2962
3097
  if ("pcb_port_id" in element && element.pcb_port_id) {
2963
3098
  return getReadableNameForPort(circuitJson, element.pcb_port_id);
2964
3099
  }
2965
- const id = getPrimaryId2(element);
3100
+ const id = getPrimaryId3(element);
2966
3101
  const readableName = getReadableNameForElementId(circuitJson, id);
2967
3102
  return readableName === "element" ? `[${id}]` : readableName;
2968
3103
  };
2969
3104
  function checkPcbComponentOverlap(circuitJson) {
2970
3105
  const errors = [];
2971
3106
  const connMap = getFullConnectivityMapFromCircuitJson7(circuitJson);
2972
- const smtPads = cju5(circuitJson).pcb_smtpad.list();
2973
- const platedHoles = cju5(circuitJson).pcb_plated_hole.list();
2974
- const holes = cju5(circuitJson).pcb_hole.list();
3107
+ const smtPads = cju6(circuitJson).pcb_smtpad.list();
3108
+ const platedHoles = cju6(circuitJson).pcb_plated_hole.list();
3109
+ const holes = cju6(circuitJson).pcb_hole.list();
2975
3110
  const courtyards = circuitJson.filter(isCourtyardElement);
2976
3111
  const componentMap = /* @__PURE__ */ new Map();
2977
3112
  for (const pad of smtPads) {
2978
- const componentId = pad.pcb_component_id || `standalone_pad_${getPrimaryId2(pad)}`;
3113
+ const componentId = pad.pcb_component_id || `standalone_pad_${getPrimaryId3(pad)}`;
2979
3114
  if (!componentMap.has(componentId)) {
2980
3115
  componentMap.set(componentId, {
2981
3116
  component_id: componentId,
@@ -2985,7 +3120,7 @@ function checkPcbComponentOverlap(circuitJson) {
2985
3120
  componentMap.get(componentId).elements.push(pad);
2986
3121
  }
2987
3122
  for (const hole of platedHoles) {
2988
- const componentId = hole.pcb_component_id || `standalone_plated_hole_${getPrimaryId2(hole)}`;
3123
+ const componentId = hole.pcb_component_id || `standalone_plated_hole_${getPrimaryId3(hole)}`;
2989
3124
  if (!componentMap.has(componentId)) {
2990
3125
  componentMap.set(componentId, {
2991
3126
  component_id: componentId,
@@ -2995,7 +3130,7 @@ function checkPcbComponentOverlap(circuitJson) {
2995
3130
  componentMap.get(componentId).elements.push(hole);
2996
3131
  }
2997
3132
  for (const hole of holes) {
2998
- const componentId = hole.pcb_component_id || `standalone_hole_${getPrimaryId2(hole)}`;
3133
+ const componentId = hole.pcb_component_id || `standalone_hole_${getPrimaryId3(hole)}`;
2999
3134
  if (!componentMap.has(componentId)) {
3000
3135
  componentMap.set(componentId, {
3001
3136
  component_id: componentId,
@@ -3031,8 +3166,8 @@ function checkPcbComponentOverlap(circuitJson) {
3031
3166
  }
3032
3167
  for (const elem1 of comp1.elements) {
3033
3168
  for (const elem2 of comp2.elements) {
3034
- const id1 = getPrimaryId2(elem1);
3035
- const id2 = getPrimaryId2(elem2);
3169
+ const id1 = getPrimaryId3(elem1);
3170
+ const id2 = getPrimaryId3(elem2);
3036
3171
  if ((isCourtyardElement(elem1) || isCourtyardElement(elem2)) && elem1.type !== "pcb_hole" && elem2.type !== "pcb_hole") {
3037
3172
  continue;
3038
3173
  }
@@ -3227,7 +3362,7 @@ var checkPcbTraceViaCounts = (circuitJson) => {
3227
3362
 
3228
3363
  // lib/check-pad-pad-clearance.ts
3229
3364
  import {
3230
- getPrimaryId as getPrimaryId3,
3365
+ getPrimaryId as getPrimaryId4,
3231
3366
  getReadableNameForElement as getReadableNameForElement6
3232
3367
  } from "@tscircuit/circuit-json-util";
3233
3368
  import { formatMm } from "format-si-unit";
@@ -3246,17 +3381,17 @@ function checkPadPadClearance(circuitJson, {
3246
3381
  const spatialIndex = new SpatialObjectIndex({
3247
3382
  objects: pads,
3248
3383
  getBounds: getPadBounds,
3249
- getId: (pad) => getPrimaryId3(pad)
3384
+ getId: (pad) => getPrimaryId4(pad)
3250
3385
  });
3251
3386
  const errors = /* @__PURE__ */ new Map();
3252
3387
  for (const padA of pads) {
3253
- const padAId = getPrimaryId3(padA);
3388
+ const padAId = getPrimaryId4(padA);
3254
3389
  const nearbyPads = spatialIndex.getObjectsInBounds(
3255
3390
  getPadBounds(padA),
3256
3391
  minClearance
3257
3392
  );
3258
3393
  for (const padB of nearbyPads) {
3259
- const padBId = getPrimaryId3(padB);
3394
+ const padBId = getPrimaryId4(padB);
3260
3395
  if (padAId === padBId) continue;
3261
3396
  if (!getLayersOfPcbElement(padA).some(
3262
3397
  (layer) => getLayersOfPcbElement(padB).includes(layer)
@@ -3292,7 +3427,7 @@ function checkPadPadClearance(circuitJson, {
3292
3427
 
3293
3428
  // lib/check-pad-trace-clearance.ts
3294
3429
  import {
3295
- getPrimaryId as getPrimaryId4,
3430
+ getPrimaryId as getPrimaryId5,
3296
3431
  getReadableNameForElement as getReadableNameForElement7
3297
3432
  } from "@tscircuit/circuit-json-util";
3298
3433
  import { formatMm as formatMm2 } from "format-si-unit";
@@ -3312,7 +3447,7 @@ function checkPadTraceClearance(circuitJson, {
3312
3447
  const spatialIndex = new SpatialObjectIndex({
3313
3448
  objects: pads,
3314
3449
  getBounds: getPadBounds,
3315
- getId: (pad) => getPrimaryId4(pad)
3450
+ getId: (pad) => getPrimaryId5(pad)
3316
3451
  });
3317
3452
  const errors = /* @__PURE__ */ new Map();
3318
3453
  const overlappingPairIds = /* @__PURE__ */ new Set();
@@ -3322,7 +3457,7 @@ function checkPadTraceClearance(circuitJson, {
3322
3457
  minClearance + segment.thickness / 2
3323
3458
  );
3324
3459
  for (const pad of nearbyPads) {
3325
- const padId = getPrimaryId4(pad);
3460
+ const padId = getPrimaryId5(pad);
3326
3461
  if (!getLayersOfPcbElement(pad).includes(segment.layer)) continue;
3327
3462
  if (connMap.areIdsConnected(segment.pcb_trace_id, padId)) continue;
3328
3463
  const pairId = `${padId}_${segment.pcb_trace_id}`;
@@ -3408,7 +3543,7 @@ function checkViaTraceClearance(circuitJson, {
3408
3543
 
3409
3544
  // lib/check-via-pad-clearance.ts
3410
3545
  import {
3411
- getPrimaryId as getPrimaryId5,
3546
+ getPrimaryId as getPrimaryId6,
3412
3547
  getReadableNameForElement as getReadableNameForElement9
3413
3548
  } from "@tscircuit/circuit-json-util";
3414
3549
  import { formatMm as formatMm4 } from "format-si-unit";
@@ -3430,7 +3565,7 @@ function checkViaPadClearance(circuitJson, {
3430
3565
  const padIndex = new SpatialObjectIndex({
3431
3566
  objects: pads,
3432
3567
  getBounds: getPadBounds,
3433
- getId: getPrimaryId5
3568
+ getId: getPrimaryId6
3434
3569
  });
3435
3570
  const errors = [];
3436
3571
  for (const via of vias) {
@@ -3439,7 +3574,7 @@ function checkViaPadClearance(circuitJson, {
3439
3574
  requiredClearance
3440
3575
  );
3441
3576
  for (const pad of nearbyPads) {
3442
- const padId = getPrimaryId5(pad);
3577
+ const padId = getPrimaryId6(pad);
3443
3578
  if (!getLayersOfPcbElement(via).some(
3444
3579
  (layer) => getLayersOfPcbElement(pad).includes(layer)
3445
3580
  )) {
@@ -3469,7 +3604,7 @@ function checkViaPadClearance(circuitJson, {
3469
3604
  }
3470
3605
 
3471
3606
  // lib/check-vias-in-pads.ts
3472
- import { getPrimaryId as getPrimaryId6 } from "@tscircuit/circuit-json-util";
3607
+ import { getPrimaryId as getPrimaryId7 } from "@tscircuit/circuit-json-util";
3473
3608
  function checkViasInPads(circuitJson) {
3474
3609
  const board = getPcbBoard(circuitJson);
3475
3610
  if (board && "is_via_in_pad_allowed" in board && board.is_via_in_pad_allowed === true) {
@@ -3481,12 +3616,12 @@ function checkViasInPads(circuitJson) {
3481
3616
  const pads = getPads(circuitJson);
3482
3617
  if (vias.length === 0 || pads.length === 0) return [];
3483
3618
  const padOrdinals = new Map(
3484
- pads.map((pad, index) => [getPrimaryId6(pad), index])
3619
+ pads.map((pad, index) => [getPrimaryId7(pad), index])
3485
3620
  );
3486
3621
  const padIndex = new SpatialObjectIndex({
3487
3622
  objects: pads,
3488
3623
  getBounds: getPadBounds,
3489
- getId: getPrimaryId6
3624
+ getId: getPrimaryId7
3490
3625
  });
3491
3626
  const errors = [];
3492
3627
  for (const via of vias) {
@@ -3501,7 +3636,7 @@ function checkViasInPads(circuitJson) {
3501
3636
  const padLayers = getLayersOfPcbElement(pad);
3502
3637
  if (!viaLayers.some((layer) => padLayers.includes(layer))) continue;
3503
3638
  if (!isPointInPad(via, pad)) continue;
3504
- const padId = getPrimaryId6(pad);
3639
+ const padId = getPrimaryId7(pad);
3505
3640
  const padOrdinal = padOrdinals.get(padId) ?? 0;
3506
3641
  const padName = getReadableNameForFootprintPad(
3507
3642
  circuitJson,
@@ -3600,7 +3735,7 @@ function checkPinMustBeConnected(circuitJson) {
3600
3735
  }
3601
3736
 
3602
3737
  // lib/check-all-pins-in-component-are-underspecified.ts
3603
- import { cju as cju6 } from "@tscircuit/circuit-json-util";
3738
+ import { cju as cju7 } from "@tscircuit/circuit-json-util";
3604
3739
  var PIN_ATTRIBUTE_KEYS = [
3605
3740
  "must_be_connected",
3606
3741
  "provides_power",
@@ -3645,7 +3780,7 @@ function hasAnyPinAttribute(port) {
3645
3780
  }
3646
3781
  function checkAllPinsInComponentAreUnderspecified(circuitJson) {
3647
3782
  const warnings = [];
3648
- const db = cju6(circuitJson);
3783
+ const db = cju7(circuitJson);
3649
3784
  const sourceComponents = db.source_component.list();
3650
3785
  const sourcePorts = db.source_port.list();
3651
3786
  const portsByComponent = /* @__PURE__ */ new Map();
@@ -3677,10 +3812,10 @@ function checkAllPinsInComponentAreUnderspecified(circuitJson) {
3677
3812
  }
3678
3813
 
3679
3814
  // lib/check-no-power-pin-defined.ts
3680
- import { cju as cju7 } from "@tscircuit/circuit-json-util";
3815
+ import { cju as cju8 } from "@tscircuit/circuit-json-util";
3681
3816
  function checkNoPowerPinDefined(circuitJson) {
3682
3817
  const warnings = [];
3683
- const db = cju7(circuitJson);
3818
+ const db = cju8(circuitJson);
3684
3819
  const sourceComponents = db.source_component.list();
3685
3820
  const sourcePorts = db.source_port.list();
3686
3821
  const portsByComponent = /* @__PURE__ */ new Map();
@@ -3712,10 +3847,10 @@ function checkNoPowerPinDefined(circuitJson) {
3712
3847
  }
3713
3848
 
3714
3849
  // lib/check-no-ground-pin-defined.ts
3715
- import { cju as cju8 } from "@tscircuit/circuit-json-util";
3850
+ import { cju as cju9 } from "@tscircuit/circuit-json-util";
3716
3851
  function checkNoGroundPinDefined(circuitJson) {
3717
3852
  const warnings = [];
3718
- const db = cju8(circuitJson);
3853
+ const db = cju9(circuitJson);
3719
3854
  const sourceComponents = db.source_component.list();
3720
3855
  const sourcePorts = db.source_port.list();
3721
3856
  const portsByComponent = /* @__PURE__ */ new Map();
@@ -4091,6 +4226,7 @@ async function runAllPlacementChecks(circuitJson) {
4091
4226
  ...checkViasInPads(circuitJson),
4092
4227
  ...checkPcbComponentsOutOfBoard(circuitJson),
4093
4228
  ...checkPcbComponentOverCutout(circuitJson),
4229
+ ...checkPcbCopperOverKeepout(circuitJson),
4094
4230
  ...checkPcbComponentOverlap(circuitJson),
4095
4231
  ...checkPcbComponentsMissingCourtyard(circuitJson),
4096
4232
  ...checkPadPadClearance(circuitJson),
@@ -4153,6 +4289,7 @@ export {
4153
4289
  checkPcbComponentOverlap,
4154
4290
  checkPcbComponentsMissingCourtyard,
4155
4291
  checkPcbComponentsOutOfBoard,
4292
+ checkPcbCopperOverKeepout,
4156
4293
  checkPcbTraceLengths,
4157
4294
  checkPcbTraceViaCounts,
4158
4295
  checkPcbTracesOutOfBoard,