import { describe, it, expect } from 'vitest';
import { stepParticle, sampleSpawn, sampleBurst } from '../particleSim';
import { createRng } from '../../math/rng';
import type { EmitterOptions } from '../Particles';
const opts: EmitterOptions = {
color: 0xffffff,
rate: 10,
lifetime: [1, 2],
velocity: { x: [-10, 10], y: [-10, 10] }
};
describe('stepParticle', () => {
it('позиция после шагов (полу-неявный Эйлер)', () => {
const p = { x: 0, y: 0, vx: 10, vy: -5, age: 0, lifetime: 10 };
stepParticle(p, 0.5, undefined, false);
expect(p.x).toBeCloseTo(5);
expect(p.y).toBeCloseTo(-2.5);
expect(p.age).toBeCloseTo(0.5);
});
it('ускорение влияет на скорость', () => {
const p = { x: 0, y: 0, vx: 0, vy: 0, age: 0, lifetime: 10 };
stepParticle(p, 1, { y: 100 }, false);
expect(p.vy).toBeCloseTo(100);
expect(p.y).toBeCloseTo(100); // скорость применилась в том же шаге
});
it('fadeOut уменьшает alpha, без fadeOut alpha = 1', () => {
const a = { x: 0, y: 0, vx: 0, vy: 0, age: 0, lifetime: 2 };
expect(stepParticle(a, 1, undefined, true)).toBeCloseTo(0.5);
const b = { x: 0, y: 0, vx: 0, vy: 0, age: 0, lifetime: 2 };
expect(stepParticle(b, 1, undefined, false)).toBe(1);
});
});
describe('sampleSpawn', () => {
it('учитывает spawnArea и диапазоны', () => {
const rng = createRng(1);
for (let i = 0; i < 50; i++) {
const s = sampleSpawn({ ...opts, spawnArea: { width: 20, height: 10 } }, rng);
expect(s.x).toBeGreaterThanOrEqual(-10);
expect(s.x).toBeLessThanOrEqual(10);
expect(s.y).toBeGreaterThanOrEqual(-5);
expect(s.y).toBeLessThanOrEqual(5);
expect(s.vx).toBeGreaterThanOrEqual(-10);
expect(s.vx).toBeLessThanOrEqual(10);
expect(s.lifetime).toBeGreaterThanOrEqual(1);
expect(s.lifetime).toBeLessThanOrEqual(2);
expect(s.age).toBe(0);
}
});
it('без области — старт из точки', () => {
const s = sampleSpawn(opts, createRng(1));
expect(s.x).toBe(0);
expect(s.y).toBe(0);
});
});
describe('sampleBurst', () => {
it('нужное количество, скорость в диапазоне', () => {
const rng = createRng(5);
const parts = sampleBurst(12, [30, 80], [0.2, 0.5], rng);
expect(parts).toHaveLength(12);
for (const p of parts) {
const v = Math.hypot(p.vx, p.vy);
expect(v).toBeGreaterThanOrEqual(30 - 1e-9);
expect(v).toBeLessThanOrEqual(80 + 1e-9);
expect(p.lifetime).toBeGreaterThanOrEqual(0.2);
expect(p.lifetime).toBeLessThanOrEqual(0.5);
}
});
it('детерминирован по seed', () => {
const a = sampleBurst(5, [10, 20], [0.1, 0.2], createRng(99));
const b = sampleBurst(5, [10, 20], [0.1, 0.2], createRng(99));
expect(a).toEqual(b);
});
});