@tscircuit/3d-viewer 0.0.404 → 0.0.405

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 +226 -99
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -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 translate5 = (out, matrix, offsets) => {
1715
+ var translate6 = (out, matrix, offsets) => {
1716
1716
  const x = offsets[0];
1717
1717
  const y = offsets[1];
1718
1718
  const z105 = offsets[2];
@@ -1765,7 +1765,7 @@ var require_translate = __commonJS({
1765
1765
  }
1766
1766
  return out;
1767
1767
  };
1768
- module.exports = translate5;
1768
+ module.exports = translate6;
1769
1769
  }
1770
1770
  });
1771
1771
 
@@ -8397,7 +8397,7 @@ var require_roundedRectangle = __commonJS({
8397
8397
  var geom2 = require_geom2();
8398
8398
  var { isGTE, isNumberArray } = require_commonChecks();
8399
8399
  var rectangle = require_rectangle();
8400
- var roundedRectangle = (options) => {
8400
+ var roundedRectangle3 = (options) => {
8401
8401
  const defaults = {
8402
8402
  center: [0, 0],
8403
8403
  size: [2, 2],
@@ -8437,7 +8437,7 @@ var require_roundedRectangle = __commonJS({
8437
8437
  }
8438
8438
  return geom2.fromPoints(corner0Points.concat(corner1Points, corner2Points, corner3Points));
8439
8439
  };
8440
- module.exports = roundedRectangle;
8440
+ module.exports = roundedRectangle3;
8441
8441
  }
8442
8442
  });
8443
8443
 
@@ -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 translate5 = (offset, ...objects) => {
9749
+ var translate6 = (offset, ...objects) => {
9750
9750
  if (!Array.isArray(offset)) 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 = (offset, ...objects) => translate5([offset, 0, 0], objects);
9765
- var translateY = (offset, ...objects) => translate5([0, offset, 0], objects);
9766
- var translateZ = (offset, ...objects) => translate5([0, 0, offset], objects);
9764
+ var translateX = (offset, ...objects) => translate6([offset, 0, 0], objects);
9765
+ var translateY = (offset, ...objects) => translate6([0, offset, 0], objects);
9766
+ var translateZ = (offset, ...objects) => translate6([0, 0, offset], objects);
9767
9767
  module.exports = {
9768
- translate: translate5,
9768
+ translate: translate6,
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: translate5 } = require_translate2();
9783
+ var { translate: translate6 } = require_translate2();
9784
9784
  var circle = 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 = translate5([outerRadius, 0], innerCircle);
9808
+ innerCircle = translate6([outerRadius, 0], innerCircle);
9809
9809
  const extrudeOptions = {
9810
9810
  startAngle,
9811
9811
  angle: outerRotation,
@@ -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 extrudeLinear4 = (options, ...objects) => {
12454
+ var extrudeLinear5 = (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 = extrudeLinear4;
12472
+ module.exports = extrudeLinear5;
12473
12473
  }
12474
12474
  });
12475
12475
 
@@ -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: translate5 } = require_translate2();
13531
+ var { translate: translate6 } = 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 translate5(translation, geometry);
13568
+ return translate6(translation, geometry);
13569
13569
  };
13570
13570
  var align = (options, ...geometries2) => {
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: translate5 } = require_translate2();
13605
+ var { translate: translate6 } = 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]) offset[0] = relativeTo[0] - (bounds[0][0] + (bounds[1][0] - bounds[0][0]) / 2);
13615
13615
  if (axes[1]) offset[1] = relativeTo[1] - (bounds[0][1] + (bounds[1][1] - bounds[0][1]) / 2);
13616
13616
  if (axes[2]) offset[2] = relativeTo[2] - (bounds[0][2] + (bounds[1][2] - bounds[0][2]) / 2);
13617
- return translate5(offset, object);
13617
+ return translate6(offset, object);
13618
13618
  };
