@3driseai/3drise-viewer 0.1.22 → 0.1.23

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 (87) hide show
  1. package/dist/components/CreatedObject.d.ts.map +1 -1
  2. package/dist/components/CreatedObject.js +6 -1
  3. package/dist/components/LightObject.d.ts.map +1 -1
  4. package/dist/components/LightObject.js +20 -13
  5. package/dist/components/ModelPrimitive.d.ts.map +1 -1
  6. package/dist/components/ModelPrimitive.js +4 -6
  7. package/dist/components/ObjectNode.js +1 -1
  8. package/dist/components/ParticlesGenerator.d.ts +3 -1
  9. package/dist/components/ParticlesGenerator.d.ts.map +1 -1
  10. package/dist/components/ParticlesGenerator.js +2 -2
  11. package/dist/components/ProjectLoader.js +1 -1
  12. package/dist/components/SceneBuilder.d.ts.map +1 -1
  13. package/dist/components/SceneBuilder.js +5 -1
  14. package/dist/components/fluorescentTube/FluorescentTube.d.ts +14 -0
  15. package/dist/components/fluorescentTube/FluorescentTube.d.ts.map +1 -0
  16. package/dist/components/fluorescentTube/FluorescentTube.js +262 -0
  17. package/dist/components/fluorescentTube/index.d.ts +5 -0
  18. package/dist/components/fluorescentTube/index.d.ts.map +1 -0
  19. package/dist/components/fluorescentTube/index.js +3 -0
  20. package/dist/components/fluorescentTube/tubeMaterials.d.ts +34 -0
  21. package/dist/components/fluorescentTube/tubeMaterials.d.ts.map +1 -0
  22. package/dist/components/fluorescentTube/tubeMaterials.js +185 -0
  23. package/dist/components/index.d.ts +3 -0
  24. package/dist/components/index.d.ts.map +1 -1
  25. package/dist/components/index.js +3 -0
  26. package/dist/components/ledArray/LedArray.d.ts +14 -0
  27. package/dist/components/ledArray/LedArray.d.ts.map +1 -0
  28. package/dist/components/ledArray/LedArray.js +243 -0
  29. package/dist/components/ledArray/index.d.ts +7 -0
  30. package/dist/components/ledArray/index.d.ts.map +1 -0
  31. package/dist/components/ledArray/index.js +4 -0
  32. package/dist/components/ledArray/ledLayout.d.ts +22 -0
  33. package/dist/components/ledArray/ledLayout.d.ts.map +1 -0
  34. package/dist/components/ledArray/ledLayout.js +59 -0
  35. package/dist/components/ledArray/ledMaterials.d.ts +14 -0
  36. package/dist/components/ledArray/ledMaterials.d.ts.map +1 -0
  37. package/dist/components/ledArray/ledMaterials.js +151 -0
  38. package/dist/components/lightBulb/LightBulb.d.ts.map +1 -1
  39. package/dist/components/lightBulb/LightBulb.js +10 -13
  40. package/dist/components/particles/BioluminescentWakeParticles.d.ts +3 -1
  41. package/dist/components/particles/BioluminescentWakeParticles.d.ts.map +1 -1
  42. package/dist/components/particles/BioluminescentWakeParticles.js +7 -3
  43. package/dist/components/particles/BrownianDustParticles.d.ts +3 -1
  44. package/dist/components/particles/BrownianDustParticles.d.ts.map +1 -1
  45. package/dist/components/particles/BrownianDustParticles.js +7 -3
  46. package/dist/components/particles/ChladniSandParticles.d.ts +3 -1
  47. package/dist/components/particles/ChladniSandParticles.d.ts.map +1 -1
  48. package/dist/components/particles/ChladniSandParticles.js +7 -3
  49. package/dist/components/particles/ElectronOrbitalsParticles.d.ts +3 -1
  50. package/dist/components/particles/ElectronOrbitalsParticles.d.ts.map +1 -1
  51. package/dist/components/particles/ElectronOrbitalsParticles.js +7 -3
  52. package/dist/components/particles/FaradayPowderParticles.d.ts +3 -1
  53. package/dist/components/particles/FaradayPowderParticles.d.ts.map +1 -1
  54. package/dist/components/particles/FaradayPowderParticles.js +7 -3
  55. package/dist/components/particles/GrinderSparksParticles.d.ts +3 -1
  56. package/dist/components/particles/GrinderSparksParticles.d.ts.map +1 -1
  57. package/dist/components/particles/GrinderSparksParticles.js +7 -3
  58. package/dist/components/particles/NeonRainParticles.d.ts +3 -1
  59. package/dist/components/particles/NeonRainParticles.d.ts.map +1 -1
  60. package/dist/components/particles/NeonRainParticles.js +7 -3
  61. package/dist/components/particles/TidalStreamParticles.d.ts +3 -1
  62. package/dist/components/particles/TidalStreamParticles.d.ts.map +1 -1
  63. package/dist/components/particles/TidalStreamParticles.js +7 -3
  64. package/dist/components/spotBeam/SpotBeam.d.ts +14 -0
  65. package/dist/components/spotBeam/SpotBeam.d.ts.map +1 -0
  66. package/dist/components/spotBeam/SpotBeam.js +244 -0
  67. package/dist/components/spotBeam/beamPatterns.d.ts +11 -0
  68. package/dist/components/spotBeam/beamPatterns.d.ts.map +1 -0
  69. package/dist/components/spotBeam/beamPatterns.js +106 -0
  70. package/dist/components/spotBeam/index.d.ts +7 -0
  71. package/dist/components/spotBeam/index.d.ts.map +1 -0
  72. package/dist/components/spotBeam/index.js +4 -0
  73. package/dist/components/spotBeam/spotBeamMaterials.d.ts +16 -0
  74. package/dist/components/spotBeam/spotBeamMaterials.d.ts.map +1 -0
  75. package/dist/components/spotBeam/spotBeamMaterials.js +307 -0
  76. package/dist/hooks/index.d.ts +2 -0
  77. package/dist/hooks/index.d.ts.map +1 -1
  78. package/dist/hooks/index.js +1 -0
  79. package/dist/hooks/useLightConfig.d.ts +25 -0
  80. package/dist/hooks/useLightConfig.d.ts.map +1 -0
  81. package/dist/hooks/useLightConfig.js +125 -0
  82. package/dist/hooks/useLightLive.d.ts.map +1 -1
  83. package/dist/hooks/useLightLive.js +9 -2
  84. package/dist/hooks/useShadowApplication.d.ts +4 -4
  85. package/dist/hooks/useShadowApplication.d.ts.map +1 -1
  86. package/dist/hooks/useShadowApplication.js +25 -10
  87. package/package.json +2 -2
