@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.
- package/dist/index.js +380 -1
- 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.
|
|
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) {
|