Newer
Older
rpg / v2 / packages / engine / src / models / __tests__ / paletteMap.test.ts
import { describe, expect, it } from 'vitest';
import { remapSlots } from '../paletteMap';
import { decodeModel } from '../format';
import type { VoxelPalette } from '../../voxel/grid';
import type { VoxelModel } from '../format';

const SCENE: VoxelPalette = {
    colors: {
        1: '#4a5340', 6: '#5b5b66', 7: '#94949e',
    },
};

/** Модель 2×1×1: воксель слота 9 с «каменным» hex и слот 2 с «луговым». */
function makeModel(hexA: string, hexB: string): VoxelModel {
    return {
        size: [2, 1, 1],
        palette: { 9: hexA, 2: hexB },
        data: btoa(String.fromCharCode(9, 2)),
    };
}

describe('remapSlots (палитра модели → палитра сцены)', () => {
    it('точное совпадение hex перекладывает слоты 1:1', () => {
        const m = remapSlots(makeModel('#5b5b66', '#4a5340'), SCENE);
        const g = decodeModel(m);
        expect(g.get(0, 0, 0)).toBe(6);
        expect(g.get(1, 0, 0)).toBe(1);
        expect(m.palette).toEqual({ 6: '#5b5b66', 1: '#4a5340' });
    });

    it('нет совпадения — ближайший по RGB', () => {
        // #5d5d68 чуть светлее камня #5b5b66 → слот 6; #ffffff ближе всего к свету #94949e
        const m = remapSlots(makeModel('#5d5d68', '#ffffff'), SCENE);
        const g = decodeModel(m);
        expect(g.get(0, 0, 0)).toBe(6);
        expect(g.get(1, 0, 0)).toBe(7);
    });

    it('детерминирован и не трогает пустоту', () => {
        const a = remapSlots(makeModel('#5d5d68', '#ffffff'), SCENE);
        const b = remapSlots(makeModel('#5d5d68', '#ffffff'), SCENE);
        expect(JSON.stringify(a)).toBe(JSON.stringify(b));
        const g = decodeModel(a);
        expect(g.get(0, 0, 1)).toBe(0); // вне размера не появилось
        expect(g.count()).toBe(2);
    });
});