@3driseai/3drise-viewer 0.1.16 → 0.1.17
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/README.md +4 -3
- package/dist/components/ChladniGrid.d.ts +15 -0
- package/dist/components/ChladniGrid.d.ts.map +1 -0
- package/dist/components/ChladniGrid.js +62 -0
- package/dist/components/CustomPrimitive.d.ts +2 -0
- package/dist/components/CustomPrimitive.d.ts.map +1 -1
- package/dist/components/CustomPrimitive.js +2 -2
- package/dist/components/EffectsGenerator.d.ts.map +1 -1
- package/dist/components/EffectsGenerator.js +26 -2
- package/dist/components/FlowNetGrid.d.ts +15 -0
- package/dist/components/FlowNetGrid.d.ts.map +1 -0
- package/dist/components/FlowNetGrid.js +61 -0
- package/dist/components/GirihGrid.d.ts +15 -0
- package/dist/components/GirihGrid.d.ts.map +1 -0
- package/dist/components/GirihGrid.js +62 -0
- package/dist/components/GravWaveGrid.d.ts +15 -0
- package/dist/components/GravWaveGrid.d.ts.map +1 -0
- package/dist/components/GravWaveGrid.js +62 -0
- package/dist/components/GridsGenerator.d.ts +2 -2
- package/dist/components/GridsGenerator.d.ts.map +1 -1
- package/dist/components/GridsGenerator.js +21 -0
- package/dist/components/HyperbolicGrid.d.ts +15 -0
- package/dist/components/HyperbolicGrid.d.ts.map +1 -0
- package/dist/components/HyperbolicGrid.js +61 -0
- package/dist/components/LayoutInstances.d.ts +13 -0
- package/dist/components/LayoutInstances.d.ts.map +1 -0
- package/dist/components/LayoutInstances.js +39 -0
- package/dist/components/ModelPrimitive.d.ts +2 -0
- package/dist/components/ModelPrimitive.d.ts.map +1 -1
- package/dist/components/ModelPrimitive.js +3 -3
- package/dist/components/ObjectNode.d.ts.map +1 -1
- package/dist/components/ObjectNode.js +36 -8
- package/dist/components/ReactionDiffusionGrid.d.ts +15 -0
- package/dist/components/ReactionDiffusionGrid.d.ts.map +1 -0
- package/dist/components/ReactionDiffusionGrid.js +376 -0
- package/dist/components/WallpaperGrid.d.ts +15 -0
- package/dist/components/WallpaperGrid.d.ts.map +1 -0
- package/dist/components/WallpaperGrid.js +555 -0
- package/dist/components/cursor/CursorController.d.ts.map +1 -1
- package/dist/components/effects/BlackHoleEffect.d.ts +3 -3
- package/dist/components/effects/BlackHoleEffect.d.ts.map +1 -1
- package/dist/components/effects/BlackHoleEffect.js +293 -163
- package/dist/components/effects/DandelionSeedsEffect.d.ts +13 -0
- package/dist/components/effects/DandelionSeedsEffect.d.ts.map +1 -0
- package/dist/components/effects/DandelionSeedsEffect.js +510 -0
- package/dist/components/effects/FallingLeavesEffect.d.ts +13 -0
- package/dist/components/effects/FallingLeavesEffect.d.ts.map +1 -0
- package/dist/components/effects/FallingLeavesEffect.js +446 -0
- package/dist/components/effects/FieldLinesEffect.d.ts +11 -0
- package/dist/components/effects/FieldLinesEffect.d.ts.map +1 -0
- package/dist/components/effects/FieldLinesEffect.js +260 -0
- package/dist/components/effects/FireflySwarmEffect.d.ts +13 -0
- package/dist/components/effects/FireflySwarmEffect.d.ts.map +1 -0
- package/dist/components/effects/FireflySwarmEffect.js +408 -0
- package/dist/components/effects/GalaxyEffect.d.ts +13 -0
- package/dist/components/effects/GalaxyEffect.d.ts.map +1 -0
- package/dist/components/effects/GalaxyEffect.js +374 -0
- package/dist/components/effects/HarmonicShellEffect.d.ts +11 -0
- package/dist/components/effects/HarmonicShellEffect.d.ts.map +1 -0
- package/dist/components/effects/HarmonicShellEffect.js +209 -0
- package/dist/components/effects/IceCrystalsEffect.d.ts +3 -3
- package/dist/components/effects/IceCrystalsEffect.d.ts.map +1 -1
- package/dist/components/effects/IceCrystalsEffect.js +286 -166
- package/dist/components/effects/IceHaloEffect.d.ts +13 -0
- package/dist/components/effects/IceHaloEffect.d.ts.map +1 -0
- package/dist/components/effects/IceHaloEffect.js +306 -0
- package/dist/components/effects/LichtenbergEffect.d.ts +13 -0
- package/dist/components/effects/LichtenbergEffect.d.ts.map +1 -0
- package/dist/components/effects/LichtenbergEffect.js +517 -0
- package/dist/components/effects/MoleculesEffect.d.ts +3 -3
- package/dist/components/effects/MoleculesEffect.d.ts.map +1 -1
- package/dist/components/effects/MoleculesEffect.js +322 -164
- package/dist/components/effects/MurmurationEffect.d.ts +13 -0
- package/dist/components/effects/MurmurationEffect.d.ts.map +1 -0
- package/dist/components/effects/MurmurationEffect.js +539 -0
- package/dist/components/effects/PlanetaryRingsEffect.d.ts +13 -0
- package/dist/components/effects/PlanetaryRingsEffect.d.ts.map +1 -0
- package/dist/components/effects/PlanetaryRingsEffect.js +458 -0
- package/dist/components/effects/PortalEffect.d.ts +3 -3
- package/dist/components/effects/PortalEffect.d.ts.map +1 -1
- package/dist/components/effects/PortalEffect.js +240 -163
- package/dist/components/effects/ShockwaveEffect.d.ts +5 -3
- package/dist/components/effects/ShockwaveEffect.d.ts.map +1 -1
- package/dist/components/effects/ShockwaveEffect.js +538 -142
- package/dist/components/effects/SmokePlumeEffect.d.ts +3 -3
- package/dist/components/effects/SmokePlumeEffect.d.ts.map +1 -1
- package/dist/components/effects/SmokePlumeEffect.js +180 -116
- package/dist/components/effects/SmokeRingEffect.d.ts +3 -3
- package/dist/components/effects/SmokeRingEffect.d.ts.map +1 -1
- package/dist/components/effects/SmokeRingEffect.js +260 -135
- package/dist/components/effects/StrangeAttractorEffect.d.ts +11 -0
- package/dist/components/effects/StrangeAttractorEffect.d.ts.map +1 -0
- package/dist/components/effects/StrangeAttractorEffect.js +229 -0
- package/dist/components/effects/VolumetricFogEffect.d.ts +5 -3
- package/dist/components/effects/VolumetricFogEffect.d.ts.map +1 -1
- package/dist/components/effects/VolumetricFogEffect.js +211 -93
- package/dist/components/effects/WaterCausticsEffect.d.ts +13 -0
- package/dist/components/effects/WaterCausticsEffect.d.ts.map +1 -0
- package/dist/components/effects/WaterCausticsEffect.js +308 -0
- package/dist/components/effects/index.d.ts +9 -0
- package/dist/components/effects/index.d.ts.map +1 -1
- package/dist/components/effects/index.js +9 -0
- package/dist/components/index.d.ts +16 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +8 -0
- package/dist/constants.d.ts +1 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +1 -1
- package/dist/hooks/useModelObject.d.ts +6 -1
- package/dist/hooks/useModelObject.d.ts.map +1 -1
- package/dist/hooks/useModelObject.js +19 -6
- package/dist/types/gridConfigs.d.ts +98 -0
- package/dist/types/gridConfigs.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +2 -2
|
@@ -1,176 +1,334 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
3
|
-
import { useFrame } from '@react-three/fiber';
|
|
2
|
+
import { useEffect, useLayoutEffect, useMemo, useRef } from 'react';
|
|
4
3
|
import * as THREE from 'three';
|
|
5
|
-
import {
|
|
4
|
+
import { useFrame } from '@react-three/fiber';
|
|
6
5
|
import { applyGenerativeAnimations } from '../../utils/handleGenerativeEffectsAnimations.js';
|
|
7
6
|
import { useObjectReady } from '../../hooks/useObjectReady.js';
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const startValuesCacheRef = useRef({});
|
|
55
|
-
const rootRef = useRef(null);
|
|
56
|
-
useObjectReady(rootRef, config, onObjectReady, onObjectDispose);
|
|
57
|
-
const shaderData = getShaderByName('Molecules');
|
|
58
|
-
const tRef = useRef(new Float32Array(atomCount));
|
|
59
|
-
const r = boundingRadius * orbitRadius;
|
|
60
|
-
const PHI = 1.6180339887;
|
|
61
|
-
// 3D Lissajous frequency ratios per atom
|
|
62
|
-
const orbitalParams = useMemo(() => {
|
|
63
|
-
const pairs = [
|
|
64
|
-
[1, 2, 3], [2, 3, 1], [3, 1, 2], [1, 3, 2], [2, 1, 3], [3, 2, 1],
|
|
65
|
-
[1, 1, 2], [2, 2, 1], [1, 2, 2], [2, 1, 1], [3, 3, 2], [2, 3, 3],
|
|
66
|
-
];
|
|
67
|
-
return Array.from({ length: atomCount }, (_, i) => {
|
|
68
|
-
const [fx, fy, fz] = pairs[i % pairs.length];
|
|
69
|
-
return {
|
|
70
|
-
fx, fy, fz,
|
|
71
|
-
px: (i * PHI * Math.PI) % (Math.PI * 2),
|
|
72
|
-
py: (i * PHI * PHI * Math.PI) % (Math.PI * 2),
|
|
73
|
-
pz: (i * PHI ** 3 * Math.PI) % (Math.PI * 2),
|
|
74
|
-
hueBase: i / Math.max(atomCount - 1, 1),
|
|
75
|
-
speedMult: 0.65 + (i % 7) * 0.07,
|
|
76
|
-
};
|
|
77
|
-
});
|
|
78
|
-
}, [atomCount]);
|
|
79
|
-
// Resize tRef and spread initial times so atoms don't cluster at cycle start
|
|
80
|
-
useMemo(() => {
|
|
81
|
-
tRef.current = new Float32Array(atomCount);
|
|
82
|
-
for (let i = 0; i < atomCount; i++)
|
|
83
|
-
tRef.current[i] = (i / atomCount) * Math.PI * 2;
|
|
84
|
-
}, [atomCount]);
|
|
85
|
-
// ── Node (point) geometry ──────────────────────────────────────────────────
|
|
86
|
-
const { nodeGeo, nodeMat } = useMemo(() => {
|
|
87
|
-
const phases = new Float32Array(atomCount);
|
|
88
|
-
const sizes = new Float32Array(atomCount);
|
|
89
|
-
for (let i = 0; i < atomCount; i++) {
|
|
90
|
-
phases[i] = i / atomCount; // 0..1 → hue spread
|
|
91
|
-
sizes[i] = 0.8 + Math.random() * 0.6; // slight size variation
|
|
7
|
+
/**
|
|
8
|
+
* A molecule drawn as quantum electron clouds.
|
|
9
|
+
*
|
|
10
|
+
* The central atom shows hydrogen orbitals as rejection samples of |psi|^2,
|
|
11
|
+
* morphing from 1s to 2p, 3d and 4f (or holding one with `orbital`); the
|
|
12
|
+
* sign of psi colours the lobes. Outer atoms sit where the Thomson problem
|
|
13
|
+
* puts them (minimum Coulomb repulsion, which reproduces VSEPR shapes), each
|
|
14
|
+
* with a 1s cloud and a sigma-bond density between the nuclei. The molecule
|
|
15
|
+
* breathes with a symmetric stretch.
|
|
16
|
+
*/
|
|
17
|
+
const PRE_ADD = { transparent: true, depthWrite: false, blending: THREE.AdditiveBlending, premultipliedAlpha: true };
|
|
18
|
+
const ORBITAL_PERIOD = 7;
|
|
19
|
+
const ORBITAL_FADE = 1.6;
|
|
20
|
+
function mulberry32(seed) {
|
|
21
|
+
return () => {
|
|
22
|
+
seed |= 0;
|
|
23
|
+
seed = (seed + 0x6d2b79f5) | 0;
|
|
24
|
+
let r = Math.imul(seed ^ (seed >>> 15), 1 | seed);
|
|
25
|
+
r = (r + Math.imul(r ^ (r >>> 7), 61 | r)) ^ r;
|
|
26
|
+
return ((r ^ (r >>> 14)) >>> 0) / 4294967296;
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function cross(a, b) { return [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]]; }
|
|
30
|
+
function add(a, b) { return [a[0] + b[0], a[1] + b[1], a[2] + b[2]]; }
|
|
31
|
+
function scale(a, s) { return [a[0] * s, a[1] * s, a[2] * s]; }
|
|
32
|
+
function norm(a) { const l = Math.hypot(a[0], a[1], a[2]) || 1; return [a[0] / l, a[1] / l, a[2] / l]; }
|
|
33
|
+
/** N points on a unit sphere minimising Coulomb repulsion (VSEPR shapes for N <= 6). */
|
|
34
|
+
function thomson(count, seed) {
|
|
35
|
+
const n = Math.round(Math.min(Math.max(count, 1), 20));
|
|
36
|
+
if (n === 1)
|
|
37
|
+
return [[0, 1, 0]];
|
|
38
|
+
const rand = mulberry32(seed);
|
|
39
|
+
let pts = Array.from({ length: n }, () => norm([rand() - 0.5, rand() - 0.5, rand() - 0.5]));
|
|
40
|
+
for (let it = 0; it < 400; it++) {
|
|
41
|
+
const step = 0.12 * (1 - it / 400) + 0.005;
|
|
42
|
+
const forces = pts.map(() => [0, 0, 0]);
|
|
43
|
+
for (let i = 0; i < n; i++) {
|
|
44
|
+
for (let j = i + 1; j < n; j++) {
|
|
45
|
+
const d = [pts[i][0] - pts[j][0], pts[i][1] - pts[j][1], pts[i][2] - pts[j][2]];
|
|
46
|
+
const l2 = d[0] * d[0] + d[1] * d[1] + d[2] * d[2] + 1e-9;
|
|
47
|
+
const f = 1 / (l2 * Math.sqrt(l2));
|
|
48
|
+
for (let k = 0; k < 3; k++) {
|
|
49
|
+
forces[i][k] += d[k] * f;
|
|
50
|
+
forces[j][k] -= d[k] * f;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
92
53
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
geo.setAttribute('aSize', new THREE.BufferAttribute(sizes, 1));
|
|
97
|
-
const mat = new THREE.ShaderMaterial({
|
|
98
|
-
vertexShader: shaderData.vertex,
|
|
99
|
-
fragmentShader: shaderData.fragment,
|
|
100
|
-
uniforms: {
|
|
101
|
-
u_time: { value: 0 },
|
|
102
|
-
uPointSize: { value: atomSize * 200 },
|
|
103
|
-
uIntensity: { value: intensity },
|
|
104
|
-
uOpacity: { value: opacity },
|
|
105
|
-
},
|
|
106
|
-
transparent: true,
|
|
107
|
-
depthWrite: false,
|
|
108
|
-
blending: THREE.AdditiveBlending,
|
|
109
|
-
});
|
|
110
|
-
return { nodeGeo: geo, nodeMat: mat };
|
|
111
|
-
}, [atomCount, atomSize, intensity, opacity]);
|
|
112
|
-
// ── Bond (line) geometry with per-vertex color ─────────────────────────────
|
|
113
|
-
const { bondGeo, bondMat } = useMemo(() => {
|
|
114
|
-
const vertCount = atomCount * 2; // each bond = 2 verts
|
|
115
|
-
const geo = new THREE.BufferGeometry();
|
|
116
|
-
const positions = new Float32Array(vertCount * 3);
|
|
117
|
-
const colors = new Float32Array(vertCount * 3);
|
|
118
|
-
geo.setAttribute('position', new THREE.BufferAttribute(positions, 3));
|
|
119
|
-
geo.setAttribute('color', new THREE.BufferAttribute(colors, 3));
|
|
120
|
-
const mat = new THREE.LineBasicMaterial({
|
|
121
|
-
vertexColors: true,
|
|
122
|
-
transparent: true,
|
|
123
|
-
opacity: opacity * 0.7,
|
|
124
|
-
depthWrite: false,
|
|
125
|
-
blending: THREE.AdditiveBlending,
|
|
54
|
+
pts = pts.map((p, i) => {
|
|
55
|
+
const fl = Math.hypot(...forces[i]) || 1;
|
|
56
|
+
return norm(add(p, scale(forces[i], step / fl)));
|
|
126
57
|
});
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
58
|
+
}
|
|
59
|
+
return pts;
|
|
60
|
+
}
|
|
61
|
+
/** Unnormalised hydrogen-like orbitals (real forms), written with z as the axis. */
|
|
62
|
+
const ORBITALS = [
|
|
63
|
+
{ name: '1s', extent: 5, psi: (x, y, z, r) => Math.exp(-r) },
|
|
64
|
+
{ name: '2p', extent: 12, psi: (x, y, z, r) => z * Math.exp(-r / 2) },
|
|
65
|
+
{ name: '3d z²', extent: 22, psi: (x, y, z, r) => (3 * z * z - r * r) * Math.exp(-r / 3) },
|
|
66
|
+
{ name: '3d xy', extent: 22, psi: (x, y, z, r) => x * y * Math.exp(-r / 3) },
|
|
67
|
+
{ name: '4f z³', extent: 34, psi: (x, y, z, r) => z * (5 * z * z - 3 * r * r) * Math.exp(-r / 4) },
|
|
68
|
+
{ name: '4f xyz', extent: 34, psi: (x, y, z, r) => x * y * z * Math.exp(-r / 4) },
|
|
69
|
+
];
|
|
70
|
+
/** Rejection-samples |psi|^2; positions are scaled so the 92nd-percentile radius is 1. */
|
|
71
|
+
function sampleOrbital(index, count, seed) {
|
|
72
|
+
const o = ORBITALS[index];
|
|
73
|
+
const rand = mulberry32(seed);
|
|
74
|
+
const L = o.extent;
|
|
75
|
+
// estimate the peak density
|
|
76
|
+
let peak = 0;
|
|
77
|
+
for (let i = 0; i < 20000; i++) {
|
|
78
|
+
const x = (rand() * 2 - 1) * L, y = (rand() * 2 - 1) * L, z = (rand() * 2 - 1) * L;
|
|
79
|
+
const v = o.psi(x, y, z, Math.hypot(x, y, z));
|
|
80
|
+
peak = Math.max(peak, v * v);
|
|
81
|
+
}
|
|
82
|
+
peak *= 1.05;
|
|
83
|
+
const pts = [];
|
|
84
|
+
const signs = [];
|
|
85
|
+
let guard = 0;
|
|
86
|
+
while (pts.length < count && guard < count * 4000) {
|
|
87
|
+
guard++;
|
|
88
|
+
const x = (rand() * 2 - 1) * L, y = (rand() * 2 - 1) * L, z = (rand() * 2 - 1) * L;
|
|
89
|
+
const v = o.psi(x, y, z, Math.hypot(x, y, z));
|
|
90
|
+
if (rand() * peak < v * v) {
|
|
91
|
+
pts.push([x, y, z]);
|
|
92
|
+
signs.push(v >= 0 ? 1 : -1);
|
|
148
93
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
94
|
+
}
|
|
95
|
+
const radii = pts.map((p) => Math.hypot(...p)).sort((a, b) => a - b);
|
|
96
|
+
const s = 1 / (radii[Math.floor(radii.length * 0.92)] || 1);
|
|
97
|
+
const pos = new Float32Array(pts.length * 3);
|
|
98
|
+
// orbitals are written with z as the axis; stand it up as world Y
|
|
99
|
+
pts.forEach((p, i) => { pos[i * 3] = p[0] * s; pos[i * 3 + 1] = p[2] * s; pos[i * 3 + 2] = p[1] * s; });
|
|
100
|
+
return { pos, signs };
|
|
101
|
+
}
|
|
102
|
+
// Orbital sampling is the slow part (~200 ms for all six), so keep it across remounts.
|
|
103
|
+
const orbitalCache = new Map();
|
|
104
|
+
function cachedOrbital(index, count) {
|
|
105
|
+
const key = index + ':' + count;
|
|
106
|
+
let hit = orbitalCache.get(key);
|
|
107
|
+
if (!hit) {
|
|
108
|
+
if (orbitalCache.size >= 24)
|
|
109
|
+
orbitalCache.clear();
|
|
110
|
+
hit = sampleOrbital(index, count, 100 + index);
|
|
111
|
+
orbitalCache.set(key, hit);
|
|
112
|
+
}
|
|
113
|
+
return hit;
|
|
114
|
+
}
|
|
115
|
+
function gauss(rand) {
|
|
116
|
+
return Math.sqrt(-2 * Math.log(1 - rand() * 0.999)) * Math.cos(Math.PI * 2 * rand());
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* One point cloud holding every orbital set (kinds 0-5), the ligand 1s clouds (kind 6)
|
|
120
|
+
* and the bond clouds (kind 7).
|
|
121
|
+
* position = orbital sample, or unit ligand direction
|
|
122
|
+
* normal = offset in atom-size units (ligands and bonds)
|
|
123
|
+
* aInfo = kind, sign, phase
|
|
124
|
+
*/
|
|
125
|
+
function buildMolecule(atomCount, density, axisSeed) {
|
|
126
|
+
const dens = Math.min(Math.max(density, 0.25), 2);
|
|
127
|
+
const perOrbital = Math.round(4000 * dens);
|
|
128
|
+
const perLigand = Math.round(260 * dens);
|
|
129
|
+
const perBond = Math.round(180 * dens);
|
|
130
|
+
const dirs = atomCount > 0 ? thomson(atomCount, 3 + axisSeed) : [];
|
|
131
|
+
const rand = mulberry32(41);
|
|
132
|
+
const pos = [], nrm = [], info = [];
|
|
133
|
+
ORBITALS.forEach((_, k) => {
|
|
134
|
+
const { pos: p, signs } = cachedOrbital(k, perOrbital);
|
|
135
|
+
for (let i = 0; i < signs.length; i++) {
|
|
136
|
+
pos.push(p[i * 3], p[i * 3 + 1], p[i * 3 + 2]);
|
|
137
|
+
nrm.push(0, 0, 0);
|
|
138
|
+
info.push(k, signs[i], rand());
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
for (const d of dirs) {
|
|
142
|
+
for (let i = 0; i < perLigand; i++) {
|
|
143
|
+
// 1s density: radial distance follows r^2 e^-2r
|
|
144
|
+
const r = -Math.log(rand() * rand() * rand() + 1e-6) / 2 / 3;
|
|
145
|
+
const u = norm([gauss(rand), gauss(rand), gauss(rand)]);
|
|
146
|
+
pos.push(d[0], d[1], d[2]);
|
|
147
|
+
nrm.push(u[0] * r, u[1] * r, u[2] * r);
|
|
148
|
+
info.push(6, 1, rand());
|
|
149
|
+
}
|
|
150
|
+
for (let i = 0; i < perBond; i++) {
|
|
151
|
+
// sigma bond: density peaks between the nuclei, gaussian across the axis
|
|
152
|
+
const t = Math.min(Math.max(0.5 + gauss(rand) * 0.17, 0.12), 0.88);
|
|
153
|
+
pos.push(d[0] * t, d[1] * t, d[2] * t);
|
|
154
|
+
nrm.push(gauss(rand) * 0.35, gauss(rand) * 0.35, gauss(rand) * 0.35);
|
|
155
|
+
info.push(7, 1, rand());
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return {
|
|
159
|
+
position: new Float32Array(pos),
|
|
160
|
+
normal: new Float32Array(nrm),
|
|
161
|
+
aInfo: new Float32Array(info),
|
|
162
|
+
ligands: dirs,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
const cloudVert = /* glsl */ `
|
|
166
|
+
attribute vec3 aInfo; // x: kind (0-5 orbital set, 6 ligand, 7 bond), y: sign, z: phase
|
|
167
|
+
uniform float uTime;
|
|
168
|
+
uniform float uSetA;
|
|
169
|
+
uniform float uSetB;
|
|
170
|
+
uniform float uMix; // 0 shows set A, 1 shows set B
|
|
171
|
+
uniform float uOrbitRadius;
|
|
172
|
+
uniform float uAtomSize;
|
|
173
|
+
uniform float uBound;
|
|
174
|
+
uniform float uStretch;
|
|
175
|
+
uniform float uShowBonds;
|
|
176
|
+
uniform float uIntensity;
|
|
177
|
+
varying float vSign;
|
|
178
|
+
varying float vAlpha;
|
|
179
|
+
varying float vKind;
|
|
180
|
+
void main(){
|
|
181
|
+
float kind = aInfo.x;
|
|
182
|
+
float w = 0.0;
|
|
183
|
+
vec3 p = position;
|
|
184
|
+
float bondLen = uOrbitRadius * uBound;
|
|
185
|
+
float atomR = uAtomSize * uBound * 6.0;
|
|
186
|
+
// symmetric stretch: ligands and bond clouds breathe together
|
|
187
|
+
float stretch = 1.0 + uStretch * 0.035 * sin(uTime * 2.2);
|
|
188
|
+
if (kind < 5.5){
|
|
189
|
+
w = (abs(kind - uSetA) < 0.5 ? 1.0 - uMix : 0.0) + (abs(kind - uSetB) < 0.5 ? uMix : 0.0);
|
|
190
|
+
p *= bondLen * 0.62;
|
|
191
|
+
} else if (kind < 6.5){
|
|
192
|
+
w = 1.0;
|
|
193
|
+
p = p * stretch * bondLen + normal * atomR;
|
|
194
|
+
} else {
|
|
195
|
+
w = uShowBonds;
|
|
196
|
+
p = p * stretch * bondLen + normal * atomR * 0.5;
|
|
197
|
+
}
|
|
198
|
+
if (w < 0.002){
|
|
199
|
+
gl_PointSize = 0.0;
|
|
200
|
+
gl_Position = vec4(2.0, 2.0, 2.0, 1.0);
|
|
201
|
+
vAlpha = 0.0;
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
vSign = aInfo.y;
|
|
205
|
+
vKind = kind;
|
|
206
|
+
// the cloud is alive: each sample fades slowly in and out
|
|
207
|
+
vAlpha = w * (0.55 + 0.45 * sin(uTime * 0.9 + aInfo.z * 6.2832));
|
|
208
|
+
vec4 mv = modelViewMatrix * vec4(p, 1.0);
|
|
209
|
+
gl_PointSize = (kind < 5.5 ? 1.0 : 0.8) * uIntensity * (3.2 * bondLen / max(-mv.z, 1e-3)) * 6.0;
|
|
210
|
+
gl_Position = projectionMatrix * mv;
|
|
211
|
+
}
|
|
212
|
+
`;
|
|
213
|
+
const cloudFrag = /* glsl */ `
|
|
214
|
+
uniform vec3 uColor;
|
|
215
|
+
uniform vec3 uPhaseColor;
|
|
216
|
+
uniform vec3 uBondColor;
|
|
217
|
+
uniform float uOpacity;
|
|
218
|
+
varying float vSign;
|
|
219
|
+
varying float vAlpha;
|
|
220
|
+
varying float vKind;
|
|
221
|
+
void main(){
|
|
222
|
+
float d = length(gl_PointCoord - 0.5);
|
|
223
|
+
if (d > 0.5 || vAlpha < 0.002) discard;
|
|
224
|
+
float g = smoothstep(0.5, 0.0, d);
|
|
225
|
+
// the wavefunction's sign colours the lobes
|
|
226
|
+
vec3 col = vKind > 6.5 ? uBondColor : (vSign > 0.0 ? uColor : uPhaseColor);
|
|
227
|
+
float a = g * g * vAlpha * uOpacity * 0.9;
|
|
228
|
+
gl_FragColor = vec4(col * a, a);
|
|
229
|
+
}
|
|
230
|
+
`;
|
|
231
|
+
const spriteVert = /* glsl */ `
|
|
232
|
+
varying vec2 vUv;
|
|
233
|
+
uniform float uOrbitRadius;
|
|
234
|
+
uniform float uBound;
|
|
235
|
+
void main(){
|
|
236
|
+
vUv = uv;
|
|
237
|
+
// camera-facing quad
|
|
238
|
+
vec4 mv = modelViewMatrix * vec4(0.0, 0.0, 0.0, 1.0);
|
|
239
|
+
mv.xy += position.xy * uOrbitRadius * uBound * 0.35;
|
|
240
|
+
gl_Position = projectionMatrix * mv;
|
|
241
|
+
}
|
|
242
|
+
`;
|
|
243
|
+
const nucleusFrag = /* glsl */ `
|
|
244
|
+
uniform vec3 uColor;
|
|
245
|
+
uniform float uOpacity;
|
|
246
|
+
uniform float uIntensity;
|
|
247
|
+
varying vec2 vUv;
|
|
248
|
+
void main(){
|
|
249
|
+
float d = length(vUv - 0.5) * 2.0;
|
|
250
|
+
float g = exp(-d * d * 18.0) + exp(-d * 5.0) * 0.15;
|
|
251
|
+
vec3 col = mix(uColor, vec3(1.0), exp(-d * d * 60.0));
|
|
252
|
+
gl_FragColor = vec4(col * g * uIntensity * uOpacity, g * uOpacity);
|
|
253
|
+
}
|
|
254
|
+
`;
|
|
255
|
+
const MoleculesEffect = ({ boundingRadius, config, animations, onObjectReady, onObjectDispose }) => {
|
|
256
|
+
const { intensity = 1, speed = 1, color = '#00d4ff', opacity = 0.9, atomCount = 6, orbitRadius = 1.5, atomSize = 0.08, showBonds = true, bondColor = '#ffffff', randomizeAxes = true, orbital = -1, phaseColor = '#ff5a8a', cloudDensity = 1, } = config;
|
|
257
|
+
const rootRef = useRef(null);
|
|
258
|
+
const spinRef = useRef(null);
|
|
259
|
+
const animCache = useRef({});
|
|
260
|
+
useObjectReady(rootRef, config, onObjectReady, onObjectDispose);
|
|
261
|
+
// a standalone effect gets boundingRadius 1; guard against an empty host
|
|
262
|
+
const b = boundingRadius > 0 ? boundingRadius : 1;
|
|
263
|
+
const uniforms = useMemo(() => ({
|
|
264
|
+
uTime: { value: 0 },
|
|
265
|
+
uSpeed: { value: 1 },
|
|
266
|
+
uBound: { value: 1 },
|
|
267
|
+
uIntensity: { value: 1 },
|
|
268
|
+
uOpacity: { value: 1 },
|
|
269
|
+
uColor: { value: new THREE.Color() },
|
|
270
|
+
uPhaseColor: { value: new THREE.Color() },
|
|
271
|
+
uBondColor: { value: new THREE.Color() },
|
|
272
|
+
uOrbitRadius: { value: 1.5 },
|
|
273
|
+
uAtomSize: { value: 0.08 },
|
|
274
|
+
uShowBonds: { value: 1 },
|
|
275
|
+
uStretch: { value: 1 },
|
|
276
|
+
uOrbital: { value: -1 },
|
|
277
|
+
uSetA: { value: 0 },
|
|
278
|
+
uSetB: { value: 1 },
|
|
279
|
+
uMix: { value: 0 },
|
|
280
|
+
}), []);
|
|
281
|
+
const materials = useMemo(() => [
|
|
282
|
+
new THREE.ShaderMaterial({ vertexShader: cloudVert, fragmentShader: cloudFrag, uniforms, ...PRE_ADD }),
|
|
283
|
+
new THREE.ShaderMaterial({ vertexShader: spriteVert, fragmentShader: nucleusFrag, uniforms, ...PRE_ADD }),
|
|
284
|
+
], [uniforms]);
|
|
285
|
+
useEffect(() => () => materials.forEach((m) => m.dispose()), [materials]);
|
|
286
|
+
const cloudGeometry = useMemo(() => {
|
|
287
|
+
const m = buildMolecule(Math.round(Math.min(Math.max(atomCount, 0), 20)), cloudDensity, 0);
|
|
288
|
+
const g = new THREE.BufferGeometry();
|
|
289
|
+
g.setAttribute('position', new THREE.BufferAttribute(m.position, 3));
|
|
290
|
+
g.setAttribute('normal', new THREE.BufferAttribute(m.normal, 3));
|
|
291
|
+
g.setAttribute('aInfo', new THREE.BufferAttribute(m.aInfo, 3));
|
|
292
|
+
return g;
|
|
293
|
+
}, [atomCount, cloudDensity]);
|
|
294
|
+
useEffect(() => () => cloudGeometry.dispose(), [cloudGeometry]);
|
|
295
|
+
const quadGeometry = useMemo(() => new THREE.PlaneGeometry(1, 1), []);
|
|
296
|
+
useEffect(() => () => quadGeometry.dispose(), [quadGeometry]);
|
|
297
|
+
useLayoutEffect(() => {
|
|
298
|
+
uniforms.uSpeed.value = speed;
|
|
299
|
+
uniforms.uBound.value = b;
|
|
300
|
+
uniforms.uIntensity.value = intensity;
|
|
301
|
+
uniforms.uOpacity.value = opacity;
|
|
302
|
+
uniforms.uColor.value.set(color);
|
|
303
|
+
uniforms.uPhaseColor.value.set(phaseColor);
|
|
304
|
+
uniforms.uBondColor.value.set(bondColor);
|
|
305
|
+
uniforms.uOrbitRadius.value = orbitRadius;
|
|
306
|
+
uniforms.uAtomSize.value = atomSize;
|
|
307
|
+
uniforms.uShowBonds.value = showBonds ? 1 : 0;
|
|
308
|
+
uniforms.uOrbital.value = orbital;
|
|
309
|
+
}, [uniforms, speed, b, intensity, opacity, color, phaseColor, bondColor, orbitRadius, atomSize, showBonds, orbital]);
|
|
310
|
+
useFrame((_, delta) => {
|
|
311
|
+
if (animations?.length)
|
|
312
|
+
applyGenerativeAnimations(materials, animations, animCache.current, Date.now(), false);
|
|
313
|
+
const t = (uniforms.uTime.value += delta * uniforms.uSpeed.value);
|
|
314
|
+
if (spinRef.current)
|
|
315
|
+
spinRef.current.rotation.y = t * 0.15;
|
|
316
|
+
const fixed = Math.round(uniforms.uOrbital.value);
|
|
317
|
+
if (fixed >= 0) {
|
|
318
|
+
// hold one orbital
|
|
319
|
+
uniforms.uSetA.value = Math.min(fixed, ORBITALS.length - 1);
|
|
320
|
+
uniforms.uSetB.value = uniforms.uSetA.value;
|
|
321
|
+
uniforms.uMix.value = 0;
|
|
169
322
|
}
|
|
170
|
-
|
|
171
|
-
|
|
323
|
+
else {
|
|
324
|
+
// cycle 1s -> 2p -> 3d -> 4f with a crossfade at the end of each period
|
|
325
|
+
const k = Math.floor(t / ORBITAL_PERIOD);
|
|
326
|
+
const local = t - k * ORBITAL_PERIOD;
|
|
327
|
+
uniforms.uSetA.value = k % ORBITALS.length;
|
|
328
|
+
uniforms.uSetB.value = (k + 1) % ORBITALS.length;
|
|
329
|
+
uniforms.uMix.value = THREE.MathUtils.smoothstep(local, ORBITAL_PERIOD - ORBITAL_FADE, ORBITAL_PERIOD);
|
|
172
330
|
}
|
|
173
331
|
});
|
|
174
|
-
return (
|
|
332
|
+
return (_jsx("group", { ref: rootRef, children: _jsx("group", { rotation: randomizeAxes ? [0.45, 0, 0.3] : [0, 0, 0], children: _jsxs("group", { ref: spinRef, children: [_jsx("points", { geometry: cloudGeometry, material: materials[0], frustumCulled: false }), _jsx("mesh", { geometry: quadGeometry, material: materials[1], frustumCulled: false })] }) }) }));
|
|
175
333
|
};
|
|
176
334
|
export default MoleculesEffect;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { AnimationOptions, MurmurationConfig } from '@3driseai/3drise-core';
|
|
3
|
+
import type { ObjectReadyProps } from '../../types/objectReady';
|
|
4
|
+
export interface MurmurationEffectProps extends ObjectReadyProps {
|
|
5
|
+
boundingRadius: number;
|
|
6
|
+
config: MurmurationConfig;
|
|
7
|
+
animations?: AnimationOptions[];
|
|
8
|
+
/** True when the effect is attached to an object, so it can react to the host. */
|
|
9
|
+
hasHost?: boolean;
|
|
10
|
+
}
|
|
11
|
+
declare const MurmurationEffect: React.FC<MurmurationEffectProps>;
|
|
12
|
+
export default MurmurationEffect;
|
|
13
|
+
//# sourceMappingURL=MurmurationEffect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MurmurationEffect.d.ts","sourceRoot":"","sources":["../../../src/components/effects/MurmurationEffect.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsD,MAAM,OAAO,CAAC;AAG3E,OAAO,KAAK,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAGjF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,MAAM,WAAW,sBAAuB,SAAQ,gBAAgB;IAC5D,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,UAAU,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAChC,kFAAkF;IAClF,OAAO,CAAC,EAAE,OAAO,CAAC;CACrB;AAodD,QAAA,MAAM,iBAAiB,EAAE,KAAK,CAAC,EAAE,CAAC,sBAAsB,CA+BvD,CAAC;AAEF,eAAe,iBAAiB,CAAC"}
|