@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/dist/ionian.js CHANGED
@@ -35,10 +35,11 @@ class DefaultEventEmitter {
35
35
  this.emitter.on(type, handler);
36
36
  }
37
37
  once(type, handler) {
38
- this.emitter.on(type, (payload) => {
39
- this.emitter.off(type, handler);
38
+ const wrappedHandler = (payload) => {
39
+ this.emitter.off(type, wrappedHandler);
40
40
  handler(payload);
41
- });
41
+ };
42
+ this.emitter.on(type, wrappedHandler);
42
43
  }
43
44
  dispose() {
44
45
  this.emitter.all.clear();
@@ -83,20 +84,27 @@ function clamp(value, min, max) {
83
84
  value = Math.max(value, min);
84
85
  return value;
85
86
  }
87
+ const DEFAULT_DRACO_DECODER_PATH = "https://www.gstatic.com/draco/versioned/decoders/1.5.7/";
86
88
  class AssetService {
87
- constructor(eventEmitter) {
89
+ constructor(eventEmitter, options = {}) {
88
90
  __publicField(this, "serviceState", "created");
89
91
  __publicField(this, "eventEmitter");
90
92
  __publicField(this, "meshes", /* @__PURE__ */ new Map());
91
93
  __publicField(this, "textures", /* @__PURE__ */ new Map());
92
94
  __publicField(this, "gltfLoader", new GLTFLoader());
93
95
  __publicField(this, "textureLoader", new THREE.TextureLoader());
94
- __publicField(this, "dracoLoader", new DRACOLoader());
96
+ __publicField(this, "dracoLoader");
95
97
  __publicField(this, "solidColorTextures", /* @__PURE__ */ new Map());
96
98
  __publicField(this, "fallbackTexture", new THREE.DataTexture(new Uint8Array([127, 127, 127, 255]), 1, 1, THREE.RGBAFormat));
97
99
  this.eventEmitter = eventEmitter;
98
- this.dracoLoader.setDecoderPath("https://www.gstatic.com/draco/versioned/decoders/1.5.7/");
99
- this.gltfLoader.setDRACOLoader(this.dracoLoader);
100
+ const decoderPath = options.dracoDecoderPath === void 0 ? DEFAULT_DRACO_DECODER_PATH : options.dracoDecoderPath;
101
+ if (decoderPath !== null) {
102
+ this.dracoLoader = new DRACOLoader();
103
+ this.dracoLoader.setDecoderPath(decoderPath);
104
+ this.gltfLoader.setDRACOLoader(this.dracoLoader);
105
+ } else {
106
+ this.dracoLoader = null;
107
+ }
100
108
  this.fallbackTexture.name = "default-fallback-texture";
101
109
  this.updateServiceState("ready");
102
110
  }
@@ -189,17 +197,15 @@ class AssetService {
189
197
  * @returns The loaded mesh or null.
190
198
  */
191
199
  async loadMeshAsync(id, url, options = {}) {
192
- const gltf = await this.gltfLoader.loadAsync(url);
193
200
  try {
194
- if (options.meshName) {
195
- const mesh = gltf.scene.getObjectByName(options.meshName);
196
- this.register(id, mesh);
197
- return mesh;
198
- } else {
199
- const mesh = gltf.scene.children[0];
200
- this.register(id, mesh);
201
- return mesh;
201
+ const gltf = await this.gltfLoader.loadAsync(url);
202
+ const mesh = this.findMesh(gltf.scene, options.meshName);
203
+ if (!mesh) {
204
+ this.eventEmitter.emit("invalidRequest", { message: `failed to load mesh: ${id}. mesh not found` });
205
+ return null;
202
206
  }
207
+ this.register(id, mesh);
208
+ return mesh;
203
209
  } catch (error) {
204
210
  this.eventEmitter.emit("invalidRequest", { message: `failed to load mesh: ${id}. ${error}` });
205
211
  return null;
@@ -222,6 +228,7 @@ class AssetService {
222
228
  }
223
229
  }
224
230
  dispose() {
231
+ var _a;
225
232
  this.updateServiceState("disposed");
226
233
  this.meshes.forEach((mesh) => disposeMesh(mesh));
227
234
  this.meshes.clear();
@@ -229,12 +236,29 @@ class AssetService {
229
236
  this.textures.clear();
230
237
  this.solidColorTextures.forEach((texture) => texture.dispose());
231
238
  this.solidColorTextures.clear();
239
+ (_a = this.dracoLoader) == null ? void 0 : _a.dispose();
232
240
  this.fallbackTexture.dispose();
233
241
  }
234
242
  updateServiceState(serviceState) {
235
243
  this.serviceState = serviceState;
236
244
  this.eventEmitter.emit("serviceStateUpdated", { type: "asset", state: serviceState });
237
245
  }
246
+ findMesh(root, meshName) {
247
+ if (meshName) {
248
+ const named = root.getObjectByName(meshName);
249
+ return isMesh(named) ? named : null;
250
+ }
251
+ let found = null;
252
+ root.traverse((object) => {
253
+ if (!found && isMesh(object)) {
254
+ found = object;
255
+ }
256
+ });
257
+ return found;
258
+ }
259
+ }
260
+ function isMesh(object) {
261
+ return !!object && "isMesh" in object && object.isMesh === true;
238
262
  }
239
263
  const _face = new Triangle();
240
264
  const _color = new Vector3();
@@ -444,14 +468,13 @@ class DataTextureService {
444
468
  if (cachedTexture) {
445
469
  return cachedTexture;
446
470
  }
447
- const meshData = parseMeshData(asset);
448
- const array = sampleMesh(meshData, this.textureSize);
471
+ const array = sampleMesh(asset, this.textureSize);
449
472
  const dataTexture = createDataTexture(array, this.textureSize);
450
473
  dataTexture.name = asset.name;
451
474
  this.dataTextures.set(asset.uuid, dataTexture);
452
475
  return dataTexture;
453
476
  }
454
- dispose() {
477
+ async dispose() {
455
478
  this.dataTextures.forEach((texture) => texture.dispose());
456
479
  this.dataTextures.clear();
457
480
  if (this.currentAtlas) {
@@ -511,38 +534,28 @@ class DataTextureService {
511
534
  }
512
535
  }
513
536
  }
514
- function parseMeshData(mesh) {
515
- var _a;
516
- return {
517
- position: mesh.geometry.attributes.position.array,
518
- normal: (_a = mesh.geometry.attributes.normal) == null ? void 0 : _a.array,
519
- scale: { x: mesh.scale.x, y: mesh.scale.y, z: mesh.scale.z }
520
- };
521
- }
522
- function sampleMesh(meshData, size) {
523
- const geometry = new THREE.BufferGeometry();
524
- geometry.setAttribute("position", new THREE.BufferAttribute(new Float32Array(meshData.position), 3));
525
- if (meshData.normal) {
526
- geometry.setAttribute("normal", new THREE.BufferAttribute(new Float32Array(meshData.normal), 3));
527
- }
528
- const material = new THREE.MeshBasicMaterial();
529
- const mesh = new THREE.Mesh(geometry, material);
530
- mesh.scale.set(meshData.scale.x, meshData.scale.y, meshData.scale.z);
537
+ function sampleMesh(sourceMesh, size) {
538
+ sourceMesh.updateWorldMatrix(true, false);
539
+ const geometry = sourceMesh.geometry.clone();
540
+ geometry.applyMatrix4(sourceMesh.matrixWorld);
541
+ const mesh = new THREE.Mesh(geometry);
531
542
  const sampler = new MeshSurfaceSampler(mesh).build();
532
543
  const data = new Float32Array(size * size * 4);
533
544
  const position = new THREE.Vector3();
534
- for (let i = 0; i < size; i++) {
535
- for (let j = 0; j < size; j++) {
536
- const index = i * size + j;
537
- sampler.sample(position);
538
- data[4 * index] = position.x * meshData.scale.x;
539
- data[4 * index + 1] = position.y * meshData.scale.y;
540
- data[4 * index + 2] = position.z * meshData.scale.z;
541
- data[4 * index + 3] = (Math.random() - 0.5) * 0.01;
545
+ try {
546
+ for (let i = 0; i < size; i++) {
547
+ for (let j = 0; j < size; j++) {
548
+ const index = i * size + j;
549
+ sampler.sample(position);
550
+ data[4 * index] = position.x;
551
+ data[4 * index + 1] = position.y;
552
+ data[4 * index + 2] = position.z;
553
+ data[4 * index + 3] = (Math.random() - 0.5) * 0.01;
554
+ }
542
555
  }
556
+ } finally {
557
+ geometry.dispose();
543
558
  }
544
- geometry.dispose();
545
- material.dispose();
546
559
  return data;
547
560
  }
548
561
  const instanceFragmentShader = `
@@ -707,15 +720,16 @@ class InstancedMeshManager {
707
720
  /**
708
721
  * Resizes or replaces the instanced mesh.
709
722
  * @param size The new size of the instanced mesh.
710
- * @returns The updated instanced mesh.
723
+ * @returns An object containing the updated mesh, the previous mesh, and a boolean indicating whether the mesh was updated.
711
724
  */
712
725
  resize(size) {
713
- if (this.size === size) return this.mesh;
726
+ if (this.size === size) return { current: this.mesh, previous: this.mesh };
714
727
  this.size = size;
715
728
  const prev = this.mesh;
716
729
  this.mesh = this.createInstancedMesh(size, prev.geometry, prev.material);
717
- prev.dispose();
718
- return this.mesh;
730
+ this.copyMeshState(prev, this.mesh);
731
+ this.replaceMeshInParent(prev, this.mesh);
732
+ return { current: this.mesh, previous: prev };
719
733
  }
720
734
  /**
721
735
  * Disposes the resources used by the InstancedMeshManager.
@@ -724,7 +738,6 @@ class InstancedMeshManager {
724
738
  this.mesh.dispose();
725
739
  this.geometries.forEach((geometry) => geometry.dispose());
726
740
  this.shaderMaterial.dispose();
727
- this.fallbackGeometry.dispose();
728
741
  this.uvRefsCache.clear();
729
742
  this.geometries.clear();
730
743
  }
@@ -783,6 +796,41 @@ class InstancedMeshManager {
783
796
  const count = size * size;
784
797
  return new THREE.InstancedMesh(geometry, material, count);
785
798
  }
799
+ copyMeshState(source, target) {
800
+ target.name = source.name;
801
+ target.up.copy(source.up);
802
+ target.position.copy(source.position);
803
+ target.quaternion.copy(source.quaternion);
804
+ target.scale.copy(source.scale);
805
+ target.matrix.copy(source.matrix);
806
+ target.matrixWorld.copy(source.matrixWorld);
807
+ target.matrixAutoUpdate = source.matrixAutoUpdate;
808
+ target.matrixWorldAutoUpdate = source.matrixWorldAutoUpdate;
809
+ target.matrixWorldNeedsUpdate = source.matrixWorldNeedsUpdate;
810
+ target.layers.mask = source.layers.mask;
811
+ target.visible = source.visible;
812
+ target.castShadow = source.castShadow;
813
+ target.receiveShadow = source.receiveShadow;
814
+ target.frustumCulled = source.frustumCulled;
815
+ target.renderOrder = source.renderOrder;
816
+ target.animations = source.animations;
817
+ target.userData = source.userData;
818
+ target.customDepthMaterial = source.customDepthMaterial;
819
+ target.customDistanceMaterial = source.customDistanceMaterial;
820
+ target.onBeforeRender = source.onBeforeRender;
821
+ target.onAfterRender = source.onAfterRender;
822
+ }
823
+ replaceMeshInParent(previous, current) {
824
+ const parent = previous.parent;
825
+ if (!parent) return;
826
+ const previousIndex = parent.children.indexOf(previous);
827
+ parent.add(current);
828
+ parent.remove(previous);
829
+ const currentIndex = parent.children.indexOf(current);
830
+ if (previousIndex < 0 || currentIndex < 0 || previousIndex === currentIndex) return;
831
+ parent.children.splice(currentIndex, 1);
832
+ parent.children.splice(Math.min(previousIndex, parent.children.length), 0, current);
833
+ }
786
834
  }
787
835
  class IntersectionService {
788
836
  /**
@@ -796,11 +844,8 @@ class IntersectionService {
796
844
  __publicField(this, "mousePosition", new THREE.Vector2());
797
845
  __publicField(this, "camera");
798
846
  __publicField(this, "meshSequenceGeometries", []);
799
- // ADDED: Store cloned geometries
800
847
  __publicField(this, "meshSequenceUUIDs", []);
801
- // ADDED: Track UUIDs to avoid redundant cloning
802
848
  __publicField(this, "overallProgress", 0);
803
- // ADDED: Store overall progress (0-1)
804
849
  __publicField(this, "intersectionMesh", new THREE.Mesh());
805
850
  // Use a single mesh for intersection target
806
851
  __publicField(this, "geometryNeedsUpdate");
@@ -1555,15 +1600,12 @@ class SimulationRendererService {
1555
1600
  __publicField(this, "state");
1556
1601
  __publicField(this, "textureSize");
1557
1602
  __publicField(this, "overallProgress");
1558
- // ADDED: Store overall progress
1559
1603
  __publicField(this, "velocityTractionForce");
1560
1604
  __publicField(this, "positionalTractionForce");
1561
1605
  __publicField(this, "simulationRenderer");
1562
1606
  __publicField(this, "webGLRenderer");
1563
1607
  __publicField(this, "eventEmitter");
1564
- // Store atlas info
1565
1608
  __publicField(this, "currentAtlasEntry", null);
1566
- // ADDED
1567
1609
  __publicField(this, "lastKnownVelocityDataTexture");
1568
1610
  __publicField(this, "lastKnownPositionDataTexture");
1569
1611
  this.eventEmitter = eventEmitter;
@@ -1642,41 +1684,13 @@ class SimulationRendererService {
1642
1684
  this.simulationRenderer.setMaxRepelDistance(distance);
1643
1685
  }
1644
1686
  }
1645
- class ExecutionStatusMap {
1646
- constructor() {
1647
- __publicField(this, "execStatus", /* @__PURE__ */ new Map());
1648
- }
1649
- get(type) {
1650
- const status = this.execStatus.get(type);
1651
- if (!status) {
1652
- this.execStatus.set(type, "idle");
1653
- return "idle";
1654
- }
1655
- return status;
1656
- }
1657
- set(type, status) {
1658
- this.execStatus.set(type, status);
1659
- }
1660
- }
1661
1687
  class TransitionService {
1662
1688
  constructor(eventEmitter) {
1663
1689
  __publicField(this, "eventEmitter");
1664
1690
  __publicField(this, "transitions", /* @__PURE__ */ new Map());
1665
- __publicField(this, "execStatus");
1666
1691
  __publicField(this, "ongoingTransitions", /* @__PURE__ */ new Map());
1667
- __publicField(this, "boundHandleTransitionCancelled");
1668
1692
  this.eventEmitter = eventEmitter;
1669
- this.execStatus = new ExecutionStatusMap();
1670
- this.boundHandleTransitionCancelled = this.handleTransitionCancelledEvent.bind(this);
1671
- this.eventEmitter.on("transitionCancelled", this.boundHandleTransitionCancelled);
1672
- }
1673
- dispose() {
1674
- this.eventEmitter.off("transitionCancelled", this.boundHandleTransitionCancelled);
1675
- this.ongoingTransitions.forEach((transition) => {
1676
- transition.cancelled = true;
1677
- });
1678
- this.ongoingTransitions.clear();
1679
- this.transitions.clear();
1693
+ this.eventEmitter.on("transitionCancelled", this.handleTransitionCancelledEvent.bind(this));
1680
1694
  }
1681
1695
  /**
1682
1696
  * Enqueues a transition.
@@ -1740,6 +1754,7 @@ class TransitionService {
1740
1754
  if (ongoingTransition) {
1741
1755
  ongoingTransition.cancelled = true;
1742
1756
  (_a = ongoingTransition.onTransitionCancelled) == null ? void 0 : _a.call(ongoingTransition);
1757
+ this.ongoingTransitions.delete(type);
1743
1758
  }
1744
1759
  }
1745
1760
  emitTransitionProgress(type, progress) {
@@ -1749,6 +1764,28 @@ class TransitionService {
1749
1764
  this.eventEmitter.emit("transitionFinished", { type });
1750
1765
  }
1751
1766
  }
1767
+ function resolveSequenceInterpolation(progress, itemCount) {
1768
+ if (itemCount <= 1) {
1769
+ return { indexA: 0, indexB: 0, localProgress: 0 };
1770
+ }
1771
+ const clampedProgress = Math.max(0, Math.min(progress, 1));
1772
+ const totalSegments = itemCount - 1;
1773
+ const scaledProgress = clampedProgress * totalSegments;
1774
+ if (clampedProgress >= 1) {
1775
+ return {
1776
+ indexA: totalSegments,
1777
+ indexB: totalSegments,
1778
+ localProgress: 1
1779
+ };
1780
+ }
1781
+ const indexA = Math.floor(scaledProgress);
1782
+ const indexB = Math.min(indexA + 1, totalSegments);
1783
+ return {
1784
+ indexA,
1785
+ indexB,
1786
+ localProgress: scaledProgress - indexA
1787
+ };
1788
+ }
1752
1789
  class ParticlesEngine {
1753
1790
  /**
1754
1791
  * Creates a new ParticlesEngine instance.
@@ -1767,21 +1804,16 @@ class ParticlesEngine {
1767
1804
  __publicField(this, "engineState");
1768
1805
  __publicField(this, "intersectionService");
1769
1806
  __publicField(this, "meshSequenceAtlasTexture", null);
1770
- // ADDED: To store the generated atlas
1807
+ __publicField(this, "lastRenderElapsedTimeSeconds", null);
1771
1808
  __publicField(this, "eventEmitter");
1772
- // Bound event handlers for proper cleanup
1773
- __publicField(this, "boundHandleServiceStateUpdated");
1774
- __publicField(this, "boundHandleInteractionPositionUpdated");
1775
1809
  const { scene, renderer, camera, textureSize, useIntersection = true } = params;
1776
1810
  this.eventEmitter = new DefaultEventEmitter();
1777
1811
  this.serviceStates = this.getInitialServiceStates();
1778
- this.boundHandleServiceStateUpdated = this.handleServiceStateUpdated.bind(this);
1779
- this.boundHandleInteractionPositionUpdated = this.handleInteractionPositionUpdated.bind(this);
1780
- this.eventEmitter.on("serviceStateUpdated", this.boundHandleServiceStateUpdated);
1812
+ this.eventEmitter.on("serviceStateUpdated", this.handleServiceStateUpdated.bind(this));
1781
1813
  this.scene = scene;
1782
1814
  this.renderer = renderer;
1783
1815
  this.engineState = this.initialEngineState(params);
1784
- this.assetService = new AssetService(this.eventEmitter);
1816
+ this.assetService = new AssetService(this.eventEmitter, { dracoDecoderPath: params.dracoDecoderPath });
1785
1817
  this.transitionService = new TransitionService(this.eventEmitter);
1786
1818
  this.dataTextureManager = new DataTextureService(this.eventEmitter, textureSize);
1787
1819
  this.simulationRendererService = new SimulationRendererService(this.eventEmitter, textureSize, this.renderer);
@@ -1790,18 +1822,23 @@ class ParticlesEngine {
1790
1822
  this.intersectionService = new IntersectionService(this.eventEmitter, camera);
1791
1823
  if (!useIntersection) this.intersectionService.setActive(false);
1792
1824
  this.setOverallProgress(0, false);
1793
- this.eventEmitter.on("interactionPositionUpdated", this.boundHandleInteractionPositionUpdated);
1825
+ this.eventEmitter.on("interactionPositionUpdated", this.handleInteractionPositionUpdated.bind(this));
1794
1826
  }
1795
1827
  /**
1796
1828
  * Renders the scene.
1797
1829
  * @param elapsedTime The elapsed time since the last frame.
1798
1830
  */
1799
- render(elapsedTime) {
1800
- const dt = elapsedTime / 1e3;
1801
- this.transitionService.compute(dt);
1831
+ render(elapsedTimeMs) {
1832
+ const elapsedTimeSeconds = elapsedTimeMs / 1e3;
1833
+ const deltaTimeSeconds = this.lastRenderElapsedTimeSeconds === null ? 0 : Math.max(0, elapsedTimeSeconds - this.lastRenderElapsedTimeSeconds);
1834
+ this.lastRenderElapsedTimeSeconds = elapsedTimeSeconds;
1835
+ this.renderFrame(deltaTimeSeconds, elapsedTimeSeconds);
1836
+ }
1837
+ renderFrame(deltaTimeSeconds, elapsedTimeSeconds) {
1838
+ this.transitionService.compute(elapsedTimeSeconds);
1802
1839
  this.intersectionService.calculate(this.instancedMeshManager.getMesh());
1803
- this.simulationRendererService.compute(dt);
1804
- this.instancedMeshManager.update(dt);
1840
+ this.simulationRendererService.compute(deltaTimeSeconds);
1841
+ this.instancedMeshManager.update(elapsedTimeSeconds);
1805
1842
  this.instancedMeshManager.updateVelocityTexture(this.simulationRendererService.getVelocityTexture());
1806
1843
  this.instancedMeshManager.updatePositionTexture(this.simulationRendererService.getPositionTexture());
1807
1844
  }
@@ -1810,27 +1847,30 @@ class ParticlesEngine {
1810
1847
  this.eventEmitter.emit("textureSequenceUpdated", { sequence });
1811
1848
  this.setOverallProgress(0, false);
1812
1849
  }
1813
- // --- Update setTextureSize ---
1814
1850
  async setTextureSize(size) {
1815
1851
  if (this.engineState.textureSize === size) {
1816
1852
  return;
1817
1853
  }
1854
+ const meshSequence = [...this.engineState.meshSequence];
1855
+ const overallProgress = this.engineState.overallProgress;
1818
1856
  this.engineState.textureSize = size;
1819
1857
  this.dataTextureManager.setTextureSize(size);
1820
1858
  this.simulationRendererService.setTextureSize(size);
1821
- this.scene.remove(this.instancedMeshManager.getMesh());
1822
- this.instancedMeshManager.resize(size);
1823
- this.scene.add(this.instancedMeshManager.getMesh());
1824
- if (this.engineState.meshSequence.length > 0) {
1825
- await this.setMeshSequence(this.engineState.meshSequence);
1859
+ const { current, previous } = this.instancedMeshManager.resize(size);
1860
+ if (current !== previous) {
1861
+ previous.dispose();
1862
+ if (!current.parent) {
1863
+ this.scene.add(current);
1864
+ }
1865
+ }
1866
+ if (meshSequence.length > 0) {
1867
+ await this.setMeshSequence(meshSequence);
1826
1868
  }
1827
1869
  this.simulationRendererService.setVelocityTractionForce(this.engineState.velocityTractionForce);
1828
1870
  this.simulationRendererService.setPositionalTractionForce(this.engineState.positionalTractionForce);
1829
1871
  this.simulationRendererService.setMaxRepelDistance(this.engineState.maxRepelDistance);
1830
- this.simulationRendererService.setOverallProgress(this.engineState.overallProgress);
1831
- this.intersectionService.setOverallProgress(this.engineState.overallProgress);
1832
1872
  this.instancedMeshManager.setGeometrySize(this.engineState.instanceGeometryScale);
1833
- this.setOverallProgress(this.engineState.overallProgress, false);
1873
+ this.setOverallProgress(overallProgress, false);
1834
1874
  }
1835
1875
  registerMesh(id, mesh) {
1836
1876
  this.assetService.register(id, mesh);
@@ -1838,8 +1878,8 @@ class ParticlesEngine {
1838
1878
  registerMatcap(id, matcap) {
1839
1879
  this.assetService.register(id, matcap);
1840
1880
  }
1841
- async fetchAndRegisterMesh(id, url) {
1842
- return await this.assetService.loadMeshAsync(id, url);
1881
+ async fetchAndRegisterMesh(id, url, options) {
1882
+ return await this.assetService.loadMeshAsync(id, url, options);
1843
1883
  }
1844
1884
  async fetchAndRegisterMatcap(id, url) {
1845
1885
  return await this.assetService.loadTextureAsync(id, url);
@@ -1932,7 +1972,7 @@ class ParticlesEngine {
1932
1972
  this.engineState.overallProgress = clampedProgress;
1933
1973
  this.simulationRendererService.setOverallProgress(clampedProgress);
1934
1974
  this.intersectionService.setOverallProgress(clampedProgress);
1935
- const { textureA, textureB, localProgress } = this.calculateTextureInterpolation(progress);
1975
+ const { textureA, textureB, localProgress } = this.calculateTextureInterpolation(clampedProgress);
1936
1976
  this.instancedMeshManager.updateTextureInterpolation(textureA, textureB, localProgress);
1937
1977
  }
1938
1978
  /**
@@ -1998,27 +2038,22 @@ class ParticlesEngine {
1998
2038
  * Disposes the resources used by the engine.
1999
2039
  */
2000
2040
  dispose() {
2001
- var _a, _b, _c, _d, _e, _f, _g;
2041
+ var _a, _b, _c, _d, _e, _f;
2002
2042
  if (this.scene && this.instancedMeshManager) {
2003
2043
  this.scene.remove(this.instancedMeshManager.getMesh());
2004
2044
  }
2005
- this.eventEmitter.off("serviceStateUpdated", this.boundHandleServiceStateUpdated);
2006
- this.eventEmitter.off("interactionPositionUpdated", this.boundHandleInteractionPositionUpdated);
2007
2045
  (_a = this.simulationRendererService) == null ? void 0 : _a.dispose();
2008
2046
  (_b = this.instancedMeshManager) == null ? void 0 : _b.dispose();
2009
2047
  (_c = this.intersectionService) == null ? void 0 : _c.dispose();
2010
2048
  (_d = this.assetService) == null ? void 0 : _d.dispose();
2011
2049
  (_e = this.dataTextureManager) == null ? void 0 : _e.dispose();
2012
- (_f = this.transitionService) == null ? void 0 : _f.dispose();
2013
- (_g = this.eventEmitter) == null ? void 0 : _g.dispose();
2050
+ (_f = this.eventEmitter) == null ? void 0 : _f.dispose();
2014
2051
  }
2015
2052
  initialEngineState(params) {
2016
2053
  return {
2017
2054
  textureSize: params.textureSize,
2018
2055
  meshSequence: [],
2019
- // ADDED
2020
2056
  overallProgress: 0,
2021
- // ADDED
2022
2057
  textureSequence: [],
2023
2058
  velocityTractionForce: 0.1,
2024
2059
  positionalTractionForce: 0.1,
@@ -2051,23 +2086,7 @@ class ParticlesEngine {
2051
2086
  const tex = this.getTextureForSequenceItem(sequence[0]);
2052
2087
  return { textureA: tex, textureB: tex, localProgress: 0 };
2053
2088
  }
2054
- const totalSegments = numItems - 1;
2055
- const progressPerSegment = 1 / totalSegments;
2056
- const scaledProgress = progress * totalSegments;
2057
- let indexA = Math.floor(scaledProgress);
2058
- let indexB = indexA + 1;
2059
- indexA = Math.max(0, Math.min(indexA, totalSegments));
2060
- indexB = Math.max(0, Math.min(indexB, totalSegments));
2061
- let localProgress = 0;
2062
- if (progressPerSegment > 0) {
2063
- localProgress = (progress - indexA * progressPerSegment) / progressPerSegment;
2064
- }
2065
- if (progress >= 1) {
2066
- indexA = totalSegments;
2067
- indexB = totalSegments;
2068
- localProgress = 1;
2069
- }
2070
- localProgress = Math.max(0, Math.min(localProgress, 1));
2089
+ const { indexA, indexB, localProgress } = resolveSequenceInterpolation(progress, numItems);
2071
2090
  const itemA = sequence[indexA];
2072
2091
  const itemB = sequence[indexB];
2073
2092
  const textureA = this.getTextureForSequenceItem(itemA);
@@ -2083,6 +2102,7 @@ class ParticlesEngine {
2083
2102
  }
2084
2103
  }
2085
2104
  export {
2086
- ParticlesEngine
2105
+ ParticlesEngine,
2106
+ resolveSequenceInterpolation
2087
2107
  };
2088
2108
  //# sourceMappingURL=ionian.js.map