Quat
Кватернион (x, y, z, w) — тип поворота за 3D-API узлов (node.quaternion). Тот же контракт,
что у Vec3: изменяемые поля, чистые методы (каждый возвращает новый
Quat; мутируют только set / copy), а сырой массив [x, y, z, w] подойдёт везде, где принимается
Quat (QuatLike). Правая система координат — как в gl-matrix / three.js.
Для повседневных поворотов ты выставляешь node.eulerAngles (градусы) и Quat не трогаешь вовсе;
тянешься к нему, когда надо комбинировать повороты, делать slerp или прицеливаться (lookRotation,
fromTo).
Обзор
const target = Quat.lookRotation(enemy.position.sub(turret.position))
setLoop(dt => {
turret.quaternion = new Quat(turret.quaternion).slerp(target, 1 - Math.exp(-8 * dt))
})
const tilt = Quat.fromEuler(0, 0, 15) // ГРАДУСЫ
const spin = Quat.fromAxisAngle([0, 1, 0], Math.PI / 2) // РАДИАНЫ
node.quaternion = spin.mul(tilt) // сначала tilt, затем spinСоздание
new Quat() // единичный (0, 0, 0, 1)
new Quat(x, y, z, w)
new Quat([x, y, z, w]) // копия любого QuatLike
Quat.from(q: QuatLike): Quat
Quat.identity // геттер — свежий единичный при каждом обращении
Quat.fromEuler(x, y, z, order? /* "YXZ" */): Quat // углы в ГРАДУСАХ
Quat.fromAxisAngle(axis, rad): Quat // угол в РАДИАНАХ; ось нормализуется за тебя
Quat.fromTo(a, b): Quat // кратчайший поворот, переводящий направление a в b
Quat.lookRotation(forward, up? /* Vec3.up */): Quat // ориентация так, чтобы локальная −Z смотрела вдоль `forward`деление на градусы/радианы следует общему правилу SDK: эйлеровы фабрики принимают градусы,
аргументы-«сырые углы» — радианы. EulerOrder — одно из "XYZ" | "YXZ" | "ZXY" | "ZYX" | "YZX" | "XZY"; по умолчанию "YXZ" — это yaw-pitch-roll (рыскание-тангаж-крен) и совпадает с
node.eulerAngles.
Композиция и применение
a.mul(b): Quat // произведение Гамильтона a ⊗ b — применяет b ПЕРВЫМ, затем a
a.invert(): Quat // обратный поворот (= conjugate() для единичных кватернионов)
a.conjugate(): Quat
a.normalize(): Quat // ре-нормализация после накопления ошибки (ноль → единичный)
a.rotateVec3(v): Vec3 // повернуть вектор; то же, что new Vec3(v).rotate(a)Порядок умножения читается справа налево, как у матриц: yaw.mul(pitch) сначала наклоняет (pitch)
в локальном пространстве, затем рыскает (yaw).
Интерполяция и сравнение
a.slerp(b, t): Quat // сферическая интерполяция, t ∈ [0, 1]; идёт коротким путём
a.angle(b): number // угловое расстояние между двумя единичными кватернионами, РАДИАНЫ
a.dot(b): number
a.equals(b, eps? /* 1e-6 */): boolean // покомпонентно — считает q и −q РАЗНЫМИ
a.sameRotation(b, eps? /* 1e-6 */): boolean // true, если это один и тот же поворот (±q равны)q и −q кодируют один и тот же поворот. Для вопроса «смотрит ли туда же» используй
sameRotation, а не equals.
Извлечение углов Эйлера
q.toEuler(order? /* "YXZ" */): Vec3 // углы в ГРАДУСАХ; обратная к Quat.fromEulerСовместимость
q.set(x, y, z, w): this // единственные мутаторы
q.copy(other): this
q.clone(): Quat
q.toArray(): [number, number, number, number]
const [x, y, z, w] = q // итерируемыйПодводные камни
// ✗ градусы в fromAxisAngle (он принимает радианы)
Quat.fromAxisAngle([0, 1, 0], 90)
Quat.fromAxisAngle([0, 1, 0], 90 * DEG2RAD) // ✓ — или используй Quat.fromEuler(0, 90, 0)
// ✗ ждать, что a.mul(b) применит сначала a — он применяет сначала b, затем a
world = local.mul(delta) // поворот на delta в локальном пространстве
world = delta.mul(local) // ✓ если нужно применить delta в мировом пространствеСмотрите также
- Vec2 и Vec3 —
Vec3.rotate(q), помощники направлений (Vec3.forward= −Z). - Mat4 —
Mat4.fromQuat,mat.rotationизвлекаетQuat. - Соглашения — градусы против радианов, −Z вперёд.