@tscircuit/3d-viewer 0.0.437 → 0.0.439

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 (2) hide show
  1. package/dist/index.js +681 -276
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -1053,7 +1053,7 @@ var require_rotateY = __commonJS({
1053
1053
  var require_rotateZ = __commonJS({
1054
1054
  "node_modules/@jscad/modeling/src/maths/vec3/rotateZ.js"(exports, module) {
1055
1055
  "use strict";
1056
- var rotateZ3 = (out, vector, origin, radians) => {
1056
+ var rotateZ4 = (out, vector, origin, radians) => {
1057
1057
  const p = [];
1058
1058
  const r = [];
1059
1059
  p[0] = vector[0] - origin[0];
@@ -1065,7 +1065,7 @@ var require_rotateZ = __commonJS({
1065
1065
  out[2] = vector[2];
1066
1066
  return out;
1067
1067
  };
1068
- module.exports = rotateZ3;
1068
+ module.exports = rotateZ4;
1069
1069
  }
1070
1070
  });
1071
1071
 
@@ -1607,7 +1607,7 @@ var require_rotateZ2 = __commonJS({
1607
1607
  "node_modules/@jscad/modeling/src/maths/mat4/rotateZ.js"(exports, module) {
1608
1608
  "use strict";
1609
1609
  var { sin: sin2, cos: cos2 } = require_trigonometry();
1610
- var rotateZ3 = (out, matrix, radians) => {
1610
+ var rotateZ4 = (out, matrix, radians) => {
1611
1611
  const s = sin2(radians);
1612
1612
  const c = cos2(radians);
1613
1613
  const a00 = matrix[0];
@@ -1638,7 +1638,7 @@ var require_rotateZ2 = __commonJS({
1638
1638
  out[7] = a13 * c - a03 * s;
1639
1639
  return out;
1640
1640
  };
1641
- module.exports = rotateZ3;
1641
+ module.exports = rotateZ4;
1642
1642
  }
1643
1643
  });
1644
1644
 
@@ -1712,7 +1712,7 @@ var require_toString2 = __commonJS({
1712
1712
  var require_translate = __commonJS({
1713
1713
  "node_modules/@jscad/modeling/src/maths/mat4/translate.js"(exports, module) {
1714
1714
  "use strict";
1715
- var translate9 = (out, matrix, offsets) => {
1715
+ var translate10 = (out, matrix, offsets) => {
1716
1716
  const x = offsets[0];
1717
1717
  const y = offsets[1];
1718
1718
  const z134 = offsets[2];
@@ -1765,7 +1765,7 @@ var require_translate = __commonJS({
1765
1765
  }
1766
1766
  return out;
1767
1767
  };
1768
- module.exports = translate9;
1768
+ module.exports = translate10;
1769
1769
  }
1770
1770
  });
1771
1771
 
@@ -7502,7 +7502,7 @@ var require_ellipse = __commonJS({
7502
7502
  var geom2 = require_geom2();
7503
7503
  var { sin: sin2, cos: cos2 } = require_trigonometry();
7504
7504
  var { isGTE, isNumberArray } = require_commonChecks();
7505
- var ellipse = (options) => {
7505
+ var ellipse2 = (options) => {
7506
7506
  const defaults = {
7507
7507
  center: [0, 0],
7508
7508
  radius: [1, 1],
@@ -7544,7 +7544,7 @@ var require_ellipse = __commonJS({
7544
7544
  if (rotation2 < TAU) points.push(centerv);
7545
7545
  return geom2.fromPoints(points);
7546
7546
  };
7547
- module.exports = ellipse;
7547
+ module.exports = ellipse2;
7548
7548
  }
7549
7549
  });
7550
7550
 
@@ -7553,7 +7553,7 @@ var require_circle = __commonJS({
7553
7553
  "node_modules/@jscad/modeling/src/primitives/circle.js"(exports, module) {
7554
7554
  "use strict";
7555
7555
  var { TAU } = require_constants();
7556
- var ellipse = require_ellipse();
7556
+ var ellipse2 = require_ellipse();
7557
7557
  var { isGTE } = require_commonChecks();
7558
7558
  var circle2 = (options) => {
7559
7559
  const defaults = {
@@ -7566,7 +7566,7 @@ var require_circle = __commonJS({
7566
7566
  let { center, radius, startAngle, endAngle, segments } = Object.assign({}, defaults, options);
7567
7567
  if (!isGTE(radius, 0)) throw new Error("radius must be positive");
7568
7568
  radius = [radius, radius];
7569
- return ellipse({ center, radius, startAngle, endAngle, segments });
7569
+ return ellipse2({ center, radius, startAngle, endAngle, segments });
7570
7570
  };
7571
7571
  module.exports = circle2;
7572
7572
  }
@@ -7579,7 +7579,7 @@ var require_cuboid = __commonJS({
7579
7579
  var geom3 = require_geom3();
7580
7580
  var poly3 = require_poly3();
7581
7581
  var { isNumberArray } = require_commonChecks();
7582
- var cuboid4 = (options) => {
7582
+ var cuboid5 = (options) => {
7583
7583
  const defaults = {
7584
7584
  center: [0, 0, 0],
7585
7585
  size: [2, 2, 2]
@@ -7612,7 +7612,7 @@ var require_cuboid = __commonJS({
7612
7612
  );
7613
7613
  return result;
7614
7614
  };
7615
- module.exports = cuboid4;
7615
+ module.exports = cuboid5;
7616
7616
  }
7617
7617
  });
7618
7618
 
@@ -7620,7 +7620,7 @@ var require_cuboid = __commonJS({
7620
7620
  var require_cube = __commonJS({
7621
7621
  "node_modules/@jscad/modeling/src/primitives/cube.js"(exports, module) {
7622
7622
  "use strict";
7623
- var cuboid4 = require_cuboid();
7623
+ var cuboid5 = require_cuboid();
7624
7624
  var { isGTE } = require_commonChecks();
7625
7625
  var cube = (options) => {
7626
7626
  const defaults = {
@@ -7630,7 +7630,7 @@ var require_cube = __commonJS({
7630
7630
  let { center, size: size5 } = Object.assign({}, defaults, options);
7631
7631
  if (!isGTE(size5, 0)) throw new Error("size must be positive");
7632
7632
  size5 = [size5, size5, size5];
7633
- return cuboid4({ center, size: size5 });
7633
+ return cuboid5({ center, size: size5 });
7634
7634
  };
7635
7635
  module.exports = cube;
7636
7636
  }
@@ -7745,7 +7745,7 @@ var require_cylinder = __commonJS({
7745
7745
  var geom3 = require_geom3();
7746
7746
  var cylinderElliptic = require_cylinderElliptic();
7747
7747
  var { isGTE } = require_commonChecks();
7748
- var cylinder3 = (options) => {
7748
+ var cylinder4 = (options) => {
7749
7749
  const defaults = {
7750
7750
  center: [0, 0, 0],
7751
7751
  height: 2,
@@ -7764,7 +7764,7 @@ var require_cylinder = __commonJS({
7764
7764
  };
7765
7765
  return cylinderElliptic(newoptions);
7766
7766
  };
7767
- module.exports = cylinder3;
7767
+ module.exports = cylinder4;
7768
7768
  }
7769
7769
  });
7770
7770
 
@@ -8133,7 +8133,7 @@ var require_roundedCuboid = __commonJS({
8133
8133
  var poly3 = require_poly3();
8134
8134
  var { sin: sin2, cos: cos2 } = require_trigonometry();
8135
8135
  var { isGTE, isNumberArray } = require_commonChecks();
8136
- var cuboid4 = require_cuboid();
8136
+ var cuboid5 = require_cuboid();
8137
8137
  var createCorners = (center, size5, radius, segments, slice, positive) => {
8138
8138
  const pitch = TAU / 4 * slice / segments;
8139
8139
  const cospitch = cos2(pitch);
@@ -8235,7 +8235,7 @@ var require_roundedCuboid = __commonJS({
8235
8235
  if (!isGTE(roundRadius, 0)) throw new Error("roundRadius must be positive");
8236
8236
  if (!isGTE(segments, 4)) throw new Error("segments must be four or more");
8237
8237
  if (size5[0] === 0 || size5[1] === 0 || size5[2] === 0) return geom3.create();
8238
- if (roundRadius === 0) return cuboid4({ center, size: size5 });
8238
+ if (roundRadius === 0) return cuboid5({ center, size: size5 });
8239
8239
  size5 = size5.map((v) => v / 2);
8240
8240
  if (roundRadius > size5[0] - EPS || roundRadius > size5[1] - EPS || roundRadius > size5[2] - EPS) throw new Error("roundRadius must be smaller than the radius of all dimensions");
8241
8241
  segments = Math.floor(segments / 4);
@@ -8285,7 +8285,7 @@ var require_roundedCylinder = __commonJS({
8285
8285
  var poly3 = require_poly3();
8286
8286
  var { sin: sin2, cos: cos2 } = require_trigonometry();
8287
8287
  var { isGTE, isNumberArray } = require_commonChecks();
8288
- var cylinder3 = require_cylinder();
8288
+ var cylinder4 = require_cylinder();
8289
8289
  var roundedCylinder = (options) => {
8290
8290
  const defaults = {
8291
8291
  center: [0, 0, 0],
@@ -8302,7 +8302,7 @@ var require_roundedCylinder = __commonJS({
8302
8302
  if (roundRadius > radius) throw new Error("roundRadius must be smaller than the radius");
8303
8303
  if (!isGTE(segments, 4)) throw new Error("segments must be four or more");
8304
8304
  if (height10 === 0 || radius === 0) return geom3.create();
8305
- if (roundRadius === 0) return cylinder3({ center, height: height10, radius });
8305
+ if (roundRadius === 0) return cylinder4({ center, height: height10, radius });
8306
8306
  const start = [0, 0, -(height10 / 2)];
8307
8307
  const end = [0, 0, height10 / 2];
8308
8308
  const direction = vec3.subtract(vec3.create(), end, start);
@@ -9727,12 +9727,12 @@ var require_rotate3 = __commonJS({
9727
9727
  };
9728
9728
  var rotateX = (angle, ...objects) => rotate2([angle, 0, 0], objects);
9729
9729
  var rotateY = (angle, ...objects) => rotate2([0, angle, 0], objects);
9730
- var rotateZ3 = (angle, ...objects) => rotate2([0, 0, angle], objects);
9730
+ var rotateZ4 = (angle, ...objects) => rotate2([0, 0, angle], objects);
9731
9731
  module.exports = {
9732
9732
  rotate: rotate2,
9733
9733
  rotateX,
9734
9734
  rotateY,
9735
- rotateZ: rotateZ3
9735
+ rotateZ: rotateZ4
9736
9736
  };
9737
9737
  }
9738
9738
  });
@@ -9746,7 +9746,7 @@ var require_translate2 = __commonJS({
9746
9746
  var geom2 = require_geom2();
9747
9747
  var geom3 = require_geom3();
9748
9748
  var path2 = require_path2();
9749
- var translate9 = (offset4, ...objects) => {
9749
+ var translate10 = (offset4, ...objects) => {
9750
9750
  if (!Array.isArray(offset4)) throw new Error("offset must be an array");
9751
9751
  objects = flatten(objects);
9752
9752
  if (objects.length === 0) throw new Error("wrong number of arguments");
@@ -9761,11 +9761,11 @@ var require_translate2 = __commonJS({
9761
9761
  });
9762
9762
  return results.length === 1 ? results[0] : results;
9763
9763
  };
9764
- var translateX = (offset4, ...objects) => translate9([offset4, 0, 0], objects);
9765
- var translateY = (offset4, ...objects) => translate9([0, offset4, 0], objects);
9766
- var translateZ = (offset4, ...objects) => translate9([0, 0, offset4], objects);
9764
+ var translateX = (offset4, ...objects) => translate10([offset4, 0, 0], objects);
9765
+ var translateY = (offset4, ...objects) => translate10([0, offset4, 0], objects);
9766
+ var translateZ = (offset4, ...objects) => translate10([0, 0, offset4], objects);
9767
9767
  module.exports = {
9768
- translate: translate9,
9768
+ translate: translate10,
9769
9769
  translateX,
9770
9770
  translateY,
9771
9771
  translateZ
@@ -9780,7 +9780,7 @@ var require_torus = __commonJS({
9780
9780
  var { TAU } = require_constants();
9781
9781
  var extrudeRotate = require_extrudeRotate();
9782
9782
  var { rotate: rotate2 } = require_rotate3();
9783
- var { translate: translate9 } = require_translate2();
9783
+ var { translate: translate10 } = require_translate2();
9784
9784
  var circle2 = require_circle();
9785
9785
  var { isGT, isGTE } = require_commonChecks();
9786
9786
  var torus = (options) => {
@@ -9805,7 +9805,7 @@ var require_torus = __commonJS({
9805
9805
  if (innerRotation !== 0) {
9806
9806
  innerCircle = rotate2([0, 0, innerRotation], innerCircle);
9807
9807
  }
9808
- innerCircle = translate9([outerRadius, 0], innerCircle);
9808
+ innerCircle = translate10([outerRadius, 0], innerCircle);
9809
9809
  const extrudeOptions = {
9810
9810
  startAngle,
9811
9811
  angle: outerRotation,
@@ -11709,7 +11709,7 @@ var require_unionGeom3 = __commonJS({
11709
11709
  var flatten = require_flatten();
11710
11710
  var retessellate = require_retessellate();
11711
11711
  var unionSub = require_unionGeom3Sub();
11712
- var union4 = (...geometries) => {
11712
+ var union5 = (...geometries) => {
11713
11713
  geometries = flatten(geometries);
11714
11714
  let i;
11715
11715
  for (i = 1; i < geometries.length; i += 2) {
@@ -11719,7 +11719,7 @@ var require_unionGeom3 = __commonJS({
11719
11719
  newgeometry = retessellate(newgeometry);
11720
11720
  return newgeometry;
11721
11721
  };
11722
- module.exports = union4;
11722
+ module.exports = union5;
11723
11723
  }
11724
11724
  });
11725
11725
 
@@ -11733,14 +11733,14 @@ var require_unionGeom2 = __commonJS({
11733
11733
  var fromFakePolygons = require_fromFakePolygons();
11734
11734
  var to3DWalls = require_to3DWalls();
11735
11735
  var unionGeom3 = require_unionGeom3();
11736
- var union4 = (...geometries) => {
11736
+ var union5 = (...geometries) => {
11737
11737
  geometries = flatten(geometries);
11738
11738
  const newgeometries = geometries.map((geometry) => to3DWalls({ z0: -1, z1: 1 }, geometry));
11739
11739
  const newgeom3 = unionGeom3(newgeometries);
11740
11740
  const epsilon = measureEpsilon(newgeom3);
11741
11741
  return fromFakePolygons(epsilon, geom3.toPolygons(newgeom3));
11742
11742
  };
11743
- module.exports = union4;
11743
+ module.exports = union5;
11744
11744
  }
11745
11745
  });
11746
11746
 
@@ -11754,7 +11754,7 @@ var require_union = __commonJS({
11754
11754
  var geom3 = require_geom3();
11755
11755
  var unionGeom2 = require_unionGeom2();
11756
11756
  var unionGeom3 = require_unionGeom3();
11757
- var union4 = (...geometries) => {
11757
+ var union5 = (...geometries) => {
11758
11758
  geometries = flatten(geometries);
11759
11759
  if (geometries.length === 0) throw new Error("wrong number of arguments");
11760
11760
  if (!areAllShapesTheSameType(geometries)) {
@@ -11765,7 +11765,7 @@ var require_union = __commonJS({
11765
11765
  if (geom3.isA(geometry)) return unionGeom3(geometries);
11766
11766
  return geometry;
11767
11767
  };
11768
- module.exports = union4;
11768
+ module.exports = union5;
11769
11769
  }
11770
11770
  });
11771
11771
 
@@ -12110,8 +12110,8 @@ var require_expandShell = __commonJS({
12110
12110
  endfacevertices.reverse();
12111
12111
  polygons2.push(poly3.create(startfacevertices));
12112
12112
  polygons2.push(poly3.create(endfacevertices));
12113
- const cylinder3 = geom3.create(polygons2);
12114
- result = unionGeom3Sub(result, cylinder3);
12113
+ const cylinder4 = geom3.create(polygons2);
12114
+ result = unionGeom3Sub(result, cylinder4);
12115
12115
  });
12116
12116
  vertices2planes.forEach((item) => {
12117
12117
  const vertex = item[0];
@@ -12155,7 +12155,7 @@ var require_expandGeom3 = __commonJS({
12155
12155
  "node_modules/@jscad/modeling/src/operations/expansions/expandGeom3.js"(exports, module) {
12156
12156
  "use strict";
12157
12157
  var geom3 = require_geom3();
12158
- var union4 = require_union();
12158
+ var union5 = require_union();
12159
12159
  var expandShell = require_expandShell();
12160
12160
  var expandGeom3 = (options, geometry) => {
12161
12161
  const defaults = {
@@ -12171,7 +12171,7 @@ var require_expandGeom3 = __commonJS({
12171
12171
  if (polygons.length === 0) throw new Error("the given geometry cannot be empty");
12172
12172
  options = { delta, corners, segments };
12173
12173
  const expanded = expandShell(options, geometry);
12174
- return union4(geometry, expanded);
12174
+ return union5(geometry, expanded);
12175
12175
  };
12176
12176
  module.exports = expandGeom3;
12177
12177
  }
@@ -12451,7 +12451,7 @@ var require_extrudeLinear = __commonJS({
12451
12451
  var path2 = require_path2();
12452
12452
  var extrudeLinearGeom2 = require_extrudeLinearGeom2();
12453
12453
  var extrudeLinearPath2 = require_extrudeLinearPath2();
12454
- var extrudeLinear8 = (options, ...objects) => {
12454
+ var extrudeLinear9 = (options, ...objects) => {
12455
12455
  const defaults = {
12456
12456
  height: 1,
12457
12457
  twistAngle: 0,
@@ -12469,7 +12469,7 @@ var require_extrudeLinear = __commonJS({
12469
12469
  });
12470
12470
  return results.length === 1 ? results[0] : results;
12471
12471
  };
12472
- module.exports = extrudeLinear8;
12472
+ module.exports = extrudeLinear9;
12473
12473
  }
12474
12474
  });
12475
12475
 
@@ -12886,7 +12886,7 @@ var require_hullChain = __commonJS({
12886
12886
  "node_modules/@jscad/modeling/src/operations/hulls/hullChain.js"(exports, module) {
12887
12887
  "use strict";
12888
12888
  var flatten = require_flatten();
12889
- var union4 = require_union();
12889
+ var union5 = require_union();
12890
12890
  var hull = require_hull();
12891
12891
  var hullChain = (...geometries) => {
12892
12892
  geometries = flatten(geometries);
@@ -12895,7 +12895,7 @@ var require_hullChain = __commonJS({
12895
12895
  for (let i = 1; i < geometries.length; i++) {
12896
12896
  hulls.push(hull(geometries[i - 1], geometries[i]));
12897
12897
  }
12898
- return union4(hulls);
12898
+ return union5(hulls);
12899
12899
  };
12900
12900
  module.exports = hullChain;
12901
12901
  }
@@ -13528,7 +13528,7 @@ var require_align = __commonJS({
13528
13528
  var flatten = require_flatten();
13529
13529
  var padArrayToLength = require_padArrayToLength();
13530
13530
  var measureAggregateBoundingBox = require_measureAggregateBoundingBox();
13531
- var { translate: translate9 } = require_translate2();
13531
+ var { translate: translate10 } = require_translate2();
13532
13532
  var validateOptions = (options) => {
13533
13533
  if (!Array.isArray(options.modes) || options.modes.length > 3) throw new Error("align(): modes must be an array of length <= 3");
13534
13534
  options.modes = padArrayToLength(options.modes, "none", 3);
@@ -13565,7 +13565,7 @@ var require_align = __commonJS({
13565
13565
  translation[i] = relativeTo[i] - bounds[0][i];
13566
13566
  }
13567
13567
  }
13568
- return translate9(translation, geometry);
13568
+ return translate10(translation, geometry);
13569
13569
  };
13570
13570
  var align = (options, ...geometries) => {
13571
13571
  const defaults = {
@@ -13602,7 +13602,7 @@ var require_center = __commonJS({
13602
13602
  var geom3 = require_geom3();
13603
13603
  var path2 = require_path2();
13604
13604
  var measureBoundingBox = require_measureBoundingBox2();
13605
- var { translate: translate9 } = require_translate2();
13605
+ var { translate: translate10 } = require_translate2();
13606
13606
  var centerGeometry = (options, object) => {
13607
13607
  const defaults = {
13608
13608
  axes: [true, true, true],
@@ -13614,7 +13614,7 @@ var require_center = __commonJS({
13614
13614
  if (axes[0]) offset4[0] = relativeTo[0] - (bounds[0][0] + (bounds[1][0] - bounds[0][0]) / 2);
13615
13615
  if (axes[1]) offset4[1] = relativeTo[1] - (bounds[0][1] + (bounds[1][1] - bounds[0][1]) / 2);
13616
13616
  if (axes[2]) offset4[2] = relativeTo[2] - (bounds[0][2] + (bounds[1][2] - bounds[0][2]) / 2);
13617
- return translate9(offset4, object);
13617
+ return translate10(offset4, object);
13618
13618
  };
13619
13619
  var center = (options, ...objects) => {
13620
13620
  const defaults = {
@@ -15847,10 +15847,12 @@ var schematic_voltage_probe = z61.object({
15847
15847
  type: z61.literal("schematic_voltage_probe"),
15848
15848
  schematic_voltage_probe_id: z61.string(),
15849
15849
  source_component_id: z61.string().optional(),
15850
+ name: z61.string().optional(),
15850
15851
  position: point,
15851
15852
  schematic_trace_id: z61.string(),
15852
15853
  voltage: voltage.optional(),
15853
- subcircuit_id: z61.string().optional()
15854
+ subcircuit_id: z61.string().optional(),
15855
+ color: z61.string().optional()
15854
15856
  }).describe("Defines a voltage probe measurement point on a schematic trace");
15855
15857
  expectTypesMatch(true);
15856
15858
  var schematic_manual_edit_conflict_warning = z62.object({
@@ -15984,7 +15986,8 @@ var pcb_hole_circle = z71.object({
15984
15986
  hole_shape: z71.literal("circle"),
15985
15987
  hole_diameter: z71.number(),
15986
15988
  x: distance,
15987
- y: distance
15989
+ y: distance,
15990
+ soldermask_margin: z71.number().optional()
15988
15991
  });
15989
15992
  var pcb_hole_circle_shape = pcb_hole_circle.describe(
15990
15993
  "Defines a circular hole on the PCB"
@@ -15999,7 +16002,8 @@ var pcb_hole_rect = z71.object({
15999
16002
  hole_width: z71.number(),
16000
16003
  hole_height: z71.number(),
16001
16004
  x: distance,
16002
- y: distance
16005
+ y: distance,
16006
+ soldermask_margin: z71.number().optional()
16003
16007
  });
16004
16008
  var pcb_hole_rect_shape = pcb_hole_rect.describe(
16005
16009
  "Defines a rectangular (square-capable) hole on the PCB. Use equal width/height for square."
@@ -16013,7 +16017,8 @@ var pcb_hole_circle_or_square = z71.object({
16013
16017
  hole_shape: z71.enum(["circle", "square"]),
16014
16018
  hole_diameter: z71.number(),
16015
16019
  x: distance,
16016
- y: distance
16020
+ y: distance,
16021
+ soldermask_margin: z71.number().optional()
16017
16022
  });
16018
16023
  var pcb_hole_circle_or_square_shape = pcb_hole_circle_or_square.describe(
16019
16024
  "Defines a circular or square hole on the PCB"
@@ -16028,7 +16033,8 @@ var pcb_hole_oval = z71.object({
16028
16033
  hole_width: z71.number(),
16029
16034
  hole_height: z71.number(),
16030
16035
  x: distance,
16031
- y: distance
16036
+ y: distance,
16037
+ soldermask_margin: z71.number().optional()
16032
16038
  });
16033
16039
  var pcb_hole_oval_shape = pcb_hole_oval.describe(
16034
16040
  "Defines an oval hole on the PCB"
@@ -16043,7 +16049,8 @@ var pcb_hole_pill = z71.object({
16043
16049
  hole_width: z71.number(),
16044
16050
  hole_height: z71.number(),
16045
16051
  x: distance,
16046
- y: distance
16052
+ y: distance,
16053
+ soldermask_margin: z71.number().optional()
16047
16054
  });
16048
16055
  var pcb_hole_pill_shape = pcb_hole_pill.describe(
16049
16056
  "Defines a pill-shaped hole on the PCB"
@@ -16059,7 +16066,8 @@ var pcb_hole_rotated_pill = z71.object({
16059
16066
  hole_height: z71.number(),
16060
16067
  x: distance,
16061
16068
  y: distance,
16062
- ccw_rotation: rotation
16069
+ ccw_rotation: rotation,
16070
+ soldermask_margin: z71.number().optional()
16063
16071
  });
16064
16072
  var pcb_hole_rotated_pill_shape = pcb_hole_rotated_pill.describe(
16065
16073
  "Defines a rotated pill-shaped hole on the PCB"
@@ -16079,7 +16087,8 @@ var pcb_plated_hole_circle = z72.object({
16079
16087
  port_hints: z72.array(z72.string()).optional(),
16080
16088
  pcb_component_id: z72.string().optional(),
16081
16089
  pcb_port_id: z72.string().optional(),
16082
- pcb_plated_hole_id: getZodPrefixedIdWithDefault("pcb_plated_hole")
16090
+ pcb_plated_hole_id: getZodPrefixedIdWithDefault("pcb_plated_hole"),
16091
+ soldermask_margin: z72.number().optional()
16083
16092
  });
16084
16093
  var pcb_plated_hole_oval = z72.object({
16085
16094
  type: z72.literal("pcb_plated_hole"),
@@ -16097,7 +16106,8 @@ var pcb_plated_hole_oval = z72.object({
16097
16106
  port_hints: z72.array(z72.string()).optional(),
16098
16107
  pcb_component_id: z72.string().optional(),
16099
16108
  pcb_port_id: z72.string().optional(),
16100
- pcb_plated_hole_id: getZodPrefixedIdWithDefault("pcb_plated_hole")
16109
+ pcb_plated_hole_id: getZodPrefixedIdWithDefault("pcb_plated_hole"),
16110
+ soldermask_margin: z72.number().optional()
16101
16111
  });
16102
16112
  var pcb_circular_hole_with_rect_pad = z72.object({
16103
16113
  type: z72.literal("pcb_plated_hole"),
@@ -16118,7 +16128,8 @@ var pcb_circular_hole_with_rect_pad = z72.object({
16118
16128
  port_hints: z72.array(z72.string()).optional(),
16119
16129
  pcb_component_id: z72.string().optional(),
16120
16130
  pcb_port_id: z72.string().optional(),
16121
- pcb_plated_hole_id: getZodPrefixedIdWithDefault("pcb_plated_hole")
16131
+ pcb_plated_hole_id: getZodPrefixedIdWithDefault("pcb_plated_hole"),
16132
+ soldermask_margin: z72.number().optional()
16122
16133
  });
16123
16134
  var pcb_pill_hole_with_rect_pad = z72.object({
16124
16135
  type: z72.literal("pcb_plated_hole"),
@@ -16140,7 +16151,8 @@ var pcb_pill_hole_with_rect_pad = z72.object({
16140
16151
  port_hints: z72.array(z72.string()).optional(),
16141
16152
  pcb_component_id: z72.string().optional(),
16142
16153
  pcb_port_id: z72.string().optional(),
16143
- pcb_plated_hole_id: getZodPrefixedIdWithDefault("pcb_plated_hole")
16154
+ pcb_plated_hole_id: getZodPrefixedIdWithDefault("pcb_plated_hole"),
16155
+ soldermask_margin: z72.number().optional()
16144
16156
  });
16145
16157
  var pcb_rotated_pill_hole_with_rect_pad = z72.object({
16146
16158
  type: z72.literal("pcb_plated_hole"),
@@ -16164,7 +16176,8 @@ var pcb_rotated_pill_hole_with_rect_pad = z72.object({
16164
16176
  port_hints: z72.array(z72.string()).optional(),
16165
16177
  pcb_component_id: z72.string().optional(),
16166
16178
  pcb_port_id: z72.string().optional(),
16167
- pcb_plated_hole_id: getZodPrefixedIdWithDefault("pcb_plated_hole")
16179
+ pcb_plated_hole_id: getZodPrefixedIdWithDefault("pcb_plated_hole"),
16180
+ soldermask_margin: z72.number().optional()
16168
16181
  });
16169
16182
  var pcb_hole_with_polygon_pad = z72.object({
16170
16183
  type: z72.literal("pcb_plated_hole"),
@@ -16189,7 +16202,8 @@ var pcb_hole_with_polygon_pad = z72.object({
16189
16202
  port_hints: z72.array(z72.string()).optional(),
16190
16203
  pcb_component_id: z72.string().optional(),
16191
16204
  pcb_port_id: z72.string().optional(),
16192
- pcb_plated_hole_id: getZodPrefixedIdWithDefault("pcb_plated_hole")
16205
+ pcb_plated_hole_id: getZodPrefixedIdWithDefault("pcb_plated_hole"),
16206
+ soldermask_margin: z72.number().optional()
16193
16207
  });
16194
16208
  var pcb_plated_hole = z72.union([
16195
16209
  pcb_plated_hole_circle,
@@ -16233,7 +16247,8 @@ var pcb_smtpad_circle = z74.object({
16233
16247
  port_hints: z74.array(z74.string()).optional(),
16234
16248
  pcb_component_id: z74.string().optional(),
16235
16249
  pcb_port_id: z74.string().optional(),
16236
- is_covered_with_solder_mask: z74.boolean().optional()
16250
+ is_covered_with_solder_mask: z74.boolean().optional(),
16251
+ soldermask_margin: z74.number().optional()
16237
16252
  });
16238
16253
  var pcb_smtpad_rect = z74.object({
16239
16254
  type: z74.literal("pcb_smtpad"),
@@ -16251,7 +16266,8 @@ var pcb_smtpad_rect = z74.object({
16251
16266
  port_hints: z74.array(z74.string()).optional(),
16252
16267
  pcb_component_id: z74.string().optional(),
16253
16268
  pcb_port_id: z74.string().optional(),
16254
- is_covered_with_solder_mask: z74.boolean().optional()
16269
+ is_covered_with_solder_mask: z74.boolean().optional(),
16270
+ soldermask_margin: z74.number().optional()
16255
16271
  });
16256
16272
  var pcb_smtpad_rotated_rect = z74.object({
16257
16273
  type: z74.literal("pcb_smtpad"),
@@ -16270,7 +16286,8 @@ var pcb_smtpad_rotated_rect = z74.object({
16270
16286
  port_hints: z74.array(z74.string()).optional(),
16271
16287
  pcb_component_id: z74.string().optional(),
16272
16288
  pcb_port_id: z74.string().optional(),
16273
- is_covered_with_solder_mask: z74.boolean().optional()
16289
+ is_covered_with_solder_mask: z74.boolean().optional(),
16290
+ soldermask_margin: z74.number().optional()
16274
16291
  });
16275
16292
  var pcb_smtpad_pill = z74.object({
16276
16293
  type: z74.literal("pcb_smtpad"),
@@ -16287,7 +16304,8 @@ var pcb_smtpad_pill = z74.object({
16287
16304
  port_hints: z74.array(z74.string()).optional(),
16288
16305
  pcb_component_id: z74.string().optional(),
16289
16306
  pcb_port_id: z74.string().optional(),
16290
- is_covered_with_solder_mask: z74.boolean().optional()
16307
+ is_covered_with_solder_mask: z74.boolean().optional(),
16308
+ soldermask_margin: z74.number().optional()
16291
16309
  });
16292
16310
  var pcb_smtpad_rotated_pill = z74.object({
16293
16311
  type: z74.literal("pcb_smtpad"),
@@ -16305,7 +16323,8 @@ var pcb_smtpad_rotated_pill = z74.object({
16305
16323
  port_hints: z74.array(z74.string()).optional(),
16306
16324
  pcb_component_id: z74.string().optional(),
16307
16325
  pcb_port_id: z74.string().optional(),
16308
- is_covered_with_solder_mask: z74.boolean().optional()
16326
+ is_covered_with_solder_mask: z74.boolean().optional(),
16327
+ soldermask_margin: z74.number().optional()
16309
16328
  });
16310
16329
  var pcb_smtpad_polygon = z74.object({
16311
16330
  type: z74.literal("pcb_smtpad"),
@@ -16318,7 +16337,8 @@ var pcb_smtpad_polygon = z74.object({
16318
16337
  port_hints: z74.array(z74.string()).optional(),
16319
16338
  pcb_component_id: z74.string().optional(),
16320
16339
  pcb_port_id: z74.string().optional(),
16321
- is_covered_with_solder_mask: z74.boolean().optional()
16340
+ is_covered_with_solder_mask: z74.boolean().optional(),
16341
+ soldermask_margin: z74.number().optional()
16322
16342
  });
16323
16343
  var pcb_smtpad = z74.discriminatedUnion("shape", [
16324
16344
  pcb_smtpad_circle,
@@ -16928,14 +16948,17 @@ var pcb_cutout_base = z106.object({
16928
16948
  type: z106.literal("pcb_cutout"),
16929
16949
  pcb_cutout_id: getZodPrefixedIdWithDefault("pcb_cutout"),
16930
16950
  pcb_group_id: z106.string().optional(),
16931
- subcircuit_id: z106.string().optional()
16951
+ subcircuit_id: z106.string().optional(),
16952
+ pcb_board_id: z106.string().optional(),
16953
+ pcb_panel_id: z106.string().optional()
16932
16954
  });
16933
16955
  var pcb_cutout_rect = pcb_cutout_base.extend({
16934
16956
  shape: z106.literal("rect"),
16935
16957
  center: point,
16936
16958
  width: length,
16937
16959
  height: length,
16938
- rotation: rotation.optional()
16960
+ rotation: rotation.optional(),
16961
+ corner_radius: length.optional()
16939
16962
  });
16940
16963
  expectTypesMatch(true);
16941
16964
  var pcb_cutout_circle = pcb_cutout_base.extend({
@@ -16949,10 +16972,20 @@ var pcb_cutout_polygon = pcb_cutout_base.extend({
16949
16972
  points: z106.array(point)
16950
16973
  });
16951
16974
  expectTypesMatch(true);
16975
+ var pcb_cutout_path = pcb_cutout_base.extend({
16976
+ shape: z106.literal("path"),
16977
+ route: z106.array(point),
16978
+ slot_width: length,
16979
+ slot_length: length.optional(),
16980
+ space_between_slots: length.optional(),
16981
+ slot_corner_radius: length.optional()
16982
+ });
16983
+ expectTypesMatch(true);
16952
16984
  var pcb_cutout = z106.discriminatedUnion("shape", [
16953
16985
  pcb_cutout_rect,
16954
16986
  pcb_cutout_circle,
16955
- pcb_cutout_polygon
16987
+ pcb_cutout_polygon,
16988
+ pcb_cutout_path
16956
16989
  ]).describe("Defines a cutout on the PCB, removing board material.");
16957
16990
  expectTypesMatch(true);
16958
16991
  var pcb_missing_footprint_error = z107.object({
@@ -17317,7 +17350,8 @@ var simulation_transient_voltage_graph = z127.object({
17317
17350
  time_per_step: duration_ms,
17318
17351
  start_time_ms: ms,
17319
17352
  end_time_ms: ms,
17320
- name: z127.string().optional()
17353
+ name: z127.string().optional(),
17354
+ color: z127.string().optional()
17321
17355
  }).describe("Stores voltage measurements over time for a simulation");
17322
17356
  expectTypesMatch(true);
17323
17357
  var simulation_switch = z128.object({
@@ -17336,9 +17370,11 @@ var simulation_voltage_probe = z129.object({
17336
17370
  "simulation_voltage_probe"
17337
17371
  ),
17338
17372
  source_component_id: z129.string().optional(),
17373
+ name: z129.string().optional(),
17339
17374
  source_port_id: z129.string().optional(),
17340
17375
  source_net_id: z129.string().optional(),
17341
- subcircuit_id: z129.string().optional()
17376
+ subcircuit_id: z129.string().optional(),
17377
+ color: z129.string().optional()
17342
17378
  }).describe(
17343
17379
  "Defines a voltage probe for simulation, connected to a port or a net"
17344
17380
  ).refine(
@@ -24390,7 +24426,7 @@ var m2host = (raw_params) => {
24390
24426
  }
24391
24427
  const centerX = (minX + maxX) / 2;
24392
24428
  const centerY = (minY + maxY) / 2;
24393
- const translate9 = (el) => {
24429
+ const translate10 = (el) => {
24394
24430
  if (typeof el.x === "number") el.x -= centerX;
24395
24431
  if (typeof el.y === "number") el.y -= centerY;
24396
24432
  if (el.center) {
@@ -24404,9 +24440,9 @@ var m2host = (raw_params) => {
24404
24440
  }));
24405
24441
  }
24406
24442
  };
24407
- for (const pad2 of pads) translate9(pad2);
24408
- translate9(cutout);
24409
- translate9(pin1Marker);
24443
+ for (const pad2 of pads) translate10(pad2);
24444
+ translate10(cutout);
24445
+ translate10(pin1Marker);
24410
24446
  return {
24411
24447
  circuitJson: [
24412
24448
  ...pads,
@@ -28252,7 +28288,7 @@ import * as THREE15 from "three";
28252
28288
  // package.json
28253
28289
  var package_default = {
28254
28290
  name: "@tscircuit/3d-viewer",
28255
- version: "0.0.436",
28291
+ version: "0.0.438",
28256
28292
  main: "./dist/index.js",
28257
28293
  module: "./dist/index.js",
28258
28294
  type: "module",
@@ -28313,7 +28349,7 @@ var package_default = {
28313
28349
  "@vitejs/plugin-react": "^4.3.4",
28314
28350
  "bun-match-svg": "^0.0.9",
28315
28351
  "bun-types": "1.2.1",
28316
- "circuit-json": "0.0.311",
28352
+ "circuit-json": "0.0.317",
28317
28353
  "circuit-to-svg": "^0.0.179",
28318
28354
  debug: "^4.4.0",
28319
28355
  "jscad-electronics": "^0.0.89",
@@ -29550,18 +29586,18 @@ var createBoardGeomFromCircuitJson = (circuitJson, opts = {}) => {
29550
29586
  };
29551
29587
 
29552
29588
  // src/BoardGeomBuilder.ts
29553
- var import_transforms7 = __toESM(require_transforms(), 1);
29554
- var import_primitives9 = __toESM(require_primitives(), 1);
29589
+ var import_transforms8 = __toESM(require_transforms(), 1);
29590
+ var import_primitives10 = __toESM(require_primitives(), 1);
29555
29591
  var import_colors7 = __toESM(require_colors(), 1);
29556
- var import_booleans5 = __toESM(require_booleans(), 1);
29592
+ var import_booleans6 = __toESM(require_booleans(), 1);
29557
29593
  import { su as su3 } from "@tscircuit/circuit-json-util";
29558
29594
 
29559
29595
  // src/geoms/plated-hole.ts
29560
- var import_primitives3 = __toESM(require_primitives(), 1);
29596
+ var import_primitives4 = __toESM(require_primitives(), 1);
29561
29597
  var import_colors2 = __toESM(require_colors(), 1);
29562
- var import_booleans = __toESM(require_booleans(), 1);
29563
- var import_extrusions2 = __toESM(require_extrusions(), 1);
29564
- var import_transforms2 = __toESM(require_transforms(), 1);
29598
+ var import_booleans2 = __toESM(require_booleans(), 1);
29599
+ var import_extrusions3 = __toESM(require_extrusions(), 1);
29600
+ var import_transforms3 = __toESM(require_transforms(), 1);
29565
29601
 
29566
29602
  // src/utils/rect-border-radius.ts
29567
29603
  function clampRectBorderRadius(width10, height10, rawRadius) {
@@ -29580,10 +29616,101 @@ function extractRectBorderRadius(source) {
29580
29616
  return source.corner_radius ?? source.cornerRadius ?? source.rect_pad_border_radius ?? source.rectPadBorderRadius ?? source.rect_border_radius ?? source.rectBorderRadius ?? void 0;
29581
29617
  }
29582
29618
 
29619
+ // src/geoms/create-hole-with-polygon-pad.ts
29620
+ var import_primitives3 = __toESM(require_primitives(), 1);
29621
+ var import_extrusions2 = __toESM(require_extrusions(), 1);
29622
+ var import_transforms2 = __toESM(require_transforms(), 1);
29623
+ var import_booleans = __toESM(require_booleans(), 1);
29624
+ var ELLIPSE_SEGMENTS = 64;
29625
+ var createEllipsePrism = (width10, height10, depth) => {
29626
+ const ellipse2d = (0, import_primitives3.ellipse)({
29627
+ center: [0, 0],
29628
+ radius: [Math.max(width10 / 2, M / 2), Math.max(height10 / 2, M / 2)],
29629
+ segments: ELLIPSE_SEGMENTS
29630
+ });
29631
+ const extruded = (0, import_extrusions2.extrudeLinear)({ height: depth }, ellipse2d);
29632
+ return (0, import_transforms2.translate)([0, 0, -depth / 2], extruded);
29633
+ };
29634
+ var createPillPrism = (width10, height10, depth) => {
29635
+ const clampedWidth = Math.max(width10, M);
29636
+ const clampedHeight = Math.max(height10, M);
29637
+ if (Math.abs(clampedWidth - clampedHeight) < 1e-6) {
29638
+ return (0, import_primitives3.cylinder)({
29639
+ center: [0, 0, 0],
29640
+ radius: clampedWidth / 2,
29641
+ height: depth
29642
+ });
29643
+ }
29644
+ const isHorizontal = clampedWidth >= clampedHeight;
29645
+ const longDim = isHorizontal ? clampedWidth : clampedHeight;
29646
+ const shortDim = isHorizontal ? clampedHeight : clampedWidth;
29647
+ const rectLength = Math.max(longDim - shortDim, 0);
29648
+ const rect = (0, import_primitives3.cuboid)({
29649
+ center: [0, 0, 0],
29650
+ size: isHorizontal ? [rectLength, shortDim, depth] : [shortDim, rectLength, depth]
29651
+ });
29652
+ if (rectLength <= 1e-6) {
29653
+ return (0, import_primitives3.cylinder)({ center: [0, 0, 0], radius: shortDim / 2, height: depth });
29654
+ }
29655
+ const offset4 = rectLength / 2;
29656
+ const firstCap = (0, import_primitives3.cylinder)({
29657
+ center: isHorizontal ? [-offset4, 0, 0] : [0, -offset4, 0],
29658
+ radius: shortDim / 2,
29659
+ height: depth
29660
+ });
29661
+ const secondCap = (0, import_primitives3.cylinder)({
29662
+ center: isHorizontal ? [offset4, 0, 0] : [0, offset4, 0],
29663
+ radius: shortDim / 2,
29664
+ height: depth
29665
+ });
29666
+ return (0, import_booleans.union)(rect, firstCap, secondCap);
29667
+ };
29668
+ var createHoleWithPolygonPadHoleGeom = (hole, height10, options = {}) => {
29669
+ const holeShape = hole.hole_shape || "circle";
29670
+ const sizeDelta = options.sizeDelta ?? 0;
29671
+ const offsetX = hole.hole_offset_x || 0;
29672
+ const offsetY = hole.hole_offset_y || 0;
29673
+ const center = [
29674
+ hole.x + offsetX,
29675
+ hole.y + offsetY,
29676
+ 0
29677
+ ];
29678
+ if (holeShape === "circle") {
29679
+ const diameter = Math.max((hole.hole_diameter ?? 0) + sizeDelta, M);
29680
+ const radius = Math.max(diameter / 2, M / 2);
29681
+ return (0, import_primitives3.cylinder)({ center, radius, height: height10 });
29682
+ }
29683
+ const baseWidth = hole.hole_width ?? hole.hole_diameter;
29684
+ const baseHeight = hole.hole_height ?? hole.hole_diameter;
29685
+ if (!baseWidth || !baseHeight) {
29686
+ return null;
29687
+ }
29688
+ const width10 = Math.max(baseWidth + sizeDelta, M);
29689
+ const heightVal = Math.max(baseHeight + sizeDelta, M);
29690
+ if (holeShape === "oval") {
29691
+ const ellipsePrism = createEllipsePrism(width10, heightVal, height10);
29692
+ return (0, import_transforms2.translate)([center[0], center[1], 0], ellipsePrism);
29693
+ }
29694
+ if (holeShape === "pill" || holeShape === "rotated_pill") {
29695
+ let pill = createPillPrism(width10, heightVal, height10);
29696
+ if (!pill) return null;
29697
+ const rotation2 = hole.ccw_rotation || 0;
29698
+ if (rotation2) {
29699
+ pill = (0, import_transforms2.rotateZ)(rotation2 * Math.PI / 180, pill);
29700
+ }
29701
+ return (0, import_transforms2.translate)(center, pill);
29702
+ }
29703
+ const fallbackDiameter = Math.max(
29704
+ (hole.hole_diameter ?? baseWidth ?? baseHeight ?? M) + sizeDelta,
29705
+ M
29706
+ );
29707
+ return (0, import_primitives3.cylinder)({ center, radius: fallbackDiameter / 2, height: height10 });
29708
+ };
29709
+
29583
29710
  // src/geoms/plated-hole.ts
29584
29711
  var platedHoleLipHeight = 0.02;
29585
29712
  var RECT_PAD_SEGMENTS = 64;
29586
- var maybeClip = (geom, clipGeom) => clipGeom ? (0, import_booleans.intersect)(clipGeom, geom) : geom;
29713
+ var maybeClip = (geom, clipGeom) => clipGeom ? (0, import_booleans2.intersect)(clipGeom, geom) : geom;
29587
29714
  var createRectPadGeom = ({
29588
29715
  width: width10,
29589
29716
  height: height10,
@@ -29593,16 +29720,16 @@ var createRectPadGeom = ({
29593
29720
  }) => {
29594
29721
  const clampedRadius = clampRectBorderRadius(width10, height10, borderRadius);
29595
29722
  if (clampedRadius <= 0) {
29596
- return (0, import_primitives3.cuboid)({ center, size: [width10, height10, thickness] });
29723
+ return (0, import_primitives4.cuboid)({ center, size: [width10, height10, thickness] });
29597
29724
  }
29598
- const rect2d = (0, import_primitives3.roundedRectangle)({
29725
+ const rect2d = (0, import_primitives4.roundedRectangle)({
29599
29726
  size: [width10, height10],
29600
29727
  roundRadius: clampedRadius,
29601
29728
  segments: RECT_PAD_SEGMENTS
29602
29729
  });
29603
- const extruded = (0, import_extrusions2.extrudeLinear)({ height: thickness }, rect2d);
29730
+ const extruded = (0, import_extrusions3.extrudeLinear)({ height: thickness }, rect2d);
29604
29731
  const offsetZ = center[2] - thickness / 2;
29605
- return (0, import_transforms2.translate)([center[0], center[1], offsetZ], extruded);
29732
+ return (0, import_transforms3.translate)([center[0], center[1], offsetZ], extruded);
29606
29733
  };
29607
29734
  var platedHole = (plated_hole, ctx, options = {}) => {
29608
29735
  const { clipGeom } = options;
@@ -29614,18 +29741,18 @@ var platedHole = (plated_hole, ctx, options = {}) => {
29614
29741
  if (plated_hole.shape === "circle") {
29615
29742
  const outerDiameter = plated_hole.outer_diameter ?? Math.max(plated_hole.hole_diameter, 0);
29616
29743
  const copperHeight = copperSpan + 0.01;
29617
- const copperBody = (0, import_primitives3.cylinder)({
29744
+ const copperBody = (0, import_primitives4.cylinder)({
29618
29745
  center: [plated_hole.x, plated_hole.y, 0],
29619
29746
  radius: outerDiameter / 2,
29620
29747
  height: copperHeight
29621
29748
  });
29622
29749
  const copperSolid = maybeClip(copperBody, clipGeom);
29623
- const drill = (0, import_primitives3.cylinder)({
29750
+ const drill = (0, import_primitives4.cylinder)({
29624
29751
  center: [plated_hole.x, plated_hole.y, 0],
29625
29752
  radius: Math.max(plated_hole.hole_diameter / 2, 0.01),
29626
29753
  height: throughDrillHeight
29627
29754
  });
29628
- return (0, import_colors2.colorize)(colors.copper, (0, import_booleans.subtract)(copperSolid, drill));
29755
+ return (0, import_colors2.colorize)(colors.copper, (0, import_booleans2.subtract)(copperSolid, drill));
29629
29756
  }
29630
29757
  if (plated_hole.shape === "circular_hole_with_rect_pad") {
29631
29758
  const holeOffsetX = plated_hole.hole_offset_x || 0;
@@ -29634,7 +29761,7 @@ var platedHole = (plated_hole, ctx, options = {}) => {
29634
29761
  const padHeight = plated_hole.rect_pad_height || plated_hole.hole_diameter;
29635
29762
  const rectBorderRadius = extractRectBorderRadius(plated_hole);
29636
29763
  const copperSolid = maybeClip(
29637
- (0, import_booleans.union)(
29764
+ (0, import_booleans2.union)(
29638
29765
  // Top rectangular pad (thicker to ensure connection)
29639
29766
  createRectPadGeom({
29640
29767
  width: padWidth,
@@ -29659,19 +29786,19 @@ var platedHole = (plated_hole, ctx, options = {}) => {
29659
29786
  const topPadBottom = topSurfaceZ;
29660
29787
  const bottomPadTop = bottomSurfaceZ;
29661
29788
  const centerZ = (topPadBottom + bottomPadTop) / 2;
29662
- const rect2d = (0, import_primitives3.roundedRectangle)({
29789
+ const rect2d = (0, import_primitives4.roundedRectangle)({
29663
29790
  size: [padWidth, padHeight],
29664
29791
  roundRadius: rectBorderRadius || 0,
29665
29792
  segments: RECT_PAD_SEGMENTS
29666
29793
  });
29667
- const extruded = (0, import_extrusions2.extrudeLinear)({ height: height10 }, rect2d);
29668
- return (0, import_transforms2.translate)(
29794
+ const extruded = (0, import_extrusions3.extrudeLinear)({ height: height10 }, rect2d);
29795
+ return (0, import_transforms3.translate)(
29669
29796
  [plated_hole.x, plated_hole.y, centerZ - height10 / 2],
29670
29797
  extruded
29671
29798
  );
29672
29799
  })(),
29673
29800
  // Plated barrel around hole (ensured connection with pads)
29674
- (0, import_primitives3.cylinder)({
29801
+ (0, import_primitives4.cylinder)({
29675
29802
  center: [
29676
29803
  plated_hole.x + (holeOffsetX || 0),
29677
29804
  plated_hole.y + (holeOffsetY || 0),
@@ -29683,7 +29810,7 @@ var platedHole = (plated_hole, ctx, options = {}) => {
29683
29810
  ),
29684
29811
  clipGeom
29685
29812
  );
29686
- const drill = (0, import_primitives3.cylinder)({
29813
+ const drill = (0, import_primitives4.cylinder)({
29687
29814
  center: [
29688
29815
  plated_hole.x + (holeOffsetX || 0),
29689
29816
  plated_hole.y + (holeOffsetY || 0),
@@ -29692,7 +29819,7 @@ var platedHole = (plated_hole, ctx, options = {}) => {
29692
29819
  radius: Math.max(plated_hole.hole_diameter / 2 - M, 0.01),
29693
29820
  height: throughDrillHeight
29694
29821
  });
29695
- const barrel = (0, import_primitives3.cylinder)({
29822
+ const barrel = (0, import_primitives4.cylinder)({
29696
29823
  center: [
29697
29824
  plated_hole.x + (holeOffsetX || 0),
29698
29825
  plated_hole.y + (holeOffsetY || 0),
@@ -29701,18 +29828,18 @@ var platedHole = (plated_hole, ctx, options = {}) => {
29701
29828
  radius: plated_hole.hole_diameter / 2,
29702
29829
  height: copperSpan
29703
29830
  });
29704
- let finalCopper = (0, import_booleans.union)(
29705
- (0, import_booleans.subtract)(copperSolid, barrel),
29831
+ let finalCopper = (0, import_booleans2.union)(
29832
+ (0, import_booleans2.subtract)(copperSolid, barrel),
29706
29833
  // Subtract the barrel from the main shape
29707
29834
  barrel
29708
29835
  // Add the barrel back to ensure proper connection
29709
29836
  );
29710
29837
  if (options.clipGeom) {
29711
- finalCopper = (0, import_booleans.subtract)(finalCopper, drill);
29712
- finalCopper = (0, import_booleans.intersect)(finalCopper, options.clipGeom);
29838
+ finalCopper = (0, import_booleans2.subtract)(finalCopper, drill);
29839
+ finalCopper = (0, import_booleans2.intersect)(finalCopper, options.clipGeom);
29713
29840
  return (0, import_colors2.colorize)(colors.copper, finalCopper);
29714
29841
  }
29715
- return (0, import_colors2.colorize)(colors.copper, (0, import_booleans.subtract)(finalCopper, drill));
29842
+ return (0, import_colors2.colorize)(colors.copper, (0, import_booleans2.subtract)(finalCopper, drill));
29716
29843
  }
29717
29844
  if (plated_hole.shape === "pill") {
29718
29845
  const shouldRotate = plated_hole.hole_height > plated_hole.hole_width;
@@ -29729,51 +29856,51 @@ var platedHole = (plated_hole, ctx, options = {}) => {
29729
29856
  const radius = height10 / 2;
29730
29857
  const length2 = Math.abs(width10 - height10);
29731
29858
  if (length2 <= 1e-6) {
29732
- return (0, import_primitives3.cylinder)({
29859
+ return (0, import_primitives4.cylinder)({
29733
29860
  center: [plated_hole.x, plated_hole.y, 0],
29734
29861
  radius,
29735
29862
  height: thickness
29736
29863
  });
29737
29864
  }
29738
- const rect = (0, import_primitives3.cuboid)({
29865
+ const rect = (0, import_primitives4.cuboid)({
29739
29866
  center: [plated_hole.x, plated_hole.y, 0],
29740
29867
  size: shouldRotate ? [height10, length2, thickness] : [length2, height10, thickness]
29741
29868
  });
29742
- const leftCap = (0, import_primitives3.cylinder)({
29869
+ const leftCap = (0, import_primitives4.cylinder)({
29743
29870
  center: shouldRotate ? [plated_hole.x, plated_hole.y - length2 / 2, 0] : [plated_hole.x - length2 / 2, plated_hole.y, 0],
29744
29871
  radius,
29745
29872
  height: thickness
29746
29873
  });
29747
- const rightCap = (0, import_primitives3.cylinder)({
29874
+ const rightCap = (0, import_primitives4.cylinder)({
29748
29875
  center: shouldRotate ? [plated_hole.x, plated_hole.y + length2 / 2, 0] : [plated_hole.x + length2 / 2, plated_hole.y, 0],
29749
29876
  radius,
29750
29877
  height: thickness
29751
29878
  });
29752
- return (0, import_booleans.union)(rect, leftCap, rightCap);
29879
+ return (0, import_booleans2.union)(rect, leftCap, rightCap);
29753
29880
  };
29754
29881
  const outerBarrel = createPillSection(
29755
29882
  outerPillWidth,
29756
29883
  outerPillHeight,
29757
29884
  copperHeight
29758
29885
  );
29759
- const drillRect = (0, import_primitives3.cuboid)({
29886
+ const drillRect = (0, import_primitives4.cuboid)({
29760
29887
  center: [plated_hole.x, plated_hole.y, 0],
29761
29888
  size: shouldRotate ? [holeHeight - 2 * M, rectLength, throughDrillHeight] : [rectLength, holeHeight - 2 * M, throughDrillHeight]
29762
29889
  });
29763
- const drillLeftCap = (0, import_primitives3.cylinder)({
29890
+ const drillLeftCap = (0, import_primitives4.cylinder)({
29764
29891
  center: shouldRotate ? [plated_hole.x, plated_hole.y - rectLength / 2, 0] : [plated_hole.x - rectLength / 2, plated_hole.y, 0],
29765
29892
  radius: holeRadius - M,
29766
29893
  height: throughDrillHeight
29767
29894
  });
29768
- const drillRightCap = (0, import_primitives3.cylinder)({
29895
+ const drillRightCap = (0, import_primitives4.cylinder)({
29769
29896
  center: shouldRotate ? [plated_hole.x, plated_hole.y + rectLength / 2, 0] : [plated_hole.x + rectLength / 2, plated_hole.y, 0],
29770
29897
  radius: holeRadius - M,
29771
29898
  height: throughDrillHeight
29772
29899
  });
29773
- const drillUnion = (0, import_booleans.union)(drillRect, drillLeftCap, drillRightCap);
29900
+ const drillUnion = (0, import_booleans2.union)(drillRect, drillLeftCap, drillRightCap);
29774
29901
  const copperSolid = maybeClip(outerBarrel, clipGeom);
29775
29902
  const drill = drillUnion;
29776
- return (0, import_colors2.colorize)(colors.copper, (0, import_booleans.subtract)(copperSolid, drill));
29903
+ return (0, import_colors2.colorize)(colors.copper, (0, import_booleans2.subtract)(copperSolid, drill));
29777
29904
  }
29778
29905
  if (plated_hole.shape === "pill_hole_with_rect_pad") {
29779
29906
  const holeOffsetX = plated_hole.hole_offset_x || 0;
@@ -29787,8 +29914,8 @@ var platedHole = (plated_hole, ctx, options = {}) => {
29787
29914
  const padHeight = plated_hole.rect_pad_height || holeHeight + 0.2;
29788
29915
  const rectBorderRadius = extractRectBorderRadius(plated_hole);
29789
29916
  const barrelMargin = 0.03;
29790
- const barrel = (0, import_booleans.union)(
29791
- (0, import_primitives3.cuboid)({
29917
+ const barrel = (0, import_booleans2.union)(
29918
+ (0, import_primitives4.cuboid)({
29792
29919
  center: [plated_hole.x + holeOffsetX, plated_hole.y + holeOffsetY, 0],
29793
29920
  size: shouldRotate ? [
29794
29921
  holeHeight + 2 * barrelMargin,
@@ -29800,7 +29927,7 @@ var platedHole = (plated_hole, ctx, options = {}) => {
29800
29927
  copperSpan
29801
29928
  ]
29802
29929
  }),
29803
- (0, import_primitives3.cylinder)({
29930
+ (0, import_primitives4.cylinder)({
29804
29931
  center: shouldRotate ? [
29805
29932
  plated_hole.x + holeOffsetX,
29806
29933
  plated_hole.y + holeOffsetY - rectLength / 2,
@@ -29813,7 +29940,7 @@ var platedHole = (plated_hole, ctx, options = {}) => {
29813
29940
  radius: holeRadius + barrelMargin,
29814
29941
  height: copperSpan
29815
29942
  }),
29816
- (0, import_primitives3.cylinder)({
29943
+ (0, import_primitives4.cylinder)({
29817
29944
  center: shouldRotate ? [
29818
29945
  plated_hole.x + holeOffsetX,
29819
29946
  plated_hole.y + holeOffsetY + rectLength / 2,
@@ -29827,12 +29954,12 @@ var platedHole = (plated_hole, ctx, options = {}) => {
29827
29954
  height: copperSpan
29828
29955
  })
29829
29956
  );
29830
- const holeCut = (0, import_booleans.union)(
29831
- (0, import_primitives3.cuboid)({
29957
+ const holeCut = (0, import_booleans2.union)(
29958
+ (0, import_primitives4.cuboid)({
29832
29959
  center: [plated_hole.x + holeOffsetX, plated_hole.y + holeOffsetY, 0],
29833
29960
  size: shouldRotate ? [holeHeight, rectLength, throughDrillHeight * 1.1] : [rectLength, holeHeight, throughDrillHeight * 1.1]
29834
29961
  }),
29835
- (0, import_primitives3.cylinder)({
29962
+ (0, import_primitives4.cylinder)({
29836
29963
  center: shouldRotate ? [
29837
29964
  plated_hole.x + holeOffsetX,
29838
29965
  plated_hole.y + holeOffsetY - rectLength / 2,
@@ -29845,7 +29972,7 @@ var platedHole = (plated_hole, ctx, options = {}) => {
29845
29972
  radius: holeRadius,
29846
29973
  height: throughDrillHeight * 1.1
29847
29974
  }),
29848
- (0, import_primitives3.cylinder)({
29975
+ (0, import_primitives4.cylinder)({
29849
29976
  center: shouldRotate ? [
29850
29977
  plated_hole.x + holeOffsetX,
29851
29978
  plated_hole.y + holeOffsetY + rectLength / 2,
@@ -29878,26 +30005,26 @@ var platedHole = (plated_hole, ctx, options = {}) => {
29878
30005
  const topPadBottom = topSurfaceZ;
29879
30006
  const bottomPadTop = bottomSurfaceZ;
29880
30007
  const centerZ = (topPadBottom + bottomPadTop) / 2;
29881
- const rect2d = (0, import_primitives3.roundedRectangle)({
30008
+ const rect2d = (0, import_primitives4.roundedRectangle)({
29882
30009
  size: [padWidth, padHeight],
29883
30010
  roundRadius: rectBorderRadius || 0,
29884
30011
  segments: RECT_PAD_SEGMENTS
29885
30012
  });
29886
- const extruded = (0, import_extrusions2.extrudeLinear)({ height: height10 }, rect2d);
29887
- return (0, import_transforms2.translate)(
30013
+ const extruded = (0, import_extrusions3.extrudeLinear)({ height: height10 }, rect2d);
30014
+ return (0, import_transforms3.translate)(
29888
30015
  [plated_hole.x, plated_hole.y, centerZ - height10 / 2],
29889
30016
  extruded
29890
30017
  );
29891
30018
  })();
29892
- const copperTopPadCut = (0, import_booleans.subtract)(copperTopPad, holeCut);
29893
- const copperBottomPadCut = (0, import_booleans.subtract)(copperBottomPad, holeCut);
29894
- const copperFillCut = (0, import_booleans.subtract)(copperFill, holeCut);
29895
- const barrelHoleCut = (0, import_booleans.union)(
29896
- (0, import_primitives3.cuboid)({
30019
+ const copperTopPadCut = (0, import_booleans2.subtract)(copperTopPad, holeCut);
30020
+ const copperBottomPadCut = (0, import_booleans2.subtract)(copperBottomPad, holeCut);
30021
+ const copperFillCut = (0, import_booleans2.subtract)(copperFill, holeCut);
30022
+ const barrelHoleCut = (0, import_booleans2.union)(
30023
+ (0, import_primitives4.cuboid)({
29897
30024
  center: [plated_hole.x + holeOffsetX, plated_hole.y + holeOffsetY, 0],
29898
30025
  size: shouldRotate ? [holeHeight - 2 * M, rectLength - 2 * M, throughDrillHeight * 1.1] : [rectLength - 2 * M, holeHeight - 2 * M, throughDrillHeight * 1.1]
29899
30026
  }),
29900
- (0, import_primitives3.cylinder)({
30027
+ (0, import_primitives4.cylinder)({
29901
30028
  center: shouldRotate ? [
29902
30029
  plated_hole.x + holeOffsetX,
29903
30030
  plated_hole.y + holeOffsetY - rectLength / 2,
@@ -29910,7 +30037,7 @@ var platedHole = (plated_hole, ctx, options = {}) => {
29910
30037
  radius: holeRadius - M,
29911
30038
  height: throughDrillHeight * 1.1
29912
30039
  }),
29913
- (0, import_primitives3.cylinder)({
30040
+ (0, import_primitives4.cylinder)({
29914
30041
  center: shouldRotate ? [
29915
30042
  plated_hole.x + holeOffsetX,
29916
30043
  plated_hole.y + holeOffsetY + rectLength / 2,
@@ -29924,19 +30051,59 @@ var platedHole = (plated_hole, ctx, options = {}) => {
29924
30051
  height: throughDrillHeight * 1.1
29925
30052
  })
29926
30053
  );
29927
- const barrelWithHole = (0, import_booleans.subtract)(barrel, barrelHoleCut);
30054
+ const barrelWithHole = (0, import_booleans2.subtract)(barrel, barrelHoleCut);
29928
30055
  const finalCopper = maybeClip(
29929
- (0, import_booleans.union)(copperTopPadCut, copperBottomPadCut, copperFillCut, barrelWithHole),
30056
+ (0, import_booleans2.union)(copperTopPadCut, copperBottomPadCut, copperFillCut, barrelWithHole),
29930
30057
  clipGeom
29931
30058
  );
29932
30059
  return (0, import_colors2.colorize)(colors.copper, finalCopper);
30060
+ } else if (plated_hole.shape === "hole_with_polygon_pad") {
30061
+ const padOutline = plated_hole.pad_outline;
30062
+ if (!Array.isArray(padOutline) || padOutline.length < 3) {
30063
+ throw new Error(
30064
+ `Invalid pad_outline for plated hole at (${plated_hole.x}, ${plated_hole.y})`
30065
+ );
30066
+ }
30067
+ const polygonPoints = padOutline.map((point2) => [
30068
+ point2.x,
30069
+ point2.y
30070
+ ]);
30071
+ const polygon2d = (0, import_primitives4.polygon)({ points: polygonPoints });
30072
+ const centerZ = (topSurfaceZ + bottomSurfaceZ) / 2;
30073
+ const createPolygonPad = (thickness, zCenter) => {
30074
+ const safeThickness = Math.max(thickness, M);
30075
+ const extruded = (0, import_extrusions3.extrudeLinear)({ height: safeThickness }, polygon2d);
30076
+ return (0, import_transforms3.translate)(
30077
+ [plated_hole.x, plated_hole.y, zCenter - safeThickness / 2],
30078
+ extruded
30079
+ );
30080
+ };
30081
+ const mainFill = createPolygonPad(
30082
+ Math.max(copperSpan - platedHoleLipHeight * 2, M),
30083
+ centerZ
30084
+ );
30085
+ const topPad = createPolygonPad(platedHoleLipHeight, topSurfaceZ);
30086
+ const bottomPad = createPolygonPad(platedHoleLipHeight, bottomSurfaceZ);
30087
+ const copperSolid = maybeClip((0, import_booleans2.union)(mainFill, topPad, bottomPad), clipGeom);
30088
+ const barrel = createHoleWithPolygonPadHoleGeom(plated_hole, copperSpan);
30089
+ if (!barrel) return (0, import_colors2.colorize)(colors.copper, copperSolid);
30090
+ const drill = createHoleWithPolygonPadHoleGeom(plated_hole, throughDrillHeight, {
30091
+ sizeDelta: -2 * M
30092
+ }) || barrel;
30093
+ let finalCopper = (0, import_booleans2.union)((0, import_booleans2.subtract)(copperSolid, barrel), barrel);
30094
+ if (options.clipGeom) {
30095
+ finalCopper = (0, import_booleans2.subtract)(finalCopper, drill);
30096
+ finalCopper = (0, import_booleans2.intersect)(finalCopper, options.clipGeom);
30097
+ return (0, import_colors2.colorize)(colors.copper, finalCopper);
30098
+ }
30099
+ return (0, import_colors2.colorize)(colors.copper, (0, import_booleans2.subtract)(finalCopper, drill));
29933
30100
  } else {
29934
30101
  throw new Error(`Unsupported plated hole shape: ${plated_hole.shape}`);
29935
30102
  }
29936
30103
  };
29937
30104
 
29938
30105
  // src/BoardGeomBuilder.ts
29939
- var import_extrusions7 = __toESM(require_extrusions(), 1);
30106
+ var import_extrusions8 = __toESM(require_extrusions(), 1);
29940
30107
  var import_expansions4 = __toESM(require_expansions(), 1);
29941
30108
 
29942
30109
  // src/geoms/create-geoms-for-silkscreen-text.ts
@@ -29959,7 +30126,7 @@ function isUndefined(val) {
29959
30126
  }
29960
30127
 
29961
30128
  // node_modules/transformation-matrix/src/translate.js
29962
- function translate3(tx, ty = 0) {
30129
+ function translate4(tx, ty = 0) {
29963
30130
  return {
29964
30131
  a: 1,
29965
30132
  c: 0,
@@ -30018,9 +30185,9 @@ function rotate(angle, cx, cy) {
30018
30185
  return rotationMatrix;
30019
30186
  }
30020
30187
  return transform([
30021
- translate3(cx, cy),
30188
+ translate4(cx, cy),
30022
30189
  rotationMatrix,
30023
- translate3(-cx, -cy)
30190
+ translate4(-cx, -cy)
30024
30191
  ]);
30025
30192
  }
30026
30193
 
@@ -30208,19 +30375,19 @@ function createSilkscreenTextGeoms(silkscreenText) {
30208
30375
  const transforms = [];
30209
30376
  if (silkscreenText.layer === "bottom") {
30210
30377
  transforms.push(
30211
- translate3(centerX, centerY),
30378
+ translate4(centerX, centerY),
30212
30379
  { a: -1, b: 0, c: 0, d: 1, e: 0, f: 0 },
30213
30380
  // horizontal flip matrix
30214
- translate3(-centerX, -centerY)
30381
+ translate4(-centerX, -centerY)
30215
30382
  );
30216
30383
  rotationDegrees = -rotationDegrees;
30217
30384
  }
30218
30385
  if (rotationDegrees) {
30219
30386
  const rad = rotationDegrees * Math.PI / 180;
30220
30387
  transforms.push(
30221
- translate3(centerX, centerY),
30388
+ translate4(centerX, centerY),
30222
30389
  rotate(rad),
30223
- translate3(-centerX, -centerY)
30390
+ translate4(-centerX, -centerY)
30224
30391
  );
30225
30392
  }
30226
30393
  let transformedOutlines = textOutlines;
@@ -30241,10 +30408,10 @@ function createSilkscreenTextGeoms(silkscreenText) {
30241
30408
  }
30242
30409
 
30243
30410
  // src/geoms/create-geoms-for-silkscreen-path.ts
30244
- var import_primitives4 = __toESM(require_primitives(), 1);
30411
+ var import_primitives5 = __toESM(require_primitives(), 1);
30245
30412
  var import_expansions2 = __toESM(require_expansions(), 1);
30246
- var import_extrusions3 = __toESM(require_extrusions(), 1);
30247
- var import_transforms3 = __toESM(require_transforms(), 1);
30413
+ var import_extrusions4 = __toESM(require_extrusions(), 1);
30414
+ var import_transforms4 = __toESM(require_transforms(), 1);
30248
30415
  var import_colors3 = __toESM(require_colors(), 1);
30249
30416
 
30250
30417
  // src/utils/units.ts
@@ -30299,7 +30466,7 @@ function createSilkscreenPathGeom(sp, ctx) {
30299
30466
  parseDimensionToMm(p.x) ?? 0,
30300
30467
  parseDimensionToMm(p.y) ?? 0
30301
30468
  ]);
30302
- const pathLine = (0, import_primitives4.line)(routePoints);
30469
+ const pathLine = (0, import_primitives5.line)(routePoints);
30303
30470
  const strokeWidth = coerceDimensionToMm(sp.stroke_width, 0.1);
30304
30471
  const expandedPath = (0, import_expansions2.expand)(
30305
30472
  { delta: strokeWidth / 2, corners: "round" },
@@ -30307,9 +30474,9 @@ function createSilkscreenPathGeom(sp, ctx) {
30307
30474
  );
30308
30475
  const layerSign = sp.layer === "bottom" ? -1 : 1;
30309
30476
  const zPos = layerSign * ctx.pcbThickness / 2 + layerSign * M * 1.5;
30310
- let pathGeom = (0, import_transforms3.translate)(
30477
+ let pathGeom = (0, import_transforms4.translate)(
30311
30478
  [0, 0, zPos],
30312
- (0, import_extrusions3.extrudeLinear)({ height: 0.012 }, expandedPath)
30479
+ (0, import_extrusions4.extrudeLinear)({ height: 0.012 }, expandedPath)
30313
30480
  // Standard silkscreen thickness
30314
30481
  );
30315
30482
  pathGeom = (0, import_colors3.colorize)([1, 1, 1], pathGeom);
@@ -30317,10 +30484,10 @@ function createSilkscreenPathGeom(sp, ctx) {
30317
30484
  }
30318
30485
 
30319
30486
  // src/geoms/create-geoms-for-silkscreen-line.ts
30320
- var import_primitives5 = __toESM(require_primitives(), 1);
30487
+ var import_primitives6 = __toESM(require_primitives(), 1);
30321
30488
  var import_expansions3 = __toESM(require_expansions(), 1);
30322
- var import_extrusions4 = __toESM(require_extrusions(), 1);
30323
- var import_transforms4 = __toESM(require_transforms(), 1);
30489
+ var import_extrusions5 = __toESM(require_extrusions(), 1);
30490
+ var import_transforms5 = __toESM(require_transforms(), 1);
30324
30491
  var import_colors4 = __toESM(require_colors(), 1);
30325
30492
  function createSilkscreenLineGeom(sl, ctx) {
30326
30493
  const x1 = parseDimensionToMm(sl.x1) ?? 0;
@@ -30332,7 +30499,7 @@ function createSilkscreenLineGeom(sl, ctx) {
30332
30499
  [x1, y1],
30333
30500
  [x2, y2]
30334
30501
  ];
30335
- const baseLine = (0, import_primitives5.line)(routePoints);
30502
+ const baseLine = (0, import_primitives6.line)(routePoints);
30336
30503
  const strokeWidth = coerceDimensionToMm(sl.stroke_width, 0.1);
30337
30504
  const expandedLine = (0, import_expansions3.expand)(
30338
30505
  { delta: strokeWidth / 2, corners: "round" },
@@ -30340,20 +30507,20 @@ function createSilkscreenLineGeom(sl, ctx) {
30340
30507
  );
30341
30508
  const layerSign = sl.layer === "bottom" ? -1 : 1;
30342
30509
  const zPos = layerSign * ctx.pcbThickness / 2 + layerSign * M * 1.5;
30343
- let lineGeom = (0, import_transforms4.translate)(
30510
+ let lineGeom = (0, import_transforms5.translate)(
30344
30511
  [0, 0, zPos],
30345
- (0, import_extrusions4.extrudeLinear)({ height: 0.012 }, expandedLine)
30512
+ (0, import_extrusions5.extrudeLinear)({ height: 0.012 }, expandedLine)
30346
30513
  );
30347
30514
  lineGeom = (0, import_colors4.colorize)([1, 1, 1], lineGeom);
30348
30515
  return lineGeom;
30349
30516
  }
30350
30517
 
30351
30518
  // src/geoms/create-geoms-for-silkscreen-rect.ts
30352
- var import_primitives6 = __toESM(require_primitives(), 1);
30353
- var import_extrusions5 = __toESM(require_extrusions(), 1);
30354
- var import_transforms5 = __toESM(require_transforms(), 1);
30519
+ var import_primitives7 = __toESM(require_primitives(), 1);
30520
+ var import_extrusions6 = __toESM(require_extrusions(), 1);
30521
+ var import_transforms6 = __toESM(require_transforms(), 1);
30355
30522
  var import_colors5 = __toESM(require_colors(), 1);
30356
- var import_booleans2 = __toESM(require_booleans(), 1);
30523
+ var import_booleans3 = __toESM(require_booleans(), 1);
30357
30524
  var RECT_SEGMENTS = 64;
30358
30525
  function createSilkscreenRectGeom(rect, ctx) {
30359
30526
  const width10 = coerceDimensionToMm(rect.width, 0);
@@ -30367,9 +30534,9 @@ function createSilkscreenRectGeom(rect, ctx) {
30367
30534
  height10,
30368
30535
  typeof rawBorderRadius === "string" ? parseDimensionToMm(rawBorderRadius) : rawBorderRadius
30369
30536
  );
30370
- const createRectGeom = (rectWidth, rectHeight, radius) => (0, import_extrusions5.extrudeLinear)(
30537
+ const createRectGeom = (rectWidth, rectHeight, radius) => (0, import_extrusions6.extrudeLinear)(
30371
30538
  { height: 0.012 },
30372
- (0, import_primitives6.roundedRectangle)({
30539
+ (0, import_primitives7.roundedRectangle)({
30373
30540
  size: [rectWidth, rectHeight],
30374
30541
  roundRadius: radius,
30375
30542
  segments: RECT_SEGMENTS
@@ -30394,28 +30561,28 @@ function createSilkscreenRectGeom(rect, ctx) {
30394
30561
  Math.max(borderRadius - strokeWidth, 0)
30395
30562
  );
30396
30563
  const innerGeom = createRectGeom(innerWidth, innerHeight, innerRadius);
30397
- strokeGeom = (0, import_booleans2.subtract)(outerGeom, innerGeom);
30564
+ strokeGeom = (0, import_booleans3.subtract)(outerGeom, innerGeom);
30398
30565
  } else {
30399
30566
  strokeGeom = outerGeom;
30400
30567
  }
30401
30568
  }
30402
30569
  let rectGeom = fillGeom;
30403
30570
  if (strokeGeom) {
30404
- rectGeom = rectGeom ? (0, import_booleans2.union)(rectGeom, strokeGeom) : strokeGeom;
30571
+ rectGeom = rectGeom ? (0, import_booleans3.union)(rectGeom, strokeGeom) : strokeGeom;
30405
30572
  }
30406
30573
  if (!rectGeom) return void 0;
30407
30574
  const layerSign = rect.layer === "bottom" ? -1 : 1;
30408
30575
  const zPos = layerSign * ctx.pcbThickness / 2 + layerSign * M * 1.5;
30409
- rectGeom = (0, import_transforms5.translate)([centerX, centerY, zPos], rectGeom);
30576
+ rectGeom = (0, import_transforms6.translate)([centerX, centerY, zPos], rectGeom);
30410
30577
  return (0, import_colors5.colorize)([1, 1, 1], rectGeom);
30411
30578
  }
30412
30579
 
30413
30580
  // src/geoms/create-geoms-for-silkscreen-circle.ts
30414
- var import_primitives7 = __toESM(require_primitives(), 1);
30415
- var import_extrusions6 = __toESM(require_extrusions(), 1);
30416
- var import_transforms6 = __toESM(require_transforms(), 1);
30581
+ var import_primitives8 = __toESM(require_primitives(), 1);
30582
+ var import_extrusions7 = __toESM(require_extrusions(), 1);
30583
+ var import_transforms7 = __toESM(require_transforms(), 1);
30417
30584
  var import_colors6 = __toESM(require_colors(), 1);
30418
- var import_booleans3 = __toESM(require_booleans(), 1);
30585
+ var import_booleans4 = __toESM(require_booleans(), 1);
30419
30586
  var CIRCLE_SEGMENTS = 64;
30420
30587
  function createSilkscreenCircleGeom(circleEl, ctx) {
30421
30588
  const radius = coerceDimensionToMm(circleEl.radius, 0);
@@ -30431,31 +30598,31 @@ function createSilkscreenCircleGeom(circleEl, ctx) {
30431
30598
  if (hasStroke) {
30432
30599
  const outerRadius = radius + strokeWidth / 2;
30433
30600
  const innerRadius = radius - strokeWidth / 2;
30434
- const outerCircle2d = (0, import_primitives7.circle)({
30601
+ const outerCircle2d = (0, import_primitives8.circle)({
30435
30602
  radius: outerRadius,
30436
30603
  segments: CIRCLE_SEGMENTS
30437
30604
  });
30438
- let ring3d = (0, import_extrusions6.extrudeLinear)({ height: baseHeight }, outerCircle2d);
30605
+ let ring3d = (0, import_extrusions7.extrudeLinear)({ height: baseHeight }, outerCircle2d);
30439
30606
  if (innerRadius > 0) {
30440
- const innerCircle2d = (0, import_primitives7.circle)({
30607
+ const innerCircle2d = (0, import_primitives8.circle)({
30441
30608
  radius: innerRadius,
30442
30609
  segments: CIRCLE_SEGMENTS
30443
30610
  });
30444
- const inner3d = (0, import_extrusions6.extrudeLinear)({ height: baseHeight }, innerCircle2d);
30445
- ring3d = (0, import_booleans3.subtract)(ring3d, inner3d);
30611
+ const inner3d = (0, import_extrusions7.extrudeLinear)({ height: baseHeight }, innerCircle2d);
30612
+ ring3d = (0, import_booleans4.subtract)(ring3d, inner3d);
30446
30613
  }
30447
30614
  circleGeom = ring3d;
30448
30615
  } else {
30449
- const filledCircle2d = (0, import_primitives7.circle)({ radius, segments: CIRCLE_SEGMENTS });
30450
- circleGeom = (0, import_extrusions6.extrudeLinear)({ height: baseHeight }, filledCircle2d);
30616
+ const filledCircle2d = (0, import_primitives8.circle)({ radius, segments: CIRCLE_SEGMENTS });
30617
+ circleGeom = (0, import_extrusions7.extrudeLinear)({ height: baseHeight }, filledCircle2d);
30451
30618
  }
30452
- const translatedGeom = (0, import_transforms6.translate)([centerX, centerY, zPos], circleGeom);
30619
+ const translatedGeom = (0, import_transforms7.translate)([centerX, centerY, zPos], circleGeom);
30453
30620
  return (0, import_colors6.colorize)([1, 1, 1], translatedGeom);
30454
30621
  }
30455
30622
 
30456
30623
  // src/geoms/brep-converter.ts
30457
- var import_primitives8 = __toESM(require_primitives(), 1);
30458
- var import_booleans4 = __toESM(require_booleans(), 1);
30624
+ var import_primitives9 = __toESM(require_primitives(), 1);
30625
+ var import_booleans5 = __toESM(require_booleans(), 1);
30459
30626
  function segmentToPoints(p1, p2, bulge, arcSegments) {
30460
30627
  if (!bulge || Math.abs(bulge) < 1e-9) {
30461
30628
  return [];
@@ -30523,7 +30690,7 @@ function createGeom2FromBRep(brep, arcSegments = 16) {
30523
30690
  if (arePointsClockwise2(outerPoints)) {
30524
30691
  outerPoints.reverse();
30525
30692
  }
30526
- const outerGeom = (0, import_primitives8.polygon)({ points: outerPoints });
30693
+ const outerGeom = (0, import_primitives9.polygon)({ points: outerPoints });
30527
30694
  if (!brep.inner_rings || brep.inner_rings.length === 0) {
30528
30695
  return outerGeom;
30529
30696
  }
@@ -30532,10 +30699,10 @@ function createGeom2FromBRep(brep, arcSegments = 16) {
30532
30699
  if (arePointsClockwise2(innerPoints)) {
30533
30700
  innerPoints.reverse();
30534
30701
  }
30535
- return (0, import_primitives8.polygon)({ points: innerPoints });
30702
+ return (0, import_primitives9.polygon)({ points: innerPoints });
30536
30703
  });
30537
30704
  if (innerGeoms.length === 0) return outerGeom;
30538
- return (0, import_booleans4.subtract)(outerGeom, innerGeoms);
30705
+ return (0, import_booleans5.subtract)(outerGeom, innerGeoms);
30539
30706
  }
30540
30707
 
30541
30708
  // src/BoardGeomBuilder.ts
@@ -30545,15 +30712,15 @@ var BOARD_CLIP_XY_OUTSET = 0.05;
30545
30712
  var createCenteredRectPadGeom = (width10, height10, thickness, rectBorderRadius) => {
30546
30713
  const clampedRadius = clampRectBorderRadius(width10, height10, rectBorderRadius);
30547
30714
  if (clampedRadius <= 0) {
30548
- return (0, import_primitives9.cuboid)({ center: [0, 0, 0], size: [width10, height10, thickness] });
30715
+ return (0, import_primitives10.cuboid)({ center: [0, 0, 0], size: [width10, height10, thickness] });
30549
30716
  }
30550
- const rect2d = (0, import_primitives9.roundedRectangle)({
30717
+ const rect2d = (0, import_primitives10.roundedRectangle)({
30551
30718
  size: [width10, height10],
30552
30719
  roundRadius: clampedRadius,
30553
30720
  segments: PAD_ROUNDED_SEGMENTS
30554
30721
  });
30555
- const extruded = (0, import_extrusions7.extrudeLinear)({ height: thickness }, rect2d);
30556
- return (0, import_transforms7.translate)([0, 0, -thickness / 2], extruded);
30722
+ const extruded = (0, import_extrusions8.extrudeLinear)({ height: thickness }, rect2d);
30723
+ return (0, import_transforms8.translate)([0, 0, -thickness / 2], extruded);
30557
30724
  };
30558
30725
  var buildStateOrder = [
30559
30726
  "initializing",
@@ -30680,11 +30847,11 @@ var BoardGeomBuilder = class {
30680
30847
  { xyOutset: BOARD_CLIP_XY_OUTSET }
30681
30848
  );
30682
30849
  } else {
30683
- this.boardGeom = (0, import_primitives9.cuboid)({
30850
+ this.boardGeom = (0, import_primitives10.cuboid)({
30684
30851
  size: [this.board.width, this.board.height, this.ctx.pcbThickness],
30685
30852
  center: [this.board.center.x, this.board.center.y, 0]
30686
30853
  });
30687
- this.boardClipGeom = (0, import_primitives9.cuboid)({
30854
+ this.boardClipGeom = (0, import_primitives10.cuboid)({
30688
30855
  size: [
30689
30856
  this.board.width + 2 * BOARD_CLIP_XY_OUTSET,
30690
30857
  this.board.height + 2 * BOARD_CLIP_XY_OUTSET,
@@ -30820,17 +30987,40 @@ var BoardGeomBuilder = class {
30820
30987
  const cutoutHeight = this.ctx.pcbThickness * 1.5;
30821
30988
  switch (cutout.shape) {
30822
30989
  case "rect":
30823
- cutoutGeom = (0, import_primitives9.cuboid)({
30824
- center: [cutout.center.x, cutout.center.y, 0],
30825
- size: [cutout.width, cutout.height, cutoutHeight]
30826
- });
30990
+ const rectCornerRadius = clampRectBorderRadius(
30991
+ cutout.width,
30992
+ cutout.height,
30993
+ extractRectBorderRadius(cutout)
30994
+ );
30995
+ if (rectCornerRadius > 0) {
30996
+ const rect2d = (0, import_primitives10.roundedRectangle)({
30997
+ size: [cutout.width, cutout.height],
30998
+ roundRadius: rectCornerRadius,
30999
+ segments: PAD_ROUNDED_SEGMENTS
31000
+ });
31001
+ cutoutGeom = (0, import_extrusions8.extrudeLinear)({ height: cutoutHeight }, rect2d);
31002
+ cutoutGeom = (0, import_transforms8.translate)([0, 0, -cutoutHeight / 2], cutoutGeom);
31003
+ cutoutGeom = (0, import_transforms8.translate)(
31004
+ [cutout.center.x, cutout.center.y, 0],
31005
+ cutoutGeom
31006
+ );
31007
+ } else {
31008
+ const baseCutoutGeom = (0, import_primitives10.cuboid)({
31009
+ center: [0, 0, 0],
31010
+ size: [cutout.width, cutout.height, cutoutHeight]
31011
+ });
31012
+ cutoutGeom = (0, import_transforms8.translate)(
31013
+ [cutout.center.x, cutout.center.y, 0],
31014
+ baseCutoutGeom
31015
+ );
31016
+ }
30827
31017
  if (cutout.rotation) {
30828
31018
  const rotationRadians = cutout.rotation * Math.PI / 180;
30829
- cutoutGeom = (0, import_transforms7.rotateZ)(rotationRadians, cutoutGeom);
31019
+ cutoutGeom = (0, import_transforms8.rotateZ)(rotationRadians, cutoutGeom);
30830
31020
  }
30831
31021
  break;
30832
31022
  case "circle":
30833
- cutoutGeom = (0, import_primitives9.cylinder)({
31023
+ cutoutGeom = (0, import_primitives10.cylinder)({
30834
31024
  center: [cutout.center.x, cutout.center.y, 0],
30835
31025
  radius: cutout.radius,
30836
31026
  height: cutoutHeight
@@ -30847,13 +31037,13 @@ var BoardGeomBuilder = class {
30847
31037
  if (arePointsClockwise(pointsVec2)) {
30848
31038
  pointsVec2 = pointsVec2.reverse();
30849
31039
  }
30850
- const polygon2d = (0, import_primitives9.polygon)({ points: pointsVec2 });
30851
- cutoutGeom = (0, import_extrusions7.extrudeLinear)({ height: cutoutHeight }, polygon2d);
30852
- cutoutGeom = (0, import_transforms7.translate)([0, 0, -cutoutHeight / 2], cutoutGeom);
31040
+ const polygon2d = (0, import_primitives10.polygon)({ points: pointsVec2 });
31041
+ cutoutGeom = (0, import_extrusions8.extrudeLinear)({ height: cutoutHeight }, polygon2d);
31042
+ cutoutGeom = (0, import_transforms8.translate)([0, 0, -cutoutHeight / 2], cutoutGeom);
30853
31043
  break;
30854
31044
  }
30855
31045
  if (cutoutGeom) {
30856
- this.boardGeom = (0, import_booleans5.subtract)(this.boardGeom, cutoutGeom);
31046
+ this.boardGeom = (0, import_booleans6.subtract)(this.boardGeom, cutoutGeom);
30857
31047
  }
30858
31048
  }
30859
31049
  processCopperPour(pour) {
@@ -30861,22 +31051,22 @@ var BoardGeomBuilder = class {
30861
31051
  const zPos = layerSign * this.ctx.pcbThickness / 2 + layerSign * BOARD_SURFACE_OFFSET.copper;
30862
31052
  let pourGeom = null;
30863
31053
  if (pour.shape === "rect") {
30864
- let baseGeom = (0, import_primitives9.cuboid)({
31054
+ let baseGeom = (0, import_primitives10.cuboid)({
30865
31055
  center: [0, 0, 0],
30866
31056
  // Create at origin for rotation
30867
31057
  size: [pour.width, pour.height, M]
30868
31058
  });
30869
31059
  if ("rotation" in pour && pour.rotation) {
30870
31060
  const rotationRadians = pour.rotation * Math.PI / 180;
30871
- baseGeom = (0, import_transforms7.rotateZ)(rotationRadians, baseGeom);
31061
+ baseGeom = (0, import_transforms8.rotateZ)(rotationRadians, baseGeom);
30872
31062
  }
30873
- pourGeom = (0, import_transforms7.translate)([pour.center.x, pour.center.y, zPos], baseGeom);
31063
+ pourGeom = (0, import_transforms8.translate)([pour.center.x, pour.center.y, zPos], baseGeom);
30874
31064
  } else if (pour.shape === "brep") {
30875
31065
  const brepShape = pour.brep_shape;
30876
31066
  if (brepShape && brepShape.outer_ring) {
30877
31067
  const pourGeom2 = createGeom2FromBRep(brepShape);
30878
- pourGeom = (0, import_extrusions7.extrudeLinear)({ height: M }, pourGeom2);
30879
- pourGeom = (0, import_transforms7.translate)([0, 0, zPos], pourGeom);
31068
+ pourGeom = (0, import_extrusions8.extrudeLinear)({ height: M }, pourGeom2);
31069
+ pourGeom = (0, import_transforms8.translate)([0, 0, zPos], pourGeom);
30880
31070
  }
30881
31071
  } else if (pour.shape === "polygon") {
30882
31072
  let pointsVec2 = pour.points.map((p) => [p.x, p.y]);
@@ -30889,13 +31079,13 @@ var BoardGeomBuilder = class {
30889
31079
  if (arePointsClockwise(pointsVec2)) {
30890
31080
  pointsVec2 = pointsVec2.reverse();
30891
31081
  }
30892
- const polygon2d = (0, import_primitives9.polygon)({ points: pointsVec2 });
30893
- pourGeom = (0, import_extrusions7.extrudeLinear)({ height: M }, polygon2d);
30894
- pourGeom = (0, import_transforms7.translate)([0, 0, zPos], pourGeom);
31082
+ const polygon2d = (0, import_primitives10.polygon)({ points: pointsVec2 });
31083
+ pourGeom = (0, import_extrusions8.extrudeLinear)({ height: M }, polygon2d);
31084
+ pourGeom = (0, import_transforms8.translate)([0, 0, zPos], pourGeom);
30895
31085
  }
30896
31086
  if (pourGeom) {
30897
31087
  if (this.boardClipGeom) {
30898
- pourGeom = (0, import_booleans5.intersect)(this.boardClipGeom, pourGeom);
31088
+ pourGeom = (0, import_booleans6.intersect)(this.boardClipGeom, pourGeom);
30899
31089
  }
30900
31090
  const covered = pour.covered_with_solder_mask !== false;
30901
31091
  const pourMaterialColor = covered ? tracesMaterialColors[this.board.material] ?? colors.fr4GreenSolderWithMask : colors.copper;
@@ -30908,7 +31098,7 @@ var BoardGeomBuilder = class {
30908
31098
  if (ph.shape === "circle" || ph.shape === "circular_hole_with_rect_pad") {
30909
31099
  let cyGeom = null;
30910
31100
  if (ph.shape === "circular_hole_with_rect_pad") {
30911
- cyGeom = (0, import_primitives9.cylinder)({
31101
+ cyGeom = (0, import_primitives10.cylinder)({
30912
31102
  center: [
30913
31103
  ph.x + (ph.hole_offset_x || 0),
30914
31104
  ph.y + (ph.hole_offset_y || 0),
@@ -30920,7 +31110,7 @@ var BoardGeomBuilder = class {
30920
31110
  // Ensure it cuts through
30921
31111
  });
30922
31112
  } else {
30923
- cyGeom = (0, import_primitives9.cylinder)({
31113
+ cyGeom = (0, import_primitives10.cylinder)({
30924
31114
  center: [ph.x, ph.y, 0],
30925
31115
  radius: ph.hole_diameter / 2 + M,
30926
31116
  // Add margin for subtraction
@@ -30929,10 +31119,10 @@ var BoardGeomBuilder = class {
30929
31119
  });
30930
31120
  }
30931
31121
  if (!opts.dontCutBoard) {
30932
- this.boardGeom = (0, import_booleans5.subtract)(this.boardGeom, cyGeom);
31122
+ this.boardGeom = (0, import_booleans6.subtract)(this.boardGeom, cyGeom);
30933
31123
  }
30934
31124
  this.padGeoms = this.padGeoms.map(
30935
- (pg) => (0, import_colors7.colorize)(colors.copper, (0, import_booleans5.subtract)(pg, cyGeom))
31125
+ (pg) => (0, import_colors7.colorize)(colors.copper, (0, import_booleans6.subtract)(pg, cyGeom))
30936
31126
  );
30937
31127
  const platedHoleGeom = platedHole(ph, this.ctx, {
30938
31128
  clipGeom: this.boardClipGeom
@@ -30946,8 +31136,8 @@ var BoardGeomBuilder = class {
30946
31136
  const rectLength = Math.abs(holeWidth - holeHeight);
30947
31137
  let pillHole;
30948
31138
  if (ph.shape === "pill_hole_with_rect_pad") {
30949
- pillHole = (0, import_booleans5.union)(
30950
- (0, import_primitives9.cuboid)({
31139
+ pillHole = (0, import_booleans6.union)(
31140
+ (0, import_primitives10.cuboid)({
30951
31141
  center: [
30952
31142
  ph.x + (ph.hole_offset_x || 0),
30953
31143
  ph.y + (ph.hole_offset_y || 0),
@@ -30955,7 +31145,7 @@ var BoardGeomBuilder = class {
30955
31145
  ],
30956
31146
  size: shouldRotate ? [holeHeight, rectLength, this.ctx.pcbThickness * 1.5] : [rectLength, holeHeight, this.ctx.pcbThickness * 1.5]
30957
31147
  }),
30958
- (0, import_primitives9.cylinder)({
31148
+ (0, import_primitives10.cylinder)({
30959
31149
  center: shouldRotate ? [
30960
31150
  ph.x + (ph.hole_offset_x || 0),
30961
31151
  ph.y + (ph.hole_offset_y || 0) - rectLength / 2,
@@ -30968,7 +31158,7 @@ var BoardGeomBuilder = class {
30968
31158
  radius: holeRadius,
30969
31159
  height: this.ctx.pcbThickness * 1.5
30970
31160
  }),
30971
- (0, import_primitives9.cylinder)({
31161
+ (0, import_primitives10.cylinder)({
30972
31162
  center: shouldRotate ? [
30973
31163
  ph.x + (ph.hole_offset_x || 0),
30974
31164
  ph.y + (ph.hole_offset_y || 0) + rectLength / 2,
@@ -30983,17 +31173,17 @@ var BoardGeomBuilder = class {
30983
31173
  })
30984
31174
  );
30985
31175
  } else {
30986
- pillHole = (0, import_booleans5.union)(
30987
- (0, import_primitives9.cuboid)({
31176
+ pillHole = (0, import_booleans6.union)(
31177
+ (0, import_primitives10.cuboid)({
30988
31178
  center: [ph.x, ph.y, 0],
30989
31179
  size: shouldRotate ? [holeHeight, rectLength, this.ctx.pcbThickness * 1.5] : [rectLength, holeHeight, this.ctx.pcbThickness * 1.5]
30990
31180
  }),
30991
- (0, import_primitives9.cylinder)({
31181
+ (0, import_primitives10.cylinder)({
30992
31182
  center: shouldRotate ? [ph.x, ph.y - rectLength / 2, 0] : [ph.x - rectLength / 2, ph.y, 0],
30993
31183
  radius: holeRadius,
30994
31184
  height: this.ctx.pcbThickness * 1.5
30995
31185
  }),
30996
- (0, import_primitives9.cylinder)({
31186
+ (0, import_primitives10.cylinder)({
30997
31187
  center: shouldRotate ? [ph.x, ph.y + rectLength / 2, 0] : [ph.x + rectLength / 2, ph.y, 0],
30998
31188
  radius: holeRadius,
30999
31189
  height: this.ctx.pcbThickness * 1.5
@@ -31001,10 +31191,36 @@ var BoardGeomBuilder = class {
31001
31191
  );
31002
31192
  }
31003
31193
  if (!opts.dontCutBoard) {
31004
- this.boardGeom = (0, import_booleans5.subtract)(this.boardGeom, pillHole);
31194
+ this.boardGeom = (0, import_booleans6.subtract)(this.boardGeom, pillHole);
31195
+ }
31196
+ this.padGeoms = this.padGeoms.map(
31197
+ (pg) => (0, import_colors7.colorize)(colors.copper, (0, import_booleans6.subtract)(pg, pillHole))
31198
+ );
31199
+ const platedHoleGeom = platedHole(ph, this.ctx, {
31200
+ clipGeom: this.boardClipGeom
31201
+ });
31202
+ this.platedHoleGeoms.push(platedHoleGeom);
31203
+ } else if (ph.shape === "hole_with_polygon_pad") {
31204
+ const padOutline = ph.pad_outline;
31205
+ if (!Array.isArray(padOutline) || padOutline.length < 3) {
31206
+ return;
31207
+ }
31208
+ const holeDepth = this.ctx.pcbThickness * 1.5;
31209
+ const boardHole = createHoleWithPolygonPadHoleGeom(ph, holeDepth, {
31210
+ sizeDelta: 2 * M
31211
+ });
31212
+ const copperHole = createHoleWithPolygonPadHoleGeom(ph, holeDepth, {
31213
+ sizeDelta: -2 * M
31214
+ });
31215
+ if (!boardHole || !copperHole) return;
31216
+ if (!opts.dontCutBoard) {
31217
+ this.boardGeom = (0, import_booleans6.subtract)(this.boardGeom, boardHole);
31005
31218
  }
31006
31219
  this.padGeoms = this.padGeoms.map(
31007
- (pg) => (0, import_colors7.colorize)(colors.copper, (0, import_booleans5.subtract)(pg, pillHole))
31220
+ (pg) => (0, import_colors7.colorize)(colors.copper, (0, import_booleans6.subtract)(pg, boardHole))
31221
+ );
31222
+ this.platedHoleGeoms = this.platedHoleGeoms.map(
31223
+ (phg) => (0, import_colors7.colorize)(colors.copper, (0, import_booleans6.subtract)(phg, copperHole))
31008
31224
  );
31009
31225
  const platedHoleGeom = platedHole(ph, this.ctx, {
31010
31226
  clipGeom: this.boardClipGeom
@@ -31017,22 +31233,22 @@ var BoardGeomBuilder = class {
31017
31233
  const holeDepth = this.ctx.pcbThickness * 1.5;
31018
31234
  const copperInset = 0.02;
31019
31235
  if (hole.hole_shape === "round" || hole.hole_shape === "circle") {
31020
- const cyGeom = (0, import_primitives9.cylinder)({
31236
+ const cyGeom = (0, import_primitives10.cylinder)({
31021
31237
  center: [hole.x, hole.y, 0],
31022
31238
  radius: hole.hole_diameter / 2 + M,
31023
31239
  height: holeDepth
31024
31240
  });
31025
- this.boardGeom = (0, import_booleans5.subtract)(this.boardGeom, cyGeom);
31241
+ this.boardGeom = (0, import_booleans6.subtract)(this.boardGeom, cyGeom);
31026
31242
  this.padGeoms = this.padGeoms.map(
31027
- (pg) => (0, import_colors7.colorize)(colors.copper, (0, import_booleans5.subtract)(pg, cyGeom))
31243
+ (pg) => (0, import_colors7.colorize)(colors.copper, (0, import_booleans6.subtract)(pg, cyGeom))
31028
31244
  );
31029
- const copperCut = (0, import_primitives9.cylinder)({
31245
+ const copperCut = (0, import_primitives10.cylinder)({
31030
31246
  center: [hole.x, hole.y, 0],
31031
31247
  radius: hole.hole_diameter / 2 + M / 2,
31032
31248
  height: holeDepth
31033
31249
  });
31034
31250
  this.platedHoleGeoms = this.platedHoleGeoms.map(
31035
- (phg) => (0, import_colors7.colorize)(colors.copper, (0, import_booleans5.subtract)(phg, copperCut))
31251
+ (phg) => (0, import_colors7.colorize)(colors.copper, (0, import_booleans6.subtract)(phg, copperCut))
31036
31252
  );
31037
31253
  } else if (hole.hole_shape === "pill" || hole.hole_shape === "rotated_pill") {
31038
31254
  const holeWidth = hole.hole_width;
@@ -31042,34 +31258,34 @@ var BoardGeomBuilder = class {
31042
31258
  const isRotated = hole.hole_shape === "rotated_pill";
31043
31259
  let pillHole;
31044
31260
  if (holeWidth > holeHeight) {
31045
- pillHole = (0, import_booleans5.union)(
31046
- (0, import_primitives9.cuboid)({
31261
+ pillHole = (0, import_booleans6.union)(
31262
+ (0, import_primitives10.cuboid)({
31047
31263
  center: [hole.x, hole.y, 0],
31048
31264
  size: [rectLength, holeHeight, holeDepth]
31049
31265
  }),
31050
- (0, import_primitives9.cylinder)({
31266
+ (0, import_primitives10.cylinder)({
31051
31267
  center: [hole.x - rectLength / 2, hole.y, 0],
31052
31268
  radius: holeRadius,
31053
31269
  height: holeDepth
31054
31270
  }),
31055
- (0, import_primitives9.cylinder)({
31271
+ (0, import_primitives10.cylinder)({
31056
31272
  center: [hole.x + rectLength / 2, hole.y, 0],
31057
31273
  radius: holeRadius,
31058
31274
  height: holeDepth
31059
31275
  })
31060
31276
  );
31061
31277
  } else {
31062
- pillHole = (0, import_booleans5.union)(
31063
- (0, import_primitives9.cuboid)({
31278
+ pillHole = (0, import_booleans6.union)(
31279
+ (0, import_primitives10.cuboid)({
31064
31280
  center: [hole.x, hole.y, 0],
31065
31281
  size: [holeWidth, rectLength, holeDepth]
31066
31282
  }),
31067
- (0, import_primitives9.cylinder)({
31283
+ (0, import_primitives10.cylinder)({
31068
31284
  center: [hole.x, hole.y - rectLength / 2, 0],
31069
31285
  radius: holeRadius,
31070
31286
  height: holeDepth
31071
31287
  }),
31072
- (0, import_primitives9.cylinder)({
31288
+ (0, import_primitives10.cylinder)({
31073
31289
  center: [hole.x, hole.y + rectLength / 2, 0],
31074
31290
  radius: holeRadius,
31075
31291
  height: holeDepth
@@ -31078,15 +31294,15 @@ var BoardGeomBuilder = class {
31078
31294
  }
31079
31295
  if (isRotated) {
31080
31296
  const rotationRadians = hole.ccw_rotation * Math.PI / 180;
31081
- pillHole = (0, import_transforms7.rotateZ)(rotationRadians, pillHole);
31297
+ pillHole = (0, import_transforms8.rotateZ)(rotationRadians, pillHole);
31082
31298
  }
31083
- this.boardGeom = (0, import_booleans5.subtract)(this.boardGeom, pillHole);
31299
+ this.boardGeom = (0, import_booleans6.subtract)(this.boardGeom, pillHole);
31084
31300
  this.padGeoms = this.padGeoms.map(
31085
- (pg) => (0, import_colors7.colorize)(colors.copper, (0, import_booleans5.subtract)(pg, pillHole))
31301
+ (pg) => (0, import_colors7.colorize)(colors.copper, (0, import_booleans6.subtract)(pg, pillHole))
31086
31302
  );
31087
31303
  const copperPill = (0, import_expansions4.expand)({ delta: -copperInset }, pillHole);
31088
31304
  this.platedHoleGeoms = this.platedHoleGeoms.map(
31089
- (phg) => (0, import_colors7.colorize)(colors.copper, (0, import_booleans5.subtract)(phg, copperPill))
31305
+ (phg) => (0, import_colors7.colorize)(colors.copper, (0, import_booleans6.subtract)(phg, copperPill))
31090
31306
  );
31091
31307
  }
31092
31308
  }
@@ -31101,10 +31317,10 @@ var BoardGeomBuilder = class {
31101
31317
  M,
31102
31318
  rectBorderRadius
31103
31319
  );
31104
- const positionedPadGeom = (0, import_transforms7.translate)([pad2.x, pad2.y, zPos], basePadGeom);
31320
+ const positionedPadGeom = (0, import_transforms8.translate)([pad2.x, pad2.y, zPos], basePadGeom);
31105
31321
  let finalPadGeom = positionedPadGeom;
31106
31322
  if (this.boardClipGeom) {
31107
- finalPadGeom = (0, import_booleans5.intersect)(this.boardClipGeom, finalPadGeom);
31323
+ finalPadGeom = (0, import_booleans6.intersect)(this.boardClipGeom, finalPadGeom);
31108
31324
  }
31109
31325
  finalPadGeom = (0, import_colors7.colorize)(colors.copper, finalPadGeom);
31110
31326
  this.padGeoms.push(finalPadGeom);
@@ -31116,22 +31332,22 @@ var BoardGeomBuilder = class {
31116
31332
  rectBorderRadius
31117
31333
  );
31118
31334
  const rotationRadians = pad2.ccw_rotation * Math.PI / 180;
31119
- basePadGeom = (0, import_transforms7.rotateZ)(rotationRadians, basePadGeom);
31120
- const positionedPadGeom = (0, import_transforms7.translate)([pad2.x, pad2.y, zPos], basePadGeom);
31335
+ basePadGeom = (0, import_transforms8.rotateZ)(rotationRadians, basePadGeom);
31336
+ const positionedPadGeom = (0, import_transforms8.translate)([pad2.x, pad2.y, zPos], basePadGeom);
31121
31337
  let finalPadGeom = positionedPadGeom;
31122
31338
  if (this.boardClipGeom) {
31123
- finalPadGeom = (0, import_booleans5.intersect)(this.boardClipGeom, finalPadGeom);
31339
+ finalPadGeom = (0, import_booleans6.intersect)(this.boardClipGeom, finalPadGeom);
31124
31340
  }
31125
31341
  finalPadGeom = (0, import_colors7.colorize)(colors.copper, finalPadGeom);
31126
31342
  this.padGeoms.push(finalPadGeom);
31127
31343
  } else if (pad2.shape === "circle") {
31128
- let padGeom = (0, import_primitives9.cylinder)({
31344
+ let padGeom = (0, import_primitives10.cylinder)({
31129
31345
  center: [pad2.x, pad2.y, zPos],
31130
31346
  radius: pad2.radius,
31131
31347
  height: M
31132
31348
  });
31133
31349
  if (this.boardClipGeom) {
31134
- padGeom = (0, import_booleans5.intersect)(this.boardClipGeom, padGeom);
31350
+ padGeom = (0, import_booleans6.intersect)(this.boardClipGeom, padGeom);
31135
31351
  }
31136
31352
  padGeom = (0, import_colors7.colorize)(colors.copper, padGeom);
31137
31353
  this.padGeoms.push(padGeom);
@@ -31147,14 +31363,14 @@ var BoardGeomBuilder = class {
31147
31363
  if (currentSegmentPoints.length >= 2 && currentLayer) {
31148
31364
  const layerSign = currentLayer === "bottom" ? -1 : 1;
31149
31365
  const zCenter = layerSign * this.ctx.pcbThickness / 2 + layerSign * BOARD_SURFACE_OFFSET.traces;
31150
- const linePath = (0, import_primitives9.line)(currentSegmentPoints);
31366
+ const linePath = (0, import_primitives10.line)(currentSegmentPoints);
31151
31367
  const expandedPath = (0, import_expansions4.expand)(
31152
31368
  { delta: currentWidth / 2, corners: "round" },
31153
31369
  linePath
31154
31370
  );
31155
- let traceGeom = (0, import_transforms7.translate)(
31371
+ let traceGeom = (0, import_transforms8.translate)(
31156
31372
  [0, 0, zCenter - M / 2],
31157
- (0, import_extrusions7.extrudeLinear)({ height: M }, expandedPath)
31373
+ (0, import_extrusions8.extrudeLinear)({ height: M }, expandedPath)
31158
31374
  );
31159
31375
  const startPointCoords = currentSegmentPoints[0];
31160
31376
  const endPointCoords = currentSegmentPoints[currentSegmentPoints.length - 1];
@@ -31163,25 +31379,25 @@ var BoardGeomBuilder = class {
31163
31379
  startPointCoords[1]
31164
31380
  );
31165
31381
  if (startHole) {
31166
- const cuttingCylinder = (0, import_primitives9.cylinder)({
31382
+ const cuttingCylinder = (0, import_primitives10.cylinder)({
31167
31383
  center: [startPointCoords[0], startPointCoords[1], zCenter],
31168
31384
  radius: startHole.diameter / 2 + M,
31169
31385
  height: M
31170
31386
  });
31171
- traceGeom = (0, import_booleans5.subtract)(traceGeom, cuttingCylinder);
31387
+ traceGeom = (0, import_booleans6.subtract)(traceGeom, cuttingCylinder);
31172
31388
  }
31173
31389
  const endHole = this.getHoleToCut(endPointCoords[0], endPointCoords[1]);
31174
31390
  if (endHole) {
31175
- const cuttingCylinder = (0, import_primitives9.cylinder)({
31391
+ const cuttingCylinder = (0, import_primitives10.cylinder)({
31176
31392
  center: [endPointCoords[0], endPointCoords[1], zCenter],
31177
31393
  radius: endHole.diameter / 2 + M,
31178
31394
  height: M
31179
31395
  });
31180
- traceGeom = (0, import_booleans5.subtract)(traceGeom, cuttingCylinder);
31396
+ traceGeom = (0, import_booleans6.subtract)(traceGeom, cuttingCylinder);
31181
31397
  }
31182
31398
  const tracesMaterialColor = tracesMaterialColors[this.board.material] ?? colors.fr4GreenSolderWithMask;
31183
31399
  if (this.boardClipGeom) {
31184
- traceGeom = (0, import_booleans5.intersect)(this.boardClipGeom, traceGeom);
31400
+ traceGeom = (0, import_booleans6.intersect)(this.boardClipGeom, traceGeom);
31185
31401
  }
31186
31402
  traceGeom = (0, import_colors7.colorize)(tracesMaterialColor, traceGeom);
31187
31403
  this.traceGeoms.push(traceGeom);
@@ -31250,7 +31466,7 @@ var BoardGeomBuilder = class {
31250
31466
  point2[0] + xOffset + st.anchor_position.x,
31251
31467
  point2[1] + yOffset + st.anchor_position.y
31252
31468
  ]);
31253
- const textPath = (0, import_primitives9.line)(alignedOutline);
31469
+ const textPath = (0, import_primitives10.line)(alignedOutline);
31254
31470
  const fontSize = st.font_size || 0.25;
31255
31471
  const expansionDelta = Math.min(
31256
31472
  Math.max(0.01, fontSize * 0.1),
@@ -31262,16 +31478,16 @@ var BoardGeomBuilder = class {
31262
31478
  );
31263
31479
  let textGeom;
31264
31480
  if (st.layer === "bottom") {
31265
- textGeom = (0, import_transforms7.translate)(
31481
+ textGeom = (0, import_transforms8.translate)(
31266
31482
  [0, 0, -this.ctx.pcbThickness / 2 - M],
31267
31483
  // Position above board
31268
- (0, import_extrusions7.extrudeLinear)({ height: 0.012 }, expandedPath)
31484
+ (0, import_extrusions8.extrudeLinear)({ height: 0.012 }, expandedPath)
31269
31485
  );
31270
31486
  } else {
31271
- textGeom = (0, import_transforms7.translate)(
31487
+ textGeom = (0, import_transforms8.translate)(
31272
31488
  [0, 0, this.ctx.pcbThickness / 2 + M],
31273
31489
  // Position above board
31274
- (0, import_extrusions7.extrudeLinear)({ height: 0.012 }, expandedPath)
31490
+ (0, import_extrusions8.extrudeLinear)({ height: 0.012 }, expandedPath)
31275
31491
  );
31276
31492
  }
31277
31493
  textGeom = (0, import_colors7.colorize)([1, 1, 1], textGeom);
@@ -32209,18 +32425,18 @@ function createSilkscreenTextureForLayer({
32209
32425
  let rotationDeg = textS.ccw_rotation ?? 0;
32210
32426
  if (textS.layer === "bottom") {
32211
32427
  transformMatrices.push(
32212
- translate3(textCenterX, textCenterY),
32428
+ translate4(textCenterX, textCenterY),
32213
32429
  { a: -1, b: 0, c: 0, d: 1, e: 0, f: 0 },
32214
- translate3(-textCenterX, -textCenterY)
32430
+ translate4(-textCenterX, -textCenterY)
32215
32431
  );
32216
32432
  rotationDeg = -rotationDeg;
32217
32433
  }
32218
32434
  if (rotationDeg) {
32219
32435
  const rad = rotationDeg * Math.PI / 180;
32220
32436
  transformMatrices.push(
32221
- translate3(textCenterX, textCenterY),
32437
+ translate4(textCenterX, textCenterY),
32222
32438
  rotate(rad),
32223
- translate3(-textCenterX, -textCenterY)
32439
+ translate4(-textCenterX, -textCenterY)
32224
32440
  );
32225
32441
  }
32226
32442
  const finalTransformMatrix = transformMatrices.length > 0 ? compose(...transformMatrices) : void 0;
@@ -32429,9 +32645,29 @@ function processNonPlatedHolesForManifold(Manifold, circuitJson, pcbThickness, m
32429
32645
  // src/utils/manifold/process-plated-holes.ts
32430
32646
  import { su as su10 } from "@tscircuit/circuit-json-util";
32431
32647
  import * as THREE21 from "three";
32648
+ var arePointsClockwise3 = (points) => {
32649
+ let area = 0;
32650
+ for (let i = 0; i < points.length; i++) {
32651
+ const j = (i + 1) % points.length;
32652
+ if (points[i] && points[j]) {
32653
+ area += points[i][0] * points[j][1];
32654
+ area -= points[j][0] * points[i][1];
32655
+ }
32656
+ }
32657
+ const signedArea = area / 2;
32658
+ return signedArea <= 0;
32659
+ };
32660
+ var createEllipsePoints = (width10, height10, segments) => {
32661
+ const points = [];
32662
+ for (let i = 0; i < segments; i++) {
32663
+ const theta = 2 * Math.PI * i / segments;
32664
+ points.push([width10 / 2 * Math.cos(theta), height10 / 2 * Math.sin(theta)]);
32665
+ }
32666
+ return points;
32667
+ };
32432
32668
  var COPPER_COLOR = new THREE21.Color(...colors.copper);
32433
32669
  var PLATED_HOLE_LIP_HEIGHT = 0.05;
32434
- function processPlatedHolesForManifold(Manifold, circuitJson, pcbThickness, manifoldInstancesForCleanup, boardClipVolume) {
32670
+ function processPlatedHolesForManifold(Manifold, CrossSection, circuitJson, pcbThickness, manifoldInstancesForCleanup, boardClipVolume) {
32435
32671
  const platedHoleBoardDrills = [];
32436
32672
  const pcbPlatedHoles = su10(circuitJson).pcb_plated_hole.list();
32437
32673
  const platedHoleCopperGeoms = [];
@@ -32447,6 +32683,86 @@ function processPlatedHolesForManifold(Manifold, circuitJson, pcbThickness, mani
32447
32683
  manifoldInstancesForCleanup.push(pillOp);
32448
32684
  return pillOp;
32449
32685
  };
32686
+ const createPolygonPadOp = ({
32687
+ padOutline,
32688
+ thickness
32689
+ }) => {
32690
+ if (!Array.isArray(padOutline) || padOutline.length < 3) return null;
32691
+ let points = padOutline.map((point2) => [
32692
+ point2.x,
32693
+ point2.y
32694
+ ]);
32695
+ if (arePointsClockwise3(points)) {
32696
+ points = points.reverse();
32697
+ }
32698
+ const crossSection = CrossSection.ofPolygons([points]);
32699
+ manifoldInstancesForCleanup.push(crossSection);
32700
+ const padOp = Manifold.extrude(crossSection, thickness, 0, 0, [1, 1], true);
32701
+ manifoldInstancesForCleanup.push(padOp);
32702
+ return padOp;
32703
+ };
32704
+ const createHoleOpForPolygonPad = ({
32705
+ ph,
32706
+ depth,
32707
+ sizeDelta = 0
32708
+ }) => {
32709
+ const shape = ph.shape;
32710
+ if (shape !== "hole_with_polygon_pad") {
32711
+ return null;
32712
+ }
32713
+ const padOutline = ph.pad_outline;
32714
+ if (!Array.isArray(padOutline) || padOutline.length < 3) {
32715
+ return null;
32716
+ }
32717
+ const holeShape = ph.hole_shape || "circle";
32718
+ const holeOffsetX = ph.hole_offset_x || 0;
32719
+ const holeOffsetY = ph.hole_offset_y || 0;
32720
+ let holeOp = null;
32721
+ if (holeShape === "circle") {
32722
+ const diameter = Math.max((ph.hole_diameter ?? 0) + sizeDelta, M);
32723
+ const radius = Math.max(diameter / 2, M / 2);
32724
+ holeOp = Manifold.cylinder(
32725
+ depth,
32726
+ radius,
32727
+ radius,
32728
+ SMOOTH_CIRCLE_SEGMENTS,
32729
+ true
32730
+ );
32731
+ manifoldInstancesForCleanup.push(holeOp);
32732
+ } else {
32733
+ const baseWidth = ph.hole_width ?? ph.hole_diameter;
32734
+ const baseHeight = ph.hole_height ?? ph.hole_diameter;
32735
+ if (!baseWidth || !baseHeight) return null;
32736
+ const width10 = Math.max(baseWidth + sizeDelta, M);
32737
+ const height10 = Math.max(baseHeight + sizeDelta, M);
32738
+ if (holeShape === "oval") {
32739
+ let points = createEllipsePoints(width10, height10, SMOOTH_CIRCLE_SEGMENTS);
32740
+ if (arePointsClockwise3(points)) {
32741
+ points = points.reverse();
32742
+ }
32743
+ const crossSection = CrossSection.ofPolygons([points]);
32744
+ manifoldInstancesForCleanup.push(crossSection);
32745
+ holeOp = Manifold.extrude(crossSection, depth, 0, 0, [1, 1], true);
32746
+ manifoldInstancesForCleanup.push(holeOp);
32747
+ } else if (holeShape === "pill" || holeShape === "rotated_pill") {
32748
+ holeOp = createRoundedRectPrism({
32749
+ Manifold,
32750
+ width: width10,
32751
+ height: height10,
32752
+ thickness: depth,
32753
+ borderRadius: Math.min(width10, height10) / 2
32754
+ });
32755
+ manifoldInstancesForCleanup.push(holeOp);
32756
+ }
32757
+ }
32758
+ if (!holeOp) return null;
32759
+ if (holeOffsetX || holeOffsetY) {
32760
+ const translated = holeOp.translate([holeOffsetX, holeOffsetY, 0]);
32761
+ manifoldInstancesForCleanup.push(translated);
32762
+ holeOp = translated;
32763
+ }
32764
+ return holeOp;
32765
+ };
32450
32766
  pcbPlatedHoles.forEach((ph, index2) => {
32451
32767
  if (ph.shape === "circle") {
32452
32768
  const translatedDrill = createPlatedHoleDrill({
@@ -32646,6 +32962,83 @@ function processPlatedHolesForManifold(Manifold, circuitJson, pcbThickness, mani
32646
32962
  geometry: threeGeom,
32647
32963
  color: COPPER_COLOR
32648
32964
  });
32965
+ } else if (ph.shape === "hole_with_polygon_pad") {
32966
+ const padOutline = ph.pad_outline;
32967
+ if (!Array.isArray(padOutline) || padOutline.length < 3) {
32968
+ return;
32969
+ }
32970
+ const boardHoleOp = createHoleOpForPolygonPad({
32971
+ ph,
32972
+ depth: pcbThickness * 1.2,
32973
+ sizeDelta: 2 * M
32974
+ });
32975
+ if (!boardHoleOp) return;
32976
+ const translatedBoardHole = boardHoleOp.translate([ph.x, ph.y, 0]);
32977
+ manifoldInstancesForCleanup.push(translatedBoardHole);
32978
+ platedHoleBoardDrills.push(translatedBoardHole);
32979
+ const padThickness = DEFAULT_SMT_PAD_THICKNESS;
32980
+ const fillThickness = Math.max(
32981
+ pcbThickness - 2 * padThickness - 2 * BOARD_SURFACE_OFFSET.copper + 0.1,
32982
+ M
32983
+ );
32984
+ const mainFill = createPolygonPadOp({
32985
+ padOutline,
32986
+ thickness: fillThickness
32987
+ });
32988
+ const topPad = createPolygonPadOp({ padOutline, thickness: padThickness });
32989
+ const bottomPad = createPolygonPadOp({
32990
+ padOutline,
32991
+ thickness: padThickness
32992
+ });
32993
+ if (!mainFill || !topPad || !bottomPad) return;
32994
+ const topTranslated = topPad.translate([
32995
+ 0,
32996
+ 0,
32997
+ pcbThickness / 2 / 2 + BOARD_SURFACE_OFFSET.copper
32998
+ ]);
32999
+ const bottomTranslated = bottomPad.translate([
33000
+ 0,
33001
+ 0,
33002
+ -pcbThickness / 2 / 2 - BOARD_SURFACE_OFFSET.copper
33003
+ ]);
33004
+ manifoldInstancesForCleanup.push(topTranslated, bottomTranslated);
33005
+ const barrelOp = createHoleOpForPolygonPad({
33006
+ ph,
33007
+ depth: pcbThickness * 1.02
33008
+ });
33009
+ if (!barrelOp) return;
33010
+ const holeCutOp = createHoleOpForPolygonPad({
33011
+ ph,
33012
+ depth: pcbThickness * 1.2,
33013
+ sizeDelta: -2 * M
33014
+ }) || barrelOp;
33015
+ const copperUnion = Manifold.union([
33016
+ mainFill,
33017
+ topTranslated,
33018
+ bottomTranslated,
33019
+ barrelOp
33020
+ ]);
33021
+ manifoldInstancesForCleanup.push(copperUnion);
33022
+ const finalCopper = copperUnion.subtract(holeCutOp);
33023
+ manifoldInstancesForCleanup.push(finalCopper);
33024
+ const translatedCopper = finalCopper.translate([ph.x, ph.y, 0]);
33025
+ manifoldInstancesForCleanup.push(translatedCopper);
33026
+ let finalCopperOp = translatedCopper;
33027
+ if (boardClipVolume) {
33028
+ const clipped = Manifold.intersection([
33029
+ translatedCopper,
33030
+ boardClipVolume
33031
+ ]);
33032
+ manifoldInstancesForCleanup.push(clipped);
33033
+ finalCopperOp = clipped;
33034
+ }
33035
+ platedHoleCopperOpsForSubtract.push(finalCopperOp);
33036
+ const threeGeom = manifoldMeshToThreeGeometry(finalCopperOp.getMesh());
33037
+ platedHoleCopperGeoms.push({
33038
+ key: `ph-${ph.pcb_plated_hole_id || index2}`,
33039
+ geometry: threeGeom,
33040
+ color: COPPER_COLOR
33041
+ });
32649
33042
  } else if (ph.shape === "circular_hole_with_rect_pad") {
32650
33043
  const holeOffsetX = ph.hole_offset_x || 0;
32651
33044
  const holeOffsetY = ph.hole_offset_y || 0;
@@ -32872,7 +33265,7 @@ function processSmtPadsForManifold(Manifold, circuitJson, pcbThickness, manifold
32872
33265
  }
32873
33266
 
32874
33267
  // src/utils/manifold/create-manifold-board.ts
32875
- var arePointsClockwise3 = (points) => {
33268
+ var arePointsClockwise4 = (points) => {
32876
33269
  let area = 0;
32877
33270
  for (let i = 0; i < points.length; i++) {
32878
33271
  const j = (i + 1) % points.length;
@@ -32892,7 +33285,7 @@ function createManifoldBoard(Manifold, CrossSection, boardData, pcbThickness, ma
32892
33285
  p.x,
32893
33286
  p.y
32894
33287
  ]);
32895
- if (arePointsClockwise3(outlineVec2)) {
33288
+ if (arePointsClockwise4(outlineVec2)) {
32896
33289
  outlineVec2 = outlineVec2.reverse();
32897
33290
  }
32898
33291
  const crossSection = CrossSection.ofPolygons([outlineVec2]);
@@ -32931,7 +33324,7 @@ function createManifoldBoard(Manifold, CrossSection, boardData, pcbThickness, ma
32931
33324
 
32932
33325
  // src/utils/manifold/process-copper-pours.ts
32933
33326
  import * as THREE24 from "three";
32934
- var arePointsClockwise4 = (points) => {
33327
+ var arePointsClockwise5 = (points) => {
32935
33328
  let area = 0;
32936
33329
  for (let i = 0; i < points.length; i++) {
32937
33330
  const j = (i + 1) % points.length;
@@ -33022,7 +33415,7 @@ function processCopperPoursForManifold(Manifold, CrossSection, circuitJson, pcbT
33022
33415
  p.x,
33023
33416
  p.y
33024
33417
  ]);
33025
- if (arePointsClockwise4(pointsVec2)) {
33418
+ if (arePointsClockwise5(pointsVec2)) {
33026
33419
  pointsVec2 = pointsVec2.reverse();
33027
33420
  }
33028
33421
  const crossSection = CrossSection.ofPolygons([pointsVec2]);
@@ -33047,14 +33440,14 @@ function processCopperPoursForManifold(Manifold, CrossSection, circuitJson, pcbT
33047
33440
  brepShape.outer_ring,
33048
33441
  SMOOTH_CIRCLE_SEGMENTS
33049
33442
  );
33050
- if (arePointsClockwise4(outerRingPoints)) {
33443
+ if (arePointsClockwise5(outerRingPoints)) {
33051
33444
  outerRingPoints = outerRingPoints.reverse();
33052
33445
  }
33053
33446
  const polygons = [outerRingPoints];
33054
33447
  if (brepShape.inner_rings) {
33055
33448
  const innerRingsPoints = brepShape.inner_rings.map((ring2) => {
33056
33449
  let points = ringToPoints2(ring2, SMOOTH_CIRCLE_SEGMENTS);
33057
- if (!arePointsClockwise4(points)) {
33450
+ if (!arePointsClockwise5(points)) {
33058
33451
  points = points.reverse();
33059
33452
  }
33060
33453
  return points;
@@ -33104,7 +33497,7 @@ function processCopperPoursForManifold(Manifold, CrossSection, circuitJson, pcbT
33104
33497
 
33105
33498
  // src/utils/manifold/process-cutouts.ts
33106
33499
  import { su as su13 } from "@tscircuit/circuit-json-util";
33107
- var arePointsClockwise5 = (points) => {
33500
+ var arePointsClockwise6 = (points) => {
33108
33501
  let area = 0;
33109
33502
  for (let i = 0; i < points.length; i++) {
33110
33503
  const j = (i + 1) % points.length;
@@ -33123,15 +33516,25 @@ function processCutoutsForManifold(Manifold, CrossSection, circuitJson, pcbThick
33123
33516
  let cutoutOp;
33124
33517
  const cutoutHeight = pcbThickness * 1.5;
33125
33518
  switch (cutout.shape) {
33126
- case "rect":
33127
- cutoutOp = Manifold.cube(
33128
- [cutout.width, cutout.height, cutoutHeight],
33129
- true
33130
- // centered
33131
- );
33519
+ case "rect": {
33520
+ const rectCornerRadius = extractRectBorderRadius(cutout);
33521
+ if (typeof rectCornerRadius === "number" && rectCornerRadius > 0) {
33522
+ cutoutOp = createRoundedRectPrism({
33523
+ Manifold,
33524
+ width: cutout.width,
33525
+ height: cutout.height,
33526
+ thickness: cutoutHeight,
33527
+ borderRadius: rectCornerRadius
33528
+ });
33529
+ } else {
33530
+ cutoutOp = Manifold.cube(
33531
+ [cutout.width, cutout.height, cutoutHeight],
33532
+ true
33533
+ // centered
33534
+ );
33535
+ }
33132
33536
  manifoldInstancesForCleanup.push(cutoutOp);
33133
33537
  if (cutout.rotation) {
33134
- const rotationRadians = cutout.rotation * Math.PI / 180;
33135
33538
  const rotatedOp = cutoutOp.rotate([0, 0, cutout.rotation]);
33136
33539
  manifoldInstancesForCleanup.push(rotatedOp);
33137
33540
  cutoutOp = rotatedOp;
@@ -33144,6 +33547,7 @@ function processCutoutsForManifold(Manifold, CrossSection, circuitJson, pcbThick
33144
33547
  ]);
33145
33548
  manifoldInstancesForCleanup.push(cutoutOp);
33146
33549
  break;
33550
+ }
33147
33551
  case "circle":
33148
33552
  cutoutOp = Manifold.cylinder(
33149
33553
  cutoutHeight,
@@ -33169,7 +33573,7 @@ function processCutoutsForManifold(Manifold, CrossSection, circuitJson, pcbThick
33169
33573
  p.x,
33170
33574
  p.y
33171
33575
  ]);
33172
- if (arePointsClockwise5(pointsVec2)) {
33576
+ if (arePointsClockwise6(pointsVec2)) {
33173
33577
  pointsVec2 = pointsVec2.reverse();
33174
33578
  }
33175
33579
  const crossSection = CrossSection.ofPolygons([pointsVec2]);
@@ -33333,6 +33737,7 @@ var useManifoldBoardBuilder = (manifoldJSModule, circuitJson) => {
33333
33737
  platedHoleSubtractOp
33334
33738
  } = processPlatedHolesForManifold(
33335
33739
  Manifold,
33740
+ CrossSection,
33336
33741
  circuitJson,
33337
33742
  currentPcbThickness,
33338
33743
  manifoldInstancesForCleanup.current,