@tscircuit/checks 0.0.117 → 0.0.118

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 CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as circuit_json from 'circuit-json';
2
- import { AnyCircuitElement, PcbPortNotConnectedError, PcbTraceError, PcbPlacementError, PcbComponentOutsideBoardError, PcbViaClearanceError, PcbTraceWarning, PcbTraceMissingError, PcbFootprintOverlapError, SourceComponentPinsUnderspecifiedWarning, SourceNoPowerPinDefinedWarning, SourceNoGroundPinDefinedWarning, PcbConnectorNotInAccessibleOrientationWarning } from 'circuit-json';
2
+ import { AnyCircuitElement, PcbPortNotConnectedError, PcbTraceError, PcbPlacementError, PcbComponentOutsideBoardError, PcbViaClearanceError, PcbTraceWarning, PcbTraceMissingError, PcbFootprintOverlapError, PcbPadPadClearanceError, PcbPadTraceClearanceError, SourceComponentPinsUnderspecifiedWarning, SourceNoPowerPinDefinedWarning, SourceNoGroundPinDefinedWarning, PcbConnectorNotInAccessibleOrientationWarning } from 'circuit-json';
3
3
  import { ConnectivityMap } from 'circuit-json-to-connectivity-map';
4
4
 
5
5
  declare function checkEachPcbPortConnectedToPcbTraces(circuitJson: AnyCircuitElement[]): PcbPortNotConnectedError[];
