@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.
- package/dist/index.js +93 -38
- 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.
|
|
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.
|
|
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 =
|
|
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
|
-
|
|
33118
|
+
16777215,
|
|
33115
33119
|
2.4,
|
|
33116
|
-
[
|
|
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
|
-
|
|
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 {
|
|
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 === "
|
|
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
|
-
|
|
45551
|
+
setLightingEnabled(!lightingEnabled);
|
|
45497
45552
|
},
|
|
45498
|
-
onMouseEnter: () => setHoveredItem("
|
|
45553
|
+
onMouseEnter: () => setHoveredItem("lighting"),
|
|
45499
45554
|
onMouseLeave: () => setHoveredItem(null),
|
|
45500
|
-
onTouchStart: () => setHoveredItem("
|
|
45555
|
+
onTouchStart: () => setHoveredItem("lighting"),
|
|
45501
45556
|
children: [
|
|
45502
|
-
/* @__PURE__ */ jsx35("span", { style: iconContainerStyles, children:
|
|
45503
|
-
/* @__PURE__ */ jsx35("span", { style: { display: "flex", alignItems: "center" }, children: "Lighting
|
|
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.
|
|
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.
|
|
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",
|