Newer
Older
rpg / packages / engine / src / render / __tests__ / lightSim.test.ts
import { describe, expect, it } from 'vitest';
import {
    GLOW_BASE_PX,
    dimColor,
    flickerFactor,
    lightFrame,
    lerpAmbient
} from '../lightSim';

describe('flickerFactor', () => {
    it('детерминирован: одинаковые (time, seed) — одинаковый результат', () => {
        const a = flickerFactor(1.234, 0.37, 0.3, 0.9);
        const b = flickerFactor(1.234, 0.37, 0.3, 0.9);
        expect(a).toBe(b);
    });

    it('разные seed дают разные значения', () => {
        const a = flickerFactor(0.5, 0.1, 0.3, 0.9);
        const b = flickerFactor(0.5, 0.9, 0.3, 0.9);
        expect(a).not.toBe(b);
    });

    it('в диапазоне [1-amount, 1] на сетке времени', () => {
        for (let i = 0; i < 180; i++) {
            const f = flickerFactor(i / 60, 0.25, 0.35, 0.9);
            expect(f).toBeGreaterThanOrEqual(1 - 0.35 - 1e-9);
            expect(f).toBeLessThanOrEqual(1 + 1e-9);
        }
    });

    it('amount = 0 — ровный свет', () => {
        for (let i = 0; i < 60; i++) {
            expect(flickerFactor(i / 60, 0.7, 0, 0.9)).toBe(1);
        }
    });
});

describe('lightFrame', () => {
    it('дефолты: intensity 1, radius 2*GLOW_BASE_PX', () => {
        const f = lightFrame({ id: 'l', x: 10, y: 20, color: 0xf2b45a }, 0);
        expect(f.tint).toBe(0xf2b45a);
        expect(f.scale).toBe(2);
        expect(f.alpha).toBeGreaterThan(0);
        expect(f.alpha).toBeLessThanOrEqual(1);
        expect(f.x).toBe(10);
        expect(f.y).toBe(20);
    });

    it('alpha = intensity * flickerFactor, scale = radius / GLOW_BASE_PX', () => {
        const def = { id: 'l', x: 0, y: 0, color: 0xffaa00, intensity: 1.5, radius: 64, flicker: 0, seed: 0 };
        const f = lightFrame(def, 1);
        expect(f.alpha).toBeCloseTo(1.5, 10);
        expect(f.scale).toBe(64 / GLOW_BASE_PX);
        // alpha клампится в адаптере, но чистый кадр может превышать 1
        expect(f.alpha).toBe(1.5);
    });

    it('выключенный источник — alpha 0', () => {
        const f = lightFrame({ id: 'l', x: 0, y: 0, color: 0xffffff, enabled: false }, 0);
        expect(f.alpha).toBe(0);
    });

    it('мерцание меняет alpha между шагами', () => {
        const def = { id: 'l', x: 0, y: 0, color: 0xffffff, intensity: 1, flicker: 0.5 };
        const a1 = lightFrame(def, 0).alpha;
        const a2 = lightFrame(def, 0.37).alpha;
        expect(a1).not.toBe(a2);
    });
});

describe('lerpAmbient', () => {
    it('краи и середина по каналам', () => {
        expect(lerpAmbient(0x000000, 0xffffff, 0)).toBe(0x000000);
        expect(lerpAmbient(0x000000, 0xffffff, 1)).toBe(0xffffff);
        expect(lerpAmbient(0xff0000, 0x00ff00, 0.5)).toBe(0x808000); // round(127.5) = 128
    });
});

describe('dimColor', () => {
    it('краи: полная яркость и ноль', () => {
        expect(dimColor(0x54586a, 1)).toBe(0x54586a);
        expect(dimColor(0x54586a, 0)).toBe(0x000000);
    });

    it('монотонность по яркости', () => {
        let prev = 0;
        for (let k = 0; k <= 1.001; k += 0.1) {
            const c = dimColor(0x808080, k);
            expect(c).toBeGreaterThanOrEqual(prev);
            prev = c;
        }
    });
});