@@ -0,0 +1,151 @@
1
+ // =============================================================================
2
+ // ledMaterials — the dies, their per-die glow, the diffuser and the halo
3
+ // =============================================================================
4
+ //
5
+ // The viewer has no tone mapping and no bloom, so every bright thing here is
6
+ // drawn by hand. An LED array is a set of very small, very bright sources, so
7
+ // the glow is per die rather than one blob: a billboard quad per instance,
8
+ // coloured from an instanced attribute so RGB mode can run a different hue down
9
+ // the array.
10
+ //
11
+ // The diffuser is the other half of the look. A bare die is a point source with
12
+ // a hard-edged shadow; a diffuser turns the whole array into one large soft
13
+ // source. The slab drawn here is that surface — and the component couples the
14
+ // same knob to the light's shadow radius, because the two are the same physics.
15
+ //
16
+ import { AdditiveBlending, Color, DoubleSide, MeshBasicMaterial, ShaderMaterial } from 'three';
17
+ const OUTPUT = /* glsl */ `
18
+ #include <tonemapping_fragment>
19
+ #include <colorspace_fragment>
20
+ `;
21
+ /**
22
+ * A billboard per instance. The quad is built in view space from the instance's
23
+ * own origin, so the dies always face the camera however the array is turned.
24
+ */
25
+ function dieGlowMaterial() {
26
+ return new ShaderMaterial({
27
+ uniforms: {
28
+ uStrength: { value: 0 },
29
+ uSize: { value: 0.09 },
30
+ },
31
+ vertexShader: /* glsl */ `
32
+ attribute vec3 aColor;
33
+ uniform float uSize;
34
+ varying vec2 vUv;
35
+ varying vec3 vColor;
36
+ void main() {
37
+ vColor = aColor;
38
+ vUv = position.xy;
39
+ // instance origin → view space, then the quad is laid out there
40
+ vec4 mv = modelViewMatrix * instanceMatrix * vec4(0.0, 0.0, 0.0, 1.0);
41
+ float s = length(modelMatrix[0].xyz) * uSize;
42
+ mv.xy += position.xy * s;
43
+ gl_Position = projectionMatrix * mv;
44
+ }`,
45
+ fragmentShader: /* glsl */ `
46
+ uniform float uStrength;
47
+ varying vec2 vUv;
48
+ varying vec3 vColor;
49
+ void main() {
50
+ float d = length(vUv);
51
+ // a tight core with a wide, faint skirt — how a small very bright
52
+ // source reads through a lens
53
+ float a = exp(-d * 11.0) + exp(-d * d * 6.0) * 0.35;
54
+ a *= 1.0 - smoothstep(0.7, 1.0, d);
55
+ gl_FragColor = vec4(vColor * uStrength * a, 1.0);
56
+ ${OUTPUT}
57
+ }`,
58
+ transparent: true,
59
+ depthWrite: false,
60
+ blending: AdditiveBlending,
61
+ });
62
+ }
63
+ /**
64
+ * The diffuser sheet. Brightest where the dies are (a soft periodic ripple that
65
+ * fades out as the sheet thickens), dimmer towards the rim where light escapes.
66
+ */
67
+ function diffuserMaterial() {
68
+ return new ShaderMaterial({
69
+ uniforms: {
70
+ uColor: { value: new Color(1, 1, 1) },
71
+ uStrength: { value: 0 },
72
+ /** 0 = the dies still show through, 1 = a single even sheet. */
73
+ uBlend: { value: 0.5 },
74
+ /** How many die pitches across the sheet, for the hot-spot ripple. */
75
+ uPitch: { value: 4 },
76
+ },
77
+ vertexShader: /* glsl */ `
78
+ varying vec2 vUv;
79
+ void main() {
80
+ vUv = uv;
81
+ gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
82
+ }`,
83
+ fragmentShader: /* glsl */ `
84
+ uniform vec3 uColor;
85
+ uniform float uStrength;
86
+ uniform float uBlend;
87
+ uniform float uPitch;
88
+ varying vec2 vUv;
89
+ void main() {
90
+ vec2 p = vUv * 2.0 - 1.0;
91
+ // light leaks away at the rim of any real diffuser
92
+ float edge = (1.0 - smoothstep(0.72, 1.0, abs(p.x))) * (1.0 - smoothstep(0.72, 1.0, abs(p.y)));
93
+ // hot spots over each die, washed out as the sheet thickens
94
+ vec2 c = cos(vUv * uPitch * 6.2831853);
95
+ float dots = (c.x * c.y) * 0.5 + 0.5;
96
+ float lit = mix(0.45 + dots * 0.75, 1.0, uBlend);
97
+ gl_FragColor = vec4(uColor * uStrength * lit * edge, 1.0);
98
+ ${OUTPUT}
99
+ }`,
100
+ transparent: true,
101
+ depthWrite: false,
102
+ side: DoubleSide,
103
+ blending: AdditiveBlending,
104
+ });
105
+ }
106
+ function haloMaterial() {
107
+ return new ShaderMaterial({
108
+ uniforms: {
109
+ uColor: { value: new Color(1, 1, 1) },
110
+ uStrength: { value: 0 },
111
+ uRadius: { value: 0.55 },
112
+ },
113
+ vertexShader: /* glsl */ `
114
+ uniform float uRadius;
115
+ varying vec2 vUv;
116
+ void main() {
117
+ vec4 mv = modelViewMatrix * vec4(0.0, 0.0, 0.0, 1.0);
118
+ float s = length(modelMatrix[0].xyz) * uRadius;
119
+ mv.xy += position.xy * s;
120
+ vUv = position.xy;
121
+ gl_Position = projectionMatrix * mv;
122
+ }`,
123
+ fragmentShader: /* glsl */ `
124
+ uniform vec3 uColor;
125
+ uniform float uStrength;
126
+ varying vec2 vUv;
127
+ void main() {
128
+ float d = length(vUv);
129
+ float a = (exp(-d * 8.0) + exp(-d * d * 4.0) * 0.3) * (1.0 - smoothstep(0.65, 1.0, d));
130
+ gl_FragColor = vec4(uColor * uStrength * a, 1.0);
131
+ ${OUTPUT}
132
+ }`,
133
+ transparent: true,
134
+ depthWrite: false,
135
+ blending: AdditiveBlending,
136
+ });
137
+ }
138
+ export function createLedArrayMaterials() {
139
+ return {
140
+ die: new MeshBasicMaterial({ color: 0xffffff, toneMapped: false }),
141
+ dieGlow: dieGlowMaterial(),
142
+ diffuser: diffuserMaterial(),
143
+ halo: haloMaterial(),
144
+ };
145
+ }
146
+ export function disposeLedArrayMaterials(m) {
147
+ m.die.dispose();
148
+ m.dieGlow.dispose();
149
+ m.diffuser.dispose();
150
+ m.halo.dispose();
151
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"LightBulb.d.ts","sourceRoot":"","sources":["../../../src/components/lightBulb/LightBulb.tsx"],"names":[],"mappings":"AAqBA,OAAO,KAAqC,MAAM,OAAO,CAAC;AAK1D,OAAO,KAAK,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAMtG,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAIhE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE7D,MAAM,MAAM,cAAc,GAAG,gBAAgB,GAAG;IAC5C,8EAA8E;IAC9E,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,eAAe,CAAC;IACxB,UAAU,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAChC,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,yEAAyE;IACzE,QAAQ,EAAE,qBAAqB,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CACzD,CAAC;AAsBF,QAAA,MAAM,SAAS,EAAE,KAAK,CAAC,EAAE,CAAC,cAAc,CAsHvC,CAAC;AACF,eAAe,SAAS,CAAC"}
1
+ {"version":3,"file":"LightBulb.d.ts","sourceRoot":"","sources":["../../../src/components/lightBulb/LightBulb.tsx"],"names":[],"mappings":"AAqBA,OAAO,KAAqC,MAAM,OAAO,CAAC;AAK1D,OAAO,KAAK,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAItG,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAIhE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE7D,MAAM,MAAM,cAAc,GAAG,gBAAgB,GAAG;IAC5C,8EAA8E;IAC9E,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,eAAe,CAAC;IACxB,UAAU,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAChC,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,yEAAyE;IACzE,QAAQ,EAAE,qBAAqB,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CACzD,CAAC;AAuBF,QAAA,MAAM,SAAS,EAAE,KAAK,CAAC,EAAE,CAAC,cAAc,CAqHvC,CAAC;AACF,eAAe,SAAS,CAAC"}
@@ -7,8 +7,8 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
7
7
  // is the filament centre, which is where the point light sits; the bulb stands
