@polarfront-lab/ionian 3.0.6-hotfix.1 → 3.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,3 +1,96 @@
1
1
  # Ionian
2
2
 
3
- Ionian is a ThreeJS Collection Set.
3
+ Ionian is a Three.js particle morphing engine.
4
+
5
+ It samples one or more `THREE.Mesh` surfaces into GPU data textures, simulates
6
+ particle motion with `GPUComputationRenderer`, and renders the particles through
7
+ an instanced mesh. Applications own the surrounding scene, renderer, camera,
8
+ asset upload flow, scroll/loop behavior, and UI.
9
+
10
+ ## Install
11
+
12
+ ```sh
13
+ pnpm add @polarfront-lab/ionian three three-stdlib mitt
14
+ ```
15
+
16
+ `three`, `three-stdlib`, and `mitt` are peer dependencies.
17
+
18
+ Ionian is currently validated against `three@0.184.0` and
19
+ `three-stdlib@2.36.1` in package smoke tests. The peer range starts at the
20
+ PFLAB production baseline, `three@0.175.0`, and intentionally stops before the
21
+ next untested Three.js minor.
22
+
23
+ ## Basic Usage
24
+
25
+ ```ts
26
+ import { ParticlesEngine } from '@polarfront-lab/ionian';
27
+
28
+ const engine = new ParticlesEngine({
29
+ textureSize: 256,
30
+ scene,
31
+ renderer,
32
+ camera,
33
+ useIntersection: false,
34
+ dracoDecoderPath: '/draco/',
35
+ });
36
+
37
+ engine.registerMesh('step-1', meshA);
38
+ engine.registerMesh('step-2', meshB);
39
+ engine.registerMatcap('warm', warmTexture);
40
+ engine.registerMatcap('cool', coolTexture);
41
+
42
+ await engine.setMeshSequence(['step-1', 'step-2']);
43
+ engine.setTextureSequence([
44
+ { type: 'matcap', id: 'warm' },
45
+ { type: 'matcap', id: 'cool' },
46
+ ]);
47
+
48
+ engine.setOverallProgress(0.5);
49
+ engine.renderFrame(deltaSeconds, elapsedSeconds);
50
+ ```
51
+
52
+ `setOverallProgress()` is the primary integration point for product code. Feed
53
+ it scroll progress, an automatic timeline, or a static value from the host app.
54
+
55
+ Use `renderFrame(deltaSeconds, elapsedSeconds)` when the host runtime already
56
+ provides frame timing, such as React Three Fiber. Use `render(elapsedTimeMs)`
57
+ for `requestAnimationFrame` timestamps.
58
+
59
+ ## Texture Sequence
60
+
61
+ Texture sequences support registered matcaps and solid colors:
62
+
63
+ ```ts
64
+ engine.setTextureSequence([
65
+ { type: 'color', value: '#ffffff' },
66
+ { type: 'color', value: '#ff7a00' },
67
+ { type: 'matcap', id: 'metal' },
68
+ ]);
69
+ ```
70
+
71
+ Ionian linearly interpolates between adjacent texture sequence entries using the
72
+ same overall progress value that drives mesh morphing.
73
+
74
+ ## Runtime Guidance
75
+
76
+ - Choose `textureSize` before constructing the engine when possible. Runtime
77
+ resizing is supported, but it recreates GPU resources and regenerates mesh
78
+ atlases.
79
+ - Use `128` or lower for mobile scenes and `256` for rich desktop scenes unless
80
+ the host app has measured the target hardware.
81
+ - Prefer host-app asset loaders for uploads and validation. `fetchAndRegister*`
82
+ is a convenience API, not an upload pipeline.
83
+ - Pass `dracoDecoderPath` when the host app self-hosts Draco decoders.
84
+ - Call `dispose()` when unmounting the scene.
85
+
86
+ ## Development
87
+
88
+ ```sh
89
+ pnpm install
90
+ pnpm test
91
+ pnpm run test:coverage
92
+ pnpm run build
93
+ pnpm run build:types
94
+ pnpm run pack:smoke
95
+ pnpm run validate
96
+ ```
package/dist/index.d.ts CHANGED
@@ -106,6 +106,9 @@ declare class DefaultEventEmitter implements EngineEventEmitter<Events> {
106
106
  once<Key extends keyof Events>(type: Key, handler: (payload: Events[Key]) => void): void;
107
107
  dispose(): void;
108
108
  }