@@ -61,6 +61,16 @@ declare function checkPcbTracesOutOfBoard(circuitJson: AnyCircuitElement[], conf
61
61
  */
62
62
  declare function checkPcbComponentOverlap(circuitJson: AnyCircuitElement[]): PcbFootprintOverlapError[];
63
63
 
64
+ declare function checkPadPadClearance(circuitJson: AnyCircuitElement[], { connMap, minSpacing, }?: {
65
+ connMap?: ConnectivityMap;
66
+ minSpacing?: number;
67
+ }): PcbPadPadClearanceError[];
68
+
69
+ declare function checkPadTraceClearance(circuitJson: AnyCircuitElement[], { connMap, minSpacing, }?: {
70
+ connMap?: ConnectivityMap;
71
+ minSpacing?: number;
72
+ }): PcbPadTraceClearanceError[];
73
+
64
74
  type SourcePinMustBeConnectedError = {
65
75
  type: "source_pin_must_be_connected_error";
66
76
  source_pin_must_be_connected_error_id: string;
@@ -96,7 +106,7 @@ declare function checkNoPowerPinDefined(circuitJson: AnyCircuitElement[]): Sourc
96
106
  */
97
107
  declare function checkNoGroundPinDefined(circuitJson: AnyCircuitElement[]): SourceNoGroundPinDefinedWarning[];
98
108
 
99
- declare function runAllPlacementChecks(circuitJson: AnyCircuitElement[]): Promise<(circuit_json.PcbPlacementError | circuit_json.PcbComponentOutsideBoardError | circuit_json.PcbFootprintOverlapError | circuit_json.PcbCourtyardOverlapError | circuit_json.PcbConnectorNotInAccessibleOrientationWarning)[]>;
109
+ declare function runAllPlacementChecks(circuitJson: AnyCircuitElement[]): Promise<(circuit_json.PcbPlacementError | circuit_json.PcbComponentOutsideBoardError | circuit_json.PcbFootprintOverlapError | circuit_json.PcbPadPadClearanceError | circuit_json.PcbPadTraceClearanceError | circuit_json.PcbConnectorNotInAccessibleOrientationWarning | circuit_json.PcbCourtyardOverlapError)[]>;
100
110
  declare function runAllNetlistChecks(circuitJson: AnyCircuitElement[]): Promise<{
101
111
  type: "source_pin_must_be_connected_error";
102
112
  source_pin_must_be_connected_error_id: string;
@@ -108,7 +118,7 @@ declare function runAllNetlistChecks(circuitJson: AnyCircuitElement[]): Promise<
108
118
  }[]>;
109
119
  declare function runAllPinSpecificationChecks(circuitJson: AnyCircuitElement[]): Promise<(circuit_json.SourceComponentPinsUnderspecifiedWarning | circuit_json.SourceNoPowerPinDefinedWarning | circuit_json.SourceNoGroundPinDefinedWarning)[]>;
110
120
  declare function runAllRoutingChecks(circuitJson: AnyCircuitElement[]): Promise<(circuit_json.PcbPortNotConnectedError | circuit_json.PcbTraceError | circuit_json.PcbViaClearanceError | circuit_json.PcbTraceMissingError)[]>;
111
- declare function runAllChecks(circuitJson: AnyCircuitElement[]): Promise<(circuit_json.PcbPortNotConnectedError | circuit_json.PcbTraceError | circuit_json.PcbPlacementError | circuit_json.PcbComponentOutsideBoardError | circuit_json.PcbViaClearanceError | circuit_json.PcbTraceMissingError | circuit_json.PcbFootprintOverlapError | {
121
+ declare function runAllChecks(circuitJson: AnyCircuitElement[]): Promise<(circuit_json.PcbPortNotConnectedError | circuit_json.PcbTraceError | circuit_json.PcbPlacementError | circuit_json.PcbComponentOutsideBoardError | circuit_json.PcbViaClearanceError | circuit_json.PcbTraceMissingError | circuit_json.PcbFootprintOverlapError | circuit_json.PcbPadPadClearanceError | circuit_json.PcbPadTraceClearanceError | {
112
122
  type: "source_pin_must_be_connected_error";
113
123
  source_pin_must_be_connected_error_id: string;
114
124
  error_type: "source_pin_must_be_connected_error";
@@ -116,8 +126,8 @@ declare function runAllChecks(circuitJson: AnyCircuitElement[]): Promise<(circui
116
126
  source_component_id: string;
117
127
  source_port_id: string;
118
128
  subcircuit_id?: string;
119
- } | circuit_json.SourceComponentPinsUnderspecifiedWarning | circuit_json.SourceNoPowerPinDefinedWarning | circuit_json.SourceNoGroundPinDefinedWarning | circuit_json.PcbCourtyardOverlapError | circuit_json.PcbConnectorNotInAccessibleOrientationWarning)[]>;
129
+ } | circuit_json.SourceComponentPinsUnderspecifiedWarning | circuit_json.SourceNoPowerPinDefinedWarning | circuit_json.SourceNoGroundPinDefinedWarning | circuit_json.PcbConnectorNotInAccessibleOrientationWarning | circuit_json.PcbCourtyardOverlapError)[]>;
120
130
 
121
131
  declare function checkConnectorAccessibleOrientation(circuitJson: AnyCircuitElement[]): PcbConnectorNotInAccessibleOrientationWarning[];
122
132
 
123
- export { NetManager, checkAllPinsInComponentAreUnderspecified, checkConnectorAccessibleOrientation, checkDifferentNetViaSpacing, checkEachPcbPortConnectedToPcbTraces, checkEachPcbTraceNonOverlapping, checkNoGroundPinDefined, checkNoPowerPinDefined, checkPcbComponentOverlap, checkPcbComponentsOutOfBoard, checkPcbTracesOutOfBoard, checkPinMustBeConnected, checkSameNetViaSpacing, checkSourceTracesHavePcbTraces, checkSourceTracesMatchPcbTraceThickness, checkViasOffBoard, runAllChecks, runAllNetlistChecks, runAllPinSpecificationChecks, runAllPlacementChecks, runAllRoutingChecks };
133
+ export { NetManager, checkAllPinsInComponentAreUnderspecified, checkConnectorAccessibleOrientation, checkDifferentNetViaSpacing, checkEachPcbPortConnectedToPcbTraces, checkEachPcbTraceNonOverlapping, checkNoGroundPinDefined, checkNoPowerPinDefined, checkPadPadClearance, checkPadTraceClearance, checkPcbComponentOverlap, checkPcbComponentsOutOfBoard, checkPcbTracesOutOfBoard, checkPinMustBeConnected, checkSameNetViaSpacing, checkSourceTracesHavePcbTraces, checkSourceTracesMatchPcbTraceThickness, checkViasOffBoard, runAllChecks, runAllNetlistChecks, runAllPinSpecificationChecks, runAllPlacementChecks, runAllRoutingChecks };
package/dist/index.js CHANGED
@@ -369,6 +369,8 @@ var DEFAULT_TRACE_THICKNESS = 0.15;
369
369
  var DEFAULT_VIA_BOARD_MARGIN = 0.3;
370
370
  var DEFAULT_SAME_NET_VIA_MARGIN = 0.2;
371
371
  var DEFAULT_DIFFERENT_NET_VIA_MARGIN = 0.3;
372
+ var DEFAULT_PAD_PAD_CLEARANCE = 0.1;
373
+ var DEFAULT_PAD_TRACE_CLEARANCE = 0.1;
372
374
  var EPSILON = 5e-3;
373
375
 
374
376
  // lib/check-each-pcb-trace-non-overlapping/getPcbPortIdsConnectedToTraces.ts
@@ -1601,6 +1603,241 @@ function checkPcbComponentOverlap(circuitJson) {
1601
1603
  return errors;
1602
1604
  }
1603
1605
 
1606
+ // lib/check-pad-pad-clearance.ts
1607
+ import {
1608
+ getPrimaryId as getPrimaryId3,
1609
+ getReadableNameForElement as getReadableNameForElement6
1610
+ } from "@tscircuit/circuit-json-util";
1611
+ import {
1612
+ getFullConnectivityMapFromCircuitJson as getFullConnectivityMapFromCircuitJson7
1613
+ } from "circuit-json-to-connectivity-map";
1614
+
1615
+ // lib/check-pad-clearance/common.ts
1616
+ import {
1617
+ cju as cju5,
1618
+ distanceBetweenCircleAndCircle,
1619
+ distanceBetweenCircleAndPolygon,
1620
+ distanceBetweenPolygonAndPolygon,
1621
+ getBoundsOfPcbElements as getBoundsOfPcbElements5
1622
+ } from "@tscircuit/circuit-json-util";
1623
+ import { midpoint } from "@tscircuit/math-utils";
1624
+ var formatMm = (value) => {
1625
+ const rounded = Math.round(value * 1e3) / 1e3;
1626
+ return `${Number(rounded.toFixed(3))}mm`;
1627
+ };
1628
+ var getPadBounds = (pad) => getBoundsOfPcbElements5([pad]);
1629
+ var getPadCenter = (pad) => {
1630
+ const bounds = getPadBounds(pad);
1631
+ return midpoint(
1632
+ { x: bounds.minX, y: bounds.minY },
1633
+ { x: bounds.maxX, y: bounds.maxY }
1634
+ );
1635
+ };
1636
+ var getPadRadius = (pad) => {
1637
+ const bounds = getPadBounds(pad);
1638
+ return Math.min(bounds.maxX - bounds.minX, bounds.maxY - bounds.minY) / 2;
1639
+ };
1640
+ var isCircularPad = (pad) => pad.shape === "circle";
1641
+ var getCircleShape = (pad) => {
1642
+ const center = getPadCenter(pad);
1643
+ return {
1644
+ kind: "circle",
1645
+ x: center.x,
1646
+ y: center.y,
1647
+ radius: getPadRadius(pad)
1648
+ };
1649
+ };
1650
+ var getPolygonShape = (pad) => {
1651
+ const bounds = getPadBounds(pad);
1652
+ return {
1653
+ kind: "polygon",
1654
+ points: [
1655
+ { x: bounds.minX, y: bounds.minY },
1656
+ { x: bounds.maxX, y: bounds.minY },
1657
+ { x: bounds.maxX, y: bounds.maxY },
1658
+ { x: bounds.minX, y: bounds.maxY }
1659
+ ]
1660
+ };
1661
+ };
1662
+ var getPadToPadGap = (padA, padB) => {
1663
+ if (isCircularPad(padA) && isCircularPad(padB)) {
1664
+ return distanceBetweenCircleAndCircle(
1665
+ getCircleShape(padA),
1666
+ getCircleShape(padB)
1667
+ );
1668
+ }
1669
+ if (isCircularPad(padA)) {
1670
+ return distanceBetweenCircleAndPolygon(
1671
+ getCircleShape(padA),
1672
+ getPolygonShape(padB)
1673
+ );
1674
+ }
1675
+ if (isCircularPad(padB)) {
1676
+ return distanceBetweenCircleAndPolygon(
1677
+ getCircleShape(padB),
1678
+ getPolygonShape(padA)
1679
+ );
1680
+ }
1681
+ return distanceBetweenPolygonAndPolygon(
1682
+ getPolygonShape(padA),
1683
+ getPolygonShape(padB)
1684
+ );
1685
+ };
1686
+ var getPads = (circuitJson) => [
1687
+ ...cju5(circuitJson).pcb_smtpad.list(),
1688
+ ...cju5(circuitJson).pcb_plated_hole.list()
1689
+ ];
1690
+ var getTraceSegments = (circuitJson) => {
1691
+ const pcbTraces = cju5(circuitJson).pcb_trace.list();
1692
+ return pcbTraces.flatMap((pcbTrace) => {
1693
+ const segments = [];
1694
+ for (let i = 0; i < pcbTrace.route.length - 1; i++) {
1695
+ const p1 = pcbTrace.route[i];
1696
+ const p2 = pcbTrace.route[i + 1];
1697
+ if (p1.route_type !== "wire") continue;
1698
+ if (p2.route_type !== "wire") continue;
1699
+ if (p1.layer !== p2.layer) continue;
1700
+ segments.push({
1701
+ type: "pcb_trace_segment",
1702
+ pcb_trace_id: pcbTrace.pcb_trace_id,
1703
+ _pcbTrace: pcbTrace,
1704
+ thickness: "width" in p1 ? p1.width : "width" in p2 ? p2.width : DEFAULT_TRACE_THICKNESS,
1705
+ layer: p1.layer,
1706
+ x1: p1.x,
1707
+ y1: p1.y,
1708
+ x2: p2.x,
1709
+ y2: p2.y
1710
+ });
1711
+ }
1712
+ return segments;
1713
+ });
1714
+ };
1715
+
1716
+ // lib/check-pad-pad-clearance.ts
1717
+ function checkPadPadClearance(circuitJson, {
1718
+ connMap,
1719
+ minSpacing = DEFAULT_PAD_PAD_CLEARANCE
1720
+ } = {}) {
1721
+ const pads = getPads(circuitJson);
1722
+ if (pads.length < 2) return [];
1723
+ connMap ??= getFullConnectivityMapFromCircuitJson7(circuitJson);
1724
+ const spatialIndex = new SpatialObjectIndex({
1725
+ objects: pads,
1726
+ getBounds: getPadBounds,
1727
+ getId: (pad) => getPrimaryId3(pad)
1728
+ });
1729
+ const errors = /* @__PURE__ */ new Map();
1730
+ for (const padA of pads) {
1731
+ const padAId = getPrimaryId3(padA);
1732
+ const nearbyPads = spatialIndex.getObjectsInBounds(
1733
+ getPadBounds(padA),
1734
+ minSpacing
1735
+ );
1736
+ for (const padB of nearbyPads) {
1737
+ const padBId = getPrimaryId3(padB);
1738
+ if (padAId === padBId) continue;
1739
+ if (!getLayersOfPcbElement(padA).some(
1740
+ (layer) => getLayersOfPcbElement(padB).includes(layer)
1741
+ )) {
1742
+ continue;
1743
+ }
1744
+ if (connMap.areIdsConnected(padAId, padBId)) continue;
1745
+ const pairId = [padAId, padBId].sort().join("_");
1746
+ const gap = getPadToPadGap(padA, padB);
1747
+ if (gap + EPSILON >= minSpacing) continue;
1748
+ const centerA = getPadCenter(padA);
1749
+ const centerB = getPadCenter(padB);
1750
+ const nextError = {
1751
+ type: "pcb_pad_pad_clearance_error",
1752
+ pcb_pad_pad_clearance_error_id: `pad_pad_clearance_${pairId}`,
1753
+ error_type: "pcb_pad_pad_clearance_error",
1754
+ message: `Pads ${getReadableNameForElement6(circuitJson, padAId)} and ${getReadableNameForElement6(circuitJson, padBId)} are too close (clearance: ${formatMm(gap)}, minimum: ${formatMm(minSpacing)})`,
1755
+ pcb_pad_ids: [padAId, padBId],
1756
+ minimum_clearance: minSpacing,
1757
+ actual_clearance: gap,
1758
+ center: {
1759
+ x: (centerA.x + centerB.x) / 2,
1760
+ y: (centerA.y + centerB.y) / 2
1761
+ }
1762
+ };
1763
+ if (!errors.has(pairId)) {
1764
+ errors.set(pairId, nextError);
1765
+ }
1766
+ }
1767
+ }
1768
+ return Array.from(errors.values());
1769
+ }
1770
+
1771
+ // lib/check-pad-trace-clearance.ts
1772
+ import {
1773
+ getPrimaryId as getPrimaryId4,
1774
+ getReadableNameForElement as getReadableNameForElement7
1775
+ } from "@tscircuit/circuit-json-util";
1776
+ import {
1777
+ segmentToBoundsMinDistance as segmentToBoundsMinDistance2,
1778
+ segmentToCircleMinDistance as segmentToCircleMinDistance2
1779
+ } from "@tscircuit/math-utils";
1780
+ import {
1781
+ getFullConnectivityMapFromCircuitJson as getFullConnectivityMapFromCircuitJson8
1782
+ } from "circuit-json-to-connectivity-map";
1783
+ function checkPadTraceClearance(circuitJson, {
1784
+ connMap,
1785
+ minSpacing = DEFAULT_PAD_TRACE_CLEARANCE
1786
+ } = {}) {
1787
+ const pads = getPads(circuitJson);
1788
+ const segments = getTraceSegments(circuitJson);
1789
+ if (pads.length === 0 || segments.length === 0) return [];
1790
+ connMap ??= getFullConnectivityMapFromCircuitJson8(circuitJson);
1791
+ const spatialIndex = new SpatialObjectIndex({
1792
+ objects: pads,
1793
+ getBounds: getPadBounds,
1794
+ getId: (pad) => getPrimaryId4(pad)
1795
+ });
1796
+ const errors = /* @__PURE__ */ new Map();
1797
+ for (const segment of segments) {
1798
+ const nearbyPads = spatialIndex.getObjectsInBounds(
1799
+ getCollidableBounds(segment),
1800
+ minSpacing + segment.thickness / 2
1801
+ );
1802
+ for (const pad of nearbyPads) {
1803
+ const padId = getPrimaryId4(pad);
1804
+ if (!getLayersOfPcbElement(pad).includes(segment.layer)) continue;
1805
+ if (connMap.areIdsConnected(segment.pcb_trace_id, padId)) continue;
1806
+ const center = getPadCenter(pad);
1807
+ const gap = isCircularPad(pad) ? segmentToCircleMinDistance2(
1808
+ { x: segment.x1, y: segment.y1 },
1809
+ { x: segment.x2, y: segment.y2 },
1810
+ { x: center.x, y: center.y, radius: getPadRadius(pad) }
1811
+ ) - segment.thickness / 2 : segmentToBoundsMinDistance2(
1812
+ { x: segment.x1, y: segment.y1 },
1813
+ { x: segment.x2, y: segment.y2 },
1814
+ getPadBounds(pad)
1815
+ ) - segment.thickness / 2;
1816
+ if (gap + EPSILON >= minSpacing) continue;
1817
+ const pairId = `${padId}_${segment.pcb_trace_id}`;
1818
+ const nextError = {
1819
+ type: "pcb_pad_trace_clearance_error",
1820
+ pcb_pad_trace_clearance_error_id: `pad_trace_clearance_${pairId}`,
1821
+ error_type: "pcb_pad_trace_clearance_error",
1822
+ message: `Pad ${getReadableNameForElement7(circuitJson, padId)} and trace ${getReadableNameForElement7(circuitJson, segment.pcb_trace_id)} are too close (clearance: ${formatMm(gap)}, minimum: ${formatMm(minSpacing)})`,
1823
+ pcb_pad_id: padId,
1824
+ pcb_trace_id: segment.pcb_trace_id,
1825
+ minimum_clearance: minSpacing,
1826
+ actual_clearance: gap,
1827
+ center: {
1828
+ x: (center.x + (segment.x1 + segment.x2) / 2) / 2,
1829
+ y: (center.y + (segment.y1 + segment.y2) / 2) / 2
1830
+ }
1831
+ };
1832
+ const current = errors.get(pairId);
1833
+ if (!current || gap < current.gap) {
1834
+ errors.set(pairId, { error: nextError, gap });
1835
+ }
1836
+ }
1837
+ }
1838
+ return Array.from(errors.values()).map(({ error }) => error);
1839
+ }
1840
+
1604
1841
  // lib/check-pin-must-be-connected.ts
1605
1842
  function checkPinMustBeConnected(circuitJson) {
1606
1843
  const errors = [];
@@ -1660,7 +1897,7 @@ function checkPinMustBeConnected(circuitJson) {
1660
1897
  }
1661
1898
 
1662
1899
  // lib/check-all-pins-in-component-are-underspecified.ts
1663
- import { cju as cju5 } from "@tscircuit/circuit-json-util";
1900
+ import { cju as cju6 } from "@tscircuit/circuit-json-util";
1664
1901
  var PIN_ATTRIBUTE_KEYS = [
1665
1902
  "must_be_connected",
1666
1903
  "provides_power",
@@ -1705,7 +1942,7 @@ function hasAnyPinAttribute(port) {
1705
1942
  }
1706
1943
  function checkAllPinsInComponentAreUnderspecified(circuitJson) {
1707
1944
  const warnings = [];
1708
- const db = cju5(circuitJson);
1945
+ const db = cju6(circuitJson);
1709
1946
  const sourceComponents = db.source_component.list();
1710
1947
  const sourcePorts = db.source_port.list();
1711
1948
  const portsByComponent = /* @__PURE__ */ new Map();
@@ -1737,10 +1974,10 @@ function checkAllPinsInComponentAreUnderspecified(circuitJson) {
1737
1974
  }
1738
1975
 
1739
1976
  // lib/check-no-power-pin-defined.ts
1740
- import { cju as cju6 } from "@tscircuit/circuit-json-util";
1977
+ import { cju as cju7 } from "@tscircuit/circuit-json-util";
1741
1978
  function checkNoPowerPinDefined(circuitJson) {
1742
1979
  const warnings = [];
1743
- const db = cju6(circuitJson);
1980
+ const db = cju7(circuitJson);
1744
1981
  const sourceComponents = db.source_component.list();
1745
1982
  const sourcePorts = db.source_port.list();
1746
1983
  const portsByComponent = /* @__PURE__ */ new Map();
@@ -1772,10 +2009,10 @@ function checkNoPowerPinDefined(circuitJson) {
1772
2009
  }
1773
2010
 
1774
2011
  // lib/check-no-ground-pin-defined.ts
1775
- import { cju as cju7 } from "@tscircuit/circuit-json-util";
2012
+ import { cju as cju8 } from "@tscircuit/circuit-json-util";
1776
2013
  function checkNoGroundPinDefined(circuitJson) {
1777
2014
  const warnings = [];
1778
- const db = cju7(circuitJson);
2015
+ const db = cju8(circuitJson);
1779
2016
  const sourceComponents = db.source_component.list();
1780
2017
  const sourcePorts = db.source_port.list();
1781
2018
  const portsByComponent = /* @__PURE__ */ new Map();
@@ -1806,6 +2043,75 @@ function checkNoGroundPinDefined(circuitJson) {
1806
2043
  return warnings;
1807
2044
  }
1808
2045
 
2046
+ // lib/check-connector-accessible-orientation.ts
2047
+ import { getBoardBounds } from "@tscircuit/circuit-json-util";
2048
+ function getFacingDirection(component) {
2049
+ if (!component.center || !component.cable_insertion_center) return null;
2050
+ const dx = component.cable_insertion_center.x - component.center.x;
2051
+ const dy = component.cable_insertion_center.y - component.center.y;
2052
+ if (Math.abs(dx) < 1e-6 && Math.abs(dy) < 1e-6) return null;
2053
+ if (Math.abs(dx) >= Math.abs(dy)) {
2054
+ return dx >= 0 ? "x+" : "x-";
2055
+ }
2056
+ return dy >= 0 ? "y+" : "y-";
2057
+ }
2058
+ function getRecommendedFacingDirection(component, bounds) {
2059
+ if (!component.center) return null;
2060
+ const distances = [
2061
+ { direction: "x-", distance: component.center.x - bounds.minX },
2062
+ { direction: "x+", distance: bounds.maxX - component.center.x },
2063
+ { direction: "y-", distance: component.center.y - bounds.minY },
2064
+ { direction: "y+", distance: bounds.maxY - component.center.y }
2065
+ ];
2066
+ distances.sort((a, b) => a.distance - b.distance);
2067
+ return distances[0]?.direction ?? null;
2068
+ }
2069
+ function checkConnectorAccessibleOrientation(circuitJson) {
2070
+ const board = circuitJson.find(
2071
+ (el) => el.type === "pcb_board"
2072
+ );
2073
+ if (!board) return [];
2074
+ const bounds = (() => {
2075
+ try {
2076
+ return getBoardBounds(board);
2077
+ } catch {
2078
+ return null;
2079
+ }
2080
+ })();
2081
+ if (!bounds) return [];
2082
+ const warnings = [];
2083
+ const components = circuitJson.filter(
2084
+ (el) => el.type === "pcb_component"
2085
+ );
2086
+ for (const component of components) {
2087
+ if (!component.cable_insertion_center) continue;
2088
+ const facingDirection = getFacingDirection(component);
2089
+ const recommendedFacingDirection = getRecommendedFacingDirection(
2090
+ component,
2091
+ bounds
2092
+ );
2093
+ if (!facingDirection || !recommendedFacingDirection) continue;
2094
+ if (facingDirection === recommendedFacingDirection) continue;
2095
+ const componentName = getReadableNameForComponent(
2096
+ circuitJson,
2097
+ component.pcb_component_id
2098
+ );
2099
+ warnings.push({
2100
+ type: "pcb_connector_not_in_accessible_orientation_warning",
2101
+ warning_type: "pcb_connector_not_in_accessible_orientation_warning",
2102
+ pcb_connector_not_in_accessible_orientation_warning_id: `pcb_connector_not_in_accessible_orientation_warning_${component.pcb_component_id}`,
2103
+ message: `${componentName} is facing ${facingDirection} but should face ${recommendedFacingDirection} so the connector is accessible from the board edge`,
2104
+ pcb_component_id: component.pcb_component_id,
2105
+ source_component_id: component.source_component_id,
2106
+ pcb_board_id: board.pcb_board_id,
2107
+ facing_direction: facingDirection,
2108
+ recommended_facing_direction: recommendedFacingDirection,
2109
+ subcircuit_id: component.subcircuit_id
2110
+ });
2111
+ }
2112
+ return warnings;
2113
+ }
2114
+
1809
2115
  // lib/check-courtyard-overlap/checkCourtyardOverlap.ts
1810
2116
  import {
1811
2117
  doSegmentsIntersect,
@@ -1912,75 +2218,6 @@ function checkCourtyardOverlap(circuitJson) {
1912
2218
  return errors;
1913
2219
  }
1914
2220
 
1915
- // lib/check-connector-accessible-orientation.ts
1916
- import { getBoardBounds } from "@tscircuit/circuit-json-util";
1917
- function getFacingDirection(component) {
1918
- if (!component.center || !component.cable_insertion_center) return null;
1919
- const dx = component.cable_insertion_center.x - component.center.x;
1920
- const dy = component.cable_insertion_center.y - component.center.y;
1921
- if (Math.abs(dx) < 1e-6 && Math.abs(dy) < 1e-6) return null;
1922
- if (Math.abs(dx) >= Math.abs(dy)) {
1923
- return dx >= 0 ? "x+" : "x-";
1924
- }
1925
- return dy >= 0 ? "y+" : "y-";
1926
- }
1927
- function getRecommendedFacingDirection(component, bounds) {
1928
- if (!component.center) return null;
1929
- const distances = [
1930
- { direction: "x-", distance: component.center.x - bounds.minX },
1931
- { direction: "x+", distance: bounds.maxX - component.center.x },
1932
- { direction: "y-", distance: component.center.y - bounds.minY },
1933
- { direction: "y+", distance: bounds.maxY - component.center.y }
1934
- ];
1935
- distances.sort((a, b) => a.distance - b.distance);
1936
- return distances[0]?.direction ?? null;
1937
- }
1938
- function checkConnectorAccessibleOrientation(circuitJson) {
1939
- const board = circuitJson.find(
1940
- (el) => el.type === "pcb_board"
1941
- );
1942
- if (!board) return [];
1943
- const bounds = (() => {
1944
- try {
1945
- return getBoardBounds(board);
1946
- } catch {
1947
- return null;
1948
- }
1949
- })();
1950
- if (!bounds) return [];
1951
- const warnings = [];
1952
- const components = circuitJson.filter(
1953
- (el) => el.type === "pcb_component"
1954
- );
1955
- for (const component of components) {
1956
- if (!component.cable_insertion_center) continue;
1957
- const facingDirection = getFacingDirection(component);
1958
- const recommendedFacingDirection = getRecommendedFacingDirection(
1959
- component,
1960
- bounds
1961
- );
1962
- if (!facingDirection || !recommendedFacingDirection) continue;
1963
- if (facingDirection === recommendedFacingDirection) continue;
1964
- const componentName = getReadableNameForComponent(
1965
- circuitJson,
1966
- component.pcb_component_id
1967
- );
1968
- warnings.push({
1969
- type: "pcb_connector_not_in_accessible_orientation_warning",
1970
- warning_type: "pcb_connector_not_in_accessible_orientation_warning",
1971
- pcb_connector_not_in_accessible_orientation_warning_id: `pcb_connector_not_in_accessible_orientation_warning_${component.pcb_component_id}`,
1972
- message: `${componentName} is facing ${facingDirection} but should face ${recommendedFacingDirection} so the connector is accessible from the board edge`,
1973
- pcb_component_id: component.pcb_component_id,
1974
- source_component_id: component.source_component_id,
1975
- pcb_board_id: board.pcb_board_id,
1976
- facing_direction: facingDirection,
1977
- recommended_facing_direction: recommendedFacingDirection,
1978
- subcircuit_id: component.subcircuit_id
1979
- });
1980
- }
1981
- return warnings;
1982
- }
1983
-
1984
2221
  // lib/check-traces-are-contiguous/check-traces-are-contiguous.ts
1985
2222
  import { getReadableNameForPcbPort as getReadableNameForPcbPort2 } from "@tscircuit/circuit-json-util";
1986
2223
 
@@ -1990,6 +2227,8 @@ async function runAllPlacementChecks(circuitJson) {
1990
2227
  ...checkViasOffBoard(circuitJson),
1991
2228
  ...checkPcbComponentsOutOfBoard(circuitJson),
1992
2229
  ...checkPcbComponentOverlap(circuitJson),
2230
+ ...checkPadPadClearance(circuitJson),
2231
+ ...checkPadTraceClearance(circuitJson),
1993
2232
  ...checkCourtyardOverlap(circuitJson),
1994
2233
  ...checkConnectorAccessibleOrientation(circuitJson)
1995
2234
  ];
@@ -2032,6 +2271,8 @@ export {
2032
2271
  checkEachPcbTraceNonOverlapping,
2033
2272
  checkNoGroundPinDefined,
2034
2273
  checkNoPowerPinDefined,
2274
+ checkPadPadClearance,
2275
+ checkPadTraceClearance,
2035
2276
  checkPcbComponentOverlap,
2036
2277
  checkPcbComponentsOutOfBoard,
2037
2278
  checkPcbTracesOutOfBoard,