circuit-json-to-gerber 0.0.46 → 0.0.48
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/{chunk-WSRKOLQZ.js → chunk-VM5XFQ53.js} +178 -165
- package/dist/chunk-VM5XFQ53.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-WSRKOLQZ.js.map +0 -1
|
@@ -1422,7 +1422,7 @@ var findApertureNumber = (glayer, search_params) => {
|
|
|
1422
1422
|
// package.json
|
|
1423
1423
|
var package_default = {
|
|
1424
1424
|
name: "circuit-json-to-gerber",
|
|
1425
|
-
version: "0.0.
|
|
1425
|
+
version: "0.0.47",
|
|
1426
1426
|
main: "dist/index.js",
|
|
1427
1427
|
type: "module",
|
|
1428
1428
|
scripts: {
|
|
@@ -1447,7 +1447,7 @@ var package_default = {
|
|
|
1447
1447
|
"@types/react-dom": "^19.1.5",
|
|
1448
1448
|
archiver: "^7.0.1",
|
|
1449
1449
|
"bun-match-svg": "^0.0.13",
|
|
1450
|
-
"circuit-json": "^0.0.
|
|
1450
|
+
"circuit-json": "^0.0.378",
|
|
1451
1451
|
commander: "^12.1.0",
|
|
1452
1452
|
"gerber-to-svg": "^4.2.8",
|
|
1453
1453
|
gerberts: "^0.0.3",
|
|
@@ -1542,6 +1542,7 @@ import {
|
|
|
1542
1542
|
} from "transformation-matrix";
|
|
1543
1543
|
var convertSoupToGerberCommands = (soup, opts = {}) => {
|
|
1544
1544
|
opts.flip_y_axis ??= false;
|
|
1545
|
+
const hasPanel = soup.some((e) => e.type === "pcb_panel");
|
|
1545
1546
|
const glayers = {
|
|
1546
1547
|
F_Cu: getCommandHeaders({
|
|
1547
1548
|
layer: "top",
|
|
@@ -1771,6 +1772,179 @@ var convertSoupToGerberCommands = (soup, opts = {}) => {
|
|
|
1771
1772
|
);
|
|
1772
1773
|
}
|
|
1773
1774
|
};
|
|
1775
|
+
const drawRingToBuilder = (builder, ring) => {
|
|
1776
|
+
if (!ring || ring.vertices.length === 0) return;
|
|
1777
|
+
builder.add("move_operation", {
|
|
1778
|
+
x: ring.vertices[0].x,
|
|
1779
|
+
y: mfy(ring.vertices[0].y)
|
|
1780
|
+
});
|
|
1781
|
+
const vertices_loop = [...ring.vertices, ring.vertices[0]];
|
|
1782
|
+
for (let i = 0; i < vertices_loop.length - 1; i++) {
|
|
1783
|
+
const start = vertices_loop[i];
|
|
1784
|
+
const end = vertices_loop[i + 1];
|
|
1785
|
+
if (start.bulge && Math.abs(start.bulge) > 1e-9) {
|
|
1786
|
+
const bulge = (opts.flip_y_axis ? -1 : 1) * start.bulge;
|
|
1787
|
+
if (bulge > 0) {
|
|
1788
|
+
builder.add("set_movement_mode_to_counterclockwise_circular", {});
|
|
1789
|
+
} else {
|
|
1790
|
+
builder.add("set_movement_mode_to_clockwise_circular", {});
|
|
1791
|
+
}
|
|
1792
|
+
const p1 = { x: start.x, y: start.y };
|
|
1793
|
+
const p2 = { x: end.x, y: end.y };
|
|
1794
|
+
const dx = p2.x - p1.x;
|
|
1795
|
+
const dy = p2.y - p1.y;
|
|
1796
|
+
const d = Math.sqrt(dx * dx + dy * dy);
|
|
1797
|
+
if (d < 1e-9) {
|
|
1798
|
+
builder.add("set_movement_mode_to_linear", {});
|
|
1799
|
+
builder.add("plot_operation", {
|
|
1800
|
+
x: end.x,
|
|
1801
|
+
y: mfy(end.y)
|
|
1802
|
+
});
|
|
1803
|
+
continue;
|
|
1804
|
+
}
|
|
1805
|
+
const abs_b = Math.abs(bulge);
|
|
1806
|
+
const theta = 4 * Math.atan(abs_b);
|
|
1807
|
+
if (Math.abs(Math.sin(theta / 2)) < 1e-9) {
|
|
1808
|
+
builder.add("set_movement_mode_to_linear", {});
|
|
1809
|
+
builder.add("plot_operation", {
|
|
1810
|
+
x: end.x,
|
|
1811
|
+
y: mfy(end.y)
|
|
1812
|
+
});
|
|
1813
|
+
continue;
|
|
1814
|
+
}
|
|
1815
|
+
const R = d / (2 * Math.sin(theta / 2));
|
|
1816
|
+
const alpha = (Math.PI - theta) / 2;
|
|
1817
|
+
const phi = Math.atan2(dy, dx);
|
|
1818
|
+
const delta_angle = bulge > 0 ? alpha : -alpha;
|
|
1819
|
+
const angle_p1_center = phi + delta_angle;
|
|
1820
|
+
const Cx = p1.x + R * Math.cos(angle_p1_center);
|
|
1821
|
+
const Cy = p1.y + R * Math.sin(angle_p1_center);
|
|
1822
|
+
const i_offset = Cx - p1.x;
|
|
1823
|
+
const j_offset = Cy - p1.y;
|
|
1824
|
+
builder.add("plot_operation", {
|
|
1825
|
+
x: end.x,
|
|
1826
|
+
y: mfy(end.y),
|
|
1827
|
+
i: i_offset,
|
|
1828
|
+
j: opts.flip_y_axis ? -j_offset : j_offset
|
|
1829
|
+
});
|
|
1830
|
+
} else {
|
|
1831
|
+
builder.add("set_movement_mode_to_linear", {});
|
|
1832
|
+
builder.add("plot_operation", { x: end.x, y: mfy(end.y) });
|
|
1833
|
+
}
|
|
1834
|
+
}
|
|
1835
|
+
};
|
|
1836
|
+
const reverseRing = (ring) => {
|
|
1837
|
+
const { vertices } = ring;
|
|
1838
|
+
if (!vertices || vertices.length === 0) return { vertices: [] };
|
|
1839
|
+
const n = vertices.length;
|
|
1840
|
+
const reversed_pts = [...vertices].reverse();
|
|
1841
|
+
const new_vertices = reversed_pts.map((pt, i) => {
|
|
1842
|
+
const bulge_v_idx = (n - 2 - i + n) % n;
|
|
1843
|
+
const bulge = vertices[bulge_v_idx].bulge;
|
|
1844
|
+
return { ...pt, bulge: bulge ? -bulge : void 0 };
|
|
1845
|
+
});
|
|
1846
|
+
return { vertices: new_vertices };
|
|
1847
|
+
};
|
|
1848
|
+
for (const layer of ["top", "bottom"]) {
|
|
1849
|
+
for (const element of soup) {
|
|
1850
|
+
if (element.type === "pcb_copper_pour" && layer === element.layer) {
|
|
1851
|
+
const copper_glayer = glayers[getGerberLayerName(layer, "copper")];
|
|
1852
|
+
const all_pour_commands = [];
|
|
1853
|
+
if (element.shape === "brep") {
|
|
1854
|
+
const { brep_shape } = element;
|
|
1855
|
+
if (!brep_shape) continue;
|
|
1856
|
+
const { outer_ring, inner_rings } = brep_shape;
|
|
1857
|
+
const outer_builder = gerberBuilder().add("select_aperture", { aperture_number: 10 }).add("start_region_statement", {});
|
|
1858
|
+
drawRingToBuilder(outer_builder, reverseRing(outer_ring));
|
|
1859
|
+
outer_builder.add("end_region_statement", {});
|
|
1860
|
+
all_pour_commands.push(...outer_builder.build());
|
|
1861
|
+
if (inner_rings && inner_rings.length > 0) {
|
|
1862
|
+
all_pour_commands.push(
|
|
1863
|
+
...gerberBuilder().add("set_layer_polarity", { polarity: "C" }).build()
|
|
1864
|
+
);
|
|
1865
|
+
for (const inner_ring of inner_rings) {
|
|
1866
|
+
const inner_builder = gerberBuilder().add("select_aperture", { aperture_number: 10 }).add("start_region_statement", {});
|
|
1867
|
+
drawRingToBuilder(inner_builder, inner_ring);
|
|
1868
|
+
inner_builder.add("end_region_statement", {});
|
|
1869
|
+
all_pour_commands.push(...inner_builder.build());
|
|
1870
|
+
}
|
|
1871
|
+
all_pour_commands.push(
|
|
1872
|
+
...gerberBuilder().add("set_layer_polarity", { polarity: "D" }).build()
|
|
1873
|
+
);
|
|
1874
|
+
}
|
|
1875
|
+
} else if (element.shape === "rect") {
|
|
1876
|
+
const { center, width, height, rotation } = element;
|
|
1877
|
+
const w = width / 2;
|
|
1878
|
+
const h = height / 2;
|
|
1879
|
+
const points = [
|
|
1880
|
+
{ x: -w, y: h },
|
|
1881
|
+
// Top-left
|
|
1882
|
+
{ x: w, y: h },
|
|
1883
|
+
// Top-right
|
|
1884
|
+
{ x: w, y: -h },
|
|
1885
|
+
// Bottom-right
|
|
1886
|
+
{ x: -w, y: -h }
|
|
1887
|
+
// Bottom-left
|
|
1888
|
+
];
|
|
1889
|
+
let transformMatrix = identity();
|
|
1890
|
+
if (rotation) {
|
|
1891
|
+
const angle_rad = rotation * Math.PI / 180;
|
|
1892
|
+
transformMatrix = rotate(angle_rad);
|
|
1893
|
+
}
|
|
1894
|
+
transformMatrix = compose(
|
|
1895
|
+
translate(center.x, center.y),
|
|
1896
|
+
transformMatrix
|
|
1897
|
+
);
|
|
1898
|
+
const transformedPoints = points.map(
|
|
1899
|
+
(p) => applyToPoint(transformMatrix, p)
|
|
1900
|
+
);
|
|
1901
|
+
const rect_builder = gerberBuilder().add("select_aperture", { aperture_number: 10 }).add("start_region_statement", {});
|
|
1902
|
+
rect_builder.add("move_operation", {
|
|
1903
|
+
x: transformedPoints[0].x,
|
|
1904
|
+
y: mfy(transformedPoints[0].y)
|
|
1905
|
+
});
|
|
1906
|
+
for (let i = 1; i < transformedPoints.length; i++) {
|
|
1907
|
+
rect_builder.add("plot_operation", {
|
|
1908
|
+
x: transformedPoints[i].x,
|
|
1909
|
+
y: mfy(transformedPoints[i].y)
|
|
1910
|
+
});
|
|
1911
|
+
}
|
|
1912
|
+
rect_builder.add("plot_operation", {
|
|
1913
|
+
x: transformedPoints[0].x,
|
|
1914
|
+
y: mfy(transformedPoints[0].y)
|
|
1915
|
+
});
|
|
1916
|
+
rect_builder.add("end_region_statement", {});
|
|
1917
|
+
all_pour_commands.push(...rect_builder.build());
|
|
1918
|
+
} else if (element.shape === "polygon") {
|
|
1919
|
+
const { points } = element;
|
|
1920
|
+
if (points.length > 0) {
|
|
1921
|
+
const poly_builder = gerberBuilder().add("select_aperture", { aperture_number: 10 }).add("start_region_statement", {});
|
|
1922
|
+
poly_builder.add("move_operation", {
|
|
1923
|
+
x: points[0].x,
|
|
1924
|
+
y: mfy(points[0].y)
|
|
1925
|
+
});
|
|
1926
|
+
for (let i = 1; i < points.length; i++) {
|
|
1927
|
+
poly_builder.add("plot_operation", {
|
|
1928
|
+
x: points[i].x,
|
|
1929
|
+
y: mfy(points[i].y)
|
|
1930
|
+
});
|
|
1931
|
+
}
|
|
1932
|
+
poly_builder.add("plot_operation", {
|
|
1933
|
+
x: points[0].x,
|
|
1934
|
+
y: mfy(points[0].y)
|
|
1935
|
+
});
|
|
1936
|
+
poly_builder.add("end_region_statement", {});
|
|
1937
|
+
all_pour_commands.push(...poly_builder.build());
|
|
1938
|
+
}
|
|
1939
|
+
}
|
|
1940
|
+
copper_glayer.push(...all_pour_commands);
|
|
1941
|
+
if (element.covered_with_solder_mask === false) {
|
|
1942
|
+
const mask_layer = glayers[getGerberLayerName(layer, "soldermask")];
|
|
1943
|
+
mask_layer.push(...all_pour_commands);
|
|
1944
|
+
}
|
|
1945
|
+
}
|
|
1946
|
+
}
|
|
1947
|
+
}
|
|
1774
1948
|
for (const layer of ["top", "bottom", "edgecut"]) {
|
|
1775
1949
|
for (const element of soup) {
|
|
1776
1950
|
if (element.type === "pcb_trace") {
|
|
@@ -2044,10 +2218,7 @@ var convertSoupToGerberCommands = (soup, opts = {}) => {
|
|
|
2044
2218
|
}
|
|
2045
2219
|
}
|
|
2046
2220
|
} else if (element.type === "pcb_board" && layer === "edgecut") {
|
|
2047
|
-
|
|
2048
|
-
if (board.pcb_panel_id) {
|
|
2049
|
-
continue;
|
|
2050
|
-
}
|
|
2221
|
+
if (hasPanel) continue;
|
|
2051
2222
|
const glayer = glayers.Edge_Cuts;
|
|
2052
2223
|
const { width, height, center, outline } = element;
|
|
2053
2224
|
const gerberBuild = gerberBuilder().add("select_aperture", {
|
|
@@ -2253,164 +2424,6 @@ var convertSoupToGerberCommands = (soup, opts = {}) => {
|
|
|
2253
2424
|
}
|
|
2254
2425
|
ec_layer.push(...cutout_builder.build());
|
|
2255
2426
|
}
|
|
2256
|
-
} else if (element.type === "pcb_copper_pour" && layer === element.layer) {
|
|
2257
|
-
const copper_glayer = glayers[getGerberLayerName(layer, "copper")];
|
|
2258
|
-
const pour_builder = gerberBuilder().add("select_aperture", { aperture_number: 10 }).add("start_region_statement", {});
|
|
2259
|
-
if (element.shape === "brep") {
|
|
2260
|
-
const { brep_shape } = element;
|
|
2261
|
-
if (!brep_shape) continue;
|
|
2262
|
-
const { outer_ring, inner_rings } = brep_shape;
|
|
2263
|
-
const drawRing = (ring) => {
|
|
2264
|
-
if (!ring || ring.vertices.length === 0) return;
|
|
2265
|
-
pour_builder.add("move_operation", {
|
|
2266
|
-
x: ring.vertices[0].x,
|
|
2267
|
-
y: mfy(ring.vertices[0].y)
|
|
2268
|
-
});
|
|
2269
|
-
const vertices_loop = [...ring.vertices, ring.vertices[0]];
|
|
2270
|
-
for (let i = 0; i < vertices_loop.length - 1; i++) {
|
|
2271
|
-
const start = vertices_loop[i];
|
|
2272
|
-
const end = vertices_loop[i + 1];
|
|
2273
|
-
if (start.bulge && Math.abs(start.bulge) > 1e-9) {
|
|
2274
|
-
const bulge = (opts.flip_y_axis ? -1 : 1) * start.bulge;
|
|
2275
|
-
if (bulge > 0) {
|
|
2276
|
-
pour_builder.add(
|
|
2277
|
-
"set_movement_mode_to_counterclockwise_circular",
|
|
2278
|
-
{}
|
|
2279
|
-
);
|
|
2280
|
-
} else {
|
|
2281
|
-
pour_builder.add(
|
|
2282
|
-
"set_movement_mode_to_clockwise_circular",
|
|
2283
|
-
{}
|
|
2284
|
-
);
|
|
2285
|
-
}
|
|
2286
|
-
const p1 = { x: start.x, y: start.y };
|
|
2287
|
-
const p2 = { x: end.x, y: end.y };
|
|
2288
|
-
const dx = p2.x - p1.x;
|
|
2289
|
-
const dy = p2.y - p1.y;
|
|
2290
|
-
const d = Math.sqrt(dx * dx + dy * dy);
|
|
2291
|
-
if (d < 1e-9) {
|
|
2292
|
-
pour_builder.add("set_movement_mode_to_linear", {});
|
|
2293
|
-
pour_builder.add("plot_operation", {
|
|
2294
|
-
x: end.x,
|
|
2295
|
-
y: mfy(end.y)
|
|
2296
|
-
});
|
|
2297
|
-
continue;
|
|
2298
|
-
}
|
|
2299
|
-
const abs_b = Math.abs(bulge);
|
|
2300
|
-
const theta = 4 * Math.atan(abs_b);
|
|
2301
|
-
if (Math.abs(Math.sin(theta / 2)) < 1e-9) {
|
|
2302
|
-
pour_builder.add("set_movement_mode_to_linear", {});
|
|
2303
|
-
pour_builder.add("plot_operation", {
|
|
2304
|
-
x: end.x,
|
|
2305
|
-
y: mfy(end.y)
|
|
2306
|
-
});
|
|
2307
|
-
continue;
|
|
2308
|
-
}
|
|
2309
|
-
const R = d / (2 * Math.sin(theta / 2));
|
|
2310
|
-
const alpha = (Math.PI - theta) / 2;
|
|
2311
|
-
const phi = Math.atan2(dy, dx);
|
|
2312
|
-
const delta_angle = bulge > 0 ? alpha : -alpha;
|
|
2313
|
-
const angle_p1_center = phi + delta_angle;
|
|
2314
|
-
const Cx = p1.x + R * Math.cos(angle_p1_center);
|
|
2315
|
-
const Cy = p1.y + R * Math.sin(angle_p1_center);
|
|
2316
|
-
const i_offset = Cx - p1.x;
|
|
2317
|
-
const j_offset = Cy - p1.y;
|
|
2318
|
-
pour_builder.add("plot_operation", {
|
|
2319
|
-
x: end.x,
|
|
2320
|
-
y: mfy(end.y),
|
|
2321
|
-
i: i_offset,
|
|
2322
|
-
j: opts.flip_y_axis ? -j_offset : j_offset
|
|
2323
|
-
});
|
|
2324
|
-
} else {
|
|
2325
|
-
pour_builder.add("set_movement_mode_to_linear", {});
|
|
2326
|
-
pour_builder.add("plot_operation", { x: end.x, y: mfy(end.y) });
|
|
2327
|
-
}
|
|
2328
|
-
}
|
|
2329
|
-
};
|
|
2330
|
-
const reverseRing = (ring) => {
|
|
2331
|
-
const { vertices } = ring;
|
|
2332
|
-
if (!vertices || vertices.length === 0) return { vertices: [] };
|
|
2333
|
-
const n = vertices.length;
|
|
2334
|
-
const reversed_pts = [...vertices].reverse();
|
|
2335
|
-
const new_vertices = reversed_pts.map((pt, i) => {
|
|
2336
|
-
const bulge_v_idx = (n - 2 - i + n) % n;
|
|
2337
|
-
const bulge = vertices[bulge_v_idx].bulge;
|
|
2338
|
-
return { ...pt, bulge: bulge ? -bulge : void 0 };
|
|
2339
|
-
});
|
|
2340
|
-
return { vertices: new_vertices };
|
|
2341
|
-
};
|
|
2342
|
-
drawRing(reverseRing(outer_ring));
|
|
2343
|
-
if (inner_rings) {
|
|
2344
|
-
for (const inner_ring of inner_rings) {
|
|
2345
|
-
drawRing(reverseRing(inner_ring));
|
|
2346
|
-
}
|
|
2347
|
-
}
|
|
2348
|
-
} else if (element.shape === "rect") {
|
|
2349
|
-
const { center, width, height, rotation } = element;
|
|
2350
|
-
const w = width / 2;
|
|
2351
|
-
const h = height / 2;
|
|
2352
|
-
const points = [
|
|
2353
|
-
{ x: -w, y: h },
|
|
2354
|
-
// Top-left
|
|
2355
|
-
{ x: w, y: h },
|
|
2356
|
-
// Top-right
|
|
2357
|
-
{ x: w, y: -h },
|
|
2358
|
-
// Bottom-right
|
|
2359
|
-
{ x: -w, y: -h }
|
|
2360
|
-
// Bottom-left
|
|
2361
|
-
];
|
|
2362
|
-
let transformMatrix = identity();
|
|
2363
|
-
if (rotation) {
|
|
2364
|
-
const angle_rad = rotation * Math.PI / 180;
|
|
2365
|
-
transformMatrix = rotate(angle_rad);
|
|
2366
|
-
}
|
|
2367
|
-
transformMatrix = compose(
|
|
2368
|
-
translate(center.x, center.y),
|
|
2369
|
-
transformMatrix
|
|
2370
|
-
);
|
|
2371
|
-
const transformedPoints = points.map(
|
|
2372
|
-
(p) => applyToPoint(transformMatrix, p)
|
|
2373
|
-
);
|
|
2374
|
-
pour_builder.add("move_operation", {
|
|
2375
|
-
x: transformedPoints[0].x,
|
|
2376
|
-
y: mfy(transformedPoints[0].y)
|
|
2377
|
-
});
|
|
2378
|
-
for (let i = 1; i < transformedPoints.length; i++) {
|
|
2379
|
-
pour_builder.add("plot_operation", {
|
|
2380
|
-
x: transformedPoints[i].x,
|
|
2381
|
-
y: mfy(transformedPoints[i].y)
|
|
2382
|
-
});
|
|
2383
|
-
}
|
|
2384
|
-
pour_builder.add("plot_operation", {
|
|
2385
|
-
x: transformedPoints[0].x,
|
|
2386
|
-
y: mfy(transformedPoints[0].y)
|
|
2387
|
-
});
|
|
2388
|
-
} else if (element.shape === "polygon") {
|
|
2389
|
-
const { points } = element;
|
|
2390
|
-
if (points.length > 0) {
|
|
2391
|
-
pour_builder.add("move_operation", {
|
|
2392
|
-
x: points[0].x,
|
|
2393
|
-
y: mfy(points[0].y)
|
|
2394
|
-
});
|
|
2395
|
-
for (let i = 1; i < points.length; i++) {
|
|
2396
|
-
pour_builder.add("plot_operation", {
|
|
2397
|
-
x: points[i].x,
|
|
2398
|
-
y: mfy(points[i].y)
|
|
2399
|
-
});
|
|
2400
|
-
}
|
|
2401
|
-
pour_builder.add("plot_operation", {
|
|
2402
|
-
x: points[0].x,
|
|
2403
|
-
y: mfy(points[0].y)
|
|
2404
|
-
});
|
|
2405
|
-
}
|
|
2406
|
-
}
|
|
2407
|
-
pour_builder.add("end_region_statement", {});
|
|
2408
|
-
const pour_commands = pour_builder.build();
|
|
2409
|
-
copper_glayer.push(...pour_commands);
|
|
2410
|
-
if (element.covered_with_solder_mask === false) {
|
|
2411
|
-
const mask_layer = glayers[getGerberLayerName(layer, "soldermask")];
|
|
2412
|
-
mask_layer.push(...pour_commands);
|
|
2413
|
-
}
|
|
2414
2427
|
}
|
|
2415
2428
|
}
|
|
2416
2429
|
}
|
|
@@ -2432,4 +2445,4 @@ export {
|
|
|
2432
2445
|
stringifyGerberCommands,
|
|
2433
2446
|
convertSoupToGerberCommands
|
|
2434
2447
|
};
|
|
2435
|
-
//# sourceMappingURL=chunk-
|
|
2448
|
+
//# sourceMappingURL=chunk-VM5XFQ53.js.map
|