@polarfront-lab/ionian 1.7.0 → 3.0.0

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.d.ts CHANGED
@@ -25,13 +25,9 @@ export type MeshData = {
25
25
  export interface EasingFunction {
26
26
  (n: number): number;
27
27
  }
28
- export type DataTextureEntry = {
29
- textureSize: number;
30
- dataTexture: THREE.DataTexture;
31
- };
32
28
  export type ServiceType = "data-texture" | "matcap" | "instanced-mesh" | "simulation" | "asset";
33
29
  export type ServiceState = "created" | "initializing" | "ready" | "disposed" | "error" | "loading";
34
- export type TransitionType = "data-texture" | "matcap" | "mesh-sequence";
30
+ export type TransitionType = "data-texture" | "texture" | "mesh-sequence";
35
31
  export interface TransitionDetail {
36
32
  duration: number;
37
33
  easing: EasingFunction;
@@ -44,6 +40,14 @@ export type TransitionOptions = {
44
40
  onTransitionFinished?: Callback;
45
41
  onTransitionCancelled?: Callback;
46
42
  };
43
+ export type TextureSequenceItem = {
44
+ type: "matcap";
45
+ id: string;
46
+ } | {
47
+ type: "color";
48
+ value: THREE.ColorRepresentation;
49
+ };
50
+ export type TextureSequence = TextureSequenceItem[];
47
51
  export type TransitionEvents = {
48
52
  /** transition started */
49
53
  transitionStarted: {
@@ -80,16 +84,13 @@ export type GlobalEvents = {
80
84
  message: string;
81
85
  };
82
86
  };
83
- export type MatcapEvents = {
84
- matcapRegistered: {
85
- id: string;
86
- };
87
- matcapReplaced: {
88
- id: string;
87
+ export type MaterialTextureEvents = {
88
+ textureSequenceUpdated: {
89
+ sequence: TextureSequence;
89
90
  };
90
91
  };
91
92
  export type SimulationEvents = {};
92
- export type Events = GlobalEvents & SimulationEvents & DataTextureEvents & MatcapEvents & TransitionEvents & AssetEvents;
93
+ export type Events = GlobalEvents & SimulationEvents & DataTextureEvents & MaterialTextureEvents & TransitionEvents & AssetEvents;
93
94
  export interface EngineEventEmitter<EventMap extends Record<string, unknown>> {
94
95
  emit<Key extends keyof EventMap>(type: Key, payload: EventMap[Key]): void;
95
96
  off<Key extends keyof EventMap>(type: Key, handler?: (payload: EventMap[Key]) => void): void;
@@ -116,9 +117,7 @@ export interface EngineState {
116
117
  velocityTractionForce: number;
117
118
  positionalTractionForce: number;
118
119
  maxRepelDistance: number;
119
- originMatcapID: string;
120
- destinationMatcapID: string;
121
- matcapTransitionProgress: number;
120
+ textureSequence: TextureSequence;
122
121
  instanceGeometryScale: THREE.Vector3Like;
123
122
  useIntersect: boolean;
124
123
  }
@@ -158,13 +157,7 @@ export declare class ParticlesEngine {
158
157
  * @param elapsedTime The elapsed time since the last frame.
159
158
  */
160
159
  render(elapsedTime: number): void;
161
- setOriginMatcap(matcapID: string, override?: boolean): void;
162
- setDestinationMatcap(matcapID: string, override?: boolean): void;
163
- setOriginColor(color: THREE.ColorRepresentation, override?: boolean): void;
164
- setDestinationColor(color: THREE.ColorRepresentation, override?: boolean): void;
165
- setOriginTexture(id: string | THREE.ColorRepresentation, override?: boolean): void;
166
- setDestinationTexture(id: string | THREE.ColorRepresentation, override?: boolean): void;
167
- setMatcapProgress(progress: number, override?: boolean): void;
160
+ setTextureSequence(sequence: TextureSequence): void;
168
161
  setTextureSize(size: number): Promise<void>;
169
162
  registerMesh(id: string, mesh: THREE.Mesh): void;
170
163
  registerMatcap(id: string, matcap: THREE.Texture): void;
@@ -188,16 +181,6 @@ export declare class ParticlesEngine {
188
181
  * @param override If true, cancels any ongoing mesh sequence transition before setting the value. Defaults to true.
189
182
  */
190
183
  setOverallProgress(progress: number, override?: boolean): void;
191
- scheduleMatcapTransition(originMatcapID: string, destinationMatcapID: string, easing?: EasingFunction, duration?: number, override?: boolean, options?: TransitionOptions): void;
192
- scheduleTextureTransition(origin: string | THREE.ColorRepresentation, destination: string | THREE.ColorRepresentation, options?: {
193
- easing?: EasingFunction;
194
- duration?: number;
195
- override?: boolean;
196
- onTransitionBegin?: Callback;
197
- onTransitionProgress?: TransitionCallback;
198
- onTransitionFinished?: Callback;
199
- onTransitionCancelled?: Callback;
200
- }): void;
201
184
  /**
202
185
  * Schedules a smooth transition for the overall mesh sequence progress.
203
186
  * @param targetProgress The final progress value (0.0 to 1.0) to transition to.
@@ -226,6 +209,8 @@ export declare class ParticlesEngine {
226
209
  private initialEngineState;
227
210
  private getInitialServiceStates;
228
211
  private handleInteractionPositionUpdated;
212
+ private calculateTextureInterpolation;
213
+ private getTextureForSequenceItem;
229
214
  }
230
215
 
231
216
  export {};
@@ -1,2 +1,2 @@
1
- var ionian=function(e,t,i){"use strict";var s=Object.defineProperty,n=(e,t,i)=>((e,t,i)=>t in e?s(e,t,{enumerable:!0,configurable:!0,writable:!0,value:i}):e[t]=i)(e,"symbol"!=typeof t?t+"":t,i);function r(e){const t=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(e)for(const i in e)if("default"!==i){const s=Object.getOwnPropertyDescriptor(e,i);Object.defineProperty(t,i,s.get?s:{enumerable:!0,get:()=>e[i]})}return t.default=e,Object.freeze(t)}const a=r(t),o=e=>e;class l{constructor(){var e;n(this,"emitter",{all:e=e||new Map,on:function(t,i){var s=e.get(t);s?s.push(i):e.set(t,[i])},off:function(t,i){var s=e.get(t);s&&(i?s.splice(s.indexOf(i)>>>0,1):e.set(t,[]))},emit:function(t,i){var s=e.get(t);s&&s.slice().map((function(e){e(i)})),(s=e.get("*"))&&s.slice().map((function(e){e(t,i)}))}})}emit(e,t){this.emitter.emit(e,t)}off(e,t){this.emitter.off(e,t)}on(e,t){this.emitter.on(e,t)}once(e,t){this.emitter.on(e,(i=>{this.emitter.off(e,t),t(i)}))}dispose(){this.emitter.all.clear()}}function u(e,t){const i=new a.DataTexture(e,t,t,a.RGBAFormat,a.FloatType);return i.needsUpdate=!0,i}function c(e){e.geometry.dispose(),e.material instanceof a.Material?e.material.dispose():e.material.forEach((e=>e.dispose()))}function h(e,t,i){return e=Math.min(e,i),e=Math.max(e,t)}class d{constructor(e){n(this,"serviceState","created"),n(this,"eventEmitter"),n(this,"meshes",new Map),n(this,"textures",new Map),n(this,"gltfLoader",new i.GLTFLoader),n(this,"textureLoader",new a.TextureLoader),n(this,"dracoLoader",new i.DRACOLoader),n(this,"solidColorTexture",new a.DataTexture(new Uint8Array([127,127,127,255]),1,1,a.RGBAFormat)),this.eventEmitter=e,this.dracoLoader.setDecoderPath("https://www.gstatic.com/draco/versioned/decoders/1.5.7/"),this.gltfLoader.setDRACOLoader(this.dracoLoader),this.updateServiceState("ready")}register(e,t){if(t.name=e,t instanceof a.Mesh){const i=this.meshes.get(e);i&&c(i),this.meshes.set(e,t)}else{const i=this.textures.get(e);i&&i.dispose(),this.textures.set(e,t)}this.eventEmitter.emit("assetRegistered",{id:e})}setSolidColor(e){this.changeColor(e)}getSolidColorTexture(){return this.solidColorTexture}getMesh(e){return this.meshes.get(e)??null}getMatcap(e){const t=this.textures.get(e);return t||this.eventEmitter.emit("invalidRequest",{message:`texture with id "${e}" not found. using solid color texture instead...`}),t??this.solidColorTexture}getMeshIDs(){return Array.from(this.meshes.keys())}getTextureIDs(){return Array.from(this.textures.keys())}getMeshes(){return Array.from(this.meshes.values())}getTextures(){return Array.from(this.textures.values())}hasMatcap(e){return this.textures.has(e)}async loadMeshAsync(e,t,i={}){const s=await this.gltfLoader.loadAsync(t);try{if(i.meshName){const t=s.scene.getObjectByName(i.meshName);return this.register(e,t),t}{const t=s.scene.children[0];return this.register(e,t),t}}catch(n){return this.eventEmitter.emit("invalidRequest",{message:`failed to load mesh: ${e}. ${n}`}),null}}async loadTextureAsync(e,t){try{const i=await this.textureLoader.loadAsync(t);return this.register(e,i),i}catch(i){return this.eventEmitter.emit("invalidRequest",{message:`failed to load texture: ${e}. ${i}`}),null}}dispose(){this.updateServiceState("disposed"),this.meshes.forEach((e=>c(e))),this.meshes.clear(),this.textures.forEach((e=>e.dispose())),this.textures.clear()}changeColor(e){const t=new a.Color(e);this.solidColorTexture=new a.DataTexture(new Uint8Array([t.r,t.g,t.b,255]),1,1,a.RGBAFormat),this.solidColorTexture.needsUpdate=!0}updateServiceState(e){this.serviceState=e,this.eventEmitter.emit("serviceStateUpdated",{type:"asset",state:e})}}const m=new t.Triangle,g=new t.Vector3,p=new t.Vector2,v=new t.Vector2,f=new t.Vector2;class x{constructor(e){this.geometry=e.geometry,this.randomFunction=Math.random,this.indexAttribute=this.geometry.index,this.positionAttribute=this.geometry.getAttribute("position"),this.normalAttribute=this.geometry.getAttribute("normal"),this.colorAttribute=this.geometry.getAttribute("color"),this.uvAttribute=this.geometry.getAttribute("uv"),this.weightAttribute=null,this.distribution=null}setWeightAttribute(e){return this.weightAttribute=e?this.geometry.getAttribute(e):null,this}build(){const e=this.indexAttribute,t=this.positionAttribute,i=this.weightAttribute,s=e?e.count/3:t.count/3,n=new Float32Array(s);for(let o=0;o<s;o++){let s=1,r=3*o,a=3*o+1,l=3*o+2;e&&(r=e.getX(r),a=e.getX(a),l=e.getX(l)),i&&(s=i.getX(r)+i.getX(a)+i.getX(l)),m.a.fromBufferAttribute(t,r),m.b.fromBufferAttribute(t,a),m.c.fromBufferAttribute(t,l),s*=m.getArea(),n[o]=s}const r=new Float32Array(s);let a=0;for(let o=0;o<s;o++)a+=n[o],r[o]=a;return this.distribution=r,this}setRandomGenerator(e){return this.randomFunction=e,this}sample(e,t,i,s){const n=this.sampleFaceIndex();return this.sampleFace(n,e,t,i,s)}sampleFaceIndex(){const e=this.distribution[this.distribution.length-1];return this.binarySearch(this.randomFunction()*e)}binarySearch(e){const t=this.distribution;let i=0,s=t.length-1,n=-1;for(;i<=s;){const r=Math.ceil((i+s)/2);if(0===r||t[r-1]<=e&&t[r]>e){n=r;break}e<t[r]?s=r-1:i=r+1}return n}sampleFace(e,t,i,s,n){let r=this.randomFunction(),a=this.randomFunction();r+a>1&&(r=1-r,a=1-a);const o=this.indexAttribute;let l=3*e,u=3*e+1,c=3*e+2;return o&&(l=o.getX(l),u=o.getX(u),c=o.getX(c)),m.a.fromBufferAttribute(this.positionAttribute,l),m.b.fromBufferAttribute(this.positionAttribute,u),m.c.fromBufferAttribute(this.positionAttribute,c),t.set(0,0,0).addScaledVector(m.a,r).addScaledVector(m.b,a).addScaledVector(m.c,1-(r+a)),void 0!==i&&(void 0!==this.normalAttribute?(m.a.fromBufferAttribute(this.normalAttribute,l),m.b.fromBufferAttribute(this.normalAttribute,u),m.c.fromBufferAttribute(this.normalAttribute,c),i.set(0,0,0).addScaledVector(m.a,r).addScaledVector(m.b,a).addScaledVector(m.c,1-(r+a)).normalize()):m.getNormal(i)),void 0!==s&&void 0!==this.colorAttribute&&(m.a.fromBufferAttribute(this.colorAttribute,l),m.b.fromBufferAttribute(this.colorAttribute,u),m.c.fromBufferAttribute(this.colorAttribute,c),g.set(0,0,0).addScaledVector(m.a,r).addScaledVector(m.b,a).addScaledVector(m.c,1-(r+a)),s.r=g.x,s.g=g.y,s.b=g.z),void 0!==n&&void 0!==this.uvAttribute&&(p.fromBufferAttribute(this.uvAttribute,l),v.fromBufferAttribute(this.uvAttribute,u),f.fromBufferAttribute(this.uvAttribute,c),n.set(0,0).addScaledVector(p,r).addScaledVector(v,a).addScaledVector(f,1-(r+a))),this}}class y{constructor(e,t){n(this,"textureSize"),n(this,"dataTextures"),n(this,"eventEmitter"),n(this,"currentAtlas",null),this.eventEmitter=e,this.textureSize=t,this.dataTextures=new Map,this.updateServiceState("ready")}setTextureSize(e){this.textureSize!==e&&(this.textureSize=e,this.dataTextures.forEach((e=>e.dispose())),this.dataTextures.clear(),this.currentAtlas&&(this.currentAtlas.dispose(),this.currentAtlas=null))}async getDataTexture(e){const t=this.dataTextures.get(e.uuid);if(t)return t;var i,s;const n=function(e,t){const i=new a.BufferGeometry;i.setAttribute("position",new a.BufferAttribute(new Float32Array(e.position),3)),e.normal&&i.setAttribute("normal",new a.BufferAttribute(new Float32Array(e.normal),3));const s=new a.MeshBasicMaterial,n=new a.Mesh(i,s);n.scale.set(e.scale.x,e.scale.y,e.scale.z);const r=new x(n).build(),o=new Float32Array(t*t*4),l=new a.Vector3;for(let a=0;a<t;a++)for(let i=0;i<t;i++){const s=a*t+i;r.sample(l),o[4*s]=l.x*e.scale.x,o[4*s+1]=l.y*e.scale.y,o[4*s+2]=l.z*e.scale.z,o[4*s+3]=.01*(Math.random()-.5)}return o}({position:(i=e).geometry.attributes.position.array,normal:null==(s=i.geometry.attributes.normal)?void 0:s.array,scale:{x:i.scale.x,y:i.scale.y,z:i.scale.z}},this.textureSize),r=u(n,this.textureSize);return r.name=e.name,this.dataTextures.set(e.uuid,r),r}async dispose(){this.dataTextures.forEach((e=>e.dispose())),this.dataTextures.clear(),this.currentAtlas&&(this.currentAtlas.dispose(),this.currentAtlas=null),this.updateServiceState("disposed")}updateServiceState(e){this.eventEmitter.emit("serviceStateUpdated",{type:"data-texture",state:e})}async createSequenceDataTextureAtlas(e,t){this.updateServiceState("loading"),this.currentAtlas&&(this.currentAtlas.dispose(),this.currentAtlas=null);const i=e.length;if(0===i)throw new Error("Mesh array cannot be empty.");const s=t*i,n=t,r=new Float32Array(s*n*4);try{for(let n=0;n<i;n++){const i=e[n],a=(await this.getDataTexture(i)).image.data;for(let e=0;e<t;e++)for(let i=0;i<t;i++){const o=4*(e*t+i),l=4*(e*s+(i+n*t));r[l]=a[o],r[l+1]=a[o+1],r[l+2]=a[o+2],r[l+3]=a[o+3]}}const o=new a.DataTexture(r,s,n,a.RGBAFormat,a.FloatType);return o.needsUpdate=!0,o.name=`atlas-${e.map((e=>e.name)).join("-")}`,this.currentAtlas=o,this.updateServiceState("ready"),o}catch(o){throw this.updateServiceState("error"),o}}}class T{constructor(e){n(this,"size"),n(this,"mesh"),n(this,"matcapMaterial"),n(this,"fallbackGeometry"),n(this,"uniforms"),n(this,"originColor"),n(this,"destinationColor"),n(this,"geometries"),n(this,"uvRefsCache"),n(this,"previousScale"),this.size=e,this.geometries=new Map,this.uvRefsCache=new Map,this.previousScale={x:1,y:1,z:1},this.originColor="grey",this.destinationColor="grey",this.uniforms={uTime:{value:0},uProgress:{value:0},uTexture:{value:null},uVelocity:{value:null},uOriginTexture:{value:null},uDestinationTexture:{value:null}},this.matcapMaterial=new a.ShaderMaterial({uniforms:this.uniforms,vertexShader:"\nvarying vec2 vUv;\nuniform sampler2D uTexture;\nuniform sampler2D uVelocity;\nuniform float uTime;\nvarying vec3 vNormal;\nattribute vec2 uvRef;\nvarying vec3 vViewPosition;\n\nvec3 rotate3D(vec3 v, vec3 vel) {\n vec3 pos = v;\n vec3 up = vec3(0, 1, 0);\n vec3 axis = normalize(cross(up, vel));\n float angle = acos(dot(up, normalize(vel)));\n pos = pos * cos(angle) + cross(axis, pos) * sin(angle) + axis * dot(axis, pos) * (1. - cos(angle));\n return pos;\n}\n\nvoid main() {\n vUv = uv;\n vNormal = normal;\n\n vec4 color = texture2D(uTexture, uvRef);\n vec4 velocity = texture2D(uVelocity, uvRef);\n vec3 pos = color.xyz;// apply the texture to the vertex distribution.\n\n vec3 localPosition = position.xyz;\n if (length (velocity.xyz) < 0.0001) {\n velocity.xyz = vec3(0.0, 0.0001, 0.0001);\n }\n localPosition.y *= max(1.0, length(velocity.xyz) * 1000.0);\n localPosition = rotate3D(localPosition, velocity.xyz);\n vNormal = rotate3D(normal, velocity.xyz);\n\n mat4 instanceMat = instanceMatrix;\n instanceMat[3].xyz = pos.xyz;\n\n // unlike the traditional mvMatrix * position, we need to additional multiplication with the instance matrix.\n vec4 modelViewPosition = modelViewMatrix * instanceMat * vec4(localPosition, 1.0);\n\n vViewPosition = - modelViewPosition.xyz;\n\n gl_Position = projectionMatrix * modelViewPosition;\n}\n",fragmentShader:"\nvarying vec2 vUv;\nuniform sampler2D uTexture;\n\nuniform sampler2D uOriginTexture;\nuniform sampler2D uDestinationTexture;\n\nuniform float uProgress;\nvarying vec3 vNormal;\nvarying vec3 vViewPosition;\nvoid main() {\n vec3 viewDir = normalize( vViewPosition );\n vec3 x = normalize( vec3( viewDir.z, 0.0, - viewDir.x ) );\n vec3 y = cross( viewDir, x );\n vec2 uv = vec2( dot( x, vNormal ), dot( y, vNormal ) ) * 0.495 + 0.5; // 0.495 to remove artifacts caused by undersized matcap disks\n\n vec4 sourceMatcap = texture2D( uOriginTexture, uv );\n vec4 targetMatcap = texture2D( uDestinationTexture, uv );\n\n vec4 matcap = mix(sourceMatcap, targetMatcap, uProgress);\n gl_FragColor = matcap;\n}\n"}),this.setOriginColor(this.originColor),this.setDestinationColor(this.destinationColor),this.fallbackGeometry=new a.BoxGeometry(.001,.001,.001),this.mesh=this.createInstancedMesh(e,this.fallbackGeometry,this.matcapMaterial)}getMesh(){return this.mesh}update(e){const t=this.mesh.material;(t instanceof a.ShaderMaterial||t instanceof a.RawShaderMaterial)&&(t.uniforms.uTime.value=e)}setOriginMatcap(e){this.disposeSolidColorOriginTexture(),this.matcapMaterial.uniforms.uOriginTexture.value=e}setDestinationMatcap(e){this.disposeSolidColorDestinationTexture(),this.matcapMaterial.uniforms.uDestinationTexture.value=e}setProgress(e){e=Math.max(0,e),e=Math.min(1,e),this.matcapMaterial.uniforms.uProgress.value=e}setGeometrySize(e){this.mesh.geometry.scale(1/this.previousScale.x,1/this.previousScale.y,1/this.previousScale.z),this.mesh.geometry.scale(e.x,e.y,e.z),this.previousScale=e}useMatcapMaterial(){this.mesh.material=this.matcapMaterial}useGeometry(e){const t=this.geometries.get(e);t&&(this.mesh.geometry=t)}updateVelocityTexture(e){this.matcapMaterial.uniforms.uVelocity.value=e}updatePositionTexture(e){this.matcapMaterial.uniforms.uTexture.value=e}resize(e){if(this.size===e)return{current:this.mesh,previous:this.mesh};this.size=e;const t=this.mesh;return this.mesh=this.createInstancedMesh(e,t.geometry,t.material),{current:this.mesh,previous:t}}dispose(){this.mesh.dispose(),this.geometries.forEach((e=>e.dispose())),this.disposeSolidColorOriginTexture(),this.disposeSolidColorDestinationTexture(),this.matcapMaterial.dispose(),this.uvRefsCache.clear(),this.geometries.clear()}registerGeometry(e,t){const i=this.geometries.get(e);if(i&&i===t)return;const s=this.createUVRefs(this.size);t.setAttribute("uvRef",s),this.geometries.set(e,t),this.mesh.geometry===i&&(this.mesh.geometry=t),null==i||i.dispose()}setOriginColor(e){this.disposeSolidColorOriginTexture(),this.originColor=e,this.uniforms.uOriginTexture.value=this.createSolidColorDataTexture(e)}setDestinationColor(e){this.disposeSolidColorDestinationTexture(),this.destinationColor=e,this.uniforms.uDestinationTexture.value=this.createSolidColorDataTexture(e)}createUVRefs(e){const t=this.uvRefsCache.get(e);if(t)return t;const i=new Float32Array(e*e*2);for(let n=0;n<e;n++)for(let t=0;t<e;t++){const s=n*e+t;i[2*s]=t/(e-1),i[2*s+1]=n/(e-1)}const s=new a.InstancedBufferAttribute(i,2);return this.uvRefsCache.set(e,s),s}createInstancedMesh(e,t,i){(t=t||this.fallbackGeometry).setAttribute("uvRef",this.createUVRefs(e));const s=e*e;return new a.InstancedMesh(t,i,s)}createSolidColorDataTexture(e,t=16){const i=new a.Color(e),s=t,n=t,r=new Uint8Array(s*n*4),o=Math.floor(255*i.r),l=Math.floor(255*i.g),u=Math.floor(255*i.b);for(let a=0;a<s*n;a++){const e=4*a;r[e]=o,r[e+1]=l,r[e+2]=u,r[e+3]=255}const c=new a.DataTexture(r,s,n,a.RGBAFormat);return c.type=a.UnsignedByteType,c.wrapS=a.RepeatWrapping,c.wrapT=a.RepeatWrapping,c.minFilter=a.NearestFilter,c.magFilter=a.NearestFilter,c.needsUpdate=!0,c}disposeSolidColorOriginTexture(){this.originColor&&(this.originColor=null,this.uniforms.uOriginTexture.value&&this.uniforms.uOriginTexture.value.dispose())}disposeSolidColorDestinationTexture(){this.destinationColor&&(this.destinationColor=null,this.uniforms.uDestinationTexture.value&&this.uniforms.uDestinationTexture.value.dispose())}}class S{constructor(e,t){n(this,"active",!0),n(this,"raycaster",new a.Raycaster),n(this,"mousePosition",new a.Vector2),n(this,"camera"),n(this,"meshSequenceGeometries",[]),n(this,"meshSequenceUUIDs",[]),n(this,"overallProgress",0),n(this,"intersectionMesh",new a.Mesh),n(this,"geometryNeedsUpdate"),n(this,"eventEmitter"),n(this,"blendedGeometry"),n(this,"intersection"),this.camera=t,this.eventEmitter=e,this.geometryNeedsUpdate=!0}setActive(e){this.active=e,e||(this.intersection=void 0,this.eventEmitter.emit("interactionPositionUpdated",{position:{x:0,y:0,z:0,w:0}}))}getIntersectionMesh(){return this.intersectionMesh}setCamera(e){this.camera=e}setMeshSequence(e){this.meshSequenceGeometries.forEach((e=>e.dispose())),this.meshSequenceGeometries=[],this.meshSequenceUUIDs=[],e&&0!==e.length?(e.forEach((e=>{if(e&&e.geometry){const t=e.geometry.clone();t.applyMatrix4(e.matrixWorld),this.meshSequenceGeometries.push(t),this.meshSequenceUUIDs.push(e.uuid)}else console.warn("Invalid mesh provided to IntersectionService sequence.")})),this.geometryNeedsUpdate=!0):this.geometryNeedsUpdate=!0}setOverallProgress(e){const t=a.MathUtils.clamp(e,0,1);this.overallProgress!==t&&(this.overallProgress=t,this.geometryNeedsUpdate=!0)}setPointerPosition(e){e&&this.mousePosition.copy(e)}calculate(e){var t,i,s;if(!this.active||!this.camera||0===this.meshSequenceGeometries.length)return void(this.intersection&&(this.intersection=void 0,this.eventEmitter.emit("interactionPositionUpdated",{position:{x:0,y:0,z:0,w:0}})));let n;this.geometryNeedsUpdate&&(this.blendedGeometry&&this.blendedGeometry!==this.intersectionMesh.geometry&&this.blendedGeometry.dispose(),this.blendedGeometry=this.getBlendedGeometry(),this.geometryNeedsUpdate=!1,this.blendedGeometry?this.intersectionMesh.geometry!==this.blendedGeometry&&(this.intersectionMesh.geometry&&this.intersectionMesh.geometry.dispose(),this.intersectionMesh.geometry=this.blendedGeometry):(this.intersectionMesh.geometry&&this.intersectionMesh.geometry.dispose(),this.intersectionMesh.geometry=new a.BufferGeometry)),this.intersectionMesh.matrixWorld.copy(e.matrixWorld),this.blendedGeometry&&this.blendedGeometry.attributes.position&&(n=this.getFirstIntersection(this.camera,this.intersectionMesh));if((null==(t=this.intersection)?void 0:t.x)!==(null==n?void 0:n.x)||(null==(i=this.intersection)?void 0:i.y)!==(null==n?void 0:n.y)||(null==(s=this.intersection)?void 0:s.z)!==(null==n?void 0:n.z)||this.intersection&&!n||!this.intersection&&n)if(this.intersection=n,this.intersection){const t=new a.Vector3(this.intersection.x,this.intersection.y,this.intersection.z),i=e.worldToLocal(t.clone());this.intersection.set(i.x,i.y,i.z,1),this.eventEmitter.emit("interactionPositionUpdated",{position:this.intersection})}else this.eventEmitter.emit("interactionPositionUpdated",{position:{x:0,y:0,z:0,w:0}});return this.intersection}dispose(){var e;this.meshSequenceGeometries.forEach((e=>e.dispose())),this.meshSequenceGeometries=[],this.meshSequenceUUIDs=[],this.blendedGeometry&&this.blendedGeometry!==this.intersectionMesh.geometry&&this.blendedGeometry.dispose(),null==(e=this.intersectionMesh.geometry)||e.dispose()}updateIntersectionMesh(e){this.blendedGeometry&&this.blendedGeometry.uuid!==this.intersectionMesh.geometry.uuid&&(this.intersectionMesh.geometry.dispose(),this.intersectionMesh.geometry=this.blendedGeometry),this.intersectionMesh.matrix.copy(e.matrixWorld),this.intersectionMesh.matrixWorld.copy(e.matrixWorld),this.intersectionMesh.matrixAutoUpdate=!1,this.intersectionMesh.updateMatrixWorld(!0)}getFirstIntersection(e,t){this.raycaster.setFromCamera(this.mousePosition,e);const i=this.raycaster.intersectObject(t,!1);if(i.length>0&&i[0].point){const e=i[0].point;return new a.Vector4(e.x,e.y,e.z,1)}}getBlendedGeometry(){const e=this.meshSequenceGeometries.length;if(0===e)return;if(1===e)return this.meshSequenceGeometries[0];const t=e-1,i=1/t,s=this.overallProgress*t;let n=Math.floor(s),r=n+1;n=a.MathUtils.clamp(n,0,t),r=a.MathUtils.clamp(r,0,t);let o=0;i>0&&(o=s-n),this.overallProgress>=1&&(n=t,r=t,o=1),o=a.MathUtils.clamp(o,0,1);const l=this.meshSequenceGeometries[n],u=this.meshSequenceGeometries[r];return l&&u?n===r?l:this.blendGeometry(l,u,o):(console.error("IntersectionService: Invalid geometries found for blending at indices",n,r),this.meshSequenceGeometries[0])}blendGeometry(e,t,i){const s=new a.BufferGeometry,n=e.attributes.position.array,r=t.attributes.position.array,o=new Float32Array(n.length);for(let l=0;l<n.length;l+=3){const e=new a.Vector3(n[l],n[l+1],n[l+2]),t=new a.Vector3(r[l],r[l+1],r[l+2]),s=(new a.Vector3).lerpVectors(e,t,i);o[l]=s.x,o[l+1]=s.y,o[l+2]=s.z}return s.setAttribute("position",new a.BufferAttribute(o,3)),e.attributes.normal&&s.setAttribute("normal",e.attributes.normal.clone()),e.attributes.uv&&s.setAttribute("uv",e.attributes.uv.clone()),e.index&&s.setIndex(e.index.clone()),s}}const M=new t.OrthographicCamera(-1,1,1,-1,0,1);class P extends t.BufferGeometry{constructor(){super(),this.setAttribute("position",new t.Float32BufferAttribute([-1,3,0,-1,-1,0,3,-1,0],3)),this.setAttribute("uv",new t.Float32BufferAttribute([0,2,0,0,2,0],2))}}const w=new P;class A{constructor(e){this._mesh=new t.Mesh(w,e)}dispose(){this._mesh.geometry.dispose()}render(e){e.render(this._mesh,M)}get material(){return this._mesh.material}set material(e){this._mesh.material=e}}class b{constructor(e,i,s){this.variables=[],this.currentTextureIndex=0;let n=t.FloatType;const r={passThruTexture:{value:null}},a=u("uniform sampler2D passThruTexture;\n\nvoid main() {\n\n\tvec2 uv = gl_FragCoord.xy / resolution.xy;\n\n\tgl_FragColor = texture2D( passThruTexture, uv );\n\n}\n",r),o=new A(a);function l(t){t.defines.resolution="vec2( "+e.toFixed(1)+", "+i.toFixed(1)+" )"}function u(e,i){i=i||{};const s=new t.ShaderMaterial({name:"GPUComputationShader",uniforms:i,vertexShader:"void main()\t{\n\n\tgl_Position = vec4( position, 1.0 );\n\n}\n",fragmentShader:e});return l(s),s}this.setDataType=function(e){return n=e,this},this.addVariable=function(e,i,s){const n={name:e,initialValueTexture:s,material:this.createShaderMaterial(i),dependencies:null,renderTargets:[],wrapS:null,wrapT:null,minFilter:t.NearestFilter,magFilter:t.NearestFilter};return this.variables.push(n),n},this.setVariableDependencies=function(e,t){e.dependencies=t},this.init=function(){if(0===s.capabilities.maxVertexTextures)return"No support for vertex shader textures.";for(let t=0;t<this.variables.length;t++){const s=this.variables[t];s.renderTargets[0]=this.createRenderTarget(e,i,s.wrapS,s.wrapT,s.minFilter,s.magFilter),s.renderTargets[1]=this.createRenderTarget(e,i,s.wrapS,s.wrapT,s.minFilter,s.magFilter),this.renderTexture(s.initialValueTexture,s.renderTargets[0]),this.renderTexture(s.initialValueTexture,s.renderTargets[1]);const n=s.material,r=n.uniforms;if(null!==s.dependencies)for(let e=0;e<s.dependencies.length;e++){const t=s.dependencies[e];if(t.name!==s.name){let e=!1;for(let i=0;i<this.variables.length;i++)if(t.name===this.variables[i].name){e=!0;break}if(!e)return"Variable dependency not found. Variable="+s.name+", dependency="+t.name}r[t.name]={value:null},n.fragmentShader="\nuniform sampler2D "+t.name+";\n"+n.fragmentShader}}return this.currentTextureIndex=0,null},this.compute=function(){const e=this.currentTextureIndex,t=0===this.currentTextureIndex?1:0;for(let i=0,s=this.variables.length;i<s;i++){const s=this.variables[i];if(null!==s.dependencies){const t=s.material.uniforms;for(let i=0,n=s.dependencies.length;i<n;i++){const n=s.dependencies[i];t[n.name].value=n.renderTargets[e].texture}}this.doRenderTarget(s.material,s.renderTargets[t])}this.currentTextureIndex=t},this.getCurrentRenderTarget=function(e){return e.renderTargets[this.currentTextureIndex]},this.getAlternateRenderTarget=function(e){return e.renderTargets[0===this.currentTextureIndex?1:0]},this.dispose=function(){o.dispose();const e=this.variables;for(let t=0;t<e.length;t++){const i=e[t];i.initialValueTexture&&i.initialValueTexture.dispose();const s=i.renderTargets;for(let e=0;e<s.length;e++){s[e].dispose()}}},this.addResolutionDefine=l,this.createShaderMaterial=u,this.createRenderTarget=function(s,r,a,o,l,u){s=s||e,r=r||i,a=a||t.ClampToEdgeWrapping,o=o||t.ClampToEdgeWrapping,l=l||t.NearestFilter,u=u||t.NearestFilter;return new t.WebGLRenderTarget(s,r,{wrapS:a,wrapT:o,minFilter:l,magFilter:u,format:t.RGBAFormat,type:n,depthBuffer:!1})},this.createTexture=function(){const s=new Float32Array(e*i*4),n=new t.DataTexture(s,e,i,t.RGBAFormat,t.FloatType);return n.needsUpdate=!0,n},this.renderTexture=function(e,t){r.passThruTexture.value=e,this.doRenderTarget(a,t),r.passThruTexture.value=null},this.doRenderTarget=function(e,t){const i=s.getRenderTarget(),n=s.xr.enabled,r=s.shadowMap.autoUpdate;s.xr.enabled=!1,s.shadowMap.autoUpdate=!1,o.material=e,s.setRenderTarget(t),o.render(s),o.material=a,s.xr.enabled=n,s.shadowMap.autoUpdate=r,s.setRenderTarget(i)}}}class V{constructor(e,t,i){n(this,"gpuComputationRenderer"),n(this,"webGLRenderer"),n(this,"velocityVar"),n(this,"positionVar"),n(this,"initialPositionDataTexture"),n(this,"initialVelocityDataTexture"),n(this,"positionAtlasTexture",null),n(this,"interactionPosition"),n(this,"lastKnownPositionDataTexture"),n(this,"lastKnownVelocityDataTexture"),this.webGLRenderer=t,this.gpuComputationRenderer=new b(e,e,this.webGLRenderer),!t.capabilities.isWebGL2&&t.extensions.get("OES_texture_float")?this.gpuComputationRenderer.setDataType(a.FloatType):t.capabilities.isWebGL2||this.gpuComputationRenderer.setDataType(a.HalfFloatType),this.initialPositionDataTexture=i??function(e){const t=new Float32Array(e*e*4);for(let i=0;i<e;i++)for(let s=0;s<e;s++){const n=i*e+s;let r=Math.random()*Math.PI*2,a=Math.acos(2*Math.random()-1),o=Math.sin(a)*Math.cos(r),l=Math.sin(a)*Math.sin(r),u=Math.cos(a);t[4*n]=o,t[4*n+1]=l,t[4*n+2]=u,t[4*n+3]=.01*(Math.random()-.5)}return u(t,e)}(e),this.initialVelocityDataTexture=function(e){return u(new Float32Array(4*e*e),e)}(e),this.interactionPosition=new a.Vector4(0,0,0,0),this.velocityVar=this.gpuComputationRenderer.addVariable("uCurrentVelocity",'\nuniform vec4 uInteractionPosition;\nuniform float uTime;\nuniform float uTractionForce;\nuniform float uMaxRepelDistance;\nuniform sampler2D uPositionAtlas;\nuniform float uOverallProgress;\nuniform int uNumMeshes;\nuniform float uSingleTextureSize;\n\nfloat rand(vec2 co) {\n return fract(sin(dot(co, vec2(12.9898, 78.233))) * 43758.5453);\n}\n\n// Helper function (same as in position shader)\nvec3 getAtlasPosition(vec2 uv, int meshIndex) {\n float atlasWidth = uSingleTextureSize * float(uNumMeshes);\n float atlasHeight = uSingleTextureSize;\n float segmentWidthRatio = uSingleTextureSize / atlasWidth;\n vec2 atlasUV = vec2(uv.x * segmentWidthRatio + segmentWidthRatio * float(meshIndex), uv.y);\n return texture2D(uPositionAtlas, atlasUV).xyz;\n}\n\nvoid main() {\n vec2 uv = gl_FragCoord.xy / resolution.xy;\n float offset = rand(uv);\n\n vec3 currentPosition = texture2D(uCurrentPosition, uv).xyz;\n vec3 currentVelocity = texture2D(uCurrentVelocity, uv).xyz;\n\n // --- Calculate Target Position from Atlas (same logic as position shader) ---\n vec3 targetPosition;\n if (uNumMeshes <= 1) {\n targetPosition = getAtlasPosition(uv, 0);\n } else {\n float totalSegments = float(uNumMeshes - 1);\n float progressPerSegment = 1.0 / totalSegments;\n float scaledProgress = uOverallProgress * totalSegments;\n int indexA = int(floor(scaledProgress));\n int indexB = min(indexA + 1, uNumMeshes - 1);\n indexA = min(indexA, uNumMeshes - 1);\n float localProgress = fract(scaledProgress);\n if (uOverallProgress == 1.0) {\n indexA = uNumMeshes - 1;\n indexB = uNumMeshes - 1;\n localProgress = 1.0;\n }\n vec3 positionA = getAtlasPosition(uv, indexA);\n vec3 positionB = getAtlasPosition(uv, indexB);\n targetPosition = mix(positionA, positionB, localProgress);\n }\n // --- End Target Position Calculation ---\n\n vec3 finalVelocity = currentVelocity * 0.9; // Dampening\n\n // Particle traction force towards target (influences velocity)\n vec3 direction = normalize(targetPosition - currentPosition);\n float dist = length(targetPosition - currentPosition);\n if (dist > 0.01) {\n // Add force proportional to distance and traction setting\n finalVelocity += direction * dist * 0.01 * uTractionForce; // Adjust multiplier as needed\n }\n\n // Mouse repel force\n if (uInteractionPosition.w > 0.0) { // Check if interaction is active (w component)\n float pointerDistance = distance(currentPosition, uInteractionPosition.xyz);\n if (pointerDistance < uMaxRepelDistance) {\n float mouseRepelModifier = smoothstep(uMaxRepelDistance, 0.0, pointerDistance); // Smoother falloff\n vec3 repelDirection = normalize(currentPosition - uInteractionPosition.xyz);\n // Apply force based on proximity and interaction strength (w)\n finalVelocity += repelDirection * mouseRepelModifier * uInteractionPosition.w * 0.01; // Adjust multiplier\n }\n }\n\n // Optional: Reset position if particle "dies" and respawns (lifespan logic)\n float lifespan = 20.0;\n float age = mod(uTime * 0.1 + lifespan * offset, lifespan); // Adjust time scale\n if (age < 0.05) { // Small window for reset\n finalVelocity = vec3(0.0); // Reset velocity on respawn\n // Note: Resetting position directly here might cause jumps.\n // It\'s often better handled in the position shader or by ensuring\n // strong attraction force when dist is large.\n }\n\n\n gl_FragColor = vec4(finalVelocity, 1.0);\n}\n',this.initialVelocityDataTexture),this.positionVar=this.gpuComputationRenderer.addVariable("uCurrentPosition","\nuniform vec4 uInteractionPosition;\nuniform float uTime;\nuniform float uTractionForce;\nuniform sampler2D uPositionAtlas;\nuniform float uOverallProgress; // (0.0 to 1.0)\nuniform int uNumMeshes;\nuniform float uSingleTextureSize;\n\nfloat rand(vec2 co) {\n return fract(sin(dot(co, vec2(12.9898, 78.233))) * 43758.5453);\n}\n\n// Helper function to get position from atlas\nvec3 getAtlasPosition(vec2 uv, int meshIndex) {\n float atlasWidth = uSingleTextureSize * float(uNumMeshes);\n float atlasHeight = uSingleTextureSize; // Assuming height is single texture size\n\n // Calculate UV within the specific mesh's section of the atlas\n float segmentWidthRatio = uSingleTextureSize / atlasWidth;\n vec2 atlasUV = vec2(\n uv.x * segmentWidthRatio + segmentWidthRatio * float(meshIndex),\n uv.y // Assuming vertical layout doesn't change y\n );\n\n return texture2D(uPositionAtlas, atlasUV).xyz;\n}\n\nvoid main() {\n // GPGPU UV calculation\n vec2 uv = gl_FragCoord.xy / resolution.xy; // resolution is the size of the *output* texture (e.g., 256x256)\n\n vec3 currentPosition = texture2D(uCurrentPosition, uv).xyz;\n vec3 currentVelocity = texture2D(uCurrentVelocity, uv).xyz;\n\n // --- Calculate Target Position from Atlas ---\n vec3 targetPosition;\n if (uNumMeshes <= 1) {\n targetPosition = getAtlasPosition(uv, 0);\n } else {\n float totalSegments = float(uNumMeshes - 1);\n float progressPerSegment = 1.0 / totalSegments;\n float scaledProgress = uOverallProgress * totalSegments;\n\n int indexA = int(floor(scaledProgress));\n // Clamp indexB to avoid going out of bounds\n int indexB = min(indexA + 1, uNumMeshes - 1);\n\n // Ensure indexA is also within bounds (important if uOverallProgress is exactly 1.0)\n indexA = min(indexA, uNumMeshes - 1);\n\n\n float localProgress = fract(scaledProgress);\n\n // Handle edge case where progress is exactly 1.0\n if (uOverallProgress == 1.0) {\n indexA = uNumMeshes - 1;\n indexB = uNumMeshes - 1;\n localProgress = 1.0; // or 0.0 depending on how you want to handle it\n }\n\n\n vec3 positionA = getAtlasPosition(uv, indexA);\n vec3 positionB = getAtlasPosition(uv, indexB);\n\n targetPosition = mix(positionA, positionB, localProgress);\n }\n // --- End Target Position Calculation ---\n\n // Particle attraction to target position\n vec3 direction = normalize(targetPosition - currentPosition);\n float dist = length(targetPosition - currentPosition);\n\n vec3 finalPosition = currentPosition;\n\n // Apply attraction force (simplified mix)\n if (dist > 0.01) { // Only apply if significantly far\n finalPosition = mix(currentPosition, targetPosition, 0.1 * uTractionForce);\n }\n\n finalPosition += currentVelocity;\n gl_FragColor = vec4(finalPosition, 1.0);\n}\n",this.initialPositionDataTexture),this.velocityVar.material.uniforms.uTime={value:0},this.velocityVar.material.uniforms.uInteractionPosition={value:this.interactionPosition},this.velocityVar.material.uniforms.uCurrentPosition={value:null},this.velocityVar.material.uniforms.uTractionForce={value:.1},this.velocityVar.material.uniforms.uMaxRepelDistance={value:.3},this.velocityVar.material.uniforms.uPositionAtlas={value:null},this.velocityVar.material.uniforms.uOverallProgress={value:0},this.velocityVar.material.uniforms.uNumMeshes={value:1},this.velocityVar.material.uniforms.uSingleTextureSize={value:e},this.positionVar.material.uniforms.uTime={value:0},this.positionVar.material.uniforms.uTractionForce={value:.1},this.positionVar.material.uniforms.uInteractionPosition={value:this.interactionPosition},this.positionVar.material.uniforms.uCurrentPosition={value:null},this.positionVar.material.uniforms.uCurrentVelocity={value:null},this.positionVar.material.uniforms.uPositionAtlas={value:null},this.positionVar.material.uniforms.uOverallProgress={value:0},this.positionVar.material.uniforms.uNumMeshes={value:1},this.positionVar.material.uniforms.uSingleTextureSize={value:e},this.gpuComputationRenderer.setVariableDependencies(this.positionVar,[this.positionVar,this.velocityVar]),this.gpuComputationRenderer.setVariableDependencies(this.velocityVar,[this.velocityVar,this.positionVar]);const s=this.gpuComputationRenderer.init();if(null!==s)throw new Error("Failed to initialize SimulationRenderer: "+s);this.positionVar.material.uniforms.uPositionAtlas.value=this.initialPositionDataTexture,this.positionVar.material.uniforms.uNumMeshes.value=1,this.velocityVar.material.uniforms.uNumMeshes.value=1,this.positionVar.material.uniforms.uSingleTextureSize.value=e,this.velocityVar.material.uniforms.uSingleTextureSize.value=e,this.positionVar.material.uniforms.uCurrentVelocity.value=this.gpuComputationRenderer.getCurrentRenderTarget(this.velocityVar).texture,this.velocityVar.material.uniforms.uCurrentPosition.value=this.gpuComputationRenderer.getCurrentRenderTarget(this.positionVar).texture,this.lastKnownVelocityDataTexture=this.gpuComputationRenderer.getCurrentRenderTarget(this.velocityVar).texture,this.lastKnownPositionDataTexture=this.gpuComputationRenderer.getCurrentRenderTarget(this.positionVar).texture}setPositionAtlas(e){const t=e.singleTextureSize*e.numMeshes;e.dataTexture.image.width===t&&e.dataTexture.image.height===e.singleTextureSize||console.error(`SimulationRenderer: Atlas texture dimension mismatch! Expected ${t}x${e.singleTextureSize}, Got ${e.dataTexture.image.width}x${e.dataTexture.image.height}`),this.positionAtlasTexture=e.dataTexture;const i=e.numMeshes>0?e.numMeshes:1;this.positionVar.material.uniforms.uPositionAtlas.value=this.positionAtlasTexture,this.positionVar.material.uniforms.uNumMeshes.value=i,this.positionVar.material.uniforms.uSingleTextureSize.value=e.singleTextureSize,this.velocityVar.material.uniforms.uPositionAtlas.value=this.positionAtlasTexture,this.velocityVar.material.uniforms.uNumMeshes.value=i,this.velocityVar.material.uniforms.uSingleTextureSize.value=e.singleTextureSize,this.positionVar.material.uniforms.uCurrentVelocity.value=this.gpuComputationRenderer.getCurrentRenderTarget(this.velocityVar).texture,this.velocityVar.material.uniforms.uCurrentPosition.value=this.gpuComputationRenderer.getCurrentRenderTarget(this.positionVar).texture}setOverallProgress(e){const t=h(e,0,1);this.positionVar.material.uniforms.uOverallProgress.value=t,this.velocityVar.material.uniforms.uOverallProgress.value=t}setMaxRepelDistance(e){this.velocityVar.material.uniforms.uMaxRepelDistance.value=e}setVelocityTractionForce(e){this.velocityVar.material.uniforms.uTractionForce.value=e}setPositionalTractionForce(e){this.positionVar.material.uniforms.uTractionForce.value=e}setInteractionPosition(e){this.interactionPosition.copy(e)}dispose(){var e,t;this.gpuComputationRenderer.dispose(),null==(e=this.initialPositionDataTexture)||e.dispose(),null==(t=this.initialVelocityDataTexture)||t.dispose(),this.positionAtlasTexture=null}compute(e){this.velocityVar.material.uniforms.uTime.value+=e,this.positionVar.material.uniforms.uTime.value+=e,this.positionVar.material.uniforms.uCurrentVelocity.value=this.gpuComputationRenderer.getCurrentRenderTarget(this.velocityVar).texture,this.velocityVar.material.uniforms.uCurrentPosition.value=this.gpuComputationRenderer.getCurrentRenderTarget(this.positionVar).texture,this.gpuComputationRenderer.compute(),this.lastKnownVelocityDataTexture=this.gpuComputationRenderer.getCurrentRenderTarget(this.velocityVar).texture,this.lastKnownPositionDataTexture=this.gpuComputationRenderer.getCurrentRenderTarget(this.positionVar).texture}getVelocityTexture(){return this.lastKnownVelocityDataTexture}getPositionTexture(){return this.lastKnownPositionDataTexture}}class R{constructor(e,t,i){n(this,"state"),n(this,"textureSize"),n(this,"overallProgress"),n(this,"velocityTractionForce"),n(this,"positionalTractionForce"),n(this,"simulationRenderer"),n(this,"webGLRenderer"),n(this,"eventEmitter"),n(this,"currentAtlasEntry",null),n(this,"lastKnownVelocityDataTexture"),n(this,"lastKnownPositionDataTexture"),this.eventEmitter=e,this.webGLRenderer=i,this.textureSize=t,this.overallProgress=0,this.velocityTractionForce=.1,this.positionalTractionForce=.1,this.updateServiceState("initializing"),this.simulationRenderer=new V(this.textureSize,this.webGLRenderer),this.lastKnownVelocityDataTexture=this.simulationRenderer.getVelocityTexture(),this.lastKnownPositionDataTexture=this.simulationRenderer.getPositionTexture(),this.updateServiceState("ready")}setPositionAtlas(e){const t=e.singleTextureSize*e.numMeshes;e.dataTexture.image.width===t?(this.currentAtlasEntry=e,this.simulationRenderer.setPositionAtlas(e)):this.eventEmitter.emit("invalidRequest",{message:"Atlas texture width mismatch."})}setOverallProgress(e){this.overallProgress=e,this.simulationRenderer.setOverallProgress(this.overallProgress)}setTextureSize(e){this.updateServiceState("initializing"),this.simulationRenderer.dispose(),this.textureSize=e,this.simulationRenderer=new V(e,this.webGLRenderer),this.updateServiceState("ready")}setVelocityTractionForce(e){this.velocityTractionForce=e,this.simulationRenderer.setVelocityTractionForce(this.velocityTractionForce)}setPositionalTractionForce(e){this.positionalTractionForce=e,this.simulationRenderer.setPositionalTractionForce(this.positionalTractionForce)}compute(e){"ready"===this.state&&(this.simulationRenderer.compute(e),this.lastKnownVelocityDataTexture=this.simulationRenderer.getVelocityTexture(),this.lastKnownPositionDataTexture=this.simulationRenderer.getPositionTexture())}getVelocityTexture(){return this.lastKnownVelocityDataTexture}getPositionTexture(){return this.lastKnownPositionDataTexture}dispose(){this.updateServiceState("disposed"),this.simulationRenderer.dispose(),this.currentAtlasEntry=null}updateServiceState(e){this.state=e,this.eventEmitter.emit("serviceStateUpdated",{type:"simulation",state:e})}setInteractionPosition(e){this.simulationRenderer.setInteractionPosition(e)}setMaxRepelDistance(e){this.simulationRenderer.setMaxRepelDistance(e)}}class C{constructor(){n(this,"execStatus",new Map)}get(e){const t=this.execStatus.get(e);return t||(this.execStatus.set(e,"idle"),"idle")}set(e,t){this.execStatus.set(e,t)}}class D{constructor(e){n(this,"eventEmitter"),n(this,"transitions",new Map),n(this,"execStatus"),n(this,"ongoingTransitions",new Map),this.eventEmitter=e,this.execStatus=new C,this.eventEmitter.on("transitionCancelled",this.handleTransitionCancelledEvent.bind(this))}enqueue(e,t,i={}){const s={...t,...i,cancelled:!1,duration:.001*t.duration};this.getQueue(e).push(s)}compute(e){this.transitions.forEach(((t,i)=>{var s;if(t.length&&!this.ongoingTransitions.has(i)){const n=t.shift();n&&(this.ongoingTransitions.set(i,{...n,startTime:e}),null==(s=n.onTransitionBegin)||s.call(n))}})),this.ongoingTransitions.forEach(((t,i)=>{var s,n,r;if(t.cancelled)return null==(s=t.onTransitionCancelled)||s.call(t),void this.ongoingTransitions.delete(i);const{startTime:a,duration:o,easing:l}=t,u=e-a,c=h(l(Math.min(1,u/o)),0,1);this.emitTransitionProgress(i,c),null==(n=t.onTransitionProgress)||n.call(t,c),c>=1&&(this.emitTransitionFinished(i),null==(r=t.onTransitionFinished)||r.call(t),this.ongoingTransitions.delete(i))}))}getQueue(e){const t=this.transitions.get(e);return t||(this.transitions.set(e,[]),this.transitions.get(e)??[])}handleTransitionCancelledEvent({type:e}){var t;const i=this.getQueue(e);for(;i.length;)i.pop();const s=this.ongoingTransitions.get(e);s&&(s.cancelled=!0,null==(t=s.onTransitionCancelled)||t.call(s))}emitTransitionProgress(e,t){this.eventEmitter.emit("transitionProgressed",{type:e,progress:t})}emitTransitionFinished(e){this.eventEmitter.emit("transitionFinished",{type:e})}}return e.ParticlesEngine=class{constructor(e){n(this,"simulationRendererService"),n(this,"renderer"),n(this,"scene"),n(this,"serviceStates"),n(this,"assetService"),n(this,"dataTextureManager"),n(this,"instancedMeshManager"),n(this,"transitionService"),n(this,"engineState"),n(this,"intersectionService"),n(this,"meshSequenceAtlasTexture",null),n(this,"eventEmitter");const{scene:t,renderer:i,camera:s,textureSize:r,useIntersection:a=!0}=e;this.eventEmitter=new l,this.serviceStates=this.getInitialServiceStates(),this.eventEmitter.on("serviceStateUpdated",this.handleServiceStateUpdated.bind(this)),this.scene=t,this.renderer=i,this.engineState=this.initialEngineState(e),this.assetService=new d(this.eventEmitter),this.transitionService=new D(this.eventEmitter),this.dataTextureManager=new y(this.eventEmitter,r),this.simulationRendererService=new R(this.eventEmitter,r,this.renderer),this.instancedMeshManager=new T(r),this.instancedMeshManager.useMatcapMaterial(),this.scene.add(this.instancedMeshManager.getMesh()),this.intersectionService=new S(this.eventEmitter,s),a||this.intersectionService.setActive(!1),this.eventEmitter.on("interactionPositionUpdated",this.handleInteractionPositionUpdated.bind(this))}render(e){const t=e/1e3;this.transitionService.compute(t),this.intersectionService.calculate(this.instancedMeshManager.getMesh()),this.simulationRendererService.compute(t),this.instancedMeshManager.update(t),this.instancedMeshManager.updateVelocityTexture(this.simulationRendererService.getVelocityTexture()),this.instancedMeshManager.updatePositionTexture(this.simulationRendererService.getPositionTexture())}setOriginMatcap(e,t=!1){t&&this.eventEmitter.emit("transitionCancelled",{type:"matcap"}),this.engineState.originMatcapID=e,this.instancedMeshManager.setOriginMatcap(this.assetService.getMatcap(e))}setDestinationMatcap(e,t=!1){t&&this.eventEmitter.emit("transitionCancelled",{type:"matcap"}),this.engineState.destinationMatcapID=e,this.instancedMeshManager.setDestinationMatcap(this.assetService.getMatcap(e))}setOriginColor(e,t=!1){t&&this.eventEmitter.emit("transitionCancelled",{type:"matcap"}),this.instancedMeshManager.setOriginColor(e)}setDestinationColor(e,t=!1){t&&this.eventEmitter.emit("transitionCancelled",{type:"matcap"}),this.instancedMeshManager.setDestinationColor(e)}setOriginTexture(e,t=!1){t&&this.eventEmitter.emit("transitionCancelled",{type:"matcap"}),"string"==typeof e&&this.assetService.hasMatcap(e)?this.setOriginMatcap(e):this.setOriginColor(e)}setDestinationTexture(e,t=!1){t&&this.eventEmitter.emit("transitionCancelled",{type:"matcap"}),"string"==typeof e&&this.assetService.hasMatcap(e)?this.setDestinationMatcap(e):this.setDestinationColor(e)}setMatcapProgress(e,t=!1){const i=h(e,0,1);t&&this.eventEmitter.emit("transitionCancelled",{type:"matcap"}),this.engineState.matcapTransitionProgress=i,this.instancedMeshManager.setProgress(i)}async setTextureSize(e){this.engineState.textureSize!==e&&(this.engineState.textureSize=e,this.dataTextureManager.setTextureSize(e),this.simulationRendererService.setTextureSize(e),this.instancedMeshManager.resize(e),this.engineState.meshSequence.length>0&&await this.setMeshSequence(this.engineState.meshSequence),this.engineState.meshSequence.length>0&&await this.setMeshSequence(this.engineState.meshSequence),this.simulationRendererService.setVelocityTractionForce(this.engineState.velocityTractionForce),this.simulationRendererService.setPositionalTractionForce(this.engineState.positionalTractionForce),this.simulationRendererService.setMaxRepelDistance(this.engineState.maxRepelDistance),this.simulationRendererService.setOverallProgress(this.engineState.overallProgress),this.intersectionService.setOverallProgress(this.engineState.overallProgress),this.instancedMeshManager.setOriginMatcap(this.assetService.getMatcap(this.engineState.originMatcapID)),this.instancedMeshManager.setDestinationMatcap(this.assetService.getMatcap(this.engineState.destinationMatcapID)),this.instancedMeshManager.setProgress(this.engineState.matcapTransitionProgress),this.instancedMeshManager.setGeometrySize(this.engineState.instanceGeometryScale))}registerMesh(e,t){this.assetService.register(e,t)}registerMatcap(e,t){this.assetService.register(e,t)}async fetchAndRegisterMesh(e,t){return await this.assetService.loadMeshAsync(e,t)}async fetchAndRegisterMatcap(e,t){return await this.assetService.loadTextureAsync(e,t)}useIntersect(e){this.intersectionService.setActive(e),this.engineState.useIntersect=e,e||(this.engineState.pointerPosition={x:-99999999,y:-99999999},this.simulationRendererService.setInteractionPosition({x:0,y:0,z:0,w:0}))}setPointerPosition(e){this.engineState.useIntersect&&(this.engineState.pointerPosition=e,this.intersectionService.setPointerPosition(e))}setGeometrySize(e){this.engineState.instanceGeometryScale=e,this.instancedMeshManager.setGeometrySize(e)}setVelocityTractionForce(e){this.engineState.velocityTractionForce=e,this.simulationRendererService.setVelocityTractionForce(e)}setPositionalTractionForce(e){this.engineState.positionalTractionForce=e,this.simulationRendererService.setPositionalTractionForce(e)}setMaxRepelDistance(e){this.engineState.maxRepelDistance=e,this.simulationRendererService.setMaxRepelDistance(e)}async setMeshSequence(e){if(!e||e.length<1)return this.eventEmitter.emit("invalidRequest",{message:"Mesh sequence must contain at least one mesh ID."}),this.engineState.meshSequence=[],void this.intersectionService.setMeshSequence([]);this.engineState.meshSequence=e,this.engineState.overallProgress=0;const t=e.map((e=>this.assetService.getMesh(e))).filter((e=>null!==e));if(t.length!==e.length){const i=e.filter((e=>!this.assetService.getMesh(e)));if(console.warn(`Could not find meshes for IDs: ${i.join(", ")}. Proceeding with ${t.length} found meshes.`),this.eventEmitter.emit("invalidRequest",{message:`Could not find meshes for IDs: ${i.join(", ")}`}),t.length<1)return this.engineState.meshSequence=[],void this.intersectionService.setMeshSequence([]);this.engineState.meshSequence=t.map((e=>e.name))}try{this.meshSequenceAtlasTexture=await this.dataTextureManager.createSequenceDataTextureAtlas(t,this.engineState.textureSize),this.simulationRendererService.setPositionAtlas({dataTexture:this.meshSequenceAtlasTexture,textureSize:this.engineState.textureSize,numMeshes:this.engineState.meshSequence.length,singleTextureSize:this.engineState.textureSize}),this.simulationRendererService.setOverallProgress(this.engineState.overallProgress),this.intersectionService.setMeshSequence(t),this.intersectionService.setOverallProgress(this.engineState.overallProgress)}catch(i){console.error("Failed during mesh sequence setup:",i),this.meshSequenceAtlasTexture=null}}setOverallProgress(e,t=!0){t&&this.eventEmitter.emit("transitionCancelled",{type:"mesh-sequence"});const i=h(e,0,1);this.engineState.overallProgress=i,this.simulationRendererService.setOverallProgress(i),this.intersectionService.setOverallProgress(i)}scheduleMatcapTransition(e,t,i=o,s=1e3,n=!1,r={}){n&&this.eventEmitter.emit("transitionCancelled",{type:"matcap"});this.transitionService.enqueue("matcap",{easing:i,duration:s},{...r,onTransitionProgress:e=>{var t;this.setMatcapProgress(e,!1),null==(t=r.onTransitionProgress)||t.call(r,e)},onTransitionBegin:()=>{var i;this.setOriginMatcap(e,!1),this.setDestinationMatcap(t,!1),this.setMatcapProgress(0,!1),null==(i=r.onTransitionBegin)||i.call(r)},onTransitionFinished:()=>{var e;this.setMatcapProgress(1,!1),null==(e=r.onTransitionFinished)||e.call(r)},onTransitionCancelled:r.onTransitionCancelled})}scheduleTextureTransition(e,t,i={}){const s=(null==i?void 0:i.easing)??o,n=(null==i?void 0:i.duration)??1e3,r={onTransitionBegin:null==i?void 0:i.onTransitionBegin,onTransitionProgress:null==i?void 0:i.onTransitionProgress,onTransitionFinished:null==i?void 0:i.onTransitionFinished,onTransitionCancelled:null==i?void 0:i.onTransitionCancelled};(null==i?void 0:i.override)&&this.eventEmitter.emit("transitionCancelled",{type:"matcap"});this.transitionService.enqueue("matcap",{easing:s,duration:n},{...r,onTransitionProgress:e=>{var t;this.setMatcapProgress(e,!1),null==(t=r.onTransitionProgress)||t.call(r,e)},onTransitionBegin:()=>{var i;this.setOriginTexture(e),this.setDestinationTexture(t),this.setMatcapProgress(0),null==(i=r.onTransitionBegin)||i.call(r)},onTransitionFinished:()=>{var e;this.setMatcapProgress(1),null==(e=r.onTransitionFinished)||e.call(r)},onTransitionCancelled:()=>{var e;null==(e=r.onTransitionCancelled)||e.call(r)}})}scheduleMeshSequenceTransition(e,t=1e3,i=o,s={},n=!0){n&&this.eventEmitter.emit("transitionCancelled",{type:"mesh-sequence"});const r=this.engineState.overallProgress,a=e-r,l={duration:t,easing:i},u={...s,onTransitionProgress:e=>{var t;const i=r+a*e;this.setOverallProgress(i,!1),null==(t=s.onTransitionProgress)||t.call(s,i)},onTransitionBegin:s.onTransitionBegin,onTransitionFinished:()=>{var t;this.setOverallProgress(e,!1),null==(t=s.onTransitionFinished)||t.call(s)},onTransitionCancelled:s.onTransitionCancelled};this.transitionService.enqueue("mesh-sequence",l,u)}handleServiceStateUpdated({type:e,state:t}){this.serviceStates[e]=t}getObject(){return this.instancedMeshManager.getMesh()}getMeshIDs(){return this.assetService.getMeshIDs()}getMatcapIDs(){return this.assetService.getTextureIDs()}getMeshes(){return this.assetService.getMeshes()}getTextures(){return this.assetService.getTextures()}getTextureSize(){return this.engineState.textureSize}getUseIntersect(){return this.engineState.useIntersect}getEngineStateSnapshot(){return{...this.engineState}}dispose(){var e,t,i,s,n,r;this.scene&&this.instancedMeshManager&&this.scene.remove(this.instancedMeshManager.getMesh()),null==(e=this.simulationRendererService)||e.dispose(),null==(t=this.instancedMeshManager)||t.dispose(),null==(i=this.intersectionService)||i.dispose(),null==(s=this.assetService)||s.dispose(),null==(n=this.dataTextureManager)||n.dispose(),null==(r=this.eventEmitter)||r.dispose()}initialEngineState(e){return{textureSize:e.textureSize,meshSequence:[],overallProgress:0,originMatcapID:"",destinationMatcapID:"",matcapTransitionProgress:0,velocityTractionForce:.1,positionalTractionForce:.1,maxRepelDistance:.3,pointerPosition:{x:0,y:0},instanceGeometryScale:{x:1,y:1,z:1},useIntersect:e.useIntersection??!0}}getInitialServiceStates(){return{"data-texture":"created","instanced-mesh":"created",matcap:"created",simulation:"created",asset:"created"}}handleInteractionPositionUpdated({position:e}){this.simulationRendererService.setInteractionPosition(e)}},Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),e}({},THREE,three-stdlib);
1
+ var ionian=function(e,t,i){"use strict";var s=Object.defineProperty,r=(e,t,i)=>((e,t,i)=>t in e?s(e,t,{enumerable:!0,configurable:!0,writable:!0,value:i}):e[t]=i)(e,"symbol"!=typeof t?t+"":t,i);function n(e){const t=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(e)for(const i in e)if("default"!==i){const s=Object.getOwnPropertyDescriptor(e,i);Object.defineProperty(t,i,s.get?s:{enumerable:!0,get:()=>e[i]})}return t.default=e,Object.freeze(t)}const o=n(t),a=e=>e;class l{constructor(){var e;r(this,"emitter",{all:e=e||new Map,on:function(t,i){var s=e.get(t);s?s.push(i):e.set(t,[i])},off:function(t,i){var s=e.get(t);s&&(i?s.splice(s.indexOf(i)>>>0,1):e.set(t,[]))},emit:function(t,i){var s=e.get(t);s&&s.slice().map((function(e){e(i)})),(s=e.get("*"))&&s.slice().map((function(e){e(t,i)}))}})}emit(e,t){this.emitter.emit(e,t)}off(e,t){this.emitter.off(e,t)}on(e,t){this.emitter.on(e,t)}once(e,t){this.emitter.on(e,(i=>{this.emitter.off(e,t),t(i)}))}dispose(){this.emitter.all.clear()}}function u(e,t){const i=new o.DataTexture(e,t,t,o.RGBAFormat,o.FloatType);return i.needsUpdate=!0,i}function c(e){e.geometry.dispose(),e.material instanceof o.Material?e.material.dispose():e.material.forEach((e=>e.dispose()))}function h(e,t,i){return e=Math.min(e,i),e=Math.max(e,t)}class d{constructor(e){r(this,"serviceState","created"),r(this,"eventEmitter"),r(this,"meshes",new Map),r(this,"textures",new Map),r(this,"gltfLoader",new i.GLTFLoader),r(this,"textureLoader",new o.TextureLoader),r(this,"dracoLoader",new i.DRACOLoader),r(this,"solidColorTextures",new Map),r(this,"fallbackTexture",new o.DataTexture(new Uint8Array([127,127,127,255]),1,1,o.RGBAFormat)),this.eventEmitter=e,this.dracoLoader.setDecoderPath("https://www.gstatic.com/draco/versioned/decoders/1.5.7/"),this.gltfLoader.setDRACOLoader(this.dracoLoader),this.fallbackTexture.name="default-fallback-texture",this.updateServiceState("ready")}register(e,t){if(t.name=e,t instanceof o.Mesh){const i=this.meshes.get(e);i&&c(i),this.meshes.set(e,t)}else{const i=this.textures.get(e);i&&i.dispose(),this.textures.set(e,t)}this.eventEmitter.emit("assetRegistered",{id:e})}getMesh(e){return this.meshes.get(e)??null}getMatcapTexture(e){const t=this.textures.get(e);return t||this.eventEmitter.emit("invalidRequest",{message:`texture with id "${e}" not found. using solid color texture instead...`}),t??this.fallbackTexture}getSolidColorTexture(e){const t=new o.Color(e).getHexString();let i=this.solidColorTextures.get(t);if(i)return i;try{const i=this.createSolidColorDataTexture(new o.Color(e));return this.solidColorTextures.set(t,i),i}catch(s){return console.error(`Invalid color value provided to getSolidColorTexture: ${e}`,s),this.eventEmitter.emit("invalidRequest",{message:`Invalid color value: ${e}. Using fallback texture.`}),this.fallbackTexture}}getFallbackTexture(){return this.fallbackTexture}getMeshIDs(){return Array.from(this.meshes.keys())}getTextureIDs(){return Array.from(this.textures.keys())}getMeshes(){return Array.from(this.meshes.values())}getTextures(){return Array.from(this.textures.values())}createSolidColorDataTexture(e,t=16){const i=new o.Color(e),s=t,r=t,n=new Uint8Array(s*r*4),a=Math.floor(255*i.r),l=Math.floor(255*i.g),u=Math.floor(255*i.b);for(let o=0;o<s*r;o++){const e=4*o;n[e]=a,n[e+1]=l,n[e+2]=u,n[e+3]=255}const c=new o.DataTexture(n,s,r,o.RGBAFormat);return c.type=o.UnsignedByteType,c.wrapS=o.RepeatWrapping,c.wrapT=o.RepeatWrapping,c.minFilter=o.NearestFilter,c.magFilter=o.NearestFilter,c.needsUpdate=!0,c}async loadMeshAsync(e,t,i={}){const s=await this.gltfLoader.loadAsync(t);try{if(i.meshName){const t=s.scene.getObjectByName(i.meshName);return this.register(e,t),t}{const t=s.scene.children[0];return this.register(e,t),t}}catch(r){return this.eventEmitter.emit("invalidRequest",{message:`failed to load mesh: ${e}. ${r}`}),null}}async loadTextureAsync(e,t){try{const i=await this.textureLoader.loadAsync(t);return this.register(e,i),i}catch(i){return this.eventEmitter.emit("invalidRequest",{message:`failed to load texture: ${e}. ${i}`}),null}}dispose(){this.updateServiceState("disposed"),this.meshes.forEach((e=>c(e))),this.meshes.clear(),this.textures.forEach((e=>e.dispose())),this.textures.clear(),this.solidColorTextures.forEach((e=>e.dispose())),this.solidColorTextures.clear(),this.fallbackTexture.dispose()}updateServiceState(e){this.serviceState=e,this.eventEmitter.emit("serviceStateUpdated",{type:"asset",state:e})}}const m=new t.Triangle,g=new t.Vector3,v=new t.Vector2,p=new t.Vector2,f=new t.Vector2;class x{constructor(e){this.geometry=e.geometry,this.randomFunction=Math.random,this.indexAttribute=this.geometry.index,this.positionAttribute=this.geometry.getAttribute("position"),this.normalAttribute=this.geometry.getAttribute("normal"),this.colorAttribute=this.geometry.getAttribute("color"),this.uvAttribute=this.geometry.getAttribute("uv"),this.weightAttribute=null,this.distribution=null}setWeightAttribute(e){return this.weightAttribute=e?this.geometry.getAttribute(e):null,this}build(){const e=this.indexAttribute,t=this.positionAttribute,i=this.weightAttribute,s=e?e.count/3:t.count/3,r=new Float32Array(s);for(let a=0;a<s;a++){let s=1,n=3*a,o=3*a+1,l=3*a+2;e&&(n=e.getX(n),o=e.getX(o),l=e.getX(l)),i&&(s=i.getX(n)+i.getX(o)+i.getX(l)),m.a.fromBufferAttribute(t,n),m.b.fromBufferAttribute(t,o),m.c.fromBufferAttribute(t,l),s*=m.getArea(),r[a]=s}const n=new Float32Array(s);let o=0;for(let a=0;a<s;a++)o+=r[a],n[a]=o;return this.distribution=n,this}setRandomGenerator(e){return this.randomFunction=e,this}sample(e,t,i,s){const r=this._sampleFaceIndex();return this._sampleFace(r,e,t,i,s)}_sampleFaceIndex(){const e=this.distribution[this.distribution.length-1];return this._binarySearch(this.randomFunction()*e)}_binarySearch(e){const t=this.distribution;let i=0,s=t.length-1,r=-1;for(;i<=s;){const n=Math.ceil((i+s)/2);if(0===n||t[n-1]<=e&&t[n]>e){r=n;break}e<t[n]?s=n-1:i=n+1}return r}_sampleFace(e,t,i,s,r){let n=this.randomFunction(),o=this.randomFunction();n+o>1&&(n=1-n,o=1-o);const a=this.indexAttribute;let l=3*e,u=3*e+1,c=3*e+2;return a&&(l=a.getX(l),u=a.getX(u),c=a.getX(c)),m.a.fromBufferAttribute(this.positionAttribute,l),m.b.fromBufferAttribute(this.positionAttribute,u),m.c.fromBufferAttribute(this.positionAttribute,c),t.set(0,0,0).addScaledVector(m.a,n).addScaledVector(m.b,o).addScaledVector(m.c,1-(n+o)),void 0!==i&&(void 0!==this.normalAttribute?(m.a.fromBufferAttribute(this.normalAttribute,l),m.b.fromBufferAttribute(this.normalAttribute,u),m.c.fromBufferAttribute(this.normalAttribute,c),i.set(0,0,0).addScaledVector(m.a,n).addScaledVector(m.b,o).addScaledVector(m.c,1-(n+o)).normalize()):m.getNormal(i)),void 0!==s&&void 0!==this.colorAttribute&&(m.a.fromBufferAttribute(this.colorAttribute,l),m.b.fromBufferAttribute(this.colorAttribute,u),m.c.fromBufferAttribute(this.colorAttribute,c),g.set(0,0,0).addScaledVector(m.a,n).addScaledVector(m.b,o).addScaledVector(m.c,1-(n+o)),s.r=g.x,s.g=g.y,s.b=g.z),void 0!==r&&void 0!==this.uvAttribute&&(v.fromBufferAttribute(this.uvAttribute,l),p.fromBufferAttribute(this.uvAttribute,u),f.fromBufferAttribute(this.uvAttribute,c),r.set(0,0).addScaledVector(v,n).addScaledVector(p,o).addScaledVector(f,1-(n+o))),this}}class y{constructor(e,t){r(this,"textureSize"),r(this,"dataTextures"),r(this,"eventEmitter"),r(this,"currentAtlas",null),this.eventEmitter=e,this.textureSize=t,this.dataTextures=new Map,this.updateServiceState("ready")}setTextureSize(e){this.textureSize!==e&&(this.textureSize=e,this.dataTextures.forEach((e=>e.dispose())),this.dataTextures.clear(),this.currentAtlas&&(this.currentAtlas.dispose(),this.currentAtlas=null))}async getDataTexture(e){const t=this.dataTextures.get(e.uuid);if(t)return t;var i,s;const r=function(e,t){const i=new o.BufferGeometry;i.setAttribute("position",new o.BufferAttribute(new Float32Array(e.position),3)),e.normal&&i.setAttribute("normal",new o.BufferAttribute(new Float32Array(e.normal),3));const s=new o.MeshBasicMaterial,r=new o.Mesh(i,s);r.scale.set(e.scale.x,e.scale.y,e.scale.z);const n=new x(r).build(),a=new Float32Array(t*t*4),l=new o.Vector3;for(let o=0;o<t;o++)for(let i=0;i<t;i++){const s=o*t+i;n.sample(l),a[4*s]=l.x*e.scale.x,a[4*s+1]=l.y*e.scale.y,a[4*s+2]=l.z*e.scale.z,a[4*s+3]=.01*(Math.random()-.5)}return a}({position:(i=e).geometry.attributes.position.array,normal:null==(s=i.geometry.attributes.normal)?void 0:s.array,scale:{x:i.scale.x,y:i.scale.y,z:i.scale.z}},this.textureSize),n=u(r,this.textureSize);return n.name=e.name,this.dataTextures.set(e.uuid,n),n}async dispose(){this.dataTextures.forEach((e=>e.dispose())),this.dataTextures.clear(),this.currentAtlas&&(this.currentAtlas.dispose(),this.currentAtlas=null),this.updateServiceState("disposed")}updateServiceState(e){this.eventEmitter.emit("serviceStateUpdated",{type:"data-texture",state:e})}async createSequenceDataTextureAtlas(e,t){this.updateServiceState("loading"),this.currentAtlas&&(this.currentAtlas.dispose(),this.currentAtlas=null);const i=e.length;if(0===i)throw new Error("Mesh array cannot be empty.");const s=t*i,r=t,n=new Float32Array(s*r*4);try{for(let r=0;r<i;r++){const i=e[r],o=(await this.getDataTexture(i)).image.data;for(let e=0;e<t;e++)for(let i=0;i<t;i++){const a=4*(e*t+i),l=4*(e*s+(i+r*t));n[l]=o[a],n[l+1]=o[a+1],n[l+2]=o[a+2],n[l+3]=o[a+3]}}const a=new o.DataTexture(n,s,r,o.RGBAFormat,o.FloatType);return a.needsUpdate=!0,a.name=`atlas-${e.map((e=>e.name)).join("-")}`,this.currentAtlas=a,this.updateServiceState("ready"),a}catch(a){throw this.updateServiceState("error"),a}}}class S{constructor(e){r(this,"size"),r(this,"mesh"),r(this,"shaderMaterial"),r(this,"fallbackGeometry"),r(this,"uniforms"),r(this,"geometries"),r(this,"uvRefsCache"),r(this,"previousScale"),this.size=e,this.geometries=new Map,this.uvRefsCache=new Map,this.previousScale={x:1,y:1,z:1},this.uniforms={uTime:{value:0},uProgress:{value:0},uTexture:{value:null},uVelocity:{value:null},uOriginTexture:{value:null},uDestinationTexture:{value:null}},this.shaderMaterial=new o.ShaderMaterial({uniforms:this.uniforms,vertexShader:"\nvarying vec2 vUv;\nuniform sampler2D uTexture;\nuniform sampler2D uVelocity;\nuniform float uTime;\nvarying vec3 vNormal;\nattribute vec2 uvRef;\nvarying vec3 vViewPosition;\n\nvec3 rotate3D(vec3 v, vec3 vel) {\n vec3 pos = v;\n vec3 up = vec3(0, 1, 0);\n vec3 axis = normalize(cross(up, vel));\n float angle = acos(dot(up, normalize(vel)));\n pos = pos * cos(angle) + cross(axis, pos) * sin(angle) + axis * dot(axis, pos) * (1. - cos(angle));\n return pos;\n}\n\nvoid main() {\n vUv = uv;\n vNormal = normal;\n\n vec4 color = texture2D(uTexture, uvRef);\n vec4 velocity = texture2D(uVelocity, uvRef);\n vec3 pos = color.xyz;// apply the texture to the vertex distribution.\n\n vec3 localPosition = position.xyz;\n if (length (velocity.xyz) < 0.0001) {\n velocity.xyz = vec3(0.0, 0.0001, 0.0001);\n }\n localPosition.y *= max(1.0, length(velocity.xyz) * 1000.0);\n localPosition = rotate3D(localPosition, velocity.xyz);\n vNormal = rotate3D(normal, velocity.xyz);\n\n mat4 instanceMat = instanceMatrix;\n instanceMat[3].xyz = pos.xyz;\n\n // unlike the traditional mvMatrix * position, we need to additional multiplication with the instance matrix.\n vec4 modelViewPosition = modelViewMatrix * instanceMat * vec4(localPosition, 1.0);\n\n vViewPosition = - modelViewPosition.xyz;\n\n gl_Position = projectionMatrix * modelViewPosition;\n}\n",fragmentShader:"\nvarying vec2 vUv;\n\nuniform sampler2D uOriginTexture;\nuniform sampler2D uDestinationTexture;\n\nuniform float uProgress;\nvarying vec3 vNormal;\nvarying vec3 vViewPosition;\nvoid main() {\n vec3 viewDir = normalize( vViewPosition );\n vec3 x = normalize( vec3( viewDir.z, 0.0, - viewDir.x ) );\n vec3 y = cross( viewDir, x );\n vec2 uv = vec2( dot( x, vNormal ), dot( y, vNormal ) ) * 0.495 + 0.5; // 0.495 to remove artifacts caused by undersized matcap disks\n\n vec4 textureA = texture2D( uOriginTexture, uv );\n vec4 textureB = texture2D( uDestinationTexture, uv );\n\n vec4 finalColor = mix(textureA, textureB, uProgress);\n gl_FragColor = finalColor;\n}\n"}),this.fallbackGeometry=new o.BoxGeometry(.001,.001,.001),this.mesh=this.createInstancedMesh(e,this.fallbackGeometry,this.shaderMaterial),this.mesh.material=this.shaderMaterial}getMesh(){return this.mesh}update(e){const t=this.mesh.material;(t instanceof o.ShaderMaterial||t instanceof o.RawShaderMaterial)&&(t.uniforms.uTime.value=e)}updateTextureInterpolation(e,t,i){this.uniforms.uOriginTexture.value=e,this.uniforms.uDestinationTexture.value=t,this.uniforms.uProgress.value=i}setGeometrySize(e){this.mesh.geometry.scale(1/this.previousScale.x,1/this.previousScale.y,1/this.previousScale.z),this.mesh.geometry.scale(e.x,e.y,e.z),this.previousScale=e}useMatcapMaterial(){this.mesh.material=this.shaderMaterial}useGeometry(e){const t=this.geometries.get(e);t&&(this.mesh.geometry=t)}updateVelocityTexture(e){this.shaderMaterial.uniforms.uVelocity.value=e}updatePositionTexture(e){this.shaderMaterial.uniforms.uTexture.value=e}resize(e){if(this.size===e)return{current:this.mesh,previous:this.mesh};this.size=e;const t=this.mesh;return this.mesh=this.createInstancedMesh(e,t.geometry,t.material),{current:this.mesh,previous:t}}dispose(){this.mesh.dispose(),this.geometries.forEach((e=>e.dispose())),this.shaderMaterial.dispose(),this.uvRefsCache.clear(),this.geometries.clear()}registerGeometry(e,t){const i=this.geometries.get(e);if(i&&i===t)return;const s=this.createUVRefs(this.size);t.setAttribute("uvRef",s),this.geometries.set(e,t),this.mesh.geometry===i&&(this.mesh.geometry=t),null==i||i.dispose()}createUVRefs(e){const t=this.uvRefsCache.get(e);if(t)return t;const i=new Float32Array(e*e*2);for(let r=0;r<e;r++)for(let t=0;t<e;t++){const s=r*e+t;i[2*s]=t/(e-1),i[2*s+1]=r/(e-1)}const s=new o.InstancedBufferAttribute(i,2);return this.uvRefsCache.set(e,s),s}createInstancedMesh(e,t,i){(t=t||this.fallbackGeometry).setAttribute("uvRef",this.createUVRefs(e));const s=e*e;return new o.InstancedMesh(t,i,s)}}class T{constructor(e,t){r(this,"active",!0),r(this,"raycaster",new o.Raycaster),r(this,"mousePosition",new o.Vector2),r(this,"camera"),r(this,"meshSequenceGeometries",[]),r(this,"meshSequenceUUIDs",[]),r(this,"overallProgress",0),r(this,"intersectionMesh",new o.Mesh),r(this,"geometryNeedsUpdate"),r(this,"eventEmitter"),r(this,"blendedGeometry"),r(this,"intersection"),this.camera=t,this.eventEmitter=e,this.geometryNeedsUpdate=!0}setActive(e){this.active=e,e||(this.intersection=void 0,this.eventEmitter.emit("interactionPositionUpdated",{position:{x:0,y:0,z:0,w:0}}))}getIntersectionMesh(){return this.intersectionMesh}setCamera(e){this.camera=e}setMeshSequence(e){this.meshSequenceGeometries.forEach((e=>e.dispose())),this.meshSequenceGeometries=[],this.meshSequenceUUIDs=[],e&&0!==e.length?(e.forEach((e=>{if(e&&e.geometry){const t=e.geometry.clone();t.applyMatrix4(e.matrixWorld),this.meshSequenceGeometries.push(t),this.meshSequenceUUIDs.push(e.uuid)}else console.warn("Invalid mesh provided to IntersectionService sequence.")})),this.geometryNeedsUpdate=!0):this.geometryNeedsUpdate=!0}setOverallProgress(e){const t=o.MathUtils.clamp(e,0,1);this.overallProgress!==t&&(this.overallProgress=t,this.geometryNeedsUpdate=!0)}setPointerPosition(e){e&&this.mousePosition.copy(e)}calculate(e){var t,i,s;if(!this.active||!this.camera||0===this.meshSequenceGeometries.length)return void(this.intersection&&(this.intersection=void 0,this.eventEmitter.emit("interactionPositionUpdated",{position:{x:0,y:0,z:0,w:0}})));let r;this.geometryNeedsUpdate&&(this.blendedGeometry&&this.blendedGeometry!==this.intersectionMesh.geometry&&this.blendedGeometry.dispose(),this.blendedGeometry=this.getBlendedGeometry(),this.geometryNeedsUpdate=!1,this.blendedGeometry?this.intersectionMesh.geometry!==this.blendedGeometry&&(this.intersectionMesh.geometry&&this.intersectionMesh.geometry.dispose(),this.intersectionMesh.geometry=this.blendedGeometry):(this.intersectionMesh.geometry&&this.intersectionMesh.geometry.dispose(),this.intersectionMesh.geometry=new o.BufferGeometry)),this.intersectionMesh.matrixWorld.copy(e.matrixWorld),this.blendedGeometry&&this.blendedGeometry.attributes.position&&(r=this.getFirstIntersection(this.camera,this.intersectionMesh));if((null==(t=this.intersection)?void 0:t.x)!==(null==r?void 0:r.x)||(null==(i=this.intersection)?void 0:i.y)!==(null==r?void 0:r.y)||(null==(s=this.intersection)?void 0:s.z)!==(null==r?void 0:r.z)||this.intersection&&!r||!this.intersection&&r)if(this.intersection=r,this.intersection){const t=new o.Vector3(this.intersection.x,this.intersection.y,this.intersection.z),i=e.worldToLocal(t.clone());this.intersection.set(i.x,i.y,i.z,1),this.eventEmitter.emit("interactionPositionUpdated",{position:this.intersection})}else this.eventEmitter.emit("interactionPositionUpdated",{position:{x:0,y:0,z:0,w:0}});return this.intersection}dispose(){var e;this.meshSequenceGeometries.forEach((e=>e.dispose())),this.meshSequenceGeometries=[],this.meshSequenceUUIDs=[],this.blendedGeometry&&this.blendedGeometry!==this.intersectionMesh.geometry&&this.blendedGeometry.dispose(),null==(e=this.intersectionMesh.geometry)||e.dispose()}updateIntersectionMesh(e){this.blendedGeometry&&this.blendedGeometry.uuid!==this.intersectionMesh.geometry.uuid&&(this.intersectionMesh.geometry.dispose(),this.intersectionMesh.geometry=this.blendedGeometry),this.intersectionMesh.matrix.copy(e.matrixWorld),this.intersectionMesh.matrixWorld.copy(e.matrixWorld),this.intersectionMesh.matrixAutoUpdate=!1,this.intersectionMesh.updateMatrixWorld(!0)}getFirstIntersection(e,t){this.raycaster.setFromCamera(this.mousePosition,e);const i=this.raycaster.intersectObject(t,!1);if(i.length>0&&i[0].point){const e=i[0].point;return new o.Vector4(e.x,e.y,e.z,1)}}getBlendedGeometry(){const e=this.meshSequenceGeometries.length;if(0===e)return;if(1===e)return this.meshSequenceGeometries[0];const t=e-1,i=1/t,s=this.overallProgress*t;let r=Math.floor(s),n=r+1;r=o.MathUtils.clamp(r,0,t),n=o.MathUtils.clamp(n,0,t);let a=0;i>0&&(a=s-r),this.overallProgress>=1&&(r=t,n=t,a=1),a=o.MathUtils.clamp(a,0,1);const l=this.meshSequenceGeometries[r],u=this.meshSequenceGeometries[n];return l&&u?r===n?l:this.blendGeometry(l,u,a):(console.error("IntersectionService: Invalid geometries found for blending at indices",r,n),this.meshSequenceGeometries[0])}blendGeometry(e,t,i){const s=new o.BufferGeometry,r=e.attributes.position.array,n=t.attributes.position.array,a=new Float32Array(r.length);for(let l=0;l<r.length;l+=3){const e=new o.Vector3(r[l],r[l+1],r[l+2]),t=new o.Vector3(n[l],n[l+1],n[l+2]),s=(new o.Vector3).lerpVectors(e,t,i);a[l]=s.x,a[l+1]=s.y,a[l+2]=s.z}return s.setAttribute("position",new o.BufferAttribute(a,3)),e.attributes.normal&&s.setAttribute("normal",e.attributes.normal.clone()),e.attributes.uv&&s.setAttribute("uv",e.attributes.uv.clone()),e.index&&s.setIndex(e.index.clone()),s}}const P=new t.OrthographicCamera(-1,1,1,-1,0,1);class M extends t.BufferGeometry{constructor(){super(),this.setAttribute("position",new t.Float32BufferAttribute([-1,3,0,-1,-1,0,3,-1,0],3)),this.setAttribute("uv",new t.Float32BufferAttribute([0,2,0,0,2,0],2))}}const b=new M;class A{constructor(e){this._mesh=new t.Mesh(b,e)}dispose(){this._mesh.geometry.dispose()}render(e){e.render(this._mesh,P)}get material(){return this._mesh.material}set material(e){this._mesh.material=e}}class w{constructor(e,i,s){this.variables=[],this.currentTextureIndex=0;let r=t.FloatType;const n={passThruTexture:{value:null}},o=u("uniform sampler2D passThruTexture;\n\nvoid main() {\n\n\tvec2 uv = gl_FragCoord.xy / resolution.xy;\n\n\tgl_FragColor = texture2D( passThruTexture, uv );\n\n}\n",n),a=new A(o);function l(t){t.defines.resolution="vec2( "+e.toFixed(1)+", "+i.toFixed(1)+" )"}function u(e,i){i=i||{};const s=new t.ShaderMaterial({name:"GPUComputationShader",uniforms:i,vertexShader:"void main()\t{\n\n\tgl_Position = vec4( position, 1.0 );\n\n}\n",fragmentShader:e});return l(s),s}this.setDataType=function(e){return r=e,this},this.addVariable=function(e,i,s){const r={name:e,initialValueTexture:s,material:this.createShaderMaterial(i),dependencies:null,renderTargets:[],wrapS:null,wrapT:null,minFilter:t.NearestFilter,magFilter:t.NearestFilter};return this.variables.push(r),r},this.setVariableDependencies=function(e,t){e.dependencies=t},this.init=function(){if(0===s.capabilities.maxVertexTextures)return"No support for vertex shader textures.";for(let t=0;t<this.variables.length;t++){const s=this.variables[t];s.renderTargets[0]=this.createRenderTarget(e,i,s.wrapS,s.wrapT,s.minFilter,s.magFilter),s.renderTargets[1]=this.createRenderTarget(e,i,s.wrapS,s.wrapT,s.minFilter,s.magFilter),this.renderTexture(s.initialValueTexture,s.renderTargets[0]),this.renderTexture(s.initialValueTexture,s.renderTargets[1]);const r=s.material,n=r.uniforms;if(null!==s.dependencies)for(let e=0;e<s.dependencies.length;e++){const t=s.dependencies[e];if(t.name!==s.name){let e=!1;for(let i=0;i<this.variables.length;i++)if(t.name===this.variables[i].name){e=!0;break}if(!e)return"Variable dependency not found. Variable="+s.name+", dependency="+t.name}n[t.name]={value:null},r.fragmentShader="\nuniform sampler2D "+t.name+";\n"+r.fragmentShader}}return this.currentTextureIndex=0,null},this.compute=function(){const e=this.currentTextureIndex,t=0===this.currentTextureIndex?1:0;for(let i=0,s=this.variables.length;i<s;i++){const s=this.variables[i];if(null!==s.dependencies){const t=s.material.uniforms;for(let i=0,r=s.dependencies.length;i<r;i++){const r=s.dependencies[i];t[r.name].value=r.renderTargets[e].texture}}this.doRenderTarget(s.material,s.renderTargets[t])}this.currentTextureIndex=t},this.getCurrentRenderTarget=function(e){return e.renderTargets[this.currentTextureIndex]},this.getAlternateRenderTarget=function(e){return e.renderTargets[0===this.currentTextureIndex?1:0]},this.dispose=function(){a.dispose();const e=this.variables;for(let t=0;t<e.length;t++){const i=e[t];i.initialValueTexture&&i.initialValueTexture.dispose();const s=i.renderTargets;for(let e=0;e<s.length;e++){s[e].dispose()}}},this.addResolutionDefine=l,this.createShaderMaterial=u,this.createRenderTarget=function(s,n,o,a,l,u){s=s||e,n=n||i,o=o||t.ClampToEdgeWrapping,a=a||t.ClampToEdgeWrapping,l=l||t.NearestFilter,u=u||t.NearestFilter;return new t.WebGLRenderTarget(s,n,{wrapS:o,wrapT:a,minFilter:l,magFilter:u,format:t.RGBAFormat,type:r,depthBuffer:!1})},this.createTexture=function(){const s=new Float32Array(e*i*4),r=new t.DataTexture(s,e,i,t.RGBAFormat,t.FloatType);return r.needsUpdate=!0,r},this.renderTexture=function(e,t){n.passThruTexture.value=e,this.doRenderTarget(o,t),n.passThruTexture.value=null},this.doRenderTarget=function(e,t){const i=s.getRenderTarget(),r=s.xr.enabled,n=s.shadowMap.autoUpdate;s.xr.enabled=!1,s.shadowMap.autoUpdate=!1,a.material=e,s.setRenderTarget(t),a.render(s),a.material=o,s.xr.enabled=r,s.shadowMap.autoUpdate=n,s.setRenderTarget(i)}}}class V{constructor(e,t,i){r(this,"gpuComputationRenderer"),r(this,"webGLRenderer"),r(this,"velocityVar"),r(this,"positionVar"),r(this,"initialPositionDataTexture"),r(this,"initialVelocityDataTexture"),r(this,"positionAtlasTexture",null),r(this,"interactionPosition"),r(this,"lastKnownPositionDataTexture"),r(this,"lastKnownVelocityDataTexture"),this.webGLRenderer=t,this.gpuComputationRenderer=new w(e,e,this.webGLRenderer),!t.capabilities.isWebGL2&&t.extensions.get("OES_texture_float")?this.gpuComputationRenderer.setDataType(o.FloatType):t.capabilities.isWebGL2||this.gpuComputationRenderer.setDataType(o.HalfFloatType),this.initialPositionDataTexture=i??function(e){const t=new Float32Array(e*e*4);for(let i=0;i<e;i++)for(let s=0;s<e;s++){const r=i*e+s;let n=Math.random()*Math.PI*2,o=Math.acos(2*Math.random()-1),a=Math.sin(o)*Math.cos(n),l=Math.sin(o)*Math.sin(n),u=Math.cos(o);t[4*r]=a,t[4*r+1]=l,t[4*r+2]=u,t[4*r+3]=.01*(Math.random()-.5)}return u(t,e)}(e),this.initialVelocityDataTexture=function(e){return u(new Float32Array(4*e*e),e)}(e),this.interactionPosition=new o.Vector4(0,0,0,0),this.velocityVar=this.gpuComputationRenderer.addVariable("uCurrentVelocity",'\nuniform vec4 uInteractionPosition;\nuniform float uTime;\nuniform float uTractionForce;\nuniform float uMaxRepelDistance;\nuniform sampler2D uPositionAtlas;\nuniform float uOverallProgress;\nuniform int uNumMeshes;\nuniform float uSingleTextureSize;\n\nfloat rand(vec2 co) {\n return fract(sin(dot(co, vec2(12.9898, 78.233))) * 43758.5453);\n}\n\n// Helper function (same as in position shader)\nvec3 getAtlasPosition(vec2 uv, int meshIndex) {\n float atlasWidth = uSingleTextureSize * float(uNumMeshes);\n float atlasHeight = uSingleTextureSize;\n float segmentWidthRatio = uSingleTextureSize / atlasWidth;\n vec2 atlasUV = vec2(uv.x * segmentWidthRatio + segmentWidthRatio * float(meshIndex), uv.y);\n return texture2D(uPositionAtlas, atlasUV).xyz;\n}\n\nvoid main() {\n vec2 uv = gl_FragCoord.xy / resolution.xy;\n float offset = rand(uv);\n\n vec3 currentPosition = texture2D(uCurrentPosition, uv).xyz;\n vec3 currentVelocity = texture2D(uCurrentVelocity, uv).xyz;\n\n // --- Calculate Target Position from Atlas (same logic as position shader) ---\n vec3 targetPosition;\n if (uNumMeshes <= 1) {\n targetPosition = getAtlasPosition(uv, 0);\n } else {\n float totalSegments = float(uNumMeshes - 1);\n float progressPerSegment = 1.0 / totalSegments;\n float scaledProgress = uOverallProgress * totalSegments;\n int indexA = int(floor(scaledProgress));\n int indexB = min(indexA + 1, uNumMeshes - 1);\n indexA = min(indexA, uNumMeshes - 1);\n float localProgress = fract(scaledProgress);\n if (uOverallProgress == 1.0) {\n indexA = uNumMeshes - 1;\n indexB = uNumMeshes - 1;\n localProgress = 1.0;\n }\n vec3 positionA = getAtlasPosition(uv, indexA);\n vec3 positionB = getAtlasPosition(uv, indexB);\n targetPosition = mix(positionA, positionB, localProgress);\n }\n // --- End Target Position Calculation ---\n\n vec3 finalVelocity = currentVelocity * 0.9; // Dampening\n\n // Particle traction force towards target (influences velocity)\n vec3 direction = normalize(targetPosition - currentPosition);\n float dist = length(targetPosition - currentPosition);\n if (dist > 0.01) {\n // Add force proportional to distance and traction setting\n finalVelocity += direction * dist * 0.01 * uTractionForce; // Adjust multiplier as needed\n }\n\n // Mouse repel force\n if (uInteractionPosition.w > 0.0) { // Check if interaction is active (w component)\n float pointerDistance = distance(currentPosition, uInteractionPosition.xyz);\n if (pointerDistance < uMaxRepelDistance) {\n float mouseRepelModifier = smoothstep(uMaxRepelDistance, 0.0, pointerDistance); // Smoother falloff\n vec3 repelDirection = normalize(currentPosition - uInteractionPosition.xyz);\n // Apply force based on proximity and interaction strength (w)\n finalVelocity += repelDirection * mouseRepelModifier * uInteractionPosition.w * 0.01; // Adjust multiplier\n }\n }\n\n // Optional: Reset position if particle "dies" and respawns (lifespan logic)\n float lifespan = 20.0;\n float age = mod(uTime * 0.1 + lifespan * offset, lifespan); // Adjust time scale\n if (age < 0.05) { // Small window for reset\n finalVelocity = vec3(0.0); // Reset velocity on respawn\n // Note: Resetting position directly here might cause jumps.\n // It\'s often better handled in the position shader or by ensuring\n // strong attraction force when dist is large.\n }\n\n\n gl_FragColor = vec4(finalVelocity, 1.0);\n}\n',this.initialVelocityDataTexture),this.positionVar=this.gpuComputationRenderer.addVariable("uCurrentPosition","\nuniform vec4 uInteractionPosition;\nuniform float uTime;\nuniform float uTractionForce;\nuniform sampler2D uPositionAtlas;\nuniform float uOverallProgress; // (0.0 to 1.0)\nuniform int uNumMeshes;\nuniform float uSingleTextureSize;\n\nfloat rand(vec2 co) {\n return fract(sin(dot(co, vec2(12.9898, 78.233))) * 43758.5453);\n}\n\n// Helper function to get position from atlas\nvec3 getAtlasPosition(vec2 uv, int meshIndex) {\n float atlasWidth = uSingleTextureSize * float(uNumMeshes);\n float atlasHeight = uSingleTextureSize; // Assuming height is single texture size\n\n // Calculate UV within the specific mesh's section of the atlas\n float segmentWidthRatio = uSingleTextureSize / atlasWidth;\n vec2 atlasUV = vec2(\n uv.x * segmentWidthRatio + segmentWidthRatio * float(meshIndex),\n uv.y // Assuming vertical layout doesn't change y\n );\n\n return texture2D(uPositionAtlas, atlasUV).xyz;\n}\n\nvoid main() {\n // GPGPU UV calculation\n vec2 uv = gl_FragCoord.xy / resolution.xy; // resolution is the size of the *output* texture (e.g., 256x256)\n\n vec3 currentPosition = texture2D(uCurrentPosition, uv).xyz;\n vec3 currentVelocity = texture2D(uCurrentVelocity, uv).xyz;\n\n // --- Calculate Target Position from Atlas ---\n vec3 targetPosition;\n if (uNumMeshes <= 1) {\n targetPosition = getAtlasPosition(uv, 0);\n } else {\n float totalSegments = float(uNumMeshes - 1);\n float progressPerSegment = 1.0 / totalSegments;\n float scaledProgress = uOverallProgress * totalSegments;\n\n int indexA = int(floor(scaledProgress));\n // Clamp indexB to avoid going out of bounds\n int indexB = min(indexA + 1, uNumMeshes - 1);\n\n // Ensure indexA is also within bounds (important if uOverallProgress is exactly 1.0)\n indexA = min(indexA, uNumMeshes - 1);\n\n\n float localProgress = fract(scaledProgress);\n\n // Handle edge case where progress is exactly 1.0\n if (uOverallProgress == 1.0) {\n indexA = uNumMeshes - 1;\n indexB = uNumMeshes - 1;\n localProgress = 1.0; // or 0.0 depending on how you want to handle it\n }\n\n\n vec3 positionA = getAtlasPosition(uv, indexA);\n vec3 positionB = getAtlasPosition(uv, indexB);\n\n targetPosition = mix(positionA, positionB, localProgress);\n }\n // --- End Target Position Calculation ---\n\n // Particle attraction to target position\n vec3 direction = normalize(targetPosition - currentPosition);\n float dist = length(targetPosition - currentPosition);\n\n vec3 finalPosition = currentPosition;\n\n // Apply attraction force (simplified mix)\n if (dist > 0.01) { // Only apply if significantly far\n finalPosition = mix(currentPosition, targetPosition, 0.1 * uTractionForce);\n }\n\n finalPosition += currentVelocity;\n gl_FragColor = vec4(finalPosition, 1.0);\n}\n",this.initialPositionDataTexture),this.velocityVar.material.uniforms.uTime={value:0},this.velocityVar.material.uniforms.uInteractionPosition={value:this.interactionPosition},this.velocityVar.material.uniforms.uCurrentPosition={value:null},this.velocityVar.material.uniforms.uTractionForce={value:.1},this.velocityVar.material.uniforms.uMaxRepelDistance={value:.3},this.velocityVar.material.uniforms.uPositionAtlas={value:null},this.velocityVar.material.uniforms.uOverallProgress={value:0},this.velocityVar.material.uniforms.uNumMeshes={value:1},this.velocityVar.material.uniforms.uSingleTextureSize={value:e},this.positionVar.material.uniforms.uTime={value:0},this.positionVar.material.uniforms.uTractionForce={value:.1},this.positionVar.material.uniforms.uInteractionPosition={value:this.interactionPosition},this.positionVar.material.uniforms.uCurrentPosition={value:null},this.positionVar.material.uniforms.uCurrentVelocity={value:null},this.positionVar.material.uniforms.uPositionAtlas={value:null},this.positionVar.material.uniforms.uOverallProgress={value:0},this.positionVar.material.uniforms.uNumMeshes={value:1},this.positionVar.material.uniforms.uSingleTextureSize={value:e},this.gpuComputationRenderer.setVariableDependencies(this.positionVar,[this.positionVar,this.velocityVar]),this.gpuComputationRenderer.setVariableDependencies(this.velocityVar,[this.velocityVar,this.positionVar]);const s=this.gpuComputationRenderer.init();if(null!==s)throw new Error("Failed to initialize SimulationRenderer: "+s);this.positionVar.material.uniforms.uPositionAtlas.value=this.initialPositionDataTexture,this.positionVar.material.uniforms.uNumMeshes.value=1,this.velocityVar.material.uniforms.uNumMeshes.value=1,this.positionVar.material.uniforms.uSingleTextureSize.value=e,this.velocityVar.material.uniforms.uSingleTextureSize.value=e,this.positionVar.material.uniforms.uCurrentVelocity.value=this.gpuComputationRenderer.getCurrentRenderTarget(this.velocityVar).texture,this.velocityVar.material.uniforms.uCurrentPosition.value=this.gpuComputationRenderer.getCurrentRenderTarget(this.positionVar).texture,this.lastKnownVelocityDataTexture=this.gpuComputationRenderer.getCurrentRenderTarget(this.velocityVar).texture,this.lastKnownPositionDataTexture=this.gpuComputationRenderer.getCurrentRenderTarget(this.positionVar).texture}setPositionAtlas(e){const t=e.singleTextureSize*e.numMeshes;e.dataTexture.image.width===t&&e.dataTexture.image.height===e.singleTextureSize||console.error(`SimulationRenderer: Atlas texture dimension mismatch! Expected ${t}x${e.singleTextureSize}, Got ${e.dataTexture.image.width}x${e.dataTexture.image.height}`),this.positionAtlasTexture=e.dataTexture;const i=e.numMeshes>0?e.numMeshes:1;this.positionVar.material.uniforms.uPositionAtlas.value=this.positionAtlasTexture,this.positionVar.material.uniforms.uNumMeshes.value=i,this.positionVar.material.uniforms.uSingleTextureSize.value=e.singleTextureSize,this.velocityVar.material.uniforms.uPositionAtlas.value=this.positionAtlasTexture,this.velocityVar.material.uniforms.uNumMeshes.value=i,this.velocityVar.material.uniforms.uSingleTextureSize.value=e.singleTextureSize,this.positionVar.material.uniforms.uCurrentVelocity.value=this.gpuComputationRenderer.getCurrentRenderTarget(this.velocityVar).texture,this.velocityVar.material.uniforms.uCurrentPosition.value=this.gpuComputationRenderer.getCurrentRenderTarget(this.positionVar).texture}setOverallProgress(e){const t=h(e,0,1);this.positionVar.material.uniforms.uOverallProgress.value=t,this.velocityVar.material.uniforms.uOverallProgress.value=t}setMaxRepelDistance(e){this.velocityVar.material.uniforms.uMaxRepelDistance.value=e}setVelocityTractionForce(e){this.velocityVar.material.uniforms.uTractionForce.value=e}setPositionalTractionForce(e){this.positionVar.material.uniforms.uTractionForce.value=e}setInteractionPosition(e){this.interactionPosition.copy(e)}dispose(){var e,t;this.gpuComputationRenderer.dispose(),null==(e=this.initialPositionDataTexture)||e.dispose(),null==(t=this.initialVelocityDataTexture)||t.dispose(),this.positionAtlasTexture=null}compute(e){this.velocityVar.material.uniforms.uTime.value+=e,this.positionVar.material.uniforms.uTime.value+=e,this.positionVar.material.uniforms.uCurrentVelocity.value=this.gpuComputationRenderer.getCurrentRenderTarget(this.velocityVar).texture,this.velocityVar.material.uniforms.uCurrentPosition.value=this.gpuComputationRenderer.getCurrentRenderTarget(this.positionVar).texture,this.gpuComputationRenderer.compute(),this.lastKnownVelocityDataTexture=this.gpuComputationRenderer.getCurrentRenderTarget(this.velocityVar).texture,this.lastKnownPositionDataTexture=this.gpuComputationRenderer.getCurrentRenderTarget(this.positionVar).texture}getVelocityTexture(){return this.lastKnownVelocityDataTexture}getPositionTexture(){return this.lastKnownPositionDataTexture}}class R{constructor(e,t,i){r(this,"state"),r(this,"textureSize"),r(this,"overallProgress"),r(this,"velocityTractionForce"),r(this,"positionalTractionForce"),r(this,"simulationRenderer"),r(this,"webGLRenderer"),r(this,"eventEmitter"),r(this,"currentAtlasEntry",null),r(this,"lastKnownVelocityDataTexture"),r(this,"lastKnownPositionDataTexture"),this.eventEmitter=e,this.webGLRenderer=i,this.textureSize=t,this.overallProgress=0,this.velocityTractionForce=.1,this.positionalTractionForce=.1,this.updateServiceState("initializing"),this.simulationRenderer=new V(this.textureSize,this.webGLRenderer),this.lastKnownVelocityDataTexture=this.simulationRenderer.getVelocityTexture(),this.lastKnownPositionDataTexture=this.simulationRenderer.getPositionTexture(),this.updateServiceState("ready")}setPositionAtlas(e){const t=e.singleTextureSize*e.numMeshes;e.dataTexture.image.width===t?(this.currentAtlasEntry=e,this.simulationRenderer.setPositionAtlas(e)):this.eventEmitter.emit("invalidRequest",{message:"Atlas texture width mismatch."})}setOverallProgress(e){this.overallProgress=e,this.simulationRenderer.setOverallProgress(this.overallProgress)}setTextureSize(e){this.updateServiceState("initializing"),this.simulationRenderer.dispose(),this.textureSize=e,this.simulationRenderer=new V(e,this.webGLRenderer),this.updateServiceState("ready")}setVelocityTractionForce(e){this.velocityTractionForce=e,this.simulationRenderer.setVelocityTractionForce(this.velocityTractionForce)}setPositionalTractionForce(e){this.positionalTractionForce=e,this.simulationRenderer.setPositionalTractionForce(this.positionalTractionForce)}compute(e){"ready"===this.state&&(this.simulationRenderer.compute(e),this.lastKnownVelocityDataTexture=this.simulationRenderer.getVelocityTexture(),this.lastKnownPositionDataTexture=this.simulationRenderer.getPositionTexture())}getVelocityTexture(){return this.lastKnownVelocityDataTexture}getPositionTexture(){return this.lastKnownPositionDataTexture}dispose(){this.updateServiceState("disposed"),this.simulationRenderer.dispose(),this.currentAtlasEntry=null}updateServiceState(e){this.state=e,this.eventEmitter.emit("serviceStateUpdated",{type:"simulation",state:e})}setInteractionPosition(e){this.simulationRenderer.setInteractionPosition(e)}setMaxRepelDistance(e){this.simulationRenderer.setMaxRepelDistance(e)}}class z{constructor(){r(this,"execStatus",new Map)}get(e){const t=this.execStatus.get(e);return t||(this.execStatus.set(e,"idle"),"idle")}set(e,t){this.execStatus.set(e,t)}}class F{constructor(e){r(this,"eventEmitter"),r(this,"transitions",new Map),r(this,"execStatus"),r(this,"ongoingTransitions",new Map),this.eventEmitter=e,this.execStatus=new z,this.eventEmitter.on("transitionCancelled",this.handleTransitionCancelledEvent.bind(this))}enqueue(e,t,i={}){const s={...t,...i,cancelled:!1,duration:.001*t.duration};this.getQueue(e).push(s)}compute(e){this.transitions.forEach(((t,i)=>{var s;if(t.length&&!this.ongoingTransitions.has(i)){const r=t.shift();r&&(this.ongoingTransitions.set(i,{...r,startTime:e}),null==(s=r.onTransitionBegin)||s.call(r))}})),this.ongoingTransitions.forEach(((t,i)=>{var s,r,n;if(t.cancelled)return null==(s=t.onTransitionCancelled)||s.call(t),void this.ongoingTransitions.delete(i);const{startTime:o,duration:a,easing:l}=t,u=e-o,c=h(l(Math.min(1,u/a)),0,1);this.emitTransitionProgress(i,c),null==(r=t.onTransitionProgress)||r.call(t,c),c>=1&&(this.emitTransitionFinished(i),null==(n=t.onTransitionFinished)||n.call(t),this.ongoingTransitions.delete(i))}))}getQueue(e){const t=this.transitions.get(e);return t||(this.transitions.set(e,[]),this.transitions.get(e)??[])}handleTransitionCancelledEvent({type:e}){var t;const i=this.getQueue(e);for(;i.length;)i.pop();const s=this.ongoingTransitions.get(e);s&&(s.cancelled=!0,null==(t=s.onTransitionCancelled)||t.call(s))}emitTransitionProgress(e,t){this.eventEmitter.emit("transitionProgressed",{type:e,progress:t})}emitTransitionFinished(e){this.eventEmitter.emit("transitionFinished",{type:e})}}return e.ParticlesEngine=class{constructor(e){r(this,"simulationRendererService"),r(this,"renderer"),r(this,"scene"),r(this,"serviceStates"),r(this,"assetService"),r(this,"dataTextureManager"),r(this,"instancedMeshManager"),r(this,"transitionService"),r(this,"engineState"),r(this,"intersectionService"),r(this,"meshSequenceAtlasTexture",null),r(this,"eventEmitter");const{scene:t,renderer:i,camera:s,textureSize:n,useIntersection:o=!0}=e;this.eventEmitter=new l,this.serviceStates=this.getInitialServiceStates(),this.eventEmitter.on("serviceStateUpdated",this.handleServiceStateUpdated.bind(this)),this.scene=t,this.renderer=i,this.engineState=this.initialEngineState(e),this.assetService=new d(this.eventEmitter),this.transitionService=new F(this.eventEmitter),this.dataTextureManager=new y(this.eventEmitter,n),this.simulationRendererService=new R(this.eventEmitter,n,this.renderer),this.instancedMeshManager=new S(n),this.scene.add(this.instancedMeshManager.getMesh()),this.intersectionService=new T(this.eventEmitter,s),o||this.intersectionService.setActive(!1),this.setOverallProgress(0,!1),this.eventEmitter.on("interactionPositionUpdated",this.handleInteractionPositionUpdated.bind(this))}render(e){const t=e/1e3;this.transitionService.compute(t),this.intersectionService.calculate(this.instancedMeshManager.getMesh()),this.simulationRendererService.compute(t),this.instancedMeshManager.update(t),this.instancedMeshManager.updateVelocityTexture(this.simulationRendererService.getVelocityTexture()),this.instancedMeshManager.updatePositionTexture(this.simulationRendererService.getPositionTexture())}setTextureSequence(e){this.engineState.textureSequence=e,this.eventEmitter.emit("textureSequenceUpdated",{sequence:e}),this.setOverallProgress(0,!1)}async setTextureSize(e){this.engineState.textureSize!==e&&(this.engineState.textureSize=e,this.dataTextureManager.setTextureSize(e),this.simulationRendererService.setTextureSize(e),this.instancedMeshManager.resize(e),this.engineState.meshSequence.length>0&&await this.setMeshSequence(this.engineState.meshSequence),this.engineState.meshSequence.length>0&&await this.setMeshSequence(this.engineState.meshSequence),this.simulationRendererService.setVelocityTractionForce(this.engineState.velocityTractionForce),this.simulationRendererService.setPositionalTractionForce(this.engineState.positionalTractionForce),this.simulationRendererService.setMaxRepelDistance(this.engineState.maxRepelDistance),this.simulationRendererService.setOverallProgress(this.engineState.overallProgress),this.intersectionService.setOverallProgress(this.engineState.overallProgress),this.instancedMeshManager.setGeometrySize(this.engineState.instanceGeometryScale),this.setOverallProgress(this.engineState.overallProgress,!1))}registerMesh(e,t){this.assetService.register(e,t)}registerMatcap(e,t){this.assetService.register(e,t)}async fetchAndRegisterMesh(e,t){return await this.assetService.loadMeshAsync(e,t)}async fetchAndRegisterMatcap(e,t){return await this.assetService.loadTextureAsync(e,t)}useIntersect(e){this.intersectionService.setActive(e),this.engineState.useIntersect=e,e||(this.engineState.pointerPosition={x:-99999999,y:-99999999},this.simulationRendererService.setInteractionPosition({x:0,y:0,z:0,w:0}))}setPointerPosition(e){this.engineState.useIntersect&&(this.engineState.pointerPosition=e,this.intersectionService.setPointerPosition(e))}setGeometrySize(e){this.engineState.instanceGeometryScale=e,this.instancedMeshManager.setGeometrySize(e)}setVelocityTractionForce(e){this.engineState.velocityTractionForce=e,this.simulationRendererService.setVelocityTractionForce(e)}setPositionalTractionForce(e){this.engineState.positionalTractionForce=e,this.simulationRendererService.setPositionalTractionForce(e)}setMaxRepelDistance(e){this.engineState.maxRepelDistance=e,this.simulationRendererService.setMaxRepelDistance(e)}async setMeshSequence(e){if(!e||e.length<1)return this.eventEmitter.emit("invalidRequest",{message:"Mesh sequence must contain at least one mesh ID."}),this.engineState.meshSequence=[],void this.intersectionService.setMeshSequence([]);this.engineState.meshSequence=e,this.engineState.overallProgress=0;const t=e.map((e=>this.assetService.getMesh(e))).filter((e=>null!==e));if(t.length!==e.length){const i=e.filter((e=>!this.assetService.getMesh(e)));if(console.warn(`Could not find meshes for IDs: ${i.join(", ")}. Proceeding with ${t.length} found meshes.`),this.eventEmitter.emit("invalidRequest",{message:`Could not find meshes for IDs: ${i.join(", ")}`}),t.length<1)return this.engineState.meshSequence=[],void this.intersectionService.setMeshSequence([]);this.engineState.meshSequence=t.map((e=>e.name))}try{this.meshSequenceAtlasTexture=await this.dataTextureManager.createSequenceDataTextureAtlas(t,this.engineState.textureSize),this.simulationRendererService.setPositionAtlas({dataTexture:this.meshSequenceAtlasTexture,textureSize:this.engineState.textureSize,numMeshes:this.engineState.meshSequence.length,singleTextureSize:this.engineState.textureSize}),this.simulationRendererService.setOverallProgress(this.engineState.overallProgress),this.intersectionService.setMeshSequence(t),this.intersectionService.setOverallProgress(this.engineState.overallProgress),this.setOverallProgress(0,!1)}catch(i){console.error("Failed during mesh sequence setup:",i),this.meshSequenceAtlasTexture=null}}setOverallProgress(e,t=!0){t&&this.eventEmitter.emit("transitionCancelled",{type:"mesh-sequence"});const i=h(e,0,1);this.engineState.overallProgress=i,this.simulationRendererService.setOverallProgress(i),this.intersectionService.setOverallProgress(i);const{textureA:s,textureB:r,localProgress:n}=this.calculateTextureInterpolation(e);this.instancedMeshManager.updateTextureInterpolation(s,r,n)}scheduleMeshSequenceTransition(e,t=1e3,i=a,s={},r=!0){r&&this.eventEmitter.emit("transitionCancelled",{type:"mesh-sequence"});const n=this.engineState.overallProgress,o=e-n,l={duration:t,easing:i},u={...s,onTransitionProgress:e=>{var t;const i=n+o*e;this.setOverallProgress(i,!1),null==(t=s.onTransitionProgress)||t.call(s,i)},onTransitionBegin:s.onTransitionBegin,onTransitionFinished:()=>{var t;this.setOverallProgress(e,!1),null==(t=s.onTransitionFinished)||t.call(s)},onTransitionCancelled:s.onTransitionCancelled};this.transitionService.enqueue("mesh-sequence",l,u)}handleServiceStateUpdated({type:e,state:t}){this.serviceStates[e]=t}getObject(){return this.instancedMeshManager.getMesh()}getMeshIDs(){return this.assetService.getMeshIDs()}getMatcapIDs(){return this.assetService.getTextureIDs()}getMeshes(){return this.assetService.getMeshes()}getTextures(){return this.assetService.getTextures()}getTextureSize(){return this.engineState.textureSize}getUseIntersect(){return this.engineState.useIntersect}getEngineStateSnapshot(){return{...this.engineState}}dispose(){var e,t,i,s,r,n;this.scene&&this.instancedMeshManager&&this.scene.remove(this.instancedMeshManager.getMesh()),null==(e=this.simulationRendererService)||e.dispose(),null==(t=this.instancedMeshManager)||t.dispose(),null==(i=this.intersectionService)||i.dispose(),null==(s=this.assetService)||s.dispose(),null==(r=this.dataTextureManager)||r.dispose(),null==(n=this.eventEmitter)||n.dispose()}initialEngineState(e){return{textureSize:e.textureSize,meshSequence:[],overallProgress:0,textureSequence:[],velocityTractionForce:.1,positionalTractionForce:.1,maxRepelDistance:.3,pointerPosition:{x:0,y:0},instanceGeometryScale:{x:1,y:1,z:1},useIntersect:e.useIntersection??!0}}getInitialServiceStates(){return{"data-texture":"created","instanced-mesh":"created",matcap:"created",simulation:"created",asset:"created"}}handleInteractionPositionUpdated({position:e}){this.simulationRendererService.setInteractionPosition(e)}calculateTextureInterpolation(e){const t=this.engineState.textureSequence,i=t.length;if(0===i){const e=this.assetService.getFallbackTexture();return{textureA:e,textureB:e,localProgress:0}}if(1===i){const e=this.getTextureForSequenceItem(t[0]);return{textureA:e,textureB:e,localProgress:0}}const s=i-1,r=1/s,n=e*s;let o=Math.floor(n),a=o+1;o=Math.max(0,Math.min(o,s)),a=Math.max(0,Math.min(a,s));let l=0;r>0&&(l=(e-o*r)/r),e>=1&&(o=s,a=s,l=1),l=Math.max(0,Math.min(l,1));const u=t[o],c=t[a];return{textureA:this.getTextureForSequenceItem(u),textureB:this.getTextureForSequenceItem(c),localProgress:l}}getTextureForSequenceItem(e){return"matcap"===e.type?this.assetService.getMatcapTexture(e.id):this.assetService.getSolidColorTexture(e.value)}},Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),e}({},THREE,three-stdlib);
2
2
  //# sourceMappingURL=ionian.iife.js.map