@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.
Files changed (2) hide show
  1. package/dist/index.js +396 -759
  2. 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 su13 } from "@tscircuit/circuit-json-util";
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.514",
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.49",
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 platedHoleLipHeight = 0.02;
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 throughDrillHeight = ctx.pcbThickness + 2 * platedHoleLipHeight + 4 * M;
29962
- const topSurfaceZ = ctx.pcbThickness / 2 + BOARD_SURFACE_OFFSET.copper;
29963
- const bottomSurfaceZ = -ctx.pcbThickness / 2 - BOARD_SURFACE_OFFSET.copper;
29964
- const copperSpan = topSurfaceZ - bottomSurfaceZ;
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 = copperSpan + 0.01;
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: copperSpan + 0.01,
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: platedHoleLipHeight,
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: platedHoleLipHeight,
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: height10 }, rect2d);
30048
+ const extruded = (0, import_extrusions3.extrudeLinear)({ height: copperFillHeight }, rect2d);
30052
30049
  return (0, import_transforms4.translate)(
30053
- [plated_hole.x, plated_hole.y, centerZ - height10 / 2],
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: copperSpan
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 barrel = (0, import_primitives4.cylinder)({
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
- finalCopper = (0, import_booleans2.subtract)(finalCopper, drill);
30096
- finalCopper = (0, import_booleans2.intersect)(finalCopper, options.clipGeom);
30097
- return (0, import_colors2.colorize)(colors.copper, finalCopper);
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, (0, import_booleans2.subtract)(finalCopper, drill));
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 = copperSpan + 0.01;
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
- copperSpan
30176
+ barrelHeight
30193
30177
  ] : [
30194
30178
  rectLength + 2 * barrelMargin,
30195
30179
  holeHeight + 2 * barrelMargin,
30196
- copperSpan
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: copperSpan
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: copperSpan
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 * 1.1] : [rectLength, holeHeight, throughDrillHeight * 1.1]
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 * 1.1
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 * 1.1
30239
+ height: throughDrillHeight
30256
30240
  })
30257
30241
  );
