@tscircuit/3d-viewer 0.0.495 → 0.0.496

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 +380 -1
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -30825,7 +30825,7 @@ import * as THREE16 from "three";
30825
30825
  // package.json
30826
30826
  var package_default = {
30827
30827
  name: "@tscircuit/3d-viewer",
30828
- version: "0.0.494",
30828
+ version: "0.0.495",
30829
30829
  main: "./dist/index.js",
30830
30830
  module: "./dist/index.js",
30831
30831
  type: "module",
@@ -32663,6 +32663,149 @@ var platedHole = (plated_hole, ctx, options = {}) => {
32663
32663
  clipGeom
32664
32664
  );
32665
32665
  return (0, import_colors2.colorize)(colors.copper, finalCopper);
32666
+ } else if (plated_hole.shape === "rotated_pill_hole_with_rect_pad") {
32667
+ if (plated_hole.hole_shape && plated_hole.hole_shape !== "rotated_pill" || plated_hole.pad_shape && plated_hole.pad_shape !== "rect") {
32668
+ throw new Error(
32669
+ `Invalid hole_shape or pad_shape for rotated_pill_hole_with_rect_pad`
32670
+ );
32671
+ }
32672
+ const holeOffsetX = plated_hole.hole_offset_x || 0;
32673
+ const holeOffsetY = plated_hole.hole_offset_y || 0;
32674
+ const holeWidth = plated_hole.hole_width;
32675
+ const holeHeight = plated_hole.hole_height;
32676
+ const isHorizontal = holeWidth >= holeHeight;
32677
+ const longDim = isHorizontal ? holeWidth : holeHeight;
32678
+ const shortDim = isHorizontal ? holeHeight : holeWidth;
32679
+ const holeRadius = shortDim / 2;
32680
+ const rectLength = Math.abs(longDim - shortDim);
32681
+ const padWidth = plated_hole.rect_pad_width || holeWidth + 0.2;
32682
+ const padHeight = plated_hole.rect_pad_height || holeHeight + 0.2;
32683
+ const rectBorderRadius = extractRectBorderRadius(plated_hole);
32684
+ const barrelMargin = 0.03;
32685
+ const holeRotationRadians = (plated_hole.hole_ccw_rotation || 0) * Math.PI / 180;
32686
+ const rectRotationRadians = (plated_hole.rect_ccw_rotation || 0) * Math.PI / 180;
32687
+ const rotateHole = (geom) => {
32688
+ const rotatedOffsetX = holeOffsetX * Math.cos(holeRotationRadians) - holeOffsetY * Math.sin(holeRotationRadians);
32689
+ const rotatedOffsetY = holeOffsetX * Math.sin(holeRotationRadians) + holeOffsetY * Math.cos(holeRotationRadians);
32690
+ const rotated = (0, import_transforms4.rotate)([0, 0, holeRotationRadians], geom);
32691
+ return (0, import_transforms4.translate)(
32692
+ [plated_hole.x + rotatedOffsetX, plated_hole.y + rotatedOffsetY, 0],
32693
+ rotated
32694
+ );
32695
+ };
32696
+ const rotateRectPad = (geom) => {
32697
+ if (!rectRotationRadians) return geom;
32698
+ const toOrigin = (0, import_transforms4.translate)([-plated_hole.x, -plated_hole.y, 0], geom);
32699
+ const rotated = (0, import_transforms4.rotate)([0, 0, rectRotationRadians], toOrigin);
32700
+ return (0, import_transforms4.translate)([plated_hole.x, plated_hole.y, 0], rotated);
32701
+ };
32702
+ const barrel = rotateHole(
32703
+ (0, import_booleans2.union)(
32704
+ (0, import_primitives4.cuboid)({
32705
+ center: [0, 0, 0],
32706
+ size: isHorizontal ? [
32707
+ rectLength + 2 * barrelMargin,
32708
+ shortDim + 2 * barrelMargin,
32709
+ copperSpan
32710
+ ] : [
32711
+ shortDim + 2 * barrelMargin,
32712
+ rectLength + 2 * barrelMargin,
32713
+ copperSpan
32714
+ ]
32715
+ }),
32716
+ (0, import_primitives4.cylinder)({
32717
+ center: isHorizontal ? [-rectLength / 2, 0, 0] : [0, -rectLength / 2, 0],
32718
+ radius: holeRadius + barrelMargin,
32719
+ height: copperSpan
32720
+ }),
32721
+ (0, import_primitives4.cylinder)({
32722
+ center: isHorizontal ? [rectLength / 2, 0, 0] : [0, rectLength / 2, 0],
32723
+ radius: holeRadius + barrelMargin,
32724
+ height: copperSpan
32725
+ })
32726
+ )
32727
+ );
32728
+ const holeCut = rotateHole(
32729
+ (0, import_booleans2.union)(
32730
+ (0, import_primitives4.cuboid)({
32731
+ center: [0, 0, 0],
32732
+ size: isHorizontal ? [rectLength, shortDim, throughDrillHeight * 1.1] : [shortDim, rectLength, throughDrillHeight * 1.1]
32733
+ }),
32734
+ (0, import_primitives4.cylinder)({
32735
+ center: isHorizontal ? [-rectLength / 2, 0, 0] : [0, -rectLength / 2, 0],
32736
+ radius: holeRadius,
32737
+ height: throughDrillHeight * 1.1
32738
+ }),
32739
+ (0, import_primitives4.cylinder)({
32740
+ center: isHorizontal ? [rectLength / 2, 0, 0] : [0, rectLength / 2, 0],
32741
+ radius: holeRadius,
32742
+ height: throughDrillHeight * 1.1
32743
+ })
32744
+ )
32745
+ );
32746
+ const copperTopPad = rotateRectPad(
32747
+ createRectPadGeom({
32748
+ width: padWidth,
32749
+ height: padHeight,
32750
+ thickness: platedHoleLipHeight,
32751
+ center: [plated_hole.x, plated_hole.y, topSurfaceZ],
32752
+ borderRadius: rectBorderRadius
32753
+ })
32754
+ );
32755
+ const copperBottomPad = rotateRectPad(
32756
+ createRectPadGeom({
32757
+ width: padWidth,
32758
+ height: padHeight,
32759
+ thickness: platedHoleLipHeight,
32760
+ center: [plated_hole.x, plated_hole.y, bottomSurfaceZ],
32761
+ borderRadius: rectBorderRadius
32762
+ })
32763
+ );
32764
+ const copperFill = rotateRectPad(
32765
+ (() => {
32766
+ const height10 = Math.max(copperSpan - platedHoleLipHeight * 2, M);
32767
+ const topPadBottom = topSurfaceZ;
32768
+ const bottomPadTop = bottomSurfaceZ;
32769
+ const centerZ = (topPadBottom + bottomPadTop) / 2;
32770
+ const rect2d = (0, import_primitives4.roundedRectangle)({
32771
+ size: [padWidth, padHeight],
32772
+ roundRadius: rectBorderRadius || 0,
32773
+ segments: RECT_PAD_SEGMENTS
32774
+ });
32775
+ const extruded = (0, import_extrusions3.extrudeLinear)({ height: height10 }, rect2d);
32776
+ return (0, import_transforms4.translate)(
32777
+ [plated_hole.x, plated_hole.y, centerZ - height10 / 2],
32778
+ extruded
32779
+ );
32780
+ })()
32781
+ );
32782
+ const copperTopPadCut = (0, import_booleans2.subtract)(copperTopPad, holeCut);
32783
+ const copperBottomPadCut = (0, import_booleans2.subtract)(copperBottomPad, holeCut);
32784
+ const copperFillCut = (0, import_booleans2.subtract)(copperFill, holeCut);
32785
+ const barrelHoleCut = rotateHole(
32786
+ (0, import_booleans2.union)(
32787
+ (0, import_primitives4.cuboid)({
32788
+ center: [0, 0, 0],
32789
+ size: isHorizontal ? [rectLength - 2 * M, shortDim - 2 * M, throughDrillHeight * 1.1] : [shortDim - 2 * M, rectLength - 2 * M, throughDrillHeight * 1.1]
32790
+ }),
32791
+ (0, import_primitives4.cylinder)({
32792
+ center: isHorizontal ? [-rectLength / 2, 0, 0] : [0, -rectLength / 2, 0],
32793
+ radius: holeRadius - M,
32794
+ height: throughDrillHeight * 1.1
32795
+ }),
32796
+ (0, import_primitives4.cylinder)({
32797
+ center: isHorizontal ? [rectLength / 2, 0, 0] : [0, rectLength / 2, 0],
32798
+ radius: holeRadius - M,
32799
+ height: throughDrillHeight * 1.1
32800
+ })
32801
+ )
32802
+ );
32803
+ const barrelWithHole = (0, import_booleans2.subtract)(barrel, barrelHoleCut);
32804
+ const finalCopper = maybeClip(
32805
+ (0, import_booleans2.union)(copperTopPadCut, copperBottomPadCut, copperFillCut, barrelWithHole),
32806
+ clipGeom
32807
+ );
32808
+ return (0, import_colors2.colorize)(colors.copper, finalCopper);
32666
32809
  } else if (plated_hole.shape === "hole_with_polygon_pad") {
32667
32810
  const padOutline = plated_hole.pad_outline;
32668
32811
  if (!Array.isArray(padOutline) || padOutline.length < 3) {
@@ -33313,6 +33456,53 @@ var BoardGeomBuilder = class {
33313
33456
  clipGeom: this.boardClipGeom
33314
33457
  });
33315
33458
  this.platedHoleGeoms.push(platedHoleGeom);
33459
+ } else if (ph.shape === "rotated_pill_hole_with_rect_pad") {
33460
+ if (ph.hole_shape && ph.hole_shape !== "rotated_pill" || ph.pad_shape && ph.pad_shape !== "rect") {
33461
+ return;
33462
+ }
33463
+ const rphHoleWidth = ph.hole_width;
33464
+ const rphHoleHeight = ph.hole_height;
33465
+ const rphIsHorizontal = rphHoleWidth >= rphHoleHeight;
33466
+ const rphLongDim = rphIsHorizontal ? rphHoleWidth : rphHoleHeight;
33467
+ const rphShortDim = rphIsHorizontal ? rphHoleHeight : rphHoleWidth;
33468
+ const rphHoleRadius = rphShortDim / 2;
33469
+ const rphRectLength = Math.abs(rphLongDim - rphShortDim);
33470
+ const rphHoleOffsetX = ph.hole_offset_x || 0;
33471
+ const rphHoleOffsetY = ph.hole_offset_y || 0;
33472
+ let rphPillHole = (0, import_booleans5.union)(
33473
+ (0, import_primitives7.cuboid)({
33474
+ center: [0, 0, 0],
33475
+ size: rphIsHorizontal ? [rphRectLength, rphShortDim, this.ctx.pcbThickness * 1.5] : [rphShortDim, rphRectLength, this.ctx.pcbThickness * 1.5]
33476
+ }),
33477
+ (0, import_primitives7.cylinder)({
33478
+ center: rphIsHorizontal ? [-rphRectLength / 2, 0, 0] : [0, -rphRectLength / 2, 0],
33479
+ radius: rphHoleRadius,
33480
+ height: this.ctx.pcbThickness * 1.5
33481
+ }),
33482
+ (0, import_primitives7.cylinder)({
33483
+ center: rphIsHorizontal ? [rphRectLength / 2, 0, 0] : [0, rphRectLength / 2, 0],
33484
+ radius: rphHoleRadius,
33485
+ height: this.ctx.pcbThickness * 1.5
33486
+ })
33487
+ );
33488
+ const rphHoleRotationRadians = (ph.hole_ccw_rotation || 0) * Math.PI / 180;
33489
+ const rphRotatedOffsetX = rphHoleOffsetX * Math.cos(rphHoleRotationRadians) - rphHoleOffsetY * Math.sin(rphHoleRotationRadians);
33490
+ const rphRotatedOffsetY = rphHoleOffsetX * Math.sin(rphHoleRotationRadians) + rphHoleOffsetY * Math.cos(rphHoleRotationRadians);
33491
+ rphPillHole = (0, import_transforms6.rotateZ)(rphHoleRotationRadians, rphPillHole);
33492
+ const pillHole = (0, import_transforms6.translate)(
33493
+ [ph.x + rphRotatedOffsetX, ph.y + rphRotatedOffsetY, 0],
33494
+ rphPillHole
33495
+ );
33496
+ if (!opts.dontCutBoard) {
33497
+ this.boardGeom = (0, import_booleans5.subtract)(this.boardGeom, pillHole);
33498
+ }
33499
+ this.padGeoms = this.padGeoms.map(
33500
+ (pg) => (0, import_colors3.colorize)(colors.copper, (0, import_booleans5.subtract)(pg, pillHole))
33501
+ );
33502
+ const platedHoleGeom = platedHole(ph, this.ctx, {
33503
+ clipGeom: this.boardClipGeom
33504
+ });
33505
+ this.platedHoleGeoms.push(platedHoleGeom);
33316
33506
  } else if (ph.shape === "hole_with_polygon_pad") {
33317
33507
  const padOutline = ph.pad_outline;
33318
33508
  if (!Array.isArray(padOutline) || padOutline.length < 3) {
@@ -34270,6 +34460,42 @@ function createSoldermaskTextureForLayer({
34270
34460
  );
34271
34461
  ctx.fill();
34272
34462
  }
34463
+ } else if (hole.shape === "rotated_pill_hole_with_rect_pad") {
34464
+ const padWidth = (hole.rect_pad_width ?? hole.hole_width ?? 0) * traceTextureResolution;
34465
+ const padHeight = (hole.rect_pad_height ?? hole.hole_height ?? 0) * traceTextureResolution;
34466
+ const rawRadius = extractRectBorderRadius(hole);
34467
+ const borderRadius = clampRectBorderRadius(
34468
+ hole.rect_pad_width ?? hole.hole_width ?? 0,
34469
+ hole.rect_pad_height ?? hole.hole_height ?? 0,
34470
+ rawRadius
34471
+ ) * traceTextureResolution;
34472
+ const rectCcwRotationDeg = hole.rect_ccw_rotation || 0;
34473
+ const rectRotation = -rectCcwRotationDeg;
34474
+ if (rectRotation) {
34475
+ ctx.save();
34476
+ ctx.translate(canvasX, canvasY);
34477
+ ctx.rotate(rectRotation * Math.PI / 180);
34478
+ ctx.beginPath();
34479
+ ctx.roundRect(
34480
+ -padWidth / 2,
34481
+ -padHeight / 2,
34482
+ padWidth,
34483
+ padHeight,
34484
+ borderRadius
34485
+ );
34486
+ ctx.fill();
34487
+ ctx.restore();
34488
+ } else {
34489
+ ctx.beginPath();
34490
+ ctx.roundRect(
34491
+ canvasX - padWidth / 2,
34492
+ canvasY - padHeight / 2,
34493
+ padWidth,
34494
+ padHeight,
34495
+ borderRadius
34496
+ );
34497
+ ctx.fill();
34498
+ }
34273
34499
  }
34274
34500
  });
