@tscircuit/checks 0.0.119 → 0.0.121

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
@@ -4,9 +4,9 @@ import { ConnectivityMap } from 'circuit-json-to-connectivity-map';
4
4
 
5
5
  declare function checkEachPcbPortConnectedToPcbTraces(circuitJson: AnyCircuitElement[]): PcbPortNotConnectedError[];
6
6
 
7
- declare function checkEachPcbTraceNonOverlapping(circuitJson: AnyCircuitElement[], { connMap, minSpacing, }?: {
7
+ declare function checkEachPcbTraceNonOverlapping(circuitJson: AnyCircuitElement[], { connMap, minClearance, }?: {
8
8
  connMap?: ConnectivityMap;
9
- minSpacing?: number;
9
+ minClearance?: number;
10
10
  }): PcbTraceError[];
11
11
 
12
12
  declare class NetManager {
@@ -23,14 +23,14 @@ declare function checkViasOffBoard(circuitJson: AnyCircuitElement[]): PcbPlaceme
23
23
  */
24
24
  declare function checkPcbComponentsOutOfBoard(circuitJson: AnyCircuitElement[]): PcbComponentOutsideBoardError[];
25
25
 
26
- declare function checkSameNetViaSpacing(circuitJson: AnyCircuitElement[], { connMap, minSpacing, }?: {
26
+ declare function checkSameNetViaSpacing(circuitJson: AnyCircuitElement[], { connMap, minClearance, }?: {
27
27
  connMap?: ConnectivityMap;
28
- minSpacing?: number;
28
+ minClearance?: number;
29
29
  }): PcbViaClearanceError[];
30
30
 
31
- declare function checkDifferentNetViaSpacing(circuitJson: AnyCircuitElement[], { connMap, minSpacing, }?: {
31
+ declare function checkDifferentNetViaSpacing(circuitJson: AnyCircuitElement[], { connMap, minClearance, }?: {
32
32
  connMap?: ConnectivityMap;
33
- minSpacing?: number;
33
+ minClearance?: number;
34
34
  }): PcbViaClearanceError[];
35
35
 
36
36
  declare function checkSourceTracesMatchPcbTraceThickness(circuitJson: AnyCircuitElement[]): PcbTraceWarning[];
@@ -61,19 +61,19 @@ 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, }?: {
64
+ declare function checkPadPadClearance(circuitJson: AnyCircuitElement[], { connMap, minClearance, }?: {
65
65
  connMap?: ConnectivityMap;
66
- minSpacing?: number;
66
+ minClearance?: number;
67
67
  }): PcbPadPadClearanceError[];
68
68
 
69
- declare function checkPadTraceClearance(circuitJson: AnyCircuitElement[], { connMap, minSpacing, }?: {
69
+ declare function checkPadTraceClearance(circuitJson: AnyCircuitElement[], { connMap, minClearance, }?: {
70
70
  connMap?: ConnectivityMap;
71
- minSpacing?: number;
71
+ minClearance?: number;
72
72
  }): PcbPadTraceClearanceError[];
73
73
 
74
- declare function checkViaTraceClearance(circuitJson: AnyCircuitElement[], { connMap, minSpacing, }?: {
74
+ declare function checkViaTraceClearance(circuitJson: AnyCircuitElement[], { connMap, minClearance, }?: {
75
75
  connMap?: ConnectivityMap;
76
- minSpacing?: number;
76
+ minClearance?: number;
77
77
  }): PcbViaTraceClearanceError[];
78
78
 
79
79
  type SourcePinMustBeConnectedError = {
package/dist/index.js CHANGED
@@ -363,14 +363,26 @@ import {
363
363
  segmentToCircleMinDistance
364
364
  } from "@tscircuit/math-utils";
365
365
 
366
+ // node_modules/@tscircuit/jlcpcb-manufacturing-specs/lib/jlcpcb-manufacturing-specs.ts
367
+ var jlcMinTolerances = {
368
+ min_trace_width: 0.1,
369
+ min_via_to_via_clearance: 0.1,
370
+ min_trace_to_pad_clearance: 0.1,
371
+ min_pad_to_pad_clearance: 0.1,
372
+ min_board_edge_clearance: 0.2,
373
+ min_via_hole_diameter: 0.2,
374
+ min_via_pad_diameter: 0.3
375
+ };
376
+
366
377
  // lib/drc-defaults.ts
367
378
  var DEFAULT_TRACE_MARGIN = 0.1;
368
- var DEFAULT_TRACE_THICKNESS = 0.15;
379
+ var DEFAULT_TRACE_THICKNESS = jlcMinTolerances.min_trace_width;
380
+ var DEFAULT_VIA_DIAMETER = jlcMinTolerances.min_via_pad_diameter;
369
381
  var DEFAULT_VIA_BOARD_MARGIN = 0.3;
370
- var DEFAULT_SAME_NET_VIA_MARGIN = 0.2;
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;
382
+ var DEFAULT_SAME_NET_VIA_MARGIN = jlcMinTolerances.min_via_to_via_clearance;
383
+ var DEFAULT_DIFFERENT_NET_VIA_MARGIN = jlcMinTolerances.min_via_to_via_clearance;
384
+ var DEFAULT_PAD_PAD_CLEARANCE = jlcMinTolerances.min_pad_to_pad_clearance;
385
+ var DEFAULT_PAD_TRACE_CLEARANCE = jlcMinTolerances.min_trace_to_pad_clearance;
374
386
  var EPSILON = 5e-3;
375
387
 
376
388
  // lib/check-each-pcb-trace-non-overlapping/getPcbPortIdsConnectedToTraces.ts
@@ -684,7 +696,7 @@ function getLayersOfPcbElement(obj) {
684
696
  // lib/check-each-pcb-trace-non-overlapping/check-each-pcb-trace-non-overlapping.ts
685
697
  function checkEachPcbTraceNonOverlapping(circuitJson, {
686
698
  connMap,
687
- minSpacing = DEFAULT_TRACE_MARGIN
699
+ minClearance = DEFAULT_TRACE_MARGIN
688
700
  } = {}) {
689
701
  const errors = [];
690
702
  addStartAndEndPortIdsIfMissing(circuitJson);
@@ -732,7 +744,7 @@ function checkEachPcbTraceNonOverlapping(circuitJson, {
732
744
  const getReadableName = (id) => getReadableNameForElement2(circuitJson, id);
733
745
  const errorIds = /* @__PURE__ */ new Set();
734
746
  for (const segmentA of pcbTraceSegments) {
735
- const requiredMargin = minSpacing;
747
+ const requiredMargin = minClearance;
736
748
  const bounds = getCollidableBounds(segmentA);
737
749
  const nearbyObjects = spatialIndex.getObjectsInBounds(
738
750
  bounds,
@@ -754,7 +766,7 @@ function checkEachPcbTraceNonOverlapping(circuitJson, {
754
766
  { x: segmentB.x1, y: segmentB.y1 },
755
767
  { x: segmentB.x2, y: segmentB.y2 }
756
768
  ) - segmentA.thickness / 2 - segmentB.thickness / 2;
757
- if (gap2 > minSpacing - EPSILON) continue;
769
+ if (gap2 > minClearance - EPSILON) continue;
758
770
  const pcb_trace_error_id = `overlap_${segmentA.pcb_trace_id}_${segmentB.pcb_trace_id}`;
759
771
  const pcb_trace_error_id_reverse = `overlap_${segmentB.pcb_trace_id}_${segmentA.pcb_trace_id}`;
760
772
  if (errorIds.has(pcb_trace_error_id)) continue;
@@ -791,7 +803,7 @@ function checkEachPcbTraceNonOverlapping(circuitJson, {
791
803
  { x: obj.x, y: obj.y, radius }
792
804
  );
793
805
  const gap2 = distance3 - segmentA.thickness / 2;
794
- if (gap2 > minSpacing - EPSILON) continue;
806
+ if (gap2 > minClearance - EPSILON) continue;
795
807
  const pcb_trace_error_id = `overlap_${segmentA.pcb_trace_id}_${primaryObjId}`;
796
808
  if (errorIds.has(pcb_trace_error_id)) continue;
797
809
  errorIds.add(pcb_trace_error_id);
@@ -1174,7 +1186,7 @@ function viasAreAtSameLocation(a, b) {
1174
1186
  // lib/check-same-net-via-spacing.ts
1175
1187
  function checkSameNetViaSpacing(circuitJson, {
1176
1188
  connMap,
1177
- minSpacing = DEFAULT_SAME_NET_VIA_MARGIN
1189
+ minClearance = DEFAULT_SAME_NET_VIA_MARGIN
1178
1190
  } = {}) {
1179
1191
  const vias = circuitJson.filter((el) => el.type === "pcb_via");
1180
1192
  if (vias.length < 2) return [];
@@ -1188,7 +1200,7 @@ function checkSameNetViaSpacing(circuitJson, {
1188
1200
  if (viasAreAtSameLocation(viaA, viaB)) continue;
1189
1201
  if (!connMap.areIdsConnected(viaA.pcb_via_id, viaB.pcb_via_id)) continue;
1190
1202
  const gap = distance2(viaA, viaB) - viaA.outer_diameter / 2 - viaB.outer_diameter / 2;
1191
- if (gap + EPSILON >= minSpacing) continue;
1203
+ if (gap + EPSILON >= minClearance) continue;
1192
1204
  const pairId = [viaA.pcb_via_id, viaB.pcb_via_id].sort().join("_");
1193
1205
  if (reported.has(pairId)) continue;
1194
1206
  reported.add(pairId);
@@ -1204,7 +1216,7 @@ function checkSameNetViaSpacing(circuitJson, {
1204
1216
  )} are too close together (gap: ${gap.toFixed(3)}mm)`,
1205
1217
  error_type: "pcb_via_clearance_error",
1206
1218
  pcb_via_ids: [viaA.pcb_via_id, viaB.pcb_via_id],
1207
- minimum_clearance: minSpacing,
1219
+ minimum_clearance: minClearance,
1208
1220
  actual_clearance: gap,
1209
1221
  pcb_center: {
1210
1222
  x: (viaA.x + viaB.x) / 2,
@@ -1223,7 +1235,7 @@ import {
1223
1235
  } from "circuit-json-to-connectivity-map";
1224
1236
  function checkDifferentNetViaSpacing(circuitJson, {
1225
1237
  connMap,
1226
- minSpacing = DEFAULT_DIFFERENT_NET_VIA_MARGIN
1238
+ minClearance = DEFAULT_DIFFERENT_NET_VIA_MARGIN
1227
1239
  } = {}) {
1228
1240
  const vias = circuitJson.filter((el) => el.type === "pcb_via");
1229
1241
  if (vias.length < 2) return [];
@@ -1237,7 +1249,7 @@ function checkDifferentNetViaSpacing(circuitJson, {
1237
1249
  if (viasAreAtSameLocation(viaA, viaB)) continue;
1238
1250
  if (connMap.areIdsConnected(viaA.pcb_via_id, viaB.pcb_via_id)) continue;
1239
1251
  const gap = distance2(viaA, viaB) - viaA.outer_diameter / 2 - viaB.outer_diameter / 2;
1240
- if (gap + EPSILON >= minSpacing) continue;
1252
+ if (gap + EPSILON >= minClearance) continue;
1241
1253
  const pairId = [viaA.pcb_via_id, viaB.pcb_via_id].sort().join("_");
1242
1254
  if (reported.has(pairId)) continue;
1243
1255
  reported.add(pairId);
@@ -1255,7 +1267,7 @@ function checkDifferentNetViaSpacing(circuitJson, {
1255
1267
  )}mm)`,
1256
1268
  error_type: "pcb_via_clearance_error",
1257
1269
  pcb_via_ids: [viaA.pcb_via_id, viaB.pcb_via_id],
1258
- minimum_clearance: minSpacing,
1270
+ minimum_clearance: minClearance,
1259
1271
  actual_clearance: gap,
1260
1272
  pcb_center: {
1261
1273
  x: (viaA.x + viaB.x) / 2,
@@ -1716,7 +1728,7 @@ var getTraceSegments = (circuitJson) => {
1716
1728
  // lib/check-pad-pad-clearance.ts
1717
1729
  function checkPadPadClearance(circuitJson, {
1718
1730
  connMap,
1719
- minSpacing = DEFAULT_PAD_PAD_CLEARANCE
1731
+ minClearance = DEFAULT_PAD_PAD_CLEARANCE
1720
1732
  } = {}) {
1721
1733
  const pads = getPads(circuitJson);
1722
1734
  if (pads.length < 2) return [];
@@ -1731,7 +1743,7 @@ function checkPadPadClearance(circuitJson, {
1731
1743
  const padAId = getPrimaryId3(padA);
1732
1744
  const nearbyPads = spatialIndex.getObjectsInBounds(
1733
1745
  getPadBounds(padA),
1734
- minSpacing
1746
+ minClearance
1735
1747
  );
1736
1748
  for (const padB of nearbyPads) {
1737
1749
  const padBId = getPrimaryId3(padB);
@@ -1744,16 +1756,16 @@ function checkPadPadClearance(circuitJson, {
1744
1756
  if (connMap.areIdsConnected(padAId, padBId)) continue;
1745
1757
  const pairId = [padAId, padBId].sort().join("_");
1746
1758
  const gap = getPadToPadGap(padA, padB);
1747
- if (gap + EPSILON >= minSpacing) continue;
1759
+ if (gap + EPSILON >= minClearance) continue;
1748
1760
  const centerA = getPadCenter(padA);
1749
1761
  const centerB = getPadCenter(padB);
1750
1762
  const nextError = {
1751
1763
  type: "pcb_pad_pad_clearance_error",
1752
1764
  pcb_pad_pad_clearance_error_id: `pad_pad_clearance_${pairId}`,
1753
1765
  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)})`,
1766
+ message: `Pads ${getReadableNameForElement6(circuitJson, padAId)} and ${getReadableNameForElement6(circuitJson, padBId)} are too close (clearance: ${formatMm(gap)}, minimum: ${formatMm(minClearance)})`,
1755
1767
  pcb_pad_ids: [padAId, padBId],
1756
- minimum_clearance: minSpacing,
1768
+ minimum_clearance: minClearance,
1757
1769
  actual_clearance: gap,
1758
1770
  center: {
1759
1771
  x: (centerA.x + centerB.x) / 2,
@@ -1782,7 +1794,7 @@ import {
1782
1794
  } from "circuit-json-to-connectivity-map";
1783
1795
  function checkPadTraceClearance(circuitJson, {
1784
1796
  connMap,
1785
- minSpacing = DEFAULT_PAD_TRACE_CLEARANCE
1797
+ minClearance = DEFAULT_PAD_TRACE_CLEARANCE
1786
1798
  } = {}) {
1787
1799
  const pads = getPads(circuitJson);
1788
1800
  const segments = getTraceSegments(circuitJson);
@@ -1797,7 +1809,7 @@ function checkPadTraceClearance(circuitJson, {
1797
1809
  for (const segment of segments) {
1798
1810
  const nearbyPads = spatialIndex.getObjectsInBounds(
1799
1811
  getCollidableBounds(segment),
1800
- minSpacing + segment.thickness / 2
1812
+ minClearance + segment.thickness / 2
1801
1813
  );
1802
1814
  for (const pad of nearbyPads) {
1803
1815
  const padId = getPrimaryId4(pad);
@@ -1813,16 +1825,16 @@ function checkPadTraceClearance(circuitJson, {
1813
1825
  { x: segment.x2, y: segment.y2 },
1814
1826
  getPadBounds(pad)
1815
1827
  ) - segment.thickness / 2;
1816
- if (gap + EPSILON >= minSpacing) continue;
1828
+ if (gap + EPSILON >= minClearance) continue;
1817
1829
  const pairId = `${padId}_${segment.pcb_trace_id}`;
1818
1830
  const nextError = {
1819
1831
  type: "pcb_pad_trace_clearance_error",
1820
1832
  pcb_pad_trace_clearance_error_id: `pad_trace_clearance_${pairId}`,
1821
1833
  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)})`,
1834
+ message: `Pad ${getReadableNameForElement7(circuitJson, padId)} and trace ${getReadableNameForElement7(circuitJson, segment.pcb_trace_id)} are too close (clearance: ${formatMm(gap)}, minimum: ${formatMm(minClearance)})`,
1823
1835
  pcb_pad_id: padId,
1824
1836
  pcb_trace_id: segment.pcb_trace_id,
1825
- minimum_clearance: minSpacing,
1837
+ minimum_clearance: minClearance,
1826
1838
  actual_clearance: gap,
1827
1839
  center: {
1828
1840
  x: (center.x + (segment.x1 + segment.x2) / 2) / 2,
@@ -1863,7 +1875,7 @@ var getClosestPointOnSegment = (point, segmentStart, segmentEnd) => {
1863
1875
  };
1864
1876
  function checkViaTraceClearance(circuitJson, {
1865
1877
  connMap,
1866
- minSpacing = DEFAULT_PAD_TRACE_CLEARANCE
1878
+ minClearance = DEFAULT_PAD_TRACE_CLEARANCE
1867
1879
  } = {}) {
1868
1880
  const vias = circuitJson.filter((el) => el.type === "pcb_via");
1869
1881
  const segments = getTraceSegments(circuitJson);
@@ -1885,7 +1897,7 @@ function checkViaTraceClearance(circuitJson, {
1885
1897
  radius: viaRadius
1886
1898
  }
1887
1899
  ) - segment.thickness / 2;
1888
- if (gap + EPSILON >= minSpacing) continue;
1900
+ if (gap + EPSILON >= minClearance) continue;
1889
1901
  const closestPoint = getClosestPointOnSegment(
1890
1902
  { x: via.x, y: via.y },
1891
1903
  { x: segment.x1, y: segment.y1 },
@@ -1896,10 +1908,10 @@ function checkViaTraceClearance(circuitJson, {
1896
1908
  type: "pcb_via_trace_clearance_error",
1897
1909
  pcb_via_trace_clearance_error_id: `via_trace_clearance_${pairId}`,
1898
1910
  error_type: "pcb_via_trace_clearance_error",
1899
- message: `Via ${getReadableNameForElement8(circuitJson, via.pcb_via_id)} and trace ${getReadableNameForElement8(circuitJson, segment.pcb_trace_id)} are too close (clearance: ${formatMm(gap)}, minimum: ${formatMm(minSpacing)})`,
1911
+ message: `Via ${getReadableNameForElement8(circuitJson, via.pcb_via_id)} and trace ${getReadableNameForElement8(circuitJson, segment.pcb_trace_id)} are too close (clearance: ${formatMm(gap)}, minimum: ${formatMm(minClearance)})`,
1900
1912
  pcb_via_id: via.pcb_via_id,
1901
1913
  pcb_trace_id: segment.pcb_trace_id,
1902
- minimum_clearance: minSpacing,
1914
+ minimum_clearance: minClearance,
1903
1915
  actual_clearance: gap,
1904
1916
  center: {
1905
1917
  x: (via.x + closestPoint.x) / 2,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../lib/add-start-and-end-port-ids-if-missing.ts","../lib/check-each-pcb-port-connected-to-pcb-trace.ts","../lib/util/get-readable-names.ts","../lib/check-each-pcb-trace-non-overlapping/check-each-pcb-trace-non-overlapping.ts","../lib/data-structures/SpatialIndex.ts","../lib/check-each-pcb-trace-non-overlapping/getCollidableBounds.ts","../lib/drc-defaults.ts","../lib/check-each-pcb-trace-non-overlapping/getPcbPortIdsConnectedToTraces.ts","../lib/check-each-pcb-trace-non-overlapping/getClosestPointBetweenSegments.ts","../lib/check-each-pcb-trace-non-overlapping/getRadiusOfCircuitJsonElement.ts","../lib/check-each-pcb-trace-non-overlapping/getClosestPointBetweenSegmentAndBounds.ts","../lib/util/getLayersOfPcbElement.ts","../lib/net-manager.ts","../lib/check-pcb-components-out-of-board/checkViasOffBoard.ts","../lib/check-pcb-components-out-of-board/checkPcbComponentsOutOfBoard.ts","../lib/check-same-net-via-spacing.ts","../lib/util/distance.ts","../lib/util/viasAreAtSameLocation.ts","../lib/check-different-net-via-spacing.ts","../lib/check-source-traces-match-pcb-trace-thickness.ts","../lib/check-source-traces-have-pcb-traces.ts","../lib/check-trace-out-of-board/checkTraceOutOfBoard.ts","../lib/check-pcb-components-overlap/checkPcbComponentOverlap.ts","../lib/check-pcb-components-overlap/doPcbElementsOverlap.ts","../lib/check-pad-pad-clearance.ts","../lib/check-pad-clearance/common.ts","../lib/check-pad-trace-clearance.ts","../lib/check-via-trace-clearance.ts","../lib/check-pin-must-be-connected.ts","../lib/check-all-pins-in-component-are-underspecified.ts","../lib/check-no-power-pin-defined.ts","../lib/check-no-ground-pin-defined.ts","../lib/check-connector-accessible-orientation.ts","../lib/check-courtyard-overlap/checkCourtyardOverlap.ts","../lib/check-traces-are-contiguous/check-traces-are-contiguous.ts","../lib/run-all-checks.ts"],"sourcesContent":["import type {\n PcbPort,\n PcbTrace,\n AnyCircuitElement,\n PcbSmtPad,\n} from \"circuit-json\"\n\nfunction distance(x1: number, y1: number, x2: number, y2: number): number {\n return Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)\n}\n\n/**\n * HACK: this whole method and all usage of it is a hack because of this issue:\n * https://github.com/tscircuit/tscircuit/issues/291\n */\nexport const addStartAndEndPortIdsIfMissing = (\n soup: AnyCircuitElement[],\n): void => {\n const pcbPorts: PcbPort[] = soup.filter((item) => item.type === \"pcb_port\")\n const pcbSmtPads: PcbSmtPad[] = soup.filter(\n (item) => item.type === \"pcb_smtpad\",\n )\n const pcbTraces: PcbTrace[] = soup.filter((item) => item.type === \"pcb_trace\")\n\n function findPortIdOverlappingPoint(\n point: {\n x: number\n y: number\n },\n options: { isFirstOrLastPoint?: boolean; traceWidth?: number } = {},\n ): string | null {\n const traceWidth = options.traceWidth || 0\n const directPort = pcbPorts.find(\n (port) => distance(port.x, port.y, point.x, point.y) < 0.01,\n )\n if (directPort) return directPort.pcb_port_id\n\n // If it starts or ends inside an smtpad, we'll connect it to the por\n if (options.isFirstOrLastPoint) {\n const smtPad = pcbSmtPads.find((pad) => {\n if (pad.shape === \"rect\") {\n return (\n Math.abs(point.x - pad.x) < pad.width / 2 + traceWidth / 2 &&\n Math.abs(point.y - pad.y) < pad.height / 2 + traceWidth / 2\n )\n // biome-ignore lint/style/noUselessElse: <explanation>\n } else if (pad.shape === \"circle\") {\n return distance(point.x, point.y, pad.x, pad.y) < pad.radius\n }\n })\n if (smtPad) return smtPad.pcb_port_id ?? null\n }\n\n return null\n }\n\n // Add start_pcb_port_id and end_pcb_port_id if not present\n for (const trace of pcbTraces) {\n for (let index = 0; index < trace.route.length; index++) {\n const segment = trace.route[index]\n const isFirstOrLastPoint = index === 0 || index === trace.route.length - 1\n if (segment.route_type === \"wire\") {\n if (!segment.start_pcb_port_id && index === 0) {\n const startPortId = findPortIdOverlappingPoint(segment, {\n isFirstOrLastPoint,\n traceWidth: segment.width,\n })\n if (startPortId) {\n segment.start_pcb_port_id = startPortId\n }\n }\n if (!segment.end_pcb_port_id && index === trace.route.length - 1) {\n const endPortId = findPortIdOverlappingPoint(segment, {\n isFirstOrLastPoint,\n traceWidth: segment.width,\n })\n if (endPortId) {\n segment.end_pcb_port_id = endPortId\n }\n }\n }\n }\n }\n}\n","import type {\n PcbPort,\n SourceTrace,\n AnyCircuitElement,\n PcbPortNotConnectedError,\n} from \"circuit-json\"\nimport { addStartAndEndPortIdsIfMissing } from \"./add-start-and-end-port-ids-if-missing\"\nimport { getFullConnectivityMapFromCircuitJson } from \"circuit-json-to-connectivity-map\"\nimport { getReadableNameForPort } from \"./util/get-readable-names\"\n\nfunction checkEachPcbPortConnectedToPcbTraces(\n circuitJson: AnyCircuitElement[],\n): PcbPortNotConnectedError[] {\n addStartAndEndPortIdsIfMissing(circuitJson)\n const sourceTraces: SourceTrace[] = circuitJson.filter(\n (item) => item.type === \"source_trace\",\n ) as SourceTrace[]\n\n const pcbPorts: PcbPort[] = circuitJson.filter(\n (item) => item.type === \"pcb_port\",\n ) as PcbPort[]\n\n const errors: PcbPortNotConnectedError[] = []\n\n // Generate the connectivity map from the circuit\n const connectivityMap = getFullConnectivityMapFromCircuitJson(circuitJson)\n\n // Create a map from source_port_id to pcb_port for quick lookup\n const sourcePortToPcbPort = new Map<string, PcbPort>()\n for (const pcbPort of pcbPorts) {\n sourcePortToPcbPort.set(pcbPort.source_port_id, pcbPort)\n }\n\n // Process each source trace\n for (const sourceTrace of sourceTraces) {\n const connectedSourcePortIds = sourceTrace.connected_source_port_ids\n\n // Skip traces with less than 2 ports (nothing to connect)\n if (connectedSourcePortIds.length < 2) {\n continue\n }\n\n // Find corresponding PCB ports for all source ports in this trace\n const pcbPortsInTrace: PcbPort[] = []\n const missingPcbPorts: string[] = []\n\n for (const sourcePortId of connectedSourcePortIds) {\n const pcbPort = sourcePortToPcbPort.get(sourcePortId)\n if (pcbPort) {\n pcbPortsInTrace.push(pcbPort)\n } else {\n missingPcbPorts.push(sourcePortId)\n }\n }\n\n // Skip if we don't have at least 2 PCB ports to connect\n if (pcbPortsInTrace.length < 2) {\n continue\n }\n\n // Get the net ID for the first PCB port as reference\n const firstPcbPort = pcbPortsInTrace[0]\n const referenceNetId = connectivityMap.getNetConnectedToId(\n firstPcbPort.pcb_port_id,\n )\n\n const netElementIds = connectivityMap.getIdsConnectedToNet(referenceNetId!)\n const pcbTraceIds = netElementIds.filter((id) =>\n circuitJson.some(\n (element) =>\n element.type === \"pcb_trace\" &&\n ((\"pcb_trace_id\" in element && element.pcb_trace_id === id) ||\n (\"route_id\" in element && element.route_id === id)),\n ),\n )\n\n if (pcbTraceIds.length === 0) {\n // Check if this is a trivial case (only 2 ports on same component)\n const uniqueComponentIds = new Set(\n pcbPortsInTrace.map((p) => p.pcb_component_id),\n )\n\n if (uniqueComponentIds.size > 1) {\n // Ports are on different components but no PCB traces connect them\n errors.push({\n type: \"pcb_port_not_connected_error\",\n message: `Ports [${pcbPortsInTrace.map((p) => getReadableNameForPort(circuitJson, p.pcb_port_id)).join(\", \")}] are not connected together through the same net.`,\n error_type: \"pcb_port_not_connected_error\",\n pcb_port_ids: pcbPortsInTrace.map((p) => p.pcb_port_id),\n pcb_component_ids: pcbPortsInTrace\n .map((p) => p.pcb_component_id)\n .filter((id): id is string => id !== undefined),\n pcb_port_not_connected_error_id: `pcb_port_not_connected_error_trace_${sourceTrace.source_trace_id}`,\n })\n }\n }\n }\n\n return errors\n}\n\nexport { checkEachPcbPortConnectedToPcbTraces }\n","import type {\n AnyCircuitElement,\n PcbPlatedHole,\n PcbSmtPad,\n SourceTrace,\n} from \"circuit-json\"\nimport {\n getReadableNameForElement,\n getReadableNameForPcbPort,\n getBoundsOfPcbElements,\n} from \"@tscircuit/circuit-json-util\"\n\nconst CIRCUIT_JSON_ID_PATTERN =\n /\\b(?:pcb|source|schematic|subcircuit)_[a-z0-9_]+\\b/i\n\nconst sanitizeReadableName = (\n candidate: string | null | undefined,\n id: string,\n fallbackLabel: string,\n): string => {\n if (\n !candidate ||\n candidate === id ||\n CIRCUIT_JSON_ID_PATTERN.test(candidate)\n ) {\n return fallbackLabel\n }\n return candidate\n}\n\nconst firstReadableName = (\n candidates: Array<string | null | undefined>,\n id: string,\n): string => {\n for (const candidate of candidates) {\n const readableName = sanitizeReadableName(candidate, id, \"\")\n if (readableName) return readableName\n }\n return \"\"\n}\n\nexport const getReadableNameForComponent = (\n circuitJson: AnyCircuitElement[],\n pcbComponentId: string,\n): string =>\n sanitizeReadableName(\n getReadableNameForElement(circuitJson, pcbComponentId),\n pcbComponentId,\n \"component\",\n )\n\nexport const getReadableNameForPort = (\n circuitJson: AnyCircuitElement[],\n pcbPortId: string,\n): string => {\n const pcbPort = circuitJson.find(\n (element) =>\n element.type === \"pcb_port\" && element.pcb_port_id === pcbPortId,\n )\n\n if (pcbPort?.type === \"pcb_port\") {\n const sourcePort = circuitJson.find(\n (element) =>\n element.type === \"source_port\" &&\n element.source_port_id === pcbPort.source_port_id,\n )\n\n const sourceComponent =\n sourcePort?.type === \"source_port\"\n ? circuitJson.find(\n (element) =>\n element.type === \"source_component\" &&\n element.source_component_id === sourcePort.source_component_id,\n )\n : null\n\n const readableSourceComponentName = firstReadableName(\n [\n sourceComponent?.type === \"source_component\"\n ? sourceComponent.name\n : null,\n ],\n sourceComponent?.type === \"source_component\"\n ? sourceComponent.source_component_id\n : \"\",\n )\n\n const readableSourcePortName = firstReadableName(\n [\n sourcePort?.type === \"source_port\" ? sourcePort.name : null,\n sourcePort?.type === \"source_port\"\n ? sourcePort.pin_number?.toString()\n : null,\n sourcePort?.type === \"source_port\" ? sourcePort.port_hints?.[0] : null,\n ],\n sourcePort?.type === \"source_port\" ? sourcePort.source_port_id : \"\",\n )\n\n if (readableSourceComponentName && readableSourcePortName) {\n return `${readableSourceComponentName}.${readableSourcePortName}`\n }\n if (readableSourcePortName) {\n return readableSourcePortName\n }\n }\n\n return sanitizeReadableName(\n getReadableNameForPcbPort(circuitJson, pcbPortId) ??\n getReadableNameForElement(circuitJson, pcbPortId),\n pcbPortId,\n \"port\",\n )\n}\n\nexport const getReadableNameForSourceTrace = (\n circuitJson: AnyCircuitElement[],\n sourceTrace: SourceTrace,\n): string => {\n const displayName = sanitizeReadableName(\n sourceTrace.display_name,\n sourceTrace.source_trace_id,\n \"\",\n )\n if (displayName) return displayName\n\n const connectedPortNames = (sourceTrace.connected_source_port_ids ?? [])\n .map((sourcePortId) => {\n const pcbPort = circuitJson.find(\n (element) =>\n element.type === \"pcb_port\" &&\n element.source_port_id === sourcePortId,\n )\n\n if (pcbPort?.type === \"pcb_port\") {\n return getReadableNameForPort(circuitJson, pcbPort.pcb_port_id)\n }\n\n const sourcePort = circuitJson.find(\n (element) =>\n element.type === \"source_port\" &&\n element.source_port_id === sourcePortId,\n )\n if (sourcePort?.type !== \"source_port\") return null\n\n const sourceComponent = circuitJson.find(\n (element) =>\n element.type === \"source_component\" &&\n element.source_component_id === sourcePort.source_component_id,\n )\n\n const sourceComponentName =\n sourceComponent?.type === \"source_component\"\n ? sanitizeReadableName(\n sourceComponent.name,\n sourceComponent.source_component_id,\n \"\",\n )\n : \"\"\n\n const sourcePortName = firstReadableName(\n [\n sourcePort.name,\n sourcePort.pin_number?.toString(),\n sourcePort.port_hints?.[0],\n ],\n sourcePort.source_port_id,\n )\n\n if (sourceComponentName && sourcePortName) {\n return `${sourceComponentName}.${sourcePortName}`\n }\n\n return sourcePortName || null\n })\n .filter((name): name is string => Boolean(name))\n\n if (connectedPortNames.length >= 2) {\n return `${connectedPortNames[0]} to ${connectedPortNames[1]}`\n }\n\n if (connectedPortNames.length === 1) {\n return `trace connected to ${connectedPortNames[0]}`\n }\n\n return `trace ${sourceTrace.source_trace_id}`\n}\n\nexport const getReadableNameForElementId = (\n circuitJson: AnyCircuitElement[],\n elementId: string,\n): string =>\n sanitizeReadableName(\n getReadableNameForElement(circuitJson, elementId),\n elementId,\n \"element\",\n )\n\nexport const getReadableNameForGroup = (\n circuitJson: AnyCircuitElement[],\n groupId: string,\n): string =>\n sanitizeReadableName(\n getReadableNameForElement(circuitJson, groupId),\n groupId,\n \"group\",\n )\n\nexport const containsCircuitJsonId = (message: string): boolean =>\n CIRCUIT_JSON_ID_PATTERN.test(message)\n\nexport function getReadableNameForFootprintPad(\n circuitJson: AnyCircuitElement[],\n pad: PcbSmtPad | PcbPlatedHole,\n ordinal: number,\n): string {\n const padKind = pad.type === \"pcb_smtpad\" ? \"SMD pad\" : \"through-hole pad\"\n const portRef = pad.pcb_port_id\n ? getReadableNameForPort(circuitJson, pad.pcb_port_id)\n : null\n const bounds = getBoundsOfPcbElements([pad])\n const centerX = (bounds.minX + bounds.maxX) / 2\n const centerY = (bounds.minY + bounds.maxY) / 2\n const location = `(${centerX.toFixed(2)}mm, ${centerY.toFixed(2)}mm)`\n\n if (portRef) return `${padKind} ${portRef} at ${location}`\n return `${padKind} #${ordinal + 1} at ${location}`\n}\n","import type { AnyCircuitElement, PcbTraceError } from \"circuit-json\"\nimport { getReadableNameForElement, cju } from \"@tscircuit/circuit-json-util\"\nimport {\n SpatialObjectIndex,\n type Bounds,\n} from \"lib/data-structures/SpatialIndex\"\nimport {\n getFullConnectivityMapFromCircuitJson,\n type ConnectivityMap,\n} from \"circuit-json-to-connectivity-map\"\nimport {\n getCollidableBounds,\n type Collidable,\n type PcbTraceSegment,\n} from \"./getCollidableBounds\"\nimport {\n segmentToBoundsMinDistance,\n segmentToCircleMinDistance,\n} from \"@tscircuit/math-utils\"\nimport {\n DEFAULT_TRACE_MARGIN,\n DEFAULT_TRACE_THICKNESS,\n EPSILON,\n} from \"lib/drc-defaults\"\nimport { getPcbPortIdsConnectedToTraces } from \"./getPcbPortIdsConnectedToTraces\"\nimport { segmentToSegmentMinDistance } from \"@tscircuit/math-utils\"\nimport { areBoundsOverlapping } from \"./areBoundsOverlapping\"\nimport { getPrimaryId } from \"@tscircuit/circuit-json-util\"\nimport { getCenterOfBoundsPair } from \"./getCenterOfBoundsPair\"\nimport { getClosestPointBetweenSegments } from \"./getClosestPointBetweenSegments\"\nimport { getCenterOfBounds } from \"./getCenterOfBounds\"\nimport { getRadiusOfCircuitJsonElement } from \"./getRadiusOfCircuitJsonElement\"\nimport { getClosestPointBetweenSegmentAndBounds } from \"./getClosestPointBetweenSegmentAndBounds\"\nimport { getLayersOfPcbElement } from \"../util/getLayersOfPcbElement\"\nimport { addStartAndEndPortIdsIfMissing } from \"lib/add-start-and-end-port-ids-if-missing\"\n\nexport function checkEachPcbTraceNonOverlapping(\n circuitJson: AnyCircuitElement[],\n {\n connMap,\n minSpacing = DEFAULT_TRACE_MARGIN,\n }: {\n connMap?: ConnectivityMap\n minSpacing?: number\n } = {},\n): PcbTraceError[] {\n const errors: PcbTraceError[] = []\n addStartAndEndPortIdsIfMissing(circuitJson)\n connMap ??= getFullConnectivityMapFromCircuitJson(circuitJson)\n\n const pcbTraces = cju(circuitJson).pcb_trace.list()\n const pcbTraceSegments = pcbTraces.flatMap((pcbTrace) => {\n const segments: PcbTraceSegment[] = []\n for (let i = 0; i < pcbTrace.route.length - 1; i++) {\n const p1 = pcbTrace.route[i]\n const p2 = pcbTrace.route[i + 1]\n if (p1.route_type !== \"wire\") continue\n if (p2.route_type !== \"wire\") continue\n if (p1.layer !== p2.layer) continue\n segments.push({\n type: \"pcb_trace_segment\",\n pcb_trace_id: pcbTrace.pcb_trace_id,\n _pcbTrace: pcbTrace,\n thickness:\n \"width\" in p1\n ? p1.width\n : \"width\" in p2\n ? p2.width\n : DEFAULT_TRACE_THICKNESS,\n layer: p1.layer,\n x1: p1.x,\n y1: p1.y,\n x2: p2.x,\n y2: p2.y,\n } as PcbTraceSegment)\n }\n return segments\n })\n const pcbSmtPads = cju(circuitJson).pcb_smtpad.list()\n const pcbPlatedHoles = cju(circuitJson).pcb_plated_hole.list()\n const pcbHoles = cju(circuitJson).pcb_hole.list()\n const pcbVias = cju(circuitJson).pcb_via.list()\n const pcbKeepouts = cju(circuitJson).pcb_keepout.list()\n\n const allObjects: Collidable[] = [\n ...pcbTraceSegments,\n ...pcbSmtPads,\n ...pcbPlatedHoles,\n ...pcbHoles,\n ...pcbVias,\n ...pcbKeepouts,\n ]\n\n const spatialIndex = new SpatialObjectIndex<Collidable>({\n objects: allObjects,\n getBounds: getCollidableBounds,\n })\n\n const getReadableName = (id: string) =>\n getReadableNameForElement(circuitJson, id)\n\n const errorIds = new Set<string>()\n\n // For each segment, check it if overlaps with anything collidable\n for (const segmentA of pcbTraceSegments) {\n const requiredMargin = minSpacing\n const bounds = getCollidableBounds(segmentA)\n const nearbyObjects = spatialIndex.getObjectsInBounds(\n bounds,\n requiredMargin + segmentA.thickness / 2,\n )\n if (segmentA.x1 === segmentA.x2 && segmentA.y1 === segmentA.y2) continue\n\n for (const obj of nearbyObjects) {\n // ignore obstacles not on the trace's layer (except vias)\n if (!getLayersOfPcbElement(obj).includes(segmentA.layer)) {\n continue\n }\n if (obj.type === \"pcb_trace_segment\") {\n const segmentB = obj\n\n if (segmentA.layer !== segmentB.layer) continue\n\n // Check if the segments are overlapping\n if (\n connMap.areIdsConnected(segmentA.pcb_trace_id, segmentB.pcb_trace_id)\n )\n continue\n\n const gap =\n segmentToSegmentMinDistance(\n { x: segmentA.x1, y: segmentA.y1 },\n { x: segmentA.x2, y: segmentA.y2 },\n { x: segmentB.x1, y: segmentB.y1 },\n { x: segmentB.x2, y: segmentB.y2 },\n ) -\n segmentA.thickness / 2 -\n segmentB.thickness / 2\n if (gap > minSpacing - EPSILON) continue\n\n const pcb_trace_error_id = `overlap_${segmentA.pcb_trace_id}_${segmentB.pcb_trace_id}`\n const pcb_trace_error_id_reverse = `overlap_${segmentB.pcb_trace_id}_${segmentA.pcb_trace_id}`\n if (errorIds.has(pcb_trace_error_id)) continue\n if (errorIds.has(pcb_trace_error_id_reverse)) continue\n\n errorIds.add(pcb_trace_error_id)\n errors.push({\n type: \"pcb_trace_error\",\n error_type: \"pcb_trace_error\",\n message: `PCB trace ${getReadableName(segmentA.pcb_trace_id)} overlaps with ${getReadableName(segmentB.pcb_trace_id)} ${gap < 0 ? \"(accidental contact)\" : `(gap: ${gap.toFixed(3)}mm)`}`,\n pcb_trace_id: segmentA.pcb_trace_id,\n source_trace_id: \"\",\n pcb_trace_error_id,\n pcb_component_ids: [],\n center: getClosestPointBetweenSegments(segmentA, segmentB),\n pcb_port_ids: getPcbPortIdsConnectedToTraces([\n segmentA._pcbTrace,\n segmentB._pcbTrace,\n ]),\n })\n continue\n }\n\n const primaryObjId = getPrimaryId(obj as any)\n if (\n connMap.areIdsConnected(\n segmentA.pcb_trace_id,\n \"pcb_trace_id\" in obj ? (obj.pcb_trace_id as string) : primaryObjId,\n )\n )\n continue\n\n const isCircular =\n obj.type === \"pcb_via\" ||\n (obj.type === \"pcb_plated_hole\" && obj.shape === \"circle\") ||\n obj.type === \"pcb_hole\" ||\n (obj.type === \"pcb_smtpad\" && obj.shape === \"circle\")\n\n if (isCircular) {\n const radius = getRadiusOfCircuitJsonElement(obj)\n const distance = segmentToCircleMinDistance(\n { x: segmentA.x1, y: segmentA.y1 },\n { x: segmentA.x2, y: segmentA.y2 },\n { x: obj.x, y: obj.y, radius },\n )\n const gap = distance - segmentA.thickness / 2\n if (gap > minSpacing - EPSILON) continue\n\n const pcb_trace_error_id = `overlap_${segmentA.pcb_trace_id}_${primaryObjId}`\n if (errorIds.has(pcb_trace_error_id)) continue\n errorIds.add(pcb_trace_error_id)\n errors.push({\n type: \"pcb_trace_error\",\n error_type: \"pcb_trace_error\",\n message: `PCB trace ${getReadableName(segmentA.pcb_trace_id)} overlaps with ${obj.type} \"${getReadableName(getPrimaryId(obj as any))}\" ${gap < 0 ? \"(accidental contact)\" : `(gap: ${gap.toFixed(3)}mm)`}`,\n pcb_trace_id: segmentA.pcb_trace_id,\n center: getClosestPointBetweenSegmentAndBounds(\n segmentA,\n getCollidableBounds(obj),\n ),\n source_trace_id: \"\",\n pcb_trace_error_id,\n pcb_component_ids: [\n \"pcb_component_id\" in obj\n ? (obj.pcb_component_id as string)\n : undefined,\n ].filter(Boolean) as string[],\n pcb_port_ids: [\n ...getPcbPortIdsConnectedToTraces([segmentA._pcbTrace]),\n \"pcb_port_id\" in obj ? obj.pcb_port_id : undefined,\n ].filter(Boolean) as string[],\n })\n }\n\n // Handle generic case of hitting the bounds of any collidable obstacle\n // using the bounds of the collidable obstacle\n const gap =\n segmentToBoundsMinDistance(\n { x: segmentA.x1, y: segmentA.y1 },\n { x: segmentA.x2, y: segmentA.y2 },\n getCollidableBounds(obj),\n ) -\n segmentA.thickness / 2\n if (gap + EPSILON < requiredMargin) {\n const pcb_trace_error_id = `overlap_${segmentA.pcb_trace_id}_${primaryObjId}`\n if (errorIds.has(pcb_trace_error_id)) continue\n errorIds.add(pcb_trace_error_id)\n errors.push({\n type: \"pcb_trace_error\",\n error_type: \"pcb_trace_error\",\n message: `PCB trace ${getReadableName(segmentA.pcb_trace_id)} overlaps with ${obj.type} \"${getReadableName(getPrimaryId(obj as any))}\" ${gap < 0 ? \"(accidental contact)\" : `(gap: ${gap.toFixed(3)}mm)`}`,\n pcb_trace_id: segmentA.pcb_trace_id,\n source_trace_id: \"\",\n pcb_trace_error_id,\n pcb_component_ids: [\n \"pcb_component_id\" in obj ? obj.pcb_component_id : undefined,\n ].filter(Boolean) as string[],\n center: getClosestPointBetweenSegmentAndBounds(\n segmentA,\n getCollidableBounds(obj),\n ),\n pcb_port_ids: [\n ...getPcbPortIdsConnectedToTraces([segmentA._pcbTrace]),\n \"pcb_port_id\" in obj ? obj.pcb_port_id : undefined,\n ].filter(Boolean) as string[],\n })\n }\n }\n }\n return errors\n}\n","export type BucketCoordinate = `${number}x${number}`\n\nexport interface Bounds {\n minX: number\n minY: number\n maxX: number\n maxY: number\n}\n\nexport type GetBoundsFn<T> = (obj: T) => Bounds\nexport type GetIdFn<T> = (obj: T) => string\n\nexport class SpatialObjectIndex<T> {\n buckets: Map<BucketCoordinate, Array<T & { spatialIndexId: string }>>\n objectsById: Map<string, T & { spatialIndexId: string }>\n getBounds: GetBoundsFn<T>\n getId: GetIdFn<T>\n CELL_SIZE = 0.4\n\n constructor({\n objects,\n getBounds,\n getId,\n CELL_SIZE,\n }: {\n objects: T[]\n getBounds: GetBoundsFn<T>\n getId?: GetIdFn<T>\n CELL_SIZE?: number\n }) {\n this.buckets = new Map()\n this.objectsById = new Map()\n this.getBounds = getBounds\n this.getId = getId ?? (() => this._getNextId())\n this.CELL_SIZE = CELL_SIZE ?? this.CELL_SIZE\n\n for (const obj of objects) {\n this.addObject(obj)\n }\n }\n\n _idCounter = 0\n _getNextId(): string {\n return `${this._idCounter++}`\n }\n\n addObject(obj: T): void {\n const bounds = this.getBounds(obj)\n const spatialIndexId = this.getId(obj)\n const objWithId = { ...obj, spatialIndexId } as T & {\n spatialIndexId: string\n }\n\n // Store in objectsById for quick lookup\n this.objectsById.set(spatialIndexId, objWithId)\n\n // Calculate the bucket coordinates that cover the object's bounds\n const minBucketX = Math.floor(bounds.minX / this.CELL_SIZE)\n const minBucketY = Math.floor(bounds.minY / this.CELL_SIZE)\n const maxBucketX = Math.floor(bounds.maxX / this.CELL_SIZE)\n const maxBucketY = Math.floor(bounds.maxY / this.CELL_SIZE)\n\n // Add the object to all buckets it intersects with\n for (let bx = minBucketX; bx <= maxBucketX; bx++) {\n for (let by = minBucketY; by <= maxBucketY; by++) {\n const bucketKey = `${bx}x${by}` as BucketCoordinate\n const bucket = this.buckets.get(bucketKey)\n if (!bucket) {\n this.buckets.set(bucketKey, [objWithId])\n } else {\n bucket.push(objWithId)\n }\n }\n }\n }\n\n removeObject(id: string): boolean {\n const obj = this.objectsById.get(id)\n if (!obj) return false\n\n // Remove from objectsById\n this.objectsById.delete(id)\n\n // Calculate the bucket coordinates that cover the object's bounds\n const bounds = this.getBounds(obj)\n const minBucketX = Math.floor(bounds.minX / this.CELL_SIZE)\n const minBucketY = Math.floor(bounds.minY / this.CELL_SIZE)\n const maxBucketX = Math.floor(bounds.maxX / this.CELL_SIZE)\n const maxBucketY = Math.floor(bounds.maxY / this.CELL_SIZE)\n\n // Remove the object from all buckets it might be in\n for (let bx = minBucketX; bx <= maxBucketX; bx++) {\n for (let by = minBucketY; by <= maxBucketY; by++) {\n const bucketKey = `${bx}x${by}` as BucketCoordinate\n const bucket = this.buckets.get(bucketKey)\n if (bucket) {\n const index = bucket.findIndex((item) => item.spatialIndexId === id)\n if (index !== -1) {\n bucket.splice(index, 1)\n if (bucket.length === 0) {\n this.buckets.delete(bucketKey)\n }\n }\n }\n }\n }\n\n return true\n }\n\n getBucketKey(x: number, y: number): BucketCoordinate {\n return `${Math.floor(x / this.CELL_SIZE)}x${Math.floor(y / this.CELL_SIZE)}`\n }\n\n getObjectsInBounds(bounds: Bounds, margin = 0): T[] {\n const objects: T[] = []\n const addedIds = new Set<string>()\n\n // Calculate the bucket coordinates that cover the requested bounds with margin\n const minBucketX = Math.floor((bounds.minX - margin) / this.CELL_SIZE)\n const minBucketY = Math.floor((bounds.minY - margin) / this.CELL_SIZE)\n const maxBucketX = Math.floor((bounds.maxX + margin) / this.CELL_SIZE)\n const maxBucketY = Math.floor((bounds.maxY + margin) / this.CELL_SIZE)\n\n // Collect objects from all buckets that intersect the bounds\n for (let bx = minBucketX; bx <= maxBucketX; bx++) {\n for (let by = minBucketY; by <= maxBucketY; by++) {\n const bucketKey = `${bx}x${by}` as BucketCoordinate\n const bucket = this.buckets.get(bucketKey) || []\n\n for (const obj of bucket) {\n const id = obj.spatialIndexId\n if (addedIds.has(id)) continue\n\n addedIds.add(id)\n objects.push(obj)\n }\n }\n }\n\n return objects\n }\n}\n","import { getBoundsOfPcbElements } from \"@tscircuit/circuit-json-util\"\nimport type {\n AnyCircuitElement,\n PcbHole,\n PCBKeepout,\n PcbPlatedHole,\n PcbSmtPad,\n PcbTrace,\n PcbTraceError,\n PcbVia,\n} from \"circuit-json\"\n\ninterface Bounds {\n minX: number\n minY: number\n maxX: number\n maxY: number\n}\n\nexport type PcbTraceSegment = {\n type: \"pcb_trace_segment\"\n _pcbTrace: PcbTrace\n pcb_trace_id: string\n thickness: number\n layer: string\n x1: number\n y1: number\n x2: number\n y2: number\n}\n\nexport type Collidable =\n | PcbTraceSegment\n | PcbSmtPad\n | PcbPlatedHole\n | PcbHole\n | PcbVia\n | PCBKeepout\n\nexport const getCollidableBounds = (collidable: Collidable): Bounds => {\n if (collidable.type === \"pcb_trace_segment\") {\n return {\n minX: Math.min(collidable.x1, collidable.x2),\n minY: Math.min(collidable.y1, collidable.y2),\n maxX: Math.max(collidable.x1, collidable.x2),\n maxY: Math.max(collidable.y1, collidable.y2),\n }\n }\n return getBoundsOfPcbElements([collidable as any])\n}\n","export const DEFAULT_TRACE_MARGIN = 0.1\nexport const DEFAULT_TRACE_THICKNESS = 0.15\nexport const DEFAULT_VIA_DIAMETER = 0.6\nexport const DEFAULT_VIA_BOARD_MARGIN = 0.3\n\nexport const DEFAULT_SAME_NET_VIA_MARGIN = 0.2\nexport const DEFAULT_DIFFERENT_NET_VIA_MARGIN = 0.3\n\n// jlcpcb default values (Might not pass kicad drc so change the project settings in kicad)\nexport const DEFAULT_PAD_PAD_CLEARANCE = 0.1\nexport const DEFAULT_PAD_TRACE_CLEARANCE = 0.1\n\nexport const EPSILON = 0.005\n","import type { PcbTrace } from \"circuit-json\"\n\nexport function getPcbPortIdsConnectedToTrace(trace: PcbTrace) {\n const connectedPcbPorts = new Set<string>()\n for (const segment of trace.route) {\n if (segment.route_type === \"wire\") {\n if (segment.start_pcb_port_id)\n connectedPcbPorts.add(segment.start_pcb_port_id)\n if (segment.end_pcb_port_id)\n connectedPcbPorts.add(segment.end_pcb_port_id)\n }\n }\n\n return Array.from(connectedPcbPorts)\n}\n\nexport function getPcbPortIdsConnectedToTraces(traces: PcbTrace[]) {\n const connectedPorts = new Set<string>()\n for (const trace of traces) {\n for (const portId of getPcbPortIdsConnectedToTrace(trace)) {\n connectedPorts.add(portId)\n }\n }\n return Array.from(connectedPorts)\n}\n","import type { PcbTraceSegment } from \"./getCollidableBounds\"\n\nexport const getClosestPointBetweenSegments = (\n segmentA: PcbTraceSegment,\n segmentB: PcbTraceSegment,\n): { x: number; y: number } => {\n // Define points for each segment\n const a1 = { x: segmentA.x1, y: segmentA.y1 }\n const a2 = { x: segmentA.x2, y: segmentA.y2 }\n const b1 = { x: segmentB.x1, y: segmentB.y1 }\n const b2 = { x: segmentB.x2, y: segmentB.y2 }\n\n // Calculate direction vectors for each segment\n const va = { x: a2.x - a1.x, y: a2.y - a1.y }\n const vb = { x: b2.x - b1.x, y: b2.y - b1.y }\n\n // Calculate squared lengths of segments\n const lenSqrA = va.x * va.x + va.y * va.y\n const lenSqrB = vb.x * vb.x + vb.y * vb.y\n\n // If either segment is a point (zero length), handle as special case\n if (lenSqrA === 0 || lenSqrB === 0) {\n if (lenSqrA === 0 && lenSqrB === 0) {\n // Both segments are points, return the distance between them\n // Calculate the average point\n return {\n x: (a1.x + b1.x) / 2,\n y: (a1.y + b1.y) / 2,\n }\n }\n if (lenSqrA === 0) {\n // First segment is a point, find closest point on second segment\n const t = clamp(\n ((a1.x - b1.x) * vb.x + (a1.y - b1.y) * vb.y) / lenSqrB,\n 0,\n 1,\n )\n const closestOnB = {\n x: b1.x + t * vb.x,\n y: b1.y + t * vb.y,\n }\n // Calculate the average point\n return {\n x: (a1.x + closestOnB.x) / 2,\n y: (a1.y + closestOnB.y) / 2,\n }\n }\n // Second segment is a point, find closest point on first segment\n const t = clamp(\n ((b1.x - a1.x) * va.x + (b1.y - a1.y) * va.y) / lenSqrA,\n 0,\n 1,\n )\n const closestOnA = {\n x: a1.x + t * va.x,\n y: a1.y + t * va.y,\n }\n // Calculate the average point\n return {\n x: (closestOnA.x + b1.x) / 2,\n y: (closestOnA.y + b1.y) / 2,\n }\n }\n\n // Vector between segment starting points\n const w = { x: a1.x - b1.x, y: a1.y - b1.y }\n\n // Calculate dot products\n const dotAA = va.x * va.x + va.y * va.y\n const dotAB = va.x * vb.x + va.y * vb.y\n const dotAW = va.x * w.x + va.y * w.y\n const dotBB = vb.x * vb.x + vb.y * vb.y\n const dotBW = vb.x * w.x + vb.y * w.y\n\n // Calculate parametric positions (t values) along each segment\n const denominator = dotAA * dotBB - dotAB * dotAB\n\n // If segments are parallel, handle separately\n if (denominator < 1e-10) {\n return closestPointsParallelSegments(\n a1,\n a2,\n b1,\n b2,\n va,\n vb,\n lenSqrA,\n lenSqrB,\n )\n }\n\n // Calculate parameters for closest points\n let tA = (dotAB * dotBW - dotBB * dotAW) / denominator\n let tB = (dotAA * dotBW - dotAB * dotAW) / denominator\n\n // Clamp parameters to segment bounds\n tA = clamp(tA, 0, 1)\n tB = clamp(tB, 0, 1)\n\n // Recalculate tB if tA was clamped\n tB = (tA * dotAB + dotBW) / dotBB\n tB = clamp(tB, 0, 1)\n\n // Recalculate tA if tB was clamped\n tA = (tB * dotAB - dotAW) / dotAA\n tA = clamp(tA, 0, 1)\n\n // Calculate closest points on each segment\n const closestOnA = {\n x: a1.x + tA * va.x,\n y: a1.y + tA * va.y,\n }\n\n const closestOnB = {\n x: b1.x + tB * vb.x,\n y: b1.y + tB * vb.y,\n }\n\n // Calculate distance between closest points\n const dx = closestOnA.x - closestOnB.x\n const dy = closestOnA.y - closestOnB.y\n const distance = Math.sqrt(dx * dx + dy * dy)\n\n // Calculate the average of the closest points\n const averagePoint = {\n x: (closestOnA.x + closestOnB.x) / 2,\n y: (closestOnA.y + closestOnB.y) / 2,\n }\n\n return averagePoint\n}\n\n// Helper function for handling parallel segments\nconst closestPointsParallelSegments = (\n a1: { x: number; y: number },\n a2: { x: number; y: number },\n b1: { x: number; y: number },\n b2: { x: number; y: number },\n va: { x: number; y: number },\n vb: { x: number; y: number },\n lenSqrA: number,\n lenSqrB: number,\n) => {\n // Project b1 onto segment A\n let tA = ((b1.x - a1.x) * va.x + (b1.y - a1.y) * va.y) / lenSqrA\n tA = clamp(tA, 0, 1)\n const pointOnA1 = { x: a1.x + tA * va.x, y: a1.y + tA * va.y }\n\n // Project b2 onto segment A\n let tA2 = ((b2.x - a1.x) * va.x + (b2.y - a1.y) * va.y) / lenSqrA\n tA2 = clamp(tA2, 0, 1)\n const pointOnA2 = { x: a1.x + tA2 * va.x, y: a1.y + tA2 * va.y }\n\n // Project a1 onto segment B\n let tB = ((a1.x - b1.x) * vb.x + (a1.y - b1.y) * vb.y) / lenSqrB\n tB = clamp(tB, 0, 1)\n const pointOnB1 = { x: b1.x + tB * vb.x, y: b1.y + tB * vb.y }\n\n // Project a2 onto segment B\n let tB2 = ((a2.x - b1.x) * vb.x + (a2.y - b1.y) * vb.y) / lenSqrB\n tB2 = clamp(tB2, 0, 1)\n const pointOnB2 = { x: b1.x + tB2 * vb.x, y: b1.y + tB2 * vb.y }\n\n // Calculate all possible distances between end points and their projections\n const distances = [\n {\n pointA: pointOnA1,\n pointB: b1,\n distance: Math.sqrt(\n (pointOnA1.x - b1.x) ** 2 + (pointOnA1.y - b1.y) ** 2,\n ),\n },\n {\n pointA: pointOnA2,\n pointB: b2,\n distance: Math.sqrt(\n (pointOnA2.x - b2.x) ** 2 + (pointOnA2.y - b2.y) ** 2,\n ),\n },\n {\n pointA: a1,\n pointB: pointOnB1,\n distance: Math.sqrt(\n (a1.x - pointOnB1.x) ** 2 + (a1.y - pointOnB1.y) ** 2,\n ),\n },\n {\n pointA: a2,\n pointB: pointOnB2,\n distance: Math.sqrt(\n (a2.x - pointOnB2.x) ** 2 + (a2.y - pointOnB2.y) ** 2,\n ),\n },\n ]\n\n // Find closest pair\n const closestPair = distances.reduce((closest, current) =>\n current.distance < closest.distance ? current : closest,\n )\n\n // Calculate the average of the closest points\n return {\n x: (closestPair.pointA.x + closestPair.pointB.x) / 2,\n y: (closestPair.pointA.y + closestPair.pointB.y) / 2,\n }\n}\n\n// Helper function to clamp a value between min and max\nconst clamp = (value: number, min: number, max: number): number => {\n return Math.max(min, Math.min(max, value))\n}\n","import type {\n AnyCircuitElement,\n PcbHole,\n PcbPlatedHole,\n PcbVia,\n PcbSmtPad,\n} from \"circuit-json\"\n\nexport const getRadiusOfCircuitJsonElement = (\n obj: PcbVia | PcbPlatedHole | PcbHole | PcbSmtPad,\n) => {\n if (obj.type === \"pcb_via\") {\n return obj.outer_diameter / 2\n }\n if (obj.type === \"pcb_plated_hole\" && obj.shape === \"circle\") {\n return obj.outer_diameter / 2\n }\n if (obj.type === \"pcb_hole\" && obj.hole_shape === \"circle\") {\n return obj.hole_diameter / 2\n }\n if (obj.type === \"pcb_smtpad\" && obj.shape === \"circle\") {\n return obj.radius\n }\n throw new Error(\n `Could not determine radius of element: ${JSON.stringify(obj)}`,\n )\n}\n","import type { Bounds } from \"lib/data-structures/SpatialIndex\"\nimport type { PcbTraceSegment } from \"./getCollidableBounds\"\n\nexport const getClosestPointBetweenSegmentAndBounds = (\n segment: PcbTraceSegment,\n bounds: Bounds,\n): { x: number; y: number } => {\n // Define segment points\n const p1 = { x: segment.x1, y: segment.y1 }\n const p2 = { x: segment.x2, y: segment.y2 }\n\n // Define bounds corners\n const minX = bounds.minX\n const minY = bounds.minY\n const maxX = bounds.maxX\n const maxY = bounds.maxY\n\n // Check if segment is a point\n if (p1.x === p2.x && p1.y === p2.y) {\n // For a point, find the closest point on the bounds\n const closestX = Math.max(minX, Math.min(maxX, p1.x))\n const closestY = Math.max(minY, Math.min(maxY, p1.y))\n\n // If the point is inside the bounds, return the point itself\n if (closestX === p1.x && closestY === p1.y) {\n return { x: p1.x, y: p1.y }\n }\n\n // Otherwise, return the closest point on the bounds\n return { x: closestX, y: closestY }\n }\n\n // Calculate direction vector of the segment\n const dx = p2.x - p1.x\n const dy = p2.y - p1.y\n\n // Calculate parameter values for intersection with each boundary\n const tMinX = dx !== 0 ? (minX - p1.x) / dx : Number.NEGATIVE_INFINITY\n const tMaxX = dx !== 0 ? (maxX - p1.x) / dx : Number.POSITIVE_INFINITY\n const tMinY = dy !== 0 ? (minY - p1.y) / dy : Number.NEGATIVE_INFINITY\n const tMaxY = dy !== 0 ? (maxY - p1.y) / dy : Number.POSITIVE_INFINITY\n\n // Find the entering and exiting parameters\n const tEnter = Math.max(Math.min(tMinX, tMaxX), Math.min(tMinY, tMaxY))\n const tExit = Math.min(Math.max(tMinX, tMaxX), Math.max(tMinY, tMaxY))\n\n // Check if segment intersects the bounds\n if (tEnter <= tExit && tExit >= 0 && tEnter <= 1) {\n // Segment intersects bounds, clamp parameter to segment\n const t = Math.max(0, Math.min(1, tEnter))\n return {\n x: p1.x + t * dx,\n y: p1.y + t * dy,\n }\n }\n\n // Segment doesn't intersect bounds, find closest point\n // Check each endpoint of the segment against the bounds\n const closestToP1 = {\n x: Math.max(minX, Math.min(maxX, p1.x)),\n y: Math.max(minY, Math.min(maxY, p1.y)),\n }\n\n const closestToP2 = {\n x: Math.max(minX, Math.min(maxX, p2.x)),\n y: Math.max(minY, Math.min(maxY, p2.y)),\n }\n\n // Calculate distances\n const distToP1Squared =\n (closestToP1.x - p1.x) ** 2 + (closestToP1.y - p1.y) ** 2\n const distToP2Squared =\n (closestToP2.x - p2.x) ** 2 + (closestToP2.y - p2.y) ** 2\n\n // Check each edge of the bounds against the segment\n const edges = [\n { start: { x: minX, y: minY }, end: { x: maxX, y: minY } }, // Bottom edge\n { start: { x: maxX, y: minY }, end: { x: maxX, y: maxY } }, // Right edge\n { start: { x: maxX, y: maxY }, end: { x: minX, y: maxY } }, // Top edge\n { start: { x: minX, y: maxY }, end: { x: minX, y: minY } }, // Left edge\n ]\n\n let minDistance = Math.min(distToP1Squared, distToP2Squared)\n let closestPoint =\n distToP1Squared <= distToP2Squared ? closestToP1 : closestToP2\n\n // Helper function to clamp a value between min and max\n const clamp = (value: number, min: number, max: number): number => {\n return Math.max(min, Math.min(max, value))\n }\n\n // Check each edge\n for (const edge of edges) {\n // Calculate direction vectors\n const va = { x: p2.x - p1.x, y: p2.y - p1.y }\n const vb = { x: edge.end.x - edge.start.x, y: edge.end.y - edge.start.y }\n const w = { x: p1.x - edge.start.x, y: p1.y - edge.start.y }\n\n // Calculate dot products\n const dotAA = va.x * va.x + va.y * va.y\n const dotAB = va.x * vb.x + va.y * vb.y\n const dotAW = va.x * w.x + va.y * w.y\n const dotBB = vb.x * vb.x + vb.y * vb.y\n const dotBW = vb.x * w.x + vb.y * w.y\n\n // Calculate parameters for closest points\n const denominator = dotAA * dotBB - dotAB * dotAB\n\n // Skip if lines are parallel\n if (Math.abs(denominator) < 1e-10) continue\n\n let tA = (dotAB * dotBW - dotBB * dotAW) / denominator\n let tB = (dotAA * dotBW - dotAB * dotAW) / denominator\n\n // Clamp parameters to segment bounds\n tA = clamp(tA, 0, 1)\n tB = clamp(tB, 0, 1)\n\n // Calculate closest points\n const closestOnSegment = {\n x: p1.x + tA * va.x,\n y: p1.y + tA * va.y,\n }\n\n const closestOnEdge = {\n x: edge.start.x + tB * vb.x,\n y: edge.start.y + tB * vb.y,\n }\n\n // Calculate distance\n const dx = closestOnSegment.x - closestOnEdge.x\n const dy = closestOnSegment.y - closestOnEdge.y\n const distSquared = dx * dx + dy * dy\n\n // Update if this is closer\n if (distSquared < minDistance) {\n minDistance = distSquared\n closestPoint = {\n x: (closestOnSegment.x + closestOnEdge.x) / 2,\n y: (closestOnSegment.y + closestOnEdge.y) / 2,\n }\n }\n }\n\n return closestPoint\n}\n","import type { Collidable } from \"lib/check-each-pcb-trace-non-overlapping/getCollidableBounds\"\nimport { all_layers } from \"circuit-json\"\n\nexport function getLayersOfPcbElement(obj: Collidable): string[] {\n if (obj.type === \"pcb_trace_segment\") {\n return [obj.layer]\n }\n if (obj.type === \"pcb_smtpad\") {\n return [obj.layer]\n }\n if (obj.type === \"pcb_plated_hole\") {\n return Array.isArray(obj.layers) ? obj.layers : [...all_layers]\n }\n if (obj.type === \"pcb_hole\") {\n return [...all_layers]\n }\n if (obj.type === \"pcb_via\") {\n return Array.isArray(obj.layers) ? obj.layers : [...all_layers]\n }\n if (obj.type === \"pcb_keepout\") {\n return Array.isArray(obj.layers) ? obj.layers : []\n }\n return []\n}\n","export class NetManager {\n private networks: Set<Set<string>> = new Set()\n\n setConnected(nodes: string[]): void {\n if (nodes.length < 2) return\n\n let targetNetwork: Set<string> | null = null\n\n // Check if any of the nodes are already in a network\n for (const network of this.networks) {\n for (const node of nodes) {\n if (network.has(node)) {\n if (targetNetwork === null) {\n targetNetwork = network\n } else if (targetNetwork !== network) {\n // Merge networks\n for (const mergeNode of network) {\n targetNetwork.add(mergeNode)\n }\n this.networks.delete(network)\n }\n break\n }\n }\n if (targetNetwork !== null && targetNetwork !== network) break\n }\n\n // If no existing network found, create a new one\n if (targetNetwork === null) {\n targetNetwork = new Set(nodes)\n this.networks.add(targetNetwork)\n } else {\n // Add all nodes to the target network\n for (const node of nodes) {\n targetNetwork.add(node)\n }\n }\n }\n\n isConnected(nodes: string[]): boolean {\n if (nodes.length < 2) return true\n\n for (const network of this.networks) {\n if (nodes.every((node) => network.has(node))) {\n return true\n }\n }\n\n return false\n }\n}\n","import type {\n AnyCircuitElement,\n PcbBoard,\n PcbVia,\n PcbPlacementError,\n} from \"circuit-json\"\nimport { getReadableNameForElement } from \"@tscircuit/circuit-json-util\"\nimport { DEFAULT_VIA_BOARD_MARGIN } from \"lib/drc-defaults\"\n\nexport function checkViasOffBoard(\n circuitJson: AnyCircuitElement[],\n): PcbPlacementError[] {\n const board = circuitJson.find((el) => el.type === \"pcb_board\") as PcbBoard\n\n if (!board) return []\n\n const vias = circuitJson.filter((el) => el.type === \"pcb_via\") as PcbVia[]\n\n if (vias.length === 0) return []\n\n if (board.width === undefined || board.height === undefined) return []\n\n const boardMinX = board.center.x - board.width / 2\n const boardMaxX = board.center.x + board.width / 2\n const boardMinY = board.center.y - board.height / 2\n const boardMaxY = board.center.y + board.height / 2\n\n const errors: PcbPlacementError[] = []\n\n for (const via of vias) {\n const viaRadius = via.outer_diameter / 2\n const viaMinX = via.x - viaRadius\n const viaMaxX = via.x + viaRadius\n const viaMinY = via.y - viaRadius\n const viaMaxY = via.y + viaRadius\n\n if (\n viaMinX < boardMinX + DEFAULT_VIA_BOARD_MARGIN ||\n viaMaxX > boardMaxX - DEFAULT_VIA_BOARD_MARGIN ||\n viaMinY < boardMinY + DEFAULT_VIA_BOARD_MARGIN ||\n viaMaxY > boardMaxY - DEFAULT_VIA_BOARD_MARGIN\n ) {\n const viaName = getReadableNameForElement(circuitJson, via.pcb_via_id)\n errors.push({\n type: \"pcb_placement_error\",\n pcb_placement_error_id: `out_of_board_${via.pcb_via_id}`,\n message: `Via ${viaName} is outside or crossing the board boundary`,\n error_type: \"pcb_placement_error\",\n })\n }\n }\n\n return errors\n}\n","import type {\n AnyCircuitElement,\n PcbBoard,\n PcbComponent,\n AnySourceComponent,\n PcbComponentOutsideBoardError,\n} from \"circuit-json\"\nimport { getReadableNameForComponent } from \"lib/util/get-readable-names\"\nimport type { Point } from \"@tscircuit/math-utils\"\nimport * as Flatten from \"@flatten-js/core\"\nimport { rotateDEG, applyToPoint } from \"transformation-matrix\"\n\n/**\n * Create a rectangle polygon centered at (cx,cy) with given width/height and rotation (degrees).\n * Uses transformation-matrix to rotate around the center and returns a Flatten.Polygon.\n */\n\nfunction isPolygonCCW(poly: Flatten.Polygon): boolean {\n // @ts-ignore\n return poly.area() >= 0\n}\n\nfunction rectanglePolygon({\n center,\n size,\n rotationDeg = 0,\n}: {\n center: Point\n size: { width: number; height: number }\n rotationDeg?: number\n}): Flatten.Polygon {\n const cx = center.x\n const cy = center.y\n const hw = size.width / 2\n const hh = size.height / 2\n\n const corners: Flatten.Point[] = [\n new Flatten.Point(cx - hw, cy - hh),\n new Flatten.Point(cx + hw, cy - hh),\n new Flatten.Point(cx + hw, cy + hh),\n new Flatten.Point(cx - hw, cy + hh),\n ]\n\n let poly = new Flatten.Polygon(corners)\n\n if (rotationDeg) {\n const matrix = rotateDEG(rotationDeg, cx, cy)\n const rotatedCorners = corners.map((pt) => {\n const p = applyToPoint(matrix, { x: pt.x, y: pt.y })\n return new Flatten.Point(p.x, p.y)\n })\n poly = new Flatten.Polygon(rotatedCorners)\n }\n\n // Ensure CCW order\n if (!isPolygonCCW(poly)) poly.reverse()\n\n return poly\n}\n\nfunction boardToPolygon({\n board,\n}: {\n board: PcbBoard\n}): Flatten.Polygon | null {\n if (board.outline && board.outline.length > 0) {\n const points = board.outline.map((p) => new Flatten.Point(p.x, p.y))\n const poly = new Flatten.Polygon(points)\n\n if (!isPolygonCCW(poly)) {\n poly.reverse()\n }\n\n return poly\n }\n\n if (\n board.center &&\n typeof board.width === \"number\" &&\n typeof board.height === \"number\"\n ) {\n return rectanglePolygon({\n center: board.center,\n size: { width: board.width, height: board.height },\n rotationDeg: 0,\n })\n }\n\n return null\n}\n\n/** Get human-readable component name from circuit JSON */\nfunction getComponentName({\n circuitJson,\n component,\n}: {\n circuitJson: AnyCircuitElement[]\n component: PcbComponent\n}): string {\n if (component.source_component_id) {\n const sourceComponent = circuitJson.find(\n (el): el is AnySourceComponent =>\n el.type === \"source_component\" &&\n el.source_component_id === component.source_component_id,\n )\n if (sourceComponent && \"name\" in sourceComponent && sourceComponent.name) {\n return sourceComponent.name\n }\n }\n\n return getReadableNameForComponent(circuitJson, component.pcb_component_id)\n}\n\n/**\n * Compute overlap distance based on polygon containment and distances.\n * - If component center is outside board, distance is from center to board polygon.\n * - Samples rotated rectangle corners and edge midpoints; for any outside points,\n * computes distance to board polygon and returns the maximum.\n * - Falls back to intersection area ratio heuristic if needed.\n * - Returns small epsilon if fully inside.\n */\nfunction computeOverlapDistance(\n compPoly: Flatten.Polygon,\n boardPoly: Flatten.Polygon,\n componentCenter: Point,\n componentWidth: number,\n componentHeight: number,\n rotationDeg: number,\n): number {\n const centerPoint = new Flatten.Point(componentCenter.x, componentCenter.y)\n // If center is outside board polygon, return distance to board polygon\n if (!boardPoly.contains(centerPoint)) {\n const dist = boardPoly.distanceTo(centerPoint)\n return Array.isArray(dist) ? dist[0] : Number(dist) || 0\n }\n\n // Sample corners and edge midpoints of rotated rectangle\n const hw = componentWidth / 2\n const hh = componentHeight / 2\n\n // Original corners\n const corners: Point[] = [\n { x: componentCenter.x - hw, y: componentCenter.y - hh },\n { x: componentCenter.x + hw, y: componentCenter.y - hh },\n { x: componentCenter.x + hw, y: componentCenter.y + hh },\n { x: componentCenter.x - hw, y: componentCenter.y + hh },\n ]\n\n // Edge midpoints\n const midpoints: Point[] = []\n for (let i = 0; i < 4; i++) {\n const next = (i + 1) % 4\n midpoints.push({\n x: (corners[i].x + corners[next].x) / 2,\n y: (corners[i].y + corners[next].y) / 2,\n })\n }\n\n // Rotate points around center by rotationDeg\n const matrix = rotateDEG(rotationDeg, componentCenter.x, componentCenter.y)\n const rotatePoint = (pt: Point) => {\n const p = applyToPoint(matrix, pt)\n return new Flatten.Point(p.x, p.y)\n }\n\n const rotatedPoints = corners.concat(midpoints).map(rotatePoint)\n\n // For any point outside board polygon, compute distance to board polygon\n let maxDistance = 0\n for (const pt of rotatedPoints) {\n if (!boardPoly.contains(pt)) {\n const dist = boardPoly.distanceTo(pt)\n const d = Array.isArray(dist) ? dist[0] : Number(dist) || 0\n if (d > maxDistance) maxDistance = d\n }\n }\n\n if (maxDistance > 0) {\n return maxDistance\n }\n\n // Fallback: use intersection area ratio heuristic\n try {\n const intersection = Flatten.BooleanOperations.intersect(\n compPoly,\n boardPoly,\n )\n\n let intersectionArea = 0\n if (!intersection) {\n intersectionArea = 0\n } else if (Array.isArray(intersection)) {\n intersectionArea = intersection.reduce(\n (sum, p) => sum + (typeof p.area === \"function\" ? p.area() : 0),\n 0,\n )\n } else if (typeof (intersection as any).area === \"function\") {\n intersectionArea = (intersection as any).area()\n } else {\n intersectionArea = 0\n }\n\n const compArea = compPoly.area()\n\n if (intersectionArea > 0 && intersectionArea < compArea) {\n const overlapRatio = 1 - intersectionArea / compArea\n const compWidth = Math.abs(componentWidth)\n const compHeight = Math.abs(componentHeight)\n return Math.min(compWidth, compHeight) * overlapRatio\n } else if (intersectionArea === 0) {\n // completely outside (should not happen here), return small epsilon\n return 0.1\n } else {\n return 0.1\n }\n } catch {\n // If boolean ops fail (unlikely), return small epsilon\n return 0.1\n }\n}\n\nfunction getRepositionSuggestion({\n componentPoly,\n boardPoly,\n}: {\n componentPoly: Flatten.Polygon\n boardPoly: Flatten.Polygon\n}): string | null {\n const boardBox = boardPoly.box\n const componentBox = componentPoly.box\n\n let deltaX = 0\n let deltaY = 0\n\n if (componentBox.xmin < boardBox.xmin) {\n deltaX = boardBox.xmin - componentBox.xmin\n } else if (componentBox.xmax > boardBox.xmax) {\n deltaX = boardBox.xmax - componentBox.xmax\n }\n\n if (componentBox.ymin < boardBox.ymin) {\n deltaY = boardBox.ymin - componentBox.ymin\n } else if (componentBox.ymax > boardBox.ymax) {\n deltaY = boardBox.ymax - componentBox.ymax\n }\n\n if (deltaX === 0 && deltaY === 0) {\n return null\n }\n\n const xDir = deltaX >= 0 ? \"right\" : \"left\"\n const yDir = deltaY >= 0 ? \"up\" : \"down\"\n const absDx = Math.abs(Math.round(deltaX * 100) / 100)\n const absDy = Math.abs(Math.round(deltaY * 100) / 100)\n\n if (absDx > 0 && absDy > 0) {\n return `Try moving it ${absDx}mm ${xDir} and ${absDy}mm ${yDir} to fit within the board edge.`\n }\n\n if (absDx > 0) {\n return `Try moving it ${absDx}mm ${xDir} to fit within the board edge.`\n }\n\n return `Try moving it ${absDy}mm ${yDir} to fit within the board edge.`\n}\n\n/**\n * Main function — polygon-first: construct polygons, test containment / intersection,\n * compute overlap distance using boolean intersection area or geometric distance.\n */\nexport function checkPcbComponentsOutOfBoard(\n circuitJson: AnyCircuitElement[],\n): PcbComponentOutsideBoardError[] {\n const board = circuitJson.find(\n (el): el is PcbBoard => el.type === \"pcb_board\",\n )\n if (!board) return []\n\n const boardPoly = boardToPolygon({ board })\n if (!boardPoly) return []\n\n const components = circuitJson.filter(\n (el): el is PcbComponent => el.type === \"pcb_component\",\n )\n if (components.length === 0) return []\n\n const errors: PcbComponentOutsideBoardError[] = []\n\n for (const c of components) {\n if (c.is_allowed_to_be_off_board) continue\n\n // need center, width, height\n if (\n !c.center ||\n typeof c.width !== \"number\" ||\n typeof c.height !== \"number\"\n )\n continue\n\n if (c.width <= 0 || c.height <= 0) continue\n\n // c.width and c.height store the world-coordinate AABB dimensions (post-rotation),\n // so we must NOT re-apply rotation here.\n const compPoly = rectanglePolygon({\n center: c.center,\n size: { width: c.width, height: c.height },\n rotationDeg: 0,\n })\n\n if (compPoly.area() === 0) continue\n\n // If component is entirely inside board polygon -> OK\n // Flatten.Polygon.contains accepts shapes; this is the correct polygon containment check.\n const isInside = boardPoly.contains(compPoly)\n if (isInside) continue\n\n // Component is at least partially outside. Compute overlapDistance:\n const overlapDistance = computeOverlapDistance(\n compPoly,\n boardPoly,\n c.center,\n c.width,\n c.height,\n 0,\n )\n\n const compName = getComponentName({ circuitJson, component: c })\n const overlapDistanceMm = Math.round(overlapDistance * 100) / 100\n const repositionSuggestion = getRepositionSuggestion({\n componentPoly: compPoly,\n boardPoly,\n })\n\n errors.push({\n type: \"pcb_component_outside_board_error\",\n error_type: \"pcb_component_outside_board_error\",\n pcb_component_outside_board_error_id: `pcb_component_outside_board_${c.pcb_component_id}`,\n message: `Component ${compName} extends outside board boundaries by ${overlapDistanceMm}mm.${repositionSuggestion ? ` ${repositionSuggestion}` : \"\"}`,\n pcb_component_id: c.pcb_component_id,\n pcb_board_id: board.pcb_board_id,\n component_center: c.center,\n component_bounds: {\n min_x: compPoly.box.xmin,\n max_x: compPoly.box.xmax,\n min_y: compPoly.box.ymin,\n max_y: compPoly.box.ymax,\n },\n subcircuit_id: c.subcircuit_id,\n source_component_id: c.source_component_id,\n })\n }\n\n return errors\n}\n","import type {\n AnyCircuitElement,\n PcbVia,\n PcbViaClearanceError,\n} from \"circuit-json\"\nimport { getReadableNameForElement } from \"@tscircuit/circuit-json-util\"\nimport {\n getFullConnectivityMapFromCircuitJson,\n type ConnectivityMap,\n} from \"circuit-json-to-connectivity-map\"\nimport { DEFAULT_SAME_NET_VIA_MARGIN, EPSILON } from \"lib/drc-defaults\"\nimport { distance } from \"lib/util/distance\"\nimport { viasAreAtSameLocation } from \"lib/util/viasAreAtSameLocation\"\n\nexport function checkSameNetViaSpacing(\n circuitJson: AnyCircuitElement[],\n {\n connMap,\n minSpacing = DEFAULT_SAME_NET_VIA_MARGIN,\n }: { connMap?: ConnectivityMap; minSpacing?: number } = {},\n): PcbViaClearanceError[] {\n const vias = circuitJson.filter((el) => el.type === \"pcb_via\") as PcbVia[]\n if (vias.length < 2) return []\n connMap ??= getFullConnectivityMapFromCircuitJson(circuitJson)\n const errors: PcbViaClearanceError[] = []\n const reported = new Set<string>()\n\n for (let i = 0; i < vias.length; i++) {\n for (let j = i + 1; j < vias.length; j++) {\n const viaA = vias[i]\n const viaB = vias[j]\n // TODO: It is a very inefficient piece of code, the way to fix it is to use flatbush.\n if (viasAreAtSameLocation(viaA, viaB)) continue\n if (!connMap.areIdsConnected(viaA.pcb_via_id, viaB.pcb_via_id)) continue\n const gap =\n distance(viaA, viaB) - viaA.outer_diameter / 2 - viaB.outer_diameter / 2\n if (gap + EPSILON >= minSpacing) continue\n const pairId = [viaA.pcb_via_id, viaB.pcb_via_id].sort().join(\"_\")\n if (reported.has(pairId)) continue\n reported.add(pairId)\n errors.push({\n type: \"pcb_via_clearance_error\",\n pcb_error_id: `same_net_vias_close_${pairId}`,\n message: `Vias ${getReadableNameForElement(\n circuitJson,\n viaA.pcb_via_id,\n )} and ${getReadableNameForElement(\n circuitJson,\n viaB.pcb_via_id,\n )} are too close together (gap: ${gap.toFixed(3)}mm)`,\n error_type: \"pcb_via_clearance_error\",\n pcb_via_ids: [viaA.pcb_via_id, viaB.pcb_via_id],\n minimum_clearance: minSpacing,\n actual_clearance: gap,\n pcb_center: {\n x: (viaA.x + viaB.x) / 2,\n y: (viaA.y + viaB.y) / 2,\n },\n })\n }\n }\n\n return errors\n}\n","import type { PcbVia } from \"circuit-json\"\n\nexport function distance(a: PcbVia, b: PcbVia): number {\n return Math.hypot(a.x - b.x, a.y - b.y)\n}\n","import type { PcbVia } from \"circuit-json\"\nimport { distance } from \"./distance\"\nimport { EPSILON } from \"lib/drc-defaults\"\n\nexport function viasAreAtSameLocation(a: PcbVia, b: PcbVia): boolean {\n return distance(a, b) <= EPSILON\n}\n","import type {\n AnyCircuitElement,\n PcbVia,\n PcbViaClearanceError,\n} from \"circuit-json\"\nimport { getReadableNameForElement } from \"@tscircuit/circuit-json-util\"\nimport {\n getFullConnectivityMapFromCircuitJson,\n type ConnectivityMap,\n} from \"circuit-json-to-connectivity-map\"\nimport { DEFAULT_DIFFERENT_NET_VIA_MARGIN, EPSILON } from \"lib/drc-defaults\"\nimport { viasAreAtSameLocation } from \"lib/util/viasAreAtSameLocation\"\nimport { distance } from \"lib/util/distance\"\n\nexport function checkDifferentNetViaSpacing(\n circuitJson: AnyCircuitElement[],\n {\n connMap,\n minSpacing = DEFAULT_DIFFERENT_NET_VIA_MARGIN,\n }: { connMap?: ConnectivityMap; minSpacing?: number } = {},\n): PcbViaClearanceError[] {\n const vias = circuitJson.filter((el) => el.type === \"pcb_via\") as PcbVia[]\n if (vias.length < 2) return []\n connMap ??= getFullConnectivityMapFromCircuitJson(circuitJson)\n const errors: PcbViaClearanceError[] = []\n const reported = new Set<string>()\n\n for (let i = 0; i < vias.length; i++) {\n for (let j = i + 1; j < vias.length; j++) {\n const viaA = vias[i]\n const viaB = vias[j]\n // TODO: It is a very inefficient piece of code, the way to fix it is to use flatbush.\n if (viasAreAtSameLocation(viaA, viaB)) continue\n if (connMap.areIdsConnected(viaA.pcb_via_id, viaB.pcb_via_id)) continue\n const gap =\n distance(viaA, viaB) - viaA.outer_diameter / 2 - viaB.outer_diameter / 2\n if (gap + EPSILON >= minSpacing) continue\n const pairId = [viaA.pcb_via_id, viaB.pcb_via_id].sort().join(\"_\")\n if (reported.has(pairId)) continue\n reported.add(pairId)\n errors.push({\n type: \"pcb_via_clearance_error\",\n pcb_error_id: `different_net_vias_close_${pairId}`,\n message: `Vias ${getReadableNameForElement(\n circuitJson,\n viaA.pcb_via_id,\n )} and ${getReadableNameForElement(\n circuitJson,\n viaB.pcb_via_id,\n )} from different nets are too close together (gap: ${gap.toFixed(\n 3,\n )}mm)`,\n error_type: \"pcb_via_clearance_error\",\n pcb_via_ids: [viaA.pcb_via_id, viaB.pcb_via_id],\n minimum_clearance: minSpacing,\n actual_clearance: gap,\n pcb_center: {\n x: (viaA.x + viaB.x) / 2,\n y: (viaA.y + viaB.y) / 2,\n },\n })\n }\n }\n\n return errors\n}\n","import { cju } from \"@tscircuit/circuit-json-util\"\nimport type {\n AnyCircuitElement,\n PcbPort,\n PcbTrace,\n PcbTraceWarning,\n SourceTrace,\n} from \"circuit-json\"\nimport { getFullConnectivityMapFromCircuitJson } from \"circuit-json-to-connectivity-map\"\nimport { getReadableNameForSourceTrace } from \"lib/util/get-readable-names\"\n\nexport function checkSourceTracesMatchPcbTraceThickness(\n circuitJson: AnyCircuitElement[],\n): PcbTraceWarning[] {\n const warnings: PcbTraceWarning[] = []\n const db = cju(circuitJson)\n\n const sourceTraces = db.source_trace.list() as SourceTrace[]\n const pcbTraces = db.pcb_trace.list() as PcbTrace[]\n const pcbPorts = db.pcb_port.list() as PcbPort[]\n const connectivityMap = getFullConnectivityMapFromCircuitJson(circuitJson)\n\n for (const sourceTrace of sourceTraces) {\n const requestedThickness = sourceTrace.min_trace_thickness\n if (requestedThickness === undefined) continue\n\n const connectedPcbPorts = pcbPorts.filter((pcbPort) =>\n sourceTrace.connected_source_port_ids?.includes(pcbPort.source_port_id),\n )\n if (connectedPcbPorts.length < 2) continue\n\n const referenceNetId = connectivityMap.getNetConnectedToId(\n connectedPcbPorts[0].pcb_port_id,\n )\n if (!referenceNetId) continue\n\n const netElementIds = connectivityMap.getIdsConnectedToNet(referenceNetId)\n const relatedPcbTraces = pcbTraces.filter((pcbTrace) =>\n netElementIds.includes(pcbTrace.pcb_trace_id),\n )\n if (relatedPcbTraces.length === 0) continue\n\n const actualWireWidths = relatedPcbTraces.flatMap((pcbTrace) =>\n pcbTrace.route\n .filter((point) => point.route_type === \"wire\")\n .map((point) => point.width),\n )\n if (actualWireWidths.length === 0) continue\n\n const actualThickness = Math.min(...actualWireWidths)\n if (actualThickness >= requestedThickness) continue\n\n let undersizedSegment:\n | { pcb_trace_id: string; center: { x: number; y: number } }\n | undefined\n\n for (const relatedPcbTrace of relatedPcbTraces) {\n for (let i = 0; i < relatedPcbTrace.route.length - 1; i++) {\n const point = relatedPcbTrace.route[i]\n const nextPoint = relatedPcbTrace.route[i + 1]\n if (!point || !nextPoint) continue\n if (point.route_type !== \"wire\" || nextPoint.route_type !== \"wire\") {\n continue\n }\n if (point.width !== actualThickness) continue\n\n undersizedSegment = {\n pcb_trace_id: relatedPcbTrace.pcb_trace_id,\n center: {\n x: (point.x + nextPoint.x) / 2,\n y: (point.y + nextPoint.y) / 2,\n },\n }\n break\n }\n\n if (undersizedSegment) break\n }\n\n if (!undersizedSegment) continue\n\n warnings.push({\n type: \"pcb_trace_warning\",\n pcb_trace_warning_id: `pcb_trace_warning_${sourceTrace.source_trace_id}`,\n warning_type: \"pcb_trace_warning\",\n message: `Trace [${getReadableNameForSourceTrace(circuitJson, sourceTrace)}] is routed thinner than requested (requested: ${requestedThickness}mm, actual: ${actualThickness}mm).`,\n center: undersizedSegment.center,\n source_trace_id: sourceTrace.source_trace_id,\n pcb_trace_id: undersizedSegment.pcb_trace_id,\n pcb_component_ids: Array.from(\n new Set(\n connectedPcbPorts\n .map((pcbPort) => pcbPort.pcb_component_id)\n .filter((id): id is string => id !== undefined),\n ),\n ),\n pcb_port_ids: connectedPcbPorts.map((pcbPort) => pcbPort.pcb_port_id),\n })\n }\n\n return warnings\n}\n","import type {\n AnyCircuitElement,\n PcbPort,\n PcbTrace,\n PcbTraceMissingError,\n SourceTrace,\n} from \"circuit-json\"\nimport { containsCircuitJsonId } from \"lib/util/get-readable-names\"\n\nconst getSourceTraceIdsFromPcbTrace = (\n pcbTraceSourceTraceId?: string,\n): string[] => {\n if (!pcbTraceSourceTraceId) return []\n if (!pcbTraceSourceTraceId.includes(\"__\")) return [pcbTraceSourceTraceId]\n\n return pcbTraceSourceTraceId\n .split(\"__\")\n .filter((part) => part.startsWith(\"source_trace_\"))\n}\n\n/**\n * Check that each source_trace which connects source ports has at least one\n * pcb_trace associated with it. If a source_trace has no corresponding\n * pcb_trace, return an error for that source_trace.\n */\nfunction checkSourceTracesHavePcbTraces(\n circuitJson: AnyCircuitElement[],\n): PcbTraceMissingError[] {\n const errors: PcbTraceMissingError[] = []\n const sourceTraces = circuitJson.filter(\n (el) => el.type === \"source_trace\",\n ) as SourceTrace[]\n const pcbTraces = circuitJson.filter(\n (el) => el.type === \"pcb_trace\",\n ) as PcbTrace[]\n\n for (const sourceTrace of sourceTraces) {\n if (!sourceTrace.connected_source_port_ids?.length) continue\n if ((sourceTrace.connected_source_net_ids?.length ?? 0) > 0) continue\n if (sourceTrace.connected_source_port_ids.length < 2) continue\n\n const hasPcbTrace = pcbTraces.some((pcbTrace) =>\n getSourceTraceIdsFromPcbTrace(pcbTrace.source_trace_id).includes(\n sourceTrace.source_trace_id,\n ),\n )\n if (!hasPcbTrace) {\n // Get PCB ports connected to this source trace\n const connectedPcbPorts = circuitJson.filter(\n (el) =>\n el.type === \"pcb_port\" &&\n sourceTrace.connected_source_port_ids.includes(el.source_port_id),\n ) as PcbPort[]\n\n // Find PCB components that these ports belong to\n const connectedPcbComponentIds = Array.from(\n new Set(\n connectedPcbPorts\n .map((port) => port.pcb_component_id)\n .filter((id): id is string => id !== undefined),\n ),\n )\n\n errors.push({\n type: \"pcb_trace_missing_error\",\n pcb_trace_missing_error_id: `pcb_trace_missing_${sourceTrace.source_trace_id}`,\n error_type: \"pcb_trace_missing_error\",\n message: `Trace [${sourceTrace.display_name && !containsCircuitJsonId(sourceTrace.display_name) ? sourceTrace.display_name : \"trace\"}] is not connected (it has no PCB trace)`,\n source_trace_id: sourceTrace.source_trace_id,\n pcb_component_ids: connectedPcbComponentIds,\n pcb_port_ids: connectedPcbPorts.map((port) => port.pcb_port_id),\n })\n }\n }\n\n return errors\n}\n\nexport { checkSourceTracesHavePcbTraces }\n","import type {\n AnyCircuitElement,\n PcbBoard,\n PcbTrace,\n PcbTraceError,\n} from \"circuit-json\"\nimport { cju } from \"@tscircuit/circuit-json-util\"\nimport type { Point, Polygon } from \"@tscircuit/math-utils\"\nimport { segmentToSegmentMinDistance } from \"@tscircuit/math-utils\"\n\n/**\n * Default margin for trace clearance from board edge (in mm)\n */\nconst DEFAULT_BOARD_MARGIN = 0.2\n\n/**\n * Configuration for trace board boundary checking\n */\nexport interface TraceBoardCheckConfig {\n /** Minimum distance from trace center to board edge (in mm) */\n margin?: number\n}\n\n/**\n * Create a board polygon representation using math-utils Polygon type\n */\nfunction getBoardPolygonPoints(board: PcbBoard): Polygon | null {\n if (board.outline && board.outline.length > 0) {\n // Use custom board outline\n return board.outline.map((p) => ({ x: p.x, y: p.y }))\n }\n\n if (\n board.center &&\n typeof board.width === \"number\" &&\n typeof board.height === \"number\"\n ) {\n // Create rectangular board outline\n const cx = board.center.x\n const cy = board.center.y\n const hw = board.width / 2\n const hh = board.height / 2\n\n return [\n { x: cx - hw, y: cy - hh }, // bottom-left\n { x: cx + hw, y: cy - hh }, // bottom-right\n { x: cx + hw, y: cy + hh }, // top-right\n { x: cx - hw, y: cy + hh }, // top-left\n ]\n }\n\n return null\n}\n\n/**\n * Check if any trace segment is too close to or outside the board outline\n * Uses segment-to-polygon distance with configurable margin\n */\nexport function checkPcbTracesOutOfBoard(\n circuitJson: AnyCircuitElement[],\n config: TraceBoardCheckConfig = {},\n): PcbTraceError[] {\n const errors: PcbTraceError[] = []\n const margin = config.margin ?? DEFAULT_BOARD_MARGIN\n\n // Find the board\n const board = circuitJson.find(\n (el): el is PcbBoard => el.type === \"pcb_board\",\n )\n if (!board) return errors\n\n // Create board polygon using math-utils Point type\n const boardPoints = getBoardPolygonPoints(board)\n if (!boardPoints) return errors\n\n // Get all PCB traces\n const pcbTraces = cju(circuitJson).pcb_trace.list()\n\n for (const trace of pcbTraces) {\n if (trace.route.length < 2) continue\n\n // Check each segment of the trace\n for (let i = 0; i < trace.route.length - 1; i++) {\n const p1 = trace.route[i]\n const p2 = trace.route[i + 1]\n\n // Only check wire segments\n if (p1.route_type !== \"wire\" || p2.route_type !== \"wire\") continue\n\n const traceWidth =\n \"width\" in p1 ? p1.width : \"width\" in p2 ? p2.width : 0.1\n const segmentStart: Point = { x: p1.x, y: p1.y }\n const segmentEnd: Point = { x: p2.x, y: p2.y }\n\n // Calculate minimum distance from trace segment to board polygon\n let minDistance = Infinity\n for (let j = 0; j < boardPoints.length; j++) {\n const edgeStart = boardPoints[j]\n const edgeEnd = boardPoints[(j + 1) % boardPoints.length]\n const distance = segmentToSegmentMinDistance(\n segmentStart,\n segmentEnd,\n edgeStart,\n edgeEnd,\n )\n if (distance < minDistance) {\n minDistance = distance\n }\n }\n\n const minimumDistance = traceWidth / 2 + margin\n\n if (minDistance < minimumDistance) {\n const error: PcbTraceError = {\n type: \"pcb_trace_error\",\n error_type: \"pcb_trace_error\",\n pcb_trace_error_id: `trace_too_close_to_board_${trace.pcb_trace_id}_segment_${i}`,\n message: `Trace too close to board edge (${minDistance.toFixed(3)}mm < ${minimumDistance.toFixed(3)}mm required, margin: ${margin}mm)`,\n pcb_trace_id: trace.pcb_trace_id,\n source_trace_id: trace.source_trace_id || \"\",\n center: {\n x: (segmentStart.x + segmentEnd.x) / 2,\n y: (segmentStart.y + segmentEnd.y) / 2,\n },\n pcb_component_ids: [],\n pcb_port_ids: [],\n }\n errors.push(error)\n }\n }\n }\n\n return errors\n}\n","import {\n cju,\n getBoundsOfPcbElements,\n getPrimaryId,\n} from \"@tscircuit/circuit-json-util\"\nimport { doBoundsOverlap } from \"@tscircuit/math-utils\"\nimport type { AnyCircuitElement, PcbFootprintOverlapError } from \"circuit-json\"\nimport { getFullConnectivityMapFromCircuitJson } from \"circuit-json-to-connectivity-map\"\nimport {\n getReadableNameForElementId,\n getReadableNameForPort,\n} from \"lib/util/get-readable-names\"\nimport {\n type OverlappableElement,\n doPcbElementsOverlap,\n} from \"./doPcbElementsOverlap\"\n\ninterface ComponentWithElements {\n component_id: string\n elements: OverlappableElement[]\n bounds: {\n minX: number\n minY: number\n maxX: number\n maxY: number\n }\n}\n\nconst formatOverlapElementDescription = (\n circuitJson: AnyCircuitElement[],\n element: OverlappableElement,\n): string => {\n if (\"pcb_port_id\" in element && element.pcb_port_id) {\n return getReadableNameForPort(circuitJson, element.pcb_port_id)\n }\n\n const id = getPrimaryId(element)\n const readableName = getReadableNameForElementId(circuitJson, id)\n return readableName === \"element\" ? `[${id}]` : readableName\n}\n\n/**\n * Check for overlapping PCB components\n * Returns errors for components that overlap inappropriately\n */\nexport function checkPcbComponentOverlap(\n circuitJson: AnyCircuitElement[],\n): PcbFootprintOverlapError[] {\n const errors: PcbFootprintOverlapError[] = []\n\n // Build connectivity map to check if components are electrically connected\n const connMap = getFullConnectivityMapFromCircuitJson(circuitJson)\n\n // Get all overlappable elements\n const smtPads = cju(circuitJson).pcb_smtpad.list()\n const platedHoles = cju(circuitJson).pcb_plated_hole.list()\n const holes = cju(circuitJson).pcb_hole.list()\n\n // Group elements by component (or treat standalone elements as their own \"component\")\n const componentMap = new Map<string, ComponentWithElements>()\n\n // Group SMT pads by component (or treat as standalone)\n for (const pad of smtPads) {\n const componentId =\n pad.pcb_component_id || `standalone_pad_${getPrimaryId(pad)}`\n if (!componentMap.has(componentId)) {\n componentMap.set(componentId, {\n component_id: componentId,\n elements: [],\n bounds: { minX: 0, minY: 0, maxX: 0, maxY: 0 },\n })\n }\n componentMap.get(componentId)!.elements.push(pad)\n }\n\n // Group plated holes by component (or treat as standalone)\n for (const hole of platedHoles) {\n const componentId =\n hole.pcb_component_id || `standalone_plated_hole_${getPrimaryId(hole)}`\n if (!componentMap.has(componentId)) {\n componentMap.set(componentId, {\n component_id: componentId,\n elements: [],\n bounds: { minX: 0, minY: 0, maxX: 0, maxY: 0 },\n })\n }\n componentMap.get(componentId)!.elements.push(hole)\n }\n\n // Holes typically don't have pcb_component_id, treat each as standalone\n for (const hole of holes) {\n const componentId = `standalone_hole_${getPrimaryId(hole)}`\n componentMap.set(componentId, {\n component_id: componentId,\n elements: [hole],\n bounds: { minX: 0, minY: 0, maxX: 0, maxY: 0 },\n })\n }\n\n // Compute bounds for each component\n for (const [componentId, componentData] of componentMap) {\n if (componentData.elements.length > 0) {\n componentData.bounds = getBoundsOfPcbElements(componentData.elements)\n }\n }\n\n // Convert map to array for pairwise iteration\n const componentsWithElements = Array.from(componentMap.values())\n\n // Pairwise check: only check elements if component bounds overlap\n for (let i = 0; i < componentsWithElements.length; i++) {\n for (let j = i + 1; j < componentsWithElements.length; j++) {\n const comp1 = componentsWithElements[i]\n const comp2 = componentsWithElements[j]\n\n // First check if component bounds overlap\n if (!doBoundsOverlap(comp1.bounds, comp2.bounds)) {\n continue\n }\n\n // Component bounds overlap, now check individual elements\n for (const elem1 of comp1.elements) {\n for (const elem2 of comp2.elements) {\n const id1 = getPrimaryId(elem1)\n const id2 = getPrimaryId(elem2)\n\n // Check if both are SMT pads and are electrically connected (same net) - if so, skip\n // This allows pads with the same subcircuit connectivity to be in contact\n if (\n elem1.type === \"pcb_smtpad\" &&\n elem2.type === \"pcb_smtpad\" &&\n connMap.areIdsConnected(id1, id2)\n ) {\n continue\n }\n\n // Check if element bounds overlap\n if (doPcbElementsOverlap(elem1, elem2)) {\n const elem1Description = formatOverlapElementDescription(\n circuitJson,\n elem1,\n )\n const elem2Description = formatOverlapElementDescription(\n circuitJson,\n elem2,\n )\n\n // Create error object\n const error: PcbFootprintOverlapError = {\n type: \"pcb_footprint_overlap_error\",\n pcb_error_id: `pcb_footprint_overlap_${id1}_${id2}`,\n error_type: \"pcb_footprint_overlap_error\",\n message: `${elem1.type} ${elem1Description} overlaps with ${elem2.type} ${elem2Description}`,\n }\n\n // Add relevant IDs based on element types\n if (elem1.type === \"pcb_smtpad\" || elem2.type === \"pcb_smtpad\") {\n error.pcb_smtpad_ids = []\n if (elem1.type === \"pcb_smtpad\") error.pcb_smtpad_ids.push(id1)\n if (elem2.type === \"pcb_smtpad\") error.pcb_smtpad_ids.push(id2)\n }\n\n if (\n elem1.type === \"pcb_plated_hole\" ||\n elem2.type === \"pcb_plated_hole\"\n ) {\n error.pcb_plated_hole_ids = []\n if (elem1.type === \"pcb_plated_hole\")\n error.pcb_plated_hole_ids.push(id1)\n if (elem2.type === \"pcb_plated_hole\")\n error.pcb_plated_hole_ids.push(id2)\n }\n\n if (elem1.type === \"pcb_hole\" || elem2.type === \"pcb_hole\") {\n error.pcb_hole_ids = []\n if (elem1.type === \"pcb_hole\") error.pcb_hole_ids.push(id1)\n if (elem2.type === \"pcb_hole\") error.pcb_hole_ids.push(id2)\n }\n\n errors.push(error)\n }\n }\n }\n }\n }\n\n return errors\n}\n","import { getBoundsOfPcbElements } from \"@tscircuit/circuit-json-util\"\nimport { doBoundsOverlap } from \"@tscircuit/math-utils\"\nimport type { PcbHole, PcbPlatedHole, PcbSmtPad } from \"circuit-json\"\nimport type { Collidable } from \"lib/check-each-pcb-trace-non-overlapping/getCollidableBounds\"\nimport { getLayersOfPcbElement } from \"lib/util/getLayersOfPcbElement\"\n\nexport type OverlappableElement = PcbSmtPad | PcbPlatedHole | PcbHole\n\nfunction getElementLayers(elem: OverlappableElement): string[] {\n return getLayersOfPcbElement(elem as Collidable)\n}\n\nfunction doLayersOverlap(layers1: string[], layers2: string[]): boolean {\n if (layers1.length === 0 || layers2.length === 0) return true\n return layers1.some((l) => layers2.includes(l))\n}\n\nexport function doPcbElementsOverlap(\n elem1: OverlappableElement,\n elem2: OverlappableElement,\n): boolean {\n // If the elements are on completely different layers they cannot overlap\n const layers1 = getElementLayers(elem1)\n const layers2 = getElementLayers(elem2)\n if (!doLayersOverlap(layers1, layers2)) return false\n\n const bounds1 = getBoundsOfPcbElements([elem1])\n const bounds2 = getBoundsOfPcbElements([elem2])\n return doBoundsOverlap(bounds1, bounds2)\n}\n","import {\n getPrimaryId,\n getReadableNameForElement,\n} from \"@tscircuit/circuit-json-util\"\nimport type { AnyCircuitElement, PcbPadPadClearanceError } from \"circuit-json\"\nimport {\n type ConnectivityMap,\n getFullConnectivityMapFromCircuitJson,\n} from \"circuit-json-to-connectivity-map\"\nimport { SpatialObjectIndex } from \"lib/data-structures/SpatialIndex\"\nimport { DEFAULT_PAD_PAD_CLEARANCE, EPSILON } from \"lib/drc-defaults\"\nimport { getLayersOfPcbElement } from \"lib/util/getLayersOfPcbElement\"\nimport {\n type PadElement,\n formatMm,\n getPadBounds,\n getPadCenter,\n getPadToPadGap,\n getPads,\n} from \"./check-pad-clearance/common\"\n\nexport function checkPadPadClearance(\n circuitJson: AnyCircuitElement[],\n {\n connMap,\n minSpacing = DEFAULT_PAD_PAD_CLEARANCE,\n }: { connMap?: ConnectivityMap; minSpacing?: number } = {},\n): PcbPadPadClearanceError[] {\n const pads = getPads(circuitJson)\n if (pads.length < 2) return []\n\n connMap ??= getFullConnectivityMapFromCircuitJson(circuitJson)\n const spatialIndex = new SpatialObjectIndex<PadElement>({\n objects: pads,\n getBounds: getPadBounds,\n getId: (pad) => getPrimaryId(pad),\n })\n\n const errors = new Map<string, PcbPadPadClearanceError>()\n\n for (const padA of pads) {\n const padAId = getPrimaryId(padA)\n const nearbyPads = spatialIndex.getObjectsInBounds(\n getPadBounds(padA),\n minSpacing,\n )\n\n for (const padB of nearbyPads) {\n const padBId = getPrimaryId(padB)\n if (padAId === padBId) continue\n if (\n !getLayersOfPcbElement(padA as any).some((layer) =>\n getLayersOfPcbElement(padB as any).includes(layer),\n )\n ) {\n continue\n }\n if (connMap.areIdsConnected(padAId, padBId)) continue\n\n const pairId = [padAId, padBId].sort().join(\"_\")\n const gap = getPadToPadGap(padA, padB)\n if (gap + EPSILON >= minSpacing) continue\n const centerA = getPadCenter(padA)\n const centerB = getPadCenter(padB)\n\n const nextError: PcbPadPadClearanceError = {\n type: \"pcb_pad_pad_clearance_error\",\n pcb_pad_pad_clearance_error_id: `pad_pad_clearance_${pairId}`,\n error_type: \"pcb_pad_pad_clearance_error\",\n message: `Pads ${getReadableNameForElement(circuitJson, padAId)} and ${getReadableNameForElement(circuitJson, padBId)} are too close (clearance: ${formatMm(gap)}, minimum: ${formatMm(minSpacing)})`,\n pcb_pad_ids: [padAId, padBId] as [string, string],\n minimum_clearance: minSpacing,\n actual_clearance: gap,\n center: {\n x: (centerA.x + centerB.x) / 2,\n y: (centerA.y + centerB.y) / 2,\n },\n }\n\n if (!errors.has(pairId)) {\n errors.set(pairId, nextError)\n }\n }\n }\n\n return Array.from(errors.values())\n}\n","import {\n cju,\n distanceBetweenCircleAndCircle,\n distanceBetweenCircleAndPolygon,\n distanceBetweenPolygonAndPolygon,\n getBoundsOfPcbElements,\n} from \"@tscircuit/circuit-json-util\"\nimport { midpoint } from \"@tscircuit/math-utils\"\nimport type {\n AnyCircuitElement,\n PcbPlatedHole,\n PcbSmtPad,\n PcbTrace,\n} from \"circuit-json\"\nimport type { PcbTraceSegment } from \"lib/check-each-pcb-trace-non-overlapping/getCollidableBounds\"\nimport type { Bounds } from \"lib/data-structures/SpatialIndex\"\nimport { DEFAULT_TRACE_THICKNESS } from \"lib/drc-defaults\"\n\nexport type PadElement = PcbSmtPad | PcbPlatedHole\n\nexport const formatMm = (value: number) => {\n const rounded = Math.round(value * 1000) / 1000\n return `${Number(rounded.toFixed(3))}mm`\n}\n\nexport const getPadBounds = (pad: PadElement): Bounds =>\n getBoundsOfPcbElements([pad])\n\nexport const getPadCenter = (pad: PadElement) => {\n const bounds = getPadBounds(pad)\n return midpoint(\n { x: bounds.minX, y: bounds.minY },\n { x: bounds.maxX, y: bounds.maxY },\n )\n}\n\nexport const getPadRadius = (pad: PadElement) => {\n const bounds = getPadBounds(pad)\n return Math.min(bounds.maxX - bounds.minX, bounds.maxY - bounds.minY) / 2\n}\n\nexport const isCircularPad = (pad: PadElement) => pad.shape === \"circle\"\n\nconst getCircleShape = (pad: PadElement) => {\n const center = getPadCenter(pad)\n return {\n kind: \"circle\" as const,\n x: center.x,\n y: center.y,\n radius: getPadRadius(pad),\n }\n}\n\nconst getPolygonShape = (pad: PadElement) => {\n const bounds = getPadBounds(pad)\n return {\n kind: \"polygon\" as const,\n points: [\n { x: bounds.minX, y: bounds.minY },\n { x: bounds.maxX, y: bounds.minY },\n { x: bounds.maxX, y: bounds.maxY },\n { x: bounds.minX, y: bounds.maxY },\n ],\n }\n}\n\nexport const getPadToPadGap = (padA: PadElement, padB: PadElement) => {\n if (isCircularPad(padA) && isCircularPad(padB)) {\n return distanceBetweenCircleAndCircle(\n getCircleShape(padA),\n getCircleShape(padB),\n )\n }\n\n if (isCircularPad(padA)) {\n return distanceBetweenCircleAndPolygon(\n getCircleShape(padA),\n getPolygonShape(padB),\n )\n }\n\n if (isCircularPad(padB)) {\n return distanceBetweenCircleAndPolygon(\n getCircleShape(padB),\n getPolygonShape(padA),\n )\n }\n\n return distanceBetweenPolygonAndPolygon(\n getPolygonShape(padA),\n getPolygonShape(padB),\n )\n}\n\nexport const getPads = (circuitJson: AnyCircuitElement[]) =>\n [\n ...cju(circuitJson).pcb_smtpad.list(),\n ...cju(circuitJson).pcb_plated_hole.list(),\n ] as PadElement[]\n\nexport const getTraceSegments = (\n circuitJson: AnyCircuitElement[],\n): PcbTraceSegment[] => {\n const pcbTraces = cju(circuitJson).pcb_trace.list()\n\n return pcbTraces.flatMap((pcbTrace) => {\n const segments: PcbTraceSegment[] = []\n\n for (let i = 0; i < pcbTrace.route.length - 1; i++) {\n const p1 = pcbTrace.route[i]\n const p2 = pcbTrace.route[i + 1]\n if (p1.route_type !== \"wire\") continue\n if (p2.route_type !== \"wire\") continue\n if (p1.layer !== p2.layer) continue\n\n segments.push({\n type: \"pcb_trace_segment\",\n pcb_trace_id: pcbTrace.pcb_trace_id,\n _pcbTrace: pcbTrace as PcbTrace,\n thickness:\n \"width\" in p1\n ? p1.width\n : \"width\" in p2\n ? p2.width\n : DEFAULT_TRACE_THICKNESS,\n layer: p1.layer,\n x1: p1.x,\n y1: p1.y,\n x2: p2.x,\n y2: p2.y,\n })\n }\n\n return segments\n })\n}\n","import {\n getPrimaryId,\n getReadableNameForElement,\n} from \"@tscircuit/circuit-json-util\"\nimport {\n segmentToBoundsMinDistance,\n segmentToCircleMinDistance,\n} from \"@tscircuit/math-utils\"\nimport type { AnyCircuitElement, PcbPadTraceClearanceError } from \"circuit-json\"\nimport {\n type ConnectivityMap,\n getFullConnectivityMapFromCircuitJson,\n} from \"circuit-json-to-connectivity-map\"\nimport { getCollidableBounds } from \"lib/check-each-pcb-trace-non-overlapping/getCollidableBounds\"\nimport { SpatialObjectIndex } from \"lib/data-structures/SpatialIndex\"\nimport { DEFAULT_PAD_TRACE_CLEARANCE, EPSILON } from \"lib/drc-defaults\"\nimport { getLayersOfPcbElement } from \"lib/util/getLayersOfPcbElement\"\nimport {\n type PadElement,\n formatMm,\n getPadBounds,\n getPadCenter,\n getPadRadius,\n getPads,\n getTraceSegments,\n isCircularPad,\n} from \"./check-pad-clearance/common\"\n\nexport function checkPadTraceClearance(\n circuitJson: AnyCircuitElement[],\n {\n connMap,\n minSpacing = DEFAULT_PAD_TRACE_CLEARANCE,\n }: { connMap?: ConnectivityMap; minSpacing?: number } = {},\n): PcbPadTraceClearanceError[] {\n const pads = getPads(circuitJson)\n const segments = getTraceSegments(circuitJson)\n if (pads.length === 0 || segments.length === 0) return []\n\n connMap ??= getFullConnectivityMapFromCircuitJson(circuitJson)\n const spatialIndex = new SpatialObjectIndex<PadElement>({\n objects: pads,\n getBounds: getPadBounds,\n getId: (pad) => getPrimaryId(pad),\n })\n\n const errors = new Map<\n string,\n { error: PcbPadTraceClearanceError; gap: number }\n >()\n\n for (const segment of segments) {\n const nearbyPads = spatialIndex.getObjectsInBounds(\n getCollidableBounds(segment),\n minSpacing + segment.thickness / 2,\n )\n\n for (const pad of nearbyPads) {\n const padId = getPrimaryId(pad)\n if (!getLayersOfPcbElement(pad as any).includes(segment.layer)) continue\n if (connMap.areIdsConnected(segment.pcb_trace_id, padId)) continue\n const center = getPadCenter(pad)\n\n const gap = isCircularPad(pad)\n ? segmentToCircleMinDistance(\n { x: segment.x1, y: segment.y1 },\n { x: segment.x2, y: segment.y2 },\n { x: center.x, y: center.y, radius: getPadRadius(pad) },\n ) -\n segment.thickness / 2\n : segmentToBoundsMinDistance(\n { x: segment.x1, y: segment.y1 },\n { x: segment.x2, y: segment.y2 },\n getPadBounds(pad),\n ) -\n segment.thickness / 2\n if (gap + EPSILON >= minSpacing) continue\n\n const pairId = `${padId}_${segment.pcb_trace_id}`\n const nextError: PcbPadTraceClearanceError = {\n type: \"pcb_pad_trace_clearance_error\" as const,\n pcb_pad_trace_clearance_error_id: `pad_trace_clearance_${pairId}`,\n error_type: \"pcb_pad_trace_clearance_error\",\n message: `Pad ${getReadableNameForElement(circuitJson, padId)} and trace ${getReadableNameForElement(circuitJson, segment.pcb_trace_id)} are too close (clearance: ${formatMm(gap)}, minimum: ${formatMm(minSpacing)})`,\n pcb_pad_id: padId,\n pcb_trace_id: segment.pcb_trace_id,\n minimum_clearance: minSpacing,\n actual_clearance: gap,\n center: {\n x: (center.x + (segment.x1 + segment.x2) / 2) / 2,\n y: (center.y + (segment.y1 + segment.y2) / 2) / 2,\n },\n }\n\n const current = errors.get(pairId)\n if (!current || gap < current.gap) {\n errors.set(pairId, { error: nextError, gap })\n }\n }\n }\n\n return Array.from(errors.values()).map(({ error }) => error)\n}\n","import { getReadableNameForElement } from \"@tscircuit/circuit-json-util\"\nimport { segmentToCircleMinDistance } from \"@tscircuit/math-utils\"\nimport type {\n AnyCircuitElement,\n PcbVia,\n PcbViaTraceClearanceError,\n} from \"circuit-json\"\nimport {\n type ConnectivityMap,\n getFullConnectivityMapFromCircuitJson,\n} from \"circuit-json-to-connectivity-map\"\nimport { DEFAULT_PAD_TRACE_CLEARANCE, EPSILON } from \"lib/drc-defaults\"\nimport { getLayersOfPcbElement } from \"lib/util/getLayersOfPcbElement\"\nimport { formatMm, getTraceSegments } from \"./check-pad-clearance/common\"\n\nconst getClosestPointOnSegment = (\n point: { x: number; y: number },\n segmentStart: { x: number; y: number },\n segmentEnd: { x: number; y: number },\n) => {\n const dx = segmentEnd.x - segmentStart.x\n const dy = segmentEnd.y - segmentStart.y\n const lengthSquared = dx * dx + dy * dy\n\n if (lengthSquared === 0) return segmentStart\n\n const t = Math.max(\n 0,\n Math.min(\n 1,\n ((point.x - segmentStart.x) * dx + (point.y - segmentStart.y) * dy) /\n lengthSquared,\n ),\n )\n\n return {\n x: segmentStart.x + t * dx,\n y: segmentStart.y + t * dy,\n }\n}\n\nexport function checkViaTraceClearance(\n circuitJson: AnyCircuitElement[],\n {\n connMap,\n minSpacing = DEFAULT_PAD_TRACE_CLEARANCE,\n }: { connMap?: ConnectivityMap; minSpacing?: number } = {},\n): PcbViaTraceClearanceError[] {\n const vias = circuitJson.filter((el) => el.type === \"pcb_via\") as PcbVia[]\n const segments = getTraceSegments(circuitJson)\n if (vias.length === 0 || segments.length === 0) return []\n\n connMap ??= getFullConnectivityMapFromCircuitJson(circuitJson)\n const errors = new Map<\n string,\n { error: PcbViaTraceClearanceError; gap: number }\n >()\n\n for (const via of vias) {\n const viaRadius = via.outer_diameter / 2\n for (const segment of segments) {\n if (!getLayersOfPcbElement(via).includes(segment.layer)) continue\n if (connMap.areIdsConnected(segment.pcb_trace_id, via.pcb_via_id))\n continue\n\n const gap =\n segmentToCircleMinDistance(\n { x: segment.x1, y: segment.y1 },\n { x: segment.x2, y: segment.y2 },\n {\n x: via.x,\n y: via.y,\n radius: viaRadius,\n },\n ) -\n segment.thickness / 2\n if (gap + EPSILON >= minSpacing) continue\n\n const closestPoint = getClosestPointOnSegment(\n { x: via.x, y: via.y },\n { x: segment.x1, y: segment.y1 },\n { x: segment.x2, y: segment.y2 },\n )\n\n const pairId = `${via.pcb_via_id}_${segment.pcb_trace_id}`\n const nextError: PcbViaTraceClearanceError = {\n type: \"pcb_via_trace_clearance_error\",\n pcb_via_trace_clearance_error_id: `via_trace_clearance_${pairId}`,\n error_type: \"pcb_via_trace_clearance_error\",\n message: `Via ${getReadableNameForElement(circuitJson, via.pcb_via_id)} and trace ${getReadableNameForElement(circuitJson, segment.pcb_trace_id)} are too close (clearance: ${formatMm(gap)}, minimum: ${formatMm(minSpacing)})`,\n pcb_via_id: via.pcb_via_id,\n pcb_trace_id: segment.pcb_trace_id,\n minimum_clearance: minSpacing,\n actual_clearance: gap,\n center: {\n x: (via.x + closestPoint.x) / 2,\n y: (via.y + closestPoint.y) / 2,\n },\n }\n\n const current = errors.get(pairId)\n if (!current || gap < current.gap) {\n errors.set(pairId, { error: nextError, gap })\n }\n }\n }\n\n return Array.from(errors.values()).map(({ error }) => error)\n}\n","import type { AnyCircuitElement } from \"circuit-json\"\n\ntype SourceComponent = Extract<\n AnyCircuitElement,\n { source_component_id: string; name: string }\n>\n\ntype SourcePort = Extract<AnyCircuitElement, { type: \"source_port\" }>\ntype SourceTrace = Extract<AnyCircuitElement, { type: \"source_trace\" }>\n\ntype SourcePinMustBeConnectedError = {\n type: \"source_pin_must_be_connected_error\"\n source_pin_must_be_connected_error_id: string\n error_type: \"source_pin_must_be_connected_error\"\n message: string\n source_component_id: string\n source_port_id: string\n subcircuit_id?: string\n}\n\n/**\n * Check that each source port with must_be_connected attribute is actually\n * connected to a trace. Returns errors for any pins that are marked as\n * must_be_connected but are floating (not connected to any trace).\n */\nexport function checkPinMustBeConnected(\n circuitJson: AnyCircuitElement[],\n): SourcePinMustBeConnectedError[] {\n const errors: SourcePinMustBeConnectedError[] = []\n\n // Get all source components, ports, and traces\n const sourceComponents = circuitJson.filter(\n (el): el is SourceComponent =>\n \"source_component_id\" in el &&\n (el.type === \"source_component\" || el.type.startsWith(\"source_simple_\")),\n )\n const sourcePorts = circuitJson.filter(\n (el): el is SourcePort => el.type === \"source_port\",\n )\n const sourceTraces = circuitJson.filter(\n (el): el is SourceTrace => el.type === \"source_trace\",\n )\n\n // Build a set of connected source port IDs\n const connectedPortIds = new Set<string>()\n for (const trace of sourceTraces) {\n for (const portId of trace.connected_source_port_ids ?? []) {\n connectedPortIds.add(portId)\n }\n }\n\n // Build a map of internally connected port groups for each component\n const componentInternalConnections = new Map<string, string[][]>()\n for (const component of sourceComponents) {\n if (\n \"internally_connected_source_port_ids\" in component &&\n component.internally_connected_source_port_ids\n ) {\n componentInternalConnections.set(\n component.source_component_id,\n component.internally_connected_source_port_ids,\n )\n }\n }\n\n // For each internal group, if any port is connected, mark all ports in the group as connected\n for (const internalGroups of componentInternalConnections.values()) {\n for (const group of internalGroups) {\n if (group.some((portId) => connectedPortIds.has(portId))) {\n for (const portId of group) {\n connectedPortIds.add(portId)\n }\n }\n }\n }\n\n // Check each port for must_be_connected attribute\n for (const port of sourcePorts) {\n if (port.must_be_connected === true) {\n if (!connectedPortIds.has(port.source_port_id)) {\n const component = sourceComponents.find(\n (c) => c.source_component_id === port.source_component_id,\n )\n const componentName = component?.name ?? \"Unknown\"\n\n errors.push({\n type: \"source_pin_must_be_connected_error\",\n source_pin_must_be_connected_error_id: `source_pin_must_be_connected_error_${port.source_port_id}`,\n error_type: \"source_pin_must_be_connected_error\",\n message: `Port ${port.name} on ${componentName} must be connected but is floating`,\n source_component_id: port.source_component_id ?? \"\",\n source_port_id: port.source_port_id,\n subcircuit_id: port.subcircuit_id,\n })\n }\n }\n }\n\n return errors\n}\n","import { cju } from \"@tscircuit/circuit-json-util\"\nimport type {\n AnyCircuitElement,\n SourceComponentPinsUnderspecifiedWarning,\n SourcePinAttributes,\n SourcePort,\n SourceComponentBase,\n} from \"circuit-json\"\n\nconst PIN_ATTRIBUTE_KEYS = [\n \"must_be_connected\",\n \"provides_power\",\n \"requires_power\",\n \"provides_ground\",\n \"requires_ground\",\n \"provides_voltage\",\n \"requires_voltage\",\n \"do_not_connect\",\n \"include_in_board_pinout\",\n \"can_use_internal_pullup\",\n \"is_using_internal_pullup\",\n \"needs_external_pullup\",\n \"can_use_internal_pulldown\",\n \"is_using_internal_pulldown\",\n \"needs_external_pulldown\",\n \"can_use_open_drain\",\n \"is_using_open_drain\",\n \"can_use_push_pull\",\n \"is_using_push_pull\",\n \"should_have_decoupling_capacitor\",\n \"recommended_decoupling_capacitor_capacitance\",\n \"is_configured_for_i2c_sda\",\n \"is_configured_for_i2c_scl\",\n \"is_configured_for_spi_mosi\",\n \"is_configured_for_spi_miso\",\n \"is_configured_for_spi_sck\",\n \"is_configured_for_spi_cs\",\n \"is_configured_for_uart_tx\",\n \"is_configured_for_uart_rx\",\n \"supports_i2c_sda\",\n \"supports_i2c_scl\",\n \"supports_spi_mosi\",\n \"supports_spi_miso\",\n \"supports_spi_sck\",\n \"supports_spi_cs\",\n \"supports_uart_tx\",\n \"supports_uart_rx\",\n] as const satisfies readonly (keyof SourcePinAttributes)[]\n\nfunction hasAnyPinAttribute(port: SourcePort): boolean {\n return PIN_ATTRIBUTE_KEYS.some((key) => port[key] !== undefined)\n}\n\n/**\n * Check that each component with ports has at least one pin attribute\n * specified across its ports. Returns a warning when all pins are\n * underspecified (no SourcePinAttributes fields set on any port).\n */\nexport function checkAllPinsInComponentAreUnderspecified(\n circuitJson: AnyCircuitElement[],\n): SourceComponentPinsUnderspecifiedWarning[] {\n const warnings: SourceComponentPinsUnderspecifiedWarning[] = []\n const db = cju(circuitJson)\n\n const sourceComponents = db.source_component.list() as SourceComponentBase[]\n const sourcePorts = db.source_port.list() as SourcePort[]\n\n const portsByComponent = new Map<string, SourcePort[]>()\n for (const port of sourcePorts) {\n if (!port.source_component_id) continue\n const existing = portsByComponent.get(port.source_component_id) ?? []\n existing.push(port)\n portsByComponent.set(port.source_component_id, existing)\n }\n\n for (const component of sourceComponents) {\n if (component.ftype !== \"simple_chip\") continue\n\n const componentPorts =\n portsByComponent.get(component.source_component_id) ?? []\n if (componentPorts.length === 0) continue\n\n const hasAnySpecifiedAttributes = componentPorts.some((port) =>\n hasAnyPinAttribute(port),\n )\n\n if (hasAnySpecifiedAttributes) continue\n\n warnings.push({\n type: \"source_component_pins_underspecified_warning\",\n source_component_pins_underspecified_warning_id: `source_component_pins_underspecified_warning_${component.source_component_id}`,\n warning_type: \"source_component_pins_underspecified_warning\",\n message: `All pins on ${component.name} are underspecified (no pinAttributes set)`,\n source_component_id: component.source_component_id,\n source_port_ids: componentPorts.map((port) => port.source_port_id),\n subcircuit_id: componentPorts[0]?.subcircuit_id,\n })\n }\n\n return warnings\n}\n","import { cju } from \"@tscircuit/circuit-json-util\"\nimport type {\n AnyCircuitElement,\n SourceComponentBase,\n SourceNoPowerPinDefinedWarning,\n SourcePort,\n} from \"circuit-json\"\n\n/**\n * Check that each chip has at least one pin marked as requires_power=true.\n * Returns warnings for chips where no pin declares requires_power.\n */\nexport function checkNoPowerPinDefined(\n circuitJson: AnyCircuitElement[],\n): SourceNoPowerPinDefinedWarning[] {\n const warnings: SourceNoPowerPinDefinedWarning[] = []\n const db = cju(circuitJson)\n\n const sourceComponents = db.source_component.list() as SourceComponentBase[]\n const sourcePorts = db.source_port.list() as SourcePort[]\n\n const portsByComponent = new Map<string, SourcePort[]>()\n for (const port of sourcePorts) {\n if (!port.source_component_id) continue\n const existing = portsByComponent.get(port.source_component_id) ?? []\n existing.push(port)\n portsByComponent.set(port.source_component_id, existing)\n }\n\n for (const component of sourceComponents) {\n if (component.ftype !== \"simple_chip\") continue\n\n const componentPorts =\n portsByComponent.get(component.source_component_id) ?? []\n if (componentPorts.length === 0) continue\n\n const hasRequiredPowerPin = componentPorts.some(\n (port) => port.requires_power === true,\n )\n if (hasRequiredPowerPin) continue\n\n warnings.push({\n type: \"source_no_power_pin_defined_warning\",\n source_no_power_pin_defined_warning_id: `source_no_power_pin_defined_warning_${component.source_component_id}`,\n warning_type: \"source_no_power_pin_defined_warning\",\n message: `${component.name} has no pin with requires_power=true`,\n source_component_id: component.source_component_id,\n source_port_ids: componentPorts.map((port) => port.source_port_id),\n subcircuit_id: componentPorts[0]?.subcircuit_id,\n })\n }\n\n return warnings\n}\n","import { cju } from \"@tscircuit/circuit-json-util\"\nimport type {\n AnyCircuitElement,\n SourceComponentBase,\n SourceNoGroundPinDefinedWarning,\n SourcePort,\n} from \"circuit-json\"\n\n/**\n * Check that each chip has at least one pin marked as requires_ground=true.\n * Returns warnings for chips where no pin declares requires_ground.\n */\nexport function checkNoGroundPinDefined(\n circuitJson: AnyCircuitElement[],\n): SourceNoGroundPinDefinedWarning[] {\n const warnings: SourceNoGroundPinDefinedWarning[] = []\n const db = cju(circuitJson)\n\n const sourceComponents = db.source_component.list() as SourceComponentBase[]\n const sourcePorts = db.source_port.list() as SourcePort[]\n\n const portsByComponent = new Map<string, SourcePort[]>()\n for (const port of sourcePorts) {\n if (!port.source_component_id) continue\n const existing = portsByComponent.get(port.source_component_id) ?? []\n existing.push(port)\n portsByComponent.set(port.source_component_id, existing)\n }\n\n for (const component of sourceComponents) {\n if (component.ftype !== \"simple_chip\") continue\n\n const componentPorts =\n portsByComponent.get(component.source_component_id) ?? []\n if (componentPorts.length === 0) continue\n\n const hasRequiredGroundPin = componentPorts.some(\n (port) => port.requires_ground === true,\n )\n if (hasRequiredGroundPin) continue\n\n warnings.push({\n type: \"source_no_ground_pin_defined_warning\",\n source_no_ground_pin_defined_warning_id: `source_no_ground_pin_defined_warning_${component.source_component_id}`,\n warning_type: \"source_no_ground_pin_defined_warning\",\n message: `${component.name} has no pin with requires_ground=true`,\n source_component_id: component.source_component_id,\n source_port_ids: componentPorts.map((port) => port.source_port_id),\n subcircuit_id: componentPorts[0]?.subcircuit_id,\n })\n }\n\n return warnings\n}\n","import { getBoardBounds } from \"@tscircuit/circuit-json-util\"\nimport type {\n AnyCircuitElement,\n PcbBoard,\n PcbComponent,\n PcbConnectorNotInAccessibleOrientationWarning,\n} from \"circuit-json\"\nimport { getReadableNameForComponent } from \"./util/get-readable-names\"\n\ntype FacingDirection = \"x-\" | \"x+\" | \"y+\" | \"y-\"\n\nfunction getFacingDirection(component: PcbComponent): FacingDirection | null {\n if (!component.center || !component.cable_insertion_center) return null\n\n const dx = component.cable_insertion_center.x - component.center.x\n const dy = component.cable_insertion_center.y - component.center.y\n\n if (Math.abs(dx) < 1e-6 && Math.abs(dy) < 1e-6) return null\n\n if (Math.abs(dx) >= Math.abs(dy)) {\n return dx >= 0 ? \"x+\" : \"x-\"\n }\n\n return dy >= 0 ? \"y+\" : \"y-\"\n}\n\nfunction getRecommendedFacingDirection(\n component: PcbComponent,\n bounds: { minX: number; maxX: number; minY: number; maxY: number },\n): FacingDirection | null {\n if (!component.center) return null\n\n const distances = [\n { direction: \"x-\" as const, distance: component.center.x - bounds.minX },\n { direction: \"x+\" as const, distance: bounds.maxX - component.center.x },\n { direction: \"y-\" as const, distance: component.center.y - bounds.minY },\n { direction: \"y+\" as const, distance: bounds.maxY - component.center.y },\n ]\n\n distances.sort((a, b) => a.distance - b.distance)\n return distances[0]?.direction ?? null\n}\n\nexport function checkConnectorAccessibleOrientation(\n circuitJson: AnyCircuitElement[],\n): PcbConnectorNotInAccessibleOrientationWarning[] {\n const board = circuitJson.find(\n (el): el is PcbBoard => el.type === \"pcb_board\",\n )\n if (!board) return []\n\n const bounds = (() => {\n try {\n return getBoardBounds(board)\n } catch {\n return null\n }\n })()\n if (!bounds) return []\n\n const warnings: PcbConnectorNotInAccessibleOrientationWarning[] = []\n\n const components = circuitJson.filter(\n (el): el is PcbComponent => el.type === \"pcb_component\",\n )\n\n for (const component of components) {\n if (!component.cable_insertion_center) continue\n\n const facingDirection = getFacingDirection(component)\n const recommendedFacingDirection = getRecommendedFacingDirection(\n component,\n bounds,\n )\n\n if (!facingDirection || !recommendedFacingDirection) continue\n if (facingDirection === recommendedFacingDirection) continue\n\n const componentName = getReadableNameForComponent(\n circuitJson,\n component.pcb_component_id,\n )\n\n warnings.push({\n type: \"pcb_connector_not_in_accessible_orientation_warning\",\n warning_type: \"pcb_connector_not_in_accessible_orientation_warning\",\n pcb_connector_not_in_accessible_orientation_warning_id: `pcb_connector_not_in_accessible_orientation_warning_${component.pcb_component_id}`,\n message: `${componentName} is facing ${facingDirection} but should face ${recommendedFacingDirection} so the connector is accessible from the board edge`,\n pcb_component_id: component.pcb_component_id,\n source_component_id: component.source_component_id,\n pcb_board_id: board.pcb_board_id,\n facing_direction: facingDirection,\n recommended_facing_direction: recommendedFacingDirection,\n subcircuit_id: component.subcircuit_id,\n })\n }\n\n return warnings\n}\n","import {\n doSegmentsIntersect,\n isPointInsidePolygon,\n} from \"@tscircuit/math-utils\"\nimport type {\n AnyCircuitElement,\n PcbCourtyardOverlapError,\n PcbCourtyardRect,\n PcbCourtyardCircle,\n PcbCourtyardOutline,\n} from \"circuit-json\"\n\ntype CourtyardElement =\n | PcbCourtyardRect\n | PcbCourtyardCircle\n | PcbCourtyardOutline\n\nfunction getCourtyardPolygon(el: CourtyardElement): { x: number; y: number }[] {\n if (el.type === \"pcb_courtyard_rect\") {\n const hw = el.width / 2\n const hh = el.height / 2\n const corners = [\n { x: -hw, y: -hh },\n { x: +hw, y: -hh },\n { x: +hw, y: +hh },\n { x: -hw, y: +hh },\n ]\n const angle = ((el.ccw_rotation ?? 0) * Math.PI) / 180\n const cos = Math.cos(angle)\n const sin = Math.sin(angle)\n return corners.map(({ x, y }) => ({\n x: el.center.x + x * cos - y * sin,\n y: el.center.y + x * sin + y * cos,\n }))\n }\n if (el.type === \"pcb_courtyard_circle\") {\n const N = 32\n return Array.from({ length: N }, (_, i) => {\n const a = (2 * Math.PI * i) / N\n return {\n x: el.center.x + el.radius * Math.cos(a),\n y: el.center.y + el.radius * Math.sin(a),\n }\n })\n }\n return el.outline\n}\n\nfunction getComponentName(\n circuitJson: AnyCircuitElement[],\n pcbComponentId: string,\n): string {\n const pcbComponent = circuitJson.find(\n (el) =>\n el.type === \"pcb_component\" && el.pcb_component_id === pcbComponentId,\n )\n if (pcbComponent?.type !== \"pcb_component\") return pcbComponentId\n const sourceComponent = circuitJson.find(\n (el) =>\n el.type === \"source_component\" &&\n el.source_component_id === pcbComponent.source_component_id,\n )\n if (sourceComponent?.type === \"source_component\" && sourceComponent.name) {\n return sourceComponent.name\n }\n return pcbComponentId\n}\n\ntype Point = { x: number; y: number }\n\nfunction polygonsOverlap(polyA: Point[], polyB: Point[]): boolean {\n // Check if any vertex of A is inside B or vice versa\n if (polyA.some((p) => isPointInsidePolygon(p, polyB))) return true\n if (polyB.some((p) => isPointInsidePolygon(p, polyA))) return true\n // Check if any edge of A intersects any edge of B\n for (let i = 0; i < polyA.length; i++) {\n const a1 = polyA[i]\n const a2 = polyA[(i + 1) % polyA.length]\n for (let j = 0; j < polyB.length; j++) {\n const b1 = polyB[j]\n const b2 = polyB[(j + 1) % polyB.length]\n if (doSegmentsIntersect(a1, a2, b1, b2)) return true\n }\n }\n return false\n}\n\n/**\n * Check for overlapping PCB component courtyards.\n * Returns one error per pair of components whose courtyard elements overlap.\n */\nexport function checkCourtyardOverlap(\n circuitJson: AnyCircuitElement[],\n): PcbCourtyardOverlapError[] {\n const courtyards = circuitJson.filter(\n (el): el is CourtyardElement =>\n el.type === \"pcb_courtyard_rect\" ||\n el.type === \"pcb_courtyard_circle\" ||\n el.type === \"pcb_courtyard_outline\",\n )\n\n // Group by component\n const byComponent = new Map<string, CourtyardElement[]>()\n for (const el of courtyards) {\n const id = el.pcb_component_id\n if (!byComponent.has(id)) byComponent.set(id, [])\n byComponent.get(id)!.push(el)\n }\n\n const componentIds = Array.from(byComponent.keys())\n const errors: PcbCourtyardOverlapError[] = []\n\n for (let i = 0; i < componentIds.length; i++) {\n for (let j = i + 1; j < componentIds.length; j++) {\n const idA = componentIds[i]\n const idB = componentIds[j]\n\n let overlapping = false\n outer: for (const a of byComponent.get(idA)!) {\n for (const b of byComponent.get(idB)!) {\n // Skip overlap check if courtyards are on different layers\n if (\"layer\" in a && \"layer\" in b && a.layer !== b.layer) {\n continue\n }\n const polyA = getCourtyardPolygon(a)\n const polyB = getCourtyardPolygon(b)\n if (polygonsOverlap(polyA, polyB)) {\n overlapping = true\n break outer\n }\n }\n }\n\n if (overlapping) {\n errors.push({\n type: \"pcb_courtyard_overlap_error\",\n pcb_error_id: `pcb_courtyard_overlap_${idA}_${idB}`,\n error_type: \"pcb_courtyard_overlap_error\",\n message: `Courtyard of ${getComponentName(circuitJson, idA)} overlaps with courtyard of ${getComponentName(circuitJson, idB)}`,\n pcb_component_ids: [idA, idB],\n })\n }\n }\n }\n\n return errors\n}\n","import type {\n AnyCircuitElement,\n PcbTraceError,\n PcbPort,\n PcbTrace,\n SourceTrace,\n PcbSmtPad,\n PcbPlatedHole,\n} from \"circuit-json\"\nimport { isPointInPad } from \"./is-point-in-pad\"\nimport { getReadableNameForPcbPort } from \"@tscircuit/circuit-json-util\"\n\nfunction checkTracesAreContiguous(\n circuitJson: AnyCircuitElement[],\n): PcbTraceError[] {\n const errors: PcbTraceError[] = []\n\n const pcbPorts = circuitJson.filter(\n (el) => el.type === \"pcb_port\",\n ) as PcbPort[]\n const pcbTraces = circuitJson.filter(\n (el) => el.type === \"pcb_trace\",\n ) as PcbTrace[]\n const sourceTraces = circuitJson.filter(\n (el) => el.type === \"source_trace\",\n ) as SourceTrace[]\n const pcbSmtPads = circuitJson.filter(\n (el) => el.type === \"pcb_smtpad\",\n ) as PcbSmtPad[]\n const pcbPlatedHoles = circuitJson.filter(\n (el) => el.type === \"pcb_plated_hole\",\n ) as PcbPlatedHole[]\n\n const padMap = new Map<string, PcbSmtPad | PcbPlatedHole>()\n\n for (const pad of pcbSmtPads) {\n if (pad.pcb_port_id) {\n padMap.set(pad.pcb_port_id, pad)\n }\n }\n\n for (const hole of pcbPlatedHoles) {\n if (hole.pcb_port_id) {\n padMap.set(hole.pcb_port_id, hole)\n }\n }\n\n for (const trace of pcbTraces) {\n if (trace.route.length === 0) continue\n\n const firstPoint = trace.route[0]\n const lastPoint = trace.route[trace.route.length - 1]\n\n const sourceTrace = sourceTraces.find(\n (st) => st.source_trace_id === trace.source_trace_id,\n )\n\n const expectedPorts = sourceTrace\n ? pcbPorts.filter((port) =>\n sourceTrace.connected_source_port_ids?.includes(port.source_port_id),\n )\n : []\n\n for (let i = 1; i < trace.route.length - 1; i++) {\n const prevPoint = trace.route[i - 1]\n const currentPoint = trace.route[i]\n const nextPoint = trace.route[i + 1]\n\n if (currentPoint.route_type === \"via\") {\n const prevIsWire = prevPoint.route_type === \"wire\"\n const nextIsWire = nextPoint.route_type === \"wire\"\n\n if (prevIsWire && nextIsWire) {\n const prevAligned =\n Math.abs(prevPoint.x - currentPoint.x) < 0.01 &&\n Math.abs(prevPoint.y - currentPoint.y) < 0.01\n\n const nextAligned =\n Math.abs(nextPoint.x - currentPoint.x) < 0.01 &&\n Math.abs(nextPoint.y - currentPoint.y) < 0.01\n\n if (!prevAligned || !nextAligned) {\n const traceName =\n sourceTrace?.display_name || trace.source_trace_id || \"unknown\"\n errors.push({\n type: \"pcb_trace_error\",\n message: `Via in trace [${traceName}] is misaligned at position {x: ${currentPoint.x}, y: ${currentPoint.y}}.`,\n source_trace_id:\n sourceTrace?.source_trace_id ||\n trace.source_trace_id ||\n `!${trace.pcb_trace_id}`,\n error_type: \"pcb_trace_error\",\n pcb_trace_id: trace.pcb_trace_id,\n pcb_trace_error_id: \"\",\n pcb_component_ids: [],\n pcb_port_ids: [],\n })\n }\n }\n }\n }\n\n const traceName =\n sourceTrace?.display_name || trace.source_trace_id || \"unknown\"\n\n // For traces with known expected ports, check specific connections\n for (const port of expectedPorts) {\n if (!port.pcb_port_id) continue\n\n const pad = padMap.get(port.pcb_port_id)\n\n if (!pad) continue\n\n const isFirstPointConnected =\n firstPoint.route_type === \"wire\" &&\n isPointInPad({ x: firstPoint.x, y: firstPoint.y }, pad)\n\n const isLastPointConnected =\n lastPoint.route_type === \"wire\" &&\n isPointInPad({ x: lastPoint.x, y: lastPoint.y }, pad)\n\n if (!isFirstPointConnected && !isLastPointConnected) {\n const portName = getReadableNameForPcbPort(\n circuitJson,\n port.pcb_port_id,\n ).replace(\"pcb_port\", \"\")\n const padType = pad.type.replace(/pcb_/, \"\")\n // Use the midpoint between trace endpoints as error location\n const errorCenter = {\n x: (firstPoint.x + lastPoint.x) / 2,\n y: (firstPoint.y + lastPoint.y) / 2,\n }\n errors.push({\n type: \"pcb_trace_error\",\n message: `Trace [${traceName}] is missing a connection to ${padType}${portName}`,\n source_trace_id:\n sourceTrace?.source_trace_id ||\n trace.source_trace_id ||\n `!${trace.pcb_trace_id}`,\n error_type: \"pcb_trace_error\",\n pcb_trace_id: trace.pcb_trace_id,\n pcb_trace_error_id: \"\",\n center: errorCenter,\n pcb_component_ids: [],\n pcb_port_ids: [port.pcb_port_id],\n })\n }\n }\n\n // For net-level traces (no expected ports), check if endpoints are floating\n if (expectedPorts.length === 0) {\n let firstConnectsToAnyPad = false\n let lastConnectsToAnyPad = false\n\n for (const [portId, pad] of padMap) {\n if (\n firstPoint.route_type === \"wire\" &&\n isPointInPad({ x: firstPoint.x, y: firstPoint.y }, pad)\n ) {\n firstConnectsToAnyPad = true\n }\n if (\n lastPoint.route_type === \"wire\" &&\n isPointInPad({ x: lastPoint.x, y: lastPoint.y }, pad)\n ) {\n lastConnectsToAnyPad = true\n }\n }\n\n if (!firstConnectsToAnyPad && firstPoint.route_type === \"wire\") {\n errors.push({\n type: \"pcb_trace_error\",\n message: `Trace [${traceName}] has disconnected endpoint at (${firstPoint.x.toFixed(2)}, ${firstPoint.y.toFixed(2)})`,\n source_trace_id:\n sourceTrace?.source_trace_id ||\n trace.source_trace_id ||\n `!${trace.pcb_trace_id}`,\n error_type: \"pcb_trace_error\",\n pcb_trace_id: trace.pcb_trace_id,\n pcb_trace_error_id: \"\",\n center: { x: firstPoint.x, y: firstPoint.y },\n pcb_component_ids: [],\n pcb_port_ids: [],\n })\n }\n if (!lastConnectsToAnyPad && lastPoint.route_type === \"wire\") {\n errors.push({\n type: \"pcb_trace_error\",\n message: `Trace [${traceName}] has disconnected endpoint at (${lastPoint.x.toFixed(2)}, ${lastPoint.y.toFixed(2)})`,\n source_trace_id:\n sourceTrace?.source_trace_id ||\n trace.source_trace_id ||\n `!${trace.pcb_trace_id}`,\n error_type: \"pcb_trace_error\",\n pcb_trace_id: trace.pcb_trace_id,\n pcb_trace_error_id: \"\",\n center: { x: lastPoint.x, y: lastPoint.y },\n pcb_component_ids: [],\n pcb_port_ids: [],\n })\n }\n }\n }\n\n return errors\n}\n\nexport { checkTracesAreContiguous }\n","import type { AnyCircuitElement } from \"circuit-json\"\nimport { checkAllPinsInComponentAreUnderspecified } from \"./check-all-pins-in-component-are-underspecified\"\nimport { checkConnectorAccessibleOrientation } from \"./check-connector-accessible-orientation\"\nimport { checkCourtyardOverlap } from \"./check-courtyard-overlap/checkCourtyardOverlap\"\nimport { checkDifferentNetViaSpacing } from \"./check-different-net-via-spacing\"\nimport { checkEachPcbPortConnectedToPcbTraces } from \"./check-each-pcb-port-connected-to-pcb-trace\"\nimport { checkEachPcbTraceNonOverlapping } from \"./check-each-pcb-trace-non-overlapping/check-each-pcb-trace-non-overlapping\"\nimport { checkNoGroundPinDefined } from \"./check-no-ground-pin-defined\"\nimport { checkNoPowerPinDefined } from \"./check-no-power-pin-defined\"\nimport { checkPadPadClearance } from \"./check-pad-pad-clearance\"\nimport { checkPadTraceClearance } from \"./check-pad-trace-clearance\"\nimport { checkPcbComponentsOutOfBoard } from \"./check-pcb-components-out-of-board/checkPcbComponentsOutOfBoard\"\nimport { checkViasOffBoard } from \"./check-pcb-components-out-of-board/checkViasOffBoard\"\nimport { checkPcbComponentOverlap } from \"./check-pcb-components-overlap/checkPcbComponentOverlap\"\nimport { checkPinMustBeConnected } from \"./check-pin-must-be-connected\"\nimport { checkSameNetViaSpacing } from \"./check-same-net-via-spacing\"\nimport { checkSourceTracesHavePcbTraces } from \"./check-source-traces-have-pcb-traces\"\nimport { checkPcbTracesOutOfBoard } from \"./check-trace-out-of-board/checkTraceOutOfBoard\"\nimport { checkTracesAreContiguous } from \"./check-traces-are-contiguous/check-traces-are-contiguous\"\nimport { checkViaTraceClearance } from \"./check-via-trace-clearance\"\n\nexport async function runAllPlacementChecks(circuitJson: AnyCircuitElement[]) {\n return [\n ...checkViasOffBoard(circuitJson),\n ...checkPcbComponentsOutOfBoard(circuitJson),\n ...checkPcbComponentOverlap(circuitJson),\n ...checkPadPadClearance(circuitJson),\n ...checkPadTraceClearance(circuitJson),\n ...checkCourtyardOverlap(circuitJson),\n ...checkConnectorAccessibleOrientation(circuitJson),\n ]\n}\n\nexport async function runAllNetlistChecks(circuitJson: AnyCircuitElement[]) {\n return [...checkPinMustBeConnected(circuitJson)]\n}\n\nexport async function runAllPinSpecificationChecks(\n circuitJson: AnyCircuitElement[],\n) {\n return [\n ...checkAllPinsInComponentAreUnderspecified(circuitJson),\n ...checkNoPowerPinDefined(circuitJson),\n ...checkNoGroundPinDefined(circuitJson),\n ]\n}\n\nexport async function runAllRoutingChecks(circuitJson: AnyCircuitElement[]) {\n return [\n ...checkEachPcbPortConnectedToPcbTraces(circuitJson),\n ...checkSourceTracesHavePcbTraces(circuitJson),\n ...checkEachPcbTraceNonOverlapping(circuitJson),\n ...checkViaTraceClearance(circuitJson),\n ...checkSameNetViaSpacing(circuitJson),\n ...checkDifferentNetViaSpacing(circuitJson),\n // ...checkTracesAreContiguous(circuitJson),\n ...checkPcbTracesOutOfBoard(circuitJson),\n ]\n}\n\nexport async function runAllChecks(circuitJson: AnyCircuitElement[]) {\n return [\n ...(await runAllPlacementChecks(circuitJson)),\n ...(await runAllNetlistChecks(circuitJson)),\n ...(await runAllPinSpecificationChecks(circuitJson)),\n ...(await runAllRoutingChecks(circuitJson)),\n ]\n}\n"],"mappings":"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","names":["getReadableNameForElement","getFullConnectivityMapFromCircuitJson","getBoundsOfPcbElements","t","closestOnB","closestOnA","distance","clamp","dx","dy","getFullConnectivityMapFromCircuitJson","getReadableNameForElement","gap","distance","getReadableNameForElement","getReadableNameForElement","getReadableNameForElement","getFullConnectivityMapFromCircuitJson","distance","distance","getFullConnectivityMapFromCircuitJson","distance","getReadableNameForElement","getReadableNameForElement","getFullConnectivityMapFromCircuitJson","getFullConnectivityMapFromCircuitJson","distance","getReadableNameForElement","cju","getFullConnectivityMapFromCircuitJson","cju","getFullConnectivityMapFromCircuitJson","cju","segmentToSegmentMinDistance","distance","cju","getBoundsOfPcbElements","getPrimaryId","doBoundsOverlap","getFullConnectivityMapFromCircuitJson","getBoundsOfPcbElements","getBoundsOfPcbElements","getPrimaryId","getFullConnectivityMapFromCircuitJson","cju","getBoundsOfPcbElements","doBoundsOverlap","getPrimaryId","getReadableNameForElement","getFullConnectivityMapFromCircuitJson","cju","getBoundsOfPcbElements","getBoundsOfPcbElements","cju","getFullConnectivityMapFromCircuitJson","getPrimaryId","getReadableNameForElement","getPrimaryId","getReadableNameForElement","segmentToBoundsMinDistance","segmentToCircleMinDistance","getFullConnectivityMapFromCircuitJson","getFullConnectivityMapFromCircuitJson","getPrimaryId","segmentToCircleMinDistance","segmentToBoundsMinDistance","getReadableNameForElement","getReadableNameForElement","segmentToCircleMinDistance","getFullConnectivityMapFromCircuitJson","getFullConnectivityMapFromCircuitJson","segmentToCircleMinDistance","getReadableNameForElement","cju","cju","cju","getComponentName","getReadableNameForPcbPort"]}
1
+ {"version":3,"sources":["../lib/add-start-and-end-port-ids-if-missing.ts","../lib/check-each-pcb-port-connected-to-pcb-trace.ts","../lib/util/get-readable-names.ts","../lib/check-each-pcb-trace-non-overlapping/check-each-pcb-trace-non-overlapping.ts","../lib/data-structures/SpatialIndex.ts","../lib/check-each-pcb-trace-non-overlapping/getCollidableBounds.ts","../node_modules/@tscircuit/jlcpcb-manufacturing-specs/lib/jlcpcb-manufacturing-specs.ts","../lib/drc-defaults.ts","../lib/check-each-pcb-trace-non-overlapping/getPcbPortIdsConnectedToTraces.ts","../lib/check-each-pcb-trace-non-overlapping/getClosestPointBetweenSegments.ts","../lib/check-each-pcb-trace-non-overlapping/getRadiusOfCircuitJsonElement.ts","../lib/check-each-pcb-trace-non-overlapping/getClosestPointBetweenSegmentAndBounds.ts","../lib/util/getLayersOfPcbElement.ts","../lib/net-manager.ts","../lib/check-pcb-components-out-of-board/checkViasOffBoard.ts","../lib/check-pcb-components-out-of-board/checkPcbComponentsOutOfBoard.ts","../lib/check-same-net-via-spacing.ts","../lib/util/distance.ts","../lib/util/viasAreAtSameLocation.ts","../lib/check-different-net-via-spacing.ts","../lib/check-source-traces-match-pcb-trace-thickness.ts","../lib/check-source-traces-have-pcb-traces.ts","../lib/check-trace-out-of-board/checkTraceOutOfBoard.ts","../lib/check-pcb-components-overlap/checkPcbComponentOverlap.ts","../lib/check-pcb-components-overlap/doPcbElementsOverlap.ts","../lib/check-pad-pad-clearance.ts","../lib/check-pad-clearance/common.ts","../lib/check-pad-trace-clearance.ts","../lib/check-via-trace-clearance.ts","../lib/check-pin-must-be-connected.ts","../lib/check-all-pins-in-component-are-underspecified.ts","../lib/check-no-power-pin-defined.ts","../lib/check-no-ground-pin-defined.ts","../lib/check-connector-accessible-orientation.ts","../lib/check-courtyard-overlap/checkCourtyardOverlap.ts","../lib/check-traces-are-contiguous/check-traces-are-contiguous.ts","../lib/run-all-checks.ts"],"sourcesContent":["import type {\n PcbPort,\n PcbTrace,\n AnyCircuitElement,\n PcbSmtPad,\n} from \"circuit-json\"\n\nfunction distance(x1: number, y1: number, x2: number, y2: number): number {\n return Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)\n}\n\n/**\n * HACK: this whole method and all usage of it is a hack because of this issue:\n * https://github.com/tscircuit/tscircuit/issues/291\n */\nexport const addStartAndEndPortIdsIfMissing = (\n soup: AnyCircuitElement[],\n): void => {\n const pcbPorts: PcbPort[] = soup.filter((item) => item.type === \"pcb_port\")\n const pcbSmtPads: PcbSmtPad[] = soup.filter(\n (item) => item.type === \"pcb_smtpad\",\n )\n const pcbTraces: PcbTrace[] = soup.filter((item) => item.type === \"pcb_trace\")\n\n function findPortIdOverlappingPoint(\n point: {\n x: number\n y: number\n },\n options: { isFirstOrLastPoint?: boolean; traceWidth?: number } = {},\n ): string | null {\n const traceWidth = options.traceWidth || 0\n const directPort = pcbPorts.find(\n (port) => distance(port.x, port.y, point.x, point.y) < 0.01,\n )\n if (directPort) return directPort.pcb_port_id\n\n // If it starts or ends inside an smtpad, we'll connect it to the por\n if (options.isFirstOrLastPoint) {\n const smtPad = pcbSmtPads.find((pad) => {\n if (pad.shape === \"rect\") {\n return (\n Math.abs(point.x - pad.x) < pad.width / 2 + traceWidth / 2 &&\n Math.abs(point.y - pad.y) < pad.height / 2 + traceWidth / 2\n )\n // biome-ignore lint/style/noUselessElse: <explanation>\n } else if (pad.shape === \"circle\") {\n return distance(point.x, point.y, pad.x, pad.y) < pad.radius\n }\n })\n if (smtPad) return smtPad.pcb_port_id ?? null\n }\n\n return null\n }\n\n // Add start_pcb_port_id and end_pcb_port_id if not present\n for (const trace of pcbTraces) {\n for (let index = 0; index < trace.route.length; index++) {\n const segment = trace.route[index]\n const isFirstOrLastPoint = index === 0 || index === trace.route.length - 1\n if (segment.route_type === \"wire\") {\n if (!segment.start_pcb_port_id && index === 0) {\n const startPortId = findPortIdOverlappingPoint(segment, {\n isFirstOrLastPoint,\n traceWidth: segment.width,\n })\n if (startPortId) {\n segment.start_pcb_port_id = startPortId\n }\n }\n if (!segment.end_pcb_port_id && index === trace.route.length - 1) {\n const endPortId = findPortIdOverlappingPoint(segment, {\n isFirstOrLastPoint,\n traceWidth: segment.width,\n })\n if (endPortId) {\n segment.end_pcb_port_id = endPortId\n }\n }\n }\n }\n }\n}\n","import type {\n PcbPort,\n SourceTrace,\n AnyCircuitElement,\n PcbPortNotConnectedError,\n} from \"circuit-json\"\nimport { addStartAndEndPortIdsIfMissing } from \"./add-start-and-end-port-ids-if-missing\"\nimport { getFullConnectivityMapFromCircuitJson } from \"circuit-json-to-connectivity-map\"\nimport { getReadableNameForPort } from \"./util/get-readable-names\"\n\nfunction checkEachPcbPortConnectedToPcbTraces(\n circuitJson: AnyCircuitElement[],\n): PcbPortNotConnectedError[] {\n addStartAndEndPortIdsIfMissing(circuitJson)\n const sourceTraces: SourceTrace[] = circuitJson.filter(\n (item) => item.type === \"source_trace\",\n ) as SourceTrace[]\n\n const pcbPorts: PcbPort[] = circuitJson.filter(\n (item) => item.type === \"pcb_port\",\n ) as PcbPort[]\n\n const errors: PcbPortNotConnectedError[] = []\n\n // Generate the connectivity map from the circuit\n const connectivityMap = getFullConnectivityMapFromCircuitJson(circuitJson)\n\n // Create a map from source_port_id to pcb_port for quick lookup\n const sourcePortToPcbPort = new Map<string, PcbPort>()\n for (const pcbPort of pcbPorts) {\n sourcePortToPcbPort.set(pcbPort.source_port_id, pcbPort)\n }\n\n // Process each source trace\n for (const sourceTrace of sourceTraces) {\n const connectedSourcePortIds = sourceTrace.connected_source_port_ids\n\n // Skip traces with less than 2 ports (nothing to connect)\n if (connectedSourcePortIds.length < 2) {\n continue\n }\n\n // Find corresponding PCB ports for all source ports in this trace\n const pcbPortsInTrace: PcbPort[] = []\n const missingPcbPorts: string[] = []\n\n for (const sourcePortId of connectedSourcePortIds) {\n const pcbPort = sourcePortToPcbPort.get(sourcePortId)\n if (pcbPort) {\n pcbPortsInTrace.push(pcbPort)\n } else {\n missingPcbPorts.push(sourcePortId)\n }\n }\n\n // Skip if we don't have at least 2 PCB ports to connect\n if (pcbPortsInTrace.length < 2) {\n continue\n }\n\n // Get the net ID for the first PCB port as reference\n const firstPcbPort = pcbPortsInTrace[0]\n const referenceNetId = connectivityMap.getNetConnectedToId(\n firstPcbPort.pcb_port_id,\n )\n\n const netElementIds = connectivityMap.getIdsConnectedToNet(referenceNetId!)\n const pcbTraceIds = netElementIds.filter((id) =>\n circuitJson.some(\n (element) =>\n element.type === \"pcb_trace\" &&\n ((\"pcb_trace_id\" in element && element.pcb_trace_id === id) ||\n (\"route_id\" in element && element.route_id === id)),\n ),\n )\n\n if (pcbTraceIds.length === 0) {\n // Check if this is a trivial case (only 2 ports on same component)\n const uniqueComponentIds = new Set(\n pcbPortsInTrace.map((p) => p.pcb_component_id),\n )\n\n if (uniqueComponentIds.size > 1) {\n // Ports are on different components but no PCB traces connect them\n errors.push({\n type: \"pcb_port_not_connected_error\",\n message: `Ports [${pcbPortsInTrace.map((p) => getReadableNameForPort(circuitJson, p.pcb_port_id)).join(\", \")}] are not connected together through the same net.`,\n error_type: \"pcb_port_not_connected_error\",\n pcb_port_ids: pcbPortsInTrace.map((p) => p.pcb_port_id),\n pcb_component_ids: pcbPortsInTrace\n .map((p) => p.pcb_component_id)\n .filter((id): id is string => id !== undefined),\n pcb_port_not_connected_error_id: `pcb_port_not_connected_error_trace_${sourceTrace.source_trace_id}`,\n })\n }\n }\n }\n\n return errors\n}\n\nexport { checkEachPcbPortConnectedToPcbTraces }\n","import type {\n AnyCircuitElement,\n PcbPlatedHole,\n PcbSmtPad,\n SourceTrace,\n} from \"circuit-json\"\nimport {\n getReadableNameForElement,\n getReadableNameForPcbPort,\n getBoundsOfPcbElements,\n} from \"@tscircuit/circuit-json-util\"\n\nconst CIRCUIT_JSON_ID_PATTERN =\n /\\b(?:pcb|source|schematic|subcircuit)_[a-z0-9_]+\\b/i\n\nconst sanitizeReadableName = (\n candidate: string | null | undefined,\n id: string,\n fallbackLabel: string,\n): string => {\n if (\n !candidate ||\n candidate === id ||\n CIRCUIT_JSON_ID_PATTERN.test(candidate)\n ) {\n return fallbackLabel\n }\n return candidate\n}\n\nconst firstReadableName = (\n candidates: Array<string | null | undefined>,\n id: string,\n): string => {\n for (const candidate of candidates) {\n const readableName = sanitizeReadableName(candidate, id, \"\")\n if (readableName) return readableName\n }\n return \"\"\n}\n\nexport const getReadableNameForComponent = (\n circuitJson: AnyCircuitElement[],\n pcbComponentId: string,\n): string =>\n sanitizeReadableName(\n getReadableNameForElement(circuitJson, pcbComponentId),\n pcbComponentId,\n \"component\",\n )\n\nexport const getReadableNameForPort = (\n circuitJson: AnyCircuitElement[],\n pcbPortId: string,\n): string => {\n const pcbPort = circuitJson.find(\n (element) =>\n element.type === \"pcb_port\" && element.pcb_port_id === pcbPortId,\n )\n\n if (pcbPort?.type === \"pcb_port\") {\n const sourcePort = circuitJson.find(\n (element) =>\n element.type === \"source_port\" &&\n element.source_port_id === pcbPort.source_port_id,\n )\n\n const sourceComponent =\n sourcePort?.type === \"source_port\"\n ? circuitJson.find(\n (element) =>\n element.type === \"source_component\" &&\n element.source_component_id === sourcePort.source_component_id,\n )\n : null\n\n const readableSourceComponentName = firstReadableName(\n [\n sourceComponent?.type === \"source_component\"\n ? sourceComponent.name\n : null,\n ],\n sourceComponent?.type === \"source_component\"\n ? sourceComponent.source_component_id\n : \"\",\n )\n\n const readableSourcePortName = firstReadableName(\n [\n sourcePort?.type === \"source_port\" ? sourcePort.name : null,\n sourcePort?.type === \"source_port\"\n ? sourcePort.pin_number?.toString()\n : null,\n sourcePort?.type === \"source_port\" ? sourcePort.port_hints?.[0] : null,\n ],\n sourcePort?.type === \"source_port\" ? sourcePort.source_port_id : \"\",\n )\n\n if (readableSourceComponentName && readableSourcePortName) {\n return `${readableSourceComponentName}.${readableSourcePortName}`\n }\n if (readableSourcePortName) {\n return readableSourcePortName\n }\n }\n\n return sanitizeReadableName(\n getReadableNameForPcbPort(circuitJson, pcbPortId) ??\n getReadableNameForElement(circuitJson, pcbPortId),\n pcbPortId,\n \"port\",\n )\n}\n\nexport const getReadableNameForSourceTrace = (\n circuitJson: AnyCircuitElement[],\n sourceTrace: SourceTrace,\n): string => {\n const displayName = sanitizeReadableName(\n sourceTrace.display_name,\n sourceTrace.source_trace_id,\n \"\",\n )\n if (displayName) return displayName\n\n const connectedPortNames = (sourceTrace.connected_source_port_ids ?? [])\n .map((sourcePortId) => {\n const pcbPort = circuitJson.find(\n (element) =>\n element.type === \"pcb_port\" &&\n element.source_port_id === sourcePortId,\n )\n\n if (pcbPort?.type === \"pcb_port\") {\n return getReadableNameForPort(circuitJson, pcbPort.pcb_port_id)\n }\n\n const sourcePort = circuitJson.find(\n (element) =>\n element.type === \"source_port\" &&\n element.source_port_id === sourcePortId,\n )\n if (sourcePort?.type !== \"source_port\") return null\n\n const sourceComponent = circuitJson.find(\n (element) =>\n element.type === \"source_component\" &&\n element.source_component_id === sourcePort.source_component_id,\n )\n\n const sourceComponentName =\n sourceComponent?.type === \"source_component\"\n ? sanitizeReadableName(\n sourceComponent.name,\n sourceComponent.source_component_id,\n \"\",\n )\n : \"\"\n\n const sourcePortName = firstReadableName(\n [\n sourcePort.name,\n sourcePort.pin_number?.toString(),\n sourcePort.port_hints?.[0],\n ],\n sourcePort.source_port_id,\n )\n\n if (sourceComponentName && sourcePortName) {\n return `${sourceComponentName}.${sourcePortName}`\n }\n\n return sourcePortName || null\n })\n .filter((name): name is string => Boolean(name))\n\n if (connectedPortNames.length >= 2) {\n return `${connectedPortNames[0]} to ${connectedPortNames[1]}`\n }\n\n if (connectedPortNames.length === 1) {\n return `trace connected to ${connectedPortNames[0]}`\n }\n\n return `trace ${sourceTrace.source_trace_id}`\n}\n\nexport const getReadableNameForElementId = (\n circuitJson: AnyCircuitElement[],\n elementId: string,\n): string =>\n sanitizeReadableName(\n getReadableNameForElement(circuitJson, elementId),\n elementId,\n \"element\",\n )\n\nexport const getReadableNameForGroup = (\n circuitJson: AnyCircuitElement[],\n groupId: string,\n): string =>\n sanitizeReadableName(\n getReadableNameForElement(circuitJson, groupId),\n groupId,\n \"group\",\n )\n\nexport const containsCircuitJsonId = (message: string): boolean =>\n CIRCUIT_JSON_ID_PATTERN.test(message)\n\nexport function getReadableNameForFootprintPad(\n circuitJson: AnyCircuitElement[],\n pad: PcbSmtPad | PcbPlatedHole,\n ordinal: number,\n): string {\n const padKind = pad.type === \"pcb_smtpad\" ? \"SMD pad\" : \"through-hole pad\"\n const portRef = pad.pcb_port_id\n ? getReadableNameForPort(circuitJson, pad.pcb_port_id)\n : null\n const bounds = getBoundsOfPcbElements([pad])\n const centerX = (bounds.minX + bounds.maxX) / 2\n const centerY = (bounds.minY + bounds.maxY) / 2\n const location = `(${centerX.toFixed(2)}mm, ${centerY.toFixed(2)}mm)`\n\n if (portRef) return `${padKind} ${portRef} at ${location}`\n return `${padKind} #${ordinal + 1} at ${location}`\n}\n","import type { AnyCircuitElement, PcbTraceError } from \"circuit-json\"\nimport { getReadableNameForElement, cju } from \"@tscircuit/circuit-json-util\"\nimport {\n SpatialObjectIndex,\n type Bounds,\n} from \"lib/data-structures/SpatialIndex\"\nimport {\n getFullConnectivityMapFromCircuitJson,\n type ConnectivityMap,\n} from \"circuit-json-to-connectivity-map\"\nimport {\n getCollidableBounds,\n type Collidable,\n type PcbTraceSegment,\n} from \"./getCollidableBounds\"\nimport {\n segmentToBoundsMinDistance,\n segmentToCircleMinDistance,\n} from \"@tscircuit/math-utils\"\nimport {\n DEFAULT_TRACE_MARGIN,\n DEFAULT_TRACE_THICKNESS,\n EPSILON,\n} from \"lib/drc-defaults\"\nimport { getPcbPortIdsConnectedToTraces } from \"./getPcbPortIdsConnectedToTraces\"\nimport { segmentToSegmentMinDistance } from \"@tscircuit/math-utils\"\nimport { areBoundsOverlapping } from \"./areBoundsOverlapping\"\nimport { getPrimaryId } from \"@tscircuit/circuit-json-util\"\nimport { getCenterOfBoundsPair } from \"./getCenterOfBoundsPair\"\nimport { getClosestPointBetweenSegments } from \"./getClosestPointBetweenSegments\"\nimport { getCenterOfBounds } from \"./getCenterOfBounds\"\nimport { getRadiusOfCircuitJsonElement } from \"./getRadiusOfCircuitJsonElement\"\nimport { getClosestPointBetweenSegmentAndBounds } from \"./getClosestPointBetweenSegmentAndBounds\"\nimport { getLayersOfPcbElement } from \"../util/getLayersOfPcbElement\"\nimport { addStartAndEndPortIdsIfMissing } from \"lib/add-start-and-end-port-ids-if-missing\"\n\nexport function checkEachPcbTraceNonOverlapping(\n circuitJson: AnyCircuitElement[],\n {\n connMap,\n minClearance = DEFAULT_TRACE_MARGIN,\n }: {\n connMap?: ConnectivityMap\n minClearance?: number\n } = {},\n): PcbTraceError[] {\n const errors: PcbTraceError[] = []\n addStartAndEndPortIdsIfMissing(circuitJson)\n connMap ??= getFullConnectivityMapFromCircuitJson(circuitJson)\n\n const pcbTraces = cju(circuitJson).pcb_trace.list()\n const pcbTraceSegments = pcbTraces.flatMap((pcbTrace) => {\n const segments: PcbTraceSegment[] = []\n for (let i = 0; i < pcbTrace.route.length - 1; i++) {\n const p1 = pcbTrace.route[i]\n const p2 = pcbTrace.route[i + 1]\n if (p1.route_type !== \"wire\") continue\n if (p2.route_type !== \"wire\") continue\n if (p1.layer !== p2.layer) continue\n segments.push({\n type: \"pcb_trace_segment\",\n pcb_trace_id: pcbTrace.pcb_trace_id,\n _pcbTrace: pcbTrace,\n thickness:\n \"width\" in p1\n ? p1.width\n : \"width\" in p2\n ? p2.width\n : DEFAULT_TRACE_THICKNESS,\n layer: p1.layer,\n x1: p1.x,\n y1: p1.y,\n x2: p2.x,\n y2: p2.y,\n } as PcbTraceSegment)\n }\n return segments\n })\n const pcbSmtPads = cju(circuitJson).pcb_smtpad.list()\n const pcbPlatedHoles = cju(circuitJson).pcb_plated_hole.list()\n const pcbHoles = cju(circuitJson).pcb_hole.list()\n const pcbVias = cju(circuitJson).pcb_via.list()\n const pcbKeepouts = cju(circuitJson).pcb_keepout.list()\n\n const allObjects: Collidable[] = [\n ...pcbTraceSegments,\n ...pcbSmtPads,\n ...pcbPlatedHoles,\n ...pcbHoles,\n ...pcbVias,\n ...pcbKeepouts,\n ]\n\n const spatialIndex = new SpatialObjectIndex<Collidable>({\n objects: allObjects,\n getBounds: getCollidableBounds,\n })\n\n const getReadableName = (id: string) =>\n getReadableNameForElement(circuitJson, id)\n\n const errorIds = new Set<string>()\n\n // For each segment, check it if overlaps with anything collidable\n for (const segmentA of pcbTraceSegments) {\n const requiredMargin = minClearance\n const bounds = getCollidableBounds(segmentA)\n const nearbyObjects = spatialIndex.getObjectsInBounds(\n bounds,\n requiredMargin + segmentA.thickness / 2,\n )\n if (segmentA.x1 === segmentA.x2 && segmentA.y1 === segmentA.y2) continue\n\n for (const obj of nearbyObjects) {\n // ignore obstacles not on the trace's layer (except vias)\n if (!getLayersOfPcbElement(obj).includes(segmentA.layer)) {\n continue\n }\n if (obj.type === \"pcb_trace_segment\") {\n const segmentB = obj\n\n if (segmentA.layer !== segmentB.layer) continue\n\n // Check if the segments are overlapping\n if (\n connMap.areIdsConnected(segmentA.pcb_trace_id, segmentB.pcb_trace_id)\n )\n continue\n\n const gap =\n segmentToSegmentMinDistance(\n { x: segmentA.x1, y: segmentA.y1 },\n { x: segmentA.x2, y: segmentA.y2 },\n { x: segmentB.x1, y: segmentB.y1 },\n { x: segmentB.x2, y: segmentB.y2 },\n ) -\n segmentA.thickness / 2 -\n segmentB.thickness / 2\n if (gap > minClearance - EPSILON) continue\n\n const pcb_trace_error_id = `overlap_${segmentA.pcb_trace_id}_${segmentB.pcb_trace_id}`\n const pcb_trace_error_id_reverse = `overlap_${segmentB.pcb_trace_id}_${segmentA.pcb_trace_id}`\n if (errorIds.has(pcb_trace_error_id)) continue\n if (errorIds.has(pcb_trace_error_id_reverse)) continue\n\n errorIds.add(pcb_trace_error_id)\n errors.push({\n type: \"pcb_trace_error\",\n error_type: \"pcb_trace_error\",\n message: `PCB trace ${getReadableName(segmentA.pcb_trace_id)} overlaps with ${getReadableName(segmentB.pcb_trace_id)} ${gap < 0 ? \"(accidental contact)\" : `(gap: ${gap.toFixed(3)}mm)`}`,\n pcb_trace_id: segmentA.pcb_trace_id,\n source_trace_id: \"\",\n pcb_trace_error_id,\n pcb_component_ids: [],\n center: getClosestPointBetweenSegments(segmentA, segmentB),\n pcb_port_ids: getPcbPortIdsConnectedToTraces([\n segmentA._pcbTrace,\n segmentB._pcbTrace,\n ]),\n })\n continue\n }\n\n const primaryObjId = getPrimaryId(obj as any)\n if (\n connMap.areIdsConnected(\n segmentA.pcb_trace_id,\n \"pcb_trace_id\" in obj ? (obj.pcb_trace_id as string) : primaryObjId,\n )\n )\n continue\n\n const isCircular =\n obj.type === \"pcb_via\" ||\n (obj.type === \"pcb_plated_hole\" && obj.shape === \"circle\") ||\n obj.type === \"pcb_hole\" ||\n (obj.type === \"pcb_smtpad\" && obj.shape === \"circle\")\n\n if (isCircular) {\n const radius = getRadiusOfCircuitJsonElement(obj)\n const distance = segmentToCircleMinDistance(\n { x: segmentA.x1, y: segmentA.y1 },\n { x: segmentA.x2, y: segmentA.y2 },\n { x: obj.x, y: obj.y, radius },\n )\n const gap = distance - segmentA.thickness / 2\n if (gap > minClearance - EPSILON) continue\n\n const pcb_trace_error_id = `overlap_${segmentA.pcb_trace_id}_${primaryObjId}`\n if (errorIds.has(pcb_trace_error_id)) continue\n errorIds.add(pcb_trace_error_id)\n errors.push({\n type: \"pcb_trace_error\",\n error_type: \"pcb_trace_error\",\n message: `PCB trace ${getReadableName(segmentA.pcb_trace_id)} overlaps with ${obj.type} \"${getReadableName(getPrimaryId(obj as any))}\" ${gap < 0 ? \"(accidental contact)\" : `(gap: ${gap.toFixed(3)}mm)`}`,\n pcb_trace_id: segmentA.pcb_trace_id,\n center: getClosestPointBetweenSegmentAndBounds(\n segmentA,\n getCollidableBounds(obj),\n ),\n source_trace_id: \"\",\n pcb_trace_error_id,\n pcb_component_ids: [\n \"pcb_component_id\" in obj\n ? (obj.pcb_component_id as string)\n : undefined,\n ].filter(Boolean) as string[],\n pcb_port_ids: [\n ...getPcbPortIdsConnectedToTraces([segmentA._pcbTrace]),\n \"pcb_port_id\" in obj ? obj.pcb_port_id : undefined,\n ].filter(Boolean) as string[],\n })\n }\n\n // Handle generic case of hitting the bounds of any collidable obstacle\n // using the bounds of the collidable obstacle\n const gap =\n segmentToBoundsMinDistance(\n { x: segmentA.x1, y: segmentA.y1 },\n { x: segmentA.x2, y: segmentA.y2 },\n getCollidableBounds(obj),\n ) -\n segmentA.thickness / 2\n if (gap + EPSILON < requiredMargin) {\n const pcb_trace_error_id = `overlap_${segmentA.pcb_trace_id}_${primaryObjId}`\n if (errorIds.has(pcb_trace_error_id)) continue\n errorIds.add(pcb_trace_error_id)\n errors.push({\n type: \"pcb_trace_error\",\n error_type: \"pcb_trace_error\",\n message: `PCB trace ${getReadableName(segmentA.pcb_trace_id)} overlaps with ${obj.type} \"${getReadableName(getPrimaryId(obj as any))}\" ${gap < 0 ? \"(accidental contact)\" : `(gap: ${gap.toFixed(3)}mm)`}`,\n pcb_trace_id: segmentA.pcb_trace_id,\n source_trace_id: \"\",\n pcb_trace_error_id,\n pcb_component_ids: [\n \"pcb_component_id\" in obj ? obj.pcb_component_id : undefined,\n ].filter(Boolean) as string[],\n center: getClosestPointBetweenSegmentAndBounds(\n segmentA,\n getCollidableBounds(obj),\n ),\n pcb_port_ids: [\n ...getPcbPortIdsConnectedToTraces([segmentA._pcbTrace]),\n \"pcb_port_id\" in obj ? obj.pcb_port_id : undefined,\n ].filter(Boolean) as string[],\n })\n }\n }\n }\n return errors\n}\n","export type BucketCoordinate = `${number}x${number}`\n\nexport interface Bounds {\n minX: number\n minY: number\n maxX: number\n maxY: number\n}\n\nexport type GetBoundsFn<T> = (obj: T) => Bounds\nexport type GetIdFn<T> = (obj: T) => string\n\nexport class SpatialObjectIndex<T> {\n buckets: Map<BucketCoordinate, Array<T & { spatialIndexId: string }>>\n objectsById: Map<string, T & { spatialIndexId: string }>\n getBounds: GetBoundsFn<T>\n getId: GetIdFn<T>\n CELL_SIZE = 0.4\n\n constructor({\n objects,\n getBounds,\n getId,\n CELL_SIZE,\n }: {\n objects: T[]\n getBounds: GetBoundsFn<T>\n getId?: GetIdFn<T>\n CELL_SIZE?: number\n }) {\n this.buckets = new Map()\n this.objectsById = new Map()\n this.getBounds = getBounds\n this.getId = getId ?? (() => this._getNextId())\n this.CELL_SIZE = CELL_SIZE ?? this.CELL_SIZE\n\n for (const obj of objects) {\n this.addObject(obj)\n }\n }\n\n _idCounter = 0\n _getNextId(): string {\n return `${this._idCounter++}`\n }\n\n addObject(obj: T): void {\n const bounds = this.getBounds(obj)\n const spatialIndexId = this.getId(obj)\n const objWithId = { ...obj, spatialIndexId } as T & {\n spatialIndexId: string\n }\n\n // Store in objectsById for quick lookup\n this.objectsById.set(spatialIndexId, objWithId)\n\n // Calculate the bucket coordinates that cover the object's bounds\n const minBucketX = Math.floor(bounds.minX / this.CELL_SIZE)\n const minBucketY = Math.floor(bounds.minY / this.CELL_SIZE)\n const maxBucketX = Math.floor(bounds.maxX / this.CELL_SIZE)\n const maxBucketY = Math.floor(bounds.maxY / this.CELL_SIZE)\n\n // Add the object to all buckets it intersects with\n for (let bx = minBucketX; bx <= maxBucketX; bx++) {\n for (let by = minBucketY; by <= maxBucketY; by++) {\n const bucketKey = `${bx}x${by}` as BucketCoordinate\n const bucket = this.buckets.get(bucketKey)\n if (!bucket) {\n this.buckets.set(bucketKey, [objWithId])\n } else {\n bucket.push(objWithId)\n }\n }\n }\n }\n\n removeObject(id: string): boolean {\n const obj = this.objectsById.get(id)\n if (!obj) return false\n\n // Remove from objectsById\n this.objectsById.delete(id)\n\n // Calculate the bucket coordinates that cover the object's bounds\n const bounds = this.getBounds(obj)\n const minBucketX = Math.floor(bounds.minX / this.CELL_SIZE)\n const minBucketY = Math.floor(bounds.minY / this.CELL_SIZE)\n const maxBucketX = Math.floor(bounds.maxX / this.CELL_SIZE)\n const maxBucketY = Math.floor(bounds.maxY / this.CELL_SIZE)\n\n // Remove the object from all buckets it might be in\n for (let bx = minBucketX; bx <= maxBucketX; bx++) {\n for (let by = minBucketY; by <= maxBucketY; by++) {\n const bucketKey = `${bx}x${by}` as BucketCoordinate\n const bucket = this.buckets.get(bucketKey)\n if (bucket) {\n const index = bucket.findIndex((item) => item.spatialIndexId === id)\n if (index !== -1) {\n bucket.splice(index, 1)\n if (bucket.length === 0) {\n this.buckets.delete(bucketKey)\n }\n }\n }\n }\n }\n\n return true\n }\n\n getBucketKey(x: number, y: number): BucketCoordinate {\n return `${Math.floor(x / this.CELL_SIZE)}x${Math.floor(y / this.CELL_SIZE)}`\n }\n\n getObjectsInBounds(bounds: Bounds, margin = 0): T[] {\n const objects: T[] = []\n const addedIds = new Set<string>()\n\n // Calculate the bucket coordinates that cover the requested bounds with margin\n const minBucketX = Math.floor((bounds.minX - margin) / this.CELL_SIZE)\n const minBucketY = Math.floor((bounds.minY - margin) / this.CELL_SIZE)\n const maxBucketX = Math.floor((bounds.maxX + margin) / this.CELL_SIZE)\n const maxBucketY = Math.floor((bounds.maxY + margin) / this.CELL_SIZE)\n\n // Collect objects from all buckets that intersect the bounds\n for (let bx = minBucketX; bx <= maxBucketX; bx++) {\n for (let by = minBucketY; by <= maxBucketY; by++) {\n const bucketKey = `${bx}x${by}` as BucketCoordinate\n const bucket = this.buckets.get(bucketKey) || []\n\n for (const obj of bucket) {\n const id = obj.spatialIndexId\n if (addedIds.has(id)) continue\n\n addedIds.add(id)\n objects.push(obj)\n }\n }\n }\n\n return objects\n }\n}\n","import { getBoundsOfPcbElements } from \"@tscircuit/circuit-json-util\"\nimport type {\n AnyCircuitElement,\n PcbHole,\n PCBKeepout,\n PcbPlatedHole,\n PcbSmtPad,\n PcbTrace,\n PcbTraceError,\n PcbVia,\n} from \"circuit-json\"\n\ninterface Bounds {\n minX: number\n minY: number\n maxX: number\n maxY: number\n}\n\nexport type PcbTraceSegment = {\n type: \"pcb_trace_segment\"\n _pcbTrace: PcbTrace\n pcb_trace_id: string\n thickness: number\n layer: string\n x1: number\n y1: number\n x2: number\n y2: number\n}\n\nexport type Collidable =\n | PcbTraceSegment\n | PcbSmtPad\n | PcbPlatedHole\n | PcbHole\n | PcbVia\n | PCBKeepout\n\nexport const getCollidableBounds = (collidable: Collidable): Bounds => {\n if (collidable.type === \"pcb_trace_segment\") {\n return {\n minX: Math.min(collidable.x1, collidable.x2),\n minY: Math.min(collidable.y1, collidable.y2),\n maxX: Math.max(collidable.x1, collidable.x2),\n maxY: Math.max(collidable.y1, collidable.y2),\n }\n }\n return getBoundsOfPcbElements([collidable as any])\n}\n","import type { PcbBoard } from \"circuit-json\"\n\nexport type JlcToleranceMap = Partial<Record<keyof PcbBoard, number>>\n\nexport const jlcMinTolerances: JlcToleranceMap = {\n min_trace_width: 0.1,\n min_via_to_via_clearance: 0.1,\n min_trace_to_pad_clearance: 0.1,\n min_pad_to_pad_clearance: 0.1,\n min_board_edge_clearance: 0.2,\n min_via_hole_diameter: 0.2,\n min_via_pad_diameter: 0.3,\n}\n\n// TODO: Update these values\nexport const jlcPreferredTolerances: JlcToleranceMap = {\n min_trace_width: 0.2,\n min_via_to_via_clearance: 0.2,\n min_trace_to_pad_clearance: 0.2,\n min_pad_to_pad_clearance: 0.2,\n min_board_edge_clearance: 0.2,\n min_via_hole_diameter: 0.4,\n min_via_pad_diameter: 0.6,\n}\n","import { jlcMinTolerances } from \"@tscircuit/jlcpcb-manufacturing-specs\"\n\nexport const DEFAULT_TRACE_MARGIN = 0.1\nexport const DEFAULT_TRACE_THICKNESS = jlcMinTolerances.min_trace_width\nexport const DEFAULT_VIA_DIAMETER = jlcMinTolerances.min_via_pad_diameter\nexport const DEFAULT_VIA_BOARD_MARGIN = 0.3\n\nexport const DEFAULT_SAME_NET_VIA_MARGIN =\n jlcMinTolerances.min_via_to_via_clearance\nexport const DEFAULT_DIFFERENT_NET_VIA_MARGIN =\n jlcMinTolerances.min_via_to_via_clearance\n\nexport const DEFAULT_PAD_PAD_CLEARANCE =\n jlcMinTolerances.min_pad_to_pad_clearance\nexport const DEFAULT_PAD_TRACE_CLEARANCE =\n jlcMinTolerances.min_trace_to_pad_clearance\n\nexport const EPSILON = 0.005\n","import type { PcbTrace } from \"circuit-json\"\n\nexport function getPcbPortIdsConnectedToTrace(trace: PcbTrace) {\n const connectedPcbPorts = new Set<string>()\n for (const segment of trace.route) {\n if (segment.route_type === \"wire\") {\n if (segment.start_pcb_port_id)\n connectedPcbPorts.add(segment.start_pcb_port_id)\n if (segment.end_pcb_port_id)\n connectedPcbPorts.add(segment.end_pcb_port_id)\n }\n }\n\n return Array.from(connectedPcbPorts)\n}\n\nexport function getPcbPortIdsConnectedToTraces(traces: PcbTrace[]) {\n const connectedPorts = new Set<string>()\n for (const trace of traces) {\n for (const portId of getPcbPortIdsConnectedToTrace(trace)) {\n connectedPorts.add(portId)\n }\n }\n return Array.from(connectedPorts)\n}\n","import type { PcbTraceSegment } from \"./getCollidableBounds\"\n\nexport const getClosestPointBetweenSegments = (\n segmentA: PcbTraceSegment,\n segmentB: PcbTraceSegment,\n): { x: number; y: number } => {\n // Define points for each segment\n const a1 = { x: segmentA.x1, y: segmentA.y1 }\n const a2 = { x: segmentA.x2, y: segmentA.y2 }\n const b1 = { x: segmentB.x1, y: segmentB.y1 }\n const b2 = { x: segmentB.x2, y: segmentB.y2 }\n\n // Calculate direction vectors for each segment\n const va = { x: a2.x - a1.x, y: a2.y - a1.y }\n const vb = { x: b2.x - b1.x, y: b2.y - b1.y }\n\n // Calculate squared lengths of segments\n const lenSqrA = va.x * va.x + va.y * va.y\n const lenSqrB = vb.x * vb.x + vb.y * vb.y\n\n // If either segment is a point (zero length), handle as special case\n if (lenSqrA === 0 || lenSqrB === 0) {\n if (lenSqrA === 0 && lenSqrB === 0) {\n // Both segments are points, return the distance between them\n // Calculate the average point\n return {\n x: (a1.x + b1.x) / 2,\n y: (a1.y + b1.y) / 2,\n }\n }\n if (lenSqrA === 0) {\n // First segment is a point, find closest point on second segment\n const t = clamp(\n ((a1.x - b1.x) * vb.x + (a1.y - b1.y) * vb.y) / lenSqrB,\n 0,\n 1,\n )\n const closestOnB = {\n x: b1.x + t * vb.x,\n y: b1.y + t * vb.y,\n }\n // Calculate the average point\n return {\n x: (a1.x + closestOnB.x) / 2,\n y: (a1.y + closestOnB.y) / 2,\n }\n }\n // Second segment is a point, find closest point on first segment\n const t = clamp(\n ((b1.x - a1.x) * va.x + (b1.y - a1.y) * va.y) / lenSqrA,\n 0,\n 1,\n )\n const closestOnA = {\n x: a1.x + t * va.x,\n y: a1.y + t * va.y,\n }\n // Calculate the average point\n return {\n x: (closestOnA.x + b1.x) / 2,\n y: (closestOnA.y + b1.y) / 2,\n }\n }\n\n // Vector between segment starting points\n const w = { x: a1.x - b1.x, y: a1.y - b1.y }\n\n // Calculate dot products\n const dotAA = va.x * va.x + va.y * va.y\n const dotAB = va.x * vb.x + va.y * vb.y\n const dotAW = va.x * w.x + va.y * w.y\n const dotBB = vb.x * vb.x + vb.y * vb.y\n const dotBW = vb.x * w.x + vb.y * w.y\n\n // Calculate parametric positions (t values) along each segment\n const denominator = dotAA * dotBB - dotAB * dotAB\n\n // If segments are parallel, handle separately\n if (denominator < 1e-10) {\n return closestPointsParallelSegments(\n a1,\n a2,\n b1,\n b2,\n va,\n vb,\n lenSqrA,\n lenSqrB,\n )\n }\n\n // Calculate parameters for closest points\n let tA = (dotAB * dotBW - dotBB * dotAW) / denominator\n let tB = (dotAA * dotBW - dotAB * dotAW) / denominator\n\n // Clamp parameters to segment bounds\n tA = clamp(tA, 0, 1)\n tB = clamp(tB, 0, 1)\n\n // Recalculate tB if tA was clamped\n tB = (tA * dotAB + dotBW) / dotBB\n tB = clamp(tB, 0, 1)\n\n // Recalculate tA if tB was clamped\n tA = (tB * dotAB - dotAW) / dotAA\n tA = clamp(tA, 0, 1)\n\n // Calculate closest points on each segment\n const closestOnA = {\n x: a1.x + tA * va.x,\n y: a1.y + tA * va.y,\n }\n\n const closestOnB = {\n x: b1.x + tB * vb.x,\n y: b1.y + tB * vb.y,\n }\n\n // Calculate distance between closest points\n const dx = closestOnA.x - closestOnB.x\n const dy = closestOnA.y - closestOnB.y\n const distance = Math.sqrt(dx * dx + dy * dy)\n\n // Calculate the average of the closest points\n const averagePoint = {\n x: (closestOnA.x + closestOnB.x) / 2,\n y: (closestOnA.y + closestOnB.y) / 2,\n }\n\n return averagePoint\n}\n\n// Helper function for handling parallel segments\nconst closestPointsParallelSegments = (\n a1: { x: number; y: number },\n a2: { x: number; y: number },\n b1: { x: number; y: number },\n b2: { x: number; y: number },\n va: { x: number; y: number },\n vb: { x: number; y: number },\n lenSqrA: number,\n lenSqrB: number,\n) => {\n // Project b1 onto segment A\n let tA = ((b1.x - a1.x) * va.x + (b1.y - a1.y) * va.y) / lenSqrA\n tA = clamp(tA, 0, 1)\n const pointOnA1 = { x: a1.x + tA * va.x, y: a1.y + tA * va.y }\n\n // Project b2 onto segment A\n let tA2 = ((b2.x - a1.x) * va.x + (b2.y - a1.y) * va.y) / lenSqrA\n tA2 = clamp(tA2, 0, 1)\n const pointOnA2 = { x: a1.x + tA2 * va.x, y: a1.y + tA2 * va.y }\n\n // Project a1 onto segment B\n let tB = ((a1.x - b1.x) * vb.x + (a1.y - b1.y) * vb.y) / lenSqrB\n tB = clamp(tB, 0, 1)\n const pointOnB1 = { x: b1.x + tB * vb.x, y: b1.y + tB * vb.y }\n\n // Project a2 onto segment B\n let tB2 = ((a2.x - b1.x) * vb.x + (a2.y - b1.y) * vb.y) / lenSqrB\n tB2 = clamp(tB2, 0, 1)\n const pointOnB2 = { x: b1.x + tB2 * vb.x, y: b1.y + tB2 * vb.y }\n\n // Calculate all possible distances between end points and their projections\n const distances = [\n {\n pointA: pointOnA1,\n pointB: b1,\n distance: Math.sqrt(\n (pointOnA1.x - b1.x) ** 2 + (pointOnA1.y - b1.y) ** 2,\n ),\n },\n {\n pointA: pointOnA2,\n pointB: b2,\n distance: Math.sqrt(\n (pointOnA2.x - b2.x) ** 2 + (pointOnA2.y - b2.y) ** 2,\n ),\n },\n {\n pointA: a1,\n pointB: pointOnB1,\n distance: Math.sqrt(\n (a1.x - pointOnB1.x) ** 2 + (a1.y - pointOnB1.y) ** 2,\n ),\n },\n {\n pointA: a2,\n pointB: pointOnB2,\n distance: Math.sqrt(\n (a2.x - pointOnB2.x) ** 2 + (a2.y - pointOnB2.y) ** 2,\n ),\n },\n ]\n\n // Find closest pair\n const closestPair = distances.reduce((closest, current) =>\n current.distance < closest.distance ? current : closest,\n )\n\n // Calculate the average of the closest points\n return {\n x: (closestPair.pointA.x + closestPair.pointB.x) / 2,\n y: (closestPair.pointA.y + closestPair.pointB.y) / 2,\n }\n}\n\n// Helper function to clamp a value between min and max\nconst clamp = (value: number, min: number, max: number): number => {\n return Math.max(min, Math.min(max, value))\n}\n","import type {\n AnyCircuitElement,\n PcbHole,\n PcbPlatedHole,\n PcbVia,\n PcbSmtPad,\n} from \"circuit-json\"\n\nexport const getRadiusOfCircuitJsonElement = (\n obj: PcbVia | PcbPlatedHole | PcbHole | PcbSmtPad,\n) => {\n if (obj.type === \"pcb_via\") {\n return obj.outer_diameter / 2\n }\n if (obj.type === \"pcb_plated_hole\" && obj.shape === \"circle\") {\n return obj.outer_diameter / 2\n }\n if (obj.type === \"pcb_hole\" && obj.hole_shape === \"circle\") {\n return obj.hole_diameter / 2\n }\n if (obj.type === \"pcb_smtpad\" && obj.shape === \"circle\") {\n return obj.radius\n }\n throw new Error(\n `Could not determine radius of element: ${JSON.stringify(obj)}`,\n )\n}\n","import type { Bounds } from \"lib/data-structures/SpatialIndex\"\nimport type { PcbTraceSegment } from \"./getCollidableBounds\"\n\nexport const getClosestPointBetweenSegmentAndBounds = (\n segment: PcbTraceSegment,\n bounds: Bounds,\n): { x: number; y: number } => {\n // Define segment points\n const p1 = { x: segment.x1, y: segment.y1 }\n const p2 = { x: segment.x2, y: segment.y2 }\n\n // Define bounds corners\n const minX = bounds.minX\n const minY = bounds.minY\n const maxX = bounds.maxX\n const maxY = bounds.maxY\n\n // Check if segment is a point\n if (p1.x === p2.x && p1.y === p2.y) {\n // For a point, find the closest point on the bounds\n const closestX = Math.max(minX, Math.min(maxX, p1.x))\n const closestY = Math.max(minY, Math.min(maxY, p1.y))\n\n // If the point is inside the bounds, return the point itself\n if (closestX === p1.x && closestY === p1.y) {\n return { x: p1.x, y: p1.y }\n }\n\n // Otherwise, return the closest point on the bounds\n return { x: closestX, y: closestY }\n }\n\n // Calculate direction vector of the segment\n const dx = p2.x - p1.x\n const dy = p2.y - p1.y\n\n // Calculate parameter values for intersection with each boundary\n const tMinX = dx !== 0 ? (minX - p1.x) / dx : Number.NEGATIVE_INFINITY\n const tMaxX = dx !== 0 ? (maxX - p1.x) / dx : Number.POSITIVE_INFINITY\n const tMinY = dy !== 0 ? (minY - p1.y) / dy : Number.NEGATIVE_INFINITY\n const tMaxY = dy !== 0 ? (maxY - p1.y) / dy : Number.POSITIVE_INFINITY\n\n // Find the entering and exiting parameters\n const tEnter = Math.max(Math.min(tMinX, tMaxX), Math.min(tMinY, tMaxY))\n const tExit = Math.min(Math.max(tMinX, tMaxX), Math.max(tMinY, tMaxY))\n\n // Check if segment intersects the bounds\n if (tEnter <= tExit && tExit >= 0 && tEnter <= 1) {\n // Segment intersects bounds, clamp parameter to segment\n const t = Math.max(0, Math.min(1, tEnter))\n return {\n x: p1.x + t * dx,\n y: p1.y + t * dy,\n }\n }\n\n // Segment doesn't intersect bounds, find closest point\n // Check each endpoint of the segment against the bounds\n const closestToP1 = {\n x: Math.max(minX, Math.min(maxX, p1.x)),\n y: Math.max(minY, Math.min(maxY, p1.y)),\n }\n\n const closestToP2 = {\n x: Math.max(minX, Math.min(maxX, p2.x)),\n y: Math.max(minY, Math.min(maxY, p2.y)),\n }\n\n // Calculate distances\n const distToP1Squared =\n (closestToP1.x - p1.x) ** 2 + (closestToP1.y - p1.y) ** 2\n const distToP2Squared =\n (closestToP2.x - p2.x) ** 2 + (closestToP2.y - p2.y) ** 2\n\n // Check each edge of the bounds against the segment\n const edges = [\n { start: { x: minX, y: minY }, end: { x: maxX, y: minY } }, // Bottom edge\n { start: { x: maxX, y: minY }, end: { x: maxX, y: maxY } }, // Right edge\n { start: { x: maxX, y: maxY }, end: { x: minX, y: maxY } }, // Top edge\n { start: { x: minX, y: maxY }, end: { x: minX, y: minY } }, // Left edge\n ]\n\n let minDistance = Math.min(distToP1Squared, distToP2Squared)\n let closestPoint =\n distToP1Squared <= distToP2Squared ? closestToP1 : closestToP2\n\n // Helper function to clamp a value between min and max\n const clamp = (value: number, min: number, max: number): number => {\n return Math.max(min, Math.min(max, value))\n }\n\n // Check each edge\n for (const edge of edges) {\n // Calculate direction vectors\n const va = { x: p2.x - p1.x, y: p2.y - p1.y }\n const vb = { x: edge.end.x - edge.start.x, y: edge.end.y - edge.start.y }\n const w = { x: p1.x - edge.start.x, y: p1.y - edge.start.y }\n\n // Calculate dot products\n const dotAA = va.x * va.x + va.y * va.y\n const dotAB = va.x * vb.x + va.y * vb.y\n const dotAW = va.x * w.x + va.y * w.y\n const dotBB = vb.x * vb.x + vb.y * vb.y\n const dotBW = vb.x * w.x + vb.y * w.y\n\n // Calculate parameters for closest points\n const denominator = dotAA * dotBB - dotAB * dotAB\n\n // Skip if lines are parallel\n if (Math.abs(denominator) < 1e-10) continue\n\n let tA = (dotAB * dotBW - dotBB * dotAW) / denominator\n let tB = (dotAA * dotBW - dotAB * dotAW) / denominator\n\n // Clamp parameters to segment bounds\n tA = clamp(tA, 0, 1)\n tB = clamp(tB, 0, 1)\n\n // Calculate closest points\n const closestOnSegment = {\n x: p1.x + tA * va.x,\n y: p1.y + tA * va.y,\n }\n\n const closestOnEdge = {\n x: edge.start.x + tB * vb.x,\n y: edge.start.y + tB * vb.y,\n }\n\n // Calculate distance\n const dx = closestOnSegment.x - closestOnEdge.x\n const dy = closestOnSegment.y - closestOnEdge.y\n const distSquared = dx * dx + dy * dy\n\n // Update if this is closer\n if (distSquared < minDistance) {\n minDistance = distSquared\n closestPoint = {\n x: (closestOnSegment.x + closestOnEdge.x) / 2,\n y: (closestOnSegment.y + closestOnEdge.y) / 2,\n }\n }\n }\n\n return closestPoint\n}\n","import type { Collidable } from \"lib/check-each-pcb-trace-non-overlapping/getCollidableBounds\"\nimport { all_layers } from \"circuit-json\"\n\nexport function getLayersOfPcbElement(obj: Collidable): string[] {\n if (obj.type === \"pcb_trace_segment\") {\n return [obj.layer]\n }\n if (obj.type === \"pcb_smtpad\") {\n return [obj.layer]\n }\n if (obj.type === \"pcb_plated_hole\") {\n return Array.isArray(obj.layers) ? obj.layers : [...all_layers]\n }\n if (obj.type === \"pcb_hole\") {\n return [...all_layers]\n }\n if (obj.type === \"pcb_via\") {\n return Array.isArray(obj.layers) ? obj.layers : [...all_layers]\n }\n if (obj.type === \"pcb_keepout\") {\n return Array.isArray(obj.layers) ? obj.layers : []\n }\n return []\n}\n","export class NetManager {\n private networks: Set<Set<string>> = new Set()\n\n setConnected(nodes: string[]): void {\n if (nodes.length < 2) return\n\n let targetNetwork: Set<string> | null = null\n\n // Check if any of the nodes are already in a network\n for (const network of this.networks) {\n for (const node of nodes) {\n if (network.has(node)) {\n if (targetNetwork === null) {\n targetNetwork = network\n } else if (targetNetwork !== network) {\n // Merge networks\n for (const mergeNode of network) {\n targetNetwork.add(mergeNode)\n }\n this.networks.delete(network)\n }\n break\n }\n }\n if (targetNetwork !== null && targetNetwork !== network) break\n }\n\n // If no existing network found, create a new one\n if (targetNetwork === null) {\n targetNetwork = new Set(nodes)\n this.networks.add(targetNetwork)\n } else {\n // Add all nodes to the target network\n for (const node of nodes) {\n targetNetwork.add(node)\n }\n }\n }\n\n isConnected(nodes: string[]): boolean {\n if (nodes.length < 2) return true\n\n for (const network of this.networks) {\n if (nodes.every((node) => network.has(node))) {\n return true\n }\n }\n\n return false\n }\n}\n","import type {\n AnyCircuitElement,\n PcbBoard,\n PcbVia,\n PcbPlacementError,\n} from \"circuit-json\"\nimport { getReadableNameForElement } from \"@tscircuit/circuit-json-util\"\nimport { DEFAULT_VIA_BOARD_MARGIN } from \"lib/drc-defaults\"\n\nexport function checkViasOffBoard(\n circuitJson: AnyCircuitElement[],\n): PcbPlacementError[] {\n const board = circuitJson.find((el) => el.type === \"pcb_board\") as PcbBoard\n\n if (!board) return []\n\n const vias = circuitJson.filter((el) => el.type === \"pcb_via\") as PcbVia[]\n\n if (vias.length === 0) return []\n\n if (board.width === undefined || board.height === undefined) return []\n\n const boardMinX = board.center.x - board.width / 2\n const boardMaxX = board.center.x + board.width / 2\n const boardMinY = board.center.y - board.height / 2\n const boardMaxY = board.center.y + board.height / 2\n\n const errors: PcbPlacementError[] = []\n\n for (const via of vias) {\n const viaRadius = via.outer_diameter / 2\n const viaMinX = via.x - viaRadius\n const viaMaxX = via.x + viaRadius\n const viaMinY = via.y - viaRadius\n const viaMaxY = via.y + viaRadius\n\n if (\n viaMinX < boardMinX + DEFAULT_VIA_BOARD_MARGIN ||\n viaMaxX > boardMaxX - DEFAULT_VIA_BOARD_MARGIN ||\n viaMinY < boardMinY + DEFAULT_VIA_BOARD_MARGIN ||\n viaMaxY > boardMaxY - DEFAULT_VIA_BOARD_MARGIN\n ) {\n const viaName = getReadableNameForElement(circuitJson, via.pcb_via_id)\n errors.push({\n type: \"pcb_placement_error\",\n pcb_placement_error_id: `out_of_board_${via.pcb_via_id}`,\n message: `Via ${viaName} is outside or crossing the board boundary`,\n error_type: \"pcb_placement_error\",\n })\n }\n }\n\n return errors\n}\n","import type {\n AnyCircuitElement,\n PcbBoard,\n PcbComponent,\n AnySourceComponent,\n PcbComponentOutsideBoardError,\n} from \"circuit-json\"\nimport { getReadableNameForComponent } from \"lib/util/get-readable-names\"\nimport type { Point } from \"@tscircuit/math-utils\"\nimport * as Flatten from \"@flatten-js/core\"\nimport { rotateDEG, applyToPoint } from \"transformation-matrix\"\n\n/**\n * Create a rectangle polygon centered at (cx,cy) with given width/height and rotation (degrees).\n * Uses transformation-matrix to rotate around the center and returns a Flatten.Polygon.\n */\n\nfunction isPolygonCCW(poly: Flatten.Polygon): boolean {\n // @ts-ignore\n return poly.area() >= 0\n}\n\nfunction rectanglePolygon({\n center,\n size,\n rotationDeg = 0,\n}: {\n center: Point\n size: { width: number; height: number }\n rotationDeg?: number\n}): Flatten.Polygon {\n const cx = center.x\n const cy = center.y\n const hw = size.width / 2\n const hh = size.height / 2\n\n const corners: Flatten.Point[] = [\n new Flatten.Point(cx - hw, cy - hh),\n new Flatten.Point(cx + hw, cy - hh),\n new Flatten.Point(cx + hw, cy + hh),\n new Flatten.Point(cx - hw, cy + hh),\n ]\n\n let poly = new Flatten.Polygon(corners)\n\n if (rotationDeg) {\n const matrix = rotateDEG(rotationDeg, cx, cy)\n const rotatedCorners = corners.map((pt) => {\n const p = applyToPoint(matrix, { x: pt.x, y: pt.y })\n return new Flatten.Point(p.x, p.y)\n })\n poly = new Flatten.Polygon(rotatedCorners)\n }\n\n // Ensure CCW order\n if (!isPolygonCCW(poly)) poly.reverse()\n\n return poly\n}\n\nfunction boardToPolygon({\n board,\n}: {\n board: PcbBoard\n}): Flatten.Polygon | null {\n if (board.outline && board.outline.length > 0) {\n const points = board.outline.map((p) => new Flatten.Point(p.x, p.y))\n const poly = new Flatten.Polygon(points)\n\n if (!isPolygonCCW(poly)) {\n poly.reverse()\n }\n\n return poly\n }\n\n if (\n board.center &&\n typeof board.width === \"number\" &&\n typeof board.height === \"number\"\n ) {\n return rectanglePolygon({\n center: board.center,\n size: { width: board.width, height: board.height },\n rotationDeg: 0,\n })\n }\n\n return null\n}\n\n/** Get human-readable component name from circuit JSON */\nfunction getComponentName({\n circuitJson,\n component,\n}: {\n circuitJson: AnyCircuitElement[]\n component: PcbComponent\n}): string {\n if (component.source_component_id) {\n const sourceComponent = circuitJson.find(\n (el): el is AnySourceComponent =>\n el.type === \"source_component\" &&\n el.source_component_id === component.source_component_id,\n )\n if (sourceComponent && \"name\" in sourceComponent && sourceComponent.name) {\n return sourceComponent.name\n }\n }\n\n return getReadableNameForComponent(circuitJson, component.pcb_component_id)\n}\n\n/**\n * Compute overlap distance based on polygon containment and distances.\n * - If component center is outside board, distance is from center to board polygon.\n * - Samples rotated rectangle corners and edge midpoints; for any outside points,\n * computes distance to board polygon and returns the maximum.\n * - Falls back to intersection area ratio heuristic if needed.\n * - Returns small epsilon if fully inside.\n */\nfunction computeOverlapDistance(\n compPoly: Flatten.Polygon,\n boardPoly: Flatten.Polygon,\n componentCenter: Point,\n componentWidth: number,\n componentHeight: number,\n rotationDeg: number,\n): number {\n const centerPoint = new Flatten.Point(componentCenter.x, componentCenter.y)\n // If center is outside board polygon, return distance to board polygon\n if (!boardPoly.contains(centerPoint)) {\n const dist = boardPoly.distanceTo(centerPoint)\n return Array.isArray(dist) ? dist[0] : Number(dist) || 0\n }\n\n // Sample corners and edge midpoints of rotated rectangle\n const hw = componentWidth / 2\n const hh = componentHeight / 2\n\n // Original corners\n const corners: Point[] = [\n { x: componentCenter.x - hw, y: componentCenter.y - hh },\n { x: componentCenter.x + hw, y: componentCenter.y - hh },\n { x: componentCenter.x + hw, y: componentCenter.y + hh },\n { x: componentCenter.x - hw, y: componentCenter.y + hh },\n ]\n\n // Edge midpoints\n const midpoints: Point[] = []\n for (let i = 0; i < 4; i++) {\n const next = (i + 1) % 4\n midpoints.push({\n x: (corners[i].x + corners[next].x) / 2,\n y: (corners[i].y + corners[next].y) / 2,\n })\n }\n\n // Rotate points around center by rotationDeg\n const matrix = rotateDEG(rotationDeg, componentCenter.x, componentCenter.y)\n const rotatePoint = (pt: Point) => {\n const p = applyToPoint(matrix, pt)\n return new Flatten.Point(p.x, p.y)\n }\n\n const rotatedPoints = corners.concat(midpoints).map(rotatePoint)\n\n // For any point outside board polygon, compute distance to board polygon\n let maxDistance = 0\n for (const pt of rotatedPoints) {\n if (!boardPoly.contains(pt)) {\n const dist = boardPoly.distanceTo(pt)\n const d = Array.isArray(dist) ? dist[0] : Number(dist) || 0\n if (d > maxDistance) maxDistance = d\n }\n }\n\n if (maxDistance > 0) {\n return maxDistance\n }\n\n // Fallback: use intersection area ratio heuristic\n try {\n const intersection = Flatten.BooleanOperations.intersect(\n compPoly,\n boardPoly,\n )\n\n let intersectionArea = 0\n if (!intersection) {\n intersectionArea = 0\n } else if (Array.isArray(intersection)) {\n intersectionArea = intersection.reduce(\n (sum, p) => sum + (typeof p.area === \"function\" ? p.area() : 0),\n 0,\n )\n } else if (typeof (intersection as any).area === \"function\") {\n intersectionArea = (intersection as any).area()\n } else {\n intersectionArea = 0\n }\n\n const compArea = compPoly.area()\n\n if (intersectionArea > 0 && intersectionArea < compArea) {\n const overlapRatio = 1 - intersectionArea / compArea\n const compWidth = Math.abs(componentWidth)\n const compHeight = Math.abs(componentHeight)\n return Math.min(compWidth, compHeight) * overlapRatio\n } else if (intersectionArea === 0) {\n // completely outside (should not happen here), return small epsilon\n return 0.1\n } else {\n return 0.1\n }\n } catch {\n // If boolean ops fail (unlikely), return small epsilon\n return 0.1\n }\n}\n\nfunction getRepositionSuggestion({\n componentPoly,\n boardPoly,\n}: {\n componentPoly: Flatten.Polygon\n boardPoly: Flatten.Polygon\n}): string | null {\n const boardBox = boardPoly.box\n const componentBox = componentPoly.box\n\n let deltaX = 0\n let deltaY = 0\n\n if (componentBox.xmin < boardBox.xmin) {\n deltaX = boardBox.xmin - componentBox.xmin\n } else if (componentBox.xmax > boardBox.xmax) {\n deltaX = boardBox.xmax - componentBox.xmax\n }\n\n if (componentBox.ymin < boardBox.ymin) {\n deltaY = boardBox.ymin - componentBox.ymin\n } else if (componentBox.ymax > boardBox.ymax) {\n deltaY = boardBox.ymax - componentBox.ymax\n }\n\n if (deltaX === 0 && deltaY === 0) {\n return null\n }\n\n const xDir = deltaX >= 0 ? \"right\" : \"left\"\n const yDir = deltaY >= 0 ? \"up\" : \"down\"\n const absDx = Math.abs(Math.round(deltaX * 100) / 100)\n const absDy = Math.abs(Math.round(deltaY * 100) / 100)\n\n if (absDx > 0 && absDy > 0) {\n return `Try moving it ${absDx}mm ${xDir} and ${absDy}mm ${yDir} to fit within the board edge.`\n }\n\n if (absDx > 0) {\n return `Try moving it ${absDx}mm ${xDir} to fit within the board edge.`\n }\n\n return `Try moving it ${absDy}mm ${yDir} to fit within the board edge.`\n}\n\n/**\n * Main function — polygon-first: construct polygons, test containment / intersection,\n * compute overlap distance using boolean intersection area or geometric distance.\n */\nexport function checkPcbComponentsOutOfBoard(\n circuitJson: AnyCircuitElement[],\n): PcbComponentOutsideBoardError[] {\n const board = circuitJson.find(\n (el): el is PcbBoard => el.type === \"pcb_board\",\n )\n if (!board) return []\n\n const boardPoly = boardToPolygon({ board })\n if (!boardPoly) return []\n\n const components = circuitJson.filter(\n (el): el is PcbComponent => el.type === \"pcb_component\",\n )\n if (components.length === 0) return []\n\n const errors: PcbComponentOutsideBoardError[] = []\n\n for (const c of components) {\n if (c.is_allowed_to_be_off_board) continue\n\n // need center, width, height\n if (\n !c.center ||\n typeof c.width !== \"number\" ||\n typeof c.height !== \"number\"\n )\n continue\n\n if (c.width <= 0 || c.height <= 0) continue\n\n // c.width and c.height store the world-coordinate AABB dimensions (post-rotation),\n // so we must NOT re-apply rotation here.\n const compPoly = rectanglePolygon({\n center: c.center,\n size: { width: c.width, height: c.height },\n rotationDeg: 0,\n })\n\n if (compPoly.area() === 0) continue\n\n // If component is entirely inside board polygon -> OK\n // Flatten.Polygon.contains accepts shapes; this is the correct polygon containment check.\n const isInside = boardPoly.contains(compPoly)\n if (isInside) continue\n\n // Component is at least partially outside. Compute overlapDistance:\n const overlapDistance = computeOverlapDistance(\n compPoly,\n boardPoly,\n c.center,\n c.width,\n c.height,\n 0,\n )\n\n const compName = getComponentName({ circuitJson, component: c })\n const overlapDistanceMm = Math.round(overlapDistance * 100) / 100\n const repositionSuggestion = getRepositionSuggestion({\n componentPoly: compPoly,\n boardPoly,\n })\n\n errors.push({\n type: \"pcb_component_outside_board_error\",\n error_type: \"pcb_component_outside_board_error\",\n pcb_component_outside_board_error_id: `pcb_component_outside_board_${c.pcb_component_id}`,\n message: `Component ${compName} extends outside board boundaries by ${overlapDistanceMm}mm.${repositionSuggestion ? ` ${repositionSuggestion}` : \"\"}`,\n pcb_component_id: c.pcb_component_id,\n pcb_board_id: board.pcb_board_id,\n component_center: c.center,\n component_bounds: {\n min_x: compPoly.box.xmin,\n max_x: compPoly.box.xmax,\n min_y: compPoly.box.ymin,\n max_y: compPoly.box.ymax,\n },\n subcircuit_id: c.subcircuit_id,\n source_component_id: c.source_component_id,\n })\n }\n\n return errors\n}\n","import type {\n AnyCircuitElement,\n PcbVia,\n PcbViaClearanceError,\n} from \"circuit-json\"\nimport { getReadableNameForElement } from \"@tscircuit/circuit-json-util\"\nimport {\n getFullConnectivityMapFromCircuitJson,\n type ConnectivityMap,\n} from \"circuit-json-to-connectivity-map\"\nimport { DEFAULT_SAME_NET_VIA_MARGIN, EPSILON } from \"lib/drc-defaults\"\nimport { distance } from \"lib/util/distance\"\nimport { viasAreAtSameLocation } from \"lib/util/viasAreAtSameLocation\"\n\nexport function checkSameNetViaSpacing(\n circuitJson: AnyCircuitElement[],\n {\n connMap,\n minClearance = DEFAULT_SAME_NET_VIA_MARGIN,\n }: { connMap?: ConnectivityMap; minClearance?: number } = {},\n): PcbViaClearanceError[] {\n const vias = circuitJson.filter((el) => el.type === \"pcb_via\") as PcbVia[]\n if (vias.length < 2) return []\n connMap ??= getFullConnectivityMapFromCircuitJson(circuitJson)\n const errors: PcbViaClearanceError[] = []\n const reported = new Set<string>()\n\n for (let i = 0; i < vias.length; i++) {\n for (let j = i + 1; j < vias.length; j++) {\n const viaA = vias[i]\n const viaB = vias[j]\n // TODO: It is a very inefficient piece of code, the way to fix it is to use flatbush.\n if (viasAreAtSameLocation(viaA, viaB)) continue\n if (!connMap.areIdsConnected(viaA.pcb_via_id, viaB.pcb_via_id)) continue\n const gap =\n distance(viaA, viaB) - viaA.outer_diameter / 2 - viaB.outer_diameter / 2\n if (gap + EPSILON >= minClearance!) continue\n const pairId = [viaA.pcb_via_id, viaB.pcb_via_id].sort().join(\"_\")\n if (reported.has(pairId)) continue\n reported.add(pairId)\n errors.push({\n type: \"pcb_via_clearance_error\",\n pcb_error_id: `same_net_vias_close_${pairId}`,\n message: `Vias ${getReadableNameForElement(\n circuitJson,\n viaA.pcb_via_id,\n )} and ${getReadableNameForElement(\n circuitJson,\n viaB.pcb_via_id,\n )} are too close together (gap: ${gap.toFixed(3)}mm)`,\n error_type: \"pcb_via_clearance_error\",\n pcb_via_ids: [viaA.pcb_via_id, viaB.pcb_via_id],\n minimum_clearance: minClearance,\n actual_clearance: gap,\n pcb_center: {\n x: (viaA.x + viaB.x) / 2,\n y: (viaA.y + viaB.y) / 2,\n },\n })\n }\n }\n\n return errors\n}\n","import type { PcbVia } from \"circuit-json\"\n\nexport function distance(a: PcbVia, b: PcbVia): number {\n return Math.hypot(a.x - b.x, a.y - b.y)\n}\n","import type { PcbVia } from \"circuit-json\"\nimport { distance } from \"./distance\"\nimport { EPSILON } from \"lib/drc-defaults\"\n\nexport function viasAreAtSameLocation(a: PcbVia, b: PcbVia): boolean {\n return distance(a, b) <= EPSILON\n}\n","import type {\n AnyCircuitElement,\n PcbVia,\n PcbViaClearanceError,\n} from \"circuit-json\"\nimport { getReadableNameForElement } from \"@tscircuit/circuit-json-util\"\nimport {\n getFullConnectivityMapFromCircuitJson,\n type ConnectivityMap,\n} from \"circuit-json-to-connectivity-map\"\nimport { DEFAULT_DIFFERENT_NET_VIA_MARGIN, EPSILON } from \"lib/drc-defaults\"\nimport { viasAreAtSameLocation } from \"lib/util/viasAreAtSameLocation\"\nimport { distance } from \"lib/util/distance\"\n\nexport function checkDifferentNetViaSpacing(\n circuitJson: AnyCircuitElement[],\n {\n connMap,\n minClearance = DEFAULT_DIFFERENT_NET_VIA_MARGIN,\n }: { connMap?: ConnectivityMap; minClearance?: number } = {},\n): PcbViaClearanceError[] {\n const vias = circuitJson.filter((el) => el.type === \"pcb_via\") as PcbVia[]\n if (vias.length < 2) return []\n connMap ??= getFullConnectivityMapFromCircuitJson(circuitJson)\n const errors: PcbViaClearanceError[] = []\n const reported = new Set<string>()\n\n for (let i = 0; i < vias.length; i++) {\n for (let j = i + 1; j < vias.length; j++) {\n const viaA = vias[i]\n const viaB = vias[j]\n // TODO: It is a very inefficient piece of code, the way to fix it is to use flatbush.\n if (viasAreAtSameLocation(viaA, viaB)) continue\n if (connMap.areIdsConnected(viaA.pcb_via_id, viaB.pcb_via_id)) continue\n const gap =\n distance(viaA, viaB) - viaA.outer_diameter / 2 - viaB.outer_diameter / 2\n if (gap + EPSILON >= minClearance!) continue\n const pairId = [viaA.pcb_via_id, viaB.pcb_via_id].sort().join(\"_\")\n if (reported.has(pairId)) continue\n reported.add(pairId)\n errors.push({\n type: \"pcb_via_clearance_error\",\n pcb_error_id: `different_net_vias_close_${pairId}`,\n message: `Vias ${getReadableNameForElement(\n circuitJson,\n viaA.pcb_via_id,\n )} and ${getReadableNameForElement(\n circuitJson,\n viaB.pcb_via_id,\n )} from different nets are too close together (gap: ${gap.toFixed(\n 3,\n )}mm)`,\n error_type: \"pcb_via_clearance_error\",\n pcb_via_ids: [viaA.pcb_via_id, viaB.pcb_via_id],\n minimum_clearance: minClearance,\n actual_clearance: gap,\n pcb_center: {\n x: (viaA.x + viaB.x) / 2,\n y: (viaA.y + viaB.y) / 2,\n },\n })\n }\n }\n\n return errors\n}\n","import { cju } from \"@tscircuit/circuit-json-util\"\nimport type {\n AnyCircuitElement,\n PcbPort,\n PcbTrace,\n PcbTraceWarning,\n SourceTrace,\n} from \"circuit-json\"\nimport { getFullConnectivityMapFromCircuitJson } from \"circuit-json-to-connectivity-map\"\nimport { getReadableNameForSourceTrace } from \"lib/util/get-readable-names\"\n\nexport function checkSourceTracesMatchPcbTraceThickness(\n circuitJson: AnyCircuitElement[],\n): PcbTraceWarning[] {\n const warnings: PcbTraceWarning[] = []\n const db = cju(circuitJson)\n\n const sourceTraces = db.source_trace.list() as SourceTrace[]\n const pcbTraces = db.pcb_trace.list() as PcbTrace[]\n const pcbPorts = db.pcb_port.list() as PcbPort[]\n const connectivityMap = getFullConnectivityMapFromCircuitJson(circuitJson)\n\n for (const sourceTrace of sourceTraces) {\n const requestedThickness = sourceTrace.min_trace_thickness\n if (requestedThickness === undefined) continue\n\n const connectedPcbPorts = pcbPorts.filter((pcbPort) =>\n sourceTrace.connected_source_port_ids?.includes(pcbPort.source_port_id),\n )\n if (connectedPcbPorts.length < 2) continue\n\n const referenceNetId = connectivityMap.getNetConnectedToId(\n connectedPcbPorts[0].pcb_port_id,\n )\n if (!referenceNetId) continue\n\n const netElementIds = connectivityMap.getIdsConnectedToNet(referenceNetId)\n const relatedPcbTraces = pcbTraces.filter((pcbTrace) =>\n netElementIds.includes(pcbTrace.pcb_trace_id),\n )\n if (relatedPcbTraces.length === 0) continue\n\n const actualWireWidths = relatedPcbTraces.flatMap((pcbTrace) =>\n pcbTrace.route\n .filter((point) => point.route_type === \"wire\")\n .map((point) => point.width),\n )\n if (actualWireWidths.length === 0) continue\n\n const actualThickness = Math.min(...actualWireWidths)\n if (actualThickness >= requestedThickness) continue\n\n let undersizedSegment:\n | { pcb_trace_id: string; center: { x: number; y: number } }\n | undefined\n\n for (const relatedPcbTrace of relatedPcbTraces) {\n for (let i = 0; i < relatedPcbTrace.route.length - 1; i++) {\n const point = relatedPcbTrace.route[i]\n const nextPoint = relatedPcbTrace.route[i + 1]\n if (!point || !nextPoint) continue\n if (point.route_type !== \"wire\" || nextPoint.route_type !== \"wire\") {\n continue\n }\n if (point.width !== actualThickness) continue\n\n undersizedSegment = {\n pcb_trace_id: relatedPcbTrace.pcb_trace_id,\n center: {\n x: (point.x + nextPoint.x) / 2,\n y: (point.y + nextPoint.y) / 2,\n },\n }\n break\n }\n\n if (undersizedSegment) break\n }\n\n if (!undersizedSegment) continue\n\n warnings.push({\n type: \"pcb_trace_warning\",\n pcb_trace_warning_id: `pcb_trace_warning_${sourceTrace.source_trace_id}`,\n warning_type: \"pcb_trace_warning\",\n message: `Trace [${getReadableNameForSourceTrace(circuitJson, sourceTrace)}] is routed thinner than requested (requested: ${requestedThickness}mm, actual: ${actualThickness}mm).`,\n center: undersizedSegment.center,\n source_trace_id: sourceTrace.source_trace_id,\n pcb_trace_id: undersizedSegment.pcb_trace_id,\n pcb_component_ids: Array.from(\n new Set(\n connectedPcbPorts\n .map((pcbPort) => pcbPort.pcb_component_id)\n .filter((id): id is string => id !== undefined),\n ),\n ),\n pcb_port_ids: connectedPcbPorts.map((pcbPort) => pcbPort.pcb_port_id),\n })\n }\n\n return warnings\n}\n","import type {\n AnyCircuitElement,\n PcbPort,\n PcbTrace,\n PcbTraceMissingError,\n SourceTrace,\n} from \"circuit-json\"\nimport { containsCircuitJsonId } from \"lib/util/get-readable-names\"\n\nconst getSourceTraceIdsFromPcbTrace = (\n pcbTraceSourceTraceId?: string,\n): string[] => {\n if (!pcbTraceSourceTraceId) return []\n if (!pcbTraceSourceTraceId.includes(\"__\")) return [pcbTraceSourceTraceId]\n\n return pcbTraceSourceTraceId\n .split(\"__\")\n .filter((part) => part.startsWith(\"source_trace_\"))\n}\n\n/**\n * Check that each source_trace which connects source ports has at least one\n * pcb_trace associated with it. If a source_trace has no corresponding\n * pcb_trace, return an error for that source_trace.\n */\nfunction checkSourceTracesHavePcbTraces(\n circuitJson: AnyCircuitElement[],\n): PcbTraceMissingError[] {\n const errors: PcbTraceMissingError[] = []\n const sourceTraces = circuitJson.filter(\n (el) => el.type === \"source_trace\",\n ) as SourceTrace[]\n const pcbTraces = circuitJson.filter(\n (el) => el.type === \"pcb_trace\",\n ) as PcbTrace[]\n\n for (const sourceTrace of sourceTraces) {\n if (!sourceTrace.connected_source_port_ids?.length) continue\n if ((sourceTrace.connected_source_net_ids?.length ?? 0) > 0) continue\n if (sourceTrace.connected_source_port_ids.length < 2) continue\n\n const hasPcbTrace = pcbTraces.some((pcbTrace) =>\n getSourceTraceIdsFromPcbTrace(pcbTrace.source_trace_id).includes(\n sourceTrace.source_trace_id,\n ),\n )\n if (!hasPcbTrace) {\n // Get PCB ports connected to this source trace\n const connectedPcbPorts = circuitJson.filter(\n (el) =>\n el.type === \"pcb_port\" &&\n sourceTrace.connected_source_port_ids.includes(el.source_port_id),\n ) as PcbPort[]\n\n // Find PCB components that these ports belong to\n const connectedPcbComponentIds = Array.from(\n new Set(\n connectedPcbPorts\n .map((port) => port.pcb_component_id)\n .filter((id): id is string => id !== undefined),\n ),\n )\n\n errors.push({\n type: \"pcb_trace_missing_error\",\n pcb_trace_missing_error_id: `pcb_trace_missing_${sourceTrace.source_trace_id}`,\n error_type: \"pcb_trace_missing_error\",\n message: `Trace [${sourceTrace.display_name && !containsCircuitJsonId(sourceTrace.display_name) ? sourceTrace.display_name : \"trace\"}] is not connected (it has no PCB trace)`,\n source_trace_id: sourceTrace.source_trace_id,\n pcb_component_ids: connectedPcbComponentIds,\n pcb_port_ids: connectedPcbPorts.map((port) => port.pcb_port_id),\n })\n }\n }\n\n return errors\n}\n\nexport { checkSourceTracesHavePcbTraces }\n","import type {\n AnyCircuitElement,\n PcbBoard,\n PcbTrace,\n PcbTraceError,\n} from \"circuit-json\"\nimport { cju } from \"@tscircuit/circuit-json-util\"\nimport type { Point, Polygon } from \"@tscircuit/math-utils\"\nimport { segmentToSegmentMinDistance } from \"@tscircuit/math-utils\"\n\n/**\n * Default margin for trace clearance from board edge (in mm)\n */\nconst DEFAULT_BOARD_MARGIN = 0.2\n\n/**\n * Configuration for trace board boundary checking\n */\nexport interface TraceBoardCheckConfig {\n /** Minimum distance from trace center to board edge (in mm) */\n margin?: number\n}\n\n/**\n * Create a board polygon representation using math-utils Polygon type\n */\nfunction getBoardPolygonPoints(board: PcbBoard): Polygon | null {\n if (board.outline && board.outline.length > 0) {\n // Use custom board outline\n return board.outline.map((p) => ({ x: p.x, y: p.y }))\n }\n\n if (\n board.center &&\n typeof board.width === \"number\" &&\n typeof board.height === \"number\"\n ) {\n // Create rectangular board outline\n const cx = board.center.x\n const cy = board.center.y\n const hw = board.width / 2\n const hh = board.height / 2\n\n return [\n { x: cx - hw, y: cy - hh }, // bottom-left\n { x: cx + hw, y: cy - hh }, // bottom-right\n { x: cx + hw, y: cy + hh }, // top-right\n { x: cx - hw, y: cy + hh }, // top-left\n ]\n }\n\n return null\n}\n\n/**\n * Check if any trace segment is too close to or outside the board outline\n * Uses segment-to-polygon distance with configurable margin\n */\nexport function checkPcbTracesOutOfBoard(\n circuitJson: AnyCircuitElement[],\n config: TraceBoardCheckConfig = {},\n): PcbTraceError[] {\n const errors: PcbTraceError[] = []\n const margin = config.margin ?? DEFAULT_BOARD_MARGIN\n\n // Find the board\n const board = circuitJson.find(\n (el): el is PcbBoard => el.type === \"pcb_board\",\n )\n if (!board) return errors\n\n // Create board polygon using math-utils Point type\n const boardPoints = getBoardPolygonPoints(board)\n if (!boardPoints) return errors\n\n // Get all PCB traces\n const pcbTraces = cju(circuitJson).pcb_trace.list()\n\n for (const trace of pcbTraces) {\n if (trace.route.length < 2) continue\n\n // Check each segment of the trace\n for (let i = 0; i < trace.route.length - 1; i++) {\n const p1 = trace.route[i]\n const p2 = trace.route[i + 1]\n\n // Only check wire segments\n if (p1.route_type !== \"wire\" || p2.route_type !== \"wire\") continue\n\n const traceWidth =\n \"width\" in p1 ? p1.width : \"width\" in p2 ? p2.width : 0.1\n const segmentStart: Point = { x: p1.x, y: p1.y }\n const segmentEnd: Point = { x: p2.x, y: p2.y }\n\n // Calculate minimum distance from trace segment to board polygon\n let minDistance = Infinity\n for (let j = 0; j < boardPoints.length; j++) {\n const edgeStart = boardPoints[j]\n const edgeEnd = boardPoints[(j + 1) % boardPoints.length]\n const distance = segmentToSegmentMinDistance(\n segmentStart,\n segmentEnd,\n edgeStart,\n edgeEnd,\n )\n if (distance < minDistance) {\n minDistance = distance\n }\n }\n\n const minimumDistance = traceWidth / 2 + margin\n\n if (minDistance < minimumDistance) {\n const error: PcbTraceError = {\n type: \"pcb_trace_error\",\n error_type: \"pcb_trace_error\",\n pcb_trace_error_id: `trace_too_close_to_board_${trace.pcb_trace_id}_segment_${i}`,\n message: `Trace too close to board edge (${minDistance.toFixed(3)}mm < ${minimumDistance.toFixed(3)}mm required, margin: ${margin}mm)`,\n pcb_trace_id: trace.pcb_trace_id,\n source_trace_id: trace.source_trace_id || \"\",\n center: {\n x: (segmentStart.x + segmentEnd.x) / 2,\n y: (segmentStart.y + segmentEnd.y) / 2,\n },\n pcb_component_ids: [],\n pcb_port_ids: [],\n }\n errors.push(error)\n }\n }\n }\n\n return errors\n}\n","import {\n cju,\n getBoundsOfPcbElements,\n getPrimaryId,\n} from \"@tscircuit/circuit-json-util\"\nimport { doBoundsOverlap } from \"@tscircuit/math-utils\"\nimport type { AnyCircuitElement, PcbFootprintOverlapError } from \"circuit-json\"\nimport { getFullConnectivityMapFromCircuitJson } from \"circuit-json-to-connectivity-map\"\nimport {\n getReadableNameForElementId,\n getReadableNameForPort,\n} from \"lib/util/get-readable-names\"\nimport {\n type OverlappableElement,\n doPcbElementsOverlap,\n} from \"./doPcbElementsOverlap\"\n\ninterface ComponentWithElements {\n component_id: string\n elements: OverlappableElement[]\n bounds: {\n minX: number\n minY: number\n maxX: number\n maxY: number\n }\n}\n\nconst formatOverlapElementDescription = (\n circuitJson: AnyCircuitElement[],\n element: OverlappableElement,\n): string => {\n if (\"pcb_port_id\" in element && element.pcb_port_id) {\n return getReadableNameForPort(circuitJson, element.pcb_port_id)\n }\n\n const id = getPrimaryId(element)\n const readableName = getReadableNameForElementId(circuitJson, id)\n return readableName === \"element\" ? `[${id}]` : readableName\n}\n\n/**\n * Check for overlapping PCB components\n * Returns errors for components that overlap inappropriately\n */\nexport function checkPcbComponentOverlap(\n circuitJson: AnyCircuitElement[],\n): PcbFootprintOverlapError[] {\n const errors: PcbFootprintOverlapError[] = []\n\n // Build connectivity map to check if components are electrically connected\n const connMap = getFullConnectivityMapFromCircuitJson(circuitJson)\n\n // Get all overlappable elements\n const smtPads = cju(circuitJson).pcb_smtpad.list()\n const platedHoles = cju(circuitJson).pcb_plated_hole.list()\n const holes = cju(circuitJson).pcb_hole.list()\n\n // Group elements by component (or treat standalone elements as their own \"component\")\n const componentMap = new Map<string, ComponentWithElements>()\n\n // Group SMT pads by component (or treat as standalone)\n for (const pad of smtPads) {\n const componentId =\n pad.pcb_component_id || `standalone_pad_${getPrimaryId(pad)}`\n if (!componentMap.has(componentId)) {\n componentMap.set(componentId, {\n component_id: componentId,\n elements: [],\n bounds: { minX: 0, minY: 0, maxX: 0, maxY: 0 },\n })\n }\n componentMap.get(componentId)!.elements.push(pad)\n }\n\n // Group plated holes by component (or treat as standalone)\n for (const hole of platedHoles) {\n const componentId =\n hole.pcb_component_id || `standalone_plated_hole_${getPrimaryId(hole)}`\n if (!componentMap.has(componentId)) {\n componentMap.set(componentId, {\n component_id: componentId,\n elements: [],\n bounds: { minX: 0, minY: 0, maxX: 0, maxY: 0 },\n })\n }\n componentMap.get(componentId)!.elements.push(hole)\n }\n\n // Holes typically don't have pcb_component_id, treat each as standalone\n for (const hole of holes) {\n const componentId = `standalone_hole_${getPrimaryId(hole)}`\n componentMap.set(componentId, {\n component_id: componentId,\n elements: [hole],\n bounds: { minX: 0, minY: 0, maxX: 0, maxY: 0 },\n })\n }\n\n // Compute bounds for each component\n for (const [componentId, componentData] of componentMap) {\n if (componentData.elements.length > 0) {\n componentData.bounds = getBoundsOfPcbElements(componentData.elements)\n }\n }\n\n // Convert map to array for pairwise iteration\n const componentsWithElements = Array.from(componentMap.values())\n\n // Pairwise check: only check elements if component bounds overlap\n for (let i = 0; i < componentsWithElements.length; i++) {\n for (let j = i + 1; j < componentsWithElements.length; j++) {\n const comp1 = componentsWithElements[i]\n const comp2 = componentsWithElements[j]\n\n // First check if component bounds overlap\n if (!doBoundsOverlap(comp1.bounds, comp2.bounds)) {\n continue\n }\n\n // Component bounds overlap, now check individual elements\n for (const elem1 of comp1.elements) {\n for (const elem2 of comp2.elements) {\n const id1 = getPrimaryId(elem1)\n const id2 = getPrimaryId(elem2)\n\n // Check if both are SMT pads and are electrically connected (same net) - if so, skip\n // This allows pads with the same subcircuit connectivity to be in contact\n if (\n elem1.type === \"pcb_smtpad\" &&\n elem2.type === \"pcb_smtpad\" &&\n connMap.areIdsConnected(id1, id2)\n ) {\n continue\n }\n\n // Check if element bounds overlap\n if (doPcbElementsOverlap(elem1, elem2)) {\n const elem1Description = formatOverlapElementDescription(\n circuitJson,\n elem1,\n )\n const elem2Description = formatOverlapElementDescription(\n circuitJson,\n elem2,\n )\n\n // Create error object\n const error: PcbFootprintOverlapError = {\n type: \"pcb_footprint_overlap_error\",\n pcb_error_id: `pcb_footprint_overlap_${id1}_${id2}`,\n error_type: \"pcb_footprint_overlap_error\",\n message: `${elem1.type} ${elem1Description} overlaps with ${elem2.type} ${elem2Description}`,\n }\n\n // Add relevant IDs based on element types\n if (elem1.type === \"pcb_smtpad\" || elem2.type === \"pcb_smtpad\") {\n error.pcb_smtpad_ids = []\n if (elem1.type === \"pcb_smtpad\") error.pcb_smtpad_ids.push(id1)\n if (elem2.type === \"pcb_smtpad\") error.pcb_smtpad_ids.push(id2)\n }\n\n if (\n elem1.type === \"pcb_plated_hole\" ||\n elem2.type === \"pcb_plated_hole\"\n ) {\n error.pcb_plated_hole_ids = []\n if (elem1.type === \"pcb_plated_hole\")\n error.pcb_plated_hole_ids.push(id1)\n if (elem2.type === \"pcb_plated_hole\")\n error.pcb_plated_hole_ids.push(id2)\n }\n\n if (elem1.type === \"pcb_hole\" || elem2.type === \"pcb_hole\") {\n error.pcb_hole_ids = []\n if (elem1.type === \"pcb_hole\") error.pcb_hole_ids.push(id1)\n if (elem2.type === \"pcb_hole\") error.pcb_hole_ids.push(id2)\n }\n\n errors.push(error)\n }\n }\n }\n }\n }\n\n return errors\n}\n","import { getBoundsOfPcbElements } from \"@tscircuit/circuit-json-util\"\nimport { doBoundsOverlap } from \"@tscircuit/math-utils\"\nimport type { PcbHole, PcbPlatedHole, PcbSmtPad } from \"circuit-json\"\nimport type { Collidable } from \"lib/check-each-pcb-trace-non-overlapping/getCollidableBounds\"\nimport { getLayersOfPcbElement } from \"lib/util/getLayersOfPcbElement\"\n\nexport type OverlappableElement = PcbSmtPad | PcbPlatedHole | PcbHole\n\nfunction getElementLayers(elem: OverlappableElement): string[] {\n return getLayersOfPcbElement(elem as Collidable)\n}\n\nfunction doLayersOverlap(layers1: string[], layers2: string[]): boolean {\n if (layers1.length === 0 || layers2.length === 0) return true\n return layers1.some((l) => layers2.includes(l))\n}\n\nexport function doPcbElementsOverlap(\n elem1: OverlappableElement,\n elem2: OverlappableElement,\n): boolean {\n // If the elements are on completely different layers they cannot overlap\n const layers1 = getElementLayers(elem1)\n const layers2 = getElementLayers(elem2)\n if (!doLayersOverlap(layers1, layers2)) return false\n\n const bounds1 = getBoundsOfPcbElements([elem1])\n const bounds2 = getBoundsOfPcbElements([elem2])\n return doBoundsOverlap(bounds1, bounds2)\n}\n","import {\n getPrimaryId,\n getReadableNameForElement,\n} from \"@tscircuit/circuit-json-util\"\nimport type { AnyCircuitElement, PcbPadPadClearanceError } from \"circuit-json\"\nimport {\n type ConnectivityMap,\n getFullConnectivityMapFromCircuitJson,\n} from \"circuit-json-to-connectivity-map\"\nimport { SpatialObjectIndex } from \"lib/data-structures/SpatialIndex\"\nimport { DEFAULT_PAD_PAD_CLEARANCE, EPSILON } from \"lib/drc-defaults\"\nimport { getLayersOfPcbElement } from \"lib/util/getLayersOfPcbElement\"\nimport {\n type PadElement,\n formatMm,\n getPadBounds,\n getPadCenter,\n getPadToPadGap,\n getPads,\n} from \"./check-pad-clearance/common\"\n\nexport function checkPadPadClearance(\n circuitJson: AnyCircuitElement[],\n {\n connMap,\n minClearance = DEFAULT_PAD_PAD_CLEARANCE,\n }: { connMap?: ConnectivityMap; minClearance?: number } = {},\n): PcbPadPadClearanceError[] {\n const pads = getPads(circuitJson)\n if (pads.length < 2) return []\n\n connMap ??= getFullConnectivityMapFromCircuitJson(circuitJson)\n const spatialIndex = new SpatialObjectIndex<PadElement>({\n objects: pads,\n getBounds: getPadBounds,\n getId: (pad) => getPrimaryId(pad),\n })\n\n const errors = new Map<string, PcbPadPadClearanceError>()\n\n for (const padA of pads) {\n const padAId = getPrimaryId(padA)\n const nearbyPads = spatialIndex.getObjectsInBounds(\n getPadBounds(padA),\n minClearance,\n )\n\n for (const padB of nearbyPads) {\n const padBId = getPrimaryId(padB)\n if (padAId === padBId) continue\n if (\n !getLayersOfPcbElement(padA as any).some((layer) =>\n getLayersOfPcbElement(padB as any).includes(layer),\n )\n ) {\n continue\n }\n if (connMap.areIdsConnected(padAId, padBId)) continue\n\n const pairId = [padAId, padBId].sort().join(\"_\")\n const gap = getPadToPadGap(padA, padB)\n if (gap + EPSILON >= minClearance!) continue\n const centerA = getPadCenter(padA)\n const centerB = getPadCenter(padB)\n\n const nextError: PcbPadPadClearanceError = {\n type: \"pcb_pad_pad_clearance_error\",\n pcb_pad_pad_clearance_error_id: `pad_pad_clearance_${pairId}`,\n error_type: \"pcb_pad_pad_clearance_error\",\n message: `Pads ${getReadableNameForElement(circuitJson, padAId)} and ${getReadableNameForElement(circuitJson, padBId)} are too close (clearance: ${formatMm(gap)}, minimum: ${formatMm(minClearance!)})`,\n pcb_pad_ids: [padAId, padBId] as [string, string],\n minimum_clearance: minClearance,\n actual_clearance: gap,\n center: {\n x: (centerA.x + centerB.x) / 2,\n y: (centerA.y + centerB.y) / 2,\n },\n }\n\n if (!errors.has(pairId)) {\n errors.set(pairId, nextError)\n }\n }\n }\n\n return Array.from(errors.values())\n}\n","import {\n cju,\n distanceBetweenCircleAndCircle,\n distanceBetweenCircleAndPolygon,\n distanceBetweenPolygonAndPolygon,\n getBoundsOfPcbElements,\n} from \"@tscircuit/circuit-json-util\"\nimport { midpoint } from \"@tscircuit/math-utils\"\nimport type {\n AnyCircuitElement,\n PcbPlatedHole,\n PcbSmtPad,\n PcbTrace,\n} from \"circuit-json\"\nimport type { PcbTraceSegment } from \"lib/check-each-pcb-trace-non-overlapping/getCollidableBounds\"\nimport type { Bounds } from \"lib/data-structures/SpatialIndex\"\nimport { DEFAULT_TRACE_THICKNESS } from \"lib/drc-defaults\"\n\nexport type PadElement = PcbSmtPad | PcbPlatedHole\n\nexport const formatMm = (value: number) => {\n const rounded = Math.round(value * 1000) / 1000\n return `${Number(rounded.toFixed(3))}mm`\n}\n\nexport const getPadBounds = (pad: PadElement): Bounds =>\n getBoundsOfPcbElements([pad])\n\nexport const getPadCenter = (pad: PadElement) => {\n const bounds = getPadBounds(pad)\n return midpoint(\n { x: bounds.minX, y: bounds.minY },\n { x: bounds.maxX, y: bounds.maxY },\n )\n}\n\nexport const getPadRadius = (pad: PadElement) => {\n const bounds = getPadBounds(pad)\n return Math.min(bounds.maxX - bounds.minX, bounds.maxY - bounds.minY) / 2\n}\n\nexport const isCircularPad = (pad: PadElement) => pad.shape === \"circle\"\n\nconst getCircleShape = (pad: PadElement) => {\n const center = getPadCenter(pad)\n return {\n kind: \"circle\" as const,\n x: center.x,\n y: center.y,\n radius: getPadRadius(pad),\n }\n}\n\nconst getPolygonShape = (pad: PadElement) => {\n const bounds = getPadBounds(pad)\n return {\n kind: \"polygon\" as const,\n points: [\n { x: bounds.minX, y: bounds.minY },\n { x: bounds.maxX, y: bounds.minY },\n { x: bounds.maxX, y: bounds.maxY },\n { x: bounds.minX, y: bounds.maxY },\n ],\n }\n}\n\nexport const getPadToPadGap = (padA: PadElement, padB: PadElement) => {\n if (isCircularPad(padA) && isCircularPad(padB)) {\n return distanceBetweenCircleAndCircle(\n getCircleShape(padA),\n getCircleShape(padB),\n )\n }\n\n if (isCircularPad(padA)) {\n return distanceBetweenCircleAndPolygon(\n getCircleShape(padA),\n getPolygonShape(padB),\n )\n }\n\n if (isCircularPad(padB)) {\n return distanceBetweenCircleAndPolygon(\n getCircleShape(padB),\n getPolygonShape(padA),\n )\n }\n\n return distanceBetweenPolygonAndPolygon(\n getPolygonShape(padA),\n getPolygonShape(padB),\n )\n}\n\nexport const getPads = (circuitJson: AnyCircuitElement[]) =>\n [\n ...cju(circuitJson).pcb_smtpad.list(),\n ...cju(circuitJson).pcb_plated_hole.list(),\n ] as PadElement[]\n\nexport const getTraceSegments = (\n circuitJson: AnyCircuitElement[],\n): PcbTraceSegment[] => {\n const pcbTraces = cju(circuitJson).pcb_trace.list()\n\n return pcbTraces.flatMap((pcbTrace) => {\n const segments: PcbTraceSegment[] = []\n\n for (let i = 0; i < pcbTrace.route.length - 1; i++) {\n const p1 = pcbTrace.route[i]\n const p2 = pcbTrace.route[i + 1]\n if (p1.route_type !== \"wire\") continue\n if (p2.route_type !== \"wire\") continue\n if (p1.layer !== p2.layer) continue\n\n segments.push({\n type: \"pcb_trace_segment\",\n pcb_trace_id: pcbTrace.pcb_trace_id,\n _pcbTrace: pcbTrace as PcbTrace,\n thickness:\n \"width\" in p1\n ? p1.width\n : \"width\" in p2\n ? p2.width\n : DEFAULT_TRACE_THICKNESS!,\n layer: p1.layer,\n x1: p1.x,\n y1: p1.y,\n x2: p2.x,\n y2: p2.y,\n })\n }\n\n return segments\n })\n}\n","import {\n getPrimaryId,\n getReadableNameForElement,\n} from \"@tscircuit/circuit-json-util\"\nimport {\n segmentToBoundsMinDistance,\n segmentToCircleMinDistance,\n} from \"@tscircuit/math-utils\"\nimport type { AnyCircuitElement, PcbPadTraceClearanceError } from \"circuit-json\"\nimport {\n type ConnectivityMap,\n getFullConnectivityMapFromCircuitJson,\n} from \"circuit-json-to-connectivity-map\"\nimport { getCollidableBounds } from \"lib/check-each-pcb-trace-non-overlapping/getCollidableBounds\"\nimport { SpatialObjectIndex } from \"lib/data-structures/SpatialIndex\"\nimport { DEFAULT_PAD_TRACE_CLEARANCE, EPSILON } from \"lib/drc-defaults\"\nimport { getLayersOfPcbElement } from \"lib/util/getLayersOfPcbElement\"\nimport {\n type PadElement,\n formatMm,\n getPadBounds,\n getPadCenter,\n getPadRadius,\n getPads,\n getTraceSegments,\n isCircularPad,\n} from \"./check-pad-clearance/common\"\n\nexport function checkPadTraceClearance(\n circuitJson: AnyCircuitElement[],\n {\n connMap,\n minClearance = DEFAULT_PAD_TRACE_CLEARANCE,\n }: { connMap?: ConnectivityMap; minClearance?: number } = {},\n): PcbPadTraceClearanceError[] {\n const pads = getPads(circuitJson)\n const segments = getTraceSegments(circuitJson)\n if (pads.length === 0 || segments.length === 0) return []\n\n connMap ??= getFullConnectivityMapFromCircuitJson(circuitJson)\n const spatialIndex = new SpatialObjectIndex<PadElement>({\n objects: pads,\n getBounds: getPadBounds,\n getId: (pad) => getPrimaryId(pad),\n })\n\n const errors = new Map<\n string,\n { error: PcbPadTraceClearanceError; gap: number }\n >()\n\n for (const segment of segments) {\n const nearbyPads = spatialIndex.getObjectsInBounds(\n getCollidableBounds(segment),\n minClearance! + segment.thickness / 2,\n )\n\n for (const pad of nearbyPads) {\n const padId = getPrimaryId(pad)\n if (!getLayersOfPcbElement(pad as any).includes(segment.layer)) continue\n if (connMap.areIdsConnected(segment.pcb_trace_id, padId)) continue\n const center = getPadCenter(pad)\n\n const gap = isCircularPad(pad)\n ? segmentToCircleMinDistance(\n { x: segment.x1, y: segment.y1 },\n { x: segment.x2, y: segment.y2 },\n { x: center.x, y: center.y, radius: getPadRadius(pad) },\n ) -\n segment.thickness / 2\n : segmentToBoundsMinDistance(\n { x: segment.x1, y: segment.y1 },\n { x: segment.x2, y: segment.y2 },\n getPadBounds(pad),\n ) -\n segment.thickness / 2\n if (gap + EPSILON >= minClearance!) continue\n\n const pairId = `${padId}_${segment.pcb_trace_id}`\n const nextError: PcbPadTraceClearanceError = {\n type: \"pcb_pad_trace_clearance_error\" as const,\n pcb_pad_trace_clearance_error_id: `pad_trace_clearance_${pairId}`,\n error_type: \"pcb_pad_trace_clearance_error\",\n message: `Pad ${getReadableNameForElement(circuitJson, padId)} and trace ${getReadableNameForElement(circuitJson, segment.pcb_trace_id)} are too close (clearance: ${formatMm(gap)}, minimum: ${formatMm(minClearance!)})`,\n pcb_pad_id: padId,\n pcb_trace_id: segment.pcb_trace_id,\n minimum_clearance: minClearance,\n actual_clearance: gap,\n center: {\n x: (center.x + (segment.x1 + segment.x2) / 2) / 2,\n y: (center.y + (segment.y1 + segment.y2) / 2) / 2,\n },\n }\n\n const current = errors.get(pairId)\n if (!current || gap < current.gap) {\n errors.set(pairId, { error: nextError, gap })\n }\n }\n }\n\n return Array.from(errors.values()).map(({ error }) => error)\n}\n","import { getReadableNameForElement } from \"@tscircuit/circuit-json-util\"\nimport { segmentToCircleMinDistance } from \"@tscircuit/math-utils\"\nimport type {\n AnyCircuitElement,\n PcbVia,\n PcbViaTraceClearanceError,\n} from \"circuit-json\"\nimport {\n type ConnectivityMap,\n getFullConnectivityMapFromCircuitJson,\n} from \"circuit-json-to-connectivity-map\"\nimport { DEFAULT_PAD_TRACE_CLEARANCE, EPSILON } from \"lib/drc-defaults\"\nimport { getLayersOfPcbElement } from \"lib/util/getLayersOfPcbElement\"\nimport { formatMm, getTraceSegments } from \"./check-pad-clearance/common\"\n\nconst getClosestPointOnSegment = (\n point: { x: number; y: number },\n segmentStart: { x: number; y: number },\n segmentEnd: { x: number; y: number },\n) => {\n const dx = segmentEnd.x - segmentStart.x\n const dy = segmentEnd.y - segmentStart.y\n const lengthSquared = dx * dx + dy * dy\n\n if (lengthSquared === 0) return segmentStart\n\n const t = Math.max(\n 0,\n Math.min(\n 1,\n ((point.x - segmentStart.x) * dx + (point.y - segmentStart.y) * dy) /\n lengthSquared,\n ),\n )\n\n return {\n x: segmentStart.x + t * dx,\n y: segmentStart.y + t * dy,\n }\n}\n\nexport function checkViaTraceClearance(\n circuitJson: AnyCircuitElement[],\n {\n connMap,\n minClearance = DEFAULT_PAD_TRACE_CLEARANCE,\n }: { connMap?: ConnectivityMap; minClearance?: number } = {},\n): PcbViaTraceClearanceError[] {\n const vias = circuitJson.filter((el) => el.type === \"pcb_via\") as PcbVia[]\n const segments = getTraceSegments(circuitJson)\n if (vias.length === 0 || segments.length === 0) return []\n\n connMap ??= getFullConnectivityMapFromCircuitJson(circuitJson)\n const errors = new Map<\n string,\n { error: PcbViaTraceClearanceError; gap: number }\n >()\n\n for (const via of vias) {\n const viaRadius = via.outer_diameter / 2\n for (const segment of segments) {\n if (!getLayersOfPcbElement(via).includes(segment.layer)) continue\n if (connMap.areIdsConnected(segment.pcb_trace_id, via.pcb_via_id))\n continue\n\n const gap =\n segmentToCircleMinDistance(\n { x: segment.x1, y: segment.y1 },\n { x: segment.x2, y: segment.y2 },\n {\n x: via.x,\n y: via.y,\n radius: viaRadius,\n },\n ) -\n segment.thickness / 2\n if (gap + EPSILON >= minClearance!) continue\n\n const closestPoint = getClosestPointOnSegment(\n { x: via.x, y: via.y },\n { x: segment.x1, y: segment.y1 },\n { x: segment.x2, y: segment.y2 },\n )\n\n const pairId = `${via.pcb_via_id}_${segment.pcb_trace_id}`\n const nextError: PcbViaTraceClearanceError = {\n type: \"pcb_via_trace_clearance_error\",\n pcb_via_trace_clearance_error_id: `via_trace_clearance_${pairId}`,\n error_type: \"pcb_via_trace_clearance_error\",\n message: `Via ${getReadableNameForElement(circuitJson, via.pcb_via_id)} and trace ${getReadableNameForElement(circuitJson, segment.pcb_trace_id)} are too close (clearance: ${formatMm(gap)}, minimum: ${formatMm(minClearance!)})`,\n pcb_via_id: via.pcb_via_id,\n pcb_trace_id: segment.pcb_trace_id,\n minimum_clearance: minClearance,\n actual_clearance: gap,\n center: {\n x: (via.x + closestPoint.x) / 2,\n y: (via.y + closestPoint.y) / 2,\n },\n }\n\n const current = errors.get(pairId)\n if (!current || gap < current.gap) {\n errors.set(pairId, { error: nextError, gap })\n }\n }\n }\n\n return Array.from(errors.values()).map(({ error }) => error)\n}\n","import type { AnyCircuitElement } from \"circuit-json\"\n\ntype SourceComponent = Extract<\n AnyCircuitElement,\n { source_component_id: string; name: string }\n>\n\ntype SourcePort = Extract<AnyCircuitElement, { type: \"source_port\" }>\ntype SourceTrace = Extract<AnyCircuitElement, { type: \"source_trace\" }>\n\ntype SourcePinMustBeConnectedError = {\n type: \"source_pin_must_be_connected_error\"\n source_pin_must_be_connected_error_id: string\n error_type: \"source_pin_must_be_connected_error\"\n message: string\n source_component_id: string\n source_port_id: string\n subcircuit_id?: string\n}\n\n/**\n * Check that each source port with must_be_connected attribute is actually\n * connected to a trace. Returns errors for any pins that are marked as\n * must_be_connected but are floating (not connected to any trace).\n */\nexport function checkPinMustBeConnected(\n circuitJson: AnyCircuitElement[],\n): SourcePinMustBeConnectedError[] {\n const errors: SourcePinMustBeConnectedError[] = []\n\n // Get all source components, ports, and traces\n const sourceComponents = circuitJson.filter(\n (el): el is SourceComponent =>\n \"source_component_id\" in el &&\n (el.type === \"source_component\" || el.type.startsWith(\"source_simple_\")),\n )\n const sourcePorts = circuitJson.filter(\n (el): el is SourcePort => el.type === \"source_port\",\n )\n const sourceTraces = circuitJson.filter(\n (el): el is SourceTrace => el.type === \"source_trace\",\n )\n\n // Build a set of connected source port IDs\n const connectedPortIds = new Set<string>()\n for (const trace of sourceTraces) {\n for (const portId of trace.connected_source_port_ids ?? []) {\n connectedPortIds.add(portId)\n }\n }\n\n // Build a map of internally connected port groups for each component\n const componentInternalConnections = new Map<string, string[][]>()\n for (const component of sourceComponents) {\n if (\n \"internally_connected_source_port_ids\" in component &&\n component.internally_connected_source_port_ids\n ) {\n componentInternalConnections.set(\n component.source_component_id,\n component.internally_connected_source_port_ids,\n )\n }\n }\n\n // For each internal group, if any port is connected, mark all ports in the group as connected\n for (const internalGroups of componentInternalConnections.values()) {\n for (const group of internalGroups) {\n if (group.some((portId) => connectedPortIds.has(portId))) {\n for (const portId of group) {\n connectedPortIds.add(portId)\n }\n }\n }\n }\n\n // Check each port for must_be_connected attribute\n for (const port of sourcePorts) {\n if (port.must_be_connected === true) {\n if (!connectedPortIds.has(port.source_port_id)) {\n const component = sourceComponents.find(\n (c) => c.source_component_id === port.source_component_id,\n )\n const componentName = component?.name ?? \"Unknown\"\n\n errors.push({\n type: \"source_pin_must_be_connected_error\",\n source_pin_must_be_connected_error_id: `source_pin_must_be_connected_error_${port.source_port_id}`,\n error_type: \"source_pin_must_be_connected_error\",\n message: `Port ${port.name} on ${componentName} must be connected but is floating`,\n source_component_id: port.source_component_id ?? \"\",\n source_port_id: port.source_port_id,\n subcircuit_id: port.subcircuit_id,\n })\n }\n }\n }\n\n return errors\n}\n","import { cju } from \"@tscircuit/circuit-json-util\"\nimport type {\n AnyCircuitElement,\n SourceComponentPinsUnderspecifiedWarning,\n SourcePinAttributes,\n SourcePort,\n SourceComponentBase,\n} from \"circuit-json\"\n\nconst PIN_ATTRIBUTE_KEYS = [\n \"must_be_connected\",\n \"provides_power\",\n \"requires_power\",\n \"provides_ground\",\n \"requires_ground\",\n \"provides_voltage\",\n \"requires_voltage\",\n \"do_not_connect\",\n \"include_in_board_pinout\",\n \"can_use_internal_pullup\",\n \"is_using_internal_pullup\",\n \"needs_external_pullup\",\n \"can_use_internal_pulldown\",\n \"is_using_internal_pulldown\",\n \"needs_external_pulldown\",\n \"can_use_open_drain\",\n \"is_using_open_drain\",\n \"can_use_push_pull\",\n \"is_using_push_pull\",\n \"should_have_decoupling_capacitor\",\n \"recommended_decoupling_capacitor_capacitance\",\n \"is_configured_for_i2c_sda\",\n \"is_configured_for_i2c_scl\",\n \"is_configured_for_spi_mosi\",\n \"is_configured_for_spi_miso\",\n \"is_configured_for_spi_sck\",\n \"is_configured_for_spi_cs\",\n \"is_configured_for_uart_tx\",\n \"is_configured_for_uart_rx\",\n \"supports_i2c_sda\",\n \"supports_i2c_scl\",\n \"supports_spi_mosi\",\n \"supports_spi_miso\",\n \"supports_spi_sck\",\n \"supports_spi_cs\",\n \"supports_uart_tx\",\n \"supports_uart_rx\",\n] as const satisfies readonly (keyof SourcePinAttributes)[]\n\nfunction hasAnyPinAttribute(port: SourcePort): boolean {\n return PIN_ATTRIBUTE_KEYS.some((key) => port[key] !== undefined)\n}\n\n/**\n * Check that each component with ports has at least one pin attribute\n * specified across its ports. Returns a warning when all pins are\n * underspecified (no SourcePinAttributes fields set on any port).\n */\nexport function checkAllPinsInComponentAreUnderspecified(\n circuitJson: AnyCircuitElement[],\n): SourceComponentPinsUnderspecifiedWarning[] {\n const warnings: SourceComponentPinsUnderspecifiedWarning[] = []\n const db = cju(circuitJson)\n\n const sourceComponents = db.source_component.list() as SourceComponentBase[]\n const sourcePorts = db.source_port.list() as SourcePort[]\n\n const portsByComponent = new Map<string, SourcePort[]>()\n for (const port of sourcePorts) {\n if (!port.source_component_id) continue\n const existing = portsByComponent.get(port.source_component_id) ?? []\n existing.push(port)\n portsByComponent.set(port.source_component_id, existing)\n }\n\n for (const component of sourceComponents) {\n if (component.ftype !== \"simple_chip\") continue\n\n const componentPorts =\n portsByComponent.get(component.source_component_id) ?? []\n if (componentPorts.length === 0) continue\n\n const hasAnySpecifiedAttributes = componentPorts.some((port) =>\n hasAnyPinAttribute(port),\n )\n\n if (hasAnySpecifiedAttributes) continue\n\n warnings.push({\n type: \"source_component_pins_underspecified_warning\",\n source_component_pins_underspecified_warning_id: `source_component_pins_underspecified_warning_${component.source_component_id}`,\n warning_type: \"source_component_pins_underspecified_warning\",\n message: `All pins on ${component.name} are underspecified (no pinAttributes set)`,\n source_component_id: component.source_component_id,\n source_port_ids: componentPorts.map((port) => port.source_port_id),\n subcircuit_id: componentPorts[0]?.subcircuit_id,\n })\n }\n\n return warnings\n}\n","import { cju } from \"@tscircuit/circuit-json-util\"\nimport type {\n AnyCircuitElement,\n SourceComponentBase,\n SourceNoPowerPinDefinedWarning,\n SourcePort,\n} from \"circuit-json\"\n\n/**\n * Check that each chip has at least one pin marked as requires_power=true.\n * Returns warnings for chips where no pin declares requires_power.\n */\nexport function checkNoPowerPinDefined(\n circuitJson: AnyCircuitElement[],\n): SourceNoPowerPinDefinedWarning[] {\n const warnings: SourceNoPowerPinDefinedWarning[] = []\n const db = cju(circuitJson)\n\n const sourceComponents = db.source_component.list() as SourceComponentBase[]\n const sourcePorts = db.source_port.list() as SourcePort[]\n\n const portsByComponent = new Map<string, SourcePort[]>()\n for (const port of sourcePorts) {\n if (!port.source_component_id) continue\n const existing = portsByComponent.get(port.source_component_id) ?? []\n existing.push(port)\n portsByComponent.set(port.source_component_id, existing)\n }\n\n for (const component of sourceComponents) {\n if (component.ftype !== \"simple_chip\") continue\n\n const componentPorts =\n portsByComponent.get(component.source_component_id) ?? []\n if (componentPorts.length === 0) continue\n\n const hasRequiredPowerPin = componentPorts.some(\n (port) => port.requires_power === true,\n )\n if (hasRequiredPowerPin) continue\n\n warnings.push({\n type: \"source_no_power_pin_defined_warning\",\n source_no_power_pin_defined_warning_id: `source_no_power_pin_defined_warning_${component.source_component_id}`,\n warning_type: \"source_no_power_pin_defined_warning\",\n message: `${component.name} has no pin with requires_power=true`,\n source_component_id: component.source_component_id,\n source_port_ids: componentPorts.map((port) => port.source_port_id),\n subcircuit_id: componentPorts[0]?.subcircuit_id,\n })\n }\n\n return warnings\n}\n","import { cju } from \"@tscircuit/circuit-json-util\"\nimport type {\n AnyCircuitElement,\n SourceComponentBase,\n SourceNoGroundPinDefinedWarning,\n SourcePort,\n} from \"circuit-json\"\n\n/**\n * Check that each chip has at least one pin marked as requires_ground=true.\n * Returns warnings for chips where no pin declares requires_ground.\n */\nexport function checkNoGroundPinDefined(\n circuitJson: AnyCircuitElement[],\n): SourceNoGroundPinDefinedWarning[] {\n const warnings: SourceNoGroundPinDefinedWarning[] = []\n const db = cju(circuitJson)\n\n const sourceComponents = db.source_component.list() as SourceComponentBase[]\n const sourcePorts = db.source_port.list() as SourcePort[]\n\n const portsByComponent = new Map<string, SourcePort[]>()\n for (const port of sourcePorts) {\n if (!port.source_component_id) continue\n const existing = portsByComponent.get(port.source_component_id) ?? []\n existing.push(port)\n portsByComponent.set(port.source_component_id, existing)\n }\n\n for (const component of sourceComponents) {\n if (component.ftype !== \"simple_chip\") continue\n\n const componentPorts =\n portsByComponent.get(component.source_component_id) ?? []\n if (componentPorts.length === 0) continue\n\n const hasRequiredGroundPin = componentPorts.some(\n (port) => port.requires_ground === true,\n )\n if (hasRequiredGroundPin) continue\n\n warnings.push({\n type: \"source_no_ground_pin_defined_warning\",\n source_no_ground_pin_defined_warning_id: `source_no_ground_pin_defined_warning_${component.source_component_id}`,\n warning_type: \"source_no_ground_pin_defined_warning\",\n message: `${component.name} has no pin with requires_ground=true`,\n source_component_id: component.source_component_id,\n source_port_ids: componentPorts.map((port) => port.source_port_id),\n subcircuit_id: componentPorts[0]?.subcircuit_id,\n })\n }\n\n return warnings\n}\n","import { getBoardBounds } from \"@tscircuit/circuit-json-util\"\nimport type {\n AnyCircuitElement,\n PcbBoard,\n PcbComponent,\n PcbConnectorNotInAccessibleOrientationWarning,\n} from \"circuit-json\"\nimport { getReadableNameForComponent } from \"./util/get-readable-names\"\n\ntype FacingDirection = \"x-\" | \"x+\" | \"y+\" | \"y-\"\n\nfunction getFacingDirection(component: PcbComponent): FacingDirection | null {\n if (!component.center || !component.cable_insertion_center) return null\n\n const dx = component.cable_insertion_center.x - component.center.x\n const dy = component.cable_insertion_center.y - component.center.y\n\n if (Math.abs(dx) < 1e-6 && Math.abs(dy) < 1e-6) return null\n\n if (Math.abs(dx) >= Math.abs(dy)) {\n return dx >= 0 ? \"x+\" : \"x-\"\n }\n\n return dy >= 0 ? \"y+\" : \"y-\"\n}\n\nfunction getRecommendedFacingDirection(\n component: PcbComponent,\n bounds: { minX: number; maxX: number; minY: number; maxY: number },\n): FacingDirection | null {\n if (!component.center) return null\n\n const distances = [\n { direction: \"x-\" as const, distance: component.center.x - bounds.minX },\n { direction: \"x+\" as const, distance: bounds.maxX - component.center.x },\n { direction: \"y-\" as const, distance: component.center.y - bounds.minY },\n { direction: \"y+\" as const, distance: bounds.maxY - component.center.y },\n ]\n\n distances.sort((a, b) => a.distance - b.distance)\n return distances[0]?.direction ?? null\n}\n\nexport function checkConnectorAccessibleOrientation(\n circuitJson: AnyCircuitElement[],\n): PcbConnectorNotInAccessibleOrientationWarning[] {\n const board = circuitJson.find(\n (el): el is PcbBoard => el.type === \"pcb_board\",\n )\n if (!board) return []\n\n const bounds = (() => {\n try {\n return getBoardBounds(board)\n } catch {\n return null\n }\n })()\n if (!bounds) return []\n\n const warnings: PcbConnectorNotInAccessibleOrientationWarning[] = []\n\n const components = circuitJson.filter(\n (el): el is PcbComponent => el.type === \"pcb_component\",\n )\n\n for (const component of components) {\n if (!component.cable_insertion_center) continue\n\n const facingDirection = getFacingDirection(component)\n const recommendedFacingDirection = getRecommendedFacingDirection(\n component,\n bounds,\n )\n\n if (!facingDirection || !recommendedFacingDirection) continue\n if (facingDirection === recommendedFacingDirection) continue\n\n const componentName = getReadableNameForComponent(\n circuitJson,\n component.pcb_component_id,\n )\n\n warnings.push({\n type: \"pcb_connector_not_in_accessible_orientation_warning\",\n warning_type: \"pcb_connector_not_in_accessible_orientation_warning\",\n pcb_connector_not_in_accessible_orientation_warning_id: `pcb_connector_not_in_accessible_orientation_warning_${component.pcb_component_id}`,\n message: `${componentName} is facing ${facingDirection} but should face ${recommendedFacingDirection} so the connector is accessible from the board edge`,\n pcb_component_id: component.pcb_component_id,\n source_component_id: component.source_component_id,\n pcb_board_id: board.pcb_board_id,\n facing_direction: facingDirection,\n recommended_facing_direction: recommendedFacingDirection,\n subcircuit_id: component.subcircuit_id,\n })\n }\n\n return warnings\n}\n","import {\n doSegmentsIntersect,\n isPointInsidePolygon,\n} from \"@tscircuit/math-utils\"\nimport type {\n AnyCircuitElement,\n PcbCourtyardOverlapError,\n PcbCourtyardRect,\n PcbCourtyardCircle,\n PcbCourtyardOutline,\n} from \"circuit-json\"\n\ntype CourtyardElement =\n | PcbCourtyardRect\n | PcbCourtyardCircle\n | PcbCourtyardOutline\n\nfunction getCourtyardPolygon(el: CourtyardElement): { x: number; y: number }[] {\n if (el.type === \"pcb_courtyard_rect\") {\n const hw = el.width / 2\n const hh = el.height / 2\n const corners = [\n { x: -hw, y: -hh },\n { x: +hw, y: -hh },\n { x: +hw, y: +hh },\n { x: -hw, y: +hh },\n ]\n const angle = ((el.ccw_rotation ?? 0) * Math.PI) / 180\n const cos = Math.cos(angle)\n const sin = Math.sin(angle)\n return corners.map(({ x, y }) => ({\n x: el.center.x + x * cos - y * sin,\n y: el.center.y + x * sin + y * cos,\n }))\n }\n if (el.type === \"pcb_courtyard_circle\") {\n const N = 32\n return Array.from({ length: N }, (_, i) => {\n const a = (2 * Math.PI * i) / N\n return {\n x: el.center.x + el.radius * Math.cos(a),\n y: el.center.y + el.radius * Math.sin(a),\n }\n })\n }\n return el.outline\n}\n\nfunction getComponentName(\n circuitJson: AnyCircuitElement[],\n pcbComponentId: string,\n): string {\n const pcbComponent = circuitJson.find(\n (el) =>\n el.type === \"pcb_component\" && el.pcb_component_id === pcbComponentId,\n )\n if (pcbComponent?.type !== \"pcb_component\") return pcbComponentId\n const sourceComponent = circuitJson.find(\n (el) =>\n el.type === \"source_component\" &&\n el.source_component_id === pcbComponent.source_component_id,\n )\n if (sourceComponent?.type === \"source_component\" && sourceComponent.name) {\n return sourceComponent.name\n }\n return pcbComponentId\n}\n\ntype Point = { x: number; y: number }\n\nfunction polygonsOverlap(polyA: Point[], polyB: Point[]): boolean {\n // Check if any vertex of A is inside B or vice versa\n if (polyA.some((p) => isPointInsidePolygon(p, polyB))) return true\n if (polyB.some((p) => isPointInsidePolygon(p, polyA))) return true\n // Check if any edge of A intersects any edge of B\n for (let i = 0; i < polyA.length; i++) {\n const a1 = polyA[i]\n const a2 = polyA[(i + 1) % polyA.length]\n for (let j = 0; j < polyB.length; j++) {\n const b1 = polyB[j]\n const b2 = polyB[(j + 1) % polyB.length]\n if (doSegmentsIntersect(a1, a2, b1, b2)) return true\n }\n }\n return false\n}\n\n/**\n * Check for overlapping PCB component courtyards.\n * Returns one error per pair of components whose courtyard elements overlap.\n */\nexport function checkCourtyardOverlap(\n circuitJson: AnyCircuitElement[],\n): PcbCourtyardOverlapError[] {\n const courtyards = circuitJson.filter(\n (el): el is CourtyardElement =>\n el.type === \"pcb_courtyard_rect\" ||\n el.type === \"pcb_courtyard_circle\" ||\n el.type === \"pcb_courtyard_outline\",\n )\n\n // Group by component\n const byComponent = new Map<string, CourtyardElement[]>()\n for (const el of courtyards) {\n const id = el.pcb_component_id\n if (!byComponent.has(id)) byComponent.set(id, [])\n byComponent.get(id)!.push(el)\n }\n\n const componentIds = Array.from(byComponent.keys())\n const errors: PcbCourtyardOverlapError[] = []\n\n for (let i = 0; i < componentIds.length; i++) {\n for (let j = i + 1; j < componentIds.length; j++) {\n const idA = componentIds[i]\n const idB = componentIds[j]\n\n let overlapping = false\n outer: for (const a of byComponent.get(idA)!) {\n for (const b of byComponent.get(idB)!) {\n // Skip overlap check if courtyards are on different layers\n if (\"layer\" in a && \"layer\" in b && a.layer !== b.layer) {\n continue\n }\n const polyA = getCourtyardPolygon(a)\n const polyB = getCourtyardPolygon(b)\n if (polygonsOverlap(polyA, polyB)) {\n overlapping = true\n break outer\n }\n }\n }\n\n if (overlapping) {\n errors.push({\n type: \"pcb_courtyard_overlap_error\",\n pcb_error_id: `pcb_courtyard_overlap_${idA}_${idB}`,\n error_type: \"pcb_courtyard_overlap_error\",\n message: `Courtyard of ${getComponentName(circuitJson, idA)} overlaps with courtyard of ${getComponentName(circuitJson, idB)}`,\n pcb_component_ids: [idA, idB],\n })\n }\n }\n }\n\n return errors\n}\n","import type {\n AnyCircuitElement,\n PcbTraceError,\n PcbPort,\n PcbTrace,\n SourceTrace,\n PcbSmtPad,\n PcbPlatedHole,\n} from \"circuit-json\"\nimport { isPointInPad } from \"./is-point-in-pad\"\nimport { getReadableNameForPcbPort } from \"@tscircuit/circuit-json-util\"\n\nfunction checkTracesAreContiguous(\n circuitJson: AnyCircuitElement[],\n): PcbTraceError[] {\n const errors: PcbTraceError[] = []\n\n const pcbPorts = circuitJson.filter(\n (el) => el.type === \"pcb_port\",\n ) as PcbPort[]\n const pcbTraces = circuitJson.filter(\n (el) => el.type === \"pcb_trace\",\n ) as PcbTrace[]\n const sourceTraces = circuitJson.filter(\n (el) => el.type === \"source_trace\",\n ) as SourceTrace[]\n const pcbSmtPads = circuitJson.filter(\n (el) => el.type === \"pcb_smtpad\",\n ) as PcbSmtPad[]\n const pcbPlatedHoles = circuitJson.filter(\n (el) => el.type === \"pcb_plated_hole\",\n ) as PcbPlatedHole[]\n\n const padMap = new Map<string, PcbSmtPad | PcbPlatedHole>()\n\n for (const pad of pcbSmtPads) {\n if (pad.pcb_port_id) {\n padMap.set(pad.pcb_port_id, pad)\n }\n }\n\n for (const hole of pcbPlatedHoles) {\n if (hole.pcb_port_id) {\n padMap.set(hole.pcb_port_id, hole)\n }\n }\n\n for (const trace of pcbTraces) {\n if (trace.route.length === 0) continue\n\n const firstPoint = trace.route[0]\n const lastPoint = trace.route[trace.route.length - 1]\n\n const sourceTrace = sourceTraces.find(\n (st) => st.source_trace_id === trace.source_trace_id,\n )\n\n const expectedPorts = sourceTrace\n ? pcbPorts.filter((port) =>\n sourceTrace.connected_source_port_ids?.includes(port.source_port_id),\n )\n : []\n\n for (let i = 1; i < trace.route.length - 1; i++) {\n const prevPoint = trace.route[i - 1]\n const currentPoint = trace.route[i]\n const nextPoint = trace.route[i + 1]\n\n if (currentPoint.route_type === \"via\") {\n const prevIsWire = prevPoint.route_type === \"wire\"\n const nextIsWire = nextPoint.route_type === \"wire\"\n\n if (prevIsWire && nextIsWire) {\n const prevAligned =\n Math.abs(prevPoint.x - currentPoint.x) < 0.01 &&\n Math.abs(prevPoint.y - currentPoint.y) < 0.01\n\n const nextAligned =\n Math.abs(nextPoint.x - currentPoint.x) < 0.01 &&\n Math.abs(nextPoint.y - currentPoint.y) < 0.01\n\n if (!prevAligned || !nextAligned) {\n const traceName =\n sourceTrace?.display_name || trace.source_trace_id || \"unknown\"\n errors.push({\n type: \"pcb_trace_error\",\n message: `Via in trace [${traceName}] is misaligned at position {x: ${currentPoint.x}, y: ${currentPoint.y}}.`,\n source_trace_id:\n sourceTrace?.source_trace_id ||\n trace.source_trace_id ||\n `!${trace.pcb_trace_id}`,\n error_type: \"pcb_trace_error\",\n pcb_trace_id: trace.pcb_trace_id,\n pcb_trace_error_id: \"\",\n pcb_component_ids: [],\n pcb_port_ids: [],\n })\n }\n }\n }\n }\n\n const traceName =\n sourceTrace?.display_name || trace.source_trace_id || \"unknown\"\n\n // For traces with known expected ports, check specific connections\n for (const port of expectedPorts) {\n if (!port.pcb_port_id) continue\n\n const pad = padMap.get(port.pcb_port_id)\n\n if (!pad) continue\n\n const isFirstPointConnected =\n firstPoint.route_type === \"wire\" &&\n isPointInPad({ x: firstPoint.x, y: firstPoint.y }, pad)\n\n const isLastPointConnected =\n lastPoint.route_type === \"wire\" &&\n isPointInPad({ x: lastPoint.x, y: lastPoint.y }, pad)\n\n if (!isFirstPointConnected && !isLastPointConnected) {\n const portName = getReadableNameForPcbPort(\n circuitJson,\n port.pcb_port_id,\n ).replace(\"pcb_port\", \"\")\n const padType = pad.type.replace(/pcb_/, \"\")\n // Use the midpoint between trace endpoints as error location\n const errorCenter = {\n x: (firstPoint.x + lastPoint.x) / 2,\n y: (firstPoint.y + lastPoint.y) / 2,\n }\n errors.push({\n type: \"pcb_trace_error\",\n message: `Trace [${traceName}] is missing a connection to ${padType}${portName}`,\n source_trace_id:\n sourceTrace?.source_trace_id ||\n trace.source_trace_id ||\n `!${trace.pcb_trace_id}`,\n error_type: \"pcb_trace_error\",\n pcb_trace_id: trace.pcb_trace_id,\n pcb_trace_error_id: \"\",\n center: errorCenter,\n pcb_component_ids: [],\n pcb_port_ids: [port.pcb_port_id],\n })\n }\n }\n\n // For net-level traces (no expected ports), check if endpoints are floating\n if (expectedPorts.length === 0) {\n let firstConnectsToAnyPad = false\n let lastConnectsToAnyPad = false\n\n for (const [portId, pad] of padMap) {\n if (\n firstPoint.route_type === \"wire\" &&\n isPointInPad({ x: firstPoint.x, y: firstPoint.y }, pad)\n ) {\n firstConnectsToAnyPad = true\n }\n if (\n lastPoint.route_type === \"wire\" &&\n isPointInPad({ x: lastPoint.x, y: lastPoint.y }, pad)\n ) {\n lastConnectsToAnyPad = true\n }\n }\n\n if (!firstConnectsToAnyPad && firstPoint.route_type === \"wire\") {\n errors.push({\n type: \"pcb_trace_error\",\n message: `Trace [${traceName}] has disconnected endpoint at (${firstPoint.x.toFixed(2)}, ${firstPoint.y.toFixed(2)})`,\n source_trace_id:\n sourceTrace?.source_trace_id ||\n trace.source_trace_id ||\n `!${trace.pcb_trace_id}`,\n error_type: \"pcb_trace_error\",\n pcb_trace_id: trace.pcb_trace_id,\n pcb_trace_error_id: \"\",\n center: { x: firstPoint.x, y: firstPoint.y },\n pcb_component_ids: [],\n pcb_port_ids: [],\n })\n }\n if (!lastConnectsToAnyPad && lastPoint.route_type === \"wire\") {\n errors.push({\n type: \"pcb_trace_error\",\n message: `Trace [${traceName}] has disconnected endpoint at (${lastPoint.x.toFixed(2)}, ${lastPoint.y.toFixed(2)})`,\n source_trace_id:\n sourceTrace?.source_trace_id ||\n trace.source_trace_id ||\n `!${trace.pcb_trace_id}`,\n error_type: \"pcb_trace_error\",\n pcb_trace_id: trace.pcb_trace_id,\n pcb_trace_error_id: \"\",\n center: { x: lastPoint.x, y: lastPoint.y },\n pcb_component_ids: [],\n pcb_port_ids: [],\n })\n }\n }\n }\n\n return errors\n}\n\nexport { checkTracesAreContiguous }\n","import type { AnyCircuitElement } from \"circuit-json\"\nimport { checkAllPinsInComponentAreUnderspecified } from \"./check-all-pins-in-component-are-underspecified\"\nimport { checkConnectorAccessibleOrientation } from \"./check-connector-accessible-orientation\"\nimport { checkCourtyardOverlap } from \"./check-courtyard-overlap/checkCourtyardOverlap\"\nimport { checkDifferentNetViaSpacing } from \"./check-different-net-via-spacing\"\nimport { checkEachPcbPortConnectedToPcbTraces } from \"./check-each-pcb-port-connected-to-pcb-trace\"\nimport { checkEachPcbTraceNonOverlapping } from \"./check-each-pcb-trace-non-overlapping/check-each-pcb-trace-non-overlapping\"\nimport { checkNoGroundPinDefined } from \"./check-no-ground-pin-defined\"\nimport { checkNoPowerPinDefined } from \"./check-no-power-pin-defined\"\nimport { checkPadPadClearance } from \"./check-pad-pad-clearance\"\nimport { checkPadTraceClearance } from \"./check-pad-trace-clearance\"\nimport { checkPcbComponentsOutOfBoard } from \"./check-pcb-components-out-of-board/checkPcbComponentsOutOfBoard\"\nimport { checkViasOffBoard } from \"./check-pcb-components-out-of-board/checkViasOffBoard\"\nimport { checkPcbComponentOverlap } from \"./check-pcb-components-overlap/checkPcbComponentOverlap\"\nimport { checkPinMustBeConnected } from \"./check-pin-must-be-connected\"\nimport { checkSameNetViaSpacing } from \"./check-same-net-via-spacing\"\nimport { checkSourceTracesHavePcbTraces } from \"./check-source-traces-have-pcb-traces\"\nimport { checkPcbTracesOutOfBoard } from \"./check-trace-out-of-board/checkTraceOutOfBoard\"\nimport { checkTracesAreContiguous } from \"./check-traces-are-contiguous/check-traces-are-contiguous\"\nimport { checkViaTraceClearance } from \"./check-via-trace-clearance\"\n\nexport async function runAllPlacementChecks(circuitJson: AnyCircuitElement[]) {\n return [\n ...checkViasOffBoard(circuitJson),\n ...checkPcbComponentsOutOfBoard(circuitJson),\n ...checkPcbComponentOverlap(circuitJson),\n ...checkPadPadClearance(circuitJson),\n ...checkPadTraceClearance(circuitJson),\n ...checkCourtyardOverlap(circuitJson),\n ...checkConnectorAccessibleOrientation(circuitJson),\n ]\n}\n\nexport async function runAllNetlistChecks(circuitJson: AnyCircuitElement[]) {\n return [...checkPinMustBeConnected(circuitJson)]\n}\n\nexport async function runAllPinSpecificationChecks(\n circuitJson: AnyCircuitElement[],\n) {\n return [\n ...checkAllPinsInComponentAreUnderspecified(circuitJson),\n ...checkNoPowerPinDefined(circuitJson),\n ...checkNoGroundPinDefined(circuitJson),\n ]\n}\n\nexport async function runAllRoutingChecks(circuitJson: AnyCircuitElement[]) {\n return [\n ...checkEachPcbPortConnectedToPcbTraces(circuitJson),\n ...checkSourceTracesHavePcbTraces(circuitJson),\n ...checkEachPcbTraceNonOverlapping(circuitJson),\n ...checkViaTraceClearance(circuitJson),\n ...checkSameNetViaSpacing(circuitJson),\n ...checkDifferentNetViaSpacing(circuitJson),\n // ...checkTracesAreContiguous(circuitJson),\n ...checkPcbTracesOutOfBoard(circuitJson),\n ]\n}\n\nexport async function runAllChecks(circuitJson: AnyCircuitElement[]) {\n return [\n ...(await runAllPlacementChecks(circuitJson)),\n ...(await runAllNetlistChecks(circuitJson)),\n ...(await runAllPinSpecificationChecks(circuitJson)),\n ...(await runAllRoutingChecks(circuitJson)),\n ]\n}\n"],"mappings":"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","names":["getReadableNameForElement","getFullConnectivityMapFromCircuitJson","getBoundsOfPcbElements","t","closestOnB","closestOnA","distance","clamp","dx","dy","getFullConnectivityMapFromCircuitJson","getReadableNameForElement","gap","distance","getReadableNameForElement","getReadableNameForElement","getReadableNameForElement","getFullConnectivityMapFromCircuitJson","distance","distance","getFullConnectivityMapFromCircuitJson","distance","getReadableNameForElement","getReadableNameForElement","getFullConnectivityMapFromCircuitJson","getFullConnectivityMapFromCircuitJson","distance","getReadableNameForElement","cju","getFullConnectivityMapFromCircuitJson","cju","getFullConnectivityMapFromCircuitJson","cju","segmentToSegmentMinDistance","distance","cju","getBoundsOfPcbElements","getPrimaryId","doBoundsOverlap","getFullConnectivityMapFromCircuitJson","getBoundsOfPcbElements","getBoundsOfPcbElements","getPrimaryId","getFullConnectivityMapFromCircuitJson","cju","getBoundsOfPcbElements","doBoundsOverlap","getPrimaryId","getReadableNameForElement","getFullConnectivityMapFromCircuitJson","cju","getBoundsOfPcbElements","getBoundsOfPcbElements","cju","getFullConnectivityMapFromCircuitJson","getPrimaryId","getReadableNameForElement","getPrimaryId","getReadableNameForElement","segmentToBoundsMinDistance","segmentToCircleMinDistance","getFullConnectivityMapFromCircuitJson","getFullConnectivityMapFromCircuitJson","getPrimaryId","segmentToCircleMinDistance","segmentToBoundsMinDistance","getReadableNameForElement","getReadableNameForElement","segmentToCircleMinDistance","getFullConnectivityMapFromCircuitJson","getFullConnectivityMapFromCircuitJson","segmentToCircleMinDistance","getReadableNameForElement","cju","cju","cju","getComponentName","getReadableNameForPcbPort"]}
package/package.json CHANGED
@@ -2,23 +2,24 @@
2
2
  "name": "@tscircuit/checks",
3
3
  "type": "module",
4
4
  "main": "./dist/index.js",
5
- "version": "0.0.119",
5
+ "version": "0.0.121",
6
6
  "files": [
7
7
  "dist"
8
8
  ],
9
9
  "scripts": {
10
- "build": "tsup-node index.ts --format esm --dts --sourcemap",
10
+ "build": "tsup-node",
11
11
  "format:check": "biome format .",
12
12
  "format": "biome format . --write"
13
13
  },
14
14
  "devDependencies": {
15
15
  "@biomejs/biome": "^1.9.0",
16
16
  "@tscircuit/circuit-json-util": "^0.0.93",
17
+ "@tscircuit/jlcpcb-manufacturing-specs": "git+https://github.com/tscircuit/jlcpcb-manufacturing-specs#5644e294acfdd545ceb468c3b95c9ed82cb9a00b",
17
18
  "@tscircuit/log-soup": "^1.0.2",
18
19
  "@types/bun": "^1.2.8",
19
20
  "@types/debug": "^4.1.12",
20
21
  "bun-match-svg": "^0.0.11",
21
- "circuit-json": "^0.0.415",
22
+ "circuit-json": "^0.0.418",
22
23
  "circuit-to-svg": "^0.0.344",
23
24
  "debug": "^4.3.5",
24
25
  "tscircuit": "^0.0.1643",