@tscircuit/3d-viewer 0.0.594 → 0.0.596

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +349 -238
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -14433,7 +14433,7 @@ var require_browser = __commonJS({
14433
14433
 
14434
14434
  // src/CadViewer.tsx
14435
14435
  import { useCallback as useCallback24, useEffect as useEffect49, useRef as useRef29, useState as useState39 } from "react";
14436
- import * as THREE48 from "three";
14436
+ import * as THREE51 from "three";
14437
14437
 
14438
14438
  // src/CadViewerJscad.tsx
14439
14439
  import { su as su11 } from "@tscircuit/circuit-json-util";
@@ -33696,7 +33696,7 @@ import * as THREE23 from "three";
33696
33696
  // package.json
33697
33697
  var package_default = {
33698
33698
  name: "@tscircuit/3d-viewer",
33699
- version: "0.0.593",
33699
+ version: "0.0.595",
33700
33700
  repository: {
33701
33701
  type: "git",
33702
33702
  url: "https://github.com/tscircuit/3d-viewer"
@@ -35559,37 +35559,10 @@ var useStlsFromGeom = (geom) => {
35559
35559
  import { useState as useState13, useEffect as useEffect22, useRef as useRef8 } from "react";
35560
35560
 
35561
35561
  // src/soup-to-3d/index.ts
35562
- var import_primitives2 = __toESM(require_primitives(), 1);
35563
35562
  var import_colors = __toESM(require_colors(), 1);
35563
+ var import_primitives2 = __toESM(require_primitives(), 1);
35564
35564
  import { su as su3 } from "@tscircuit/circuit-json-util";
35565
35565
 
35566
- // src/geoms/constants.ts
35567
- var M = 0.01;
35568
- var colors = {
35569
- // Warm ENIG gold, rather than saturated orange, matches manufactured pads.
35570
- copper: [0.78, 0.63, 0.39],
35571
- fr4Tan: [0.6, 0.43, 0.28],
35572
- // Deep, slightly blue-green solder mask keeps detail visible on dark UI.
35573
- fr4SolderMaskGreen: [0.06, 0.31, 0.19],
35574
- fr4TracesWithMaskGreen: [0.09, 0.38, 0.23],
35575
- fr4TracesWithoutMaskTan: [0.6, 0.43, 0.28],
35576
- fr1Tan: [0.8, 0.4, 0.2],
35577
- fr1TracesWithMaskCopper: [0.9, 0.6, 0.2],
35578
- fr1SolderMaskGreen: [0.02, 0.1, 0.04]
35579
- };
35580
- var MANIFOLD_Z_OFFSET = 1e-3;
35581
- var SMOOTH_CIRCLE_SEGMENTS = 32;
35582
- var TRACE_TEXTURE_RESOLUTION = 150;
35583
- var FAUX_BOARD_OPACITY = 0.6;
35584
- var boardMaterialColors = {
35585
- fr1: colors.fr1Tan,
35586
- fr4: colors.fr4Tan
35587
- };
35588
- var soldermaskColors = {
35589
- fr1: colors.fr1SolderMaskGreen,
35590
- fr4: colors.fr4SolderMaskGreen
35591
- };
35592
-
35593
35566
  // src/geoms/create-board-with-outline.ts
35594
35567
  var import_extrusions = __toESM(require_extrusions(), 1);
35595
35568
  var import_primitives = __toESM(require_primitives(), 1);
@@ -35621,6 +35594,109 @@ var createBoardGeomWithOutline = (board, depth = 1.2, options = {}) => {
35621
35594
  return boardGeom;
35622
35595
  };
35623
35596
 
35597
+ // src/utils/get-board-edge-color.ts
35598
+ import * as THREE25 from "three";
35599
+
35600
+ // src/geoms/constants.ts
35601
+ var M = 0.01;
35602
+ var colors = {
35603
+ // Warm ENIG gold, rather than saturated orange, matches manufactured pads.
35604
+ copper: [0.78, 0.63, 0.39],
35605
+ fr4Tan: [0.6, 0.43, 0.28],
35606
+ // Deep, slightly blue-green solder mask keeps detail visible on dark UI.
35607
+ fr4SolderMaskGreen: [0.06, 0.31, 0.19],
35608
+ fr4TracesWithMaskGreen: [0.09, 0.38, 0.23],
35609
+ fr4TracesWithoutMaskTan: [0.6, 0.43, 0.28],
35610
+ fr1Tan: [0.8, 0.4, 0.2],
35611
+ fr1TracesWithMaskCopper: [0.9, 0.6, 0.2],
35612
+ fr1SolderMaskGreen: [0.02, 0.1, 0.04]
35613
+ };
35614
+ var MANIFOLD_Z_OFFSET = 1e-3;
35615
+ var SMOOTH_CIRCLE_SEGMENTS = 32;
35616
+ var TRACE_TEXTURE_RESOLUTION = 150;
35617
+ var FAUX_BOARD_OPACITY = 0.6;
35618
+ var boardMaterialColors = {
35619
+ fr1: colors.fr1Tan,
35620
+ fr4: colors.fr4Tan
35621
+ };
35622
+ var soldermaskColors = {
35623
+ fr1: colors.fr1SolderMaskGreen,
35624
+ fr4: colors.fr4SolderMaskGreen
35625
+ };
35626
+
35627
+ // src/utils/soldermask-color.ts
35628
+ import * as THREE24 from "three";
35629
+ var SOLDERMASK_PRESET_HEX = {
35630
+ red: "#650202",
35631
+ blue: "#003f7d",
35632
+ purple: "#4c1d69",
35633
+ black: "#071014",
35634
+ white: "#dddddd",
35635
+ yellow: "#dcc84a"
35636
+ };
35637
+ var DEFAULT_SOLDERMASK_OPACITY = 0.875;
35638
+ var CSS_HEX_COLOR_PATTERN = /^#(?:[\da-f]{3}|[\da-f]{6})$/i;
35639
+ var CSS_RGB_INTEGER_PATTERN = /^rgb\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*\)$/i;
35640
+ var CSS_RGB_PERCENT_PATTERN = /^rgb\(\s*\d+%\s*,\s*\d+%\s*,\s*\d+%\s*\)$/i;
35641
+ var CSS_HSL_PATTERN = /^hsl\(\s*\d*\.?\d+\s*,\s*\d*\.?\d+%\s*,\s*\d*\.?\d+%\s*\)$/i;
35642
+ var SOLDERMASK_PRESET_OPACITY = {
35643
+ blue: 0.96,
35644
+ purple: 0.96,
35645
+ black: 0.99,
35646
+ yellow: 0.92
35647
+ };
35648
+ var resolveSoldermaskColor = (requestedColor) => {
35649
+ const normalizedColor = requestedColor?.trim();
35650
+ if (!normalizedColor) {
35651
+ return null;
35652
+ }
35653
+ const lowercaseColor = normalizedColor.toLowerCase();
35654
+ if (lowercaseColor === "green" || lowercaseColor === "not_specified") {
35655
+ return null;
35656
+ }
35657
+ const presetName = lowercaseColor;
35658
+ const presetColor = SOLDERMASK_PRESET_HEX[presetName];
35659
+ if (presetColor) return new THREE24.Color(presetColor);
35660
+ const isCssColor = Object.hasOwn(THREE24.Color.NAMES, lowercaseColor) || CSS_HEX_COLOR_PATTERN.test(normalizedColor) || CSS_RGB_INTEGER_PATTERN.test(normalizedColor) || CSS_RGB_PERCENT_PATTERN.test(normalizedColor) || CSS_HSL_PATTERN.test(normalizedColor);
35661
+ return isCssColor ? new THREE24.Color(lowercaseColor) : null;
35662
+ };
35663
+ var resolveSoldermaskOpacity = (requestedColor) => {
35664
+ const presetName = requestedColor?.trim().toLowerCase();
35665
+ return SOLDERMASK_PRESET_OPACITY[presetName] ?? DEFAULT_SOLDERMASK_OPACITY;
35666
+ };
35667
+ var isYellowSoldermaskColor = ({
35668
+ red,
35669
+ green,
35670
+ blue
35671
+ }) => Math.hypot(red - 220, green - 200, blue - 74) < 4 || Math.hypot(red - 218, green - 197, blue - 76) < 4;
35672
+ var compositeSoldermaskOverCopper = ({
35673
+ soldermaskColor,
35674
+ copperColor,
35675
+ soldermaskOpacity
35676
+ }) => soldermaskColor.clone().multiplyScalar(soldermaskOpacity).add(copperColor.clone().multiplyScalar(1 - soldermaskOpacity));
35677
+ var soldermaskColorToCss = (color) => {
35678
+ const displayColor = color.clone().convertLinearToSRGB();
35679
+ const channels = [
35680
+ Math.round(displayColor.r * 255),
35681
+ Math.round(displayColor.g * 255),
35682
+ Math.round(displayColor.b * 255)
35683
+ ];
35684
+ return `rgb(${channels.join(",")})`;
35685
+ };
35686
+
35687
+ // src/utils/get-board-edge-color.ts
35688
+ var LEGACY_FR4_EDGE_COLOR = new THREE25.Color(1063462);
35689
+ var getBoardEdgeColor = ({
35690
+ material,
35691
+ solder_mask_color
35692
+ }) => {
35693
+ const soldermaskColor = resolveSoldermaskColor(solder_mask_color);
35694
+ if (soldermaskColor) return soldermaskColor;
35695
+ if (material === "fr4") return LEGACY_FR4_EDGE_COLOR.clone();
35696
+ const materialColor = boardMaterialColors[material] ?? colors.fr4Tan;
35697
+ return new THREE25.Color(materialColor[0], materialColor[1], materialColor[2]);
35698
+ };
35699
+
35624
35700
  // src/soup-to-3d/index.ts
35625
35701
  var createSimplifiedBoardGeom = (circuitJson) => {
35626
35702
  const panels = circuitJson.filter(
@@ -35628,13 +35704,12 @@ var createSimplifiedBoardGeom = (circuitJson) => {
35628
35704
  );
35629
35705
  const boards = su3(circuitJson).pcb_board.list();
35630
35706
  let boardOrPanel;
35707
+ let board;
35631
35708
  let pcbThickness = 1.2;
35632
35709
  if (panels.length > 0) {
35633
35710
  boardOrPanel = panels[0];
35634
- const firstBoardInPanel = boards.find(
35635
- (b) => b.pcb_panel_id === boardOrPanel.pcb_panel_id
35636
- );
35637
- pcbThickness = firstBoardInPanel?.thickness ?? 1.2;
35711
+ board = boards.find((b) => b.pcb_panel_id === boardOrPanel.pcb_panel_id);
35712
+ pcbThickness = board?.thickness ?? 1.2;
35638
35713
  } else {
35639
35714
  const boardsNotInPanel = boards.filter(
35640
35715
  (b) => !b.pcb_panel_id
@@ -35644,6 +35719,7 @@ var createSimplifiedBoardGeom = (circuitJson) => {
35644
35719
  console.warn("No pcb_board or pcb_panel found for simplified geometry");
35645
35720
  return [];
35646
35721
  }
35722
+ board = boardOrPanel;
35647
35723
  pcbThickness = boardOrPanel.thickness ?? 1.2;
35648
35724
  }
35649
35725
  let boardGeom;
@@ -35660,11 +35736,13 @@ var createSimplifiedBoardGeom = (circuitJson) => {
35660
35736
  center: [boardOrPanel.center.x, boardOrPanel.center.y, 0]
35661
35737
  });
35662
35738
  }
35663
- const materialName = "material" in boardOrPanel && boardOrPanel.material ? boardOrPanel.material : panels.length > 0 ? boards.find(
35664
- (b) => b.pcb_panel_id === boardOrPanel.pcb_panel_id
35665
- )?.material ?? "fr4" : "fr4";
35666
- const material = boardMaterialColors[materialName] ?? colors.fr4Tan;
35667
- return [(0, import_colors.colorize)(material, boardGeom)];
35739
+ const boardEdgeColor = getBoardEdgeColor({
35740
+ material: board?.material ?? "fr4",
35741
+ solder_mask_color: board?.solder_mask_color
35742
+ });
35743
+ return [
35744
+ (0, import_colors.colorize)([boardEdgeColor.r, boardEdgeColor.g, boardEdgeColor.b], boardGeom)
35745
+ ];
35668
35746
  };
35669
35747
 
35670
35748
  // src/BoardGeomBuilder.ts
@@ -36381,7 +36459,8 @@ var BoardGeomBuilder = class {
36381
36459
  height: panel.height,
36382
36460
  thickness: firstBoardInPanel?.thickness ?? 1.4,
36383
36461
  material: firstBoardInPanel?.material ?? "fr4",
36384
- num_layers: firstBoardInPanel?.num_layers ?? 2
36462
+ num_layers: firstBoardInPanel?.num_layers ?? 2,
36463
+ solder_mask_color: firstBoardInPanel?.solder_mask_color
36385
36464
  };
36386
36465
  } else {
36387
36466
  const boardsNotInPanel = boards.filter((b) => !b.pcb_panel_id);
@@ -36850,8 +36929,11 @@ var BoardGeomBuilder = class {
36850
36929
  }
36851
36930
  finalize() {
36852
36931
  if (!this.boardGeom) return;
36853
- const boardMaterialColor = boardMaterialColors[this.board.material] ?? colors.fr4Tan;
36854
- this.boardGeom = (0, import_colors3.colorize)(boardMaterialColor, this.boardGeom);
36932
+ const boardEdgeColor = getBoardEdgeColor(this.board);
36933
+ this.boardGeom = (0, import_colors3.colorize)(
36934
+ [boardEdgeColor.r, boardEdgeColor.g, boardEdgeColor.b],
36935
+ this.boardGeom
36936
+ );
36855
36937
  this.finalGeoms = [
36856
36938
  this.boardGeom,
36857
36939
  ...this.platedHoleGeoms,
@@ -36972,7 +37054,7 @@ var Text = ({
36972
37054
  };
36973
37055
 
36974
37056
  // src/three-components/Error3d.tsx
36975
- import * as THREE24 from "three";
37057
+ import * as THREE26 from "three";
36976
37058
  import { Fragment as Fragment5, jsx as jsx16, jsxs as jsxs4 } from "react/jsx-runtime";
36977
37059
  var Error3d = ({
36978
37060
  error,
@@ -37006,7 +37088,7 @@ var Error3d = ({
37006
37088
  return [0, 0, 0];
37007
37089
  }, [cad_component]);
37008
37090
  const group = useMemo19(() => {
37009
- const g = new THREE24.Group();
37091
+ const g = new THREE26.Group();
37010
37092
  g.position.fromArray(position);
37011
37093
  return g;
37012
37094
  }, [position]);
@@ -37066,9 +37148,9 @@ var Error3d = ({
37066
37148
  };
37067
37149
  var ErrorBox = ({ parent }) => {
37068
37150
  const mesh = useMemo19(() => {
37069
- const m = new THREE24.Mesh(
37070
- new THREE24.BoxGeometry(0.5, 0.5, 0.5),
37071
- new THREE24.MeshStandardMaterial({
37151
+ const m = new THREE26.Mesh(
37152
+ new THREE26.BoxGeometry(0.5, 0.5, 0.5),
37153
+ new THREE26.MeshStandardMaterial({
37072
37154
  depthTest: false,
37073
37155
  transparent: true,
37074
37156
  color: "red",
@@ -37093,7 +37175,7 @@ import { su as su8 } from "@tscircuit/circuit-json-util";
37093
37175
  import { useEffect as useEffect25, useMemo as useMemo20 } from "react";
37094
37176
 
37095
37177
  // src/textures/create-combined-board-textures.ts
37096
- import * as THREE36 from "three";
37178
+ import * as THREE39 from "three";
37097
37179
 
37098
37180
  // node_modules/@tscircuit/math-utils/dist/chunk-5N7UJNVK.js
37099
37181
  var getBoundsFromPoints = (points) => {
@@ -37148,7 +37230,7 @@ function calculateOutlineBounds(boardData) {
37148
37230
  // src/utils/pad-texture.ts
37149
37231
  import { su as su5 } from "@tscircuit/circuit-json-util";
37150
37232
  import { CircuitToCanvasDrawer } from "circuit-to-canvas";
37151
- import * as THREE25 from "three";
37233
+ import * as THREE27 from "three";
37152
37234
  function createPadTextureForLayer({
37153
37235
  layer,
37154
37236
  circuitJson,
@@ -37228,10 +37310,10 @@ function createPadTextureForLayer({
37228
37310
  drawSoldermaskTop: false,
37229
37311
  drawSoldermaskBottom: false
37230
37312
  });
37231
- const texture = new THREE25.CanvasTexture(canvas);
37313
+ const texture = new THREE27.CanvasTexture(canvas);
37232
37314
  texture.generateMipmaps = true;
37233
- texture.minFilter = THREE25.LinearMipmapLinearFilter;
37234
- texture.magFilter = THREE25.LinearFilter;
37315
+ texture.minFilter = THREE27.LinearMipmapLinearFilter;
37316
+ texture.magFilter = THREE27.LinearFilter;
37235
37317
  texture.anisotropy = 16;
37236
37318
  texture.needsUpdate = true;
37237
37319
  return texture;
@@ -37239,7 +37321,7 @@ function createPadTextureForLayer({
37239
37321
 
37240
37322
  // src/utils/panel-outline-texture.ts
37241
37323
  import { su as su6 } from "@tscircuit/circuit-json-util";
37242
- import * as THREE26 from "three";
37324
+ import * as THREE28 from "three";
37243
37325
  var resolvePanelIdForTexture = (circuitJson) => {
37244
37326
  const panels = circuitJson.filter(
37245
37327
  (e) => e.type === "pcb_panel"
@@ -37307,17 +37389,17 @@ function createPanelOutlineTextureForLayer({
37307
37389
  );
37308
37390
  }
37309
37391
  });
37310
- const texture = new THREE26.CanvasTexture(canvas);
37392
+ const texture = new THREE28.CanvasTexture(canvas);
37311
37393
  texture.generateMipmaps = true;
37312
- texture.minFilter = THREE26.LinearMipmapLinearFilter;
37313
- texture.magFilter = THREE26.LinearFilter;
37394
+ texture.minFilter = THREE28.LinearMipmapLinearFilter;
37395
+ texture.magFilter = THREE28.LinearFilter;
37314
37396
  texture.anisotropy = 16;
37315
37397
  texture.needsUpdate = true;
37316
37398
  return texture;
37317
37399
  }
37318
37400
 
37319
37401
  // src/utils/trace-texture.ts
37320
- import * as THREE27 from "three";
37402
+ import * as THREE29 from "three";
37321
37403
  import { CircuitToCanvasDrawer as CircuitToCanvasDrawer2 } from "circuit-to-canvas";
37322
37404
  import { getElementRenderLayers, su as su7 } from "@tscircuit/circuit-json-util";
37323
37405
 
@@ -37437,10 +37519,10 @@ function createTraceTextureForLayer({
37437
37519
  drawSoldermaskTop: false,
37438
37520
  drawSoldermaskBottom: false
37439
37521
  });
37440
- const texture = new THREE27.CanvasTexture(canvas);
37522
+ const texture = new THREE29.CanvasTexture(canvas);
37441
37523
  texture.generateMipmaps = true;
37442
- texture.minFilter = THREE27.LinearMipmapLinearFilter;
37443
- texture.magFilter = THREE27.LinearFilter;
37524
+ texture.minFilter = THREE29.LinearMipmapLinearFilter;
37525
+ texture.magFilter = THREE29.LinearFilter;
37444
37526
  texture.anisotropy = 16;
37445
37527
  texture.needsUpdate = true;
37446
37528
  return texture;
@@ -37448,7 +37530,7 @@ function createTraceTextureForLayer({
37448
37530
 
37449
37531
  // src/textures/create-copper-pour-texture-for-layer.ts
37450
37532
  import { CircuitToCanvasDrawer as CircuitToCanvasDrawer3 } from "circuit-to-canvas";
37451
- import * as THREE28 from "three";
37533
+ import * as THREE30 from "three";
37452
37534
  var toRgb = (colorArr) => {
37453
37535
  const [r = 0, g = 0, b = 0] = colorArr;
37454
37536
  return `rgb(${Math.round(r * 255)}, ${Math.round(g * 255)}, ${Math.round(
@@ -37572,17 +37654,17 @@ function createCopperPourTextureForLayer({
37572
37654
  if (!onlyCoveredBySoldermask) {
37573
37655
  setColorAndDraw(uncoveredPours, uncoveredColor);
37574
37656
  }
37575
- const texture = new THREE28.CanvasTexture(canvas);
37657
+ const texture = new THREE30.CanvasTexture(canvas);
37576
37658
  texture.generateMipmaps = true;
37577
- texture.minFilter = THREE28.LinearMipmapLinearFilter;
37578
- texture.magFilter = THREE28.LinearFilter;
37659
+ texture.minFilter = THREE30.LinearMipmapLinearFilter;
37660
+ texture.magFilter = THREE30.LinearFilter;
37579
37661
  texture.anisotropy = 16;
37580
37662
  texture.needsUpdate = true;
37581
37663
  return texture;
37582
37664
  }
37583
37665
 
37584
37666
  // src/textures/create-copper-text-texture-for-layer.ts
37585
- import * as THREE29 from "three";
37667
+ import * as THREE31 from "three";
37586
37668
 
37587
37669
  // src/textures/copper-text/copper-text-drawing.ts
37588
37670
  import { CircuitToCanvasDrawer as CircuitToCanvasDrawer4 } from "circuit-to-canvas";
@@ -37697,17 +37779,17 @@ function createCopperTextTextureForLayer({
37697
37779
  elements,
37698
37780
  copperColor
37699
37781
  });
37700
- const texture = new THREE29.CanvasTexture(canvas);
37782
+ const texture = new THREE31.CanvasTexture(canvas);
37701
37783
  texture.generateMipmaps = true;
37702
- texture.minFilter = THREE29.LinearMipmapLinearFilter;
37703
- texture.magFilter = THREE29.LinearFilter;
37784
+ texture.minFilter = THREE31.LinearMipmapLinearFilter;
37785
+ texture.magFilter = THREE31.LinearFilter;
37704
37786
  texture.anisotropy = 16;
37705
37787
  texture.needsUpdate = true;
37706
37788
  return texture;
37707
37789
  }
37708
37790
 
37709
37791
  // src/textures/create-fabrication-note-texture-for-layer.ts
37710
- import * as THREE30 from "three";
37792
+ import * as THREE32 from "three";
37711
37793
 
37712
37794
  // src/textures/fabrication-note/fabrication-note-drawing.ts
37713
37795
  import { CircuitToCanvasDrawer as CircuitToCanvasDrawer5 } from "circuit-to-canvas";
@@ -37937,17 +38019,17 @@ function createFabricationNoteTextureForLayer({
37937
38019
  bounds,
37938
38020
  elements
37939
38021
  });
37940
- const texture = new THREE30.CanvasTexture(canvas);
38022
+ const texture = new THREE32.CanvasTexture(canvas);
37941
38023
  texture.generateMipmaps = true;
37942
- texture.minFilter = THREE30.LinearMipmapLinearFilter;
37943
- texture.magFilter = THREE30.LinearFilter;
38024
+ texture.minFilter = THREE32.LinearMipmapLinearFilter;
38025
+ texture.magFilter = THREE32.LinearFilter;
37944
38026
  texture.anisotropy = 16;
37945
38027
  texture.needsUpdate = true;
37946
38028
  return texture;
37947
38029
  }
37948
38030
 
37949
38031
  // src/textures/create-keepout-texture-for-layer.ts
37950
- import * as THREE31 from "three";
38032
+ import * as THREE33 from "three";
37951
38033
 
37952
38034
  // src/textures/keepout/keepout-drawing.ts
37953
38035
  import { CircuitToCanvasDrawer as CircuitToCanvasDrawer6 } from "circuit-to-canvas";
@@ -38022,17 +38104,17 @@ function createKeepoutTextureForLayer({
38022
38104
  elements,
38023
38105
  keepoutColor
38024
38106
  });
38025
- const texture = new THREE31.CanvasTexture(canvas);
38107
+ const texture = new THREE33.CanvasTexture(canvas);
38026
38108
  texture.generateMipmaps = true;
38027
- texture.minFilter = THREE31.LinearMipmapLinearFilter;
38028
- texture.magFilter = THREE31.LinearFilter;
38109
+ texture.minFilter = THREE33.LinearMipmapLinearFilter;
38110
+ texture.magFilter = THREE33.LinearFilter;
38029
38111
  texture.anisotropy = 16;
38030
38112
  texture.needsUpdate = true;
38031
38113
  return texture;
38032
38114
  }
38033
38115
 
38034
38116
  // src/textures/create-pcb-note-texture-for-layer.ts
38035
- import * as THREE32 from "three";
38117
+ import * as THREE34 from "three";
38036
38118
 
38037
38119
  // src/textures/pcb-note/pcb-note-drawing.ts
38038
38120
  import { CircuitToCanvasDrawer as CircuitToCanvasDrawer7 } from "circuit-to-canvas";
@@ -38185,17 +38267,17 @@ function createPcbNoteTextureForLayer({
38185
38267
  bounds,
38186
38268
  elements
38187
38269
  });
38188
- const texture = new THREE32.CanvasTexture(canvas);
38270
+ const texture = new THREE34.CanvasTexture(canvas);
38189
38271
  texture.generateMipmaps = true;
38190
- texture.minFilter = THREE32.LinearMipmapLinearFilter;
38191
- texture.magFilter = THREE32.LinearFilter;
38272
+ texture.minFilter = THREE34.LinearMipmapLinearFilter;
38273
+ texture.magFilter = THREE34.LinearFilter;
38192
38274
  texture.anisotropy = 16;
38193
38275
  texture.needsUpdate = true;
38194
38276
  return texture;
38195
38277
  }
38196
38278
 
38197
38279
  // src/textures/create-silkscreen-texture-for-layer.ts
38198
- import * as THREE33 from "three";
38280
+ import * as THREE35 from "three";
38199
38281
 
38200
38282
  // src/textures/silkscreen/silkscreen-drawing.ts
38201
38283
  import { CircuitToCanvasDrawer as CircuitToCanvasDrawer8 } from "circuit-to-canvas";
@@ -38323,34 +38405,60 @@ function createSilkscreenTextureForLayer({
38323
38405
  apertureElements,
38324
38406
  silkscreenColor
38325
38407
  });
38326
- const texture = new THREE33.CanvasTexture(canvas);
38408
+ const texture = new THREE35.CanvasTexture(canvas);
38327
38409
  texture.generateMipmaps = true;
38328
- texture.minFilter = THREE33.LinearMipmapLinearFilter;
38329
- texture.magFilter = THREE33.LinearFilter;
38410
+ texture.minFilter = THREE35.LinearMipmapLinearFilter;
38411
+ texture.magFilter = THREE35.LinearFilter;
38330
38412
  texture.anisotropy = 16;
38331
38413
  texture.needsUpdate = true;
38332
38414
  return texture;
38333
38415
  }
38334
38416
 
38335
38417
  // src/textures/create-soldermask-texture-for-layer.ts
38336
- import * as THREE34 from "three";
38418
+ import * as THREE37 from "three";
38337
38419
 
38338
38420
  // src/textures/soldermask/soldermask-drawing.ts
38339
38421
  import { CircuitToCanvasDrawer as CircuitToCanvasDrawer9 } from "circuit-to-canvas";
38422
+ import * as THREE36 from "three";
38340
38423
  var toRgb2 = (colorArr) => {
38341
38424
  const [r = 0, g = 0, b = 0] = colorArr;
38342
38425
  return `rgb(${Math.round(r * 255)}, ${Math.round(g * 255)}, ${Math.round(
38343
38426
  b * 255
38344
38427
  )})`;
38345
38428
  };
38346
- var getSoldermaskPalette = (material) => {
38347
- const soldermask = toRgb2(
38348
- soldermaskColors[material] ?? colors.fr4SolderMaskGreen
38429
+ var getSoldermaskPalette = (boardData) => {
38430
+ const legacySoldermask = toRgb2(
38431
+ soldermaskColors[boardData.material] ?? colors.fr4SolderMaskGreen
38432
+ );
38433
+ const legacySoldermaskOverCopper = boardData.material === "fr1" ? toRgb2(colors.fr1TracesWithMaskCopper) : toRgb2(colors.fr4TracesWithMaskGreen);
38434
+ const requestedSoldermaskColor = resolveSoldermaskColor(
38435
+ boardData.solder_mask_color
38436
+ );
38437
+ if (!requestedSoldermaskColor) {
38438
+ return {
38439
+ soldermask: legacySoldermask,
38440
+ soldermaskOverCopper: legacySoldermaskOverCopper,
38441
+ copper: toRgb2(colors.copper),
38442
+ transparent: "rgba(0,0,0,0)"
38443
+ };
38444
+ }
38445
+ const copperColor = new THREE36.Color().setRGB(
38446
+ colors.copper[0],
38447
+ colors.copper[1],
38448
+ colors.copper[2],
38449
+ THREE36.SRGBColorSpace
38349
38450
  );
38350
- const soldermaskOverCopper = material === "fr1" ? toRgb2(colors.fr1TracesWithMaskCopper) : toRgb2(colors.fr4TracesWithMaskGreen);
38351
38451
  return {
38352
- soldermask,
38353
- soldermaskOverCopper,
38452
+ soldermask: soldermaskColorToCss(requestedSoldermaskColor),
38453
+ soldermaskOverCopper: soldermaskColorToCss(
38454
+ compositeSoldermaskOverCopper({
38455
+ soldermaskColor: requestedSoldermaskColor,
38456
+ copperColor,
38457
+ soldermaskOpacity: resolveSoldermaskOpacity(
38458
+ boardData.solder_mask_color
38459
+ )
38460
+ })
38461
+ ),
38354
38462
  copper: toRgb2(colors.copper),
38355
38463
  transparent: "rgba(0,0,0,0)"
38356
38464
  };
@@ -38368,9 +38476,9 @@ var drawSoldermaskLayer = ({
38368
38476
  layer,
38369
38477
  bounds,
38370
38478
  elements,
38371
- boardMaterial
38479
+ boardData
38372
38480
  }) => {
38373
- const palette = getSoldermaskPalette(boardMaterial);
38481
+ const palette = getSoldermaskPalette(boardData);
38374
38482
  const copperRenderLayer = layer === "top" ? "top_copper" : "bottom_copper";
38375
38483
  const drawer = new CircuitToCanvasDrawer9(ctx);
38376
38484
  drawer.configure({
@@ -38470,19 +38578,19 @@ function createSoldermaskTextureForLayer({
38470
38578
  layer,
38471
38579
  bounds,
38472
38580
  elements,
38473
- boardMaterial: boardData.material
38581
+ boardData
38474
38582
  });
38475
- const texture = new THREE34.CanvasTexture(canvas);
38583
+ const texture = new THREE37.CanvasTexture(canvas);
38476
38584
  texture.generateMipmaps = true;
38477
- texture.minFilter = THREE34.LinearMipmapLinearFilter;
38478
- texture.magFilter = THREE34.LinearFilter;
38585
+ texture.minFilter = THREE37.LinearMipmapLinearFilter;
38586
+ texture.magFilter = THREE37.LinearFilter;
38479
38587
  texture.anisotropy = 16;
38480
38588
  texture.needsUpdate = true;
38481
38589
  return texture;
38482
38590
  }
38483
38591
 
38484
38592
  // src/textures/create-through-hole-texture-for-layer.ts
38485
- import * as THREE35 from "three";
38593
+ import * as THREE38 from "three";
38486
38594
 
38487
38595
  // src/textures/through-hole/through-hole-drawing.ts
38488
38596
  import { CircuitToCanvasDrawer as CircuitToCanvasDrawer10 } from "circuit-to-canvas";
@@ -38577,10 +38685,10 @@ function createThroughHoleTextureForLayer({
38577
38685
  elements,
38578
38686
  copperColor
38579
38687
  });
38580
- const texture = new THREE35.CanvasTexture(canvas);
38688
+ const texture = new THREE38.CanvasTexture(canvas);
38581
38689
  texture.generateMipmaps = true;
38582
- texture.minFilter = THREE35.LinearMipmapLinearFilter;
38583
- texture.magFilter = THREE35.LinearFilter;
38690
+ texture.minFilter = THREE38.LinearMipmapLinearFilter;
38691
+ texture.magFilter = THREE38.LinearFilter;
38584
38692
  texture.anisotropy = 16;
38585
38693
  texture.needsUpdate = true;
38586
38694
  return texture;
@@ -38668,10 +38776,10 @@ var createCombinedTexture = ({
38668
38776
  applySoldermaskSurfaceFilter(ctx, canvasWidth, canvasHeight, {
38669
38777
  includeReflection: layer === "top"
38670
38778
  });
38671
- const combinedTexture = new THREE36.CanvasTexture(canvas);
38779
+ const combinedTexture = new THREE39.CanvasTexture(canvas);
38672
38780
  combinedTexture.generateMipmaps = false;
38673
- combinedTexture.minFilter = THREE36.LinearFilter;
38674
- combinedTexture.magFilter = THREE36.LinearFilter;
38781
+ combinedTexture.minFilter = THREE39.LinearFilter;
38782
+ combinedTexture.magFilter = THREE39.LinearFilter;
38675
38783
  combinedTexture.premultiplyAlpha = true;
38676
38784
  combinedTexture.anisotropy = 16;
38677
38785
  combinedTexture.needsUpdate = true;
@@ -38696,11 +38804,11 @@ var createMaskedCopperMask = ({
38696
38804
  if (!texture?.image) continue;
38697
38805
  ctx.drawImage(texture.image, 0, 0, width10, height10);
38698
38806
  }
38699
- const maskTexture = new THREE36.CanvasTexture(canvas);
38700
- maskTexture.colorSpace = THREE36.NoColorSpace;
38807
+ const maskTexture = new THREE39.CanvasTexture(canvas);
38808
+ maskTexture.colorSpace = THREE39.NoColorSpace;
38701
38809
  maskTexture.generateMipmaps = false;
38702
- maskTexture.minFilter = THREE36.LinearFilter;
38703
- maskTexture.magFilter = THREE36.LinearFilter;
38810
+ maskTexture.minFilter = THREE39.LinearFilter;
38811
+ maskTexture.magFilter = THREE39.LinearFilter;
38704
38812
  maskTexture.premultiplyAlpha = false;
38705
38813
  maskTexture.needsUpdate = true;
38706
38814
  return maskTexture;
@@ -38839,7 +38947,7 @@ function createCombinedBoardTextures({
38839
38947
  }
38840
38948
 
38841
38949
  // src/textures/create-three-texture-meshes.ts
38842
- import * as THREE38 from "three";
38950
+ import * as THREE41 from "three";
38843
38951
 
38844
38952
  // src/board-surface-textures.ts
38845
38953
  var REALISTIC_BOARD_SURFACE_MATERIAL = {
@@ -38862,10 +38970,26 @@ var PAD_COPPER_TEXTURE_MATERIAL = {
38862
38970
  };
38863
38971
 
38864
38972
  // src/utils/create-board-relief-textures.ts
38865
- import * as THREE37 from "three";
38973
+ import * as THREE40 from "three";
38866
38974
  var PLAIN_SOLDERMASK_HEIGHT = 0.22;
38867
38975
  var MASKED_COPPER_HEIGHT = 0.7;
38868
38976
  var EXPOSED_COPPER_HEIGHT = 0.86;
38977
+ var PLAIN_SOLDERMASK_PROFILE = {
38978
+ height: PLAIN_SOLDERMASK_HEIGHT,
38979
+ microSurfaceWeight: 1,
38980
+ roughness: 0.7,
38981
+ metalness: 0.015,
38982
+ isExposedCopper: false,
38983
+ isMaskedCopper: false
38984
+ };
38985
+ var MASKED_COPPER_PROFILE = {
38986
+ height: MASKED_COPPER_HEIGHT,
38987
+ microSurfaceWeight: 0.9,
38988
+ roughness: 0.54,
38989
+ metalness: 0.025,
38990
+ isExposedCopper: false,
38991
+ isMaskedCopper: true
38992
+ };
38869
38993
  var clamp01 = (value) => Math.max(0, Math.min(1, value));
38870
38994
  var invertSurfaceHeight = (height10) => 1 - height10;
38871
38995
  var hashNoise = (x, y, salt) => {
@@ -38901,6 +39025,10 @@ var createMaskedTraceDetail = (x, y, salt) => {
38901
39025
  return (etch + routingGrain) * 0.045;
38902
39026
  };
38903
39027
  var getBoardSurfaceProfile = (r, g, b, hasMaskedCopper) => {
39028
+ if (hasMaskedCopper) return MASKED_COPPER_PROFILE;
39029
+ if (isYellowSoldermaskColor({ red: r, green: g, blue: b })) {
39030
+ return PLAIN_SOLDERMASK_PROFILE;
39031
+ }
38904
39032
  const isExposedCopper = r > 120 && g > 70 && b < 150 && r > g * 1.05 && g > b * 1.15;
38905
39033
  if (isExposedCopper) {
38906
39034
  return {
@@ -38934,30 +39062,16 @@ var getBoardSurfaceProfile = (r, g, b, hasMaskedCopper) => {
38934
39062
  isMaskedCopper: false
38935
39063
  };
38936
39064
  }
38937
- return hasMaskedCopper ? {
38938
- height: MASKED_COPPER_HEIGHT,
38939
- microSurfaceWeight: 0.9,
38940
- roughness: 0.54,
38941
- metalness: 0.025,
38942
- isExposedCopper: false,
38943
- isMaskedCopper: true
38944
- } : {
38945
- height: PLAIN_SOLDERMASK_HEIGHT,
38946
- microSurfaceWeight: 1,
38947
- roughness: 0.7,
38948
- metalness: 0.015,
38949
- isExposedCopper: false,
38950
- isMaskedCopper: false
38951
- };
39065
+ return PLAIN_SOLDERMASK_PROFILE;
38952
39066
  };
38953
39067
  var createDataTexture = (canvas) => {
38954
- const texture = new THREE37.CanvasTexture(canvas);
38955
- texture.colorSpace = THREE37.NoColorSpace;
39068
+ const texture = new THREE40.CanvasTexture(canvas);
39069
+ texture.colorSpace = THREE40.NoColorSpace;
38956
39070
  texture.generateMipmaps = false;
38957
- texture.minFilter = THREE37.LinearFilter;
38958
- texture.magFilter = THREE37.LinearFilter;
38959
- texture.wrapS = THREE37.ClampToEdgeWrapping;
38960
- texture.wrapT = THREE37.ClampToEdgeWrapping;
39071
+ texture.minFilter = THREE40.LinearFilter;
39072
+ texture.magFilter = THREE40.LinearFilter;
39073
+ texture.wrapS = THREE40.ClampToEdgeWrapping;
39074
+ texture.wrapT = THREE40.ClampToEdgeWrapping;
38961
39075
  texture.needsUpdate = true;
38962
39076
  return texture;
38963
39077
  };
@@ -39064,7 +39178,7 @@ var createBoardReliefTextures = (texture, maskedCopperMask) => {
39064
39178
  for (let x = 0; x < sourceCanvas.width; x++) {
39065
39179
  const dx = (getHeight4(x + 1, y) - getHeight4(x - 1, y)) * 4;
39066
39180
  const dy = (getHeight4(x, y + 1) - getHeight4(x, y - 1)) * 4;
39067
- const normal = new THREE37.Vector3(-dx, -dy, 1).normalize();
39181
+ const normal = new THREE40.Vector3(-dx, -dy, 1).normalize();
39068
39182
  const i = (y * sourceCanvas.width + x) * 4;
39069
39183
  normalData[i] = (normal.x * 0.5 + 0.5) * 255;
39070
39184
  normalData[i + 1] = (normal.y * 0.5 + 0.5) * 255;
@@ -39094,16 +39208,16 @@ function createTexturePlane(config, boardData) {
39094
39208
  } = config;
39095
39209
  if (!texture) return null;
39096
39210
  const boardOutlineBounds = calculateOutlineBounds(boardData);
39097
- const planeGeom = new THREE38.PlaneGeometry(
39211
+ const planeGeom = new THREE41.PlaneGeometry(
39098
39212
  boardOutlineBounds.width,
39099
39213
  boardOutlineBounds.height
39100
39214
  );
39101
- texture.colorSpace = THREE38.SRGBColorSpace;
39215
+ texture.colorSpace = THREE41.SRGBColorSpace;
39102
39216
  const sharedMaterialOptions = {
39103
39217
  map: texture,
39104
39218
  transparent: true,
39105
39219
  alphaTest: 0.08,
39106
- side: THREE38.FrontSide,
39220
+ side: THREE41.FrontSide,
39107
39221
  depthWrite: true,
39108
39222
  polygonOffset: usePolygonOffset,
39109
39223
  polygonOffsetFactor: usePolygonOffset ? -4 : 0,
@@ -39112,12 +39226,12 @@ function createTexturePlane(config, boardData) {
39112
39226
  opacity: isFaux ? FAUX_BOARD_OPACITY : 1
39113
39227
  };
39114
39228
  const reliefTextures = createBoardReliefTextures(texture, maskedCopperMask);
39115
- const material = new THREE38.MeshPhysicalMaterial({
39229
+ const material = new THREE41.MeshPhysicalMaterial({
39116
39230
  ...sharedMaterialOptions,
39117
39231
  bumpMap: reliefTextures?.bumpMap ?? null,
39118
39232
  bumpScale: REALISTIC_BOARD_SURFACE_MATERIAL.bumpScale,
39119
39233
  normalMap: reliefTextures?.normalMap ?? null,
39120
- normalScale: new THREE38.Vector2(
39234
+ normalScale: new THREE41.Vector2(
39121
39235
  REALISTIC_BOARD_SURFACE_MATERIAL.normalScale,
39122
39236
  REALISTIC_BOARD_SURFACE_MATERIAL.normalScale
39123
39237
  ),
@@ -39131,7 +39245,7 @@ function createTexturePlane(config, boardData) {
39131
39245
  clearcoatRoughness: REALISTIC_BOARD_SURFACE_MATERIAL.clearcoatRoughness,
39132
39246
  envMapIntensity: 0.18
39133
39247
  });
39134
- const mesh = new THREE38.Mesh(planeGeom, material);
39248
+ const mesh = new THREE41.Mesh(planeGeom, material);
39135
39249
  mesh.position.set(
39136
39250
  boardOutlineBounds.centerX,
39137
39251
  boardOutlineBounds.centerY,
@@ -39179,7 +39293,7 @@ function createTextureMeshes(textures, boardData, pcbThickness, isFaux = false)
39179
39293
  }
39180
39294
 
39181
39295
  // src/three-components/JscadBoardTextures.tsx
39182
- import * as THREE39 from "three";
39296
+ import * as THREE42 from "three";
39183
39297
 
39184
39298
  // src/utils/layer-texture-resolution.ts
39185
39299
  var DEFAULT_MAX_TEXTURE_PIXELS = 4e6;
@@ -39234,7 +39348,8 @@ function JscadBoardTextures({
39234
39348
  height: panel.height,
39235
39349
  thickness: firstBoardInPanel?.thickness ?? 1.4,
39236
39350
  material: firstBoardInPanel?.material ?? "fr4",
39237
- num_layers: firstBoardInPanel?.num_layers ?? 2
39351
+ num_layers: firstBoardInPanel?.num_layers ?? 2,
39352
+ solder_mask_color: firstBoardInPanel?.solder_mask_color
39238
39353
  };
39239
39354
  }
39240
39355
  const boardsNotInPanel = boards.filter(
@@ -39275,7 +39390,7 @@ function JscadBoardTextures({
39275
39390
  const typedMaterial = material;
39276
39391
  for (const prop of textureProps) {
39277
39392
  const texture = typedMaterial[prop];
39278
- if (texture && texture instanceof THREE39.Texture) {
39393
+ if (texture && texture instanceof THREE42.Texture) {
39279
39394
  texture.dispose();
39280
39395
  typedMaterial[prop] = null;
39281
39396
  }
@@ -39294,16 +39409,16 @@ function JscadBoardTextures({
39294
39409
  }) => {
39295
39410
  if (!texture) return null;
39296
39411
  const boardOutlineBounds = calculateOutlineBounds(boardData);
39297
- const planeGeom = new THREE39.PlaneGeometry(
39412
+ const planeGeom = new THREE42.PlaneGeometry(
39298
39413
  boardOutlineBounds.width,
39299
39414
  boardOutlineBounds.height
39300
39415
  );
39301
- texture.colorSpace = THREE39.SRGBColorSpace;
39416
+ texture.colorSpace = THREE42.SRGBColorSpace;
39302
39417
  const sharedMaterialOptions = {
39303
39418
  map: texture,
39304
39419
  transparent: true,
39305
39420
  alphaTest: 0.08,
39306
- side: THREE39.FrontSide,
39421
+ side: THREE42.FrontSide,
39307
39422
  depthWrite,
39308
39423
  polygonOffset: usePolygonOffset,
39309
39424
  polygonOffsetFactor: usePolygonOffset ? -4 : 0,
@@ -39314,12 +39429,12 @@ function JscadBoardTextures({
39314
39429
  texture,
39315
39430
  maskedCopperMask
39316
39431
  );
39317
- const material = new THREE39.MeshPhysicalMaterial({
39432
+ const material = new THREE42.MeshPhysicalMaterial({
39318
39433
  ...sharedMaterialOptions,
39319
39434
  bumpMap: reliefTextures?.bumpMap ?? null,
39320
39435
  bumpScale: REALISTIC_BOARD_SURFACE_MATERIAL.bumpScale,
39321
39436
  normalMap: reliefTextures?.normalMap ?? null,
39322
- normalScale: new THREE39.Vector2(
39437
+ normalScale: new THREE42.Vector2(
39323
39438
  REALISTIC_BOARD_SURFACE_MATERIAL.normalScale,
39324
39439
  REALISTIC_BOARD_SURFACE_MATERIAL.normalScale
39325
39440
  ),
@@ -39331,7 +39446,7 @@ function JscadBoardTextures({
39331
39446
  clearcoatRoughness: REALISTIC_BOARD_SURFACE_MATERIAL.clearcoatRoughness,
39332
39447
  envMapIntensity: 0.18
39333
39448
  });
39334
- const mesh = new THREE39.Mesh(planeGeom, material);
39449
+ const mesh = new THREE42.Mesh(planeGeom, material);
39335
39450
  mesh.position.set(
39336
39451
  boardOutlineBounds.centerX,
39337
39452
  boardOutlineBounds.centerY,
@@ -39381,7 +39496,7 @@ function JscadBoardTextures({
39381
39496
  for (const material of mesh.material) {
39382
39497
  disposeTextureMaterial(material);
39383
39498
  }
39384
- } else if (mesh.material instanceof THREE39.Material) {
39499
+ } else if (mesh.material instanceof THREE42.Material) {
39385
39500
  disposeTextureMaterial(mesh.material);
39386
39501
  }
39387
39502
  }
@@ -39394,7 +39509,7 @@ function JscadBoardTextures({
39394
39509
 
39395
39510
  // src/three-components/STLModel.tsx
39396
39511
  import { useState as useState15, useEffect as useEffect26, useMemo as useMemo21 } from "react";
39397
- import * as THREE40 from "three";
39512
+ import * as THREE43 from "three";
39398
39513
  import { STLLoader } from "three-stdlib";
39399
39514
  function STLModel({
39400
39515
  stlUrl,
@@ -39427,15 +39542,15 @@ function STLModel({
39427
39542
  const mesh = useMemo21(() => {
39428
39543
  if (!geom) return null;
39429
39544
  const isBoardLayer = layerType === "board";
39430
- const material = new THREE40.MeshStandardMaterial({
39431
- color: Array.isArray(color) ? new THREE40.Color(color[0], color[1], color[2]) : color,
39545
+ const material = new THREE43.MeshStandardMaterial({
39546
+ color: Array.isArray(color) ? new THREE43.Color(color[0], color[1], color[2]) : color,
39432
39547
  transparent: opacity !== 1,
39433
39548
  opacity,
39434
39549
  polygonOffset: isBoardLayer,
39435
39550
  polygonOffsetFactor: isBoardLayer ? 6 : 0,
39436
39551
  polygonOffsetUnits: isBoardLayer ? 6 : 0
39437
39552
  });
39438
- const createdMesh = new THREE40.Mesh(geom, material);
39553
+ const createdMesh = new THREE43.Mesh(geom, material);
39439
39554
  createdMesh.renderOrder = isBoardLayer ? -1 : 1;
39440
39555
  configureObjectShadows(createdMesh, {
39441
39556
  castShadow: !isBoardLayer && opacity === 1,
@@ -39720,12 +39835,12 @@ var CadViewerJscad = forwardRef3(
39720
39835
  // src/CadViewerManifold.tsx
39721
39836
  import { su as su17 } from "@tscircuit/circuit-json-util";
39722
39837
  import { useEffect as useEffect28, useMemo as useMemo24, useState as useState17 } from "react";
39723
- import * as THREE47 from "three";
39838
+ import * as THREE50 from "three";
39724
39839
 
39725
39840
  // src/hooks/useManifoldBoardBuilder.ts
39726
39841
  import { su as su16 } from "@tscircuit/circuit-json-util";
39727
39842
  import { useEffect as useEffect27, useMemo as useMemo23, useRef as useRef9, useState as useState16 } from "react";
39728
- import * as THREE44 from "three";
39843
+ import * as THREE47 from "three";
39729
39844
 
39730
39845
  // src/utils/manifold/create-manifold-board.ts
39731
39846
  var arePointsClockwise2 = (points) => {
@@ -40074,17 +40189,17 @@ function processNonPlatedHolesForManifold(Manifold, CrossSection, circuitJson, p
40074
40189
 
40075
40190
  // src/utils/manifold/process-plated-holes.ts
40076
40191
  import { su as su14 } from "@tscircuit/circuit-json-util";
40077
- import * as THREE42 from "three";
40192
+ import * as THREE45 from "three";
40078
40193
 
40079
40194
  // src/utils/manifold-mesh-to-three-geometry.ts
40080
- import * as THREE41 from "three";
40195
+ import * as THREE44 from "three";
40081
40196
  function manifoldMeshToThreeGeometry(manifoldMesh) {
40082
- const geometry = new THREE41.BufferGeometry();
40197
+ const geometry = new THREE44.BufferGeometry();
40083
40198
  geometry.setAttribute(
40084
40199
  "position",
40085
- new THREE41.Float32BufferAttribute(manifoldMesh.vertProperties, 3)
40200
+ new THREE44.Float32BufferAttribute(manifoldMesh.vertProperties, 3)
40086
40201
  );
40087
- geometry.setIndex(new THREE41.Uint32BufferAttribute(manifoldMesh.triVerts, 1));
40202
+ geometry.setIndex(new THREE44.Uint32BufferAttribute(manifoldMesh.triVerts, 1));
40088
40203
  if (manifoldMesh.runIndex && manifoldMesh.runIndex.length > 1 && manifoldMesh.runOriginalID) {
40089
40204
  for (let i = 0; i < manifoldMesh.runIndex.length - 1; i++) {
40090
40205
  const start = manifoldMesh.runIndex[i];
@@ -40118,7 +40233,7 @@ var createEllipsePoints = (width10, height10, segments) => {
40118
40233
  }
40119
40234
  return points;
40120
40235
  };
40121
- var COPPER_COLOR = new THREE42.Color(...colors.copper);
40236
+ var COPPER_COLOR = new THREE45.Color(...colors.copper);
40122
40237
  var PLATED_HOLE_LIP_HEIGHT = 0.05;
40123
40238
  var PLATED_HOLE_PAD_THICKNESS = 3e-3;
40124
40239
  var PLATED_HOLE_SURFACE_CLEARANCE = 5e-4;
@@ -40749,7 +40864,7 @@ function processPlatedHolesForManifold(Manifold, CrossSection, circuitJson, pcbT
40749
40864
 
40750
40865
  // src/utils/manifold/process-vias.ts
40751
40866
  import { su as su15 } from "@tscircuit/circuit-json-util";
40752
- import * as THREE43 from "three";
40867
+ import * as THREE46 from "three";
40753
40868
 
40754
40869
  // src/utils/via-geoms.ts
40755
40870
  function createViaCopper2({
@@ -40787,7 +40902,7 @@ function createViaCopper2({
40787
40902
  }
40788
40903
 
40789
40904
  // src/utils/manifold/process-vias.ts
40790
- var COPPER_COLOR2 = new THREE43.Color(...colors.copper);
40905
+ var COPPER_COLOR2 = new THREE46.Color(...colors.copper);
40791
40906
  function processViasForManifold(Manifold, circuitJson, pcbThickness, manifoldInstancesForCleanup, boardClipVolume) {
40792
40907
  const viaBoardDrills = [];
40793
40908
  const pcbVias = su15(circuitJson).pcb_via.list();
@@ -40863,7 +40978,8 @@ var useManifoldBoardBuilder = (manifoldJSModule, circuitJson, visibility) => {
40863
40978
  height: panel.height,
40864
40979
  thickness: firstBoardInPanel?.thickness ?? 1.4,
40865
40980
  material: firstBoardInPanel?.material ?? "fr4",
40866
- num_layers: firstBoardInPanel?.num_layers ?? 2
40981
+ num_layers: firstBoardInPanel?.num_layers ?? 2,
40982
+ solder_mask_color: firstBoardInPanel?.solder_mask_color
40867
40983
  };
40868
40984
  }
40869
40985
  const boardsNotInPanel = boards.filter((b) => !b.pcb_panel_id);
@@ -41007,7 +41123,7 @@ var useManifoldBoardBuilder = (manifoldJSModule, circuitJson, visibility) => {
41007
41123
  {
41008
41124
  key: "plated-holes-union",
41009
41125
  geometry: cutPlatedGeom,
41010
- color: new THREE44.Color(
41126
+ color: new THREE47.Color(
41011
41127
  colors.copper[0],
41012
41128
  colors.copper[1],
41013
41129
  colors.copper[2]
@@ -41034,14 +41150,9 @@ var useManifoldBoardBuilder = (manifoldJSModule, circuitJson, visibility) => {
41034
41150
  if (boardManifold) {
41035
41151
  const boardThreeMesh = boardManifold.getMesh();
41036
41152
  const finalBoardGeom = manifoldMeshToThreeGeometry(boardThreeMesh);
41037
- const matColorArray = boardMaterialColors[boardData.material] ?? colors.fr4Tan;
41038
41153
  currentGeoms.board = {
41039
41154
  geometry: finalBoardGeom,
41040
- color: new THREE44.Color(
41041
- matColorArray[0],
41042
- matColorArray[1],
41043
- matColorArray[2]
41044
- ),
41155
+ color: getBoardEdgeColor(boardData),
41045
41156
  material: boardData.material,
41046
41157
  isFaux: isFauxBoard
41047
41158
  };
@@ -41082,11 +41193,11 @@ var useManifoldBoardBuilder = (manifoldJSModule, circuitJson, visibility) => {
41082
41193
  };
41083
41194
 
41084
41195
  // src/utils/manifold/create-three-geometry-meshes.ts
41085
- import * as THREE46 from "three";
41196
+ import * as THREE49 from "three";
41086
41197
 
41087
41198
  // src/utils/create-board-material.ts
41088
- import * as THREE45 from "three";
41089
- var DEFAULT_SIDE = THREE45.DoubleSide;
41199
+ import * as THREE48 from "three";
41200
+ var DEFAULT_SIDE = THREE48.DoubleSide;
41090
41201
  var createBoardMaterial = ({
41091
41202
  material,
41092
41203
  color,
@@ -41094,10 +41205,8 @@ var createBoardMaterial = ({
41094
41205
  isFaux = false
41095
41206
  }) => {
41096
41207
  if (material === "fr4") {
41097
- return new THREE45.MeshPhysicalMaterial({
41098
- // A dark edge lets the green solder mask read as a finished PCB rather
41099
- // than a tan substrate with a decal placed on top.
41100
- color: 1063462,
41208
+ return new THREE48.MeshPhysicalMaterial({
41209
+ color,
41101
41210
  side,
41102
41211
  metalness: 0,
41103
41212
  roughness: 0.48,
@@ -41114,7 +41223,7 @@ var createBoardMaterial = ({
41114
41223
  polygonOffsetUnits: 1
41115
41224
  });
41116
41225
  }
41117
- return new THREE45.MeshStandardMaterial({
41226
+ return new THREE48.MeshStandardMaterial({
41118
41227
  color,
41119
41228
  side,
41120
41229
  flatShading: true,
@@ -41133,12 +41242,12 @@ function createGeometryMeshes(geoms) {
41133
41242
  const meshes = [];
41134
41243
  if (!geoms) return meshes;
41135
41244
  if (geoms.board?.geometry) {
41136
- const mesh = new THREE46.Mesh(
41245
+ const mesh = new THREE49.Mesh(
41137
41246
  geoms.board.geometry,
41138
41247
  createBoardMaterial({
41139
41248
  material: geoms.board.material,
41140
41249
  color: geoms.board.color,
41141
- side: THREE46.DoubleSide,
41250
+ side: THREE49.DoubleSide,
41142
41251
  isFaux: geoms.board.isFaux
41143
41252
  })
41144
41253
  );
@@ -41149,11 +41258,11 @@ function createGeometryMeshes(geoms) {
41149
41258
  const createMeshesFromArray = (geomArray) => {
41150
41259
  if (geomArray) {
41151
41260
  for (const comp of geomArray) {
41152
- const mesh = new THREE46.Mesh(
41261
+ const mesh = new THREE49.Mesh(
41153
41262
  comp.geometry,
41154
- new THREE46.MeshStandardMaterial({
41263
+ new THREE49.MeshStandardMaterial({
41155
41264
  color: comp.color,
41156
- side: THREE46.DoubleSide,
41265
+ side: THREE49.DoubleSide,
41157
41266
  flatShading: true
41158
41267
  // Consistent with board
41159
41268
  })
@@ -41198,7 +41307,7 @@ var BoardMeshes = ({
41198
41307
  const typedMaterial = material;
41199
41308
  for (const prop of textureProps) {
41200
41309
  const texture = typedMaterial[prop];
41201
- if (texture && texture instanceof THREE47.Texture) {
41310
+ if (texture && texture instanceof THREE50.Texture) {
41202
41311
  texture.dispose();
41203
41312
  }
41204
41313
  }
@@ -49131,7 +49240,7 @@ var useGlobalDownloadGltf = () => {
49131
49240
 
49132
49241
  // src/CadViewer.tsx
49133
49242
  import { jsx as jsx37, jsxs as jsxs11 } from "react/jsx-runtime";
49134
- var DEFAULT_TARGET = new THREE48.Vector3(0, 0, 0);
49243
+ var DEFAULT_TARGET = new THREE51.Vector3(0, 0, 0);
49135
49244
  var INITIAL_CAMERA_POSITION = [5, -5, 5];
49136
49245
  var readStoredCameraType = () => {
49137
49246
  if (typeof window === "undefined") return void 0;
@@ -49411,11 +49520,11 @@ var CadViewer = (props) => {
49411
49520
  // src/convert-circuit-json-to-3d-svg.ts
49412
49521
  var import_debug = __toESM(require_browser(), 1);
49413
49522
  import { su as su18 } from "@tscircuit/circuit-json-util";
49414
- import * as THREE52 from "three";
49523
+ import * as THREE55 from "three";
49415
49524
  import { SVGRenderer } from "three/examples/jsm/renderers/SVGRenderer.js";
49416
49525
 
49417
49526
  // src/utils/create-geometry-from-polygons.ts
49418
- import * as THREE49 from "three";
49527
+ import * as THREE52 from "three";
49419
49528
  import { BufferGeometry as BufferGeometry5, Float32BufferAttribute as Float32BufferAttribute4 } from "three";
49420
49529
  function createGeometryFromPolygons(polygons) {
49421
49530
  const geometry = new BufferGeometry5();
@@ -49429,12 +49538,12 @@ function createGeometryFromPolygons(polygons) {
49429
49538
  ...polygon2.vertices[i + 1]
49430
49539
  // Third vertex
49431
49540
  );
49432
- const v1 = new THREE49.Vector3(...polygon2.vertices[0]);
49433
- const v2 = new THREE49.Vector3(...polygon2.vertices[i]);
49434
- const v3 = new THREE49.Vector3(...polygon2.vertices[i + 1]);
49435
- const normal = new THREE49.Vector3().crossVectors(
49436
- new THREE49.Vector3().subVectors(v2, v1),
49437
- new THREE49.Vector3().subVectors(v3, v1)
49541
+ const v1 = new THREE52.Vector3(...polygon2.vertices[0]);
49542
+ const v2 = new THREE52.Vector3(...polygon2.vertices[i]);
49543
+ const v3 = new THREE52.Vector3(...polygon2.vertices[i + 1]);
49544
+ const normal = new THREE52.Vector3().crossVectors(
49545
+ new THREE52.Vector3().subVectors(v2, v1),
49546
+ new THREE52.Vector3().subVectors(v3, v1)
49438
49547
  ).normalize();
49439
49548
  normals.push(
49440
49549
  normal.x,
@@ -49458,10 +49567,10 @@ function createGeometryFromPolygons(polygons) {
49458
49567
  var import_modeling2 = __toESM(require_src(), 1);
49459
49568
  var import_jscad_planner2 = __toESM(require_dist(), 1);
49460
49569
  var jscadModeling2 = __toESM(require_src(), 1);
49461
- import * as THREE51 from "three";
49570
+ import * as THREE54 from "three";
49462
49571
 
49463
49572
  // src/utils/load-model.ts
49464
- import * as THREE50 from "three";
49573
+ import * as THREE53 from "three";
49465
49574
  import { GLTFLoader as GLTFLoader2 } from "three/examples/jsm/loaders/GLTFLoader.js";
49466
49575
  import { OBJLoader as OBJLoader2 } from "three/examples/jsm/loaders/OBJLoader.js";
49467
49576
  import { STLLoader as STLLoader2 } from "three/examples/jsm/loaders/STLLoader.js";
@@ -49469,12 +49578,12 @@ async function load3DModel(url) {
49469
49578
  if (url.endsWith(".stl")) {
49470
49579
  const loader = new STLLoader2();
49471
49580
  const geometry = await loader.loadAsync(url);
49472
- const material = new THREE50.MeshStandardMaterial({
49581
+ const material = new THREE53.MeshStandardMaterial({
49473
49582
  color: 8947848,
49474
49583
  metalness: 0.5,
49475
49584
  roughness: 0.5
49476
49585
  });
49477
- return new THREE50.Mesh(geometry, material);
49586
+ return new THREE53.Mesh(geometry, material);
49478
49587
  }
49479
49588
  if (url.endsWith(".obj")) {
49480
49589
  const loader = new OBJLoader2();
@@ -49507,9 +49616,9 @@ async function renderComponent(component, scene) {
49507
49616
  }
49508
49617
  if (component.rotation) {
49509
49618
  model.rotation.set(
49510
- THREE51.MathUtils.degToRad(component.rotation.x ?? 0),
49511
- THREE51.MathUtils.degToRad(component.rotation.y ?? 0),
49512
- THREE51.MathUtils.degToRad(component.rotation.z ?? 0)
49619
+ THREE54.MathUtils.degToRad(component.rotation.x ?? 0),
49620
+ THREE54.MathUtils.degToRad(component.rotation.y ?? 0),
49621
+ THREE54.MathUtils.degToRad(component.rotation.z ?? 0)
49513
49622
  );
49514
49623
  }
49515
49624
  scene.add(model);
@@ -49523,13 +49632,13 @@ async function renderComponent(component, scene) {
49523
49632
  );
49524
49633
  if (jscadObject && (jscadObject.polygons || jscadObject.sides)) {
49525
49634
  const threeGeom = convertCSGToThreeGeom(jscadObject);
49526
- const material2 = new THREE51.MeshStandardMaterial({
49635
+ const material2 = new THREE54.MeshStandardMaterial({
49527
49636
  color: 8947848,
49528
49637
  metalness: 0.5,
49529
49638
  roughness: 0.5,
49530
- side: THREE51.DoubleSide
49639
+ side: THREE54.DoubleSide
49531
49640
  });
49532
- const mesh2 = new THREE51.Mesh(threeGeom, material2);
49641
+ const mesh2 = new THREE54.Mesh(threeGeom, material2);
49533
49642
  if (component.position) {
49534
49643
  mesh2.position.set(
49535
49644
  component.position.x ?? 0,
@@ -49539,9 +49648,9 @@ async function renderComponent(component, scene) {
49539
49648
  }
49540
49649
  if (component.rotation) {
49541
49650
  mesh2.rotation.set(
49542
- THREE51.MathUtils.degToRad(component.rotation.x ?? 0),
49543
- THREE51.MathUtils.degToRad(component.rotation.y ?? 0),
49544
- THREE51.MathUtils.degToRad(component.rotation.z ?? 0)
49651
+ THREE54.MathUtils.degToRad(component.rotation.x ?? 0),
49652
+ THREE54.MathUtils.degToRad(component.rotation.y ?? 0),
49653
+ THREE54.MathUtils.degToRad(component.rotation.z ?? 0)
49545
49654
  );
49546
49655
  }
49547
49656
  scene.add(mesh2);
@@ -49558,17 +49667,17 @@ async function renderComponent(component, scene) {
49558
49667
  if (!geom || !geom.polygons && !geom.sides) {
49559
49668
  continue;
49560
49669
  }
49561
- const color = new THREE51.Color(geomInfo.color);
49670
+ const color = new THREE54.Color(geomInfo.color);
49562
49671
  color.convertLinearToSRGB();
49563
49672
  const geomWithColor = { ...geom, color: [color.r, color.g, color.b] };
49564
49673
  const threeGeom = convertCSGToThreeGeom(geomWithColor);
49565
- const material2 = new THREE51.MeshStandardMaterial({
49674
+ const material2 = new THREE54.MeshStandardMaterial({
49566
49675
  vertexColors: true,
49567
49676
  metalness: 0.2,
49568
49677
  roughness: 0.8,
49569
- side: THREE51.DoubleSide
49678
+ side: THREE54.DoubleSide
49570
49679
  });
49571
- const mesh2 = new THREE51.Mesh(threeGeom, material2);
49680
+ const mesh2 = new THREE54.Mesh(threeGeom, material2);
49572
49681
  if (component.position) {
49573
49682
  mesh2.position.set(
49574
49683
  component.position.x ?? 0,
@@ -49578,22 +49687,22 @@ async function renderComponent(component, scene) {
49578
49687
  }
49579
49688
  if (component.rotation) {
49580
49689
  mesh2.rotation.set(
49581
- THREE51.MathUtils.degToRad(component.rotation.x ?? 0),
49582
- THREE51.MathUtils.degToRad(component.rotation.y ?? 0),
49583
- THREE51.MathUtils.degToRad(component.rotation.z ?? 0)
49690
+ THREE54.MathUtils.degToRad(component.rotation.x ?? 0),
49691
+ THREE54.MathUtils.degToRad(component.rotation.y ?? 0),
49692
+ THREE54.MathUtils.degToRad(component.rotation.z ?? 0)
49584
49693
  );
49585
49694
  }
49586
49695
  scene.add(mesh2);
49587
49696
  }
49588
49697
  return;
49589
49698
  }
49590
- const geometry = new THREE51.BoxGeometry(0.5, 0.5, 0.5);
49591
- const material = new THREE51.MeshStandardMaterial({
49699
+ const geometry = new THREE54.BoxGeometry(0.5, 0.5, 0.5);
49700
+ const material = new THREE54.MeshStandardMaterial({
49592
49701
  color: 16711680,
49593
49702
  transparent: true,
49594
49703
  opacity: 0.25
49595
49704
  });
49596
- const mesh = new THREE51.Mesh(geometry, material);
49705
+ const mesh = new THREE54.Mesh(geometry, material);
49597
49706
  if (component.position) {
49598
49707
  mesh.position.set(
49599
49708
  component.position.x ?? 0,
@@ -49614,11 +49723,11 @@ async function convertCircuitJsonTo3dSvg(circuitJson, options = {}) {
49614
49723
  padding = 20,
49615
49724
  zoom = 1.5
49616
49725
  } = options;
49617
- const scene = new THREE52.Scene();
49726
+ const scene = new THREE55.Scene();
49618
49727
  const renderer = new SVGRenderer();
49619
49728
  renderer.setSize(width10, height10);
49620
- renderer.setClearColor(new THREE52.Color(backgroundColor), 1);
49621
- const camera = new THREE52.OrthographicCamera();
49729
+ renderer.setClearColor(new THREE55.Color(backgroundColor), 1);
49730
+ const camera = new THREE55.OrthographicCamera();
49622
49731
  const aspect = width10 / height10;
49623
49732
  const frustumSize = 100;
49624
49733
  const halfFrustumSize = frustumSize / 2 / zoom;
@@ -49632,55 +49741,57 @@ async function convertCircuitJsonTo3dSvg(circuitJson, options = {}) {
49632
49741
  camera.position.set(position.x, position.y, position.z);
49633
49742
  camera.up.set(0, 1, 0);
49634
49743
  const lookAt = options.camera?.lookAt ?? { x: 0, y: 0, z: 0 };
49635
- camera.lookAt(new THREE52.Vector3(lookAt.x, lookAt.y, lookAt.z));
49744
+ camera.lookAt(new THREE55.Vector3(lookAt.x, lookAt.y, lookAt.z));
49636
49745
  camera.updateProjectionMatrix();
49637
- const ambientLight = new THREE52.AmbientLight(16777215, Math.PI / 2);
49746
+ const ambientLight = new THREE55.AmbientLight(16777215, Math.PI / 2);
49638
49747
  scene.add(ambientLight);
49639
- const pointLight = new THREE52.PointLight(16777215, Math.PI / 4);
49748
+ const pointLight = new THREE55.PointLight(16777215, Math.PI / 4);
49640
49749
  pointLight.position.set(-10, -10, 10);
49641
49750
  scene.add(pointLight);
49642
49751
  const components = su18(circuitJson).cad_component.list();
49643
49752
  for (const component of components) {
49644
49753
  await renderComponent(component, scene);
49645
49754
  }
49646
- const boardData = su18(circuitJson).pcb_board.list()[0];
49755
+ const boards = su18(circuitJson).pcb_board.list();
49756
+ const firstPanel = circuitJson.find(
49757
+ (circuitElement) => circuitElement.type === "pcb_panel"
49758
+ );
49759
+ const boardData = firstPanel ? boards.find((board) => board.pcb_panel_id === firstPanel.pcb_panel_id) : boards.find((board) => !board.pcb_panel_id);
49647
49760
  const boardGeom = createSimplifiedBoardGeom(circuitJson);
49648
49761
  if (boardGeom) {
49649
- const solderMaskColor = colors.fr4SolderMaskGreen;
49650
- const baseColor = new THREE52.Color(
49651
- solderMaskColor[0],
49652
- solderMaskColor[1],
49653
- solderMaskColor[2]
49654
- );
49762
+ const edgeColor = getBoardEdgeColor({
49763
+ material: boardData?.material ?? "fr4",
49764
+ solder_mask_color: boardData?.solder_mask_color
49765
+ });
49655
49766
  for (const geom of boardGeom) {
49656
49767
  const g = geom;
49657
49768
  if (!g.polygons || g.polygons.length === 0) continue;
49658
49769
  const geometry = createGeometryFromPolygons(g.polygons);
49659
49770
  const material = createBoardMaterial({
49660
49771
  material: boardData?.material,
49661
- color: baseColor,
49662
- side: THREE52.DoubleSide
49772
+ color: edgeColor,
49773
+ side: THREE55.DoubleSide
49663
49774
  });
49664
- const mesh = new THREE52.Mesh(geometry, material);
49775
+ const mesh = new THREE55.Mesh(geometry, material);
49665
49776
  scene.add(mesh);
49666
49777
  }
49667
49778
  }
49668
- const gridColor = new THREE52.Color(8947848);
49669
- const gridHelper = new THREE52.GridHelper(100, 100, gridColor, gridColor);
49779
+ const gridColor = new THREE55.Color(8947848);
49780
+ const gridHelper = new THREE55.GridHelper(100, 100, gridColor, gridColor);
49670
49781
  gridHelper.rotation.x = Math.PI / 2;
49671
49782
  const materials = Array.isArray(gridHelper.material) ? gridHelper.material : [gridHelper.material];
49672
49783
  for (const mat of materials) {
49673
49784
  mat.transparent = true;
49674
49785
  mat.opacity = 0.3;
49675
- if (mat instanceof THREE52.LineBasicMaterial) {
49786
+ if (mat instanceof THREE55.LineBasicMaterial) {
49676
49787
  mat.color = gridColor;
49677
49788
  mat.vertexColors = false;
49678
49789
  }
49679
49790
  }
49680
49791
  scene.add(gridHelper);
49681
- const box = new THREE52.Box3().setFromObject(scene);
49682
- const center = box.getCenter(new THREE52.Vector3());
49683
- const size4 = box.getSize(new THREE52.Vector3());
49792
+ const box = new THREE55.Box3().setFromObject(scene);
49793
+ const center = box.getCenter(new THREE55.Vector3());
49794
+ const size4 = box.getSize(new THREE55.Vector3());
49684
49795
  scene.position.sub(center);
49685
49796
  const maxDim = Math.max(size4.x, size4.y, size4.z);
49686
49797
  if (maxDim > 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tscircuit/3d-viewer",
3
- "version": "0.0.594",
3
+ "version": "0.0.596",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/tscircuit/3d-viewer"