@flighthq/particles 0.5.1-next.685.f72fc7f → 0.5.1-next.704.19c91ec
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 +2 -2
- package/dist/contract.d.ts +1 -0
- package/dist/contract.d.ts.map +1 -1
- package/dist/contract.js +1 -0
- package/dist/contract.js.map +1 -1
- package/dist/particleSpawnOffset.d.ts +11 -0
- package/dist/particleSpawnOffset.d.ts.map +1 -0
- package/dist/particleSpawnOffset.js +37 -0
- package/dist/particleSpawnOffset.js.map +1 -0
- package/dist/updateParticleObjects.d.ts.map +1 -1
- package/dist/updateParticleObjects.js +11 -17
- package/dist/updateParticleObjects.js.map +1 -1
- package/package.json +4 -4
- package/src/particleSpawnOffset.test.ts +57 -0
- package/src/updateParticleObjects.test.ts +95 -0
package/README.md
CHANGED
|
@@ -13,5 +13,5 @@ Import the supported application-facing API from `@flighthq/particles`. The `@fl
|
|
|
13
13
|
This package is part of the locked-version Flight SDK graph. Applications may instead install and import `@flighthq/sdk` when package-level tree shaking is sufficient.
|
|
14
14
|
|
|
15
15
|
- [Flight project](https://github.com/flighthq/flight)
|
|
16
|
-
- [Source for @flighthq/particles@0.5.1-next.
|
|
17
|
-
- [License](https://github.com/flighthq/flight/blob/
|
|
16
|
+
- [Source for @flighthq/particles@0.5.1-next.704.19c91ec](https://github.com/flighthq/flight/tree/19c91ec3e569d6b3b471682726465a79fba9f8cb/packages/particles)
|
|
17
|
+
- [License](https://github.com/flighthq/flight/blob/19c91ec3e569d6b3b471682726465a79fba9f8cb/LICENSE.md)
|
package/dist/contract.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export * from './particleEmitterConfig';
|
|
|
5
5
|
export * from './particleEmitterSignals';
|
|
6
6
|
export * from './particleEmitterState';
|
|
7
7
|
export * from './particleObjectsState';
|
|
8
|
+
export * from './particleSpawnOffset';
|
|
8
9
|
export * from './stepParticleObjects';
|
|
9
10
|
export * from './updateParticleObjects';
|
|
10
11
|
export * from './validateParticleEmitterConfig';
|
package/dist/contract.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,cAAc,2BAA2B,CAAC;AAC1C,cAAc,uBAAuB,CAAC;AACtC,cAAc,SAAS,CAAC;AACxB,cAAc,yBAAyB,CAAC;AACxC,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC;AACvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,iCAAiC,CAAC"}
|
|
1
|
+
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,cAAc,2BAA2B,CAAC;AAC1C,cAAc,uBAAuB,CAAC;AACtC,cAAc,SAAS,CAAC;AACxB,cAAc,yBAAyB,CAAC;AACxC,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC;AACvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,uBAAuB,CAAC;AACtC,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,iCAAiC,CAAC"}
|
package/dist/contract.js
CHANGED
|
@@ -5,6 +5,7 @@ export * from './particleEmitterConfig';
|
|
|
5
5
|
export * from './particleEmitterSignals';
|
|
6
6
|
export * from './particleEmitterState';
|
|
7
7
|
export * from './particleObjectsState';
|
|
8
|
+
export * from './particleSpawnOffset';
|
|
8
9
|
export * from './stepParticleObjects';
|
|
9
10
|
export * from './updateParticleObjects';
|
|
10
11
|
export * from './validateParticleEmitterConfig';
|
package/dist/contract.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,cAAc,2BAA2B,CAAC;AAC1C,cAAc,uBAAuB,CAAC;AACtC,cAAc,SAAS,CAAC;AACxB,cAAc,yBAAyB,CAAC;AACxC,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC;AACvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,iCAAiC,CAAC"}
|
|
1
|
+
{"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,cAAc,2BAA2B,CAAC;AAC1C,cAAc,uBAAuB,CAAC;AACtC,cAAc,SAAS,CAAC;AACxB,cAAc,yBAAyB,CAAC;AACxC,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC;AACvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,uBAAuB,CAAC;AACtC,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,iCAAiC,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ParticleEmitterConfig, RandomSource } from '@flighthq/types/contract';
|
|
2
|
+
/** Write a deterministic 2D spawn-shape offset at `offset` in `out`.
|
|
3
|
+
*
|
|
4
|
+
* Point and zero-size shapes write the origin without drawing from `random`.
|
|
5
|
+
* Circle samples its area, ring samples its circumference, rectangle samples its
|
|
6
|
+
* area, and line samples the horizontal segment from `-width / 2` (inclusive) to
|
|
7
|
+
* `width / 2` (exclusive), matching the SDK `RandomSource` range of `[0, 1)`.
|
|
8
|
+
* Shapes with a 3D interpretation are left at the origin for their caller to
|
|
9
|
+
* handle. */
|
|
10
|
+
export declare function writeParticleSpawnOffset(out: number[] | Float32Array, offset: number, config: Readonly<ParticleEmitterConfig>, random: RandomSource): void;
|
|
11
|
+
//# sourceMappingURL=particleSpawnOffset.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"particleSpawnOffset.d.ts","sourceRoot":"","sources":["../src/particleSpawnOffset.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAIpF;;;;;;;aAOa;AACb,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,MAAM,EAAE,GAAG,YAAY,EAC5B,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,QAAQ,CAAC,qBAAqB,CAAC,EACvC,MAAM,EAAE,YAAY,GACnB,IAAI,CAwBN"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
const TWO_PI = Math.PI * 2;
|
|
2
|
+
/** Write a deterministic 2D spawn-shape offset at `offset` in `out`.
|
|
3
|
+
*
|
|
4
|
+
* Point and zero-size shapes write the origin without drawing from `random`.
|
|
5
|
+
* Circle samples its area, ring samples its circumference, rectangle samples its
|
|
6
|
+
* area, and line samples the horizontal segment from `-width / 2` (inclusive) to
|
|
7
|
+
* `width / 2` (exclusive), matching the SDK `RandomSource` range of `[0, 1)`.
|
|
8
|
+
* Shapes with a 3D interpretation are left at the origin for their caller to
|
|
9
|
+
* handle. */
|
|
10
|
+
export function writeParticleSpawnOffset(out, offset, config, random) {
|
|
11
|
+
let x = 0;
|
|
12
|
+
let y = 0;
|
|
13
|
+
const shape = config.emitterShape;
|
|
14
|
+
if (shape === 'circle' && config.emitterRadius > 0) {
|
|
15
|
+
const radius = Math.sqrt(random()) * config.emitterRadius;
|
|
16
|
+
const angle = random() * TWO_PI;
|
|
17
|
+
x = Math.cos(angle) * radius;
|
|
18
|
+
y = Math.sin(angle) * radius;
|
|
19
|
+
}
|
|
20
|
+
else if (shape === 'line' && config.emitterWidth > 0) {
|
|
21
|
+
x = (random() - 0.5) * config.emitterWidth;
|
|
22
|
+
}
|
|
23
|
+
else if (shape === 'rect' && (config.emitterWidth > 0 || config.emitterHeight > 0)) {
|
|
24
|
+
const xSample = random();
|
|
25
|
+
const ySample = random();
|
|
26
|
+
x = (xSample - 0.5) * config.emitterWidth;
|
|
27
|
+
y = (ySample - 0.5) * config.emitterHeight;
|
|
28
|
+
}
|
|
29
|
+
else if (shape === 'ring' && config.emitterRadius > 0) {
|
|
30
|
+
const angle = random() * TWO_PI;
|
|
31
|
+
x = Math.cos(angle) * config.emitterRadius;
|
|
32
|
+
y = Math.sin(angle) * config.emitterRadius;
|
|
33
|
+
}
|
|
34
|
+
out[offset] = x;
|
|
35
|
+
out[offset + 1] = y;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=particleSpawnOffset.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"particleSpawnOffset.js","sourceRoot":"","sources":["../src/particleSpawnOffset.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,GAAG,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC;AAE3B;;;;;;;aAOa;AACb,MAAM,UAAU,wBAAwB,CACtC,GAA4B,EAC5B,MAAc,EACd,MAAuC,EACvC,MAAoB;IAEpB,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,MAAM,KAAK,GAAG,MAAM,CAAC,YAAY,CAAC;IAClC,IAAI,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,aAAa,GAAG,CAAC,EAAE,CAAC;QACnD,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,GAAG,MAAM,CAAC,aAAa,CAAC;QAC1D,MAAM,KAAK,GAAG,MAAM,EAAE,GAAG,MAAM,CAAC;QAChC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC;QAC7B,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC;IAC/B,CAAC;SAAM,IAAI,KAAK,KAAK,MAAM,IAAI,MAAM,CAAC,YAAY,GAAG,CAAC,EAAE,CAAC;QACvD,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,GAAG,MAAM,CAAC,YAAY,CAAC;IAC7C,CAAC;SAAM,IAAI,KAAK,KAAK,MAAM,IAAI,CAAC,MAAM,CAAC,YAAY,GAAG,CAAC,IAAI,MAAM,CAAC,aAAa,GAAG,CAAC,CAAC,EAAE,CAAC;QACrF,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC;QACzB,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC;QACzB,CAAC,GAAG,CAAC,OAAO,GAAG,GAAG,CAAC,GAAG,MAAM,CAAC,YAAY,CAAC;QAC1C,CAAC,GAAG,CAAC,OAAO,GAAG,GAAG,CAAC,GAAG,MAAM,CAAC,aAAa,CAAC;IAC7C,CAAC;SAAM,IAAI,KAAK,KAAK,MAAM,IAAI,MAAM,CAAC,aAAa,GAAG,CAAC,EAAE,CAAC;QACxD,MAAM,KAAK,GAAG,MAAM,EAAE,GAAG,MAAM,CAAC;QAChC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,aAAa,CAAC;QAC3C,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,aAAa,CAAC;IAC7C,CAAC;IAED,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAChB,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;AACtB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"updateParticleObjects.d.ts","sourceRoot":"","sources":["../src/updateParticleObjects.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,qBAAqB,EACrB,cAAc,EACd,oBAAoB,EACpB,4BAA4B,EAC7B,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"updateParticleObjects.d.ts","sourceRoot":"","sources":["../src/updateParticleObjects.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,qBAAqB,EACrB,cAAc,EACd,oBAAoB,EACpB,4BAA4B,EAC7B,MAAM,0BAA0B,CAAC;AAQlC;;yEAEyE;AACzE,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,SAAS,cAAc,EAAE,EAClC,KAAK,EAAE,QAAQ,CAAC,oBAAoB,CAAC,EACrC,MAAM,EAAE,QAAQ,CAAC,qBAAqB,CAAC,GACtC,OAAO,CAOT;AAED,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,SAAS,cAAc,EAAE,EAClC,KAAK,EAAE,oBAAoB,EAC3B,MAAM,EAAE,QAAQ,CAAC,qBAAqB,CAAC,EACvC,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,4BAA4B,GACrC,IAAI,CAsIN"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { sampleParticleCurve } from './curve';
|
|
2
2
|
import { ensureParticleObjectsStateCapacity } from './particleObjectsState';
|
|
3
|
-
|
|
3
|
+
import { writeParticleSpawnOffset } from './particleSpawnOffset';
|
|
4
|
+
const PARTICLE_SPAWN_OFFSET = [0, 0];
|
|
4
5
|
/** True once a finite, non-looping object emitter has finished emitting AND all of
|
|
5
6
|
* its objects are dead (invisible) — a one-shot effect that is safe to recycle.
|
|
6
7
|
* Always false for infinite or looping emitters (they never finish). */
|
|
@@ -54,8 +55,11 @@ export function updateParticleObjects(objects, state, config, deltaTime, options
|
|
|
54
55
|
lifetimes[lt] += deltaTime;
|
|
55
56
|
if (lifetimes[lt] >= lifetimes[lt + 1]) {
|
|
56
57
|
lifetimes[lt + 1] = 0;
|
|
57
|
-
objects[i]
|
|
58
|
-
|
|
58
|
+
const object = objects[i];
|
|
59
|
+
const deathX = object.x;
|
|
60
|
+
const deathY = object.y;
|
|
61
|
+
object.visible = false;
|
|
62
|
+
onDeath?.(deathX, deathY, 0);
|
|
59
63
|
continue;
|
|
60
64
|
}
|
|
61
65
|
const vt = i * 2;
|
|
@@ -113,19 +117,9 @@ export function updateParticleObjects(objects, state, config, deltaTime, options
|
|
|
113
117
|
Math.cos(angle) * speed + (config.velocityInheritance !== 0 ? emitterVelX * config.velocityInheritance : 0);
|
|
114
118
|
velocities[vt + 1] =
|
|
115
119
|
Math.sin(angle) * speed + (config.velocityInheritance !== 0 ? emitterVelY * config.velocityInheritance : 0);
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
if (config.emitterShape === 'circle' && config.emitterRadius > 0) {
|
|
120
|
-
const r = Math.sqrt(state.random()) * config.emitterRadius;
|
|
121
|
-
const a = state.random() * TWO_PI;
|
|
122
|
-
spawnX = Math.cos(a) * r;
|
|
123
|
-
spawnY = Math.sin(a) * r;
|
|
124
|
-
}
|
|
125
|
-
else if (config.emitterShape === 'rect' && (config.emitterWidth > 0 || config.emitterHeight > 0)) {
|
|
126
|
-
spawnX = (state.random() - 0.5) * config.emitterWidth;
|
|
127
|
-
spawnY = (state.random() - 0.5) * config.emitterHeight;
|
|
128
|
-
}
|
|
120
|
+
writeParticleSpawnOffset(PARTICLE_SPAWN_OFFSET, 0, config, state.random);
|
|
121
|
+
const spawnX = PARTICLE_SPAWN_OFFSET[0];
|
|
122
|
+
const spawnY = PARTICLE_SPAWN_OFFSET[1];
|
|
129
123
|
const spawnScale = config.scaleMin + state.random() * (config.scaleMax - config.scaleMin);
|
|
130
124
|
scales[i] = spawnScale;
|
|
131
125
|
rotationSpeeds[i] = hasRotSpeed ? config.rotationSpeedMin + state.random() * rotSpeedRange : 0;
|
|
@@ -139,7 +133,7 @@ export function updateParticleObjects(objects, state, config, deltaTime, options
|
|
|
139
133
|
obj.alpha = hasAlphaCurve ? sampleParticleCurve(alphaCurve, 0) : config.alphaStart;
|
|
140
134
|
obj.visible = true;
|
|
141
135
|
toSpawn--;
|
|
142
|
-
onSpawn?.(spawnX, spawnY);
|
|
136
|
+
onSpawn?.(spawnX, spawnY, 0);
|
|
143
137
|
}
|
|
144
138
|
}
|
|
145
139
|
// Track emitter position for next frame's velocity inheritance.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"updateParticleObjects.js","sourceRoot":"","sources":["../src/updateParticleObjects.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAC9C,OAAO,EAAE,kCAAkC,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"updateParticleObjects.js","sourceRoot":"","sources":["../src/updateParticleObjects.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flighthq/particles",
|
|
3
|
-
"version": "0.5.1-next.
|
|
3
|
+
"version": "0.5.1-next.704.19c91ec",
|
|
4
4
|
"author": "Joshua Granick and other contributors",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -38,9 +38,9 @@
|
|
|
38
38
|
"clean:dist": "tsx ../../scripts/clean-package-dist.ts"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@flighthq/geometry": "0.5.1-next.
|
|
42
|
-
"@flighthq/signals": "0.5.1-next.
|
|
43
|
-
"@flighthq/types": "0.5.1-next.
|
|
41
|
+
"@flighthq/geometry": "0.5.1-next.704.19c91ec",
|
|
42
|
+
"@flighthq/signals": "0.5.1-next.704.19c91ec",
|
|
43
|
+
"@flighthq/types": "0.5.1-next.704.19c91ec"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@flighthq/math": "*",
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { createParticleEmitterConfig } from './particleEmitterConfig';
|
|
2
|
+
import { writeParticleSpawnOffset } from './particleSpawnOffset';
|
|
3
|
+
|
|
4
|
+
describe('writeParticleSpawnOffset', () => {
|
|
5
|
+
it('samples circle area and rectangle bounds with their existing draw order', () => {
|
|
6
|
+
const out = [NaN, NaN];
|
|
7
|
+
const samples = [0.25, 0, 0, 0.75];
|
|
8
|
+
let index = 0;
|
|
9
|
+
writeParticleSpawnOffset(
|
|
10
|
+
out,
|
|
11
|
+
0,
|
|
12
|
+
createParticleEmitterConfig({ emitterRadius: 10, emitterShape: 'circle' }),
|
|
13
|
+
() => samples[index++],
|
|
14
|
+
);
|
|
15
|
+
expect(out[0]).toBeCloseTo(5);
|
|
16
|
+
expect(out[1]).toBeCloseTo(0);
|
|
17
|
+
writeParticleSpawnOffset(
|
|
18
|
+
out,
|
|
19
|
+
0,
|
|
20
|
+
createParticleEmitterConfig({ emitterHeight: 10, emitterShape: 'rect', emitterWidth: 20 }),
|
|
21
|
+
() => samples[index++],
|
|
22
|
+
);
|
|
23
|
+
expect(out).toEqual([-10, 2.5]);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('samples the line from its inclusive left boundary', () => {
|
|
27
|
+
const out = [NaN, NaN];
|
|
28
|
+
writeParticleSpawnOffset(out, 0, createParticleEmitterConfig({ emitterShape: 'line', emitterWidth: 20 }), () => 0);
|
|
29
|
+
expect(out).toEqual([-10, 0]);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('samples the ring circumference', () => {
|
|
33
|
+
const out = [NaN, NaN];
|
|
34
|
+
writeParticleSpawnOffset(
|
|
35
|
+
out,
|
|
36
|
+
0,
|
|
37
|
+
createParticleEmitterConfig({ emitterRadius: 10, emitterShape: 'ring' }),
|
|
38
|
+
() => 0.25,
|
|
39
|
+
);
|
|
40
|
+
expect(out[0]).toBeCloseTo(0);
|
|
41
|
+
expect(out[1]).toBeCloseTo(10);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it.each([
|
|
45
|
+
['line', { emitterWidth: 0 }],
|
|
46
|
+
['ring', { emitterRadius: 0 }],
|
|
47
|
+
] as const)('writes the origin for a degenerate %s without drawing randomness', (emitterShape, size) => {
|
|
48
|
+
const out = [NaN, NaN];
|
|
49
|
+
let randomCalls = 0;
|
|
50
|
+
writeParticleSpawnOffset(out, 0, createParticleEmitterConfig({ ...size, emitterShape }), () => {
|
|
51
|
+
randomCalls++;
|
|
52
|
+
return 0.5;
|
|
53
|
+
});
|
|
54
|
+
expect(out).toEqual([0, 0]);
|
|
55
|
+
expect(randomCalls).toBe(0);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
@@ -187,6 +187,71 @@ describe('updateParticleObjects', () => {
|
|
|
187
187
|
}
|
|
188
188
|
});
|
|
189
189
|
|
|
190
|
+
it('spawns objects on the line emitter boundary', () => {
|
|
191
|
+
const objects = [makeObject()];
|
|
192
|
+
const state = createParticleObjectsState(1, () => 0);
|
|
193
|
+
const config = createParticleEmitterConfig({
|
|
194
|
+
spawnRate: 1,
|
|
195
|
+
lifetimeMin: 10,
|
|
196
|
+
lifetimeMax: 10,
|
|
197
|
+
speedMin: 0,
|
|
198
|
+
speedMax: 0,
|
|
199
|
+
emitterShape: 'line',
|
|
200
|
+
emitterWidth: 80,
|
|
201
|
+
});
|
|
202
|
+
updateParticleObjects(objects, state, config, 1);
|
|
203
|
+
expect(objects[0].x).toBe(-40);
|
|
204
|
+
expect(objects[0].y).toBe(0);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it('spawns objects on the ring emitter boundary', () => {
|
|
208
|
+
const objects = Array.from({ length: 20 }, makeObject);
|
|
209
|
+
const state = createParticleObjectsState(20, createRandomSource(12345));
|
|
210
|
+
const config = createParticleEmitterConfig({
|
|
211
|
+
spawnRate: 20,
|
|
212
|
+
maxParticles: 20,
|
|
213
|
+
lifetimeMin: 10,
|
|
214
|
+
lifetimeMax: 10,
|
|
215
|
+
speedMin: 0,
|
|
216
|
+
speedMax: 0,
|
|
217
|
+
emitterShape: 'ring',
|
|
218
|
+
emitterRadius: 50,
|
|
219
|
+
});
|
|
220
|
+
updateParticleObjects(objects, state, config, 1);
|
|
221
|
+
for (const obj of objects) {
|
|
222
|
+
expect(Math.sqrt(obj.x * obj.x + obj.y * obj.y)).toBeCloseTo(50, 4);
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
it.each([
|
|
227
|
+
['line', { emitterWidth: 0 }],
|
|
228
|
+
['ring', { emitterRadius: 0 }],
|
|
229
|
+
] as const)('treats a degenerate %s emitter as a point without consuming a shape sample', (emitterShape, size) => {
|
|
230
|
+
const objects = [makeObject()];
|
|
231
|
+
let randomCalls = 0;
|
|
232
|
+
const state = createParticleObjectsState(1, () => {
|
|
233
|
+
randomCalls++;
|
|
234
|
+
return 0.25;
|
|
235
|
+
});
|
|
236
|
+
const config = createParticleEmitterConfig({
|
|
237
|
+
...size,
|
|
238
|
+
emitterShape,
|
|
239
|
+
lifetimeMin: 10,
|
|
240
|
+
lifetimeMax: 10,
|
|
241
|
+
regionIdMax: 0,
|
|
242
|
+
regionIdMin: 0,
|
|
243
|
+
scaleMax: 1,
|
|
244
|
+
scaleMin: 1,
|
|
245
|
+
spawnRate: 1,
|
|
246
|
+
speedMax: 0,
|
|
247
|
+
speedMin: 0,
|
|
248
|
+
spread: 0,
|
|
249
|
+
});
|
|
250
|
+
updateParticleObjects(objects, state, config, 1);
|
|
251
|
+
expect([objects[0].x, objects[0].y]).toEqual([0, 0]);
|
|
252
|
+
expect(randomCalls).toBe(4);
|
|
253
|
+
});
|
|
254
|
+
|
|
190
255
|
it('fires a one-shot burst on first frame', () => {
|
|
191
256
|
const objects = Array.from({ length: 30 }, makeObject);
|
|
192
257
|
const state = createParticleObjectsState(30);
|
|
@@ -218,6 +283,36 @@ describe('updateParticleObjects', () => {
|
|
|
218
283
|
expect(count).toBe(2);
|
|
219
284
|
});
|
|
220
285
|
|
|
286
|
+
it('uses the shared 3D callback payload for object spawns and deaths', () => {
|
|
287
|
+
const objects = [makeObject()];
|
|
288
|
+
const state = createParticleObjectsState(1, () => 0);
|
|
289
|
+
const config = createParticleEmitterConfig({
|
|
290
|
+
duration: 1,
|
|
291
|
+
emitterShape: 'line',
|
|
292
|
+
emitterWidth: 20,
|
|
293
|
+
lifetimeMax: 0.5,
|
|
294
|
+
lifetimeMin: 0.5,
|
|
295
|
+
loop: false,
|
|
296
|
+
spawnRate: 1,
|
|
297
|
+
speedMax: 0,
|
|
298
|
+
speedMin: 0,
|
|
299
|
+
});
|
|
300
|
+
const spawns: number[][] = [];
|
|
301
|
+
const deaths: number[][] = [];
|
|
302
|
+
const callbacks = {
|
|
303
|
+
onDeath: (x: number, y: number, z: number): void => {
|
|
304
|
+
deaths.push([x, y, z]);
|
|
305
|
+
},
|
|
306
|
+
onSpawn: (x: number, y: number, z: number): void => {
|
|
307
|
+
spawns.push([x, y, z]);
|
|
308
|
+
},
|
|
309
|
+
};
|
|
310
|
+
updateParticleObjects(objects, state, config, 1, { callbacks });
|
|
311
|
+
expect(spawns).toEqual([[-10, 0, 0]]);
|
|
312
|
+
updateParticleObjects(objects, state, config, 0.6, { callbacks });
|
|
313
|
+
expect(deaths).toEqual([[-10, 0, 0]]);
|
|
314
|
+
});
|
|
315
|
+
|
|
221
316
|
it('ignores a zero-deltaTime frame: no spawning or velocity corruption', () => {
|
|
222
317
|
const objects = [makeObject()];
|
|
223
318
|
const state = createParticleObjectsState(1);
|