circuit-json-to-lbrn 0.0.17 → 0.0.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +221 -2
- package/lib/element-handlers/addPcbCutout/addCirclePcbCutout.ts +52 -0
- package/lib/element-handlers/addPcbCutout/addPathPcbCutout.ts +73 -0
- package/lib/element-handlers/addPcbCutout/addPolygonPcbCutout.ts +68 -0
- package/lib/element-handlers/addPcbCutout/addRectPcbCutout.ts +71 -0
- package/lib/element-handlers/addPcbCutout/index.ts +31 -0
- package/lib/index.ts +5 -0
- package/package.json +1 -1
- package/tests/examples/addPcbCutout/__snapshots__/pcb-cutout-circle.snap.svg +8 -0
- package/tests/examples/addPcbCutout/__snapshots__/pcb-cutout-path.snap.svg +8 -0
- package/tests/examples/addPcbCutout/__snapshots__/pcb-cutout-polygon.snap.svg +8 -0
- package/tests/examples/addPcbCutout/__snapshots__/pcb-cutout-rect.snap.svg +8 -0
- package/tests/examples/addPcbCutout/pcb-cutout-circle.test.ts +49 -0
- package/tests/examples/addPcbCutout/pcb-cutout-path.test.ts +44 -0
- package/tests/examples/addPcbCutout/pcb-cutout-polygon.test.ts +54 -0
- package/tests/examples/addPcbCutout/pcb-cutout-rect.test.ts +48 -0
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// lib/index.ts
|
|
2
|
-
import { LightBurnProject, CutSetting, ShapePath as
|
|
2
|
+
import { LightBurnProject, CutSetting, ShapePath as ShapePath25 } from "lbrnts";
|
|
3
3
|
import { cju as cju2 } from "@tscircuit/circuit-json-util";
|
|
4
4
|
|
|
5
5
|
// lib/element-handlers/addPlatedHole/addCirclePlatedHole.ts
|
|
@@ -1690,6 +1690,222 @@ var addPcbHole = (hole, ctx) => {
|
|
|
1690
1690
|
}
|
|
1691
1691
|
};
|
|
1692
1692
|
|
|
1693
|
+
// lib/element-handlers/addPcbCutout/addCirclePcbCutout.ts
|
|
1694
|
+
import { ShapePath as ShapePath21 } from "lbrnts";
|
|
1695
|
+
var addCirclePcbCutout = (cutout, ctx) => {
|
|
1696
|
+
const {
|
|
1697
|
+
project,
|
|
1698
|
+
throughBoardCutSetting,
|
|
1699
|
+
origin,
|
|
1700
|
+
includeCopper,
|
|
1701
|
+
includeSoldermask,
|
|
1702
|
+
soldermaskMargin,
|
|
1703
|
+
soldermaskCutSetting
|
|
1704
|
+
} = ctx;
|
|
1705
|
+
const centerX = cutout.center.x + origin.x;
|
|
1706
|
+
const centerY = cutout.center.y + origin.y;
|
|
1707
|
+
if (cutout.radius > 0 && includeCopper) {
|
|
1708
|
+
const circlePath = createCirclePath(centerX, centerY, cutout.radius);
|
|
1709
|
+
project.children.push(
|
|
1710
|
+
new ShapePath21({
|
|
1711
|
+
cutIndex: throughBoardCutSetting.index,
|
|
1712
|
+
verts: circlePath.verts,
|
|
1713
|
+
prims: circlePath.prims,
|
|
1714
|
+
isClosed: true
|
|
1715
|
+
})
|
|
1716
|
+
);
|
|
1717
|
+
}
|
|
1718
|
+
if (cutout.radius > 0 && includeSoldermask) {
|
|
1719
|
+
const smRadius = cutout.radius + soldermaskMargin;
|
|
1720
|
+
const outer = createCirclePath(centerX, centerY, smRadius);
|
|
1721
|
+
project.children.push(
|
|
1722
|
+
new ShapePath21({
|
|
1723
|
+
cutIndex: soldermaskCutSetting.index,
|
|
1724
|
+
verts: outer.verts,
|
|
1725
|
+
prims: outer.prims,
|
|
1726
|
+
isClosed: true
|
|
1727
|
+
})
|
|
1728
|
+
);
|
|
1729
|
+
}
|
|
1730
|
+
};
|
|
1731
|
+
|
|
1732
|
+
// lib/element-handlers/addPcbCutout/addRectPcbCutout.ts
|
|
1733
|
+
import { ShapePath as ShapePath22 } from "lbrnts";
|
|
1734
|
+
var addRectPcbCutout = (cutout, ctx) => {
|
|
1735
|
+
const {
|
|
1736
|
+
project,
|
|
1737
|
+
throughBoardCutSetting,
|
|
1738
|
+
origin,
|
|
1739
|
+
includeCopper,
|
|
1740
|
+
includeSoldermask,
|
|
1741
|
+
soldermaskMargin,
|
|
1742
|
+
soldermaskCutSetting
|
|
1743
|
+
} = ctx;
|
|
1744
|
+
const centerX = cutout.center.x + origin.x;
|
|
1745
|
+
const centerY = cutout.center.y + origin.y;
|
|
1746
|
+
if (cutout.width > 0 && cutout.height > 0 && includeCopper) {
|
|
1747
|
+
const rotation = (cutout.rotation ?? 0) * (Math.PI / 180);
|
|
1748
|
+
const rectPath = createRoundedRectPath(
|
|
1749
|
+
centerX,
|
|
1750
|
+
centerY,
|
|
1751
|
+
cutout.width,
|
|
1752
|
+
cutout.height,
|
|
1753
|
+
0,
|
|
1754
|
+
// no border radius for cutouts
|
|
1755
|
+
4,
|
|
1756
|
+
// segments
|
|
1757
|
+
rotation
|
|
1758
|
+
);
|
|
1759
|
+
project.children.push(
|
|
1760
|
+
new ShapePath22({
|
|
1761
|
+
cutIndex: throughBoardCutSetting.index,
|
|
1762
|
+
verts: rectPath.verts,
|
|
1763
|
+
prims: rectPath.prims,
|
|
1764
|
+
isClosed: true
|
|
1765
|
+
})
|
|
1766
|
+
);
|
|
1767
|
+
}
|
|
1768
|
+
if (cutout.width > 0 && cutout.height > 0 && includeSoldermask) {
|
|
1769
|
+
const rotation = (cutout.rotation ?? 0) * (Math.PI / 180);
|
|
1770
|
+
const smWidth = cutout.width + 2 * soldermaskMargin;
|
|
1771
|
+
const smHeight = cutout.height + 2 * soldermaskMargin;
|
|
1772
|
+
const rectPath = createRoundedRectPath(
|
|
1773
|
+
centerX,
|
|
1774
|
+
centerY,
|
|
1775
|
+
smWidth,
|
|
1776
|
+
smHeight,
|
|
1777
|
+
0,
|
|
1778
|
+
// no border radius for cutouts
|
|
1779
|
+
4,
|
|
1780
|
+
// segments
|
|
1781
|
+
rotation
|
|
1782
|
+
);
|
|
1783
|
+
project.children.push(
|
|
1784
|
+
new ShapePath22({
|
|
1785
|
+
cutIndex: soldermaskCutSetting.index,
|
|
1786
|
+
verts: rectPath.verts,
|
|
1787
|
+
prims: rectPath.prims,
|
|
1788
|
+
isClosed: true
|
|
1789
|
+
})
|
|
1790
|
+
);
|
|
1791
|
+
}
|
|
1792
|
+
};
|
|
1793
|
+
|
|
1794
|
+
// lib/element-handlers/addPcbCutout/addPolygonPcbCutout.ts
|
|
1795
|
+
import { ShapePath as ShapePath23 } from "lbrnts";
|
|
1796
|
+
var addPolygonPcbCutout = (cutout, ctx) => {
|
|
1797
|
+
const {
|
|
1798
|
+
project,
|
|
1799
|
+
throughBoardCutSetting,
|
|
1800
|
+
origin,
|
|
1801
|
+
includeCopper,
|
|
1802
|
+
includeSoldermask,
|
|
1803
|
+
soldermaskCutSetting,
|
|
1804
|
+
soldermaskMargin
|
|
1805
|
+
} = ctx;
|
|
1806
|
+
if (cutout.points.length >= 3 && includeCopper) {
|
|
1807
|
+
const polygonPath = createPolygonPathFromOutline(
|
|
1808
|
+
cutout.points,
|
|
1809
|
+
origin.x,
|
|
1810
|
+
origin.y
|
|
1811
|
+
);
|
|
1812
|
+
project.children.push(
|
|
1813
|
+
new ShapePath23({
|
|
1814
|
+
cutIndex: throughBoardCutSetting.index,
|
|
1815
|
+
verts: polygonPath.verts,
|
|
1816
|
+
prims: polygonPath.prims,
|
|
1817
|
+
isClosed: true
|
|
1818
|
+
})
|
|
1819
|
+
);
|
|
1820
|
+
}
|
|
1821
|
+
if (cutout.points.length >= 3 && includeSoldermask) {
|
|
1822
|
+
const points = soldermaskMargin && soldermaskMargin > 0 ? cutout.points.map((p) => ({
|
|
1823
|
+
x: (p.x ?? 0) + (p.x ?? 0) > 0 ? soldermaskMargin : -soldermaskMargin,
|
|
1824
|
+
y: (p.y ?? 0) + (p.y ?? 0) > 0 ? soldermaskMargin : -soldermaskMargin
|
|
1825
|
+
})) : cutout.points;
|
|
1826
|
+
const polygonPath = createPolygonPathFromOutline(points, origin.x, origin.y);
|
|
1827
|
+
project.children.push(
|
|
1828
|
+
new ShapePath23({
|
|
1829
|
+
cutIndex: soldermaskCutSetting.index,
|
|
1830
|
+
verts: polygonPath.verts,
|
|
1831
|
+
prims: polygonPath.prims,
|
|
1832
|
+
isClosed: true
|
|
1833
|
+
})
|
|
1834
|
+
);
|
|
1835
|
+
}
|
|
1836
|
+
};
|
|
1837
|
+
|
|
1838
|
+
// lib/element-handlers/addPcbCutout/addPathPcbCutout.ts
|
|
1839
|
+
import { ShapePath as ShapePath24 } from "lbrnts";
|
|
1840
|
+
var addPathPcbCutout = (cutout, ctx) => {
|
|
1841
|
+
const {
|
|
1842
|
+
project,
|
|
1843
|
+
throughBoardCutSetting,
|
|
1844
|
+
origin,
|
|
1845
|
+
includeCopper,
|
|
1846
|
+
includeSoldermask,
|
|
1847
|
+
soldermaskMargin,
|
|
1848
|
+
soldermaskCutSetting
|
|
1849
|
+
} = ctx;
|
|
1850
|
+
if (cutout.route.length >= 2 && includeCopper) {
|
|
1851
|
+
const verts = [];
|
|
1852
|
+
const prims = [];
|
|
1853
|
+
for (const point6 of cutout.route) {
|
|
1854
|
+
verts.push({
|
|
1855
|
+
x: point6.x + origin.x,
|
|
1856
|
+
y: point6.y + origin.y
|
|
1857
|
+
});
|
|
1858
|
+
prims.push({ type: 0 });
|
|
1859
|
+
}
|
|
1860
|
+
project.children.push(
|
|
1861
|
+
new ShapePath24({
|
|
1862
|
+
cutIndex: throughBoardCutSetting.index,
|
|
1863
|
+
verts,
|
|
1864
|
+
prims,
|
|
1865
|
+
isClosed: false
|
|
1866
|
+
// Paths are typically not closed
|
|
1867
|
+
})
|
|
1868
|
+
);
|
|
1869
|
+
}
|
|
1870
|
+
if (cutout.route.length >= 2 && includeSoldermask) {
|
|
1871
|
+
const verts = [];
|
|
1872
|
+
const prims = [];
|
|
1873
|
+
for (const point6 of cutout.route) {
|
|
1874
|
+
verts.push({
|
|
1875
|
+
x: point6.x + origin.x,
|
|
1876
|
+
y: point6.y + origin.y
|
|
1877
|
+
});
|
|
1878
|
+
prims.push({ type: 0 });
|
|
1879
|
+
}
|
|
1880
|
+
project.children.push(
|
|
1881
|
+
new ShapePath24({
|
|
1882
|
+
cutIndex: soldermaskCutSetting.index,
|
|
1883
|
+
verts,
|
|
1884
|
+
prims,
|
|
1885
|
+
isClosed: false
|
|
1886
|
+
// Match the same path as the copper cutout
|
|
1887
|
+
})
|
|
1888
|
+
);
|
|
1889
|
+
}
|
|
1890
|
+
};
|
|
1891
|
+
|
|
1892
|
+
// lib/element-handlers/addPcbCutout/index.ts
|
|
1893
|
+
var addPcbCutout = (cutout, ctx) => {
|
|
1894
|
+
switch (cutout.shape) {
|
|
1895
|
+
case "circle":
|
|
1896
|
+
return addCirclePcbCutout(cutout, ctx);
|
|
1897
|
+
case "rect":
|
|
1898
|
+
return addRectPcbCutout(cutout, ctx);
|
|
1899
|
+
case "polygon":
|
|
1900
|
+
return addPolygonPcbCutout(cutout, ctx);
|
|
1901
|
+
case "path":
|
|
1902
|
+
return addPathPcbCutout(cutout, ctx);
|
|
1903
|
+
default:
|
|
1904
|
+
const _exhaustive = cutout;
|
|
1905
|
+
console.warn(`Unknown cutout shape: ${cutout.shape}`);
|
|
1906
|
+
}
|
|
1907
|
+
};
|
|
1908
|
+
|
|
1693
1909
|
// lib/index.ts
|
|
1694
1910
|
var convertCircuitJsonToLbrn = (circuitJson, options = {}) => {
|
|
1695
1911
|
const db = cju2(circuitJson);
|
|
@@ -1767,6 +1983,9 @@ var convertCircuitJsonToLbrn = (circuitJson, options = {}) => {
|
|
|
1767
1983
|
for (const hole of db.pcb_hole.list()) {
|
|
1768
1984
|
addPcbHole(hole, ctx);
|
|
1769
1985
|
}
|
|
1986
|
+
for (const cutout of db.pcb_cutout.list()) {
|
|
1987
|
+
addPcbCutout(cutout, ctx);
|
|
1988
|
+
}
|
|
1770
1989
|
if (ctx.includeCopper) {
|
|
1771
1990
|
for (const net of Object.keys(connMap.netMap)) {
|
|
1772
1991
|
const netGeoms = ctx.netGeoms.get(net);
|
|
@@ -1787,7 +2006,7 @@ var convertCircuitJsonToLbrn = (circuitJson, options = {}) => {
|
|
|
1787
2006
|
for (const island of union.splitToIslands()) {
|
|
1788
2007
|
const { verts, prims } = polygonToShapePathData(island);
|
|
1789
2008
|
project.children.push(
|
|
1790
|
-
new
|
|
2009
|
+
new ShapePath25({
|
|
1791
2010
|
cutIndex: copperCutSetting.index,
|
|
1792
2011
|
verts,
|
|
1793
2012
|
prims,
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { PcbCutoutCircle } from "circuit-json"
|
|
2
|
+
import type { ConvertContext } from "../../ConvertContext"
|
|
3
|
+
import { ShapePath } from "lbrnts"
|
|
4
|
+
import { createCirclePath } from "../../helpers/circleShape"
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Adds a circular PCB cutout to the project
|
|
8
|
+
* Cutouts are regions removed from the board entirely
|
|
9
|
+
*/
|
|
10
|
+
export const addCirclePcbCutout = (
|
|
11
|
+
cutout: PcbCutoutCircle,
|
|
12
|
+
ctx: ConvertContext,
|
|
13
|
+
): void => {
|
|
14
|
+
const {
|
|
15
|
+
project,
|
|
16
|
+
throughBoardCutSetting,
|
|
17
|
+
origin,
|
|
18
|
+
includeCopper,
|
|
19
|
+
includeSoldermask,
|
|
20
|
+
soldermaskMargin,
|
|
21
|
+
soldermaskCutSetting,
|
|
22
|
+
} = ctx
|
|
23
|
+
const centerX = cutout.center.x + origin.x
|
|
24
|
+
const centerY = cutout.center.y + origin.y
|
|
25
|
+
|
|
26
|
+
// Add the cutout - cut through the board
|
|
27
|
+
if (cutout.radius > 0 && includeCopper) {
|
|
28
|
+
const circlePath = createCirclePath(centerX, centerY, cutout.radius)
|
|
29
|
+
project.children.push(
|
|
30
|
+
new ShapePath({
|
|
31
|
+
cutIndex: throughBoardCutSetting.index,
|
|
32
|
+
verts: circlePath.verts,
|
|
33
|
+
prims: circlePath.prims,
|
|
34
|
+
isClosed: true,
|
|
35
|
+
}),
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Add soldermask opening if drawing soldermask
|
|
40
|
+
if (cutout.radius > 0 && includeSoldermask) {
|
|
41
|
+
const smRadius = cutout.radius + soldermaskMargin
|
|
42
|
+
const outer = createCirclePath(centerX, centerY, smRadius)
|
|
43
|
+
project.children.push(
|
|
44
|
+
new ShapePath({
|
|
45
|
+
cutIndex: soldermaskCutSetting.index,
|
|
46
|
+
verts: outer.verts,
|
|
47
|
+
prims: outer.prims,
|
|
48
|
+
isClosed: true,
|
|
49
|
+
}),
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { PcbCutoutPath } from "circuit-json"
|
|
2
|
+
import type { ConvertContext } from "../../ConvertContext"
|
|
3
|
+
import { ShapePath } from "lbrnts"
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Adds a path-based PCB cutout to the project
|
|
7
|
+
* Path cutouts create slots along a route (e.g., for routed slots or complex cutout paths)
|
|
8
|
+
*/
|
|
9
|
+
export const addPathPcbCutout = (
|
|
10
|
+
cutout: PcbCutoutPath,
|
|
11
|
+
ctx: ConvertContext,
|
|
12
|
+
): void => {
|
|
13
|
+
const {
|
|
14
|
+
project,
|
|
15
|
+
throughBoardCutSetting,
|
|
16
|
+
origin,
|
|
17
|
+
includeCopper,
|
|
18
|
+
includeSoldermask,
|
|
19
|
+
soldermaskMargin,
|
|
20
|
+
soldermaskCutSetting,
|
|
21
|
+
} = ctx
|
|
22
|
+
|
|
23
|
+
// For path cutouts, we need to create a stroke along the route with the given width
|
|
24
|
+
// For simplicity, we'll convert the path to a polyline
|
|
25
|
+
if (cutout.route.length >= 2 && includeCopper) {
|
|
26
|
+
const verts: { x: number; y: number }[] = []
|
|
27
|
+
const prims: { type: number }[] = []
|
|
28
|
+
|
|
29
|
+
for (const point of cutout.route) {
|
|
30
|
+
verts.push({
|
|
31
|
+
x: point.x + origin.x,
|
|
32
|
+
y: point.y + origin.y,
|
|
33
|
+
})
|
|
34
|
+
prims.push({ type: 0 })
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Note: slot_width, slot_length, space_between_slots, and slot_corner_radius
|
|
38
|
+
// would require more complex path offsetting and aren't currently implemented
|
|
39
|
+
// For now, we just create a simple path along the route
|
|
40
|
+
project.children.push(
|
|
41
|
+
new ShapePath({
|
|
42
|
+
cutIndex: throughBoardCutSetting.index,
|
|
43
|
+
verts,
|
|
44
|
+
prims,
|
|
45
|
+
isClosed: false, // Paths are typically not closed
|
|
46
|
+
}),
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Add soldermask opening if drawing soldermask
|
|
51
|
+
if (cutout.route.length >= 2 && includeSoldermask) {
|
|
52
|
+
const verts: { x: number; y: number }[] = []
|
|
53
|
+
const prims: { type: number }[] = []
|
|
54
|
+
|
|
55
|
+
// Create path with soldermask margin
|
|
56
|
+
for (const point of cutout.route) {
|
|
57
|
+
verts.push({
|
|
58
|
+
x: point.x + origin.x,
|
|
59
|
+
y: point.y + origin.y,
|
|
60
|
+
})
|
|
61
|
+
prims.push({ type: 0 })
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
project.children.push(
|
|
65
|
+
new ShapePath({
|
|
66
|
+
cutIndex: soldermaskCutSetting.index,
|
|
67
|
+
verts,
|
|
68
|
+
prims,
|
|
69
|
+
isClosed: false, // Match the same path as the copper cutout
|
|
70
|
+
}),
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { PcbCutoutPolygon } from "circuit-json"
|
|
2
|
+
import type { ConvertContext } from "../../ConvertContext"
|
|
3
|
+
import { ShapePath } from "lbrnts"
|
|
4
|
+
import { createPolygonPathFromOutline } from "../../helpers/polygonShape"
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Adds a polygon PCB cutout to the project
|
|
8
|
+
* Cutouts are regions removed from the board entirely
|
|
9
|
+
*/
|
|
10
|
+
export const addPolygonPcbCutout = (
|
|
11
|
+
cutout: PcbCutoutPolygon,
|
|
12
|
+
ctx: ConvertContext,
|
|
13
|
+
): void => {
|
|
14
|
+
const {
|
|
15
|
+
project,
|
|
16
|
+
throughBoardCutSetting,
|
|
17
|
+
origin,
|
|
18
|
+
includeCopper,
|
|
19
|
+
includeSoldermask,
|
|
20
|
+
soldermaskCutSetting,
|
|
21
|
+
soldermaskMargin,
|
|
22
|
+
} = ctx
|
|
23
|
+
|
|
24
|
+
// Add the cutout - cut through the board
|
|
25
|
+
if (cutout.points.length >= 3 && includeCopper) {
|
|
26
|
+
const polygonPath = createPolygonPathFromOutline(
|
|
27
|
+
cutout.points,
|
|
28
|
+
origin.x,
|
|
29
|
+
origin.y,
|
|
30
|
+
)
|
|
31
|
+
project.children.push(
|
|
32
|
+
new ShapePath({
|
|
33
|
+
cutIndex: throughBoardCutSetting.index,
|
|
34
|
+
verts: polygonPath.verts,
|
|
35
|
+
prims: polygonPath.prims,
|
|
36
|
+
isClosed: true,
|
|
37
|
+
}),
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Add soldermask opening if drawing soldermask
|
|
42
|
+
if (cutout.points.length >= 3 && includeSoldermask) {
|
|
43
|
+
// Apply soldermask margin to the points if margin is specified
|
|
44
|
+
const points =
|
|
45
|
+
soldermaskMargin && soldermaskMargin > 0
|
|
46
|
+
? cutout.points.map((p) => ({
|
|
47
|
+
x:
|
|
48
|
+
(p.x ?? 0) + (p.x ?? 0) > 0
|
|
49
|
+
? soldermaskMargin
|
|
50
|
+
: -soldermaskMargin,
|
|
51
|
+
y:
|
|
52
|
+
(p.y ?? 0) + (p.y ?? 0) > 0
|
|
53
|
+
? soldermaskMargin
|
|
54
|
+
: -soldermaskMargin,
|
|
55
|
+
}))
|
|
56
|
+
: cutout.points
|
|
57
|
+
|
|
58
|
+
const polygonPath = createPolygonPathFromOutline(points, origin.x, origin.y)
|
|
59
|
+
project.children.push(
|
|
60
|
+
new ShapePath({
|
|
61
|
+
cutIndex: soldermaskCutSetting.index,
|
|
62
|
+
verts: polygonPath.verts,
|
|
63
|
+
prims: polygonPath.prims,
|
|
64
|
+
isClosed: true,
|
|
65
|
+
}),
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { PcbCutoutRect } from "circuit-json"
|
|
2
|
+
import type { ConvertContext } from "../../ConvertContext"
|
|
3
|
+
import { ShapePath } from "lbrnts"
|
|
4
|
+
import { createRoundedRectPath } from "../../helpers/roundedRectShape"
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Adds a rectangular PCB cutout to the project
|
|
8
|
+
* Cutouts are regions removed from the board entirely
|
|
9
|
+
*/
|
|
10
|
+
export const addRectPcbCutout = (
|
|
11
|
+
cutout: PcbCutoutRect,
|
|
12
|
+
ctx: ConvertContext,
|
|
13
|
+
): void => {
|
|
14
|
+
const {
|
|
15
|
+
project,
|
|
16
|
+
throughBoardCutSetting,
|
|
17
|
+
origin,
|
|
18
|
+
includeCopper,
|
|
19
|
+
includeSoldermask,
|
|
20
|
+
soldermaskMargin,
|
|
21
|
+
soldermaskCutSetting,
|
|
22
|
+
} = ctx
|
|
23
|
+
const centerX = cutout.center.x + origin.x
|
|
24
|
+
const centerY = cutout.center.y + origin.y
|
|
25
|
+
|
|
26
|
+
// Add the cutout - cut through the board
|
|
27
|
+
if (cutout.width > 0 && cutout.height > 0 && includeCopper) {
|
|
28
|
+
const rotation = (cutout.rotation ?? 0) * (Math.PI / 180) // Convert degrees to radians
|
|
29
|
+
const rectPath = createRoundedRectPath(
|
|
30
|
+
centerX,
|
|
31
|
+
centerY,
|
|
32
|
+
cutout.width,
|
|
33
|
+
cutout.height,
|
|
34
|
+
0, // no border radius for cutouts
|
|
35
|
+
4, // segments
|
|
36
|
+
rotation,
|
|
37
|
+
)
|
|
38
|
+
project.children.push(
|
|
39
|
+
new ShapePath({
|
|
40
|
+
cutIndex: throughBoardCutSetting.index,
|
|
41
|
+
verts: rectPath.verts,
|
|
42
|
+
prims: rectPath.prims,
|
|
43
|
+
isClosed: true,
|
|
44
|
+
}),
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Add soldermask opening if drawing soldermask
|
|
49
|
+
if (cutout.width > 0 && cutout.height > 0 && includeSoldermask) {
|
|
50
|
+
const rotation = (cutout.rotation ?? 0) * (Math.PI / 180) // Convert degrees to radians
|
|
51
|
+
const smWidth = cutout.width + 2 * soldermaskMargin
|
|
52
|
+
const smHeight = cutout.height + 2 * soldermaskMargin
|
|
53
|
+
const rectPath = createRoundedRectPath(
|
|
54
|
+
centerX,
|
|
55
|
+
centerY,
|
|
56
|
+
smWidth,
|
|
57
|
+
smHeight,
|
|
58
|
+
0, // no border radius for cutouts
|
|
59
|
+
4, // segments
|
|
60
|
+
rotation,
|
|
61
|
+
)
|
|
62
|
+
project.children.push(
|
|
63
|
+
new ShapePath({
|
|
64
|
+
cutIndex: soldermaskCutSetting.index,
|
|
65
|
+
verts: rectPath.verts,
|
|
66
|
+
prims: rectPath.prims,
|
|
67
|
+
isClosed: true,
|
|
68
|
+
}),
|
|
69
|
+
)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { PcbCutout } from "circuit-json"
|
|
2
|
+
import type { ConvertContext } from "../../ConvertContext"
|
|
3
|
+
import { addCirclePcbCutout } from "./addCirclePcbCutout"
|
|
4
|
+
import { addRectPcbCutout } from "./addRectPcbCutout"
|
|
5
|
+
import { addPolygonPcbCutout } from "./addPolygonPcbCutout"
|
|
6
|
+
import { addPathPcbCutout } from "./addPathPcbCutout"
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Main dispatcher function that routes PCB cutouts to the appropriate handler
|
|
10
|
+
* based on their shape property
|
|
11
|
+
*/
|
|
12
|
+
export const addPcbCutout = (cutout: PcbCutout, ctx: ConvertContext): void => {
|
|
13
|
+
switch (cutout.shape) {
|
|
14
|
+
case "circle":
|
|
15
|
+
return addCirclePcbCutout(cutout, ctx)
|
|
16
|
+
|
|
17
|
+
case "rect":
|
|
18
|
+
return addRectPcbCutout(cutout, ctx)
|
|
19
|
+
|
|
20
|
+
case "polygon":
|
|
21
|
+
return addPolygonPcbCutout(cutout, ctx)
|
|
22
|
+
|
|
23
|
+
case "path":
|
|
24
|
+
return addPathPcbCutout(cutout, ctx)
|
|
25
|
+
|
|
26
|
+
default:
|
|
27
|
+
// Type guard to ensure we handle all cases
|
|
28
|
+
const _exhaustive: never = cutout
|
|
29
|
+
console.warn(`Unknown cutout shape: ${(cutout as any).shape}`)
|
|
30
|
+
}
|
|
31
|
+
}
|
package/lib/index.ts
CHANGED
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
} from "./calculateBounds"
|
|
16
16
|
import { addPcbVia } from "./element-handlers/addPcbVia"
|
|
17
17
|
import { addPcbHole } from "./element-handlers/addPcbHole"
|
|
18
|
+
import { addPcbCutout } from "./element-handlers/addPcbCutout"
|
|
18
19
|
// import { writeDebugSvg } from "./writeDebugSvg"
|
|
19
20
|
|
|
20
21
|
export const convertCircuitJsonToLbrn = (
|
|
@@ -114,6 +115,10 @@ export const convertCircuitJsonToLbrn = (
|
|
|
114
115
|
addPcbHole(hole, ctx)
|
|
115
116
|
}
|
|
116
117
|
|
|
118
|
+
for (const cutout of db.pcb_cutout.list()) {
|
|
119
|
+
addPcbCutout(cutout, ctx)
|
|
120
|
+
}
|
|
121
|
+
|
|
117
122
|
// Draw each individual shape geometry as a ShapePath
|
|
118
123
|
// FOR DEBUGGING!!!
|
|
119
124
|
// for (const net of Object.keys(connMap.netMap)) {
|
package/package.json
CHANGED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<svg width="800" height="1400" viewBox="0 0 800 1400" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<g transform="translate(0, 0)">
|
|
3
|
+
<style></style><rect class="boundary" x="0" y="0" fill="#000" width="800" height="600" data-type="pcb_background" data-pcb-layer="global"/><rect class="pcb-boundary" fill="none" stroke="#fff" stroke-width="0.3" x="127.27272727272725" y="27.272727272727252" width="545.4545454545455" height="545.4545454545455" data-type="pcb_boundary" data-pcb-layer="global"/><path class="pcb-board" d="M 127.27272727272725 572.7272727272727 L 672.7272727272727 572.7272727272727 L 672.7272727272727 27.272727272727252 L 127.27272727272725 27.272727272727252 Z" fill="none" stroke="rgba(255, 255, 255, 0.5)" stroke-width="2.7272727272727275" data-type="pcb_board" data-pcb-layer="board"/><circle class="pcb-cutout pcb-cutout-circle" cx="263.6363636363636" cy="163.63636363636363" r="54.54545454545455" fill="#FF26E2" data-type="pcb_cutout" data-pcb-layer="drill"/><circle class="pcb-cutout pcb-cutout-circle" cx="536.3636363636364" cy="436.3636363636364" r="81.81818181818181" fill="#FF26E2" data-type="pcb_cutout" data-pcb-layer="drill"/>
|
|
4
|
+
</g>
|
|
5
|
+
<g transform="translate(0, 600) scale(20, 20) translate(19.9, 19.9)">
|
|
6
|
+
<rect x="-19.9" y="-19.9" width="40" height="40" fill="white"/><g transform="matrix(1 0 0 -1 0 0.20000000000000284)"><g transform="matrix(1,0,0,1,0,0)"><path d="M -9.9 -9.9 L 10.1 -9.9 L 10.1 10.1 L -9.9 10.1 L -9.9 -9.9 L -9.9 -9.9 Z" fill="none" stroke="#0000FF" stroke-width="0.1"/></g><g transform="matrix(1,0,0,1,0,0)"><path d="M -9.9 -9.9 L 10.1 -9.9 L 10.1 10.1 L -9.9 10.1 L -9.9 -9.9 L -9.9 -9.9" fill="none" stroke="#FF0000" stroke-width="0.1"/></g><g transform="matrix(1,0,0,1,0,0)"><path d="M -2.9000000000000004 5.1 L -2.9096305466556065 5.2960342806591205 L -2.9384294391935395 5.4901806440322565 L -2.9861193285355827 5.680569354508925 L -3.052240934977427 5.865366864730179 L -3.1361574713032905 6.042793473651995 L -3.2370607753949097 6.211140466039204 L -3.3539790932745266 6.368786568327291 L -3.4857864376269054 6.514213562373095 L -3.6312134316727094 6.646020906725473 L -3.788859533960796 6.76293922460509 L -3.9572065263480045 6.8638425286967095 L -4.134633135269821 6.947759065022574 L -4.319430645491075 7.013880671464417 L -4.5098193559677435 7.0615705608064605 L -4.703965719340879 7.090369453344393 L -4.9 7.1 L -5.096034280659121 7.090369453344394 L -5.290180644032256 7.0615705608064605 L -5.4805693545089245 7.013880671464418 L -5.66536686473018 6.947759065022574 L -5.842793473651996 6.8638425286967095 L -6.011140466039205 6.76293922460509 L -6.168786568327291 6.646020906725473 L -6.314213562373095 6.514213562373095 L -6.446020906725474 6.368786568327291 L -6.562939224605091 6.211140466039204 L -6.66384252869671 6.0427934736519955 L -6.747759065022574 5.86536686473018 L -6.813880671464418 5.680569354508925 L -6.861570560806461 5.4901806440322565 L -6.890369453344394 5.296034280659121 L -6.9 5.1 L -6.890369453344395 4.903965719340879 L -6.861570560806461 4.709819355967743 L -6.8138806714644184 4.5194306454910755 L -6.747759065022574 4.33463313526982 L -6.66384252869671 4.157206526348005 L -6.562939224605091 3.9888595339607957 L -6.446020906725474 3.831213431672709 L -6.314213562373096 3.6857864376269047 L -6.168786568327292 3.5539790932745263 L -6.011140466039205 3.437060775394909 L -5.842793473651996 3.3361574713032898 L -5.665366864730181 3.2522409349774266 L -5.480569354508925 3.186119328535582 L -5.290180644032258 3.138429439193539 L -5.096034280659121 3.109630546655606 L -4.9 3.0999999999999996 L -4.70396571934088 3.109630546655606 L -4.5098193559677435 3.138429439193539 L -4.319430645491076 3.1861193285355816 L -4.13463313526982 3.2522409349774266 L -3.9572065263480054 3.3361574713032898 L -3.788859533960797 3.437060775394909 L -3.631213431672709 3.553979093274526 L -3.4857864376269054 3.6857864376269043 L -3.353979093274527 3.831213431672708 L -3.2370607753949097 3.9888595339607953 L -3.1361574713032905 4.157206526348004 L -3.0522409349774273 4.334633135269819 L -2.9861193285355827 4.519430645491075 L -2.9384294391935395 4.709819355967742 L -2.9096305466556065 4.903965719340879 L -2.9000000000000004 5.1 L -2.9000000000000004 5.1 Z" fill="none" stroke="#0000FF" stroke-width="0.1"/></g><g transform="matrix(1,0,0,1,0,0)"><clipPath id="clip-1764627670784-sxvpqol5n__stack1"><path d="M -2.8000000000000003 5.1 L -2.8101120739883867 5.305835994692077 L -2.8403509111532164 5.509689676233869 L -2.8904252949623617 5.709597822234371 L -2.959852981726298 5.903635207966688 L -3.0479653448684547 6.089933147334595 L -3.153913814164655 6.266697489341164 L -3.276678047938253 6.432225896743655 L -3.4150757595082504 6.584924240491749 L -3.567774103256345 6.723321952061747 L -3.7333025106588353 6.846086185835345 L -3.910066852665405 6.952034655131545 L -4.096364792033311 7.040147018273702 L -4.290402177765629 7.109574705037638 L -4.490310323766131 7.159649088846784 L -4.694164005307923 7.189887926011613 L -4.9 7.199999999999999 L -5.105835994692078 7.189887926011613 L -5.3096896762338694 7.159649088846784 L -5.509597822234371 7.109574705037638 L -5.7036352079666885 7.040147018273702 L -5.889933147334595 6.952034655131545 L -6.0666974893411645 6.846086185835345 L -6.232225896743656 6.723321952061747 L -6.38492424049175 6.58492424049175 L -6.523321952061748 6.432225896743655 L -6.646086185835346 6.266697489341164 L -6.7520346551315455 6.0899331473345955 L -6.8401470182737025 5.903635207966689 L -6.909574705037639 5.709597822234371 L -6.959649088846785 5.50968967623387 L -6.989887926011614 5.305835994692077 L -7 5.1 L -6.989887926011614 4.894164005307922 L -6.959649088846785 4.69031032376613 L -6.909574705037639 4.490402177765629 L -6.8401470182737025 4.2963647920333115 L -6.7520346551315455 4.110066852665405 L -6.646086185835346 3.9333025106588355 L -6.523321952061748 3.7677741032563445 L -6.384924240491751 3.61507575950825 L -6.232225896743657 3.4766780479382526 L -6.0666974893411645 3.3539138141646543 L -5.889933147334596 3.2479653448684545 L -5.70363520796669 3.159852981726298 L -5.5095978222343716 3.090425294962361 L -5.30968967623387 3.040350911153216 L -5.105835994692077 3.010112073988386 L -4.9 2.9999999999999996 L -4.6941640053079245 3.010112073988386 L -4.490310323766131 3.0403509111532157 L -4.29040217776563 3.090425294962361 L -4.096364792033311 3.1598529817262975 L -3.9100668526654054 3.247965344868454 L -3.7333025106588362 3.3539138141646543 L -3.567774103256345 3.476678047938252 L -3.415075759508251 3.615075759508249 L -3.2766780479382533 3.767774103256343 L -3.153913814164655 3.933302510658835 L -3.047965344868455 4.110066852665404 L -2.9598529817262986 4.29636479203331 L -2.8904252949623617 4.4904021777656284 L -2.840350911153217 4.69031032376613 L -2.8101120739883867 4.894164005307923 L -2.8000000000000003 5.1 L -2.8000000000000003 5.1 Z"/></clipPath><g clip-path="url(#clip-1764627670784-sxvpqol5n__stack1)"><line x1="-3.054344160979066" y1="2.9999999999999996" x2="-2.8000000000000003" y2="3.2543441609790653" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-3.308902602206223" y1="2.9999999999999996" x2="-2.8000000000000003" y2="3.508902602206222" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-3.5634610434333807" y1="2.9999999999999996" x2="-2.8000000000000003" y2="3.76346104343338" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-3.8180194846605358" y1="2.9999999999999996" x2="-2.8000000000000003" y2="4.018019484660535" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-4.072577925887693" y1="2.9999999999999996" x2="-2.8000000000000003" y2="4.272577925887692" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-4.32713636711485" y1="2.9999999999999996" x2="-2.8000000000000003" y2="4.527136367114849" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-4.581694808342007" y1="2.9999999999999996" x2="-2.8000000000000003" y2="4.7816948083420066" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-4.836253249569165" y1="2.9999999999999996" x2="-2.8000000000000003" y2="5.036253249569164" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-5.090811690796322" y1="2.9999999999999996" x2="-2.8000000000000003" y2="5.290811690796321" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-5.345370132023479" y1="2.9999999999999996" x2="-2.8000000000000003" y2="5.5453701320234785" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-5.5999285732506365" y1="2.9999999999999996" x2="-2.8000000000000003" y2="5.799928573250636" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-5.854487014477794" y1="2.9999999999999996" x2="-2.8000000000000003" y2="6.054487014477793" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-6.109045455704949" y1="2.9999999999999996" x2="-2.8000000000000003" y2="6.309045455704949" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-6.3636038969321085" y1="2.9999999999999996" x2="-2.8000000000000003" y2="6.563603896932107" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-6.618162338159264" y1="2.9999999999999996" x2="-2.8000000000000003" y2="6.818162338159262" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-6.872720779386423" y1="2.9999999999999996" x2="-2.8000000000000003" y2="7.072720779386421" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-7" y1="3.127279220613578" x2="-2.9272792206135785" y2="7.199999999999999" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-7" y1="3.3818376618407364" x2="-3.181837661840738" y2="7.199999999999999" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-7" y1="3.636396103067893" x2="-3.436396103067893" y2="7.199999999999999" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-7" y1="3.890954544295052" x2="-3.690954544295052" y2="7.199999999999999" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-7" y1="4.145512985522206" x2="-3.945512985522207" y2="7.199999999999999" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-7" y1="4.4000714267493635" x2="-4.200071426749364" y2="7.199999999999999" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-7" y1="4.654629867976521" x2="-4.4546298679765215" y2="7.199999999999999" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-7" y1="4.909188309203678" x2="-4.709188309203679" y2="7.199999999999999" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-7" y1="5.163746750430835" x2="-4.963746750430836" y2="7.199999999999999" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-7" y1="5.418305191657993" x2="-5.2183051916579934" y2="7.199999999999999" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-7" y1="5.67286363288515" x2="-5.472863632885151" y2="7.199999999999999" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-7" y1="5.927422074112307" x2="-5.727422074112308" y2="7.199999999999999" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-7" y1="6.181980515339465" x2="-5.981980515339465" y2="7.199999999999999" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-7" y1="6.43653895656662" x2="-6.236538956566621" y2="7.199999999999999" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-7" y1="6.691097397793779" x2="-6.49109739779378" y2="7.199999999999999" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-7" y1="6.945655839020935" x2="-6.745655839020936" y2="7.199999999999999" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-2.8000000000000003" y1="6.945655839020933" x2="-3.0543441609790665" y2="7.199999999999999" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-2.8000000000000003" y1="6.6910973977937775" x2="-3.308902602206222" y2="7.199999999999999" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-2.8000000000000003" y1="6.4365389565666185" x2="-3.563461043433381" y2="7.199999999999999" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-2.8000000000000003" y1="6.181980515339465" x2="-3.818019484660535" y2="7.199999999999999" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-2.8000000000000003" y1="5.927422074112307" x2="-4.072577925887693" y2="7.199999999999999" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-2.8000000000000003" y1="5.67286363288515" x2="-4.32713636711485" y2="7.199999999999999" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-2.8000000000000003" y1="5.418305191657993" x2="-4.581694808342007" y2="7.199999999999999" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-2.8000000000000003" y1="5.163746750430835" x2="-4.836253249569165" y2="7.199999999999999" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-2.8000000000000003" y1="4.909188309203678" x2="-5.090811690796322" y2="7.199999999999999" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-2.8000000000000003" y1="4.654629867976521" x2="-5.345370132023479" y2="7.199999999999999" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-2.8000000000000003" y1="4.4000714267493635" x2="-5.5999285732506365" y2="7.199999999999999" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-2.8000000000000003" y1="4.145512985522206" x2="-5.854487014477794" y2="7.199999999999999" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-2.8000000000000003" y1="3.8909545442950506" x2="-6.109045455704948" y2="7.199999999999999" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-2.8000000000000003" y1="3.6363961030678924" x2="-6.363603896932108" y2="7.199999999999999" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-2.8000000000000003" y1="3.381837661840735" x2="-6.618162338159262" y2="7.199999999999999" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-2.8000000000000003" y1="3.127279220613578" x2="-6.872720779386423" y2="7.199999999999999" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-2.927279220613579" y1="2.9999999999999996" x2="-7" y2="7.072720779386422" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-3.1818376618407362" y1="2.9999999999999996" x2="-7" y2="6.818162338159263" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-3.4363961030678936" y1="2.9999999999999996" x2="-7" y2="6.563603896932108" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-3.690954544295051" y1="2.9999999999999996" x2="-7" y2="6.309045455704949" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-3.9455129855222064" y1="2.9999999999999996" x2="-7" y2="6.054487014477793" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-4.200071426749364" y1="2.9999999999999996" x2="-7" y2="5.799928573250636" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-4.4546298679765215" y1="2.9999999999999996" x2="-7" y2="5.5453701320234785" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-4.709188309203679" y1="2.9999999999999996" x2="-7" y2="5.290811690796321" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-4.963746750430836" y1="2.9999999999999996" x2="-7" y2="5.036253249569164" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-5.2183051916579934" y1="2.9999999999999996" x2="-7" y2="4.7816948083420066" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-5.47286363288515" y1="2.9999999999999996" x2="-7" y2="4.527136367114849" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-5.727422074112307" y1="2.9999999999999996" x2="-7" y2="4.272577925887692" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-5.9819805153394645" y1="2.9999999999999996" x2="-7" y2="4.018019484660535" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-6.23653895656662" y1="2.9999999999999996" x2="-7" y2="3.76346104343338" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-6.491097397793778" y1="2.9999999999999996" x2="-7" y2="3.5089026022062213" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="-6.745655839020935" y1="2.9999999999999996" x2="-7" y2="3.254344160979065" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/></g><path d="M -2.8000000000000003 5.1 L -2.8101120739883867 5.305835994692077 L -2.8403509111532164 5.509689676233869 L -2.8904252949623617 5.709597822234371 L -2.959852981726298 5.903635207966688 L -3.0479653448684547 6.089933147334595 L -3.153913814164655 6.266697489341164 L -3.276678047938253 6.432225896743655 L -3.4150757595082504 6.584924240491749 L -3.567774103256345 6.723321952061747 L -3.7333025106588353 6.846086185835345 L -3.910066852665405 6.952034655131545 L -4.096364792033311 7.040147018273702 L -4.290402177765629 7.109574705037638 L -4.490310323766131 7.159649088846784 L -4.694164005307923 7.189887926011613 L -4.9 7.199999999999999 L -5.105835994692078 7.189887926011613 L -5.3096896762338694 7.159649088846784 L -5.509597822234371 7.109574705037638 L -5.7036352079666885 7.040147018273702 L -5.889933147334595 6.952034655131545 L -6.0666974893411645 6.846086185835345 L -6.232225896743656 6.723321952061747 L -6.38492424049175 6.58492424049175 L -6.523321952061748 6.432225896743655 L -6.646086185835346 6.266697489341164 L -6.7520346551315455 6.0899331473345955 L -6.8401470182737025 5.903635207966689 L -6.909574705037639 5.709597822234371 L -6.959649088846785 5.50968967623387 L -6.989887926011614 5.305835994692077 L -7 5.1 L -6.989887926011614 4.894164005307922 L -6.959649088846785 4.69031032376613 L -6.909574705037639 4.490402177765629 L -6.8401470182737025 4.2963647920333115 L -6.7520346551315455 4.110066852665405 L -6.646086185835346 3.9333025106588355 L -6.523321952061748 3.7677741032563445 L -6.384924240491751 3.61507575950825 L -6.232225896743657 3.4766780479382526 L -6.0666974893411645 3.3539138141646543 L -5.889933147334596 3.2479653448684545 L -5.70363520796669 3.159852981726298 L -5.5095978222343716 3.090425294962361 L -5.30968967623387 3.040350911153216 L -5.105835994692077 3.010112073988386 L -4.9 2.9999999999999996 L -4.6941640053079245 3.010112073988386 L -4.490310323766131 3.0403509111532157 L -4.29040217776563 3.090425294962361 L -4.096364792033311 3.1598529817262975 L -3.9100668526654054 3.247965344868454 L -3.7333025106588362 3.3539138141646543 L -3.567774103256345 3.476678047938252 L -3.415075759508251 3.615075759508249 L -3.2766780479382533 3.767774103256343 L -3.153913814164655 3.933302510658835 L -3.047965344868455 4.110066852665404 L -2.9598529817262986 4.29636479203331 L -2.8904252949623617 4.4904021777656284 L -2.840350911153217 4.69031032376613 L -2.8101120739883867 4.894164005307923 L -2.8000000000000003 5.1 L -2.8000000000000003 5.1 Z" fill="none" stroke="#FF0000" stroke-width="0.1"/></g><g transform="matrix(1,0,0,1,0,0)"><path d="M 8.1 -4.9 L 8.08555418001659 -4.605948579011319 L 8.042355841209691 -4.3147290339516156 L 7.970821007196626 -4.029145968236613 L 7.87163859753386 -3.751949702904731 L 7.7457637930450645 -3.4858097895220075 L 7.594408836907635 -3.233289300941194 L 7.41903136008821 -2.996820147509064 L 7.2213203435596425 -2.778679656440358 L 7.003179852490936 -2.5809686399117893 L 6.766710699058807 -2.4055911630923648 L 6.514190210477993 -2.2542362069549355 L 6.248050297095269 -2.1283614024661404 L 5.970854031763387 -2.0291789928033737 L 5.6852709660483844 -1.957644158790309 L 5.394051420988682 -1.9144458199834098 L 5.1 -1.9000000000000004 L 4.805948579011318 -1.9144458199834098 L 4.514729033951615 -1.957644158790309 L 4.2291459682366135 -2.0291789928033737 L 3.9519497029047304 -2.1283614024661404 L 3.6858097895220068 -2.254236206954935 L 3.433289300941194 -2.4055911630923643 L 3.1968201475090634 -2.580968639911789 L 2.9786796564403573 -2.7786796564403575 L 2.7809686399117886 -2.996820147509064 L 2.6055911630923636 -3.233289300941194 L 2.454236206954935 -3.4858097895220066 L 2.3283614024661397 -3.7519497029047306 L 2.229178992803373 -4.029145968236613 L 2.1576441587903084 -4.314729033951615 L 2.114445819983409 -4.605948579011318 L 2.0999999999999996 -4.9 L 2.114445819983409 -5.194051420988682 L 2.1576441587903084 -5.485270966048385 L 2.229178992803373 -5.7708540317633865 L 2.328361402466139 -6.048050297095269 L 2.4542362069549344 -6.314190210477993 L 2.605591163092363 -6.566710699058806 L 2.780968639911788 -6.803179852490937 L 2.978679656440357 -7.021320343559642 L 3.196820147509062 -7.21903136008821 L 3.433289300941193 -7.394408836907636 L 3.685809789522006 -7.545763793045065 L 3.9519497029047286 -7.67163859753386 L 4.229145968236613 -7.770821007196627 L 4.514729033951614 -7.842355841209692 L 4.805948579011318 -7.885554180016591 L 5.099999999999999 -7.9 L 5.39405142098868 -7.885554180016591 L 5.6852709660483844 -7.842355841209692 L 5.970854031763386 -7.770821007196627 L 6.248050297095269 -7.67163859753386 L 6.5141902104779925 -7.545763793045065 L 6.766710699058805 -7.394408836907637 L 7.003179852490936 -7.219031360088211 L 7.221320343559642 -7.021320343559643 L 7.419031360088209 -6.803179852490938 L 7.594408836907635 -6.566710699058807 L 7.7457637930450645 -6.314190210477994 L 7.87163859753386 -6.048050297095271 L 7.970821007196626 -5.770854031763388 L 8.042355841209691 -5.485270966048386 L 8.08555418001659 -5.194051420988682 L 8.1 -4.9 L 8.1 -4.9 Z" fill="none" stroke="#0000FF" stroke-width="0.1"/></g><g transform="matrix(1,0,0,1,0,0)"><clipPath id="clip-1764627670784-c401vgn6f__stack1"><path d="M 8.2 -4.9 L 8.185072652683811 -4.596146864978363 L 8.140434369250015 -4.295220001750002 L 8.066515040769847 -4.000117500511167 L 7.964026550784989 -3.713681359668222 L 7.8339559194799 -3.4386701158394075 L 7.67755579813789 -3.1777322776392336 L 7.496332405424484 -2.933380819092699 L 7.292031021678297 -2.7079689783217034 L 7.066619180907301 -2.5036675945755156 L 6.822267722360767 -2.32244420186211 L 6.5613298841605925 -2.1660440805201 L 6.286318640331778 -2.035973449215011 L 5.999882499488833 -1.933484959230153 L 5.704779998249998 -1.859565630749986 L 5.403853135021638 -1.81492734731619 L 5.1 -1.8000000000000003 L 4.796146864978361 -1.81492734731619 L 4.495220001750003 -1.859565630749986 L 4.200117500511167 -1.9334849592301526 L 3.9136813596682214 -2.035973449215011 L 3.638670115839407 -2.1660440805201 L 3.3777322776392333 -2.3224442018621096 L 3.133380819092699 -2.503667594575515 L 2.9079689783217026 -2.707968978321703 L 2.703667594575515 -2.933380819092699 L 2.522444201862109 -3.1777322776392336 L 2.366044080520099 -3.4386701158394066 L 2.2359734492150105 -3.7136813596682217 L 2.1334849592301524 -4.000117500511167 L 2.059565630749985 -4.2952200017500015 L 2.0149273473161893 -4.596146864978362 L 1.9999999999999996 -4.9 L 2.0149273473161893 -5.203853135021638 L 2.059565630749985 -5.504779998249998 L 2.133484959230152 -5.799882499488833 L 2.2359734492150105 -6.086318640331778 L 2.366044080520099 -6.361329884160593 L 2.5224442018621085 -6.622267722360767 L 2.7036675945755144 -6.866619180907301 L 2.9079689783217018 -7.092031021678297 L 3.1333808190926975 -7.296332405424485 L 3.377732277639233 -7.477555798137891 L 3.638670115839406 -7.6339559194799005 L 3.9136813596682196 -7.764026550784989 L 4.200117500511166 -7.866515040769848 L 4.495220001750001 -7.940434369250014 L 4.796146864978362 -7.985072652683811 L 5.099999999999999 -8 L 5.403853135021636 -7.985072652683811 L 5.704779998249998 -7.940434369250015 L 5.999882499488832 -7.866515040769848 L 6.286318640331778 -7.764026550784989 L 6.5613298841605925 -7.6339559194799005 L 6.822267722360765 -7.4775557981378915 L 7.066619180907301 -7.296332405424485 L 7.292031021678296 -7.092031021678299 L 7.496332405424484 -6.8666191809073025 L 7.67755579813789 -6.6222677223607676 L 7.8339559194799 -6.361329884160594 L 7.964026550784988 -6.086318640331781 L 8.066515040769847 -5.7998824994888345 L 8.140434369250013 -5.504779998249999 L 8.185072652683811 -5.203853135021638 L 8.2 -4.9 L 8.2 -4.9 Z"/></clipPath><g clip-path="url(#clip-1764627670784-c401vgn6f__stack1)"><line x1="7.982123368838192" y1="-8" x2="8.2" y2="-7.782123368838192" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="7.727564927611034" y1="-8" x2="8.2" y2="-7.527564927611035" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="7.473006486383877" y1="-8" x2="8.2" y2="-7.273006486383878" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="7.218448045156721" y1="-8" x2="8.2" y2="-7.018448045156721" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="6.963889603929562" y1="-8" x2="8.2" y2="-6.763889603929563" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="6.709331162702406" y1="-8" x2="8.2" y2="-6.509331162702407" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="6.454772721475248" y1="-8" x2="8.2" y2="-6.254772721475248" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="6.200214280248091" y1="-8" x2="8.2" y2="-6.000214280248092" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="5.945655839020935" y1="-8" x2="8.2" y2="-5.745655839020936" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="5.6910973977937775" y1="-8" x2="8.2" y2="-5.491097397793779" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="5.43653895656662" y1="-8" x2="8.2" y2="-5.236538956566621" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="5.181980515339465" y1="-8" x2="8.2" y2="-4.981980515339465" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="4.927422074112307" y1="-8" x2="8.2" y2="-4.727422074112308" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="4.67286363288515" y1="-8" x2="8.2" y2="-4.472863632885151" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="4.418305191657992" y1="-8" x2="8.2" y2="-4.2183051916579934" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="4.163746750430835" y1="-8" x2="8.2" y2="-3.963746750430836" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="3.909188309203677" y1="-8" x2="8.2" y2="-3.7091883092036797" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="3.654629867976521" y1="-8" x2="8.2" y2="-3.4546298679765215" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="3.4000714267493626" y1="-8" x2="8.2" y2="-3.200071426749365" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="3.145512985522206" y1="-8" x2="8.2" y2="-2.945512985522207" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="2.89095454429505" y1="-8" x2="8.2" y2="-2.690954544295053" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="2.636396103067893" y1="-8" x2="8.2" y2="-2.436396103067894" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="2.3818376618407355" y1="-8" x2="8.2" y2="-2.1818376618407367" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="2.127279220613578" y1="-8" x2="8.2" y2="-1.9272792206135794" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="1.9999999999999996" y1="-7.872720779386421" x2="8.07272077938642" y2="-1.8000000000000003" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="1.9999999999999996" y1="-7.618162338159264" x2="7.818162338159263" y2="-1.8000000000000003" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="1.9999999999999996" y1="-7.363603896932107" x2="7.563603896932106" y2="-1.8000000000000003" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="1.9999999999999996" y1="-7.109045455704949" x2="7.309045455704949" y2="-1.8000000000000003" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="1.9999999999999996" y1="-6.854487014477794" x2="7.054487014477793" y2="-1.8000000000000003" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="1.9999999999999996" y1="-6.5999285732506365" x2="6.799928573250636" y2="-1.8000000000000003" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="1.9999999999999996" y1="-6.345370132023479" x2="6.5453701320234785" y2="-1.8000000000000003" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="1.9999999999999996" y1="-6.090811690796322" x2="6.290811690796321" y2="-1.8000000000000003" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="1.9999999999999996" y1="-5.836253249569165" x2="6.036253249569164" y2="-1.8000000000000003" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="1.9999999999999996" y1="-5.581694808342007" x2="5.781694808342007" y2="-1.8000000000000003" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="1.9999999999999996" y1="-5.327136367114851" x2="5.52713636711485" y2="-1.8000000000000003" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="1.9999999999999996" y1="-5.072577925887693" x2="5.272577925887692" y2="-1.8000000000000003" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="1.9999999999999996" y1="-4.818019484660535" x2="5.018019484660535" y2="-1.8000000000000003" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="1.9999999999999996" y1="-4.563461043433381" x2="4.76346104343338" y2="-1.8000000000000003" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="1.9999999999999996" y1="-4.3089026022062225" x2="4.508902602206222" y2="-1.8000000000000003" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="1.9999999999999996" y1="-4.054344160979066" x2="4.254344160979066" y2="-1.8000000000000003" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="1.9999999999999996" y1="-3.7997857197519087" x2="3.999785719751908" y2="-1.8000000000000003" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="1.9999999999999996" y1="-3.5452272785247514" x2="3.7452272785247507" y2="-1.8000000000000003" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="1.9999999999999996" y1="-3.290668837297594" x2="3.4906688372975934" y2="-1.8000000000000003" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="1.9999999999999996" y1="-3.0361103960704368" x2="3.236110396070436" y2="-1.8000000000000003" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="1.9999999999999996" y1="-2.7815519548432794" x2="2.9815519548432787" y2="-1.8000000000000003" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="1.9999999999999996" y1="-2.526993513616122" x2="2.7269935136161214" y2="-1.8000000000000003" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="1.9999999999999996" y1="-2.272435072388964" x2="2.472435072388963" y2="-1.8000000000000003" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="1.9999999999999996" y1="-2.0178766311618084" x2="2.2178766311618077" y2="-1.8000000000000003" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="8.2" y1="-2.0178766311618084" x2="7.982123368838192" y2="-1.8000000000000003" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="8.2" y1="-2.2724350723889657" x2="7.727564927611034" y2="-1.8000000000000003" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="8.2" y1="-2.526993513616123" x2="7.473006486383877" y2="-1.8000000000000003" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="8.2" y1="-2.7815519548432794" x2="7.218448045156721" y2="-1.8000000000000003" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="8.2" y1="-3.0361103960704376" x2="6.963889603929562" y2="-1.8000000000000003" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="8.2" y1="-3.290668837297594" x2="6.709331162702406" y2="-1.8000000000000003" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="8.2" y1="-3.5452272785247514" x2="6.454772721475249" y2="-1.8000000000000003" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="8.2" y1="-3.7997857197519096" x2="6.20021428024809" y2="-1.8000000000000003" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="8.2" y1="-4.054344160979066" x2="5.945655839020935" y2="-1.8000000000000003" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="8.2" y1="-4.308902602206222" x2="5.6910973977937775" y2="-1.8000000000000003" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="8.2" y1="-4.563461043433381" x2="5.43653895656662" y2="-1.8000000000000003" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="8.2" y1="-4.818019484660535" x2="5.181980515339465" y2="-1.8000000000000003" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="8.2" y1="-5.072577925887694" x2="4.9274220741123065" y2="-1.8000000000000003" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="8.2" y1="-5.32713636711485" x2="4.67286363288515" y2="-1.8000000000000003" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="8.2" y1="-5.581694808342009" x2="4.418305191657993" y2="-1.8000000000000003" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="8.2" y1="-5.836253249569165" x2="4.163746750430835" y2="-1.8000000000000003" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="8.2" y1="-6.090811690796324" x2="3.909188309203678" y2="-1.8000000000000003" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="8.2" y1="-6.345370132023479" x2="3.654629867976521" y2="-1.8000000000000003" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="8.2" y1="-6.599928573250638" x2="3.4000714267493644" y2="-1.8000000000000003" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="8.2" y1="-6.854487014477794" x2="3.145512985522206" y2="-1.8000000000000003" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="8.2" y1="-7.109045455704949" x2="2.8909545442950506" y2="-1.8000000000000003" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="8.2" y1="-7.3636038969321085" x2="2.6363961030678924" y2="-1.8000000000000003" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="8.2" y1="-7.618162338159264" x2="2.381837661840736" y2="-1.8000000000000003" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="8.2" y1="-7.872720779386423" x2="2.1272792206135778" y2="-1.8000000000000003" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="8.07272077938642" y1="-8" x2="1.9999999999999996" y2="-1.9272792206135785" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="7.8181623381592615" y1="-8" x2="1.9999999999999996" y2="-2.1818376618407376" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="7.563603896932106" y1="-8" x2="1.9999999999999996" y2="-2.4363961030678922" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="7.309045455704948" y1="-8" x2="1.9999999999999996" y2="-2.690954544295052" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="7.054487014477793" y1="-8" x2="1.9999999999999996" y2="-2.945512985522207" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="6.799928573250636" y1="-8" x2="1.9999999999999996" y2="-3.2000714267493633" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="6.5453701320234785" y1="-8" x2="1.9999999999999996" y2="-3.4546298679765215" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="6.29081169079632" y1="-8" x2="1.9999999999999996" y2="-3.709188309203678" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="6.036253249569164" y1="-8" x2="1.9999999999999996" y2="-3.963746750430836" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="5.781694808342006" y1="-8" x2="1.9999999999999996" y2="-4.2183051916579934" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="5.527136367114849" y1="-8" x2="1.9999999999999996" y2="-4.47286363288515" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="5.272577925887691" y1="-8" x2="1.9999999999999996" y2="-4.727422074112307" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="5.018019484660535" y1="-8" x2="1.9999999999999996" y2="-4.981980515339465" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="4.76346104343338" y1="-8" x2="1.9999999999999996" y2="-5.236538956566619" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="4.508902602206221" y1="-8" x2="1.9999999999999996" y2="-5.491097397793778" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="4.254344160979065" y1="-8" x2="1.9999999999999996" y2="-5.745655839020934" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="3.999785719751908" y1="-8" x2="1.9999999999999996" y2="-6.000214280248091" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="3.74522727852475" y1="-8" x2="1.9999999999999996" y2="-6.254772721475249" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="3.4906688372975934" y1="-8" x2="1.9999999999999996" y2="-6.509331162702406" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="3.236110396070436" y1="-8" x2="1.9999999999999996" y2="-6.763889603929563" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="2.9815519548432787" y1="-8" x2="1.9999999999999996" y2="-7.01844804515672" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="2.7269935136161205" y1="-8" x2="1.9999999999999996" y2="-7.273006486383879" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="2.472435072388964" y1="-8" x2="1.9999999999999996" y2="-7.527564927611035" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/><line x1="2.2178766311618077" y1="-8" x2="1.9999999999999996" y2="-7.782123368838192" stroke="#FF0000" stroke-width="0.1" stroke-opacity="0.8"/></g><path d="M 8.2 -4.9 L 8.185072652683811 -4.596146864978363 L 8.140434369250015 -4.295220001750002 L 8.066515040769847 -4.000117500511167 L 7.964026550784989 -3.713681359668222 L 7.8339559194799 -3.4386701158394075 L 7.67755579813789 -3.1777322776392336 L 7.496332405424484 -2.933380819092699 L 7.292031021678297 -2.7079689783217034 L 7.066619180907301 -2.5036675945755156 L 6.822267722360767 -2.32244420186211 L 6.5613298841605925 -2.1660440805201 L 6.286318640331778 -2.035973449215011 L 5.999882499488833 -1.933484959230153 L 5.704779998249998 -1.859565630749986 L 5.403853135021638 -1.81492734731619 L 5.1 -1.8000000000000003 L 4.796146864978361 -1.81492734731619 L 4.495220001750003 -1.859565630749986 L 4.200117500511167 -1.9334849592301526 L 3.9136813596682214 -2.035973449215011 L 3.638670115839407 -2.1660440805201 L 3.3777322776392333 -2.3224442018621096 L 3.133380819092699 -2.503667594575515 L 2.9079689783217026 -2.707968978321703 L 2.703667594575515 -2.933380819092699 L 2.522444201862109 -3.1777322776392336 L 2.366044080520099 -3.4386701158394066 L 2.2359734492150105 -3.7136813596682217 L 2.1334849592301524 -4.000117500511167 L 2.059565630749985 -4.2952200017500015 L 2.0149273473161893 -4.596146864978362 L 1.9999999999999996 -4.9 L 2.0149273473161893 -5.203853135021638 L 2.059565630749985 -5.504779998249998 L 2.133484959230152 -5.799882499488833 L 2.2359734492150105 -6.086318640331778 L 2.366044080520099 -6.361329884160593 L 2.5224442018621085 -6.622267722360767 L 2.7036675945755144 -6.866619180907301 L 2.9079689783217018 -7.092031021678297 L 3.1333808190926975 -7.296332405424485 L 3.377732277639233 -7.477555798137891 L 3.638670115839406 -7.6339559194799005 L 3.9136813596682196 -7.764026550784989 L 4.200117500511166 -7.866515040769848 L 4.495220001750001 -7.940434369250014 L 4.796146864978362 -7.985072652683811 L 5.099999999999999 -8 L 5.403853135021636 -7.985072652683811 L 5.704779998249998 -7.940434369250015 L 5.999882499488832 -7.866515040769848 L 6.286318640331778 -7.764026550784989 L 6.5613298841605925 -7.6339559194799005 L 6.822267722360765 -7.4775557981378915 L 7.066619180907301 -7.296332405424485 L 7.292031021678296 -7.092031021678299 L 7.496332405424484 -6.8666191809073025 L 7.67755579813789 -6.6222677223607676 L 7.8339559194799 -6.361329884160594 L 7.964026550784988 -6.086318640331781 L 8.066515040769847 -5.7998824994888345 L 8.140434369250013 -5.504779998249999 L 8.185072652683811 -5.203853135021638 L 8.2 -4.9 L 8.2 -4.9 Z" fill="none" stroke="#FF0000" stroke-width="0.1"/></g></g>
|
|
7
|
+
</g>
|
|
8
|
+
</svg>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<svg width="800" height="1400" viewBox="0 0 800 1400" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<g transform="translate(0, 0)">
|
|
3
|
+
<style></style><rect class="boundary" x="0" y="0" fill="#000" width="800" height="600" data-type="pcb_background" data-pcb-layer="global"/><rect class="pcb-boundary" fill="none" stroke="#fff" stroke-width="0.3" x="127.27272727272725" y="27.272727272727252" width="545.4545454545455" height="545.4545454545455" data-type="pcb_boundary" data-pcb-layer="global"/><path class="pcb-board" d="M 127.27272727272725 572.7272727272727 L 672.7272727272727 572.7272727272727 L 672.7272727272727 27.272727272727252 L 127.27272727272725 27.272727272727252 Z" fill="none" stroke="rgba(255, 255, 255, 0.5)" stroke-width="2.7272727272727275" data-type="pcb_board" data-pcb-layer="board"/>
|
|
4
|
+
</g>
|
|
5
|
+
<g transform="translate(0, 600) scale(20, 20) translate(19.9, 19.9)">
|
|
6
|
+
<rect x="-19.9" y="-19.9" width="40" height="40" fill="white"/><g transform="matrix(1 0 0 -1 0 0.20000000000000284)"><g transform="matrix(1,0,0,1,0,0)"><path d="M -9.9 -9.9 L 10.1 -9.9 L 10.1 10.1 L -9.9 10.1 L -9.9 -9.9 L -9.9 -9.9 Z" fill="none" stroke="#0000FF" stroke-width="0.1"/></g><g transform="matrix(1,0,0,1,0,0)"><path d="M -7.9 0.1 L -3.9 4.1 L 0.1 0.1 L -3.9 -3.9 L -7.9 0.1 L -7.9 0.1" fill="none" stroke="#0000FF" stroke-width="0.1"/></g></g>
|
|
7
|
+
</g>
|
|
8
|
+
</svg>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<svg width="800" height="1400" viewBox="0 0 800 1400" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<g transform="translate(0, 0)">
|
|
3
|
+
<style></style><rect class="boundary" x="0" y="0" fill="#000" width="800" height="600" data-type="pcb_background" data-pcb-layer="global"/><rect class="pcb-boundary" fill="none" stroke="#fff" stroke-width="0.3" x="127.27272727272725" y="27.272727272727252" width="545.4545454545455" height="545.4545454545455" data-type="pcb_boundary" data-pcb-layer="global"/><path class="pcb-board" d="M 127.27272727272725 572.7272727272727 L 672.7272727272727 572.7272727272727 L 672.7272727272727 27.272727272727252 L 127.27272727272725 27.272727272727252 Z" fill="none" stroke="rgba(255, 255, 255, 0.5)" stroke-width="2.7272727272727275" data-type="pcb_board" data-pcb-layer="board"/><polygon class="pcb-cutout pcb-cutout-polygon" points="181.8181818181818,136.36363636363637 290.9090909090909,81.81818181818181 345.45454545454544,136.36363636363637 290.9090909090909,190.9090909090909" fill="#FF26E2" data-type="pcb_cutout" data-pcb-layer="drill"/><polygon class="pcb-cutout pcb-cutout-polygon" points="481.8181818181818,381.8181818181818 590.9090909090909,381.8181818181818 590.9090909090909,490.9090909090909 536.3636363636364,518.1818181818182 481.8181818181818,490.9090909090909" fill="#FF26E2" data-type="pcb_cutout" data-pcb-layer="drill"/>
|
|
4
|
+
</g>
|
|
5
|
+
<g transform="translate(0, 600) scale(20, 20) translate(19.9, 19.9)">
|
|
6
|
+
<rect x="-19.9" y="-19.9" width="40" height="40" fill="white"/><g transform="matrix(1 0 0 -1 0 0.20000000000000284)"><g transform="matrix(1,0,0,1,0,0)"><path d="M -9.9 -9.9 L 10.1 -9.9 L 10.1 10.1 L -9.9 10.1 L -9.9 -9.9 L -9.9 -9.9 Z" fill="none" stroke="#0000FF" stroke-width="0.1"/></g><g transform="matrix(1,0,0,1,0,0)"><path d="M -7.9 6.1 L -3.9 8.1 L -1.9 6.1 L -3.9 4.1 L -7.9 6.1 L -7.9 6.1 Z" fill="none" stroke="#0000FF" stroke-width="0.1"/></g><g transform="matrix(1,0,0,1,0,0)"><path d="M 3.1 -2.9 L 7.1 -2.9 L 7.1 -6.9 L 5.1 -7.9 L 3.1 -6.9 L 3.1 -2.9 L 3.1 -2.9 Z" fill="none" stroke="#0000FF" stroke-width="0.1"/></g></g>
|
|
7
|
+
</g>
|
|
8
|
+
</svg>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<svg width="800" height="1400" viewBox="0 0 800 1400" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<g transform="translate(0, 0)">
|
|
3
|
+
<style></style><rect class="boundary" x="0" y="0" fill="#000" width="800" height="600" data-type="pcb_background" data-pcb-layer="global"/><rect class="pcb-boundary" fill="none" stroke="#fff" stroke-width="0.3" x="127.27272727272725" y="27.272727272727252" width="545.4545454545455" height="545.4545454545455" data-type="pcb_boundary" data-pcb-layer="global"/><path class="pcb-board" d="M 127.27272727272725 572.7272727272727 L 672.7272727272727 572.7272727272727 L 672.7272727272727 27.272727272727252 L 127.27272727272725 27.272727272727252 Z" fill="none" stroke="rgba(255, 255, 255, 0.5)" stroke-width="2.7272727272727275" data-type="pcb_board" data-pcb-layer="board"/><rect class="pcb-cutout pcb-cutout-rect" x="-54.54545454545455" y="-40.90909090909091" width="109.0909090909091" height="81.81818181818181" fill="#FF26E2" transform="matrix(1,0,0,1,263.6363636363636,163.63636363636363)" data-type="pcb_cutout" data-pcb-layer="drill"/><rect class="pcb-cutout pcb-cutout-rect" x="-40.90909090909091" y="-54.54545454545455" width="81.81818181818181" height="109.0909090909091" fill="#FF26E2" transform="matrix(0.7071067811865476,-0.7071067811865475,0.7071067811865475,0.7071067811865476,536.3636363636364,436.3636363636364)" data-type="pcb_cutout" data-pcb-layer="drill"/>
|
|
4
|
+
</g>
|
|
5
|
+
<g transform="translate(0, 600) scale(20, 20) translate(19.9, 19.9)">
|
|
6
|
+
<rect x="-19.9" y="-19.9" width="40" height="40" fill="white"/><g transform="matrix(1 0 0 -1 0 0.20000000000000284)"><g transform="matrix(1,0,0,1,0,0)"><path d="M -9.9 -9.9 L 10.1 -9.9 L 10.1 10.1 L -9.9 10.1 L -9.9 -9.9 L -9.9 -9.9 Z" fill="none" stroke="#0000FF" stroke-width="0.1"/></g><g transform="matrix(1,0,0,1,0,0)"><path d="M -6.9 3.5999999999999996 L -2.9000000000000004 3.5999999999999996 L -2.9000000000000004 3.5999999999999996 L -2.9000000000000004 3.5999999999999996 L -2.9000000000000004 6.6 L -2.9000000000000004 6.6 L -2.9000000000000004 6.6 L -6.9 6.6 L -6.9 6.6 L -6.9 6.6 L -6.9 3.5999999999999996 L -6.9 3.5999999999999996 L -6.9 3.5999999999999996 L -6.9 3.5999999999999996 Z" fill="none" stroke="#0000FF" stroke-width="0.1"/></g><g transform="matrix(1,0,0,1,0,0)"><path d="M 5.453553390593273 -7.374873734152917 L 7.574873734152916 -5.253553390593274 L 7.574873734152916 -5.253553390593274 L 7.574873734152916 -5.253553390593274 L 4.746446609406727 -2.425126265847084 L 4.746446609406727 -2.425126265847084 L 4.746446609406727 -2.425126265847084 L 2.6251262658470833 -4.546446609406726 L 2.6251262658470833 -4.546446609406726 L 2.6251262658470833 -4.546446609406726 L 5.453553390593273 -7.374873734152917 L 5.453553390593273 -7.374873734152917 L 5.453553390593273 -7.374873734152917 L 5.453553390593273 -7.374873734152917 Z" fill="none" stroke="#0000FF" stroke-width="0.1"/></g></g>
|
|
7
|
+
</g>
|
|
8
|
+
</svg>
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { test, expect } from "bun:test"
|
|
2
|
+
import { convertCircuitJsonToPcbSvg } from "circuit-to-svg"
|
|
3
|
+
import { generateLightBurnSvg } from "lbrnts"
|
|
4
|
+
import { convertCircuitJsonToLbrn } from "../../../lib"
|
|
5
|
+
import { stackSvgsVertically } from "stack-svgs"
|
|
6
|
+
import type { CircuitJson } from "circuit-json"
|
|
7
|
+
|
|
8
|
+
const circuitJson: CircuitJson = [
|
|
9
|
+
{
|
|
10
|
+
type: "pcb_board",
|
|
11
|
+
pcb_board_id: "board_circle_cutout",
|
|
12
|
+
width: 20,
|
|
13
|
+
height: 20,
|
|
14
|
+
center: { x: 0, y: 0 },
|
|
15
|
+
thickness: 1.6,
|
|
16
|
+
num_layers: 2,
|
|
17
|
+
material: "fr4",
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
type: "pcb_cutout",
|
|
21
|
+
pcb_cutout_id: "cutout_1",
|
|
22
|
+
shape: "circle",
|
|
23
|
+
center: { x: -5, y: 5 },
|
|
24
|
+
radius: 2,
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
type: "pcb_cutout",
|
|
28
|
+
pcb_cutout_id: "cutout_2",
|
|
29
|
+
shape: "circle",
|
|
30
|
+
center: { x: 5, y: -5 },
|
|
31
|
+
radius: 3,
|
|
32
|
+
},
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
test("renders circular pcb cutouts with through-board cuts", async () => {
|
|
36
|
+
const pcbSvg = await convertCircuitJsonToPcbSvg(circuitJson)
|
|
37
|
+
|
|
38
|
+
const project = convertCircuitJsonToLbrn(circuitJson, {
|
|
39
|
+
includeCopper: true,
|
|
40
|
+
includeSoldermask: true,
|
|
41
|
+
soldermaskMargin: 0.1,
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
const lbrnSvg = await generateLightBurnSvg(project)
|
|
45
|
+
|
|
46
|
+
expect(stackSvgsVertically([pcbSvg, lbrnSvg])).toMatchSvgSnapshot(
|
|
47
|
+
import.meta.filename,
|
|
48
|
+
)
|
|
49
|
+
})
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { test, expect } from "bun:test"
|
|
2
|
+
import { convertCircuitJsonToPcbSvg } from "circuit-to-svg"
|
|
3
|
+
import { generateLightBurnSvg } from "lbrnts"
|
|
4
|
+
import { convertCircuitJsonToLbrn } from "../../../lib"
|
|
5
|
+
import { stackSvgsVertically } from "stack-svgs"
|
|
6
|
+
import type { CircuitJson } from "circuit-json"
|
|
7
|
+
|
|
8
|
+
const circuitJson: CircuitJson = [
|
|
9
|
+
{
|
|
10
|
+
type: "pcb_board",
|
|
11
|
+
pcb_board_id: "board_path_cutout",
|
|
12
|
+
width: 20,
|
|
13
|
+
height: 20,
|
|
14
|
+
center: { x: 0, y: 0 },
|
|
15
|
+
thickness: 1.6,
|
|
16
|
+
num_layers: 2,
|
|
17
|
+
material: "fr4",
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
type: "pcb_cutout",
|
|
21
|
+
pcb_cutout_id: "cutout_1",
|
|
22
|
+
shape: "path",
|
|
23
|
+
route: [
|
|
24
|
+
{ x: -8, y: 0 },
|
|
25
|
+
{ x: -4, y: 4 },
|
|
26
|
+
{ x: 0, y: 0 },
|
|
27
|
+
{ x: -4, y: -4 },
|
|
28
|
+
{ x: -8, y: 0 },
|
|
29
|
+
],
|
|
30
|
+
slot_width: 0.5,
|
|
31
|
+
},
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
test("renders path-based pcb cutouts with through-board cuts", async () => {
|
|
35
|
+
const pcbSvg = await convertCircuitJsonToPcbSvg(circuitJson)
|
|
36
|
+
|
|
37
|
+
const project = convertCircuitJsonToLbrn(circuitJson)
|
|
38
|
+
|
|
39
|
+
const lbrnSvg = await generateLightBurnSvg(project)
|
|
40
|
+
|
|
41
|
+
expect(stackSvgsVertically([pcbSvg, lbrnSvg])).toMatchSvgSnapshot(
|
|
42
|
+
import.meta.filename,
|
|
43
|
+
)
|
|
44
|
+
})
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { test, expect } from "bun:test"
|
|
2
|
+
import { convertCircuitJsonToPcbSvg } from "circuit-to-svg"
|
|
3
|
+
import { generateLightBurnSvg } from "lbrnts"
|
|
4
|
+
import { convertCircuitJsonToLbrn } from "../../../lib"
|
|
5
|
+
import { stackSvgsVertically } from "stack-svgs"
|
|
6
|
+
import type { CircuitJson } from "circuit-json"
|
|
7
|
+
|
|
8
|
+
const circuitJson: CircuitJson = [
|
|
9
|
+
{
|
|
10
|
+
type: "pcb_board",
|
|
11
|
+
pcb_board_id: "board_polygon_cutout",
|
|
12
|
+
width: 20,
|
|
13
|
+
height: 20,
|
|
14
|
+
center: { x: 0, y: 0 },
|
|
15
|
+
thickness: 1.6,
|
|
16
|
+
num_layers: 2,
|
|
17
|
+
material: "fr4",
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
type: "pcb_cutout",
|
|
21
|
+
pcb_cutout_id: "cutout_1",
|
|
22
|
+
shape: "polygon",
|
|
23
|
+
points: [
|
|
24
|
+
{ x: -8, y: 6 },
|
|
25
|
+
{ x: -4, y: 8 },
|
|
26
|
+
{ x: -2, y: 6 },
|
|
27
|
+
{ x: -4, y: 4 },
|
|
28
|
+
],
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
type: "pcb_cutout",
|
|
32
|
+
pcb_cutout_id: "cutout_2",
|
|
33
|
+
shape: "polygon",
|
|
34
|
+
points: [
|
|
35
|
+
{ x: 3, y: -3 },
|
|
36
|
+
{ x: 7, y: -3 },
|
|
37
|
+
{ x: 7, y: -7 },
|
|
38
|
+
{ x: 5, y: -8 },
|
|
39
|
+
{ x: 3, y: -7 },
|
|
40
|
+
],
|
|
41
|
+
},
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
test("renders polygon pcb cutouts with through-board cuts", async () => {
|
|
45
|
+
const pcbSvg = await convertCircuitJsonToPcbSvg(circuitJson)
|
|
46
|
+
|
|
47
|
+
const project = convertCircuitJsonToLbrn(circuitJson)
|
|
48
|
+
|
|
49
|
+
const lbrnSvg = await generateLightBurnSvg(project)
|
|
50
|
+
|
|
51
|
+
expect(stackSvgsVertically([pcbSvg, lbrnSvg])).toMatchSvgSnapshot(
|
|
52
|
+
import.meta.filename,
|
|
53
|
+
)
|
|
54
|
+
})
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { test, expect } from "bun:test"
|
|
2
|
+
import { convertCircuitJsonToPcbSvg } from "circuit-to-svg"
|
|
3
|
+
import { generateLightBurnSvg } from "lbrnts"
|
|
4
|
+
import { convertCircuitJsonToLbrn } from "../../../lib"
|
|
5
|
+
import { stackSvgsVertically } from "stack-svgs"
|
|
6
|
+
import type { CircuitJson } from "circuit-json"
|
|
7
|
+
|
|
8
|
+
const circuitJson: CircuitJson = [
|
|
9
|
+
{
|
|
10
|
+
type: "pcb_board",
|
|
11
|
+
pcb_board_id: "board_rect_cutout",
|
|
12
|
+
width: 20,
|
|
13
|
+
height: 20,
|
|
14
|
+
center: { x: 0, y: 0 },
|
|
15
|
+
thickness: 1.6,
|
|
16
|
+
num_layers: 2,
|
|
17
|
+
material: "fr4",
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
type: "pcb_cutout",
|
|
21
|
+
pcb_cutout_id: "cutout_1",
|
|
22
|
+
shape: "rect",
|
|
23
|
+
center: { x: -5, y: 5 },
|
|
24
|
+
width: 4,
|
|
25
|
+
height: 3,
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
type: "pcb_cutout",
|
|
29
|
+
pcb_cutout_id: "cutout_2",
|
|
30
|
+
shape: "rect",
|
|
31
|
+
center: { x: 5, y: -5 },
|
|
32
|
+
width: 3,
|
|
33
|
+
height: 4,
|
|
34
|
+
rotation: 45, // 45 degrees rotation
|
|
35
|
+
},
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
test("renders rectangular pcb cutouts with through-board cuts", async () => {
|
|
39
|
+
const pcbSvg = await convertCircuitJsonToPcbSvg(circuitJson)
|
|
40
|
+
|
|
41
|
+
const project = convertCircuitJsonToLbrn(circuitJson)
|
|
42
|
+
|
|
43
|
+
const lbrnSvg = await generateLightBurnSvg(project)
|
|
44
|
+
|
|
45
|
+
expect(stackSvgsVertically([pcbSvg, lbrnSvg])).toMatchSvgSnapshot(
|
|
46
|
+
import.meta.filename,
|
|
47
|
+
)
|
|
48
|
+
})
|