@tscircuit/3d-viewer 0.0.516 → 0.0.518
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 +476 -815
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -14248,7 +14248,7 @@ import { useState as useState36, useCallback as useCallback21, useRef as useRef2
|
|
|
14248
14248
|
import * as THREE36 from "three";
|
|
14249
14249
|
|
|
14250
14250
|
// src/CadViewerJscad.tsx
|
|
14251
|
-
import { su as
|
|
14251
|
+
import { su as su12 } from "@tscircuit/circuit-json-util";
|
|
14252
14252
|
import { forwardRef as forwardRef3, useMemo as useMemo20 } from "react";
|
|
14253
14253
|
|
|
14254
14254
|
// src/AnyCadComponent.tsx
|
|
@@ -28483,7 +28483,7 @@ import * as THREE16 from "three";
|
|
|
28483
28483
|
// package.json
|
|
28484
28484
|
var package_default = {
|
|
28485
28485
|
name: "@tscircuit/3d-viewer",
|
|
28486
|
-
version: "0.0.
|
|
28486
|
+
version: "0.0.517",
|
|
28487
28487
|
main: "./dist/index.js",
|
|
28488
28488
|
module: "./dist/index.js",
|
|
28489
28489
|
type: "module",
|
|
@@ -28513,7 +28513,7 @@ var package_default = {
|
|
|
28513
28513
|
"@jscad/regl-renderer": "^2.6.12",
|
|
28514
28514
|
"@jscad/stl-serializer": "^2.1.20",
|
|
28515
28515
|
"circuit-json": "^0.0.372",
|
|
28516
|
-
"circuit-to-canvas": "^0.0.
|
|
28516
|
+
"circuit-to-canvas": "^0.0.80",
|
|
28517
28517
|
"react-hot-toast": "^2.6.0",
|
|
28518
28518
|
three: "^0.165.0",
|
|
28519
28519
|
"three-stdlib": "^2.36.0",
|
|
@@ -29687,10 +29687,6 @@ import { su as su3 } from "@tscircuit/circuit-json-util";
|
|
|
29687
29687
|
|
|
29688
29688
|
// src/geoms/constants.ts
|
|
29689
29689
|
var M = 0.01;
|
|
29690
|
-
var BOARD_SURFACE_OFFSET = {
|
|
29691
|
-
traces: 1e-3,
|
|
29692
|
-
copper: 2e-3
|
|
29693
|
-
};
|
|
29694
29690
|
var colors = {
|
|
29695
29691
|
copper: [0.9, 0.6, 0.2],
|
|
29696
29692
|
fr4Tan: [0.6, 0.43, 0.28],
|
|
@@ -29703,7 +29699,6 @@ var colors = {
|
|
|
29703
29699
|
};
|
|
29704
29700
|
var MANIFOLD_Z_OFFSET = 1e-3;
|
|
29705
29701
|
var SMOOTH_CIRCLE_SEGMENTS = 32;
|
|
29706
|
-
var DEFAULT_SMT_PAD_THICKNESS = 0.035;
|
|
29707
29702
|
var TRACE_TEXTURE_RESOLUTION = 150;
|
|
29708
29703
|
var FAUX_BOARD_OPACITY = 0.6;
|
|
29709
29704
|
var boardMaterialColors = {
|
|
@@ -29932,7 +29927,7 @@ function extractRectBorderRadius(source) {
|
|
|
29932
29927
|
}
|
|
29933
29928
|
|
|
29934
29929
|
// src/geoms/plated-hole.ts
|
|
29935
|
-
var
|
|
29930
|
+
var PLATED_HOLE_DRILL_OVERREACH = 0.05;
|
|
29936
29931
|
var RECT_PAD_SEGMENTS = 64;
|
|
29937
29932
|
var maybeClip = (geom, clipGeom) => clipGeom ? (0, import_booleans2.intersect)(clipGeom, geom) : geom;
|
|
29938
29933
|
var createRectPadGeom = ({
|
|
@@ -29958,13 +29953,21 @@ var createRectPadGeom = ({
|
|
|
29958
29953
|
var platedHole = (plated_hole, ctx, options = {}) => {
|
|
29959
29954
|
const { clipGeom } = options;
|
|
29960
29955
|
if (!plated_hole.shape) plated_hole.shape = "circle";
|
|
29961
|
-
const
|
|
29962
|
-
const
|
|
29963
|
-
const
|
|
29964
|
-
const
|
|
29956
|
+
const padThickness = Math.max(ctx.pcbThickness * 2e-3, M / 5);
|
|
29957
|
+
const surfaceClearance = Math.max(ctx.pcbThickness * 3e-4, M / 20);
|
|
29958
|
+
const fillClearance = Math.max(ctx.pcbThickness * 25e-4, M / 4);
|
|
29959
|
+
const throughDrillHeight = ctx.pcbThickness + 2 * PLATED_HOLE_DRILL_OVERREACH + 4 * M;
|
|
29960
|
+
const topSurfaceZ = ctx.pcbThickness / 2 + surfaceClearance + padThickness / 2;
|
|
29961
|
+
const bottomSurfaceZ = -ctx.pcbThickness / 2 - surfaceClearance - padThickness / 2;
|
|
29962
|
+
const copperSurfaceSpan = ctx.pcbThickness + 2 * surfaceClearance;
|
|
29963
|
+
const copperFillHeight = Math.max(
|
|
29964
|
+
ctx.pcbThickness - 2 * (padThickness + surfaceClearance + fillClearance),
|
|
29965
|
+
M
|
|
29966
|
+
);
|
|
29967
|
+
const barrelHeight = Math.max(copperFillHeight, M);
|
|
29965
29968
|
if (plated_hole.shape === "circle") {
|
|
29966
29969
|
const outerDiameter = plated_hole.outer_diameter ?? Math.max(plated_hole.hole_diameter, 0);
|
|
29967
|
-
const copperHeight =
|
|
29970
|
+
const copperHeight = copperSurfaceSpan;
|
|
29968
29971
|
const copperBody = (0, import_primitives4.cylinder)({
|
|
29969
29972
|
center: [plated_hole.x, plated_hole.y, 0],
|
|
29970
29973
|
radius: outerDiameter / 2,
|
|
@@ -29987,7 +29990,7 @@ var platedHole = (plated_hole, ctx, options = {}) => {
|
|
|
29987
29990
|
const circle = (0, import_primitives4.cylinder)({
|
|
29988
29991
|
center: [0, 0, 0],
|
|
29989
29992
|
radius: 1,
|
|
29990
|
-
height:
|
|
29993
|
+
height: copperSurfaceSpan,
|
|
29991
29994
|
segments: 64
|
|
29992
29995
|
// High segment count for smooth ellipse
|
|
29993
29996
|
});
|
|
@@ -30023,8 +30026,7 @@ var platedHole = (plated_hole, ctx, options = {}) => {
|
|
|
30023
30026
|
createRectPadGeom({
|
|
30024
30027
|
width: padWidth,
|
|
30025
30028
|
height: padHeight,
|
|
30026
|
-
thickness:
|
|
30027
|
-
// Slightly thicker to ensure connection
|
|
30029
|
+
thickness: padThickness,
|
|
30028
30030
|
center: [plated_hole.x, plated_hole.y, topSurfaceZ],
|
|
30029
30031
|
borderRadius: rectBorderRadius
|
|
30030
30032
|
}),
|
|
@@ -30032,25 +30034,20 @@ var platedHole = (plated_hole, ctx, options = {}) => {
|
|
|
30032
30034
|
createRectPadGeom({
|
|
30033
30035
|
width: padWidth,
|
|
30034
30036
|
height: padHeight,
|
|
30035
|
-
thickness:
|
|
30036
|
-
// Slightly thicker to ensure connection
|
|
30037
|
+
thickness: padThickness,
|
|
30037
30038
|
center: [plated_hole.x, plated_hole.y, bottomSurfaceZ],
|
|
30038
30039
|
borderRadius: rectBorderRadius
|
|
30039
30040
|
}),
|
|
30040
30041
|
// Main copper fill between pads with rounded corners
|
|
30041
30042
|
(() => {
|
|
30042
|
-
const height10 = Math.max(copperSpan - platedHoleLipHeight * 2, M);
|
|
30043
|
-
const topPadBottom = topSurfaceZ;
|
|
30044
|
-
const bottomPadTop = bottomSurfaceZ;
|
|
30045
|
-
const centerZ = (topPadBottom + bottomPadTop) / 2;
|
|
30046
30043
|
const rect2d = (0, import_primitives4.roundedRectangle)({
|
|
30047
30044
|
size: [padWidth, padHeight],
|
|
30048
30045
|
roundRadius: rectBorderRadius || 0,
|
|
30049
30046
|
segments: RECT_PAD_SEGMENTS
|
|
30050
30047
|
});
|
|
30051
|
-
const extruded = (0, import_extrusions3.extrudeLinear)({ height:
|
|
30048
|
+
const extruded = (0, import_extrusions3.extrudeLinear)({ height: copperFillHeight }, rect2d);
|
|
30052
30049
|
return (0, import_transforms4.translate)(
|
|
30053
|
-
[plated_hole.x, plated_hole.y,
|
|
30050
|
+
[plated_hole.x, plated_hole.y, -copperFillHeight / 2],
|
|
30054
30051
|
extruded
|
|
30055
30052
|
);
|
|
30056
30053
|
})(),
|
|
@@ -30062,7 +30059,7 @@ var platedHole = (plated_hole, ctx, options = {}) => {
|
|
|
30062
30059
|
0
|
|
30063
30060
|
],
|
|
30064
30061
|
radius: plated_hole.hole_diameter / 2,
|
|
30065
|
-
height:
|
|
30062
|
+
height: barrelHeight
|
|
30066
30063
|
})
|
|
30067
30064
|
),
|
|
30068
30065
|
clipGeom
|
|
@@ -30076,27 +30073,14 @@ var platedHole = (plated_hole, ctx, options = {}) => {
|
|
|
30076
30073
|
radius: Math.max(plated_hole.hole_diameter / 2 - M, 0.01),
|
|
30077
30074
|
height: throughDrillHeight
|
|
30078
30075
|
});
|
|
30079
|
-
const
|
|
30080
|
-
center: [
|
|
30081
|
-
plated_hole.x + (holeOffsetX || 0),
|
|
30082
|
-
plated_hole.y + (holeOffsetY || 0),
|
|
30083
|
-
0
|
|
30084
|
-
],
|
|
30085
|
-
radius: plated_hole.hole_diameter / 2,
|
|
30086
|
-
height: copperSpan
|
|
30087
|
-
});
|
|
30088
|
-
let finalCopper = (0, import_booleans2.union)(
|
|
30089
|
-
(0, import_booleans2.subtract)(copperSolid, barrel),
|
|
30090
|
-
// Subtract the barrel from the main shape
|
|
30091
|
-
barrel
|
|
30092
|
-
// Add the barrel back to ensure proper connection
|
|
30093
|
-
);
|
|
30076
|
+
const copperWithDrill = (0, import_booleans2.subtract)(copperSolid, drill);
|
|
30094
30077
|
if (options.clipGeom) {
|
|
30095
|
-
|
|
30096
|
-
|
|
30097
|
-
|
|
30078
|
+
return (0, import_colors2.colorize)(
|
|
30079
|
+
colors.copper,
|
|
30080
|
+
(0, import_booleans2.intersect)(copperWithDrill, options.clipGeom)
|
|
30081
|
+
);
|
|
30098
30082
|
}
|
|
30099
|
-
return (0, import_colors2.colorize)(colors.copper,
|
|
30083
|
+
return (0, import_colors2.colorize)(colors.copper, copperWithDrill);
|
|
30100
30084
|
}
|
|
30101
30085
|
if (plated_hole.shape === "pill") {
|
|
30102
30086
|
const rotationRadians = (plated_hole.ccw_rotation || 0) * Math.PI / 180;
|
|
@@ -30115,7 +30099,7 @@ var platedHole = (plated_hole, ctx, options = {}) => {
|
|
|
30115
30099
|
const outerRadius = outerPillHeight / 2;
|
|
30116
30100
|
const rectLength = Math.abs(holeWidth - holeHeight);
|
|
30117
30101
|
const outerRectLength = Math.abs(outerPillWidth - outerPillHeight);
|
|
30118
|
-
const copperHeight =
|
|
30102
|
+
const copperHeight = copperSurfaceSpan;
|
|
30119
30103
|
const createPillSection = (width10, height10, thickness) => {
|
|
30120
30104
|
const radius = height10 / 2;
|
|
30121
30105
|
const length51 = Math.abs(width10 - height10);
|
|
@@ -30189,11 +30173,11 @@ var platedHole = (plated_hole, ctx, options = {}) => {
|
|
|
30189
30173
|
size: shouldRotate ? [
|
|
30190
30174
|
holeHeight + 2 * barrelMargin,
|
|
30191
30175
|
rectLength + 2 * barrelMargin,
|
|
30192
|
-
|
|
30176
|
+
barrelHeight
|
|
30193
30177
|
] : [
|
|
30194
30178
|
rectLength + 2 * barrelMargin,
|
|
30195
30179
|
holeHeight + 2 * barrelMargin,
|
|
30196
|
-
|
|
30180
|
+
barrelHeight
|
|
30197
30181
|
]
|
|
30198
30182
|
}),
|
|
30199
30183
|
(0, import_primitives4.cylinder)({
|
|
@@ -30207,7 +30191,7 @@ var platedHole = (plated_hole, ctx, options = {}) => {
|
|
|
30207
30191
|
0
|
|
30208
30192
|
],
|
|
30209
30193
|
radius: holeRadius + barrelMargin,
|
|
30210
|
-
height:
|
|
30194
|
+
height: barrelHeight
|
|
30211
30195
|
}),
|
|
30212
30196
|
(0, import_primitives4.cylinder)({
|
|
30213
30197
|
center: shouldRotate ? [
|
|
@@ -30220,13 +30204,13 @@ var platedHole = (plated_hole, ctx, options = {}) => {
|
|
|
30220
30204
|
0
|
|
30221
30205
|
],
|
|
30222
30206
|
radius: holeRadius + barrelMargin,
|
|
30223
|
-
height:
|
|
30207
|
+
height: barrelHeight
|
|
30224
30208
|
})
|
|
30225
30209
|
);
|
|
30226
30210
|
const holeCut = (0, import_booleans2.union)(
|
|
30227
30211
|
(0, import_primitives4.cuboid)({
|
|
30228
30212
|
center: [plated_hole.x + holeOffsetX, plated_hole.y + holeOffsetY, 0],
|
|
30229
|
-
size: shouldRotate ? [holeHeight, rectLength, throughDrillHeight
|
|
30213
|
+
size: shouldRotate ? [holeHeight, rectLength, throughDrillHeight] : [rectLength, holeHeight, throughDrillHeight]
|
|
30230
30214
|
}),
|
|
30231
30215
|
(0, import_primitives4.cylinder)({
|
|
30232
30216
|
center: shouldRotate ? [
|
|
@@ -30239,7 +30223,7 @@ var platedHole = (plated_hole, ctx, options = {}) => {
|
|
|
30239
30223
|
0
|
|
30240
30224
|
],
|
|
30241
30225
|
radius: holeRadius,
|
|
30242
|
-
height: throughDrillHeight
|
|
30226
|
+
height: throughDrillHeight
|
|
30243
30227
|
}),
|
|
30244
30228
|
(0, import_primitives4.cylinder)({
|
|
30245
30229
|
center: shouldRotate ? [
|
|
@@ -30252,36 +30236,32 @@ var platedHole = (plated_hole, ctx, options = {}) => {
|
|
|
30252
30236
|
0
|
|
30253
30237
|
],
|
|
30254
30238
|
radius: holeRadius,
|
|
30255
|
-
height: throughDrillHeight
|
|
30239
|
+
height: throughDrillHeight
|
|
30256
30240
|
})
|
|
30257
30241
|
);
|
|
30258
30242
|
const copperTopPad = createRectPadGeom({
|
|
30259
30243
|
width: padWidth,
|
|
30260
30244
|
height: padHeight,
|
|
30261
|
-
thickness:
|
|
30245
|
+
thickness: padThickness,
|
|
30262
30246
|
center: [plated_hole.x, plated_hole.y, topSurfaceZ],
|
|
30263
30247
|
borderRadius: rectBorderRadius
|
|
30264
30248
|
});
|
|
30265
30249
|
const copperBottomPad = createRectPadGeom({
|
|
30266
30250
|
width: padWidth,
|
|
30267
30251
|
height: padHeight,
|
|
30268
|
-
thickness:
|
|
30252
|
+
thickness: padThickness,
|
|
30269
30253
|
center: [plated_hole.x, plated_hole.y, bottomSurfaceZ],
|
|
30270
30254
|
borderRadius: rectBorderRadius
|
|
30271
30255
|
});
|
|
30272
30256
|
const copperFill = (() => {
|
|
30273
|
-
const height10 = Math.max(copperSpan - platedHoleLipHeight * 2, M);
|
|
30274
|
-
const topPadBottom = topSurfaceZ;
|
|
30275
|
-
const bottomPadTop = bottomSurfaceZ;
|
|
30276
|
-
const centerZ = (topPadBottom + bottomPadTop) / 2;
|
|
30277
30257
|
const rect2d = (0, import_primitives4.roundedRectangle)({
|
|
30278
30258
|
size: [padWidth, padHeight],
|
|
30279
30259
|
roundRadius: rectBorderRadius || 0,
|
|
30280
30260
|
segments: RECT_PAD_SEGMENTS
|
|
30281
30261
|
});
|
|
30282
|
-
const extruded = (0, import_extrusions3.extrudeLinear)({ height:
|
|
30262
|
+
const extruded = (0, import_extrusions3.extrudeLinear)({ height: copperFillHeight }, rect2d);
|
|
30283
30263
|
return (0, import_transforms4.translate)(
|
|
30284
|
-
[plated_hole.x, plated_hole.y,
|
|
30264
|
+
[plated_hole.x, plated_hole.y, -copperFillHeight / 2],
|
|
30285
30265
|
extruded
|
|
30286
30266
|
);
|
|
30287
30267
|
})();
|
|
@@ -30291,7 +30271,7 @@ var platedHole = (plated_hole, ctx, options = {}) => {
|
|
|
30291
30271
|
const barrelHoleCut = (0, import_booleans2.union)(
|
|
30292
30272
|
(0, import_primitives4.cuboid)({
|
|
30293
30273
|
center: [plated_hole.x + holeOffsetX, plated_hole.y + holeOffsetY, 0],
|
|
30294
|
-
size: shouldRotate ? [holeHeight - 2 * M, rectLength - 2 * M, throughDrillHeight
|
|
30274
|
+
size: shouldRotate ? [holeHeight - 2 * M, rectLength - 2 * M, throughDrillHeight] : [rectLength - 2 * M, holeHeight - 2 * M, throughDrillHeight]
|
|
30295
30275
|
}),
|
|
30296
30276
|
(0, import_primitives4.cylinder)({
|
|
30297
30277
|
center: shouldRotate ? [
|
|
@@ -30304,7 +30284,7 @@ var platedHole = (plated_hole, ctx, options = {}) => {
|
|
|
30304
30284
|
0
|
|
30305
30285
|
],
|
|
30306
30286
|
radius: holeRadius - M,
|
|
30307
|
-
height: throughDrillHeight
|
|
30287
|
+
height: throughDrillHeight
|
|
30308
30288
|
}),
|
|
30309
30289
|
(0, import_primitives4.cylinder)({
|
|
30310
30290
|
center: shouldRotate ? [
|
|
@@ -30317,7 +30297,7 @@ var platedHole = (plated_hole, ctx, options = {}) => {
|
|
|
30317
30297
|
0
|
|
30318
30298
|
],
|
|
30319
30299
|
radius: holeRadius - M,
|
|
30320
|
-
height: throughDrillHeight
|
|
30300
|
+
height: throughDrillHeight
|
|
30321
30301
|
})
|
|
30322
30302
|
);
|
|
30323
30303
|
const barrelWithHole = (0, import_booleans2.subtract)(barrel, barrelHoleCut);
|
|
@@ -30369,22 +30349,22 @@ var platedHole = (plated_hole, ctx, options = {}) => {
|
|
|
30369
30349
|
size: isHorizontal ? [
|
|
30370
30350
|
rectLength + 2 * barrelMargin,
|
|
30371
30351
|
shortDim + 2 * barrelMargin,
|
|
30372
|
-
|
|
30352
|
+
barrelHeight
|
|
30373
30353
|
] : [
|
|
30374
30354
|
shortDim + 2 * barrelMargin,
|
|
30375
30355
|
rectLength + 2 * barrelMargin,
|
|
30376
|
-
|
|
30356
|
+
barrelHeight
|
|
30377
30357
|
]
|
|
30378
30358
|
}),
|
|
30379
30359
|
(0, import_primitives4.cylinder)({
|
|
30380
30360
|
center: isHorizontal ? [-rectLength / 2, 0, 0] : [0, -rectLength / 2, 0],
|
|
30381
30361
|
radius: holeRadius + barrelMargin,
|
|
30382
|
-
height:
|
|
30362
|
+
height: barrelHeight
|
|
30383
30363
|
}),
|
|
30384
30364
|
(0, import_primitives4.cylinder)({
|
|
30385
30365
|
center: isHorizontal ? [rectLength / 2, 0, 0] : [0, rectLength / 2, 0],
|
|
30386
30366
|
radius: holeRadius + barrelMargin,
|
|
30387
|
-
height:
|
|
30367
|
+
height: barrelHeight
|
|
30388
30368
|
})
|
|
30389
30369
|
)
|
|
30390
30370
|
);
|
|
@@ -30392,17 +30372,17 @@ var platedHole = (plated_hole, ctx, options = {}) => {
|
|
|
30392
30372
|
(0, import_booleans2.union)(
|
|
30393
30373
|
(0, import_primitives4.cuboid)({
|
|
30394
30374
|
center: [0, 0, 0],
|
|
30395
|
-
size: isHorizontal ? [rectLength, shortDim, throughDrillHeight
|
|
30375
|
+
size: isHorizontal ? [rectLength, shortDim, throughDrillHeight] : [shortDim, rectLength, throughDrillHeight]
|
|
30396
30376
|
}),
|
|
30397
30377
|
(0, import_primitives4.cylinder)({
|
|
30398
30378
|
center: isHorizontal ? [-rectLength / 2, 0, 0] : [0, -rectLength / 2, 0],
|
|
30399
30379
|
radius: holeRadius,
|
|
30400
|
-
height: throughDrillHeight
|
|
30380
|
+
height: throughDrillHeight
|
|
30401
30381
|
}),
|
|
30402
30382
|
(0, import_primitives4.cylinder)({
|
|
30403
30383
|
center: isHorizontal ? [rectLength / 2, 0, 0] : [0, rectLength / 2, 0],
|
|
30404
30384
|
radius: holeRadius,
|
|
30405
|
-
height: throughDrillHeight
|
|
30385
|
+
height: throughDrillHeight
|
|
30406
30386
|
})
|
|
30407
30387
|
)
|
|
30408
30388
|
);
|
|
@@ -30410,7 +30390,7 @@ var platedHole = (plated_hole, ctx, options = {}) => {
|
|
|
30410
30390
|
createRectPadGeom({
|
|
30411
30391
|
width: padWidth,
|
|
30412
30392
|
height: padHeight,
|
|
30413
|
-
thickness:
|
|
30393
|
+
thickness: padThickness,
|
|
30414
30394
|
center: [plated_hole.x, plated_hole.y, topSurfaceZ],
|
|
30415
30395
|
borderRadius: rectBorderRadius
|
|
30416
30396
|
})
|
|
@@ -30419,25 +30399,21 @@ var platedHole = (plated_hole, ctx, options = {}) => {
|
|
|
30419
30399
|
createRectPadGeom({
|
|
30420
30400
|
width: padWidth,
|
|
30421
30401
|
height: padHeight,
|
|
30422
|
-
thickness:
|
|
30402
|
+
thickness: padThickness,
|
|
30423
30403
|
center: [plated_hole.x, plated_hole.y, bottomSurfaceZ],
|
|
30424
30404
|
borderRadius: rectBorderRadius
|
|
30425
30405
|
})
|
|
30426
30406
|
);
|
|
30427
30407
|
const copperFill = rotateRectPad(
|
|
30428
30408
|
(() => {
|
|
30429
|
-
const height10 = Math.max(copperSpan - platedHoleLipHeight * 2, M);
|
|
30430
|
-
const topPadBottom = topSurfaceZ;
|
|
30431
|
-
const bottomPadTop = bottomSurfaceZ;
|
|
30432
|
-
const centerZ = (topPadBottom + bottomPadTop) / 2;
|
|
30433
30409
|
const rect2d = (0, import_primitives4.roundedRectangle)({
|
|
30434
30410
|
size: [padWidth, padHeight],
|
|
30435
30411
|
roundRadius: rectBorderRadius || 0,
|
|
30436
30412
|
segments: RECT_PAD_SEGMENTS
|
|
30437
30413
|
});
|
|
30438
|
-
const extruded = (0, import_extrusions3.extrudeLinear)({ height:
|
|
30414
|
+
const extruded = (0, import_extrusions3.extrudeLinear)({ height: copperFillHeight }, rect2d);
|
|
30439
30415
|
return (0, import_transforms4.translate)(
|
|
30440
|
-
[plated_hole.x, plated_hole.y,
|
|
30416
|
+
[plated_hole.x, plated_hole.y, -copperFillHeight / 2],
|
|
30441
30417
|
extruded
|
|
30442
30418
|
);
|
|
30443
30419
|
})()
|
|
@@ -30449,17 +30425,17 @@ var platedHole = (plated_hole, ctx, options = {}) => {
|
|
|
30449
30425
|
(0, import_booleans2.union)(
|
|
30450
30426
|
(0, import_primitives4.cuboid)({
|
|
30451
30427
|
center: [0, 0, 0],
|
|
30452
|
-
size: isHorizontal ? [rectLength - 2 * M, shortDim - 2 * M, throughDrillHeight
|
|
30428
|
+
size: isHorizontal ? [rectLength - 2 * M, shortDim - 2 * M, throughDrillHeight] : [shortDim - 2 * M, rectLength - 2 * M, throughDrillHeight]
|
|
30453
30429
|
}),
|
|
30454
30430
|
(0, import_primitives4.cylinder)({
|
|
30455
30431
|
center: isHorizontal ? [-rectLength / 2, 0, 0] : [0, -rectLength / 2, 0],
|
|
30456
30432
|
radius: holeRadius - M,
|
|
30457
|
-
height: throughDrillHeight
|
|
30433
|
+
height: throughDrillHeight
|
|
30458
30434
|
}),
|
|
30459
30435
|
(0, import_primitives4.cylinder)({
|
|
30460
30436
|
center: isHorizontal ? [rectLength / 2, 0, 0] : [0, rectLength / 2, 0],
|
|
30461
30437
|
radius: holeRadius - M,
|
|
30462
|
-
height: throughDrillHeight
|
|
30438
|
+
height: throughDrillHeight
|
|
30463
30439
|
})
|
|
30464
30440
|
)
|
|
30465
30441
|
);
|
|
@@ -30481,7 +30457,7 @@ var platedHole = (plated_hole, ctx, options = {}) => {
|
|
|
30481
30457
|
point.y
|
|
30482
30458
|
]);
|
|
30483
30459
|
const polygon2d = (0, import_primitives4.polygon)({ points: polygonPoints });
|
|
30484
|
-
const centerZ =
|
|
30460
|
+
const centerZ = 0;
|
|
30485
30461
|
const createPolygonPad = (thickness, zCenter) => {
|
|
30486
30462
|
const safeThickness = Math.max(thickness, M);
|
|
30487
30463
|
const extruded = (0, import_extrusions3.extrudeLinear)({ height: safeThickness }, polygon2d);
|
|
@@ -30490,14 +30466,11 @@ var platedHole = (plated_hole, ctx, options = {}) => {
|
|
|
30490
30466
|
extruded
|
|
30491
30467
|
);
|
|
30492
30468
|
};
|
|
30493
|
-
const mainFill = createPolygonPad(
|
|
30494
|
-
|
|
30495
|
-
|
|
30496
|
-
);
|
|
30497
|
-
const topPad = createPolygonPad(platedHoleLipHeight, topSurfaceZ);
|
|
30498
|
-
const bottomPad = createPolygonPad(platedHoleLipHeight, bottomSurfaceZ);
|
|
30469
|
+
const mainFill = createPolygonPad(copperFillHeight, centerZ);
|
|
30470
|
+
const topPad = createPolygonPad(padThickness, topSurfaceZ);
|
|
30471
|
+
const bottomPad = createPolygonPad(padThickness, bottomSurfaceZ);
|
|
30499
30472
|
const copperSolid = maybeClip((0, import_booleans2.union)(mainFill, topPad, bottomPad), clipGeom);
|
|
30500
|
-
const barrel = createHoleWithPolygonPadHoleGeom(plated_hole,
|
|
30473
|
+
const barrel = createHoleWithPolygonPadHoleGeom(plated_hole, barrelHeight);
|
|
30501
30474
|
if (!barrel) return (0, import_colors2.colorize)(colors.copper, copperSolid);
|
|
30502
30475
|
const drill = createHoleWithPolygonPadHoleGeom(plated_hole, throughDrillHeight, {
|
|
30503
30476
|
sizeDelta: -2 * M
|
|
@@ -31367,7 +31340,8 @@ function STLModel({
|
|
|
31367
31340
|
stlData,
|
|
31368
31341
|
mtlUrl,
|
|
31369
31342
|
color,
|
|
31370
|
-
opacity = 1
|
|
31343
|
+
opacity = 1,
|
|
31344
|
+
layerType
|
|
31371
31345
|
}) {
|
|
31372
31346
|
const { rootObject } = useThree();
|
|
31373
31347
|
const [geom, setGeom] = useState14(null);
|
|
@@ -31391,13 +31365,19 @@ function STLModel({
|
|
|
31391
31365
|
}, [stlUrl, stlData]);
|
|
31392
31366
|
const mesh = useMemo18(() => {
|
|
31393
31367
|
if (!geom) return null;
|
|
31368
|
+
const isBoardLayer = layerType === "board";
|
|
31394
31369
|
const material = new THREE18.MeshStandardMaterial({
|
|
31395
31370
|
color: Array.isArray(color) ? new THREE18.Color(color[0], color[1], color[2]) : color,
|
|
31396
31371
|
transparent: opacity !== 1,
|
|
31397
|
-
opacity
|
|
31372
|
+
opacity,
|
|
31373
|
+
polygonOffset: isBoardLayer,
|
|
31374
|
+
polygonOffsetFactor: isBoardLayer ? 6 : 0,
|
|
31375
|
+
polygonOffsetUnits: isBoardLayer ? 6 : 0
|
|
31398
31376
|
});
|
|
31399
|
-
|
|
31400
|
-
|
|
31377
|
+
const createdMesh = new THREE18.Mesh(geom, material);
|
|
31378
|
+
createdMesh.renderOrder = isBoardLayer ? -1 : 1;
|
|
31379
|
+
return createdMesh;
|
|
31380
|
+
}, [geom, color, opacity, layerType]);
|
|
31401
31381
|
useEffect22(() => {
|
|
31402
31382
|
if (!rootObject || !mesh) return;
|
|
31403
31383
|
rootObject.add(mesh);
|
|
@@ -31438,7 +31418,15 @@ function VisibleSTLModel({
|
|
|
31438
31418
|
if (!shouldShow) {
|
|
31439
31419
|
return null;
|
|
31440
31420
|
}
|
|
31441
|
-
return /* @__PURE__ */ jsx16(
|
|
31421
|
+
return /* @__PURE__ */ jsx16(
|
|
31422
|
+
STLModel,
|
|
31423
|
+
{
|
|
31424
|
+
stlData,
|
|
31425
|
+
color,
|
|
31426
|
+
opacity,
|
|
31427
|
+
layerType
|
|
31428
|
+
}
|
|
31429
|
+
);
|
|
31442
31430
|
}
|
|
31443
31431
|
|
|
31444
31432
|
// src/three-components/ThreeErrorBoundary.tsx
|
|
@@ -31460,7 +31448,7 @@ var ThreeErrorBoundary = class extends React11.Component {
|
|
|
31460
31448
|
};
|
|
31461
31449
|
|
|
31462
31450
|
// src/three-components/JscadBoardTextures.tsx
|
|
31463
|
-
import { su as
|
|
31451
|
+
import { su as su9 } from "@tscircuit/circuit-json-util";
|
|
31464
31452
|
import { useEffect as useEffect23, useMemo as useMemo19 } from "react";
|
|
31465
31453
|
|
|
31466
31454
|
// src/textures/create-combined-board-textures.ts
|
|
@@ -32058,6 +32046,31 @@ function createPadTextureForLayer({
|
|
|
32058
32046
|
ctx.beginPath();
|
|
32059
32047
|
ctx.arc(canvasX, canvasY, radius, 0, 2 * Math.PI);
|
|
32060
32048
|
ctx.fill();
|
|
32049
|
+
} else if (pad2.shape === "pill" || pad2.shape === "rotated_pill") {
|
|
32050
|
+
const width10 = pad2.width * traceTextureResolution;
|
|
32051
|
+
const height10 = pad2.height * traceTextureResolution;
|
|
32052
|
+
const borderRadius = Math.min(width10, height10) / 2;
|
|
32053
|
+
const ccwRotation = pad2.ccw_rotation || 0;
|
|
32054
|
+
const rotation = -ccwRotation * (Math.PI / 180);
|
|
32055
|
+
if (rotation) {
|
|
32056
|
+
ctx.save();
|
|
32057
|
+
ctx.translate(canvasX, canvasY);
|
|
32058
|
+
ctx.rotate(rotation);
|
|
32059
|
+
ctx.beginPath();
|
|
32060
|
+
ctx.roundRect(-width10 / 2, -height10 / 2, width10, height10, borderRadius);
|
|
32061
|
+
ctx.fill();
|
|
32062
|
+
ctx.restore();
|
|
32063
|
+
} else {
|
|
32064
|
+
ctx.beginPath();
|
|
32065
|
+
ctx.roundRect(
|
|
32066
|
+
canvasX - width10 / 2,
|
|
32067
|
+
canvasY - height10 / 2,
|
|
32068
|
+
width10,
|
|
32069
|
+
height10,
|
|
32070
|
+
borderRadius
|
|
32071
|
+
);
|
|
32072
|
+
ctx.fill();
|
|
32073
|
+
}
|
|
32061
32074
|
} else if (pad2.shape === "rotated_rect") {
|
|
32062
32075
|
const width10 = pad2.width * traceTextureResolution;
|
|
32063
32076
|
const height10 = pad2.height * traceTextureResolution;
|
|
@@ -32611,16 +32624,28 @@ function createSilkscreenTextureForLayer({
|
|
|
32611
32624
|
return texture;
|
|
32612
32625
|
}
|
|
32613
32626
|
|
|
32614
|
-
// src/utils/
|
|
32615
|
-
import { su as su8 } from "@tscircuit/circuit-json-util";
|
|
32627
|
+
// src/utils/trace-texture.ts
|
|
32616
32628
|
import * as THREE23 from "three";
|
|
32617
|
-
|
|
32629
|
+
import { su as su8 } from "@tscircuit/circuit-json-util";
|
|
32630
|
+
function isWireRoutePoint(point) {
|
|
32631
|
+
return point && point.route_type === "wire" && typeof point.layer === "string" && typeof point.width === "number";
|
|
32632
|
+
}
|
|
32633
|
+
function createTraceTextureForLayer({
|
|
32618
32634
|
layer,
|
|
32619
32635
|
circuitJson,
|
|
32620
32636
|
boardData,
|
|
32621
|
-
|
|
32637
|
+
traceColor,
|
|
32622
32638
|
traceTextureResolution
|
|
32623
32639
|
}) {
|
|
32640
|
+
const pcbTraces = su8(circuitJson).pcb_trace.list();
|
|
32641
|
+
const allPcbVias = su8(circuitJson).pcb_via.list();
|
|
32642
|
+
const allPcbPlatedHoles = su8(
|
|
32643
|
+
circuitJson
|
|
32644
|
+
).pcb_plated_hole.list();
|
|
32645
|
+
const tracesOnLayer = pcbTraces.filter(
|
|
32646
|
+
(t) => t.route.some((p) => isWireRoutePoint(p) && p.layer === layer)
|
|
32647
|
+
);
|
|
32648
|
+
if (tracesOnLayer.length === 0) return null;
|
|
32624
32649
|
const boardOutlineBounds = calculateOutlineBounds(boardData);
|
|
32625
32650
|
const canvas = document.createElement("canvas");
|
|
32626
32651
|
const canvasWidth = Math.floor(
|
|
@@ -32637,546 +32662,81 @@ function createSoldermaskTextureForLayer({
|
|
|
32637
32662
|
ctx.translate(0, canvasHeight);
|
|
32638
32663
|
ctx.scale(1, -1);
|
|
32639
32664
|
}
|
|
32640
|
-
|
|
32641
|
-
|
|
32642
|
-
ctx.fillStyle = soldermaskColor;
|
|
32643
|
-
if (boardData.outline && boardData.outline.length >= 3) {
|
|
32665
|
+
tracesOnLayer.forEach((trace) => {
|
|
32666
|
+
let firstPoint = true;
|
|
32644
32667
|
ctx.beginPath();
|
|
32645
|
-
|
|
32646
|
-
ctx.
|
|
32647
|
-
|
|
32648
|
-
|
|
32649
|
-
|
|
32650
|
-
|
|
32651
|
-
|
|
32652
|
-
|
|
32653
|
-
|
|
32654
|
-
|
|
32655
|
-
|
|
32656
|
-
|
|
32657
|
-
|
|
32658
|
-
|
|
32659
|
-
|
|
32660
|
-
|
|
32661
|
-
|
|
32662
|
-
|
|
32663
|
-
|
|
32664
|
-
const px = canvasXFromPcb(point.x);
|
|
32665
|
-
const py = canvasYFromPcb(point.y);
|
|
32666
|
-
if (index2 === 0) {
|
|
32667
|
-
ctx.moveTo(px, py);
|
|
32668
|
-
} else {
|
|
32669
|
-
ctx.lineTo(px, py);
|
|
32670
|
-
}
|
|
32671
|
-
});
|
|
32672
|
-
ctx.closePath();
|
|
32673
|
-
ctx.fill();
|
|
32674
|
-
return;
|
|
32675
|
-
}
|
|
32676
|
-
if (pad2.x === void 0 || pad2.y === void 0) return;
|
|
32677
|
-
if (Number.isNaN(pad2.x) || Number.isNaN(pad2.y)) {
|
|
32678
|
-
console.warn(
|
|
32679
|
-
`[soldermask-texture] Skipping pad ${pad2.pcb_smtpad_id} with NaN coordinates`
|
|
32680
|
-
);
|
|
32681
|
-
return;
|
|
32682
|
-
}
|
|
32683
|
-
const x = pad2.x;
|
|
32684
|
-
const y = pad2.y;
|
|
32685
|
-
const canvasX = canvasXFromPcb(x);
|
|
32686
|
-
const canvasY = canvasYFromPcb(y);
|
|
32687
|
-
if (pad2.shape === "rect") {
|
|
32688
|
-
const width10 = pad2.width * traceTextureResolution;
|
|
32689
|
-
const height10 = pad2.height * traceTextureResolution;
|
|
32690
|
-
const rawRadius = extractRectBorderRadius(pad2);
|
|
32691
|
-
const borderRadius = clampRectBorderRadius(
|
|
32692
|
-
pad2.width,
|
|
32693
|
-
pad2.height,
|
|
32694
|
-
rawRadius
|
|
32695
|
-
) * traceTextureResolution;
|
|
32696
|
-
if (borderRadius > 0) {
|
|
32697
|
-
ctx.beginPath();
|
|
32698
|
-
ctx.roundRect(
|
|
32699
|
-
canvasX - width10 / 2,
|
|
32700
|
-
canvasY - height10 / 2,
|
|
32701
|
-
width10,
|
|
32702
|
-
height10,
|
|
32703
|
-
borderRadius
|
|
32704
|
-
);
|
|
32705
|
-
ctx.fill();
|
|
32668
|
+
ctx.strokeStyle = traceColor;
|
|
32669
|
+
ctx.lineCap = "round";
|
|
32670
|
+
ctx.lineJoin = "round";
|
|
32671
|
+
let currentLineWidth = 0;
|
|
32672
|
+
for (const point of trace.route) {
|
|
32673
|
+
if (!isWireRoutePoint(point) || point.layer !== layer) {
|
|
32674
|
+
if (!firstPoint) ctx.stroke();
|
|
32675
|
+
firstPoint = true;
|
|
32676
|
+
continue;
|
|
32677
|
+
}
|
|
32678
|
+
const pcbX = point.x;
|
|
32679
|
+
const pcbY = point.y;
|
|
32680
|
+
currentLineWidth = point.width * traceTextureResolution;
|
|
32681
|
+
ctx.lineWidth = currentLineWidth;
|
|
32682
|
+
const canvasX = (pcbX - boardOutlineBounds.minX) * traceTextureResolution;
|
|
32683
|
+
const canvasY = (boardOutlineBounds.maxY - pcbY) * traceTextureResolution;
|
|
32684
|
+
if (firstPoint) {
|
|
32685
|
+
ctx.moveTo(canvasX, canvasY);
|
|
32686
|
+
firstPoint = false;
|
|
32706
32687
|
} else {
|
|
32707
|
-
ctx.
|
|
32688
|
+
ctx.lineTo(canvasX, canvasY);
|
|
32708
32689
|
}
|
|
32709
|
-
}
|
|
32710
|
-
|
|
32711
|
-
ctx.
|
|
32712
|
-
ctx.arc(canvasX, canvasY, radius, 0, 2 * Math.PI);
|
|
32713
|
-
ctx.fill();
|
|
32714
|
-
} else if (pad2.shape === "pill") {
|
|
32715
|
-
const width10 = pad2.width * traceTextureResolution;
|
|
32716
|
-
const height10 = pad2.height * traceTextureResolution;
|
|
32717
|
-
const rawRadius = extractRectBorderRadius(pad2);
|
|
32718
|
-
const borderRadius = clampRectBorderRadius(
|
|
32719
|
-
pad2.width,
|
|
32720
|
-
pad2.height,
|
|
32721
|
-
rawRadius
|
|
32722
|
-
) * traceTextureResolution;
|
|
32723
|
-
ctx.beginPath();
|
|
32724
|
-
ctx.roundRect(
|
|
32725
|
-
canvasX - width10 / 2,
|
|
32726
|
-
canvasY - height10 / 2,
|
|
32727
|
-
width10,
|
|
32728
|
-
height10,
|
|
32729
|
-
borderRadius
|
|
32730
|
-
);
|
|
32731
|
-
ctx.fill();
|
|
32732
|
-
} else if (pad2.shape === "rotated_rect") {
|
|
32733
|
-
const width10 = pad2.width * traceTextureResolution;
|
|
32734
|
-
const height10 = pad2.height * traceTextureResolution;
|
|
32735
|
-
const rawRadius = extractRectBorderRadius(pad2);
|
|
32736
|
-
const borderRadius = clampRectBorderRadius(
|
|
32737
|
-
pad2.width,
|
|
32738
|
-
pad2.height,
|
|
32739
|
-
rawRadius
|
|
32740
|
-
) * traceTextureResolution;
|
|
32741
|
-
const ccwRotation = pad2.ccw_rotation || 0;
|
|
32742
|
-
const rotation = -ccwRotation * (Math.PI / 180);
|
|
32743
|
-
ctx.save();
|
|
32744
|
-
ctx.translate(canvasX, canvasY);
|
|
32745
|
-
ctx.rotate(rotation);
|
|
32746
|
-
ctx.beginPath();
|
|
32747
|
-
ctx.roundRect(-width10 / 2, -height10 / 2, width10, height10, borderRadius);
|
|
32748
|
-
ctx.fill();
|
|
32749
|
-
ctx.restore();
|
|
32690
|
+
}
|
|
32691
|
+
if (!firstPoint) {
|
|
32692
|
+
ctx.stroke();
|
|
32750
32693
|
}
|
|
32751
32694
|
});
|
|
32752
|
-
|
|
32753
|
-
|
|
32754
|
-
|
|
32755
|
-
const
|
|
32695
|
+
ctx.globalCompositeOperation = "destination-out";
|
|
32696
|
+
ctx.fillStyle = "black";
|
|
32697
|
+
allPcbVias.forEach((via) => {
|
|
32698
|
+
const canvasX = (via.x - boardOutlineBounds.minX) * traceTextureResolution;
|
|
32699
|
+
const canvasY = (boardOutlineBounds.maxY - via.y) * traceTextureResolution;
|
|
32756
32700
|
const canvasRadius = via.outer_diameter / 2 * traceTextureResolution;
|
|
32757
32701
|
ctx.beginPath();
|
|
32758
|
-
ctx.arc(canvasX, canvasY, canvasRadius, 0, 2 * Math.PI);
|
|
32702
|
+
ctx.arc(canvasX, canvasY, canvasRadius, 0, 2 * Math.PI, false);
|
|
32759
32703
|
ctx.fill();
|
|
32760
32704
|
});
|
|
32761
|
-
|
|
32762
|
-
|
|
32763
|
-
|
|
32764
|
-
|
|
32765
|
-
|
|
32766
|
-
const canvasX = canvasXFromPcb(x);
|
|
32767
|
-
const canvasY = canvasYFromPcb(y);
|
|
32768
|
-
if (hole.shape === "circle") {
|
|
32769
|
-
const outerDiameter = hole.outer_diameter;
|
|
32770
|
-
const canvasRadius = outerDiameter / 2 * traceTextureResolution;
|
|
32705
|
+
allPcbPlatedHoles.forEach((ph) => {
|
|
32706
|
+
if (ph.layers.includes(layer) && ph.shape === "circle") {
|
|
32707
|
+
const canvasX = (ph.x - boardOutlineBounds.minX) * traceTextureResolution;
|
|
32708
|
+
const canvasY = (boardOutlineBounds.maxY - ph.y) * traceTextureResolution;
|
|
32709
|
+
const canvasRadius = ph.outer_diameter / 2 * traceTextureResolution;
|
|
32771
32710
|
ctx.beginPath();
|
|
32772
|
-
ctx.arc(canvasX, canvasY, canvasRadius, 0, 2 * Math.PI);
|
|
32711
|
+
ctx.arc(canvasX, canvasY, canvasRadius, 0, 2 * Math.PI, false);
|
|
32773
32712
|
ctx.fill();
|
|
32774
|
-
} else if (
|
|
32775
|
-
const
|
|
32776
|
-
const
|
|
32777
|
-
const
|
|
32778
|
-
const
|
|
32779
|
-
const
|
|
32780
|
-
|
|
32781
|
-
|
|
32782
|
-
ctx.translate(canvasX, canvasY);
|
|
32783
|
-
ctx.rotate(rotation * Math.PI / 180);
|
|
32784
|
-
ctx.beginPath();
|
|
32785
|
-
ctx.roundRect(-width10 / 2, -height10 / 2, width10, height10, radius);
|
|
32786
|
-
ctx.fill();
|
|
32787
|
-
ctx.restore();
|
|
32788
|
-
} else {
|
|
32789
|
-
ctx.beginPath();
|
|
32790
|
-
ctx.roundRect(
|
|
32791
|
-
canvasX - width10 / 2,
|
|
32792
|
-
canvasY - height10 / 2,
|
|
32793
|
-
width10,
|
|
32794
|
-
height10,
|
|
32795
|
-
radius
|
|
32796
|
-
);
|
|
32797
|
-
ctx.fill();
|
|
32798
|
-
}
|
|
32799
|
-
} else if (hole.shape === "oval") {
|
|
32800
|
-
const width10 = (hole.outer_width ?? hole.outer_diameter ?? hole.hole_width) * traceTextureResolution;
|
|
32801
|
-
const height10 = (hole.outer_height ?? hole.outer_diameter ?? hole.hole_height) * traceTextureResolution;
|
|
32802
|
-
const radiusX = width10 / 2;
|
|
32803
|
-
const radiusY = height10 / 2;
|
|
32804
|
-
const ccwRotationDeg = hole.ccw_rotation || 0;
|
|
32805
|
-
const rotation = -ccwRotationDeg;
|
|
32806
|
-
if (rotation) {
|
|
32713
|
+
} else if (ph.layers.includes(layer) && ph.shape === "rotated_pill_hole_with_rect_pad") {
|
|
32714
|
+
const canvasX = (ph.x - boardOutlineBounds.minX) * traceTextureResolution;
|
|
32715
|
+
const canvasY = (boardOutlineBounds.maxY - ph.y) * traceTextureResolution;
|
|
32716
|
+
const padWidth = (ph.rect_pad_width ?? ph.hole_width ?? 0) * traceTextureResolution;
|
|
32717
|
+
const padHeight = (ph.rect_pad_height ?? ph.hole_height ?? 0) * traceTextureResolution;
|
|
32718
|
+
const rectCcwRotationDeg = ph.rect_ccw_rotation || 0;
|
|
32719
|
+
const rectRotation = -rectCcwRotationDeg;
|
|
32720
|
+
if (rectRotation) {
|
|
32807
32721
|
ctx.save();
|
|
32808
32722
|
ctx.translate(canvasX, canvasY);
|
|
32809
|
-
ctx.rotate(
|
|
32723
|
+
ctx.rotate(rectRotation * Math.PI / 180);
|
|
32810
32724
|
ctx.beginPath();
|
|
32811
|
-
ctx.
|
|
32725
|
+
ctx.rect(-padWidth / 2, -padHeight / 2, padWidth, padHeight);
|
|
32812
32726
|
ctx.fill();
|
|
32813
32727
|
ctx.restore();
|
|
32814
32728
|
} else {
|
|
32815
32729
|
ctx.beginPath();
|
|
32816
|
-
ctx.
|
|
32817
|
-
ctx.fill();
|
|
32818
|
-
}
|
|
32819
|
-
} else if (hole.shape === "hole_with_polygon_pad") {
|
|
32820
|
-
const holeShape = hole.hole_shape || "circle";
|
|
32821
|
-
const holeOffsetX = hole.hole_offset_x || 0;
|
|
32822
|
-
const holeOffsetY = hole.hole_offset_y || 0;
|
|
32823
|
-
const adjustedCanvasX = canvasXFromPcb(hole.x + holeOffsetX);
|
|
32824
|
-
const adjustedCanvasY = canvasYFromPcb(hole.y + holeOffsetY);
|
|
32825
|
-
if (holeShape === "pill" || holeShape === "rotated_pill") {
|
|
32826
|
-
const width10 = (hole.outer_width ?? hole.outer_diameter ?? hole.hole_width) * traceTextureResolution;
|
|
32827
|
-
const height10 = (hole.outer_height ?? hole.outer_diameter ?? hole.hole_height) * traceTextureResolution;
|
|
32828
|
-
const radius = Math.min(width10, height10) / 2;
|
|
32829
|
-
const ccwRotationDeg = hole.ccw_rotation || 0;
|
|
32830
|
-
const rotation = -ccwRotationDeg;
|
|
32831
|
-
if (rotation) {
|
|
32832
|
-
ctx.save();
|
|
32833
|
-
ctx.translate(adjustedCanvasX, adjustedCanvasY);
|
|
32834
|
-
ctx.rotate(rotation * Math.PI / 180);
|
|
32835
|
-
ctx.beginPath();
|
|
32836
|
-
ctx.roundRect(-width10 / 2, -height10 / 2, width10, height10, radius);
|
|
32837
|
-
ctx.fill();
|
|
32838
|
-
ctx.restore();
|
|
32839
|
-
} else {
|
|
32840
|
-
ctx.beginPath();
|
|
32841
|
-
ctx.roundRect(
|
|
32842
|
-
adjustedCanvasX - width10 / 2,
|
|
32843
|
-
adjustedCanvasY - height10 / 2,
|
|
32844
|
-
width10,
|
|
32845
|
-
height10,
|
|
32846
|
-
radius
|
|
32847
|
-
);
|
|
32848
|
-
ctx.fill();
|
|
32849
|
-
}
|
|
32850
|
-
} else if (holeShape === "oval") {
|
|
32851
|
-
const width10 = (hole.outer_width ?? hole.outer_diameter ?? hole.hole_width) * traceTextureResolution;
|
|
32852
|
-
const height10 = (hole.outer_height ?? hole.outer_diameter ?? hole.hole_height) * traceTextureResolution;
|
|
32853
|
-
const radiusX = width10 / 2;
|
|
32854
|
-
const radiusY = height10 / 2;
|
|
32855
|
-
const ccwRotationDeg = hole.ccw_rotation || 0;
|
|
32856
|
-
const rotation = -ccwRotationDeg;
|
|
32857
|
-
if (rotation) {
|
|
32858
|
-
ctx.save();
|
|
32859
|
-
ctx.translate(adjustedCanvasX, adjustedCanvasY);
|
|
32860
|
-
ctx.rotate(rotation * Math.PI / 180);
|
|
32861
|
-
ctx.beginPath();
|
|
32862
|
-
ctx.ellipse(0, 0, radiusX, radiusY, 0, 0, 2 * Math.PI);
|
|
32863
|
-
ctx.fill();
|
|
32864
|
-
ctx.restore();
|
|
32865
|
-
} else {
|
|
32866
|
-
ctx.beginPath();
|
|
32867
|
-
ctx.ellipse(
|
|
32868
|
-
adjustedCanvasX,
|
|
32869
|
-
adjustedCanvasY,
|
|
32870
|
-
radiusX,
|
|
32871
|
-
radiusY,
|
|
32872
|
-
0,
|
|
32873
|
-
0,
|
|
32874
|
-
2 * Math.PI
|
|
32875
|
-
);
|
|
32876
|
-
ctx.fill();
|
|
32877
|
-
}
|
|
32878
|
-
} else if (holeShape === "circle") {
|
|
32879
|
-
const outerDiameter = (hole.outer_diameter ?? hole.hole_diameter ?? 0) * traceTextureResolution;
|
|
32880
|
-
const canvasRadius = outerDiameter / 2;
|
|
32881
|
-
ctx.beginPath();
|
|
32882
|
-
ctx.arc(adjustedCanvasX, adjustedCanvasY, canvasRadius, 0, 2 * Math.PI);
|
|
32883
|
-
ctx.fill();
|
|
32884
|
-
}
|
|
32885
|
-
if (hole.pad_outline && hole.pad_outline.length >= 3) {
|
|
32886
|
-
ctx.beginPath();
|
|
32887
|
-
hole.pad_outline.forEach(
|
|
32888
|
-
(point, index2) => {
|
|
32889
|
-
const px = canvasXFromPcb(hole.x + point.x);
|
|
32890
|
-
const py = canvasYFromPcb(hole.y + point.y);
|
|
32891
|
-
if (index2 === 0) {
|
|
32892
|
-
ctx.moveTo(px, py);
|
|
32893
|
-
} else {
|
|
32894
|
-
ctx.lineTo(px, py);
|
|
32895
|
-
}
|
|
32896
|
-
}
|
|
32897
|
-
);
|
|
32898
|
-
ctx.closePath();
|
|
32899
|
-
ctx.fill();
|
|
32900
|
-
}
|
|
32901
|
-
} else if (hole.shape === "circular_hole_with_rect_pad") {
|
|
32902
|
-
const padWidth = (hole.rect_pad_width ?? hole.hole_diameter ?? 0) * traceTextureResolution;
|
|
32903
|
-
const padHeight = (hole.rect_pad_height ?? hole.hole_diameter ?? 0) * traceTextureResolution;
|
|
32904
|
-
const rawRadius = extractRectBorderRadius(hole);
|
|
32905
|
-
const borderRadius = clampRectBorderRadius(
|
|
32906
|
-
hole.rect_pad_width ?? hole.hole_diameter ?? 0,
|
|
32907
|
-
hole.rect_pad_height ?? hole.hole_diameter ?? 0,
|
|
32908
|
-
rawRadius
|
|
32909
|
-
) * traceTextureResolution;
|
|
32910
|
-
if (borderRadius > 0) {
|
|
32911
|
-
ctx.beginPath();
|
|
32912
|
-
ctx.roundRect(
|
|
32913
|
-
canvasX - padWidth / 2,
|
|
32914
|
-
canvasY - padHeight / 2,
|
|
32915
|
-
padWidth,
|
|
32916
|
-
padHeight,
|
|
32917
|
-
borderRadius
|
|
32918
|
-
);
|
|
32919
|
-
ctx.fill();
|
|
32920
|
-
} else {
|
|
32921
|
-
ctx.fillRect(
|
|
32730
|
+
ctx.rect(
|
|
32922
32731
|
canvasX - padWidth / 2,
|
|
32923
32732
|
canvasY - padHeight / 2,
|
|
32924
32733
|
padWidth,
|
|
32925
32734
|
padHeight
|
|
32926
32735
|
);
|
|
32927
|
-
|
|
32928
|
-
} else if (hole.shape === "pill_hole_with_rect_pad") {
|
|
32929
|
-
const padWidth = (hole.rect_pad_width ?? hole.hole_width ?? 0) * traceTextureResolution;
|
|
32930
|
-
const padHeight = (hole.rect_pad_height ?? hole.hole_height ?? 0) * traceTextureResolution;
|
|
32931
|
-
const rawRadius = extractRectBorderRadius(hole);
|
|
32932
|
-
const borderRadius = clampRectBorderRadius(
|
|
32933
|
-
hole.rect_pad_width ?? hole.hole_width ?? 0,
|
|
32934
|
-
hole.rect_pad_height ?? hole.hole_height ?? 0,
|
|
32935
|
-
rawRadius
|
|
32936
|
-
) * traceTextureResolution;
|
|
32937
|
-
const ccwRotationDeg = hole.ccw_rotation || 0;
|
|
32938
|
-
const rotation = -ccwRotationDeg;
|
|
32939
|
-
if (rotation) {
|
|
32940
|
-
ctx.save();
|
|
32941
|
-
ctx.translate(canvasX, canvasY);
|
|
32942
|
-
ctx.rotate(rotation * Math.PI / 180);
|
|
32943
|
-
ctx.beginPath();
|
|
32944
|
-
ctx.roundRect(
|
|
32945
|
-
-padWidth / 2,
|
|
32946
|
-
-padHeight / 2,
|
|
32947
|
-
padWidth,
|
|
32948
|
-
padHeight,
|
|
32949
|
-
borderRadius
|
|
32950
|
-
);
|
|
32951
|
-
ctx.fill();
|
|
32952
|
-
ctx.restore();
|
|
32953
|
-
} else {
|
|
32954
|
-
ctx.beginPath();
|
|
32955
|
-
ctx.roundRect(
|
|
32956
|
-
canvasX - padWidth / 2,
|
|
32957
|
-
canvasY - padHeight / 2,
|
|
32958
|
-
padWidth,
|
|
32959
|
-
padHeight,
|
|
32960
|
-
borderRadius
|
|
32961
|
-
);
|
|
32962
|
-
ctx.fill();
|
|
32963
|
-
}
|
|
32964
|
-
} else if (hole.shape === "rotated_pill_hole_with_rect_pad") {
|
|
32965
|
-
const padWidth = (hole.rect_pad_width ?? hole.hole_width ?? 0) * traceTextureResolution;
|
|
32966
|
-
const padHeight = (hole.rect_pad_height ?? hole.hole_height ?? 0) * traceTextureResolution;
|
|
32967
|
-
const rawRadius = extractRectBorderRadius(hole);
|
|
32968
|
-
const borderRadius = clampRectBorderRadius(
|
|
32969
|
-
hole.rect_pad_width ?? hole.hole_width ?? 0,
|
|
32970
|
-
hole.rect_pad_height ?? hole.hole_height ?? 0,
|
|
32971
|
-
rawRadius
|
|
32972
|
-
) * traceTextureResolution;
|
|
32973
|
-
const rectCcwRotationDeg = hole.rect_ccw_rotation || 0;
|
|
32974
|
-
const rectRotation = -rectCcwRotationDeg;
|
|
32975
|
-
if (rectRotation) {
|
|
32976
|
-
ctx.save();
|
|
32977
|
-
ctx.translate(canvasX, canvasY);
|
|
32978
|
-
ctx.rotate(rectRotation * Math.PI / 180);
|
|
32979
|
-
ctx.beginPath();
|
|
32980
|
-
ctx.roundRect(
|
|
32981
|
-
-padWidth / 2,
|
|
32982
|
-
-padHeight / 2,
|
|
32983
|
-
padWidth,
|
|
32984
|
-
padHeight,
|
|
32985
|
-
borderRadius
|
|
32986
|
-
);
|
|
32987
|
-
ctx.fill();
|
|
32988
|
-
ctx.restore();
|
|
32989
|
-
} else {
|
|
32990
|
-
ctx.beginPath();
|
|
32991
|
-
ctx.roundRect(
|
|
32992
|
-
canvasX - padWidth / 2,
|
|
32993
|
-
canvasY - padHeight / 2,
|
|
32994
|
-
padWidth,
|
|
32995
|
-
padHeight,
|
|
32996
|
-
borderRadius
|
|
32997
|
-
);
|
|
32998
|
-
ctx.fill();
|
|
32736
|
+
ctx.fill();
|
|
32999
32737
|
}
|
|
33000
32738
|
}
|
|
33001
32739
|
});
|
|
33002
|
-
const pcbHoles = su8(circuitJson).pcb_hole.list();
|
|
33003
|
-
pcbHoles.forEach((hole) => {
|
|
33004
|
-
const x = hole.x;
|
|
33005
|
-
const y = hole.y;
|
|
33006
|
-
const canvasX = canvasXFromPcb(x);
|
|
33007
|
-
const canvasY = canvasYFromPcb(y);
|
|
33008
|
-
const holeShape = hole.hole_shape;
|
|
33009
|
-
if (holeShape === "circle" && typeof hole.hole_diameter === "number") {
|
|
33010
|
-
const canvasRadius = hole.hole_diameter / 2 * traceTextureResolution;
|
|
33011
|
-
ctx.beginPath();
|
|
33012
|
-
ctx.arc(canvasX, canvasY, canvasRadius, 0, 2 * Math.PI);
|
|
33013
|
-
ctx.fill();
|
|
33014
|
-
} else if (holeShape === "pill" && typeof hole.hole_width === "number" && typeof hole.hole_height === "number") {
|
|
33015
|
-
const width10 = hole.hole_width * traceTextureResolution;
|
|
33016
|
-
const height10 = hole.hole_height * traceTextureResolution;
|
|
33017
|
-
const radius = Math.min(width10, height10) / 2;
|
|
33018
|
-
ctx.beginPath();
|
|
33019
|
-
ctx.roundRect(
|
|
33020
|
-
canvasX - width10 / 2,
|
|
33021
|
-
canvasY - height10 / 2,
|
|
33022
|
-
width10,
|
|
33023
|
-
height10,
|
|
33024
|
-
radius
|
|
33025
|
-
);
|
|
33026
|
-
ctx.fill();
|
|
33027
|
-
} else if (holeShape === "rotated_pill" && typeof hole.hole_width === "number" && typeof hole.hole_height === "number") {
|
|
33028
|
-
const width10 = hole.hole_width * traceTextureResolution;
|
|
33029
|
-
const height10 = hole.hole_height * traceTextureResolution;
|
|
33030
|
-
const radius = Math.min(width10, height10) / 2;
|
|
33031
|
-
const ccwRotationDeg = hole.ccw_rotation || 0;
|
|
33032
|
-
const rotation = -ccwRotationDeg * (Math.PI / 180);
|
|
33033
|
-
if (rotation) {
|
|
33034
|
-
ctx.save();
|
|
33035
|
-
ctx.translate(canvasX, canvasY);
|
|
33036
|
-
ctx.rotate(rotation);
|
|
33037
|
-
ctx.beginPath();
|
|
33038
|
-
ctx.roundRect(-width10 / 2, -height10 / 2, width10, height10, radius);
|
|
33039
|
-
ctx.fill();
|
|
33040
|
-
ctx.restore();
|
|
33041
|
-
} else {
|
|
33042
|
-
ctx.beginPath();
|
|
33043
|
-
ctx.roundRect(
|
|
33044
|
-
canvasX - width10 / 2,
|
|
33045
|
-
canvasY - height10 / 2,
|
|
33046
|
-
width10,
|
|
33047
|
-
height10,
|
|
33048
|
-
radius
|
|
33049
|
-
);
|
|
33050
|
-
ctx.fill();
|
|
33051
|
-
}
|
|
33052
|
-
} else if (holeShape === "oval" && typeof hole.hole_width === "number" && typeof hole.hole_height === "number") {
|
|
33053
|
-
const width10 = hole.hole_width * traceTextureResolution;
|
|
33054
|
-
const height10 = hole.hole_height * traceTextureResolution;
|
|
33055
|
-
const radiusX = width10 / 2;
|
|
33056
|
-
const radiusY = height10 / 2;
|
|
33057
|
-
ctx.beginPath();
|
|
33058
|
-
ctx.ellipse(canvasX, canvasY, radiusX, radiusY, 0, 0, 2 * Math.PI);
|
|
33059
|
-
ctx.fill();
|
|
33060
|
-
}
|
|
33061
|
-
});
|
|
33062
|
-
const pcbCopperPours = su8(circuitJson).pcb_copper_pour.list();
|
|
33063
|
-
pcbCopperPours.forEach((pour) => {
|
|
33064
|
-
if (pour.layer !== layer) return;
|
|
33065
|
-
if (pour.covered_with_solder_mask !== false) return;
|
|
33066
|
-
if (pour.shape === "rect") {
|
|
33067
|
-
const centerX = canvasXFromPcb(pour.center.x);
|
|
33068
|
-
const centerY = canvasYFromPcb(pour.center.y);
|
|
33069
|
-
const width10 = pour.width * traceTextureResolution;
|
|
33070
|
-
const height10 = pour.height * traceTextureResolution;
|
|
33071
|
-
ctx.fillRect(centerX - width10 / 2, centerY - height10 / 2, width10, height10);
|
|
33072
|
-
} else if (pour.shape === "polygon" && pour.points) {
|
|
33073
|
-
ctx.beginPath();
|
|
33074
|
-
pour.points.forEach((point, index2) => {
|
|
33075
|
-
const px = canvasXFromPcb(point.x);
|
|
33076
|
-
const py = canvasYFromPcb(point.y);
|
|
33077
|
-
if (index2 === 0) {
|
|
33078
|
-
ctx.moveTo(px, py);
|
|
33079
|
-
} else {
|
|
33080
|
-
ctx.lineTo(px, py);
|
|
33081
|
-
}
|
|
33082
|
-
});
|
|
33083
|
-
ctx.closePath();
|
|
33084
|
-
ctx.fill();
|
|
33085
|
-
}
|
|
33086
|
-
});
|
|
33087
|
-
const pcbCutouts = su8(circuitJson).pcb_cutout.list();
|
|
33088
|
-
pcbCutouts.forEach((cutout) => {
|
|
33089
|
-
switch (cutout.shape) {
|
|
33090
|
-
case "rect": {
|
|
33091
|
-
const canvasX = canvasXFromPcb(cutout.center.x);
|
|
33092
|
-
const canvasY = canvasYFromPcb(cutout.center.y);
|
|
33093
|
-
const width10 = cutout.width * traceTextureResolution;
|
|
33094
|
-
const height10 = cutout.height * traceTextureResolution;
|
|
33095
|
-
const rectCornerRadius = extractRectBorderRadius(cutout);
|
|
33096
|
-
const borderRadius = clampRectBorderRadius(
|
|
33097
|
-
cutout.width,
|
|
33098
|
-
cutout.height,
|
|
33099
|
-
rectCornerRadius
|
|
33100
|
-
);
|
|
33101
|
-
if (cutout.rotation) {
|
|
33102
|
-
ctx.save();
|
|
33103
|
-
ctx.translate(canvasX, canvasY);
|
|
33104
|
-
const rotation = -cutout.rotation * (Math.PI / 180);
|
|
33105
|
-
ctx.rotate(rotation);
|
|
33106
|
-
if (borderRadius > 0) {
|
|
33107
|
-
ctx.beginPath();
|
|
33108
|
-
ctx.roundRect(
|
|
33109
|
-
-width10 / 2,
|
|
33110
|
-
-height10 / 2,
|
|
33111
|
-
width10,
|
|
33112
|
-
height10,
|
|
33113
|
-
borderRadius * traceTextureResolution
|
|
33114
|
-
);
|
|
33115
|
-
ctx.fill();
|
|
33116
|
-
} else {
|
|
33117
|
-
ctx.fillRect(-width10 / 2, -height10 / 2, width10, height10);
|
|
33118
|
-
}
|
|
33119
|
-
ctx.restore();
|
|
33120
|
-
} else {
|
|
33121
|
-
if (borderRadius > 0) {
|
|
33122
|
-
ctx.beginPath();
|
|
33123
|
-
ctx.roundRect(
|
|
33124
|
-
canvasX - width10 / 2,
|
|
33125
|
-
canvasY - height10 / 2,
|
|
33126
|
-
width10,
|
|
33127
|
-
height10,
|
|
33128
|
-
borderRadius * traceTextureResolution
|
|
33129
|
-
);
|
|
33130
|
-
ctx.fill();
|
|
33131
|
-
} else {
|
|
33132
|
-
ctx.fillRect(
|
|
33133
|
-
canvasX - width10 / 2,
|
|
33134
|
-
canvasY - height10 / 2,
|
|
33135
|
-
width10,
|
|
33136
|
-
height10
|
|
33137
|
-
);
|
|
33138
|
-
}
|
|
33139
|
-
}
|
|
33140
|
-
break;
|
|
33141
|
-
}
|
|
33142
|
-
case "circle": {
|
|
33143
|
-
const canvasX = canvasXFromPcb(cutout.center.x);
|
|
33144
|
-
const canvasY = canvasYFromPcb(cutout.center.y);
|
|
33145
|
-
const canvasRadius = cutout.radius * traceTextureResolution;
|
|
33146
|
-
ctx.beginPath();
|
|
33147
|
-
ctx.arc(canvasX, canvasY, canvasRadius, 0, 2 * Math.PI);
|
|
33148
|
-
ctx.fill();
|
|
33149
|
-
break;
|
|
33150
|
-
}
|
|
33151
|
-
case "polygon": {
|
|
33152
|
-
if (!cutout.points || cutout.points.length < 3) {
|
|
33153
|
-
console.warn(
|
|
33154
|
-
`PCB Cutout [${cutout.pcb_cutout_id}] polygon has fewer than 3 points, skipping in soldermask texture.`
|
|
33155
|
-
);
|
|
33156
|
-
break;
|
|
33157
|
-
}
|
|
33158
|
-
ctx.beginPath();
|
|
33159
|
-
cutout.points.forEach(
|
|
33160
|
-
(point, index2) => {
|
|
33161
|
-
const px = canvasXFromPcb(point.x);
|
|
33162
|
-
const py = canvasYFromPcb(point.y);
|
|
33163
|
-
if (index2 === 0) {
|
|
33164
|
-
ctx.moveTo(px, py);
|
|
33165
|
-
} else {
|
|
33166
|
-
ctx.lineTo(px, py);
|
|
33167
|
-
}
|
|
33168
|
-
}
|
|
33169
|
-
);
|
|
33170
|
-
ctx.closePath();
|
|
33171
|
-
ctx.fill();
|
|
33172
|
-
break;
|
|
33173
|
-
}
|
|
33174
|
-
default:
|
|
33175
|
-
console.warn(
|
|
33176
|
-
`Unsupported cutout shape: ${cutout.shape} for cutout ${cutout.pcb_cutout_id} in soldermask texture.`
|
|
33177
|
-
);
|
|
33178
|
-
}
|
|
33179
|
-
});
|
|
33180
32740
|
ctx.globalCompositeOperation = "source-over";
|
|
33181
32741
|
const texture = new THREE23.CanvasTexture(canvas);
|
|
33182
32742
|
texture.generateMipmaps = true;
|
|
@@ -33187,131 +32747,8 @@ function createSoldermaskTextureForLayer({
|
|
|
33187
32747
|
return texture;
|
|
33188
32748
|
}
|
|
33189
32749
|
|
|
33190
|
-
// src/utils/trace-texture.ts
|
|
33191
|
-
import * as THREE24 from "three";
|
|
33192
|
-
import { su as su9 } from "@tscircuit/circuit-json-util";
|
|
33193
|
-
function isWireRoutePoint(point) {
|
|
33194
|
-
return point && point.route_type === "wire" && typeof point.layer === "string" && typeof point.width === "number";
|
|
33195
|
-
}
|
|
33196
|
-
function createTraceTextureForLayer({
|
|
33197
|
-
layer,
|
|
33198
|
-
circuitJson,
|
|
33199
|
-
boardData,
|
|
33200
|
-
traceColor,
|
|
33201
|
-
traceTextureResolution
|
|
33202
|
-
}) {
|
|
33203
|
-
const pcbTraces = su9(circuitJson).pcb_trace.list();
|
|
33204
|
-
const allPcbVias = su9(circuitJson).pcb_via.list();
|
|
33205
|
-
const allPcbPlatedHoles = su9(
|
|
33206
|
-
circuitJson
|
|
33207
|
-
).pcb_plated_hole.list();
|
|
33208
|
-
const tracesOnLayer = pcbTraces.filter(
|
|
33209
|
-
(t) => t.route.some((p) => isWireRoutePoint(p) && p.layer === layer)
|
|
33210
|
-
);
|
|
33211
|
-
if (tracesOnLayer.length === 0) return null;
|
|
33212
|
-
const boardOutlineBounds = calculateOutlineBounds(boardData);
|
|
33213
|
-
const canvas = document.createElement("canvas");
|
|
33214
|
-
const canvasWidth = Math.floor(
|
|
33215
|
-
boardOutlineBounds.width * traceTextureResolution
|
|
33216
|
-
);
|
|
33217
|
-
const canvasHeight = Math.floor(
|
|
33218
|
-
boardOutlineBounds.height * traceTextureResolution
|
|
33219
|
-
);
|
|
33220
|
-
canvas.width = canvasWidth;
|
|
33221
|
-
canvas.height = canvasHeight;
|
|
33222
|
-
const ctx = canvas.getContext("2d");
|
|
33223
|
-
if (!ctx) return null;
|
|
33224
|
-
if (layer === "bottom") {
|
|
33225
|
-
ctx.translate(0, canvasHeight);
|
|
33226
|
-
ctx.scale(1, -1);
|
|
33227
|
-
}
|
|
33228
|
-
tracesOnLayer.forEach((trace) => {
|
|
33229
|
-
let firstPoint = true;
|
|
33230
|
-
ctx.beginPath();
|
|
33231
|
-
ctx.strokeStyle = traceColor;
|
|
33232
|
-
ctx.lineCap = "round";
|
|
33233
|
-
ctx.lineJoin = "round";
|
|
33234
|
-
let currentLineWidth = 0;
|
|
33235
|
-
for (const point of trace.route) {
|
|
33236
|
-
if (!isWireRoutePoint(point) || point.layer !== layer) {
|
|
33237
|
-
if (!firstPoint) ctx.stroke();
|
|
33238
|
-
firstPoint = true;
|
|
33239
|
-
continue;
|
|
33240
|
-
}
|
|
33241
|
-
const pcbX = point.x;
|
|
33242
|
-
const pcbY = point.y;
|
|
33243
|
-
currentLineWidth = point.width * traceTextureResolution;
|
|
33244
|
-
ctx.lineWidth = currentLineWidth;
|
|
33245
|
-
const canvasX = (pcbX - boardOutlineBounds.minX) * traceTextureResolution;
|
|
33246
|
-
const canvasY = (boardOutlineBounds.maxY - pcbY) * traceTextureResolution;
|
|
33247
|
-
if (firstPoint) {
|
|
33248
|
-
ctx.moveTo(canvasX, canvasY);
|
|
33249
|
-
firstPoint = false;
|
|
33250
|
-
} else {
|
|
33251
|
-
ctx.lineTo(canvasX, canvasY);
|
|
33252
|
-
}
|
|
33253
|
-
}
|
|
33254
|
-
if (!firstPoint) {
|
|
33255
|
-
ctx.stroke();
|
|
33256
|
-
}
|
|
33257
|
-
});
|
|
33258
|
-
ctx.globalCompositeOperation = "destination-out";
|
|
33259
|
-
ctx.fillStyle = "black";
|
|
33260
|
-
allPcbVias.forEach((via) => {
|
|
33261
|
-
const canvasX = (via.x - boardOutlineBounds.minX) * traceTextureResolution;
|
|
33262
|
-
const canvasY = (boardOutlineBounds.maxY - via.y) * traceTextureResolution;
|
|
33263
|
-
const canvasRadius = via.outer_diameter / 2 * traceTextureResolution;
|
|
33264
|
-
ctx.beginPath();
|
|
33265
|
-
ctx.arc(canvasX, canvasY, canvasRadius, 0, 2 * Math.PI, false);
|
|
33266
|
-
ctx.fill();
|
|
33267
|
-
});
|
|
33268
|
-
allPcbPlatedHoles.forEach((ph) => {
|
|
33269
|
-
if (ph.layers.includes(layer) && ph.shape === "circle") {
|
|
33270
|
-
const canvasX = (ph.x - boardOutlineBounds.minX) * traceTextureResolution;
|
|
33271
|
-
const canvasY = (boardOutlineBounds.maxY - ph.y) * traceTextureResolution;
|
|
33272
|
-
const canvasRadius = ph.outer_diameter / 2 * traceTextureResolution;
|
|
33273
|
-
ctx.beginPath();
|
|
33274
|
-
ctx.arc(canvasX, canvasY, canvasRadius, 0, 2 * Math.PI, false);
|
|
33275
|
-
ctx.fill();
|
|
33276
|
-
} else if (ph.layers.includes(layer) && ph.shape === "rotated_pill_hole_with_rect_pad") {
|
|
33277
|
-
const canvasX = (ph.x - boardOutlineBounds.minX) * traceTextureResolution;
|
|
33278
|
-
const canvasY = (boardOutlineBounds.maxY - ph.y) * traceTextureResolution;
|
|
33279
|
-
const padWidth = (ph.rect_pad_width ?? ph.hole_width ?? 0) * traceTextureResolution;
|
|
33280
|
-
const padHeight = (ph.rect_pad_height ?? ph.hole_height ?? 0) * traceTextureResolution;
|
|
33281
|
-
const rectCcwRotationDeg = ph.rect_ccw_rotation || 0;
|
|
33282
|
-
const rectRotation = -rectCcwRotationDeg;
|
|
33283
|
-
if (rectRotation) {
|
|
33284
|
-
ctx.save();
|
|
33285
|
-
ctx.translate(canvasX, canvasY);
|
|
33286
|
-
ctx.rotate(rectRotation * Math.PI / 180);
|
|
33287
|
-
ctx.beginPath();
|
|
33288
|
-
ctx.rect(-padWidth / 2, -padHeight / 2, padWidth, padHeight);
|
|
33289
|
-
ctx.fill();
|
|
33290
|
-
ctx.restore();
|
|
33291
|
-
} else {
|
|
33292
|
-
ctx.beginPath();
|
|
33293
|
-
ctx.rect(
|
|
33294
|
-
canvasX - padWidth / 2,
|
|
33295
|
-
canvasY - padHeight / 2,
|
|
33296
|
-
padWidth,
|
|
33297
|
-
padHeight
|
|
33298
|
-
);
|
|
33299
|
-
ctx.fill();
|
|
33300
|
-
}
|
|
33301
|
-
}
|
|
33302
|
-
});
|
|
33303
|
-
ctx.globalCompositeOperation = "source-over";
|
|
33304
|
-
const texture = new THREE24.CanvasTexture(canvas);
|
|
33305
|
-
texture.generateMipmaps = true;
|
|
33306
|
-
texture.minFilter = THREE24.LinearMipmapLinearFilter;
|
|
33307
|
-
texture.magFilter = THREE24.LinearFilter;
|
|
33308
|
-
texture.anisotropy = 16;
|
|
33309
|
-
texture.needsUpdate = true;
|
|
33310
|
-
return texture;
|
|
33311
|
-
}
|
|
33312
|
-
|
|
33313
32750
|
// src/textures/create-copper-pour-texture-for-layer.ts
|
|
33314
|
-
import * as
|
|
32751
|
+
import * as THREE24 from "three";
|
|
33315
32752
|
import { CircuitToCanvasDrawer } from "circuit-to-canvas";
|
|
33316
32753
|
|
|
33317
32754
|
// src/geoms/brep-converter.ts
|
|
@@ -33517,6 +32954,190 @@ function createCopperPourTextureForLayer({
|
|
|
33517
32954
|
ctx.fillStyle = copperColor;
|
|
33518
32955
|
drawBrepShape({ ctx, pour, canvasXFromPcb, canvasYFromPcb });
|
|
33519
32956
|
}
|
|
32957
|
+
const texture = new THREE24.CanvasTexture(canvas);
|
|
32958
|
+
texture.generateMipmaps = true;
|
|
32959
|
+
texture.minFilter = THREE24.LinearMipmapLinearFilter;
|
|
32960
|
+
texture.magFilter = THREE24.LinearFilter;
|
|
32961
|
+
texture.anisotropy = 16;
|
|
32962
|
+
texture.needsUpdate = true;
|
|
32963
|
+
return texture;
|
|
32964
|
+
}
|
|
32965
|
+
|
|
32966
|
+
// src/textures/create-soldermask-texture-for-layer.ts
|
|
32967
|
+
import * as THREE25 from "three";
|
|
32968
|
+
|
|
32969
|
+
// src/textures/soldermask/soldermask-drawing.ts
|
|
32970
|
+
import { CircuitToCanvasDrawer as CircuitToCanvasDrawer2 } from "circuit-to-canvas";
|
|
32971
|
+
var toRgb = (colorArr) => {
|
|
32972
|
+
const [r = 0, g = 0, b = 0] = colorArr;
|
|
32973
|
+
return `rgb(${Math.round(r * 255)}, ${Math.round(g * 255)}, ${Math.round(
|
|
32974
|
+
b * 255
|
|
32975
|
+
)})`;
|
|
32976
|
+
};
|
|
32977
|
+
var getSoldermaskPalette = (material) => {
|
|
32978
|
+
const soldermask = toRgb(
|
|
32979
|
+
soldermaskColors[material] ?? colors.fr4SolderMaskGreen
|
|
32980
|
+
);
|
|
32981
|
+
const soldermaskOverCopper = material === "fr1" ? toRgb(colors.fr1TracesWithMaskCopper) : toRgb(colors.fr4TracesWithMaskGreen);
|
|
32982
|
+
return {
|
|
32983
|
+
soldermask,
|
|
32984
|
+
soldermaskOverCopper,
|
|
32985
|
+
copper: toRgb(colors.copper),
|
|
32986
|
+
transparent: "rgba(0,0,0,0)"
|
|
32987
|
+
};
|
|
32988
|
+
};
|
|
32989
|
+
var setDrawerBounds = (drawer, bounds) => {
|
|
32990
|
+
drawer.setCameraBounds({
|
|
32991
|
+
minX: bounds.minX,
|
|
32992
|
+
maxX: bounds.maxX,
|
|
32993
|
+
minY: bounds.minY,
|
|
32994
|
+
maxY: bounds.maxY
|
|
32995
|
+
});
|
|
32996
|
+
};
|
|
32997
|
+
var drawSoldermaskLayer = ({
|
|
32998
|
+
ctx,
|
|
32999
|
+
layer,
|
|
33000
|
+
bounds,
|
|
33001
|
+
elements,
|
|
33002
|
+
boardMaterial
|
|
33003
|
+
}) => {
|
|
33004
|
+
const palette = getSoldermaskPalette(boardMaterial);
|
|
33005
|
+
const copperRenderLayer = layer === "top" ? "top_copper" : "bottom_copper";
|
|
33006
|
+
const drawer = new CircuitToCanvasDrawer2(ctx);
|
|
33007
|
+
drawer.configure({
|
|
33008
|
+
colorOverrides: {
|
|
33009
|
+
copper: {
|
|
33010
|
+
top: palette.transparent,
|
|
33011
|
+
bottom: palette.transparent,
|
|
33012
|
+
inner1: palette.transparent,
|
|
33013
|
+
inner2: palette.transparent,
|
|
33014
|
+
inner3: palette.transparent,
|
|
33015
|
+
inner4: palette.transparent,
|
|
33016
|
+
inner5: palette.transparent,
|
|
33017
|
+
inner6: palette.transparent
|
|
33018
|
+
},
|
|
33019
|
+
drill: palette.transparent,
|
|
33020
|
+
boardOutline: palette.transparent,
|
|
33021
|
+
substrate: palette.transparent,
|
|
33022
|
+
keepout: palette.transparent,
|
|
33023
|
+
fabricationNote: palette.transparent,
|
|
33024
|
+
silkscreen: { top: palette.transparent, bottom: palette.transparent },
|
|
33025
|
+
courtyard: { top: palette.transparent, bottom: palette.transparent },
|
|
33026
|
+
soldermask: { top: palette.soldermask, bottom: palette.soldermask },
|
|
33027
|
+
soldermaskWithCopperUnderneath: {
|
|
33028
|
+
top: palette.soldermaskOverCopper,
|
|
33029
|
+
bottom: palette.soldermaskOverCopper
|
|
33030
|
+
},
|
|
33031
|
+
soldermaskOverCopper: {
|
|
33032
|
+
top: palette.soldermaskOverCopper,
|
|
33033
|
+
bottom: palette.soldermaskOverCopper
|
|
33034
|
+
}
|
|
33035
|
+
}
|
|
33036
|
+
});
|
|
33037
|
+
setDrawerBounds(drawer, bounds);
|
|
33038
|
+
drawer.drawElements(elements, {
|
|
33039
|
+
layers: [copperRenderLayer],
|
|
33040
|
+
drawSoldermask: true,
|
|
33041
|
+
drawSoldermaskTop: layer === "top",
|
|
33042
|
+
drawSoldermaskBottom: layer === "bottom"
|
|
33043
|
+
});
|
|
33044
|
+
const uncoveredPours = elements.filter(
|
|
33045
|
+
(e) => e.type === "pcb_copper_pour" && e.layer === layer && e.covered_with_solder_mask === false
|
|
33046
|
+
);
|
|
33047
|
+
if (uncoveredPours.length > 0) {
|
|
33048
|
+
ctx.save();
|
|
33049
|
+
ctx.globalCompositeOperation = "destination-out";
|
|
33050
|
+
const cutoutDrawer = new CircuitToCanvasDrawer2(ctx);
|
|
33051
|
+
cutoutDrawer.configure({
|
|
33052
|
+
colorOverrides: {
|
|
33053
|
+
copper: {
|
|
33054
|
+
top: palette.copper,
|
|
33055
|
+
bottom: palette.copper,
|
|
33056
|
+
inner1: palette.copper,
|
|
33057
|
+
inner2: palette.copper,
|
|
33058
|
+
inner3: palette.copper,
|
|
33059
|
+
inner4: palette.copper,
|
|
33060
|
+
inner5: palette.copper,
|
|
33061
|
+
inner6: palette.copper
|
|
33062
|
+
}
|
|
33063
|
+
}
|
|
33064
|
+
});
|
|
33065
|
+
setDrawerBounds(cutoutDrawer, bounds);
|
|
33066
|
+
cutoutDrawer.drawElements(uncoveredPours, { layers: [copperRenderLayer] });
|
|
33067
|
+
ctx.restore();
|
|
33068
|
+
}
|
|
33069
|
+
};
|
|
33070
|
+
|
|
33071
|
+
// src/textures/soldermask/soldermask-bounds.ts
|
|
33072
|
+
var boundsFromPanel = (panel) => ({
|
|
33073
|
+
minX: panel.center.x - panel.width / 2,
|
|
33074
|
+
maxX: panel.center.x + panel.width / 2,
|
|
33075
|
+
minY: panel.center.y - panel.height / 2,
|
|
33076
|
+
maxY: panel.center.y + panel.height / 2,
|
|
33077
|
+
width: panel.width,
|
|
33078
|
+
height: panel.height,
|
|
33079
|
+
centerX: panel.center.x,
|
|
33080
|
+
centerY: panel.center.y
|
|
33081
|
+
});
|
|
33082
|
+
var mergeBounds = (a, b) => {
|
|
33083
|
+
const minX = Math.min(a.minX, b.minX);
|
|
33084
|
+
const maxX = Math.max(a.maxX, b.maxX);
|
|
33085
|
+
const minY = Math.min(a.minY, b.minY);
|
|
33086
|
+
const maxY = Math.max(a.maxY, b.maxY);
|
|
33087
|
+
return {
|
|
33088
|
+
minX,
|
|
33089
|
+
maxX,
|
|
33090
|
+
minY,
|
|
33091
|
+
maxY,
|
|
33092
|
+
width: maxX - minX,
|
|
33093
|
+
height: maxY - minY,
|
|
33094
|
+
centerX: (minX + maxX) / 2,
|
|
33095
|
+
centerY: (minY + maxY) / 2
|
|
33096
|
+
};
|
|
33097
|
+
};
|
|
33098
|
+
var getSoldermaskRenderBounds = (circuitJson, boardData) => {
|
|
33099
|
+
const panels = circuitJson.filter(
|
|
33100
|
+
(e) => e.type === "pcb_panel"
|
|
33101
|
+
);
|
|
33102
|
+
const boards = circuitJson.filter(
|
|
33103
|
+
(e) => e.type === "pcb_board"
|
|
33104
|
+
);
|
|
33105
|
+
const activePanel = panels.find((panel) => panel.pcb_panel_id === boardData.pcb_board_id) ?? panels[0];
|
|
33106
|
+
if (activePanel && activePanel.width > 0 && activePanel.height > 0) {
|
|
33107
|
+
return boundsFromPanel(activePanel);
|
|
33108
|
+
}
|
|
33109
|
+
const boardsForBounds = boards.length > 1 ? boards : [boardData];
|
|
33110
|
+
return boardsForBounds.map((board) => calculateOutlineBounds(board)).reduce((acc, bounds) => mergeBounds(acc, bounds));
|
|
33111
|
+
};
|
|
33112
|
+
|
|
33113
|
+
// src/textures/create-soldermask-texture-for-layer.ts
|
|
33114
|
+
function createSoldermaskTextureForLayer({
|
|
33115
|
+
layer,
|
|
33116
|
+
circuitJson,
|
|
33117
|
+
boardData,
|
|
33118
|
+
traceTextureResolution = TRACE_TEXTURE_RESOLUTION
|
|
33119
|
+
}) {
|
|
33120
|
+
const bounds = getSoldermaskRenderBounds(circuitJson, boardData);
|
|
33121
|
+
const canvasWidth = Math.floor(bounds.width * traceTextureResolution);
|
|
33122
|
+
const canvasHeight = Math.floor(bounds.height * traceTextureResolution);
|
|
33123
|
+
if (canvasWidth <= 0 || canvasHeight <= 0) return null;
|
|
33124
|
+
const canvas = document.createElement("canvas");
|
|
33125
|
+
canvas.width = canvasWidth;
|
|
33126
|
+
canvas.height = canvasHeight;
|
|
33127
|
+
const ctx = canvas.getContext("2d");
|
|
33128
|
+
if (!ctx) return null;
|
|
33129
|
+
if (layer === "bottom") {
|
|
33130
|
+
ctx.translate(0, canvasHeight);
|
|
33131
|
+
ctx.scale(1, -1);
|
|
33132
|
+
}
|
|
33133
|
+
const elements = circuitJson.some((e) => e.type === "pcb_board") ? circuitJson : [boardData, ...circuitJson];
|
|
33134
|
+
drawSoldermaskLayer({
|
|
33135
|
+
ctx,
|
|
33136
|
+
layer,
|
|
33137
|
+
bounds,
|
|
33138
|
+
elements,
|
|
33139
|
+
boardMaterial: boardData.material
|
|
33140
|
+
});
|
|
33520
33141
|
const texture = new THREE25.CanvasTexture(canvas);
|
|
33521
33142
|
texture.generateMipmaps = true;
|
|
33522
33143
|
texture.minFilter = THREE25.LinearMipmapLinearFilter;
|
|
@@ -33527,7 +33148,7 @@ function createCopperPourTextureForLayer({
|
|
|
33527
33148
|
}
|
|
33528
33149
|
|
|
33529
33150
|
// src/textures/create-combined-board-textures.ts
|
|
33530
|
-
var
|
|
33151
|
+
var toRgb2 = (colorArr) => {
|
|
33531
33152
|
const [r = 0, g = 0, b = 0] = colorArr;
|
|
33532
33153
|
return `rgb(${Math.round(r * 255)}, ${Math.round(g * 255)}, ${Math.round(
|
|
33533
33154
|
b * 255
|
|
@@ -33550,7 +33171,7 @@ var createCombinedTexture = ({
|
|
|
33550
33171
|
if (canvasWidth <= 0 || canvasHeight <= 0) return null;
|
|
33551
33172
|
const canvas = document.createElement("canvas");
|
|
33552
33173
|
canvas.width = canvasWidth;
|
|
33553
|
-
canvas.height = canvasHeight;
|
|
33174
|
+
canvas.height = canvasHeight + 1;
|
|
33554
33175
|
const ctx = canvas.getContext("2d");
|
|
33555
33176
|
if (!ctx) return null;
|
|
33556
33177
|
textures.forEach((texture) => {
|
|
@@ -33559,9 +33180,10 @@ var createCombinedTexture = ({
|
|
|
33559
33180
|
ctx.drawImage(image, 0, 0, canvasWidth, canvasHeight);
|
|
33560
33181
|
});
|
|
33561
33182
|
const combinedTexture = new THREE26.CanvasTexture(canvas);
|
|
33562
|
-
combinedTexture.generateMipmaps =
|
|
33563
|
-
combinedTexture.minFilter = THREE26.
|
|
33183
|
+
combinedTexture.generateMipmaps = false;
|
|
33184
|
+
combinedTexture.minFilter = THREE26.LinearFilter;
|
|
33564
33185
|
combinedTexture.magFilter = THREE26.LinearFilter;
|
|
33186
|
+
combinedTexture.premultiplyAlpha = true;
|
|
33565
33187
|
combinedTexture.anisotropy = 16;
|
|
33566
33188
|
combinedTexture.needsUpdate = true;
|
|
33567
33189
|
return combinedTexture;
|
|
@@ -33572,13 +33194,10 @@ function createCombinedBoardTextures({
|
|
|
33572
33194
|
traceTextureResolution,
|
|
33573
33195
|
visibility
|
|
33574
33196
|
}) {
|
|
33575
|
-
const
|
|
33576
|
-
|
|
33577
|
-
);
|
|
33578
|
-
const traceColorWithMask = toRgb(colors.fr4TracesWithMaskGreen);
|
|
33579
|
-
const traceColorWithoutMask = toRgb(colors.fr4TracesWithoutMaskTan);
|
|
33197
|
+
const traceColorWithMask = toRgb2(colors.fr4TracesWithMaskGreen);
|
|
33198
|
+
const traceColorWithoutMask = toRgb2(colors.fr4TracesWithoutMaskTan);
|
|
33580
33199
|
const silkscreenColor = "rgb(255,255,255)";
|
|
33581
|
-
const copperColor =
|
|
33200
|
+
const copperColor = toRgb2(colors.copper);
|
|
33582
33201
|
const showBoardBody = visibility?.boardBody ?? true;
|
|
33583
33202
|
const buildForLayer = (layer) => {
|
|
33584
33203
|
const showMask = (layer === "top" ? visibility?.topMask : visibility?.bottomMask) ?? true;
|
|
@@ -33588,7 +33207,6 @@ function createCombinedBoardTextures({
|
|
|
33588
33207
|
layer,
|
|
33589
33208
|
circuitJson,
|
|
33590
33209
|
boardData,
|
|
33591
|
-
soldermaskColor,
|
|
33592
33210
|
traceTextureResolution
|
|
33593
33211
|
}) : null;
|
|
33594
33212
|
const traceTexture = showCopper ? createTraceTextureForLayer({
|
|
@@ -33633,11 +33251,11 @@ function createCombinedBoardTextures({
|
|
|
33633
33251
|
}) : null;
|
|
33634
33252
|
return createCombinedTexture({
|
|
33635
33253
|
textures: [
|
|
33636
|
-
soldermaskTexture,
|
|
33637
33254
|
copperPourTexture,
|
|
33638
33255
|
traceTexture,
|
|
33639
33256
|
copperTextTexture,
|
|
33640
33257
|
padTexture,
|
|
33258
|
+
soldermaskTexture,
|
|
33641
33259
|
silkscreenTexture,
|
|
33642
33260
|
panelOutlineTexture
|
|
33643
33261
|
],
|
|
@@ -33672,8 +33290,9 @@ function createTexturePlane(config, boardData) {
|
|
|
33672
33290
|
const material = new THREE27.MeshBasicMaterial({
|
|
33673
33291
|
map: texture,
|
|
33674
33292
|
transparent: true,
|
|
33293
|
+
alphaTest: 0.08,
|
|
33675
33294
|
side: THREE27.DoubleSide,
|
|
33676
|
-
depthWrite:
|
|
33295
|
+
depthWrite: true,
|
|
33677
33296
|
polygonOffset: usePolygonOffset,
|
|
33678
33297
|
polygonOffsetFactor: usePolygonOffset ? -4 : 0,
|
|
33679
33298
|
// Increased for better z-fighting prevention
|
|
@@ -33696,10 +33315,11 @@ function createTexturePlane(config, boardData) {
|
|
|
33696
33315
|
function createTextureMeshes(textures, boardData, pcbThickness, isFaux = false) {
|
|
33697
33316
|
const meshes = [];
|
|
33698
33317
|
if (!textures || !boardData || pcbThickness === null) return meshes;
|
|
33318
|
+
const SURFACE_OFFSET = 5e-3;
|
|
33699
33319
|
const topBoardMesh = createTexturePlane(
|
|
33700
33320
|
{
|
|
33701
33321
|
texture: textures.topBoard,
|
|
33702
|
-
yOffset: pcbThickness / 2 +
|
|
33322
|
+
yOffset: pcbThickness / 2 + SURFACE_OFFSET,
|
|
33703
33323
|
isBottomLayer: false,
|
|
33704
33324
|
usePolygonOffset: true,
|
|
33705
33325
|
renderOrder: 1,
|
|
@@ -33711,7 +33331,7 @@ function createTextureMeshes(textures, boardData, pcbThickness, isFaux = false)
|
|
|
33711
33331
|
const bottomBoardMesh = createTexturePlane(
|
|
33712
33332
|
{
|
|
33713
33333
|
texture: textures.bottomBoard,
|
|
33714
|
-
yOffset: -pcbThickness / 2 -
|
|
33334
|
+
yOffset: -pcbThickness / 2 - SURFACE_OFFSET,
|
|
33715
33335
|
isBottomLayer: true,
|
|
33716
33336
|
usePolygonOffset: true,
|
|
33717
33337
|
renderOrder: 1,
|
|
@@ -33765,7 +33385,7 @@ function JscadBoardTextures({
|
|
|
33765
33385
|
const panels = circuitJson.filter(
|
|
33766
33386
|
(e) => e.type === "pcb_panel"
|
|
33767
33387
|
);
|
|
33768
|
-
const boards =
|
|
33388
|
+
const boards = su9(circuitJson).pcb_board.list();
|
|
33769
33389
|
if (panels.length > 0) {
|
|
33770
33390
|
const panel = panels[0];
|
|
33771
33391
|
const firstBoardInPanel = boards.find(
|
|
@@ -33827,7 +33447,7 @@ function JscadBoardTextures({
|
|
|
33827
33447
|
}
|
|
33828
33448
|
material.dispose();
|
|
33829
33449
|
};
|
|
33830
|
-
const createTexturePlane2 = (texture, zOffset, isBottomLayer, name, usePolygonOffset = false, depthWrite =
|
|
33450
|
+
const createTexturePlane2 = (texture, zOffset, isBottomLayer, name, usePolygonOffset = false, depthWrite = true, renderOrder = 1) => {
|
|
33831
33451
|
if (!texture) return null;
|
|
33832
33452
|
const boardOutlineBounds = calculateOutlineBounds(boardData);
|
|
33833
33453
|
const planeGeom = new THREE28.PlaneGeometry(
|
|
@@ -33837,9 +33457,11 @@ function JscadBoardTextures({
|
|
|
33837
33457
|
const material = new THREE28.MeshBasicMaterial({
|
|
33838
33458
|
map: texture,
|
|
33839
33459
|
transparent: true,
|
|
33460
|
+
alphaTest: 0.08,
|
|
33840
33461
|
side: THREE28.DoubleSide,
|
|
33841
33462
|
depthWrite,
|
|
33842
33463
|
polygonOffset: usePolygonOffset,
|
|
33464
|
+
polygonOffsetFactor: usePolygonOffset ? -4 : 0,
|
|
33843
33465
|
polygonOffsetUnits: usePolygonOffset ? -4 : 0,
|
|
33844
33466
|
opacity: isFaux ? FAUX_BOARD_OPACITY : 1
|
|
33845
33467
|
});
|
|
@@ -33857,7 +33479,7 @@ function JscadBoardTextures({
|
|
|
33857
33479
|
mesh.frustumCulled = false;
|
|
33858
33480
|
return mesh;
|
|
33859
33481
|
};
|
|
33860
|
-
const SURFACE_OFFSET =
|
|
33482
|
+
const SURFACE_OFFSET = 5e-3;
|
|
33861
33483
|
const topBoardMesh = createTexturePlane2(
|
|
33862
33484
|
textures.topBoard,
|
|
33863
33485
|
pcbThickness / 2 + SURFACE_OFFSET,
|
|
@@ -33900,16 +33522,16 @@ function JscadBoardTextures({
|
|
|
33900
33522
|
}
|
|
33901
33523
|
|
|
33902
33524
|
// src/utils/preprocess-circuit-json.ts
|
|
33903
|
-
import { su as
|
|
33525
|
+
import { su as su11 } from "@tscircuit/circuit-json-util";
|
|
33904
33526
|
|
|
33905
33527
|
// src/utils/create-faux-board.ts
|
|
33906
|
-
import { su as
|
|
33528
|
+
import { su as su10, getBoundsOfPcbElements } from "@tscircuit/circuit-json-util";
|
|
33907
33529
|
function createFauxBoard(circuitJson) {
|
|
33908
|
-
const cadComponents =
|
|
33909
|
-
const pads =
|
|
33910
|
-
const holes =
|
|
33911
|
-
const platedHoles =
|
|
33912
|
-
const vias =
|
|
33530
|
+
const cadComponents = su10(circuitJson).cad_component.list();
|
|
33531
|
+
const pads = su10(circuitJson).pcb_smtpad.list();
|
|
33532
|
+
const holes = su10(circuitJson).pcb_hole.list();
|
|
33533
|
+
const platedHoles = su10(circuitJson).pcb_plated_hole.list();
|
|
33534
|
+
const vias = su10(circuitJson).pcb_via.list();
|
|
33913
33535
|
if (cadComponents.length === 0 && pads.length === 0 && holes.length === 0 && platedHoles.length === 0 && vias.length === 0) {
|
|
33914
33536
|
return null;
|
|
33915
33537
|
}
|
|
@@ -33958,7 +33580,7 @@ function createFauxBoard(circuitJson) {
|
|
|
33958
33580
|
|
|
33959
33581
|
// src/utils/preprocess-circuit-json.ts
|
|
33960
33582
|
function addFauxBoardIfNeeded(circuitJson) {
|
|
33961
|
-
const boards =
|
|
33583
|
+
const boards = su11(circuitJson).pcb_board.list();
|
|
33962
33584
|
if (boards.length > 0) {
|
|
33963
33585
|
return circuitJson;
|
|
33964
33586
|
}
|
|
@@ -34009,7 +33631,7 @@ var CadViewerJscad = forwardRef3(
|
|
|
34009
33631
|
const initialCameraPosition = useMemo20(() => {
|
|
34010
33632
|
if (!internalCircuitJson) return [5, -5, 5];
|
|
34011
33633
|
try {
|
|
34012
|
-
const board =
|
|
33634
|
+
const board = su12(internalCircuitJson).pcb_board.list()[0];
|
|
34013
33635
|
if (!board) return [5, -5, 5];
|
|
34014
33636
|
const { width: width10, height: height10 } = board;
|
|
34015
33637
|
if (!width10 && !height10) {
|
|
@@ -34035,7 +33657,7 @@ var CadViewerJscad = forwardRef3(
|
|
|
34035
33657
|
const isFauxBoard = useMemo20(() => {
|
|
34036
33658
|
if (!internalCircuitJson) return false;
|
|
34037
33659
|
try {
|
|
34038
|
-
const board =
|
|
33660
|
+
const board = su12(internalCircuitJson).pcb_board.list()[0];
|
|
34039
33661
|
return !!board && board.pcb_board_id === "faux-board";
|
|
34040
33662
|
} catch (e) {
|
|
34041
33663
|
return false;
|
|
@@ -34044,7 +33666,7 @@ var CadViewerJscad = forwardRef3(
|
|
|
34044
33666
|
const boardDimensions = useMemo20(() => {
|
|
34045
33667
|
if (!internalCircuitJson) return void 0;
|
|
34046
33668
|
try {
|
|
34047
|
-
const board =
|
|
33669
|
+
const board = su12(internalCircuitJson).pcb_board.list()[0];
|
|
34048
33670
|
if (!board) return void 0;
|
|
34049
33671
|
return { width: board.width ?? 0, height: board.height ?? 0 };
|
|
34050
33672
|
} catch (e) {
|
|
@@ -34055,7 +33677,7 @@ var CadViewerJscad = forwardRef3(
|
|
|
34055
33677
|
const boardCenter = useMemo20(() => {
|
|
34056
33678
|
if (!internalCircuitJson) return void 0;
|
|
34057
33679
|
try {
|
|
34058
|
-
const board =
|
|
33680
|
+
const board = su12(internalCircuitJson).pcb_board.list()[0];
|
|
34059
33681
|
if (!board || !board.center) return void 0;
|
|
34060
33682
|
return { x: board.center.x, y: board.center.y };
|
|
34061
33683
|
} catch (e) {
|
|
@@ -34065,7 +33687,7 @@ var CadViewerJscad = forwardRef3(
|
|
|
34065
33687
|
}, [internalCircuitJson]);
|
|
34066
33688
|
const pcbThickness = usePcbThickness(internalCircuitJson);
|
|
34067
33689
|
const { stls: boardStls, loading } = useStlsFromGeom(boardGeom);
|
|
34068
|
-
const cad_components =
|
|
33690
|
+
const cad_components = su12(internalCircuitJson).cad_component.list();
|
|
34069
33691
|
return /* @__PURE__ */ jsxs5(
|
|
34070
33692
|
CadViewerContainer,
|
|
34071
33693
|
{
|
|
@@ -34119,12 +33741,12 @@ var CadViewerJscad = forwardRef3(
|
|
|
34119
33741
|
);
|
|
34120
33742
|
|
|
34121
33743
|
// src/CadViewerManifold.tsx
|
|
34122
|
-
import { su as
|
|
33744
|
+
import { su as su18 } from "@tscircuit/circuit-json-util";
|
|
34123
33745
|
import { useEffect as useEffect25, useMemo as useMemo22, useState as useState16 } from "react";
|
|
34124
33746
|
import * as THREE35 from "three";
|
|
34125
33747
|
|
|
34126
33748
|
// src/hooks/useManifoldBoardBuilder.ts
|
|
34127
|
-
import { su as
|
|
33749
|
+
import { su as su17 } from "@tscircuit/circuit-json-util";
|
|
34128
33750
|
import { useEffect as useEffect24, useMemo as useMemo21, useRef as useRef9, useState as useState15 } from "react";
|
|
34129
33751
|
import * as THREE32 from "three";
|
|
34130
33752
|
|
|
@@ -34187,7 +33809,7 @@ function createManifoldBoard(Manifold, CrossSection, boardData, pcbThickness, ma
|
|
|
34187
33809
|
}
|
|
34188
33810
|
|
|
34189
33811
|
// src/utils/manifold/process-cutouts.ts
|
|
34190
|
-
import { su as
|
|
33812
|
+
import { su as su13 } from "@tscircuit/circuit-json-util";
|
|
34191
33813
|
|
|
34192
33814
|
// src/utils/pad-geoms.ts
|
|
34193
33815
|
var RECT_PAD_SEGMENTS2 = 64;
|
|
@@ -34246,7 +33868,7 @@ var arePointsClockwise3 = (points) => {
|
|
|
34246
33868
|
};
|
|
34247
33869
|
function processCutoutsForManifold(Manifold, CrossSection, circuitJson, pcbThickness, manifoldInstancesForCleanup) {
|
|
34248
33870
|
const cutoutOps = [];
|
|
34249
|
-
const pcbCutouts =
|
|
33871
|
+
const pcbCutouts = su13(circuitJson).pcb_cutout.list();
|
|
34250
33872
|
for (const cutout of pcbCutouts) {
|
|
34251
33873
|
let cutoutOp;
|
|
34252
33874
|
const cutoutHeight = pcbThickness * 1.5;
|
|
@@ -34341,7 +33963,7 @@ function processCutoutsForManifold(Manifold, CrossSection, circuitJson, pcbThick
|
|
|
34341
33963
|
}
|
|
34342
33964
|
|
|
34343
33965
|
// src/utils/manifold/process-non-plated-holes.ts
|
|
34344
|
-
import { su as
|
|
33966
|
+
import { su as su14 } from "@tscircuit/circuit-json-util";
|
|
34345
33967
|
|
|
34346
33968
|
// src/utils/hole-geoms.ts
|
|
34347
33969
|
function createCircleHoleDrill({
|
|
@@ -34384,7 +34006,7 @@ function createPlatedHoleDrill({
|
|
|
34384
34006
|
// src/utils/manifold/process-non-plated-holes.ts
|
|
34385
34007
|
function processNonPlatedHolesForManifold(Manifold, CrossSection, circuitJson, pcbThickness, manifoldInstancesForCleanup) {
|
|
34386
34008
|
const nonPlatedHoleBoardDrills = [];
|
|
34387
|
-
const pcbHoles =
|
|
34009
|
+
const pcbHoles = su14(circuitJson).pcb_hole.list();
|
|
34388
34010
|
const createPillOp = (width10, height10, depth) => {
|
|
34389
34011
|
const pillOp = createRoundedRectPrism({
|
|
34390
34012
|
Manifold,
|
|
@@ -34467,7 +34089,7 @@ function processNonPlatedHolesForManifold(Manifold, CrossSection, circuitJson, p
|
|
|
34467
34089
|
}
|
|
34468
34090
|
|
|
34469
34091
|
// src/utils/manifold/process-plated-holes.ts
|
|
34470
|
-
import { su as
|
|
34092
|
+
import { su as su15 } from "@tscircuit/circuit-json-util";
|
|
34471
34093
|
import * as THREE30 from "three";
|
|
34472
34094
|
|
|
34473
34095
|
// src/utils/manifold-mesh-to-three-geometry.ts
|
|
@@ -34514,9 +34136,12 @@ var createEllipsePoints = (width10, height10, segments) => {
|
|
|
34514
34136
|
};
|
|
34515
34137
|
var COPPER_COLOR = new THREE30.Color(...colors.copper);
|
|
34516
34138
|
var PLATED_HOLE_LIP_HEIGHT = 0.05;
|
|
34139
|
+
var PLATED_HOLE_PAD_THICKNESS = 3e-3;
|
|
34140
|
+
var PLATED_HOLE_SURFACE_CLEARANCE = 5e-4;
|
|
34141
|
+
var PLATED_HOLE_FILL_CLEARANCE = 4e-3;
|
|
34517
34142
|
function processPlatedHolesForManifold(Manifold, CrossSection, circuitJson, pcbThickness, manifoldInstancesForCleanup, boardClipVolume) {
|
|
34518
34143
|
const platedHoleBoardDrills = [];
|
|
34519
|
-
const pcbPlatedHoles =
|
|
34144
|
+
const pcbPlatedHoles = su15(circuitJson).pcb_plated_hole.list();
|
|
34520
34145
|
const platedHoleCopperGeoms = [];
|
|
34521
34146
|
const platedHoleCopperOpsForSubtract = [];
|
|
34522
34147
|
const createPillOp = (width10, height10, depth) => {
|
|
@@ -34733,7 +34358,7 @@ function processPlatedHolesForManifold(Manifold, CrossSection, circuitJson, pcbT
|
|
|
34733
34358
|
const padWidth = ph.rect_pad_width;
|
|
34734
34359
|
const padHeight = ph.rect_pad_height;
|
|
34735
34360
|
const rectBorderRadius = extractRectBorderRadius(ph);
|
|
34736
|
-
const padThickness =
|
|
34361
|
+
const padThickness = PLATED_HOLE_PAD_THICKNESS;
|
|
34737
34362
|
const drillW = holeW + 2 * MANIFOLD_Z_OFFSET;
|
|
34738
34363
|
const drillH = holeH + 2 * MANIFOLD_Z_OFFSET;
|
|
34739
34364
|
const drillDepth = pcbThickness * 1.2;
|
|
@@ -34753,8 +34378,11 @@ function processPlatedHolesForManifold(Manifold, CrossSection, circuitJson, pcbT
|
|
|
34753
34378
|
Manifold,
|
|
34754
34379
|
width: padWidth,
|
|
34755
34380
|
height: padHeight,
|
|
34756
|
-
thickness:
|
|
34757
|
-
|
|
34381
|
+
thickness: Math.max(
|
|
34382
|
+
pcbThickness - 2 * (padThickness + PLATED_HOLE_SURFACE_CLEARANCE + PLATED_HOLE_FILL_CLEARANCE),
|
|
34383
|
+
M
|
|
34384
|
+
),
|
|
34385
|
+
// Fill between pads (recessed from board surfaces)
|
|
34758
34386
|
borderRadius: rectBorderRadius
|
|
34759
34387
|
});
|
|
34760
34388
|
manifoldInstancesForCleanup.push(mainFill);
|
|
@@ -34764,20 +34392,28 @@ function processPlatedHolesForManifold(Manifold, CrossSection, circuitJson, pcbT
|
|
|
34764
34392
|
height: padHeight,
|
|
34765
34393
|
thickness: padThickness,
|
|
34766
34394
|
borderRadius: rectBorderRadius
|
|
34767
|
-
}).translate([
|
|
34395
|
+
}).translate([
|
|
34396
|
+
0,
|
|
34397
|
+
0,
|
|
34398
|
+
pcbThickness / 2 + PLATED_HOLE_SURFACE_CLEARANCE + padThickness / 2
|
|
34399
|
+
]);
|
|
34768
34400
|
const bottomPad = createRoundedRectPrism({
|
|
34769
34401
|
Manifold,
|
|
34770
34402
|
width: padWidth,
|
|
34771
34403
|
height: padHeight,
|
|
34772
34404
|
thickness: padThickness,
|
|
34773
34405
|
borderRadius: rectBorderRadius
|
|
34774
|
-
}).translate([
|
|
34406
|
+
}).translate([
|
|
34407
|
+
0,
|
|
34408
|
+
0,
|
|
34409
|
+
-pcbThickness / 2 - PLATED_HOLE_SURFACE_CLEARANCE - padThickness / 2
|
|
34410
|
+
]);
|
|
34775
34411
|
manifoldInstancesForCleanup.push(topPad, bottomPad);
|
|
34776
34412
|
const barrelPill = createPillOp(
|
|
34777
34413
|
holeW,
|
|
34778
34414
|
holeH,
|
|
34779
|
-
pcbThickness *
|
|
34780
|
-
// Slightly
|
|
34415
|
+
pcbThickness * 0.8
|
|
34416
|
+
// Slightly shorter than board
|
|
34781
34417
|
).translate([holeOffsetX, holeOffsetY, 0]);
|
|
34782
34418
|
manifoldInstancesForCleanup.push(barrelPill);
|
|
34783
34419
|
const copperUnion = Manifold.union([
|
|
@@ -34825,7 +34461,7 @@ function processPlatedHolesForManifold(Manifold, CrossSection, circuitJson, pcbT
|
|
|
34825
34461
|
const padWidth = ph.rect_pad_width;
|
|
34826
34462
|
const padHeight = ph.rect_pad_height;
|
|
34827
34463
|
const rectBorderRadius = extractRectBorderRadius(ph);
|
|
34828
|
-
const padThickness =
|
|
34464
|
+
const padThickness = PLATED_HOLE_PAD_THICKNESS;
|
|
34829
34465
|
const drillW = holeW + 2 * MANIFOLD_Z_OFFSET;
|
|
34830
34466
|
const drillH = holeH + 2 * MANIFOLD_Z_OFFSET;
|
|
34831
34467
|
const drillDepth = pcbThickness * 1.2;
|
|
@@ -34852,8 +34488,11 @@ function processPlatedHolesForManifold(Manifold, CrossSection, circuitJson, pcbT
|
|
|
34852
34488
|
Manifold,
|
|
34853
34489
|
width: padWidth,
|
|
34854
34490
|
height: padHeight,
|
|
34855
|
-
thickness:
|
|
34856
|
-
|
|
34491
|
+
thickness: Math.max(
|
|
34492
|
+
pcbThickness - 2 * (padThickness + PLATED_HOLE_SURFACE_CLEARANCE + PLATED_HOLE_FILL_CLEARANCE),
|
|
34493
|
+
M
|
|
34494
|
+
),
|
|
34495
|
+
// Fill between pads (recessed from board surfaces)
|
|
34857
34496
|
borderRadius: rectBorderRadius
|
|
34858
34497
|
});
|
|
34859
34498
|
manifoldInstancesForCleanup.push(mainFill);
|
|
@@ -34868,7 +34507,11 @@ function processPlatedHolesForManifold(Manifold, CrossSection, circuitJson, pcbT
|
|
|
34868
34507
|
height: padHeight,
|
|
34869
34508
|
thickness: padThickness,
|
|
34870
34509
|
borderRadius: rectBorderRadius
|
|
34871
|
-
}).translate([
|
|
34510
|
+
}).translate([
|
|
34511
|
+
0,
|
|
34512
|
+
0,
|
|
34513
|
+
pcbThickness / 2 + PLATED_HOLE_SURFACE_CLEARANCE + padThickness / 2
|
|
34514
|
+
]);
|
|
34872
34515
|
if (ph.rect_ccw_rotation) {
|
|
34873
34516
|
const rotatedTopPad = topPad.rotate([0, 0, ph.rect_ccw_rotation]);
|
|
34874
34517
|
manifoldInstancesForCleanup.push(rotatedTopPad);
|
|
@@ -34880,7 +34523,11 @@ function processPlatedHolesForManifold(Manifold, CrossSection, circuitJson, pcbT
|
|
|
34880
34523
|
height: padHeight,
|
|
34881
34524
|
thickness: padThickness,
|
|
34882
34525
|
borderRadius: rectBorderRadius
|
|
34883
|
-
}).translate([
|
|
34526
|
+
}).translate([
|
|
34527
|
+
0,
|
|
34528
|
+
0,
|
|
34529
|
+
-pcbThickness / 2 - PLATED_HOLE_SURFACE_CLEARANCE - padThickness / 2
|
|
34530
|
+
]);
|
|
34884
34531
|
if (ph.rect_ccw_rotation) {
|
|
34885
34532
|
const rotatedBottomPad = bottomPad.rotate([0, 0, ph.rect_ccw_rotation]);
|
|
34886
34533
|
manifoldInstancesForCleanup.push(rotatedBottomPad);
|
|
@@ -34890,7 +34537,7 @@ function processPlatedHolesForManifold(Manifold, CrossSection, circuitJson, pcbT
|
|
|
34890
34537
|
let barrelPill = createPillOp(
|
|
34891
34538
|
holeW,
|
|
34892
34539
|
holeH,
|
|
34893
|
-
pcbThickness *
|
|
34540
|
+
pcbThickness * 0.8
|
|
34894
34541
|
// Slightly taller than board
|
|
34895
34542
|
).translate([holeOffsetX, holeOffsetY, 0]);
|
|
34896
34543
|
if (ph.hole_ccw_rotation) {
|
|
@@ -34952,9 +34599,9 @@ function processPlatedHolesForManifold(Manifold, CrossSection, circuitJson, pcbT
|
|
|
34952
34599
|
const translatedBoardHole = boardHoleOp.translate([ph.x, ph.y, 0]);
|
|
34953
34600
|
manifoldInstancesForCleanup.push(translatedBoardHole);
|
|
34954
34601
|
platedHoleBoardDrills.push(translatedBoardHole);
|
|
34955
|
-
const padThickness =
|
|
34602
|
+
const padThickness = PLATED_HOLE_PAD_THICKNESS;
|
|
34956
34603
|
const fillThickness = Math.max(
|
|
34957
|
-
pcbThickness - 2 * padThickness
|
|
34604
|
+
pcbThickness - 2 * (padThickness + PLATED_HOLE_SURFACE_CLEARANCE + PLATED_HOLE_FILL_CLEARANCE),
|
|
34958
34605
|
M
|
|
34959
34606
|
);
|
|
34960
34607
|
const mainFill = createPolygonPadOp({
|
|
@@ -34970,12 +34617,12 @@ function processPlatedHolesForManifold(Manifold, CrossSection, circuitJson, pcbT
|
|
|
34970
34617
|
const topTranslated = topPad.translate([
|
|
34971
34618
|
0,
|
|
34972
34619
|
0,
|
|
34973
|
-
pcbThickness / 2
|
|
34620
|
+
pcbThickness / 2 + PLATED_HOLE_SURFACE_CLEARANCE + padThickness / 2
|
|
34974
34621
|
]);
|
|
34975
34622
|
const bottomTranslated = bottomPad.translate([
|
|
34976
34623
|
0,
|
|
34977
34624
|
0,
|
|
34978
|
-
-pcbThickness / 2
|
|
34625
|
+
-pcbThickness / 2 - PLATED_HOLE_SURFACE_CLEARANCE - padThickness / 2
|
|
34979
34626
|
]);
|
|
34980
34627
|
manifoldInstancesForCleanup.push(topTranslated, bottomTranslated);
|
|
34981
34628
|
const barrelOp = createHoleOpForPolygonPad({
|
|
@@ -34985,7 +34632,7 @@ function processPlatedHolesForManifold(Manifold, CrossSection, circuitJson, pcbT
|
|
|
34985
34632
|
if (!barrelOp) return;
|
|
34986
34633
|
const holeCutOp = createHoleOpForPolygonPad({
|
|
34987
34634
|
ph,
|
|
34988
|
-
depth: pcbThickness *
|
|
34635
|
+
depth: pcbThickness * 0.8,
|
|
34989
34636
|
sizeDelta: -2 * M
|
|
34990
34637
|
}) || barrelOp;
|
|
34991
34638
|
const copperUnion = Manifold.union([
|
|
@@ -35135,14 +34782,17 @@ function processPlatedHolesForManifold(Manifold, CrossSection, circuitJson, pcbT
|
|
|
35135
34782
|
const padWidth = ph.rect_pad_width ?? ph.hole_diameter;
|
|
35136
34783
|
const padHeight = ph.rect_pad_height ?? ph.hole_diameter;
|
|
35137
34784
|
const rectBorderRadius = extractRectBorderRadius(ph);
|
|
35138
|
-
const padThickness =
|
|
34785
|
+
const padThickness = PLATED_HOLE_PAD_THICKNESS;
|
|
35139
34786
|
const holeRadius = ph.hole_diameter / 2;
|
|
35140
34787
|
const mainFill = createRoundedRectPrism({
|
|
35141
34788
|
Manifold,
|
|
35142
34789
|
width: padWidth,
|
|
35143
34790
|
height: padHeight,
|
|
35144
|
-
thickness:
|
|
35145
|
-
|
|
34791
|
+
thickness: Math.max(
|
|
34792
|
+
pcbThickness - 2 * (padThickness + PLATED_HOLE_SURFACE_CLEARANCE + PLATED_HOLE_FILL_CLEARANCE),
|
|
34793
|
+
M
|
|
34794
|
+
),
|
|
34795
|
+
// Fill between pads (recessed from board surfaces)
|
|
35146
34796
|
borderRadius: rectBorderRadius
|
|
35147
34797
|
});
|
|
35148
34798
|
manifoldInstancesForCleanup.push(mainFill);
|
|
@@ -35152,17 +34802,25 @@ function processPlatedHolesForManifold(Manifold, CrossSection, circuitJson, pcbT
|
|
|
35152
34802
|
height: padHeight,
|
|
35153
34803
|
thickness: padThickness,
|
|
35154
34804
|
borderRadius: rectBorderRadius
|
|
35155
|
-
}).translate([
|
|
34805
|
+
}).translate([
|
|
34806
|
+
0,
|
|
34807
|
+
0,
|
|
34808
|
+
pcbThickness / 2 + PLATED_HOLE_SURFACE_CLEARANCE + padThickness / 2
|
|
34809
|
+
]);
|
|
35156
34810
|
const bottomPad = createRoundedRectPrism({
|
|
35157
34811
|
Manifold,
|
|
35158
34812
|
width: padWidth,
|
|
35159
34813
|
height: padHeight,
|
|
35160
34814
|
thickness: padThickness,
|
|
35161
34815
|
borderRadius: rectBorderRadius
|
|
35162
|
-
}).translate([
|
|
34816
|
+
}).translate([
|
|
34817
|
+
0,
|
|
34818
|
+
0,
|
|
34819
|
+
-pcbThickness / 2 - PLATED_HOLE_SURFACE_CLEARANCE - padThickness / 2
|
|
34820
|
+
]);
|
|
35163
34821
|
manifoldInstancesForCleanup.push(topPad, bottomPad);
|
|
35164
34822
|
const barrelCylinder = Manifold.cylinder(
|
|
35165
|
-
pcbThickness *
|
|
34823
|
+
pcbThickness * 0.8,
|
|
35166
34824
|
// Slightly taller than board
|
|
35167
34825
|
holeRadius,
|
|
35168
34826
|
holeRadius,
|
|
@@ -35217,7 +34875,7 @@ function processPlatedHolesForManifold(Manifold, CrossSection, circuitJson, pcbT
|
|
|
35217
34875
|
}
|
|
35218
34876
|
|
|
35219
34877
|
// src/utils/manifold/process-vias.ts
|
|
35220
|
-
import { su as
|
|
34878
|
+
import { su as su16 } from "@tscircuit/circuit-json-util";
|
|
35221
34879
|
import * as THREE31 from "three";
|
|
35222
34880
|
|
|
35223
34881
|
// src/utils/via-geoms.ts
|
|
@@ -35274,7 +34932,7 @@ function createViaCopper2({
|
|
|
35274
34932
|
var COPPER_COLOR2 = new THREE31.Color(...colors.copper);
|
|
35275
34933
|
function processViasForManifold(Manifold, circuitJson, pcbThickness, manifoldInstancesForCleanup, boardClipVolume) {
|
|
35276
34934
|
const viaBoardDrills = [];
|
|
35277
|
-
const pcbVias =
|
|
34935
|
+
const pcbVias = su16(circuitJson).pcb_via.list();
|
|
35278
34936
|
const viaCopperGeoms = [];
|
|
35279
34937
|
pcbVias.forEach((via, index2) => {
|
|
35280
34938
|
if (typeof via.hole_diameter === "number") {
|
|
@@ -35333,7 +34991,7 @@ var useManifoldBoardBuilder = (manifoldJSModule, circuitJson, visibility) => {
|
|
|
35333
34991
|
const panels = circuitJson.filter(
|
|
35334
34992
|
(e) => e.type === "pcb_panel"
|
|
35335
34993
|
);
|
|
35336
|
-
const boards =
|
|
34994
|
+
const boards = su17(circuitJson).pcb_board.list();
|
|
35337
34995
|
if (panels.length > 0) {
|
|
35338
34996
|
const panel = panels[0];
|
|
35339
34997
|
const firstBoardInPanel = boards.find(
|
|
@@ -35354,7 +35012,7 @@ var useManifoldBoardBuilder = (manifoldJSModule, circuitJson, visibility) => {
|
|
|
35354
35012
|
return boardsNotInPanel.length > 0 ? boardsNotInPanel[0] : null;
|
|
35355
35013
|
}, [circuitJson]);
|
|
35356
35014
|
const isFauxBoard = useMemo21(() => {
|
|
35357
|
-
const boards =
|
|
35015
|
+
const boards = su17(circuitJson).pcb_board.list();
|
|
35358
35016
|
return boards.length > 0 && boards[0].pcb_board_id === "faux-board";
|
|
35359
35017
|
}, [circuitJson]);
|
|
35360
35018
|
const traceTextureResolution = useMemo21(() => {
|
|
@@ -35589,7 +35247,10 @@ var createBoardMaterial = ({
|
|
|
35589
35247
|
clearcoat: 0,
|
|
35590
35248
|
transparent: isFaux,
|
|
35591
35249
|
opacity: isFaux ? FAUX_BOARD_OPACITY : 1,
|
|
35592
|
-
flatShading: true
|
|
35250
|
+
flatShading: true,
|
|
35251
|
+
polygonOffset: true,
|
|
35252
|
+
polygonOffsetFactor: 1,
|
|
35253
|
+
polygonOffsetUnits: 1
|
|
35593
35254
|
});
|
|
35594
35255
|
}
|
|
35595
35256
|
return new THREE33.MeshStandardMaterial({
|
|
@@ -35599,7 +35260,10 @@ var createBoardMaterial = ({
|
|
|
35599
35260
|
metalness: 0.1,
|
|
35600
35261
|
roughness: 0.8,
|
|
35601
35262
|
transparent: true,
|
|
35602
|
-
opacity: isFaux ? FAUX_BOARD_OPACITY : 0.9
|
|
35263
|
+
opacity: isFaux ? FAUX_BOARD_OPACITY : 0.9,
|
|
35264
|
+
polygonOffset: true,
|
|
35265
|
+
polygonOffsetFactor: 1,
|
|
35266
|
+
polygonOffsetUnits: 1
|
|
35603
35267
|
});
|
|
35604
35268
|
};
|
|
35605
35269
|
|
|
@@ -35628,11 +35292,8 @@ function createGeometryMeshes(geoms) {
|
|
|
35628
35292
|
new THREE34.MeshStandardMaterial({
|
|
35629
35293
|
color: comp.color,
|
|
35630
35294
|
side: THREE34.DoubleSide,
|
|
35631
|
-
flatShading: true
|
|
35295
|
+
flatShading: true
|
|
35632
35296
|
// Consistent with board
|
|
35633
|
-
polygonOffset: true,
|
|
35634
|
-
polygonOffsetFactor: -1,
|
|
35635
|
-
polygonOffsetUnits: -1
|
|
35636
35297
|
})
|
|
35637
35298
|
);
|
|
35638
35299
|
mesh.name = comp.key;
|
|
@@ -35809,7 +35470,7 @@ try {
|
|
|
35809
35470
|
[textures, boardData, pcbThickness, isFauxBoard]
|
|
35810
35471
|
);
|
|
35811
35472
|
const cadComponents = useMemo22(
|
|
35812
|
-
() =>
|
|
35473
|
+
() => su18(circuitJson).cad_component.list(),
|
|
35813
35474
|
[circuitJson]
|
|
35814
35475
|
);
|
|
35815
35476
|
const boardDimensions = useMemo22(() => {
|
|
@@ -42823,7 +42484,7 @@ var CadViewer = (props) => {
|
|
|
42823
42484
|
|
|
42824
42485
|
// src/convert-circuit-json-to-3d-svg.ts
|
|
42825
42486
|
var import_debug = __toESM(require_browser(), 1);
|
|
42826
|
-
import { su as
|
|
42487
|
+
import { su as su19 } from "@tscircuit/circuit-json-util";
|
|
42827
42488
|
import * as THREE40 from "three";
|
|
42828
42489
|
import { SVGRenderer } from "three/examples/jsm/renderers/SVGRenderer.js";
|
|
42829
42490
|
|
|
@@ -43052,11 +42713,11 @@ async function convertCircuitJsonTo3dSvg(circuitJson, options = {}) {
|
|
|
43052
42713
|
const pointLight = new THREE40.PointLight(16777215, Math.PI / 4);
|
|
43053
42714
|
pointLight.position.set(-10, -10, 10);
|
|
43054
42715
|
scene.add(pointLight);
|
|
43055
|
-
const components =
|
|
42716
|
+
const components = su19(circuitJson).cad_component.list();
|
|
43056
42717
|
for (const component of components) {
|
|
43057
42718
|
await renderComponent(component, scene);
|
|
43058
42719
|
}
|
|
43059
|
-
const boardData =
|
|
42720
|
+
const boardData = su19(circuitJson).pcb_board.list()[0];
|
|
43060
42721
|
const boardGeom = createBoardGeomFromCircuitJson(circuitJson);
|
|
43061
42722
|
if (boardGeom) {
|
|
43062
42723
|
const solderMaskColor = colors.fr4SolderMaskGreen;
|