158 lines
4.0 KiB
TypeScript
158 lines
4.0 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import { crop, flip, resize, rotate90 } from './geometry';
|
|
import { makeImage } from './test-helpers';
|
|
|
|
const square = () =>
|
|
makeImage(2, 2, [
|
|
[1, 1, 1, 1],
|
|
[2, 2, 2, 2],
|
|
[3, 3, 3, 3],
|
|
[4, 4, 4, 4]
|
|
]);
|
|
|
|
describe('flip', () => {
|
|
it('отражает по горизонтали (зеркало слева-направо)', () => {
|
|
const out = flip(square(), 'horizontal');
|
|
expect(out.width).toBe(2);
|
|
expect(out.height).toBe(2);
|
|
expect([...out.data]).toEqual([
|
|
2, 2, 2, 2,
|
|
1, 1, 1, 1,
|
|
4, 4, 4, 4,
|
|
3, 3, 3, 3
|
|
]);
|
|
});
|
|
|
|
it('отражает по вертикали (сверху-вниз)', () => {
|
|
const out = flip(square(), 'vertical');
|
|
expect([...out.data]).toEqual([
|
|
3, 3, 3, 3,
|
|
4, 4, 4, 4,
|
|
1, 1, 1, 1,
|
|
2, 2, 2, 2
|
|
]);
|
|
});
|
|
|
|
it('не мутирует вход', () => {
|
|
const img = square();
|
|
flip(img, 'horizontal');
|
|
expect([...img.data]).toEqual([1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4]);
|
|
});
|
|
});
|
|
|
|
describe('rotate90', () => {
|
|
it('поворачивает неквадрат 2x3 на 90° по часовой', () => {
|
|
const rect = makeImage(2, 3, [
|
|
[1, 1, 1, 1],
|
|
[2, 2, 2, 2],
|
|
[3, 3, 3, 3],
|
|
[4, 4, 4, 4],
|
|
[5, 5, 5, 5],
|
|
[6, 6, 6, 6]
|
|
]);
|
|
const out = rotate90(rect, 1);
|
|
expect(out.width).toBe(3);
|
|
expect(out.height).toBe(2);
|
|
expect([...out.data]).toEqual([
|
|
5, 5, 5, 5,
|
|
3, 3, 3, 3,
|
|
1, 1, 1, 1,
|
|
6, 6, 6, 6,
|
|
4, 4, 4, 4,
|
|
2, 2, 2, 2
|
|
]);
|
|
});
|
|
|
|
it('turns=0 возвращает копию без изменений', () => {
|
|
const img = square();
|
|
const out = rotate90(img, 0);
|
|
expect(out).not.toBe(img);
|
|
expect([...out.data]).toEqual([...img.data]);
|
|
});
|
|
|
|
it('нормализует turns: 5 ≡ 1, -3 ≡ 1, 4 ≡ 0', () => {
|
|
const once = [...rotate90(square(), 1).data];
|
|
expect([...rotate90(square(), 5).data]).toEqual(once);
|
|
expect([...rotate90(square(), -3).data]).toEqual(once);
|
|
expect([...rotate90(square(), 4).data]).toEqual([...square().data]);
|
|
});
|
|
|
|
it('turns=2 на квадрате равно двойному отражению', () => {
|
|
const out = rotate90(square(), 2);
|
|
expect([...out.data]).toEqual([
|
|
4, 4, 4, 4,
|
|
3, 3, 3, 3,
|
|
2, 2, 2, 2,
|
|
1, 1, 1, 1
|
|
]);
|
|
});
|
|
});
|
|
|
|
describe('crop', () => {
|
|
const grid = () =>
|
|
makeImage(3, 3, [
|
|
[1, 1, 1, 1],
|
|
[2, 2, 2, 2],
|
|
[3, 3, 3, 3],
|
|
[4, 4, 4, 4],
|
|
[5, 5, 5, 5],
|
|
[6, 6, 6, 6],
|
|
[7, 7, 7, 7],
|
|
[8, 8, 8, 8],
|
|
[9, 9, 9, 9]
|
|
]);
|
|
|
|
it('вырезает центральную область 2x2 из 3x3', () => {
|
|
const out = crop(grid(), 1, 1, 2, 2);
|
|
expect(out.width).toBe(2);
|
|
expect(out.height).toBe(2);
|
|
expect([...out.data]).toEqual([
|
|
5, 5, 5, 5,
|
|
6, 6, 6, 6,
|
|
8, 8, 8, 8,
|
|
9, 9, 9, 9
|
|
]);
|
|
});
|
|
|
|
it('усекает область, выходящую за границы', () => {
|
|
const out = crop(grid(), -1, -1, 2, 2);
|
|
expect(out.width).toBe(1);
|
|
expect(out.height).toBe(1);
|
|
expect([...out.data]).toEqual([1, 1, 1, 1]);
|
|
});
|
|
|
|
it('бросает RangeError для области вне изображения', () => {
|
|
expect(() => crop(grid(), 5, 5, 2, 2)).toThrow(RangeError);
|
|
});
|
|
});
|
|
|
|
describe('resize', () => {
|
|
const twoByTwo = () =>
|
|
makeImage(2, 2, [
|
|
[0, 0, 0, 255],
|
|
[100, 100, 100, 255],
|
|
[200, 200, 200, 255],
|
|
[255, 255, 255, 255]
|
|
]);
|
|
|
|
it('совпадает с входом при тех же размерах', () => {
|
|
const img = twoByTwo();
|
|
expect([...resize(img, 2, 2).data]).toEqual([...img.data]);
|
|
});
|
|
|
|
it('апскейл 2x2 -> 4x4 билинейно интерполирует', () => {
|
|
const out = resize(twoByTwo(), 4, 4);
|
|
expect(out.width).toBe(4);
|
|
expect(out.height).toBe(4);
|
|
const red = [...out.data].filter((_, i) => i % 4 === 0);
|
|
expect(red.slice(0, 4)).toEqual([0, 25, 75, 100]);
|
|
expect(red.slice(4, 8)).toEqual([50, 72, 117, 139]);
|
|
});
|
|
|
|
it('бросает RangeError на некорректные размеры', () => {
|
|
const img = twoByTwo();
|
|
expect(() => resize(img, 0, 10)).toThrow(RangeError);
|
|
expect(() => resize(img, 10.5, 10)).toThrow(RangeError);
|
|
});
|
|
});
|