@tscircuit/3d-viewer 0.0.477 → 0.0.478

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 +484 -428
  3. package/package.json +2 -1
package/dist/index.d.ts CHANGED
@@ -44,6 +44,23 @@ declare function useExportGltfUrl(options?: Options): [
44
44
  error: ErrorEvent | undefined
45
45
  ];
46
46
 
47
+ interface LayerTextures {
48
+ topTrace?: THREE.CanvasTexture | null;
49
+ bottomTrace?: THREE.CanvasTexture | null;
50
+ topTraceWithMask?: THREE.CanvasTexture | null;
51
+ bottomTraceWithMask?: THREE.CanvasTexture | null;
52
+ topSilkscreen?: THREE.CanvasTexture | null;
53
+ bottomSilkscreen?: THREE.CanvasTexture | null;
54
+ topSoldermask?: THREE.CanvasTexture | null;
55
+ bottomSoldermask?: THREE.CanvasTexture | null;
56
+ topCopperText?: THREE.CanvasTexture | null;
57
+ bottomCopperText?: THREE.CanvasTexture | null;
58
+ topPanelOutlines?: THREE.CanvasTexture | null;
59
+ bottomPanelOutlines?: THREE.CanvasTexture | null;
60
+ topCopper?: THREE.CanvasTexture | null;
61
+ bottomCopper?: THREE.CanvasTexture | null;
62
+ }
63
+
47
64
  interface ManifoldGeoms {
48
65
  board?: {
49
66
  geometry: THREE.BufferGeometry;
@@ -66,29 +83,10 @@ interface ManifoldGeoms {
66
83
  geometry: THREE.BufferGeometry;
67
84
  color: THREE.Color;
68
85
  }>;
69
- copperPours?: Array<{
70
- key: string;
71
- geometry: THREE.BufferGeometry;
72
- color: THREE.Color;
73
- }>;
74
- }
75
- interface ManifoldTextures {
76
- topTrace?: THREE.CanvasTexture | null;
77
- bottomTrace?: THREE.CanvasTexture | null;
78
- topTraceWithMask?: THREE.CanvasTexture | null;
79
- bottomTraceWithMask?: THREE.CanvasTexture | null;
80
- topSilkscreen?: THREE.CanvasTexture | null;
81
- bottomSilkscreen?: THREE.CanvasTexture | null;
82
- topSoldermask?: THREE.CanvasTexture | null;
83
- bottomSoldermask?: THREE.CanvasTexture | null;
84
- topCopperText?: THREE.CanvasTexture | null;
85
- bottomCopperText?: THREE.CanvasTexture | null;
86
- topPanelOutlines?: THREE.CanvasTexture | null;
87
- bottomPanelOutlines?: THREE.CanvasTexture | null;
88
86
  }
89
87
  interface UseManifoldBoardBuilderResult {
90
88
  geoms: ManifoldGeoms | null;
91
- textures: ManifoldTextures | null;
89
+ textures: LayerTextures | null;
92
90
  pcbThickness: number | null;
93
91
  error: string | null;
94
92
  isLoading: boolean;
@@ -145,4 +143,4 @@ declare const useCameraSession: () => {
145
143
  handleControlsChange: () => void;
146
144
  };
147
145
 
148
- export { CadViewer, CameraAnimatorWithContext, CameraControllerProvider, type ManifoldGeoms, type ManifoldTextures, applyJsdomShim, convertCircuitJsonTo3dSvg, loadCameraFromSession, saveCameraToSession, useCameraController, useCameraSession, useExportGltfUrl, useManifoldBoardBuilder, useSaveGltfAs };
146
+ export { CadViewer, CameraAnimatorWithContext, CameraControllerProvider, type ManifoldGeoms, applyJsdomShim, convertCircuitJsonTo3dSvg, loadCameraFromSession, saveCameraToSession, useCameraController, useCameraSession, useExportGltfUrl, useManifoldBoardBuilder, useSaveGltfAs };
package/dist/index.js CHANGED
@@ -29492,7 +29492,7 @@ import * as THREE15 from "three";
29492
29492
  // package.json
29493
29493
  var package_default = {
29494
29494
  name: "@tscircuit/3d-viewer",
29495
- version: "0.0.476",
29495
+ version: "0.0.477",
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",
@@ -34357,7 +34358,7 @@ function JscadBoardTextures({
34357
34358
  useEffect22(() => {
34358
34359
  if (!rootObject || !boardData || !textures) return;
34359
34360
  const meshes = [];
34360
- const createTexturePlane = (texture, zOffset, isBottomLayer, name, usePolygonOffset = false, depthWrite = false) => {
34361
+ const createTexturePlane2 = (texture, zOffset, isBottomLayer, name, usePolygonOffset = false, depthWrite = false) => {
34361
34362
  if (!texture) return null;
34362
34363
  const boardOutlineBounds = calculateOutlineBounds(boardData);
34363
34364
  const planeGeom = new THREE23.PlaneGeometry(
@@ -34387,7 +34388,7 @@ function JscadBoardTextures({
34387
34388
  };
34388
34389
  const SURFACE_OFFSET = 1e-3;
34389
34390
  if (visibility.topMask) {
34390
- const topSoldermaskMesh = createTexturePlane(
34391
+ const topSoldermaskMesh = createTexturePlane2(
34391
34392
  textures.topSoldermask,
34392
34393
  pcbThickness / 2 + SURFACE_OFFSET,
34393
34394
  false,
@@ -34400,7 +34401,7 @@ function JscadBoardTextures({
34400
34401
  }
34401
34402
  }
34402
34403
  if (visibility.bottomMask) {
34403
- const bottomSoldermaskMesh = createTexturePlane(
34404
+ const bottomSoldermaskMesh = createTexturePlane2(
34404
34405
  textures.bottomSoldermask,
34405
34406
  -pcbThickness / 2 - SURFACE_OFFSET,
34406
34407
  true,
@@ -34413,7 +34414,7 @@ function JscadBoardTextures({
34413
34414
  }
34414
34415
  }
34415
34416
  if (visibility.topCopper && visibility.topMask) {
34416
- const topTraceWithMaskMesh = createTexturePlane(
34417
+ const topTraceWithMaskMesh = createTexturePlane2(
34417
34418
  textures.topTraceWithMask,
34418
34419
  pcbThickness / 2 + BOARD_SURFACE_OFFSET.traces + 4e-3,
34419
34420
  false,
@@ -34425,7 +34426,7 @@ function JscadBoardTextures({
34425
34426
  }
34426
34427
  }
34427
34428
  if (visibility.bottomCopper && visibility.bottomMask) {
34428
- const bottomTraceWithMaskMesh = createTexturePlane(
34429
+ const bottomTraceWithMaskMesh = createTexturePlane2(
34429
34430
  textures.bottomTraceWithMask,
34430
34431
  -pcbThickness / 2 - BOARD_SURFACE_OFFSET.traces - 5e-3,
34431
34432
  true,
@@ -34437,7 +34438,7 @@ function JscadBoardTextures({
34437
34438
  }
34438
34439
  }
34439
34440
  if (visibility.topSilkscreen) {
34440
- const topSilkscreenMesh = createTexturePlane(
34441
+ const topSilkscreenMesh = createTexturePlane2(
34441
34442
  textures.topSilkscreen,
34442
34443
  pcbThickness / 2 + SURFACE_OFFSET + 2e-3,
34443
34444
  false,
@@ -34449,7 +34450,7 @@ function JscadBoardTextures({
34449
34450
  }
34450
34451
  }
34451
34452
  if (visibility.bottomSilkscreen) {
34452
- const bottomSilkscreenMesh = createTexturePlane(
34453
+ const bottomSilkscreenMesh = createTexturePlane2(
34453
34454
  textures.bottomSilkscreen,
34454
34455
  -pcbThickness / 2 - SURFACE_OFFSET - 2e-3,
34455
34456
  true,
@@ -34461,7 +34462,7 @@ function JscadBoardTextures({
34461
34462
  }
34462
34463
  }
34463
34464
  if (visibility.topCopper) {
34464
- const topCopperTextMesh = createTexturePlane(
34465
+ const topCopperTextMesh = createTexturePlane2(
34465
34466
  textures.topCopperText,
34466
34467
  pcbThickness / 2 + BOARD_SURFACE_OFFSET.copper,
34467
34468
  false,
@@ -34473,7 +34474,7 @@ function JscadBoardTextures({
34473
34474
  }
34474
34475
  }
34475
34476
  if (visibility.bottomCopper) {
34476
- const bottomCopperTextMesh = createTexturePlane(
34477
+ const bottomCopperTextMesh = createTexturePlane2(
34477
34478
  textures.bottomCopperText,
34478
34479
  -pcbThickness / 2 - BOARD_SURFACE_OFFSET.copper,
34479
34480
  true,
@@ -34485,7 +34486,7 @@ function JscadBoardTextures({
34485
34486
  }
34486
34487
  }
34487
34488
  if (visibility.boardBody) {
34488
- const topPanelOutlinesMesh = createTexturePlane(
34489
+ const topPanelOutlinesMesh = createTexturePlane2(
34489
34490
  textures.topPanelOutlines,
34490
34491
  pcbThickness / 2 + SURFACE_OFFSET + 3e-3,
34491
34492
  // Above silkscreen
@@ -34498,7 +34499,7 @@ function JscadBoardTextures({
34498
34499
  meshes.push(topPanelOutlinesMesh);
34499
34500
  rootObject.add(topPanelOutlinesMesh);
34500
34501
  }
34501
- const bottomPanelOutlinesMesh = createTexturePlane(
34502
+ const bottomPanelOutlinesMesh = createTexturePlane2(
34502
34503
  textures.bottomPanelOutlines,
34503
34504
  -pcbThickness / 2 - SURFACE_OFFSET - 3e-3,
34504
34505
  // Below bottom silkscreen
@@ -34750,7 +34751,7 @@ import { useEffect as useEffect24, useMemo as useMemo22, useState as useState15
34750
34751
  // src/hooks/useManifoldBoardBuilder.ts
34751
34752
  import { useState as useState14, useEffect as useEffect23, useMemo as useMemo21, useRef as useRef9 } from "react";
34752
34753
  import { su as su18 } from "@tscircuit/circuit-json-util";
34753
- import * as THREE29 from "three";
34754
+ import * as THREE30 from "three";
34754
34755
 
34755
34756
  // src/utils/manifold/create-manifold-board.ts
34756
34757
  var arePointsClockwise3 = (points) => {
@@ -34810,202 +34811,6 @@ function createManifoldBoard(Manifold, CrossSection, boardData, pcbThickness, ma
34810
34811
  return { boardOp, outlineCrossSection };
34811
34812
  }
34812
34813
 
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
34814
  // src/utils/manifold/process-cutouts.ts
35010
34815
  import { su as su13 } from "@tscircuit/circuit-json-util";
35011
34816
 
@@ -35085,7 +34890,7 @@ function createPadManifoldOp({
35085
34890
  }
35086
34891
 
35087
34892
  // src/utils/manifold/process-cutouts.ts
35088
- var arePointsClockwise5 = (points) => {
34893
+ var arePointsClockwise4 = (points) => {
35089
34894
  let area = 0;
35090
34895
  for (let i = 0; i < points.length; i++) {
35091
34896
  const j = (i + 1) % points.length;
@@ -35161,7 +34966,7 @@ function processCutoutsForManifold(Manifold, CrossSection, circuitJson, pcbThick
35161
34966
  p.x,
35162
34967
  p.y
35163
34968
  ]);
35164
- if (arePointsClockwise5(pointsVec2)) {
34969
+ if (arePointsClockwise4(pointsVec2)) {
35165
34970
  pointsVec2 = pointsVec2.reverse();
35166
34971
  }
35167
34972
  const crossSection = CrossSection.ofPolygons([pointsVec2]);
@@ -35296,8 +35101,31 @@ function processNonPlatedHolesForManifold(Manifold, circuitJson, pcbThickness, m
35296
35101
 
35297
35102
  // src/utils/manifold/process-plated-holes.ts
35298
35103
  import { su as su15 } from "@tscircuit/circuit-json-util";
35299
- import * as THREE26 from "three";
35300
- var arePointsClockwise6 = (points) => {
35104
+ import * as THREE25 from "three";
35105
+
35106
+ // src/utils/manifold-mesh-to-three-geometry.ts
35107
+ import * as THREE24 from "three";
35108
+ function manifoldMeshToThreeGeometry(manifoldMesh) {
35109
+ const geometry = new THREE24.BufferGeometry();
35110
+ geometry.setAttribute(
35111
+ "position",
35112
+ new THREE24.Float32BufferAttribute(manifoldMesh.vertProperties, 3)
35113
+ );
35114
+ geometry.setIndex(new THREE24.Uint32BufferAttribute(manifoldMesh.triVerts, 1));
35115
+ if (manifoldMesh.runIndex && manifoldMesh.runIndex.length > 1 && manifoldMesh.runOriginalID) {
35116
+ for (let i = 0; i < manifoldMesh.runIndex.length - 1; i++) {
35117
+ const start = manifoldMesh.runIndex[i];
35118
+ const count3 = manifoldMesh.runIndex[i + 1] - start;
35119
+ geometry.addGroup(start, count3, 0);
35120
+ }
35121
+ } else {
35122
+ geometry.addGroup(0, manifoldMesh.triVerts.length, 0);
35123
+ }
35124
+ return geometry;
35125
+ }
35126
+
35127
+ // src/utils/manifold/process-plated-holes.ts
35128
+ var arePointsClockwise5 = (points) => {
35301
35129
  let area = 0;
35302
35130
  for (let i = 0; i < points.length; i++) {
35303
35131
  const j = (i + 1) % points.length;
@@ -35317,7 +35145,7 @@ var createEllipsePoints = (width10, height10, segments) => {
35317
35145
  }
35318
35146
  return points;
35319
35147
  };
35320
- var COPPER_COLOR = new THREE26.Color(...colors.copper);
35148
+ var COPPER_COLOR = new THREE25.Color(...colors.copper);
35321
35149
  var PLATED_HOLE_LIP_HEIGHT = 0.05;
35322
35150
  function processPlatedHolesForManifold(Manifold, CrossSection, circuitJson, pcbThickness, manifoldInstancesForCleanup, boardClipVolume) {
35323
35151
  const platedHoleBoardDrills = [];
@@ -35344,7 +35172,7 @@ function processPlatedHolesForManifold(Manifold, CrossSection, circuitJson, pcbT
35344
35172
  point2.x,
35345
35173
  point2.y
35346
35174
  ]);
35347
- if (arePointsClockwise6(points)) {
35175
+ if (arePointsClockwise5(points)) {
35348
35176
  points = points.reverse();
35349
35177
  }
35350
35178
  const crossSection = CrossSection.ofPolygons([points]);
@@ -35389,7 +35217,7 @@ function processPlatedHolesForManifold(Manifold, CrossSection, circuitJson, pcbT
35389
35217
  const height10 = Math.max(baseHeight + sizeDelta, M);
35390
35218
  if (holeShape === "oval") {
35391
35219
  let points = createEllipsePoints(width10, height10, SMOOTH_CIRCLE_SEGMENTS);
35392
- if (arePointsClockwise6(points)) {
35220
+ if (arePointsClockwise5(points)) {
35393
35221
  points = points.reverse();
35394
35222
  }
35395
35223
  const crossSection = CrossSection.ofPolygons([points]);
@@ -35704,7 +35532,7 @@ function processPlatedHolesForManifold(Manifold, CrossSection, circuitJson, pcbT
35704
35532
  drillH,
35705
35533
  SMOOTH_CIRCLE_SEGMENTS
35706
35534
  );
35707
- if (arePointsClockwise6(boardDrillPoints)) {
35535
+ if (arePointsClockwise5(boardDrillPoints)) {
35708
35536
  boardDrillPoints = boardDrillPoints.reverse();
35709
35537
  }
35710
35538
  const boardDrillCrossSection = CrossSection.ofPolygons([boardDrillPoints]);
@@ -35732,7 +35560,7 @@ function processPlatedHolesForManifold(Manifold, CrossSection, circuitJson, pcbT
35732
35560
  outerH,
35733
35561
  SMOOTH_CIRCLE_SEGMENTS
35734
35562
  );
35735
- if (arePointsClockwise6(outerPoints)) {
35563
+ if (arePointsClockwise5(outerPoints)) {
35736
35564
  outerPoints = outerPoints.reverse();
35737
35565
  }
35738
35566
  const outerCrossSection = CrossSection.ofPolygons([outerPoints]);
@@ -35751,7 +35579,7 @@ function processPlatedHolesForManifold(Manifold, CrossSection, circuitJson, pcbT
35751
35579
  holeH,
35752
35580
  SMOOTH_CIRCLE_SEGMENTS
35753
35581
  );
35754
- if (arePointsClockwise6(innerPoints)) {
35582
+ if (arePointsClockwise5(innerPoints)) {
35755
35583
  innerPoints = innerPoints.reverse();
35756
35584
  }
35757
35585
  const innerCrossSection = CrossSection.ofPolygons([innerPoints]);
@@ -35891,8 +35719,8 @@ function processPlatedHolesForManifold(Manifold, CrossSection, circuitJson, pcbT
35891
35719
 
35892
35720
  // src/utils/manifold/process-smt-pads.ts
35893
35721
  import { su as su16 } from "@tscircuit/circuit-json-util";
35894
- import * as THREE27 from "three";
35895
- var COPPER_COLOR2 = new THREE27.Color(...colors.copper);
35722
+ import * as THREE26 from "three";
35723
+ var COPPER_COLOR2 = new THREE26.Color(...colors.copper);
35896
35724
  function processSmtPadsForManifold(Manifold, circuitJson, pcbThickness, manifoldInstancesForCleanup, holeUnion, boardClipVolume) {
35897
35725
  const smtPadGeoms = [];
35898
35726
  const smtPads = su16(circuitJson).pcb_smtpad.list();
@@ -35931,7 +35759,7 @@ function processSmtPadsForManifold(Manifold, circuitJson, pcbThickness, manifold
35931
35759
 
35932
35760
  // src/utils/manifold/process-vias.ts
35933
35761
  import { su as su17 } from "@tscircuit/circuit-json-util";
35934
- import * as THREE28 from "three";
35762
+ import * as THREE27 from "three";
35935
35763
 
35936
35764
  // src/utils/via-geoms.ts
35937
35765
  function createViaCopper2({
@@ -35984,7 +35812,7 @@ function createViaCopper2({
35984
35812
  }
35985
35813
 
35986
35814
  // src/utils/manifold/process-vias.ts
35987
- var COPPER_COLOR3 = new THREE28.Color(...colors.copper);
35815
+ var COPPER_COLOR3 = new THREE27.Color(...colors.copper);
35988
35816
  function processViasForManifold(Manifold, circuitJson, pcbThickness, manifoldInstancesForCleanup, boardClipVolume) {
35989
35817
  const viaBoardDrills = [];
35990
35818
  const pcbVias = su17(circuitJson).pcb_via.list();
@@ -36035,6 +35863,397 @@ function processViasForManifold(Manifold, circuitJson, pcbThickness, manifoldIns
36035
35863
  return { viaBoardDrills, viaCopperGeoms };
36036
35864
  }
36037
35865
 
35866
+ // src/textures/create-copper-pour-texture-for-layer.ts
35867
+ import * as THREE28 from "three";
35868
+ import { CircuitToCanvasDrawer } from "circuit-to-canvas";
35869
+ function drawPolygon({
35870
+ ctx,
35871
+ points,
35872
+ canvasXFromPcb,
35873
+ canvasYFromPcb
35874
+ }) {
35875
+ if (points.length < 3) return;
35876
+ ctx.beginPath();
35877
+ points.forEach((point2, index2) => {
35878
+ const canvasX = canvasXFromPcb(point2[0]);
35879
+ const canvasY = canvasYFromPcb(point2[1]);
35880
+ if (index2 === 0) {
35881
+ ctx.moveTo(canvasX, canvasY);
35882
+ } else {
35883
+ ctx.lineTo(canvasX, canvasY);
35884
+ }
35885
+ });
35886
+ ctx.closePath();
35887
+ ctx.fill();
35888
+ }
35889
+ function drawBrepShape({
35890
+ ctx,
35891
+ pour,
35892
+ canvasXFromPcb,
35893
+ canvasYFromPcb
35894
+ }) {
35895
+ const brepShape = pour.brep_shape;
35896
+ if (!brepShape || !brepShape.outer_ring) return;
35897
+ const outerRingPoints = ringToPoints(brepShape.outer_ring, 32);
35898
+ if (outerRingPoints.length >= 3) {
35899
+ drawPolygon({
35900
+ ctx,
35901
+ points: outerRingPoints,
35902
+ canvasXFromPcb,
35903
+ canvasYFromPcb
35904
+ });
35905
+ }
35906
+ if (brepShape.inner_rings && brepShape.inner_rings.length > 0) {
35907
+ ctx.globalCompositeOperation = "destination-out";
35908
+ for (const innerRing of brepShape.inner_rings) {
35909
+ const innerRingPoints = ringToPoints(innerRing, 32);
35910
+ if (innerRingPoints.length >= 3) {
35911
+ drawPolygon({
35912
+ ctx,
35913
+ points: innerRingPoints,
35914
+ canvasXFromPcb,
35915
+ canvasYFromPcb
35916
+ });
35917
+ }
35918
+ }
35919
+ ctx.globalCompositeOperation = "source-over";
35920
+ }
35921
+ }
35922
+ function createCopperPourTextureForLayer({
35923
+ layer,
35924
+ circuitJson,
35925
+ boardData,
35926
+ traceTextureResolution = TRACE_TEXTURE_RESOLUTION
35927
+ }) {
35928
+ const copperPours = circuitJson.filter(
35929
+ (e) => e.type === "pcb_copper_pour"
35930
+ );
35931
+ const poursOnLayer = copperPours.filter((p) => p.layer === layer);
35932
+ if (poursOnLayer.length === 0) return null;
35933
+ const boardOutlineBounds = calculateOutlineBounds(boardData);
35934
+ const canvas = document.createElement("canvas");
35935
+ const canvasWidth = Math.floor(
35936
+ boardOutlineBounds.width * traceTextureResolution
35937
+ );
35938
+ const canvasHeight = Math.floor(
35939
+ boardOutlineBounds.height * traceTextureResolution
35940
+ );
35941
+ canvas.width = canvasWidth;
35942
+ canvas.height = canvasHeight;
35943
+ const ctx = canvas.getContext("2d");
35944
+ if (!ctx) return null;
35945
+ if (layer === "bottom") {
35946
+ ctx.translate(0, canvasHeight);
35947
+ ctx.scale(1, -1);
35948
+ }
35949
+ const canvasXFromPcb = (pcbX) => (pcbX - boardOutlineBounds.minX) * traceTextureResolution;
35950
+ const canvasYFromPcb = (pcbY) => (boardOutlineBounds.maxY - pcbY) * traceTextureResolution;
35951
+ const rectAndPolygonPours = poursOnLayer.filter(
35952
+ (pour) => pour.shape === "rect" || pour.shape === "polygon"
35953
+ );
35954
+ const brepPours = poursOnLayer.filter((pour) => pour.shape === "brep");
35955
+ if (rectAndPolygonPours.length > 0) {
35956
+ const drawer = new CircuitToCanvasDrawer(ctx);
35957
+ drawer.setCameraBounds({
35958
+ minX: boardOutlineBounds.minX,
35959
+ maxX: boardOutlineBounds.maxX,
35960
+ minY: boardOutlineBounds.minY,
35961
+ maxY: boardOutlineBounds.maxY
35962
+ });
35963
+ const coveredPours = rectAndPolygonPours.filter(
35964
+ (p) => p.covered_with_solder_mask !== false
35965
+ );
35966
+ const uncoveredPours = rectAndPolygonPours.filter(
35967
+ (p) => p.covered_with_solder_mask === false
35968
+ );
35969
+ const coveredColor = `rgb(${colors.fr4TracesWithMaskGreen.map((c) => c * 255).join(",")})`;
35970
+ const uncoveredColor = `rgb(${colors.copper.map((c) => c * 255).join(",")})`;
35971
+ if (coveredPours.length > 0) {
35972
+ drawer.configure({
35973
+ colorOverrides: {
35974
+ copper: {
35975
+ top: coveredColor,
35976
+ bottom: coveredColor,
35977
+ inner1: coveredColor,
35978
+ inner2: coveredColor,
35979
+ inner3: coveredColor,
35980
+ inner4: coveredColor,
35981
+ inner5: coveredColor,
35982
+ inner6: coveredColor
35983
+ }
35984
+ }
35985
+ });
35986
+ drawer.drawElements(coveredPours, { layers: [layer] });
35987
+ }
35988
+ if (uncoveredPours.length > 0) {
35989
+ drawer.configure({
35990
+ colorOverrides: {
35991
+ copper: {
35992
+ top: uncoveredColor,
35993
+ bottom: uncoveredColor,
35994
+ inner1: uncoveredColor,
35995
+ inner2: uncoveredColor,
35996
+ inner3: uncoveredColor,
35997
+ inner4: uncoveredColor,
35998
+ inner5: uncoveredColor,
35999
+ inner6: uncoveredColor
36000
+ }
36001
+ }
36002
+ });
36003
+ drawer.drawElements(uncoveredPours, { layers: [layer] });
36004
+ }
36005
+ }
36006
+ for (const pour of brepPours) {
36007
+ const covered = pour.covered_with_solder_mask !== false;
36008
+ const colorArr = covered ? colors.fr4TracesWithMaskGreen : colors.copper;
36009
+ const copperColor = `rgb(${colorArr[0] * 255}, ${colorArr[1] * 255}, ${colorArr[2] * 255})`;
36010
+ ctx.fillStyle = copperColor;
36011
+ drawBrepShape({ ctx, pour, canvasXFromPcb, canvasYFromPcb });
36012
+ }
36013
+ const texture = new THREE28.CanvasTexture(canvas);
36014
+ texture.generateMipmaps = true;
36015
+ texture.minFilter = THREE28.LinearMipmapLinearFilter;
36016
+ texture.magFilter = THREE28.LinearFilter;
36017
+ texture.anisotropy = 16;
36018
+ texture.needsUpdate = true;
36019
+ return texture;
36020
+ }
36021
+
36022
+ // src/textures/create-three-texture-meshes.ts
36023
+ import * as THREE29 from "three";
36024
+ function createTexturePlane(config, boardData) {
36025
+ const {
36026
+ texture,
36027
+ yOffset,
36028
+ isBottomLayer,
36029
+ textureType,
36030
+ usePolygonOffset = false,
36031
+ renderOrder = 0
36032
+ } = config;
36033
+ if (!texture) return null;
36034
+ const boardOutlineBounds = calculateOutlineBounds(boardData);
36035
+ const planeGeom = new THREE29.PlaneGeometry(
36036
+ boardOutlineBounds.width,
36037
+ boardOutlineBounds.height
36038
+ );
36039
+ const material = new THREE29.MeshBasicMaterial({
36040
+ map: texture,
36041
+ transparent: true,
36042
+ side: THREE29.DoubleSide,
36043
+ depthWrite: textureType === "panel-outlines",
36044
+ polygonOffset: usePolygonOffset,
36045
+ polygonOffsetFactor: usePolygonOffset ? -4 : 0,
36046
+ // Increased for better z-fighting prevention
36047
+ polygonOffsetUnits: usePolygonOffset ? -4 : 0
36048
+ });
36049
+ const mesh = new THREE29.Mesh(planeGeom, material);
36050
+ mesh.position.set(
36051
+ boardOutlineBounds.centerX,
36052
+ boardOutlineBounds.centerY,
36053
+ yOffset
36054
+ );
36055
+ if (isBottomLayer) {
36056
+ mesh.rotation.set(Math.PI, 0, 0);
36057
+ }
36058
+ mesh.name = `${isBottomLayer ? "bottom" : "top"}-${textureType}-texture-plane`;
36059
+ mesh.renderOrder = renderOrder;
36060
+ return mesh;
36061
+ }
36062
+ function createTextureMeshes(textures, boardData, pcbThickness) {
36063
+ const meshes = [];
36064
+ if (!textures || !boardData || pcbThickness === null) return meshes;
36065
+ const topTraceMesh = createTexturePlane(
36066
+ {
36067
+ texture: textures.topTrace,
36068
+ yOffset: pcbThickness / 2 + BOARD_SURFACE_OFFSET.traces,
36069
+ // Use consistent copper offset
36070
+ isBottomLayer: false,
36071
+ textureType: "trace",
36072
+ usePolygonOffset: false,
36073
+ renderOrder: 2
36074
+ // Render after soldermask
36075
+ },
36076
+ boardData
36077
+ );
36078
+ if (topTraceMesh) meshes.push(topTraceMesh);
36079
+ const topTraceWithMaskMesh = createTexturePlane(
36080
+ {
36081
+ texture: textures.topTraceWithMask,
36082
+ yOffset: pcbThickness / 2 + BOARD_SURFACE_OFFSET.traces,
36083
+ isBottomLayer: false,
36084
+ textureType: "trace-with-mask",
36085
+ usePolygonOffset: false,
36086
+ renderOrder: 2
36087
+ // Render after soldermask
36088
+ },
36089
+ boardData
36090
+ );
36091
+ if (topTraceWithMaskMesh) meshes.push(topTraceWithMaskMesh);
36092
+ const topSilkscreenMesh = createTexturePlane(
36093
+ {
36094
+ texture: textures.topSilkscreen,
36095
+ yOffset: pcbThickness / 2 + 3e-3,
36096
+ // Slightly above soldermask
36097
+ isBottomLayer: false,
36098
+ textureType: "silkscreen",
36099
+ usePolygonOffset: false,
36100
+ renderOrder: 3
36101
+ // Render after traces
36102
+ },
36103
+ boardData
36104
+ );
36105
+ if (topSilkscreenMesh) meshes.push(topSilkscreenMesh);
36106
+ const bottomTraceMesh = createTexturePlane(
36107
+ {
36108
+ texture: textures.bottomTrace,
36109
+ yOffset: -pcbThickness / 2 - BOARD_SURFACE_OFFSET.traces,
36110
+ // Use consistent copper offset
36111
+ isBottomLayer: true,
36112
+ textureType: "trace",
36113
+ usePolygonOffset: false,
36114
+ renderOrder: 2
36115
+ // Render after soldermask
36116
+ },
36117
+ boardData
36118
+ );
36119
+ if (bottomTraceMesh) meshes.push(bottomTraceMesh);
36120
+ const bottomTraceWithMaskMesh = createTexturePlane(
36121
+ {
36122
+ texture: textures.bottomTraceWithMask,
36123
+ yOffset: -pcbThickness / 2 - BOARD_SURFACE_OFFSET.traces,
36124
+ isBottomLayer: true,
36125
+ textureType: "trace-with-mask",
36126
+ usePolygonOffset: false,
36127
+ renderOrder: 2
36128
+ // Render after soldermask
36129
+ },
36130
+ boardData
36131
+ );
36132
+ if (bottomTraceWithMaskMesh) meshes.push(bottomTraceWithMaskMesh);
36133
+ const bottomSilkscreenMesh = createTexturePlane(
36134
+ {
36135
+ texture: textures.bottomSilkscreen,
36136
+ yOffset: -pcbThickness / 2 - 3e-3,
36137
+ isBottomLayer: true,
36138
+ textureType: "silkscreen",
36139
+ usePolygonOffset: false,
36140
+ renderOrder: 3
36141
+ // Render after traces
36142
+ },
36143
+ boardData
36144
+ );
36145
+ if (bottomSilkscreenMesh) meshes.push(bottomSilkscreenMesh);
36146
+ const topSoldermaskMesh = createTexturePlane(
36147
+ {
36148
+ texture: textures.topSoldermask,
36149
+ yOffset: pcbThickness / 2 + 1e-3,
36150
+ // Just above board surface
36151
+ isBottomLayer: false,
36152
+ textureType: "soldermask",
36153
+ usePolygonOffset: true,
36154
+ // Enable polygon offset
36155
+ renderOrder: 1
36156
+ // Render after board (renderOrder)
36157
+ },
36158
+ boardData
36159
+ );
36160
+ if (topSoldermaskMesh) meshes.push(topSoldermaskMesh);
36161
+ const bottomSoldermaskMesh = createTexturePlane(
36162
+ {
36163
+ texture: textures.bottomSoldermask,
36164
+ yOffset: -pcbThickness / 2 - 1e-3,
36165
+ // Just below board surface (bottom side)
36166
+ isBottomLayer: true,
36167
+ textureType: "soldermask",
36168
+ usePolygonOffset: true,
36169
+ // Enable polygon offset
36170
+ renderOrder: 1
36171
+ // Render after board (renderOrder)
36172
+ },
36173
+ boardData
36174
+ );
36175
+ if (bottomSoldermaskMesh) meshes.push(bottomSoldermaskMesh);
36176
+ const topCopperTextMesh = createTexturePlane(
36177
+ {
36178
+ texture: textures.topCopperText,
36179
+ yOffset: pcbThickness / 2 + BOARD_SURFACE_OFFSET.copper,
36180
+ isBottomLayer: false,
36181
+ textureType: "copper-text",
36182
+ usePolygonOffset: false,
36183
+ renderOrder: 2
36184
+ // Render after soldermask
36185
+ },
36186
+ boardData
36187
+ );
36188
+ if (topCopperTextMesh) meshes.push(topCopperTextMesh);
36189
+ const bottomCopperTextMesh = createTexturePlane(
36190
+ {
36191
+ texture: textures.bottomCopperText,
36192
+ yOffset: -pcbThickness / 2 - BOARD_SURFACE_OFFSET.copper,
36193
+ isBottomLayer: true,
36194
+ textureType: "copper-text",
36195
+ usePolygonOffset: false,
36196
+ renderOrder: 2
36197
+ // Render after soldermask
36198
+ },
36199
+ boardData
36200
+ );
36201
+ if (bottomCopperTextMesh) meshes.push(bottomCopperTextMesh);
36202
+ const topCopperMesh = createTexturePlane(
36203
+ {
36204
+ texture: textures.topCopper,
36205
+ yOffset: pcbThickness / 2 + BOARD_SURFACE_OFFSET.copper,
36206
+ isBottomLayer: false,
36207
+ textureType: "copper",
36208
+ usePolygonOffset: false,
36209
+ renderOrder: 2
36210
+ // Render after soldermask
36211
+ },
36212
+ boardData
36213
+ );
36214
+ if (topCopperMesh) meshes.push(topCopperMesh);
36215
+ const bottomCopperMesh = createTexturePlane(
36216
+ {
36217
+ texture: textures.bottomCopper,
36218
+ yOffset: -pcbThickness / 2 - BOARD_SURFACE_OFFSET.copper,
36219
+ isBottomLayer: true,
36220
+ textureType: "copper",
36221
+ usePolygonOffset: false,
36222
+ renderOrder: 2
36223
+ // Render after soldermask
36224
+ },
36225
+ boardData
36226
+ );
36227
+ if (bottomCopperMesh) meshes.push(bottomCopperMesh);
36228
+ const topPanelOutlinesMesh = createTexturePlane(
36229
+ {
36230
+ texture: textures.topPanelOutlines,
36231
+ yOffset: pcbThickness / 2 + 4e-3,
36232
+ // Above silkscreen
36233
+ isBottomLayer: false,
36234
+ textureType: "panel-outlines",
36235
+ usePolygonOffset: false,
36236
+ renderOrder: 4
36237
+ },
36238
+ boardData
36239
+ );
36240
+ if (topPanelOutlinesMesh) meshes.push(topPanelOutlinesMesh);
36241
+ const bottomPanelOutlinesMesh = createTexturePlane(
36242
+ {
36243
+ texture: textures.bottomPanelOutlines,
36244
+ yOffset: -pcbThickness / 2 - 4e-3,
36245
+ // Below bottom silkscreen
36246
+ isBottomLayer: true,
36247
+ textureType: "panel-outlines",
36248
+ usePolygonOffset: false,
36249
+ renderOrder: 4
36250
+ },
36251
+ boardData
36252
+ );
36253
+ if (bottomPanelOutlinesMesh) meshes.push(bottomPanelOutlinesMesh);
36254
+ return meshes;
36255
+ }
36256
+
36038
36257
  // src/hooks/useManifoldBoardBuilder.ts
36039
36258
  var useManifoldBoardBuilder = (manifoldJSModule, circuitJson) => {
36040
36259
  const [geoms, setGeoms] = useState14(null);
@@ -36104,7 +36323,7 @@ var useManifoldBoardBuilder = (manifoldJSModule, circuitJson) => {
36104
36323
  manifoldInstancesForCleanup.current = [];
36105
36324
  let boardManifold = null;
36106
36325
  const currentGeoms = {};
36107
- const currentTextures = {};
36326
+ const layerTextureMap = {};
36108
36327
  try {
36109
36328
  const currentPcbThickness = boardData.thickness || 1.6;
36110
36329
  setPcbThickness(currentPcbThickness);
@@ -36203,7 +36422,7 @@ var useManifoldBoardBuilder = (manifoldJSModule, circuitJson) => {
36203
36422
  {
36204
36423
  key: "plated-holes-union",
36205
36424
  geometry: cutPlatedGeom,
36206
- color: new THREE29.Color(
36425
+ color: new THREE30.Color(
36207
36426
  colors.copper[0],
36208
36427
  colors.copper[1],
36209
36428
  colors.copper[2]
@@ -36233,7 +36452,7 @@ var useManifoldBoardBuilder = (manifoldJSModule, circuitJson) => {
36233
36452
  const matColorArray = boardMaterialColors[boardData.material] ?? colors.fr4Tan;
36234
36453
  currentGeoms.board = {
36235
36454
  geometry: finalBoardGeom,
36236
- color: new THREE29.Color(
36455
+ color: new THREE30.Color(
36237
36456
  matColorArray[0],
36238
36457
  matColorArray[1],
36239
36458
  matColorArray[2]
@@ -36251,28 +36470,17 @@ var useManifoldBoardBuilder = (manifoldJSModule, circuitJson) => {
36251
36470
  boardClipVolume
36252
36471
  );
36253
36472
  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
36473
  setGeoms(currentGeoms);
36266
36474
  const traceColorWithoutMaskArr = colors.fr4TracesWithoutMaskTan;
36267
36475
  const traceColorWithoutMask = `rgb(${Math.round(traceColorWithoutMaskArr[0] * 255)}, ${Math.round(traceColorWithoutMaskArr[1] * 255)}, ${Math.round(traceColorWithoutMaskArr[2] * 255)})`;
36268
- currentTextures.topTrace = createTraceTextureForLayer({
36476
+ layerTextureMap.topTrace = createTraceTextureForLayer({
36269
36477
  layer: "top",
36270
36478
  circuitJson,
36271
36479
  boardData,
36272
36480
  traceColor: traceColorWithoutMask,
36273
36481
  traceTextureResolution: TRACE_TEXTURE_RESOLUTION
36274
36482
  });
36275
- currentTextures.bottomTrace = createTraceTextureForLayer({
36483
+ layerTextureMap.bottomTrace = createTraceTextureForLayer({
36276
36484
  layer: "bottom",
36277
36485
  circuitJson,
36278
36486
  boardData,
@@ -36281,14 +36489,14 @@ var useManifoldBoardBuilder = (manifoldJSModule, circuitJson) => {
36281
36489
  });
36282
36490
  const traceColorWithMaskArr = colors.fr4TracesWithMaskGreen;
36283
36491
  const traceColorWithMask = `rgb(${Math.round(traceColorWithMaskArr[0] * 255)}, ${Math.round(traceColorWithMaskArr[1] * 255)}, ${Math.round(traceColorWithMaskArr[2] * 255)})`;
36284
- currentTextures.topTraceWithMask = createTraceTextureForLayer({
36492
+ layerTextureMap.topTraceWithMask = createTraceTextureForLayer({
36285
36493
  layer: "top",
36286
36494
  circuitJson,
36287
36495
  boardData,
36288
36496
  traceColor: traceColorWithMask,
36289
36497
  traceTextureResolution: TRACE_TEXTURE_RESOLUTION
36290
36498
  });
36291
- currentTextures.bottomTraceWithMask = createTraceTextureForLayer({
36499
+ layerTextureMap.bottomTraceWithMask = createTraceTextureForLayer({
36292
36500
  layer: "bottom",
36293
36501
  circuitJson,
36294
36502
  boardData,
@@ -36296,14 +36504,14 @@ var useManifoldBoardBuilder = (manifoldJSModule, circuitJson) => {
36296
36504
  traceTextureResolution: TRACE_TEXTURE_RESOLUTION
36297
36505
  });
36298
36506
  const silkscreenColor = "rgb(255,255,255)";
36299
- currentTextures.topSilkscreen = createSilkscreenTextureForLayer({
36507
+ layerTextureMap.topSilkscreen = createSilkscreenTextureForLayer({
36300
36508
  layer: "top",
36301
36509
  circuitJson,
36302
36510
  boardData,
36303
36511
  silkscreenColor,
36304
36512
  traceTextureResolution: TRACE_TEXTURE_RESOLUTION
36305
36513
  });
36306
- currentTextures.bottomSilkscreen = createSilkscreenTextureForLayer({
36514
+ layerTextureMap.bottomSilkscreen = createSilkscreenTextureForLayer({
36307
36515
  layer: "bottom",
36308
36516
  circuitJson,
36309
36517
  boardData,
@@ -36312,14 +36520,14 @@ var useManifoldBoardBuilder = (manifoldJSModule, circuitJson) => {
36312
36520
  });
36313
36521
  const soldermaskColorArr = soldermaskColors[boardData.material] ?? colors.fr4SolderMaskGreen;
36314
36522
  const soldermaskColor = `rgb(${Math.round(soldermaskColorArr[0] * 255)}, ${Math.round(soldermaskColorArr[1] * 255)}, ${Math.round(soldermaskColorArr[2] * 255)})`;
36315
- currentTextures.topSoldermask = createSoldermaskTextureForLayer({
36523
+ layerTextureMap.topSoldermask = createSoldermaskTextureForLayer({
36316
36524
  layer: "top",
36317
36525
  circuitJson,
36318
36526
  boardData,
36319
36527
  soldermaskColor,
36320
36528
  traceTextureResolution: TRACE_TEXTURE_RESOLUTION
36321
36529
  });
36322
- currentTextures.bottomSoldermask = createSoldermaskTextureForLayer({
36530
+ layerTextureMap.bottomSoldermask = createSoldermaskTextureForLayer({
36323
36531
  layer: "bottom",
36324
36532
  circuitJson,
36325
36533
  boardData,
@@ -36328,33 +36536,45 @@ var useManifoldBoardBuilder = (manifoldJSModule, circuitJson) => {
36328
36536
  });
36329
36537
  const copperColorArr = colors.copper;
36330
36538
  const copperColor = `rgb(${Math.round(copperColorArr[0] * 255)}, ${Math.round(copperColorArr[1] * 255)}, ${Math.round(copperColorArr[2] * 255)})`;
36331
- currentTextures.topCopperText = createCopperTextTextureForLayer({
36539
+ layerTextureMap.topCopperText = createCopperTextTextureForLayer({
36332
36540
  layer: "top",
36333
36541
  circuitJson,
36334
36542
  boardData,
36335
36543
  copperColor,
36336
36544
  traceTextureResolution: TRACE_TEXTURE_RESOLUTION
36337
36545
  });
36338
- currentTextures.bottomCopperText = createCopperTextTextureForLayer({
36546
+ layerTextureMap.bottomCopperText = createCopperTextTextureForLayer({
36339
36547
  layer: "bottom",
36340
36548
  circuitJson,
36341
36549
  boardData,
36342
36550
  copperColor,
36343
36551
  traceTextureResolution: TRACE_TEXTURE_RESOLUTION
36344
36552
  });
36345
- currentTextures.topPanelOutlines = createPanelOutlineTextureForLayer({
36553
+ layerTextureMap.topPanelOutlines = createPanelOutlineTextureForLayer({
36346
36554
  layer: "top",
36347
36555
  circuitJson,
36348
36556
  panelData: boardData,
36349
36557
  traceTextureResolution: TRACE_TEXTURE_RESOLUTION
36350
36558
  });
36351
- currentTextures.bottomPanelOutlines = createPanelOutlineTextureForLayer({
36559
+ layerTextureMap.bottomPanelOutlines = createPanelOutlineTextureForLayer({
36352
36560
  layer: "bottom",
36353
36561
  circuitJson,
36354
36562
  panelData: boardData,
36355
36563
  traceTextureResolution: TRACE_TEXTURE_RESOLUTION
36356
36564
  });
36357
- setTextures(currentTextures);
36565
+ layerTextureMap.topCopper = createCopperPourTextureForLayer({
36566
+ layer: "top",
36567
+ circuitJson,
36568
+ boardData,
36569
+ traceTextureResolution: TRACE_TEXTURE_RESOLUTION
36570
+ });
36571
+ layerTextureMap.bottomCopper = createCopperPourTextureForLayer({
36572
+ layer: "bottom",
36573
+ circuitJson,
36574
+ boardData,
36575
+ traceTextureResolution: TRACE_TEXTURE_RESOLUTION
36576
+ });
36577
+ setTextures(layerTextureMap);
36358
36578
  } catch (e) {
36359
36579
  console.error("Error processing geometry with Manifold in hook:", e);
36360
36580
  setError(
@@ -36382,11 +36602,11 @@ var useManifoldBoardBuilder = (manifoldJSModule, circuitJson) => {
36382
36602
  };
36383
36603
 
36384
36604
  // src/utils/manifold/create-three-geometry-meshes.ts
36385
- import * as THREE31 from "three";
36605
+ import * as THREE32 from "three";
36386
36606
 
36387
36607
  // src/utils/create-board-material.ts
36388
- import * as THREE30 from "three";
36389
- var DEFAULT_SIDE = THREE30.DoubleSide;
36608
+ import * as THREE31 from "three";
36609
+ var DEFAULT_SIDE = THREE31.DoubleSide;
36390
36610
  var createBoardMaterial = ({
36391
36611
  material,
36392
36612
  color,
@@ -36394,7 +36614,7 @@ var createBoardMaterial = ({
36394
36614
  isFaux = false
36395
36615
  }) => {
36396
36616
  if (material === "fr4") {
36397
- return new THREE30.MeshPhysicalMaterial({
36617
+ return new THREE31.MeshPhysicalMaterial({
36398
36618
  color,
36399
36619
  side,
36400
36620
  metalness: 0,
@@ -36408,7 +36628,7 @@ var createBoardMaterial = ({
36408
36628
  flatShading: true
36409
36629
  });
36410
36630
  }
36411
- return new THREE30.MeshStandardMaterial({
36631
+ return new THREE31.MeshStandardMaterial({
36412
36632
  color,
36413
36633
  side,
36414
36634
  flatShading: true,
@@ -36424,12 +36644,12 @@ function createGeometryMeshes(geoms) {
36424
36644
  const meshes = [];
36425
36645
  if (!geoms) return meshes;
36426
36646
  if (geoms.board && geoms.board.geometry) {
36427
- const mesh = new THREE31.Mesh(
36647
+ const mesh = new THREE32.Mesh(
36428
36648
  geoms.board.geometry,
36429
36649
  createBoardMaterial({
36430
36650
  material: geoms.board.material,
36431
36651
  color: geoms.board.color,
36432
- side: THREE31.DoubleSide,
36652
+ side: THREE32.DoubleSide,
36433
36653
  isFaux: geoms.board.isFaux
36434
36654
  })
36435
36655
  );
@@ -36439,11 +36659,11 @@ function createGeometryMeshes(geoms) {
36439
36659
  const createMeshesFromArray = (geomArray) => {
36440
36660
  if (geomArray) {
36441
36661
  geomArray.forEach((comp) => {
36442
- const mesh = new THREE31.Mesh(
36662
+ const mesh = new THREE32.Mesh(
36443
36663
  comp.geometry,
36444
- new THREE31.MeshStandardMaterial({
36664
+ new THREE32.MeshStandardMaterial({
36445
36665
  color: comp.color,
36446
- side: THREE31.DoubleSide,
36666
+ side: THREE32.DoubleSide,
36447
36667
  flatShading: true,
36448
36668
  // Consistent with board
36449
36669
  polygonOffset: true,
@@ -36459,172 +36679,6 @@ function createGeometryMeshes(geoms) {
36459
36679
  createMeshesFromArray(geoms.platedHoles);
36460
36680
  createMeshesFromArray(geoms.smtPads);
36461
36681
  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
36682
  return meshes;
36629
36683
  }
36630
36684
 
@@ -36652,8 +36706,6 @@ var BoardMeshes = ({
36652
36706
  }
36653
36707
  } else if (mesh.name.includes("plated_hole") || mesh.name.includes("via")) {
36654
36708
  shouldShow = visibility.topCopper || visibility.bottomCopper;
36655
- } else if (mesh.name.includes("copper_pour")) {
36656
- shouldShow = visibility.topCopper || visibility.bottomCopper;
36657
36709
  }
36658
36710
  if (shouldShow) {
36659
36711
  rootObject.add(mesh);
@@ -36681,6 +36733,10 @@ var BoardMeshes = ({
36681
36733
  shouldShow = visibility.topCopper;
36682
36734
  } else if (mesh.name.includes("bottom-copper-text")) {
36683
36735
  shouldShow = visibility.bottomCopper;
36736
+ } else if (mesh.name.includes("top-copper")) {
36737
+ shouldShow = visibility.topCopper;
36738
+ } else if (mesh.name.includes("bottom-copper")) {
36739
+ shouldShow = visibility.bottomCopper;
36684
36740
  } else if (mesh.name.includes("panel-outlines")) {
36685
36741
  shouldShow = visibility.boardBody;
36686
36742
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tscircuit/3d-viewer",
3
- "version": "0.0.477",
3
+ "version": "0.0.478",
4
4
  "main": "./dist/index.js",
5
5
  "module": "./dist/index.js",
6
6
  "type": "module",
@@ -29,6 +29,7 @@
29
29
  "dependencies": {
30
30
  "@jscad/regl-renderer": "^2.6.12",
31
31
  "@jscad/stl-serializer": "^2.1.20",
32
+ "circuit-to-canvas": "^0.0.26",
32
33
  "react-hot-toast": "^2.6.0",
33
34
  "three": "^0.165.0",
34
35
  "three-stdlib": "^2.36.0",