@tscircuit/checks 0.0.161 → 0.0.162

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -28,7 +28,7 @@ and output an array of arrays for any issues found.
28
28
  | [`checkSourceTracesHavePcbTraces`](./lib/check-source-traces-have-pcb-traces.ts) | Returns `pcb_trace_error` when source traces are missing corresponding `pcb_trace` routes. |
29
29
  | [`checkTracesAreContiguous`](./lib/check-traces-are-contiguous/check-traces-are-contiguous.ts) | Returns `pcb_trace_error` when trace endpoints are floating or do not connect as expected. |
30
30
  | [`checkViasOffBoard`](./lib/check-pcb-components-out-of-board/checkViasOffBoard.ts) | Returns `pcb_placement_error` if any PCB via lies outside or crosses the board boundary. |
31
- | [`checkCopperToBoardEdgeClearance`](./lib/check-copper-to-board-edge-clearance.ts) | Checks via, SMT-pad, and plated-hole copper against the polygon board outline and required edge clearance. |
31
+ | [`checkCopperToBoardEdgeClearance`](./lib/check-copper-to-board-edge-clearance.ts) | Checks via, SMT-pad, plated-hole, and copper-pour geometry against the polygon board outline and required edge clearance. |
32
32
 
33
33
  ## Aggregate check runner functions
34
34
 