34275
34501
  const pcbHoles = su5(circuitJson).pcb_hole.list();
@@ -35216,6 +35442,31 @@ function createTraceTextureForLayer({
35216
35442
  ctx.beginPath();
35217
35443
  ctx.arc(canvasX, canvasY, canvasRadius, 0, 2 * Math.PI, false);
35218
35444
  ctx.fill();
35445
+ } else if (ph.layers.includes(layer) && ph.shape === "rotated_pill_hole_with_rect_pad") {
35446
+ const canvasX = (ph.x - boardOutlineBounds.minX) * traceTextureResolution;
35447
+ const canvasY = (boardOutlineBounds.maxY - ph.y) * traceTextureResolution;
35448
+ const padWidth = (ph.rect_pad_width ?? ph.hole_width ?? 0) * traceTextureResolution;
35449
+ const padHeight = (ph.rect_pad_height ?? ph.hole_height ?? 0) * traceTextureResolution;
35450
+ const rectCcwRotationDeg = ph.rect_ccw_rotation || 0;
35451
+ const rectRotation = -rectCcwRotationDeg;
35452
+ if (rectRotation) {
35453
+ ctx.save();
35454
+ ctx.translate(canvasX, canvasY);
35455
+ ctx.rotate(rectRotation * Math.PI / 180);
35456
+ ctx.beginPath();
35457
+ ctx.rect(-padWidth / 2, -padHeight / 2, padWidth, padHeight);
35458
+ ctx.fill();
35459
+ ctx.restore();
35460
+ } else {
35461
+ ctx.beginPath();
35462
+ ctx.rect(
35463
+ canvasX - padWidth / 2,
35464
+ canvasY - padHeight / 2,
35465
+ padWidth,
35466
+ padHeight
35467
+ );
35468
+ ctx.fill();
35469
+ }
35219
35470
  }
35220
35471
  });
