circuit-json-to-gerber 0.0.68 → 0.0.70

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.
@@ -1112,7 +1112,17 @@ var define_aperture_template = defineGerberCommand({
1112
1112
  if (macro_name === "HORZPILL" || macro_name === "VERTPILL") {
1113
1113
  commandString += `${props.x_size.toFixed(6)}X${props.y_size.toFixed(6)}X${props.circle_diameter.toFixed(6)}X${props.circle_center_offset.toFixed(6)}`;
1114
1114
  } else if (macro_name === "ROUNDRECT") {
1115
- throw new Error("ROUNDRECT macro not implemented yet");
1115
+ commandString += [
1116
+ props.corner_radius,
1117
+ props.corner_1_x,
1118
+ props.corner_1_y,
1119
+ props.corner_2_x,
1120
+ props.corner_2_y,
1121
+ props.corner_3_x,
1122
+ props.corner_3_y,
1123
+ props.corner_4_x,
1124
+ props.corner_4_y
1125
+ ].map((value) => value.toFixed(6)).join("X");
1116
1126
  }
1117
1127
  commandString += "*%";
1118
1128
  return commandString;
@@ -1289,7 +1299,7 @@ var defineCommonMacros = (glayer) => {
1289
1299
  1,1,$3,0.0,$4*
1290
1300
  `.trim()
1291
1301
  }).add("define_macro_aperture_template", {
1292
- macro_name: "RoundRect",
1302
+ macro_name: "ROUNDRECT",
1293
1303
  template_code: `
1294
1304
  0 Rectangle with rounded corners*
1295
1305
  0 $1 Corner radius*
@@ -1355,6 +1365,26 @@ var getLayerRefFromGerberLayerName = (glayer_name) => {
1355
1365
  if (innerLayerMatch) return `inner${innerLayerMatch[1]}`;
1356
1366
  throw new Error(`Could not infer layer ref from ${glayer_name}`);
1357
1367
  };
1368
+ var getClampedRoundedRectCornerRadius = (width, height, cornerRadius) => {
1369
+ if (typeof cornerRadius !== "number") return 0;
1370
+ return Math.max(0, Math.min(cornerRadius, width / 2, height / 2));
1371
+ };
1372
+ var getRoundedRectApertureConfig = (width, height, cornerRadius) => {
1373
+ const halfWidth = width / 2;
1374
+ const halfHeight = height / 2;
1375
+ return {
1376
+ macro_name: "ROUNDRECT",
1377
+ corner_radius: cornerRadius,
1378
+ corner_1_x: -halfWidth + cornerRadius,
1379
+ corner_1_y: halfHeight - cornerRadius,
1380
+ corner_2_x: halfWidth - cornerRadius,
1381
+ corner_2_y: halfHeight - cornerRadius,
1382
+ corner_3_x: halfWidth - cornerRadius,
1383
+ corner_3_y: -halfHeight + cornerRadius,
1384
+ corner_4_x: -halfWidth + cornerRadius,
1385
+ corner_4_y: -halfHeight + cornerRadius
1386
+ };
1387
+ };
1358
1388
  function defineAperturesForLayer({
1359
1389
  glayer,
1360
1390
  circuitJson,
@@ -1403,7 +1433,21 @@ function defineAperturesForLayer({
1403
1433
  ...gerberBuilder().add("delete_attribute", {}).add("comment", { comment: "aperture END LIST" }).build()
1404
1434
  );
1405
1435
  }
1436
+ var REGION_APERTURE_CONFIG = {
1437
+ standard_template_code: "C",
1438
+ diameter: 1e-3
1439
+ };
1406
1440
  var getApertureConfigFromPcbSmtpad = (elm) => {
1441
+ if (elm.shape === "rect" || elm.shape === "rotated_rect") {
1442
+ const cornerRadius = getClampedRoundedRectCornerRadius(
1443
+ elm.width,
1444
+ elm.height,
1445
+ "corner_radius" in elm ? elm.corner_radius : void 0
1446
+ );
1447
+ if (cornerRadius > 0) {
1448
+ return getRoundedRectApertureConfig(elm.width, elm.height, cornerRadius);
1449
+ }
1450
+ }
1407
1451
  if (elm.shape === "rect") {
1408
1452
  return {
1409
1453
  standard_template_code: "R",
@@ -1457,6 +1501,18 @@ var getApertureConfigFromPcbSmtpadSoldermask = (elm) => {
1457
1501
  if ("soldermask_margin" in elm && typeof elm.soldermask_margin === "number") {
1458
1502
  soldermaskMargin = elm.soldermask_margin;
1459
1503
  }
1504
+ if (elm.shape === "rect" || elm.shape === "rotated_rect") {
1505
+ const width = elm.width + soldermaskMargin * 2;
1506
+ const height = elm.height + soldermaskMargin * 2;
1507
+ const cornerRadius = getClampedRoundedRectCornerRadius(
1508
+ width,
1509
+ height,
1510
+ "corner_radius" in elm && typeof elm.corner_radius === "number" ? elm.corner_radius + soldermaskMargin : void 0
1511
+ );
1512
+ if (cornerRadius > 0) {
1513
+ return getRoundedRectApertureConfig(width, height, cornerRadius);
1514
+ }
1515
+ }
1460
1516
  if (elm.shape === "rect") {
1461
1517
  return {
1462
1518
  standard_template_code: "R",
@@ -1737,6 +1793,7 @@ function getAllApertureTemplateConfigsForLayer({
1737
1793
  const configs = [];
1738
1794
  const configHashMap = /* @__PURE__ */ new Set();
1739
1795
  const isSoldermaskLayer = glayer_name.endsWith("_Mask");
1796
+ const isCopperLayer = glayer_name.endsWith("_Cu");
1740
1797
  const addConfigIfNew = (config) => {
1741
1798
  const hash = stableStringify(config);
1742
1799
  if (!configHashMap.has(hash)) {
@@ -1768,6 +1825,9 @@ function getAllApertureTemplateConfigsForLayer({
1768
1825
  }
1769
1826
  } else if (elm.type === "pcb_plated_hole") {
1770
1827
  if (elm.layers.includes(layer)) {
1828
+ if (elm.shape === "hole_with_polygon_pad") {
1829
+ continue;
1830
+ }
1771
1831
  if (glayer_name.endsWith("_Mask") && elm.is_covered_with_solder_mask === true) {
1772
1832
  continue;
1773
1833
  }
@@ -1806,6 +1866,27 @@ function getAllApertureTemplateConfigsForLayer({
1806
1866
  addConfigIfNew(getApertureConfigFromPcbCopperText(elm));
1807
1867
  }
1808
1868
  }
1869
+ const needsRegionAperture = circuitJson.some((elm) => {
1870
+ if (elm.type === "pcb_copper_pour") {
1871
+ if (elm.layer !== layer) return false;
1872
+ if (isCopperLayer) return true;
1873
+ return isSoldermaskLayer && elm.covered_with_solder_mask === false;
1874
+ }
1875
+ if (elm.type === "pcb_smtpad" && elm.shape === "polygon") {
1876
+ if (elm.layer !== layer) return false;
1877
+ if (isCopperLayer) return true;
1878
+ return isSoldermaskLayer && elm.is_covered_with_solder_mask !== true;
1879
+ }
1880
+ if (elm.type === "pcb_plated_hole" && elm.shape === "hole_with_polygon_pad") {
1881
+ if (!elm.layers.includes(layer)) return false;
1882
+ if (isCopperLayer) return true;
1883
+ return isSoldermaskLayer && elm.is_covered_with_solder_mask !== true;
1884
+ }
1885
+ return false;
1886
+ });
1887
+ if (needsRegionAperture) {
1888
+ addConfigIfNew(REGION_APERTURE_CONFIG);
1889
+ }
1809
1890
  return configs;
1810
1891
  }
1811
1892
 
@@ -1835,7 +1916,7 @@ var findApertureNumber = (glayer, search_params) => {
1835
1916
  // package.json
1836
1917
  var package_default = {
1837
1918
  name: "circuit-json-to-gerber",
1838
- version: "0.0.67",
1919
+ version: "0.0.68",
1839
1920
  main: "dist/index.js",
1840
1921
  type: "module",
1841
1922
  scripts: {
@@ -2068,6 +2149,34 @@ var convertSoupToGerberCommands = (circuitJson, opts = {}) => {
2068
2149
  }).build()
2069
2150
  );
2070
2151
  const mfy = (y) => opts.flip_y_axis ? -y : y;
2152
+ const getRegionApertureNumber = (glayer) => findApertureNumber(glayer, REGION_APERTURE_CONFIG);
2153
+ const addClosedRegionFromPoints = ({
2154
+ target,
2155
+ apertureSource,
2156
+ points
2157
+ }) => {
2158
+ if (points.length === 0) return;
2159
+ const regionApertureNumber = getRegionApertureNumber(apertureSource);
2160
+ const regionBuilder = gerberBuilder().add("select_aperture", {
2161
+ aperture_number: regionApertureNumber
2162
+ }).add("start_region_statement", {});
2163
+ regionBuilder.add("move_operation", {
2164
+ x: points[0].x,
2165
+ y: mfy(points[0].y)
2166
+ });
2167
+ for (let i = 1; i < points.length; i++) {
2168
+ regionBuilder.add("plot_operation", {
2169
+ x: points[i].x,
2170
+ y: mfy(points[i].y)
2171
+ });
2172
+ }
2173
+ regionBuilder.add("plot_operation", {
2174
+ x: points[0].x,
2175
+ y: mfy(points[0].y)
2176
+ });
2177
+ regionBuilder.add("end_region_statement", {});
2178
+ target.push(...regionBuilder.build());
2179
+ };
2071
2180
  const renderVectorText = (element, layer, layerType, getApertureConfig) => {
2072
2181
  if (element.layer !== layer) return;
2073
2182
  const CAP_HEIGHT_SCALE = 0.7;
@@ -2308,7 +2417,8 @@ var convertSoupToGerberCommands = (circuitJson, opts = {}) => {
2308
2417
  const { brep_shape } = element;
2309
2418
  if (!brep_shape) continue;
2310
2419
  const { outer_ring, inner_rings } = brep_shape;
2311
- const outer_builder = gerberBuilder().add("select_aperture", { aperture_number: 10 }).add("start_region_statement", {});
2420
+ const regionApertureNumber = getRegionApertureNumber(copper_glayer);
2421
+ const outer_builder = gerberBuilder().add("select_aperture", { aperture_number: regionApertureNumber }).add("start_region_statement", {});
2312
2422
  drawRingToBuilder(outer_builder, reverseRing(outer_ring));
2313
2423
  outer_builder.add("end_region_statement", {});
2314
2424
  all_pour_commands.push(...outer_builder.build());
@@ -2317,7 +2427,9 @@ var convertSoupToGerberCommands = (circuitJson, opts = {}) => {
2317
2427
  ...gerberBuilder().add("set_layer_polarity", { polarity: "C" }).build()
2318
2428
  );
2319
2429
  for (const inner_ring of inner_rings) {
2320
- const inner_builder = gerberBuilder().add("select_aperture", { aperture_number: 10 }).add("start_region_statement", {});
2430
+ const inner_builder = gerberBuilder().add("select_aperture", {
2431
+ aperture_number: regionApertureNumber
2432
+ }).add("start_region_statement", {});
2321
2433
  drawRingToBuilder(inner_builder, inner_ring);
2322
2434
  inner_builder.add("end_region_statement", {});
2323
2435
  all_pour_commands.push(...inner_builder.build());
@@ -2352,7 +2464,8 @@ var convertSoupToGerberCommands = (circuitJson, opts = {}) => {
2352
2464
  const transformedPoints = points.map(
2353
2465
  (p) => applyToPoint(transformMatrix, p)
2354
2466
  );
2355
- const rect_builder = gerberBuilder().add("select_aperture", { aperture_number: 10 }).add("start_region_statement", {});
2467
+ const regionApertureNumber = getRegionApertureNumber(copper_glayer);
2468
+ const rect_builder = gerberBuilder().add("select_aperture", { aperture_number: regionApertureNumber }).add("start_region_statement", {});
2356
2469
  rect_builder.add("move_operation", {
2357
2470
  x: transformedPoints[0].x,
2358
2471
  y: mfy(transformedPoints[0].y)
@@ -2371,25 +2484,11 @@ var convertSoupToGerberCommands = (circuitJson, opts = {}) => {
2371
2484
  all_pour_commands.push(...rect_builder.build());
2372
2485
  } else if (element.shape === "polygon") {
2373
2486
  const { points } = element;
2374
- if (points.length > 0) {
2375
- const poly_builder = gerberBuilder().add("select_aperture", { aperture_number: 10 }).add("start_region_statement", {});
2376
- poly_builder.add("move_operation", {
2377
- x: points[0].x,
2378
- y: mfy(points[0].y)
2379
- });
2380
- for (let i = 1; i < points.length; i++) {
2381
- poly_builder.add("plot_operation", {
2382
- x: points[i].x,
2383
- y: mfy(points[i].y)
2384
- });
2385
- }
2386
- poly_builder.add("plot_operation", {
2387
- x: points[0].x,
2388
- y: mfy(points[0].y)
2389
- });
2390
- poly_builder.add("end_region_statement", {});
2391
- all_pour_commands.push(...poly_builder.build());
2392
- }
2487
+ addClosedRegionFromPoints({
2488
+ target: all_pour_commands,
2489
+ apertureSource: copper_glayer,
2490
+ points
2491
+ });
2393
2492
  }
2394
2493
  copper_glayer.push(...all_pour_commands);
2395
2494
  if (element.covered_with_solder_mask === false) {
@@ -2540,26 +2639,13 @@ var convertSoupToGerberCommands = (circuitJson, opts = {}) => {
2540
2639
  );
2541
2640
  }
2542
2641
  for (const glayer of layers_to_add_to) {
2543
- const pad_builder = gerberBuilder().add("select_aperture", { aperture_number: 10 }).add("start_region_statement", {});
2544
2642
  const { points } = element;
2545
- if (points && points.length > 0) {
2546
- pad_builder.add("move_operation", {
2547
- x: points[0].x,
2548
- y: mfy(points[0].y)
2549
- });
2550
- for (let i = 1; i < points.length; i++) {
2551
- pad_builder.add("plot_operation", {
2552
- x: points[i].x,
2553
- y: mfy(points[i].y)
2554
- });
2555
- }
2556
- pad_builder.add("plot_operation", {
2557
- x: points[0].x,
2558
- y: mfy(points[0].y)
2559
- });
2560
- }
2561
- pad_builder.add("end_region_statement", {});
2562
- glayer.push(...pad_builder.build());
2643
+ if (!points) continue;
2644
+ addClosedRegionFromPoints({
2645
+ target: glayer,
2646
+ apertureSource: glayer,
2647
+ points
2648
+ });
2563
2649
  }
2564
2650
  }
2565
2651
  } else if (element.type === "pcb_solder_paste") {
@@ -2609,6 +2695,16 @@ var convertSoupToGerberCommands = (circuitJson, opts = {}) => {
2609
2695
  layersToAddTo.push(glayers[getGerberLayerName(layer, "soldermask")]);
2610
2696
  }
2611
2697
  for (const glayer of layersToAddTo) {
2698
+ if (element.shape === "hole_with_polygon_pad") {
2699
+ const { pad_outline } = element;
2700
+ if (!pad_outline?.length) continue;
2701
+ addClosedRegionFromPoints({
2702
+ target: glayer,
2703
+ apertureSource: glayer,
2704
+ points: pad_outline
2705
+ });
2706
+ continue;
2707
+ }
2612
2708
  const holeW = "hole_width" in element && typeof element.hole_width === "number" ? element.hole_width : 0;
2613
2709
  const holeH = "hole_height" in element && typeof element.hole_height === "number" ? element.hole_height : 0;
2614
2710
  const padWidthCandidates = [holeW];
@@ -2988,4 +3084,4 @@ export {
2988
3084
  stringifyGerberCommands,
2989
3085
  convertSoupToGerberCommands
2990
3086
  };
2991
- //# sourceMappingURL=chunk-LCX42IEO.js.map
3087
+ //# sourceMappingURL=chunk-ISNPG4RS.js.map