8
8
  // upright (base down, -Y) — rotate the object 180° for a pendant.
9
9
  //
10
- // Intensity ownership: useLightAnimation / useLightMouseMove (and R3F's
11
- // `intensity` prop) write `light.intensity` as usual. This component then
10
+ // Intensity ownership: useLightConfig (keyframes + pointer bindings) and R3F's
11
+ // `intensity` prop write `light.intensity` as usual. This component then
12
12
  // multiplies in the power level (on/off, warm-up, flicker) each frame. Any
13
13
  // intensity it did not write itself is adopted as the new base, so the
14
14
  // multiplier never compounds and external writers stay in charge of the base.
@@ -26,9 +26,7 @@ import { Color } from 'three';
26
26
  import { SELF_OWNED_MATERIAL_KEY } from '@3driseai/3drise-core';
27
27
  import { useObjectReady } from '../../hooks/useObjectReady.js';
28
28
  import { useLightShadow } from '../../hooks/useLightShadow.js';
29
- import { useLightAnimation } from '../../hooks/useLightAnimation.js';
30
- import { useLightMouseMove } from '../../hooks/useLightMouseMove.js';
31
- import { useLightLive } from '../../hooks/useLightLive.js';
29
+ import { useLightConfig } from '../../hooks/useLightConfig.js';
32
30
  import { buildBulbShape, disposeBulbShape } from './bulbShapes.js';
33
31
  import { applyGlass, createBulbMaterials, disposeBulbMaterials, GLASS_RESPONSE } from './bulbMaterials.js';
34
32
  import { kelvinToColor } from './kelvin.js';