35221
35472
  ctx.globalCompositeOperation = "source-over";
@@ -36775,6 +37026,134 @@ function processPlatedHolesForManifold(Manifold, CrossSection, circuitJson, pcbT
36775
37026
  geometry: threeGeom,
36776
37027
  color: COPPER_COLOR
36777
37028
  });
37029
+ } else if (ph.shape === "rotated_pill_hole_with_rect_pad") {
37030
+ if (ph.hole_shape && ph.hole_shape !== "rotated_pill" || ph.pad_shape && ph.pad_shape !== "rect") {
37031
+ return;
37032
+ }
37033
+ const holeW = ph.hole_width;
37034
+ const holeH = ph.hole_height;
37035
+ const holeOffsetX = ph.hole_offset_x || 0;
37036
+ const holeOffsetY = ph.hole_offset_y || 0;
37037
+ const padWidth = ph.rect_pad_width;
37038
+ const padHeight = ph.rect_pad_height;
37039
+ const rectBorderRadius = extractRectBorderRadius(ph);
37040
+ const padThickness = DEFAULT_SMT_PAD_THICKNESS;
37041
+ const drillW = holeW + 2 * MANIFOLD_Z_OFFSET;
37042
+ const drillH = holeH + 2 * MANIFOLD_Z_OFFSET;
37043
+ const drillDepth = pcbThickness * 1.2;
37044
+ let boardPillDrillOp = createPillOp(drillW, drillH, drillDepth).translate(
37045
+ [holeOffsetX, holeOffsetY, 0]
37046
+ );
37047
+ if (ph.hole_ccw_rotation) {
37048
+ const rotatedDrill = boardPillDrillOp.rotate([
37049
+ 0,
37050
+ 0,
37051
+ ph.hole_ccw_rotation
37052
+ ]);
37053
+ manifoldInstancesForCleanup.push(rotatedDrill);
37054
+ boardPillDrillOp = rotatedDrill;
37055
+ }
37056
+ const translatedBoardPillDrill = boardPillDrillOp.translate([
37057
+ ph.x,
37058
+ ph.y,
37059
+ 0
37060
+ ]);
37061
+ manifoldInstancesForCleanup.push(translatedBoardPillDrill);
37062
+ platedHoleBoardDrills.push(translatedBoardPillDrill);
37063
+ let mainFill = createRoundedRectPrism({
37064
+ Manifold,
37065
+ width: padWidth,
37066
+ height: padHeight,
37067
+ thickness: pcbThickness - 2 * padThickness - 2 * BOARD_SURFACE_OFFSET.copper + 0.1,
37068
+ // Fill between pads
37069
+ borderRadius: rectBorderRadius
37070
+ });
37071
+ manifoldInstancesForCleanup.push(mainFill);
37072
+ if (ph.rect_ccw_rotation) {
37073
+ const rotatedMainFill = mainFill.rotate([0, 0, ph.rect_ccw_rotation]);
37074
+ manifoldInstancesForCleanup.push(rotatedMainFill);
37075
+ mainFill = rotatedMainFill;
37076
+ }
37077
+ let topPad = createRoundedRectPrism({
37078
+ Manifold,
37079
+ width: padWidth,
37080
+ height: padHeight,
37081
+ thickness: padThickness,
37082
+ borderRadius: rectBorderRadius
37083
+ }).translate([0, 0, pcbThickness / 2 / 2 + BOARD_SURFACE_OFFSET.copper]);
37084
+ if (ph.rect_ccw_rotation) {
37085
+ const rotatedTopPad = topPad.rotate([0, 0, ph.rect_ccw_rotation]);
37086
+ manifoldInstancesForCleanup.push(rotatedTopPad);
37087
+ topPad = rotatedTopPad;
37088
+ }
37089
+ let bottomPad = createRoundedRectPrism({
37090
+ Manifold,
37091
+ width: padWidth,
37092
+ height: padHeight,
37093
+ thickness: padThickness,
37094
+ borderRadius: rectBorderRadius
37095
+ }).translate([0, 0, -pcbThickness / 2 / 2 - BOARD_SURFACE_OFFSET.copper]);
37096
+ if (ph.rect_ccw_rotation) {
37097
+ const rotatedBottomPad = bottomPad.rotate([0, 0, ph.rect_ccw_rotation]);
37098
+ manifoldInstancesForCleanup.push(rotatedBottomPad);
37099
+ bottomPad = rotatedBottomPad;
37100
+ }
37101
+ manifoldInstancesForCleanup.push(topPad, bottomPad);
37102
+ const barrelPill = createPillOp(
37103
+ holeW,
37104
+ holeH,
37105
+ pcbThickness * 1.02
37106
+ // Slightly taller than board
37107
+ ).translate([holeOffsetX, holeOffsetY, 0]);
37108
+ if (ph.hole_ccw_rotation) {
37109
+ const rotatedDrill = boardPillDrillOp.rotate([
37110
+ 0,
37111
+ 0,
37112
+ ph.hole_ccw_rotation
37113
+ ]);
37114
+ manifoldInstancesForCleanup.push(rotatedDrill);
37115
+ boardPillDrillOp = rotatedDrill;
37116
+ }
37117
+ manifoldInstancesForCleanup.push(barrelPill);
37118
+ const copperUnion = Manifold.union([
37119
+ mainFill,
37120
+ topPad,
37121
+ bottomPad,
37122
+ barrelPill
37123
+ ]);
37124
+ manifoldInstancesForCleanup.push(copperUnion);
37125
+ let holeCutOp = createPillOp(
37126
+ Math.max(holeW - 2 * PLATED_HOLE_LIP_HEIGHT, 0.01),
37127
+ Math.max(holeH - 2 * PLATED_HOLE_LIP_HEIGHT, 0.01),
37128
+ pcbThickness * 1.2
37129
+ // Ensure it cuts through
37130
+ ).translate([holeOffsetX, holeOffsetY, 0]);
37131
+ if (ph.hole_ccw_rotation) {
37132
+ const rotatedHoleCut = holeCutOp.rotate([0, 0, ph.hole_ccw_rotation]);
37133
+ manifoldInstancesForCleanup.push(rotatedHoleCut);
37134
+ holeCutOp = rotatedHoleCut;
37135
+ }
37136
+ manifoldInstancesForCleanup.push(holeCutOp);
37137
+ const finalCopper = copperUnion.subtract(holeCutOp);
37138
+ manifoldInstancesForCleanup.push(finalCopper);
37139
+ const translatedCopper = finalCopper.translate([ph.x, ph.y, 0]);
37140
+ manifoldInstancesForCleanup.push(translatedCopper);
37141
+ let finalCopperOp = translatedCopper;
37142
+ if (boardClipVolume) {
37143
+ const clipped = Manifold.intersection([
37144
+ translatedCopper,
37145
+ boardClipVolume
37146
+ ]);
37147
+ manifoldInstancesForCleanup.push(clipped);
37148
+ finalCopperOp = clipped;
37149
+ }
37150
+ platedHoleCopperOpsForSubtract.push(finalCopperOp);
37151
+ const threeGeom = manifoldMeshToThreeGeometry(finalCopperOp.getMesh());
37152
+ platedHoleCopperGeoms.push({
37153
+ key: `ph-${ph.pcb_plated_hole_id || index2}`,
37154
+ geometry: threeGeom,
37155
+ color: COPPER_COLOR
37156
+ });
36778
37157
  } else if (ph.shape === "hole_with_polygon_pad") {
36779
37158
  const padOutline = ph.pad_outline;
36780
37159
  if (!Array.isArray(padOutline) || padOutline.length < 3) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tscircuit/3d-viewer",
3
- "version": "0.0.495",
3
+ "version": "0.0.496",
4
4
  "main": "./dist/index.js",
5
5
  "module": "./dist/index.js",
6
6
  "type": "module",