109
+ export interface LoadMeshOptions {
110
+ meshName?: string;
111
+ }
109
112
  /**
110
113
  * Represents the current state of the system.
111
114
  */
@@ -130,6 +133,7 @@ export type ParticlesEngineParameters = {
130
133
  renderer: THREE.WebGLRenderer;
131
134
  camera?: THREE.Camera;
132
135
  useIntersection?: boolean;
136
+ dracoDecoderPath?: string | null;
133
137
  };
134
138
  /**
135
139
  * The main class for the particle engine.
@@ -146,9 +150,8 @@ export declare class ParticlesEngine {
146
150
  private engineState;
147
151
  private intersectionService;
148
152
  private meshSequenceAtlasTexture;
153
+ private lastRenderElapsedTimeSeconds;
149
154
  eventEmitter: DefaultEventEmitter;
150
- private readonly boundHandleServiceStateUpdated;
151
- private readonly boundHandleInteractionPositionUpdated;
152
155
  /**
153
156
  * Creates a new ParticlesEngine instance.
154
157
  * @param params The parameters for creating the instance.
@@ -158,12 +161,13 @@ export declare class ParticlesEngine {
158
161
  * Renders the scene.
159
162
  * @param elapsedTime The elapsed time since the last frame.
160
163
  */
161
- render(elapsedTime: number): void;
164
+ render(elapsedTimeMs: number): void;
165
+ renderFrame(deltaTimeSeconds: number, elapsedTimeSeconds: number): void;
162
166
  setTextureSequence(sequence: TextureSequence): void;
163
167
  setTextureSize(size: number): Promise<void>;
164
168
  registerMesh(id: string, mesh: THREE.Mesh): void;
165
169
  registerMatcap(id: string, matcap: THREE.Texture): void;
166
- fetchAndRegisterMesh(id: string, url: string): Promise<THREE.Mesh<THREE.BufferGeometry<THREE.NormalBufferAttributes>, THREE.Material | THREE.Material[], THREE.Object3DEventMap> | null>;
170
+ fetchAndRegisterMesh(id: string, url: string, options?: LoadMeshOptions): Promise<THREE.Mesh<THREE.BufferGeometry<THREE.NormalBufferAttributes>, THREE.Material | THREE.Material[], THREE.Object3DEventMap> | null>;
167
171
  fetchAndRegisterMatcap(id: string, url: string): Promise<THREE.Texture | null>;
168
172
  useIntersect(use: boolean): void;
169
173
  setPointerPosition(position: THREE.Vector2Like): void;
@@ -214,5 +218,11 @@ export declare class ParticlesEngine {
214
218
  private calculateTextureInterpolation;
215
219
  private getTextureForSequenceItem;
216
220
  }
221
+ export interface SequenceInterpolation {
222
+ indexA: number;
223
+ indexB: number;
224
+ localProgress: number;
225
+ }
226
+ export declare function resolveSequenceInterpolation(progress: number, itemCount: number): SequenceInterpolation;
217
227
 
