circuit-json-to-lbrn 0.0.20 → 0.0.22

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.
Files changed (81) hide show
  1. package/dist/index.d.ts +1 -0
  2. package/dist/index.js +784 -345
  3. package/lib/ConvertContext.ts +6 -2
  4. package/lib/element-handlers/addPcbCutout/addCirclePcbCutout.ts +10 -2
  5. package/lib/element-handlers/addPcbCutout/addPolygonPcbCutout.ts +10 -6
  6. package/lib/element-handlers/addPcbCutout/addRectPcbCutout.ts +12 -12
  7. package/lib/element-handlers/addPcbHole/addCirclePcbHole.ts +10 -2
  8. package/lib/element-handlers/addPcbHole/addOvalPcbHole.ts +8 -8
  9. package/lib/element-handlers/addPcbHole/addPillPcbHole.ts +8 -8
  10. package/lib/element-handlers/addPcbHole/addRectPcbHole.ts +10 -10
  11. package/lib/element-handlers/addPcbHole/addRotatedPillPcbHole.ts +8 -8
  12. package/lib/element-handlers/addPcbTrace/index.ts +145 -61
  13. package/lib/element-handlers/addPcbVia/index.ts +49 -15
  14. package/lib/element-handlers/addPlatedHole/addCirclePlatedHole.ts +49 -15
  15. package/lib/element-handlers/addPlatedHole/addCircularHoleWithRectPad.ts +38 -18
  16. package/lib/element-handlers/addPlatedHole/addHoleWithPolygonPad.ts +41 -16
  17. package/lib/element-handlers/addPlatedHole/addOvalPlatedHole.ts +39 -18
  18. package/lib/element-handlers/addPlatedHole/addPillHoleWithRectPad.ts +38 -23
  19. package/lib/element-handlers/addPlatedHole/addPillPlatedHole.ts +39 -18
  20. package/lib/element-handlers/addPlatedHole/addRotatedPillHoleWithRectPad.ts +43 -28
  21. package/lib/element-handlers/addSmtPad/addCircleSmtPad.ts +31 -4
  22. package/lib/element-handlers/addSmtPad/addPillSmtPad.ts +33 -4
  23. package/lib/element-handlers/addSmtPad/addPolygonSmtPad.ts +25 -3
  24. package/lib/element-handlers/addSmtPad/addRectSmtPad.ts +20 -3
  25. package/lib/element-handlers/addSmtPad/addRotatedPillSmtPad.ts +31 -12
  26. package/lib/element-handlers/addSmtPad/addRotatedRectSmtPad.ts +31 -12
  27. package/lib/helpers/circleShape.ts +13 -6
  28. package/lib/helpers/ovalShape.ts +17 -8
  29. package/lib/helpers/pathPointUtils.ts +11 -5
  30. package/lib/helpers/pillShape.ts +24 -11
  31. package/lib/helpers/polygonShape.ts +11 -5
  32. package/lib/helpers/roundedRectShape.ts +19 -9
  33. package/lib/index.ts +92 -41
  34. package/package.json +1 -1
  35. package/tests/assets/keyboard-default60.json +92565 -0
  36. package/tests/examples/__snapshots__/board-outline-soldermask-preset.snap.svg +1 -1
  37. package/tests/examples/__snapshots__/board-outline.snap.svg +1 -1
  38. package/tests/examples/__snapshots__/lga-interconnect.snap.svg +1 -1
  39. package/tests/examples/__snapshots__/single-trace.snap.svg +1 -1
  40. package/tests/examples/addPcbCutout/__snapshots__/pcb-cutout-circle.snap.svg +1 -1
  41. package/tests/examples/addPcbCutout/__snapshots__/pcb-cutout-path.snap.svg +1 -1
  42. package/tests/examples/addPcbCutout/__snapshots__/pcb-cutout-polygon.snap.svg +1 -1
  43. package/tests/examples/addPcbCutout/__snapshots__/pcb-cutout-rect.snap.svg +1 -1
  44. package/tests/examples/addPcbHole/__snapshots__/pcb-hole-circle.snap.svg +1 -1
  45. package/tests/examples/addPcbHole/__snapshots__/pcb-hole-oval.snap.svg +1 -1
  46. package/tests/examples/addPcbHole/__snapshots__/pcb-hole-pill.snap.svg +1 -1
  47. package/tests/examples/addPcbHole/__snapshots__/pcb-hole-rect.snap.svg +1 -1
  48. package/tests/examples/addPcbHole/__snapshots__/pcb-hole-rotated-pill.snap.svg +2 -2
  49. package/tests/examples/addPcbHole/__snapshots__/pcb-hole-with-soldermask.snap.svg +1 -1
  50. package/tests/examples/addPcbVia/__snapshots__/pcb-via-basic.snap.svg +1 -1
  51. package/tests/examples/addPcbVia/__snapshots__/pcb-via-with-net.snap.svg +1 -1
  52. package/tests/examples/addPcbVia/__snapshots__/pcb-via-with-soldermask.snap.svg +1 -1
  53. package/tests/examples/addPlatedHole/__snapshots__/pcb-plated-hole-circle.snap.svg +1 -1
  54. package/tests/examples/addPlatedHole/__snapshots__/pcb-plated-hole-circular-hole-with-rect-pad.snap.svg +1 -1
  55. package/tests/examples/addPlatedHole/__snapshots__/pcb-plated-hole-oval.snap.svg +1 -1
  56. package/tests/examples/addPlatedHole/__snapshots__/pcb-plated-hole-pill-with-rect-pad.snap.svg +1 -1
  57. package/tests/examples/addPlatedHole/__snapshots__/pcb-plated-hole-pill.snap.svg +1 -1
  58. package/tests/examples/addPlatedHole/__snapshots__/pcb-plated-hole-polygon.snap.svg +1 -1
  59. package/tests/examples/addPlatedHole/__snapshots__/pcb-plated-hole-rotated-pill-with-rect-pad.snap.svg +1 -1
  60. package/tests/examples/addSmtPad/__snapshots__/circleSmtPad.snap.svg +1 -1
  61. package/tests/examples/addSmtPad/__snapshots__/pillSmtPad.snap.svg +1 -1
  62. package/tests/examples/addSmtPad/__snapshots__/polygonSmtPad.snap.svg +1 -1
  63. package/tests/examples/addSmtPad/__snapshots__/rotatedPillSmtPad.snap.svg +1 -1
  64. package/tests/examples/addSmtPad/__snapshots__/rotatedRectSmtPad.snap.svg +1 -1
  65. package/tests/examples/keyboard-defaul60/__snapshots__/keyboard-both-layer-includeSoldermask.snap.svg +8 -0
  66. package/tests/examples/keyboard-defaul60/__snapshots__/keyboard-both-layers.snap.svg +8 -0
  67. package/tests/examples/keyboard-defaul60/__snapshots__/keyboard-bottom-layer.snap.svg +8 -0
  68. package/tests/examples/keyboard-defaul60/__snapshots__/keyboard-top-layer.snap.svg +8 -0
  69. package/tests/examples/keyboard-defaul60/keyboard-both-layer-includeSoldermask.test.ts +27 -0
  70. package/tests/examples/keyboard-defaul60/keyboard-both-layers.test.ts +26 -0
  71. package/tests/examples/keyboard-defaul60/keyboard-bottom-layer.test.ts +26 -0
  72. package/tests/examples/keyboard-defaul60/keyboard-top-layer.test.ts +26 -0
  73. package/tests/examples/lga-interconnect.test.ts +3 -2
  74. package/tests/examples/soldermask/__snapshots__/copper-and-soldermask.snap.svg +1 -1
  75. package/tests/examples/soldermask/__snapshots__/copper-only.snap.svg +1 -1
  76. package/tests/examples/soldermask/__snapshots__/soldermask-only.snap.svg +1 -1
  77. package/tests/examples/soldermask/copper-and-soldermask.test.ts +18 -10
  78. package/tests/examples/soldermask/soldermask-only.test.ts +3 -3
  79. package/tests/examples/soldermask-margin/__snapshots__/negative-soldermask-margin.snap.svg +1 -1
  80. package/tests/examples/soldermask-margin/__snapshots__/positive-soldermask-margin.snap.svg +1 -1
  81. package/tsconfig.json +2 -1
package/dist/index.js CHANGED
@@ -6,7 +6,12 @@ import { cju as cju2 } from "@tscircuit/circuit-json-util";
6
6
  import { ShapePath } from "lbrnts";
7
7
 
8
8
  // lib/helpers/circleShape.ts
