@tscircuit/checks 0.0.116 → 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.js CHANGED
@@ -120,6 +120,53 @@ var getReadableNameForPort = (circuitJson, pcbPortId) => {
120
120
  "port"
121
121
  );
122
122
  };
123
+ var getReadableNameForSourceTrace = (circuitJson, sourceTrace) => {
124
+ const displayName = sanitizeReadableName(
125
+ sourceTrace.display_name,
126
+ sourceTrace.source_trace_id,
127
+ ""
128
+ );
129
+ if (displayName) return displayName;
130
+ const connectedPortNames = (sourceTrace.connected_source_port_ids ?? []).map((sourcePortId) => {
131
+ const pcbPort = circuitJson.find(
132
+ (element) => element.type === "pcb_port" && element.source_port_id === sourcePortId
133
+ );
134
+ if (pcbPort?.type === "pcb_port") {
135
+ return getReadableNameForPort(circuitJson, pcbPort.pcb_port_id);
136
+ }
137
+ const sourcePort = circuitJson.find(
138
+ (element) => element.type === "source_port" && element.source_port_id === sourcePortId
139
+ );
140
+ if (sourcePort?.type !== "source_port") return null;
141
+ const sourceComponent = circuitJson.find(
142
+ (element) => element.type === "source_component" && element.source_component_id === sourcePort.source_component_id
143
+ );
144
+ const sourceComponentName = sourceComponent?.type === "source_component" ? sanitizeReadableName(
145
+ sourceComponent.name,
146
+ sourceComponent.source_component_id,
147
+ ""
148
+ ) : "";
149
+ const sourcePortName = firstReadableName(
150
+ [
151
+ sourcePort.name,
152
+ sourcePort.pin_number?.toString(),
153
+ sourcePort.port_hints?.[0]
154
+ ],
155
+ sourcePort.source_port_id
156
+ );
157
+ if (sourceComponentName && sourcePortName) {
158
+ return `${sourceComponentName}.${sourcePortName}`;
159
+ }
160
+ return sourcePortName || null;
161
+ }).filter((name) => Boolean(name));
162
+ if (connectedPortNames.length >= 2) {
163
+ return `${connectedPortNames[0]} to ${connectedPortNames[1]}`;
164
+ }
165
+ if (connectedPortNames.length === 1) {
166
+ return `trace connected to ${connectedPortNames[0]}`;
167
+ }
168
+ return `trace ${sourceTrace.source_trace_id}`;
169
+ };
123
170
  var getReadableNameForElementId = (circuitJson, elementId) => sanitizeReadableName(
124
171
  getReadableNameForElement(circuitJson, elementId),
125
172
  elementId,
@@ -322,6 +369,8 @@ var DEFAULT_TRACE_THICKNESS = 0.15;
322
369
  var DEFAULT_VIA_BOARD_MARGIN = 0.3;
323
370
  var DEFAULT_SAME_NET_VIA_MARGIN = 0.2;
324
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;
325
374
  var EPSILON = 5e-3;
326
375
 
327
376
  // lib/check-each-pcb-trace-non-overlapping/getPcbPortIdsConnectedToTraces.ts
@@ -1218,6 +1267,79 @@ function checkDifferentNetViaSpacing(circuitJson, {
1218
1267
  return errors;
1219
1268
  }
1220
1269
 
1270
+ // lib/check-source-traces-match-pcb-trace-thickness.ts
1271
+ import { cju as cju2 } from "@tscircuit/circuit-json-util";
1272
+ import { getFullConnectivityMapFromCircuitJson as getFullConnectivityMapFromCircuitJson5 } from "circuit-json-to-connectivity-map";
1273
+ function checkSourceTracesMatchPcbTraceThickness(circuitJson) {
1274
+ const warnings = [];
1275
+ const db = cju2(circuitJson);
1276
+ const sourceTraces = db.source_trace.list();
1277
+ const pcbTraces = db.pcb_trace.list();
1278
+ const pcbPorts = db.pcb_port.list();
1279
+ const connectivityMap = getFullConnectivityMapFromCircuitJson5(circuitJson);
1280
+ for (const sourceTrace of sourceTraces) {
1281
+ const requestedThickness = sourceTrace.min_trace_thickness;
1282
+ if (requestedThickness === void 0) continue;
1283
+ const connectedPcbPorts = pcbPorts.filter(
1284
+ (pcbPort) => sourceTrace.connected_source_port_ids?.includes(pcbPort.source_port_id)
1285
+ );
1286
+ if (connectedPcbPorts.length < 2) continue;
1287
+ const referenceNetId = connectivityMap.getNetConnectedToId(
1288
+ connectedPcbPorts[0].pcb_port_id
1289
+ );
1290
+ if (!referenceNetId) continue;
1291
+ const netElementIds = connectivityMap.getIdsConnectedToNet(referenceNetId);
1292
+ const relatedPcbTraces = pcbTraces.filter(
1293
+ (pcbTrace) => netElementIds.includes(pcbTrace.pcb_trace_id)
1294
+ );
1295
+ if (relatedPcbTraces.length === 0) continue;
1296
+ const actualWireWidths = relatedPcbTraces.flatMap(
1297
+ (pcbTrace) => pcbTrace.route.filter((point) => point.route_type === "wire").map((point) => point.width)
1298
+ );
1299
+ if (actualWireWidths.length === 0) continue;
1300
+ const actualThickness = Math.min(...actualWireWidths);
1301
+ if (actualThickness >= requestedThickness) continue;
1302
+ let undersizedSegment;
1303
+ for (const relatedPcbTrace of relatedPcbTraces) {
1304
+ for (let i = 0; i < relatedPcbTrace.route.length - 1; i++) {
1305
+ const point = relatedPcbTrace.route[i];
1306
+ const nextPoint = relatedPcbTrace.route[i + 1];
1307
+ if (!point || !nextPoint) continue;
1308
+ if (point.route_type !== "wire" || nextPoint.route_type !== "wire") {
1309
+ continue;
1310
+ }
1311
+ if (point.width !== actualThickness) continue;
1312
+ undersizedSegment = {
1313
+ pcb_trace_id: relatedPcbTrace.pcb_trace_id,
1314
+ center: {
1315
+ x: (point.x + nextPoint.x) / 2,
1316
+ y: (point.y + nextPoint.y) / 2
1317
+ }
1318
+ };
1319
+ break;
1320
+ }
1321
+ if (undersizedSegment) break;
1322
+ }
1323
+ if (!undersizedSegment) continue;
1324
+ warnings.push({
1325
+ type: "pcb_trace_warning",
1326
+ pcb_trace_warning_id: `pcb_trace_warning_${sourceTrace.source_trace_id}`,
1327
+ warning_type: "pcb_trace_warning",
1328
+ message: `Trace [${getReadableNameForSourceTrace(circuitJson, sourceTrace)}] is routed thinner than requested (requested: ${requestedThickness}mm, actual: ${actualThickness}mm).`,
1329
+ center: undersizedSegment.center,
1330
+ source_trace_id: sourceTrace.source_trace_id,
1331
+ pcb_trace_id: undersizedSegment.pcb_trace_id,
1332
+ pcb_component_ids: Array.from(
1333
+ new Set(
1334
+ connectedPcbPorts.map((pcbPort) => pcbPort.pcb_component_id).filter((id) => id !== void 0)
1335
+ )
1336
+ ),
1337
+ pcb_port_ids: connectedPcbPorts.map((pcbPort) => pcbPort.pcb_port_id)
1338
+ });
1339
+ }
1340
+ return warnings;
1341
+ }
1342
+
1221
1343
  // lib/check-source-traces-have-pcb-traces.ts
1222
1344
  var getSourceTraceIdsFromPcbTrace = (pcbTraceSourceTraceId) => {
1223
1345
  if (!pcbTraceSourceTraceId) return [];
@@ -1265,7 +1387,7 @@ function checkSourceTracesHavePcbTraces(circuitJson) {
1265
1387
  }
1266
1388
 
1267
1389
  // lib/check-trace-out-of-board/checkTraceOutOfBoard.ts
1268
- import { cju as cju2 } from "@tscircuit/circuit-json-util";
1390
+ import { cju as cju3 } from "@tscircuit/circuit-json-util";
1269
1391
  import { segmentToSegmentMinDistance as segmentToSegmentMinDistance2 } from "@tscircuit/math-utils";
1270
1392
  var DEFAULT_BOARD_MARGIN = 0.2;
1271
1393
  function getBoardPolygonPoints(board) {
@@ -1299,7 +1421,7 @@ function checkPcbTracesOutOfBoard(circuitJson, config = {}) {
1299
1421
  if (!board) return errors;
1300
1422
  const boardPoints = getBoardPolygonPoints(board);
1301
1423
  if (!boardPoints) return errors;
1302
- const pcbTraces = cju2(circuitJson).pcb_trace.list();
1424
+ const pcbTraces = cju3(circuitJson).pcb_trace.list();
1303
1425
  for (const trace of pcbTraces) {
1304
1426
  if (trace.route.length < 2) continue;
1305
1427
  for (let i = 0; i < trace.route.length - 1; i++) {
@@ -1348,12 +1470,12 @@ function checkPcbTracesOutOfBoard(circuitJson, config = {}) {
1348
1470
 
1349
1471
  // lib/check-pcb-components-overlap/checkPcbComponentOverlap.ts
1350
1472
  import {
1351
- cju as cju3,
1473
+ cju as cju4,
1352
1474
  getBoundsOfPcbElements as getBoundsOfPcbElements4,
1353
1475
  getPrimaryId as getPrimaryId2
1354
1476
  } from "@tscircuit/circuit-json-util";
1355
1477
  import { doBoundsOverlap as doBoundsOverlap2 } from "@tscircuit/math-utils";
1356
- import { getFullConnectivityMapFromCircuitJson as getFullConnectivityMapFromCircuitJson5 } from "circuit-json-to-connectivity-map";
1478
+ import { getFullConnectivityMapFromCircuitJson as getFullConnectivityMapFromCircuitJson6 } from "circuit-json-to-connectivity-map";
1357
1479
 
1358
1480
  // lib/check-pcb-components-overlap/doPcbElementsOverlap.ts
1359
1481
  import { getBoundsOfPcbElements as getBoundsOfPcbElements3 } from "@tscircuit/circuit-json-util";
@@ -1385,10 +1507,10 @@ var formatOverlapElementDescription = (circuitJson, element) => {
1385
1507
  };
1386
1508
  function checkPcbComponentOverlap(circuitJson) {
1387
1509
  const errors = [];
1388
- const connMap = getFullConnectivityMapFromCircuitJson5(circuitJson);
1389
- const smtPads = cju3(circuitJson).pcb_smtpad.list();
1390
- const platedHoles = cju3(circuitJson).pcb_plated_hole.list();
1391
- const holes = cju3(circuitJson).pcb_hole.list();
1510
+ const connMap = getFullConnectivityMapFromCircuitJson6(circuitJson);
1511
+ const smtPads = cju4(circuitJson).pcb_smtpad.list();
1512
+ const platedHoles = cju4(circuitJson).pcb_plated_hole.list();
1513
+ const holes = cju4(circuitJson).pcb_hole.list();
1392
1514
  const componentMap = /* @__PURE__ */ new Map();
1393
1515
  for (const pad of smtPads) {
1394
1516
  const componentId = pad.pcb_component_id || `standalone_pad_${getPrimaryId2(pad)}`;
@@ -1481,6 +1603,241 @@ function checkPcbComponentOverlap(circuitJson) {
1481
1603
  return errors;
1482
1604
  }
1483
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
+
1484
1841
  // lib/check-pin-must-be-connected.ts
1485
1842
  function checkPinMustBeConnected(circuitJson) {
1486
1843
  const errors = [];
@@ -1540,7 +1897,7 @@ function checkPinMustBeConnected(circuitJson) {
1540
1897
  }
1541
1898
 
1542
1899
  // lib/check-all-pins-in-component-are-underspecified.ts
1543
- import { cju as cju4 } from "@tscircuit/circuit-json-util";
1900
+ import { cju as cju6 } from "@tscircuit/circuit-json-util";
1544
1901
  var PIN_ATTRIBUTE_KEYS = [
1545
1902
  "must_be_connected",
1546
1903
  "provides_power",
@@ -1585,7 +1942,7 @@ function hasAnyPinAttribute(port) {
1585
1942
  }
1586
1943
  function checkAllPinsInComponentAreUnderspecified(circuitJson) {
1587
1944
  const warnings = [];
1588
- const db = cju4(circuitJson);
1945
+ const db = cju6(circuitJson);
1589
1946
  const sourceComponents = db.source_component.list();
1590
1947
  const sourcePorts = db.source_port.list();
1591
1948
  const portsByComponent = /* @__PURE__ */ new Map();
@@ -1617,10 +1974,10 @@ function checkAllPinsInComponentAreUnderspecified(circuitJson) {
1617
1974
  }
1618
1975
 
1619
1976
  // lib/check-no-power-pin-defined.ts
1620
- import { cju as cju5 } from "@tscircuit/circuit-json-util";
1977
+ import { cju as cju7 } from "@tscircuit/circuit-json-util";
1621
1978
  function checkNoPowerPinDefined(circuitJson) {
1622
1979
  const warnings = [];
1623
- const db = cju5(circuitJson);
1980
+ const db = cju7(circuitJson);
1624
1981
  const sourceComponents = db.source_component.list();
1625
1982
  const sourcePorts = db.source_port.list();
1626
1983
  const portsByComponent = /* @__PURE__ */ new Map();
@@ -1652,10 +2009,10 @@ function checkNoPowerPinDefined(circuitJson) {
1652
2009
  }
1653
2010
 
1654
2011
  // lib/check-no-ground-pin-defined.ts
1655
- import { cju as cju6 } from "@tscircuit/circuit-json-util";
2012
+ import { cju as cju8 } from "@tscircuit/circuit-json-util";
1656
2013
  function checkNoGroundPinDefined(circuitJson) {
1657
2014
  const warnings = [];
1658
- const db = cju6(circuitJson);
2015
+ const db = cju8(circuitJson);
1659
2016
  const sourceComponents = db.source_component.list();
1660
2017
  const sourcePorts = db.source_port.list();
1661
2018
  const portsByComponent = /* @__PURE__ */ new Map();
@@ -1686,6 +2043,75 @@ function checkNoGroundPinDefined(circuitJson) {
1686
2043
  return warnings;
1687
2044
  }
1688
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
+
1689
2115
  // lib/check-courtyard-overlap/checkCourtyardOverlap.ts
1690
2116
  import {
1691
2117
  doSegmentsIntersect,
@@ -1792,75 +2218,6 @@ function checkCourtyardOverlap(circuitJson) {
1792
2218
  return errors;
1793
2219
  }
1794
2220
 
1795
- // lib/check-connector-accessible-orientation.ts
1796
- import { getBoardBounds } from "@tscircuit/circuit-json-util";
1797
- function getFacingDirection(component) {
1798
- if (!component.center || !component.cable_insertion_center) return null;
1799
- const dx = component.cable_insertion_center.x - component.center.x;
1800
- const dy = component.cable_insertion_center.y - component.center.y;
1801
- if (Math.abs(dx) < 1e-6 && Math.abs(dy) < 1e-6) return null;
1802
- if (Math.abs(dx) >= Math.abs(dy)) {
1803
- return dx >= 0 ? "x+" : "x-";
1804
- }
1805
- return dy >= 0 ? "y+" : "y-";
1806
- }
1807
- function getRecommendedFacingDirection(component, bounds) {
1808
- if (!component.center) return null;
1809
- const distances = [
1810
- { direction: "x-", distance: component.center.x - bounds.minX },
1811
- { direction: "x+", distance: bounds.maxX - component.center.x },
1812
- { direction: "y-", distance: component.center.y - bounds.minY },
1813
- { direction: "y+", distance: bounds.maxY - component.center.y }
1814
- ];
1815
- distances.sort((a, b) => a.distance - b.distance);
1816
- return distances[0]?.direction ?? null;
1817
- }
1818
- function checkConnectorAccessibleOrientation(circuitJson) {
1819
- const board = circuitJson.find(
1820
- (el) => el.type === "pcb_board"
1821
- );
1822
- if (!board) return [];
1823
- const bounds = (() => {
1824
- try {
1825
- return getBoardBounds(board);
1826
- } catch {
1827
- return null;
1828
- }
1829
- })();
1830
- if (!bounds) return [];
1831
- const warnings = [];
1832
- const components = circuitJson.filter(
1833
- (el) => el.type === "pcb_component"
1834
- );
1835
- for (const component of components) {
1836
- if (!component.cable_insertion_center) continue;
1837
- const facingDirection = getFacingDirection(component);
1838
- const recommendedFacingDirection = getRecommendedFacingDirection(
1839
- component,
1840
- bounds
1841
- );
1842
- if (!facingDirection || !recommendedFacingDirection) continue;
1843
- if (facingDirection === recommendedFacingDirection) continue;
1844
- const componentName = getReadableNameForComponent(
1845
- circuitJson,
1846
- component.pcb_component_id
1847
- );
1848
- warnings.push({
1849
- type: "pcb_connector_not_in_accessible_orientation_warning",
1850
- warning_type: "pcb_connector_not_in_accessible_orientation_warning",
1851
- pcb_connector_not_in_accessible_orientation_warning_id: `pcb_connector_not_in_accessible_orientation_warning_${component.pcb_component_id}`,
1852
- message: `${componentName} is facing ${facingDirection} but should face ${recommendedFacingDirection} so the connector is accessible from the board edge`,
1853
- pcb_component_id: component.pcb_component_id,
1854
- source_component_id: component.source_component_id,
1855
- pcb_board_id: board.pcb_board_id,
1856
- facing_direction: facingDirection,
1857
- recommended_facing_direction: recommendedFacingDirection,
1858
- subcircuit_id: component.subcircuit_id
1859
- });
1860
- }
1861
- return warnings;
1862
- }
1863
-
1864
2221
  // lib/check-traces-are-contiguous/check-traces-are-contiguous.ts
1865
2222
  import { getReadableNameForPcbPort as getReadableNameForPcbPort2 } from "@tscircuit/circuit-json-util";
1866
2223
 
@@ -1870,6 +2227,8 @@ async function runAllPlacementChecks(circuitJson) {
1870
2227
  ...checkViasOffBoard(circuitJson),
1871
2228
  ...checkPcbComponentsOutOfBoard(circuitJson),
1872
2229
  ...checkPcbComponentOverlap(circuitJson),
2230
+ ...checkPadPadClearance(circuitJson),
2231
+ ...checkPadTraceClearance(circuitJson),
1873
2232
  ...checkCourtyardOverlap(circuitJson),
1874
2233
  ...checkConnectorAccessibleOrientation(circuitJson)
1875
2234
  ];
@@ -1912,12 +2271,15 @@ export {
1912
2271
  checkEachPcbTraceNonOverlapping,
1913
2272
  checkNoGroundPinDefined,
1914
2273
  checkNoPowerPinDefined,
2274
+ checkPadPadClearance,
2275
+ checkPadTraceClearance,
1915
2276
  checkPcbComponentOverlap,
1916
2277
  checkPcbComponentsOutOfBoard,
1917
2278
  checkPcbTracesOutOfBoard,
1918
2279
  checkPinMustBeConnected,
1919
2280
  checkSameNetViaSpacing,
1920
2281
  checkSourceTracesHavePcbTraces,
2282
+ checkSourceTracesMatchPcbTraceThickness,
1921
2283
  checkViasOffBoard,
1922
2284
  runAllChecks,
1923
2285
  runAllNetlistChecks,