/**
 * Чистая математика перспективной камеры (тесты без WebGL).
 * Конвенция yaw — угол «target → камера» в XZ (как у орбиты ModelViewer
 * и screenMoveToWorld): камера стоит в yaw-направлении от цели и смотрит
 * в противоположную горизонталь. pitch > 0 — камера смотрит вниз,
 * pitch < 0 — вверх. Знак Y согласован с граблёй «камера НАД сценой»:
 * отрицательный forward.y при pitch > 0 — взгляд к земле.
 */

/** Вертикальный FOV перспективной камеры (рад, ~50°). */
export const PERSP_FOV = 50 * (Math.PI / 180);

/** Режим камеры: изометрия (ортокамера), третье и первое лицо (перспектива). */
export type CameraMode = 'iso' | 'third' | 'first';

/** Клампы pitch орбиты (рад): third person — камера не ныряет под землю
 *  и не съезжает на горизонт; first person — свободнее, можно смотреть вверх. */
export const PERSP_PITCH_THIRD: readonly [number, number] = [0.08, 1.35];
export const PERSP_PITCH_FIRST: readonly [number, number] = [-1.2, 1.2];

/** Клампы дистанции орбиты в долях viewSize: ближе — упираемся в героя,
 *  дальше — теряем кадр. */
export const PERSP_DIST_K: readonly [number, number] = [0.25, 3];

/** Cap дальней границы окна частиц в долях глубины цели: без него окно
 *  первого лица при взгляде вдоль горизонта уходит в бесконечность —
 *  плотность размазывается, wrap гоняет частицы по километрам. */
export const PERSP_WINDOW_FAR_K = 3;

/** Направление взгляда (unit): горизонталь противоположна орбит-направлению
 *  «target → камера». Чистая функция. */
export function camForward(yaw: number, pitch: number): [number, number, number] {
    const cp = Math.cos(pitch);
    return [-Math.cos(yaw) * cp, -Math.sin(pitch), -Math.sin(yaw) * cp];
}

/** Позиция камеры на орбите вокруг target: yaw — угол «target → камера»
 *  в XZ, pitch > 0 — камера выше цели. Чистая функция (вьювер модели
 *  переэкспортирует её — прежний дом). */
export function orbitPosition(
    yaw: number, pitch: number, dist: number, target: readonly [number, number, number],
): [number, number, number] {
    const cp = Math.cos(pitch);
    return [
        target[0] + dist * cp * Math.cos(yaw),
        target[1] + dist * Math.sin(pitch),
        target[2] + dist * cp * Math.sin(yaw),
    ];
}