circuit-json-to-gerber 0.0.81 → 0.0.82

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.
@@ -1329,6 +1329,134 @@ var defineCommonMacros = (glayer) => {
1329
1329
  );
1330
1330
  };
1331
1331
 
1332
+ // src/gerber/convert-soup-to-gerber-commands/getSilkscreenShapeStroke.ts
1333
+ import {
1334
+ applyToPoint,
1335
+ compose,
1336
+ identity,
1337
+ rotate,
1338
+ translate
1339
+ } from "transformation-matrix";
1340
+ var SILKSCREEN_SHAPE_TYPES = [
1341
+ "pcb_silkscreen_line",
1342
+ "pcb_silkscreen_rect",
1343
+ "pcb_silkscreen_circle",
1344
+ "pcb_silkscreen_oval",
1345
+ "pcb_silkscreen_pill"
1346
+ ];
1347
+ var isSilkscreenShape = (element) => SILKSCREEN_SHAPE_TYPES.includes(element.type);
1348
+ var DEFAULT_STROKE_WIDTH = 0.1;
1349
+ var ELLIPSE_SEGMENTS = 48;
1350
+ var CORNER_SEGMENTS = 12;
1351
+ var rotationAboutCenter = (center, ccwDegrees) => ccwDegrees ? compose(
1352
+ translate(center.x, center.y),
1353
+ rotate(ccwDegrees * Math.PI / 180),
1354
+ translate(-center.x, -center.y)
1355
+ ) : identity();
1356
+ var ellipseRoute = (center, radiusX, radiusY, ccwDegrees = 0) => {
1357
+ const matrix = rotationAboutCenter(center, ccwDegrees);
1358
+ return Array.from({ length: ELLIPSE_SEGMENTS + 1 }, (_, i) => {
1359
+ const angle = i / ELLIPSE_SEGMENTS * Math.PI * 2;
1360
+ return applyToPoint(matrix, {
1361
+ x: center.x + radiusX * Math.cos(angle),
1362
+ y: center.y + radiusY * Math.sin(angle)
1363
+ });
1364
+ });
1365
+ };
1366
+ var rectangleRoute = (center, width, height, cornerRadius, ccwDegrees = 0) => {
1367
+ const halfW = width / 2;
1368
+ const halfH = height / 2;
1369
+ const route = [];
1370
+ if (cornerRadius <= 0) {
1371
+ route.push(
1372
+ { x: center.x - halfW, y: center.y - halfH },
1373
+ { x: center.x + halfW, y: center.y - halfH },
1374
+ { x: center.x + halfW, y: center.y + halfH },
1375
+ { x: center.x - halfW, y: center.y + halfH }
1376
+ );
1377
+ } else {
1378
+ const insetW = halfW - cornerRadius;
1379
+ const insetH = halfH - cornerRadius;
1380
+ const corners = [
1381
+ { x: center.x + insetW, y: center.y + insetH, start: 0 },
1382
+ { x: center.x - insetW, y: center.y + insetH, start: Math.PI / 2 },
1383
+ { x: center.x - insetW, y: center.y - insetH, start: Math.PI },
1384
+ { x: center.x + insetW, y: center.y - insetH, start: 3 * Math.PI / 2 }
1385
+ ];
1386
+ for (const corner of corners) {
1387
+ for (let i = 0; i <= CORNER_SEGMENTS; i++) {
1388
+ const angle = corner.start + i / CORNER_SEGMENTS * (Math.PI / 2);
1389
+ route.push({
1390
+ x: corner.x + cornerRadius * Math.cos(angle),
1391
+ y: corner.y + cornerRadius * Math.sin(angle)
1392
+ });
1393
+ }
1394
+ }
1395
+ }
1396
+ route.push(route[0]);
1397
+ const matrix = rotationAboutCenter(center, ccwDegrees);
1398
+ return route.map((point) => applyToPoint(matrix, point));
1399
+ };
1400
+ var clampCornerRadius = (width, height, cornerRadius) => {
1401
+ if (typeof cornerRadius !== "number" || cornerRadius <= 0) return 0;
1402
+ return Math.min(cornerRadius, width / 2, height / 2);
1403
+ };
1404
+ var getSilkscreenShapeStroke = (element) => {
1405
+ const strokeWidth = element.stroke_width ?? DEFAULT_STROKE_WIDTH;
1406
+ switch (element.type) {
1407
+ case "pcb_silkscreen_line":
1408
+ return {
1409
+ route: [
1410
+ { x: element.x1, y: element.y1 },
1411
+ { x: element.x2, y: element.y2 }
1412
+ ],
1413
+ strokeWidth
1414
+ };
1415
+ case "pcb_silkscreen_rect":
1416
+ return {
1417
+ route: rectangleRoute(
1418
+ element.center,
1419
+ element.width,
1420
+ element.height,
1421
+ clampCornerRadius(
1422
+ element.width,
1423
+ element.height,
1424
+ element.corner_radius
1425
+ ),
1426
+ element.ccw_rotation ?? 0
1427
+ ),
1428
+ strokeWidth
1429
+ };
1430
+ case "pcb_silkscreen_circle":
1431
+ return {
1432
+ route: ellipseRoute(element.center, element.radius, element.radius),
1433
+ strokeWidth
1434
+ };
1435
+ case "pcb_silkscreen_oval":
1436
+ return {
1437
+ route: ellipseRoute(
1438
+ element.center,
1439
+ element.radius_x,
1440
+ element.radius_y,
1441
+ element.ccw_rotation ?? 0
1442
+ ),
1443
+ strokeWidth
1444
+ };
1445
+ case "pcb_silkscreen_pill":
1446
+ return {
1447
+ route: rectangleRoute(
1448
+ element.center,
1449
+ element.width,
1450
+ element.height,
1451
+ Math.min(element.width, element.height) / 2
1452
+ ),
1453
+ strokeWidth
1454
+ };
1455
+ default:
1456
+ return null;
1457
+ }
1458
+ };
1459
+
1332
1460
  // src/gerber/convert-soup-to-gerber-commands/defineAperturesForLayer.ts
