@tscircuit/3d-viewer 0.0.515 → 0.0.517
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 +396 -759
- 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.516",
|
|
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
|
|
@@ -32611,585 +32599,9 @@ function createSilkscreenTextureForLayer({
|
|
|
32611
32599
|
return texture;
|
|
32612
32600
|
}
|
|
32613
32601
|
|
|
32614
|
-
// src/utils/soldermask-texture.ts
|
|
32615
|
-
import { su as su8 } from "@tscircuit/circuit-json-util";
|
|
32616
|
-
import * as THREE23 from "three";
|
|
32617
|
-
function createSoldermaskTextureForLayer({
|
|
32618
|
-
layer,
|
|
32619
|
-
circuitJson,
|
|
32620
|
-
boardData,
|
|
32621
|
-
soldermaskColor,
|
|
32622
|
-
traceTextureResolution
|
|
32623
|
-
}) {
|
|
32624
|
-
const boardOutlineBounds = calculateOutlineBounds(boardData);
|
|
32625
|
-
const canvas = document.createElement("canvas");
|
|
32626
|
-
const canvasWidth = Math.floor(
|
|
32627
|
-
boardOutlineBounds.width * traceTextureResolution
|
|
32628
|
-
);
|
|
32629
|
-
const canvasHeight = Math.floor(
|
|
32630
|
-
boardOutlineBounds.height * traceTextureResolution
|
|
32631
|
-
);
|
|
32632
|
-
canvas.width = canvasWidth;
|
|
32633
|
-
canvas.height = canvasHeight;
|
|
32634
|
-
const ctx = canvas.getContext("2d");
|
|
32635
|
-
if (!ctx) return null;
|
|
32636
|
-
if (layer === "bottom") {
|
|
32637
|
-
ctx.translate(0, canvasHeight);
|
|
32638
|
-
ctx.scale(1, -1);
|
|
32639
|
-
}
|
|
32640
|
-
const canvasXFromPcb = (pcbX) => (pcbX - boardOutlineBounds.minX) * traceTextureResolution;
|
|
32641
|
-
const canvasYFromPcb = (pcbY) => (boardOutlineBounds.maxY - pcbY) * traceTextureResolution;
|
|
32642
|
-
ctx.fillStyle = soldermaskColor;
|
|
32643
|
-
if (boardData.outline && boardData.outline.length >= 3) {
|
|
32644
|
-
ctx.beginPath();
|
|
32645
|
-
const firstPoint = boardData.outline[0];
|
|
32646
|
-
ctx.moveTo(canvasXFromPcb(firstPoint.x), canvasYFromPcb(firstPoint.y));
|
|
32647
|
-
for (let i = 1; i < boardData.outline.length; i++) {
|
|
32648
|
-
const point = boardData.outline[i];
|
|
32649
|
-
ctx.lineTo(canvasXFromPcb(point.x), canvasYFromPcb(point.y));
|
|
32650
|
-
}
|
|
32651
|
-
ctx.closePath();
|
|
32652
|
-
ctx.fill();
|
|
32653
|
-
} else {
|
|
32654
|
-
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
|
|
32655
|
-
}
|
|
32656
|
-
ctx.globalCompositeOperation = "destination-out";
|
|
32657
|
-
ctx.fillStyle = "black";
|
|
32658
|
-
const pcbSmtPads = su8(circuitJson).pcb_smtpad.list();
|
|
32659
|
-
const smtPadsOnLayer = pcbSmtPads.filter((pad2) => pad2.layer === layer);
|
|
32660
|
-
smtPadsOnLayer.forEach((pad2) => {
|
|
32661
|
-
if (pad2.shape === "polygon" && pad2.points) {
|
|
32662
|
-
ctx.beginPath();
|
|
32663
|
-
pad2.points.forEach((point, index2) => {
|
|
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();
|
|
32706
|
-
} else {
|
|
32707
|
-
ctx.fillRect(canvasX - width10 / 2, canvasY - height10 / 2, width10, height10);
|
|
32708
|
-
}
|
|
32709
|
-
} else if (pad2.shape === "circle") {
|
|
32710
|
-
const radius = (pad2.radius ?? pad2.width / 2) * traceTextureResolution;
|
|
32711
|
-
ctx.beginPath();
|
|
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();
|
|
32750
|
-
}
|
|
32751
|
-
});
|
|
32752
|
-
const pcbVias = su8(circuitJson).pcb_via.list();
|
|
32753
|
-
pcbVias.forEach((via) => {
|
|
32754
|
-
const canvasX = canvasXFromPcb(via.x);
|
|
32755
|
-
const canvasY = canvasYFromPcb(via.y);
|
|
32756
|
-
const canvasRadius = via.outer_diameter / 2 * traceTextureResolution;
|
|
32757
|
-
ctx.beginPath();
|
|
32758
|
-
ctx.arc(canvasX, canvasY, canvasRadius, 0, 2 * Math.PI);
|
|
32759
|
-
ctx.fill();
|
|
32760
|
-
});
|
|
32761
|
-
const pcbPlatedHoles = su8(circuitJson).pcb_plated_hole.list();
|
|
32762
|
-
pcbPlatedHoles.forEach((hole) => {
|
|
32763
|
-
if (!hole.layers?.includes(layer)) return;
|
|
32764
|
-
const x = hole.x;
|
|
32765
|
-
const y = hole.y;
|
|
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;
|
|
32771
|
-
ctx.beginPath();
|
|
32772
|
-
ctx.arc(canvasX, canvasY, canvasRadius, 0, 2 * Math.PI);
|
|
32773
|
-
ctx.fill();
|
|
32774
|
-
} else if (hole.shape === "pill") {
|
|
32775
|
-
const width10 = (hole.outer_width ?? hole.outer_diameter ?? hole.hole_width) * traceTextureResolution;
|
|
32776
|
-
const height10 = (hole.outer_height ?? hole.outer_diameter ?? hole.hole_height) * traceTextureResolution;
|
|
32777
|
-
const radius = Math.min(width10, height10) / 2;
|
|
32778
|
-
const ccwRotationDeg = hole.ccw_rotation || 0;
|
|
32779
|
-
const rotation = -ccwRotationDeg;
|
|
32780
|
-
if (rotation) {
|
|
32781
|
-
ctx.save();
|
|
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) {
|
|
32807
|
-
ctx.save();
|
|
32808
|
-
ctx.translate(canvasX, canvasY);
|
|
32809
|
-
ctx.rotate(rotation * Math.PI / 180);
|
|
32810
|
-
ctx.beginPath();
|
|
32811
|
-
ctx.ellipse(0, 0, radiusX, radiusY, 0, 0, 2 * Math.PI);
|
|
32812
|
-
ctx.fill();
|
|
32813
|
-
ctx.restore();
|
|
32814
|
-
} else {
|
|
32815
|
-
ctx.beginPath();
|
|
32816
|
-
ctx.ellipse(canvasX, canvasY, radiusX, radiusY, 0, 0, 2 * Math.PI);
|
|
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(
|
|
32922
|
-
canvasX - padWidth / 2,
|
|
32923
|
-
canvasY - padHeight / 2,
|
|
32924
|
-
padWidth,
|
|
32925
|
-
padHeight
|
|
32926
|
-
);
|
|
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();
|
|
32999
|
-
}
|
|
33000
|
-
}
|
|
33001
|
-
});
|
|
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
|
-
ctx.globalCompositeOperation = "source-over";
|
|
33181
|
-
const texture = new THREE23.CanvasTexture(canvas);
|
|
33182
|
-
texture.generateMipmaps = true;
|
|
33183
|
-
texture.minFilter = THREE23.LinearMipmapLinearFilter;
|
|
33184
|
-
texture.magFilter = THREE23.LinearFilter;
|
|
33185
|
-
texture.anisotropy = 16;
|
|
33186
|
-
texture.needsUpdate = true;
|
|
33187
|
-
return texture;
|
|
33188
|
-
}
|
|
33189
|
-
|
|
33190
32602
|
// src/utils/trace-texture.ts
|
|
33191
|
-
import * as
|
|
33192
|
-
import { su as
|
|
32603
|
+
import * as THREE23 from "three";
|
|
32604
|
+
import { su as su8 } from "@tscircuit/circuit-json-util";
|
|
33193
32605
|
function isWireRoutePoint(point) {
|
|
33194
32606
|
return point && point.route_type === "wire" && typeof point.layer === "string" && typeof point.width === "number";
|
|
33195
32607
|
}
|
|
@@ -33200,9 +32612,9 @@ function createTraceTextureForLayer({
|
|
|
33200
32612
|
traceColor,
|
|
33201
32613
|
traceTextureResolution
|
|
33202
32614
|
}) {
|
|
33203
|
-
const pcbTraces =
|
|
33204
|
-
const allPcbVias =
|
|
33205
|
-
const allPcbPlatedHoles =
|
|
32615
|
+
const pcbTraces = su8(circuitJson).pcb_trace.list();
|
|
32616
|
+
const allPcbVias = su8(circuitJson).pcb_via.list();
|
|
32617
|
+
const allPcbPlatedHoles = su8(
|
|
33206
32618
|
circuitJson
|
|
33207
32619
|
).pcb_plated_hole.list();
|
|
33208
32620
|
const tracesOnLayer = pcbTraces.filter(
|
|
@@ -33301,17 +32713,17 @@ function createTraceTextureForLayer({
|
|
|
33301
32713
|
}
|
|
33302
32714
|
});
|
|
33303
32715
|
ctx.globalCompositeOperation = "source-over";
|
|
33304
|
-
const texture = new
|
|
32716
|
+
const texture = new THREE23.CanvasTexture(canvas);
|
|
33305
32717
|
texture.generateMipmaps = true;
|
|
33306
|
-
texture.minFilter =
|
|
33307
|
-
texture.magFilter =
|
|
32718
|
+
texture.minFilter = THREE23.LinearMipmapLinearFilter;
|
|
32719
|
+
texture.magFilter = THREE23.LinearFilter;
|
|
33308
32720
|
texture.anisotropy = 16;
|
|
33309
32721
|
texture.needsUpdate = true;
|
|
33310
32722
|
return texture;
|
|
33311
32723
|
}
|
|
33312
32724
|
|
|
33313
32725
|
// src/textures/create-copper-pour-texture-for-layer.ts
|
|
33314
|
-
import * as
|
|
32726
|
+
import * as THREE24 from "three";
|
|
33315
32727
|
import { CircuitToCanvasDrawer } from "circuit-to-canvas";
|
|
33316
32728
|
|
|
33317
32729
|
// src/geoms/brep-converter.ts
|
|
@@ -33517,6 +32929,190 @@ function createCopperPourTextureForLayer({
|
|
|
33517
32929
|
ctx.fillStyle = copperColor;
|
|
33518
32930
|
drawBrepShape({ ctx, pour, canvasXFromPcb, canvasYFromPcb });
|
|
33519
32931
|
}
|
|
32932
|
+
const texture = new THREE24.CanvasTexture(canvas);
|
|
32933
|
+
texture.generateMipmaps = true;
|
|
32934
|
+
texture.minFilter = THREE24.LinearMipmapLinearFilter;
|
|
32935
|
+
texture.magFilter = THREE24.LinearFilter;
|
|
32936
|
+
texture.anisotropy = 16;
|
|
32937
|
+
texture.needsUpdate = true;
|
|
32938
|
+
return texture;
|
|
32939
|
+
}
|
|
32940
|
+
|
|
32941
|
+
// src/textures/create-soldermask-texture-for-layer.ts
|
|
32942
|
+
import * as THREE25 from "three";
|
|
32943
|
+
|
|
32944
|
+
// src/textures/soldermask/soldermask-drawing.ts
|
|
32945
|
+
import { CircuitToCanvasDrawer as CircuitToCanvasDrawer2 } from "circuit-to-canvas";
|
|
32946
|
+
var toRgb = (colorArr) => {
|
|
32947
|
+
const [r = 0, g = 0, b = 0] = colorArr;
|
|
32948
|
+
return `rgb(${Math.round(r * 255)}, ${Math.round(g * 255)}, ${Math.round(
|
|
32949
|
+
b * 255
|
|
32950
|
+
)})`;
|
|
32951
|
+
};
|
|
32952
|
+
var getSoldermaskPalette = (material) => {
|
|
32953
|
+
const soldermask = toRgb(
|
|
32954
|
+
soldermaskColors[material] ?? colors.fr4SolderMaskGreen
|
|
32955
|
+
);
|
|
32956
|
+
const soldermaskOverCopper = material === "fr1" ? toRgb(colors.fr1TracesWithMaskCopper) : toRgb(colors.fr4TracesWithMaskGreen);
|
|
32957
|
+
return {
|
|
32958
|
+
soldermask,
|
|
32959
|
+
soldermaskOverCopper,
|
|
32960
|
+
copper: toRgb(colors.copper),
|
|
32961
|
+
transparent: "rgba(0,0,0,0)"
|
|
32962
|
+
};
|
|
32963
|
+
};
|
|
32964
|
+
var setDrawerBounds = (drawer, bounds) => {
|
|
32965
|
+
drawer.setCameraBounds({
|
|
32966
|
+
minX: bounds.minX,
|
|
32967
|
+
maxX: bounds.maxX,
|
|
32968
|
+
minY: bounds.minY,
|
|
32969
|
+
maxY: bounds.maxY
|
|
32970
|
+
});
|
|
32971
|
+
};
|
|
32972
|
+
var drawSoldermaskLayer = ({
|
|
32973
|
+
ctx,
|
|
32974
|
+
layer,
|
|
32975
|
+
bounds,
|
|
32976
|
+
elements,
|
|
32977
|
+
boardMaterial
|
|
32978
|
+
}) => {
|
|
32979
|
+
const palette = getSoldermaskPalette(boardMaterial);
|
|
32980
|
+
const copperRenderLayer = layer === "top" ? "top_copper" : "bottom_copper";
|
|
32981
|
+
const drawer = new CircuitToCanvasDrawer2(ctx);
|
|
32982
|
+
drawer.configure({
|
|
32983
|
+
colorOverrides: {
|
|
32984
|
+
copper: {
|
|
32985
|
+
top: palette.transparent,
|
|
32986
|
+
bottom: palette.transparent,
|
|
32987
|
+
inner1: palette.transparent,
|
|
32988
|
+
inner2: palette.transparent,
|
|
32989
|
+
inner3: palette.transparent,
|
|
32990
|
+
inner4: palette.transparent,
|
|
32991
|
+
inner5: palette.transparent,
|
|
32992
|
+
inner6: palette.transparent
|
|
32993
|
+
},
|
|
32994
|
+
drill: palette.transparent,
|
|
32995
|
+
boardOutline: palette.transparent,
|
|
32996
|
+
substrate: palette.transparent,
|
|
32997
|
+
keepout: palette.transparent,
|
|
32998
|
+
fabricationNote: palette.transparent,
|
|
32999
|
+
silkscreen: { top: palette.transparent, bottom: palette.transparent },
|
|
33000
|
+
courtyard: { top: palette.transparent, bottom: palette.transparent },
|
|
33001
|
+
soldermask: { top: palette.soldermask, bottom: palette.soldermask },
|
|
33002
|
+
soldermaskWithCopperUnderneath: {
|
|
33003
|
+
top: palette.soldermaskOverCopper,
|
|
33004
|
+
bottom: palette.soldermaskOverCopper
|
|
33005
|
+
},
|
|
33006
|
+
soldermaskOverCopper: {
|
|
33007
|
+
top: palette.soldermaskOverCopper,
|
|
33008
|
+
bottom: palette.soldermaskOverCopper
|
|
33009
|
+
}
|
|
33010
|
+
}
|
|
33011
|
+
});
|
|
33012
|
+
setDrawerBounds(drawer, bounds);
|
|
33013
|
+
drawer.drawElements(elements, {
|
|
33014
|
+
layers: [copperRenderLayer],
|
|
33015
|
+
drawSoldermask: true,
|
|
33016
|
+
drawSoldermaskTop: layer === "top",
|
|
33017
|
+
drawSoldermaskBottom: layer === "bottom"
|
|
33018
|
+
});
|
|
33019
|
+
const uncoveredPours = elements.filter(
|
|
33020
|
+
(e) => e.type === "pcb_copper_pour" && e.layer === layer && e.covered_with_solder_mask === false
|
|
33021
|
+
);
|
|
33022
|
+
if (uncoveredPours.length > 0) {
|
|
33023
|
+
ctx.save();
|
|
33024
|
+
ctx.globalCompositeOperation = "destination-out";
|
|
33025
|
+
const cutoutDrawer = new CircuitToCanvasDrawer2(ctx);
|
|
33026
|
+
cutoutDrawer.configure({
|
|
33027
|
+
colorOverrides: {
|
|
33028
|
+
copper: {
|
|
33029
|
+
top: palette.copper,
|
|
33030
|
+
bottom: palette.copper,
|
|
33031
|
+
inner1: palette.copper,
|
|
33032
|
+
inner2: palette.copper,
|
|
33033
|
+
inner3: palette.copper,
|
|
33034
|
+
inner4: palette.copper,
|
|
33035
|
+
inner5: palette.copper,
|
|
33036
|
+
inner6: palette.copper
|
|
33037
|
+
}
|
|
33038
|
+
}
|
|
33039
|
+
});
|
|
33040
|
+
setDrawerBounds(cutoutDrawer, bounds);
|
|
33041
|
+
cutoutDrawer.drawElements(uncoveredPours, { layers: [copperRenderLayer] });
|
|
33042
|
+
ctx.restore();
|
|
33043
|
+
}
|
|
33044
|
+
};
|
|
33045
|
+
|
|
33046
|
+
// src/textures/soldermask/soldermask-bounds.ts
|
|
33047
|
+
var boundsFromPanel = (panel) => ({
|
|
33048
|
+
minX: panel.center.x - panel.width / 2,
|
|
33049
|
+
maxX: panel.center.x + panel.width / 2,
|
|
33050
|
+
minY: panel.center.y - panel.height / 2,
|
|
33051
|
+
maxY: panel.center.y + panel.height / 2,
|
|
33052
|
+
width: panel.width,
|
|
33053
|
+
height: panel.height,
|
|
33054
|
+
centerX: panel.center.x,
|
|
33055
|
+
centerY: panel.center.y
|
|
33056
|
+
});
|
|
33057
|
+
var mergeBounds = (a, b) => {
|
|
33058
|
+
const minX = Math.min(a.minX, b.minX);
|
|
33059
|
+
const maxX = Math.max(a.maxX, b.maxX);
|
|
33060
|
+
const minY = Math.min(a.minY, b.minY);
|
|
33061
|
+
const maxY = Math.max(a.maxY, b.maxY);
|
|
33062
|
+
return {
|
|
33063
|
+
minX,
|
|
33064
|
+
maxX,
|
|
33065
|
+
minY,
|
|
33066
|
+
maxY,
|
|
33067
|
+
width: maxX - minX,
|
|
33068
|
+
height: maxY - minY,
|
|
33069
|
+
centerX: (minX + maxX) / 2,
|
|
33070
|
+
centerY: (minY + maxY) / 2
|
|
33071
|
+
};
|
|
33072
|
+
};
|
|
33073
|
+
var getSoldermaskRenderBounds = (circuitJson, boardData) => {
|
|
33074
|
+
const panels = circuitJson.filter(
|
|
33075
|
+
(e) => e.type === "pcb_panel"
|
|
33076
|
+
);
|
|
33077
|
+
const boards = circuitJson.filter(
|
|
33078
|
+
(e) => e.type === "pcb_board"
|
|
33079
|
+
);
|
|
33080
|
+
const activePanel = panels.find((panel) => panel.pcb_panel_id === boardData.pcb_board_id) ?? panels[0];
|
|
33081
|
+
if (activePanel && activePanel.width > 0 && activePanel.height > 0) {
|
|
33082
|
+
return boundsFromPanel(activePanel);
|
|
33083
|
+
}
|
|
33084
|
+
const boardsForBounds = boards.length > 1 ? boards : [boardData];
|
|
33085
|
+
return boardsForBounds.map((board) => calculateOutlineBounds(board)).reduce((acc, bounds) => mergeBounds(acc, bounds));
|
|
33086
|
+
};
|
|
33087
|
+
|
|
33088
|
+
// src/textures/create-soldermask-texture-for-layer.ts
|
|
33089
|
+
function createSoldermaskTextureForLayer({
|
|
33090
|
+
layer,
|
|
33091
|
+
circuitJson,
|
|
33092
|
+
boardData,
|
|
33093
|
+
traceTextureResolution = TRACE_TEXTURE_RESOLUTION
|
|
33094
|
+
}) {
|
|
33095
|
+
const bounds = getSoldermaskRenderBounds(circuitJson, boardData);
|
|
33096
|
+
const canvasWidth = Math.floor(bounds.width * traceTextureResolution);
|
|
33097
|
+
const canvasHeight = Math.floor(bounds.height * traceTextureResolution);
|
|
33098
|
+
if (canvasWidth <= 0 || canvasHeight <= 0) return null;
|
|
33099
|
+
const canvas = document.createElement("canvas");
|
|
33100
|
+
canvas.width = canvasWidth;
|
|
33101
|
+
canvas.height = canvasHeight;
|
|
33102
|
+
const ctx = canvas.getContext("2d");
|
|
33103
|
+
if (!ctx) return null;
|
|
33104
|
+
if (layer === "bottom") {
|
|
33105
|
+
ctx.translate(0, canvasHeight);
|
|
33106
|
+
ctx.scale(1, -1);
|
|
33107
|
+
}
|
|
33108
|
+
const elements = circuitJson.some((e) => e.type === "pcb_board") ? circuitJson : [boardData, ...circuitJson];
|
|
33109
|
+
drawSoldermaskLayer({
|
|
33110
|
+
ctx,
|
|
33111
|
+
layer,
|
|
33112
|
+
bounds,
|
|
33113
|
+
elements,
|
|
33114
|
+
boardMaterial: boardData.material
|
|
33115
|
+
});
|
|
33520
33116
|
const texture = new THREE25.CanvasTexture(canvas);
|
|
33521
33117
|
texture.generateMipmaps = true;
|
|
33522
33118
|
texture.minFilter = THREE25.LinearMipmapLinearFilter;
|
|
@@ -33527,7 +33123,7 @@ function createCopperPourTextureForLayer({
|
|
|
33527
33123
|
}
|
|
33528
33124
|
|
|
33529
33125
|
// src/textures/create-combined-board-textures.ts
|
|
33530
|
-
var
|
|
33126
|
+
var toRgb2 = (colorArr) => {
|
|
33531
33127
|
const [r = 0, g = 0, b = 0] = colorArr;
|
|
33532
33128
|
return `rgb(${Math.round(r * 255)}, ${Math.round(g * 255)}, ${Math.round(
|
|
33533
33129
|
b * 255
|
|
@@ -33550,7 +33146,7 @@ var createCombinedTexture = ({
|
|
|
33550
33146
|
if (canvasWidth <= 0 || canvasHeight <= 0) return null;
|
|
33551
33147
|
const canvas = document.createElement("canvas");
|
|
33552
33148
|
canvas.width = canvasWidth;
|
|
33553
|
-
canvas.height = canvasHeight;
|
|
33149
|
+
canvas.height = canvasHeight + 1;
|
|
33554
33150
|
const ctx = canvas.getContext("2d");
|
|
33555
33151
|
if (!ctx) return null;
|
|
33556
33152
|
textures.forEach((texture) => {
|
|
@@ -33559,9 +33155,10 @@ var createCombinedTexture = ({
|
|
|
33559
33155
|
ctx.drawImage(image, 0, 0, canvasWidth, canvasHeight);
|
|
33560
33156
|
});
|
|
33561
33157
|
const combinedTexture = new THREE26.CanvasTexture(canvas);
|
|
33562
|
-
combinedTexture.generateMipmaps =
|
|
33563
|
-
combinedTexture.minFilter = THREE26.
|
|
33158
|
+
combinedTexture.generateMipmaps = false;
|
|
33159
|
+
combinedTexture.minFilter = THREE26.LinearFilter;
|
|
33564
33160
|
combinedTexture.magFilter = THREE26.LinearFilter;
|
|
33161
|
+
combinedTexture.premultiplyAlpha = true;
|
|
33565
33162
|
combinedTexture.anisotropy = 16;
|
|
33566
33163
|
combinedTexture.needsUpdate = true;
|
|
33567
33164
|
return combinedTexture;
|
|
@@ -33572,13 +33169,10 @@ function createCombinedBoardTextures({
|
|
|
33572
33169
|
traceTextureResolution,
|
|
33573
33170
|
visibility
|
|
33574
33171
|
}) {
|
|
33575
|
-
const
|
|
33576
|
-
|
|
33577
|
-
);
|
|
33578
|
-
const traceColorWithMask = toRgb(colors.fr4TracesWithMaskGreen);
|
|
33579
|
-
const traceColorWithoutMask = toRgb(colors.fr4TracesWithoutMaskTan);
|
|
33172
|
+
const traceColorWithMask = toRgb2(colors.fr4TracesWithMaskGreen);
|
|
33173
|
+
const traceColorWithoutMask = toRgb2(colors.fr4TracesWithoutMaskTan);
|
|
33580
33174
|
const silkscreenColor = "rgb(255,255,255)";
|
|
33581
|
-
const copperColor =
|
|
33175
|
+
const copperColor = toRgb2(colors.copper);
|
|
33582
33176
|
const showBoardBody = visibility?.boardBody ?? true;
|
|
33583
33177
|
const buildForLayer = (layer) => {
|
|
33584
33178
|
const showMask = (layer === "top" ? visibility?.topMask : visibility?.bottomMask) ?? true;
|
|
@@ -33588,7 +33182,6 @@ function createCombinedBoardTextures({
|
|
|
33588
33182
|
layer,
|
|
33589
33183
|
circuitJson,
|
|
33590
33184
|
boardData,
|
|
33591
|
-
soldermaskColor,
|
|
33592
33185
|
traceTextureResolution
|
|
33593
33186
|
}) : null;
|
|
33594
33187
|
const traceTexture = showCopper ? createTraceTextureForLayer({
|
|
@@ -33633,11 +33226,11 @@ function createCombinedBoardTextures({
|
|
|
33633
33226
|
}) : null;
|
|
33634
33227
|
return createCombinedTexture({
|
|
33635
33228
|
textures: [
|
|
33636
|
-
soldermaskTexture,
|
|
33637
33229
|
copperPourTexture,
|
|
33638
33230
|
traceTexture,
|
|
33639
33231
|
copperTextTexture,
|
|
33640
33232
|
padTexture,
|
|
33233
|
+
soldermaskTexture,
|
|
33641
33234
|
silkscreenTexture,
|
|
33642
33235
|
panelOutlineTexture
|
|
33643
33236
|
],
|
|
@@ -33645,9 +33238,10 @@ function createCombinedBoardTextures({
|
|
|
33645
33238
|
traceTextureResolution
|
|
33646
33239
|
});
|
|
33647
33240
|
};
|
|
33241
|
+
const numLayers = boardData.num_layers ?? 2;
|
|
33648
33242
|
return {
|
|
33649
33243
|
topBoard: buildForLayer("top"),
|
|
33650
|
-
bottomBoard: buildForLayer("bottom")
|
|
33244
|
+
bottomBoard: numLayers < 2 ? null : buildForLayer("bottom")
|
|
33651
33245
|
};
|
|
33652
33246
|
}
|
|
33653
33247
|
|
|
@@ -33671,8 +33265,9 @@ function createTexturePlane(config, boardData) {
|
|
|
33671
33265
|
const material = new THREE27.MeshBasicMaterial({
|
|
33672
33266
|
map: texture,
|
|
33673
33267
|
transparent: true,
|
|
33268
|
+
alphaTest: 0.08,
|
|
33674
33269
|
side: THREE27.DoubleSide,
|
|
33675
|
-
depthWrite:
|
|
33270
|
+
depthWrite: true,
|
|
33676
33271
|
polygonOffset: usePolygonOffset,
|
|
33677
33272
|
polygonOffsetFactor: usePolygonOffset ? -4 : 0,
|
|
33678
33273
|
// Increased for better z-fighting prevention
|
|
@@ -33695,10 +33290,11 @@ function createTexturePlane(config, boardData) {
|
|
|
33695
33290
|
function createTextureMeshes(textures, boardData, pcbThickness, isFaux = false) {
|
|
33696
33291
|
const meshes = [];
|
|
33697
33292
|
if (!textures || !boardData || pcbThickness === null) return meshes;
|
|
33293
|
+
const SURFACE_OFFSET = 5e-3;
|
|
33698
33294
|
const topBoardMesh = createTexturePlane(
|
|
33699
33295
|
{
|
|
33700
33296
|
texture: textures.topBoard,
|
|
33701
|
-
yOffset: pcbThickness / 2 +
|
|
33297
|
+
yOffset: pcbThickness / 2 + SURFACE_OFFSET,
|
|
33702
33298
|
isBottomLayer: false,
|
|
33703
33299
|
usePolygonOffset: true,
|
|
33704
33300
|
renderOrder: 1,
|
|
@@ -33710,7 +33306,7 @@ function createTextureMeshes(textures, boardData, pcbThickness, isFaux = false)
|
|
|
33710
33306
|
const bottomBoardMesh = createTexturePlane(
|
|
33711
33307
|
{
|
|
33712
33308
|
texture: textures.bottomBoard,
|
|
33713
|
-
yOffset: -pcbThickness / 2 -
|
|
33309
|
+
yOffset: -pcbThickness / 2 - SURFACE_OFFSET,
|
|
33714
33310
|
isBottomLayer: true,
|
|
33715
33311
|
usePolygonOffset: true,
|
|
33716
33312
|
renderOrder: 1,
|
|
@@ -33764,7 +33360,7 @@ function JscadBoardTextures({
|
|
|
33764
33360
|
const panels = circuitJson.filter(
|
|
33765
33361
|
(e) => e.type === "pcb_panel"
|
|
33766
33362
|
);
|
|
33767
|
-
const boards =
|
|
33363
|
+
const boards = su9(circuitJson).pcb_board.list();
|
|
33768
33364
|
if (panels.length > 0) {
|
|
33769
33365
|
const panel = panels[0];
|
|
33770
33366
|
const firstBoardInPanel = boards.find(
|
|
@@ -33826,7 +33422,7 @@ function JscadBoardTextures({
|
|
|
33826
33422
|
}
|
|
33827
33423
|
material.dispose();
|
|
33828
33424
|
};
|
|
33829
|
-
const createTexturePlane2 = (texture, zOffset, isBottomLayer, name, usePolygonOffset = false, depthWrite =
|
|
33425
|
+
const createTexturePlane2 = (texture, zOffset, isBottomLayer, name, usePolygonOffset = false, depthWrite = true, renderOrder = 1) => {
|
|
33830
33426
|
if (!texture) return null;
|
|
33831
33427
|
const boardOutlineBounds = calculateOutlineBounds(boardData);
|
|
33832
33428
|
const planeGeom = new THREE28.PlaneGeometry(
|
|
@@ -33836,9 +33432,11 @@ function JscadBoardTextures({
|
|
|
33836
33432
|
const material = new THREE28.MeshBasicMaterial({
|
|
33837
33433
|
map: texture,
|
|
33838
33434
|
transparent: true,
|
|
33435
|
+
alphaTest: 0.08,
|
|
33839
33436
|
side: THREE28.DoubleSide,
|
|
33840
33437
|
depthWrite,
|
|
33841
33438
|
polygonOffset: usePolygonOffset,
|
|
33439
|
+
polygonOffsetFactor: usePolygonOffset ? -4 : 0,
|
|
33842
33440
|
polygonOffsetUnits: usePolygonOffset ? -4 : 0,
|
|
33843
33441
|
opacity: isFaux ? FAUX_BOARD_OPACITY : 1
|
|
33844
33442
|
});
|
|
@@ -33856,7 +33454,7 @@ function JscadBoardTextures({
|
|
|
33856
33454
|
mesh.frustumCulled = false;
|
|
33857
33455
|
return mesh;
|
|
33858
33456
|
};
|
|
33859
|
-
const SURFACE_OFFSET =
|
|
33457
|
+
const SURFACE_OFFSET = 5e-3;
|
|
33860
33458
|
const topBoardMesh = createTexturePlane2(
|
|
33861
33459
|
textures.topBoard,
|
|
33862
33460
|
pcbThickness / 2 + SURFACE_OFFSET,
|
|
@@ -33899,16 +33497,16 @@ function JscadBoardTextures({
|
|
|
33899
33497
|
}
|
|
33900
33498
|
|
|
33901
33499
|
// src/utils/preprocess-circuit-json.ts
|
|
33902
|
-
import { su as
|
|
33500
|
+
import { su as su11 } from "@tscircuit/circuit-json-util";
|
|
33903
33501
|
|
|
33904
33502
|
// src/utils/create-faux-board.ts
|
|
33905
|
-
import { su as
|
|
33503
|
+
import { su as su10, getBoundsOfPcbElements } from "@tscircuit/circuit-json-util";
|
|
33906
33504
|
function createFauxBoard(circuitJson) {
|
|
33907
|
-
const cadComponents =
|
|
33908
|
-
const pads =
|
|
33909
|
-
const holes =
|
|
33910
|
-
const platedHoles =
|
|
33911
|
-
const vias =
|
|
33505
|
+
const cadComponents = su10(circuitJson).cad_component.list();
|
|
33506
|
+
const pads = su10(circuitJson).pcb_smtpad.list();
|
|
33507
|
+
const holes = su10(circuitJson).pcb_hole.list();
|
|
33508
|
+
const platedHoles = su10(circuitJson).pcb_plated_hole.list();
|
|
33509
|
+
const vias = su10(circuitJson).pcb_via.list();
|
|
33912
33510
|
if (cadComponents.length === 0 && pads.length === 0 && holes.length === 0 && platedHoles.length === 0 && vias.length === 0) {
|
|
33913
33511
|
return null;
|
|
33914
33512
|
}
|
|
@@ -33957,7 +33555,7 @@ function createFauxBoard(circuitJson) {
|
|
|
33957
33555
|
|
|
33958
33556
|
// src/utils/preprocess-circuit-json.ts
|
|
33959
33557
|
function addFauxBoardIfNeeded(circuitJson) {
|
|
33960
|
-
const boards =
|
|
33558
|
+
const boards = su11(circuitJson).pcb_board.list();
|
|
33961
33559
|
if (boards.length > 0) {
|
|
33962
33560
|
return circuitJson;
|
|
33963
33561
|
}
|
|
@@ -34008,7 +33606,7 @@ var CadViewerJscad = forwardRef3(
|
|
|
34008
33606
|
const initialCameraPosition = useMemo20(() => {
|
|
34009
33607
|
if (!internalCircuitJson) return [5, -5, 5];
|
|
34010
33608
|
try {
|
|
34011
|
-
const board =
|
|
33609
|
+
const board = su12(internalCircuitJson).pcb_board.list()[0];
|
|
34012
33610
|
if (!board) return [5, -5, 5];
|
|
34013
33611
|
const { width: width10, height: height10 } = board;
|
|
34014
33612
|
if (!width10 && !height10) {
|
|
@@ -34034,7 +33632,7 @@ var CadViewerJscad = forwardRef3(
|
|
|
34034
33632
|
const isFauxBoard = useMemo20(() => {
|
|
34035
33633
|
if (!internalCircuitJson) return false;
|
|
34036
33634
|
try {
|
|
34037
|
-
const board =
|
|
33635
|
+
const board = su12(internalCircuitJson).pcb_board.list()[0];
|
|
34038
33636
|
return !!board && board.pcb_board_id === "faux-board";
|
|
34039
33637
|
} catch (e) {
|
|
34040
33638
|
return false;
|
|
@@ -34043,7 +33641,7 @@ var CadViewerJscad = forwardRef3(
|
|
|
34043
33641
|
const boardDimensions = useMemo20(() => {
|
|
34044
33642
|
if (!internalCircuitJson) return void 0;
|
|
34045
33643
|
try {
|
|
34046
|
-
const board =
|
|
33644
|
+
const board = su12(internalCircuitJson).pcb_board.list()[0];
|
|
34047
33645
|
if (!board) return void 0;
|
|
34048
33646
|
return { width: board.width ?? 0, height: board.height ?? 0 };
|
|
34049
33647
|
} catch (e) {
|
|
@@ -34054,7 +33652,7 @@ var CadViewerJscad = forwardRef3(
|
|
|
34054
33652
|
const boardCenter = useMemo20(() => {
|
|
34055
33653
|
if (!internalCircuitJson) return void 0;
|
|
34056
33654
|
try {
|
|
34057
|
-
const board =
|
|
33655
|
+
const board = su12(internalCircuitJson).pcb_board.list()[0];
|
|
34058
33656
|
if (!board || !board.center) return void 0;
|
|
34059
33657
|
return { x: board.center.x, y: board.center.y };
|
|
34060
33658
|
} catch (e) {
|
|
@@ -34064,7 +33662,7 @@ var CadViewerJscad = forwardRef3(
|
|
|
34064
33662
|
}, [internalCircuitJson]);
|
|
34065
33663
|
const pcbThickness = usePcbThickness(internalCircuitJson);
|
|
34066
33664
|
const { stls: boardStls, loading } = useStlsFromGeom(boardGeom);
|
|
34067
|
-
const cad_components =
|
|
33665
|
+
const cad_components = su12(internalCircuitJson).cad_component.list();
|
|
34068
33666
|
return /* @__PURE__ */ jsxs5(
|
|
34069
33667
|
CadViewerContainer,
|
|
34070
33668
|
{
|
|
@@ -34118,12 +33716,12 @@ var CadViewerJscad = forwardRef3(
|
|
|
34118
33716
|
);
|
|
34119
33717
|
|
|
34120
33718
|
// src/CadViewerManifold.tsx
|
|
34121
|
-
import { su as
|
|
33719
|
+
import { su as su18 } from "@tscircuit/circuit-json-util";
|
|
34122
33720
|
import { useEffect as useEffect25, useMemo as useMemo22, useState as useState16 } from "react";
|
|
34123
33721
|
import * as THREE35 from "three";
|
|
34124
33722
|
|
|
34125
33723
|
// src/hooks/useManifoldBoardBuilder.ts
|
|
34126
|
-
import { su as
|
|
33724
|
+
import { su as su17 } from "@tscircuit/circuit-json-util";
|
|
34127
33725
|
import { useEffect as useEffect24, useMemo as useMemo21, useRef as useRef9, useState as useState15 } from "react";
|
|
34128
33726
|
import * as THREE32 from "three";
|
|
34129
33727
|
|
|
@@ -34186,7 +33784,7 @@ function createManifoldBoard(Manifold, CrossSection, boardData, pcbThickness, ma
|
|
|
34186
33784
|
}
|
|
34187
33785
|
|
|
34188
33786
|
// src/utils/manifold/process-cutouts.ts
|
|
34189
|
-
import { su as
|
|
33787
|
+
import { su as su13 } from "@tscircuit/circuit-json-util";
|
|
34190
33788
|
|
|
34191
33789
|
// src/utils/pad-geoms.ts
|
|
34192
33790
|
var RECT_PAD_SEGMENTS2 = 64;
|
|
@@ -34245,7 +33843,7 @@ var arePointsClockwise3 = (points) => {
|
|
|
34245
33843
|
};
|
|
34246
33844
|
function processCutoutsForManifold(Manifold, CrossSection, circuitJson, pcbThickness, manifoldInstancesForCleanup) {
|
|
34247
33845
|
const cutoutOps = [];
|
|
34248
|
-
const pcbCutouts =
|
|
33846
|
+
const pcbCutouts = su13(circuitJson).pcb_cutout.list();
|
|
34249
33847
|
for (const cutout of pcbCutouts) {
|
|
34250
33848
|
let cutoutOp;
|
|
34251
33849
|
const cutoutHeight = pcbThickness * 1.5;
|
|
@@ -34340,7 +33938,7 @@ function processCutoutsForManifold(Manifold, CrossSection, circuitJson, pcbThick
|
|
|
34340
33938
|
}
|
|
34341
33939
|
|
|
34342
33940
|
// src/utils/manifold/process-non-plated-holes.ts
|
|
34343
|
-
import { su as
|
|
33941
|
+
import { su as su14 } from "@tscircuit/circuit-json-util";
|
|
34344
33942
|
|
|
34345
33943
|
// src/utils/hole-geoms.ts
|
|
34346
33944
|
function createCircleHoleDrill({
|
|
@@ -34383,7 +33981,7 @@ function createPlatedHoleDrill({
|
|
|
34383
33981
|
// src/utils/manifold/process-non-plated-holes.ts
|
|
34384
33982
|
function processNonPlatedHolesForManifold(Manifold, CrossSection, circuitJson, pcbThickness, manifoldInstancesForCleanup) {
|
|
34385
33983
|
const nonPlatedHoleBoardDrills = [];
|
|
34386
|
-
const pcbHoles =
|
|
33984
|
+
const pcbHoles = su14(circuitJson).pcb_hole.list();
|
|
34387
33985
|
const createPillOp = (width10, height10, depth) => {
|
|
34388
33986
|
const pillOp = createRoundedRectPrism({
|
|
34389
33987
|
Manifold,
|
|
@@ -34466,7 +34064,7 @@ function processNonPlatedHolesForManifold(Manifold, CrossSection, circuitJson, p
|
|
|
34466
34064
|
}
|
|
34467
34065
|
|
|
34468
34066
|
// src/utils/manifold/process-plated-holes.ts
|
|
34469
|
-
import { su as
|
|
34067
|
+
import { su as su15 } from "@tscircuit/circuit-json-util";
|
|
34470
34068
|
import * as THREE30 from "three";
|
|
34471
34069
|
|
|
34472
34070
|
// src/utils/manifold-mesh-to-three-geometry.ts
|
|
@@ -34513,9 +34111,12 @@ var createEllipsePoints = (width10, height10, segments) => {
|
|
|
34513
34111
|
};
|
|
34514
34112
|
var COPPER_COLOR = new THREE30.Color(...colors.copper);
|
|
34515
34113
|
var PLATED_HOLE_LIP_HEIGHT = 0.05;
|
|
34114
|
+
var PLATED_HOLE_PAD_THICKNESS = 3e-3;
|
|
34115
|
+
var PLATED_HOLE_SURFACE_CLEARANCE = 5e-4;
|
|
34116
|
+
var PLATED_HOLE_FILL_CLEARANCE = 4e-3;
|
|
34516
34117
|
function processPlatedHolesForManifold(Manifold, CrossSection, circuitJson, pcbThickness, manifoldInstancesForCleanup, boardClipVolume) {
|
|
34517
34118
|
const platedHoleBoardDrills = [];
|
|
34518
|
-
const pcbPlatedHoles =
|
|
34119
|
+
const pcbPlatedHoles = su15(circuitJson).pcb_plated_hole.list();
|
|
34519
34120
|
const platedHoleCopperGeoms = [];
|
|
34520
34121
|
const platedHoleCopperOpsForSubtract = [];
|
|
34521
34122
|
const createPillOp = (width10, height10, depth) => {
|
|
@@ -34732,7 +34333,7 @@ function processPlatedHolesForManifold(Manifold, CrossSection, circuitJson, pcbT
|
|
|
34732
34333
|
const padWidth = ph.rect_pad_width;
|
|
34733
34334
|
const padHeight = ph.rect_pad_height;
|
|
34734
34335
|
const rectBorderRadius = extractRectBorderRadius(ph);
|
|
34735
|
-
const padThickness =
|
|
34336
|
+
const padThickness = PLATED_HOLE_PAD_THICKNESS;
|
|
34736
34337
|
const drillW = holeW + 2 * MANIFOLD_Z_OFFSET;
|
|
34737
34338
|
const drillH = holeH + 2 * MANIFOLD_Z_OFFSET;
|
|
34738
34339
|
const drillDepth = pcbThickness * 1.2;
|
|
@@ -34752,8 +34353,11 @@ function processPlatedHolesForManifold(Manifold, CrossSection, circuitJson, pcbT
|
|
|
34752
34353
|
Manifold,
|
|
34753
34354
|
width: padWidth,
|
|
34754
34355
|
height: padHeight,
|
|
34755
|
-
thickness:
|
|
34756
|
-
|
|
34356
|
+
thickness: Math.max(
|
|
34357
|
+
pcbThickness - 2 * (padThickness + PLATED_HOLE_SURFACE_CLEARANCE + PLATED_HOLE_FILL_CLEARANCE),
|
|
34358
|
+
M
|
|
34359
|
+
),
|
|
34360
|
+
// Fill between pads (recessed from board surfaces)
|
|
34757
34361
|
borderRadius: rectBorderRadius
|
|
34758
34362
|
});
|
|
34759
34363
|
manifoldInstancesForCleanup.push(mainFill);
|
|
@@ -34763,20 +34367,28 @@ function processPlatedHolesForManifold(Manifold, CrossSection, circuitJson, pcbT
|
|
|
34763
34367
|
height: padHeight,
|
|
34764
34368
|
thickness: padThickness,
|
|
34765
34369
|
borderRadius: rectBorderRadius
|
|
34766
|
-
}).translate([
|
|
34370
|
+
}).translate([
|
|
34371
|
+
0,
|
|
34372
|
+
0,
|
|
34373
|
+
pcbThickness / 2 + PLATED_HOLE_SURFACE_CLEARANCE + padThickness / 2
|
|
34374
|
+
]);
|
|
34767
34375
|
const bottomPad = createRoundedRectPrism({
|
|
34768
34376
|
Manifold,
|
|
34769
34377
|
width: padWidth,
|
|
34770
34378
|
height: padHeight,
|
|
34771
34379
|
thickness: padThickness,
|
|
34772
34380
|
borderRadius: rectBorderRadius
|
|
34773
|
-
}).translate([
|
|
34381
|
+
}).translate([
|
|
34382
|
+
0,
|
|
34383
|
+
0,
|
|
34384
|
+
-pcbThickness / 2 - PLATED_HOLE_SURFACE_CLEARANCE - padThickness / 2
|
|
34385
|
+
]);
|
|
34774
34386
|
manifoldInstancesForCleanup.push(topPad, bottomPad);
|
|
34775
34387
|
const barrelPill = createPillOp(
|
|
34776
34388
|
holeW,
|
|
34777
34389
|
holeH,
|
|
34778
|
-
pcbThickness *
|
|
34779
|
-
// Slightly
|
|
34390
|
+
pcbThickness * 0.8
|
|
34391
|
+
// Slightly shorter than board
|
|
34780
34392
|
).translate([holeOffsetX, holeOffsetY, 0]);
|
|
34781
34393
|
manifoldInstancesForCleanup.push(barrelPill);
|
|
34782
34394
|
const copperUnion = Manifold.union([
|
|
@@ -34824,7 +34436,7 @@ function processPlatedHolesForManifold(Manifold, CrossSection, circuitJson, pcbT
|
|
|
34824
34436
|
const padWidth = ph.rect_pad_width;
|
|
34825
34437
|
const padHeight = ph.rect_pad_height;
|
|
34826
34438
|
const rectBorderRadius = extractRectBorderRadius(ph);
|
|
34827
|
-
const padThickness =
|
|
34439
|
+
const padThickness = PLATED_HOLE_PAD_THICKNESS;
|
|
34828
34440
|
const drillW = holeW + 2 * MANIFOLD_Z_OFFSET;
|
|
34829
34441
|
const drillH = holeH + 2 * MANIFOLD_Z_OFFSET;
|
|
34830
34442
|
const drillDepth = pcbThickness * 1.2;
|
|
@@ -34851,8 +34463,11 @@ function processPlatedHolesForManifold(Manifold, CrossSection, circuitJson, pcbT
|
|
|
34851
34463
|
Manifold,
|
|
34852
34464
|
width: padWidth,
|
|
34853
34465
|
height: padHeight,
|
|
34854
|
-
thickness:
|
|
34855
|
-
|
|
34466
|
+
thickness: Math.max(
|
|
34467
|
+
pcbThickness - 2 * (padThickness + PLATED_HOLE_SURFACE_CLEARANCE + PLATED_HOLE_FILL_CLEARANCE),
|
|
34468
|
+
M
|
|
34469
|
+
),
|
|
34470
|
+
// Fill between pads (recessed from board surfaces)
|
|
34856
34471
|
borderRadius: rectBorderRadius
|
|
34857
34472
|
});
|
|
34858
34473
|
manifoldInstancesForCleanup.push(mainFill);
|
|
@@ -34867,7 +34482,11 @@ function processPlatedHolesForManifold(Manifold, CrossSection, circuitJson, pcbT
|
|
|
34867
34482
|
height: padHeight,
|
|
34868
34483
|
thickness: padThickness,
|
|
34869
34484
|
borderRadius: rectBorderRadius
|
|
34870
|
-
}).translate([
|
|
34485
|
+
}).translate([
|
|
34486
|
+
0,
|
|
34487
|
+
0,
|
|
34488
|
+
pcbThickness / 2 + PLATED_HOLE_SURFACE_CLEARANCE + padThickness / 2
|
|
34489
|
+
]);
|
|
34871
34490
|
if (ph.rect_ccw_rotation) {
|
|
34872
34491
|
const rotatedTopPad = topPad.rotate([0, 0, ph.rect_ccw_rotation]);
|
|
34873
34492
|
manifoldInstancesForCleanup.push(rotatedTopPad);
|
|
@@ -34879,7 +34498,11 @@ function processPlatedHolesForManifold(Manifold, CrossSection, circuitJson, pcbT
|
|
|
34879
34498
|
height: padHeight,
|
|
34880
34499
|
thickness: padThickness,
|
|
34881
34500
|
borderRadius: rectBorderRadius
|
|
34882
|
-
}).translate([
|
|
34501
|
+
}).translate([
|
|
34502
|
+
0,
|
|
34503
|
+
0,
|
|
34504
|
+
-pcbThickness / 2 - PLATED_HOLE_SURFACE_CLEARANCE - padThickness / 2
|
|
34505
|
+
]);
|
|
34883
34506
|
if (ph.rect_ccw_rotation) {
|
|
34884
34507
|
const rotatedBottomPad = bottomPad.rotate([0, 0, ph.rect_ccw_rotation]);
|
|
34885
34508
|
manifoldInstancesForCleanup.push(rotatedBottomPad);
|
|
@@ -34889,7 +34512,7 @@ function processPlatedHolesForManifold(Manifold, CrossSection, circuitJson, pcbT
|
|
|
34889
34512
|
let barrelPill = createPillOp(
|
|
34890
34513
|
holeW,
|
|
34891
34514
|
holeH,
|
|
34892
|
-
pcbThickness *
|
|
34515
|
+
pcbThickness * 0.8
|
|
34893
34516
|
// Slightly taller than board
|
|
34894
34517
|
).translate([holeOffsetX, holeOffsetY, 0]);
|
|
34895
34518
|
if (ph.hole_ccw_rotation) {
|
|
@@ -34951,9 +34574,9 @@ function processPlatedHolesForManifold(Manifold, CrossSection, circuitJson, pcbT
|
|
|
34951
34574
|
const translatedBoardHole = boardHoleOp.translate([ph.x, ph.y, 0]);
|
|
34952
34575
|
manifoldInstancesForCleanup.push(translatedBoardHole);
|
|
34953
34576
|
platedHoleBoardDrills.push(translatedBoardHole);
|
|
34954
|
-
const padThickness =
|
|
34577
|
+
const padThickness = PLATED_HOLE_PAD_THICKNESS;
|
|
34955
34578
|
const fillThickness = Math.max(
|
|
34956
|
-
pcbThickness - 2 * padThickness
|
|
34579
|
+
pcbThickness - 2 * (padThickness + PLATED_HOLE_SURFACE_CLEARANCE + PLATED_HOLE_FILL_CLEARANCE),
|
|
34957
34580
|
M
|
|
34958
34581
|
);
|
|
34959
34582
|
const mainFill = createPolygonPadOp({
|
|
@@ -34969,12 +34592,12 @@ function processPlatedHolesForManifold(Manifold, CrossSection, circuitJson, pcbT
|
|
|
34969
34592
|
const topTranslated = topPad.translate([
|
|
34970
34593
|
0,
|
|
34971
34594
|
0,
|
|
34972
|
-
pcbThickness / 2
|
|
34595
|
+
pcbThickness / 2 + PLATED_HOLE_SURFACE_CLEARANCE + padThickness / 2
|
|
34973
34596
|
]);
|
|
34974
34597
|
const bottomTranslated = bottomPad.translate([
|
|
34975
34598
|
0,
|
|
34976
34599
|
0,
|
|
34977
|
-
-pcbThickness / 2
|
|
34600
|
+
-pcbThickness / 2 - PLATED_HOLE_SURFACE_CLEARANCE - padThickness / 2
|
|
34978
34601
|
]);
|
|
34979
34602
|
manifoldInstancesForCleanup.push(topTranslated, bottomTranslated);
|
|
34980
34603
|
const barrelOp = createHoleOpForPolygonPad({
|
|
@@ -34984,7 +34607,7 @@ function processPlatedHolesForManifold(Manifold, CrossSection, circuitJson, pcbT
|
|
|
34984
34607
|
if (!barrelOp) return;
|
|
34985
34608
|
const holeCutOp = createHoleOpForPolygonPad({
|
|
34986
34609
|
ph,
|
|
34987
|
-
depth: pcbThickness *
|
|
34610
|
+
depth: pcbThickness * 0.8,
|
|
34988
34611
|
sizeDelta: -2 * M
|
|
34989
34612
|
}) || barrelOp;
|
|
34990
34613
|
const copperUnion = Manifold.union([
|
|
@@ -35134,14 +34757,17 @@ function processPlatedHolesForManifold(Manifold, CrossSection, circuitJson, pcbT
|
|
|
35134
34757
|
const padWidth = ph.rect_pad_width ?? ph.hole_diameter;
|
|
35135
34758
|
const padHeight = ph.rect_pad_height ?? ph.hole_diameter;
|
|
35136
34759
|
const rectBorderRadius = extractRectBorderRadius(ph);
|
|
35137
|
-
const padThickness =
|
|
34760
|
+
const padThickness = PLATED_HOLE_PAD_THICKNESS;
|
|
35138
34761
|
const holeRadius = ph.hole_diameter / 2;
|
|
35139
34762
|
const mainFill = createRoundedRectPrism({
|
|
35140
34763
|
Manifold,
|
|
35141
34764
|
width: padWidth,
|
|
35142
34765
|
height: padHeight,
|
|
35143
|
-
thickness:
|
|
35144
|
-
|
|
34766
|
+
thickness: Math.max(
|
|
34767
|
+
pcbThickness - 2 * (padThickness + PLATED_HOLE_SURFACE_CLEARANCE + PLATED_HOLE_FILL_CLEARANCE),
|
|
34768
|
+
M
|
|
34769
|
+
),
|
|
34770
|
+
// Fill between pads (recessed from board surfaces)
|
|
35145
34771
|
borderRadius: rectBorderRadius
|
|
35146
34772
|
});
|
|
35147
34773
|
manifoldInstancesForCleanup.push(mainFill);
|
|
@@ -35151,17 +34777,25 @@ function processPlatedHolesForManifold(Manifold, CrossSection, circuitJson, pcbT
|
|
|
35151
34777
|
height: padHeight,
|
|
35152
34778
|
thickness: padThickness,
|
|
35153
34779
|
borderRadius: rectBorderRadius
|
|
35154
|
-
}).translate([
|
|
34780
|
+
}).translate([
|
|
34781
|
+
0,
|
|
34782
|
+
0,
|
|
34783
|
+
pcbThickness / 2 + PLATED_HOLE_SURFACE_CLEARANCE + padThickness / 2
|
|
34784
|
+
]);
|
|
35155
34785
|
const bottomPad = createRoundedRectPrism({
|
|
35156
34786
|
Manifold,
|
|
35157
34787
|
width: padWidth,
|
|
35158
34788
|
height: padHeight,
|
|
35159
34789
|
thickness: padThickness,
|
|
35160
34790
|
borderRadius: rectBorderRadius
|
|
35161
|
-
}).translate([
|
|
34791
|
+
}).translate([
|
|
34792
|
+
0,
|
|
34793
|
+
0,
|
|
34794
|
+
-pcbThickness / 2 - PLATED_HOLE_SURFACE_CLEARANCE - padThickness / 2
|
|
34795
|
+
]);
|
|
35162
34796
|
manifoldInstancesForCleanup.push(topPad, bottomPad);
|
|
35163
34797
|
const barrelCylinder = Manifold.cylinder(
|
|
35164
|
-
pcbThickness *
|
|
34798
|
+
pcbThickness * 0.8,
|
|
35165
34799
|
// Slightly taller than board
|
|
35166
34800
|
holeRadius,
|
|
35167
34801
|
holeRadius,
|
|
@@ -35216,7 +34850,7 @@ function processPlatedHolesForManifold(Manifold, CrossSection, circuitJson, pcbT
|
|
|
35216
34850
|
}
|
|
35217
34851
|
|
|
35218
34852
|
// src/utils/manifold/process-vias.ts
|
|
35219
|
-
import { su as
|
|
34853
|
+
import { su as su16 } from "@tscircuit/circuit-json-util";
|
|
35220
34854
|
import * as THREE31 from "three";
|
|
35221
34855
|
|
|
35222
34856
|
// src/utils/via-geoms.ts
|
|
@@ -35273,7 +34907,7 @@ function createViaCopper2({
|
|
|
35273
34907
|
var COPPER_COLOR2 = new THREE31.Color(...colors.copper);
|
|
35274
34908
|
function processViasForManifold(Manifold, circuitJson, pcbThickness, manifoldInstancesForCleanup, boardClipVolume) {
|
|
35275
34909
|
const viaBoardDrills = [];
|
|
35276
|
-
const pcbVias =
|
|
34910
|
+
const pcbVias = su16(circuitJson).pcb_via.list();
|
|
35277
34911
|
const viaCopperGeoms = [];
|
|
35278
34912
|
pcbVias.forEach((via, index2) => {
|
|
35279
34913
|
if (typeof via.hole_diameter === "number") {
|
|
@@ -35332,7 +34966,7 @@ var useManifoldBoardBuilder = (manifoldJSModule, circuitJson, visibility) => {
|
|
|
35332
34966
|
const panels = circuitJson.filter(
|
|
35333
34967
|
(e) => e.type === "pcb_panel"
|
|
35334
34968
|
);
|
|
35335
|
-
const boards =
|
|
34969
|
+
const boards = su17(circuitJson).pcb_board.list();
|
|
35336
34970
|
if (panels.length > 0) {
|
|
35337
34971
|
const panel = panels[0];
|
|
35338
34972
|
const firstBoardInPanel = boards.find(
|
|
@@ -35353,7 +34987,7 @@ var useManifoldBoardBuilder = (manifoldJSModule, circuitJson, visibility) => {
|
|
|
35353
34987
|
return boardsNotInPanel.length > 0 ? boardsNotInPanel[0] : null;
|
|
35354
34988
|
}, [circuitJson]);
|
|
35355
34989
|
const isFauxBoard = useMemo21(() => {
|
|
35356
|
-
const boards =
|
|
34990
|
+
const boards = su17(circuitJson).pcb_board.list();
|
|
35357
34991
|
return boards.length > 0 && boards[0].pcb_board_id === "faux-board";
|
|
35358
34992
|
}, [circuitJson]);
|
|
35359
34993
|
const traceTextureResolution = useMemo21(() => {
|
|
@@ -35588,7 +35222,10 @@ var createBoardMaterial = ({
|
|
|
35588
35222
|
clearcoat: 0,
|
|
35589
35223
|
transparent: isFaux,
|
|
35590
35224
|
opacity: isFaux ? FAUX_BOARD_OPACITY : 1,
|
|
35591
|
-
flatShading: true
|
|
35225
|
+
flatShading: true,
|
|
35226
|
+
polygonOffset: true,
|
|
35227
|
+
polygonOffsetFactor: 1,
|
|
35228
|
+
polygonOffsetUnits: 1
|
|
35592
35229
|
});
|
|
35593
35230
|
}
|
|
35594
35231
|
return new THREE33.MeshStandardMaterial({
|
|
@@ -35598,7 +35235,10 @@ var createBoardMaterial = ({
|
|
|
35598
35235
|
metalness: 0.1,
|
|
35599
35236
|
roughness: 0.8,
|
|
35600
35237
|
transparent: true,
|
|
35601
|
-
opacity: isFaux ? FAUX_BOARD_OPACITY : 0.9
|
|
35238
|
+
opacity: isFaux ? FAUX_BOARD_OPACITY : 0.9,
|
|
35239
|
+
polygonOffset: true,
|
|
35240
|
+
polygonOffsetFactor: 1,
|
|
35241
|
+
polygonOffsetUnits: 1
|
|
35602
35242
|
});
|
|
35603
35243
|
};
|
|
35604
35244
|
|
|
@@ -35627,11 +35267,8 @@ function createGeometryMeshes(geoms) {
|
|
|
35627
35267
|
new THREE34.MeshStandardMaterial({
|
|
35628
35268
|
color: comp.color,
|
|
35629
35269
|
side: THREE34.DoubleSide,
|
|
35630
|
-
flatShading: true
|
|
35270
|
+
flatShading: true
|
|
35631
35271
|
// Consistent with board
|
|
35632
|
-
polygonOffset: true,
|
|
35633
|
-
polygonOffsetFactor: -1,
|
|
35634
|
-
polygonOffsetUnits: -1
|
|
35635
35272
|
})
|
|
35636
35273
|
);
|
|
35637
35274
|
mesh.name = comp.key;
|
|
@@ -35808,7 +35445,7 @@ try {
|
|
|
35808
35445
|
[textures, boardData, pcbThickness, isFauxBoard]
|
|
35809
35446
|
);
|
|
35810
35447
|
const cadComponents = useMemo22(
|
|
35811
|
-
() =>
|
|
35448
|
+
() => su18(circuitJson).cad_component.list(),
|
|
35812
35449
|
[circuitJson]
|
|
35813
35450
|
);
|
|
35814
35451
|
const boardDimensions = useMemo22(() => {
|
|
@@ -42822,7 +42459,7 @@ var CadViewer = (props) => {
|
|
|
42822
42459
|
|
|
42823
42460
|
// src/convert-circuit-json-to-3d-svg.ts
|
|
42824
42461
|
var import_debug = __toESM(require_browser(), 1);
|
|
42825
|
-
import { su as
|
|
42462
|
+
import { su as su19 } from "@tscircuit/circuit-json-util";
|
|
42826
42463
|
import * as THREE40 from "three";
|
|
42827
42464
|
import { SVGRenderer } from "three/examples/jsm/renderers/SVGRenderer.js";
|
|
42828
42465
|
|
|
@@ -43051,11 +42688,11 @@ async function convertCircuitJsonTo3dSvg(circuitJson, options = {}) {
|
|
|
43051
42688
|
const pointLight = new THREE40.PointLight(16777215, Math.PI / 4);
|
|
43052
42689
|
pointLight.position.set(-10, -10, 10);
|
|
43053
42690
|
scene.add(pointLight);
|
|
43054
|
-
const components =
|
|
42691
|
+
const components = su19(circuitJson).cad_component.list();
|
|
43055
42692
|
for (const component of components) {
|
|
43056
42693
|
await renderComponent(component, scene);
|
|
43057
42694
|
}
|
|
43058
|
-
const boardData =
|
|
42695
|
+
const boardData = su19(circuitJson).pcb_board.list()[0];
|
|
43059
42696
|
const boardGeom = createBoardGeomFromCircuitJson(circuitJson);
|
|
43060
42697
|
if (boardGeom) {
|
|
43061
42698
|
const solderMaskColor = colors.fr4SolderMaskGreen;
|