LeCodesdocs

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).

Обзор

TypeScript
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

Создание

TypeScript
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`
Note

деление на градусы/радианы следует общему правилу SDK: эйлеровы фабрики принимают градусы, аргументы-«сырые углы» — радианы. EulerOrder — одно из "XYZ" | "YXZ" | "ZXY" | "ZYX" | "YZX" | "XZY"; по умолчанию "YXZ" — это yaw-pitch-roll (рыскание-тангаж-крен) и совпадает с node.eulerAngles.

Композиция и применение

TypeScript
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).

Интерполяция и сравнение

TypeScript
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 равны)
Note

q и −q кодируют один и тот же поворот. Для вопроса «смотрит ли туда же» используй sameRotation, а не equals.

Извлечение углов Эйлера

TypeScript
q.toEuler(order? /* "YXZ" */): Vec3   // углы в ГРАДУСАХ; обратная к Quat.fromEuler

Совместимость

TypeScript
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       // итерируемый

Подводные камни

TypeScript
// ✗ градусы в 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 и Vec3Vec3.rotate(q), помощники направлений (Vec3.forward = −Z).
  • Mat4Mat4.fromQuat, mat.rotation извлекает Quat.
  • Соглашения — градусы против радианов, −Z вперёд.