1333
1461
  import stableStringify from "fast-json-stable-stringify";
1334
1462
 
@@ -1878,6 +2006,20 @@ function getAllApertureTemplateConfigsForLayer({
1878
2006
  } else if (elm.type === "pcb_silkscreen_path") {
1879
2007
  if (!isFabricationLayer)
1880
2008
  addConfigIfNew(getApertureConfigFromPcbSilkscreenPath(elm));
2009
+ } else if (isSilkscreenShape(elm)) {
2010
+ if (!isFabricationLayer) {
2011
+ addConfigIfNew({
2012
+ standard_template_code: "C",
2013
+ diameter: elm.stroke_width ?? 0.1
2014
+ });
2015
+ if (elm.is_filled === true) {
2016
+ addConfigIfNew(REGION_APERTURE_CONFIG);
2017
+ }
2018
+ }
2019
+ } else if (elm.type === "pcb_silkscreen_graphic") {
2020
+ if (glayer_name.endsWith("_SilkScreen")) {
2021
+ addConfigIfNew(REGION_APERTURE_CONFIG);
2022
+ }
1881
2023
  } else if (elm.type === "pcb_silkscreen_text") {
1882
2024
  if (!isFabricationLayer)
1883
2025
  addConfigIfNew(getApertureConfigFromPcbSilkscreenText(elm));
@@ -1953,7 +2095,7 @@ var findApertureNumber = (glayer, search_params) => {
1953
2095
  // package.json
1954
2096
  var package_default = {
1955
2097
  name: "circuit-json-to-gerber",
1956
- version: "0.0.80",
2098
+ version: "0.0.81",
1957
2099
  main: "dist/index.js",
1958
2100
  type: "module",
1959
2101
  scripts: {
@@ -2164,11 +2306,11 @@ var offsetPolygonOutline = (points, offset) => {
2164
2306
  // src/gerber/convert-soup-to-gerber-commands/index.ts
2165
2307
  import { lineAlphabet } from "@tscircuit/alphabet";
2166
2308
  import {
2167
- applyToPoint,
2168
- compose,
2169
- identity,
2170
- rotate,
2171
- translate
2309
+ applyToPoint as applyToPoint2,
2310
+ compose as compose2,
2311
+ identity as identity2,
2312
+ rotate as rotate2,
2313
+ translate as translate2
2172
2314
  } from "transformation-matrix";
2173
2315
 
2174
2316
  // src/gerber/convert-soup-to-gerber-commands/fabricationLayerRefs.ts
@@ -2597,18 +2739,18 @@ var convertSoupToGerberCommands = (circuitJson, opts = {}) => {
2597
2739
  const shouldMirror = element.is_mirrored !== void 0 ? element.is_mirrored : element.layer === "bottom";
2598
2740
  if (shouldMirror) {
2599
2741
  transforms.push(
2600
- translate(cx, cy),
2742
+ translate2(cx, cy),
2601
2743
  { a: -1, b: 0, c: 0, d: 1, e: 0, f: 0 },
2602
- translate(-cx, -cy)
2744
+ translate2(-cx, -cy)
2603
2745
  );
2604
2746
  rotation = -rotation;
2605
2747
  }
2606
2748
  if (rotation) {
2607
2749
  const rad = rotation * Math.PI / 180;
2608
- transforms.push(translate(cx, cy), rotate(rad), translate(-cx, -cy));
2750
+ transforms.push(translate2(cx, cy), rotate2(rad), translate2(-cx, -cy));
2609
2751
  }
2610
- const transformMatrix = transforms.length > 0 ? compose(...transforms) : void 0;
2611
- const applyTransform = (point) => transformMatrix ? applyToPoint(transformMatrix, point) : point;
2752
+ const transformMatrix = transforms.length > 0 ? compose2(...transforms) : void 0;
2753
+ const applyTransform = (point) => transformMatrix ? applyToPoint2(transformMatrix, point) : point;
2612
2754
  if (element.is_knockout) {
2613
2755
  const padding = element.knockout_padding ?? {
2614
2756
  left: 0.2,
@@ -2797,17 +2939,17 @@ var convertSoupToGerberCommands = (circuitJson, opts = {}) => {
2797
2939
  { x: -w, y: -h }
2798
2940
  // Bottom-left
2799
2941
  ];
2800
- let transformMatrix = identity();
2942
+ let transformMatrix = identity2();
2801
2943
  if (rotation) {
2802
2944
  const angle_rad = rotation * Math.PI / 180;
2803
- transformMatrix = rotate(angle_rad);
2945
+ transformMatrix = rotate2(angle_rad);
2804
2946
  }
2805
- transformMatrix = compose(
2806
- translate(center.x, center.y),
2947
+ transformMatrix = compose2(
2948
+ translate2(center.x, center.y),
2807
2949
  transformMatrix
2808
2950
  );
2809
2951
  const transformedPoints = points.map(
2810
- (p) => applyToPoint(transformMatrix, p)
2952
+ (p) => applyToPoint2(transformMatrix, p)
2811
2953
  );
2812
2954
  const regionApertureNumber = getRegionApertureNumber(copper_glayer);
2813
2955
  const rect_builder = gerberBuilder().add("select_aperture", { aperture_number: regionApertureNumber }).add("start_region_statement", {});
@@ -2931,6 +3073,69 @@ var convertSoupToGerberCommands = (circuitJson, opts = {}) => {
2931
3073
  }
2932
3074
  glayer.push(...gerber.build());
2933
3075
  }
3076
+ } else if (isSilkscreenShape(element) && isOuterLayerRef(layer)) {
3077
+ if (element.layer === layer) {
3078
+ const stroke = getSilkscreenShapeStroke(element);
3079
+ if (stroke) {
3080
+ const glayer = glayers[getGerberLayerName(layer, "silkscreen")];
3081
+ const isFilled = element.is_filled === true;
3082
+ const isStroked = element.has_stroke !== false;
3083
+ if (isFilled) {
3084
+ addClosedRegionFromPoints({
3085
+ target: glayer,
3086
+ apertureSource: glayer,
3087
+ points: stroke.route
3088
+ });
3089
+ }
3090
+ if (isStroked) {
3091
+ renderOpenPath({
3092
+ element: {
3093
+ type: element.type,
3094
+ stroke_width: stroke.strokeWidth,
3095
+ is_stroke_dashed: element.is_stroke_dashed
3096
+ },
3097
+ glayer,
3098
+ apertureConfig: {
3099
+ standard_template_code: "C",
3100
+ diameter: stroke.strokeWidth
3101
+ },
3102
+ route: stroke.route,
3103
+ mapY: mfy
3104
+ });
3105
+ }
3106
+ }
3107
+ }
3108
+ } else if (
3109
+ // pcb_silkscreen_graphic is newer than this repo's pinned circuit-json,
3110
+ // so it isn't in the AnyCircuitElement union yet — match it structurally.
3111
+ element.type === "pcb_silkscreen_graphic" && isOuterLayerRef(layer)
3112
+ ) {
3113
+ const graphic = element;
3114
+ if (graphic.layer === layer && graphic.brep_shape) {
3115
+ const glayer = glayers[getGerberLayerName(layer, "silkscreen")];
3116
+ const { outer_ring, inner_rings } = graphic.brep_shape;
3117
+ const regionApertureNumber = getRegionApertureNumber(glayer);
3118
+ const outerBuilder = gerberBuilder().add("select_aperture", { aperture_number: regionApertureNumber }).add("start_region_statement", {});
3119
+ drawRingToBuilder(outerBuilder, reverseRing(outer_ring));
3120
+ outerBuilder.add("end_region_statement", {});
3121
+ glayer.push(...outerBuilder.build());
3122
+ if (inner_rings && inner_rings.length > 0) {
3123
+ glayer.push(
3124
+ ...gerberBuilder().add("set_layer_polarity", { polarity: "C" }).build()
3125
+ );
3126
+ for (const inner_ring of inner_rings) {
3127
+ const innerBuilder = gerberBuilder().add("select_aperture", {
3128
+ aperture_number: regionApertureNumber
3129
+ }).add("start_region_statement", {});
3130
+ drawRingToBuilder(innerBuilder, inner_ring);
3131
+ innerBuilder.add("end_region_statement", {});
3132
+ glayer.push(...innerBuilder.build());
3133
+ }
3134
+ glayer.push(
3135
+ ...gerberBuilder().add("set_layer_polarity", { polarity: "D" }).build()
3136
+ );
3137
+ }
3138
+ }
2934
3139
  } else if (element.type === "pcb_silkscreen_text" && isOuterLayerRef(layer)) {
2935
3140
  renderVectorText(
2936
3141
  element,
@@ -3371,23 +3576,23 @@ var convertSoupToGerberCommands = (circuitJson, opts = {}) => {
3371
3576
  Math.min(corner_radius ?? 0, Math.abs(w), Math.abs(h))
3372
3577
  );
3373
3578
  const makeTransformMatrix = () => {
3374
- let transformMatrix2 = identity();
3579
+ let transformMatrix2 = identity2();
3375
3580
  if (rotation) {
3376
3581
  const angle_rad = rotation * Math.PI / 180;
3377
- transformMatrix2 = rotate(angle_rad);
3582
+ transformMatrix2 = rotate2(angle_rad);
3378
3583
  }
3379
- return compose(translate(center.x, center.y), transformMatrix2);
3584
+ return compose2(translate2(center.x, center.y), transformMatrix2);
3380
3585
  };
3381
3586
  const transformMatrix = makeTransformMatrix();
3382
3587
  if (r > 0) {
3383
3588
  const startPoint = { x: -w + r, y: h };
3384
- let currentPoint = applyToPoint(transformMatrix, startPoint);
3589
+ let currentPoint = applyToPoint2(transformMatrix, startPoint);
3385
3590
  cutout_builder.add("move_operation", {
3386
3591
  x: currentPoint.x,
3387
3592
  y: mfy(currentPoint.y)
3388
3593
  });
3389
3594
  const addLine = (point) => {
3390
- const transformedPoint = applyToPoint(transformMatrix, point);
3595
+ const transformedPoint = applyToPoint2(transformMatrix, point);
3391
3596
  cutout_builder.add("plot_operation", {
3392
3597
  x: transformedPoint.x,
3393
3598
  y: mfy(transformedPoint.y)
@@ -3395,11 +3600,11 @@ var convertSoupToGerberCommands = (circuitJson, opts = {}) => {
3395
3600
  currentPoint = transformedPoint;
3396
3601
  };
3397
3602
  const addArc = (options) => {
3398
- const transformedPoint = applyToPoint(
3603
+ const transformedPoint = applyToPoint2(
3399
3604
  transformMatrix,
3400
3605
  options.point
3401
3606
  );
3402
- const transformedCenter = applyToPoint(
3607
+ const transformedCenter = applyToPoint2(
3403
3608
  transformMatrix,
3404
3609
  options.center
3405
3610
  );
@@ -3443,7 +3648,7 @@ var convertSoupToGerberCommands = (circuitJson, opts = {}) => {
3443
3648
  // Bottom-left
3444
3649
  ];
3445
3650
  const transformedPoints = points.map(
3446
- (p) => applyToPoint(transformMatrix, p)
3651
+ (p) => applyToPoint2(transformMatrix, p)
3447
3652
  );
3448
3653
  cutout_builder.add("move_operation", {
3449
3654
  x: transformedPoints[0].x,
@@ -3521,4 +3726,4 @@ export {
3521
3726
  stringifyGerberCommands,
3522
3727
  convertSoupToGerberCommands
3523
3728
  };
3524
- //# sourceMappingURL=chunk-LJRHQISL.js.map
3729
+ //# sourceMappingURL=chunk-7LG6NVYJ.js.map