export type Vec3 = { x: number; y: number; z: number } /** Plain 3D vector math. Every operation returns a fresh object (no in-place * mutation) to keep call sites easy to reason about. */ export namespace Vec3 { export function add(a: Vec3, b: Vec3): Vec3 { return { x: a.x + b.x, y: a.y + b.y, z: a.z + b.z } } export function sub(a: Vec3, b: Vec3): Vec3 { return { x: a.x - b.x, y: a.y - b.y, z: a.z - b.z } } export function scale(v: Vec3, s: number): Vec3 { return { x: v.x * s, y: v.y * s, z: v.z * s } } export function dot(a: Vec3, b: Vec3): number { return a.x * b.x + a.y * b.y + a.z * b.z } export function cross(a: Vec3, b: Vec3): Vec3 { return { x: a.y * b.z - a.z * b.y, y: a.z * b.x - a.x * b.z, z: a.x * b.y - a.y * b.x, } } export function length(v: Vec3): number { return Math.sqrt(dot(v, v)) } export function normalize(v: Vec3): Vec3 { const len = length(v) return len === 0 ? v : scale(v, 1 / len) } }