@tscircuit/checks 0.0.157 → 0.0.159

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
@@ -23,6 +23,12 @@ declare function checkViasOffBoard(circuitJson: AnyCircuitElement[]): PcbPlaceme
23
23
  */
24
24
  declare function checkPcbComponentsOutOfBoard(circuitJson: AnyCircuitElement[]): PcbComponentOutsideBoardError[];
25
25
 
26
+ /**
27
+ * Cutouts remove PCB material on every layer, so a component body or SMT pad
28
+ * overlapping a cutout is invalid on both top and bottom.
29
+ */
30
+ declare function checkPcbComponentOverCutout(circuitJson: AnyCircuitElement[]): PcbPlacementError[];
31
+
26
32
  declare function checkSameNetViaSpacing(circuitJson: AnyCircuitElement[], { connMap, minClearance, }?: {
27
33
  connMap?: ConnectivityMap;
28
34
  minClearance?: number;
@@ -166,4 +172,4 @@ declare function checkConnectorAccessibleOrientation(circuitJson: AnyCircuitElem
166
172
  */
167
173
  declare function checkTestPointAccessibility(circuitJson: AnyCircuitElement[]): PcbPlacementError[];
168
174
 
169
- export { NetManager, checkAllPinsInComponentAreUnderspecified, checkConnectorAccessibleOrientation, checkDifferentNetViaSpacing, checkEachPcbPortConnectedToPcbTraces, checkEachPcbTraceNonOverlapping, checkNoGroundPinDefined, checkNoPowerPinDefined, checkPadPadClearance, checkPadTraceClearance, checkPcbComponentOverlap, checkPcbComponentsOutOfBoard, checkPcbTraceLengths, checkPcbTracesOutOfBoard, checkPinMustBeConnected, checkSameNetViaSpacing, checkSourceTracesHavePcbTraces, checkSourceTracesMatchPcbTraceThickness, checkTestPointAccessibility, checkTracesAreContiguous, checkViaPadClearance, checkViaTraceClearance, checkViasInPads, checkViasOffBoard, dedupePcbDrcErrors, runAllChecks, runAllNetlistChecks, runAllPinSpecificationChecks, runAllPlacementChecks, runAllRoutingChecks };
175
+ export { NetManager, checkAllPinsInComponentAreUnderspecified, checkConnectorAccessibleOrientation, checkDifferentNetViaSpacing, checkEachPcbPortConnectedToPcbTraces, checkEachPcbTraceNonOverlapping, checkNoGroundPinDefined, checkNoPowerPinDefined, checkPadPadClearance, checkPadTraceClearance, checkPcbComponentOverCutout, checkPcbComponentOverlap, checkPcbComponentsOutOfBoard, checkPcbTraceLengths, checkPcbTracesOutOfBoard, checkPinMustBeConnected, checkSameNetViaSpacing, checkSourceTracesHavePcbTraces, checkSourceTracesMatchPcbTraceThickness, checkTestPointAccessibility, checkTracesAreContiguous, checkViaPadClearance, checkViaTraceClearance, checkViasInPads, checkViasOffBoard, dedupePcbDrcErrors, runAllChecks, runAllNetlistChecks, runAllPinSpecificationChecks, runAllPlacementChecks, runAllRoutingChecks };
package/dist/index.js CHANGED
@@ -1789,6 +1789,132 @@ function checkPcbComponentsOutOfBoard(circuitJson) {
1789
1789
  return errors;
1790
1790
  }
1791
1791
 
1792
+ // lib/check-pcb-component-over-cutout.ts
1793
+ import { doBoundsOverlap } from "@tscircuit/math-utils";
1794
+ import * as Flatten2 from "@flatten-js/core";
1795
+ import { applyToPoint as applyToPoint2, rotateDEG as rotateDEG2 } from "transformation-matrix";
1796
+ var CUTOUT_CIRCLE_SEGMENTS = 32;
1797
+ function rectanglePolygon2({
1798
+ center,
1799
+ width,
1800
+ height,
1801
+ rotation = 0
1802
+ }) {
1803
+ const halfWidth = width / 2;
1804
+ const halfHeight = height / 2;
1805
+ const corners = [
1806
+ { x: center.x - halfWidth, y: center.y - halfHeight },
1807
+ { x: center.x + halfWidth, y: center.y - halfHeight },
1808
+ { x: center.x + halfWidth, y: center.y + halfHeight },
1809
+ { x: center.x - halfWidth, y: center.y + halfHeight }
1810
+ ];
1811
+ const matrix = rotateDEG2(rotation, center.x, center.y);
1812
+ return new Flatten2.Polygon(
1813
+ corners.map((corner) => {
1814
+ const rotated = rotation ? applyToPoint2(matrix, corner) : corner;
1815
+ return new Flatten2.Point(rotated.x, rotated.y);
1816
+ })
1817
+ );
1818
+ }
1819
+ function circlePolygon({
1820
+ center,
1821
+ radius
1822
+ }) {
1823
+ return new Flatten2.Polygon(
1824
+ Array.from({ length: CUTOUT_CIRCLE_SEGMENTS }, (_, index) => {
1825
+ const angle = 2 * Math.PI * index / CUTOUT_CIRCLE_SEGMENTS;
1826
+ return new Flatten2.Point(
1827
+ center.x + Math.cos(angle) * radius,
1828
+ center.y + Math.sin(angle) * radius
1829
+ );
1830
+ })
1831
+ );
1832
+ }
1833
+ function cutoutToPolygon(cutout) {
1834
+ if (cutout.shape === "rect") {
1835
+ return rectanglePolygon2({
1836
+ center: cutout.center,
1837
+ width: cutout.width,
1838
+ height: cutout.height,
1839
+ rotation: cutout.rotation ?? 0
1840
+ });
1841
+ }
1842
+ if (cutout.shape === "circle") {
1843
+ return circlePolygon({ center: cutout.center, radius: cutout.radius });
1844
+ }
1845
+ if (cutout.shape === "polygon") {
1846
+ return new Flatten2.Polygon(
1847
+ cutout.points.map((point) => new Flatten2.Point(point.x, point.y))
1848
+ );
1849
+ }
1850
+ return null;
1851
+ }
1852
+ function polygonBoxToBounds(polygon) {
1853
+ return {
1854
+ minX: polygon.box.xmin,
1855
+ minY: polygon.box.ymin,
1856
+ maxX: polygon.box.xmax,
1857
+ maxY: polygon.box.ymax
1858
+ };
1859
+ }
1860
+ function doPolygonsOverlap(polygonA, polygonB) {
1861
+ if (!doBoundsOverlap(polygonBoxToBounds(polygonA), polygonBoxToBounds(polygonB))) {
1862
+ return false;
1863
+ }
1864
+ if (polygonA.contains(polygonB) || polygonB.contains(polygonA)) return true;
1865
+ try {
1866
+ const intersections = Flatten2.BooleanOperations.intersect(
1867
+ polygonA,
1868
+ polygonB
1869
+ );
1870
+ if (Array.isArray(intersections)) {
1871
+ return intersections.some((polygon) => polygon.area() > 0);
1872
+ }
1873
+ return intersections.area() > 0;
1874
+ } catch {
1875
+ return false;
1876
+ }
1877
+ }
1878
+ function checkPcbComponentOverCutout(circuitJson) {
1879
+ const cutouts = circuitJson.filter(
1880
+ (element) => element.type === "pcb_cutout"
1881
+ );
1882
+ const components = circuitJson.filter(
1883
+ (element) => element.type === "pcb_component"
1884
+ );
1885
+ if (cutouts.length === 0 || components.length === 0) return [];
1886
+ const cutoutPolygons = cutouts.map((cutout) => ({ cutout, polygon: cutoutToPolygon(cutout) })).filter(
1887
+ (entry) => entry.polygon !== null && entry.polygon.area() > 0
1888
+ );
1889
+ const errors = [];
1890
+ for (const component of components) {
1891
+ if (!component.center || component.width <= 0 || component.height <= 0) {
1892
+ continue;
1893
+ }
1894
+ const componentPolygon = rectanglePolygon2({
1895
+ center: component.center,
1896
+ width: component.width,
1897
+ height: component.height
1898
+ });
1899
+ for (const { cutout, polygon: cutoutPolygon } of cutoutPolygons) {
1900
+ if (!doPolygonsOverlap(componentPolygon, cutoutPolygon)) continue;
1901
+ const componentName = getReadableNameForComponent(
1902
+ circuitJson,
1903
+ component.pcb_component_id
1904
+ );
1905
+ const cutoutId = cutout.pcb_cutout_id;
1906
+ errors.push({
1907
+ type: "pcb_placement_error",
1908
+ pcb_placement_error_id: `component_over_cutout_${component.pcb_component_id}_${cutoutId}`,
1909
+ error_type: "pcb_placement_error",
1910
+ message: `Component ${componentName} overlaps with pcb_cutout [${cutoutId}]`,
1911
+ subcircuit_id: component.subcircuit_id
1912
+ });
1913
+ }
1914
+ }
1915
+ return errors;
1916
+ }
1917
+
1792
1918
  // lib/check-same-net-via-spacing.ts
1793
1919
  import { getReadableNameForElement as getReadableNameForElement4 } from "@tscircuit/circuit-json-util";
1794
1920
  import {
@@ -2377,12 +2503,12 @@ import {
2377
2503
  getBoundsOfPcbElements as getBoundsOfPcbElements5,
2378
2504
  getPrimaryId as getPrimaryId2
2379
2505
  } from "@tscircuit/circuit-json-util";
2380
- import { doBoundsOverlap as doBoundsOverlap2 } from "@tscircuit/math-utils";
2506
+ import { doBoundsOverlap as doBoundsOverlap3 } from "@tscircuit/math-utils";
2381
2507
  import { getFullConnectivityMapFromCircuitJson as getFullConnectivityMapFromCircuitJson7 } from "circuit-json-to-connectivity-map";
2382
2508
 
2383
2509
  // lib/check-pcb-components-overlap/doPcbElementsOverlap.ts
2384
2510
  import { getBoundsOfPcbElements as getBoundsOfPcbElements4 } from "@tscircuit/circuit-json-util";
2385
- import { doBoundsOverlap } from "@tscircuit/math-utils";
2511
+ import { doBoundsOverlap as doBoundsOverlap2 } from "@tscircuit/math-utils";
2386
2512
  function getElementLayers(elem) {
2387
2513
  if (elem.type === "pcb_courtyard_circle" || elem.type === "pcb_courtyard_outline" || elem.type === "pcb_courtyard_polygon" || elem.type === "pcb_courtyard_rect") {
2388
2514
  return [elem.layer];
@@ -2402,7 +2528,7 @@ function doPcbElementsOverlap(elem1, elem2) {
2402
2528
  }
2403
2529
  const bounds1 = getBoundsOfPcbElements4([elem1]);
2404
2530
  const bounds2 = getBoundsOfPcbElements4([elem2]);
2405
- return doBoundsOverlap(bounds1, bounds2);
2531
+ return doBoundsOverlap2(bounds1, bounds2);
2406
2532
  }
2407
2533
 
2408
2534
  // lib/check-pcb-components-overlap/checkPcbComponentOverlap.ts
@@ -2475,7 +2601,7 @@ function checkPcbComponentOverlap(circuitJson) {
2475
2601
  if (!comp1.bounds || !comp2.bounds) {
2476
2602
  continue;
2477
2603
  }
2478
- if (!doBoundsOverlap2(comp1.bounds, comp2.bounds)) {
2604
+ if (!doBoundsOverlap3(comp1.bounds, comp2.bounds)) {
2479
2605
  continue;
2480
2606
  }
2481
2607
  for (const elem1 of comp1.elements) {
@@ -3385,6 +3511,7 @@ async function runAllPlacementChecks(circuitJson) {
3385
3511
  ...checkViasOffBoard(circuitJson),
3386
3512
  ...checkViasInPads(circuitJson),
3387
3513
  ...checkPcbComponentsOutOfBoard(circuitJson),
3514
+ ...checkPcbComponentOverCutout(circuitJson),
3388
3515
  ...checkPcbComponentOverlap(circuitJson),
3389
3516
  ...checkPadPadClearance(circuitJson),
3390
3517
  ...checkCourtyardOverlap(circuitJson),
@@ -3436,6 +3563,7 @@ export {
3436
3563
  checkNoPowerPinDefined,
3437
3564
  checkPadPadClearance,
3438
3565
  checkPadTraceClearance,
3566
+ checkPcbComponentOverCutout,
3439
3567
  checkPcbComponentOverlap,
3440
3568
  checkPcbComponentsOutOfBoard,
3441
3569
  checkPcbTraceLengths,