easyeda 0.0.230 → 0.0.232
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/browser/{chunk-6S7LH5YP.js → chunk-U5UUPCU3.js} +1200 -1005
- package/dist/browser/chunk-U5UUPCU3.js.map +1 -0
- package/dist/browser/{dist-K77PLYJN.js → dist-7RT7ZSAD.js} +2335 -457
- package/dist/browser/dist-7RT7ZSAD.js.map +1 -0
- package/dist/browser/index.js +20 -18
- package/dist/browser/index.js.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/main.cjs +2633 -2436
- package/dist/main.cjs.map +1 -1
- package/package.json +1 -1
- package/dist/browser/chunk-6S7LH5YP.js.map +0 -1
- package/dist/browser/dist-K77PLYJN.js.map +0 -1
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
toSVG,
|
|
15
15
|
toString,
|
|
16
16
|
translate
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-U5UUPCU3.js";
|
|
18
18
|
|
|
19
19
|
// node_modules/circuit-to-svg/dist/index.js
|
|
20
20
|
var import_svgson = __toESM(require_svgson_umd(), 1);
|
|
@@ -1560,9 +1560,14 @@ function createSvgObjectsFromPcbNoteLine(noteLine, ctx) {
|
|
|
1560
1560
|
return [svgObject];
|
|
1561
1561
|
}
|
|
1562
1562
|
function createSvgObjectsFromPcbPlatedHole(hole, ctx) {
|
|
1563
|
-
const { transform, colorMap: colorMap2 } = ctx;
|
|
1563
|
+
const { transform, colorMap: colorMap2, showSolderMask } = ctx;
|
|
1564
1564
|
const [x, y] = applyToPoint(transform, [hole.x, hole.y]);
|
|
1565
|
-
const
|
|
1565
|
+
const layer = Array.isArray(hole.layers) && hole.layers[0] || hole.layer || "top";
|
|
1566
|
+
const maskLayer = layer;
|
|
1567
|
+
const isCoveredWithSolderMask = Boolean(hole.is_covered_with_solder_mask);
|
|
1568
|
+
const soldermaskMargin = (hole.soldermask_margin ?? 0) * Math.abs(transform.a);
|
|
1569
|
+
const shouldShowSolderMask = showSolderMask && isCoveredWithSolderMask && soldermaskMargin !== 0;
|
|
1570
|
+
const solderMaskColor = colorMap2.soldermaskWithCopperUnderneath.top;
|
|
1566
1571
|
if (hole.shape === "pill") {
|
|
1567
1572
|
const scaledOuterWidth = hole.outer_width * Math.abs(transform.a);
|
|
1568
1573
|
const scaledOuterHeight = hole.outer_height * Math.abs(transform.a);
|
|
@@ -1585,46 +1590,161 @@ function createSvgObjectsFromPcbPlatedHole(hole, ctx) {
|
|
|
1585
1590
|
return `M${-radius},0 a${radius},${radius} 0 0 1 ${width},0 a${radius},${radius} 0 0 1 ${-width},0 z`;
|
|
1586
1591
|
}
|
|
1587
1592
|
};
|
|
1588
|
-
|
|
1593
|
+
let children = [
|
|
1594
|
+
// Outer pill shape
|
|
1589
1595
|
{
|
|
1590
|
-
name: "
|
|
1596
|
+
name: "path",
|
|
1591
1597
|
type: "element",
|
|
1592
1598
|
attributes: {
|
|
1599
|
+
class: "pcb-hole-outer",
|
|
1600
|
+
fill: colorMap2.copper.top,
|
|
1601
|
+
d: createPillPath(scaledOuterWidth, scaledOuterHeight),
|
|
1602
|
+
transform: outerTransform,
|
|
1593
1603
|
"data-type": "pcb_plated_hole",
|
|
1594
|
-
"data-pcb-layer":
|
|
1604
|
+
"data-pcb-layer": layer
|
|
1595
1605
|
},
|
|
1596
|
-
|
|
1597
|
-
|
|
1606
|
+
value: "",
|
|
1607
|
+
children: []
|
|
1608
|
+
},
|
|
1609
|
+
// Inner pill shape
|
|
1610
|
+
{
|
|
1611
|
+
name: "path",
|
|
1612
|
+
type: "element",
|
|
1613
|
+
attributes: {
|
|
1614
|
+
class: "pcb-hole-inner",
|
|
1615
|
+
fill: colorMap2.drill,
|
|
1616
|
+
d: createPillPath(scaledHoleWidth, scaledHoleHeight),
|
|
1617
|
+
transform: innerTransform,
|
|
1618
|
+
"data-type": "pcb_plated_hole_drill",
|
|
1619
|
+
"data-pcb-layer": "drill"
|
|
1620
|
+
},
|
|
1621
|
+
value: "",
|
|
1622
|
+
children: []
|
|
1623
|
+
}
|
|
1624
|
+
];
|
|
1625
|
+
if (shouldShowSolderMask) {
|
|
1626
|
+
const maskWidth = scaledOuterWidth + 2 * soldermaskMargin;
|
|
1627
|
+
const maskHeight = scaledOuterHeight + 2 * soldermaskMargin;
|
|
1628
|
+
if (soldermaskMargin < 0) {
|
|
1629
|
+
children = [
|
|
1630
|
+
// 1. Draw the outer pad in soldermask color (covered)
|
|
1598
1631
|
{
|
|
1599
1632
|
name: "path",
|
|
1600
1633
|
type: "element",
|
|
1601
1634
|
attributes: {
|
|
1602
|
-
class: "pcb-hole-outer",
|
|
1603
|
-
fill:
|
|
1635
|
+
class: "pcb-hole-outer-covered",
|
|
1636
|
+
fill: solderMaskColor,
|
|
1604
1637
|
d: createPillPath(scaledOuterWidth, scaledOuterHeight),
|
|
1605
1638
|
transform: outerTransform,
|
|
1606
1639
|
"data-type": "pcb_plated_hole",
|
|
1607
|
-
"data-pcb-layer":
|
|
1640
|
+
"data-pcb-layer": layer
|
|
1608
1641
|
},
|
|
1609
1642
|
value: "",
|
|
1610
1643
|
children: []
|
|
1611
1644
|
},
|
|
1612
|
-
//
|
|
1645
|
+
// 2. Draw the exposed opening in copper color
|
|
1613
1646
|
{
|
|
1614
1647
|
name: "path",
|
|
1615
1648
|
type: "element",
|
|
1616
1649
|
attributes: {
|
|
1617
|
-
class: "pcb-hole-
|
|
1618
|
-
fill: colorMap2.
|
|
1619
|
-
d: createPillPath(
|
|
1620
|
-
transform:
|
|
1621
|
-
"data-type": "
|
|
1622
|
-
"data-pcb-layer":
|
|
1650
|
+
class: "pcb-hole-outer-exposed",
|
|
1651
|
+
fill: colorMap2.copper.top,
|
|
1652
|
+
d: createPillPath(maskWidth, maskHeight),
|
|
1653
|
+
transform: outerTransform,
|
|
1654
|
+
"data-type": "pcb_soldermask",
|
|
1655
|
+
"data-pcb-layer": maskLayer
|
|
1623
1656
|
},
|
|
1624
1657
|
value: "",
|
|
1625
1658
|
children: []
|
|
1626
|
-
}
|
|
1627
|
-
|
|
1659
|
+
},
|
|
1660
|
+
// 3. Draw the drill hole on top
|
|
1661
|
+
children[1]
|
|
1662
|
+
// Original inner hole
|
|
1663
|
+
];
|
|
1664
|
+
} else {
|
|
1665
|
+
children.unshift({
|
|
1666
|
+
name: "path",
|
|
1667
|
+
type: "element",
|
|
1668
|
+
attributes: {
|
|
1669
|
+
class: "pcb-soldermask-cutout",
|
|
1670
|
+
fill: colorMap2.substrate,
|
|
1671
|
+
d: createPillPath(maskWidth, maskHeight),
|
|
1672
|
+
transform: outerTransform,
|
|
1673
|
+
"data-type": "pcb_soldermask_opening",
|
|
1674
|
+
"data-pcb-layer": maskLayer
|
|
1675
|
+
},
|
|
1676
|
+
value: "",
|
|
1677
|
+
children: []
|
|
1678
|
+
});
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1681
|
+
return [
|
|
1682
|
+
{
|
|
1683
|
+
name: "g",
|
|
1684
|
+
type: "element",
|
|
1685
|
+
attributes: {
|
|
1686
|
+
"data-type": "pcb_plated_hole",
|
|
1687
|
+
"data-pcb-layer": "through"
|
|
1688
|
+
},
|
|
1689
|
+
children,
|
|
1690
|
+
value: ""
|
|
1691
|
+
}
|
|
1692
|
+
];
|
|
1693
|
+
}
|
|
1694
|
+
if (hole.shape === "oval") {
|
|
1695
|
+
const scaledOuterWidth = hole.outer_width * Math.abs(transform.a);
|
|
1696
|
+
const scaledOuterHeight = hole.outer_height * Math.abs(transform.a);
|
|
1697
|
+
const scaledHoleWidth = hole.hole_width * Math.abs(transform.a);
|
|
1698
|
+
const scaledHoleHeight = hole.hole_height * Math.abs(transform.a);
|
|
1699
|
+
const rotation = hole.ccw_rotation || 0;
|
|
1700
|
+
const transformStr = rotation ? `translate(${x} ${y}) rotate(${-rotation})` : `translate(${x} ${y})`;
|
|
1701
|
+
const children = [
|
|
1702
|
+
// Outer oval shape
|
|
1703
|
+
{
|
|
1704
|
+
name: "ellipse",
|
|
1705
|
+
type: "element",
|
|
1706
|
+
attributes: {
|
|
1707
|
+
class: "pcb-hole-outer",
|
|
1708
|
+
fill: colorMap2.copper.top,
|
|
1709
|
+
cx: "0",
|
|
1710
|
+
cy: "0",
|
|
1711
|
+
rx: (scaledOuterWidth / 2).toString(),
|
|
1712
|
+
ry: (scaledOuterHeight / 2).toString(),
|
|
1713
|
+
transform: transformStr,
|
|
1714
|
+
"data-type": "pcb_plated_hole",
|
|
1715
|
+
"data-pcb-layer": layer
|
|
1716
|
+
},
|
|
1717
|
+
value: "",
|
|
1718
|
+
children: []
|
|
1719
|
+
},
|
|
1720
|
+
// Inner oval shape
|
|
1721
|
+
{
|
|
1722
|
+
name: "ellipse",
|
|
1723
|
+
type: "element",
|
|
1724
|
+
attributes: {
|
|
1725
|
+
class: "pcb-hole-inner",
|
|
1726
|
+
fill: colorMap2.drill,
|
|
1727
|
+
cx: "0",
|
|
1728
|
+
cy: "0",
|
|
1729
|
+
rx: (scaledHoleWidth / 2).toString(),
|
|
1730
|
+
ry: (scaledHoleHeight / 2).toString(),
|
|
1731
|
+
transform: transformStr,
|
|
1732
|
+
"data-type": "pcb_plated_hole_drill",
|
|
1733
|
+
"data-pcb-layer": "drill"
|
|
1734
|
+
},
|
|
1735
|
+
value: "",
|
|
1736
|
+
children: []
|
|
1737
|
+
}
|
|
1738
|
+
];
|
|
1739
|
+
return [
|
|
1740
|
+
{
|
|
1741
|
+
name: "g",
|
|
1742
|
+
type: "element",
|
|
1743
|
+
attributes: {
|
|
1744
|
+
"data-type": "pcb_plated_hole",
|
|
1745
|
+
"data-pcb-layer": "through"
|
|
1746
|
+
},
|
|
1747
|
+
children,
|
|
1628
1748
|
value: ""
|
|
1629
1749
|
}
|
|
1630
1750
|
];
|
|
@@ -1636,46 +1756,105 @@ function createSvgObjectsFromPcbPlatedHole(hole, ctx) {
|
|
|
1636
1756
|
const scaledHoleHeight = hole.hole_diameter * Math.abs(transform.a);
|
|
1637
1757
|
const outerRadius = Math.min(scaledOuterWidth, scaledOuterHeight) / 2;
|
|
1638
1758
|
const innerRadius = Math.min(scaledHoleWidth, scaledHoleHeight) / 2;
|
|
1639
|
-
|
|
1759
|
+
let children = [
|
|
1640
1760
|
{
|
|
1641
|
-
name: "
|
|
1761
|
+
name: "circle",
|
|
1642
1762
|
type: "element",
|
|
1643
1763
|
attributes: {
|
|
1764
|
+
class: "pcb-hole-outer",
|
|
1765
|
+
fill: colorMap2.copper.top,
|
|
1766
|
+
cx: x.toString(),
|
|
1767
|
+
cy: y.toString(),
|
|
1768
|
+
r: outerRadius.toString(),
|
|
1644
1769
|
"data-type": "pcb_plated_hole",
|
|
1645
|
-
"data-pcb-layer":
|
|
1770
|
+
"data-pcb-layer": layer
|
|
1646
1771
|
},
|
|
1647
|
-
|
|
1772
|
+
value: "",
|
|
1773
|
+
children: []
|
|
1774
|
+
},
|
|
1775
|
+
{
|
|
1776
|
+
name: "circle",
|
|
1777
|
+
type: "element",
|
|
1778
|
+
attributes: {
|
|
1779
|
+
class: "pcb-hole-inner",
|
|
1780
|
+
fill: colorMap2.drill,
|
|
1781
|
+
cx: x.toString(),
|
|
1782
|
+
cy: y.toString(),
|
|
1783
|
+
r: innerRadius.toString(),
|
|
1784
|
+
"data-type": "pcb_plated_hole_drill",
|
|
1785
|
+
"data-pcb-layer": "drill"
|
|
1786
|
+
},
|
|
1787
|
+
value: "",
|
|
1788
|
+
children: []
|
|
1789
|
+
}
|
|
1790
|
+
];
|
|
1791
|
+
if (shouldShowSolderMask) {
|
|
1792
|
+
const maskRadius = outerRadius + soldermaskMargin;
|
|
1793
|
+
if (soldermaskMargin < 0) {
|
|
1794
|
+
children = [
|
|
1795
|
+
// 1. Draw the outer ring in soldermask color (covered)
|
|
1648
1796
|
{
|
|
1649
1797
|
name: "circle",
|
|
1650
1798
|
type: "element",
|
|
1651
1799
|
attributes: {
|
|
1652
|
-
class: "pcb-hole-outer",
|
|
1653
|
-
fill:
|
|
1800
|
+
class: "pcb-hole-outer-covered",
|
|
1801
|
+
fill: solderMaskColor,
|
|
1654
1802
|
cx: x.toString(),
|
|
1655
1803
|
cy: y.toString(),
|
|
1656
1804
|
r: outerRadius.toString(),
|
|
1657
1805
|
"data-type": "pcb_plated_hole",
|
|
1658
|
-
"data-pcb-layer":
|
|
1806
|
+
"data-pcb-layer": layer
|
|
1659
1807
|
},
|
|
1660
1808
|
value: "",
|
|
1661
1809
|
children: []
|
|
1662
1810
|
},
|
|
1811
|
+
// 2. Draw the exposed opening in copper color
|
|
1663
1812
|
{
|
|
1664
1813
|
name: "circle",
|
|
1665
1814
|
type: "element",
|
|
1666
1815
|
attributes: {
|
|
1667
|
-
class: "pcb-hole-
|
|
1668
|
-
fill: colorMap2.
|
|
1816
|
+
class: "pcb-hole-outer-exposed",
|
|
1817
|
+
fill: colorMap2.copper.top,
|
|
1669
1818
|
cx: x.toString(),
|
|
1670
1819
|
cy: y.toString(),
|
|
1671
|
-
r:
|
|
1672
|
-
"data-type": "
|
|
1673
|
-
"data-pcb-layer":
|
|
1820
|
+
r: maskRadius.toString(),
|
|
1821
|
+
"data-type": "pcb_soldermask",
|
|
1822
|
+
"data-pcb-layer": maskLayer
|
|
1674
1823
|
},
|
|
1675
1824
|
value: "",
|
|
1676
1825
|
children: []
|
|
1677
|
-
}
|
|
1678
|
-
|
|
1826
|
+
},
|
|
1827
|
+
// 3. Draw the drill hole on top
|
|
1828
|
+
children[1]
|
|
1829
|
+
// Original inner hole
|
|
1830
|
+
];
|
|
1831
|
+
} else {
|
|
1832
|
+
children.unshift({
|
|
1833
|
+
name: "circle",
|
|
1834
|
+
type: "element",
|
|
1835
|
+
attributes: {
|
|
1836
|
+
class: "pcb-soldermask-cutout",
|
|
1837
|
+
fill: colorMap2.substrate,
|
|
1838
|
+
cx: x.toString(),
|
|
1839
|
+
cy: y.toString(),
|
|
1840
|
+
r: maskRadius.toString(),
|
|
1841
|
+
"data-type": "pcb_soldermask_opening",
|
|
1842
|
+
"data-pcb-layer": maskLayer
|
|
1843
|
+
},
|
|
1844
|
+
value: "",
|
|
1845
|
+
children: []
|
|
1846
|
+
});
|
|
1847
|
+
}
|
|
1848
|
+
}
|
|
1849
|
+
return [
|
|
1850
|
+
{
|
|
1851
|
+
name: "g",
|
|
1852
|
+
type: "element",
|
|
1853
|
+
attributes: {
|
|
1854
|
+
"data-type": "pcb_plated_hole",
|
|
1855
|
+
"data-pcb-layer": "through"
|
|
1856
|
+
},
|
|
1857
|
+
children,
|
|
1679
1858
|
value: ""
|
|
1680
1859
|
}
|
|
1681
1860
|
];
|
|
@@ -1691,22 +1870,58 @@ function createSvgObjectsFromPcbPlatedHole(hole, ctx) {
|
|
|
1691
1870
|
h.x + (h.hole_offset_x ?? 0),
|
|
1692
1871
|
h.y + (h.hole_offset_y ?? 0)
|
|
1693
1872
|
]);
|
|
1694
|
-
|
|
1873
|
+
let children = [
|
|
1874
|
+
// Rectangular pad (outer shape)
|
|
1695
1875
|
{
|
|
1696
|
-
name: "
|
|
1876
|
+
name: "rect",
|
|
1697
1877
|
type: "element",
|
|
1698
1878
|
attributes: {
|
|
1879
|
+
class: "pcb-hole-outer-pad",
|
|
1880
|
+
fill: colorMap2.copper.top,
|
|
1881
|
+
x: (x - scaledRectPadWidth / 2).toString(),
|
|
1882
|
+
y: (y - scaledRectPadHeight / 2).toString(),
|
|
1883
|
+
width: scaledRectPadWidth.toString(),
|
|
1884
|
+
height: scaledRectPadHeight.toString(),
|
|
1885
|
+
...scaledRectBorderRadius ? {
|
|
1886
|
+
rx: scaledRectBorderRadius.toString(),
|
|
1887
|
+
ry: scaledRectBorderRadius.toString()
|
|
1888
|
+
} : {},
|
|
1699
1889
|
"data-type": "pcb_plated_hole",
|
|
1700
|
-
"data-pcb-layer":
|
|
1890
|
+
"data-pcb-layer": layer
|
|
1701
1891
|
},
|
|
1702
|
-
|
|
1703
|
-
|
|
1892
|
+
value: "",
|
|
1893
|
+
children: []
|
|
1894
|
+
},
|
|
1895
|
+
// Circular hole inside the rectangle (with optional offset)
|
|
1896
|
+
{
|
|
1897
|
+
name: "circle",
|
|
1898
|
+
type: "element",
|
|
1899
|
+
attributes: {
|
|
1900
|
+
class: "pcb-hole-inner",
|
|
1901
|
+
fill: colorMap2.drill,
|
|
1902
|
+
cx: holeCx.toString(),
|
|
1903
|
+
cy: holeCy.toString(),
|
|
1904
|
+
r: holeRadius.toString(),
|
|
1905
|
+
"data-type": "pcb_plated_hole_drill",
|
|
1906
|
+
"data-pcb-layer": "drill"
|
|
1907
|
+
},
|
|
1908
|
+
value: "",
|
|
1909
|
+
children: []
|
|
1910
|
+
}
|
|
1911
|
+
];
|
|
1912
|
+
if (shouldShowSolderMask) {
|
|
1913
|
+
const maskWidth = scaledRectPadWidth + 2 * soldermaskMargin;
|
|
1914
|
+
const maskHeight = scaledRectPadHeight + 2 * soldermaskMargin;
|
|
1915
|
+
const maskBorderRadius = scaledRectBorderRadius + soldermaskMargin;
|
|
1916
|
+
if (soldermaskMargin < 0) {
|
|
1917
|
+
children = [
|
|
1918
|
+
// 1. Draw the outer pad in soldermask color (covered)
|
|
1704
1919
|
{
|
|
1705
1920
|
name: "rect",
|
|
1706
1921
|
type: "element",
|
|
1707
1922
|
attributes: {
|
|
1708
|
-
class: "pcb-hole-outer-
|
|
1709
|
-
fill:
|
|
1923
|
+
class: "pcb-hole-outer-covered",
|
|
1924
|
+
fill: solderMaskColor,
|
|
1710
1925
|
x: (x - scaledRectPadWidth / 2).toString(),
|
|
1711
1926
|
y: (y - scaledRectPadHeight / 2).toString(),
|
|
1712
1927
|
width: scaledRectPadWidth.toString(),
|
|
@@ -1716,28 +1931,68 @@ function createSvgObjectsFromPcbPlatedHole(hole, ctx) {
|
|
|
1716
1931
|
ry: scaledRectBorderRadius.toString()
|
|
1717
1932
|
} : {},
|
|
1718
1933
|
"data-type": "pcb_plated_hole",
|
|
1719
|
-
"data-pcb-layer":
|
|
1934
|
+
"data-pcb-layer": layer
|
|
1720
1935
|
},
|
|
1721
1936
|
value: "",
|
|
1722
1937
|
children: []
|
|
1723
1938
|
},
|
|
1724
|
-
//
|
|
1939
|
+
// 2. Draw the exposed opening in copper color
|
|
1725
1940
|
{
|
|
1726
|
-
name: "
|
|
1941
|
+
name: "rect",
|
|
1727
1942
|
type: "element",
|
|
1728
1943
|
attributes: {
|
|
1729
|
-
class: "pcb-hole-
|
|
1730
|
-
fill: colorMap2.
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1944
|
+
class: "pcb-hole-outer-exposed",
|
|
1945
|
+
fill: colorMap2.copper.top,
|
|
1946
|
+
x: (x - maskWidth / 2).toString(),
|
|
1947
|
+
y: (y - maskHeight / 2).toString(),
|
|
1948
|
+
width: maskWidth.toString(),
|
|
1949
|
+
height: maskHeight.toString(),
|
|
1950
|
+
...maskBorderRadius > 0 ? {
|
|
1951
|
+
rx: maskBorderRadius.toString(),
|
|
1952
|
+
ry: maskBorderRadius.toString()
|
|
1953
|
+
} : {},
|
|
1954
|
+
"data-type": "pcb_soldermask",
|
|
1955
|
+
"data-pcb-layer": maskLayer
|
|
1736
1956
|
},
|
|
1737
1957
|
value: "",
|
|
1738
1958
|
children: []
|
|
1739
|
-
}
|
|
1740
|
-
|
|
1959
|
+
},
|
|
1960
|
+
// 3. Draw the drill hole on top
|
|
1961
|
+
children[1]
|
|
1962
|
+
// Original hole
|
|
1963
|
+
];
|
|
1964
|
+
} else {
|
|
1965
|
+
children.unshift({
|
|
1966
|
+
name: "rect",
|
|
1967
|
+
type: "element",
|
|
1968
|
+
attributes: {
|
|
1969
|
+
class: "pcb-soldermask-cutout",
|
|
1970
|
+
fill: colorMap2.substrate,
|
|
1971
|
+
x: (x - maskWidth / 2).toString(),
|
|
1972
|
+
y: (y - maskHeight / 2).toString(),
|
|
1973
|
+
width: maskWidth.toString(),
|
|
1974
|
+
height: maskHeight.toString(),
|
|
1975
|
+
...scaledRectBorderRadius ? {
|
|
1976
|
+
rx: maskBorderRadius.toString(),
|
|
1977
|
+
ry: maskBorderRadius.toString()
|
|
1978
|
+
} : {},
|
|
1979
|
+
"data-type": "pcb_soldermask_opening",
|
|
1980
|
+
"data-pcb-layer": maskLayer
|
|
1981
|
+
},
|
|
1982
|
+
value: "",
|
|
1983
|
+
children: []
|
|
1984
|
+
});
|
|
1985
|
+
}
|
|
1986
|
+
}
|
|
1987
|
+
return [
|
|
1988
|
+
{
|
|
1989
|
+
name: "g",
|
|
1990
|
+
type: "element",
|
|
1991
|
+
attributes: {
|
|
1992
|
+
"data-type": "pcb_plated_hole",
|
|
1993
|
+
"data-pcb-layer": "through"
|
|
1994
|
+
},
|
|
1995
|
+
children,
|
|
1741
1996
|
value: ""
|
|
1742
1997
|
}
|
|
1743
1998
|
];
|
|
@@ -1757,22 +2012,61 @@ function createSvgObjectsFromPcbPlatedHole(hole, ctx) {
|
|
|
1757
2012
|
pillHole.y + holeOffsetY
|
|
1758
2013
|
]);
|
|
1759
2014
|
const holeRadius = Math.min(scaledHoleHeight, scaledHoleWidth) / 2;
|
|
1760
|
-
|
|
2015
|
+
let children = [
|
|
2016
|
+
// Rectangular pad (outer shape)
|
|
1761
2017
|
{
|
|
1762
|
-
name: "
|
|
2018
|
+
name: "rect",
|
|
1763
2019
|
type: "element",
|
|
1764
2020
|
attributes: {
|
|
2021
|
+
class: "pcb-hole-outer-pad",
|
|
2022
|
+
fill: colorMap2.copper.top,
|
|
2023
|
+
x: (x - scaledRectPadWidth / 2).toString(),
|
|
2024
|
+
y: (y - scaledRectPadHeight / 2).toString(),
|
|
2025
|
+
width: scaledRectPadWidth.toString(),
|
|
2026
|
+
height: scaledRectPadHeight.toString(),
|
|
2027
|
+
...scaledRectBorderRadius ? {
|
|
2028
|
+
rx: scaledRectBorderRadius.toString(),
|
|
2029
|
+
ry: scaledRectBorderRadius.toString()
|
|
2030
|
+
} : {},
|
|
1765
2031
|
"data-type": "pcb_plated_hole",
|
|
1766
|
-
"data-pcb-layer":
|
|
2032
|
+
"data-pcb-layer": layer
|
|
1767
2033
|
},
|
|
1768
|
-
|
|
1769
|
-
|
|
2034
|
+
value: "",
|
|
2035
|
+
children: []
|
|
2036
|
+
},
|
|
2037
|
+
// pill hole inside the rectangle
|
|
2038
|
+
{
|
|
2039
|
+
name: "rect",
|
|
2040
|
+
type: "element",
|
|
2041
|
+
attributes: {
|
|
2042
|
+
class: "pcb-hole-inner",
|
|
2043
|
+
fill: colorMap2.drill,
|
|
2044
|
+
x: (holeCenterX - scaledHoleWidth / 2).toString(),
|
|
2045
|
+
y: (holeCenterY - scaledHoleHeight / 2).toString(),
|
|
2046
|
+
width: scaledHoleWidth.toString(),
|
|
2047
|
+
height: scaledHoleHeight.toString(),
|
|
2048
|
+
rx: holeRadius.toString(),
|
|
2049
|
+
ry: holeRadius.toString(),
|
|
2050
|
+
"data-type": "pcb_plated_hole_drill",
|
|
2051
|
+
"data-pcb-layer": "drill"
|
|
2052
|
+
},
|
|
2053
|
+
value: "",
|
|
2054
|
+
children: []
|
|
2055
|
+
}
|
|
2056
|
+
];
|
|
2057
|
+
if (shouldShowSolderMask) {
|
|
2058
|
+
const maskWidth = scaledRectPadWidth + 2 * soldermaskMargin;
|
|
2059
|
+
const maskHeight = scaledRectPadHeight + 2 * soldermaskMargin;
|
|
2060
|
+
const maskBorderRadius = scaledRectBorderRadius + soldermaskMargin;
|
|
2061
|
+
if (soldermaskMargin < 0) {
|
|
2062
|
+
children = [
|
|
2063
|
+
// 1. Draw the outer pad in soldermask color (covered)
|
|
1770
2064
|
{
|
|
1771
2065
|
name: "rect",
|
|
1772
2066
|
type: "element",
|
|
1773
2067
|
attributes: {
|
|
1774
|
-
class: "pcb-hole-outer-
|
|
1775
|
-
fill:
|
|
2068
|
+
class: "pcb-hole-outer-covered",
|
|
2069
|
+
fill: solderMaskColor,
|
|
1776
2070
|
x: (x - scaledRectPadWidth / 2).toString(),
|
|
1777
2071
|
y: (y - scaledRectPadHeight / 2).toString(),
|
|
1778
2072
|
width: scaledRectPadWidth.toString(),
|
|
@@ -1782,31 +2076,68 @@ function createSvgObjectsFromPcbPlatedHole(hole, ctx) {
|
|
|
1782
2076
|
ry: scaledRectBorderRadius.toString()
|
|
1783
2077
|
} : {},
|
|
1784
2078
|
"data-type": "pcb_plated_hole",
|
|
1785
|
-
"data-pcb-layer":
|
|
2079
|
+
"data-pcb-layer": layer
|
|
1786
2080
|
},
|
|
1787
2081
|
value: "",
|
|
1788
2082
|
children: []
|
|
1789
2083
|
},
|
|
1790
|
-
//
|
|
2084
|
+
// 2. Draw the exposed opening in copper color
|
|
1791
2085
|
{
|
|
1792
2086
|
name: "rect",
|
|
1793
2087
|
type: "element",
|
|
1794
2088
|
attributes: {
|
|
1795
|
-
class: "pcb-hole-
|
|
1796
|
-
fill: colorMap2.
|
|
1797
|
-
x: (
|
|
1798
|
-
y: (
|
|
1799
|
-
width:
|
|
1800
|
-
height:
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
2089
|
+
class: "pcb-hole-outer-exposed",
|
|
2090
|
+
fill: colorMap2.copper.top,
|
|
2091
|
+
x: (x - maskWidth / 2).toString(),
|
|
2092
|
+
y: (y - maskHeight / 2).toString(),
|
|
2093
|
+
width: maskWidth.toString(),
|
|
2094
|
+
height: maskHeight.toString(),
|
|
2095
|
+
...maskBorderRadius > 0 ? {
|
|
2096
|
+
rx: maskBorderRadius.toString(),
|
|
2097
|
+
ry: maskBorderRadius.toString()
|
|
2098
|
+
} : {},
|
|
2099
|
+
"data-type": "pcb_soldermask",
|
|
2100
|
+
"data-pcb-layer": maskLayer
|
|
1805
2101
|
},
|
|
1806
2102
|
value: "",
|
|
1807
2103
|
children: []
|
|
1808
|
-
}
|
|
1809
|
-
|
|
2104
|
+
},
|
|
2105
|
+
// 3. Draw the drill hole on top
|
|
2106
|
+
children[1]
|
|
2107
|
+
// Original hole
|
|
2108
|
+
];
|
|
2109
|
+
} else {
|
|
2110
|
+
children.unshift({
|
|
2111
|
+
name: "rect",
|
|
2112
|
+
type: "element",
|
|
2113
|
+
attributes: {
|
|
2114
|
+
class: "pcb-soldermask-cutout",
|
|
2115
|
+
fill: colorMap2.substrate,
|
|
2116
|
+
x: (x - maskWidth / 2).toString(),
|
|
2117
|
+
y: (y - maskHeight / 2).toString(),
|
|
2118
|
+
width: maskWidth.toString(),
|
|
2119
|
+
height: maskHeight.toString(),
|
|
2120
|
+
...scaledRectBorderRadius ? {
|
|
2121
|
+
rx: maskBorderRadius.toString(),
|
|
2122
|
+
ry: maskBorderRadius.toString()
|
|
2123
|
+
} : {},
|
|
2124
|
+
"data-type": "pcb_soldermask_opening",
|
|
2125
|
+
"data-pcb-layer": maskLayer
|
|
2126
|
+
},
|
|
2127
|
+
value: "",
|
|
2128
|
+
children: []
|
|
2129
|
+
});
|
|
2130
|
+
}
|
|
2131
|
+
}
|
|
2132
|
+
return [
|
|
2133
|
+
{
|
|
2134
|
+
name: "g",
|
|
2135
|
+
type: "element",
|
|
2136
|
+
attributes: {
|
|
2137
|
+
"data-type": "pcb_plated_hole",
|
|
2138
|
+
"data-pcb-layer": "through"
|
|
2139
|
+
},
|
|
2140
|
+
children,
|
|
1810
2141
|
value: ""
|
|
1811
2142
|
}
|
|
1812
2143
|
];
|
|
@@ -1826,21 +2157,61 @@ function createSvgObjectsFromPcbPlatedHole(hole, ctx) {
|
|
|
1826
2157
|
rotatedHole.y + holeOffsetY
|
|
1827
2158
|
]);
|
|
1828
2159
|
const holeRadius = Math.min(scaledHoleHeight, scaledHoleWidth) / 2;
|
|
1829
|
-
|
|
2160
|
+
let children = [
|
|
1830
2161
|
{
|
|
1831
|
-
name: "
|
|
2162
|
+
name: "rect",
|
|
1832
2163
|
type: "element",
|
|
1833
2164
|
attributes: {
|
|
2165
|
+
class: "pcb-hole-outer-pad",
|
|
2166
|
+
fill: colorMap2.copper.top,
|
|
2167
|
+
x: (-scaledRectPadWidth / 2).toString(),
|
|
2168
|
+
y: (-scaledRectPadHeight / 2).toString(),
|
|
2169
|
+
width: scaledRectPadWidth.toString(),
|
|
2170
|
+
height: scaledRectPadHeight.toString(),
|
|
2171
|
+
transform: `translate(${x} ${y}) rotate(${-rotatedHole.rect_ccw_rotation})`,
|
|
2172
|
+
...scaledRectBorderRadius ? {
|
|
2173
|
+
rx: scaledRectBorderRadius.toString(),
|
|
2174
|
+
ry: scaledRectBorderRadius.toString()
|
|
2175
|
+
} : {},
|
|
1834
2176
|
"data-type": "pcb_plated_hole",
|
|
1835
|
-
"data-pcb-layer":
|
|
2177
|
+
"data-pcb-layer": layer
|
|
1836
2178
|
},
|
|
1837
|
-
|
|
2179
|
+
value: "",
|
|
2180
|
+
children: []
|
|
2181
|
+
},
|
|
2182
|
+
{
|
|
2183
|
+
name: "rect",
|
|
2184
|
+
type: "element",
|
|
2185
|
+
attributes: {
|
|
2186
|
+
class: "pcb-hole-inner",
|
|
2187
|
+
fill: colorMap2.drill,
|
|
2188
|
+
x: (-scaledHoleWidth / 2).toString(),
|
|
2189
|
+
y: (-scaledHoleHeight / 2).toString(),
|
|
2190
|
+
width: scaledHoleWidth.toString(),
|
|
2191
|
+
height: scaledHoleHeight.toString(),
|
|
2192
|
+
rx: holeRadius.toString(),
|
|
2193
|
+
ry: holeRadius.toString(),
|
|
2194
|
+
transform: `translate(${holeCenterX} ${holeCenterY}) rotate(${-rotatedHole.hole_ccw_rotation})`,
|
|
2195
|
+
"data-type": "pcb_plated_hole_drill",
|
|
2196
|
+
"data-pcb-layer": "drill"
|
|
2197
|
+
},
|
|
2198
|
+
value: "",
|
|
2199
|
+
children: []
|
|
2200
|
+
}
|
|
2201
|
+
];
|
|
2202
|
+
if (shouldShowSolderMask) {
|
|
2203
|
+
const maskWidth = scaledRectPadWidth + 2 * soldermaskMargin;
|
|
2204
|
+
const maskHeight = scaledRectPadHeight + 2 * soldermaskMargin;
|
|
2205
|
+
const maskBorderRadius = scaledRectBorderRadius + soldermaskMargin;
|
|
2206
|
+
if (soldermaskMargin < 0) {
|
|
2207
|
+
children = [
|
|
2208
|
+
// 1. Draw the outer pad in soldermask color (covered)
|
|
1838
2209
|
{
|
|
1839
2210
|
name: "rect",
|
|
1840
2211
|
type: "element",
|
|
1841
2212
|
attributes: {
|
|
1842
|
-
class: "pcb-hole-outer-
|
|
1843
|
-
fill:
|
|
2213
|
+
class: "pcb-hole-outer-covered",
|
|
2214
|
+
fill: solderMaskColor,
|
|
1844
2215
|
x: (-scaledRectPadWidth / 2).toString(),
|
|
1845
2216
|
y: (-scaledRectPadHeight / 2).toString(),
|
|
1846
2217
|
width: scaledRectPadWidth.toString(),
|
|
@@ -1851,30 +2222,186 @@ function createSvgObjectsFromPcbPlatedHole(hole, ctx) {
|
|
|
1851
2222
|
ry: scaledRectBorderRadius.toString()
|
|
1852
2223
|
} : {},
|
|
1853
2224
|
"data-type": "pcb_plated_hole",
|
|
1854
|
-
"data-pcb-layer":
|
|
2225
|
+
"data-pcb-layer": layer
|
|
1855
2226
|
},
|
|
1856
2227
|
value: "",
|
|
1857
2228
|
children: []
|
|
1858
2229
|
},
|
|
2230
|
+
// 2. Draw the exposed opening in copper color
|
|
1859
2231
|
{
|
|
1860
2232
|
name: "rect",
|
|
1861
2233
|
type: "element",
|
|
1862
2234
|
attributes: {
|
|
1863
|
-
class: "pcb-hole-
|
|
1864
|
-
fill: colorMap2.
|
|
1865
|
-
x: (-
|
|
1866
|
-
y: (-
|
|
1867
|
-
width:
|
|
1868
|
-
height:
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
2235
|
+
class: "pcb-hole-outer-exposed",
|
|
2236
|
+
fill: colorMap2.copper.top,
|
|
2237
|
+
x: (-maskWidth / 2).toString(),
|
|
2238
|
+
y: (-maskHeight / 2).toString(),
|
|
2239
|
+
width: maskWidth.toString(),
|
|
2240
|
+
height: maskHeight.toString(),
|
|
2241
|
+
transform: `translate(${x} ${y}) rotate(${-rotatedHole.rect_ccw_rotation})`,
|
|
2242
|
+
...maskBorderRadius > 0 ? {
|
|
2243
|
+
rx: maskBorderRadius.toString(),
|
|
2244
|
+
ry: maskBorderRadius.toString()
|
|
2245
|
+
} : {},
|
|
2246
|
+
"data-type": "pcb_soldermask",
|
|
2247
|
+
"data-pcb-layer": maskLayer
|
|
1874
2248
|
},
|
|
1875
2249
|
value: "",
|
|
1876
2250
|
children: []
|
|
1877
|
-
}
|
|
2251
|
+
},
|
|
2252
|
+
// 3. Draw the drill hole on top
|
|
2253
|
+
children[1]
|
|
2254
|
+
// Original hole
|
|
2255
|
+
];
|
|
2256
|
+
} else {
|
|
2257
|
+
children.push({
|
|
2258
|
+
name: "rect",
|
|
2259
|
+
type: "element",
|
|
2260
|
+
attributes: {
|
|
2261
|
+
class: "pcb-solder-mask",
|
|
2262
|
+
fill: solderMaskColor,
|
|
2263
|
+
x: (-maskWidth / 2).toString(),
|
|
2264
|
+
y: (-maskHeight / 2).toString(),
|
|
2265
|
+
width: maskWidth.toString(),
|
|
2266
|
+
height: maskHeight.toString(),
|
|
2267
|
+
transform: `translate(${x} ${y}) rotate(${-rotatedHole.rect_ccw_rotation})`,
|
|
2268
|
+
...scaledRectBorderRadius ? {
|
|
2269
|
+
rx: maskBorderRadius.toString(),
|
|
2270
|
+
ry: maskBorderRadius.toString()
|
|
2271
|
+
} : {},
|
|
2272
|
+
"data-type": "pcb_soldermask",
|
|
2273
|
+
"data-pcb-layer": maskLayer
|
|
2274
|
+
},
|
|
2275
|
+
value: "",
|
|
2276
|
+
children: []
|
|
2277
|
+
});
|
|
2278
|
+
}
|
|
2279
|
+
}
|
|
2280
|
+
return [
|
|
2281
|
+
{
|
|
2282
|
+
name: "g",
|
|
2283
|
+
type: "element",
|
|
2284
|
+
attributes: {
|
|
2285
|
+
"data-type": "pcb_plated_hole",
|
|
2286
|
+
"data-pcb-layer": "through"
|
|
2287
|
+
},
|
|
2288
|
+
children,
|
|
2289
|
+
value: ""
|
|
2290
|
+
}
|
|
2291
|
+
];
|
|
2292
|
+
}
|
|
2293
|
+
if (hole.shape === "hole_with_polygon_pad") {
|
|
2294
|
+
const polygonHole = hole;
|
|
2295
|
+
const padOutline = polygonHole.pad_outline || [];
|
|
2296
|
+
const holeX = polygonHole.x ?? 0;
|
|
2297
|
+
const holeY = polygonHole.y ?? 0;
|
|
2298
|
+
const padPoints = padOutline.map(
|
|
2299
|
+
(point) => applyToPoint(transform, [holeX + point.x, holeY + point.y])
|
|
2300
|
+
);
|
|
2301
|
+
const padPointsString = padPoints.map((p) => p.join(",")).join(" ");
|
|
2302
|
+
const [holeCenterX, holeCenterY] = applyToPoint(transform, [
|
|
2303
|
+
holeX + polygonHole.hole_offset_x,
|
|
2304
|
+
holeY + polygonHole.hole_offset_y
|
|
2305
|
+
]);
|
|
2306
|
+
const createHoleSvgObject = () => {
|
|
2307
|
+
if (polygonHole.hole_shape === "circle") {
|
|
2308
|
+
const scaledDiameter = (polygonHole.hole_diameter ?? 0) * Math.abs(transform.a);
|
|
2309
|
+
const radius = scaledDiameter / 2;
|
|
2310
|
+
return {
|
|
2311
|
+
name: "circle",
|
|
2312
|
+
type: "element",
|
|
2313
|
+
attributes: {
|
|
2314
|
+
class: "pcb-hole-inner",
|
|
2315
|
+
fill: colorMap2.drill,
|
|
2316
|
+
cx: holeCenterX.toString(),
|
|
2317
|
+
cy: holeCenterY.toString(),
|
|
2318
|
+
r: radius.toString(),
|
|
2319
|
+
"data-type": "pcb_plated_hole_drill",
|
|
2320
|
+
"data-pcb-layer": "drill"
|
|
2321
|
+
},
|
|
2322
|
+
value: "",
|
|
2323
|
+
children: []
|
|
2324
|
+
};
|
|
2325
|
+
}
|
|
2326
|
+
if (polygonHole.hole_shape === "oval") {
|
|
2327
|
+
const scaledWidth = (polygonHole.hole_width ?? 0) * Math.abs(transform.a);
|
|
2328
|
+
const scaledHeight = (polygonHole.hole_height ?? 0) * Math.abs(transform.a);
|
|
2329
|
+
const rx = scaledWidth / 2;
|
|
2330
|
+
const ry = scaledHeight / 2;
|
|
2331
|
+
return {
|
|
2332
|
+
name: "ellipse",
|
|
2333
|
+
type: "element",
|
|
2334
|
+
attributes: {
|
|
2335
|
+
class: "pcb-hole-inner",
|
|
2336
|
+
fill: colorMap2.drill,
|
|
2337
|
+
cx: holeCenterX.toString(),
|
|
2338
|
+
cy: holeCenterY.toString(),
|
|
2339
|
+
rx: rx.toString(),
|
|
2340
|
+
ry: ry.toString(),
|
|
2341
|
+
"data-type": "pcb_plated_hole_drill",
|
|
2342
|
+
"data-pcb-layer": "drill"
|
|
2343
|
+
},
|
|
2344
|
+
value: "",
|
|
2345
|
+
children: []
|
|
2346
|
+
};
|
|
2347
|
+
}
|
|
2348
|
+
if (polygonHole.hole_shape === "pill" || polygonHole.hole_shape === "rotated_pill") {
|
|
2349
|
+
const scaledWidth = (polygonHole.hole_width ?? 0) * Math.abs(transform.a);
|
|
2350
|
+
const scaledHeight = (polygonHole.hole_height ?? 0) * Math.abs(transform.a);
|
|
2351
|
+
const isHorizontal = scaledWidth > scaledHeight;
|
|
2352
|
+
const radius = Math.min(scaledWidth, scaledHeight) / 2;
|
|
2353
|
+
const straightLength = Math.abs(
|
|
2354
|
+
isHorizontal ? scaledWidth - scaledHeight : scaledHeight - scaledWidth
|
|
2355
|
+
);
|
|
2356
|
+
const pathD = isHorizontal ? `M${-straightLength / 2},${-radius} h${straightLength} a${radius},${radius} 0 0 1 0,${scaledHeight} h-${straightLength} a${radius},${radius} 0 0 1 0,-${scaledHeight} z` : `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`;
|
|
2357
|
+
return {
|
|
2358
|
+
name: "path",
|
|
2359
|
+
type: "element",
|
|
2360
|
+
attributes: {
|
|
2361
|
+
class: "pcb-hole-inner",
|
|
2362
|
+
fill: colorMap2.drill,
|
|
2363
|
+
d: pathD,
|
|
2364
|
+
transform: `translate(${holeCenterX} ${holeCenterY})`,
|
|
2365
|
+
"data-type": "pcb_plated_hole_drill",
|
|
2366
|
+
"data-pcb-layer": "drill"
|
|
2367
|
+
},
|
|
2368
|
+
value: "",
|
|
2369
|
+
children: []
|
|
2370
|
+
};
|
|
2371
|
+
}
|
|
2372
|
+
return {
|
|
2373
|
+
name: "g",
|
|
2374
|
+
type: "element",
|
|
2375
|
+
attributes: {},
|
|
2376
|
+
value: "",
|
|
2377
|
+
children: []
|
|
2378
|
+
};
|
|
2379
|
+
};
|
|
2380
|
+
return [
|
|
2381
|
+
{
|
|
2382
|
+
name: "g",
|
|
2383
|
+
type: "element",
|
|
2384
|
+
attributes: {
|
|
2385
|
+
"data-type": "pcb_plated_hole",
|
|
2386
|
+
"data-pcb-layer": "through"
|
|
2387
|
+
},
|
|
2388
|
+
children: [
|
|
2389
|
+
// Polygon pad (outer shape)
|
|
2390
|
+
{
|
|
2391
|
+
name: "polygon",
|
|
2392
|
+
type: "element",
|
|
2393
|
+
attributes: {
|
|
2394
|
+
class: "pcb-hole-outer-pad",
|
|
2395
|
+
fill: colorMap2.copper.top,
|
|
2396
|
+
points: padPointsString,
|
|
2397
|
+
"data-type": "pcb_plated_hole",
|
|
2398
|
+
"data-pcb-layer": layer
|
|
2399
|
+
},
|
|
2400
|
+
value: "",
|
|
2401
|
+
children: []
|
|
2402
|
+
},
|
|
2403
|
+
// Hole inside the polygon (with offset)
|
|
2404
|
+
createHoleSvgObject()
|
|
1878
2405
|
],
|
|
1879
2406
|
value: ""
|
|
1880
2407
|
}
|
|
@@ -1920,7 +2447,7 @@ function createSvgObjectsFromPcbSilkscreenPath(silkscreenPath, ctx) {
|
|
|
1920
2447
|
];
|
|
1921
2448
|
}
|
|
1922
2449
|
function createSvgObjectsFromPcbSilkscreenText(pcbSilkscreenText, ctx) {
|
|
1923
|
-
const { transform, layer: layerFilter, colorMap: colorMap2 } = ctx;
|
|
2450
|
+
const { transform, layer: layerFilter, colorMap: colorMap2, circuitJson } = ctx;
|
|
1924
2451
|
const {
|
|
1925
2452
|
anchor_position,
|
|
1926
2453
|
text,
|
|
@@ -2260,10 +2787,20 @@ var DEFAULT_PCB_COLOR_MAP = {
|
|
|
2260
2787
|
inner6: "rgb(255, 105, 180)",
|
|
2261
2788
|
bottom: "rgb(77, 127, 196)"
|
|
2262
2789
|
},
|
|
2263
|
-
|
|
2790
|
+
soldermaskWithCopperUnderneath: {
|
|
2264
2791
|
top: "rgb(18, 82, 50)",
|
|
2265
2792
|
bottom: "rgb(77, 127, 196)"
|
|
2266
2793
|
},
|
|
2794
|
+
soldermask: {
|
|
2795
|
+
top: "rgb(12, 55, 33)",
|
|
2796
|
+
bottom: "rgb(12, 55, 33)"
|
|
2797
|
+
},
|
|
2798
|
+
soldermaskOverCopper: {
|
|
2799
|
+
top: "rgb(52, 135, 73)",
|
|
2800
|
+
bottom: "rgb(52, 135, 73)"
|
|
2801
|
+
},
|
|
2802
|
+
substrate: "rgb(201, 162, 110)",
|
|
2803
|
+
// FR4 substrate color (tan/beige)
|
|
2267
2804
|
drill: "#FF26E2",
|
|
2268
2805
|
silkscreen: {
|
|
2269
2806
|
top: "#f2eda1",
|
|
@@ -2304,29 +2841,12 @@ function createSvgObjectsFromPcbTrace(trace, ctx) {
|
|
|
2304
2841
|
const layer = "layer" in start ? start.layer : "layer" in end ? end.layer : null;
|
|
2305
2842
|
if (!layer) continue;
|
|
2306
2843
|
if (layerFilter && layer !== layerFilter) continue;
|
|
2844
|
+
const maskLayer = layer;
|
|
2307
2845
|
const copperColor = layerNameToColor(layer, colorMap2);
|
|
2308
|
-
const maskColor = colorMap2.
|
|
2846
|
+
const maskColor = colorMap2.soldermaskWithCopperUnderneath[layer];
|
|
2309
2847
|
const traceWidth = "width" in start ? start.width : "width" in end ? end.width : null;
|
|
2310
2848
|
const width = traceWidth ? (traceWidth * Math.abs(transform.a)).toString() : "0.3";
|
|
2311
2849
|
if (showSolderMask) {
|
|
2312
|
-
const copperObject = {
|
|
2313
|
-
name: "path",
|
|
2314
|
-
type: "element",
|
|
2315
|
-
value: "",
|
|
2316
|
-
children: [],
|
|
2317
|
-
attributes: {
|
|
2318
|
-
class: "pcb-trace",
|
|
2319
|
-
stroke: copperColor,
|
|
2320
|
-
fill: "none",
|
|
2321
|
-
d: `M ${startPoint[0]} ${startPoint[1]} L ${endPoint[0]} ${endPoint[1]}`,
|
|
2322
|
-
"stroke-width": width,
|
|
2323
|
-
"stroke-linecap": "round",
|
|
2324
|
-
"stroke-linejoin": "round",
|
|
2325
|
-
"shape-rendering": "crispEdges",
|
|
2326
|
-
"data-type": "pcb_trace",
|
|
2327
|
-
"data-pcb-layer": layer
|
|
2328
|
-
}
|
|
2329
|
-
};
|
|
2330
2850
|
const maskObject = {
|
|
2331
2851
|
name: "path",
|
|
2332
2852
|
type: "element",
|
|
@@ -2341,11 +2861,11 @@ function createSvgObjectsFromPcbTrace(trace, ctx) {
|
|
|
2341
2861
|
"stroke-linecap": "round",
|
|
2342
2862
|
"stroke-linejoin": "round",
|
|
2343
2863
|
"shape-rendering": "crispEdges",
|
|
2344
|
-
"data-type": "
|
|
2864
|
+
"data-type": "pcb_trace_soldermask",
|
|
2345
2865
|
"data-pcb-layer": layer
|
|
2346
2866
|
}
|
|
2347
2867
|
};
|
|
2348
|
-
svgObjects.push(maskObject
|
|
2868
|
+
svgObjects.push(maskObject);
|
|
2349
2869
|
} else {
|
|
2350
2870
|
const maskOnlyObject = {
|
|
2351
2871
|
name: "path",
|
|
@@ -2374,8 +2894,9 @@ function createSvgObjectsFromSmtPad(pad, ctx) {
|
|
|
2374
2894
|
const { transform, layer: layerFilter, colorMap: colorMap2, showSolderMask } = ctx;
|
|
2375
2895
|
if (layerFilter && pad.layer !== layerFilter) return [];
|
|
2376
2896
|
const isCoveredWithSolderMask = Boolean(pad?.is_covered_with_solder_mask);
|
|
2377
|
-
const
|
|
2378
|
-
const
|
|
2897
|
+
const shouldShowSolderMask = showSolderMask && isCoveredWithSolderMask;
|
|
2898
|
+
const soldermaskWithCopperUnderneathColor = colorMap2.soldermaskWithCopperUnderneath[pad.layer] ?? colorMap2.soldermaskWithCopperUnderneath.top;
|
|
2899
|
+
const soldermaskMargin = (pad.soldermask_margin ?? 0) * Math.abs(transform.a);
|
|
2379
2900
|
if (pad.shape === "rect" || pad.shape === "rotated_rect") {
|
|
2380
2901
|
const width = pad.width * Math.abs(transform.a);
|
|
2381
2902
|
const height = pad.height * Math.abs(transform.d);
|
|
@@ -2404,22 +2925,103 @@ function createSvgObjectsFromSmtPad(pad, ctx) {
|
|
|
2404
2925
|
} : {}
|
|
2405
2926
|
}
|
|
2406
2927
|
};
|
|
2407
|
-
if (!
|
|
2928
|
+
if (!shouldShowSolderMask) {
|
|
2408
2929
|
return [padElement2];
|
|
2409
2930
|
}
|
|
2410
|
-
const
|
|
2411
|
-
|
|
2412
|
-
|
|
2931
|
+
const maskWidth2 = width + 2 * soldermaskMargin;
|
|
2932
|
+
const maskHeight2 = height + 2 * soldermaskMargin;
|
|
2933
|
+
const maskBorderRadius2 = scaledBorderRadius ? scaledBorderRadius + soldermaskMargin : 0;
|
|
2934
|
+
if (soldermaskMargin < 0) {
|
|
2935
|
+
const coveredPadElement = {
|
|
2936
|
+
name: "rect",
|
|
2937
|
+
type: "element",
|
|
2938
|
+
value: "",
|
|
2939
|
+
children: [],
|
|
2940
|
+
attributes: {
|
|
2941
|
+
class: "pcb-pad-covered",
|
|
2942
|
+
fill: soldermaskWithCopperUnderneathColor,
|
|
2943
|
+
x: (-width / 2).toString(),
|
|
2944
|
+
y: (-height / 2).toString(),
|
|
2945
|
+
width: width.toString(),
|
|
2946
|
+
height: height.toString(),
|
|
2947
|
+
transform: `translate(${x} ${y}) rotate(${-pad.ccw_rotation})`,
|
|
2948
|
+
"data-type": "pcb_smtpad",
|
|
2949
|
+
"data-pcb-layer": pad.layer,
|
|
2950
|
+
...scaledBorderRadius ? {
|
|
2951
|
+
rx: scaledBorderRadius.toString(),
|
|
2952
|
+
ry: scaledBorderRadius.toString()
|
|
2953
|
+
} : {}
|
|
2954
|
+
}
|
|
2955
|
+
};
|
|
2956
|
+
const exposedOpeningElement = {
|
|
2957
|
+
name: "rect",
|
|
2958
|
+
type: "element",
|
|
2959
|
+
value: "",
|
|
2960
|
+
children: [],
|
|
2961
|
+
attributes: {
|
|
2962
|
+
class: "pcb-pad-exposed",
|
|
2963
|
+
fill: layerNameToColor(pad.layer, colorMap2),
|
|
2964
|
+
x: (-maskWidth2 / 2).toString(),
|
|
2965
|
+
y: (-maskHeight2 / 2).toString(),
|
|
2966
|
+
width: maskWidth2.toString(),
|
|
2967
|
+
height: maskHeight2.toString(),
|
|
2968
|
+
transform: `translate(${x} ${y}) rotate(${-pad.ccw_rotation})`,
|
|
2969
|
+
"data-type": "pcb_soldermask",
|
|
2970
|
+
"data-pcb-layer": pad.layer,
|
|
2971
|
+
...maskBorderRadius2 > 0 ? {
|
|
2972
|
+
rx: maskBorderRadius2.toString(),
|
|
2973
|
+
ry: maskBorderRadius2.toString()
|
|
2974
|
+
} : {}
|
|
2975
|
+
}
|
|
2976
|
+
};
|
|
2977
|
+
return [coveredPadElement, exposedOpeningElement];
|
|
2978
|
+
}
|
|
2979
|
+
if (soldermaskMargin === 0) {
|
|
2980
|
+
const coveredPadElement = {
|
|
2981
|
+
name: "rect",
|
|
2982
|
+
type: "element",
|
|
2983
|
+
value: "",
|
|
2984
|
+
children: [],
|
|
2985
|
+
attributes: {
|
|
2986
|
+
class: "pcb-pad-covered",
|
|
2987
|
+
fill: soldermaskWithCopperUnderneathColor,
|
|
2988
|
+
x: (-width / 2).toString(),
|
|
2989
|
+
y: (-height / 2).toString(),
|
|
2990
|
+
width: width.toString(),
|
|
2991
|
+
height: height.toString(),
|
|
2992
|
+
transform: `translate(${x} ${y}) rotate(${-pad.ccw_rotation})`,
|
|
2993
|
+
"data-type": "pcb_smtpad",
|
|
2994
|
+
"data-pcb-layer": pad.layer,
|
|
2995
|
+
...scaledBorderRadius ? {
|
|
2996
|
+
rx: scaledBorderRadius.toString(),
|
|
2997
|
+
ry: scaledBorderRadius.toString()
|
|
2998
|
+
} : {}
|
|
2999
|
+
}
|
|
3000
|
+
};
|
|
3001
|
+
return [coveredPadElement];
|
|
3002
|
+
}
|
|
3003
|
+
const substrateElement2 = {
|
|
3004
|
+
name: "rect",
|
|
3005
|
+
type: "element",
|
|
2413
3006
|
value: "",
|
|
2414
3007
|
children: [],
|
|
2415
3008
|
attributes: {
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
3009
|
+
class: "pcb-soldermask-cutout",
|
|
3010
|
+
fill: colorMap2.substrate,
|
|
3011
|
+
x: (-maskWidth2 / 2).toString(),
|
|
3012
|
+
y: (-maskHeight2 / 2).toString(),
|
|
3013
|
+
width: maskWidth2.toString(),
|
|
3014
|
+
height: maskHeight2.toString(),
|
|
3015
|
+
transform: `translate(${x} ${y}) rotate(${-pad.ccw_rotation})`,
|
|
3016
|
+
...maskBorderRadius2 > 0 ? {
|
|
3017
|
+
rx: maskBorderRadius2.toString(),
|
|
3018
|
+
ry: maskBorderRadius2.toString()
|
|
3019
|
+
} : {},
|
|
3020
|
+
"data-type": "pcb_soldermask_opening",
|
|
3021
|
+
"data-pcb-layer": pad.layer
|
|
2420
3022
|
}
|
|
2421
3023
|
};
|
|
2422
|
-
return [
|
|
3024
|
+
return [substrateElement2, padElement2];
|
|
2423
3025
|
}
|
|
2424
3026
|
const padElement = {
|
|
2425
3027
|
name: "rect",
|
|
@@ -2441,62 +3043,220 @@ function createSvgObjectsFromSmtPad(pad, ctx) {
|
|
|
2441
3043
|
} : {}
|
|
2442
3044
|
}
|
|
2443
3045
|
};
|
|
2444
|
-
if (!
|
|
3046
|
+
if (!shouldShowSolderMask) {
|
|
2445
3047
|
return [padElement];
|
|
2446
3048
|
}
|
|
2447
|
-
const
|
|
2448
|
-
|
|
2449
|
-
|
|
3049
|
+
const maskWidth = width + 2 * soldermaskMargin;
|
|
3050
|
+
const maskHeight = height + 2 * soldermaskMargin;
|
|
3051
|
+
const maskBorderRadius = scaledBorderRadius ? scaledBorderRadius + soldermaskMargin : 0;
|
|
3052
|
+
if (soldermaskMargin < 0) {
|
|
3053
|
+
const coveredPadElement = {
|
|
3054
|
+
name: "rect",
|
|
3055
|
+
type: "element",
|
|
3056
|
+
value: "",
|
|
3057
|
+
children: [],
|
|
3058
|
+
attributes: {
|
|
3059
|
+
class: "pcb-pad-covered",
|
|
3060
|
+
fill: soldermaskWithCopperUnderneathColor,
|
|
3061
|
+
x: (x - width / 2).toString(),
|
|
3062
|
+
y: (y - height / 2).toString(),
|
|
3063
|
+
width: width.toString(),
|
|
3064
|
+
height: height.toString(),
|
|
3065
|
+
"data-type": "pcb_smtpad",
|
|
3066
|
+
"data-pcb-layer": pad.layer,
|
|
3067
|
+
...scaledBorderRadius ? {
|
|
3068
|
+
rx: scaledBorderRadius.toString(),
|
|
3069
|
+
ry: scaledBorderRadius.toString()
|
|
3070
|
+
} : {}
|
|
3071
|
+
}
|
|
3072
|
+
};
|
|
3073
|
+
const exposedOpeningElement = {
|
|
3074
|
+
name: "rect",
|
|
3075
|
+
type: "element",
|
|
3076
|
+
value: "",
|
|
3077
|
+
children: [],
|
|
3078
|
+
attributes: {
|
|
3079
|
+
class: "pcb-pad-exposed",
|
|
3080
|
+
fill: layerNameToColor(pad.layer, colorMap2),
|
|
3081
|
+
x: (x - maskWidth / 2).toString(),
|
|
3082
|
+
y: (y - maskHeight / 2).toString(),
|
|
3083
|
+
width: maskWidth.toString(),
|
|
3084
|
+
height: maskHeight.toString(),
|
|
3085
|
+
"data-type": "pcb_soldermask",
|
|
3086
|
+
"data-pcb-layer": pad.layer,
|
|
3087
|
+
...maskBorderRadius > 0 ? {
|
|
3088
|
+
rx: maskBorderRadius.toString(),
|
|
3089
|
+
ry: maskBorderRadius.toString()
|
|
3090
|
+
} : {}
|
|
3091
|
+
}
|
|
3092
|
+
};
|
|
3093
|
+
return [coveredPadElement, exposedOpeningElement];
|
|
3094
|
+
}
|
|
3095
|
+
if (soldermaskMargin === 0) {
|
|
3096
|
+
const coveredPadElement = {
|
|
3097
|
+
name: "rect",
|
|
3098
|
+
type: "element",
|
|
3099
|
+
value: "",
|
|
3100
|
+
children: [],
|
|
3101
|
+
attributes: {
|
|
3102
|
+
class: "pcb-pad-covered",
|
|
3103
|
+
fill: soldermaskWithCopperUnderneathColor,
|
|
3104
|
+
x: (x - width / 2).toString(),
|
|
3105
|
+
y: (y - height / 2).toString(),
|
|
3106
|
+
width: width.toString(),
|
|
3107
|
+
height: height.toString(),
|
|
3108
|
+
...maskBorderRadius > 0 ? {
|
|
3109
|
+
rx: scaledBorderRadius.toString(),
|
|
3110
|
+
ry: scaledBorderRadius.toString()
|
|
3111
|
+
} : {},
|
|
3112
|
+
"data-type": "pcb_smtpad",
|
|
3113
|
+
"data-pcb-layer": pad.layer
|
|
3114
|
+
}
|
|
3115
|
+
};
|
|
3116
|
+
return [coveredPadElement];
|
|
3117
|
+
}
|
|
3118
|
+
const substrateElement = {
|
|
3119
|
+
name: "rect",
|
|
3120
|
+
type: "element",
|
|
2450
3121
|
value: "",
|
|
2451
3122
|
children: [],
|
|
2452
3123
|
attributes: {
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
3124
|
+
class: "pcb-soldermask-cutout",
|
|
3125
|
+
fill: colorMap2.substrate,
|
|
3126
|
+
x: (x - maskWidth / 2).toString(),
|
|
3127
|
+
y: (y - maskHeight / 2).toString(),
|
|
3128
|
+
width: maskWidth.toString(),
|
|
3129
|
+
height: maskHeight.toString(),
|
|
3130
|
+
...maskBorderRadius > 0 ? {
|
|
3131
|
+
rx: maskBorderRadius.toString(),
|
|
3132
|
+
ry: maskBorderRadius.toString()
|
|
3133
|
+
} : {},
|
|
3134
|
+
"data-type": "pcb_soldermask_opening",
|
|
3135
|
+
"data-pcb-layer": pad.layer
|
|
2457
3136
|
}
|
|
2458
3137
|
};
|
|
2459
|
-
return [
|
|
3138
|
+
return [substrateElement, padElement];
|
|
2460
3139
|
}
|
|
2461
|
-
if (pad.shape === "pill") {
|
|
3140
|
+
if (pad.shape === "pill" || pad.shape === "rotated_pill") {
|
|
3141
|
+
const isRotated = pad.shape === "rotated_pill";
|
|
2462
3142
|
const width = pad.width * Math.abs(transform.a);
|
|
2463
3143
|
const height = pad.height * Math.abs(transform.d);
|
|
2464
3144
|
const radius = pad.radius * Math.abs(transform.a);
|
|
2465
3145
|
const [x, y] = applyToPoint(transform, [pad.x, pad.y]);
|
|
3146
|
+
const rotationTransformAttributes = isRotated ? {
|
|
3147
|
+
transform: `translate(${x} ${y}) rotate(${-(pad.ccw_rotation ?? 0)})`
|
|
3148
|
+
} : void 0;
|
|
3149
|
+
const baseAttributes = {
|
|
3150
|
+
class: "pcb-pad",
|
|
3151
|
+
fill: layerNameToColor(pad.layer, colorMap2),
|
|
3152
|
+
x: isRotated ? (-width / 2).toString() : (x - width / 2).toString(),
|
|
3153
|
+
y: isRotated ? (-height / 2).toString() : (y - height / 2).toString(),
|
|
3154
|
+
width: width.toString(),
|
|
3155
|
+
height: height.toString(),
|
|
3156
|
+
rx: radius.toString(),
|
|
3157
|
+
ry: radius.toString(),
|
|
3158
|
+
"data-type": "pcb_smtpad",
|
|
3159
|
+
"data-pcb-layer": pad.layer,
|
|
3160
|
+
...rotationTransformAttributes ?? {}
|
|
3161
|
+
};
|
|
2466
3162
|
const padElement = {
|
|
2467
3163
|
name: "rect",
|
|
2468
3164
|
type: "element",
|
|
2469
3165
|
value: "",
|
|
2470
3166
|
children: [],
|
|
2471
|
-
attributes:
|
|
2472
|
-
class: "pcb-pad",
|
|
2473
|
-
fill: layerNameToColor(pad.layer, colorMap2),
|
|
2474
|
-
x: (x - width / 2).toString(),
|
|
2475
|
-
y: (y - height / 2).toString(),
|
|
2476
|
-
width: width.toString(),
|
|
2477
|
-
height: height.toString(),
|
|
2478
|
-
rx: radius.toString(),
|
|
2479
|
-
ry: radius.toString(),
|
|
2480
|
-
"data-type": "pcb_smtpad",
|
|
2481
|
-
"data-pcb-layer": pad.layer
|
|
2482
|
-
}
|
|
3167
|
+
attributes: baseAttributes
|
|
2483
3168
|
};
|
|
2484
|
-
if (!
|
|
3169
|
+
if (!shouldShowSolderMask) {
|
|
2485
3170
|
return [padElement];
|
|
2486
3171
|
}
|
|
2487
|
-
const
|
|
2488
|
-
|
|
2489
|
-
|
|
3172
|
+
const maskWidth = width + 2 * soldermaskMargin;
|
|
3173
|
+
const maskHeight = height + 2 * soldermaskMargin;
|
|
3174
|
+
const maskRadius = radius + soldermaskMargin;
|
|
3175
|
+
if (soldermaskMargin < 0) {
|
|
3176
|
+
const coveredPadElement = {
|
|
3177
|
+
name: "rect",
|
|
3178
|
+
type: "element",
|
|
3179
|
+
value: "",
|
|
3180
|
+
children: [],
|
|
3181
|
+
attributes: {
|
|
3182
|
+
class: "pcb-pad-covered",
|
|
3183
|
+
fill: soldermaskWithCopperUnderneathColor,
|
|
3184
|
+
x: isRotated ? (-width / 2).toString() : (x - width / 2).toString(),
|
|
3185
|
+
y: isRotated ? (-height / 2).toString() : (y - height / 2).toString(),
|
|
3186
|
+
width: width.toString(),
|
|
3187
|
+
height: height.toString(),
|
|
3188
|
+
rx: radius.toString(),
|
|
3189
|
+
ry: radius.toString(),
|
|
3190
|
+
"data-type": "pcb_smtpad",
|
|
3191
|
+
"data-pcb-layer": pad.layer,
|
|
3192
|
+
...rotationTransformAttributes ?? {}
|
|
3193
|
+
}
|
|
3194
|
+
};
|
|
3195
|
+
const exposedAttributes = {
|
|
3196
|
+
class: "pcb-pad-exposed",
|
|
3197
|
+
fill: layerNameToColor(pad.layer, colorMap2),
|
|
3198
|
+
x: isRotated ? (-maskWidth / 2).toString() : (x - maskWidth / 2).toString(),
|
|
3199
|
+
y: isRotated ? (-maskHeight / 2).toString() : (y - maskHeight / 2).toString(),
|
|
3200
|
+
width: maskWidth.toString(),
|
|
3201
|
+
height: maskHeight.toString(),
|
|
3202
|
+
rx: maskRadius.toString(),
|
|
3203
|
+
ry: maskRadius.toString(),
|
|
3204
|
+
"data-type": "pcb_soldermask",
|
|
3205
|
+
"data-pcb-layer": pad.layer,
|
|
3206
|
+
...rotationTransformAttributes ?? {}
|
|
3207
|
+
};
|
|
3208
|
+
const exposedOpeningElement = {
|
|
3209
|
+
name: "rect",
|
|
3210
|
+
type: "element",
|
|
3211
|
+
value: "",
|
|
3212
|
+
children: [],
|
|
3213
|
+
attributes: exposedAttributes
|
|
3214
|
+
};
|
|
3215
|
+
return [coveredPadElement, exposedOpeningElement];
|
|
3216
|
+
}
|
|
3217
|
+
if (soldermaskMargin === 0) {
|
|
3218
|
+
const coveredPadElement = {
|
|
3219
|
+
name: "rect",
|
|
3220
|
+
type: "element",
|
|
3221
|
+
value: "",
|
|
3222
|
+
children: [],
|
|
3223
|
+
attributes: {
|
|
3224
|
+
class: "pcb-pad-covered",
|
|
3225
|
+
fill: soldermaskWithCopperUnderneathColor,
|
|
3226
|
+
x: isRotated ? (-width / 2).toString() : (x - width / 2).toString(),
|
|
3227
|
+
y: isRotated ? (-height / 2).toString() : (y - height / 2).toString(),
|
|
3228
|
+
width: width.toString(),
|
|
3229
|
+
height: height.toString(),
|
|
3230
|
+
rx: radius.toString(),
|
|
3231
|
+
ry: radius.toString(),
|
|
3232
|
+
"data-type": "pcb_smtpad",
|
|
3233
|
+
"data-pcb-layer": pad.layer,
|
|
3234
|
+
...rotationTransformAttributes ?? {}
|
|
3235
|
+
}
|
|
3236
|
+
};
|
|
3237
|
+
return [coveredPadElement];
|
|
3238
|
+
}
|
|
3239
|
+
const substrateAttributes = {
|
|
3240
|
+
class: "pcb-soldermask-cutout",
|
|
3241
|
+
fill: colorMap2.substrate,
|
|
3242
|
+
x: isRotated ? (-maskWidth / 2).toString() : (x - maskWidth / 2).toString(),
|
|
3243
|
+
y: isRotated ? (-maskHeight / 2).toString() : (y - maskHeight / 2).toString(),
|
|
3244
|
+
width: maskWidth.toString(),
|
|
3245
|
+
height: maskHeight.toString(),
|
|
3246
|
+
rx: maskRadius.toString(),
|
|
3247
|
+
ry: maskRadius.toString(),
|
|
3248
|
+
"data-type": "pcb_soldermask_opening",
|
|
3249
|
+
"data-pcb-layer": pad.layer,
|
|
3250
|
+
...rotationTransformAttributes ?? {}
|
|
3251
|
+
};
|
|
3252
|
+
const substrateElement = {
|
|
3253
|
+
name: "rect",
|
|
3254
|
+
type: "element",
|
|
2490
3255
|
value: "",
|
|
2491
3256
|
children: [],
|
|
2492
|
-
attributes:
|
|
2493
|
-
...padElement.attributes,
|
|
2494
|
-
class: "pcb-solder-mask",
|
|
2495
|
-
fill: solderMaskColor,
|
|
2496
|
-
"data-type": "pcb_soldermask"
|
|
2497
|
-
}
|
|
3257
|
+
attributes: substrateAttributes
|
|
2498
3258
|
};
|
|
2499
|
-
return [
|
|
3259
|
+
return [substrateElement, padElement];
|
|
2500
3260
|
}
|
|
2501
3261
|
if (pad.shape === "circle") {
|
|
2502
3262
|
const radius = pad.radius * Math.abs(transform.a);
|
|
@@ -2516,22 +3276,77 @@ function createSvgObjectsFromSmtPad(pad, ctx) {
|
|
|
2516
3276
|
"data-pcb-layer": pad.layer
|
|
2517
3277
|
}
|
|
2518
3278
|
};
|
|
2519
|
-
if (!
|
|
3279
|
+
if (!shouldShowSolderMask) {
|
|
2520
3280
|
return [padElement];
|
|
2521
3281
|
}
|
|
2522
|
-
const
|
|
2523
|
-
|
|
2524
|
-
|
|
3282
|
+
const maskRadius = radius + soldermaskMargin;
|
|
3283
|
+
if (soldermaskMargin < 0) {
|
|
3284
|
+
const coveredPadElement = {
|
|
3285
|
+
name: "circle",
|
|
3286
|
+
type: "element",
|
|
3287
|
+
value: "",
|
|
3288
|
+
children: [],
|
|
3289
|
+
attributes: {
|
|
3290
|
+
class: "pcb-pad-covered",
|
|
3291
|
+
fill: soldermaskWithCopperUnderneathColor,
|
|
3292
|
+
cx: x.toString(),
|
|
3293
|
+
cy: y.toString(),
|
|
3294
|
+
r: radius.toString(),
|
|
3295
|
+
"data-type": "pcb_smtpad",
|
|
3296
|
+
"data-pcb-layer": pad.layer
|
|
3297
|
+
}
|
|
3298
|
+
};
|
|
3299
|
+
const exposedOpeningElement = {
|
|
3300
|
+
name: "circle",
|
|
3301
|
+
type: "element",
|
|
3302
|
+
value: "",
|
|
3303
|
+
children: [],
|
|
3304
|
+
attributes: {
|
|
3305
|
+
class: "pcb-pad-exposed",
|
|
3306
|
+
fill: layerNameToColor(pad.layer, colorMap2),
|
|
3307
|
+
cx: x.toString(),
|
|
3308
|
+
cy: y.toString(),
|
|
3309
|
+
r: maskRadius.toString(),
|
|
3310
|
+
"data-type": "pcb_soldermask",
|
|
3311
|
+
"data-pcb-layer": pad.layer
|
|
3312
|
+
}
|
|
3313
|
+
};
|
|
3314
|
+
return [coveredPadElement, exposedOpeningElement];
|
|
3315
|
+
}
|
|
3316
|
+
if (soldermaskMargin === 0) {
|
|
3317
|
+
const coveredPadElement = {
|
|
3318
|
+
name: "circle",
|
|
3319
|
+
type: "element",
|
|
3320
|
+
value: "",
|
|
3321
|
+
children: [],
|
|
3322
|
+
attributes: {
|
|
3323
|
+
class: "pcb-pad-covered",
|
|
3324
|
+
fill: soldermaskWithCopperUnderneathColor,
|
|
3325
|
+
cx: x.toString(),
|
|
3326
|
+
cy: y.toString(),
|
|
3327
|
+
r: radius.toString(),
|
|
3328
|
+
"data-type": "pcb_smtpad",
|
|
3329
|
+
"data-pcb-layer": pad.layer
|
|
3330
|
+
}
|
|
3331
|
+
};
|
|
3332
|
+
return [coveredPadElement];
|
|
3333
|
+
}
|
|
3334
|
+
const substrateElement = {
|
|
3335
|
+
name: "circle",
|
|
3336
|
+
type: "element",
|
|
2525
3337
|
value: "",
|
|
2526
3338
|
children: [],
|
|
2527
3339
|
attributes: {
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
3340
|
+
class: "pcb-soldermask-cutout",
|
|
3341
|
+
fill: colorMap2.substrate,
|
|
3342
|
+
cx: x.toString(),
|
|
3343
|
+
cy: y.toString(),
|
|
3344
|
+
r: maskRadius.toString(),
|
|
3345
|
+
"data-type": "pcb_soldermask_opening",
|
|
3346
|
+
"data-pcb-layer": pad.layer
|
|
2532
3347
|
}
|
|
2533
3348
|
};
|
|
2534
|
-
return [
|
|
3349
|
+
return [substrateElement, padElement];
|
|
2535
3350
|
}
|
|
2536
3351
|
if (pad.shape === "polygon") {
|
|
2537
3352
|
const points = (pad.points ?? []).map(
|
|
@@ -2550,27 +3365,90 @@ function createSvgObjectsFromSmtPad(pad, ctx) {
|
|
|
2550
3365
|
"data-pcb-layer": pad.layer
|
|
2551
3366
|
}
|
|
2552
3367
|
};
|
|
2553
|
-
if (!
|
|
3368
|
+
if (!shouldShowSolderMask) {
|
|
2554
3369
|
return [padElement];
|
|
2555
3370
|
}
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
3371
|
+
let maskPoints = points;
|
|
3372
|
+
if (soldermaskMargin !== 0) {
|
|
3373
|
+
const centroidX = points.reduce((sum, p) => sum + p[0], 0) / points.length;
|
|
3374
|
+
const centroidY = points.reduce((sum, p) => sum + p[1], 0) / points.length;
|
|
3375
|
+
maskPoints = points.map(([px, py]) => {
|
|
3376
|
+
const dx = px - centroidX;
|
|
3377
|
+
const dy = py - centroidY;
|
|
3378
|
+
const distance3 = Math.sqrt(dx * dx + dy * dy);
|
|
3379
|
+
if (distance3 === 0) return [px, py];
|
|
3380
|
+
const normalizedDx = dx / distance3;
|
|
3381
|
+
const normalizedDy = dy / distance3;
|
|
3382
|
+
return [
|
|
3383
|
+
px + normalizedDx * soldermaskMargin,
|
|
3384
|
+
py + normalizedDy * soldermaskMargin
|
|
3385
|
+
];
|
|
3386
|
+
});
|
|
3387
|
+
}
|
|
3388
|
+
if (soldermaskMargin < 0) {
|
|
3389
|
+
const coveredPadElement = {
|
|
3390
|
+
name: "polygon",
|
|
3391
|
+
type: "element",
|
|
3392
|
+
value: "",
|
|
3393
|
+
children: [],
|
|
3394
|
+
attributes: {
|
|
3395
|
+
class: "pcb-pad-covered",
|
|
3396
|
+
fill: soldermaskWithCopperUnderneathColor,
|
|
3397
|
+
points: points.map((p) => p.join(",")).join(" "),
|
|
3398
|
+
"data-type": "pcb_smtpad",
|
|
3399
|
+
"data-pcb-layer": pad.layer
|
|
3400
|
+
}
|
|
3401
|
+
};
|
|
3402
|
+
const exposedOpeningElement = {
|
|
3403
|
+
name: "polygon",
|
|
3404
|
+
type: "element",
|
|
3405
|
+
value: "",
|
|
3406
|
+
children: [],
|
|
3407
|
+
attributes: {
|
|
3408
|
+
class: "pcb-pad-exposed",
|
|
3409
|
+
fill: layerNameToColor(pad.layer, colorMap2),
|
|
3410
|
+
points: maskPoints.map((p) => p.join(",")).join(" "),
|
|
3411
|
+
"data-type": "pcb_soldermask",
|
|
3412
|
+
"data-pcb-layer": pad.layer
|
|
3413
|
+
}
|
|
3414
|
+
};
|
|
3415
|
+
return [coveredPadElement, exposedOpeningElement];
|
|
3416
|
+
}
|
|
3417
|
+
if (soldermaskMargin === 0) {
|
|
3418
|
+
const coveredPadElement = {
|
|
3419
|
+
name: "polygon",
|
|
3420
|
+
type: "element",
|
|
3421
|
+
value: "",
|
|
3422
|
+
children: [],
|
|
3423
|
+
attributes: {
|
|
3424
|
+
class: "pcb-pad-covered",
|
|
3425
|
+
fill: soldermaskWithCopperUnderneathColor,
|
|
3426
|
+
points: points.map((p) => p.join(",")).join(" "),
|
|
3427
|
+
"data-type": "pcb_smtpad",
|
|
3428
|
+
"data-pcb-layer": pad.layer
|
|
3429
|
+
}
|
|
3430
|
+
};
|
|
3431
|
+
return [coveredPadElement];
|
|
3432
|
+
}
|
|
3433
|
+
const substrateElement = {
|
|
3434
|
+
name: "polygon",
|
|
3435
|
+
type: "element",
|
|
2559
3436
|
value: "",
|
|
2560
3437
|
children: [],
|
|
2561
3438
|
attributes: {
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
"data-type": "
|
|
3439
|
+
class: "pcb-soldermask-cutout",
|
|
3440
|
+
fill: colorMap2.substrate,
|
|
3441
|
+
points: maskPoints.map((p) => p.join(",")).join(" "),
|
|
3442
|
+
"data-type": "pcb_soldermask_opening",
|
|
3443
|
+
"data-pcb-layer": pad.layer
|
|
2566
3444
|
}
|
|
2567
3445
|
};
|
|
2568
|
-
return [
|
|
3446
|
+
return [substrateElement, padElement];
|
|
2569
3447
|
}
|
|
2570
3448
|
return [];
|
|
2571
3449
|
}
|
|
2572
3450
|
function createSvgObjectsFromPcbBoard(pcbBoard, ctx) {
|
|
2573
|
-
const { transform, colorMap: colorMap2 } = ctx;
|
|
3451
|
+
const { transform, colorMap: colorMap2, showSolderMask } = ctx;
|
|
2574
3452
|
const { width, height, center, outline } = pcbBoard;
|
|
2575
3453
|
let path;
|
|
2576
3454
|
if (outline && Array.isArray(outline) && outline.length >= 3) {
|
|
@@ -2600,35 +3478,69 @@ function createSvgObjectsFromPcbBoard(pcbBoard, ctx) {
|
|
|
2600
3478
|
path = `M ${topLeft[0]} ${topLeft[1]} L ${topRight[0]} ${topRight[1]} L ${bottomRight[0]} ${bottomRight[1]} L ${bottomLeft[0]} ${bottomLeft[1]}`;
|
|
2601
3479
|
}
|
|
2602
3480
|
path += " Z";
|
|
2603
|
-
|
|
2604
|
-
|
|
3481
|
+
const svgObjects = [];
|
|
3482
|
+
if (showSolderMask) {
|
|
3483
|
+
const layer = ctx.layer ?? "top";
|
|
3484
|
+
const maskLayer = layer === "bottom" ? "soldermask-bottom" : "soldermask-top";
|
|
3485
|
+
svgObjects.push({
|
|
2605
3486
|
name: "path",
|
|
2606
3487
|
type: "element",
|
|
2607
3488
|
value: "",
|
|
2608
3489
|
children: [],
|
|
2609
3490
|
attributes: {
|
|
2610
|
-
class: "pcb-board",
|
|
3491
|
+
class: "pcb-board-soldermask",
|
|
2611
3492
|
d: path,
|
|
2612
|
-
fill:
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
"data-type": "
|
|
2616
|
-
"data-pcb-layer":
|
|
3493
|
+
fill: colorMap2.soldermask.top,
|
|
3494
|
+
"fill-opacity": "0.8",
|
|
3495
|
+
stroke: "none",
|
|
3496
|
+
"data-type": "pcb_soldermask",
|
|
3497
|
+
"data-pcb-layer": maskLayer
|
|
2617
3498
|
}
|
|
3499
|
+
});
|
|
3500
|
+
}
|
|
3501
|
+
svgObjects.push({
|
|
3502
|
+
name: "path",
|
|
3503
|
+
type: "element",
|
|
3504
|
+
value: "",
|
|
3505
|
+
children: [],
|
|
3506
|
+
attributes: {
|
|
3507
|
+
class: "pcb-board",
|
|
3508
|
+
d: path,
|
|
3509
|
+
fill: "none",
|
|
3510
|
+
stroke: colorMap2.boardOutline,
|
|
3511
|
+
"stroke-width": (0.1 * Math.abs(transform.a)).toString(),
|
|
3512
|
+
"data-type": "pcb_board",
|
|
3513
|
+
"data-pcb-layer": "board"
|
|
2618
3514
|
}
|
|
2619
|
-
|
|
3515
|
+
});
|
|
3516
|
+
return svgObjects;
|
|
2620
3517
|
}
|
|
2621
3518
|
function createSvgObjectsFromPcbPanel(pcbPanel, ctx) {
|
|
2622
3519
|
const { transform, colorMap: colorMap2, showSolderMask } = ctx;
|
|
2623
3520
|
const width = Number(pcbPanel.width);
|
|
2624
3521
|
const height = Number(pcbPanel.height);
|
|
2625
|
-
const
|
|
2626
|
-
const
|
|
2627
|
-
const
|
|
2628
|
-
const
|
|
3522
|
+
const center = pcbPanel.center ?? { x: width / 2, y: height / 2 };
|
|
3523
|
+
const halfWidth = width / 2;
|
|
3524
|
+
const halfHeight = height / 2;
|
|
3525
|
+
const topLeft = applyToPoint(transform, [
|
|
3526
|
+
center.x - halfWidth,
|
|
3527
|
+
center.y - halfHeight
|
|
3528
|
+
]);
|
|
3529
|
+
const topRight = applyToPoint(transform, [
|
|
3530
|
+
center.x + halfWidth,
|
|
3531
|
+
center.y - halfHeight
|
|
3532
|
+
]);
|
|
3533
|
+
const bottomRight = applyToPoint(transform, [
|
|
3534
|
+
center.x + halfWidth,
|
|
3535
|
+
center.y + halfHeight
|
|
3536
|
+
]);
|
|
3537
|
+
const bottomLeft = applyToPoint(transform, [
|
|
3538
|
+
center.x - halfWidth,
|
|
3539
|
+
center.y + halfHeight
|
|
3540
|
+
]);
|
|
2629
3541
|
const path = `M ${topLeft[0]} ${topLeft[1]} L ${topRight[0]} ${topRight[1]} L ${bottomRight[0]} ${bottomRight[1]} L ${bottomLeft[0]} ${bottomLeft[1]} Z`;
|
|
2630
3542
|
const isCoveredWithSolderMask = pcbPanel.covered_with_solder_mask !== false;
|
|
2631
|
-
const
|
|
3543
|
+
const shouldShowSolderMask = Boolean(
|
|
2632
3544
|
showSolderMask && isCoveredWithSolderMask
|
|
2633
3545
|
);
|
|
2634
3546
|
return [
|
|
@@ -2696,72 +3608,314 @@ function createSvgObjectsFromPcbVia(hole, ctx) {
|
|
|
2696
3608
|
};
|
|
2697
3609
|
}
|
|
2698
3610
|
function createSvgObjectsFromPcbHole(hole, ctx) {
|
|
2699
|
-
const { transform, colorMap: colorMap2 } = ctx;
|
|
3611
|
+
const { transform, colorMap: colorMap2, showSolderMask } = ctx;
|
|
3612
|
+
const layer = ctx.layer ?? "top";
|
|
2700
3613
|
const [x, y] = applyToPoint(transform, [hole.x, hole.y]);
|
|
3614
|
+
const isCoveredWithSolderMask = Boolean(hole.is_covered_with_solder_mask);
|
|
3615
|
+
const soldermaskMargin = (hole.soldermask_margin ?? 0) * Math.abs(transform.a);
|
|
3616
|
+
const shouldShowSolderMask = showSolderMask && isCoveredWithSolderMask && soldermaskMargin !== 0;
|
|
3617
|
+
const solderMaskColor = colorMap2.soldermask.top;
|
|
2701
3618
|
if (hole.hole_shape === "circle" || hole.hole_shape === "square") {
|
|
2702
3619
|
const scaledDiameter = hole.hole_diameter * Math.abs(transform.a);
|
|
2703
3620
|
const radius = scaledDiameter / 2;
|
|
2704
3621
|
if (hole.hole_shape === "circle") {
|
|
2705
|
-
|
|
2706
|
-
|
|
3622
|
+
const holeElement2 = {
|
|
3623
|
+
name: "circle",
|
|
3624
|
+
type: "element",
|
|
3625
|
+
attributes: {
|
|
3626
|
+
class: "pcb-hole",
|
|
3627
|
+
cx: x.toString(),
|
|
3628
|
+
cy: y.toString(),
|
|
3629
|
+
r: radius.toString(),
|
|
3630
|
+
fill: colorMap2.drill,
|
|
3631
|
+
"data-type": "pcb_hole",
|
|
3632
|
+
"data-pcb-layer": "drill"
|
|
3633
|
+
},
|
|
3634
|
+
children: [],
|
|
3635
|
+
value: ""
|
|
3636
|
+
};
|
|
3637
|
+
if (!shouldShowSolderMask) {
|
|
3638
|
+
return [holeElement2];
|
|
3639
|
+
}
|
|
3640
|
+
const maskRadius = radius + soldermaskMargin;
|
|
3641
|
+
if (soldermaskMargin < 0) {
|
|
3642
|
+
const coveredElement = {
|
|
2707
3643
|
name: "circle",
|
|
2708
3644
|
type: "element",
|
|
3645
|
+
value: "",
|
|
3646
|
+
children: [],
|
|
2709
3647
|
attributes: {
|
|
2710
|
-
class: "pcb-hole",
|
|
3648
|
+
class: "pcb-hole-covered",
|
|
3649
|
+
fill: solderMaskColor,
|
|
2711
3650
|
cx: x.toString(),
|
|
2712
3651
|
cy: y.toString(),
|
|
2713
3652
|
r: radius.toString(),
|
|
2714
|
-
fill: colorMap2.drill,
|
|
2715
3653
|
"data-type": "pcb_hole",
|
|
2716
3654
|
"data-pcb-layer": "drill"
|
|
2717
|
-
}
|
|
3655
|
+
}
|
|
3656
|
+
};
|
|
3657
|
+
const exposedElement = {
|
|
3658
|
+
name: "circle",
|
|
3659
|
+
type: "element",
|
|
3660
|
+
value: "",
|
|
2718
3661
|
children: [],
|
|
2719
|
-
|
|
3662
|
+
attributes: {
|
|
3663
|
+
class: "pcb-hole-exposed",
|
|
3664
|
+
fill: colorMap2.drill,
|
|
3665
|
+
cx: x.toString(),
|
|
3666
|
+
cy: y.toString(),
|
|
3667
|
+
r: maskRadius.toString(),
|
|
3668
|
+
"data-type": "pcb_soldermask",
|
|
3669
|
+
"data-pcb-layer": "drill"
|
|
3670
|
+
}
|
|
3671
|
+
};
|
|
3672
|
+
return [coveredElement, exposedElement];
|
|
3673
|
+
}
|
|
3674
|
+
const substrateElement2 = {
|
|
3675
|
+
name: "circle",
|
|
3676
|
+
type: "element",
|
|
3677
|
+
value: "",
|
|
3678
|
+
children: [],
|
|
3679
|
+
attributes: {
|
|
3680
|
+
class: "pcb-soldermask-cutout",
|
|
3681
|
+
fill: colorMap2.substrate,
|
|
3682
|
+
cx: x.toString(),
|
|
3683
|
+
cy: y.toString(),
|
|
3684
|
+
r: maskRadius.toString(),
|
|
3685
|
+
"data-type": "pcb_soldermask_opening",
|
|
3686
|
+
"data-pcb-layer": layer
|
|
2720
3687
|
}
|
|
2721
|
-
|
|
3688
|
+
};
|
|
3689
|
+
return [substrateElement2, holeElement2];
|
|
2722
3690
|
}
|
|
2723
|
-
|
|
2724
|
-
|
|
3691
|
+
const holeElement = {
|
|
3692
|
+
name: "rect",
|
|
3693
|
+
type: "element",
|
|
3694
|
+
attributes: {
|
|
3695
|
+
class: "pcb-hole",
|
|
3696
|
+
x: (x - radius).toString(),
|
|
3697
|
+
y: (y - radius).toString(),
|
|
3698
|
+
width: scaledDiameter.toString(),
|
|
3699
|
+
height: scaledDiameter.toString(),
|
|
3700
|
+
fill: colorMap2.drill,
|
|
3701
|
+
"data-type": "pcb_hole",
|
|
3702
|
+
"data-pcb-layer": "drill"
|
|
3703
|
+
},
|
|
3704
|
+
children: [],
|
|
3705
|
+
value: ""
|
|
3706
|
+
};
|
|
3707
|
+
if (!shouldShowSolderMask) {
|
|
3708
|
+
return [holeElement];
|
|
3709
|
+
}
|
|
3710
|
+
const maskDiameter = scaledDiameter + 2 * soldermaskMargin;
|
|
3711
|
+
if (soldermaskMargin < 0) {
|
|
3712
|
+
const coveredElement = {
|
|
2725
3713
|
name: "rect",
|
|
2726
3714
|
type: "element",
|
|
3715
|
+
value: "",
|
|
3716
|
+
children: [],
|
|
2727
3717
|
attributes: {
|
|
2728
|
-
class: "pcb-hole",
|
|
3718
|
+
class: "pcb-hole-covered",
|
|
3719
|
+
fill: solderMaskColor,
|
|
2729
3720
|
x: (x - radius).toString(),
|
|
2730
3721
|
y: (y - radius).toString(),
|
|
2731
3722
|
width: scaledDiameter.toString(),
|
|
2732
3723
|
height: scaledDiameter.toString(),
|
|
2733
|
-
fill: colorMap2.drill,
|
|
2734
3724
|
"data-type": "pcb_hole",
|
|
2735
3725
|
"data-pcb-layer": "drill"
|
|
2736
|
-
}
|
|
3726
|
+
}
|
|
3727
|
+
};
|
|
3728
|
+
const exposedElement = {
|
|
3729
|
+
name: "rect",
|
|
3730
|
+
type: "element",
|
|
3731
|
+
value: "",
|
|
2737
3732
|
children: [],
|
|
2738
|
-
|
|
3733
|
+
attributes: {
|
|
3734
|
+
class: "pcb-hole-exposed",
|
|
3735
|
+
fill: colorMap2.drill,
|
|
3736
|
+
x: (x - maskDiameter / 2).toString(),
|
|
3737
|
+
y: (y - maskDiameter / 2).toString(),
|
|
3738
|
+
width: maskDiameter.toString(),
|
|
3739
|
+
height: maskDiameter.toString(),
|
|
3740
|
+
"data-type": "pcb_soldermask",
|
|
3741
|
+
"data-pcb-layer": "drill"
|
|
3742
|
+
}
|
|
3743
|
+
};
|
|
3744
|
+
return [coveredElement, exposedElement];
|
|
3745
|
+
}
|
|
3746
|
+
const substrateElement = {
|
|
3747
|
+
name: "rect",
|
|
3748
|
+
type: "element",
|
|
3749
|
+
value: "",
|
|
3750
|
+
children: [],
|
|
3751
|
+
attributes: {
|
|
3752
|
+
class: "pcb-soldermask-cutout",
|
|
3753
|
+
fill: colorMap2.substrate,
|
|
3754
|
+
x: (x - maskDiameter / 2).toString(),
|
|
3755
|
+
y: (y - maskDiameter / 2).toString(),
|
|
3756
|
+
width: maskDiameter.toString(),
|
|
3757
|
+
height: maskDiameter.toString(),
|
|
3758
|
+
"data-type": "pcb_soldermask_opening",
|
|
3759
|
+
"data-pcb-layer": layer
|
|
2739
3760
|
}
|
|
2740
|
-
|
|
3761
|
+
};
|
|
3762
|
+
return [substrateElement, holeElement];
|
|
2741
3763
|
}
|
|
2742
3764
|
if (hole.hole_shape === "oval") {
|
|
2743
3765
|
const scaledWidth = hole.hole_width * Math.abs(transform.a);
|
|
2744
3766
|
const scaledHeight = hole.hole_height * Math.abs(transform.a);
|
|
2745
3767
|
const rx = scaledWidth / 2;
|
|
2746
3768
|
const ry = scaledHeight / 2;
|
|
2747
|
-
|
|
2748
|
-
|
|
3769
|
+
const holeElement = {
|
|
3770
|
+
name: "ellipse",
|
|
3771
|
+
type: "element",
|
|
3772
|
+
attributes: {
|
|
3773
|
+
class: "pcb-hole",
|
|
3774
|
+
cx: x.toString(),
|
|
3775
|
+
cy: y.toString(),
|
|
3776
|
+
rx: rx.toString(),
|
|
3777
|
+
ry: ry.toString(),
|
|
3778
|
+
fill: colorMap2.drill,
|
|
3779
|
+
"data-type": "pcb_hole",
|
|
3780
|
+
"data-pcb-layer": "drill"
|
|
3781
|
+
},
|
|
3782
|
+
children: [],
|
|
3783
|
+
value: ""
|
|
3784
|
+
};
|
|
3785
|
+
if (!shouldShowSolderMask) {
|
|
3786
|
+
return [holeElement];
|
|
3787
|
+
}
|
|
3788
|
+
const maskRx = rx + soldermaskMargin;
|
|
3789
|
+
const maskRy = ry + soldermaskMargin;
|
|
3790
|
+
if (soldermaskMargin < 0) {
|
|
3791
|
+
const coveredElement = {
|
|
2749
3792
|
name: "ellipse",
|
|
2750
3793
|
type: "element",
|
|
3794
|
+
value: "",
|
|
3795
|
+
children: [],
|
|
2751
3796
|
attributes: {
|
|
2752
|
-
class: "pcb-hole",
|
|
3797
|
+
class: "pcb-hole-covered",
|
|
3798
|
+
fill: solderMaskColor,
|
|
2753
3799
|
cx: x.toString(),
|
|
2754
3800
|
cy: y.toString(),
|
|
2755
3801
|
rx: rx.toString(),
|
|
2756
3802
|
ry: ry.toString(),
|
|
3803
|
+
"data-type": "pcb_hole",
|
|
3804
|
+
"data-pcb-layer": "drill"
|
|
3805
|
+
}
|
|
3806
|
+
};
|
|
3807
|
+
const exposedElement = {
|
|
3808
|
+
name: "ellipse",
|
|
3809
|
+
type: "element",
|
|
3810
|
+
value: "",
|
|
3811
|
+
children: [],
|
|
3812
|
+
attributes: {
|
|
3813
|
+
class: "pcb-hole-exposed",
|
|
2757
3814
|
fill: colorMap2.drill,
|
|
3815
|
+
cx: x.toString(),
|
|
3816
|
+
cy: y.toString(),
|
|
3817
|
+
rx: maskRx.toString(),
|
|
3818
|
+
ry: maskRy.toString(),
|
|
3819
|
+
"data-type": "pcb_soldermask",
|
|
3820
|
+
"data-pcb-layer": "drill"
|
|
3821
|
+
}
|
|
3822
|
+
};
|
|
3823
|
+
return [coveredElement, exposedElement];
|
|
3824
|
+
}
|
|
3825
|
+
const substrateElement = {
|
|
3826
|
+
name: "ellipse",
|
|
3827
|
+
type: "element",
|
|
3828
|
+
value: "",
|
|
3829
|
+
children: [],
|
|
3830
|
+
attributes: {
|
|
3831
|
+
class: "pcb-soldermask-cutout",
|
|
3832
|
+
fill: colorMap2.substrate,
|
|
3833
|
+
cx: x.toString(),
|
|
3834
|
+
cy: y.toString(),
|
|
3835
|
+
rx: maskRx.toString(),
|
|
3836
|
+
ry: maskRy.toString(),
|
|
3837
|
+
"data-type": "pcb_soldermask_opening",
|
|
3838
|
+
"data-pcb-layer": layer
|
|
3839
|
+
}
|
|
3840
|
+
};
|
|
3841
|
+
return [substrateElement, holeElement];
|
|
3842
|
+
}
|
|
3843
|
+
if (hole.hole_shape === "rect") {
|
|
3844
|
+
const scaledWidth = hole.hole_width * Math.abs(transform.a);
|
|
3845
|
+
const scaledHeight = hole.hole_height * Math.abs(transform.a);
|
|
3846
|
+
const holeElement = {
|
|
3847
|
+
name: "rect",
|
|
3848
|
+
type: "element",
|
|
3849
|
+
attributes: {
|
|
3850
|
+
class: "pcb-hole",
|
|
3851
|
+
x: (x - scaledWidth / 2).toString(),
|
|
3852
|
+
y: (y - scaledHeight / 2).toString(),
|
|
3853
|
+
width: scaledWidth.toString(),
|
|
3854
|
+
height: scaledHeight.toString(),
|
|
3855
|
+
fill: colorMap2.drill,
|
|
3856
|
+
"data-type": "pcb_hole",
|
|
3857
|
+
"data-pcb-layer": "drill"
|
|
3858
|
+
},
|
|
3859
|
+
children: [],
|
|
3860
|
+
value: ""
|
|
3861
|
+
};
|
|
3862
|
+
if (!shouldShowSolderMask) {
|
|
3863
|
+
return [holeElement];
|
|
3864
|
+
}
|
|
3865
|
+
const maskWidth = scaledWidth + 2 * soldermaskMargin;
|
|
3866
|
+
const maskHeight = scaledHeight + 2 * soldermaskMargin;
|
|
3867
|
+
if (soldermaskMargin < 0) {
|
|
3868
|
+
const coveredElement = {
|
|
3869
|
+
name: "rect",
|
|
3870
|
+
type: "element",
|
|
3871
|
+
value: "",
|
|
3872
|
+
children: [],
|
|
3873
|
+
attributes: {
|
|
3874
|
+
class: "pcb-hole-covered",
|
|
3875
|
+
fill: solderMaskColor,
|
|
3876
|
+
x: (x - scaledWidth / 2).toString(),
|
|
3877
|
+
y: (y - scaledHeight / 2).toString(),
|
|
3878
|
+
width: scaledWidth.toString(),
|
|
3879
|
+
height: scaledHeight.toString(),
|
|
2758
3880
|
"data-type": "pcb_hole",
|
|
2759
3881
|
"data-pcb-layer": "drill"
|
|
2760
|
-
}
|
|
3882
|
+
}
|
|
3883
|
+
};
|
|
3884
|
+
const exposedElement = {
|
|
3885
|
+
name: "rect",
|
|
3886
|
+
type: "element",
|
|
3887
|
+
value: "",
|
|
2761
3888
|
children: [],
|
|
2762
|
-
|
|
3889
|
+
attributes: {
|
|
3890
|
+
class: "pcb-hole-exposed",
|
|
3891
|
+
fill: colorMap2.drill,
|
|
3892
|
+
x: (x - maskWidth / 2).toString(),
|
|
3893
|
+
y: (y - maskHeight / 2).toString(),
|
|
3894
|
+
width: maskWidth.toString(),
|
|
3895
|
+
height: maskHeight.toString(),
|
|
3896
|
+
"data-type": "pcb_soldermask",
|
|
3897
|
+
"data-pcb-layer": "drill"
|
|
3898
|
+
}
|
|
3899
|
+
};
|
|
3900
|
+
return [coveredElement, exposedElement];
|
|
3901
|
+
}
|
|
3902
|
+
const substrateElement = {
|
|
3903
|
+
name: "rect",
|
|
3904
|
+
type: "element",
|
|
3905
|
+
value: "",
|
|
3906
|
+
children: [],
|
|
3907
|
+
attributes: {
|
|
3908
|
+
class: "pcb-soldermask-cutout",
|
|
3909
|
+
fill: colorMap2.substrate,
|
|
3910
|
+
x: (x - maskWidth / 2).toString(),
|
|
3911
|
+
y: (y - maskHeight / 2).toString(),
|
|
3912
|
+
width: maskWidth.toString(),
|
|
3913
|
+
height: maskHeight.toString(),
|
|
3914
|
+
"data-type": "pcb_soldermask_opening",
|
|
3915
|
+
"data-pcb-layer": layer
|
|
2763
3916
|
}
|
|
2764
|
-
|
|
3917
|
+
};
|
|
3918
|
+
return [substrateElement, holeElement];
|
|
2765
3919
|
}
|
|
2766
3920
|
if (hole.hole_shape === "pill") {
|
|
2767
3921
|
const scaledWidth = hole.hole_width * Math.abs(transform.a);
|
|
@@ -2778,21 +3932,79 @@ function createSvgObjectsFromPcbHole(hole, ctx) {
|
|
|
2778
3932
|
// Vertical pill (taller than wide)
|
|
2779
3933
|
`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`
|
|
2780
3934
|
);
|
|
2781
|
-
|
|
2782
|
-
|
|
3935
|
+
const holeElement = {
|
|
3936
|
+
name: "path",
|
|
3937
|
+
type: "element",
|
|
3938
|
+
attributes: {
|
|
3939
|
+
class: "pcb-hole",
|
|
3940
|
+
fill: colorMap2.drill,
|
|
3941
|
+
d: pathD,
|
|
3942
|
+
"data-type": "pcb_hole",
|
|
3943
|
+
"data-pcb-layer": "drill"
|
|
3944
|
+
},
|
|
3945
|
+
children: [],
|
|
3946
|
+
value: ""
|
|
3947
|
+
};
|
|
3948
|
+
if (!shouldShowSolderMask) {
|
|
3949
|
+
return [holeElement];
|
|
3950
|
+
}
|
|
3951
|
+
const maskWidth = scaledWidth + 2 * soldermaskMargin;
|
|
3952
|
+
const maskHeight = scaledHeight + 2 * soldermaskMargin;
|
|
3953
|
+
const maskIsHorizontal = maskWidth > maskHeight;
|
|
3954
|
+
const maskRadius = Math.min(maskWidth, maskHeight) / 2;
|
|
3955
|
+
const maskStraightLength = Math.abs(
|
|
3956
|
+
maskIsHorizontal ? maskWidth - maskHeight : maskHeight - maskWidth
|
|
3957
|
+
);
|
|
3958
|
+
const maskPathD = maskIsHorizontal ? (
|
|
3959
|
+
// Horizontal pill (wider than tall)
|
|
3960
|
+
`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`
|
|
3961
|
+
) : (
|
|
3962
|
+
// Vertical pill (taller than wide)
|
|
3963
|
+
`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`
|
|
3964
|
+
);
|
|
3965
|
+
if (soldermaskMargin < 0) {
|
|
3966
|
+
const coveredElement = {
|
|
2783
3967
|
name: "path",
|
|
2784
3968
|
type: "element",
|
|
3969
|
+
value: "",
|
|
3970
|
+
children: [],
|
|
2785
3971
|
attributes: {
|
|
2786
|
-
class: "pcb-hole",
|
|
2787
|
-
fill:
|
|
3972
|
+
class: "pcb-hole-covered",
|
|
3973
|
+
fill: solderMaskColor,
|
|
2788
3974
|
d: pathD,
|
|
2789
3975
|
"data-type": "pcb_hole",
|
|
2790
3976
|
"data-pcb-layer": "drill"
|
|
2791
|
-
}
|
|
3977
|
+
}
|
|
3978
|
+
};
|
|
3979
|
+
const exposedElement = {
|
|
3980
|
+
name: "path",
|
|
3981
|
+
type: "element",
|
|
3982
|
+
value: "",
|
|
2792
3983
|
children: [],
|
|
2793
|
-
|
|
3984
|
+
attributes: {
|
|
3985
|
+
class: "pcb-hole-exposed",
|
|
3986
|
+
fill: colorMap2.drill,
|
|
3987
|
+
d: maskPathD,
|
|
3988
|
+
"data-type": "pcb_soldermask",
|
|
3989
|
+
"data-pcb-layer": "drill"
|
|
3990
|
+
}
|
|
3991
|
+
};
|
|
3992
|
+
return [coveredElement, exposedElement];
|
|
3993
|
+
}
|
|
3994
|
+
const substrateElement = {
|
|
3995
|
+
name: "path",
|
|
3996
|
+
type: "element",
|
|
3997
|
+
value: "",
|
|
3998
|
+
children: [],
|
|
3999
|
+
attributes: {
|
|
4000
|
+
class: "pcb-soldermask-cutout",
|
|
4001
|
+
fill: colorMap2.substrate,
|
|
4002
|
+
d: maskPathD,
|
|
4003
|
+
"data-type": "pcb_soldermask_opening",
|
|
4004
|
+
"data-pcb-layer": layer
|
|
2794
4005
|
}
|
|
2795
|
-
|
|
4006
|
+
};
|
|
4007
|
+
return [substrateElement, holeElement];
|
|
2796
4008
|
}
|
|
2797
4009
|
if (hole.hole_shape === "rotated_pill") {
|
|
2798
4010
|
const scaledWidth = hole.hole_width * Math.abs(transform.a);
|
|
@@ -2810,22 +4022,82 @@ function createSvgObjectsFromPcbHole(hole, ctx) {
|
|
|
2810
4022
|
// Vertical pill (taller than wide)
|
|
2811
4023
|
`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`
|
|
2812
4024
|
);
|
|
2813
|
-
|
|
2814
|
-
|
|
4025
|
+
const holeElement = {
|
|
4026
|
+
name: "path",
|
|
4027
|
+
type: "element",
|
|
4028
|
+
attributes: {
|
|
4029
|
+
class: "pcb-hole",
|
|
4030
|
+
fill: colorMap2.drill,
|
|
4031
|
+
d: pathD,
|
|
4032
|
+
transform: `translate(${x} ${y}) rotate(${-rotation})`,
|
|
4033
|
+
"data-type": "pcb_hole",
|
|
4034
|
+
"data-pcb-layer": "drill"
|
|
4035
|
+
},
|
|
4036
|
+
children: [],
|
|
4037
|
+
value: ""
|
|
4038
|
+
};
|
|
4039
|
+
if (!shouldShowSolderMask) {
|
|
4040
|
+
return [holeElement];
|
|
4041
|
+
}
|
|
4042
|
+
const maskWidth = scaledWidth + 2 * soldermaskMargin;
|
|
4043
|
+
const maskHeight = scaledHeight + 2 * soldermaskMargin;
|
|
4044
|
+
const maskIsHorizontal = maskWidth > maskHeight;
|
|
4045
|
+
const maskRadius = Math.min(maskWidth, maskHeight) / 2;
|
|
4046
|
+
const maskStraightLength = Math.abs(
|
|
4047
|
+
maskIsHorizontal ? maskWidth - maskHeight : maskHeight - maskWidth
|
|
4048
|
+
);
|
|
4049
|
+
const maskPathD = maskIsHorizontal ? (
|
|
4050
|
+
// Horizontal pill (wider than tall)
|
|
4051
|
+
`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`
|
|
4052
|
+
) : (
|
|
4053
|
+
// Vertical pill (taller than wide)
|
|
4054
|
+
`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`
|
|
4055
|
+
);
|
|
4056
|
+
if (soldermaskMargin < 0) {
|
|
4057
|
+
const coveredElement = {
|
|
2815
4058
|
name: "path",
|
|
2816
4059
|
type: "element",
|
|
4060
|
+
value: "",
|
|
4061
|
+
children: [],
|
|
2817
4062
|
attributes: {
|
|
2818
|
-
class: "pcb-hole",
|
|
2819
|
-
fill:
|
|
4063
|
+
class: "pcb-hole-covered",
|
|
4064
|
+
fill: solderMaskColor,
|
|
2820
4065
|
d: pathD,
|
|
2821
4066
|
transform: `translate(${x} ${y}) rotate(${-rotation})`,
|
|
2822
4067
|
"data-type": "pcb_hole",
|
|
2823
4068
|
"data-pcb-layer": "drill"
|
|
2824
|
-
}
|
|
4069
|
+
}
|
|
4070
|
+
};
|
|
4071
|
+
const exposedElement = {
|
|
4072
|
+
name: "path",
|
|
4073
|
+
type: "element",
|
|
4074
|
+
value: "",
|
|
2825
4075
|
children: [],
|
|
2826
|
-
|
|
4076
|
+
attributes: {
|
|
4077
|
+
class: "pcb-hole-exposed",
|
|
4078
|
+
fill: colorMap2.drill,
|
|
4079
|
+
d: maskPathD,
|
|
4080
|
+
transform: `translate(${x} ${y}) rotate(${-rotation})`,
|
|
4081
|
+
"data-type": "pcb_soldermask",
|
|
4082
|
+
"data-pcb-layer": "drill"
|
|
4083
|
+
}
|
|
4084
|
+
};
|
|
4085
|
+
return [coveredElement, exposedElement];
|
|
4086
|
+
}
|
|
4087
|
+
const substrateElement = {
|
|
4088
|
+
name: "path",
|
|
4089
|
+
type: "element",
|
|
4090
|
+
value: "",
|
|
4091
|
+
children: [],
|
|
4092
|
+
attributes: {
|
|
4093
|
+
class: "pcb-soldermask-cutout",
|
|
4094
|
+
fill: colorMap2.substrate,
|
|
4095
|
+
d: maskPathD,
|
|
4096
|
+
"data-type": "pcb_soldermask_opening",
|
|
4097
|
+
"data-pcb-layer": layer
|
|
2827
4098
|
}
|
|
2828
|
-
|
|
4099
|
+
};
|
|
4100
|
+
return [substrateElement, holeElement];
|
|
2829
4101
|
}
|
|
2830
4102
|
return [];
|
|
2831
4103
|
}
|
|
@@ -2939,23 +4211,34 @@ function createSvgObjectsFromPcbCutout(cutout, ctx) {
|
|
|
2939
4211
|
const scaledWidth = rectCutout.width * Math.abs(transform.a);
|
|
2940
4212
|
const scaledHeight = rectCutout.height * Math.abs(transform.d);
|
|
2941
4213
|
const svgRotation = -(rectCutout.rotation ?? 0);
|
|
4214
|
+
const { corner_radius } = rectCutout;
|
|
4215
|
+
const baseCornerRadius = typeof corner_radius === "number" && corner_radius > 0 ? corner_radius : 0;
|
|
4216
|
+
const transformedCornerRadiusX = baseCornerRadius * Math.abs(transform.a);
|
|
4217
|
+
const transformedCornerRadiusY = baseCornerRadius * Math.abs(transform.d);
|
|
4218
|
+
const attributes = {
|
|
4219
|
+
class: "pcb-cutout pcb-cutout-rect",
|
|
4220
|
+
x: (-scaledWidth / 2).toString(),
|
|
4221
|
+
y: (-scaledHeight / 2).toString(),
|
|
4222
|
+
width: scaledWidth.toString(),
|
|
4223
|
+
height: scaledHeight.toString(),
|
|
4224
|
+
fill: colorMap2.drill,
|
|
4225
|
+
transform: toString(
|
|
4226
|
+
compose(translate(cx, cy), rotate(svgRotation * Math.PI / 180))
|
|
4227
|
+
),
|
|
4228
|
+
"data-type": "pcb_cutout",
|
|
4229
|
+
"data-pcb-layer": "drill"
|
|
4230
|
+
};
|
|
4231
|
+
if (transformedCornerRadiusX > 0) {
|
|
4232
|
+
attributes.rx = transformedCornerRadiusX.toString();
|
|
4233
|
+
}
|
|
4234
|
+
if (transformedCornerRadiusY > 0) {
|
|
4235
|
+
attributes.ry = transformedCornerRadiusY.toString();
|
|
4236
|
+
}
|
|
2942
4237
|
return [
|
|
2943
4238
|
{
|
|
2944
4239
|
name: "rect",
|
|
2945
4240
|
type: "element",
|
|
2946
|
-
attributes
|
|
2947
|
-
class: "pcb-cutout pcb-cutout-rect",
|
|
2948
|
-
x: (-scaledWidth / 2).toString(),
|
|
2949
|
-
y: (-scaledHeight / 2).toString(),
|
|
2950
|
-
width: scaledWidth.toString(),
|
|
2951
|
-
height: scaledHeight.toString(),
|
|
2952
|
-
fill: colorMap2.drill,
|
|
2953
|
-
transform: toString(
|
|
2954
|
-
compose(translate(cx, cy), rotate(svgRotation * Math.PI / 180))
|
|
2955
|
-
),
|
|
2956
|
-
"data-type": "pcb_cutout",
|
|
2957
|
-
"data-pcb-layer": "drill"
|
|
2958
|
-
},
|
|
4241
|
+
attributes,
|
|
2959
4242
|
children: [],
|
|
2960
4243
|
value: ""
|
|
2961
4244
|
}
|
|
@@ -3038,39 +4321,113 @@ function ringToPathD(vertices, transform) {
|
|
|
3038
4321
|
d += " Z";
|
|
3039
4322
|
return d;
|
|
3040
4323
|
}
|
|
4324
|
+
function createSoldermaskCutoutElement({
|
|
4325
|
+
elementType,
|
|
4326
|
+
shapeAttributes,
|
|
4327
|
+
layer,
|
|
4328
|
+
colorMap: colorMap2,
|
|
4329
|
+
additionalAttributes
|
|
4330
|
+
}) {
|
|
4331
|
+
const baseAttributes = {
|
|
4332
|
+
class: "pcb-soldermask-cutout",
|
|
4333
|
+
fill: colorMap2.substrate,
|
|
4334
|
+
"data-type": "pcb_soldermask_opening",
|
|
4335
|
+
"data-pcb-layer": layer,
|
|
4336
|
+
...shapeAttributes,
|
|
4337
|
+
...additionalAttributes
|
|
4338
|
+
};
|
|
4339
|
+
return {
|
|
4340
|
+
name: elementType,
|
|
4341
|
+
type: "element",
|
|
4342
|
+
value: "",
|
|
4343
|
+
children: [],
|
|
4344
|
+
attributes: baseAttributes
|
|
4345
|
+
};
|
|
4346
|
+
}
|
|
4347
|
+
function createSoldermaskOverlayElement({
|
|
4348
|
+
elementType,
|
|
4349
|
+
shapeAttributes,
|
|
4350
|
+
layer,
|
|
4351
|
+
fillColor,
|
|
4352
|
+
fillOpacity,
|
|
4353
|
+
className,
|
|
4354
|
+
additionalAttributes
|
|
4355
|
+
}) {
|
|
4356
|
+
const baseAttributes = {
|
|
4357
|
+
class: className,
|
|
4358
|
+
fill: fillColor,
|
|
4359
|
+
"fill-opacity": fillOpacity,
|
|
4360
|
+
"data-type": "pcb_soldermask",
|
|
4361
|
+
"data-pcb-layer": layer,
|
|
4362
|
+
...shapeAttributes,
|
|
4363
|
+
...additionalAttributes
|
|
4364
|
+
};
|
|
4365
|
+
return {
|
|
4366
|
+
name: elementType,
|
|
4367
|
+
type: "element",
|
|
4368
|
+
value: "",
|
|
4369
|
+
children: [],
|
|
4370
|
+
attributes: baseAttributes
|
|
4371
|
+
};
|
|
4372
|
+
}
|
|
3041
4373
|
function createSvgObjectsFromPcbCopperPour(pour, ctx) {
|
|
3042
|
-
const { transform, layer: layerFilter, colorMap: colorMap2 } = ctx;
|
|
4374
|
+
const { transform, layer: layerFilter, colorMap: colorMap2, showSolderMask } = ctx;
|
|
3043
4375
|
const { layer } = pour;
|
|
3044
4376
|
if (layerFilter && layer !== layerFilter) return [];
|
|
3045
4377
|
const color = layerNameToColor(layer, colorMap2);
|
|
3046
4378
|
const opacity = "0.5";
|
|
4379
|
+
const isCoveredWithSolderMask = pour.covered_with_solder_mask !== false;
|
|
4380
|
+
const maskOverlayColor = layer === "bottom" ? colorMap2.soldermaskOverCopper.bottom : colorMap2.soldermaskOverCopper.top;
|
|
4381
|
+
const maskOverlayOpacity = "0.9";
|
|
3047
4382
|
if (pour.shape === "rect") {
|
|
3048
4383
|
const [cx, cy] = applyToPoint(transform, [pour.center.x, pour.center.y]);
|
|
3049
4384
|
const scaledWidth = pour.width * Math.abs(transform.a);
|
|
3050
4385
|
const scaledHeight = pour.height * Math.abs(transform.d);
|
|
3051
4386
|
const svgRotation = -(pour.rotation ?? 0);
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
4387
|
+
const rectAttributes = {
|
|
4388
|
+
x: (-scaledWidth / 2).toString(),
|
|
4389
|
+
y: (-scaledHeight / 2).toString(),
|
|
4390
|
+
width: scaledWidth.toString(),
|
|
4391
|
+
height: scaledHeight.toString(),
|
|
4392
|
+
transform: toString(
|
|
4393
|
+
compose(translate(cx, cy), rotate(svgRotation * Math.PI / 180))
|
|
4394
|
+
)
|
|
4395
|
+
};
|
|
4396
|
+
const copperRect = {
|
|
4397
|
+
name: "rect",
|
|
4398
|
+
type: "element",
|
|
4399
|
+
value: "",
|
|
4400
|
+
children: [],
|
|
4401
|
+
attributes: {
|
|
4402
|
+
class: "pcb-copper-pour pcb-copper-pour-rect",
|
|
4403
|
+
...rectAttributes,
|
|
4404
|
+
fill: color,
|
|
4405
|
+
"fill-opacity": opacity,
|
|
4406
|
+
"data-type": "pcb_copper_pour",
|
|
4407
|
+
"data-pcb-layer": layer
|
|
3072
4408
|
}
|
|
3073
|
-
|
|
4409
|
+
};
|
|
4410
|
+
const maskRect = showSolderMask ? isCoveredWithSolderMask ? createSoldermaskOverlayElement({
|
|
4411
|
+
elementType: "rect",
|
|
4412
|
+
shapeAttributes: rectAttributes,
|
|
4413
|
+
layer,
|
|
4414
|
+
fillColor: maskOverlayColor,
|
|
4415
|
+
fillOpacity: maskOverlayOpacity,
|
|
4416
|
+
className: "pcb-soldermask-covered-pour"
|
|
4417
|
+
}) : createSoldermaskCutoutElement({
|
|
4418
|
+
elementType: "rect",
|
|
4419
|
+
shapeAttributes: rectAttributes,
|
|
4420
|
+
layer,
|
|
4421
|
+
colorMap: colorMap2
|
|
4422
|
+
}) : null;
|
|
4423
|
+
if (!maskRect) {
|
|
4424
|
+
return [copperRect];
|
|
4425
|
+
}
|
|
4426
|
+
const isSubstrateOnly = !isCoveredWithSolderMask && pour.pcb_copper_pour_id?.includes("substrate_only");
|
|
4427
|
+
if (isSubstrateOnly) {
|
|
4428
|
+
return [maskRect];
|
|
4429
|
+
}
|
|
4430
|
+
return [copperRect, maskRect];
|
|
3074
4431
|
}
|
|
3075
4432
|
if (pour.shape === "polygon") {
|
|
3076
4433
|
if (!pour.points || pour.points.length === 0) return [];
|
|
@@ -3078,22 +4435,41 @@ function createSvgObjectsFromPcbCopperPour(pour, ctx) {
|
|
|
3078
4435
|
(p) => applyToPoint(transform, [p.x, p.y])
|
|
3079
4436
|
);
|
|
3080
4437
|
const pointsString = transformedPoints.map((p) => `${p[0]},${p[1]}`).join(" ");
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
children: [],
|
|
3094
|
-
value: ""
|
|
4438
|
+
const copperPolygon = {
|
|
4439
|
+
name: "polygon",
|
|
4440
|
+
type: "element",
|
|
4441
|
+
value: "",
|
|
4442
|
+
children: [],
|
|
4443
|
+
attributes: {
|
|
4444
|
+
class: "pcb-copper-pour pcb-copper-pour-polygon",
|
|
4445
|
+
points: pointsString,
|
|
4446
|
+
fill: color,
|
|
4447
|
+
"fill-opacity": opacity,
|
|
4448
|
+
"data-type": "pcb_copper_pour",
|
|
4449
|
+
"data-pcb-layer": layer
|
|
3095
4450
|
}
|
|
3096
|
-
|
|
4451
|
+
};
|
|
4452
|
+
const maskPolygon = showSolderMask ? isCoveredWithSolderMask ? createSoldermaskOverlayElement({
|
|
4453
|
+
elementType: "polygon",
|
|
4454
|
+
shapeAttributes: { points: pointsString },
|
|
4455
|
+
layer,
|
|
4456
|
+
fillColor: maskOverlayColor,
|
|
4457
|
+
fillOpacity: maskOverlayOpacity,
|
|
4458
|
+
className: "pcb-soldermask-covered-pour"
|
|
4459
|
+
}) : createSoldermaskCutoutElement({
|
|
4460
|
+
elementType: "polygon",
|
|
4461
|
+
shapeAttributes: { points: pointsString },
|
|
4462
|
+
layer,
|
|
4463
|
+
colorMap: colorMap2
|
|
4464
|
+
}) : null;
|
|
4465
|
+
if (!maskPolygon) {
|
|
4466
|
+
return [copperPolygon];
|
|
4467
|
+
}
|
|
4468
|
+
const isSubstrateOnly = !isCoveredWithSolderMask && pour.pcb_copper_pour_id?.includes("substrate_only");
|
|
4469
|
+
if (isSubstrateOnly) {
|
|
4470
|
+
return [maskPolygon];
|
|
4471
|
+
}
|
|
4472
|
+
return [copperPolygon, maskPolygon];
|
|
3097
4473
|
}
|
|
3098
4474
|
if (pour.shape === "brep") {
|
|
3099
4475
|
const { brep_shape } = pour;
|
|
@@ -3101,23 +4477,42 @@ function createSvgObjectsFromPcbCopperPour(pour, ctx) {
|
|
|
3101
4477
|
for (const inner_ring of brep_shape.inner_rings ?? []) {
|
|
3102
4478
|
d += ` ${ringToPathD(inner_ring.vertices, transform)}`;
|
|
3103
4479
|
}
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
children: [],
|
|
3118
|
-
value: ""
|
|
4480
|
+
const copperPath = {
|
|
4481
|
+
name: "path",
|
|
4482
|
+
type: "element",
|
|
4483
|
+
value: "",
|
|
4484
|
+
children: [],
|
|
4485
|
+
attributes: {
|
|
4486
|
+
class: "pcb-copper-pour pcb-copper-pour-brep",
|
|
4487
|
+
d,
|
|
4488
|
+
fill: color,
|
|
4489
|
+
"fill-rule": "evenodd",
|
|
4490
|
+
"fill-opacity": opacity,
|
|
4491
|
+
"data-type": "pcb_copper_pour",
|
|
4492
|
+
"data-pcb-layer": layer
|
|
3119
4493
|
}
|
|
3120
|
-
|
|
4494
|
+
};
|
|
4495
|
+
const maskPath = showSolderMask ? isCoveredWithSolderMask ? createSoldermaskOverlayElement({
|
|
4496
|
+
elementType: "path",
|
|
4497
|
+
shapeAttributes: { d, "fill-rule": "evenodd" },
|
|
4498
|
+
layer,
|
|
4499
|
+
fillColor: maskOverlayColor,
|
|
4500
|
+
fillOpacity: maskOverlayOpacity,
|
|
4501
|
+
className: "pcb-soldermask-covered-pour"
|
|
4502
|
+
}) : createSoldermaskCutoutElement({
|
|
4503
|
+
elementType: "path",
|
|
4504
|
+
shapeAttributes: { d, "fill-rule": "evenodd" },
|
|
4505
|
+
layer,
|
|
4506
|
+
colorMap: colorMap2
|
|
4507
|
+
}) : null;
|
|
4508
|
+
if (!maskPath) {
|
|
4509
|
+
return [copperPath];
|
|
4510
|
+
}
|
|
4511
|
+
const isSubstrateOnly = !isCoveredWithSolderMask && pour.pcb_copper_pour_id?.includes("substrate_only");
|
|
4512
|
+
if (isSubstrateOnly) {
|
|
4513
|
+
return [maskPath];
|
|
4514
|
+
}
|
|
4515
|
+
return [copperPath, maskPath];
|
|
3121
4516
|
}
|
|
3122
4517
|
return [];
|
|
3123
4518
|
}
|
|
@@ -3255,45 +4650,362 @@ function createMajorGridPatternChildren(cellSize, majorCellSize, lineColor, majo
|
|
|
3255
4650
|
}
|
|
3256
4651
|
return children;
|
|
3257
4652
|
}
|
|
4653
|
+
var OFFSET_THRESHOLD_MM = 0.01;
|
|
4654
|
+
var TICK_SIZE_PX = 4;
|
|
4655
|
+
var LABEL_GAP_PX = 8;
|
|
4656
|
+
var LABEL_FONT_SIZE_PX = 11;
|
|
4657
|
+
var STROKE_WIDTH_PX = 1;
|
|
4658
|
+
var ANCHOR_MARKER_SIZE_PX = 5;
|
|
4659
|
+
var ANCHOR_MARKER_STROKE_WIDTH_PX = 1.5;
|
|
4660
|
+
var COMPONENT_GAP_PX = 15;
|
|
4661
|
+
var COMPONENT_SIDE_GAP_PX = 10;
|
|
4662
|
+
var DISTANCE_MULTIPLIER = 0.2;
|
|
4663
|
+
var MAX_OFFSET_PX = 50;
|
|
4664
|
+
function createAnchorOffsetIndicators(params) {
|
|
4665
|
+
const {
|
|
4666
|
+
groupAnchorPosition,
|
|
4667
|
+
componentPosition,
|
|
4668
|
+
transform,
|
|
4669
|
+
componentWidth = 0,
|
|
4670
|
+
componentHeight = 0
|
|
4671
|
+
} = params;
|
|
4672
|
+
const objects = [];
|
|
4673
|
+
const [screenGroupAnchorX, screenGroupAnchorY] = applyToPoint(transform, [
|
|
4674
|
+
groupAnchorPosition.x,
|
|
4675
|
+
groupAnchorPosition.y
|
|
4676
|
+
]);
|
|
4677
|
+
const [screenComponentX, screenComponentY] = applyToPoint(transform, [
|
|
4678
|
+
componentPosition.x,
|
|
4679
|
+
componentPosition.y
|
|
4680
|
+
]);
|
|
4681
|
+
const offsetX = componentPosition.x - groupAnchorPosition.x;
|
|
4682
|
+
const offsetY = componentPosition.y - groupAnchorPosition.y;
|
|
4683
|
+
const scale9 = Math.abs(transform.a);
|
|
4684
|
+
const screenComponentWidth = componentWidth * scale9;
|
|
4685
|
+
const screenComponentHeight = componentHeight * scale9;
|
|
4686
|
+
objects.push(createAnchorMarker(screenGroupAnchorX, screenGroupAnchorY));
|
|
4687
|
+
objects.push({
|
|
4688
|
+
name: "line",
|
|
4689
|
+
type: "element",
|
|
4690
|
+
attributes: {
|
|
4691
|
+
x1: screenGroupAnchorX.toString(),
|
|
4692
|
+
y1: screenGroupAnchorY.toString(),
|
|
4693
|
+
x2: screenComponentX.toString(),
|
|
4694
|
+
y2: screenComponentY.toString(),
|
|
4695
|
+
stroke: "#ffffff",
|
|
4696
|
+
"stroke-width": "0.5",
|
|
4697
|
+
"stroke-dasharray": "3,3",
|
|
4698
|
+
opacity: "0.5",
|
|
4699
|
+
class: "anchor-offset-connector"
|
|
4700
|
+
},
|
|
4701
|
+
children: [],
|
|
4702
|
+
value: ""
|
|
4703
|
+
});
|
|
4704
|
+
objects.push({
|
|
4705
|
+
name: "circle",
|
|
4706
|
+
type: "element",
|
|
4707
|
+
attributes: {
|
|
4708
|
+
cx: screenComponentX.toString(),
|
|
4709
|
+
cy: screenComponentY.toString(),
|
|
4710
|
+
r: "2",
|
|
4711
|
+
fill: "#ffffff",
|
|
4712
|
+
opacity: "0.7",
|
|
4713
|
+
class: "anchor-offset-component-marker"
|
|
4714
|
+
},
|
|
4715
|
+
children: [],
|
|
4716
|
+
value: ""
|
|
4717
|
+
});
|
|
4718
|
+
const yDistance = Math.abs(screenComponentY - screenGroupAnchorY);
|
|
4719
|
+
const xDistance = Math.abs(screenComponentX - screenGroupAnchorX);
|
|
4720
|
+
const totalDistance = Math.sqrt(xDistance * xDistance + yDistance * yDistance);
|
|
4721
|
+
const componentHeightOffset = screenComponentHeight / 2 + COMPONENT_GAP_PX;
|
|
4722
|
+
const dynamicOffset = Math.max(
|
|
4723
|
+
componentHeightOffset,
|
|
4724
|
+
Math.min(MAX_OFFSET_PX, totalDistance * DISTANCE_MULTIPLIER)
|
|
4725
|
+
);
|
|
4726
|
+
const horizontalLineY = offsetY > 0 ? screenComponentY - dynamicOffset : screenComponentY + dynamicOffset;
|
|
4727
|
+
const componentWidthOffset = screenComponentWidth / 2 + COMPONENT_SIDE_GAP_PX;
|
|
4728
|
+
const verticalLineX = offsetX > 0 ? screenComponentX + componentWidthOffset : screenComponentX - componentWidthOffset;
|
|
4729
|
+
if (Math.abs(offsetX) > OFFSET_THRESHOLD_MM) {
|
|
4730
|
+
objects.push(
|
|
4731
|
+
...createHorizontalDimension({
|
|
4732
|
+
startX: screenGroupAnchorX,
|
|
4733
|
+
endX: screenComponentX,
|
|
4734
|
+
y: horizontalLineY,
|
|
4735
|
+
offsetMm: offsetX,
|
|
4736
|
+
offsetY
|
|
4737
|
+
})
|
|
4738
|
+
);
|
|
4739
|
+
}
|
|
4740
|
+
if (Math.abs(offsetY) > OFFSET_THRESHOLD_MM) {
|
|
4741
|
+
objects.push(
|
|
4742
|
+
...createVerticalDimension({
|
|
4743
|
+
x: verticalLineX,
|
|
4744
|
+
startY: screenGroupAnchorY,
|
|
4745
|
+
endY: screenComponentY,
|
|
4746
|
+
offsetMm: -offsetY,
|
|
4747
|
+
offsetX
|
|
4748
|
+
})
|
|
4749
|
+
);
|
|
4750
|
+
}
|
|
4751
|
+
return objects;
|
|
4752
|
+
}
|
|
4753
|
+
function createAnchorMarker(x, y) {
|
|
4754
|
+
return {
|
|
4755
|
+
name: "g",
|
|
4756
|
+
type: "element",
|
|
4757
|
+
attributes: {
|
|
4758
|
+
class: "anchor-offset-marker",
|
|
4759
|
+
"data-type": "anchor_offset_marker"
|
|
4760
|
+
},
|
|
4761
|
+
children: [
|
|
4762
|
+
{
|
|
4763
|
+
name: "line",
|
|
4764
|
+
type: "element",
|
|
4765
|
+
attributes: {
|
|
4766
|
+
x1: x.toString(),
|
|
4767
|
+
y1: (y - ANCHOR_MARKER_SIZE_PX).toString(),
|
|
4768
|
+
x2: x.toString(),
|
|
4769
|
+
y2: (y + ANCHOR_MARKER_SIZE_PX).toString(),
|
|
4770
|
+
stroke: "#ffffff",
|
|
4771
|
+
"stroke-width": ANCHOR_MARKER_STROKE_WIDTH_PX.toString(),
|
|
4772
|
+
"stroke-linecap": "round"
|
|
4773
|
+
},
|
|
4774
|
+
children: [],
|
|
4775
|
+
value: ""
|
|
4776
|
+
},
|
|
4777
|
+
{
|
|
4778
|
+
name: "line",
|
|
4779
|
+
type: "element",
|
|
4780
|
+
attributes: {
|
|
4781
|
+
x1: (x - ANCHOR_MARKER_SIZE_PX).toString(),
|
|
4782
|
+
y1: y.toString(),
|
|
4783
|
+
x2: (x + ANCHOR_MARKER_SIZE_PX).toString(),
|
|
4784
|
+
y2: y.toString(),
|
|
4785
|
+
stroke: "#ffffff",
|
|
4786
|
+
"stroke-width": ANCHOR_MARKER_STROKE_WIDTH_PX.toString(),
|
|
4787
|
+
"stroke-linecap": "round"
|
|
4788
|
+
},
|
|
4789
|
+
children: [],
|
|
4790
|
+
value: ""
|
|
4791
|
+
}
|
|
4792
|
+
],
|
|
4793
|
+
value: ""
|
|
4794
|
+
};
|
|
4795
|
+
}
|
|
4796
|
+
function createHorizontalDimension({
|
|
4797
|
+
startX,
|
|
4798
|
+
endX,
|
|
4799
|
+
y,
|
|
4800
|
+
offsetMm,
|
|
4801
|
+
offsetY
|
|
4802
|
+
}) {
|
|
4803
|
+
const objects = [];
|
|
4804
|
+
objects.push({
|
|
4805
|
+
name: "line",
|
|
4806
|
+
type: "element",
|
|
4807
|
+
attributes: {
|
|
4808
|
+
x1: startX.toString(),
|
|
4809
|
+
y1: y.toString(),
|
|
4810
|
+
x2: endX.toString(),
|
|
4811
|
+
y2: y.toString(),
|
|
4812
|
+
stroke: "#ffffff",
|
|
4813
|
+
"stroke-width": STROKE_WIDTH_PX.toString(),
|
|
4814
|
+
class: "anchor-offset-dimension-x"
|
|
4815
|
+
},
|
|
4816
|
+
children: [],
|
|
4817
|
+
value: ""
|
|
4818
|
+
});
|
|
4819
|
+
objects.push({
|
|
4820
|
+
name: "line",
|
|
4821
|
+
type: "element",
|
|
4822
|
+
attributes: {
|
|
4823
|
+
x1: startX.toString(),
|
|
4824
|
+
y1: (y - TICK_SIZE_PX).toString(),
|
|
4825
|
+
x2: startX.toString(),
|
|
4826
|
+
y2: (y + TICK_SIZE_PX).toString(),
|
|
4827
|
+
stroke: "#ffffff",
|
|
4828
|
+
"stroke-width": STROKE_WIDTH_PX.toString()
|
|
4829
|
+
},
|
|
4830
|
+
children: [],
|
|
4831
|
+
value: ""
|
|
4832
|
+
});
|
|
4833
|
+
objects.push({
|
|
4834
|
+
name: "line",
|
|
4835
|
+
type: "element",
|
|
4836
|
+
attributes: {
|
|
4837
|
+
x1: endX.toString(),
|
|
4838
|
+
y1: (y - TICK_SIZE_PX).toString(),
|
|
4839
|
+
x2: endX.toString(),
|
|
4840
|
+
y2: (y + TICK_SIZE_PX).toString(),
|
|
4841
|
+
stroke: "#ffffff",
|
|
4842
|
+
"stroke-width": STROKE_WIDTH_PX.toString()
|
|
4843
|
+
},
|
|
4844
|
+
children: [],
|
|
4845
|
+
value: ""
|
|
4846
|
+
});
|
|
4847
|
+
const midX = (startX + endX) / 2;
|
|
4848
|
+
const labelY = offsetY > 0 ? y - TICK_SIZE_PX - LABEL_GAP_PX : y + TICK_SIZE_PX + LABEL_GAP_PX;
|
|
4849
|
+
objects.push({
|
|
4850
|
+
name: "text",
|
|
4851
|
+
type: "element",
|
|
4852
|
+
attributes: {
|
|
4853
|
+
x: midX.toString(),
|
|
4854
|
+
y: labelY.toString(),
|
|
4855
|
+
fill: "#ffffff",
|
|
4856
|
+
"font-size": LABEL_FONT_SIZE_PX.toString(),
|
|
4857
|
+
"font-family": "Arial, sans-serif",
|
|
4858
|
+
"text-anchor": "middle",
|
|
4859
|
+
"dominant-baseline": offsetY > 0 ? "baseline" : "hanging",
|
|
4860
|
+
class: "anchor-offset-label"
|
|
4861
|
+
},
|
|
4862
|
+
children: [
|
|
4863
|
+
{
|
|
4864
|
+
type: "text",
|
|
4865
|
+
value: `X: ${offsetMm.toFixed(2)}mm`,
|
|
4866
|
+
name: "",
|
|
4867
|
+
attributes: {},
|
|
4868
|
+
children: []
|
|
4869
|
+
}
|
|
4870
|
+
],
|
|
4871
|
+
value: ""
|
|
4872
|
+
});
|
|
4873
|
+
return objects;
|
|
4874
|
+
}
|
|
4875
|
+
function createVerticalDimension({
|
|
4876
|
+
x,
|
|
4877
|
+
startY,
|
|
4878
|
+
endY,
|
|
4879
|
+
offsetMm,
|
|
4880
|
+
offsetX
|
|
4881
|
+
}) {
|
|
4882
|
+
const objects = [];
|
|
4883
|
+
objects.push({
|
|
4884
|
+
name: "line",
|
|
4885
|
+
type: "element",
|
|
4886
|
+
attributes: {
|
|
4887
|
+
x1: x.toString(),
|
|
4888
|
+
y1: startY.toString(),
|
|
4889
|
+
x2: x.toString(),
|
|
4890
|
+
y2: endY.toString(),
|
|
4891
|
+
stroke: "#ffffff",
|
|
4892
|
+
"stroke-width": STROKE_WIDTH_PX.toString(),
|
|
4893
|
+
class: "anchor-offset-dimension-y"
|
|
4894
|
+
},
|
|
4895
|
+
children: [],
|
|
4896
|
+
value: ""
|
|
4897
|
+
});
|
|
4898
|
+
objects.push({
|
|
4899
|
+
name: "line",
|
|
4900
|
+
type: "element",
|
|
4901
|
+
attributes: {
|
|
4902
|
+
x1: (x - TICK_SIZE_PX).toString(),
|
|
4903
|
+
y1: startY.toString(),
|
|
4904
|
+
x2: (x + TICK_SIZE_PX).toString(),
|
|
4905
|
+
y2: startY.toString(),
|
|
4906
|
+
stroke: "#ffffff",
|
|
4907
|
+
"stroke-width": STROKE_WIDTH_PX.toString()
|
|
4908
|
+
},
|
|
4909
|
+
children: [],
|
|
4910
|
+
value: ""
|
|
4911
|
+
});
|
|
4912
|
+
objects.push({
|
|
4913
|
+
name: "line",
|
|
4914
|
+
type: "element",
|
|
4915
|
+
attributes: {
|
|
4916
|
+
x1: (x - TICK_SIZE_PX).toString(),
|
|
4917
|
+
y1: endY.toString(),
|
|
4918
|
+
x2: (x + TICK_SIZE_PX).toString(),
|
|
4919
|
+
y2: endY.toString(),
|
|
4920
|
+
stroke: "#ffffff",
|
|
4921
|
+
"stroke-width": STROKE_WIDTH_PX.toString()
|
|
4922
|
+
},
|
|
4923
|
+
children: [],
|
|
4924
|
+
value: ""
|
|
4925
|
+
});
|
|
4926
|
+
const midY = (startY + endY) / 2;
|
|
4927
|
+
const labelX = offsetX < 0 ? x - TICK_SIZE_PX - 4 : x + TICK_SIZE_PX + 4;
|
|
4928
|
+
objects.push({
|
|
4929
|
+
name: "text",
|
|
4930
|
+
type: "element",
|
|
4931
|
+
attributes: {
|
|
4932
|
+
x: labelX.toString(),
|
|
4933
|
+
y: midY.toString(),
|
|
4934
|
+
fill: "#ffffff",
|
|
4935
|
+
"font-size": LABEL_FONT_SIZE_PX.toString(),
|
|
4936
|
+
"font-family": "Arial, sans-serif",
|
|
4937
|
+
"text-anchor": offsetX < 0 ? "end" : "start",
|
|
4938
|
+
"dominant-baseline": "middle",
|
|
4939
|
+
class: "anchor-offset-label"
|
|
4940
|
+
},
|
|
4941
|
+
children: [
|
|
4942
|
+
{
|
|
4943
|
+
type: "text",
|
|
4944
|
+
value: `Y: ${offsetMm.toFixed(2)}mm`,
|
|
4945
|
+
name: "",
|
|
4946
|
+
attributes: {},
|
|
4947
|
+
children: []
|
|
4948
|
+
}
|
|
4949
|
+
],
|
|
4950
|
+
value: ""
|
|
4951
|
+
});
|
|
4952
|
+
return objects;
|
|
4953
|
+
}
|
|
3258
4954
|
function createSvgObjectsFromPcbComponent(component, ctx) {
|
|
3259
|
-
const { transform } = ctx;
|
|
4955
|
+
const { transform, circuitJson } = ctx;
|
|
3260
4956
|
const { center, width, height, rotation = 0 } = component;
|
|
3261
4957
|
const [x, y] = applyToPoint(transform, [center.x, center.y]);
|
|
3262
4958
|
const scaledWidth = width * Math.abs(transform.a);
|
|
3263
4959
|
const scaledHeight = height * Math.abs(transform.d);
|
|
3264
4960
|
const transformStr = `translate(${x}, ${y}) rotate(${-rotation}) scale(1, -1)`;
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
type: "element",
|
|
3281
|
-
attributes: {
|
|
3282
|
-
class: "pcb-component",
|
|
3283
|
-
x: (-scaledWidth / 2).toString(),
|
|
3284
|
-
y: (-scaledHeight / 2).toString(),
|
|
3285
|
-
width: scaledWidth.toString(),
|
|
3286
|
-
height: scaledHeight.toString(),
|
|
3287
|
-
fill: ctx.colorMap.debugComponent.fill ?? "transparent",
|
|
3288
|
-
stroke: ctx.colorMap.debugComponent.stroke ?? "transparent",
|
|
3289
|
-
"data-type": "pcb_component",
|
|
3290
|
-
"data-pcb-layer": component.layer ?? "top"
|
|
3291
|
-
}
|
|
3292
|
-
}
|
|
3293
|
-
],
|
|
3294
|
-
value: ""
|
|
4961
|
+
const svgObjects = [];
|
|
4962
|
+
if (ctx.showAnchorOffsets && component.positioned_relative_to_pcb_group_id && component.position_mode === "relative" && circuitJson) {
|
|
4963
|
+
const pcbGroup = circuitJson.find(
|
|
4964
|
+
(elm) => elm.type === "pcb_group" && elm.pcb_group_id === component.positioned_relative_to_pcb_group_id
|
|
4965
|
+
);
|
|
4966
|
+
if (pcbGroup?.center) {
|
|
4967
|
+
svgObjects.push(
|
|
4968
|
+
...createAnchorOffsetIndicators({
|
|
4969
|
+
groupAnchorPosition: pcbGroup.center,
|
|
4970
|
+
componentPosition: center,
|
|
4971
|
+
transform,
|
|
4972
|
+
componentWidth: width,
|
|
4973
|
+
componentHeight: height
|
|
4974
|
+
})
|
|
4975
|
+
);
|
|
3295
4976
|
}
|
|
3296
|
-
|
|
4977
|
+
}
|
|
4978
|
+
if (!ctx.colorMap.debugComponent?.fill && !ctx.colorMap.debugComponent?.stroke) {
|
|
4979
|
+
return svgObjects;
|
|
4980
|
+
}
|
|
4981
|
+
svgObjects.push({
|
|
4982
|
+
name: "g",
|
|
4983
|
+
type: "element",
|
|
4984
|
+
attributes: {
|
|
4985
|
+
transform: transformStr,
|
|
4986
|
+
"data-type": "pcb_component",
|
|
4987
|
+
"data-pcb-layer": component.layer ?? "top"
|
|
4988
|
+
},
|
|
4989
|
+
children: [
|
|
4990
|
+
{
|
|
4991
|
+
name: "rect",
|
|
4992
|
+
type: "element",
|
|
4993
|
+
attributes: {
|
|
4994
|
+
class: "pcb-component",
|
|
4995
|
+
x: (-scaledWidth / 2).toString(),
|
|
4996
|
+
y: (-scaledHeight / 2).toString(),
|
|
4997
|
+
width: scaledWidth.toString(),
|
|
4998
|
+
height: scaledHeight.toString(),
|
|
4999
|
+
fill: ctx.colorMap.debugComponent.fill ?? "transparent",
|
|
5000
|
+
stroke: ctx.colorMap.debugComponent.stroke ?? "transparent",
|
|
5001
|
+
"data-type": "pcb_component",
|
|
5002
|
+
"data-pcb-layer": component.layer ?? "top"
|
|
5003
|
+
}
|
|
5004
|
+
}
|
|
5005
|
+
],
|
|
5006
|
+
value: ""
|
|
5007
|
+
});
|
|
5008
|
+
return svgObjects;
|
|
3297
5009
|
}
|
|
3298
5010
|
var DEFAULT_GROUP_COLOR = "rgba(100, 200, 255, 0.6)";
|
|
3299
5011
|
var DEFAULT_STROKE_WIDTH = 0.1;
|
|
@@ -3379,7 +5091,7 @@ function getSoftwareUsedString(circuitJson) {
|
|
|
3379
5091
|
var package_default = {
|
|
3380
5092
|
name: "circuit-to-svg",
|
|
3381
5093
|
type: "module",
|
|
3382
|
-
version: "0.0.
|
|
5094
|
+
version: "0.0.282",
|
|
3383
5095
|
description: "Convert Circuit JSON to SVG",
|
|
3384
5096
|
main: "dist/index.js",
|
|
3385
5097
|
files: [
|
|
@@ -3403,12 +5115,12 @@ var package_default = {
|
|
|
3403
5115
|
"bun-match-svg": "^0.0.12",
|
|
3404
5116
|
esbuild: "^0.20.2",
|
|
3405
5117
|
"performance-now": "^2.1.0",
|
|
3406
|
-
"circuit-json": "^0.0.
|
|
5118
|
+
"circuit-json": "^0.0.319",
|
|
3407
5119
|
react: "19.1.0",
|
|
3408
5120
|
"react-cosmos": "7.0.0",
|
|
3409
5121
|
"react-cosmos-plugin-vite": "7.0.0",
|
|
3410
5122
|
"react-dom": "19.1.0",
|
|
3411
|
-
tscircuit: "^0.0.
|
|
5123
|
+
tscircuit: "^0.0.937",
|
|
3412
5124
|
tsup: "^8.0.2",
|
|
3413
5125
|
typescript: "^5.4.5",
|
|
3414
5126
|
"vite-tsconfig-paths": "^5.0.1"
|
|
@@ -3431,11 +5143,13 @@ var TYPE_PRIORITY = {
|
|
|
3431
5143
|
pcb_hole: 18,
|
|
3432
5144
|
pcb_plated_hole_drill: 19,
|
|
3433
5145
|
pcb_plated_hole: 20,
|
|
5146
|
+
pcb_trace_soldermask: 25,
|
|
3434
5147
|
pcb_trace: 30,
|
|
3435
5148
|
pcb_smtpad: 30,
|
|
3436
5149
|
pcb_copper_pour: 35,
|
|
3437
5150
|
pcb_via: 36,
|
|
3438
5151
|
pcb_soldermask: 40,
|
|
5152
|
+
pcb_soldermask_opening: 25,
|
|
3439
5153
|
pcb_solder_paste: 45,
|
|
3440
5154
|
pcb_silkscreen_text: 50,
|
|
3441
5155
|
pcb_silkscreen_path: 50,
|
|
@@ -3480,15 +5194,17 @@ function getLayerPriority(layer) {
|
|
|
3480
5194
|
if (!layer) return 500;
|
|
3481
5195
|
const normalized = layer.toLowerCase();
|
|
3482
5196
|
if (normalized === "global") return -100;
|
|
3483
|
-
if (normalized === "bottom") return
|
|
5197
|
+
if (normalized === "bottom") return 4;
|
|
3484
5198
|
if (normalized === "board") return 2;
|
|
5199
|
+
if (normalized === "soldermask-top" || normalized === "soldermask-bottom")
|
|
5200
|
+
return 3;
|
|
3485
5201
|
if (normalized.startsWith("inner")) {
|
|
3486
5202
|
const match = normalized.match(/\d+/);
|
|
3487
|
-
const layerIndex = match ? parseInt(match[0], 10) : 0;
|
|
5203
|
+
const layerIndex = match ? Number.parseInt(match[0], 10) : 0;
|
|
3488
5204
|
return 5 + layerIndex;
|
|
3489
5205
|
}
|
|
3490
5206
|
if (normalized === "through") return 18;
|
|
3491
|
-
if (normalized === "top") return
|
|
5207
|
+
if (normalized === "top") return 17;
|
|
3492
5208
|
if (normalized === "drill") return 30;
|
|
3493
5209
|
if (normalized === "overlay") return 40;
|
|
3494
5210
|
return 10;
|
|
@@ -3497,6 +5213,51 @@ function getTypePriority(type) {
|
|
|
3497
5213
|
if (!type) return DEFAULT_TYPE_PRIORITY;
|
|
3498
5214
|
return TYPE_PRIORITY[type] ?? DEFAULT_TYPE_PRIORITY;
|
|
3499
5215
|
}
|
|
5216
|
+
function createErrorTextOverlay(circuitJson, dataType = "error_text_overlay") {
|
|
5217
|
+
const errorElms = circuitJson.filter(
|
|
5218
|
+
(elm) => elm.type.endsWith("_error")
|
|
5219
|
+
);
|
|
5220
|
+
if (errorElms.length === 0) {
|
|
5221
|
+
return null;
|
|
5222
|
+
}
|
|
5223
|
+
const errorMessages = errorElms.map((e) => e.message).filter((m) => !!m);
|
|
5224
|
+
if (errorMessages.length === 0) {
|
|
5225
|
+
return null;
|
|
5226
|
+
}
|
|
5227
|
+
const textBlock = {
|
|
5228
|
+
name: "text",
|
|
5229
|
+
type: "element",
|
|
5230
|
+
value: "",
|
|
5231
|
+
attributes: {
|
|
5232
|
+
x: "10",
|
|
5233
|
+
y: "20",
|
|
5234
|
+
fill: "red",
|
|
5235
|
+
"font-family": "monospace",
|
|
5236
|
+
"font-size": "12px",
|
|
5237
|
+
"data-type": dataType,
|
|
5238
|
+
"data-layer": "global"
|
|
5239
|
+
},
|
|
5240
|
+
children: errorMessages.map((msg, i) => ({
|
|
5241
|
+
name: "tspan",
|
|
5242
|
+
type: "element",
|
|
5243
|
+
value: "",
|
|
5244
|
+
attributes: {
|
|
5245
|
+
x: "10",
|
|
5246
|
+
dy: i === 0 ? "0" : "1.2em"
|
|
5247
|
+
},
|
|
5248
|
+
children: [
|
|
5249
|
+
{
|
|
5250
|
+
type: "text",
|
|
5251
|
+
value: msg,
|
|
5252
|
+
name: "",
|
|
5253
|
+
attributes: {},
|
|
5254
|
+
children: []
|
|
5255
|
+
}
|
|
5256
|
+
]
|
|
5257
|
+
}))
|
|
5258
|
+
};
|
|
5259
|
+
return textBlock;
|
|
5260
|
+
}
|
|
3500
5261
|
function convertCircuitJsonToPcbSvg(circuitJson, options) {
|
|
3501
5262
|
const drawPaddingOutsideBoard = options?.drawPaddingOutsideBoard ?? true;
|
|
3502
5263
|
const layer = options?.layer;
|
|
@@ -3523,6 +5284,15 @@ function convertCircuitJsonToPcbSvg(circuitJson, options) {
|
|
|
3523
5284
|
top: colorOverrides?.soldermask?.top ?? DEFAULT_PCB_COLOR_MAP.soldermask.top,
|
|
3524
5285
|
bottom: colorOverrides?.soldermask?.bottom ?? DEFAULT_PCB_COLOR_MAP.soldermask.bottom
|
|
3525
5286
|
},
|
|
5287
|
+
soldermaskOverCopper: {
|
|
5288
|
+
top: colorOverrides?.soldermaskOverCopper?.top ?? DEFAULT_PCB_COLOR_MAP.soldermaskOverCopper.top,
|
|
5289
|
+
bottom: colorOverrides?.soldermaskOverCopper?.bottom ?? DEFAULT_PCB_COLOR_MAP.soldermaskOverCopper.bottom
|
|
5290
|
+
},
|
|
5291
|
+
soldermaskWithCopperUnderneath: {
|
|
5292
|
+
top: colorOverrides?.soldermaskWithCopperUnderneath?.top ?? DEFAULT_PCB_COLOR_MAP.soldermaskWithCopperUnderneath.top,
|
|
5293
|
+
bottom: colorOverrides?.soldermaskWithCopperUnderneath?.bottom ?? DEFAULT_PCB_COLOR_MAP.soldermaskWithCopperUnderneath.bottom
|
|
5294
|
+
},
|
|
5295
|
+
substrate: colorOverrides?.substrate ?? DEFAULT_PCB_COLOR_MAP.substrate,
|
|
3526
5296
|
courtyard: colorOverrides?.courtyard ?? DEFAULT_PCB_COLOR_MAP.courtyard,
|
|
3527
5297
|
debugComponent: {
|
|
3528
5298
|
fill: colorOverrides?.debugComponent?.fill ?? DEFAULT_PCB_COLOR_MAP.debugComponent.fill,
|
|
@@ -3547,7 +5317,7 @@ function convertCircuitJsonToPcbSvg(circuitJson, options) {
|
|
|
3547
5317
|
if (width === void 0 || height === void 0) {
|
|
3548
5318
|
continue;
|
|
3549
5319
|
}
|
|
3550
|
-
const center = { x: width / 2, y: height / 2 };
|
|
5320
|
+
const center = panel.center ?? { x: width / 2, y: height / 2 };
|
|
3551
5321
|
updateBounds(center, width, height);
|
|
3552
5322
|
} else if (circuitJsonElm.type === "pcb_board") {
|
|
3553
5323
|
if (circuitJsonElm.outline && Array.isArray(circuitJsonElm.outline) && circuitJsonElm.outline.length >= 3) {
|
|
@@ -3663,12 +5433,13 @@ function convertCircuitJsonToPcbSvg(circuitJson, options) {
|
|
|
3663
5433
|
showPcbGroups: options?.showPcbGroups,
|
|
3664
5434
|
drawPaddingOutsideBoard,
|
|
3665
5435
|
colorMap: colorMap2,
|
|
3666
|
-
showSolderMask: options?.showSolderMask
|
|
5436
|
+
showSolderMask: options?.showSolderMask,
|
|
5437
|
+
showAnchorOffsets: options?.showAnchorOffsets,
|
|
5438
|
+
circuitJson
|
|
3667
5439
|
};
|
|
3668
|
-
|
|
5440
|
+
let unsortedSvgObjects = circuitJson.flatMap(
|
|
3669
5441
|
(elm) => createSvgObjects({ elm, circuitJson, ctx })
|
|
3670
5442
|
);
|
|
3671
|
-
let svgObjects = sortSvgObjectsByPcbLayer(unsortedSvgObjects);
|
|
3672
5443
|
let strokeWidth = String(0.05 * scaleFactor);
|
|
3673
5444
|
for (const element of circuitJson) {
|
|
3674
5445
|
if ("stroke_width" in element) {
|
|
@@ -3678,8 +5449,9 @@ function convertCircuitJsonToPcbSvg(circuitJson, options) {
|
|
|
3678
5449
|
}
|
|
3679
5450
|
if (options?.shouldDrawRatsNest) {
|
|
3680
5451
|
const ratsNestObjects = createSvgObjectsForRatsNest(circuitJson, ctx);
|
|
3681
|
-
|
|
5452
|
+
unsortedSvgObjects = [...unsortedSvgObjects, ...ratsNestObjects];
|
|
3682
5453
|
}
|
|
5454
|
+
const svgObjects = sortSvgObjectsByPcbLayer(unsortedSvgObjects);
|
|
3683
5455
|
const children = [
|
|
3684
5456
|
{
|
|
3685
5457
|
name: "style",
|
|
@@ -3730,6 +5502,15 @@ function convertCircuitJsonToPcbSvg(circuitJson, options) {
|
|
|
3730
5502
|
if (gridObjects.rect) {
|
|
3731
5503
|
children.push(gridObjects.rect);
|
|
3732
5504
|
}
|
|
5505
|
+
if (options?.showErrorsInTextOverlay) {
|
|
5506
|
+
const errorOverlay = createErrorTextOverlay(
|
|
5507
|
+
circuitJson,
|
|
5508
|
+
"pcb_error_text_overlay"
|
|
5509
|
+
);
|
|
5510
|
+
if (errorOverlay) {
|
|
5511
|
+
children.push(errorOverlay);
|
|
5512
|
+
}
|
|
5513
|
+
}
|
|
3733
5514
|
const softwareUsedString = getSoftwareUsedString(circuitJson);
|
|
3734
5515
|
const version = CIRCUIT_TO_SVG_VERSION;
|
|
3735
5516
|
const svgObject = {
|
|
@@ -4704,29 +6485,14 @@ function convertCircuitJsonToAssemblySvg(soup, options) {
|
|
|
4704
6485
|
).flatMap((item) => createSvgObjects2(item, ctx, soup));
|
|
4705
6486
|
const softwareUsedString = getSoftwareUsedString(soup);
|
|
4706
6487
|
const version = CIRCUIT_TO_SVG_VERSION;
|
|
4707
|
-
const
|
|
4708
|
-
|
|
4709
|
-
|
|
4710
|
-
|
|
4711
|
-
|
|
4712
|
-
|
|
4713
|
-
|
|
4714
|
-
|
|
4715
|
-
"data-software-used-string": softwareUsedString
|
|
4716
|
-
},
|
|
4717
|
-
...options?.includeVersion && {
|
|
4718
|
-
"data-circuit-to-svg-version": version
|
|
4719
|
-
}
|
|
4720
|
-
},
|
|
4721
|
-
value: "",
|
|
4722
|
-
children: [
|
|
4723
|
-
{
|
|
4724
|
-
name: "style",
|
|
4725
|
-
type: "element",
|
|
4726
|
-
children: [
|
|
4727
|
-
{
|
|
4728
|
-
type: "text",
|
|
4729
|
-
value: `
|
|
6488
|
+
const children = [
|
|
6489
|
+
{
|
|
6490
|
+
name: "style",
|
|
6491
|
+
type: "element",
|
|
6492
|
+
children: [
|
|
6493
|
+
{
|
|
6494
|
+
type: "text",
|
|
6495
|
+
value: `
|
|
4730
6496
|
.assembly-component {
|
|
4731
6497
|
fill: none;
|
|
4732
6498
|
stroke: #000;
|
|
@@ -4749,30 +6515,52 @@ function convertCircuitJsonToAssemblySvg(soup, options) {
|
|
|
4749
6515
|
stroke-width: 0.2;
|
|
4750
6516
|
}
|
|
4751
6517
|
`,
|
|
4752
|
-
|
|
4753
|
-
|
|
4754
|
-
|
|
4755
|
-
|
|
4756
|
-
|
|
4757
|
-
|
|
4758
|
-
|
|
6518
|
+
name: "",
|
|
6519
|
+
attributes: {},
|
|
6520
|
+
children: []
|
|
6521
|
+
}
|
|
6522
|
+
],
|
|
6523
|
+
value: "",
|
|
6524
|
+
attributes: {}
|
|
6525
|
+
},
|
|
6526
|
+
{
|
|
6527
|
+
name: "rect",
|
|
6528
|
+
type: "element",
|
|
6529
|
+
attributes: {
|
|
6530
|
+
fill: "#fff",
|
|
6531
|
+
x: "0",
|
|
6532
|
+
y: "0",
|
|
6533
|
+
width: svgWidth.toString(),
|
|
6534
|
+
height: svgHeight.toString()
|
|
4759
6535
|
},
|
|
4760
|
-
|
|
4761
|
-
|
|
4762
|
-
|
|
4763
|
-
|
|
4764
|
-
|
|
4765
|
-
|
|
4766
|
-
|
|
4767
|
-
|
|
4768
|
-
|
|
4769
|
-
|
|
4770
|
-
|
|
4771
|
-
|
|
6536
|
+
value: "",
|
|
6537
|
+
children: []
|
|
6538
|
+
},
|
|
6539
|
+
createSvgObjectFromAssemblyBoundary(transform, minX, minY, maxX, maxY),
|
|
6540
|
+
...svgObjects
|
|
6541
|
+
].filter((child) => child !== null);
|
|
6542
|
+
if (options?.showErrorsInTextOverlay) {
|
|
6543
|
+
const errorOverlay = createErrorTextOverlay(soup);
|
|
6544
|
+
if (errorOverlay) {
|
|
6545
|
+
children.push(errorOverlay);
|
|
6546
|
+
}
|
|
6547
|
+
}
|
|
6548
|
+
const svgObject = {
|
|
6549
|
+
name: "svg",
|
|
6550
|
+
type: "element",
|
|
6551
|
+
attributes: {
|
|
6552
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
6553
|
+
width: svgWidth.toString(),
|
|
6554
|
+
height: svgHeight.toString(),
|
|
6555
|
+
...softwareUsedString && {
|
|
6556
|
+
"data-software-used-string": softwareUsedString
|
|
4772
6557
|
},
|
|
4773
|
-
|
|
4774
|
-
|
|
4775
|
-
|
|
6558
|
+
...options?.includeVersion && {
|
|
6559
|
+
"data-circuit-to-svg-version": version
|
|
6560
|
+
}
|
|
6561
|
+
},
|
|
6562
|
+
value: "",
|
|
6563
|
+
children
|
|
4776
6564
|
};
|
|
4777
6565
|
return (0, import_svgson4.stringify)(svgObject);
|
|
4778
6566
|
}
|
|
@@ -6024,6 +7812,28 @@ function convertCircuitJsonToPinoutSvg(soup, options) {
|
|
|
6024
7812
|
).flatMap((item) => createSvgObjects3(item, ctx, soup));
|
|
6025
7813
|
const softwareUsedString = getSoftwareUsedString(soup);
|
|
6026
7814
|
const version = CIRCUIT_TO_SVG_VERSION;
|
|
7815
|
+
const children = [
|
|
7816
|
+
{
|
|
7817
|
+
name: "rect",
|
|
7818
|
+
type: "element",
|
|
7819
|
+
attributes: {
|
|
7820
|
+
fill: "rgb(255, 255, 255)",
|
|
7821
|
+
x: "0",
|
|
7822
|
+
y: "0",
|
|
7823
|
+
width: svgWidth.toString(),
|
|
7824
|
+
height: svgHeight.toString()
|
|
7825
|
+
},
|
|
7826
|
+
value: "",
|
|
7827
|
+
children: []
|
|
7828
|
+
},
|
|
7829
|
+
...svgObjects
|
|
7830
|
+
].filter((child) => child !== null);
|
|
7831
|
+
if (options?.showErrorsInTextOverlay) {
|
|
7832
|
+
const errorOverlay = createErrorTextOverlay(soup);
|
|
7833
|
+
if (errorOverlay) {
|
|
7834
|
+
children.push(errorOverlay);
|
|
7835
|
+
}
|
|
7836
|
+
}
|
|
6027
7837
|
const svgObject = {
|
|
6028
7838
|
name: "svg",
|
|
6029
7839
|
type: "element",
|
|
@@ -6039,22 +7849,7 @@ function convertCircuitJsonToPinoutSvg(soup, options) {
|
|
|
6039
7849
|
}
|
|
6040
7850
|
},
|
|
6041
7851
|
value: "",
|
|
6042
|
-
children
|
|
6043
|
-
{
|
|
6044
|
-
name: "rect",
|
|
6045
|
-
type: "element",
|
|
6046
|
-
attributes: {
|
|
6047
|
-
fill: "rgb(255, 255, 255)",
|
|
6048
|
-
x: "0",
|
|
6049
|
-
y: "0",
|
|
6050
|
-
width: svgWidth.toString(),
|
|
6051
|
-
height: svgHeight.toString()
|
|
6052
|
-
},
|
|
6053
|
-
value: "",
|
|
6054
|
-
children: []
|
|
6055
|
-
},
|
|
6056
|
-
...svgObjects
|
|
6057
|
-
].filter((child) => child !== null)
|
|
7852
|
+
children
|
|
6058
7853
|
};
|
|
6059
7854
|
return (0, import_svgson5.stringify)(svgObject);
|
|
6060
7855
|
}
|
|
@@ -8079,6 +9874,7 @@ function createSvgObjectsFromSchVoltageProbe({
|
|
|
8079
9874
|
probe.position.x,
|
|
8080
9875
|
probe.position.y
|
|
8081
9876
|
]);
|
|
9877
|
+
const probeColor = probe.color ?? colorMap2.schematic.reference;
|
|
8082
9878
|
const arrowLength = Math.abs(transform.a) * 0.6;
|
|
8083
9879
|
const arrowWidth = Math.abs(transform.a) * 0.28;
|
|
8084
9880
|
const baseX = screenX + arrowLength * Math.cos(-50 * Math.PI / 180);
|
|
@@ -8093,14 +9889,68 @@ function createSvgObjectsFromSchVoltageProbe({
|
|
|
8093
9889
|
`L ${tipX - arrowWidth * Math.cos((-50 + 210) * Math.PI / 180)},${tipY - arrowWidth * Math.sin((-50 + 210) * Math.PI / 180)}`,
|
|
8094
9890
|
"Z"
|
|
8095
9891
|
].join(" ");
|
|
9892
|
+
const x = (baseX + 8 - (baseX - baseX)).toString();
|
|
9893
|
+
const textChildren = [];
|
|
9894
|
+
if (probe.name && probe.voltage !== void 0) {
|
|
9895
|
+
textChildren.push({
|
|
9896
|
+
type: "element",
|
|
9897
|
+
name: "tspan",
|
|
9898
|
+
value: "",
|
|
9899
|
+
attributes: {
|
|
9900
|
+
x
|
|
9901
|
+
},
|
|
9902
|
+
children: [
|
|
9903
|
+
{
|
|
9904
|
+
type: "text",
|
|
9905
|
+
value: probe.name,
|
|
9906
|
+
name: "",
|
|
9907
|
+
attributes: {},
|
|
9908
|
+
children: []
|
|
9909
|
+
}
|
|
9910
|
+
]
|
|
9911
|
+
});
|
|
9912
|
+
textChildren.push({
|
|
9913
|
+
type: "element",
|
|
9914
|
+
name: "tspan",
|
|
9915
|
+
value: "",
|
|
9916
|
+
attributes: {
|
|
9917
|
+
x,
|
|
9918
|
+
dy: "1.2em"
|
|
9919
|
+
},
|
|
9920
|
+
children: [
|
|
9921
|
+
{
|
|
9922
|
+
type: "text",
|
|
9923
|
+
value: `${probe.voltage}V`,
|
|
9924
|
+
name: "",
|
|
9925
|
+
attributes: {},
|
|
9926
|
+
children: []
|
|
9927
|
+
}
|
|
9928
|
+
]
|
|
9929
|
+
});
|
|
9930
|
+
} else {
|
|
9931
|
+
const textParts = [];
|
|
9932
|
+
if (probe.name) {
|
|
9933
|
+
textParts.push(probe.name);
|
|
9934
|
+
}
|
|
9935
|
+
if (probe.voltage !== void 0) {
|
|
9936
|
+
textParts.push(`${probe.voltage}V`);
|
|
9937
|
+
}
|
|
9938
|
+
textChildren.push({
|
|
9939
|
+
type: "text",
|
|
9940
|
+
value: textParts.join(" "),
|
|
9941
|
+
name: "",
|
|
9942
|
+
attributes: {},
|
|
9943
|
+
children: []
|
|
9944
|
+
});
|
|
9945
|
+
}
|
|
8096
9946
|
return [
|
|
8097
9947
|
{
|
|
8098
9948
|
name: "path",
|
|
8099
9949
|
type: "element",
|
|
8100
9950
|
attributes: {
|
|
8101
9951
|
d: arrowPath,
|
|
8102
|
-
stroke:
|
|
8103
|
-
fill:
|
|
9952
|
+
stroke: probeColor,
|
|
9953
|
+
fill: probeColor,
|
|
8104
9954
|
"stroke-width": `${getSchStrokeSize(transform)}px`
|
|
8105
9955
|
},
|
|
8106
9956
|
value: "",
|
|
@@ -8111,25 +9961,17 @@ function createSvgObjectsFromSchVoltageProbe({
|
|
|
8111
9961
|
name: "text",
|
|
8112
9962
|
value: "",
|
|
8113
9963
|
attributes: {
|
|
8114
|
-
x
|
|
8115
|
-
y:
|
|
8116
|
-
fill:
|
|
8117
|
-
"text-anchor": "
|
|
9964
|
+
x,
|
|
9965
|
+
y: baseY.toString(),
|
|
9966
|
+
fill: probeColor,
|
|
9967
|
+
"text-anchor": "start",
|
|
8118
9968
|
"dominant-baseline": "middle",
|
|
8119
9969
|
"font-family": "sans-serif",
|
|
8120
9970
|
"font-size": `${getSchScreenFontSize(transform, "reference_designator")}px`,
|
|
8121
9971
|
"font-weight": "bold",
|
|
8122
9972
|
"data-schematic-voltage-probe-id": probe.schematic_voltage_probe_id
|
|
8123
9973
|
},
|
|
8124
|
-
children:
|
|
8125
|
-
{
|
|
8126
|
-
type: "text",
|
|
8127
|
-
value: probe.voltage ? `${probe.voltage}V` : "",
|
|
8128
|
-
name: "",
|
|
8129
|
-
attributes: {},
|
|
8130
|
-
children: []
|
|
8131
|
-
}
|
|
8132
|
-
]
|
|
9974
|
+
children: textChildren
|
|
8133
9975
|
}
|
|
8134
9976
|
];
|
|
8135
9977
|
}
|
|
@@ -9442,6 +11284,12 @@ function convertCircuitJsonToSchematicSvg(circuitJson, options) {
|
|
|
9442
11284
|
}
|
|
9443
11285
|
const softwareUsedString = getSoftwareUsedString(circuitJson);
|
|
9444
11286
|
const version = CIRCUIT_TO_SVG_VERSION;
|
|
11287
|
+
if (options?.showErrorsInTextOverlay) {
|
|
11288
|
+
const errorOverlay = createErrorTextOverlay(circuitJson);
|
|
11289
|
+
if (errorOverlay) {
|
|
11290
|
+
svgChildren.push(errorOverlay);
|
|
11291
|
+
}
|
|
11292
|
+
}
|
|
9445
11293
|
const svgObject = {
|
|
9446
11294
|
name: "svg",
|
|
9447
11295
|
type: "element",
|
|
@@ -9549,7 +11397,10 @@ function convertCircuitJsonToSimulationGraphSvg({
|
|
|
9549
11397
|
);
|
|
9550
11398
|
}
|
|
9551
11399
|
const timeAxis = buildAxisInfo(allPoints.map((point) => point.timeMs));
|
|
9552
|
-
const voltageAxis = buildAxisInfo(
|
|
11400
|
+
const voltageAxis = buildAxisInfo(
|
|
11401
|
+
allPoints.map((point) => point.voltage),
|
|
11402
|
+
true
|
|
11403
|
+
);
|
|
9553
11404
|
const plotWidth = Math.max(1, width - MARGIN.left - MARGIN.right);
|
|
9554
11405
|
const plotHeight = Math.max(1, height - MARGIN.top - MARGIN.bottom);
|
|
9555
11406
|
const scaleX = createLinearScale(
|
|
@@ -9620,19 +11471,23 @@ function convertCircuitJsonToSimulationGraphSvg({
|
|
|
9620
11471
|
function prepareSimulationGraphs(graphs, circuitJson) {
|
|
9621
11472
|
const palette = Array.isArray(colorMap.palette) ? colorMap.palette : [];
|
|
9622
11473
|
const voltageProbes = circuitJson.filter(isSimulationVoltageProbe);
|
|
9623
|
-
const
|
|
11474
|
+
const sourceComponentIdToProbeName = /* @__PURE__ */ new Map();
|
|
11475
|
+
const sourceComponentIdToProbeColor = /* @__PURE__ */ new Map();
|
|
9624
11476
|
for (const probe of voltageProbes) {
|
|
9625
|
-
if (probe.name && probe.
|
|
9626
|
-
|
|
11477
|
+
if (probe.name && probe.source_component_id) {
|
|
11478
|
+
sourceComponentIdToProbeName.set(probe.source_component_id, probe.name);
|
|
11479
|
+
}
|
|
11480
|
+
if (probe.color && probe.source_component_id) {
|
|
11481
|
+
sourceComponentIdToProbeColor.set(probe.source_component_id, probe.color);
|
|
9627
11482
|
}
|
|
9628
11483
|
}
|
|
9629
11484
|
return graphs.map((graph, index) => {
|
|
9630
11485
|
const points = createGraphPoints(graph);
|
|
9631
11486
|
const paletteColor = palette.length > 0 ? palette[index % palette.length] : FALLBACK_LINE_COLOR;
|
|
9632
|
-
const
|
|
9633
|
-
const
|
|
9634
|
-
const probeName =
|
|
9635
|
-
const label = probeName ? `V(${probeName})` : graph.name || (
|
|
11487
|
+
const probeColor = graph.source_component_id ? sourceComponentIdToProbeColor.get(graph.source_component_id) : void 0;
|
|
11488
|
+
const color = graph.color ?? probeColor ?? paletteColor ?? FALLBACK_LINE_COLOR;
|
|
11489
|
+
const probeName = graph.source_component_id ? sourceComponentIdToProbeName.get(graph.source_component_id) : void 0;
|
|
11490
|
+
const label = probeName ? `V(${probeName})` : graph.name || (graph.source_component_id ? `Probe ${graph.source_component_id}` : graph.simulation_transient_voltage_graph_id);
|
|
9636
11491
|
return { graph, points, color, label };
|
|
9637
11492
|
}).filter((entry) => entry.points.length > 0);
|
|
9638
11493
|
}
|
|
@@ -9664,7 +11519,7 @@ function getTimestamps(graph) {
|
|
|
9664
11519
|
}
|
|
9665
11520
|
return timestamps;
|
|
9666
11521
|
}
|
|
9667
|
-
function buildAxisInfo(values) {
|
|
11522
|
+
function buildAxisInfo(values, applyPadding = false) {
|
|
9668
11523
|
if (values.length === 0) {
|
|
9669
11524
|
return {
|
|
9670
11525
|
domainMin: 0,
|
|
@@ -9683,9 +11538,21 @@ function buildAxisInfo(values) {
|
|
|
9683
11538
|
};
|
|
9684
11539
|
}
|
|
9685
11540
|
const ticks = generateTickValues(min, max);
|
|
9686
|
-
const safeTicks = ticks.length > 0 ? ticks : [min, max];
|
|
9687
|
-
|
|
9688
|
-
|
|
11541
|
+
const safeTicks = ticks.length > 0 ? [...ticks] : [min, max];
|
|
11542
|
+
let domainMin = safeTicks[0];
|
|
11543
|
+
let domainMax = safeTicks[safeTicks.length - 1];
|
|
11544
|
+
if (applyPadding && safeTicks.length > 1) {
|
|
11545
|
+
const tickStep = Math.abs(safeTicks[1] - safeTicks[0]);
|
|
11546
|
+
const PADDING_TOLERANCE_RATIO = 0.1;
|
|
11547
|
+
if (min < domainMin + tickStep * PADDING_TOLERANCE_RATIO) {
|
|
11548
|
+
domainMin -= tickStep;
|
|
11549
|
+
safeTicks.unshift(domainMin);
|
|
11550
|
+
}
|
|
11551
|
+
if (max > domainMax - tickStep * PADDING_TOLERANCE_RATIO) {
|
|
11552
|
+
domainMax += tickStep;
|
|
11553
|
+
safeTicks.push(domainMax);
|
|
11554
|
+
}
|
|
11555
|
+
}
|
|
9689
11556
|
return { domainMin, domainMax, ticks: safeTicks };
|
|
9690
11557
|
}
|
|
9691
11558
|
function generateTickValues(min, max, desired = 6) {
|
|
@@ -10016,8 +11883,8 @@ function createDataGroup(graphs, clipPathId, scaleX, scaleY) {
|
|
|
10016
11883
|
"clip-path": `url(#${clipPathId})`,
|
|
10017
11884
|
"data-simulation-transient-voltage-graph-id": entry.graph.simulation_transient_voltage_graph_id
|
|
10018
11885
|
};
|
|
10019
|
-
if (entry.graph.
|
|
10020
|
-
baseAttributes["data-
|
|
11886
|
+
if (entry.graph.source_component_id) {
|
|
11887
|
+
baseAttributes["data-source-component-id"] = entry.graph.source_component_id;
|
|
10021
11888
|
}
|
|
10022
11889
|
if (entry.graph.subcircuit_connectivity_map_key) {
|
|
10023
11890
|
baseAttributes["data-subcircuit-connectivity-map-key"] = entry.graph.subcircuit_connectivity_map_key;
|
|
@@ -10125,7 +11992,8 @@ function convertCircuitJsonToSchematicSimulationSvg({
|
|
|
10125
11992
|
height = DEFAULT_HEIGHT2,
|
|
10126
11993
|
schematicHeightRatio = DEFAULT_SCHEMATIC_RATIO,
|
|
10127
11994
|
schematicOptions,
|
|
10128
|
-
includeVersion
|
|
11995
|
+
includeVersion,
|
|
11996
|
+
showErrorsInTextOverlay
|
|
10129
11997
|
}) {
|
|
10130
11998
|
const schematicElements = circuitJson.filter(
|
|
10131
11999
|
(element) => !isSimulationExperiment(element) && !isSimulationTransientVoltageGraph(element)
|
|
@@ -10141,7 +12009,8 @@ function convertCircuitJsonToSchematicSimulationSvg({
|
|
|
10141
12009
|
...schematicOptions,
|
|
10142
12010
|
width,
|
|
10143
12011
|
height: schematicHeight,
|
|
10144
|
-
includeVersion: false
|
|
12012
|
+
includeVersion: false,
|
|
12013
|
+
showErrorsInTextOverlay
|
|
10145
12014
|
});
|
|
10146
12015
|
const simulationSvg = convertCircuitJsonToSimulationGraphSvg({
|
|
10147
12016
|
circuitJson,
|
|
@@ -10333,7 +12202,8 @@ function convertCircuitJsonToSolderPasteMask(circuitJson, options) {
|
|
|
10333
12202
|
const width = distance.parse(panel.width);
|
|
10334
12203
|
const height = distance.parse(panel.height);
|
|
10335
12204
|
if (width !== void 0 && height !== void 0) {
|
|
10336
|
-
|
|
12205
|
+
const center = panel.center ?? { x: width / 2, y: height / 2 };
|
|
12206
|
+
updateBounds(center, width, height);
|
|
10337
12207
|
}
|
|
10338
12208
|
} else if (item.type === "pcb_solder_paste" && "x" in item && "y" in item) {
|
|
10339
12209
|
updateBounds({ x: item.x, y: item.y }, 0, 0);
|
|
@@ -10367,6 +12237,38 @@ function convertCircuitJsonToSolderPasteMask(circuitJson, options) {
|
|
|
10367
12237
|
).flatMap((item) => createSvgObjects4({ elm: item, ctx }));
|
|
10368
12238
|
const softwareUsedString = getSoftwareUsedString(circuitJson);
|
|
10369
12239
|
const version = CIRCUIT_TO_SVG_VERSION;
|
|
12240
|
+
const children = [
|
|
12241
|
+
{
|
|
12242
|
+
name: "style",
|
|
12243
|
+
type: "element",
|
|
12244
|
+
children: [
|
|
12245
|
+
{
|
|
12246
|
+
type: "text",
|
|
12247
|
+
value: ""
|
|
12248
|
+
}
|
|
12249
|
+
]
|
|
12250
|
+
},
|
|
12251
|
+
{
|
|
12252
|
+
name: "rect",
|
|
12253
|
+
type: "element",
|
|
12254
|
+
attributes: {
|
|
12255
|
+
class: "boundary",
|
|
12256
|
+
x: "0",
|
|
12257
|
+
y: "0",
|
|
12258
|
+
fill: "#000",
|
|
12259
|
+
width: svgWidth.toString(),
|
|
12260
|
+
height: svgHeight.toString()
|
|
12261
|
+
}
|
|
12262
|
+
},
|
|
12263
|
+
createSvgObjectFromPcbBoundary2(transform, minX, minY, maxX, maxY),
|
|
12264
|
+
...svgObjects
|
|
12265
|
+
].filter((child) => child !== null);
|
|
12266
|
+
if (options?.showErrorsInTextOverlay) {
|
|
12267
|
+
const errorOverlay = createErrorTextOverlay(circuitJson);
|
|
12268
|
+
if (errorOverlay) {
|
|
12269
|
+
children.push(errorOverlay);
|
|
12270
|
+
}
|
|
12271
|
+
}
|
|
10370
12272
|
const svgObject = {
|
|
10371
12273
|
name: "svg",
|
|
10372
12274
|
type: "element",
|
|
@@ -10382,32 +12284,7 @@ function convertCircuitJsonToSolderPasteMask(circuitJson, options) {
|
|
|
10382
12284
|
}
|
|
10383
12285
|
},
|
|
10384
12286
|
value: "",
|
|
10385
|
-
children
|
|
10386
|
-
{
|
|
10387
|
-
name: "style",
|
|
10388
|
-
type: "element",
|
|
10389
|
-
children: [
|
|
10390
|
-
{
|
|
10391
|
-
type: "text",
|
|
10392
|
-
value: ""
|
|
10393
|
-
}
|
|
10394
|
-
]
|
|
10395
|
-
},
|
|
10396
|
-
{
|
|
10397
|
-
name: "rect",
|
|
10398
|
-
type: "element",
|
|
10399
|
-
attributes: {
|
|
10400
|
-
class: "boundary",
|
|
10401
|
-
x: "0",
|
|
10402
|
-
y: "0",
|
|
10403
|
-
fill: "#000",
|
|
10404
|
-
width: svgWidth.toString(),
|
|
10405
|
-
height: svgHeight.toString()
|
|
10406
|
-
}
|
|
10407
|
-
},
|
|
10408
|
-
createSvgObjectFromPcbBoundary2(transform, minX, minY, maxX, maxY),
|
|
10409
|
-
...svgObjects
|
|
10410
|
-
].filter((child) => child !== null)
|
|
12287
|
+
children
|
|
10411
12288
|
};
|
|
10412
12289
|
try {
|
|
10413
12290
|
return (0, import_svgson11.stringify)(svgObject);
|
|
@@ -10478,10 +12355,11 @@ export {
|
|
|
10478
12355
|
convertCircuitJsonToSchematicSvg,
|
|
10479
12356
|
convertCircuitJsonToSimulationGraphSvg,
|
|
10480
12357
|
convertCircuitJsonToSolderPasteMask,
|
|
12358
|
+
createErrorTextOverlay,
|
|
10481
12359
|
createSvgObjectsForSchComponentPortHovers,
|
|
10482
12360
|
getSoftwareUsedString,
|
|
10483
12361
|
isSimulationExperiment,
|
|
10484
12362
|
isSimulationTransientVoltageGraph,
|
|
10485
12363
|
isSimulationVoltageProbe
|
|
10486
12364
|
};
|
|
10487
|
-
//# sourceMappingURL=dist-
|
|
12365
|
+
//# sourceMappingURL=dist-7RT7ZSAD.js.map
|