9
- var createCirclePath = (centerX, centerY, radius, segments = 64) => {
9
+ var createCirclePath = ({
10
+ centerX,
11
+ centerY,
12
+ radius,
13
+ segments = 64
14
+ }) => {
10
15
  const verts = [];
11
16
  const prims = [];
12
17
  for (let i = 0; i < segments; i++) {
@@ -46,14 +51,18 @@ var circleToPolygon = (circle, segments = 32) => {
46
51
  var addCirclePlatedHole = (platedHole, ctx) => {
47
52
  const {
48
53
  project,
49
- copperCutSetting,
54
+ topCopperCutSetting,
55
+ bottomCopperCutSetting,
50
56
  soldermaskCutSetting,
51
57
  throughBoardCutSetting,
58
+ topNetGeoms,
59
+ bottomNetGeoms,
52
60
  origin,
53
61
  includeCopper,
54
62
  includeSoldermask,
55
63
  connMap,
56
- soldermaskMargin
64
+ soldermaskMargin,
65
+ includeLayers
57
66
  } = ctx;
58
67
  const centerX = platedHole.x + origin.x;
59
68
  const centerY = platedHole.y + origin.y;
@@ -63,22 +72,47 @@ var addCirclePlatedHole = (platedHole, ctx) => {
63
72
  const circle = new Circle(point(centerX, centerY), outerRadius);
64
73
  const polygon = circleToPolygon(circle);
65
74
  if (netId) {
66
- ctx.netGeoms.get(netId)?.push(polygon);
75
+ if (includeLayers.includes("top")) {
76
+ topNetGeoms.get(netId)?.push(polygon.clone());
77
+ }
78
+ if (includeLayers.includes("bottom")) {
79
+ bottomNetGeoms.get(netId)?.push(polygon.clone());
80
+ }
67
81
  } else {
68
- const outer = createCirclePath(centerX, centerY, outerRadius);
69
- project.children.push(
70
- new ShapePath({
71
- cutIndex: copperCutSetting.index,
72
- verts: outer.verts,
73
- prims: outer.prims,
74
- isClosed: true
75
- })
76
- );
82
+ const outer = createCirclePath({
83
+ centerX,
84
+ centerY,
85
+ radius: outerRadius
86
+ });
87
+ if (includeLayers.includes("top")) {
88
+ project.children.push(
89
+ new ShapePath({
90
+ cutIndex: topCopperCutSetting.index,
91
+ verts: outer.verts,
92
+ prims: outer.prims,
93
+ isClosed: true
94
+ })
95
+ );
96
+ }
97
+ if (includeLayers.includes("bottom")) {
98
+ project.children.push(
99
+ new ShapePath({
100
+ cutIndex: bottomCopperCutSetting.index,
101
+ verts: outer.verts,
102
+ prims: outer.prims,
103
+ isClosed: true
104
+ })
105
+ );
106
+ }
77
107
  }
78
108
  }
79
109
  if (platedHole.outer_diameter > 0 && includeSoldermask) {
80
110
  const smRadius = platedHole.outer_diameter / 2 + soldermaskMargin;
81
- const outer = createCirclePath(centerX, centerY, smRadius);
111
+ const outer = createCirclePath({
112
+ centerX,
113
+ centerY,
114
+ radius: smRadius
115
+ });
82
116
  project.children.push(
83
117
  new ShapePath({
84
118
  cutIndex: soldermaskCutSetting.index,
@@ -90,7 +124,11 @@ var addCirclePlatedHole = (platedHole, ctx) => {
90
124
  }
91
125
  if (platedHole.hole_diameter > 0 && includeCopper) {
92
126
  const innerRadius = platedHole.hole_diameter / 2;
93
- const inner = createCirclePath(centerX, centerY, innerRadius);
127
+ const inner = createCirclePath({
128
+ centerX,
129
+ centerY,
130
+ radius: innerRadius
131
+ });
94
132
  project.children.push(
95
133
  new ShapePath({
96
134
  cutIndex: throughBoardCutSetting.index,
@@ -106,7 +144,14 @@ var addCirclePlatedHole = (platedHole, ctx) => {
106
144
  import { ShapePath as ShapePath2 } from "lbrnts";
107
145
 
108
146
  // lib/helpers/ovalShape.ts
109
- var createOvalPath = (centerX, centerY, width, height, rotation = 0, segments = 64) => {
147
+ var createOvalPath = ({
148
+ centerX,
149
+ centerY,
150
+ width,
151
+ height,
152
+ rotation = 0,
153
+ segments = 64
154
+ }) => {
110
155
  const verts = [];
111
156
  const prims = [];
112
157
  const radiusX = width / 2;
@@ -136,13 +181,15 @@ var createOvalPath = (centerX, centerY, width, height, rotation = 0, segments =
136
181
  var addOvalPlatedHole = (platedHole, ctx) => {
137
182
  const {
138
183
  project,
139
- copperCutSetting,
184
+ topCopperCutSetting,
185
+ bottomCopperCutSetting,
140
186
  soldermaskCutSetting,
141
187
  throughBoardCutSetting,
142
188
  origin,
143
189
  includeCopper,
144
190
  includeSoldermask,
145
- soldermaskMargin
191
+ soldermaskMargin,
192
+ includeLayers
146
193
  } = ctx;
147
194
  if (platedHole.outer_width <= 0 || platedHole.outer_height <= 0) {
148
195
  return;
@@ -151,26 +198,44 @@ var addOvalPlatedHole = (platedHole, ctx) => {
151
198
  const centerY = platedHole.y + origin.y;
152
199
  const rotation = (platedHole.ccw_rotation ?? 0) * (Math.PI / 180);
153
200
  if (platedHole.outer_width > 0 && platedHole.outer_height > 0 && includeCopper) {
154
- const outer = createOvalPath(
201
+ const outer = createOvalPath({
155
202
  centerX,
156
203
  centerY,
157
- platedHole.outer_width,
158
- platedHole.outer_height,
204
+ width: platedHole.outer_width,
205
+ height: platedHole.outer_height,
159
206
  rotation
160
- );
161
- project.children.push(
162
- new ShapePath2({
163
- cutIndex: copperCutSetting.index,
164
- verts: outer.verts,
165
- prims: outer.prims,
166
- isClosed: true
167
- })
168
- );
207
+ });
208
+ if (includeLayers.includes("top")) {
209
+ project.children.push(
210
+ new ShapePath2({
211
+ cutIndex: topCopperCutSetting.index,
212
+ verts: outer.verts,
213
+ prims: outer.prims,
214
+ isClosed: true
215
+ })
216
+ );
217
+ }
218
+ if (includeLayers.includes("bottom")) {
219
+ project.children.push(
220
+ new ShapePath2({
221
+ cutIndex: bottomCopperCutSetting.index,
222
+ verts: outer.verts,
223
+ prims: outer.prims,
224
+ isClosed: true
225
+ })
226
+ );
227
+ }
169
228
  }
170
229
  if (platedHole.outer_width > 0 && platedHole.outer_height > 0 && includeSoldermask) {
171
230
  const smWidth = platedHole.outer_width + 2 * soldermaskMargin;
172
231
  const smHeight = platedHole.outer_height + 2 * soldermaskMargin;
173
- const outer = createOvalPath(centerX, centerY, smWidth, smHeight, rotation);
232
+ const outer = createOvalPath({
233
+ centerX,
234
+ centerY,
235
+ width: smWidth,
236
+ height: smHeight,
237
+ rotation
238
+ });
174
239
  project.children.push(
175
240
  new ShapePath2({
176
241
  cutIndex: soldermaskCutSetting.index,
@@ -181,13 +246,13 @@ var addOvalPlatedHole = (platedHole, ctx) => {
181
246
  );
182
247
  }
183
248
  if (platedHole.hole_width > 0 && platedHole.hole_height > 0 && includeCopper) {
184
- const inner = createOvalPath(
249
+ const inner = createOvalPath({
185
250
  centerX,
186
251
  centerY,
187
- platedHole.hole_width,
188
- platedHole.hole_height,
252
+ width: platedHole.hole_width,
253
+ height: platedHole.hole_height,
189
254
  rotation
190
- );
255
+ });
191
256
  project.children.push(
192
257
  new ShapePath2({
193
258
  cutIndex: throughBoardCutSetting.index,
@@ -203,7 +268,15 @@ var addOvalPlatedHole = (platedHole, ctx) => {
203
268
  import { ShapePath as ShapePath3 } from "lbrnts";
204
269
 
205
270
  // lib/helpers/roundedRectShape.ts
206
- var createRoundedRectPath = (centerX, centerY, width, height, borderRadius = 0, segments = 4, rotation = 0) => {
271
+ var createRoundedRectPath = ({
272
+ centerX,
273
+ centerY,
274
+ width,
275
+ height,
276
+ borderRadius = 0,
277
+ segments = 4,
278
+ rotation = 0
279
+ }) => {
207
280
  const verts = [];
208
281
  const prims = [];
209
282
  const halfWidth = width / 2;
@@ -287,13 +360,15 @@ var createRoundedRectPath = (centerX, centerY, width, height, borderRadius = 0,
287
360
  var addCircularHoleWithRectPad = (platedHole, ctx) => {
288
361
  const {
289
362
  project,
290
- copperCutSetting,
363
+ topCopperCutSetting,
364
+ bottomCopperCutSetting,
291
365
  soldermaskCutSetting,
292
366
  throughBoardCutSetting,
293
367
  origin,
294
368
  includeCopper,
295
369
  includeSoldermask,
296
- soldermaskMargin
370
+ soldermaskMargin,
371
+ includeLayers
297
372
  } = ctx;
298
373
  const centerX = platedHole.x + origin.x;
299
374
  const centerY = platedHole.y + origin.y;
@@ -301,33 +376,45 @@ var addCircularHoleWithRectPad = (platedHole, ctx) => {
301
376
  const padWidth = platedHole.rect_pad_width;
302
377
  const padHeight = platedHole.rect_pad_height;
303
378
  const borderRadius = platedHole.rect_border_radius ?? 0;
304
- const padPath = createRoundedRectPath(
379
+ const padPath = createRoundedRectPath({
305
380
  centerX,
306
381
  centerY,
307
- padWidth,
308
- padHeight,
382
+ width: padWidth,
383
+ height: padHeight,
309
384
  borderRadius
310
- );
385
+ });
311
386
  if (includeCopper) {
312
- project.children.push(
313
- new ShapePath3({
314
- cutIndex: copperCutSetting.index,
315
- verts: padPath.verts,
316
- prims: padPath.prims,
317
- isClosed: true
318
- })
319
- );
387
+ if (includeLayers.includes("top")) {
388
+ project.children.push(
389
+ new ShapePath3({
390
+ cutIndex: topCopperCutSetting.index,
391
+ verts: padPath.verts,
392
+ prims: padPath.prims,
393
+ isClosed: true
394
+ })
395
+ );
396
+ }
397
+ if (includeLayers.includes("bottom")) {
398
+ project.children.push(
399
+ new ShapePath3({
400
+ cutIndex: bottomCopperCutSetting.index,
401
+ verts: padPath.verts,
402
+ prims: padPath.prims,
403
+ isClosed: true
404
+ })
405
+ );
406
+ }
320
407
  }
321
408
  if (includeSoldermask) {
322
409
  const smPadWidth = padWidth + 2 * soldermaskMargin;
323
410
  const smPadHeight = padHeight + 2 * soldermaskMargin;
324
- const smPadPath = createRoundedRectPath(
411
+ const smPadPath = createRoundedRectPath({
325
412
  centerX,
326
413
  centerY,
327
- smPadWidth,
328
- smPadHeight,
414
+ width: smPadWidth,
415
+ height: smPadHeight,
329
416
  borderRadius
330
- );
417
+ });
331
418
  project.children.push(
332
419
  new ShapePath3({
333
420
  cutIndex: soldermaskCutSetting.index,
@@ -340,7 +427,12 @@ var addCircularHoleWithRectPad = (platedHole, ctx) => {
340
427
  if (holeRadius > 0 && includeCopper) {
341
428
  const holeCenterX = centerX + platedHole.hole_offset_x;
342
429
  const holeCenterY = centerY + platedHole.hole_offset_y;
343
- const holePath = createCirclePath(holeCenterX, holeCenterY, holeRadius, 32);
430
+ const holePath = createCirclePath({
431
+ centerX: holeCenterX,
432
+ centerY: holeCenterY,
433
+ radius: holeRadius,
434
+ segments: 32
435
+ });
344
436
  project.children.push(
345
437
  new ShapePath3({
346
438
  cutIndex: throughBoardCutSetting.index,
@@ -356,7 +448,11 @@ var addCircularHoleWithRectPad = (platedHole, ctx) => {
356
448
  import { ShapePath as ShapePath4 } from "lbrnts";
357
449
 
358
450
  // lib/helpers/pathPointUtils.ts
359
- var createPointAdder = (verts, prims, options = {}) => {
451
+ var createPointAdder = ({
452
+ verts,
453
+ prims,
454
+ options = {}
455
+ }) => {
360
456
  const { rotation = 0, rotationCenter, translation } = options;
361
457
  const cos = Math.cos(rotation);
362
458
  const sin = Math.sin(rotation);
@@ -380,16 +476,27 @@ var createPointAdder = (verts, prims, options = {}) => {
380
476
  };
381
477
 
382
478
  // lib/helpers/pillShape.ts
383
- var createPillPath = (centerX, centerY, width, height, rotation = 0, segments = 32) => {
479
+ var createPillPath = ({
480
+ centerX,
481
+ centerY,
482
+ width,
483
+ height,
484
+ rotation = 0,
485
+ segments = 32
486
+ }) => {
384
487
  const verts = [];
385
488
  const prims = [];
386
489
  const halfWidth = width / 2;
387
490
  const halfHeight = height / 2;
388
491
  const radius = Math.min(halfWidth, halfHeight);
389
492
  const isVertical = height > width;
390
- const addPoint = createPointAdder(verts, prims, {
391
- rotation,
392
- rotationCenter: { x: centerX, y: centerY }
493
+ const addPoint = createPointAdder({
494
+ verts,
495
+ prims,
496
+ options: {
497
+ rotation,
498
+ rotationCenter: { x: centerX, y: centerY }
499
+ }
393
500
  });
394
501
  if (isVertical) {
395
502
  const capOffset = halfHeight - radius;
@@ -438,13 +545,15 @@ var createPillPath = (centerX, centerY, width, height, rotation = 0, segments =
438
545
  var addPillHoleWithRectPad = (platedHole, ctx) => {
439
546
  const {
440
547
  project,
441
- copperCutSetting,
548
+ topCopperCutSetting,
549
+ bottomCopperCutSetting,
442
550
  soldermaskCutSetting,
443
551
  throughBoardCutSetting,
444
552
  origin,
445
553
  includeCopper,
446
554
  includeSoldermask,
447
- soldermaskMargin
555
+ soldermaskMargin,
556
+ includeLayers
448
557
  } = ctx;
449
558
  const centerX = platedHole.x + origin.x;
450
559
  const centerY = platedHole.y + origin.y;
@@ -452,33 +561,45 @@ var addPillHoleWithRectPad = (platedHole, ctx) => {
452
561
  const padHeight = platedHole.rect_pad_height;
453
562
  const borderRadius = platedHole.rect_border_radius ?? 0;
454
563
  if (padWidth > 0 && padHeight > 0) {
455
- const padPath = createRoundedRectPath(
564
+ const padPath = createRoundedRectPath({
456
565
  centerX,
457
566
  centerY,
458
- padWidth,
459
- padHeight,
567
+ width: padWidth,
568
+ height: padHeight,
460
569
  borderRadius
461
- );
570
+ });
462
571
  if (includeCopper) {
463
- project.children.push(
464
- new ShapePath4({
465
- cutIndex: copperCutSetting.index,
466
- verts: padPath.verts,
467
- prims: padPath.prims,
468
- isClosed: true
469
- })
470
- );
572
+ if (includeLayers.includes("top")) {
573
+ project.children.push(
574
+ new ShapePath4({
575
+ cutIndex: topCopperCutSetting.index,
576
+ verts: padPath.verts,
577
+ prims: padPath.prims,
578
+ isClosed: true
579
+ })
580
+ );
581
+ }
582
+ if (includeLayers.includes("bottom")) {
583
+ project.children.push(
584
+ new ShapePath4({
585
+ cutIndex: bottomCopperCutSetting.index,
586
+ verts: padPath.verts,
587
+ prims: padPath.prims,
588
+ isClosed: true
589
+ })
590
+ );
591
+ }
471
592
  }
472
593
  if (includeSoldermask) {
473
594
  const smPadWidth = padWidth + 2 * soldermaskMargin;
474
595
  const smPadHeight = padHeight + 2 * soldermaskMargin;
475
- const smPadPath = createRoundedRectPath(
596
+ const smPadPath = createRoundedRectPath({
476
597
  centerX,
477
598
  centerY,
478
- smPadWidth,
479
- smPadHeight,
599
+ width: smPadWidth,
600
+ height: smPadHeight,
480
601
  borderRadius
481
- );
602
+ });
482
603
  project.children.push(
483
604
  new ShapePath4({
484
605
  cutIndex: soldermaskCutSetting.index,
@@ -494,12 +615,12 @@ var addPillHoleWithRectPad = (platedHole, ctx) => {
494
615
  if (holeWidth > 0 && holeHeight > 0 && includeCopper) {
495
616
  const holeCenterX = centerX + platedHole.hole_offset_x;
496
617
  const holeCenterY = centerY + platedHole.hole_offset_y;
497
- const holePath = createPillPath(
498
- holeCenterX,
499
- holeCenterY,
500
- holeWidth,
501
- holeHeight
502
- );
618
+ const holePath = createPillPath({
619
+ centerX: holeCenterX,
620
+ centerY: holeCenterY,
621
+ width: holeWidth,
622
+ height: holeHeight
623
+ });
503
624
  project.children.push(
504
625
  new ShapePath4({
505
626
  cutIndex: throughBoardCutSetting.index,
@@ -516,13 +637,15 @@ import { ShapePath as ShapePath5 } from "lbrnts";
516
637
  var addRotatedPillHoleWithRectPad = (platedHole, ctx) => {
517
638
  const {
518
639
  project,
519
- copperCutSetting,
640
+ topCopperCutSetting,
641
+ bottomCopperCutSetting,
520
642
  soldermaskCutSetting,
521
643
  throughBoardCutSetting,
522
644
  origin,
523
645
  includeCopper,
524
646
  includeSoldermask,
525
- soldermaskMargin
647
+ soldermaskMargin,
648
+ includeLayers
526
649
  } = ctx;
527
650
  const centerX = platedHole.x + origin.x;
528
651
  const centerY = platedHole.y + origin.y;
@@ -531,37 +654,49 @@ var addRotatedPillHoleWithRectPad = (platedHole, ctx) => {
531
654
  const borderRadius = platedHole.rect_border_radius ?? 0;
532
655
  const padRotation = (platedHole.rect_ccw_rotation ?? 0) * (Math.PI / 180);
533
656
  if (padWidth > 0 && padHeight > 0) {
534
- const padPath = createRoundedRectPath(
657
+ const padPath = createRoundedRectPath({
535
658
  centerX,
536
659
  centerY,
537
- padWidth,
538
- padHeight,
660
+ width: padWidth,
661
+ height: padHeight,
539
662
  borderRadius,
540
- 4,
541
- padRotation
542
- );
663
+ segments: 4,
664
+ rotation: padRotation
665
+ });
543
666
  if (includeCopper) {
544
- project.children.push(
545
- new ShapePath5({
546
- cutIndex: copperCutSetting.index,
547
- verts: padPath.verts,
548
- prims: padPath.prims,
549
- isClosed: true
550
- })
551
- );
667
+ if (includeLayers.includes("top")) {
668
+ project.children.push(
669
+ new ShapePath5({
670
+ cutIndex: topCopperCutSetting.index,
671
+ verts: padPath.verts,
672
+ prims: padPath.prims,
673
+ isClosed: true
674
+ })
675
+ );
676
+ }
677
+ if (includeLayers.includes("bottom")) {
678
+ project.children.push(
679
+ new ShapePath5({
680
+ cutIndex: bottomCopperCutSetting.index,
681
+ verts: padPath.verts,
682
+ prims: padPath.prims,
683
+ isClosed: true
684
+ })
685
+ );
686
+ }
552
687
  }
553
688
  if (includeSoldermask) {
554
689
  const smPadWidth = padWidth + 2 * soldermaskMargin;
555
690
  const smPadHeight = padHeight + 2 * soldermaskMargin;
556
- const smPadPath = createRoundedRectPath(
691
+ const smPadPath = createRoundedRectPath({
557
692
  centerX,
558
693
  centerY,
559
- smPadWidth,
560
- smPadHeight,
694
+ width: smPadWidth,
695
+ height: smPadHeight,
561
696
  borderRadius,
562
- 4,
563
- padRotation
564
- );
697
+ segments: 4,
698
+ rotation: padRotation
699
+ });
565
700
  project.children.push(
566
701
  new ShapePath5({
567
702
  cutIndex: soldermaskCutSetting.index,
@@ -578,13 +713,13 @@ var addRotatedPillHoleWithRectPad = (platedHole, ctx) => {
578
713
  if (holeWidth > 0 && holeHeight > 0 && includeCopper) {
579
714
  const holeCenterX = centerX + platedHole.hole_offset_x;
580
715
  const holeCenterY = centerY + platedHole.hole_offset_y;
581
- const holePath = createPillPath(
582
- holeCenterX,
583
- holeCenterY,
584
- holeWidth,
585
- holeHeight,
586
- holeRotation
587
- );
716
+ const holePath = createPillPath({
717
+ centerX: holeCenterX,
718
+ centerY: holeCenterY,
719
+ width: holeWidth,
720
+ height: holeHeight,
721
+ rotation: holeRotation
722
+ });
588
723
  project.children.push(
589
724
  new ShapePath5({
590
725
  cutIndex: throughBoardCutSetting.index,
@@ -600,7 +735,11 @@ var addRotatedPillHoleWithRectPad = (platedHole, ctx) => {
600
735
  import { ShapePath as ShapePath6 } from "lbrnts";
601
736
 
602
737
  // lib/helpers/polygonShape.ts
603
- var createPolygonPathFromOutline = (outline, offsetX, offsetY) => {
738
+ var createPolygonPathFromOutline = ({
739
+ outline,
740
+ offsetX,
741
+ offsetY
742
+ }) => {
604
743
  const verts = [];
605
744
  for (const point6 of outline) {
606
745
  const x = (point6.x ?? 0) + offsetX;
@@ -620,29 +759,43 @@ var createPolygonPathFromOutline = (outline, offsetX, offsetY) => {
620
759
  var addHoleWithPolygonPad = (platedHole, ctx) => {
621
760
  const {
622
761
  project,
623
- copperCutSetting,
762
+ topCopperCutSetting,
763
+ bottomCopperCutSetting,
624
764
  soldermaskCutSetting,
625
765
  throughBoardCutSetting,
626
766
  origin,
627
767
  includeCopper,
628
768
  includeSoldermask,
629
- soldermaskMargin
769
+ soldermaskMargin,
770
+ includeLayers
630
771
  } = ctx;
631
772
  if (platedHole.pad_outline.length >= 3 && includeCopper) {
632
- const pad = createPolygonPathFromOutline(
633
- platedHole.pad_outline,
634
- platedHole.x + origin.x,
635
- platedHole.y + origin.y
636
- );
773
+ const pad = createPolygonPathFromOutline({
774
+ outline: platedHole.pad_outline,
775
+ offsetX: platedHole.x + origin.x,
776
+ offsetY: platedHole.y + origin.y
777
+ });
637
778
  if (includeCopper) {
638
- project.children.push(
639
- new ShapePath6({
640
- cutIndex: copperCutSetting.index,
641
- verts: pad.verts,
642
- prims: pad.prims,
643
- isClosed: true
644
- })
645
- );
779
+ if (includeLayers.includes("top")) {
780
+ project.children.push(
781
+ new ShapePath6({
782
+ cutIndex: topCopperCutSetting.index,
783
+ verts: pad.verts,
784
+ prims: pad.prims,
785
+ isClosed: true
786
+ })
787
+ );
788
+ }
789
+ if (includeLayers.includes("bottom")) {
790
+ project.children.push(
791
+ new ShapePath6({
792
+ cutIndex: bottomCopperCutSetting.index,
793
+ verts: pad.verts,
794
+ prims: pad.prims,
795
+ isClosed: true
796
+ })
797
+ );
798
+ }
646
799
  }
647
800
  if (includeSoldermask) {
648
801
  project.children.push(
@@ -659,7 +812,12 @@ var addHoleWithPolygonPad = (platedHole, ctx) => {
659
812
  const centerX = platedHole.x + platedHole.hole_offset_x + origin.x;
660
813
  const centerY = platedHole.y + platedHole.hole_offset_y + origin.y;
661
814
  const radius = platedHole.hole_diameter / 2;
662
- const hole = createCirclePath(centerX, centerY, radius, 64);
815
+ const hole = createCirclePath({
816
+ centerX,
817
+ centerY,
818
+ radius,
819
+ segments: 64
820
+ });
663
821
  project.children.push(
664
822
  new ShapePath6({
665
823
  cutIndex: throughBoardCutSetting.index,
@@ -673,7 +831,12 @@ var addHoleWithPolygonPad = (platedHole, ctx) => {
673
831
  const centerX = platedHole.x + platedHole.hole_offset_x + origin.x;
674
832
  const centerY = platedHole.y + platedHole.hole_offset_y + origin.y;
675
833
  const radius = platedHole.hole_diameter / 2;
676
- const hole = createCirclePath(centerX, centerY, radius, 64);
834
+ const hole = createCirclePath({
835
+ centerX,
836
+ centerY,
837
+ radius,
838
+ segments: 64
839
+ });
677
840
  project.children.push(
678
841
  new ShapePath6({
679
842
  cutIndex: throughBoardCutSetting.index,
@@ -690,38 +853,58 @@ import { ShapePath as ShapePath7 } from "lbrnts";
690
853
  var addPcbPlatedHolePill = (platedHole, ctx) => {
691
854
  const {
692
855
  project,
693
- copperCutSetting,
856
+ topCopperCutSetting,
857
+ bottomCopperCutSetting,
694
858
  soldermaskCutSetting,
695
859
  throughBoardCutSetting,
696
860
  origin,
697
861
  includeCopper,
698
862
  includeSoldermask,
699
- soldermaskMargin
863
+ soldermaskMargin,
864
+ includeLayers
700
865
  } = ctx;
701
866
  const centerX = platedHole.x + origin.x;
702
867
  const centerY = platedHole.y + origin.y;
703
868
  const rotation = (platedHole.ccw_rotation || 0) * (Math.PI / 180);
704
869
  if (platedHole.outer_width > 0 && platedHole.outer_height > 0 && includeCopper) {
705
- const outer = createPillPath(
870
+ const outer = createPillPath({
706
871
  centerX,
707
872
  centerY,
708
- platedHole.outer_width,
709
- platedHole.outer_height,
873
+ width: platedHole.outer_width,
874
+ height: platedHole.outer_height,
710
875
  rotation
711
- );
712
- project.children.push(
713
- new ShapePath7({
714
- cutIndex: copperCutSetting.index,
715
- verts: outer.verts,
716
- prims: outer.prims,
717
- isClosed: true
718
- })
719
- );
876
+ });
877
+ if (includeLayers.includes("top")) {
878
+ project.children.push(
879
+ new ShapePath7({
880
+ cutIndex: topCopperCutSetting.index,
881
+ verts: outer.verts,
882
+ prims: outer.prims,
883
+ isClosed: true
884
+ })
885
+ );
886
+ }
887
+ if (includeLayers.includes("bottom")) {
888
+ project.children.push(
889
+ new ShapePath7({
890
+ cutIndex: bottomCopperCutSetting.index,
891
+ verts: outer.verts,
892
+ prims: outer.prims,
893
+ isClosed: true
894
+ })
895
+ );
896
+ }
720
897
  }
721
898
  if (platedHole.outer_width > 0 && platedHole.outer_height > 0 && includeSoldermask) {
722
899
  const smWidth = platedHole.outer_width + 2 * soldermaskMargin;
723
900
  const smHeight = platedHole.outer_height + 2 * soldermaskMargin;
724
- const outer = createPillPath(centerX, centerY, smWidth, smHeight, rotation);
901
+ const outer = createPillPath({
902
+ centerX,
903
+ centerY,
904
+ width: smWidth,
905
+ height: smHeight,
906
+ rotation
907
+ });
725
908
  project.children.push(
726
909
  new ShapePath7({
727
910
  cutIndex: soldermaskCutSetting.index,
@@ -732,13 +915,13 @@ var addPcbPlatedHolePill = (platedHole, ctx) => {
732
915
  );
733
916
  }
734
917
  if (platedHole.hole_width > 0 && platedHole.hole_height > 0 && includeCopper) {
735
- const inner = createPillPath(
918
+ const inner = createPillPath({
736
919
  centerX,
737
920
  centerY,
738
- platedHole.hole_width,
739
- platedHole.hole_height,
921
+ width: platedHole.hole_width,
922
+ height: platedHole.hole_height,
740
923
  rotation
741
- );
924
+ });
742
925
  project.children.push(
743
926
  new ShapePath7({
744
927
  cutIndex: throughBoardCutSetting.index,
@@ -779,23 +962,35 @@ import { ShapePath as ShapePath8 } from "lbrnts";
779
962
  var addRectSmtPad = (smtPad, ctx) => {
780
963
  const {
781
964
  project,
782
- copperCutSetting,
965
+ topCopperCutSetting,
966
+ bottomCopperCutSetting,
783
967
  soldermaskCutSetting,
784
968
  connMap,
785
- netGeoms,
969
+ topNetGeoms,
970
+ bottomNetGeoms,
786
971
  origin,
787
972
  includeCopper,
788
973
  includeSoldermask,
789
- soldermaskMargin
974
+ soldermaskMargin,
975
+ includeLayers
790
976
  } = ctx;
977
+ const padLayer = smtPad.layer || "top";
978
+ if (padLayer !== "top" && padLayer !== "bottom") {
979
+ return;
980
+ }
981
+ if (!includeLayers.includes(padLayer)) {
982
+ return;
983
+ }
791
984
  const centerX = smtPad.x + origin.x;
792
985
  const centerY = smtPad.y + origin.y;
793
986
  const halfWidth = smtPad.width / 2;
794
987
  const halfHeight = smtPad.height / 2;
795
988
  const netId = connMap.getNetConnectedToId(smtPad.pcb_smtpad_id);
989
+ const copperCutSetting = padLayer === "top" ? topCopperCutSetting : bottomCopperCutSetting;
990
+ const netGeoms = padLayer === "top" ? topNetGeoms : bottomNetGeoms;
796
991
  if (includeCopper) {
797
992
  if (netId) {
798
- ctx.netGeoms.get(netId)?.push(
993
+ netGeoms.get(netId)?.push(
799
994
  new Box(
800
995
  centerX - halfWidth,
801
996
  centerY - halfHeight,
@@ -864,14 +1059,27 @@ import { Circle as Circle2, point as point2 } from "@flatten-js/core";
864
1059
  var addCircleSmtPad = (smtPad, ctx) => {
865
1060
  const {
866
1061
  project,
867
- copperCutSetting,
1062
+ topCopperCutSetting,
1063
+ bottomCopperCutSetting,
868
1064
  soldermaskCutSetting,
1065
+ topNetGeoms,
1066
+ bottomNetGeoms,
869
1067
  origin,
870
1068
  includeCopper,
871
1069
  includeSoldermask,
872
1070
  connMap,
873
- soldermaskMargin
1071
+ soldermaskMargin,
1072
+ includeLayers
874
1073
  } = ctx;
1074
+ const padLayer = smtPad.layer || "top";
1075
+ if (padLayer !== "top" && padLayer !== "bottom") {
1076
+ return;
1077
+ }
1078
+ if (!includeLayers.includes(padLayer)) {
1079
+ return;
1080
+ }
1081
+ const copperCutSetting = padLayer === "top" ? topCopperCutSetting : bottomCopperCutSetting;
1082
+ const netGeoms = padLayer === "top" ? topNetGeoms : bottomNetGeoms;
875
1083
  const centerX = smtPad.x + origin.x;
876
1084
  const centerY = smtPad.y + origin.y;
877
1085
  if (smtPad.radius > 0) {
@@ -881,9 +1089,13 @@ var addCircleSmtPad = (smtPad, ctx) => {
881
1089
  const circle = new Circle2(point2(centerX, centerY), outerRadius);
882
1090
  const polygon = circleToPolygon(circle);
883
1091
  if (netId) {
884
- ctx.netGeoms.get(netId)?.push(polygon);
1092
+ netGeoms.get(netId)?.push(polygon);
885
1093
  } else {
886
- const outer = createCirclePath(centerX, centerY, outerRadius);
1094
+ const outer = createCirclePath({
1095
+ centerX,
1096
+ centerY,
1097
+ radius: outerRadius
1098
+ });
887
1099
  project.children.push(
888
1100
  new ShapePath9({
889
1101
  cutIndex: copperCutSetting.index,
@@ -896,7 +1108,11 @@ var addCircleSmtPad = (smtPad, ctx) => {
896
1108
  }
897
1109
  if (includeSoldermask) {
898
1110
  const smRadius = outerRadius + soldermaskMargin;
899
- const outer = createCirclePath(centerX, centerY, smRadius);
1111
+ const outer = createCirclePath({
1112
+ centerX,
1113
+ centerY,
1114
+ radius: smRadius
1115
+ });
900
1116
  project.children.push(
901
1117
  new ShapePath9({
902
1118
  cutIndex: soldermaskCutSetting.index,
@@ -923,23 +1139,41 @@ var pathToPolygon = (verts) => {
923
1139
  var addPillSmtPad = (smtPad, ctx) => {
924
1140
  const {
925
1141
  project,
926
- copperCutSetting,
1142
+ topCopperCutSetting,
1143
+ bottomCopperCutSetting,
927
1144
  soldermaskCutSetting,
1145
+ topNetGeoms,
1146
+ bottomNetGeoms,
928
1147
  origin,
929
1148
  includeCopper,
930
1149
  includeSoldermask,
931
1150
  connMap,
932
- soldermaskMargin
1151
+ soldermaskMargin,
1152
+ includeLayers
933
1153
  } = ctx;
1154
+ const padLayer = smtPad.layer || "top";
1155
+ if (padLayer !== "top" && padLayer !== "bottom") {
1156
+ return;
1157
+ }
1158
+ if (!includeLayers.includes(padLayer)) {
1159
+ return;
1160
+ }
1161
+ const copperCutSetting = padLayer === "top" ? topCopperCutSetting : bottomCopperCutSetting;
1162
+ const netGeoms = padLayer === "top" ? topNetGeoms : bottomNetGeoms;
934
1163
  const centerX = smtPad.x + origin.x;
935
1164
  const centerY = smtPad.y + origin.y;
936
1165
  if (smtPad.width > 0 && smtPad.height > 0) {
937
- const outer = createPillPath(centerX, centerY, smtPad.width, smtPad.height);
1166
+ const outer = createPillPath({
1167
+ centerX,
1168
+ centerY,
1169
+ width: smtPad.width,
1170
+ height: smtPad.height
1171
+ });
938
1172
  if (includeCopper) {
939
1173
  const netId = connMap.getNetConnectedToId(smtPad.pcb_smtpad_id);
940
1174
  const polygon = pathToPolygon(outer.verts);
941
1175
  if (netId) {
942
- ctx.netGeoms.get(netId)?.push(polygon);
1176
+ netGeoms.get(netId)?.push(polygon);
943
1177
  } else {
944
1178
  project.children.push(
945
1179
  new ShapePath10({
@@ -954,7 +1188,12 @@ var addPillSmtPad = (smtPad, ctx) => {
954
1188
  if (includeSoldermask) {
955
1189
  const smWidth = smtPad.width + 2 * soldermaskMargin;
956
1190
  const smHeight = smtPad.height + 2 * soldermaskMargin;
957
- const smOuter = createPillPath(centerX, centerY, smWidth, smHeight);
1191
+ const smOuter = createPillPath({
1192
+ centerX,
1193
+ centerY,
1194
+ width: smWidth,
1195
+ height: smHeight
1196
+ });
958
1197
  project.children.push(
959
1198
  new ShapePath10({
960
1199
  cutIndex: soldermaskCutSetting.index,
@@ -972,29 +1211,42 @@ import { ShapePath as ShapePath11 } from "lbrnts";
972
1211
  var addRotatedPillSmtPad = (smtPad, ctx) => {
973
1212
  const {
974
1213
  project,
975
- copperCutSetting,
1214
+ topCopperCutSetting,
1215
+ bottomCopperCutSetting,
976
1216
  soldermaskCutSetting,
1217
+ topNetGeoms,
1218
+ bottomNetGeoms,
977
1219
  origin,
978
1220
  includeCopper,
979
1221
  includeSoldermask,
980
1222
  connMap,
981
- soldermaskMargin
1223
+ soldermaskMargin,
1224
+ includeLayers
982
1225
  } = ctx;
1226
+ const padLayer = smtPad.layer || "top";
1227
+ if (padLayer !== "top" && padLayer !== "bottom") {
1228
+ return;
1229
+ }
1230
+ if (!includeLayers.includes(padLayer)) {
1231
+ return;
1232
+ }
1233
+ const copperCutSetting = padLayer === "top" ? topCopperCutSetting : bottomCopperCutSetting;
1234
+ const netGeoms = padLayer === "top" ? topNetGeoms : bottomNetGeoms;
983
1235
  const centerX = smtPad.x + origin.x;
984
1236
  const centerY = smtPad.y + origin.y;
985
1237
  if (smtPad.width > 0 && smtPad.height > 0) {
986
- const outer = createPillPath(
1238
+ const outer = createPillPath({
987
1239
  centerX,
988
1240
  centerY,
989
- smtPad.width,
990
- smtPad.height,
991
- (smtPad.ccw_rotation ?? 0) * (Math.PI / 180)
992
- );
1241
+ width: smtPad.width,
1242
+ height: smtPad.height,
1243
+ rotation: (smtPad.ccw_rotation ?? 0) * (Math.PI / 180)
1244
+ });
993
1245
  if (includeCopper) {
994
1246
  const netId = connMap.getNetConnectedToId(smtPad.pcb_smtpad_id);
995
1247
  const polygon = pathToPolygon(outer.verts);
996
1248
  if (netId) {
997
- ctx.netGeoms.get(netId)?.push(polygon);
1249
+ netGeoms.get(netId)?.push(polygon);
998
1250
  } else {
999
1251
  project.children.push(
1000
1252
  new ShapePath11({
@@ -1009,13 +1261,13 @@ var addRotatedPillSmtPad = (smtPad, ctx) => {
1009
1261
  if (includeSoldermask) {
1010
1262
  const smWidth = smtPad.width + 2 * soldermaskMargin;
1011
1263
  const smHeight = smtPad.height + 2 * soldermaskMargin;
1012
- const smOuter = createPillPath(
1264
+ const smOuter = createPillPath({
1013
1265
  centerX,
1014
1266
  centerY,
1015
- smWidth,
1016
- smHeight,
1017
- (smtPad.ccw_rotation ?? 0) * (Math.PI / 180)
1018
- );
1267
+ width: smWidth,
1268
+ height: smHeight,
1269
+ rotation: (smtPad.ccw_rotation ?? 0) * (Math.PI / 180)
1270
+ });
1019
1271
  project.children.push(
1020
1272
  new ShapePath11({
1021
1273
  cutIndex: soldermaskCutSetting.index,
@@ -1062,21 +1314,38 @@ function polygonToShapePathData(polygon) {
1062
1314
  var addPolygonSmtPad = (smtPad, ctx) => {
1063
1315
  const {
1064
1316
  project,
1065
- copperCutSetting,
1317
+ topCopperCutSetting,
1318
+ bottomCopperCutSetting,
1066
1319
  soldermaskCutSetting,
1320
+ topNetGeoms,
1321
+ bottomNetGeoms,
1067
1322
  origin,
1068
1323
  includeCopper,
1069
1324
  includeSoldermask,
1070
1325
  connMap,
1071
- soldermaskMargin
1326
+ soldermaskMargin,
1327
+ includeLayers
1072
1328
  } = ctx;
1329
+ const padLayer = smtPad.layer || "top";
1330
+ if (padLayer !== "top" && padLayer !== "bottom") {
1331
+ return;
1332
+ }
1333
+ if (!includeLayers.includes(padLayer)) {
1334
+ return;
1335
+ }
1336
+ const copperCutSetting = padLayer === "top" ? topCopperCutSetting : bottomCopperCutSetting;
1337
+ const netGeoms = padLayer === "top" ? topNetGeoms : bottomNetGeoms;
1073
1338
  if (smtPad.points.length >= 3) {
1074
- const pad = createPolygonPathFromOutline(smtPad.points, origin.x, origin.y);
1339
+ const pad = createPolygonPathFromOutline({
1340
+ outline: smtPad.points,
1341
+ offsetX: origin.x,
1342
+ offsetY: origin.y
1343
+ });
1075
1344
  if (includeCopper) {
1076
1345
  const netId = connMap.getNetConnectedToId(smtPad.pcb_smtpad_id);
1077
1346
  const polygon = pathToPolygon(pad.verts);
1078
1347
  if (netId) {
1079
- ctx.netGeoms.get(netId)?.push(polygon);
1348
+ netGeoms.get(netId)?.push(polygon);
1080
1349
  } else {
1081
1350
  project.children.push(
1082
1351
  new ShapePath12({
@@ -1106,33 +1375,46 @@ import { ShapePath as ShapePath13 } from "lbrnts";
1106
1375
  var addRotatedRectSmtPad = (smtPad, ctx) => {
1107
1376
  const {
1108
1377
  project,
1109
- copperCutSetting,
1378
+ topCopperCutSetting,
1379
+ bottomCopperCutSetting,
1110
1380
  soldermaskCutSetting,
1381
+ topNetGeoms,
1382
+ bottomNetGeoms,
1111
1383
  origin,
1112
1384
  includeCopper,
1113
1385
  includeSoldermask,
1114
1386
  connMap,
1115
- soldermaskMargin
1387
+ soldermaskMargin,
1388
+ includeLayers
1116
1389
  } = ctx;
1390
+ const padLayer = smtPad.layer || "top";
1391
+ if (padLayer !== "top" && padLayer !== "bottom") {
1392
+ return;
1393
+ }
1394
+ if (!includeLayers.includes(padLayer)) {
1395
+ return;
1396
+ }
1397
+ const copperCutSetting = padLayer === "top" ? topCopperCutSetting : bottomCopperCutSetting;
1398
+ const netGeoms = padLayer === "top" ? topNetGeoms : bottomNetGeoms;
1117
1399
  const centerX = smtPad.x + origin.x;
1118
1400
  const centerY = smtPad.y + origin.y;
1119
1401
  const rotation = (smtPad.ccw_rotation ?? 0) * (Math.PI / 180);
1120
1402
  const borderRadius = smtPad.rect_border_radius ?? 0;
1121
1403
  if (smtPad.width > 0 && smtPad.height > 0) {
1122
- const outer = createRoundedRectPath(
1404
+ const outer = createRoundedRectPath({
1123
1405
  centerX,
1124
1406
  centerY,
1125
- smtPad.width,
1126
- smtPad.height,
1407
+ width: smtPad.width,
1408
+ height: smtPad.height,
1127
1409
  borderRadius,
1128
- 4,
1410
+ segments: 4,
1129
1411
  rotation
1130
- );
1412
+ });
1131
1413
  if (includeCopper) {
1132
1414
  const netId = connMap.getNetConnectedToId(smtPad.pcb_smtpad_id);
1133
1415
  const polygon = pathToPolygon(outer.verts);
1134
1416
  if (netId) {
1135
- ctx.netGeoms.get(netId)?.push(polygon);
1417
+ netGeoms.get(netId)?.push(polygon);
1136
1418
  } else {
1137
1419
  project.children.push(
1138
1420
  new ShapePath13({
@@ -1147,15 +1429,15 @@ var addRotatedRectSmtPad = (smtPad, ctx) => {
1147
1429
  if (includeSoldermask) {
1148
1430
  const smWidth = smtPad.width + 2 * soldermaskMargin;
1149
1431
  const smHeight = smtPad.height + 2 * soldermaskMargin;
1150
- const smOuter = createRoundedRectPath(
1432
+ const smOuter = createRoundedRectPath({
1151
1433
  centerX,
1152
1434
  centerY,
1153
- smWidth,
1154
- smHeight,
1435
+ width: smWidth,
1436
+ height: smHeight,
1155
1437
  borderRadius,
1156
- 4,
1438
+ segments: 4,
1157
1439
  rotation
1158
- );
1440
+ });
1159
1441
  project.children.push(
1160
1442
  new ShapePath13({
1161
1443
  cutIndex: soldermaskCutSetting.index,
@@ -1191,7 +1473,14 @@ var addSmtPad = (smtPad, ctx) => {
1191
1473
  // lib/element-handlers/addPcbTrace/index.ts
1192
1474
  import Flatten2, { BooleanOperations } from "@flatten-js/core";
1193
1475
  var addPcbTrace = (trace, ctx) => {
1194
- const { netGeoms, connMap, origin, includeCopper, includeSoldermask } = ctx;
1476
+ const {
1477
+ topNetGeoms,
1478
+ bottomNetGeoms,
1479
+ connMap,
1480
+ origin,
1481
+ includeCopper,
1482
+ includeLayers
1483
+ } = ctx;
1195
1484
  if (!includeCopper) {
1196
1485
  return;
1197
1486
  }
@@ -1199,60 +1488,113 @@ var addPcbTrace = (trace, ctx) => {
1199
1488
  trace.source_trace_id ?? trace.pcb_trace_id
1200
1489
  );
1201
1490
  if (!netId) {
1202
- console.warn(`Trace ${trace.pcb_trace_id} is not connected to any net`);
1203
1491
  return;
1204
1492
  }
1205
1493
  if (!trace.route || trace.route.length < 2) {
1206
- console.warn(`Trace ${trace.pcb_trace_id} has insufficient route points`);
1207
1494
  return;
1208
1495
  }
1209
1496
  const { route } = trace;
1210
- const traceWidth = route.find((point6) => point6.route_type === "wire")?.width ?? 0.15;
1211
- const polygons = [];
1212
- for (const routePoint of route) {
1213
- const circle = new Flatten2.Circle(
1214
- new Flatten2.Point(routePoint.x + origin.x, routePoint.y + origin.y),
1215
- traceWidth / 2
1216
- );
1217
- polygons.push(circleToPolygon(circle));
1218
- }
1219
- for (let i = 0; i < route.length - 1; i++) {
1220
- const p1 = route[i];
1221
- const p2 = route[i + 1];
1222
- if (!p1 || !p2) continue;
1223
- const segmentLength = Math.hypot(p1.x - p2.x, p1.y - p2.y);
1224
- if (segmentLength === 0) continue;
1225
- const centerX = (p1.x + p2.x) / 2 + origin.x;
1226
- const centerY = (p1.y + p2.y) / 2 + origin.y;
1227
- const rotationDeg = Math.atan2(p2.y - p1.y, p2.x - p1.x) * 180 / Math.PI;
1228
- const w2 = segmentLength / 2;
1229
- const h2 = traceWidth / 2;
1230
- const angleRad = rotationDeg * Math.PI / 180;
1231
- const cosAngle = Math.cos(angleRad);
1232
- const sinAngle = Math.sin(angleRad);
1233
- const corners = [
1234
- { x: -w2, y: -h2 },
1235
- { x: w2, y: -h2 },
1236
- { x: w2, y: h2 },
1237
- { x: -w2, y: h2 }
1238
- ];
1239
- const rotatedCorners = corners.map((p) => ({
1240
- x: centerX + p.x * cosAngle - p.y * sinAngle,
1241
- y: centerY + p.x * sinAngle + p.y * cosAngle
1242
- }));
1243
- polygons.push(
1244
- new Flatten2.Polygon(rotatedCorners.map((p) => Flatten2.point(p.x, p.y)))
1245
- );
1497
+ const wirePoint = route.find((point6) => {
1498
+ if (!("route_type" in point6)) return true;
1499
+ return point6.route_type === "wire";
1500
+ });
1501
+ const traceWidth = wirePoint?.width ?? 0.15;
1502
+ const layerSegments = /* @__PURE__ */ new Map();
1503
+ let currentSegment = [];
1504
+ let currentLayer = null;
1505
+ for (const point6 of route) {
1506
+ if ("route_type" in point6 && point6.route_type === "via") {
1507
+ if (currentLayer && currentSegment.length > 0) {
1508
+ if (!layerSegments.has(currentLayer)) {
1509
+ layerSegments.set(currentLayer, []);
1510
+ }
1511
+ layerSegments.get(currentLayer).push(currentSegment);
1512
+ }
1513
+ currentSegment = [];
1514
+ currentLayer = null;
1515
+ continue;
1516
+ }
1517
+ const isWirePoint = !("route_type" in point6) || point6.route_type === "wire";
1518
+ if (isWirePoint && "layer" in point6 && point6.layer) {
1519
+ const pointLayer = point6.layer;
1520
+ if (pointLayer !== "top" && pointLayer !== "bottom") {
1521
+ continue;
1522
+ }
1523
+ if (currentLayer !== null && currentLayer !== pointLayer) {
1524
+ if (currentSegment.length > 0) {
1525
+ if (!layerSegments.has(currentLayer)) {
1526
+ layerSegments.set(currentLayer, []);
1527
+ }
1528
+ layerSegments.get(currentLayer).push(currentSegment);
1529
+ }
1530
+ currentSegment = [];
1531
+ }
1532
+ currentLayer = pointLayer;
1533
+ currentSegment.push({ x: point6.x, y: point6.y });
1534
+ }
1535
+ }
1536
+ if (currentLayer && currentSegment.length > 0) {
1537
+ if (!layerSegments.has(currentLayer)) {
1538
+ layerSegments.set(currentLayer, []);
1539
+ }
1540
+ layerSegments.get(currentLayer).push(currentSegment);
1246
1541
  }
1247
- if (polygons.length === 0) return;
1248
- let tracePolygon = polygons[0];
1249
- for (let i = 1; i < polygons.length; i++) {
1250
- const poly = polygons[i];
1251
- if (poly) {
1252
- tracePolygon = BooleanOperations.unify(tracePolygon, poly);
1542
+ for (const [layer, segments] of layerSegments.entries()) {
1543
+ if (!includeLayers.includes(layer)) {
1544
+ continue;
1545
+ }
1546
+ const polygons = [];
1547
+ for (const points of segments) {
1548
+ if (points.length < 2) continue;
1549
+ for (const routePoint of points) {
1550
+ const circle = new Flatten2.Circle(
1551
+ new Flatten2.Point(routePoint.x + origin.x, routePoint.y + origin.y),
1552
+ traceWidth / 2
1553
+ );
1554
+ polygons.push(circleToPolygon(circle));
1555
+ }
1556
+ for (let i = 0; i < points.length - 1; i++) {
1557
+ const p1 = points[i];
1558
+ const p2 = points[i + 1];
1559
+ if (!p1 || !p2) continue;
1560
+ const segmentLength = Math.hypot(p1.x - p2.x, p1.y - p2.y);
1561
+ if (segmentLength === 0) continue;
1562
+ const centerX = (p1.x + p2.x) / 2 + origin.x;
1563
+ const centerY = (p1.y + p2.y) / 2 + origin.y;
1564
+ const rotationDeg = Math.atan2(p2.y - p1.y, p2.x - p1.x) * 180 / Math.PI;
1565
+ const w2 = segmentLength / 2;
1566
+ const h2 = traceWidth / 2;
1567
+ const angleRad = rotationDeg * Math.PI / 180;
1568
+ const cosAngle = Math.cos(angleRad);
1569
+ const sinAngle = Math.sin(angleRad);
1570
+ const corners = [
1571
+ { x: -w2, y: -h2 },
1572
+ { x: w2, y: -h2 },
1573
+ { x: w2, y: h2 },
1574
+ { x: -w2, y: h2 }
1575
+ ];
1576
+ const rotatedCorners = corners.map((p) => ({
1577
+ x: centerX + p.x * cosAngle - p.y * sinAngle,
1578
+ y: centerY + p.x * sinAngle + p.y * cosAngle
1579
+ }));
1580
+ polygons.push(
1581
+ new Flatten2.Polygon(
1582
+ rotatedCorners.map((p) => Flatten2.point(p.x, p.y))
1583
+ )
1584
+ );
1585
+ }
1586
+ }
1587
+ if (polygons.length === 0) continue;
1588
+ let tracePolygon = polygons[0];
1589
+ for (let i = 1; i < polygons.length; i++) {
1590
+ const poly = polygons[i];
1591
+ if (poly) {
1592
+ tracePolygon = BooleanOperations.unify(tracePolygon, poly);
1593
+ }
1253
1594
  }
1595
+ const netGeoms = layer === "top" ? topNetGeoms : bottomNetGeoms;
1596
+ netGeoms.get(netId)?.push(tracePolygon);
1254
1597
  }
1255
- netGeoms.get(netId)?.push(tracePolygon);
1256
1598
  };
1257
1599
 
1258
1600
  // lib/element-handlers/addPcbBoard/index.ts
@@ -1379,14 +1721,18 @@ var addPcbVia = (via, ctx) => {
1379
1721
  const {
1380
1722
  db,
1381
1723
  project,
1382
- copperCutSetting,
1724
+ topCopperCutSetting,
1725
+ bottomCopperCutSetting,
1383
1726
  soldermaskCutSetting,
1384
1727
  throughBoardCutSetting,
1728
+ topNetGeoms,
1729
+ bottomNetGeoms,
1385
1730
  origin,
1386
1731
  includeCopper,
1387
1732
  includeSoldermask,
1388
1733
  connMap,
1389
- soldermaskMargin
1734
+ soldermaskMargin,
1735
+ includeLayers
1390
1736
  } = ctx;
1391
1737
  const centerX = via.x + origin.x;
1392
1738
  const centerY = via.y + origin.y;
@@ -1397,22 +1743,47 @@ var addPcbVia = (via, ctx) => {
1397
1743
  const circle = new Circle3(point5(centerX, centerY), outerRadius);
1398
1744
  const polygon = circleToPolygon(circle);
1399
1745
  if (netId) {
1400
- ctx.netGeoms.get(netId)?.push(polygon);
1746
+ if (includeLayers.includes("top")) {
1747
+ topNetGeoms.get(netId)?.push(polygon.clone());
1748
+ }
1749
+ if (includeLayers.includes("bottom")) {
1750
+ bottomNetGeoms.get(netId)?.push(polygon.clone());
1751
+ }
1401
1752
  } else {
1402
- const outer = createCirclePath(centerX, centerY, outerRadius);
1403
- project.children.push(
1404
- new ShapePath15({
1405
- cutIndex: copperCutSetting.index,
1406
- verts: outer.verts,
1407
- prims: outer.prims,
1408
- isClosed: true
1409
- })
1410
- );
1753
+ const outer = createCirclePath({
1754
+ centerX,
1755
+ centerY,
1756
+ radius: outerRadius
1757
+ });
1758
+ if (includeLayers.includes("top")) {
1759
+ project.children.push(
1760
+ new ShapePath15({
1761
+ cutIndex: topCopperCutSetting.index,
1762
+ verts: outer.verts,
1763
+ prims: outer.prims,
1764
+ isClosed: true
1765
+ })
1766
+ );
1767
+ }
1768
+ if (includeLayers.includes("bottom")) {
1769
+ project.children.push(
1770
+ new ShapePath15({
1771
+ cutIndex: bottomCopperCutSetting.index,
1772
+ verts: outer.verts,
1773
+ prims: outer.prims,
1774
+ isClosed: true
1775
+ })
1776
+ );
1777
+ }
1411
1778
  }
1412
1779
  }
1413
1780
  if (via.outer_diameter > 0 && includeSoldermask) {
1414
1781
  const smRadius = via.outer_diameter / 2 + soldermaskMargin;
1415
- const outer = createCirclePath(centerX, centerY, smRadius);
1782
+ const outer = createCirclePath({
1783
+ centerX,
1784
+ centerY,
1785
+ radius: smRadius
1786
+ });
1416
1787
  project.children.push(
1417
1788
  new ShapePath15({
1418
1789
  cutIndex: soldermaskCutSetting.index,
@@ -1424,7 +1795,11 @@ var addPcbVia = (via, ctx) => {
1424
1795
  }
1425
1796
  if (via.hole_diameter > 0 && includeCopper) {
1426
1797
  const innerRadius = via.hole_diameter / 2;
1427
- const inner = createCirclePath(centerX, centerY, innerRadius);
1798
+ const inner = createCirclePath({
1799
+ centerX,
1800
+ centerY,
1801
+ radius: innerRadius
1802
+ });
1428
1803
  project.children.push(
1429
1804
  new ShapePath15({
1430
1805
  cutIndex: throughBoardCutSetting.index,
@@ -1452,7 +1827,11 @@ var addCirclePcbHole = (hole, ctx) => {
1452
1827
  const centerY = hole.y + origin.y;
1453
1828
  if (hole.hole_diameter > 0 && includeSoldermask) {
1454
1829
  const smRadius = hole.hole_diameter / 2 + soldermaskMargin;
1455
- const soldermaskPath = createCirclePath(centerX, centerY, smRadius);
1830
+ const soldermaskPath = createCirclePath({
1831
+ centerX,
1832
+ centerY,
1833
+ radius: smRadius
1834
+ });
1456
1835
  project.children.push(
1457
1836
  new ShapePath16({
1458
1837
  cutIndex: soldermaskCutSetting.index,
@@ -1464,7 +1843,11 @@ var addCirclePcbHole = (hole, ctx) => {
1464
1843
  }
1465
1844
  if (hole.hole_diameter > 0 && includeCopper) {
1466
1845
  const radius = hole.hole_diameter / 2;
1467
- const circlePath = createCirclePath(centerX, centerY, radius);
1846
+ const circlePath = createCirclePath({
1847
+ centerX,
1848
+ centerY,
1849
+ radius
1850
+ });
1468
1851
  project.children.push(
1469
1852
  new ShapePath16({
1470
1853
  cutIndex: throughBoardCutSetting.index,
@@ -1491,14 +1874,14 @@ var addRectPcbHole = (hole, ctx) => {
1491
1874
  const centerX = hole.x + origin.x;
1492
1875
  const centerY = hole.y + origin.y;
1493
1876
  if (hole.hole_width > 0 && hole.hole_height > 0 && includeSoldermask) {
1494
- const soldermaskPath = createRoundedRectPath(
1877
+ const soldermaskPath = createRoundedRectPath({
1495
1878
  centerX,
1496
1879
  centerY,
1497
- hole.hole_width + soldermaskMargin * 2,
1498
- hole.hole_height + soldermaskMargin * 2,
1499
- 0
1880
+ width: hole.hole_width + soldermaskMargin * 2,
1881
+ height: hole.hole_height + soldermaskMargin * 2,
1882
+ borderRadius: 0
1500
1883
  // no border radius for rect holes
1501
- );
1884
+ });
1502
1885
  project.children.push(
1503
1886
  new ShapePath17({
1504
1887
  cutIndex: soldermaskCutSetting.index,
@@ -1509,14 +1892,14 @@ var addRectPcbHole = (hole, ctx) => {
1509
1892
  );
1510
1893
  }
1511
1894
  if (hole.hole_width > 0 && hole.hole_height > 0 && includeCopper) {
1512
- const rectPath = createRoundedRectPath(
1895
+ const rectPath = createRoundedRectPath({
1513
1896
  centerX,
1514
1897
  centerY,
1515
- hole.hole_width,
1516
- hole.hole_height,
1517
- 0
1898
+ width: hole.hole_width,
1899
+ height: hole.hole_height,
1900
+ borderRadius: 0
1518
1901
  // no border radius for rect holes
1519
- );
1902
+ });
1520
1903
  project.children.push(
1521
1904
  new ShapePath17({
1522
1905
  cutIndex: throughBoardCutSetting.index,
@@ -1543,12 +1926,12 @@ var addOvalPcbHole = (hole, ctx) => {
1543
1926
  const centerX = hole.x + origin.x;
1544
1927
  const centerY = hole.y + origin.y;
1545
1928
  if (hole.hole_width > 0 && hole.hole_height > 0 && includeSoldermask) {
1546
- const soldermaskPath = createOvalPath(
1929
+ const soldermaskPath = createOvalPath({
1547
1930
  centerX,
1548
1931
  centerY,
1549
- hole.hole_width + soldermaskMargin * 2,
1550
- hole.hole_height + soldermaskMargin * 2
1551
- );
1932
+ width: hole.hole_width + soldermaskMargin * 2,
1933
+ height: hole.hole_height + soldermaskMargin * 2
1934
+ });
1552
1935
  project.children.push(
1553
1936
  new ShapePath18({
1554
1937
  cutIndex: soldermaskCutSetting.index,
@@ -1559,12 +1942,12 @@ var addOvalPcbHole = (hole, ctx) => {
1559
1942
  );
1560
1943
  }
1561
1944
  if (hole.hole_width > 0 && hole.hole_height > 0 && includeCopper) {
1562
- const ovalPath = createOvalPath(
1945
+ const ovalPath = createOvalPath({
1563
1946
  centerX,
1564
1947
  centerY,
1565
- hole.hole_width,
1566
- hole.hole_height
1567
- );
1948
+ width: hole.hole_width,
1949
+ height: hole.hole_height
1950
+ });
1568
1951
  project.children.push(
1569
1952
  new ShapePath18({
1570
1953
  cutIndex: throughBoardCutSetting.index,
@@ -1591,12 +1974,12 @@ var addPillPcbHole = (hole, ctx) => {
1591
1974
  const centerX = hole.x + origin.x;
1592
1975
  const centerY = hole.y + origin.y;
1593
1976
  if (hole.hole_width > 0 && hole.hole_height > 0 && includeSoldermask) {
1594
- const soldermaskPath = createPillPath(
1977
+ const soldermaskPath = createPillPath({
1595
1978
  centerX,
1596
1979
  centerY,
1597
- hole.hole_width + soldermaskMargin * 2,
1598
- hole.hole_height + soldermaskMargin * 2
1599
- );
1980
+ width: hole.hole_width + soldermaskMargin * 2,
1981
+ height: hole.hole_height + soldermaskMargin * 2
1982
+ });
1600
1983
  project.children.push(
1601
1984
  new ShapePath19({
1602
1985
  cutIndex: soldermaskCutSetting.index,
@@ -1607,12 +1990,12 @@ var addPillPcbHole = (hole, ctx) => {
1607
1990
  );
1608
1991
  }
1609
1992
  if (hole.hole_width > 0 && hole.hole_height > 0 && includeCopper) {
1610
- const pillPath = createPillPath(
1993
+ const pillPath = createPillPath({
1611
1994
  centerX,
1612
1995
  centerY,
1613
- hole.hole_width,
1614
- hole.hole_height
1615
- );
1996
+ width: hole.hole_width,
1997
+ height: hole.hole_height
1998
+ });
1616
1999
  project.children.push(
1617
2000
  new ShapePath19({
1618
2001
  cutIndex: throughBoardCutSetting.index,
@@ -1640,13 +2023,13 @@ var addRotatedPillPcbHole = (hole, ctx) => {
1640
2023
  const centerY = hole.y + origin.y;
1641
2024
  const rotation = (hole.ccw_rotation || 0) * (Math.PI / 180);
1642
2025
  if (hole.hole_width > 0 && hole.hole_height > 0 && includeSoldermask) {
1643
- const soldermaskPath = createPillPath(
2026
+ const soldermaskPath = createPillPath({
1644
2027
  centerX,
1645
2028
  centerY,
1646
- hole.hole_width + soldermaskMargin * 2,
1647
- hole.hole_height + soldermaskMargin * 2,
2029
+ width: hole.hole_width + soldermaskMargin * 2,
2030
+ height: hole.hole_height + soldermaskMargin * 2,
1648
2031
  rotation
1649
- );
2032
+ });
1650
2033
  project.children.push(
1651
2034
  new ShapePath20({
1652
2035
  cutIndex: soldermaskCutSetting.index,
@@ -1657,13 +2040,13 @@ var addRotatedPillPcbHole = (hole, ctx) => {
1657
2040
  );
1658
2041
  }
1659
2042
  if (hole.hole_width > 0 && hole.hole_height > 0 && includeCopper) {
1660
- const pillPath = createPillPath(
2043
+ const pillPath = createPillPath({
1661
2044
  centerX,
1662
2045
  centerY,
1663
- hole.hole_width,
1664
- hole.hole_height,
2046
+ width: hole.hole_width,
2047
+ height: hole.hole_height,
1665
2048
  rotation
1666
- );
2049
+ });
1667
2050
  project.children.push(
1668
2051
  new ShapePath20({
1669
2052
  cutIndex: throughBoardCutSetting.index,
@@ -1710,7 +2093,11 @@ var addCirclePcbCutout = (cutout, ctx) => {
1710
2093
  const centerX = cutout.center.x + origin.x;
1711
2094
  const centerY = cutout.center.y + origin.y;
1712
2095
  if (cutout.radius > 0 && includeCopper) {
1713
- const circlePath = createCirclePath(centerX, centerY, cutout.radius);
2096
+ const circlePath = createCirclePath({
2097
+ centerX,
2098
+ centerY,
2099
+ radius: cutout.radius
2100
+ });
1714
2101
  project.children.push(
1715
2102
  new ShapePath21({
1716
2103
  cutIndex: throughBoardCutSetting.index,
@@ -1722,7 +2109,11 @@ var addCirclePcbCutout = (cutout, ctx) => {
1722
2109
  }
1723
2110
  if (cutout.radius > 0 && includeSoldermask) {
1724
2111
  const smRadius = cutout.radius + soldermaskMargin;
1725
- const outer = createCirclePath(centerX, centerY, smRadius);
2112
+ const outer = createCirclePath({
2113
+ centerX,
2114
+ centerY,
2115
+ radius: smRadius
2116
+ });
1726
2117
  project.children.push(
1727
2118
  new ShapePath21({
1728
2119
  cutIndex: soldermaskCutSetting.index,
@@ -1750,17 +2141,17 @@ var addRectPcbCutout = (cutout, ctx) => {
1750
2141
  const centerY = cutout.center.y + origin.y;
1751
2142
  if (cutout.width > 0 && cutout.height > 0 && includeCopper) {
1752
2143
  const rotation = (cutout.rotation ?? 0) * (Math.PI / 180);
1753
- const rectPath = createRoundedRectPath(
2144
+ const rectPath = createRoundedRectPath({
1754
2145
  centerX,
1755
2146
  centerY,
1756
- cutout.width,
1757
- cutout.height,
1758
- 0,
2147
+ width: cutout.width,
2148
+ height: cutout.height,
2149
+ borderRadius: 0,
1759
2150
  // no border radius for cutouts
1760
- 4,
2151
+ segments: 4,
1761
2152
  // segments
1762
2153
  rotation
1763
- );
2154
+ });
1764
2155
  project.children.push(
1765
2156
  new ShapePath22({
1766
2157
  cutIndex: throughBoardCutSetting.index,
@@ -1774,17 +2165,17 @@ var addRectPcbCutout = (cutout, ctx) => {
1774
2165
  const rotation = (cutout.rotation ?? 0) * (Math.PI / 180);
1775
2166
  const smWidth = cutout.width + 2 * soldermaskMargin;
1776
2167
  const smHeight = cutout.height + 2 * soldermaskMargin;
1777
- const rectPath = createRoundedRectPath(
2168
+ const rectPath = createRoundedRectPath({
1778
2169
  centerX,
1779
2170
  centerY,
1780
- smWidth,
1781
- smHeight,
1782
- 0,
2171
+ width: smWidth,
2172
+ height: smHeight,
2173
+ borderRadius: 0,
1783
2174
  // no border radius for cutouts
1784
- 4,
2175
+ segments: 4,
1785
2176
  // segments
1786
2177
  rotation
1787
- );
2178
+ });
1788
2179
  project.children.push(
1789
2180
  new ShapePath22({
1790
2181
  cutIndex: soldermaskCutSetting.index,
@@ -1809,11 +2200,11 @@ var addPolygonPcbCutout = (cutout, ctx) => {
1809
2200
  soldermaskMargin
1810
2201
  } = ctx;
1811
2202
  if (cutout.points.length >= 3 && includeCopper) {
1812
- const polygonPath = createPolygonPathFromOutline(
1813
- cutout.points,
1814
- origin.x,
1815
- origin.y
1816
- );
2203
+ const polygonPath = createPolygonPathFromOutline({
2204
+ outline: cutout.points,
2205
+ offsetX: origin.x,
2206
+ offsetY: origin.y
2207
+ });
1817
2208
  project.children.push(
1818
2209
  new ShapePath23({
1819
2210
  cutIndex: throughBoardCutSetting.index,
@@ -1828,7 +2219,11 @@ var addPolygonPcbCutout = (cutout, ctx) => {
1828
2219
  x: (p.x ?? 0) + (p.x ?? 0) > 0 ? soldermaskMargin : -soldermaskMargin,
1829
2220
  y: (p.y ?? 0) + (p.y ?? 0) > 0 ? soldermaskMargin : -soldermaskMargin
1830
2221
  })) : cutout.points;
1831
- const polygonPath = createPolygonPathFromOutline(points, origin.x, origin.y);
2222
+ const polygonPath = createPolygonPathFromOutline({
2223
+ outline: points,
2224
+ offsetX: origin.x,
2225
+ offsetY: origin.y
2226
+ });
1832
2227
  project.children.push(
1833
2228
  new ShapePath23({
1834
2229
  cutIndex: soldermaskCutSetting.index,
@@ -1918,15 +2313,23 @@ var convertCircuitJsonToLbrn = (circuitJson, options = {}) => {
1918
2313
  appVersion: "1.7.03",
1919
2314
  formatVersion: "1"
1920
2315
  });
1921
- const copperCutSetting = new CutSetting({
2316
+ const includeLayers = options.includeLayers ?? ["top", "bottom"];
2317
+ const topCopperCutSetting = new CutSetting({
1922
2318
  index: 0,
1923
- name: "Cut Copper",
2319
+ name: "Cut Top Copper",
1924
2320
  numPasses: 12,
1925
2321
  speed: 100
1926
2322
  });
1927
- project.children.push(copperCutSetting);
1928
- const throughBoardCutSetting = new CutSetting({
2323
+ project.children.push(topCopperCutSetting);
2324
+ const bottomCopperCutSetting = new CutSetting({
1929
2325
  index: 1,
2326
+ name: "Cut Bottom Copper",
2327
+ numPasses: 12,
2328
+ speed: 100
2329
+ });
2330
+ project.children.push(bottomCopperCutSetting);
2331
+ const throughBoardCutSetting = new CutSetting({
2332
+ index: 2,
1930
2333
  name: "Cut Through Board",
1931
2334
  numPasses: 3,
1932
2335
  speed: 50
@@ -1934,8 +2337,8 @@ var convertCircuitJsonToLbrn = (circuitJson, options = {}) => {
1934
2337
  project.children.push(throughBoardCutSetting);
1935
2338
  const soldermaskCutSetting = new CutSetting({
1936
2339
  type: "Scan",
1937
- // Use Scan mode to fill the pad shapes for Kapton tape cutting
1938
- index: 2,
2340
+ // Use Scan mode to fill pad shapes for Kapton tape cutting
2341
+ index: 3,
1939
2342
  name: "Cut Soldermask",
1940
2343
  numPasses: 1,
1941
2344
  speed: 150,
@@ -1944,7 +2347,7 @@ var convertCircuitJsonToLbrn = (circuitJson, options = {}) => {
1944
2347
  interval: 0.18,
1945
2348
  // Distance between cross-hatch lines
1946
2349
  angle: 45,
1947
- // Angle of the cross-hatch lines
2350
+ // Angle of cross-hatch lines
1948
2351
  crossHatch: true
1949
2352
  });
1950
2353
  project.children.push(soldermaskCutSetting);
@@ -1957,18 +2360,22 @@ var convertCircuitJsonToLbrn = (circuitJson, options = {}) => {
1957
2360
  const ctx = {
1958
2361
  db,
1959
2362
  project,
1960
- copperCutSetting,
2363
+ topCopperCutSetting,
2364
+ bottomCopperCutSetting,
1961
2365
  throughBoardCutSetting,
1962
2366
  soldermaskCutSetting,
1963
2367
  connMap,
1964
- netGeoms: /* @__PURE__ */ new Map(),
2368
+ topNetGeoms: /* @__PURE__ */ new Map(),
2369
+ bottomNetGeoms: /* @__PURE__ */ new Map(),
1965
2370
  origin,
1966
2371
  includeCopper: options.includeCopper ?? true,
1967
2372
  includeSoldermask: options.includeSoldermask ?? false,
1968
- soldermaskMargin: options.soldermaskMargin ?? 0
2373
+ soldermaskMargin: options.soldermaskMargin ?? 0,
2374
+ includeLayers
1969
2375
  };
1970
2376
  for (const net of Object.keys(connMap.netMap)) {
1971
- ctx.netGeoms.set(net, []);
2377
+ ctx.topNetGeoms.set(net, []);
2378
+ ctx.bottomNetGeoms.set(net, []);
1972
2379
  }
1973
2380
  for (const smtpad of db.pcb_smtpad.list()) {
1974
2381
  addSmtPad(smtpad, ctx);
@@ -1992,32 +2399,64 @@ var convertCircuitJsonToLbrn = (circuitJson, options = {}) => {
1992
2399
  addPcbCutout(cutout, ctx);
1993
2400
  }
1994
2401
  if (ctx.includeCopper) {
1995
- for (const net of Object.keys(connMap.netMap)) {
1996
- const netGeoms = ctx.netGeoms.get(net);
1997
- if (netGeoms.length === 0) {
1998
- continue;
1999
- }
2000
- let union = netGeoms[0];
2001
- if (union instanceof Box2) {
2002
- union = new Polygon4(union);
2003
- }
2004
- for (const geom of netGeoms.slice(1)) {
2005
- if (geom instanceof Polygon4) {
2006
- union = BooleanOperations2.unify(union, geom);
2007
- } else if (geom instanceof Box2) {
2008
- union = BooleanOperations2.unify(union, new Polygon4(geom));
2402
+ if (includeLayers.includes("top")) {
2403
+ for (const net of Object.keys(connMap.netMap)) {
2404
+ const netGeoms = ctx.topNetGeoms.get(net);
2405
+ if (netGeoms.length === 0) {
2406
+ continue;
2407
+ }
2408
+ let union = netGeoms[0];
2409
+ if (union instanceof Box2) {
2410
+ union = new Polygon4(union);
2411
+ }
2412
+ for (const geom of netGeoms.slice(1)) {
2413
+ if (geom instanceof Polygon4) {
2414
+ union = BooleanOperations2.unify(union, geom);
2415
+ } else if (geom instanceof Box2) {
2416
+ union = BooleanOperations2.unify(union, new Polygon4(geom));
2417
+ }
2418
+ }
2419
+ for (const island of union.splitToIslands()) {
2420
+ const { verts, prims } = polygonToShapePathData(island);
2421
+ project.children.push(
2422
+ new ShapePath25({
2423
+ cutIndex: topCopperCutSetting.index,
2424
+ verts,
2425
+ prims,
2426
+ isClosed: false
2427
+ })
2428
+ );
2009
2429
  }
2010
2430
  }
2011
- for (const island of union.splitToIslands()) {
2012
- const { verts, prims } = polygonToShapePathData(island);
2013
- project.children.push(
2014
- new ShapePath25({
2015
- cutIndex: copperCutSetting.index,
2016
- verts,
2017
- prims,
2018
- isClosed: false
2019
- })
2020
- );
2431
+ }
2432
+ if (includeLayers.includes("bottom")) {
2433
+ for (const net of Object.keys(connMap.netMap)) {
2434
+ const netGeoms = ctx.bottomNetGeoms.get(net);
2435
+ if (netGeoms.length === 0) {
2436
+ continue;
2437
+ }
2438
+ let union = netGeoms[0];
2439
+ if (union instanceof Box2) {
2440
+ union = new Polygon4(union);
2441
+ }
2442
+ for (const geom of netGeoms.slice(1)) {
2443
+ if (geom instanceof Polygon4) {
2444
+ union = BooleanOperations2.unify(union, geom);
2445
+ } else if (geom instanceof Box2) {
2446
+ union = BooleanOperations2.unify(union, new Polygon4(geom));
2447
+ }
2448
+ }
2449
+ for (const island of union.splitToIslands()) {
2450
+ const { verts, prims } = polygonToShapePathData(island);
2451
+ project.children.push(
2452
+ new ShapePath25({
2453
+ cutIndex: bottomCopperCutSetting.index,
2454
+ verts,
2455
+ prims,
2456
+ isClosed: false
2457
+ })
2458
+ );
2459
+ }
2021
2460
  }
2022
2461
  }
2023
2462
  }