218
228
  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 o=r(t),a=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 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){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 o.TextureLoader),n(this,"dracoLoader",new i.DRACOLoader),n(this,"solidColorTextures",new Map),n(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,n=t,r=new Uint8Array(s*n*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*n;o++){const e=4*o;r[e]=a,r[e+1]=l,r[e+2]=u,r[e+3]=255}const c=new o.DataTexture(r,s,n,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(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(),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,n=new Float32Array(s);for(let a=0;a<s;a++){let s=1,r=3*a,o=3*a+1,l=3*a+2;e&&(r=e.getX(r),o=e.getX(o),l=e.getX(l)),i&&(s=i.getX(r)+i.getX(o)+i.getX(l)),m.a.fromBufferAttribute(t,r),m.b.fromBufferAttribute(t,o),m.c.fromBufferAttribute(t,l),s*=m.getArea(),n[a]=s}const r=new Float32Array(s);let o=0;for(let a=0;a<s;a++)o+=n[a],r[a]=o;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(),o=this.randomFunction();r+o>1&&(r=1-r,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,r).addScaledVector(m.b,o).addScaledVector(m.c,1-(r+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,r).addScaledVector(m.b,o).addScaledVector(m.c,1-(r+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,r).addScaledVector(m.b,o).addScaledVector(m.c,1-(r+o)),s.r=g.x,s.g=g.y,s.b=g.z),void 0!==n&&void 0!==this.uvAttribute&&(v.fromBufferAttribute(this.uvAttribute,l),p.fromBufferAttribute(this.uvAttribute,u),f.fromBufferAttribute(this.uvAttribute,c),n.set(0,0).addScaledVector(v,r).addScaledVector(p,o).addScaledVector(f,1-(r+o))),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 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,n=new o.Mesh(i,s);n.scale.set(e.scale.x,e.scale.y,e.scale.z);const r=new x(n).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;r.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 i.dispose(),s.dispose(),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),r=u(n,this.textureSize);return r.name=e.name,this.dataTextures.set(e.uuid,r),r}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],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+n*t));r[l]=o[a],r[l+1]=o[a+1],r[l+2]=o[a+2],r[l+3]=o[a+3]}}const a=new o.DataTexture(r,s,n,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){n(this,"size"),n(this,"mesh"),n(this,"shaderMaterial"),n(this,"fallbackGeometry"),n(this,"uniforms"),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.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 this.mesh;this.size=e;const t=this.mesh;return this.mesh=this.createInstancedMesh(e,t.geometry,t.material),t.dispose(),this.mesh}dispose(){this.mesh.dispose(),this.geometries.forEach((e=>e.dispose())),this.shaderMaterial.dispose(),this.fallbackGeometry.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 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 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){n(this,"active",!0),n(this,"raycaster",new o.Raycaster),n(this,"mousePosition",new o.Vector2),n(this,"camera"),n(this,"meshSequenceGeometries",[]),n(this,"meshSequenceUUIDs",[]),n(this,"overallProgress",0),n(this,"intersectionMesh",new o.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=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 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 o.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 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 n=Math.floor(s),r=n+1;n=o.MathUtils.clamp(n,0,t),r=o.MathUtils.clamp(r,0,t);let a=0;i>0&&(a=s-n),this.overallProgress>=1&&(n=t,r=t,a=1),a=o.MathUtils.clamp(a,0,1);const l=this.meshSequenceGeometries[n],u=this.meshSequenceGeometries[r];return l&&u?n===r?l:this.blendGeometry(l,u,a):(console.error("IntersectionService: Invalid geometries found for blending at indices",n,r),this.meshSequenceGeometries[0])}blendGeometry(e,t,i){const s=new o.BufferGeometry,n=e.attributes.position.array,r=t.attributes.position.array,a=new Float32Array(n.length);for(let l=0;l<n.length;l+=3){const e=new o.Vector3(n[l],n[l+1],n[l+2]),t=new o.Vector3(r[l],r[l+1],r[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 n=t.FloatType;const r={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",r),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 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(){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,r,o,a,l,u){s=s||e,r=r||i,o=o||t.ClampToEdgeWrapping,a=a||t.ClampToEdgeWrapping,l=l||t.NearestFilter,u=u||t.NearestFilter;return new t.WebGLRenderTarget(s,r,{wrapS:o,wrapT:a,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(o,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,a.material=e,s.setRenderTarget(t),a.render(s),a.material=o,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 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 n=i*e+s;let r=Math.random()*Math.PI*2,o=Math.acos(2*Math.random()-1),a=Math.sin(o)*Math.cos(r),l=Math.sin(o)*Math.sin(r),u=Math.cos(o);t[4*n]=a,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 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){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 z{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 F{constructor(e){n(this,"eventEmitter"),n(this,"transitions",new Map),n(this,"execStatus"),n(this,"ongoingTransitions",new Map),n(this,"boundHandleTransitionCancelled"),this.eventEmitter=e,this.execStatus=new z,this.boundHandleTransitionCancelled=this.handleTransitionCancelledEvent.bind(this),this.eventEmitter.on("transitionCancelled",this.boundHandleTransitionCancelled)}dispose(){this.eventEmitter.off("transitionCancelled",this.boundHandleTransitionCancelled),this.ongoingTransitions.forEach((e=>{e.cancelled=!0})),this.ongoingTransitions.clear(),this.transitions.clear()}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:o,duration:a,easing:l}=t,u=e-o,c=h(l(Math.min(1,u/a)),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"),n(this,"boundHandleServiceStateUpdated"),n(this,"boundHandleInteractionPositionUpdated");const{scene:t,renderer:i,camera:s,textureSize:r,useIntersection:o=!0}=e;this.eventEmitter=new l,this.serviceStates=this.getInitialServiceStates(),this.boundHandleServiceStateUpdated=this.handleServiceStateUpdated.bind(this),this.boundHandleInteractionPositionUpdated=this.handleInteractionPositionUpdated.bind(this),this.eventEmitter.on("serviceStateUpdated",this.boundHandleServiceStateUpdated),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,r),this.simulationRendererService=new R(this.eventEmitter,r,this.renderer),this.instancedMeshManager=new S(r),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.boundHandleInteractionPositionUpdated)}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.scene.remove(this.instancedMeshManager.getMesh()),this.instancedMeshManager.resize(e),this.scene.add(this.instancedMeshManager.getMesh()),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:n,localProgress:r}=this.calculateTextureInterpolation(e);this.instancedMeshManager.updateTextureInterpolation(s,n,r)}scheduleMeshSequenceTransition(e,t=1e3,i=a,s={},n=!0){n&&this.eventEmitter.emit("transitionCancelled",{type:"mesh-sequence"});const r=this.engineState.overallProgress,o=e-r,l={duration:t,easing:i},u={...s,onTransitionProgress:e=>{var t;const i=r+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,n,r,o;this.scene&&this.instancedMeshManager&&this.scene.remove(this.instancedMeshManager.getMesh()),this.eventEmitter.off("serviceStateUpdated",this.boundHandleServiceStateUpdated),this.eventEmitter.off("interactionPositionUpdated",this.boundHandleInteractionPositionUpdated),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.transitionService)||r.dispose(),null==(o=this.eventEmitter)||o.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,n=1/s,r=e*s;let o=Math.floor(r),a=o+1;o=Math.max(0,Math.min(o,s)),a=Math.max(0,Math.min(a,s));let l=0;n>0&&(l=(e-o*n)/n),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);
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){const i=s=>{this.emitter.off(e,i),t(s)};this.emitter.on(e,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,t={}){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"),r(this,"solidColorTextures",new Map),r(this,"fallbackTexture",new o.DataTexture(new Uint8Array([127,127,127,255]),1,1,o.RGBAFormat)),this.eventEmitter=e;const s=void 0===t.dracoDecoderPath?"https://www.gstatic.com/draco/versioned/decoders/1.5.7/":t.dracoDecoderPath;null!==s?(this.dracoLoader=new i.DRACOLoader,this.dracoLoader.setDecoderPath(s),this.gltfLoader.setDRACOLoader(this.dracoLoader)):this.dracoLoader=null,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={}){try{const s=await this.gltfLoader.loadAsync(t),r=this.findMesh(s.scene,i.meshName);return r?(this.register(e,r),r):(this.eventEmitter.emit("invalidRequest",{message:`failed to load mesh: ${e}. mesh not found`}),null)}catch(s){return this.eventEmitter.emit("invalidRequest",{message:`failed to load mesh: ${e}. ${s}`}),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(){var e;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(),null==(e=this.dracoLoader)||e.dispose(),this.fallbackTexture.dispose()}updateServiceState(e){this.serviceState=e,this.eventEmitter.emit("serviceStateUpdated",{type:"asset",state:e})}findMesh(e,t){if(t){const i=e.getObjectByName(t);return m(i)?i:null}let i=null;return e.traverse((e=>{!i&&m(e)&&(i=e)})),i}}function m(e){return!!e&&"isMesh"in e&&!0===e.isMesh}const g=new t.Triangle,v=new t.Vector3,p=new t.Vector2,f=new t.Vector2,x=new t.Vector2;class y{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)),g.a.fromBufferAttribute(t,n),g.b.fromBufferAttribute(t,o),g.c.fromBufferAttribute(t,l),s*=g.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)),g.a.fromBufferAttribute(this.positionAttribute,l),g.b.fromBufferAttribute(this.positionAttribute,u),g.c.fromBufferAttribute(this.positionAttribute,c),t.set(0,0,0).addScaledVector(g.a,n).addScaledVector(g.b,o).addScaledVector(g.c,1-(n+o)),void 0!==i&&(void 0!==this.normalAttribute?(g.a.fromBufferAttribute(this.normalAttribute,l),g.b.fromBufferAttribute(this.normalAttribute,u),g.c.fromBufferAttribute(this.normalAttribute,c),i.set(0,0,0).addScaledVector(g.a,n).addScaledVector(g.b,o).addScaledVector(g.c,1-(n+o)).normalize()):g.getNormal(i)),void 0!==s&&void 0!==this.colorAttribute&&(g.a.fromBufferAttribute(this.colorAttribute,l),g.b.fromBufferAttribute(this.colorAttribute,u),g.c.fromBufferAttribute(this.colorAttribute,c),v.set(0,0,0).addScaledVector(g.a,n).addScaledVector(g.b,o).addScaledVector(g.c,1-(n+o)),s.r=v.x,s.g=v.y,s.b=v.z),void 0!==r&&void 0!==this.uvAttribute&&(p.fromBufferAttribute(this.uvAttribute,l),f.fromBufferAttribute(this.uvAttribute,u),x.fromBufferAttribute(this.uvAttribute,c),r.set(0,0).addScaledVector(p,n).addScaledVector(f,o).addScaledVector(x,1-(n+o))),this}}class S{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;const i=u(function(e,t){e.updateWorldMatrix(!0,!1);const i=e.geometry.clone();i.applyMatrix4(e.matrixWorld);const s=new o.Mesh(i),r=new y(s).build(),n=new Float32Array(t*t*4),a=new o.Vector3;try{for(let e=0;e<t;e++)for(let i=0;i<t;i++){const s=e*t+i;r.sample(a),n[4*s]=a.x,n[4*s+1]=a.y,n[4*s+2]=a.z,n[4*s+3]=.01*(Math.random()-.5)}}finally{i.dispose()}return n}(e,this.textureSize),this.textureSize);return i.name=e.name,this.dataTextures.set(e.uuid,i),i}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 T{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),this.copyMeshState(t,this.mesh),this.replaceMeshInParent(t,this.mesh),{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)}copyMeshState(e,t){t.name=e.name,t.up.copy(e.up),t.position.copy(e.position),t.quaternion.copy(e.quaternion),t.scale.copy(e.scale),t.matrix.copy(e.matrix),t.matrixWorld.copy(e.matrixWorld),t.matrixAutoUpdate=e.matrixAutoUpdate,t.matrixWorldAutoUpdate=e.matrixWorldAutoUpdate,t.matrixWorldNeedsUpdate=e.matrixWorldNeedsUpdate,t.layers.mask=e.layers.mask,t.visible=e.visible,t.castShadow=e.castShadow,t.receiveShadow=e.receiveShadow,t.frustumCulled=e.frustumCulled,t.renderOrder=e.renderOrder,t.animations=e.animations,t.userData=e.userData,t.customDepthMaterial=e.customDepthMaterial,t.customDistanceMaterial=e.customDistanceMaterial,t.onBeforeRender=e.onBeforeRender,t.onAfterRender=e.onAfterRender}replaceMeshInParent(e,t){const i=e.parent;if(!i)return;const s=i.children.indexOf(e);i.add(t),i.remove(e);const r=i.children.indexOf(t);s<0||r<0||s===r||(i.children.splice(r,1),i.children.splice(Math.min(s,i.children.length),0,t))}}class P{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 M=new t.OrthographicCamera(-1,1,1,-1,0,1);class A 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 A;class w{constructor(e){this._mesh=new t.Mesh(b,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 V{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 w(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 R{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 V(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 D{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 R(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 R(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 F{constructor(e){r(this,"eventEmitter"),r(this,"transitions",new Map),r(this,"ongoingTransitions",new Map),this.eventEmitter=e,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),this.ongoingTransitions.delete(e))}emitTransitionProgress(e,t){this.eventEmitter.emit("transitionProgressed",{type:e,progress:t})}emitTransitionFinished(e){this.eventEmitter.emit("transitionFinished",{type:e})}}function z(e,t){if(t<=1)return{indexA:0,indexB:0,localProgress:0};const i=Math.max(0,Math.min(e,1)),s=t-1,r=i*s;if(i>=1)return{indexA:s,indexB:s,localProgress:1};const n=Math.floor(r);return{indexA:n,indexB:Math.min(n+1,s),localProgress:r-n}}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,"lastRenderElapsedTimeSeconds",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,{dracoDecoderPath:e.dracoDecoderPath}),this.transitionService=new F(this.eventEmitter),this.dataTextureManager=new S(this.eventEmitter,n),this.simulationRendererService=new D(this.eventEmitter,n,this.renderer),this.instancedMeshManager=new T(n),this.scene.add(this.instancedMeshManager.getMesh()),this.intersectionService=new P(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,i=null===this.lastRenderElapsedTimeSeconds?0:Math.max(0,t-this.lastRenderElapsedTimeSeconds);this.lastRenderElapsedTimeSeconds=t,this.renderFrame(i,t)}renderFrame(e,t){this.transitionService.compute(t),this.intersectionService.calculate(this.instancedMeshManager.getMesh()),this.simulationRendererService.compute(e),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){if(this.engineState.textureSize===e)return;const t=[...this.engineState.meshSequence],i=this.engineState.overallProgress;this.engineState.textureSize=e,this.dataTextureManager.setTextureSize(e),this.simulationRendererService.setTextureSize(e);const{current:s,previous:r}=this.instancedMeshManager.resize(e);s!==r&&(r.dispose(),s.parent||this.scene.add(s)),t.length>0&&await this.setMeshSequence(t),this.simulationRendererService.setVelocityTractionForce(this.engineState.velocityTractionForce),this.simulationRendererService.setPositionalTractionForce(this.engineState.positionalTractionForce),this.simulationRendererService.setMaxRepelDistance(this.engineState.maxRepelDistance),this.instancedMeshManager.setGeometrySize(this.engineState.instanceGeometryScale),this.setOverallProgress(i,!1)}registerMesh(e,t){this.assetService.register(e,t)}registerMatcap(e,t){this.assetService.register(e,t)}async fetchAndRegisterMesh(e,t,i){return await this.assetService.loadMeshAsync(e,t,i)}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(i);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{indexA:s,indexB:r,localProgress:n}=z(e,i),o=t[s],a=t[r];return{textureA:this.getTextureForSequenceItem(o),textureB:this.getTextureForSequenceItem(a),localProgress:n}}getTextureForSequenceItem(e){return"matcap"===e.type?this.assetService.getMatcapTexture(e.id):this.assetService.getSolidColorTexture(e.value)}},e.resolveSequenceInterpolation=z,Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),e}({},THREE,three-stdlib);
2
2
  //# sourceMappingURL=ionian.iife.js.map