import { describe, expect, it } from 'vitest';
import { blobPlacement, sunShadow } from '../shadowSim';
describe('sunShadow', () => {
it('ночью тени нет', () => {
expect(sunShadow(2)).toBeNull();
expect(sunShadow(23)).toBeNull();
expect(sunShadow(5.5)).toBeNull();
});
it('на границах дня альфа нулевая, в разгар дня максимальная', () => {
expect(sunShadow(6)!.alpha).toBeCloseTo(0);
expect(sunShadow(19.9)!.alpha).toBeCloseTo(0); // к закату гаснет (в 20 — null)
const noon = sunShadow(13)!.alpha;
expect(noon).toBeGreaterThan(0.3);
const morning = sunShadow(7)!.alpha; // ещё в разгоне (dawn..day = 6..8)
expect(morning).toBeGreaterThan(0);
expect(morning).toBeLessThan(noon);
});
it('тень утром на запад экрана, вечером на восток', () => {
expect(sunShadow(7)!.dirX).toBeLessThan(-0.5);
expect(sunShadow(19)!.dirX).toBeGreaterThan(0.5);
expect(sunShadow(13)!.dirX).toBeCloseTo(0); // полдень — почти под объектом
});
it('длина максимальна утром/вечером, минимальна в полдень', () => {
const noon = sunShadow(13)!.len;
const morning = sunShadow(7)!.len;
const evening = sunShadow(19)!.len;
expect(noon).toBeLessThan(morning);
expect(noon).toBeLessThan(evening);
expect(morning).toBeCloseTo(evening, 1); // симметричный день
});
it('вектор направления единичный', () => {
for (let h = 6; h < 20; h += 0.5) {
const s = sunShadow(h)!;
expect(Math.hypot(s.dirX, s.dirY)).toBeCloseTo(1);
}
});
});
describe('blobPlacement', () => {
it('масса эллипса сидит у базы: ближний край заходит за базу, вылет — вперёд', () => {
const sun = { dirX: 1, dirY: 0, len: 1, alpha: 0.3 };
const b = blobPlacement(10, 40, sun);
const center = 10 * 0.3 + 40 * 0.2; // 11 — центр массы от базы
expect(b.x).toBeCloseTo(center);
expect(b.y).toBe(0);
expect(b.scaleX).toBeCloseTo(center + 5.5 + 8.8); // полуось вдоль тени
expect(b.scaleY).toBeCloseTo(10); // 0.75 + 0.25·min(1,len) = 1 при len=1
expect(b.alpha).toBeCloseTo(0.3);
expect(b.rotation).toBeCloseTo(0);
// Ближний край эллипса за базой (тень «приклеена» к объекту)…
expect(b.x - b.scaleX).toBeLessThan(0);
// …а дальний край вылетает вперёд по тени.
expect(b.x + b.scaleX).toBeGreaterThan(0);
});
it('длинная утренняя тень дальше, чем короткая полуденная', () => {
const morning = blobPlacement(10, 40, { dirX: -1, dirY: 0, len: 1.5, alpha: 0.3 });
const noon = blobPlacement(10, 40, { dirX: 0, dirY: 1, len: 0.6, alpha: 0.3 });
expect(Math.hypot(morning.x, morning.y)).toBeGreaterThan(Math.hypot(noon.x, noon.y));
});
it('поворот соответствует направлению тени', () => {
const b = blobPlacement(10, 40, { dirX: 0, dirY: 1, len: 1, alpha: 0.3 });
expect(b.rotation).toBeCloseTo(Math.PI / 2);
});
it('центр массы близко к базе даже при длинной тени (объект не «висит»)', () => {
const long = blobPlacement(9, 12, { dirX: -0.9, dirY: 0.45, len: 1.6, alpha: 0.3 });
expect(Math.hypot(long.x, long.y)).toBeLessThan(9); // герой: центр ~5-6 px от ног
});
});