import { describe, expect, it } from 'vitest';
import { mannequin } from '../mannequin';
import { validateRig, poseVoxels } from '../../animation/rig';
import { decodeModel } from '../format';
describe('mannequin (манекен + рига)', () => {
it('детерминирован', () => {
expect(JSON.stringify(mannequin())).toBe(JSON.stringify(mannequin()));
});
it('ригa проходит числовую валидацию', () => {
const m = mannequin();
expect(validateRig(m.rig, m.model)).toEqual([]);
});
it('состав частей: 5 ног ×2, 4 руки ×2, торс 12, голова 2', () => {
const { model, rig } = mannequin();
const g = decodeModel(model);
expect(g.count()).toBe(32); // 10 ног + 12 торс + 2 голова + 8 рук
const byBone = [0, 0, 0, 0, 0];
for (let i = 0; i < g.data.length; i++)
if (g.data[i] !== 0) byBone[rig.binding[i]]++;
expect(byBone).toEqual([14, 5, 5, 4, 4]); // root: торс+голова
});
it('каждая часть — связная колонна (6-связность)', () => {
const { model, rig } = mannequin();
const g = decodeModel(model);
const cells = (bone: number) => {
const out: [number, number, number][] = [];
for (let i = 0; i < g.data.length; i++)
if (g.data[i] !== 0 && rig.binding[i] === bone) {
const x = i % g.sx, y = Math.floor(i / g.sx) % g.sy, z = Math.floor(i / (g.sx * g.sy));
out.push([x, y, z]);
}
return out.sort((a, b) => a[1] - b[1]);
};
for (const bone of [1, 2, 3, 4]) {
const col = cells(bone);
expect(col.length).toBeGreaterThan(3);
for (let k = 1; k < col.length; k++) {
expect(Math.abs(col[k][0] - col[k - 1][0]) + Math.abs(col[k][1] - col[k - 1][1]) + Math.abs(col[k][2] - col[k - 1][2])).toBe(1);
}
}
});
it('поворот ноги двигает ногу, но не торс (жёсткая привязка частей)', () => {
const { model, rig } = mannequin();
const bindPosed = poseVoxels(rig, model, {});
const swing = poseVoxels(rig, model, { legL: { rot: [0.6, 0, 0] } });
for (const a of bindPosed) {
const b = swing.find((v) => v.index === a.index)!;
const moved = Math.abs(a.pos[0] - b.pos[0]) + Math.abs(a.pos[1] - b.pos[1]) + Math.abs(a.pos[2] - b.pos[2]);
const isLegL = rig.binding[a.index] === 1;
if (isLegL) expect(moved).toBeGreaterThan(0.1);
else expect(moved).toBe(0);
}
});
});