circuit-to-svg 0.0.271 → 0.0.273

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1580,9 +1580,12 @@ function createSvgObjectsFromPcbNoteLine(noteLine, ctx) {
1580
1580
  // lib/pcb/svg-object-fns/create-svg-objects-from-pcb-plated-hole.ts
1581
1581
  import { applyToPoint as applyToPoint12 } from "transformation-matrix";
1582
1582
  function createSvgObjectsFromPcbPlatedHole(hole, ctx) {
1583
- const { transform, colorMap: colorMap2 } = ctx;
1583
+ const { transform, colorMap: colorMap2, showSolderMask } = ctx;
1584
1584
  const [x, y] = applyToPoint12(transform, [hole.x, hole.y]);
1585
1585
  const copperLayer = Array.isArray(hole.layers) && hole.layers[0] || hole.layer || "top";
1586
+ const soldermaskMargin = (hole.soldermask_margin ?? 0) * Math.abs(transform.a);
1587
+ const shouldShowSolderMask = showSolderMask && soldermaskMargin !== 0;
1588
+ const solderMaskColor = colorMap2.soldermask.top;
1586
1589
  if (hole.shape === "pill") {
1587
1590
  const scaledOuterWidth = hole.outer_width * Math.abs(transform.a);
1588
1591
  const scaledOuterHeight = hole.outer_height * Math.abs(transform.a);
@@ -1605,6 +1608,55 @@ function createSvgObjectsFromPcbPlatedHole(hole, ctx) {
1605
1608
  return `M${-radius},0 a${radius},${radius} 0 0 1 ${width},0 a${radius},${radius} 0 0 1 ${-width},0 z`;
1606
1609
  }
1607
1610
  };
1611
+ const children = [
1612
+ // Outer pill shape
1613
+ {
1614
+ name: "path",
1615
+ type: "element",
1616
+ attributes: {
1617
+ class: "pcb-hole-outer",
1618
+ fill: colorMap2.copper.top,
1619
+ d: createPillPath(scaledOuterWidth, scaledOuterHeight),
1620
+ transform: outerTransform,
1621
+ "data-type": "pcb_plated_hole",
1622
+ "data-pcb-layer": copperLayer
1623
+ },
1624
+ value: "",
1625
+ children: []
1626
+ },
1627
+ // Inner pill shape
1628
+ {
1629
+ name: "path",
1630
+ type: "element",
1631
+ attributes: {
1632
+ class: "pcb-hole-inner",
1633
+ fill: colorMap2.drill,
1634
+ d: createPillPath(scaledHoleWidth, scaledHoleHeight),
1635
+ transform: innerTransform,
1636
+ "data-type": "pcb_plated_hole_drill",
1637
+ "data-pcb-layer": "drill"
1638
+ },
1639
+ value: "",
1640
+ children: []
1641
+ }
1642
+ ];
1643
+ if (shouldShowSolderMask) {
1644
+ const maskWidth = scaledOuterWidth + 2 * soldermaskMargin;
1645
+ const maskHeight = scaledOuterHeight + 2 * soldermaskMargin;
1646
+ children.push({
1647
+ name: "path",
1648
+ type: "element",
1649
+ attributes: {
1650
+ class: "pcb-solder-mask",
1651
+ fill: solderMaskColor,
1652
+ d: createPillPath(maskWidth, maskHeight),
1653
+ transform: outerTransform,
1654
+ "data-type": "pcb_soldermask"
1655
+ },
1656
+ value: "",
1657
+ children: []
1658
+ });
1659
+ }
1608
1660
  return [
1609
1661
  {
1610
1662
  name: "g",
@@ -1613,38 +1665,7 @@ function createSvgObjectsFromPcbPlatedHole(hole, ctx) {
1613
1665
  "data-type": "pcb_plated_hole",
1614
1666
  "data-pcb-layer": "through"
1615
1667
  },
1616
- children: [
1617
- // Outer pill shape
1618
- {
1619
- name: "path",
1620
- type: "element",
1621
- attributes: {
1622
- class: "pcb-hole-outer",
1623
- fill: colorMap2.copper.top,
1624
- d: createPillPath(scaledOuterWidth, scaledOuterHeight),
1625
- transform: outerTransform,
1626
- "data-type": "pcb_plated_hole",
1627
- "data-pcb-layer": copperLayer
1628
- },
1629
- value: "",
1630
- children: []
1631
- },
1632
- // Inner pill shape
1633
- {
1634
- name: "path",
1635
- type: "element",
1636
- attributes: {
1637
- class: "pcb-hole-inner",
1638
- fill: colorMap2.drill,
1639
- d: createPillPath(scaledHoleWidth, scaledHoleHeight),
1640
- transform: innerTransform,
1641
- "data-type": "pcb_plated_hole_drill",
1642
- "data-pcb-layer": "drill"
1643
- },
1644
- value: "",
1645
- children: []
1646
- }
1647
- ],
1668
+ children,
1648
1669
  value: ""
1649
1670
  }
1650
1671
  ];
@@ -1656,6 +1677,55 @@ function createSvgObjectsFromPcbPlatedHole(hole, ctx) {
1656
1677
  const scaledHoleHeight = hole.hole_diameter * Math.abs(transform.a);
1657
1678
  const outerRadius = Math.min(scaledOuterWidth, scaledOuterHeight) / 2;
1658
1679
  const innerRadius = Math.min(scaledHoleWidth, scaledHoleHeight) / 2;
1680
+ const children = [
1681
+ {
1682
+ name: "circle",
1683
+ type: "element",
1684
+ attributes: {
1685
+ class: "pcb-hole-outer",
1686
+ fill: colorMap2.copper.top,
1687
+ cx: x.toString(),
1688
+ cy: y.toString(),
1689
+ r: outerRadius.toString(),
1690
+ "data-type": "pcb_plated_hole",
1691
+ "data-pcb-layer": copperLayer
1692
+ },
1693
+ value: "",
1694
+ children: []
1695
+ },
1696
+ {
1697
+ name: "circle",
1698
+ type: "element",
1699
+ attributes: {
1700
+ class: "pcb-hole-inner",
1701
+ fill: colorMap2.drill,
1702
+ cx: x.toString(),
1703
+ cy: y.toString(),
1704
+ r: innerRadius.toString(),
1705
+ "data-type": "pcb_plated_hole_drill",
1706
+ "data-pcb-layer": "drill"
1707
+ },
1708
+ value: "",
1709
+ children: []
1710
+ }
1711
+ ];
1712
+ if (shouldShowSolderMask) {
1713
+ const maskRadius = outerRadius + soldermaskMargin;
1714
+ children.push({
1715
+ name: "circle",
1716
+ type: "element",
1717
+ attributes: {
1718
+ class: "pcb-solder-mask",
1719
+ fill: solderMaskColor,
1720
+ cx: x.toString(),
1721
+ cy: y.toString(),
1722
+ r: maskRadius.toString(),
1723
+ "data-type": "pcb_soldermask"
1724
+ },
1725
+ value: "",
1726
+ children: []
1727
+ });
1728
+ }
1659
1729
  return [
1660
1730
  {
1661
1731
  name: "g",
@@ -1664,38 +1734,7 @@ function createSvgObjectsFromPcbPlatedHole(hole, ctx) {
1664
1734
  "data-type": "pcb_plated_hole",
1665
1735
  "data-pcb-layer": "through"
1666
1736
  },
1667
- children: [
1668
- {
1669
- name: "circle",
1670
- type: "element",
1671
- attributes: {
1672
- class: "pcb-hole-outer",
1673
- fill: colorMap2.copper.top,
1674
- cx: x.toString(),
1675
- cy: y.toString(),
1676
- r: outerRadius.toString(),
1677
- "data-type": "pcb_plated_hole",
1678
- "data-pcb-layer": copperLayer
1679
- },
1680
- value: "",
1681
- children: []
1682
- },
1683
- {
1684
- name: "circle",
1685
- type: "element",
1686
- attributes: {
1687
- class: "pcb-hole-inner",
1688
- fill: colorMap2.drill,
1689
- cx: x.toString(),
1690
- cy: y.toString(),
1691
- r: innerRadius.toString(),
1692
- "data-type": "pcb_plated_hole_drill",
1693
- "data-pcb-layer": "drill"
1694
- },
1695
- value: "",
1696
- children: []
1697
- }
1698
- ],
1737
+ children,
1699
1738
  value: ""
1700
1739
  }
1701
1740
  ];
@@ -1711,6 +1750,69 @@ function createSvgObjectsFromPcbPlatedHole(hole, ctx) {
1711
1750
  h.x + (h.hole_offset_x ?? 0),
1712
1751
  h.y + (h.hole_offset_y ?? 0)
1713
1752
  ]);
1753
+ const children = [
1754
+ // Rectangular pad (outer shape)
1755
+ {
1756
+ name: "rect",
1757
+ type: "element",
1758
+ attributes: {
1759
+ class: "pcb-hole-outer-pad",
1760
+ fill: colorMap2.copper.top,
1761
+ x: (x - scaledRectPadWidth / 2).toString(),
1762
+ y: (y - scaledRectPadHeight / 2).toString(),
1763
+ width: scaledRectPadWidth.toString(),
1764
+ height: scaledRectPadHeight.toString(),
1765
+ ...scaledRectBorderRadius ? {
1766
+ rx: scaledRectBorderRadius.toString(),
1767
+ ry: scaledRectBorderRadius.toString()
1768
+ } : {},
1769
+ "data-type": "pcb_plated_hole",
1770
+ "data-pcb-layer": copperLayer
1771
+ },
1772
+ value: "",
1773
+ children: []
1774
+ },
1775
+ // Circular hole inside the rectangle (with optional offset)
1776
+ {
1777
+ name: "circle",
1778
+ type: "element",
1779
+ attributes: {
1780
+ class: "pcb-hole-inner",
1781
+ fill: colorMap2.drill,
1782
+ cx: holeCx.toString(),
1783
+ cy: holeCy.toString(),
1784
+ r: holeRadius.toString(),
1785
+ "data-type": "pcb_plated_hole_drill",
1786
+ "data-pcb-layer": "drill"
1787
+ },
1788
+ value: "",
1789
+ children: []
1790
+ }
1791
+ ];
1792
+ if (shouldShowSolderMask) {
1793
+ const maskWidth = scaledRectPadWidth + 2 * soldermaskMargin;
1794
+ const maskHeight = scaledRectPadHeight + 2 * soldermaskMargin;
1795
+ const maskBorderRadius = scaledRectBorderRadius + soldermaskMargin;
1796
+ children.push({
1797
+ name: "rect",
1798
+ type: "element",
1799
+ attributes: {
1800
+ class: "pcb-solder-mask",
1801
+ fill: solderMaskColor,
1802
+ x: (x - maskWidth / 2).toString(),
1803
+ y: (y - maskHeight / 2).toString(),
1804
+ width: maskWidth.toString(),
1805
+ height: maskHeight.toString(),
1806
+ ...scaledRectBorderRadius ? {
1807
+ rx: maskBorderRadius.toString(),
1808
+ ry: maskBorderRadius.toString()
1809
+ } : {},
1810
+ "data-type": "pcb_soldermask"
1811
+ },
1812
+ value: "",
1813
+ children: []
1814
+ });
1815
+ }
1714
1816
  return [
1715
1817
  {
1716
1818
  name: "g",
@@ -1719,45 +1821,7 @@ function createSvgObjectsFromPcbPlatedHole(hole, ctx) {
1719
1821
  "data-type": "pcb_plated_hole",
1720
1822
  "data-pcb-layer": "through"
1721
1823
  },
1722
- children: [
1723
- // Rectangular pad (outer shape)
1724
- {
1725
- name: "rect",
1726
- type: "element",
1727
- attributes: {
1728
- class: "pcb-hole-outer-pad",
1729
- fill: colorMap2.copper.top,
1730
- x: (x - scaledRectPadWidth / 2).toString(),
1731
- y: (y - scaledRectPadHeight / 2).toString(),
1732
- width: scaledRectPadWidth.toString(),
1733
- height: scaledRectPadHeight.toString(),
1734
- ...scaledRectBorderRadius ? {
1735
- rx: scaledRectBorderRadius.toString(),
1736
- ry: scaledRectBorderRadius.toString()
1737
- } : {},
1738
- "data-type": "pcb_plated_hole",
1739
- "data-pcb-layer": copperLayer
1740
- },
1741
- value: "",
1742
- children: []
1743
- },
1744
- // Circular hole inside the rectangle (with optional offset)
1745
- {
1746
- name: "circle",
1747
- type: "element",
1748
- attributes: {
1749
- class: "pcb-hole-inner",
1750
- fill: colorMap2.drill,
1751
- cx: holeCx.toString(),
1752
- cy: holeCy.toString(),
1753
- r: holeRadius.toString(),
1754
- "data-type": "pcb_plated_hole_drill",
1755
- "data-pcb-layer": "drill"
1756
- },
1757
- value: "",
1758
- children: []
1759
- }
1760
- ],
1824
+ children,
1761
1825
  value: ""
1762
1826
  }
1763
1827
  ];
@@ -1777,6 +1841,72 @@ function createSvgObjectsFromPcbPlatedHole(hole, ctx) {
1777
1841
  pillHole.y + holeOffsetY
1778
1842
  ]);
1779
1843
  const holeRadius = Math.min(scaledHoleHeight, scaledHoleWidth) / 2;
1844
+ const children = [
1845
+ // Rectangular pad (outer shape)
1846
+ {
1847
+ name: "rect",
1848
+ type: "element",
1849
+ attributes: {
1850
+ class: "pcb-hole-outer-pad",
1851
+ fill: colorMap2.copper.top,
1852
+ x: (x - scaledRectPadWidth / 2).toString(),
1853
+ y: (y - scaledRectPadHeight / 2).toString(),
1854
+ width: scaledRectPadWidth.toString(),
1855
+ height: scaledRectPadHeight.toString(),
1856
+ ...scaledRectBorderRadius ? {
1857
+ rx: scaledRectBorderRadius.toString(),
1858
+ ry: scaledRectBorderRadius.toString()
1859
+ } : {},
1860
+ "data-type": "pcb_plated_hole",
1861
+ "data-pcb-layer": copperLayer
1862
+ },
1863
+ value: "",
1864
+ children: []
1865
+ },
1866
+ // pill hole inside the rectangle
1867
+ {
1868
+ name: "rect",
1869
+ type: "element",
1870
+ attributes: {
1871
+ class: "pcb-hole-inner",
1872
+ fill: colorMap2.drill,
1873
+ x: (holeCenterX - scaledHoleWidth / 2).toString(),
1874
+ y: (holeCenterY - scaledHoleHeight / 2).toString(),
1875
+ width: scaledHoleWidth.toString(),
1876
+ height: scaledHoleHeight.toString(),
1877
+ rx: holeRadius.toString(),
1878
+ ry: holeRadius.toString(),
1879
+ "data-type": "pcb_plated_hole_drill",
1880
+ "data-pcb-layer": "drill"
1881
+ },
1882
+ value: "",
1883
+ children: []
1884
+ }
1885
+ ];
1886
+ if (shouldShowSolderMask) {
1887
+ const maskWidth = scaledRectPadWidth + 2 * soldermaskMargin;
1888
+ const maskHeight = scaledRectPadHeight + 2 * soldermaskMargin;
1889
+ const maskBorderRadius = scaledRectBorderRadius + soldermaskMargin;
1890
+ children.push({
1891
+ name: "rect",
1892
+ type: "element",
1893
+ attributes: {
1894
+ class: "pcb-solder-mask",
1895
+ fill: solderMaskColor,
1896
+ x: (x - maskWidth / 2).toString(),
1897
+ y: (y - maskHeight / 2).toString(),
1898
+ width: maskWidth.toString(),
1899
+ height: maskHeight.toString(),
1900
+ ...scaledRectBorderRadius ? {
1901
+ rx: maskBorderRadius.toString(),
1902
+ ry: maskBorderRadius.toString()
1903
+ } : {},
1904
+ "data-type": "pcb_soldermask"
1905
+ },
1906
+ value: "",
1907
+ children: []
1908
+ });
1909
+ }
1780
1910
  return [
1781
1911
  {
1782
1912
  name: "g",
@@ -1785,67 +1915,93 @@ function createSvgObjectsFromPcbPlatedHole(hole, ctx) {
1785
1915
  "data-type": "pcb_plated_hole",
1786
1916
  "data-pcb-layer": "through"
1787
1917
  },
1788
- children: [
1789
- // Rectangular pad (outer shape)
1790
- {
1791
- name: "rect",
1792
- type: "element",
1793
- attributes: {
1794
- class: "pcb-hole-outer-pad",
1795
- fill: colorMap2.copper.top,
1796
- x: (x - scaledRectPadWidth / 2).toString(),
1797
- y: (y - scaledRectPadHeight / 2).toString(),
1798
- width: scaledRectPadWidth.toString(),
1799
- height: scaledRectPadHeight.toString(),
1800
- ...scaledRectBorderRadius ? {
1801
- rx: scaledRectBorderRadius.toString(),
1802
- ry: scaledRectBorderRadius.toString()
1803
- } : {},
1804
- "data-type": "pcb_plated_hole",
1805
- "data-pcb-layer": copperLayer
1806
- },
1807
- value: "",
1808
- children: []
1809
- },
1810
- // pill hole inside the rectangle
1811
- {
1812
- name: "rect",
1813
- type: "element",
1814
- attributes: {
1815
- class: "pcb-hole-inner",
1816
- fill: colorMap2.drill,
1817
- x: (holeCenterX - scaledHoleWidth / 2).toString(),
1818
- y: (holeCenterY - scaledHoleHeight / 2).toString(),
1819
- width: scaledHoleWidth.toString(),
1820
- height: scaledHoleHeight.toString(),
1821
- rx: holeRadius.toString(),
1822
- ry: holeRadius.toString(),
1823
- "data-type": "pcb_plated_hole_drill",
1824
- "data-pcb-layer": "drill"
1825
- },
1826
- value: "",
1827
- children: []
1828
- }
1829
- ],
1918
+ children,
1830
1919
  value: ""
1831
1920
  }
1832
1921
  ];
1833
- }
1834
- if (hole.shape === "rotated_pill_hole_with_rect_pad") {
1835
- const rotatedHole = hole;
1836
- const scaledRectPadWidth = rotatedHole.rect_pad_width * Math.abs(transform.a);
1837
- const scaledRectPadHeight = rotatedHole.rect_pad_height * Math.abs(transform.a);
1838
- const scaledRectBorderRadius = (rotatedHole.rect_border_radius ?? 0) * Math.abs(transform.a);
1839
- const scaledHoleHeight = rotatedHole.hole_height * Math.abs(transform.a);
1840
- const scaledHoleWidth = rotatedHole.hole_width * Math.abs(transform.a);
1841
- const rotatedHoleWithOffsets = rotatedHole;
1842
- const holeOffsetX = rotatedHoleWithOffsets.hole_offset_x ?? 0;
1843
- const holeOffsetY = rotatedHoleWithOffsets.hole_offset_y ?? 0;
1844
- const [holeCenterX, holeCenterY] = applyToPoint12(transform, [
1845
- rotatedHole.x + holeOffsetX,
1846
- rotatedHole.y + holeOffsetY
1847
- ]);
1848
- const holeRadius = Math.min(scaledHoleHeight, scaledHoleWidth) / 2;
1922
+ }
1923
+ if (hole.shape === "rotated_pill_hole_with_rect_pad") {
1924
+ const rotatedHole = hole;
1925
+ const scaledRectPadWidth = rotatedHole.rect_pad_width * Math.abs(transform.a);
1926
+ const scaledRectPadHeight = rotatedHole.rect_pad_height * Math.abs(transform.a);
1927
+ const scaledRectBorderRadius = (rotatedHole.rect_border_radius ?? 0) * Math.abs(transform.a);
1928
+ const scaledHoleHeight = rotatedHole.hole_height * Math.abs(transform.a);
1929
+ const scaledHoleWidth = rotatedHole.hole_width * Math.abs(transform.a);
1930
+ const rotatedHoleWithOffsets = rotatedHole;
1931
+ const holeOffsetX = rotatedHoleWithOffsets.hole_offset_x ?? 0;
1932
+ const holeOffsetY = rotatedHoleWithOffsets.hole_offset_y ?? 0;
1933
+ const [holeCenterX, holeCenterY] = applyToPoint12(transform, [
1934
+ rotatedHole.x + holeOffsetX,
1935
+ rotatedHole.y + holeOffsetY
1936
+ ]);
1937
+ const holeRadius = Math.min(scaledHoleHeight, scaledHoleWidth) / 2;
1938
+ const children = [
1939
+ {
1940
+ name: "rect",
1941
+ type: "element",
1942
+ attributes: {
1943
+ class: "pcb-hole-outer-pad",
1944
+ fill: colorMap2.copper.top,
1945
+ x: (-scaledRectPadWidth / 2).toString(),
1946
+ y: (-scaledRectPadHeight / 2).toString(),
1947
+ width: scaledRectPadWidth.toString(),
1948
+ height: scaledRectPadHeight.toString(),
1949
+ transform: `translate(${x} ${y}) rotate(${-rotatedHole.rect_ccw_rotation})`,
1950
+ ...scaledRectBorderRadius ? {
1951
+ rx: scaledRectBorderRadius.toString(),
1952
+ ry: scaledRectBorderRadius.toString()
1953
+ } : {},
1954
+ "data-type": "pcb_plated_hole",
1955
+ "data-pcb-layer": copperLayer
1956
+ },
1957
+ value: "",
1958
+ children: []
1959
+ },
1960
+ {
1961
+ name: "rect",
1962
+ type: "element",
1963
+ attributes: {
1964
+ class: "pcb-hole-inner",
1965
+ fill: colorMap2.drill,
1966
+ x: (-scaledHoleWidth / 2).toString(),
1967
+ y: (-scaledHoleHeight / 2).toString(),
1968
+ width: scaledHoleWidth.toString(),
1969
+ height: scaledHoleHeight.toString(),
1970
+ rx: holeRadius.toString(),
1971
+ ry: holeRadius.toString(),
1972
+ transform: `translate(${holeCenterX} ${holeCenterY}) rotate(${-rotatedHole.hole_ccw_rotation})`,
1973
+ "data-type": "pcb_plated_hole_drill",
1974
+ "data-pcb-layer": "drill"
1975
+ },
1976
+ value: "",
1977
+ children: []
1978
+ }
1979
+ ];
1980
+ if (shouldShowSolderMask) {
1981
+ const maskWidth = scaledRectPadWidth + 2 * soldermaskMargin;
1982
+ const maskHeight = scaledRectPadHeight + 2 * soldermaskMargin;
1983
+ const maskBorderRadius = scaledRectBorderRadius + soldermaskMargin;
1984
+ children.push({
1985
+ name: "rect",
1986
+ type: "element",
1987
+ attributes: {
1988
+ class: "pcb-solder-mask",
1989
+ fill: solderMaskColor,
1990
+ x: (-maskWidth / 2).toString(),
1991
+ y: (-maskHeight / 2).toString(),
1992
+ width: maskWidth.toString(),
1993
+ height: maskHeight.toString(),
1994
+ transform: `translate(${x} ${y}) rotate(${-rotatedHole.rect_ccw_rotation})`,
1995
+ ...scaledRectBorderRadius ? {
1996
+ rx: maskBorderRadius.toString(),
1997
+ ry: maskBorderRadius.toString()
1998
+ } : {},
1999
+ "data-type": "pcb_soldermask"
2000
+ },
2001
+ value: "",
2002
+ children: []
2003
+ });
2004
+ }
1849
2005
  return [
1850
2006
  {
1851
2007
  name: "g",
@@ -1854,48 +2010,7 @@ function createSvgObjectsFromPcbPlatedHole(hole, ctx) {
1854
2010
  "data-type": "pcb_plated_hole",
1855
2011
  "data-pcb-layer": "through"
1856
2012
  },
1857
- children: [
1858
- {
1859
- name: "rect",
1860
- type: "element",
1861
- attributes: {
1862
- class: "pcb-hole-outer-pad",
1863
- fill: colorMap2.copper.top,
1864
- x: (-scaledRectPadWidth / 2).toString(),
1865
- y: (-scaledRectPadHeight / 2).toString(),
1866
- width: scaledRectPadWidth.toString(),
1867
- height: scaledRectPadHeight.toString(),
1868
- transform: `translate(${x} ${y}) rotate(${-rotatedHole.rect_ccw_rotation})`,
1869
- ...scaledRectBorderRadius ? {
1870
- rx: scaledRectBorderRadius.toString(),
1871
- ry: scaledRectBorderRadius.toString()
1872
- } : {},
1873
- "data-type": "pcb_plated_hole",
1874
- "data-pcb-layer": copperLayer
1875
- },
1876
- value: "",
1877
- children: []
1878
- },
1879
- {
1880
- name: "rect",
1881
- type: "element",
1882
- attributes: {
1883
- class: "pcb-hole-inner",
1884
- fill: colorMap2.drill,
1885
- x: (-scaledHoleWidth / 2).toString(),
1886
- y: (-scaledHoleHeight / 2).toString(),
1887
- width: scaledHoleWidth.toString(),
1888
- height: scaledHoleHeight.toString(),
1889
- rx: holeRadius.toString(),
1890
- ry: holeRadius.toString(),
1891
- transform: `translate(${holeCenterX} ${holeCenterY}) rotate(${-rotatedHole.hole_ccw_rotation})`,
1892
- "data-type": "pcb_plated_hole_drill",
1893
- "data-pcb-layer": "drill"
1894
- },
1895
- value: "",
1896
- children: []
1897
- }
1898
- ],
2013
+ children,
1899
2014
  value: ""
1900
2015
  }
1901
2016
  ];
@@ -2552,6 +2667,7 @@ function createSvgObjectsFromSmtPad(pad, ctx) {
2552
2667
  const isCoveredWithSolderMask = Boolean(pad?.is_covered_with_solder_mask);
2553
2668
  const shouldshowSolderMask = showSolderMask && isCoveredWithSolderMask;
2554
2669
  const solderMaskColor = colorMap2.soldermask[pad.layer] ?? colorMap2.soldermask.top;
2670
+ const soldermaskMargin = (pad.soldermask_margin ?? 0) * Math.abs(transform.a);
2555
2671
  if (pad.shape === "rect" || pad.shape === "rotated_rect") {
2556
2672
  const width = pad.width * Math.abs(transform.a);
2557
2673
  const height = pad.height * Math.abs(transform.d);
@@ -2583,6 +2699,9 @@ function createSvgObjectsFromSmtPad(pad, ctx) {
2583
2699
  if (!shouldshowSolderMask) {
2584
2700
  return [padElement2];
2585
2701
  }
2702
+ const maskWidth2 = width + 2 * soldermaskMargin;
2703
+ const maskHeight2 = height + 2 * soldermaskMargin;
2704
+ const maskBorderRadius2 = scaledBorderRadius ? scaledBorderRadius + soldermaskMargin : 0;
2586
2705
  const maskElement2 = {
2587
2706
  name: padElement2.name,
2588
2707
  type: padElement2.type,
@@ -2592,7 +2711,15 @@ function createSvgObjectsFromSmtPad(pad, ctx) {
2592
2711
  ...padElement2.attributes,
2593
2712
  class: "pcb-solder-mask",
2594
2713
  fill: solderMaskColor,
2595
- "data-type": "pcb_soldermask"
2714
+ "data-type": "pcb_soldermask",
2715
+ x: (-maskWidth2 / 2).toString(),
2716
+ y: (-maskHeight2 / 2).toString(),
2717
+ width: maskWidth2.toString(),
2718
+ height: maskHeight2.toString(),
2719
+ ...maskBorderRadius2 > 0 ? {
2720
+ rx: maskBorderRadius2.toString(),
2721
+ ry: maskBorderRadius2.toString()
2722
+ } : {}
2596
2723
  }
2597
2724
  };
2598
2725
  return [padElement2, maskElement2];
@@ -2620,6 +2747,9 @@ function createSvgObjectsFromSmtPad(pad, ctx) {
2620
2747
  if (!shouldshowSolderMask) {
2621
2748
  return [padElement];
2622
2749
  }
2750
+ const maskWidth = width + 2 * soldermaskMargin;
2751
+ const maskHeight = height + 2 * soldermaskMargin;
2752
+ const maskBorderRadius = scaledBorderRadius ? scaledBorderRadius + soldermaskMargin : 0;
2623
2753
  const maskElement = {
2624
2754
  name: padElement.name,
2625
2755
  type: padElement.type,
@@ -2629,7 +2759,15 @@ function createSvgObjectsFromSmtPad(pad, ctx) {
2629
2759
  ...padElement.attributes,
2630
2760
  class: "pcb-solder-mask",
2631
2761
  fill: solderMaskColor,
2632
- "data-type": "pcb_soldermask"
2762
+ "data-type": "pcb_soldermask",
2763
+ x: (x - maskWidth / 2).toString(),
2764
+ y: (y - maskHeight / 2).toString(),
2765
+ width: maskWidth.toString(),
2766
+ height: maskHeight.toString(),
2767
+ ...maskBorderRadius > 0 ? {
2768
+ rx: maskBorderRadius.toString(),
2769
+ ry: maskBorderRadius.toString()
2770
+ } : {}
2633
2771
  }
2634
2772
  };
2635
2773
  return [padElement, maskElement];
@@ -2660,6 +2798,9 @@ function createSvgObjectsFromSmtPad(pad, ctx) {
2660
2798
  if (!shouldshowSolderMask) {
2661
2799
  return [padElement];
2662
2800
  }
2801
+ const maskWidth = width + 2 * soldermaskMargin;
2802
+ const maskHeight = height + 2 * soldermaskMargin;
2803
+ const maskRadius = radius + soldermaskMargin;
2663
2804
  const maskElement = {
2664
2805
  name: padElement.name,
2665
2806
  type: padElement.type,
@@ -2669,7 +2810,13 @@ function createSvgObjectsFromSmtPad(pad, ctx) {
2669
2810
  ...padElement.attributes,
2670
2811
  class: "pcb-solder-mask",
2671
2812
  fill: solderMaskColor,
2672
- "data-type": "pcb_soldermask"
2813
+ "data-type": "pcb_soldermask",
2814
+ x: (x - maskWidth / 2).toString(),
2815
+ y: (y - maskHeight / 2).toString(),
2816
+ width: maskWidth.toString(),
2817
+ height: maskHeight.toString(),
2818
+ rx: maskRadius.toString(),
2819
+ ry: maskRadius.toString()
2673
2820
  }
2674
2821
  };
2675
2822
  return [padElement, maskElement];
@@ -2695,6 +2842,7 @@ function createSvgObjectsFromSmtPad(pad, ctx) {
2695
2842
  if (!shouldshowSolderMask) {
2696
2843
  return [padElement];
2697
2844
  }
2845
+ const maskRadius = radius + soldermaskMargin;
2698
2846
  const maskElement = {
2699
2847
  name: padElement.name,
2700
2848
  type: padElement.type,
@@ -2704,7 +2852,8 @@ function createSvgObjectsFromSmtPad(pad, ctx) {
2704
2852
  ...padElement.attributes,
2705
2853
  class: "pcb-solder-mask",
2706
2854
  fill: solderMaskColor,
2707
- "data-type": "pcb_soldermask"
2855
+ "data-type": "pcb_soldermask",
2856
+ r: maskRadius.toString()
2708
2857
  }
2709
2858
  };
2710
2859
  return [padElement, maskElement];
@@ -2729,6 +2878,23 @@ function createSvgObjectsFromSmtPad(pad, ctx) {
2729
2878
  if (!shouldshowSolderMask) {
2730
2879
  return [padElement];
2731
2880
  }
2881
+ let maskPoints = points;
2882
+ if (soldermaskMargin !== 0) {
2883
+ const centroidX = points.reduce((sum, p) => sum + p[0], 0) / points.length;
2884
+ const centroidY = points.reduce((sum, p) => sum + p[1], 0) / points.length;
2885
+ maskPoints = points.map(([px, py]) => {
2886
+ const dx = px - centroidX;
2887
+ const dy = py - centroidY;
2888
+ const distance3 = Math.sqrt(dx * dx + dy * dy);
2889
+ if (distance3 === 0) return [px, py];
2890
+ const normalizedDx = dx / distance3;
2891
+ const normalizedDy = dy / distance3;
2892
+ return [
2893
+ px + normalizedDx * soldermaskMargin,
2894
+ py + normalizedDy * soldermaskMargin
2895
+ ];
2896
+ });
2897
+ }
2732
2898
  const maskElement = {
2733
2899
  name: padElement.name,
2734
2900
  type: padElement.type,
@@ -2738,7 +2904,8 @@ function createSvgObjectsFromSmtPad(pad, ctx) {
2738
2904
  ...padElement.attributes,
2739
2905
  class: "pcb-solder-mask",
2740
2906
  fill: solderMaskColor,
2741
- "data-type": "pcb_soldermask"
2907
+ "data-type": "pcb_soldermask",
2908
+ points: maskPoints.map((p) => p.join(",")).join(" ")
2742
2909
  }
2743
2910
  };
2744
2911
  return [padElement, maskElement];
@@ -2899,94 +3066,170 @@ function createSvgObjectsFromPcbVia(hole, ctx) {
2899
3066
  // lib/pcb/svg-object-fns/create-svg-objects-from-pcb-hole.ts
2900
3067
  import { applyToPoint as applyToPoint24 } from "transformation-matrix";
2901
3068
  function createSvgObjectsFromPcbHole(hole, ctx) {
2902
- const { transform, colorMap: colorMap2 } = ctx;
3069
+ const { transform, colorMap: colorMap2, showSolderMask } = ctx;
2903
3070
  const [x, y] = applyToPoint24(transform, [hole.x, hole.y]);
3071
+ const soldermaskMargin = (hole.soldermask_margin ?? 0) * Math.abs(transform.a);
3072
+ const shouldShowSolderMask = showSolderMask && soldermaskMargin !== 0;
3073
+ const solderMaskColor = colorMap2.soldermask.top;
2904
3074
  if (hole.hole_shape === "circle" || hole.hole_shape === "square") {
2905
3075
  const scaledDiameter = hole.hole_diameter * Math.abs(transform.a);
2906
3076
  const radius = scaledDiameter / 2;
2907
3077
  if (hole.hole_shape === "circle") {
2908
- return [
2909
- {
2910
- name: "circle",
2911
- type: "element",
2912
- attributes: {
2913
- class: "pcb-hole",
2914
- cx: x.toString(),
2915
- cy: y.toString(),
2916
- r: radius.toString(),
2917
- fill: colorMap2.drill,
2918
- "data-type": "pcb_hole",
2919
- "data-pcb-layer": "drill"
2920
- },
2921
- children: [],
2922
- value: ""
2923
- }
2924
- ];
2925
- }
2926
- return [
2927
- {
2928
- name: "rect",
3078
+ const holeElement2 = {
3079
+ name: "circle",
2929
3080
  type: "element",
2930
3081
  attributes: {
2931
3082
  class: "pcb-hole",
2932
- x: (x - radius).toString(),
2933
- y: (y - radius).toString(),
2934
- width: scaledDiameter.toString(),
2935
- height: scaledDiameter.toString(),
3083
+ cx: x.toString(),
3084
+ cy: y.toString(),
3085
+ r: radius.toString(),
2936
3086
  fill: colorMap2.drill,
2937
3087
  "data-type": "pcb_hole",
2938
3088
  "data-pcb-layer": "drill"
2939
3089
  },
2940
3090
  children: [],
2941
3091
  value: ""
3092
+ };
3093
+ if (!shouldShowSolderMask) {
3094
+ return [holeElement2];
2942
3095
  }
2943
- ];
3096
+ const maskRadius = radius + soldermaskMargin;
3097
+ const maskElement2 = {
3098
+ name: holeElement2.name,
3099
+ type: holeElement2.type,
3100
+ value: "",
3101
+ children: [],
3102
+ attributes: {
3103
+ ...holeElement2.attributes,
3104
+ class: "pcb-solder-mask",
3105
+ fill: solderMaskColor,
3106
+ "data-type": "pcb_soldermask",
3107
+ r: maskRadius.toString()
3108
+ }
3109
+ };
3110
+ return [holeElement2, maskElement2];
3111
+ }
3112
+ const holeElement = {
3113
+ name: "rect",
3114
+ type: "element",
3115
+ attributes: {
3116
+ class: "pcb-hole",
3117
+ x: (x - radius).toString(),
3118
+ y: (y - radius).toString(),
3119
+ width: scaledDiameter.toString(),
3120
+ height: scaledDiameter.toString(),
3121
+ fill: colorMap2.drill,
3122
+ "data-type": "pcb_hole",
3123
+ "data-pcb-layer": "drill"
3124
+ },
3125
+ children: [],
3126
+ value: ""
3127
+ };
3128
+ if (!shouldShowSolderMask) {
3129
+ return [holeElement];
3130
+ }
3131
+ const maskDiameter = scaledDiameter + 2 * soldermaskMargin;
3132
+ const maskElement = {
3133
+ name: holeElement.name,
3134
+ type: holeElement.type,
3135
+ value: "",
3136
+ children: [],
3137
+ attributes: {
3138
+ ...holeElement.attributes,
3139
+ class: "pcb-solder-mask",
3140
+ fill: solderMaskColor,
3141
+ "data-type": "pcb_soldermask",
3142
+ x: (x - maskDiameter / 2).toString(),
3143
+ y: (y - maskDiameter / 2).toString(),
3144
+ width: maskDiameter.toString(),
3145
+ height: maskDiameter.toString()
3146
+ }
3147
+ };
3148
+ return [holeElement, maskElement];
2944
3149
  }
2945
3150
  if (hole.hole_shape === "oval") {
2946
3151
  const scaledWidth = hole.hole_width * Math.abs(transform.a);
2947
3152
  const scaledHeight = hole.hole_height * Math.abs(transform.a);
2948
3153
  const rx = scaledWidth / 2;
2949
3154
  const ry = scaledHeight / 2;
2950
- return [
2951
- {
2952
- name: "ellipse",
2953
- type: "element",
2954
- attributes: {
2955
- class: "pcb-hole",
2956
- cx: x.toString(),
2957
- cy: y.toString(),
2958
- rx: rx.toString(),
2959
- ry: ry.toString(),
2960
- fill: colorMap2.drill,
2961
- "data-type": "pcb_hole",
2962
- "data-pcb-layer": "drill"
2963
- },
2964
- children: [],
2965
- value: ""
3155
+ const holeElement = {
3156
+ name: "ellipse",
3157
+ type: "element",
3158
+ attributes: {
3159
+ class: "pcb-hole",
3160
+ cx: x.toString(),
3161
+ cy: y.toString(),
3162
+ rx: rx.toString(),
3163
+ ry: ry.toString(),
3164
+ fill: colorMap2.drill,
3165
+ "data-type": "pcb_hole",
3166
+ "data-pcb-layer": "drill"
3167
+ },
3168
+ children: [],
3169
+ value: ""
3170
+ };
3171
+ if (!shouldShowSolderMask) {
3172
+ return [holeElement];
3173
+ }
3174
+ const maskRx = rx + soldermaskMargin;
3175
+ const maskRy = ry + soldermaskMargin;
3176
+ const maskElement = {
3177
+ name: holeElement.name,
3178
+ type: holeElement.type,
3179
+ value: "",
3180
+ children: [],
3181
+ attributes: {
3182
+ ...holeElement.attributes,
3183
+ class: "pcb-solder-mask",
3184
+ fill: solderMaskColor,
3185
+ "data-type": "pcb_soldermask",
3186
+ rx: maskRx.toString(),
3187
+ ry: maskRy.toString()
2966
3188
  }
2967
- ];
3189
+ };
3190
+ return [holeElement, maskElement];
2968
3191
  }
2969
3192
  if (hole.hole_shape === "rect") {
2970
3193
  const scaledWidth = hole.hole_width * Math.abs(transform.a);
2971
3194
  const scaledHeight = hole.hole_height * Math.abs(transform.a);
2972
- return [
2973
- {
2974
- name: "rect",
2975
- type: "element",
2976
- attributes: {
2977
- class: "pcb-hole",
2978
- x: (x - scaledWidth / 2).toString(),
2979
- y: (y - scaledHeight / 2).toString(),
2980
- width: scaledWidth.toString(),
2981
- height: scaledHeight.toString(),
2982
- fill: colorMap2.drill,
2983
- "data-type": "pcb_hole",
2984
- "data-pcb-layer": "drill"
2985
- },
2986
- children: [],
2987
- value: ""
3195
+ const holeElement = {
3196
+ name: "rect",
3197
+ type: "element",
3198
+ attributes: {
3199
+ class: "pcb-hole",
3200
+ x: (x - scaledWidth / 2).toString(),
3201
+ y: (y - scaledHeight / 2).toString(),
3202
+ width: scaledWidth.toString(),
3203
+ height: scaledHeight.toString(),
3204
+ fill: colorMap2.drill,
3205
+ "data-type": "pcb_hole",
3206
+ "data-pcb-layer": "drill"
3207
+ },
3208
+ children: [],
3209
+ value: ""
3210
+ };
3211
+ if (!shouldShowSolderMask) {
3212
+ return [holeElement];
3213
+ }
3214
+ const maskWidth = scaledWidth + 2 * soldermaskMargin;
3215
+ const maskHeight = scaledHeight + 2 * soldermaskMargin;
3216
+ const maskElement = {
3217
+ name: holeElement.name,
3218
+ type: holeElement.type,
3219
+ value: "",
3220
+ children: [],
3221
+ attributes: {
3222
+ ...holeElement.attributes,
3223
+ class: "pcb-solder-mask",
3224
+ fill: solderMaskColor,
3225
+ "data-type": "pcb_soldermask",
3226
+ x: (x - maskWidth / 2).toString(),
3227
+ y: (y - maskHeight / 2).toString(),
3228
+ width: maskWidth.toString(),
3229
+ height: maskHeight.toString()
2988
3230
  }
2989
- ];
3231
+ };
3232
+ return [holeElement, maskElement];
2990
3233
  }
2991
3234
  if (hole.hole_shape === "pill") {
2992
3235
  const scaledWidth = hole.hole_width * Math.abs(transform.a);
@@ -3003,21 +3246,50 @@ function createSvgObjectsFromPcbHole(hole, ctx) {
3003
3246
  // Vertical pill (taller than wide)
3004
3247
  `M${x - radius},${y - straightLength / 2} v${straightLength} a${radius},${radius} 0 0 0 ${scaledWidth},0 v-${straightLength} a${radius},${radius} 0 0 0 -${scaledWidth},0 z`
3005
3248
  );
3006
- return [
3007
- {
3008
- name: "path",
3009
- type: "element",
3010
- attributes: {
3011
- class: "pcb-hole",
3012
- fill: colorMap2.drill,
3013
- d: pathD,
3014
- "data-type": "pcb_hole",
3015
- "data-pcb-layer": "drill"
3016
- },
3017
- children: [],
3018
- value: ""
3249
+ const holeElement = {
3250
+ name: "path",
3251
+ type: "element",
3252
+ attributes: {
3253
+ class: "pcb-hole",
3254
+ fill: colorMap2.drill,
3255
+ d: pathD,
3256
+ "data-type": "pcb_hole",
3257
+ "data-pcb-layer": "drill"
3258
+ },
3259
+ children: [],
3260
+ value: ""
3261
+ };
3262
+ if (!shouldShowSolderMask) {
3263
+ return [holeElement];
3264
+ }
3265
+ const maskWidth = scaledWidth + 2 * soldermaskMargin;
3266
+ const maskHeight = scaledHeight + 2 * soldermaskMargin;
3267
+ const maskIsHorizontal = maskWidth > maskHeight;
3268
+ const maskRadius = Math.min(maskWidth, maskHeight) / 2;
3269
+ const maskStraightLength = Math.abs(
3270
+ maskIsHorizontal ? maskWidth - maskHeight : maskHeight - maskWidth
3271
+ );
3272
+ const maskPathD = maskIsHorizontal ? (
3273
+ // Horizontal pill (wider than tall)
3274
+ `M${x - maskStraightLength / 2},${y - maskRadius} h${maskStraightLength} a${maskRadius},${maskRadius} 0 0 1 0,${maskHeight} h-${maskStraightLength} a${maskRadius},${maskRadius} 0 0 1 0,-${maskHeight} z`
3275
+ ) : (
3276
+ // Vertical pill (taller than wide)
3277
+ `M${x - maskRadius},${y - maskStraightLength / 2} v${maskStraightLength} a${maskRadius},${maskRadius} 0 0 0 ${maskWidth},0 v-${maskStraightLength} a${maskRadius},${maskRadius} 0 0 0 -${maskWidth},0 z`
3278
+ );
3279
+ const maskElement = {
3280
+ name: holeElement.name,
3281
+ type: holeElement.type,
3282
+ value: "",
3283
+ children: [],
3284
+ attributes: {
3285
+ ...holeElement.attributes,
3286
+ class: "pcb-solder-mask",
3287
+ fill: solderMaskColor,
3288
+ "data-type": "pcb_soldermask",
3289
+ d: maskPathD
3019
3290
  }
3020
- ];
3291
+ };
3292
+ return [holeElement, maskElement];
3021
3293
  }
3022
3294
  if (hole.hole_shape === "rotated_pill") {
3023
3295
  const scaledWidth = hole.hole_width * Math.abs(transform.a);
@@ -3035,22 +3307,51 @@ function createSvgObjectsFromPcbHole(hole, ctx) {
3035
3307
  // Vertical pill (taller than wide)
3036
3308
  `M${-radius},${-straightLength / 2} v${straightLength} a${radius},${radius} 0 0 0 ${scaledWidth},0 v-${straightLength} a${radius},${radius} 0 0 0 -${scaledWidth},0 z`
3037
3309
  );
3038
- return [
3039
- {
3040
- name: "path",
3041
- type: "element",
3042
- attributes: {
3043
- class: "pcb-hole",
3044
- fill: colorMap2.drill,
3045
- d: pathD,
3046
- transform: `translate(${x} ${y}) rotate(${-rotation})`,
3047
- "data-type": "pcb_hole",
3048
- "data-pcb-layer": "drill"
3049
- },
3050
- children: [],
3051
- value: ""
3310
+ const holeElement = {
3311
+ name: "path",
3312
+ type: "element",
3313
+ attributes: {
3314
+ class: "pcb-hole",
3315
+ fill: colorMap2.drill,
3316
+ d: pathD,
3317
+ transform: `translate(${x} ${y}) rotate(${-rotation})`,
3318
+ "data-type": "pcb_hole",
3319
+ "data-pcb-layer": "drill"
3320
+ },
3321
+ children: [],
3322
+ value: ""
3323
+ };
3324
+ if (!shouldShowSolderMask) {
3325
+ return [holeElement];
3326
+ }
3327
+ const maskWidth = scaledWidth + 2 * soldermaskMargin;
3328
+ const maskHeight = scaledHeight + 2 * soldermaskMargin;
3329
+ const maskIsHorizontal = maskWidth > maskHeight;
3330
+ const maskRadius = Math.min(maskWidth, maskHeight) / 2;
3331
+ const maskStraightLength = Math.abs(
3332
+ maskIsHorizontal ? maskWidth - maskHeight : maskHeight - maskWidth
3333
+ );
3334
+ const maskPathD = maskIsHorizontal ? (
3335
+ // Horizontal pill (wider than tall)
3336
+ `M${-maskStraightLength / 2},${-maskRadius} h${maskStraightLength} a${maskRadius},${maskRadius} 0 0 1 0,${maskHeight} h-${maskStraightLength} a${maskRadius},${maskRadius} 0 0 1 0,-${maskHeight} z`
3337
+ ) : (
3338
+ // Vertical pill (taller than wide)
3339
+ `M${-maskRadius},${-maskStraightLength / 2} v${maskStraightLength} a${maskRadius},${maskRadius} 0 0 0 ${maskWidth},0 v-${maskStraightLength} a${maskRadius},${maskRadius} 0 0 0 -${maskWidth},0 z`
3340
+ );
3341
+ const maskElement = {
3342
+ name: holeElement.name,
3343
+ type: holeElement.type,
3344
+ value: "",
3345
+ children: [],
3346
+ attributes: {
3347
+ ...holeElement.attributes,
3348
+ class: "pcb-solder-mask",
3349
+ fill: solderMaskColor,
3350
+ "data-type": "pcb_soldermask",
3351
+ d: maskPathD
3052
3352
  }
3053
- ];
3353
+ };
3354
+ return [holeElement, maskElement];
3054
3355
  }
3055
3356
  return [];
3056
3357
  }
@@ -3978,7 +4279,7 @@ function getSoftwareUsedString(circuitJson) {
3978
4279
  var package_default = {
3979
4280
  name: "circuit-to-svg",
3980
4281
  type: "module",
3981
- version: "0.0.270",
4282
+ version: "0.0.272",
3982
4283
  description: "Convert Circuit JSON to SVG",
3983
4284
  main: "dist/index.js",
3984
4285
  files: [
@@ -4002,7 +4303,7 @@ var package_default = {
4002
4303
  "bun-match-svg": "^0.0.12",
4003
4304
  esbuild: "^0.20.2",
4004
4305
  "performance-now": "^2.1.0",
4005
- "circuit-json": "^0.0.315",
4306
+ "circuit-json": "^0.0.316",
4006
4307
  react: "19.1.0",
4007
4308
  "react-cosmos": "7.0.0",
4008
4309
  "react-cosmos-plugin-vite": "7.0.0",