package/dist/index.d.ts CHANGED
@@ -18,8 +18,9 @@ declare class NetManager {
18
18
  declare function checkViasOffBoard(circuitJson: AnyCircuitElement[]): PcbPlacementError[];
19
19
 
20
20
  /**
21
- * Checks the finished copper geometry of every via, SMT pad, and plated hole
22
- * against the real board outline and its configured edge-clearance rule.
21
+ * Checks the finished copper geometry of every via, SMT pad, plated hole, and
22
+ * copper pour against the real board outline and its configured edge-clearance
23
+ * rule.
23
24
  *
24
25
  * Component and footprint rotations are normally composed into Circuit JSON's
25
26
  * absolute coordinates and `ccw_rotation` fields. Polygon-pad plated holes are
package/dist/index.js CHANGED
@@ -1536,6 +1536,57 @@ var pointsToPolygon = (points) => {
1536
1536
  if (points.length < 3) return null;
1537
1537
  return new Flatten.Polygon(points.map(({ x, y }) => new Flatten.Point(x, y)));
1538
1538
  };
1539
+ var brepRingToPolygon = (vertices) => {
1540
+ const ring = vertices.filter((vertex, index) => {
1541
+ const previous = vertices[index - 1];
1542
+ return !previous || Math.abs(previous.x - vertex.x) > GEOMETRY_EPSILON || Math.abs(previous.y - vertex.y) > GEOMETRY_EPSILON;
1543
+ });
1544
+ if (ring.length > 1 && Math.abs(ring[0].x - ring.at(-1).x) <= GEOMETRY_EPSILON && Math.abs(ring[0].y - ring.at(-1).y) <= GEOMETRY_EPSILON) {
1545
+ ring.pop();
1546
+ }
1547
+ if (ring.length < 3) return null;
1548
+ const edges = [];
1549
+ for (let index = 0; index < ring.length; index++) {
1550
+ const start = ring[index];
1551
+ const end = ring[(index + 1) % ring.length];
1552
+ const startPoint = new Flatten.Point(start.x, start.y);
1553
+ const endPoint = new Flatten.Point(end.x, end.y);
1554
+ const bulge = start.bulge ?? 0;
1555
+ if (Math.abs(bulge) <= GEOMETRY_EPSILON) {
1556
+ edges.push(new Flatten.Segment(startPoint, endPoint));
1557
+ continue;
1558
+ }
1559
+ const chordLength = startPoint.distanceTo(endPoint)[0];
1560
+ if (chordLength <= GEOMETRY_EPSILON) continue;
1561
+ const midpoint2 = {
1562
+ x: (start.x + end.x) / 2,
1563
+ y: (start.y + end.y) / 2
1564
+ };
1565
+ const leftNormal = {
1566
+ x: -(end.y - start.y) / chordLength,
1567
+ y: (end.x - start.x) / chordLength
1568
+ };
1569
+ const centerOffset = chordLength * (1 - bulge * bulge) / (4 * bulge);
1570
+ const center = new Flatten.Point(
1571
+ midpoint2.x + leftNormal.x * centerOffset,
1572
+ midpoint2.y + leftNormal.y * centerOffset
1573
+ );
1574
+ const radius = chordLength * (1 + bulge * bulge) / (4 * Math.abs(bulge));
1575
+ edges.push(
1576
+ new Flatten.Arc(
1577
+ center,
1578
+ radius,
1579
+ Math.atan2(start.y - center.y, start.x - center.x),
1580
+ Math.atan2(end.y - center.y, end.x - center.x),
1581
+ bulge > 0
1582
+ )
1583
+ );
1584
+ }
1585
+ if (edges.length < 3) return null;
1586
+ const polygon = new Flatten.Polygon();
1587
+ polygon.addFace(edges);
1588
+ return polygon;
1589
+ };
1539
1590
  var boardToPolygon = (board) => {
1540
1591
  if (board.outline && board.outline.length >= 3) {
1541
1592
  return pointsToPolygon(board.outline);
@@ -1773,22 +1824,45 @@ var getCopperGeometry = (element, componentCcwRotationsById) => {
1773
1824
  };
1774
1825
  }
1775
1826
  if (element.type === "pcb_smtpad") return getSmtPadGeometry(element);
1776
- return getPlatedHoleGeometry(
1777
- element,
1778
- element.pcb_component_id ? componentCcwRotationsById.get(
1779
- toPcbComponentId(element.pcb_component_id)
1780
- ) ?? 0 : 0
1781
- );
1827
+ if (element.type === "pcb_plated_hole") {
1828
+ return getPlatedHoleGeometry(
1829
+ element,
1830
+ element.pcb_component_id ? componentCcwRotationsById.get(
1831
+ toPcbComponentId(element.pcb_component_id)
1832
+ ) ?? 0 : 0
1833
+ );
1834
+ }
1835
+ let polygon;
1836
+ switch (element.shape) {
1837
+ case "rect":
1838
+ polygon = getRectanglePolygon({
1839
+ x: element.center.x,
1840
+ y: element.center.y,
1841
+ width: element.width,
1842
+ height: element.height,
1843
+ ccwRotationDegrees: element.rotation ?? 0
1844
+ });
1845
+ break;
1846
+ case "polygon":
1847
+ polygon = pointsToPolygon(element.points);
1848
+ break;
1849
+ case "brep":
1850
+ polygon = brepRingToPolygon(element.brep_shape.outer_ring.vertices);
1851
+ break;
1852
+ }
1853
+ return polygon ? { kind: "shapes", shapes: [polygon] } : null;
1782
1854
  };
1783
1855
  var getCopperElementId = (element) => {
1784
1856
  if (element.type === "pcb_via") return element.pcb_via_id;
1785
1857
  if (element.type === "pcb_smtpad") return element.pcb_smtpad_id;
1786
- return element.pcb_plated_hole_id;
1858
+ if (element.type === "pcb_plated_hole") return element.pcb_plated_hole_id;
1859
+ return element.pcb_copper_pour_id;
1787
1860
  };
1788
1861
  var getCopperElementLabel = (element) => {
1789
1862
  if (element.type === "pcb_via") return "Via";
1790
1863
  if (element.type === "pcb_smtpad") return "SMT pad";
1791
- return "Plated hole";
1864
+ if (element.type === "pcb_plated_hole") return "Plated hole";
1865
+ return "Copper pour";
1792
1866
  };
1793
1867
  var measureClearance = (board, geometry) => {
1794
1868
  if (geometry.kind === "shapes") {
@@ -1816,7 +1890,7 @@ function checkCopperToBoardEdgeClearance(circuitJson) {
1816
1890
  const requiredClearance = getBoardDrcValue(board, "min_board_edge_clearance") ?? jlcMinTolerances.min_board_edge_clearance;
1817
1891
  if (requiredClearance === void 0) return [];
1818
1892
  const copperElements = circuitJson.filter(
1819
- (element) => element.type === "pcb_via" || element.type === "pcb_smtpad" || element.type === "pcb_plated_hole"
1893
+ (element) => element.type === "pcb_via" || element.type === "pcb_smtpad" || element.type === "pcb_plated_hole" || element.type === "pcb_copper_pour"
1820
1894
  );
1821
1895
  const componentCcwRotationsById = new Map(
1822
1896
  circuitJson.filter(