13619
13619
  var center = (options, ...objects) => {
13620
13620
  const defaults = {
@@ -23720,7 +23720,7 @@ var m2host = (raw_params) => {
23720
23720
  }
23721
23721
  const centerX = (minX + maxX) / 2;
23722
23722
  const centerY = (minY + maxY) / 2;
23723
- const translate5 = (el) => {
23723
+ const translate6 = (el) => {
23724
23724
  if (typeof el.x === "number") el.x -= centerX;
23725
23725
  if (typeof el.y === "number") el.y -= centerY;
23726
23726
  if (el.center) {
@@ -23734,9 +23734,9 @@ var m2host = (raw_params) => {
23734
23734
  }));
23735
23735
  }
23736
23736
  };
23737
- for (const pad2 of pads) translate5(pad2);
23738
- translate5(cutout);
23739
- translate5(pin1Marker);
23737
+ for (const pad2 of pads) translate6(pad2);
23738
+ translate6(cutout);
23739
+ translate6(pin1Marker);
23740
23740
  return {
23741
23741
  circuitJson: [
23742
23742
  ...pads,
@@ -26033,7 +26033,7 @@ import * as THREE11 from "three";
26033
26033
  // package.json
26034
26034
  var package_default = {
26035
26035
  name: "@tscircuit/3d-viewer",
26036
- version: "0.0.403",
26036
+ version: "0.0.404",
26037
26037
  main: "./dist/index.js",
26038
26038
  module: "./dist/index.js",
26039
26039
  type: "module",
@@ -26883,7 +26883,7 @@ var createBoardGeomFromCircuitJson = (circuitJson, opts = {}) => {
26883
26883
  };
26884
26884
 
26885
26885
  // src/BoardGeomBuilder.ts
26886
- var import_transforms3 = __toESM(require_transforms(), 1);
26886
+ var import_transforms4 = __toESM(require_transforms(), 1);
26887
26887
  var import_primitives6 = __toESM(require_primitives(), 1);
26888
26888
  var import_colors4 = __toESM(require_colors(), 1);
26889
26889
  var import_booleans3 = __toESM(require_booleans(), 1);
@@ -26893,7 +26893,49 @@ import { su as su3 } from "@tscircuit/circuit-json-util";
26893
26893
  var import_primitives3 = __toESM(require_primitives(), 1);
26894
26894
  var import_colors2 = __toESM(require_colors(), 1);
26895
26895
  var import_booleans = __toESM(require_booleans(), 1);
26896
+ var import_extrusions2 = __toESM(require_extrusions(), 1);
26897
+ var import_transforms2 = __toESM(require_transforms(), 1);
26898
+
26899
+ // src/utils/rect-border-radius.ts
26900
+ function clampRectBorderRadius(width10, height10, rawRadius) {
26901
+ if (typeof rawRadius !== "number" || !Number.isFinite(rawRadius)) {
26902
+ return 0;
26903
+ }
26904
+ if (rawRadius <= 0) {
26905
+ return 0;
26906
+ }
26907
+ const halfWidth = width10 / 2;
26908
+ const halfHeight = height10 / 2;
26909
+ return Math.max(0, Math.min(rawRadius, halfWidth, halfHeight));
26910
+ }
26911
+ function extractRectBorderRadius(source) {
26912
+ if (!source || typeof source !== "object") return void 0;
26913
+ return source.rect_pad_border_radius ?? source.rectPadBorderRadius ?? source.rect_border_radius ?? source.rectBorderRadius ?? void 0;
26914
+ }
26915
+
26916
+ // src/geoms/plated-hole.ts
26896
26917
  var platedHoleLipHeight = 0.05;
26918
+ var RECT_PAD_SEGMENTS = 64;
26919
+ var createRectPadGeom = ({
26920
+ width: width10,
26921
+ height: height10,
26922
+ thickness,
26923
+ center,
26924
+ borderRadius
26925
+ }) => {
26926
+ const clampedRadius = clampRectBorderRadius(width10, height10, borderRadius);
26927
+ if (clampedRadius <= 0) {
26928
+ return (0, import_primitives3.cuboid)({ center, size: [width10, height10, thickness] });
26929
+ }
26930
+ const rect2d = (0, import_primitives3.roundedRectangle)({
26931
+ size: [width10, height10],
26932
+ roundRadius: clampedRadius,
26933
+ segments: RECT_PAD_SEGMENTS
26934
+ });
26935
+ const extruded = (0, import_extrusions2.extrudeLinear)({ height: thickness }, rect2d);
26936
+ const offsetZ = center[2] - thickness / 2;
26937
+ return (0, import_transforms2.translate)([center[0], center[1], offsetZ], extruded);
26938
+ };
26897
26939
  var platedHole = (plated_hole, ctx) => {
26898
26940
  if (!plated_hole.shape) plated_hole.shape = "circle";
26899
26941
  if (plated_hole.shape === "circle") {
@@ -26936,19 +26978,26 @@ var platedHole = (plated_hole, ctx) => {
26936
26978
  if (plated_hole.shape === "circular_hole_with_rect_pad") {
26937
26979
  const padWidth = plated_hole.rect_pad_width || plated_hole.hole_diameter;
26938
26980
  const padHeight = plated_hole.rect_pad_height || plated_hole.hole_diameter;
26981
+ const rectBorderRadius = extractRectBorderRadius(plated_hole);
26939
26982
  return (0, import_colors2.colorize)(
26940
26983
  colors.copper,
26941
26984
  (0, import_booleans.subtract)(
26942
26985
  (0, import_booleans.union)(
26943
26986
  // Top rectangular pad
26944
- (0, import_primitives3.cuboid)({
26987
+ createRectPadGeom({
26988
+ width: padWidth,
26989
+ height: padHeight,
26990
+ thickness: platedHoleLipHeight,
26945
26991
  center: [plated_hole.x, plated_hole.y, 1.2 / 2],
26946
- size: [padWidth, padHeight, platedHoleLipHeight]
26992
+ borderRadius: rectBorderRadius
26947
26993
  }),
26948
26994
  // Bottom rectangular pad
26949
- (0, import_primitives3.cuboid)({
26995
+ createRectPadGeom({
26996
+ width: padWidth,
26997
+ height: padHeight,
26998
+ thickness: platedHoleLipHeight,
26950
26999
  center: [plated_hole.x, plated_hole.y, -1.2 / 2],
26951
- size: [padWidth, padHeight, platedHoleLipHeight]
27000
+ borderRadius: rectBorderRadius
26952
27001
  }),
26953
27002
  // Plated barrel around hole
26954
27003
  (0, import_primitives3.cylinder)({
@@ -27055,6 +27104,7 @@ var platedHole = (plated_hole, ctx) => {
27055
27104
  const rectLength = Math.abs(holeWidth - holeHeight);
27056
27105
  const padWidth = plated_hole.rect_pad_width || holeWidth + 0.2;
27057
27106
  const padHeight = plated_hole.rect_pad_height || holeHeight + 0.2;
27107
+ const rectBorderRadius = extractRectBorderRadius(plated_hole);
27058
27108
  const mainRect = (0, import_primitives3.cuboid)({
27059
27109
  center: [plated_hole.x, plated_hole.y, 0],
27060
27110
  size: shouldRotate ? [holeHeight, rectLength, 1.2] : [rectLength, holeHeight, 1.2]
@@ -27069,13 +27119,19 @@ var platedHole = (plated_hole, ctx) => {
27069
27119
  radius: holeRadius,
27070
27120
  height: 1.2
27071
27121
  });
27072
- const topPad = (0, import_primitives3.cuboid)({
27122
+ const topPad = createRectPadGeom({
27123
+ width: padWidth,
27124
+ height: padHeight,
27125
+ thickness: platedHoleLipHeight,
27073
27126
  center: [plated_hole.x, plated_hole.y, 1.2 / 2],
27074
- size: [padWidth, padHeight, platedHoleLipHeight]
27127
+ borderRadius: rectBorderRadius
27075
27128
  });
27076
- const bottomPad = (0, import_primitives3.cuboid)({
27129
+ const bottomPad = createRectPadGeom({
27130
+ width: padWidth,
27131
+ height: padHeight,
27132
+ thickness: platedHoleLipHeight,
27077
27133
  center: [plated_hole.x, plated_hole.y, -1.2 / 2],
27078
- size: [padWidth, padHeight, platedHoleLipHeight]
27134
+ borderRadius: rectBorderRadius
27079
27135
  });
27080
27136
  const holeCut = (0, import_booleans.union)(
27081
27137
  (0, import_primitives3.cuboid)({
@@ -27103,7 +27159,7 @@ var platedHole = (plated_hole, ctx) => {
27103
27159
  };
27104
27160
 
27105
27161
  // src/BoardGeomBuilder.ts
27106
- var import_extrusions3 = __toESM(require_extrusions(), 1);
27162
+ var import_extrusions4 = __toESM(require_extrusions(), 1);
27107
27163
  var import_expansions2 = __toESM(require_expansions(), 1);
27108
27164
 
27109
27165
  // src/geoms/create-geoms-for-silkscreen-text.ts
@@ -27126,7 +27182,7 @@ function isUndefined(val) {
27126
27182
  }
27127
27183
 
27128
27184
  // node_modules/transformation-matrix/src/translate.js
27129
- function translate2(tx, ty = 0) {
27185
+ function translate3(tx, ty = 0) {
27130
27186
  return {
27131
27187
  a: 1,
27132
27188
  c: 0,
@@ -27185,9 +27241,9 @@ function rotate(angle, cx, cy) {
27185
27241
  return rotationMatrix;
27186
27242
  }
27187
27243
  return transform([
27188
- translate2(cx, cy),
27244
+ translate3(cx, cy),
27189
27245
  rotationMatrix,
27190
- translate2(-cx, -cy)
27246
+ translate3(-cx, -cy)
27191
27247
  ]);
27192
27248
  }
27193
27249
 
@@ -27375,19 +27431,19 @@ function createSilkscreenTextGeoms(silkscreenText) {
27375
27431
  const transforms2 = [];
27376
27432
  if (silkscreenText.layer === "bottom") {
27377
27433
  transforms2.push(
27378
- translate2(centerX, centerY),
27434
+ translate3(centerX, centerY),
27379
27435
  { a: -1, b: 0, c: 0, d: 1, e: 0, f: 0 },
27380
27436
  // horizontal flip matrix
27381
- translate2(-centerX, -centerY)
27437
+ translate3(-centerX, -centerY)
27382
27438
  );
27383
27439
  rotationDegrees = -rotationDegrees;
27384
27440
  }
27385
27441
  if (rotationDegrees) {
27386
27442
  const rad = rotationDegrees * Math.PI / 180;
27387
27443
  transforms2.push(
27388
- translate2(centerX, centerY),
27444
+ translate3(centerX, centerY),
27389
27445
  rotate(rad),
27390
- translate2(-centerX, -centerY)
27446
+ translate3(-centerX, -centerY)
27391
27447
  );
27392
27448
  }
27393
27449
  let transformedOutlines = textOutlines;
@@ -27410,8 +27466,8 @@ function createSilkscreenTextGeoms(silkscreenText) {
27410
27466
  // src/geoms/create-geoms-for-silkscreen-path.ts
27411
27467
  var import_primitives4 = __toESM(require_primitives(), 1);
27412
27468
  var import_expansions = __toESM(require_expansions(), 1);
27413
- var import_extrusions2 = __toESM(require_extrusions(), 1);
27414
- var import_transforms2 = __toESM(require_transforms(), 1);
27469
+ var import_extrusions3 = __toESM(require_extrusions(), 1);
27470
+ var import_transforms3 = __toESM(require_transforms(), 1);
27415
27471
  var import_colors3 = __toESM(require_colors(), 1);
27416
27472
  function createSilkscreenPathGeom(sp, ctx) {
27417
27473
  if (sp.route.length < 2) return void 0;
@@ -27424,9 +27480,9 @@ function createSilkscreenPathGeom(sp, ctx) {
27424
27480
  );
27425
27481
  const layerSign = sp.layer === "bottom" ? -1 : 1;
27426
27482
  const zPos = layerSign * ctx.pcbThickness / 2 + layerSign * M * 1.5;
27427
- let pathGeom = (0, import_transforms2.translate)(
27483
+ let pathGeom = (0, import_transforms3.translate)(
27428
27484
  [0, 0, zPos],
27429
- (0, import_extrusions2.extrudeLinear)({ height: 0.012 }, expandedPath)
27485
+ (0, import_extrusions3.extrudeLinear)({ height: 0.012 }, expandedPath)
27430
27486
  // Standard silkscreen thickness
27431
27487
  );
27432
27488
  pathGeom = (0, import_colors3.colorize)([1, 1, 1], pathGeom);
@@ -27519,6 +27575,20 @@ function createGeom2FromBRep(brep, arcSegments = 16) {
27519
27575
  }
27520
27576
 
27521
27577
  // src/BoardGeomBuilder.ts
27578
+ var PAD_ROUNDED_SEGMENTS = 64;
27579
+ var createCenteredRectPadGeom = (width10, height10, thickness, rectBorderRadius) => {
27580
+ const clampedRadius = clampRectBorderRadius(width10, height10, rectBorderRadius);
27581
+ if (clampedRadius <= 0) {
27582
+ return (0, import_primitives6.cuboid)({ center: [0, 0, 0], size: [width10, height10, thickness] });
27583
+ }
27584
+ const rect2d = (0, import_primitives6.roundedRectangle)({
27585
+ size: [width10, height10],
27586
+ roundRadius: clampedRadius,
27587
+ segments: PAD_ROUNDED_SEGMENTS
27588
+ });
27589
+ const extruded = (0, import_extrusions4.extrudeLinear)({ height: thickness }, rect2d);
27590
+ return (0, import_transforms4.translate)([0, 0, -thickness / 2], extruded);
27591
+ };
27522
27592
  var buildStateOrder = [
27523
27593
  "initializing",
27524
27594
  "processing_pads",
@@ -27715,7 +27785,7 @@ var BoardGeomBuilder = class {
27715
27785
  });
27716
27786
  if (cutout.rotation) {
27717
27787
  const rotationRadians = cutout.rotation * Math.PI / 180;
27718
- cutoutGeom = (0, import_transforms3.rotateZ)(rotationRadians, cutoutGeom);
27788
+ cutoutGeom = (0, import_transforms4.rotateZ)(rotationRadians, cutoutGeom);
27719
27789
  }
27720
27790
  break;
27721
27791
  case "circle":
@@ -27737,8 +27807,8 @@ var BoardGeomBuilder = class {
27737
27807
  pointsVec2 = pointsVec2.reverse();
27738
27808
  }
27739
27809
  const polygon2d = (0, import_primitives6.polygon)({ points: pointsVec2 });
27740
- cutoutGeom = (0, import_extrusions3.extrudeLinear)({ height: cutoutHeight }, polygon2d);
27741
- cutoutGeom = (0, import_transforms3.translate)([0, 0, -cutoutHeight / 2], cutoutGeom);
27810
+ cutoutGeom = (0, import_extrusions4.extrudeLinear)({ height: cutoutHeight }, polygon2d);
27811
+ cutoutGeom = (0, import_transforms4.translate)([0, 0, -cutoutHeight / 2], cutoutGeom);
27742
27812
  break;
27743
27813
  }
27744
27814
  if (cutoutGeom) {
@@ -27757,15 +27827,15 @@ var BoardGeomBuilder = class {
27757
27827
  });
27758
27828
  if ("rotation" in pour && pour.rotation) {
27759
27829
  const rotationRadians = pour.rotation * Math.PI / 180;
27760
- baseGeom = (0, import_transforms3.rotateZ)(rotationRadians, baseGeom);
27830
+ baseGeom = (0, import_transforms4.rotateZ)(rotationRadians, baseGeom);
27761
27831
  }
27762
- pourGeom = (0, import_transforms3.translate)([pour.center.x, pour.center.y, zPos], baseGeom);
27832
+ pourGeom = (0, import_transforms4.translate)([pour.center.x, pour.center.y, zPos], baseGeom);
27763
27833
  } else if (pour.shape === "brep") {
27764
27834
  const brepShape = pour.brep_shape;
27765
27835
  if (brepShape && brepShape.outer_ring) {
27766
27836
  const pourGeom2 = createGeom2FromBRep(brepShape);
27767
- pourGeom = (0, import_extrusions3.extrudeLinear)({ height: M }, pourGeom2);
27768
- pourGeom = (0, import_transforms3.translate)([0, 0, zPos], pourGeom);
27837
+ pourGeom = (0, import_extrusions4.extrudeLinear)({ height: M }, pourGeom2);
27838
+ pourGeom = (0, import_transforms4.translate)([0, 0, zPos], pourGeom);
27769
27839
  }
27770
27840
  } else if (pour.shape === "polygon") {
27771
27841
  let pointsVec2 = pour.points.map((p) => [p.x, p.y]);
@@ -27779,8 +27849,8 @@ var BoardGeomBuilder = class {
27779
27849
  pointsVec2 = pointsVec2.reverse();
27780
27850
  }
27781
27851
  const polygon2d = (0, import_primitives6.polygon)({ points: pointsVec2 });
27782
- pourGeom = (0, import_extrusions3.extrudeLinear)({ height: M }, polygon2d);
27783
- pourGeom = (0, import_transforms3.translate)([0, 0, zPos], pourGeom);
27852
+ pourGeom = (0, import_extrusions4.extrudeLinear)({ height: M }, polygon2d);
27853
+ pourGeom = (0, import_transforms4.translate)([0, 0, zPos], pourGeom);
27784
27854
  }
27785
27855
  if (pourGeom) {
27786
27856
  const coloredPourGeom = (0, import_colors4.colorize)(colors.copper, pourGeom);
@@ -27856,24 +27926,27 @@ var BoardGeomBuilder = class {
27856
27926
  processPad(pad2) {
27857
27927
  const layerSign = pad2.layer === "bottom" ? -1 : 1;
27858
27928
  const zPos = layerSign * this.ctx.pcbThickness / 2 + layerSign * M * 2;
27929
+ const rectBorderRadius = extractRectBorderRadius(pad2);
27859
27930
  if (pad2.shape === "rect") {
27860
- const padGeom = (0, import_colors4.colorize)(
27861
- colors.copper,
27862
- (0, import_primitives6.cuboid)({
27863
- center: [pad2.x, pad2.y, zPos],
27864
- size: [pad2.width, pad2.height, M]
27865
- })
27931
+ const basePadGeom = createCenteredRectPadGeom(
27932
+ pad2.width,
27933
+ pad2.height,
27934
+ M,
27935
+ rectBorderRadius
27866
27936
  );
27937
+ const positionedPadGeom = (0, import_transforms4.translate)([pad2.x, pad2.y, zPos], basePadGeom);
27938
+ const padGeom = (0, import_colors4.colorize)(colors.copper, positionedPadGeom);
27867
27939
  this.padGeoms.push(padGeom);
27868
27940
  } else if (pad2.shape === "rotated_rect") {
27869
- let basePadGeom = (0, import_primitives6.cuboid)({
27870
- // Create at origin for rotation, then translate
27871
- center: [0, 0, 0],
27872
- size: [pad2.width, pad2.height, M]
27873
- });
27941
+ let basePadGeom = createCenteredRectPadGeom(
27942
+ pad2.width,
27943
+ pad2.height,
27944
+ M,
27945
+ rectBorderRadius
27946
+ );
27874
27947
  const rotationRadians = pad2.ccw_rotation * Math.PI / 180;
27875
- basePadGeom = (0, import_transforms3.rotateZ)(rotationRadians, basePadGeom);
27876
- const positionedPadGeom = (0, import_transforms3.translate)([pad2.x, pad2.y, zPos], basePadGeom);
27948
+ basePadGeom = (0, import_transforms4.rotateZ)(rotationRadians, basePadGeom);
27949
+ const positionedPadGeom = (0, import_transforms4.translate)([pad2.x, pad2.y, zPos], basePadGeom);
27877
27950
  const padGeom = (0, import_colors4.colorize)(colors.copper, positionedPadGeom);
27878
27951
  this.padGeoms.push(padGeom);
27879
27952
  } else if (pad2.shape === "circle") {
@@ -27903,9 +27976,9 @@ var BoardGeomBuilder = class {
27903
27976
  { delta: currentWidth / 2, corners: "round" },
27904
27977
  linePath
27905
27978
  );
27906
- let traceGeom = (0, import_transforms3.translate)(
27979
+ let traceGeom = (0, import_transforms4.translate)(
27907
27980
  [0, 0, zPos],
27908
- (0, import_extrusions3.extrudeLinear)({ height: M }, expandedPath)
27981
+ (0, import_extrusions4.extrudeLinear)({ height: M }, expandedPath)
27909
27982
  );
27910
27983
  const startPointCoords = currentSegmentPoints[0];
27911
27984
  const endPointCoords = currentSegmentPoints[currentSegmentPoints.length - 1];
@@ -28010,16 +28083,16 @@ var BoardGeomBuilder = class {
28010
28083
  );
28011
28084
  let textGeom;
28012
28085
  if (st.layer === "bottom") {
28013
- textGeom = (0, import_transforms3.translate)(
28086
+ textGeom = (0, import_transforms4.translate)(
28014
28087
  [0, 0, -this.ctx.pcbThickness / 2 - M],
28015
28088
  // Position above board
28016
- (0, import_extrusions3.extrudeLinear)({ height: 0.012 }, expandedPath)
28089
+ (0, import_extrusions4.extrudeLinear)({ height: 0.012 }, expandedPath)
28017
28090
  );
28018
28091
  } else {
28019
- textGeom = (0, import_transforms3.translate)(
28092
+ textGeom = (0, import_transforms4.translate)(
28020
28093
  [0, 0, this.ctx.pcbThickness / 2 + M],
28021
28094
  // Position above board
28022
- (0, import_extrusions3.extrudeLinear)({ height: 0.012 }, expandedPath)
28095
+ (0, import_extrusions4.extrudeLinear)({ height: 0.012 }, expandedPath)
28023
28096
  );
28024
28097
  }
28025
28098
  textGeom = (0, import_colors4.colorize)([1, 1, 1], textGeom);
@@ -28616,18 +28689,18 @@ function createSilkscreenTextureForLayer({
28616
28689
  let rotationDeg = textS.ccw_rotation ?? 0;
28617
28690
  if (textS.layer === "bottom") {
28618
28691
  transformMatrices.push(
28619
- translate2(textCenterX, textCenterY),
28692
+ translate3(textCenterX, textCenterY),
28620
28693
  { a: -1, b: 0, c: 0, d: 1, e: 0, f: 0 },
28621
- translate2(-textCenterX, -textCenterY)
28694
+ translate3(-textCenterX, -textCenterY)
28622
28695
  );
28623
28696
  rotationDeg = -rotationDeg;
28624
28697
  }
28625
28698
  if (rotationDeg) {
28626
28699
  const rad = rotationDeg * Math.PI / 180;
28627
28700
  transformMatrices.push(
28628
- translate2(textCenterX, textCenterY),
28701
+ translate3(textCenterX, textCenterY),
28629
28702
  rotate(rad),
28630
- translate2(-textCenterX, -textCenterY)
28703
+ translate3(-textCenterX, -textCenterY)
28631
28704
  );
28632
28705
  }
28633
28706
  const finalTransformMatrix = transformMatrices.length > 0 ? compose(...transformMatrices) : void 0;
@@ -28725,6 +28798,69 @@ function processNonPlatedHolesForManifold(Manifold, circuitJson, pcbThickness, m
28725
28798
  // src/utils/manifold/process-plated-holes.ts
28726
28799
  import { su as su8 } from "@tscircuit/circuit-json-util";
28727
28800
  import * as THREE17 from "three";
28801
+
28802
+ // src/utils/pad-geoms.ts
28803
+ var RECT_PAD_SEGMENTS2 = 64;
28804
+ function createRoundedRectPrism({
28805
+ Manifold,
28806
+ width: width10,
28807
+ height: height10,
28808
+ thickness,
28809
+ borderRadius
28810
+ }) {
28811
+ const clampedRadius = clampRectBorderRadius(width10, height10, borderRadius);
28812
+ if (clampedRadius <= 0) {
28813
+ return Manifold.cube([width10, height10, thickness], true);
28814
+ }
28815
+ const shapes = [];
28816
+ const innerWidth = width10 - 2 * clampedRadius;
28817
+ const innerHeight = height10 - 2 * clampedRadius;
28818
+ if (innerWidth > 0) {
28819
+ shapes.push(Manifold.cube([innerWidth, height10, thickness], true));
28820
+ }
28821
+ if (innerHeight > 0) {
28822
+ shapes.push(Manifold.cube([width10, innerHeight, thickness], true));
28823
+ }
28824
+ const cornerOffsets = [
28825
+ [width10 / 2 - clampedRadius, height10 / 2 - clampedRadius],
28826
+ [-width10 / 2 + clampedRadius, height10 / 2 - clampedRadius],
28827
+ [-width10 / 2 + clampedRadius, -height10 / 2 + clampedRadius],
28828
+ [width10 / 2 - clampedRadius, -height10 / 2 + clampedRadius]
28829
+ ];
28830
+ cornerOffsets.forEach(([x, y]) => {
28831
+ shapes.push(
28832
+ Manifold.cylinder(
28833
+ thickness,
28834
+ clampedRadius,
28835
+ clampedRadius,
28836
+ RECT_PAD_SEGMENTS2,
28837
+ true
28838
+ ).translate([x, y, 0])
28839
+ );
28840
+ });
28841
+ return Manifold.union(shapes);
28842
+ }
28843
+ function createPadManifoldOp({
28844
+ Manifold,
28845
+ pad: pad2,
28846
+ padBaseThickness
28847
+ }) {
28848
+ if (pad2.shape === "rect") {
28849
+ const rectBorderRadius = extractRectBorderRadius(pad2);
28850
+ return createRoundedRectPrism({
28851
+ Manifold,
28852
+ width: pad2.width,
28853
+ height: pad2.height,
28854
+ thickness: padBaseThickness,
28855
+ borderRadius: rectBorderRadius
28856
+ });
28857
+ } else if (pad2.shape === "circle" && pad2.radius) {
28858
+ return Manifold.cylinder(padBaseThickness, pad2.radius, -1, 32, true);
28859
+ }
28860
+ return null;
28861
+ }
28862
+
28863
+ // src/utils/manifold/process-plated-holes.ts
28728
28864
  var COPPER_COLOR = new THREE17.Color(...colors.copper);
28729
28865
  function processPlatedHolesForManifold(Manifold, circuitJson, pcbThickness, manifoldInstancesForCleanup) {
28730
28866
  const platedHoleBoardDrills = [];
@@ -28900,19 +29036,26 @@ function processPlatedHolesForManifold(Manifold, circuitJson, pcbThickness, mani
28900
29036
  manifoldInstancesForCleanup.push(barrelCylinder);
28901
29037
  const padWidth = ph.rect_pad_width ?? ph.hole_diameter;
28902
29038
  const padHeight = ph.rect_pad_height ?? ph.hole_diameter;
29039
+ const rectBorderRadius = extractRectBorderRadius(ph);
28903
29040
  const padThickness = DEFAULT_SMT_PAD_THICKNESS;
28904
- const topPad = Manifold.cube(
28905
- [padWidth, padHeight, padThickness],
28906
- true
28907
- ).translate([
29041
+ const topPad = createRoundedRectPrism({
29042
+ Manifold,
29043
+ width: padWidth,
29044
+ height: padHeight,
29045
+ thickness: padThickness,
29046
+ borderRadius: rectBorderRadius
29047
+ }).translate([
28908
29048
  0,
28909
29049
  0,
28910
29050
  pcbThickness / 2 + padThickness / 2 + MANIFOLD_Z_OFFSET
28911
29051
  ]);
28912
- const bottomPad = Manifold.cube(
28913
- [padWidth, padHeight, padThickness],
28914
- true
28915
- ).translate([
29052
+ const bottomPad = createRoundedRectPrism({
29053
+ Manifold,
29054
+ width: padWidth,
29055
+ height: padHeight,
29056
+ thickness: padThickness,
29057
+ borderRadius: rectBorderRadius
29058
+ }).translate([
28916
29059
  0,
28917
29060
  0,
28918
29061
  -pcbThickness / 2 - padThickness / 2 - MANIFOLD_Z_OFFSET
@@ -29030,22 +29173,6 @@ function processViasForManifold(Manifold, circuitJson, pcbThickness, manifoldIns
29030
29173
  // src/utils/manifold/process-smt-pads.ts
29031
29174
  import { su as su10 } from "@tscircuit/circuit-json-util";
29032
29175
  import * as THREE19 from "three";
29033
-
29034
- // src/utils/pad-geoms.ts
29035
- function createPadManifoldOp({
29036
- Manifold,
29037
- pad: pad2,
29038
- padBaseThickness
29039
- }) {
29040
- if (pad2.shape === "rect") {
29041
- return Manifold.cube([pad2.width, pad2.height, padBaseThickness], true);
29042
- } else if (pad2.shape === "circle" && pad2.radius) {
29043
- return Manifold.cylinder(padBaseThickness, pad2.radius, -1, 32, true);
29044
- }
29045
- return null;
29046
- }
29047
-
29048
- // src/utils/manifold/process-smt-pads.ts
29049
29176
  var COPPER_COLOR3 = new THREE19.Color(...colors.copper);
29050
29177
  function processSmtPadsForManifold(Manifold, circuitJson, pcbThickness, manifoldInstancesForCleanup, holeUnion) {
29051
29178
  const smtPadGeoms = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tscircuit/3d-viewer",
3
- "version": "0.0.404",
3
+ "version": "0.0.405",
4
4
  "main": "./dist/index.js",
5
5
  "module": "./dist/index.js",
6
6
  "type": "module",