@tscircuit/3d-viewer 0.0.573 → 0.0.575

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 +93 -38
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -32429,7 +32429,7 @@ import * as THREE21 from "three";
32429
32429
  // package.json
32430
32430
  var package_default = {
32431
32431
  name: "@tscircuit/3d-viewer",
32432
- version: "0.0.572",
32432
+ version: "0.0.574",
32433
32433
  main: "./dist/index.js",
32434
32434
  module: "./dist/index.js",
32435
32435
  type: "module",
@@ -32459,7 +32459,7 @@ var package_default = {
32459
32459
  "@jscad/regl-renderer": "^2.6.12",
32460
32460
  "@jscad/stl-serializer": "^2.1.20",
32461
32461
  "circuit-json": "^0.0.421",
32462
- "circuit-to-canvas": "^0.0.110",
32462
+ "circuit-to-canvas": "^0.0.111",
32463
32463
  "react-hot-toast": "^2.6.0",
32464
32464
  three: "^0.165.0",
32465
32465
  "three-stdlib": "^2.36.0",
@@ -33076,6 +33076,7 @@ var Lights = ({
33076
33076
  );
33077
33077
  const shadowHalfSize = largestBoardDimension * 0.8;
33078
33078
  const lightDistance = largestBoardDimension;
33079
+ const keyLightDistance = largestBoardDimension * 8.3;
33079
33080
  const ambientLight = new THREE18.AmbientLight(16777215, 0.18);
33080
33081
  ambientLight.name = "cad-viewer-soft-ambient";
33081
33082
  rig.add(ambientLight);
@@ -33103,7 +33104,10 @@ var Lights = ({
33103
33104
  shadowCamera.top = shadowHalfSize;
33104
33105
  shadowCamera.bottom = -shadowHalfSize;
33105
33106
  shadowCamera.near = 0.5;
33106
- shadowCamera.far = largestBoardDimension * 4;
33107
+ shadowCamera.far = Math.max(
33108
+ largestBoardDimension * 4,
33109
+ Math.hypot(...position) + largestBoardDimension * 2
33110
+ );
33107
33111
  shadowCamera.updateProjectionMatrix();
33108
33112
  }
33109
33113
  rig.add(light);
@@ -33111,9 +33115,13 @@ var Lights = ({
33111
33115
  };
33112
33116
  addDirectionalLight(
33113
33117
  "cad-viewer-key-light",
33114
- 16773343,
33118
+ 16777215,
33115
33119
  2.4,
33116
- [lightDistance * 0.22, -lightDistance * 0.28, lightDistance * 1.15],
33120
+ [
33121
+ keyLightDistance * 0.22,
33122
+ -keyLightDistance * 0.28,
33123
+ keyLightDistance * 1.15
33124
+ ],
33117
33125
  shadowsEnabled
33118
33126
  );
33119
33127
  addDirectionalLight("cad-viewer-fill-light", 14543103, 0.7, [
@@ -33290,29 +33298,45 @@ import {
33290
33298
  } from "react";
33291
33299
  import { jsx as jsx13 } from "react/jsx-runtime";
33292
33300
  var STORAGE_KEY = "cadViewerRenderingMode";
33301
+ var LIGHTING_STORAGE_KEY = "cadViewerLightingEnabled";
33293
33302
  var readStoredRenderingMode = () => {
33294
33303
  if (typeof window === "undefined") return "engineering";
33295
33304
  const stored = window.localStorage.getItem(STORAGE_KEY);
33296
33305
  return stored === "realistic" || stored === "engineering" ? stored : "engineering";
33297
33306
  };
33307
+ var readStoredLightingEnabled = () => {
33308
+ if (typeof window === "undefined") return true;
33309
+ return window.localStorage.getItem(LIGHTING_STORAGE_KEY) !== "false";
33310
+ };
33298
33311
  var RenderingModeContext = createContext5(void 0);
33299
33312
  var RenderingModeProvider = ({ children }) => {
33300
33313
  const [renderingMode, setRenderingModeState] = useState10(
33301
33314
  readStoredRenderingMode
33302
33315
  );
33316
+ const [lightingEnabled, setLightingEnabled] = useState10(
33317
+ readStoredLightingEnabled
33318
+ );
33303
33319
  const setRenderingMode = useCallback7((mode) => {
33304
33320
  setRenderingModeState(mode);
33305
33321
  }, []);
33306
33322
  useEffect17(() => {
33307
33323
  window.localStorage.setItem(STORAGE_KEY, renderingMode);
33308
33324
  }, [renderingMode]);
33325
+ useEffect17(() => {
33326
+ window.localStorage.setItem(
33327
+ LIGHTING_STORAGE_KEY,
33328
+ lightingEnabled ? "true" : "false"
33329
+ );
33330
+ }, [lightingEnabled]);
33309
33331
  const value = useMemo14(
33310
33332
  () => ({
33311
33333
  renderingMode,
33312
33334
  setRenderingMode,
33313
- shadowsEnabled: renderingMode === "realistic"
33335
+ lightingEnabled,
33336
+ setLightingEnabled,
33337
+ shadowsEnabled: lightingEnabled && renderingMode === "realistic"
33314
33338
  }),
33315
- [renderingMode, setRenderingMode]
33339
+ [lightingEnabled, renderingMode, setRenderingMode]
33316
33340
  );
33317
33341
  return /* @__PURE__ */ jsx13(RenderingModeContext.Provider, { value, children });
33318
33342
  };
@@ -35444,6 +35468,7 @@ function createPadTextureForLayer({
35444
35468
  const holes = su5(circuitJson).pcb_hole.list();
35445
35469
  const platedHolesOnLayer = su5(circuitJson).pcb_plated_hole.list().filter((e) => !Array.isArray(e.layers) || e.layers.includes(layer));
35446
35470
  const viasOnLayer = su5(circuitJson).pcb_via.list().filter((e) => !Array.isArray(e.layers) || e.layers.includes(layer));
35471
+ const cutouts = su5(circuitJson).pcb_cutout.list();
35447
35472
  const drillElements = [...holes, ...platedHolesOnLayer, ...viasOnLayer];
35448
35473
  const pcbRenderLayer = layer === "top" ? "top_copper" : "bottom_copper";
35449
35474
  const boardOutlineBounds = calculateOutlineBounds(boardData);
@@ -35501,7 +35526,7 @@ function createPadTextureForLayer({
35501
35526
  minY: boardOutlineBounds.minY,
35502
35527
  maxY: boardOutlineBounds.maxY
35503
35528
  });
35504
- drawer.drawElements([...smtPadsOnLayer, ...drillElements], {
35529
+ drawer.drawElements([...smtPadsOnLayer, ...drillElements, ...cutouts], {
35505
35530
  layers: [pcbRenderLayer],
35506
35531
  drawSoldermask: false,
35507
35532
  drawSoldermaskTop: false,
@@ -36829,10 +36854,57 @@ var toRgb3 = (colorArr) => {
36829
36854
  b * 255
36830
36855
  )})`;
36831
36856
  };
36857
+ var clampChannel = (value) => Math.max(0, Math.min(255, value));
36858
+ var applySoldermaskSurfaceFilter = (ctx, width10, height10, options = {}) => {
36859
+ const imageData = ctx.getImageData(0, 0, width10, height10);
36860
+ const data = imageData.data;
36861
+ const maxX = Math.max(width10 - 1, 1);
36862
+ const maxY = Math.max(height10 - 1, 1);
36863
+ for (let i = 0; i < data.length; i += 4) {
36864
+ const alpha = data[i + 3] ?? 0;
36865
+ if (alpha < 16) continue;
36866
+ const r = data[i] ?? 0;
36867
+ const g = data[i + 1] ?? 0;
36868
+ const b = data[i + 2] ?? 0;
36869
+ const luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b;
36870
+ const isDarkGreen = g > r * 1.35 && g > b * 1.15 && luminance < 90 && r < 80 && b < 80;
36871
+ if (!isDarkGreen) continue;
36872
+ const pixelIndex = i / 4;
36873
+ const x = pixelIndex % width10;
36874
+ const y = Math.floor(pixelIndex / width10);
36875
+ const u = x / maxX;
36876
+ const v = y / maxY;
36877
+ const diagonalLight = u * 0.62 + (1 - v) * 0.38;
36878
+ const broadVariation = Math.sin((u * 1.15 + v * 0.35) * Math.PI) * 0.04;
36879
+ const lightFactor = 0.74 + diagonalLight * 0.18 + broadVariation;
36880
+ const whiteReflection = options.includeReflection ? (() => {
36881
+ const reflectionX = 0.36;
36882
+ const reflectionY = 0.22;
36883
+ const dx = u - reflectionX;
36884
+ const dy = v - reflectionY;
36885
+ const radialFalloff = Math.max(0, 1 - Math.hypot(dx, dy) / 0.38);
36886
+ return radialFalloff * radialFalloff * 0.07;
36887
+ })() : 0;
36888
+ const filteredR = (r * 0.9 + 3) * lightFactor;
36889
+ const filteredG = (g * 1.08 + 15) * lightFactor;
36890
+ const filteredB = (b * 1.28 + 13) * lightFactor;
36891
+ data[i] = clampChannel(
36892
+ filteredR * (1 - whiteReflection) + 255 * whiteReflection
36893
+ );
36894
+ data[i + 1] = clampChannel(
36895
+ filteredG * (1 - whiteReflection) + 255 * whiteReflection
36896
+ );
36897
+ data[i + 2] = clampChannel(
36898
+ filteredB * (1 - whiteReflection) + 255 * whiteReflection
36899
+ );
36900
+ }
36901
+ ctx.putImageData(imageData, 0, 0);
36902
+ };
36832
36903
  var createCombinedTexture = ({
36833
36904
  textures,
36834
36905
  boardData,
36835
- traceTextureResolution
36906
+ traceTextureResolution,
36907
+ layer
36836
36908
  }) => {
36837
36909
  const hasImage = textures.some((texture) => texture?.image);
36838
36910
  if (!hasImage) return null;
@@ -36854,6 +36926,9 @@ var createCombinedTexture = ({
36854
36926
  const image = texture.image;
36855
36927
  ctx.drawImage(image, 0, 0, canvasWidth, canvasHeight);
36856
36928
  });
36929
+ applySoldermaskSurfaceFilter(ctx, canvasWidth, canvasHeight, {
36930
+ includeReflection: layer === "top"
36931
+ });
36857
36932
  const combinedTexture = new THREE35.CanvasTexture(canvas);
36858
36933
  combinedTexture.generateMipmaps = false;
36859
36934
  combinedTexture.minFilter = THREE35.LinearFilter;
@@ -36966,7 +37041,8 @@ function createCombinedBoardTextures({
36966
37041
  keepoutTexture
36967
37042
  ],
36968
37043
  boardData,
36969
- traceTextureResolution
37044
+ traceTextureResolution,
37045
+ layer
36970
37046
  });
36971
37047
  };
36972
37048
  const numLayers = boardData.num_layers ?? 2;
@@ -45421,7 +45497,7 @@ var iconContainerStyles = {
45421
45497
  };
45422
45498
  var AppearanceMenu = () => {
45423
45499
  const { visibility, setLayerVisibility } = useLayerVisibility();
45424
- const { renderingMode, setRenderingMode } = useRenderingMode();
45500
+ const { lightingEnabled, setLightingEnabled } = useRenderingMode();
45425
45501
  const [appearanceSubOpen, setAppearanceSubOpen] = useState35(false);
45426
45502
  const [hoveredItem, setHoveredItem] = useState35(null);
45427
45503
  return /* @__PURE__ */ jsxs8(Fragment11, { children: [
@@ -45467,40 +45543,19 @@ var AppearanceMenu = () => {
45467
45543
  {
45468
45544
  style: {
45469
45545
  ...itemStyles,
45470
- backgroundColor: hoveredItem === "engineeringMode" ? "#404040" : "transparent"
45471
- },
45472
- onSelect: (e) => e.preventDefault(),
45473
- onPointerDown: (e) => {
45474
- e.preventDefault();
45475
- setRenderingMode("engineering");
45476
- },
45477
- onMouseEnter: () => setHoveredItem("engineeringMode"),
45478
- onMouseLeave: () => setHoveredItem(null),
45479
- onTouchStart: () => setHoveredItem("engineeringMode"),
45480
- children: [
45481
- /* @__PURE__ */ jsx35("span", { style: iconContainerStyles, children: renderingMode === "engineering" && /* @__PURE__ */ jsx35(CheckIcon, {}) }),
45482
- /* @__PURE__ */ jsx35("span", { style: { display: "flex", alignItems: "center" }, children: "Engineering Mode" })
45483
- ]
45484
- }
45485
- ),
45486
- /* @__PURE__ */ jsxs8(
45487
- Item22,
45488
- {
45489
- style: {
45490
- ...itemStyles,
45491
- backgroundColor: hoveredItem === "realisticMode" ? "#404040" : "transparent"
45546
+ backgroundColor: hoveredItem === "lighting" ? "#404040" : "transparent"
45492
45547
  },
45493
45548
  onSelect: (e) => e.preventDefault(),
45494
45549
  onPointerDown: (e) => {
45495
45550
  e.preventDefault();
45496
- setRenderingMode("realistic");
45551
+ setLightingEnabled(!lightingEnabled);
45497
45552
  },
45498
- onMouseEnter: () => setHoveredItem("realisticMode"),
45553
+ onMouseEnter: () => setHoveredItem("lighting"),
45499
45554
  onMouseLeave: () => setHoveredItem(null),
45500
- onTouchStart: () => setHoveredItem("realisticMode"),
45555
+ onTouchStart: () => setHoveredItem("lighting"),
45501
45556
  children: [
45502
- /* @__PURE__ */ jsx35("span", { style: iconContainerStyles, children: renderingMode === "realistic" && /* @__PURE__ */ jsx35(CheckIcon, {}) }),
45503
- /* @__PURE__ */ jsx35("span", { style: { display: "flex", alignItems: "center" }, children: "Lighting Mode" })
45557
+ /* @__PURE__ */ jsx35("span", { style: iconContainerStyles, children: lightingEnabled && /* @__PURE__ */ jsx35(CheckIcon, {}) }),
45558
+ /* @__PURE__ */ jsx35("span", { style: { display: "flex", alignItems: "center" }, children: "Lighting" })
45504
45559
  ]
45505
45560
  }
45506
45561
  ),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tscircuit/3d-viewer",
3
- "version": "0.0.573",
3
+ "version": "0.0.575",
4
4
  "main": "./dist/index.js",
5
5
  "module": "./dist/index.js",
6
6
  "type": "module",
@@ -30,7 +30,7 @@
30
30
  "@jscad/regl-renderer": "^2.6.12",
31
31
  "@jscad/stl-serializer": "^2.1.20",
32
32
  "circuit-json": "^0.0.421",
33
- "circuit-to-canvas": "^0.0.110",
33
+ "circuit-to-canvas": "^0.0.111",
34
34
  "react-hot-toast": "^2.6.0",
35
35
  "three": "^0.165.0",
36
36
  "three-stdlib": "^2.36.0",