30258
30242
  const copperTopPad = createRectPadGeom({
30259
30243
  width: padWidth,
30260
30244
  height: padHeight,
30261
- thickness: platedHoleLipHeight,
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: platedHoleLipHeight,
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: height10 }, rect2d);
30262
+ const extruded = (0, import_extrusions3.extrudeLinear)({ height: copperFillHeight }, rect2d);
30283
30263
  return (0, import_transforms4.translate)(
30284
- [plated_hole.x, plated_hole.y, centerZ - height10 / 2],
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 * 1.1] : [rectLength - 2 * M, holeHeight - 2 * M, throughDrillHeight * 1.1]
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 * 1.1
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 * 1.1
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
- copperSpan
30352
+ barrelHeight
30373
30353
  ] : [
30374
30354
  shortDim + 2 * barrelMargin,
30375
30355
  rectLength + 2 * barrelMargin,
30376
- copperSpan
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: copperSpan
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: copperSpan
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 * 1.1] : [shortDim, rectLength, throughDrillHeight * 1.1]
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 * 1.1
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 * 1.1
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: platedHoleLipHeight,
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: platedHoleLipHeight,
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: height10 }, rect2d);
30414
+ const extruded = (0, import_extrusions3.extrudeLinear)({ height: copperFillHeight }, rect2d);
30439
30415
  return (0, import_transforms4.translate)(
30440
- [plated_hole.x, plated_hole.y, centerZ - height10 / 2],
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 * 1.1] : [shortDim - 2 * M, rectLength - 2 * M, throughDrillHeight * 1.1]
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 * 1.1
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 * 1.1
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 = (topSurfaceZ + bottomSurfaceZ) / 2;
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
- Math.max(copperSpan - platedHoleLipHeight * 2, M),
30495
- centerZ
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, copperSpan);
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
- return new THREE18.Mesh(geom, material);
31400
- }, [geom, color, opacity]);
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(STLModel, { stlData, color, opacity });
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 su10 } from "@tscircuit/circuit-json-util";
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 THREE24 from "three";
33192
- import { su as su9 } from "@tscircuit/circuit-json-util";
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 = su9(circuitJson).pcb_trace.list();
33204
- const allPcbVias = su9(circuitJson).pcb_via.list();
33205
- const allPcbPlatedHoles = su9(
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 THREE24.CanvasTexture(canvas);
32716
+ const texture = new THREE23.CanvasTexture(canvas);
33305
32717
  texture.generateMipmaps = true;
33306
- texture.minFilter = THREE24.LinearMipmapLinearFilter;
33307
- texture.magFilter = THREE24.LinearFilter;
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 THREE25 from "three";
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 toRgb = (colorArr) => {
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 = true;
33563
- combinedTexture.minFilter = THREE26.LinearMipmapLinearFilter;
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 soldermaskColor = toRgb(
33576
- soldermaskColors[boardData.material] ?? colors.fr4SolderMaskGreen
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 = toRgb(colors.copper);
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: false,
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 + 1e-3,
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 - 1e-3,
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 = su10(circuitJson).pcb_board.list();
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 = false, renderOrder = 1) => {
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 = 1e-3;
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 su12 } from "@tscircuit/circuit-json-util";
33500
+ import { su as su11 } from "@tscircuit/circuit-json-util";
33903
33501
 
33904
33502
  // src/utils/create-faux-board.ts
33905
- import { su as su11, getBoundsOfPcbElements } from "@tscircuit/circuit-json-util";
33503
+ import { su as su10, getBoundsOfPcbElements } from "@tscircuit/circuit-json-util";
33906
33504
  function createFauxBoard(circuitJson) {
33907
- const cadComponents = su11(circuitJson).cad_component.list();
33908
- const pads = su11(circuitJson).pcb_smtpad.list();
33909
- const holes = su11(circuitJson).pcb_hole.list();
33910
- const platedHoles = su11(circuitJson).pcb_plated_hole.list();
33911
- const vias = su11(circuitJson).pcb_via.list();
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 = su12(circuitJson).pcb_board.list();
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 = su13(internalCircuitJson).pcb_board.list()[0];
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 = su13(internalCircuitJson).pcb_board.list()[0];
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 = su13(internalCircuitJson).pcb_board.list()[0];
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 = su13(internalCircuitJson).pcb_board.list()[0];
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 = su13(internalCircuitJson).cad_component.list();
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 su19 } from "@tscircuit/circuit-json-util";
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 su18 } from "@tscircuit/circuit-json-util";
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 su14 } from "@tscircuit/circuit-json-util";
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 = su14(circuitJson).pcb_cutout.list();
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 su15 } from "@tscircuit/circuit-json-util";
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 = su15(circuitJson).pcb_hole.list();
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 su16 } from "@tscircuit/circuit-json-util";
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 = su16(circuitJson).pcb_plated_hole.list();
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 = DEFAULT_SMT_PAD_THICKNESS;
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: pcbThickness - 2 * padThickness - 2 * BOARD_SURFACE_OFFSET.copper + 0.1,
34756
- // Fill between pads
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([0, 0, pcbThickness / 2 / 2 + BOARD_SURFACE_OFFSET.copper]);
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([0, 0, -pcbThickness / 2 / 2 - BOARD_SURFACE_OFFSET.copper]);
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 * 1.02
34779
- // Slightly taller than board
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 = DEFAULT_SMT_PAD_THICKNESS;
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: pcbThickness - 2 * padThickness - 2 * BOARD_SURFACE_OFFSET.copper + 0.1,
34855
- // Fill between pads
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([0, 0, pcbThickness / 2 / 2 + BOARD_SURFACE_OFFSET.copper]);
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([0, 0, -pcbThickness / 2 / 2 - BOARD_SURFACE_OFFSET.copper]);
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 * 1.02
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 = DEFAULT_SMT_PAD_THICKNESS;
34577
+ const padThickness = PLATED_HOLE_PAD_THICKNESS;
34955
34578
  const fillThickness = Math.max(
34956
- pcbThickness - 2 * padThickness - 2 * BOARD_SURFACE_OFFSET.copper + 0.1,
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 / 2 + BOARD_SURFACE_OFFSET.copper
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 / 2 - BOARD_SURFACE_OFFSET.copper
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 * 1.2,
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 = DEFAULT_SMT_PAD_THICKNESS;
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: pcbThickness - 2 * padThickness - 2 * BOARD_SURFACE_OFFSET.copper + 0.1,
35144
- // Fill between pads
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([0, 0, pcbThickness / 2 / 2 + BOARD_SURFACE_OFFSET.copper]);
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([0, 0, -pcbThickness / 2 / 2 - BOARD_SURFACE_OFFSET.copper]);
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 * 1.02,
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 su17 } from "@tscircuit/circuit-json-util";
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 = su17(circuitJson).pcb_via.list();
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 = su18(circuitJson).pcb_board.list();
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 = su18(circuitJson).pcb_board.list();
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
- () => su19(circuitJson).cad_component.list(),
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 su20 } from "@tscircuit/circuit-json-util";
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 = su20(circuitJson).cad_component.list();
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 = su20(circuitJson).pcb_board.list()[0];
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;