@@ -53,10 +51,9 @@ const LightBulb = ({ id, config, animations, mouseMove, settings, onObjectReady,
53
51
  const lightRef = useRef(null);
54
52
  useObjectReady(groupRef, settings, onObjectReady, onObjectDispose);
55
53
  useLightShadow(lightRef, config.shadow);
56
- useLightAnimation(lightRef, animations);
57
- useLightMouseMove(lightRef, mouseMove, { lightConfig: config });
58
- // Slider drags: light props land on the light; flicker / halo / warmup are read per frame below.
59
- const live = useLightLive(groupRef, lightRef, id, config);
54
+ // Keyframes, pointer bindings and slider drags: the light's own knobs land on
55
+ // the light, and the merged config is what the filament, glow and halo read.
56
+ const { merged } = useLightConfig(groupRef, lightRef, id, config, animations, mouseMove);
60
57
  const glass = config.glass ?? 'clear';
61
58
  const glassTint = config.glassTint ?? '#ffb070';
62
59
  const on = config.on ?? true;
@@ -99,10 +96,10 @@ const LightBulb = ({ id, config, animations, mouseMove, settings, onObjectReady,
99
96
  if (!light)
100
97
  return;
101
98
  const p = power.current;
102
- const lv = live.current;
103
- const warmup = Math.max(num(lv.warmup, config.warmup ?? 0.25), 0);
104
- const flicker = Math.min(Math.max(num(lv.flicker, config.flicker ?? 0), 0), 1);
105
- const haloAmount = Math.min(Math.max(num(lv.halo, config.halo ?? 1), 0), 2);
99
+ const cfg = merged.current;
100
+ const warmup = Math.max(num(cfg.warmup, 0.25), 0);
101
+ const flicker = Math.min(Math.max(num(cfg.flicker, 0), 0), 1);
102
+ const haloAmount = Math.min(Math.max(num(cfg.halo, 1), 0), 2);
106
103
  if (light.intensity !== p.written)
107
104
  p.base = light.intensity;
108
105
  const target = on ? 1 : 0;
@@ -1,7 +1,9 @@
1
1
  import React from 'react';
2
- import type { ObjectAnimations, BioluminescentWakeParticlesConfig } from '@3driseai/3drise-core';
2
+ import type { ObjectAnimations, BioluminescentWakeParticlesConfig, MouseMoveDomain } from '@3driseai/3drise-core';
3
3
  import type { ObjectReadyProps } from '../../types/objectReady';
4
4
  export interface BioluminescentWakeParticlesProps extends ObjectReadyProps {
5
+ /** Pointer bindings for this object's config knobs (mouseMove.particles). */
6
+ mouseMove?: MouseMoveDomain;
5
7
  /** Scene object id: the controller finds the live-edit handle by it. */
6
8
  id?: string;
7
9
  /** World size of the object (units); the object's transform scales it further. */
@@ -1 +1 @@
1
- {"version":3,"file":"BioluminescentWakeParticles.d.ts","sourceRoot":"","sources":["../../../src/components/particles/BioluminescentWakeParticles.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmE,MAAM,OAAO,CAAC;AAIxF,OAAO,KAAK,EAAE,gBAAgB,EAAE,iCAAiC,EAAE,MAAM,uBAAuB,CAAC;AAMjG,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,MAAM,WAAW,gCAAiC,SAAQ,gBAAgB;IACtE,wEAAwE;IACxE,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,kFAAkF;IAClF,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,iCAAiC,CAAC;IAC1C,sFAAsF;IACtF,UAAU,CAAC,EAAE,gBAAgB,CAAC;CACjC;AA09BD,QAAA,MAAM,2BAA2B,EAAE,KAAK,CAAC,EAAE,CAAC,gCAAgC,CAiD3E,CAAC;AAEF,eAAe,2BAA2B,CAAC"}
1
+ {"version":3,"file":"BioluminescentWakeParticles.d.ts","sourceRoot":"","sources":["../../../src/components/particles/BioluminescentWakeParticles.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmE,MAAM,OAAO,CAAC;AAIxF,OAAO,KAAK,EAAE,gBAAgB,EAAE,iCAAiC,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAMlH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,MAAM,WAAW,gCAAiC,SAAQ,gBAAgB;IACtE,6EAA6E;IAC7E,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,wEAAwE;IACxE,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,kFAAkF;IAClF,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,iCAAiC,CAAC;IAC1C,sFAAsF;IACtF,UAAU,CAAC,EAAE,gBAAgB,CAAC;CACjC;AA09BD,QAAA,MAAM,2BAA2B,EAAE,KAAK,CAAC,EAAE,CAAC,gCAAgC,CAqD3E,CAAC;AAEF,eAAe,2BAA2B,CAAC"}
@@ -3,7 +3,7 @@ import { useCallback, useEffect, useLayoutEffect, useMemo, useRef } from 'react'
3
3
  import * as THREE from 'three';
4
4
  import { useFrame } from '@react-three/fiber';
5
5
  import { GENERATIVE_PARTICLES_ANIMATABLE } from '@3driseai/3drise-core';
6
- import { applyConfigKeyframeAnimations } from '../../utils/configAnimations.js';
6
+ import { applyConfigKeyframeAnimations, applyConfigMouseMove } from '../../utils/configAnimations.js';
7
7
  import { createPointerUniforms, stepPointerField, usePointerField } from '../../utils/pointerField.js';
8
8
  import { createTrailUniforms, applyTrailConfig, createCpuTrail } from '../../utils/particleTrails.js';
9
9
  import { useObjectReady } from '../../hooks/useObjectReady.js';
@@ -1103,7 +1103,7 @@ const DEFAULTS = {
1103
1103
  const STRUCTURAL = ['particleCount', 'seed', 'quality'];
1104
1104
  /** Knobs an animation (or mouse move) may drive per frame — generated from applyConfig. */
1105
1105
  const ANIMATABLE = new Set(GENERATIVE_PARTICLES_ANIMATABLE.bioluminescentWake.map((p) => p.value));
1106
- const BioluminescentWakeParticles = ({ id, size, config, animations, onObjectReady, onObjectDispose }) => {
1106
+ const BioluminescentWakeParticles = ({ id, size, config, animations, mouseMove, onObjectReady, onObjectDispose }) => {
1107
1107
  const rootRef = useRef(null);
1108
1108
  useObjectReady(rootRef, config, onObjectReady, onObjectDispose);
1109
1109
  const s = size > 0 ? size : 1;
@@ -1135,8 +1135,12 @@ const BioluminescentWakeParticles = ({ id, size, config, animations, onObjectRea
1135
1135
  const pointer = usePointerField();
1136
1136
  useFrame((state, delta) => {
1137
1137
  instance.update(Math.min(delta, 0.1), state.camera, pointer);
1138
- if (keyframes?.length) {
1138
+ // Keyframes first, pointer on top: a knob bound to both follows the cursor.
1139
+ const pointerBound = !!mouseMove?.enabled && !!mouseMove.properties?.length;
1140
+ if (keyframes?.length || pointerBound) {
1139
1141
  applyConfigKeyframeAnimations(keyframes, animFrom.current, cfg, animOverride.current, performance.now() - animStart.current, ANIMATABLE);
1142
+ if (pointerBound)
1143
+ applyConfigMouseMove(mouseMove.properties, cfg, animOverride.current, state.pointer, ANIMATABLE);
1140
1144
  push(live.current);
1141
1145
  }
1142
1146
  });
@@ -1,7 +1,9 @@
1
1
  import React from 'react';
2
- import type { ObjectAnimations, BrownianDustParticlesConfig } from '@3driseai/3drise-core';
2
+ import type { ObjectAnimations, BrownianDustParticlesConfig, MouseMoveDomain } from '@3driseai/3drise-core';
3
3
  import type { ObjectReadyProps } from '../../types/objectReady';
4
4
  export interface BrownianDustParticlesProps extends ObjectReadyProps {
5
+ /** Pointer bindings for this object's config knobs (mouseMove.particles). */
6
+ mouseMove?: MouseMoveDomain;
5
7
  /** Scene object id: the controller finds the live-edit handle by it. */
6
8
  id?: string;
7
9
  /** World size of the object (units); the object's transform scales it further. */
@@ -1 +1 @@
1
- {"version":3,"file":"BrownianDustParticles.d.ts","sourceRoot":"","sources":["../../../src/components/particles/BrownianDustParticles.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmE,MAAM,OAAO,CAAC;AAIxF,OAAO,KAAK,EAAE,gBAAgB,EAAE,2BAA2B,EAAE,MAAM,uBAAuB,CAAC;AAM3F,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,MAAM,WAAW,0BAA2B,SAAQ,gBAAgB;IAChE,wEAAwE;IACxE,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,kFAAkF;IAClF,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,2BAA2B,CAAC;IACpC,sFAAsF;IACtF,UAAU,CAAC,EAAE,gBAAgB,CAAC;CACjC;AA4qBD,QAAA,MAAM,qBAAqB,EAAE,KAAK,CAAC,EAAE,CAAC,0BAA0B,CAiD/D,CAAC;AAEF,eAAe,qBAAqB,CAAC"}
1
+ {"version":3,"file":"BrownianDustParticles.d.ts","sourceRoot":"","sources":["../../../src/components/particles/BrownianDustParticles.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmE,MAAM,OAAO,CAAC;AAIxF,OAAO,KAAK,EAAE,gBAAgB,EAAE,2BAA2B,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAM5G,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,MAAM,WAAW,0BAA2B,SAAQ,gBAAgB;IAChE,6EAA6E;IAC7E,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,wEAAwE;IACxE,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,kFAAkF;IAClF,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,2BAA2B,CAAC;IACpC,sFAAsF;IACtF,UAAU,CAAC,EAAE,gBAAgB,CAAC;CACjC;AA4qBD,QAAA,MAAM,qBAAqB,EAAE,KAAK,CAAC,EAAE,CAAC,0BAA0B,CAqD/D,CAAC;AAEF,eAAe,qBAAqB,CAAC"}
@@ -3,7 +3,7 @@ import { useCallback, useEffect, useLayoutEffect, useMemo, useRef } from 'react'
3
3
  import * as THREE from 'three';
4
4
  import { useFrame } from '@react-three/fiber';
5
5
  import { GENERATIVE_PARTICLES_ANIMATABLE } from '@3driseai/3drise-core';
6
- import { applyConfigKeyframeAnimations } from '../../utils/configAnimations.js';
6
+ import { applyConfigKeyframeAnimations, applyConfigMouseMove } from '../../utils/configAnimations.js';
7
7
  import { createPointerUniforms, stepPointerField, usePointerField } from '../../utils/pointerField.js';
8
8
  import { createTrailUniforms, applyTrailConfig, createCpuTrail } from '../../utils/particleTrails.js';
9
9
  import { useObjectReady } from '../../hooks/useObjectReady.js';
@@ -737,7 +737,7 @@ const DEFAULTS = {
737
737
  const ANIMATABLE = new Set(GENERATIVE_PARTICLES_ANIMATABLE.brownianDust.map((p) => p.value));
738
738
  /** Knobs that rebuild the simulation: they change on commit, never through the live path. */
739
739
  const STRUCTURAL = ['particleCount'];
740
- const BrownianDustParticles = ({ id, size, config, animations, onObjectReady, onObjectDispose }) => {
740
+ const BrownianDustParticles = ({ id, size, config, animations, mouseMove, onObjectReady, onObjectDispose }) => {
741
741
  const rootRef = useRef(null);
742
742
  useObjectReady(rootRef, config, onObjectReady, onObjectDispose);
743
743
  const s = size > 0 ? size : 1;
@@ -769,8 +769,12 @@ const BrownianDustParticles = ({ id, size, config, animations, onObjectReady, on
769
769
  const pointer = usePointerField();
770
770
  useFrame((state, delta) => {
771
771
  instance.update(Math.min(delta, 0.1), state.camera, pointer);
772
- if (keyframes?.length) {
772
+ // Keyframes first, pointer on top: a knob bound to both follows the cursor.
773
+ const pointerBound = !!mouseMove?.enabled && !!mouseMove.properties?.length;
774
+ if (keyframes?.length || pointerBound) {
773
775
  applyConfigKeyframeAnimations(keyframes, animFrom.current, cfg, animOverride.current, performance.now() - animStart.current, ANIMATABLE);
776
+ if (pointerBound)
777
+ applyConfigMouseMove(mouseMove.properties, cfg, animOverride.current, state.pointer, ANIMATABLE);
774
778
  push(live.current);
775
779
  }
776
780
  });
@@ -1,7 +1,9 @@
1
1
  import React from 'react';
2
- import type { ObjectAnimations, ChladniSandParticlesConfig } from '@3driseai/3drise-core';
2
+ import type { ObjectAnimations, ChladniSandParticlesConfig, MouseMoveDomain } from '@3driseai/3drise-core';
3
3
  import type { ObjectReadyProps } from '../../types/objectReady';
4
4
  export interface ChladniSandParticlesProps extends ObjectReadyProps {
5
+ /** Pointer bindings for this object's config knobs (mouseMove.particles). */
6
+ mouseMove?: MouseMoveDomain;
5
7
  /** Scene object id: the controller finds the live-edit handle by it. */
6
8
  id?: string;
7
9
  /** World size of the object (units); the object's transform scales it further. */
@@ -1 +1 @@
1
- {"version":3,"file":"ChladniSandParticles.d.ts","sourceRoot":"","sources":["../../../src/components/particles/ChladniSandParticles.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmE,MAAM,OAAO,CAAC;AAIxF,OAAO,KAAK,EAAE,gBAAgB,EAAE,0BAA0B,EAAE,MAAM,uBAAuB,CAAC;AAM1F,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,MAAM,WAAW,yBAA0B,SAAQ,gBAAgB;IAC/D,wEAAwE;IACxE,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,kFAAkF;IAClF,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,0BAA0B,CAAC;IACnC,sFAAsF;IACtF,UAAU,CAAC,EAAE,gBAAgB,CAAC;CACjC;AAsvBD,QAAA,MAAM,oBAAoB,EAAE,KAAK,CAAC,EAAE,CAAC,yBAAyB,CAiD7D,CAAC;AAEF,eAAe,oBAAoB,CAAC"}
1
+ {"version":3,"file":"ChladniSandParticles.d.ts","sourceRoot":"","sources":["../../../src/components/particles/ChladniSandParticles.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmE,MAAM,OAAO,CAAC;AAIxF,OAAO,KAAK,EAAE,gBAAgB,EAAE,0BAA0B,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAM3G,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,MAAM,WAAW,yBAA0B,SAAQ,gBAAgB;IAC/D,6EAA6E;IAC7E,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,wEAAwE;IACxE,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,kFAAkF;IAClF,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,0BAA0B,CAAC;IACnC,sFAAsF;IACtF,UAAU,CAAC,EAAE,gBAAgB,CAAC;CACjC;AAsvBD,QAAA,MAAM,oBAAoB,EAAE,KAAK,CAAC,EAAE,CAAC,yBAAyB,CAqD7D,CAAC;AAEF,eAAe,oBAAoB,CAAC"}
@@ -3,7 +3,7 @@ import { useCallback, useEffect, useLayoutEffect, useMemo, useRef } from 'react'
3
3
  import * as THREE from 'three';
4
4
  import { useFrame } from '@react-three/fiber';
5
5
  import { GENERATIVE_PARTICLES_ANIMATABLE } from '@3driseai/3drise-core';
6
- import { applyConfigKeyframeAnimations } from '../../utils/configAnimations.js';
6
+ import { applyConfigKeyframeAnimations, applyConfigMouseMove } from '../../utils/configAnimations.js';
7
7
  import { createPointerUniforms, stepPointerField, usePointerField } from '../../utils/pointerField.js';
8
8
  import { createTrailUniforms, applyTrailConfig, createCpuTrail } from '../../utils/particleTrails.js';
9
9
  import { useObjectReady } from '../../hooks/useObjectReady.js';
@@ -870,7 +870,7 @@ const DEFAULTS = {
870
870
  const STRUCTURAL = ['particleCount', 'seed', 'quality'];
871
871
  /** Knobs an animation (or mouse move) may drive per frame — generated from applyConfig. */
872
872
  const ANIMATABLE = new Set(GENERATIVE_PARTICLES_ANIMATABLE.chladniSand.map((p) => p.value));
873
- const ChladniSandParticles = ({ id, size, config, animations, onObjectReady, onObjectDispose }) => {
873
+ const ChladniSandParticles = ({ id, size, config, animations, mouseMove, onObjectReady, onObjectDispose }) => {
874
874
  const rootRef = useRef(null);
875
875
  useObjectReady(rootRef, config, onObjectReady, onObjectDispose);
876
876
  const s = size > 0 ? size : 1;
@@ -902,8 +902,12 @@ const ChladniSandParticles = ({ id, size, config, animations, onObjectReady, onO
902
902
  const pointer = usePointerField();
903
903
  useFrame((state, delta) => {
904
904
  instance.update(Math.min(delta, 0.1), state.camera, pointer);
905
- if (keyframes?.length) {
905
+ // Keyframes first, pointer on top: a knob bound to both follows the cursor.
906
+ const pointerBound = !!mouseMove?.enabled && !!mouseMove.properties?.length;
907
+ if (keyframes?.length || pointerBound) {
906
908
  applyConfigKeyframeAnimations(keyframes, animFrom.current, cfg, animOverride.current, performance.now() - animStart.current, ANIMATABLE);
909
+ if (pointerBound)
910
+ applyConfigMouseMove(mouseMove.properties, cfg, animOverride.current, state.pointer, ANIMATABLE);
907
911
  push(live.current);
908
912
  }
909
913
  });
@@ -1,7 +1,9 @@
1
1
  import React from 'react';
2
- import type { ObjectAnimations, ElectronOrbitalsParticlesConfig } from '@3driseai/3drise-core';
2
+ import type { ObjectAnimations, ElectronOrbitalsParticlesConfig, MouseMoveDomain } from '@3driseai/3drise-core';
3
3
  import type { ObjectReadyProps } from '../../types/objectReady';
4
4
  export interface ElectronOrbitalsParticlesProps extends ObjectReadyProps {
5
+ /** Pointer bindings for this object's config knobs (mouseMove.particles). */
6
+ mouseMove?: MouseMoveDomain;
5
7
  /** Scene object id: the controller finds the live-edit handle by it. */
6
8
  id?: string;
7
9
  /** World size of the object (units); the object's transform scales it further. */
@@ -1 +1 @@
1
- {"version":3,"file":"ElectronOrbitalsParticles.d.ts","sourceRoot":"","sources":["../../../src/components/particles/ElectronOrbitalsParticles.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmE,MAAM,OAAO,CAAC;AAIxF,OAAO,KAAK,EAAE,gBAAgB,EAAE,+BAA+B,EAAE,MAAM,uBAAuB,CAAC;AAM/F,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,MAAM,WAAW,8BAA+B,SAAQ,gBAAgB;IACpE,wEAAwE;IACxE,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,kFAAkF;IAClF,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,+BAA+B,CAAC;IACxC,sFAAsF;IACtF,UAAU,CAAC,EAAE,gBAAgB,CAAC;CACjC;AA0xBD,QAAA,MAAM,yBAAyB,EAAE,KAAK,CAAC,EAAE,CAAC,8BAA8B,CAiDvE,CAAC;AAEF,eAAe,yBAAyB,CAAC"}
1
+ {"version":3,"file":"ElectronOrbitalsParticles.d.ts","sourceRoot":"","sources":["../../../src/components/particles/ElectronOrbitalsParticles.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmE,MAAM,OAAO,CAAC;AAIxF,OAAO,KAAK,EAAE,gBAAgB,EAAE,+BAA+B,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAMhH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,MAAM,WAAW,8BAA+B,SAAQ,gBAAgB;IACpE,6EAA6E;IAC7E,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,wEAAwE;IACxE,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,kFAAkF;IAClF,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,+BAA+B,CAAC;IACxC,sFAAsF;IACtF,UAAU,CAAC,EAAE,gBAAgB,CAAC;CACjC;AA0xBD,QAAA,MAAM,yBAAyB,EAAE,KAAK,CAAC,EAAE,CAAC,8BAA8B,CAqDvE,CAAC;AAEF,eAAe,yBAAyB,CAAC"}
@@ -3,7 +3,7 @@ import { useCallback, useEffect, useLayoutEffect, useMemo, useRef } from 'react'
3
3
  import * as THREE from 'three';
4
4
  import { useFrame } from '@react-three/fiber';
5
5
  import { GENERATIVE_PARTICLES_ANIMATABLE } from '@3driseai/3drise-core';
6
- import { applyConfigKeyframeAnimations } from '../../utils/configAnimations.js';
6
+ import { applyConfigKeyframeAnimations, applyConfigMouseMove } from '../../utils/configAnimations.js';
7
7
  import { createPointerUniforms, stepPointerField, usePointerField } from '../../utils/pointerField.js';
8
8
  import { createTrailUniforms, applyTrailConfig, createCpuTrail } from '../../utils/particleTrails.js';
9
9
  import { useObjectReady } from '../../hooks/useObjectReady.js';
@@ -924,7 +924,7 @@ const DEFAULTS = {
924
924
  const STRUCTURAL = ['particleCount', 'stateA', 'stateB', 'seed', 'quality'];
925
925
  /** Knobs an animation (or mouse move) may drive per frame — generated from applyConfig. */
926
926
  const ANIMATABLE = new Set(GENERATIVE_PARTICLES_ANIMATABLE.electronOrbitals.map((p) => p.value));
927
- const ElectronOrbitalsParticles = ({ id, size, config, animations, onObjectReady, onObjectDispose }) => {
927
+ const ElectronOrbitalsParticles = ({ id, size, config, animations, mouseMove, onObjectReady, onObjectDispose }) => {
928
928
  const rootRef = useRef(null);
929
929
  useObjectReady(rootRef, config, onObjectReady, onObjectDispose);
930
930
  const s = size > 0 ? size : 1;
@@ -956,8 +956,12 @@ const ElectronOrbitalsParticles = ({ id, size, config, animations, onObjectReady
956
956
  const pointer = usePointerField();
957
957
  useFrame((state, delta) => {
958
958
  instance.update(Math.min(delta, 0.1), state.camera, pointer);
959
- if (keyframes?.length) {
959
+ // Keyframes first, pointer on top: a knob bound to both follows the cursor.
960
+ const pointerBound = !!mouseMove?.enabled && !!mouseMove.properties?.length;
961
+ if (keyframes?.length || pointerBound) {
960
962
  applyConfigKeyframeAnimations(keyframes, animFrom.current, cfg, animOverride.current, performance.now() - animStart.current, ANIMATABLE);
963
+ if (pointerBound)
964
+ applyConfigMouseMove(mouseMove.properties, cfg, animOverride.current, state.pointer, ANIMATABLE);
961
965
  push(live.current);
962
966
  }
963
967
  });
@@ -1,7 +1,9 @@
1
1
  import React from 'react';
2
- import type { ObjectAnimations, FaradayPowderParticlesConfig } from '@3driseai/3drise-core';
2
+ import type { ObjectAnimations, FaradayPowderParticlesConfig, MouseMoveDomain } from '@3driseai/3drise-core';
3
3
  import type { ObjectReadyProps } from '../../types/objectReady';
4
4
  export interface FaradayPowderParticlesProps extends ObjectReadyProps {
5
+ /** Pointer bindings for this object's config knobs (mouseMove.particles). */
6
+ mouseMove?: MouseMoveDomain;
5
7
  /** Scene object id: the controller finds the live-edit handle by it. */
6
8
  id?: string;
7
9
  /** World size of the object (units); the object's transform scales it further. */
@@ -1 +1 @@
1
- {"version":3,"file":"FaradayPowderParticles.d.ts","sourceRoot":"","sources":["../../../src/components/particles/FaradayPowderParticles.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmE,MAAM,OAAO,CAAC;AAIxF,OAAO,KAAK,EAAE,gBAAgB,EAAE,4BAA4B,EAAE,MAAM,uBAAuB,CAAC;AAM5F,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,MAAM,WAAW,2BAA4B,SAAQ,gBAAgB;IACjE,wEAAwE;IACxE,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,kFAAkF;IAClF,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,4BAA4B,CAAC;IACrC,sFAAsF;IACtF,UAAU,CAAC,EAAE,gBAAgB,CAAC;CACjC;AAypBD,QAAA,MAAM,sBAAsB,EAAE,KAAK,CAAC,EAAE,CAAC,2BAA2B,CAiDjE,CAAC;AAEF,eAAe,sBAAsB,CAAC"}
1
+ {"version":3,"file":"FaradayPowderParticles.d.ts","sourceRoot":"","sources":["../../../src/components/particles/FaradayPowderParticles.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmE,MAAM,OAAO,CAAC;AAIxF,OAAO,KAAK,EAAE,gBAAgB,EAAE,4BAA4B,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAM7G,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,MAAM,WAAW,2BAA4B,SAAQ,gBAAgB;IACjE,6EAA6E;IAC7E,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,wEAAwE;IACxE,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,kFAAkF;IAClF,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,4BAA4B,CAAC;IACrC,sFAAsF;IACtF,UAAU,CAAC,EAAE,gBAAgB,CAAC;CACjC;AAypBD,QAAA,MAAM,sBAAsB,EAAE,KAAK,CAAC,EAAE,CAAC,2BAA2B,CAqDjE,CAAC;AAEF,eAAe,sBAAsB,CAAC"}
@@ -3,7 +3,7 @@ import { useCallback, useEffect, useLayoutEffect, useMemo, useRef } from 'react'
3
3
  import * as THREE from 'three';
4
4
  import { useFrame } from '@react-three/fiber';
5
5
  import { GENERATIVE_PARTICLES_ANIMATABLE } from '@3driseai/3drise-core';
6
- import { applyConfigKeyframeAnimations } from '../../utils/configAnimations.js';
6
+ import { applyConfigKeyframeAnimations, applyConfigMouseMove } from '../../utils/configAnimations.js';
7
7
  import { createPointerUniforms, stepPointerField, usePointerField } from '../../utils/pointerField.js';
8
8
  import { createTrailUniforms, applyTrailConfig, createCpuTrail } from '../../utils/particleTrails.js';
9
9
  import { useObjectReady } from '../../hooks/useObjectReady.js';
@@ -741,7 +741,7 @@ const DEFAULTS = {
741
741
  const STRUCTURAL = ['particleCount', 'seed', 'quality'];
742
742
  /** Knobs an animation (or mouse move) may drive per frame — generated from applyConfig. */
743
743
  const ANIMATABLE = new Set(GENERATIVE_PARTICLES_ANIMATABLE.faradayPowder.map((p) => p.value));
744
- const FaradayPowderParticles = ({ id, size, config, animations, onObjectReady, onObjectDispose }) => {
744
+ const FaradayPowderParticles = ({ id, size, config, animations, mouseMove, onObjectReady, onObjectDispose }) => {
745
745
  const rootRef = useRef(null);
746
746
  useObjectReady(rootRef, config, onObjectReady, onObjectDispose);
747
747
  const s = size > 0 ? size : 1;
@@ -773,8 +773,12 @@ const FaradayPowderParticles = ({ id, size, config, animations, onObjectReady, o
773
773
  const pointer = usePointerField();
774
774
  useFrame((state, delta) => {
775
775
  instance.update(Math.min(delta, 0.1), state.camera, pointer);
776
- if (keyframes?.length) {
776
+ // Keyframes first, pointer on top: a knob bound to both follows the cursor.
777
+ const pointerBound = !!mouseMove?.enabled && !!mouseMove.properties?.length;
778
+ if (keyframes?.length || pointerBound) {
777
779
  applyConfigKeyframeAnimations(keyframes, animFrom.current, cfg, animOverride.current, performance.now() - animStart.current, ANIMATABLE);
780
+ if (pointerBound)
781
+ applyConfigMouseMove(mouseMove.properties, cfg, animOverride.current, state.pointer, ANIMATABLE);
778
782
  push(live.current);
779
783
  }
780
784
  });
@@ -1,7 +1,9 @@
1
1
  import React from 'react';
2
- import type { ObjectAnimations, GrinderSparksParticlesConfig } from '@3driseai/3drise-core';
2
+ import type { ObjectAnimations, GrinderSparksParticlesConfig, MouseMoveDomain } from '@3driseai/3drise-core';
3
3
  import type { ObjectReadyProps } from '../../types/objectReady';
4
4
  export interface GrinderSparksParticlesProps extends ObjectReadyProps {
5
+ /** Pointer bindings for this object's config knobs (mouseMove.particles). */
6
+ mouseMove?: MouseMoveDomain;
5
7
  /** Scene object id: the controller finds the live-edit handle by it. */
6
8
  id?: string;
7
9
  /** World size of the object (units); the object's transform scales it further. */
@@ -1 +1 @@
1
- {"version":3,"file":"GrinderSparksParticles.d.ts","sourceRoot":"","sources":["../../../src/components/particles/GrinderSparksParticles.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmE,MAAM,OAAO,CAAC;AAIxF,OAAO,KAAK,EAAE,gBAAgB,EAAE,4BAA4B,EAAE,MAAM,uBAAuB,CAAC;AAM5F,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,MAAM,WAAW,2BAA4B,SAAQ,gBAAgB;IACjE,wEAAwE;IACxE,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,kFAAkF;IAClF,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,4BAA4B,CAAC;IACrC,sFAAsF;IACtF,UAAU,CAAC,EAAE,gBAAgB,CAAC;CACjC;AA8pBD,QAAA,MAAM,sBAAsB,EAAE,KAAK,CAAC,EAAE,CAAC,2BAA2B,CAiDjE,CAAC;AAEF,eAAe,sBAAsB,CAAC"}
1
+ {"version":3,"file":"GrinderSparksParticles.d.ts","sourceRoot":"","sources":["../../../src/components/particles/GrinderSparksParticles.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmE,MAAM,OAAO,CAAC;AAIxF,OAAO,KAAK,EAAE,gBAAgB,EAAE,4BAA4B,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAM7G,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,MAAM,WAAW,2BAA4B,SAAQ,gBAAgB;IACjE,6EAA6E;IAC7E,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,wEAAwE;IACxE,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,kFAAkF;IAClF,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,4BAA4B,CAAC;IACrC,sFAAsF;IACtF,UAAU,CAAC,EAAE,gBAAgB,CAAC;CACjC;AA8pBD,QAAA,MAAM,sBAAsB,EAAE,KAAK,CAAC,EAAE,CAAC,2BAA2B,CAqDjE,CAAC;AAEF,eAAe,sBAAsB,CAAC"}
@@ -3,7 +3,7 @@ import { useCallback, useEffect, useLayoutEffect, useMemo, useRef } from 'react'
3
3
  import * as THREE from 'three';
4
4
  import { useFrame } from '@react-three/fiber';
5
5
  import { GENERATIVE_PARTICLES_ANIMATABLE } from '@3driseai/3drise-core';
6
- import { applyConfigKeyframeAnimations } from '../../utils/configAnimations.js';
6
+ import { applyConfigKeyframeAnimations, applyConfigMouseMove } from '../../utils/configAnimations.js';
7
7
  import { createPointerUniforms, stepPointerField, usePointerField } from '../../utils/pointerField.js';
8
8
  import { createTrailUniforms, applyTrailConfig, createCpuTrail } from '../../utils/particleTrails.js';
9
9
  import { useObjectReady } from '../../hooks/useObjectReady.js';
@@ -726,7 +726,7 @@ const DEFAULTS = {
726
726
  const STRUCTURAL = ['particleCount', 'seed', 'quality'];
727
727
  /** Knobs an animation (or mouse move) may drive per frame — generated from applyConfig. */
728
728
  const ANIMATABLE = new Set(GENERATIVE_PARTICLES_ANIMATABLE.grinderSparks.map((p) => p.value));
729
- const GrinderSparksParticles = ({ id, size, config, animations, onObjectReady, onObjectDispose }) => {
729
+ const GrinderSparksParticles = ({ id, size, config, animations, mouseMove, onObjectReady, onObjectDispose }) => {
730
730
  const rootRef = useRef(null);
731
731
  useObjectReady(rootRef, config, onObjectReady, onObjectDispose);
732
732
  const s = size > 0 ? size : 1;
@@ -758,8 +758,12 @@ const GrinderSparksParticles = ({ id, size, config, animations, onObjectReady, o
758
758
  const pointer = usePointerField();
759
759
  useFrame((state, delta) => {
760
760
  instance.update(Math.min(delta, 0.1), state.camera, pointer);
761
- if (keyframes?.length) {
761
+ // Keyframes first, pointer on top: a knob bound to both follows the cursor.
762
+ const pointerBound = !!mouseMove?.enabled && !!mouseMove.properties?.length;
763
+ if (keyframes?.length || pointerBound) {
762
764
  applyConfigKeyframeAnimations(keyframes, animFrom.current, cfg, animOverride.current, performance.now() - animStart.current, ANIMATABLE);
765
+ if (pointerBound)
766
+ applyConfigMouseMove(mouseMove.properties, cfg, animOverride.current, state.pointer, ANIMATABLE);
763
767
  push(live.current);
764
768
  }
765
769
  });
@@ -1,7 +1,9 @@
1
1
  import React from 'react';
2
- import type { ObjectAnimations, NeonRainParticlesConfig } from '@3driseai/3drise-core';
2
+ import type { ObjectAnimations, NeonRainParticlesConfig, MouseMoveDomain } from '@3driseai/3drise-core';
3
3
  import type { ObjectReadyProps } from '../../types/objectReady';
4
4
  export interface NeonRainParticlesProps extends ObjectReadyProps {
5
+ /** Pointer bindings for this object's config knobs (mouseMove.particles). */
6
+ mouseMove?: MouseMoveDomain;
5
7
  /** Scene object id: the controller finds the live-edit handle by it. */
6
8
  id?: string;
7
9
  /** World size of the object (units); the object's transform scales it further. */
@@ -1 +1 @@
1
- {"version":3,"file":"NeonRainParticles.d.ts","sourceRoot":"","sources":["../../../src/components/particles/NeonRainParticles.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmE,MAAM,OAAO,CAAC;AAIxF,OAAO,KAAK,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAMvF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,MAAM,WAAW,sBAAuB,SAAQ,gBAAgB;IAC5D,wEAAwE;IACxE,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,kFAAkF;IAClF,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,uBAAuB,CAAC;IAChC,sFAAsF;IACtF,UAAU,CAAC,EAAE,gBAAgB,CAAC;CACjC;AAinBD,QAAA,MAAM,iBAAiB,EAAE,KAAK,CAAC,EAAE,CAAC,sBAAsB,CAiDvD,CAAC;AAEF,eAAe,iBAAiB,CAAC"}
1
+ {"version":3,"file":"NeonRainParticles.d.ts","sourceRoot":"","sources":["../../../src/components/particles/NeonRainParticles.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmE,MAAM,OAAO,CAAC;AAIxF,OAAO,KAAK,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAMxG,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,MAAM,WAAW,sBAAuB,SAAQ,gBAAgB;IAC5D,6EAA6E;IAC7E,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,wEAAwE;IACxE,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,kFAAkF;IAClF,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,uBAAuB,CAAC;IAChC,sFAAsF;IACtF,UAAU,CAAC,EAAE,gBAAgB,CAAC;CACjC;AAinBD,QAAA,MAAM,iBAAiB,EAAE,KAAK,CAAC,EAAE,CAAC,sBAAsB,CAqDvD,CAAC;AAEF,eAAe,iBAAiB,CAAC"}
@@ -3,7 +3,7 @@ import { useCallback, useEffect, useLayoutEffect, useMemo, useRef } from 'react'
3
3
  import * as THREE from 'three';
4
4
  import { useFrame } from '@react-three/fiber';
5
5
  import { GENERATIVE_PARTICLES_ANIMATABLE } from '@3driseai/3drise-core';
6
- import { applyConfigKeyframeAnimations } from '../../utils/configAnimations.js';
6
+ import { applyConfigKeyframeAnimations, applyConfigMouseMove } from '../../utils/configAnimations.js';
7
7
  import { createPointerUniforms, stepPointerField, usePointerField } from '../../utils/pointerField.js';
8
8
  import { createTrailUniforms, applyTrailConfig, createCpuTrail } from '../../utils/particleTrails.js';
9
9
  import { useObjectReady } from '../../hooks/useObjectReady.js';
@@ -674,7 +674,7 @@ const DEFAULTS = {
674
674
  const STRUCTURAL = ['particleCount', 'seed', 'quality'];
675
675
  /** Knobs an animation (or mouse move) may drive per frame — generated from applyConfig. */
676
676
  const ANIMATABLE = new Set(GENERATIVE_PARTICLES_ANIMATABLE.neonRain.map((p) => p.value));
677
- const NeonRainParticles = ({ id, size, config, animations, onObjectReady, onObjectDispose }) => {
677
+ const NeonRainParticles = ({ id, size, config, animations, mouseMove, onObjectReady, onObjectDispose }) => {
678
678
  const rootRef = useRef(null);
679
679
  useObjectReady(rootRef, config, onObjectReady, onObjectDispose);
680
680
  const s = size > 0 ? size : 1;
@@ -706,8 +706,12 @@ const NeonRainParticles = ({ id, size, config, animations, onObjectReady, onObje
706
706
  const pointer = usePointerField();
707
707
  useFrame((state, delta) => {
708
708
  instance.update(Math.min(delta, 0.1), state.camera, pointer);
709
- if (keyframes?.length) {
709
+ // Keyframes first, pointer on top: a knob bound to both follows the cursor.
710
+ const pointerBound = !!mouseMove?.enabled && !!mouseMove.properties?.length;
711
+ if (keyframes?.length || pointerBound) {
710
712
  applyConfigKeyframeAnimations(keyframes, animFrom.current, cfg, animOverride.current, performance.now() - animStart.current, ANIMATABLE);
713
+ if (pointerBound)
714
+ applyConfigMouseMove(mouseMove.properties, cfg, animOverride.current, state.pointer, ANIMATABLE);
711
715
  push(live.current);
712
716
  }
713
717
  });
@@ -1,7 +1,9 @@
1
1
  import React from 'react';
2
- import type { ObjectAnimations, TidalStreamParticlesConfig } from '@3driseai/3drise-core';
2
+ import type { ObjectAnimations, TidalStreamParticlesConfig, MouseMoveDomain } from '@3driseai/3drise-core';
3
3
  import type { ObjectReadyProps } from '../../types/objectReady';
4
4
  export interface TidalStreamParticlesProps extends ObjectReadyProps {
5
+ /** Pointer bindings for this object's config knobs (mouseMove.particles). */
6
+ mouseMove?: MouseMoveDomain;
5
7
  /** Scene object id: the controller finds the live-edit handle by it. */
6
8
  id?: string;
7
9
  /** World size of the object (units); the object's transform scales it further. */
@@ -1 +1 @@
1
- {"version":3,"file":"TidalStreamParticles.d.ts","sourceRoot":"","sources":["../../../src/components/particles/TidalStreamParticles.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmE,MAAM,OAAO,CAAC;AAIxF,OAAO,KAAK,EAAE,gBAAgB,EAAE,0BAA0B,EAAE,MAAM,uBAAuB,CAAC;AAM1F,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,MAAM,WAAW,yBAA0B,SAAQ,gBAAgB;IAC/D,wEAAwE;IACxE,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,kFAAkF;IAClF,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,0BAA0B,CAAC;IACnC,sFAAsF;IACtF,UAAU,CAAC,EAAE,gBAAgB,CAAC;CACjC;AAmtBD,QAAA,MAAM,oBAAoB,EAAE,KAAK,CAAC,EAAE,CAAC,yBAAyB,CAiD7D,CAAC;AAEF,eAAe,oBAAoB,CAAC"}
1
+ {"version":3,"file":"TidalStreamParticles.d.ts","sourceRoot":"","sources":["../../../src/components/particles/TidalStreamParticles.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmE,MAAM,OAAO,CAAC;AAIxF,OAAO,KAAK,EAAE,gBAAgB,EAAE,0BAA0B,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAM3G,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,MAAM,WAAW,yBAA0B,SAAQ,gBAAgB;IAC/D,6EAA6E;IAC7E,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,wEAAwE;IACxE,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,kFAAkF;IAClF,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,0BAA0B,CAAC;IACnC,sFAAsF;IACtF,UAAU,CAAC,EAAE,gBAAgB,CAAC;CACjC;AAmtBD,QAAA,MAAM,oBAAoB,EAAE,KAAK,CAAC,EAAE,CAAC,yBAAyB,CAqD7D,CAAC;AAEF,eAAe,oBAAoB,CAAC"}