@tscircuit/3d-viewer 0.0.477 → 0.0.479

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 (3) hide show
  1. package/dist/index.d.ts +19 -21
  2. package/dist/index.js +610 -545
  3. package/package.json +2 -1
package/dist/index.js CHANGED
@@ -14226,7 +14226,7 @@ var require_browser = __commonJS({
14226
14226
 
14227
14227
  // src/CadViewer.tsx
14228
14228
  import { useState as useState35, useCallback as useCallback21, useRef as useRef26, useEffect as useEffect43 } from "react";
14229
- import * as THREE33 from "three";
14229
+ import * as THREE34 from "three";
14230
14230
 
14231
14231
  // src/CadViewerJscad.tsx
14232
14232
  import { su as su12 } from "@tscircuit/circuit-json-util";
@@ -29487,12 +29487,12 @@ var AnyCadComponent = ({
29487
29487
 
29488
29488
  // src/CadViewerContainer.tsx
29489
29489
  import { forwardRef as forwardRef2, useEffect as useEffect16, useMemo as useMemo13, useState as useState9 } from "react";
29490
- import * as THREE15 from "three";
29490
+ import * as THREE16 from "three";
29491
29491
 
29492
29492
  // package.json
29493
29493
  var package_default = {
29494
29494
  name: "@tscircuit/3d-viewer",
29495
- version: "0.0.476",
29495
+ version: "0.0.478",
29496
29496
  main: "./dist/index.js",
29497
29497
  module: "./dist/index.js",
29498
29498
  type: "module",
@@ -29521,6 +29521,7 @@ var package_default = {
29521
29521
  dependencies: {
29522
29522
  "@jscad/regl-renderer": "^2.6.12",
29523
29523
  "@jscad/stl-serializer": "^2.1.20",
29524
+ "circuit-to-canvas": "^0.0.26",
29524
29525
  "react-hot-toast": "^2.6.0",
29525
29526
  three: "^0.165.0",
29526
29527
  "three-stdlib": "^2.36.0",
@@ -29936,6 +29937,7 @@ var Canvas = forwardRef(
29936
29937
 
29937
29938
  // src/react-three/OrbitControls.tsx
29938
29939
  import { useEffect as useEffect11, useMemo as useMemo10 } from "react";
29940
+ import * as THREE11 from "three";
29939
29941
  import { OrbitControls as ThreeOrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
29940
29942
  var OrbitControls = ({
29941
29943
  autoRotate,
@@ -29978,6 +29980,14 @@ var OrbitControls = ({
29978
29980
  if (enableDamping !== void 0) controls.enableDamping = enableDamping;
29979
29981
  if (dampingFactor !== void 0) controls.dampingFactor = dampingFactor;
29980
29982
  controls.zoomToCursor = true;
29983
+ controls.mouseButtons = {
29984
+ LEFT: THREE11.MOUSE.ROTATE,
29985
+ // Left click to rotate
29986
+ MIDDLE: THREE11.MOUSE.PAN,
29987
+ // Middle click to pan
29988
+ RIGHT: null
29989
+ // Right-click always disabled - only context menu
29990
+ };
29981
29991
  if (target) {
29982
29992
  controls.target.set(target[0], target[1], target[2]);
29983
29993
  controls.update();
@@ -30014,7 +30024,7 @@ var OrbitControls = ({
30014
30024
 
30015
30025
  // src/react-three/Grid.tsx
30016
30026
  import { useEffect as useEffect12, useMemo as useMemo11 } from "react";
30017
- import * as THREE11 from "three";
30027
+ import * as THREE12 from "three";
30018
30028
  var vertexShader = `
30019
30029
  varying vec3 worldPosition;
30020
30030
  void main() {
@@ -30060,24 +30070,24 @@ var Grid = ({
30060
30070
  const { scene, camera } = useThree();
30061
30071
  const size5 = 1e3;
30062
30072
  const gridMesh = useMemo11(() => {
30063
- const geometry = new THREE11.PlaneGeometry(size5, size5);
30073
+ const geometry = new THREE12.PlaneGeometry(size5, size5);
30064
30074
  geometry.rotateX(-Math.PI / 2);
30065
- const material = new THREE11.ShaderMaterial({
30075
+ const material = new THREE12.ShaderMaterial({
30066
30076
  vertexShader,
30067
30077
  fragmentShader,
30068
30078
  uniforms: {
30069
30079
  cellSize: { value: cellSize },
30070
30080
  sectionSize: { value: sectionSize },
30071
- gridColor: { value: new THREE11.Color(15658734) },
30072
- sectionColor: { value: new THREE11.Color(13421823) },
30081
+ gridColor: { value: new THREE12.Color(15658734) },
30082
+ sectionColor: { value: new THREE12.Color(13421823) },
30073
30083
  fadeDistance: { value: 100 },
30074
30084
  // Fade out based on sectionSize
30075
30085
  fadeStrength: { value: 1.5 }
30076
30086
  },
30077
30087
  transparent: true,
30078
- side: THREE11.DoubleSide
30088
+ side: THREE12.DoubleSide
30079
30089
  });
30080
- const mesh = new THREE11.Mesh(geometry, material);
30090
+ const mesh = new THREE12.Mesh(geometry, material);
30081
30091
  if (rotation2) {
30082
30092
  mesh.rotation.fromArray(rotation2);
30083
30093
  }
@@ -30106,15 +30116,15 @@ var Grid = ({
30106
30116
 
30107
30117
  // src/react-three/Lights.tsx
30108
30118
  import { useEffect as useEffect13, useMemo as useMemo12 } from "react";
30109
- import * as THREE12 from "three";
30119
+ import * as THREE13 from "three";
30110
30120
  var Lights = () => {
30111
30121
  const { scene } = useThree();
30112
30122
  const ambientLight = useMemo12(
30113
- () => new THREE12.AmbientLight(16777215, Math.PI / 2),
30123
+ () => new THREE13.AmbientLight(16777215, Math.PI / 2),
30114
30124
  []
30115
30125
  );
30116
30126
  const pointLight = useMemo12(() => {
30117
- const light = new THREE12.PointLight(16777215, Math.PI / 4);
30127
+ const light = new THREE13.PointLight(16777215, Math.PI / 4);
30118
30128
  light.position.set(-10, -10, 10);
30119
30129
  light.decay = 0;
30120
30130
  return light;
@@ -30133,7 +30143,7 @@ var Lights = () => {
30133
30143
 
30134
30144
  // src/hooks/cameraAnimation.ts
30135
30145
  import { useCallback as useCallback6, useEffect as useEffect14, useRef as useRef5 } from "react";
30136
- import * as THREE13 from "three";
30146
+ import * as THREE14 from "three";
30137
30147
  var easeInOutCubic = (t) => t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;
30138
30148
  var CameraAnimatorWithContext = () => {
30139
30149
  const {
@@ -30144,24 +30154,24 @@ var CameraAnimatorWithContext = () => {
30144
30154
  getPresetConfig
30145
30155
  } = useCameraController();
30146
30156
  const animationRef = useRef5(null);
30147
- const tempQuaternion = useRef5(new THREE13.Quaternion());
30148
- const tempTarget = useRef5(new THREE13.Vector3());
30149
- const tempUp = useRef5(new THREE13.Vector3());
30150
- const tempRoll = useRef5(new THREE13.Quaternion());
30151
- const tempRollTarget = useRef5(new THREE13.Quaternion());
30152
- const baseOrientationHelper = useRef5(new THREE13.Object3D());
30153
- const orientationHelper = useRef5(new THREE13.Object3D());
30157
+ const tempQuaternion = useRef5(new THREE14.Quaternion());
30158
+ const tempTarget = useRef5(new THREE14.Vector3());
30159
+ const tempUp = useRef5(new THREE14.Vector3());
30160
+ const tempRoll = useRef5(new THREE14.Quaternion());
30161
+ const tempRollTarget = useRef5(new THREE14.Quaternion());
30162
+ const baseOrientationHelper = useRef5(new THREE14.Object3D());
30163
+ const orientationHelper = useRef5(new THREE14.Object3D());
30154
30164
  const animateTo = useCallback6(
30155
30165
  ({ position, target, up, durationMs = 600 }) => {
30156
30166
  if (!mainCameraRef.current) return;
30157
30167
  const currentTarget = controlsRef.current?.target ?? defaultTarget;
30158
- const toPosition = new THREE13.Vector3(
30168
+ const toPosition = new THREE14.Vector3(
30159
30169
  position[0],
30160
30170
  position[1],
30161
30171
  position[2]
30162
30172
  );
30163
- const resolvedTarget = target ? new THREE13.Vector3(target[0], target[1], target[2]) : defaultTarget.clone();
30164
- const resolvedUp = new THREE13.Vector3(...up ?? [0, 0, 1]).normalize();
30173
+ const resolvedTarget = target ? new THREE14.Vector3(target[0], target[1], target[2]) : defaultTarget.clone();
30174
+ const resolvedUp = new THREE14.Vector3(...up ?? [0, 0, 1]).normalize();
30165
30175
  const toOrientationHelper = orientationHelper.current;
30166
30176
  toOrientationHelper.position.copy(toPosition);
30167
30177
  toOrientationHelper.up.copy(resolvedUp);
@@ -30318,14 +30328,14 @@ var useCameraSession = () => {
30318
30328
 
30319
30329
  // src/three-components/OrientationCubeCanvas.tsx
30320
30330
  import { useEffect as useEffect15, useRef as useRef7 } from "react";
30321
- import * as THREE14 from "three";
30331
+ import * as THREE15 from "three";
30322
30332
  import { Text as TroikaText } from "troika-three-text";
30323
30333
  import { jsx as jsx12 } from "react/jsx-runtime";
30324
30334
  function computePointInFront(rotationVector, distance3) {
30325
- const quaternion = new THREE14.Quaternion().setFromEuler(
30326
- new THREE14.Euler(rotationVector.x, rotationVector.y, rotationVector.z)
30335
+ const quaternion = new THREE15.Quaternion().setFromEuler(
30336
+ new THREE15.Euler(rotationVector.x, rotationVector.y, rotationVector.z)
30327
30337
  );
30328
- const forwardVector = new THREE14.Vector3(0, 0, 1);
30338
+ const forwardVector = new THREE15.Vector3(0, 0, 1);
30329
30339
  forwardVector.applyQuaternion(quaternion);
30330
30340
  const result = forwardVector.multiplyScalar(distance3);
30331
30341
  return result;
@@ -30343,7 +30353,7 @@ var OrientationCubeCanvas = () => {
30343
30353
  const canvas = document.createElement("canvas");
30344
30354
  canvasRef.current = canvas;
30345
30355
  containerRef.current.appendChild(canvas);
30346
- const renderer = new THREE14.WebGLRenderer({
30356
+ const renderer = new THREE15.WebGLRenderer({
30347
30357
  canvas,
30348
30358
  antialias: true,
30349
30359
  alpha: true
@@ -30351,26 +30361,26 @@ var OrientationCubeCanvas = () => {
30351
30361
  renderer.setSize(120, 120);
30352
30362
  renderer.setPixelRatio(window.devicePixelRatio);
30353
30363
  rendererRef.current = renderer;
30354
- const scene = new THREE14.Scene();
30364
+ const scene = new THREE15.Scene();
30355
30365
  sceneRef.current = scene;
30356
- const camera = new THREE14.PerspectiveCamera(75, 1, 0.1, 1e3);
30366
+ const camera = new THREE15.PerspectiveCamera(75, 1, 0.1, 1e3);
30357
30367
  camera.up.set(0, 0, 1);
30358
30368
  cameraRef.current = camera;
30359
- const ambientLight = new THREE14.AmbientLight(16777215, Math.PI / 2);
30369
+ const ambientLight = new THREE15.AmbientLight(16777215, Math.PI / 2);
30360
30370
  scene.add(ambientLight);
30361
- const group = new THREE14.Group();
30371
+ const group = new THREE15.Group();
30362
30372
  group.rotation.fromArray([Math.PI / 2, 0, 0]);
30363
30373
  const cubeSize = 1;
30364
- const box = new THREE14.Mesh(
30365
- new THREE14.BoxGeometry(cubeSize, cubeSize, cubeSize),
30366
- new THREE14.MeshStandardMaterial({ color: "white" })
30374
+ const box = new THREE15.Mesh(
30375
+ new THREE15.BoxGeometry(cubeSize, cubeSize, cubeSize),
30376
+ new THREE15.MeshStandardMaterial({ color: "white" })
30367
30377
  );
30368
30378
  group.add(box);
30369
- const edges = new THREE14.LineSegments(
30370
- new THREE14.EdgesGeometry(
30371
- new THREE14.BoxGeometry(cubeSize, cubeSize, cubeSize)
30379
+ const edges = new THREE15.LineSegments(
30380
+ new THREE15.EdgesGeometry(
30381
+ new THREE15.BoxGeometry(cubeSize, cubeSize, cubeSize)
30372
30382
  ),
30373
- new THREE14.LineBasicMaterial({ color: 0, linewidth: 2 })
30383
+ new THREE15.LineBasicMaterial({ color: 0, linewidth: 2 })
30374
30384
  );
30375
30385
  group.add(edges);
30376
30386
  scene.add(group);
@@ -30424,7 +30434,7 @@ var OrientationCubeCanvas = () => {
30424
30434
  const animate = () => {
30425
30435
  if (mainCameraRef.current) {
30426
30436
  const cameraPosition = computePointInFront(
30427
- mainCameraRef.current.rotation ?? new THREE14.Euler(0, 0, 0),
30437
+ mainCameraRef.current.rotation ?? new THREE15.Euler(0, 0, 0),
30428
30438
  2
30429
30439
  );
30430
30440
  if (!cameraPosition.equals(camera.position)) {
@@ -30527,7 +30537,7 @@ var CadViewerContainer = forwardRef2(
30527
30537
  Canvas,
30528
30538
  {
30529
30539
  ref,
30530
- scene: { up: new THREE15.Vector3(0, 0, 1) },
30540
+ scene: { up: new THREE16.Vector3(0, 0, 1) },
30531
30541
  camera: { up: [0, 0, 1], position: initialCameraPosition },
30532
30542
  onCreated: ({ camera }) => {
30533
30543
  mainCameraRef.current = camera;
@@ -32382,7 +32392,7 @@ var Text = ({
32382
32392
  };
32383
32393
 
32384
32394
  // src/three-components/Error3d.tsx
32385
- import * as THREE16 from "three";
32395
+ import * as THREE17 from "three";
32386
32396
  import { Fragment as Fragment4, jsx as jsx14, jsxs as jsxs4 } from "react/jsx-runtime";
32387
32397
  var Error3d = ({
32388
32398
  error,
@@ -32416,7 +32426,7 @@ var Error3d = ({
32416
32426
  return [0, 0, 0];
32417
32427
  }, [cad_component2]);
32418
32428
  const group = useMemo17(() => {
32419
- const g = new THREE16.Group();
32429
+ const g = new THREE17.Group();
32420
32430
  g.position.fromArray(position);
32421
32431
  return g;
32422
32432
  }, [position]);
@@ -32476,9 +32486,9 @@ var Error3d = ({
32476
32486
  };
32477
32487
  var ErrorBox = ({ parent }) => {
32478
32488
  const mesh = useMemo17(() => {
32479
- const m = new THREE16.Mesh(
32480
- new THREE16.BoxGeometry(0.5, 0.5, 0.5),
32481
- new THREE16.MeshStandardMaterial({
32489
+ const m = new THREE17.Mesh(
32490
+ new THREE17.BoxGeometry(0.5, 0.5, 0.5),
32491
+ new THREE17.MeshStandardMaterial({
32482
32492
  depthTest: false,
32483
32493
  transparent: true,
32484
32494
  color: "red",
@@ -32500,7 +32510,7 @@ var ErrorBox = ({ parent }) => {
32500
32510
 
32501
32511
  // src/three-components/STLModel.tsx
32502
32512
  import { useState as useState13, useEffect as useEffect21, useMemo as useMemo18 } from "react";
32503
- import * as THREE17 from "three";
32513
+ import * as THREE18 from "three";
32504
32514
  import { STLLoader } from "three-stdlib";
32505
32515
  function STLModel({
32506
32516
  stlUrl,
@@ -32531,12 +32541,12 @@ function STLModel({
32531
32541
  }, [stlUrl, stlData]);
32532
32542
  const mesh = useMemo18(() => {
32533
32543
  if (!geom) return null;
32534
- const material = new THREE17.MeshStandardMaterial({
32535
- color: Array.isArray(color) ? new THREE17.Color(color[0], color[1], color[2]) : color,
32544
+ const material = new THREE18.MeshStandardMaterial({
32545
+ color: Array.isArray(color) ? new THREE18.Color(color[0], color[1], color[2]) : color,
32536
32546
  transparent: opacity !== 1,
32537
32547
  opacity
32538
32548
  });
32539
- return new THREE17.Mesh(geom, material);
32549
+ return new THREE18.Mesh(geom, material);
32540
32550
  }, [geom, color, opacity]);
32541
32551
  useEffect21(() => {
32542
32552
  if (!rootObject || !mesh) return;
@@ -32601,11 +32611,11 @@ var ThreeErrorBoundary = class extends React11.Component {
32601
32611
 
32602
32612
  // src/three-components/JscadBoardTextures.tsx
32603
32613
  import { useEffect as useEffect22, useMemo as useMemo19 } from "react";
32604
- import * as THREE23 from "three";
32614
+ import * as THREE24 from "three";
32605
32615
  import { su as su9 } from "@tscircuit/circuit-json-util";
32606
32616
 
32607
32617
  // src/utils/soldermask-texture.ts
32608
- import * as THREE18 from "three";
32618
+ import * as THREE19 from "three";
32609
32619
  import { su as su5 } from "@tscircuit/circuit-json-util";
32610
32620
 
32611
32621
  // node_modules/@tscircuit/math-utils/dist/chunk-5N7UJNVK.js
@@ -33150,10 +33160,10 @@ function createSoldermaskTextureForLayer({
33150
33160
  }
33151
33161
  });
33152
33162
  ctx.globalCompositeOperation = "source-over";
33153
- const texture = new THREE18.CanvasTexture(canvas);
33163
+ const texture = new THREE19.CanvasTexture(canvas);
33154
33164
  texture.generateMipmaps = true;
33155
- texture.minFilter = THREE18.LinearMipmapLinearFilter;
33156
- texture.magFilter = THREE18.LinearFilter;
33165
+ texture.minFilter = THREE19.LinearMipmapLinearFilter;
33166
+ texture.magFilter = THREE19.LinearFilter;
33157
33167
  texture.anisotropy = 16;
33158
33168
  texture.needsUpdate = true;
33159
33169
  return texture;
@@ -33161,7 +33171,7 @@ function createSoldermaskTextureForLayer({
33161
33171
 
33162
33172
  // src/utils/silkscreen-texture.ts
33163
33173
  var import_text = __toESM(require_text(), 1);
33164
- import * as THREE19 from "three";
33174
+ import * as THREE20 from "three";
33165
33175
 
33166
33176
  // node_modules/transformation-matrix/src/applyToPoint.js
33167
33177
  function applyToPoint(matrix, point2) {
@@ -33820,17 +33830,17 @@ function createSilkscreenTextureForLayer({
33820
33830
  ctx.stroke();
33821
33831
  });
33822
33832
  });
33823
- const texture = new THREE19.CanvasTexture(canvas);
33833
+ const texture = new THREE20.CanvasTexture(canvas);
33824
33834
  texture.generateMipmaps = true;
33825
- texture.minFilter = THREE19.LinearMipmapLinearFilter;
33826
- texture.magFilter = THREE19.LinearFilter;
33835
+ texture.minFilter = THREE20.LinearMipmapLinearFilter;
33836
+ texture.magFilter = THREE20.LinearFilter;
33827
33837
  texture.anisotropy = 16;
33828
33838
  texture.needsUpdate = true;
33829
33839
  return texture;
33830
33840
  }
33831
33841
 
33832
33842
  // src/utils/trace-texture.ts
33833
- import * as THREE20 from "three";
33843
+ import * as THREE21 from "three";
33834
33844
  import { su as su7 } from "@tscircuit/circuit-json-util";
33835
33845
  function isWireRoutePoint(point2) {
33836
33846
  return point2 && point2.route_type === "wire" && typeof point2.layer === "string" && typeof point2.width === "number";
@@ -33918,10 +33928,10 @@ function createTraceTextureForLayer({
33918
33928
  }
33919
33929
  });
33920
33930
  ctx.globalCompositeOperation = "source-over";
33921
- const texture = new THREE20.CanvasTexture(canvas);
33931
+ const texture = new THREE21.CanvasTexture(canvas);
33922
33932
  texture.generateMipmaps = true;
33923
- texture.minFilter = THREE20.LinearMipmapLinearFilter;
33924
- texture.magFilter = THREE20.LinearFilter;
33933
+ texture.minFilter = THREE21.LinearMipmapLinearFilter;
33934
+ texture.magFilter = THREE21.LinearFilter;
33925
33935
  texture.anisotropy = 16;
33926
33936
  texture.needsUpdate = true;
33927
33937
  return texture;
@@ -33929,7 +33939,7 @@ function createTraceTextureForLayer({
33929
33939
 
33930
33940
  // src/utils/copper-text-texture.ts
33931
33941
  var import_text2 = __toESM(require_text(), 1);
33932
- import * as THREE21 from "three";
33942
+ import * as THREE22 from "three";
33933
33943
  function parseDimension2(value, defaultValue) {
33934
33944
  if (value === void 0) return defaultValue;
33935
33945
  if (typeof value === "number") return value;
@@ -34168,17 +34178,17 @@ function createCopperTextTextureForLayer({
34168
34178
  );
34169
34179
  }
34170
34180
  });
34171
- const texture = new THREE21.CanvasTexture(canvas);
34181
+ const texture = new THREE22.CanvasTexture(canvas);
34172
34182
  texture.generateMipmaps = true;
34173
- texture.minFilter = THREE21.LinearMipmapLinearFilter;
34174
- texture.magFilter = THREE21.LinearFilter;
34183
+ texture.minFilter = THREE22.LinearMipmapLinearFilter;
34184
+ texture.magFilter = THREE22.LinearFilter;
34175
34185
  texture.anisotropy = 16;
34176
34186
  texture.needsUpdate = true;
34177
34187
  return texture;
34178
34188
  }
34179
34189
 
34180
34190
  // src/utils/panel-outline-texture.ts
34181
- import * as THREE22 from "three";
34191
+ import * as THREE23 from "three";
34182
34192
  import { su as su8 } from "@tscircuit/circuit-json-util";
34183
34193
  function createPanelOutlineTextureForLayer({
34184
34194
  layer,
@@ -34234,10 +34244,10 @@ function createPanelOutlineTextureForLayer({
34234
34244
  );
34235
34245
  }
34236
34246
  });
34237
- const texture = new THREE22.CanvasTexture(canvas);
34247
+ const texture = new THREE23.CanvasTexture(canvas);
34238
34248
  texture.generateMipmaps = true;
34239
- texture.minFilter = THREE22.LinearMipmapLinearFilter;
34240
- texture.magFilter = THREE22.LinearFilter;
34249
+ texture.minFilter = THREE23.LinearMipmapLinearFilter;
34250
+ texture.magFilter = THREE23.LinearFilter;
34241
34251
  texture.anisotropy = 16;
34242
34252
  texture.needsUpdate = true;
34243
34253
  return texture;
@@ -34357,23 +34367,23 @@ function JscadBoardTextures({
34357
34367
  useEffect22(() => {
34358
34368
  if (!rootObject || !boardData || !textures) return;
34359
34369
  const meshes = [];
34360
- const createTexturePlane = (texture, zOffset, isBottomLayer, name, usePolygonOffset = false, depthWrite = false) => {
34370
+ const createTexturePlane2 = (texture, zOffset, isBottomLayer, name, usePolygonOffset = false, depthWrite = false) => {
34361
34371
  if (!texture) return null;
34362
34372
  const boardOutlineBounds = calculateOutlineBounds(boardData);
34363
- const planeGeom = new THREE23.PlaneGeometry(
34373
+ const planeGeom = new THREE24.PlaneGeometry(
34364
34374
  boardOutlineBounds.width,
34365
34375
  boardOutlineBounds.height
34366
34376
  );
34367
- const material = new THREE23.MeshBasicMaterial({
34377
+ const material = new THREE24.MeshBasicMaterial({
34368
34378
  map: texture,
34369
34379
  transparent: true,
34370
- side: THREE23.DoubleSide,
34380
+ side: THREE24.DoubleSide,
34371
34381
  depthWrite,
34372
34382
  polygonOffset: usePolygonOffset,
34373
34383
  polygonOffsetFactor: usePolygonOffset ? -1 : 0,
34374
34384
  polygonOffsetUnits: usePolygonOffset ? -1 : 0
34375
34385
  });
34376
- const mesh = new THREE23.Mesh(planeGeom, material);
34386
+ const mesh = new THREE24.Mesh(planeGeom, material);
34377
34387
  mesh.position.set(
34378
34388
  boardOutlineBounds.centerX,
34379
34389
  boardOutlineBounds.centerY,
@@ -34387,7 +34397,7 @@ function JscadBoardTextures({
34387
34397
  };
34388
34398
  const SURFACE_OFFSET = 1e-3;
34389
34399
  if (visibility.topMask) {
34390
- const topSoldermaskMesh = createTexturePlane(
34400
+ const topSoldermaskMesh = createTexturePlane2(
34391
34401
  textures.topSoldermask,
34392
34402
  pcbThickness / 2 + SURFACE_OFFSET,
34393
34403
  false,
@@ -34400,7 +34410,7 @@ function JscadBoardTextures({
34400
34410
  }
34401
34411
  }
34402
34412
  if (visibility.bottomMask) {
34403
- const bottomSoldermaskMesh = createTexturePlane(
34413
+ const bottomSoldermaskMesh = createTexturePlane2(
34404
34414
  textures.bottomSoldermask,
34405
34415
  -pcbThickness / 2 - SURFACE_OFFSET,
34406
34416
  true,
@@ -34413,7 +34423,7 @@ function JscadBoardTextures({
34413
34423
  }
34414
34424
  }
34415
34425
  if (visibility.topCopper && visibility.topMask) {
34416
- const topTraceWithMaskMesh = createTexturePlane(
34426
+ const topTraceWithMaskMesh = createTexturePlane2(
34417
34427
  textures.topTraceWithMask,
34418
34428
  pcbThickness / 2 + BOARD_SURFACE_OFFSET.traces + 4e-3,
34419
34429
  false,
@@ -34425,7 +34435,7 @@ function JscadBoardTextures({
34425
34435
  }
34426
34436
  }
34427
34437
  if (visibility.bottomCopper && visibility.bottomMask) {
34428
- const bottomTraceWithMaskMesh = createTexturePlane(
34438
+ const bottomTraceWithMaskMesh = createTexturePlane2(
34429
34439
  textures.bottomTraceWithMask,
34430
34440
  -pcbThickness / 2 - BOARD_SURFACE_OFFSET.traces - 5e-3,
34431
34441
  true,
@@ -34437,7 +34447,7 @@ function JscadBoardTextures({
34437
34447
  }
34438
34448
  }
34439
34449
  if (visibility.topSilkscreen) {
34440
- const topSilkscreenMesh = createTexturePlane(
34450
+ const topSilkscreenMesh = createTexturePlane2(
34441
34451
  textures.topSilkscreen,
34442
34452
  pcbThickness / 2 + SURFACE_OFFSET + 2e-3,
34443
34453
  false,
@@ -34449,7 +34459,7 @@ function JscadBoardTextures({
34449
34459
  }
34450
34460
  }
34451
34461
  if (visibility.bottomSilkscreen) {
34452
- const bottomSilkscreenMesh = createTexturePlane(
34462
+ const bottomSilkscreenMesh = createTexturePlane2(
34453
34463
  textures.bottomSilkscreen,
34454
34464
  -pcbThickness / 2 - SURFACE_OFFSET - 2e-3,
34455
34465
  true,
@@ -34461,7 +34471,7 @@ function JscadBoardTextures({
34461
34471
  }
34462
34472
  }
34463
34473
  if (visibility.topCopper) {
34464
- const topCopperTextMesh = createTexturePlane(
34474
+ const topCopperTextMesh = createTexturePlane2(
34465
34475
  textures.topCopperText,
34466
34476
  pcbThickness / 2 + BOARD_SURFACE_OFFSET.copper,
34467
34477
  false,
@@ -34473,7 +34483,7 @@ function JscadBoardTextures({
34473
34483
  }
34474
34484
  }
34475
34485
  if (visibility.bottomCopper) {
34476
- const bottomCopperTextMesh = createTexturePlane(
34486
+ const bottomCopperTextMesh = createTexturePlane2(
34477
34487
  textures.bottomCopperText,
34478
34488
  -pcbThickness / 2 - BOARD_SURFACE_OFFSET.copper,
34479
34489
  true,
@@ -34485,7 +34495,7 @@ function JscadBoardTextures({
34485
34495
  }
34486
34496
  }
34487
34497
  if (visibility.boardBody) {
34488
- const topPanelOutlinesMesh = createTexturePlane(
34498
+ const topPanelOutlinesMesh = createTexturePlane2(
34489
34499
  textures.topPanelOutlines,
34490
34500
  pcbThickness / 2 + SURFACE_OFFSET + 3e-3,
34491
34501
  // Above silkscreen
@@ -34498,7 +34508,7 @@ function JscadBoardTextures({
34498
34508
  meshes.push(topPanelOutlinesMesh);
34499
34509
  rootObject.add(topPanelOutlinesMesh);
34500
34510
  }
34501
- const bottomPanelOutlinesMesh = createTexturePlane(
34511
+ const bottomPanelOutlinesMesh = createTexturePlane2(
34502
34512
  textures.bottomPanelOutlines,
34503
34513
  -pcbThickness / 2 - SURFACE_OFFSET - 3e-3,
34504
34514
  // Below bottom silkscreen
@@ -34518,7 +34528,7 @@ function JscadBoardTextures({
34518
34528
  rootObject.remove(mesh);
34519
34529
  }
34520
34530
  mesh.geometry.dispose();
34521
- if (mesh.material instanceof THREE23.Material) {
34531
+ if (mesh.material instanceof THREE24.Material) {
34522
34532
  mesh.material.dispose();
34523
34533
  }
34524
34534
  });
@@ -34750,7 +34760,7 @@ import { useEffect as useEffect24, useMemo as useMemo22, useState as useState15
34750
34760
  // src/hooks/useManifoldBoardBuilder.ts
34751
34761
  import { useState as useState14, useEffect as useEffect23, useMemo as useMemo21, useRef as useRef9 } from "react";
34752
34762
  import { su as su18 } from "@tscircuit/circuit-json-util";
34753
- import * as THREE29 from "three";
34763
+ import * as THREE31 from "three";
34754
34764
 
34755
34765
  // src/utils/manifold/create-manifold-board.ts
34756
34766
  var arePointsClockwise3 = (points) => {
@@ -34810,202 +34820,6 @@ function createManifoldBoard(Manifold, CrossSection, boardData, pcbThickness, ma
34810
34820
  return { boardOp, outlineCrossSection };
34811
34821
  }
34812
34822
 
34813
- // src/utils/manifold/process-copper-pours.ts
34814
- import * as THREE25 from "three";
34815
-
34816
- // src/utils/manifold-mesh-to-three-geometry.ts
34817
- import * as THREE24 from "three";
34818
- function manifoldMeshToThreeGeometry(manifoldMesh) {
34819
- const geometry = new THREE24.BufferGeometry();
34820
- geometry.setAttribute(
34821
- "position",
34822
- new THREE24.Float32BufferAttribute(manifoldMesh.vertProperties, 3)
34823
- );
34824
- geometry.setIndex(new THREE24.Uint32BufferAttribute(manifoldMesh.triVerts, 1));
34825
- if (manifoldMesh.runIndex && manifoldMesh.runIndex.length > 1 && manifoldMesh.runOriginalID) {
34826
- for (let i = 0; i < manifoldMesh.runIndex.length - 1; i++) {
34827
- const start = manifoldMesh.runIndex[i];
34828
- const count3 = manifoldMesh.runIndex[i + 1] - start;
34829
- geometry.addGroup(start, count3, 0);
34830
- }
34831
- } else {
34832
- geometry.addGroup(0, manifoldMesh.triVerts.length, 0);
34833
- }
34834
- return geometry;
34835
- }
34836
-
34837
- // src/utils/manifold/process-copper-pours.ts
34838
- var arePointsClockwise4 = (points) => {
34839
- let area = 0;
34840
- for (let i = 0; i < points.length; i++) {
34841
- const j = (i + 1) % points.length;
34842
- if (points[i] && points[j]) {
34843
- area += points[i][0] * points[j][1];
34844
- area -= points[j][0] * points[i][1];
34845
- }
34846
- }
34847
- const signedArea = area / 2;
34848
- return signedArea <= 0;
34849
- };
34850
- function segmentToPoints2(p1, p2, bulge, arcSegments) {
34851
- if (!bulge || Math.abs(bulge) < 1e-9) {
34852
- return [];
34853
- }
34854
- const theta = 4 * Math.atan(bulge);
34855
- const dx = p2[0] - p1[0];
34856
- const dy = p2[1] - p1[1];
34857
- const dist = Math.sqrt(dx * dx + dy * dy);
34858
- if (dist < 1e-9) return [];
34859
- const radius = Math.abs(dist / (2 * Math.sin(theta / 2)));
34860
- const m = Math.sqrt(Math.max(0, radius * radius - dist / 2 * (dist / 2)));
34861
- const midPoint = [(p1[0] + p2[0]) / 2, (p1[1] + p2[1]) / 2];
34862
- const ux = dx / dist;
34863
- const uy = dy / dist;
34864
- const nx = -uy;
34865
- const ny = ux;
34866
- const centerX = midPoint[0] + nx * m * Math.sign(bulge);
34867
- const centerY = midPoint[1] + ny * m * Math.sign(bulge);
34868
- const startAngle = Math.atan2(p1[1] - centerY, p1[0] - centerX);
34869
- const points = [];
34870
- const numSteps = Math.max(
34871
- 2,
34872
- Math.ceil(arcSegments * Math.abs(theta) / (Math.PI * 2) * 4)
34873
- );
34874
- const angleStep = theta / numSteps;
34875
- for (let i = 1; i < numSteps; i++) {
34876
- const angle = startAngle + angleStep * i;
34877
- points.push([
34878
- centerX + radius * Math.cos(angle),
34879
- centerY + radius * Math.sin(angle)
34880
- ]);
34881
- }
34882
- return points;
34883
- }
34884
- function ringToPoints2(ring2, arcSegments) {
34885
- const allPoints = [];
34886
- const vertices = ring2.vertices;
34887
- for (let i = 0; i < vertices.length; i++) {
34888
- const p1 = vertices[i];
34889
- const p2 = vertices[(i + 1) % vertices.length];
34890
- allPoints.push([p1.x, p1.y]);
34891
- if (p1.bulge) {
34892
- const arcPoints = segmentToPoints2(
34893
- [p1.x, p1.y],
34894
- [p2.x, p2.y],
34895
- p1.bulge,
34896
- arcSegments
34897
- );
34898
- allPoints.push(...arcPoints);
34899
- }
34900
- }
34901
- return allPoints;
34902
- }
34903
- function processCopperPoursForManifold(Manifold, CrossSection, circuitJson, pcbThickness, manifoldInstancesForCleanup, boardMaterial, holeUnion, boardClipVolume) {
34904
- const copperPourGeoms = [];
34905
- const copperPours = circuitJson.filter(
34906
- (e) => e.type === "pcb_copper_pour"
34907
- );
34908
- for (const pour of copperPours) {
34909
- const pourThickness = DEFAULT_SMT_PAD_THICKNESS;
34910
- const layerSign = pour.layer === "bottom" ? -1 : 1;
34911
- const zPos = layerSign * (pcbThickness / 2 + pourThickness / 2 + MANIFOLD_Z_OFFSET);
34912
- let pourOp;
34913
- if (pour.shape === "rect") {
34914
- pourOp = Manifold.cube([pour.width, pour.height, pourThickness], true);
34915
- manifoldInstancesForCleanup.push(pourOp);
34916
- if (pour.rotation) {
34917
- const rotatedOp = pourOp.rotate([0, 0, pour.rotation]);
34918
- manifoldInstancesForCleanup.push(rotatedOp);
34919
- pourOp = rotatedOp;
34920
- }
34921
- pourOp = pourOp.translate([pour.center.x, pour.center.y, zPos]);
34922
- manifoldInstancesForCleanup.push(pourOp);
34923
- } else if (pour.shape === "polygon") {
34924
- if (pour.points.length < 3) continue;
34925
- let pointsVec2 = pour.points.map((p) => [
34926
- p.x,
34927
- p.y
34928
- ]);
34929
- if (arePointsClockwise4(pointsVec2)) {
34930
- pointsVec2 = pointsVec2.reverse();
34931
- }
34932
- const crossSection = CrossSection.ofPolygons([pointsVec2]);
34933
- manifoldInstancesForCleanup.push(crossSection);
34934
- pourOp = Manifold.extrude(
34935
- crossSection,
34936
- pourThickness,
34937
- 0,
34938
- // nDivisions
34939
- 0,
34940
- // twistDegrees
34941
- [1, 1],
34942
- // scaleTop
34943
- true
34944
- // center extrusion
34945
- ).translate([0, 0, zPos]);
34946
- manifoldInstancesForCleanup.push(pourOp);
34947
- } else if (pour.shape === "brep") {
34948
- const brepShape = pour.brep_shape;
34949
- if (!brepShape || !brepShape.outer_ring) continue;
34950
- let outerRingPoints = ringToPoints2(
34951
- brepShape.outer_ring,
34952
- SMOOTH_CIRCLE_SEGMENTS
34953
- );
34954
- if (arePointsClockwise4(outerRingPoints)) {
34955
- outerRingPoints = outerRingPoints.reverse();
34956
- }
34957
- const polygons = [outerRingPoints];
34958
- if (brepShape.inner_rings) {
34959
- const innerRingsPoints = brepShape.inner_rings.map((ring2) => {
34960
- let points = ringToPoints2(ring2, SMOOTH_CIRCLE_SEGMENTS);
34961
- if (!arePointsClockwise4(points)) {
34962
- points = points.reverse();
34963
- }
34964
- return points;
34965
- });
34966
- polygons.push(...innerRingsPoints);
34967
- }
34968
- const crossSection = CrossSection.ofPolygons(polygons);
34969
- manifoldInstancesForCleanup.push(crossSection);
34970
- pourOp = Manifold.extrude(
34971
- crossSection,
34972
- pourThickness,
34973
- 0,
34974
- // nDivisions
34975
- 0,
34976
- // twistDegrees
34977
- [1, 1],
34978
- // scaleTop
34979
- true
34980
- // center extrusion
34981
- ).translate([0, 0, zPos]);
34982
- manifoldInstancesForCleanup.push(pourOp);
34983
- }
34984
- if (pourOp) {
34985
- if (holeUnion) {
34986
- const withHoles = pourOp.subtract(holeUnion);
34987
- manifoldInstancesForCleanup.push(withHoles);
34988
- pourOp = withHoles;
34989
- }
34990
- if (boardClipVolume) {
34991
- const clipped = Manifold.intersection([pourOp, boardClipVolume]);
34992
- manifoldInstancesForCleanup.push(clipped);
34993
- pourOp = clipped;
34994
- }
34995
- const covered = pour.covered_with_solder_mask !== false;
34996
- const pourColorArr = covered ? tracesMaterialColors[boardMaterial] ?? colors.fr4TracesWithoutMaskTan : colors.copper;
34997
- const pourColor = new THREE25.Color(...pourColorArr);
34998
- const threeGeom = manifoldMeshToThreeGeometry(pourOp.getMesh());
34999
- copperPourGeoms.push({
35000
- key: `coppour-${pour.pcb_copper_pour_id}`,
35001
- geometry: threeGeom,
35002
- color: pourColor
35003
- });
35004
- }
35005
- }
35006
- return { copperPourGeoms };
35007
- }
35008
-
35009
34823
  // src/utils/manifold/process-cutouts.ts
35010
34824
  import { su as su13 } from "@tscircuit/circuit-json-util";
35011
34825
 
@@ -35085,7 +34899,7 @@ function createPadManifoldOp({
35085
34899
  }
35086
34900
 
35087
34901
  // src/utils/manifold/process-cutouts.ts
35088
- var arePointsClockwise5 = (points) => {
34902
+ var arePointsClockwise4 = (points) => {
35089
34903
  let area = 0;
35090
34904
  for (let i = 0; i < points.length; i++) {
35091
34905
  const j = (i + 1) % points.length;
@@ -35161,7 +34975,7 @@ function processCutoutsForManifold(Manifold, CrossSection, circuitJson, pcbThick
35161
34975
  p.x,
35162
34976
  p.y
35163
34977
  ]);
35164
- if (arePointsClockwise5(pointsVec2)) {
34978
+ if (arePointsClockwise4(pointsVec2)) {
35165
34979
  pointsVec2 = pointsVec2.reverse();
35166
34980
  }
35167
34981
  const crossSection = CrossSection.ofPolygons([pointsVec2]);
@@ -35297,7 +35111,30 @@ function processNonPlatedHolesForManifold(Manifold, circuitJson, pcbThickness, m
35297
35111
  // src/utils/manifold/process-plated-holes.ts
35298
35112
  import { su as su15 } from "@tscircuit/circuit-json-util";
35299
35113
  import * as THREE26 from "three";
35300
- var arePointsClockwise6 = (points) => {
35114
+
35115
+ // src/utils/manifold-mesh-to-three-geometry.ts
35116
+ import * as THREE25 from "three";
35117
+ function manifoldMeshToThreeGeometry(manifoldMesh) {
35118
+ const geometry = new THREE25.BufferGeometry();
35119
+ geometry.setAttribute(
35120
+ "position",
35121
+ new THREE25.Float32BufferAttribute(manifoldMesh.vertProperties, 3)
35122
+ );
35123
+ geometry.setIndex(new THREE25.Uint32BufferAttribute(manifoldMesh.triVerts, 1));
35124
+ if (manifoldMesh.runIndex && manifoldMesh.runIndex.length > 1 && manifoldMesh.runOriginalID) {
35125
+ for (let i = 0; i < manifoldMesh.runIndex.length - 1; i++) {
35126
+ const start = manifoldMesh.runIndex[i];
35127
+ const count3 = manifoldMesh.runIndex[i + 1] - start;
35128
+ geometry.addGroup(start, count3, 0);
35129
+ }
35130
+ } else {
35131
+ geometry.addGroup(0, manifoldMesh.triVerts.length, 0);
35132
+ }
35133
+ return geometry;
35134
+ }
35135
+
35136
+ // src/utils/manifold/process-plated-holes.ts
35137
+ var arePointsClockwise5 = (points) => {
35301
35138
  let area = 0;
35302
35139
  for (let i = 0; i < points.length; i++) {
35303
35140
  const j = (i + 1) % points.length;
@@ -35344,7 +35181,7 @@ function processPlatedHolesForManifold(Manifold, CrossSection, circuitJson, pcbT
35344
35181
  point2.x,
35345
35182
  point2.y
35346
35183
  ]);
35347
- if (arePointsClockwise6(points)) {
35184
+ if (arePointsClockwise5(points)) {
35348
35185
  points = points.reverse();
35349
35186
  }
35350
35187
  const crossSection = CrossSection.ofPolygons([points]);
@@ -35389,7 +35226,7 @@ function processPlatedHolesForManifold(Manifold, CrossSection, circuitJson, pcbT
35389
35226
  const height10 = Math.max(baseHeight + sizeDelta, M);
35390
35227
  if (holeShape === "oval") {
35391
35228
  let points = createEllipsePoints(width10, height10, SMOOTH_CIRCLE_SEGMENTS);
35392
- if (arePointsClockwise6(points)) {
35229
+ if (arePointsClockwise5(points)) {
35393
35230
  points = points.reverse();
35394
35231
  }
35395
35232
  const crossSection = CrossSection.ofPolygons([points]);
@@ -35704,7 +35541,7 @@ function processPlatedHolesForManifold(Manifold, CrossSection, circuitJson, pcbT
35704
35541
  drillH,
35705
35542
  SMOOTH_CIRCLE_SEGMENTS
35706
35543
  );
35707
- if (arePointsClockwise6(boardDrillPoints)) {
35544
+ if (arePointsClockwise5(boardDrillPoints)) {
35708
35545
  boardDrillPoints = boardDrillPoints.reverse();
35709
35546
  }
35710
35547
  const boardDrillCrossSection = CrossSection.ofPolygons([boardDrillPoints]);
@@ -35732,7 +35569,7 @@ function processPlatedHolesForManifold(Manifold, CrossSection, circuitJson, pcbT
35732
35569
  outerH,
35733
35570
  SMOOTH_CIRCLE_SEGMENTS
35734
35571
  );
35735
- if (arePointsClockwise6(outerPoints)) {
35572
+ if (arePointsClockwise5(outerPoints)) {
35736
35573
  outerPoints = outerPoints.reverse();
35737
35574
  }
35738
35575
  const outerCrossSection = CrossSection.ofPolygons([outerPoints]);
@@ -35751,7 +35588,7 @@ function processPlatedHolesForManifold(Manifold, CrossSection, circuitJson, pcbT
35751
35588
  holeH,
35752
35589
  SMOOTH_CIRCLE_SEGMENTS
35753
35590
  );
35754
- if (arePointsClockwise6(innerPoints)) {
35591
+ if (arePointsClockwise5(innerPoints)) {
35755
35592
  innerPoints = innerPoints.reverse();
35756
35593
  }
35757
35594
  const innerCrossSection = CrossSection.ofPolygons([innerPoints]);
@@ -36035,6 +35872,397 @@ function processViasForManifold(Manifold, circuitJson, pcbThickness, manifoldIns
36035
35872
  return { viaBoardDrills, viaCopperGeoms };
36036
35873
  }
36037
35874
 
35875
+ // src/textures/create-copper-pour-texture-for-layer.ts
35876
+ import * as THREE29 from "three";
35877
+ import { CircuitToCanvasDrawer } from "circuit-to-canvas";
35878
+ function drawPolygon({
35879
+ ctx,
35880
+ points,
35881
+ canvasXFromPcb,
35882
+ canvasYFromPcb
35883
+ }) {
35884
+ if (points.length < 3) return;
35885
+ ctx.beginPath();
35886
+ points.forEach((point2, index2) => {
35887
+ const canvasX = canvasXFromPcb(point2[0]);
35888
+ const canvasY = canvasYFromPcb(point2[1]);
35889
+ if (index2 === 0) {
35890
+ ctx.moveTo(canvasX, canvasY);
35891
+ } else {
35892
+ ctx.lineTo(canvasX, canvasY);
35893
+ }
35894
+ });
35895
+ ctx.closePath();
35896
+ ctx.fill();
35897
+ }
35898
+ function drawBrepShape({
35899
+ ctx,
35900
+ pour,
35901
+ canvasXFromPcb,
35902
+ canvasYFromPcb
35903
+ }) {
35904
+ const brepShape = pour.brep_shape;
35905
+ if (!brepShape || !brepShape.outer_ring) return;
35906
+ const outerRingPoints = ringToPoints(brepShape.outer_ring, 32);
35907
+ if (outerRingPoints.length >= 3) {
35908
+ drawPolygon({
35909
+ ctx,
35910
+ points: outerRingPoints,
35911
+ canvasXFromPcb,
35912
+ canvasYFromPcb
35913
+ });
35914
+ }
35915
+ if (brepShape.inner_rings && brepShape.inner_rings.length > 0) {
35916
+ ctx.globalCompositeOperation = "destination-out";
35917
+ for (const innerRing of brepShape.inner_rings) {
35918
+ const innerRingPoints = ringToPoints(innerRing, 32);
35919
+ if (innerRingPoints.length >= 3) {
35920
+ drawPolygon({
35921
+ ctx,
35922
+ points: innerRingPoints,
35923
+ canvasXFromPcb,
35924
+ canvasYFromPcb
35925
+ });
35926
+ }
35927
+ }
35928
+ ctx.globalCompositeOperation = "source-over";
35929
+ }
35930
+ }
35931
+ function createCopperPourTextureForLayer({
35932
+ layer,
35933
+ circuitJson,
35934
+ boardData,
35935
+ traceTextureResolution = TRACE_TEXTURE_RESOLUTION
35936
+ }) {
35937
+ const copperPours = circuitJson.filter(
35938
+ (e) => e.type === "pcb_copper_pour"
35939
+ );
35940
+ const poursOnLayer = copperPours.filter((p) => p.layer === layer);
35941
+ if (poursOnLayer.length === 0) return null;
35942
+ const boardOutlineBounds = calculateOutlineBounds(boardData);
35943
+ const canvas = document.createElement("canvas");
35944
+ const canvasWidth = Math.floor(
35945
+ boardOutlineBounds.width * traceTextureResolution
35946
+ );
35947
+ const canvasHeight = Math.floor(
35948
+ boardOutlineBounds.height * traceTextureResolution
35949
+ );
35950
+ canvas.width = canvasWidth;
35951
+ canvas.height = canvasHeight;
35952
+ const ctx = canvas.getContext("2d");
35953
+ if (!ctx) return null;
35954
+ if (layer === "bottom") {
35955
+ ctx.translate(0, canvasHeight);
35956
+ ctx.scale(1, -1);
35957
+ }
35958
+ const canvasXFromPcb = (pcbX) => (pcbX - boardOutlineBounds.minX) * traceTextureResolution;
35959
+ const canvasYFromPcb = (pcbY) => (boardOutlineBounds.maxY - pcbY) * traceTextureResolution;
35960
+ const rectAndPolygonPours = poursOnLayer.filter(
35961
+ (pour) => pour.shape === "rect" || pour.shape === "polygon"
35962
+ );
35963
+ const brepPours = poursOnLayer.filter((pour) => pour.shape === "brep");
35964
+ if (rectAndPolygonPours.length > 0) {
35965
+ const drawer = new CircuitToCanvasDrawer(ctx);
35966
+ drawer.setCameraBounds({
35967
+ minX: boardOutlineBounds.minX,
35968
+ maxX: boardOutlineBounds.maxX,
35969
+ minY: boardOutlineBounds.minY,
35970
+ maxY: boardOutlineBounds.maxY
35971
+ });
35972
+ const coveredPours = rectAndPolygonPours.filter(
35973
+ (p) => p.covered_with_solder_mask !== false
35974
+ );
35975
+ const uncoveredPours = rectAndPolygonPours.filter(
35976
+ (p) => p.covered_with_solder_mask === false
35977
+ );
35978
+ const coveredColor = `rgb(${colors.fr4TracesWithMaskGreen.map((c) => c * 255).join(",")})`;
35979
+ const uncoveredColor = `rgb(${colors.copper.map((c) => c * 255).join(",")})`;
35980
+ if (coveredPours.length > 0) {
35981
+ drawer.configure({
35982
+ colorOverrides: {
35983
+ copper: {
35984
+ top: coveredColor,
35985
+ bottom: coveredColor,
35986
+ inner1: coveredColor,
35987
+ inner2: coveredColor,
35988
+ inner3: coveredColor,
35989
+ inner4: coveredColor,
35990
+ inner5: coveredColor,
35991
+ inner6: coveredColor
35992
+ }
35993
+ }
35994
+ });
35995
+ drawer.drawElements(coveredPours, { layers: [layer] });
35996
+ }
35997
+ if (uncoveredPours.length > 0) {
35998
+ drawer.configure({
35999
+ colorOverrides: {
36000
+ copper: {
36001
+ top: uncoveredColor,
36002
+ bottom: uncoveredColor,
36003
+ inner1: uncoveredColor,
36004
+ inner2: uncoveredColor,
36005
+ inner3: uncoveredColor,
36006
+ inner4: uncoveredColor,
36007
+ inner5: uncoveredColor,
36008
+ inner6: uncoveredColor
36009
+ }
36010
+ }
36011
+ });
36012
+ drawer.drawElements(uncoveredPours, { layers: [layer] });
36013
+ }
36014
+ }
36015
+ for (const pour of brepPours) {
36016
+ const covered = pour.covered_with_solder_mask !== false;
36017
+ const colorArr = covered ? colors.fr4TracesWithMaskGreen : colors.copper;
36018
+ const copperColor = `rgb(${colorArr[0] * 255}, ${colorArr[1] * 255}, ${colorArr[2] * 255})`;
36019
+ ctx.fillStyle = copperColor;
36020
+ drawBrepShape({ ctx, pour, canvasXFromPcb, canvasYFromPcb });
36021
+ }
36022
+ const texture = new THREE29.CanvasTexture(canvas);
36023
+ texture.generateMipmaps = true;
36024
+ texture.minFilter = THREE29.LinearMipmapLinearFilter;
36025
+ texture.magFilter = THREE29.LinearFilter;
36026
+ texture.anisotropy = 16;
36027
+ texture.needsUpdate = true;
36028
+ return texture;
36029
+ }
36030
+
36031
+ // src/textures/create-three-texture-meshes.ts
36032
+ import * as THREE30 from "three";
36033
+ function createTexturePlane(config, boardData) {
36034
+ const {
36035
+ texture,
36036
+ yOffset,
36037
+ isBottomLayer,
36038
+ textureType,
36039
+ usePolygonOffset = false,
36040
+ renderOrder = 0
36041
+ } = config;
36042
+ if (!texture) return null;
36043
+ const boardOutlineBounds = calculateOutlineBounds(boardData);
36044
+ const planeGeom = new THREE30.PlaneGeometry(
36045
+ boardOutlineBounds.width,
36046
+ boardOutlineBounds.height
36047
+ );
36048
+ const material = new THREE30.MeshBasicMaterial({
36049
+ map: texture,
36050
+ transparent: true,
36051
+ side: THREE30.DoubleSide,
36052
+ depthWrite: textureType === "panel-outlines",
36053
+ polygonOffset: usePolygonOffset,
36054
+ polygonOffsetFactor: usePolygonOffset ? -4 : 0,
36055
+ // Increased for better z-fighting prevention
36056
+ polygonOffsetUnits: usePolygonOffset ? -4 : 0
36057
+ });
36058
+ const mesh = new THREE30.Mesh(planeGeom, material);
36059
+ mesh.position.set(
36060
+ boardOutlineBounds.centerX,
36061
+ boardOutlineBounds.centerY,
36062
+ yOffset
36063
+ );
36064
+ if (isBottomLayer) {
36065
+ mesh.rotation.set(Math.PI, 0, 0);
36066
+ }
36067
+ mesh.name = `${isBottomLayer ? "bottom" : "top"}-${textureType}-texture-plane`;
36068
+ mesh.renderOrder = renderOrder;
36069
+ return mesh;
36070
+ }
36071
+ function createTextureMeshes(textures, boardData, pcbThickness) {
36072
+ const meshes = [];
36073
+ if (!textures || !boardData || pcbThickness === null) return meshes;
36074
+ const topTraceMesh = createTexturePlane(
36075
+ {
36076
+ texture: textures.topTrace,
36077
+ yOffset: pcbThickness / 2 + BOARD_SURFACE_OFFSET.traces,
36078
+ // Use consistent copper offset
36079
+ isBottomLayer: false,
36080
+ textureType: "trace",
36081
+ usePolygonOffset: false,
36082
+ renderOrder: 2
36083
+ // Render after soldermask
36084
+ },
36085
+ boardData
36086
+ );
36087
+ if (topTraceMesh) meshes.push(topTraceMesh);
36088
+ const topTraceWithMaskMesh = createTexturePlane(
36089
+ {
36090
+ texture: textures.topTraceWithMask,
36091
+ yOffset: pcbThickness / 2 + BOARD_SURFACE_OFFSET.traces,
36092
+ isBottomLayer: false,
36093
+ textureType: "trace-with-mask",
36094
+ usePolygonOffset: false,
36095
+ renderOrder: 2
36096
+ // Render after soldermask
36097
+ },
36098
+ boardData
36099
+ );
36100
+ if (topTraceWithMaskMesh) meshes.push(topTraceWithMaskMesh);
36101
+ const topSilkscreenMesh = createTexturePlane(
36102
+ {
36103
+ texture: textures.topSilkscreen,
36104
+ yOffset: pcbThickness / 2 + 3e-3,
36105
+ // Slightly above soldermask
36106
+ isBottomLayer: false,
36107
+ textureType: "silkscreen",
36108
+ usePolygonOffset: false,
36109
+ renderOrder: 3
36110
+ // Render after traces
36111
+ },
36112
+ boardData
36113
+ );
36114
+ if (topSilkscreenMesh) meshes.push(topSilkscreenMesh);
36115
+ const bottomTraceMesh = createTexturePlane(
36116
+ {
36117
+ texture: textures.bottomTrace,
36118
+ yOffset: -pcbThickness / 2 - BOARD_SURFACE_OFFSET.traces,
36119
+ // Use consistent copper offset
36120
+ isBottomLayer: true,
36121
+ textureType: "trace",
36122
+ usePolygonOffset: false,
36123
+ renderOrder: 2
36124
+ // Render after soldermask
36125
+ },
36126
+ boardData
36127
+ );
36128
+ if (bottomTraceMesh) meshes.push(bottomTraceMesh);
36129
+ const bottomTraceWithMaskMesh = createTexturePlane(
36130
+ {
36131
+ texture: textures.bottomTraceWithMask,
36132
+ yOffset: -pcbThickness / 2 - BOARD_SURFACE_OFFSET.traces,
36133
+ isBottomLayer: true,
36134
+ textureType: "trace-with-mask",
36135
+ usePolygonOffset: false,
36136
+ renderOrder: 2
36137
+ // Render after soldermask
36138
+ },
36139
+ boardData
36140
+ );
36141
+ if (bottomTraceWithMaskMesh) meshes.push(bottomTraceWithMaskMesh);
36142
+ const bottomSilkscreenMesh = createTexturePlane(
36143
+ {
36144
+ texture: textures.bottomSilkscreen,
36145
+ yOffset: -pcbThickness / 2 - 3e-3,
36146
+ isBottomLayer: true,
36147
+ textureType: "silkscreen",
36148
+ usePolygonOffset: false,
36149
+ renderOrder: 3
36150
+ // Render after traces
36151
+ },
36152
+ boardData
36153
+ );
36154
+ if (bottomSilkscreenMesh) meshes.push(bottomSilkscreenMesh);
36155
+ const topSoldermaskMesh = createTexturePlane(
36156
+ {
36157
+ texture: textures.topSoldermask,
36158
+ yOffset: pcbThickness / 2 + 1e-3,
36159
+ // Just above board surface
36160
+ isBottomLayer: false,
36161
+ textureType: "soldermask",
36162
+ usePolygonOffset: true,
36163
+ // Enable polygon offset
36164
+ renderOrder: 1
36165
+ // Render after board (renderOrder)
36166
+ },
36167
+ boardData
36168
+ );
36169
+ if (topSoldermaskMesh) meshes.push(topSoldermaskMesh);
36170
+ const bottomSoldermaskMesh = createTexturePlane(
36171
+ {
36172
+ texture: textures.bottomSoldermask,
36173
+ yOffset: -pcbThickness / 2 - 1e-3,
36174
+ // Just below board surface (bottom side)
36175
+ isBottomLayer: true,
36176
+ textureType: "soldermask",
36177
+ usePolygonOffset: true,
36178
+ // Enable polygon offset
36179
+ renderOrder: 1
36180
+ // Render after board (renderOrder)
36181
+ },
36182
+ boardData
36183
+ );
36184
+ if (bottomSoldermaskMesh) meshes.push(bottomSoldermaskMesh);
36185
+ const topCopperTextMesh = createTexturePlane(
36186
+ {
36187
+ texture: textures.topCopperText,
36188
+ yOffset: pcbThickness / 2 + BOARD_SURFACE_OFFSET.copper,
36189
+ isBottomLayer: false,
36190
+ textureType: "copper-text",
36191
+ usePolygonOffset: false,
36192
+ renderOrder: 2
36193
+ // Render after soldermask
36194
+ },
36195
+ boardData
36196
+ );
36197
+ if (topCopperTextMesh) meshes.push(topCopperTextMesh);
36198
+ const bottomCopperTextMesh = createTexturePlane(
36199
+ {
36200
+ texture: textures.bottomCopperText,
36201
+ yOffset: -pcbThickness / 2 - BOARD_SURFACE_OFFSET.copper,
36202
+ isBottomLayer: true,
36203
+ textureType: "copper-text",
36204
+ usePolygonOffset: false,
36205
+ renderOrder: 2
36206
+ // Render after soldermask
36207
+ },
36208
+ boardData
36209
+ );
36210
+ if (bottomCopperTextMesh) meshes.push(bottomCopperTextMesh);
36211
+ const topCopperMesh = createTexturePlane(
36212
+ {
36213
+ texture: textures.topCopper,
36214
+ yOffset: pcbThickness / 2 + BOARD_SURFACE_OFFSET.copper,
36215
+ isBottomLayer: false,
36216
+ textureType: "copper",
36217
+ usePolygonOffset: false,
36218
+ renderOrder: 2
36219
+ // Render after soldermask
36220
+ },
36221
+ boardData
36222
+ );
36223
+ if (topCopperMesh) meshes.push(topCopperMesh);
36224
+ const bottomCopperMesh = createTexturePlane(
36225
+ {
36226
+ texture: textures.bottomCopper,
36227
+ yOffset: -pcbThickness / 2 - BOARD_SURFACE_OFFSET.copper,
36228
+ isBottomLayer: true,
36229
+ textureType: "copper",
36230
+ usePolygonOffset: false,
36231
+ renderOrder: 2
36232
+ // Render after soldermask
36233
+ },
36234
+ boardData
36235
+ );
36236
+ if (bottomCopperMesh) meshes.push(bottomCopperMesh);
36237
+ const topPanelOutlinesMesh = createTexturePlane(
36238
+ {
36239
+ texture: textures.topPanelOutlines,
36240
+ yOffset: pcbThickness / 2 + 4e-3,
36241
+ // Above silkscreen
36242
+ isBottomLayer: false,
36243
+ textureType: "panel-outlines",
36244
+ usePolygonOffset: false,
36245
+ renderOrder: 4
36246
+ },
36247
+ boardData
36248
+ );
36249
+ if (topPanelOutlinesMesh) meshes.push(topPanelOutlinesMesh);
36250
+ const bottomPanelOutlinesMesh = createTexturePlane(
36251
+ {
36252
+ texture: textures.bottomPanelOutlines,
36253
+ yOffset: -pcbThickness / 2 - 4e-3,
36254
+ // Below bottom silkscreen
36255
+ isBottomLayer: true,
36256
+ textureType: "panel-outlines",
36257
+ usePolygonOffset: false,
36258
+ renderOrder: 4
36259
+ },
36260
+ boardData
36261
+ );
36262
+ if (bottomPanelOutlinesMesh) meshes.push(bottomPanelOutlinesMesh);
36263
+ return meshes;
36264
+ }
36265
+
36038
36266
  // src/hooks/useManifoldBoardBuilder.ts
36039
36267
  var useManifoldBoardBuilder = (manifoldJSModule, circuitJson) => {
36040
36268
  const [geoms, setGeoms] = useState14(null);
@@ -36104,7 +36332,7 @@ var useManifoldBoardBuilder = (manifoldJSModule, circuitJson) => {
36104
36332
  manifoldInstancesForCleanup.current = [];
36105
36333
  let boardManifold = null;
36106
36334
  const currentGeoms = {};
36107
- const currentTextures = {};
36335
+ const layerTextureMap = {};
36108
36336
  try {
36109
36337
  const currentPcbThickness = boardData.thickness || 1.6;
36110
36338
  setPcbThickness(currentPcbThickness);
@@ -36203,7 +36431,7 @@ var useManifoldBoardBuilder = (manifoldJSModule, circuitJson) => {
36203
36431
  {
36204
36432
  key: "plated-holes-union",
36205
36433
  geometry: cutPlatedGeom,
36206
- color: new THREE29.Color(
36434
+ color: new THREE31.Color(
36207
36435
  colors.copper[0],
36208
36436
  colors.copper[1],
36209
36437
  colors.copper[2]
@@ -36233,7 +36461,7 @@ var useManifoldBoardBuilder = (manifoldJSModule, circuitJson) => {
36233
36461
  const matColorArray = boardMaterialColors[boardData.material] ?? colors.fr4Tan;
36234
36462
  currentGeoms.board = {
36235
36463
  geometry: finalBoardGeom,
36236
- color: new THREE29.Color(
36464
+ color: new THREE31.Color(
36237
36465
  matColorArray[0],
36238
36466
  matColorArray[1],
36239
36467
  matColorArray[2]
@@ -36251,28 +36479,17 @@ var useManifoldBoardBuilder = (manifoldJSModule, circuitJson) => {
36251
36479
  boardClipVolume
36252
36480
  );
36253
36481
  currentGeoms.smtPads = smtPadGeoms;
36254
- const { copperPourGeoms } = processCopperPoursForManifold(
36255
- Manifold,
36256
- CrossSection,
36257
- circuitJson,
36258
- currentPcbThickness,
36259
- manifoldInstancesForCleanup.current,
36260
- boardData.material,
36261
- holeUnion,
36262
- boardClipVolume
36263
- );
36264
- currentGeoms.copperPours = copperPourGeoms;
36265
36482
  setGeoms(currentGeoms);
36266
36483
  const traceColorWithoutMaskArr = colors.fr4TracesWithoutMaskTan;
36267
36484
  const traceColorWithoutMask = `rgb(${Math.round(traceColorWithoutMaskArr[0] * 255)}, ${Math.round(traceColorWithoutMaskArr[1] * 255)}, ${Math.round(traceColorWithoutMaskArr[2] * 255)})`;
36268
- currentTextures.topTrace = createTraceTextureForLayer({
36485
+ layerTextureMap.topTrace = createTraceTextureForLayer({
36269
36486
  layer: "top",
36270
36487
  circuitJson,
36271
36488
  boardData,
36272
36489
  traceColor: traceColorWithoutMask,
36273
36490
  traceTextureResolution: TRACE_TEXTURE_RESOLUTION
36274
36491
  });
36275
- currentTextures.bottomTrace = createTraceTextureForLayer({
36492
+ layerTextureMap.bottomTrace = createTraceTextureForLayer({
36276
36493
  layer: "bottom",
36277
36494
  circuitJson,
36278
36495
  boardData,
@@ -36281,14 +36498,14 @@ var useManifoldBoardBuilder = (manifoldJSModule, circuitJson) => {
36281
36498
  });
36282
36499
  const traceColorWithMaskArr = colors.fr4TracesWithMaskGreen;
36283
36500
  const traceColorWithMask = `rgb(${Math.round(traceColorWithMaskArr[0] * 255)}, ${Math.round(traceColorWithMaskArr[1] * 255)}, ${Math.round(traceColorWithMaskArr[2] * 255)})`;
36284
- currentTextures.topTraceWithMask = createTraceTextureForLayer({
36501
+ layerTextureMap.topTraceWithMask = createTraceTextureForLayer({
36285
36502
  layer: "top",
36286
36503
  circuitJson,
36287
36504
  boardData,
36288
36505
  traceColor: traceColorWithMask,
36289
36506
  traceTextureResolution: TRACE_TEXTURE_RESOLUTION
36290
36507
  });
36291
- currentTextures.bottomTraceWithMask = createTraceTextureForLayer({
36508
+ layerTextureMap.bottomTraceWithMask = createTraceTextureForLayer({
36292
36509
  layer: "bottom",
36293
36510
  circuitJson,
36294
36511
  boardData,
@@ -36296,14 +36513,14 @@ var useManifoldBoardBuilder = (manifoldJSModule, circuitJson) => {
36296
36513
  traceTextureResolution: TRACE_TEXTURE_RESOLUTION
36297
36514
  });
36298
36515
  const silkscreenColor = "rgb(255,255,255)";
36299
- currentTextures.topSilkscreen = createSilkscreenTextureForLayer({
36516
+ layerTextureMap.topSilkscreen = createSilkscreenTextureForLayer({
36300
36517
  layer: "top",
36301
36518
  circuitJson,
36302
36519
  boardData,
36303
36520
  silkscreenColor,
36304
36521
  traceTextureResolution: TRACE_TEXTURE_RESOLUTION
36305
36522
  });
36306
- currentTextures.bottomSilkscreen = createSilkscreenTextureForLayer({
36523
+ layerTextureMap.bottomSilkscreen = createSilkscreenTextureForLayer({
36307
36524
  layer: "bottom",
36308
36525
  circuitJson,
36309
36526
  boardData,
@@ -36312,14 +36529,14 @@ var useManifoldBoardBuilder = (manifoldJSModule, circuitJson) => {
36312
36529
  });
36313
36530
  const soldermaskColorArr = soldermaskColors[boardData.material] ?? colors.fr4SolderMaskGreen;
36314
36531
  const soldermaskColor = `rgb(${Math.round(soldermaskColorArr[0] * 255)}, ${Math.round(soldermaskColorArr[1] * 255)}, ${Math.round(soldermaskColorArr[2] * 255)})`;
36315
- currentTextures.topSoldermask = createSoldermaskTextureForLayer({
36532
+ layerTextureMap.topSoldermask = createSoldermaskTextureForLayer({
36316
36533
  layer: "top",
36317
36534
  circuitJson,
36318
36535
  boardData,
36319
36536
  soldermaskColor,
36320
36537
  traceTextureResolution: TRACE_TEXTURE_RESOLUTION
36321
36538
  });
36322
- currentTextures.bottomSoldermask = createSoldermaskTextureForLayer({
36539
+ layerTextureMap.bottomSoldermask = createSoldermaskTextureForLayer({
36323
36540
  layer: "bottom",
36324
36541
  circuitJson,
36325
36542
  boardData,
@@ -36328,33 +36545,45 @@ var useManifoldBoardBuilder = (manifoldJSModule, circuitJson) => {
36328
36545
  });
36329
36546
  const copperColorArr = colors.copper;
36330
36547
  const copperColor = `rgb(${Math.round(copperColorArr[0] * 255)}, ${Math.round(copperColorArr[1] * 255)}, ${Math.round(copperColorArr[2] * 255)})`;
36331
- currentTextures.topCopperText = createCopperTextTextureForLayer({
36548
+ layerTextureMap.topCopperText = createCopperTextTextureForLayer({
36332
36549
  layer: "top",
36333
36550
  circuitJson,
36334
36551
  boardData,
36335
36552
  copperColor,
36336
36553
  traceTextureResolution: TRACE_TEXTURE_RESOLUTION
36337
36554
  });
36338
- currentTextures.bottomCopperText = createCopperTextTextureForLayer({
36555
+ layerTextureMap.bottomCopperText = createCopperTextTextureForLayer({
36339
36556
  layer: "bottom",
36340
36557
  circuitJson,
36341
36558
  boardData,
36342
36559
  copperColor,
36343
36560
  traceTextureResolution: TRACE_TEXTURE_RESOLUTION
36344
36561
  });
36345
- currentTextures.topPanelOutlines = createPanelOutlineTextureForLayer({
36562
+ layerTextureMap.topPanelOutlines = createPanelOutlineTextureForLayer({
36346
36563
  layer: "top",
36347
36564
  circuitJson,
36348
36565
  panelData: boardData,
36349
36566
  traceTextureResolution: TRACE_TEXTURE_RESOLUTION
36350
36567
  });
36351
- currentTextures.bottomPanelOutlines = createPanelOutlineTextureForLayer({
36568
+ layerTextureMap.bottomPanelOutlines = createPanelOutlineTextureForLayer({
36352
36569
  layer: "bottom",
36353
36570
  circuitJson,
36354
36571
  panelData: boardData,
36355
36572
  traceTextureResolution: TRACE_TEXTURE_RESOLUTION
36356
36573
  });
36357
- setTextures(currentTextures);
36574
+ layerTextureMap.topCopper = createCopperPourTextureForLayer({
36575
+ layer: "top",
36576
+ circuitJson,
36577
+ boardData,
36578
+ traceTextureResolution: TRACE_TEXTURE_RESOLUTION
36579
+ });
36580
+ layerTextureMap.bottomCopper = createCopperPourTextureForLayer({
36581
+ layer: "bottom",
36582
+ circuitJson,
36583
+ boardData,
36584
+ traceTextureResolution: TRACE_TEXTURE_RESOLUTION
36585
+ });
36586
+ setTextures(layerTextureMap);
36358
36587
  } catch (e) {
36359
36588
  console.error("Error processing geometry with Manifold in hook:", e);
36360
36589
  setError(
@@ -36382,11 +36611,11 @@ var useManifoldBoardBuilder = (manifoldJSModule, circuitJson) => {
36382
36611
  };
36383
36612
 
36384
36613
  // src/utils/manifold/create-three-geometry-meshes.ts
36385
- import * as THREE31 from "three";
36614
+ import * as THREE33 from "three";
36386
36615
 
36387
36616
  // src/utils/create-board-material.ts
36388
- import * as THREE30 from "three";
36389
- var DEFAULT_SIDE = THREE30.DoubleSide;
36617
+ import * as THREE32 from "three";
36618
+ var DEFAULT_SIDE = THREE32.DoubleSide;
36390
36619
  var createBoardMaterial = ({
36391
36620
  material,
36392
36621
  color,
@@ -36394,7 +36623,7 @@ var createBoardMaterial = ({
36394
36623
  isFaux = false
36395
36624
  }) => {
36396
36625
  if (material === "fr4") {
36397
- return new THREE30.MeshPhysicalMaterial({
36626
+ return new THREE32.MeshPhysicalMaterial({
36398
36627
  color,
36399
36628
  side,
36400
36629
  metalness: 0,
@@ -36408,7 +36637,7 @@ var createBoardMaterial = ({
36408
36637
  flatShading: true
36409
36638
  });
36410
36639
  }
36411
- return new THREE30.MeshStandardMaterial({
36640
+ return new THREE32.MeshStandardMaterial({
36412
36641
  color,
36413
36642
  side,
36414
36643
  flatShading: true,
@@ -36424,12 +36653,12 @@ function createGeometryMeshes(geoms) {
36424
36653
  const meshes = [];
36425
36654
  if (!geoms) return meshes;
36426
36655
  if (geoms.board && geoms.board.geometry) {
36427
- const mesh = new THREE31.Mesh(
36656
+ const mesh = new THREE33.Mesh(
36428
36657
  geoms.board.geometry,
36429
36658
  createBoardMaterial({
36430
36659
  material: geoms.board.material,
36431
36660
  color: geoms.board.color,
36432
- side: THREE31.DoubleSide,
36661
+ side: THREE33.DoubleSide,
36433
36662
  isFaux: geoms.board.isFaux
36434
36663
  })
36435
36664
  );
@@ -36439,11 +36668,11 @@ function createGeometryMeshes(geoms) {
36439
36668
  const createMeshesFromArray = (geomArray) => {
36440
36669
  if (geomArray) {
36441
36670
  geomArray.forEach((comp) => {
36442
- const mesh = new THREE31.Mesh(
36671
+ const mesh = new THREE33.Mesh(
36443
36672
  comp.geometry,
36444
- new THREE31.MeshStandardMaterial({
36673
+ new THREE33.MeshStandardMaterial({
36445
36674
  color: comp.color,
36446
- side: THREE31.DoubleSide,
36675
+ side: THREE33.DoubleSide,
36447
36676
  flatShading: true,
36448
36677
  // Consistent with board
36449
36678
  polygonOffset: true,
@@ -36459,172 +36688,6 @@ function createGeometryMeshes(geoms) {
36459
36688
  createMeshesFromArray(geoms.platedHoles);
36460
36689
  createMeshesFromArray(geoms.smtPads);
36461
36690
  createMeshesFromArray(geoms.vias);
36462
- createMeshesFromArray(geoms.copperPours);
36463
- return meshes;
36464
- }
36465
-
36466
- // src/utils/manifold/create-three-texture-meshes.ts
36467
- import * as THREE32 from "three";
36468
- function createTextureMeshes(textures, boardData, pcbThickness) {
36469
- const meshes = [];
36470
- if (!textures || !boardData || pcbThickness === null) return meshes;
36471
- const createTexturePlane = (texture, yOffset, isBottomLayer, keySuffix, usePolygonOffset = false, renderOrder = 0) => {
36472
- if (!texture) return null;
36473
- const boardOutlineBounds = calculateOutlineBounds(boardData);
36474
- const planeGeom = new THREE32.PlaneGeometry(
36475
- boardOutlineBounds.width,
36476
- boardOutlineBounds.height
36477
- );
36478
- const material = new THREE32.MeshBasicMaterial({
36479
- map: texture,
36480
- transparent: true,
36481
- side: THREE32.DoubleSide,
36482
- depthWrite: keySuffix === "panel-outlines",
36483
- polygonOffset: usePolygonOffset,
36484
- polygonOffsetFactor: usePolygonOffset ? -4 : 0,
36485
- // Increased for better z-fighting prevention
36486
- polygonOffsetUnits: usePolygonOffset ? -4 : 0
36487
- });
36488
- const mesh = new THREE32.Mesh(planeGeom, material);
36489
- mesh.position.set(
36490
- boardOutlineBounds.centerX,
36491
- boardOutlineBounds.centerY,
36492
- yOffset
36493
- );
36494
- if (isBottomLayer) {
36495
- mesh.rotation.set(Math.PI, 0, 0);
36496
- }
36497
- mesh.name = `${isBottomLayer ? "bottom" : "top"}-${keySuffix}-texture-plane`;
36498
- mesh.renderOrder = renderOrder;
36499
- return mesh;
36500
- };
36501
- const topTraceMesh = createTexturePlane(
36502
- textures.topTrace,
36503
- pcbThickness / 2 + BOARD_SURFACE_OFFSET.traces,
36504
- // Use consistent copper offset
36505
- false,
36506
- "trace",
36507
- false,
36508
- 2
36509
- // Render after soldermask
36510
- );
36511
- if (topTraceMesh) meshes.push(topTraceMesh);
36512
- const topTraceWithMaskMesh = createTexturePlane(
36513
- textures.topTraceWithMask,
36514
- pcbThickness / 2 + BOARD_SURFACE_OFFSET.traces,
36515
- false,
36516
- "trace-with-mask",
36517
- false,
36518
- 2
36519
- // Render after soldermask
36520
- );
36521
- if (topTraceWithMaskMesh) meshes.push(topTraceWithMaskMesh);
36522
- const topSilkscreenMesh = createTexturePlane(
36523
- textures.topSilkscreen,
36524
- pcbThickness / 2 + 3e-3,
36525
- // Slightly above soldermask
36526
- false,
36527
- "silkscreen",
36528
- false,
36529
- 3
36530
- // Render after traces
36531
- );
36532
- if (topSilkscreenMesh) meshes.push(topSilkscreenMesh);
36533
- const bottomTraceMesh = createTexturePlane(
36534
- textures.bottomTrace,
36535
- -pcbThickness / 2 - BOARD_SURFACE_OFFSET.traces,
36536
- // Use consistent copper offset
36537
- true,
36538
- "trace",
36539
- false,
36540
- 2
36541
- // Render after soldermask
36542
- );
36543
- if (bottomTraceMesh) meshes.push(bottomTraceMesh);
36544
- const bottomTraceWithMaskMesh = createTexturePlane(
36545
- textures.bottomTraceWithMask,
36546
- -pcbThickness / 2 - BOARD_SURFACE_OFFSET.traces,
36547
- true,
36548
- "trace-with-mask",
36549
- false,
36550
- 2
36551
- // Render after soldermask
36552
- );
36553
- if (bottomTraceWithMaskMesh) meshes.push(bottomTraceWithMaskMesh);
36554
- const bottomSilkscreenMesh = createTexturePlane(
36555
- textures.bottomSilkscreen,
36556
- -pcbThickness / 2 - 3e-3,
36557
- true,
36558
- "silkscreen",
36559
- false,
36560
- 3
36561
- // Render after traces
36562
- );
36563
- if (bottomSilkscreenMesh) meshes.push(bottomSilkscreenMesh);
36564
- const topSoldermaskMesh = createTexturePlane(
36565
- textures.topSoldermask,
36566
- pcbThickness / 2 + 1e-3,
36567
- // Just above board surface
36568
- false,
36569
- "soldermask",
36570
- true,
36571
- // Enable polygon offset
36572
- 1
36573
- // Render after board (renderOrder)
36574
- );
36575
- if (topSoldermaskMesh) meshes.push(topSoldermaskMesh);
36576
- const bottomSoldermaskMesh = createTexturePlane(
36577
- textures.bottomSoldermask,
36578
- -pcbThickness / 2 - 1e-3,
36579
- // Just below board surface (bottom side)
36580
- true,
36581
- "soldermask",
36582
- true,
36583
- // Enable polygon offset
36584
- 1
36585
- // Render after board (renderOrder)
36586
- );
36587
- if (bottomSoldermaskMesh) meshes.push(bottomSoldermaskMesh);
36588
- const topCopperTextMesh = createTexturePlane(
36589
- textures.topCopperText,
36590
- pcbThickness / 2 + BOARD_SURFACE_OFFSET.copper,
36591
- false,
36592
- "copper-text",
36593
- false,
36594
- 2
36595
- // Render after soldermask
36596
- );
36597
- if (topCopperTextMesh) meshes.push(topCopperTextMesh);
36598
- const bottomCopperTextMesh = createTexturePlane(
36599
- textures.bottomCopperText,
36600
- -pcbThickness / 2 - BOARD_SURFACE_OFFSET.copper,
36601
- true,
36602
- "copper-text",
36603
- false,
36604
- 2
36605
- // Render after soldermask
36606
- );
36607
- if (bottomCopperTextMesh) meshes.push(bottomCopperTextMesh);
36608
- const topPanelOutlinesMesh = createTexturePlane(
36609
- textures.topPanelOutlines,
36610
- pcbThickness / 2 + 4e-3,
36611
- // Above silkscreen
36612
- false,
36613
- "panel-outlines",
36614
- false,
36615
- 4
36616
- );
36617
- if (topPanelOutlinesMesh) meshes.push(topPanelOutlinesMesh);
36618
- const bottomPanelOutlinesMesh = createTexturePlane(
36619
- textures.bottomPanelOutlines,
36620
- -pcbThickness / 2 - 4e-3,
36621
- // Below bottom silkscreen
36622
- true,
36623
- "panel-outlines",
36624
- false,
36625
- 4
36626
- );
36627
- if (bottomPanelOutlinesMesh) meshes.push(bottomPanelOutlinesMesh);
36628
36691
  return meshes;
36629
36692
  }
36630
36693
 
@@ -36652,8 +36715,6 @@ var BoardMeshes = ({
36652
36715
  }
36653
36716
  } else if (mesh.name.includes("plated_hole") || mesh.name.includes("via")) {
36654
36717
  shouldShow = visibility.topCopper || visibility.bottomCopper;
36655
- } else if (mesh.name.includes("copper_pour")) {
36656
- shouldShow = visibility.topCopper || visibility.bottomCopper;
36657
36718
  }
36658
36719
  if (shouldShow) {
36659
36720
  rootObject.add(mesh);
@@ -36681,6 +36742,10 @@ var BoardMeshes = ({
36681
36742
  shouldShow = visibility.topCopper;
36682
36743
  } else if (mesh.name.includes("bottom-copper-text")) {
36683
36744
  shouldShow = visibility.bottomCopper;
36745
+ } else if (mesh.name.includes("top-copper")) {
36746
+ shouldShow = visibility.topCopper;
36747
+ } else if (mesh.name.includes("bottom-copper")) {
36748
+ shouldShow = visibility.bottomCopper;
36684
36749
  } else if (mesh.name.includes("panel-outlines")) {
36685
36750
  shouldShow = visibility.boardBody;
36686
36751
  }
@@ -43536,7 +43601,7 @@ var KeyboardShortcutsDialog = ({
43536
43601
 
43537
43602
  // src/CadViewer.tsx
43538
43603
  import { jsx as jsx37, jsxs as jsxs11 } from "react/jsx-runtime";
43539
- var DEFAULT_TARGET = new THREE33.Vector3(0, 0, 0);
43604
+ var DEFAULT_TARGET = new THREE34.Vector3(0, 0, 0);
43540
43605
  var INITIAL_CAMERA_POSITION = [5, -5, 5];
43541
43606
  var CadViewerInner = (props) => {
43542
43607
  const [engine, setEngine] = useState35("manifold");
@@ -43803,11 +43868,11 @@ var CadViewer = (props) => {
43803
43868
  // src/convert-circuit-json-to-3d-svg.ts
43804
43869
  var import_debug = __toESM(require_browser(), 1);
43805
43870
  import { su as su20 } from "@tscircuit/circuit-json-util";
43806
- import * as THREE37 from "three";
43871
+ import * as THREE38 from "three";
43807
43872
  import { SVGRenderer } from "three/examples/jsm/renderers/SVGRenderer.js";
43808
43873
 
43809
43874
  // src/utils/create-geometry-from-polygons.ts
43810
- import * as THREE34 from "three";
43875
+ import * as THREE35 from "three";
43811
43876
  import { BufferGeometry as BufferGeometry3, Float32BufferAttribute as Float32BufferAttribute2 } from "three";
43812
43877
  function createGeometryFromPolygons(polygons) {
43813
43878
  const geometry = new BufferGeometry3();
@@ -43821,12 +43886,12 @@ function createGeometryFromPolygons(polygons) {
43821
43886
  ...polygon3.vertices[i + 1]
43822
43887
  // Third vertex
43823
43888
  );
43824
- const v1 = new THREE34.Vector3(...polygon3.vertices[0]);
43825
- const v2 = new THREE34.Vector3(...polygon3.vertices[i]);
43826
- const v3 = new THREE34.Vector3(...polygon3.vertices[i + 1]);
43827
- const normal = new THREE34.Vector3().crossVectors(
43828
- new THREE34.Vector3().subVectors(v2, v1),
43829
- new THREE34.Vector3().subVectors(v3, v1)
43889
+ const v1 = new THREE35.Vector3(...polygon3.vertices[0]);
43890
+ const v2 = new THREE35.Vector3(...polygon3.vertices[i]);
43891
+ const v3 = new THREE35.Vector3(...polygon3.vertices[i + 1]);
43892
+ const normal = new THREE35.Vector3().crossVectors(
43893
+ new THREE35.Vector3().subVectors(v2, v1),
43894
+ new THREE35.Vector3().subVectors(v3, v1)
43830
43895
  ).normalize();
43831
43896
  normals.push(
43832
43897
  normal.x,
@@ -43850,10 +43915,10 @@ function createGeometryFromPolygons(polygons) {
43850
43915
  var import_modeling2 = __toESM(require_src(), 1);
43851
43916
  var import_jscad_planner2 = __toESM(require_dist(), 1);
43852
43917
  var jscadModeling2 = __toESM(require_src(), 1);
43853
- import * as THREE36 from "three";
43918
+ import * as THREE37 from "three";
43854
43919
 
43855
43920
  // src/utils/load-model.ts
43856
- import * as THREE35 from "three";
43921
+ import * as THREE36 from "three";
43857
43922
  import { GLTFLoader as GLTFLoader2 } from "three/examples/jsm/loaders/GLTFLoader.js";
43858
43923
  import { OBJLoader as OBJLoader2 } from "three/examples/jsm/loaders/OBJLoader.js";
43859
43924
  import { STLLoader as STLLoader2 } from "three/examples/jsm/loaders/STLLoader.js";
@@ -43861,12 +43926,12 @@ async function load3DModel(url) {
43861
43926
  if (url.endsWith(".stl")) {
43862
43927
  const loader = new STLLoader2();
43863
43928
  const geometry = await loader.loadAsync(url);
43864
- const material = new THREE35.MeshStandardMaterial({
43929
+ const material = new THREE36.MeshStandardMaterial({
43865
43930
  color: 8947848,
43866
43931
  metalness: 0.5,
43867
43932
  roughness: 0.5
43868
43933
  });
43869
- return new THREE35.Mesh(geometry, material);
43934
+ return new THREE36.Mesh(geometry, material);
43870
43935
  }
43871
43936
  if (url.endsWith(".obj")) {
43872
43937
  const loader = new OBJLoader2();
@@ -43899,9 +43964,9 @@ async function renderComponent(component, scene) {
43899
43964
  }
43900
43965
  if (component.rotation) {
43901
43966
  model.rotation.set(
43902
- THREE36.MathUtils.degToRad(component.rotation.x ?? 0),
43903
- THREE36.MathUtils.degToRad(component.rotation.y ?? 0),
43904
- THREE36.MathUtils.degToRad(component.rotation.z ?? 0)
43967
+ THREE37.MathUtils.degToRad(component.rotation.x ?? 0),
43968
+ THREE37.MathUtils.degToRad(component.rotation.y ?? 0),
43969
+ THREE37.MathUtils.degToRad(component.rotation.z ?? 0)
43905
43970
  );
43906
43971
  }
43907
43972
  scene.add(model);
@@ -43915,13 +43980,13 @@ async function renderComponent(component, scene) {
43915
43980
  );
43916
43981
  if (jscadObject && (jscadObject.polygons || jscadObject.sides)) {
43917
43982
  const threeGeom = convertCSGToThreeGeom(jscadObject);
43918
- const material2 = new THREE36.MeshStandardMaterial({
43983
+ const material2 = new THREE37.MeshStandardMaterial({
43919
43984
  color: 8947848,
43920
43985
  metalness: 0.5,
43921
43986
  roughness: 0.5,
43922
- side: THREE36.DoubleSide
43987
+ side: THREE37.DoubleSide
43923
43988
  });
43924
- const mesh2 = new THREE36.Mesh(threeGeom, material2);
43989
+ const mesh2 = new THREE37.Mesh(threeGeom, material2);
43925
43990
  if (component.position) {
43926
43991
  mesh2.position.set(
43927
43992
  component.position.x ?? 0,
@@ -43931,9 +43996,9 @@ async function renderComponent(component, scene) {
43931
43996
  }
43932
43997
  if (component.rotation) {
43933
43998
  mesh2.rotation.set(
43934
- THREE36.MathUtils.degToRad(component.rotation.x ?? 0),
43935
- THREE36.MathUtils.degToRad(component.rotation.y ?? 0),
43936
- THREE36.MathUtils.degToRad(component.rotation.z ?? 0)
43999
+ THREE37.MathUtils.degToRad(component.rotation.x ?? 0),
44000
+ THREE37.MathUtils.degToRad(component.rotation.y ?? 0),
44001
+ THREE37.MathUtils.degToRad(component.rotation.z ?? 0)
43937
44002
  );
43938
44003
  }
43939
44004
  scene.add(mesh2);
@@ -43950,17 +44015,17 @@ async function renderComponent(component, scene) {
43950
44015
  if (!geom || !geom.polygons && !geom.sides) {
43951
44016
  continue;
43952
44017
  }
43953
- const color = new THREE36.Color(geomInfo.color);
44018
+ const color = new THREE37.Color(geomInfo.color);
43954
44019
  color.convertLinearToSRGB();
43955
44020
  const geomWithColor = { ...geom, color: [color.r, color.g, color.b] };
43956
44021
  const threeGeom = convertCSGToThreeGeom(geomWithColor);
43957
- const material2 = new THREE36.MeshStandardMaterial({
44022
+ const material2 = new THREE37.MeshStandardMaterial({
43958
44023
  vertexColors: true,
43959
44024
  metalness: 0.2,
43960
44025
  roughness: 0.8,
43961
- side: THREE36.DoubleSide
44026
+ side: THREE37.DoubleSide
43962
44027
  });
43963
- const mesh2 = new THREE36.Mesh(threeGeom, material2);
44028
+ const mesh2 = new THREE37.Mesh(threeGeom, material2);
43964
44029
  if (component.position) {
43965
44030
  mesh2.position.set(
43966
44031
  component.position.x ?? 0,
@@ -43970,22 +44035,22 @@ async function renderComponent(component, scene) {
43970
44035
  }
43971
44036
  if (component.rotation) {
43972
44037
  mesh2.rotation.set(
43973
- THREE36.MathUtils.degToRad(component.rotation.x ?? 0),
43974
- THREE36.MathUtils.degToRad(component.rotation.y ?? 0),
43975
- THREE36.MathUtils.degToRad(component.rotation.z ?? 0)
44038
+ THREE37.MathUtils.degToRad(component.rotation.x ?? 0),
44039
+ THREE37.MathUtils.degToRad(component.rotation.y ?? 0),
44040
+ THREE37.MathUtils.degToRad(component.rotation.z ?? 0)
43976
44041
  );
43977
44042
  }
43978
44043
  scene.add(mesh2);
43979
44044
  }
43980
44045
  return;
43981
44046
  }
43982
- const geometry = new THREE36.BoxGeometry(0.5, 0.5, 0.5);
43983
- const material = new THREE36.MeshStandardMaterial({
44047
+ const geometry = new THREE37.BoxGeometry(0.5, 0.5, 0.5);
44048
+ const material = new THREE37.MeshStandardMaterial({
43984
44049
  color: 16711680,
43985
44050
  transparent: true,
43986
44051
  opacity: 0.25
43987
44052
  });
43988
- const mesh = new THREE36.Mesh(geometry, material);
44053
+ const mesh = new THREE37.Mesh(geometry, material);
43989
44054
  if (component.position) {
43990
44055
  mesh.position.set(
43991
44056
  component.position.x ?? 0,
@@ -44006,11 +44071,11 @@ async function convertCircuitJsonTo3dSvg(circuitJson, options = {}) {
44006
44071
  padding = 20,
44007
44072
  zoom = 1.5
44008
44073
  } = options;
44009
- const scene = new THREE37.Scene();
44074
+ const scene = new THREE38.Scene();
44010
44075
  const renderer = new SVGRenderer();
44011
44076
  renderer.setSize(width10, height10);
44012
- renderer.setClearColor(new THREE37.Color(backgroundColor), 1);
44013
- const camera = new THREE37.OrthographicCamera();
44077
+ renderer.setClearColor(new THREE38.Color(backgroundColor), 1);
44078
+ const camera = new THREE38.OrthographicCamera();
44014
44079
  const aspect = width10 / height10;
44015
44080
  const frustumSize = 100;
44016
44081
  const halfFrustumSize = frustumSize / 2 / zoom;
@@ -44024,11 +44089,11 @@ async function convertCircuitJsonTo3dSvg(circuitJson, options = {}) {
44024
44089
  camera.position.set(position.x, position.y, position.z);
44025
44090
  camera.up.set(0, 1, 0);
44026
44091
  const lookAt = options.camera?.lookAt ?? { x: 0, y: 0, z: 0 };
44027
- camera.lookAt(new THREE37.Vector3(lookAt.x, lookAt.y, lookAt.z));
44092
+ camera.lookAt(new THREE38.Vector3(lookAt.x, lookAt.y, lookAt.z));
44028
44093
  camera.updateProjectionMatrix();
44029
- const ambientLight = new THREE37.AmbientLight(16777215, Math.PI / 2);
44094
+ const ambientLight = new THREE38.AmbientLight(16777215, Math.PI / 2);
44030
44095
  scene.add(ambientLight);
44031
- const pointLight = new THREE37.PointLight(16777215, Math.PI / 4);
44096
+ const pointLight = new THREE38.PointLight(16777215, Math.PI / 4);
44032
44097
  pointLight.position.set(-10, -10, 10);
44033
44098
  scene.add(pointLight);
44034
44099
  const components = su20(circuitJson).cad_component.list();
@@ -44039,7 +44104,7 @@ async function convertCircuitJsonTo3dSvg(circuitJson, options = {}) {
44039
44104
  const boardGeom = createBoardGeomFromCircuitJson(circuitJson);
44040
44105
  if (boardGeom) {
44041
44106
  const solderMaskColor = colors.fr4SolderMaskGreen;
44042
- const baseColor = new THREE37.Color(
44107
+ const baseColor = new THREE38.Color(
44043
44108
  solderMaskColor[0],
44044
44109
  solderMaskColor[1],
44045
44110
  solderMaskColor[2]
@@ -44051,28 +44116,28 @@ async function convertCircuitJsonTo3dSvg(circuitJson, options = {}) {
44051
44116
  const material = createBoardMaterial({
44052
44117
  material: boardData?.material,
44053
44118
  color: baseColor,
44054
- side: THREE37.DoubleSide
44119
+ side: THREE38.DoubleSide
44055
44120
  });
44056
- const mesh = new THREE37.Mesh(geometry, material);
44121
+ const mesh = new THREE38.Mesh(geometry, material);
44057
44122
  scene.add(mesh);
44058
44123
  }
44059
44124
  }
44060
- const gridColor = new THREE37.Color(8947848);
44061
- const gridHelper = new THREE37.GridHelper(100, 100, gridColor, gridColor);
44125
+ const gridColor = new THREE38.Color(8947848);
44126
+ const gridHelper = new THREE38.GridHelper(100, 100, gridColor, gridColor);
44062
44127
  gridHelper.rotation.x = Math.PI / 2;
44063
44128
  const materials = Array.isArray(gridHelper.material) ? gridHelper.material : [gridHelper.material];
44064
44129
  for (const mat of materials) {
44065
44130
  mat.transparent = true;
44066
44131
  mat.opacity = 0.3;
44067
- if (mat instanceof THREE37.LineBasicMaterial) {
44132
+ if (mat instanceof THREE38.LineBasicMaterial) {
44068
44133
  mat.color = gridColor;
44069
44134
  mat.vertexColors = false;
44070
44135
  }
44071
44136
  }
44072
44137
  scene.add(gridHelper);
44073
- const box = new THREE37.Box3().setFromObject(scene);
44074
- const center = box.getCenter(new THREE37.Vector3());
44075
- const size5 = box.getSize(new THREE37.Vector3());
44138
+ const box = new THREE38.Box3().setFromObject(scene);
44139
+ const center = box.getCenter(new THREE38.Vector3());
44140
+ const size5 = box.getSize(new THREE38.Vector3());
44076
44141
  scene.position.sub(center);
44077
44142
  const maxDim = Math.max(size5.x, size5.y, size5.z);
44078
44143
  if (maxDim > 0) {