circuit-json-to-gerber 0.0.45 → 0.0.47

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.
@@ -1225,6 +1225,16 @@ var getApertureConfigFromPcbSilkscreenText = (elm) => {
1225
1225
  }
1226
1226
  throw new Error(`Provide font_size for: ${elm}`);
1227
1227
  };
1228
+ var getApertureConfigFromPcbCopperText = (elm) => {
1229
+ if ("font_size" in elm) {
1230
+ return {
1231
+ standard_template_code: "C",
1232
+ diameter: elm.font_size / 8
1233
+ // font size and diamater have different units of measurement
1234
+ };
1235
+ }
1236
+ throw new Error(`Provide font_size for: ${elm}`);
1237
+ };
1228
1238
  var getApertureConfigFromPcbSolderPaste = (elm) => {
1229
1239
  if (elm.shape === "rect") {
1230
1240
  return {
@@ -1378,6 +1388,10 @@ function getAllApertureTemplateConfigsForLayer(soup, layer) {
1378
1388
  addConfigIfNew(getApertureConfigFromPcbSilkscreenPath(elm));
1379
1389
  else if (elm.type === "pcb_silkscreen_text")
1380
1390
  addConfigIfNew(getApertureConfigFromPcbSilkscreenText(elm));
1391
+ else if (elm.type === "pcb_copper_text") {
1392
+ if (elm.layer === layer)
1393
+ addConfigIfNew(getApertureConfigFromPcbCopperText(elm));
1394
+ }
1381
1395
  }
1382
1396
  return configs;
1383
1397
  }
@@ -1408,7 +1422,7 @@ var findApertureNumber = (glayer, search_params) => {
1408
1422
  // package.json
1409
1423
  var package_default = {
1410
1424
  name: "circuit-json-to-gerber",
1411
- version: "0.0.44",
1425
+ version: "0.0.46",
1412
1426
  main: "dist/index.js",
1413
1427
  type: "module",
1414
1428
  scripts: {
@@ -1433,7 +1447,7 @@ var package_default = {
1433
1447
  "@types/react-dom": "^19.1.5",
1434
1448
  archiver: "^7.0.1",
1435
1449
  "bun-match-svg": "^0.0.13",
1436
- "circuit-json": "^0.0.317",
1450
+ "circuit-json": "^0.0.378",
1437
1451
  commander: "^12.1.0",
1438
1452
  "gerber-to-svg": "^4.2.8",
1439
1453
  gerberts: "^0.0.3",
@@ -1528,6 +1542,7 @@ import {
1528
1542
  } from "transformation-matrix";
1529
1543
  var convertSoupToGerberCommands = (soup, opts = {}) => {
1530
1544
  opts.flip_y_axis ??= false;
1545
+ const hasPanel = soup.some((e) => e.type === "pcb_panel");
1531
1546
  const glayers = {
1532
1547
  F_Cu: getCommandHeaders({
1533
1548
  layer: "top",
@@ -1599,6 +1614,164 @@ var convertSoupToGerberCommands = (soup, opts = {}) => {
1599
1614
  rotationLookup.set(`${element.x}:${element.y}`, rotation);
1600
1615
  }
1601
1616
  }
1617
+ const renderVectorText = (element, layer, layerType, getApertureConfig) => {
1618
+ if (element.layer !== layer) return;
1619
+ const CAP_HEIGHT_SCALE = 0.7;
1620
+ const glayer = glayers[getGerberLayerName(layer, layerType)];
1621
+ const apertureConfig = getApertureConfig(element);
1622
+ const gerber = gerberBuilder().add("select_aperture", {
1623
+ aperture_number: findApertureNumber(glayer, apertureConfig)
1624
+ });
1625
+ let initialX = element.anchor_position.x;
1626
+ let initialY = element.anchor_position.y;
1627
+ const fontSize = element.font_size * CAP_HEIGHT_SCALE;
1628
+ const letterSpacing = fontSize * 0.4;
1629
+ const spaceWidth = fontSize * 0.5;
1630
+ const textWidth = element.text.split("").reduce((width, char) => {
1631
+ if (char === " ") {
1632
+ return width + spaceWidth + letterSpacing;
1633
+ }
1634
+ return width + fontSize + letterSpacing;
1635
+ }, 0) - letterSpacing;
1636
+ const textHeight = fontSize;
1637
+ const anchorAlignment = element.anchor_alignment || (() => {
1638
+ const side = element.anchor_side;
1639
+ if (!side) return void 0;
1640
+ switch (side) {
1641
+ case "top":
1642
+ return "top_center";
1643
+ case "bottom":
1644
+ return "bottom_center";
1645
+ case "left":
1646
+ return "center_left";
1647
+ case "right":
1648
+ return "center_right";
1649
+ }
1650
+ })() || "center";
1651
+ switch (anchorAlignment) {
1652
+ case "top_left":
1653
+ break;
1654
+ case "top_center":
1655
+ initialX -= textWidth / 2;
1656
+ break;
1657
+ case "top_right":
1658
+ initialX -= textWidth;
1659
+ break;
1660
+ case "center_right":
1661
+ initialY -= textHeight / 2;
1662
+ break;
1663
+ case "center_left":
1664
+ initialX -= textWidth;
1665
+ initialY -= textHeight / 2;
1666
+ break;
1667
+ case "bottom_left":
1668
+ initialY -= textHeight;
1669
+ break;
1670
+ case "bottom_center":
1671
+ initialX -= textWidth / 2;
1672
+ initialY -= textHeight;
1673
+ break;
1674
+ case "bottom_right":
1675
+ initialX -= textWidth;
1676
+ initialY -= textHeight;
1677
+ break;
1678
+ default:
1679
+ initialX -= textWidth / 2;
1680
+ initialY -= textHeight / 2;
1681
+ break;
1682
+ }
1683
+ let anchoredX = initialX;
1684
+ const anchoredY = initialY;
1685
+ let rotation = element.ccw_rotation || 0;
1686
+ const cx = anchoredX + textWidth / 2;
1687
+ const cy = anchoredY + textHeight / 2;
1688
+ const transforms = [];
1689
+ const shouldMirror = element.is_mirrored !== void 0 ? element.is_mirrored : element.layer === "bottom";
1690
+ if (shouldMirror) {
1691
+ transforms.push(
1692
+ translate(cx, cy),
1693
+ { a: -1, b: 0, c: 0, d: 1, e: 0, f: 0 },
1694
+ translate(-cx, -cy)
1695
+ );
1696
+ rotation = -rotation;
1697
+ }
1698
+ if (rotation) {
1699
+ const rad = rotation * Math.PI / 180;
1700
+ transforms.push(translate(cx, cy), rotate(rad), translate(-cx, -cy));
1701
+ }
1702
+ const transformMatrix = transforms.length > 0 ? compose(...transforms) : void 0;
1703
+ const applyTransform = (point) => transformMatrix ? applyToPoint(transformMatrix, point) : point;
1704
+ if (layerType === "copper" && element.is_knockout) {
1705
+ const padding = element.knockout_padding ?? {
1706
+ left: 0.2,
1707
+ right: 0.2,
1708
+ top: 0.2,
1709
+ bottom: 0.2
1710
+ };
1711
+ const paddedRect = [
1712
+ { x: initialX - padding.left, y: initialY - padding.top },
1713
+ {
1714
+ x: initialX + textWidth + padding.right,
1715
+ y: initialY - padding.top
1716
+ },
1717
+ {
1718
+ x: initialX + textWidth + padding.right,
1719
+ y: initialY + textHeight + padding.bottom
1720
+ },
1721
+ {
1722
+ x: initialX - padding.left,
1723
+ y: initialY + textHeight + padding.bottom
1724
+ }
1725
+ ].map(applyTransform);
1726
+ glayer.push(
1727
+ ...gerberBuilder().add("select_aperture", {
1728
+ aperture_number: findApertureNumber(glayer, apertureConfig)
1729
+ }).add("start_region_statement", {}).add("move_operation", {
1730
+ x: paddedRect[0].x,
1731
+ y: mfy(paddedRect[0].y)
1732
+ }).add("plot_operation", {
1733
+ x: paddedRect[1].x,
1734
+ y: mfy(paddedRect[1].y)
1735
+ }).add("plot_operation", {
1736
+ x: paddedRect[2].x,
1737
+ y: mfy(paddedRect[2].y)
1738
+ }).add("plot_operation", {
1739
+ x: paddedRect[3].x,
1740
+ y: mfy(paddedRect[3].y)
1741
+ }).add("plot_operation", {
1742
+ x: paddedRect[0].x,
1743
+ y: mfy(paddedRect[0].y)
1744
+ }).add("end_region_statement", {}).build()
1745
+ );
1746
+ glayer.push(
1747
+ ...gerberBuilder().add("set_layer_polarity", { polarity: "C" }).build()
1748
+ );
1749
+ }
1750
+ for (const char of element.text.toUpperCase()) {
1751
+ if (char === " ") {
1752
+ anchoredX += spaceWidth + letterSpacing;
1753
+ continue;
1754
+ }
1755
+ const letterPaths = lineAlphabet[char] || [];
1756
+ for (const path of letterPaths) {
1757
+ const x1 = anchoredX + path.x1 * fontSize;
1758
+ const y1 = anchoredY + path.y1 * fontSize;
1759
+ const x2 = anchoredX + path.x2 * fontSize;
1760
+ const y2 = anchoredY + path.y2 * fontSize;
1761
+ const p1 = applyTransform({ x: x1, y: y1 });
1762
+ const p2 = applyTransform({ x: x2, y: y2 });
1763
+ gerber.add("move_operation", { x: p1.x, y: mfy(p1.y) });
1764
+ gerber.add("plot_operation", { x: p2.x, y: mfy(p2.y) });
1765
+ }
1766
+ anchoredX += fontSize + letterSpacing;
1767
+ }
1768
+ glayer.push(...gerber.build());
1769
+ if (layerType === "copper" && element.is_knockout) {
1770
+ glayer.push(
1771
+ ...gerberBuilder().add("set_layer_polarity", { polarity: "D" }).build()
1772
+ );
1773
+ }
1774
+ };
1602
1775
  for (const layer of ["top", "bottom", "edgecut"]) {
1603
1776
  for (const element of soup) {
1604
1777
  if (element.type === "pcb_trace") {
@@ -1638,123 +1811,20 @@ var convertSoupToGerberCommands = (soup, opts = {}) => {
1638
1811
  }
1639
1812
  glayer.push(...gerber.build());
1640
1813
  }
1641
- } else if (element.type === "pcb_silkscreen_text") {
1642
- if (element.layer === layer) {
1643
- const CAP_HEIGHT_SCALE = 0.7;
1644
- const glayer = glayers[getGerberLayerName(layer, "silkscreen")];
1645
- const apertureConfig = getApertureConfigFromPcbSilkscreenText(element);
1646
- const gerber = gerberBuilder().add("select_aperture", {
1647
- aperture_number: findApertureNumber(glayer, apertureConfig)
1648
- });
1649
- let initialX = element.anchor_position.x;
1650
- let initialY = element.anchor_position.y;
1651
- const fontSize = element.font_size * CAP_HEIGHT_SCALE;
1652
- const letterSpacing = fontSize * 0.4;
1653
- const spaceWidth = fontSize * 0.5;
1654
- const textWidth = element.text.split("").reduce((width, char) => {
1655
- if (char === " ") {
1656
- return width + spaceWidth + letterSpacing;
1657
- }
1658
- return width + fontSize + letterSpacing;
1659
- }, 0) - letterSpacing;
1660
- const textHeight = fontSize;
1661
- const anchorAlignment = element.anchor_alignment || (() => {
1662
- const side = element.anchor_side;
1663
- if (!side) return void 0;
1664
- switch (side) {
1665
- case "top":
1666
- return "top_center";
1667
- case "bottom":
1668
- return "bottom_center";
1669
- case "left":
1670
- return "center_left";
1671
- case "right":
1672
- return "center_right";
1673
- }
1674
- })() || "center";
1675
- switch (anchorAlignment) {
1676
- case "top_left":
1677
- break;
1678
- case "top_center":
1679
- initialX -= textWidth / 2;
1680
- break;
1681
- case "top_right":
1682
- initialX -= textWidth;
1683
- break;
1684
- case "center_right":
1685
- initialY -= textHeight / 2;
1686
- break;
1687
- case "center_left":
1688
- initialX -= textWidth;
1689
- initialY -= textHeight / 2;
1690
- break;
1691
- case "bottom_left":
1692
- initialY -= textHeight;
1693
- break;
1694
- case "bottom_center":
1695
- initialX -= textWidth / 2;
1696
- initialY -= textHeight;
1697
- break;
1698
- case "bottom_right":
1699
- initialX -= textWidth;
1700
- initialY -= textHeight;
1701
- break;
1702
- default:
1703
- initialX -= textWidth / 2;
1704
- initialY -= textHeight / 2;
1705
- break;
1706
- }
1707
- let anchoredX = initialX;
1708
- const anchoredY = initialY;
1709
- let rotation = element.ccw_rotation || 0;
1710
- const cx = anchoredX + textWidth / 2;
1711
- const cy = anchoredY + textHeight / 2;
1712
- const transforms = [];
1713
- if (element.layer === "bottom") {
1714
- transforms.push(
1715
- translate(cx, cy),
1716
- { a: -1, b: 0, c: 0, d: 1, e: 0, f: 0 },
1717
- // Horizontal flip
1718
- translate(-cx, -cy)
1719
- );
1720
- rotation = -rotation;
1721
- }
1722
- if (rotation) {
1723
- const rad = rotation * Math.PI / 180;
1724
- transforms.push(
1725
- translate(cx, cy),
1726
- // Translate to center of rotation
1727
- rotate(rad),
1728
- // Apply rotation
1729
- translate(-cx, -cy)
1730
- // Translate back
1731
- );
1732
- }
1733
- for (const char of element.text.toUpperCase()) {
1734
- if (char === " ") {
1735
- anchoredX += spaceWidth + letterSpacing;
1736
- continue;
1737
- }
1738
- const letterPaths = lineAlphabet[char] || [];
1739
- for (const path of letterPaths) {
1740
- const x1 = anchoredX + path.x1 * fontSize;
1741
- const y1 = anchoredY + path.y1 * fontSize;
1742
- const x2 = anchoredX + path.x2 * fontSize;
1743
- const y2 = anchoredY + path.y2 * fontSize;
1744
- let p1 = { x: x1, y: y1 };
1745
- let p2 = { x: x2, y: y2 };
1746
- if (transforms.length > 0) {
1747
- const transformMatrix = compose(...transforms);
1748
- p1 = applyToPoint(transformMatrix, p1);
1749
- p2 = applyToPoint(transformMatrix, p2);
1750
- }
1751
- gerber.add("move_operation", { x: p1.x, y: mfy(p1.y) });
1752
- gerber.add("plot_operation", { x: p2.x, y: mfy(p2.y) });
1753
- }
1754
- anchoredX += fontSize + letterSpacing;
1755
- }
1756
- glayer.push(...gerber.build());
1757
- }
1814
+ } else if (element.type === "pcb_silkscreen_text" && layer !== "edgecut") {
1815
+ renderVectorText(
1816
+ element,
1817
+ layer,
1818
+ "silkscreen",
1819
+ getApertureConfigFromPcbSilkscreenText
1820
+ );
1821
+ } else if (element.type === "pcb_copper_text" && layer !== "edgecut") {
1822
+ renderVectorText(
1823
+ element,
1824
+ layer,
1825
+ "copper",
1826
+ getApertureConfigFromPcbCopperText
1827
+ );
1758
1828
  } else if (element.type === "pcb_smtpad" && element.shape !== "polygon") {
1759
1829
  if (element.layer === layer) {
1760
1830
  for (const glayer of [
@@ -1975,10 +2045,7 @@ var convertSoupToGerberCommands = (soup, opts = {}) => {
1975
2045
  }
1976
2046
  }
1977
2047
  } else if (element.type === "pcb_board" && layer === "edgecut") {
1978
- const board = element;
1979
- if (board.pcb_panel_id) {
1980
- continue;
1981
- }
2048
+ if (hasPanel) continue;
1982
2049
  const glayer = glayers.Edge_Cuts;
1983
2050
  const { width, height, center, outline } = element;
1984
2051
  const gerberBuild = gerberBuilder().add("select_aperture", {
@@ -2363,4 +2430,4 @@ export {
2363
2430
  stringifyGerberCommands,
2364
2431
  convertSoupToGerberCommands
2365
2432
  };
2366
- //# sourceMappingURL=chunk-4KGM4B43.js.map
2433
+ //# sourceMappingURL=chunk-LRAY3J4M.js.map