use crate::matrix_op as matrix;
use crate::vector_op as vector;
use crate::{Float, Num, f_const};
#[must_use]
#[inline]
pub fn new<V: Num>() -> [V; 4] {
[V::ZERO, V::ZERO, V::ZERO, V::ONE]
}
#[must_use]
#[inline]
pub fn as_rijk<V: Num>(v: &[V; 4]) -> (V, V, V, V) {
(v[3], v[0], v[1], v[2])
}
#[must_use]
#[inline]
pub fn of_rijk<V: Num>(r: V, i: V, j: V, k: V) -> [V; 4] {
[i, j, k, r]
}
#[must_use]
#[inline]
pub fn identity<V: Num>() -> [V; 4] {
[V::ZERO, V::ZERO, V::ZERO, V::ONE]
}
#[must_use]
#[inline]
pub fn of_axis_angle<V: Float>(axis: &[V; 3], angle: V) -> [V; 4] {
let (s, c) = V::sin_cos(angle * f_const!(V, 1, 2));
let l = vector::length(axis);
if l < V::epsilon() {
identity()
} else {
let s = s / l;
let i = s * axis[0];
let j = s * axis[1];
let k = s * axis[2];
let r = c;
[i, j, k, r]
}
}
#[must_use]
#[inline]
pub fn as_axis_angle<V: Float>(q: &[V; 4]) -> ([V; 3], V) {
let (r, i, j, k) = as_rijk(q);
let i2 = i * i;
let j2 = j * j;
let k2 = k * k;
let l = (i2 + j2 + k2).sqrt();
if l < V::epsilon() {
([i, j, k], V::ZERO)
} else {
let rl = V::ONE / l;
([i * rl, j * rl, k * rl], V::atan2(l, r))
}
}
pub fn to_rotation3<V: Float>(q: &[V; 4], m: &mut [V; 9]) {
let i2 = q[0] * q[0];
let j2 = q[1] * q[1];
let k2 = q[2] * q[2];
let r2 = q[3] * q[3];
let l2 = r2 + i2 + j2 + k2;
let rl2 = V::ONE / l2;
m[0] = (r2 + i2 - j2 - k2) * rl2;
m[4] = (r2 - i2 + j2 - k2) * rl2;
m[8] = (r2 - i2 - j2 + k2) * rl2;
let drl2 = rl2 + rl2;
m[1] = (q[0] * q[1] - q[2] * q[3]) * drl2;
m[3] = (q[0] * q[1] + q[2] * q[3]) * drl2;
m[2] = (q[2] * q[0] + q[1] * q[3]) * drl2;
m[6] = (q[2] * q[0] - q[1] * q[3]) * drl2;
m[5] = (q[1] * q[2] - q[0] * q[3]) * drl2;
m[7] = (q[1] * q[2] + q[0] * q[3]) * drl2;
}
pub fn to_rotation4<V: Float>(q: &[V; 4], m: &mut [V; 16]) {
let i2 = q[0] * q[0];
let j2 = q[1] * q[1];
let k2 = q[2] * q[2];
let r2 = q[3] * q[3];
let l2 = r2 + i2 + j2 + k2;
let rl2 = V::ONE / l2;
m[0] = (r2 + i2 - j2 - k2) * rl2;
m[5] = (r2 - i2 + j2 - k2) * rl2;
m[10] = (r2 - i2 - j2 + k2) * rl2;
let drl2 = rl2 + rl2;
m[1] = (q[0] * q[1] - q[2] * q[3]) * drl2;
m[4] = (q[0] * q[1] + q[2] * q[3]) * drl2;
m[2] = (q[2] * q[0] + q[1] * q[3]) * drl2;
m[8] = (q[2] * q[0] - q[1] * q[3]) * drl2;
m[6] = (q[1] * q[2] - q[0] * q[3]) * drl2;
m[9] = (q[1] * q[2] + q[0] * q[3]) * drl2;
m[3] = V::ZERO;
m[7] = V::ZERO;
m[11] = V::ZERO;
m[12] = V::ZERO;
m[13] = V::ZERO;
m[14] = V::ZERO;
m[15] = V::ONE;
}
#[must_use]
pub fn of_rotation<V: Float>(m: &[V; 9]) -> [V; 4] {
fn safe_sqrt<V: Float>(x: V) -> V {
if x < V::ZERO { V::ZERO } else { x.sqrt() }
}
let half = f_const!(V, 1, 2);
let r = safe_sqrt(V::ONE + m[0] + m[4] + m[8]) * half;
let mut i = safe_sqrt(V::ONE + m[0] - m[4] - m[8]) * half;
let mut j = safe_sqrt(V::ONE - m[0] + m[4] - m[8]) * half;
let mut k = safe_sqrt(V::ONE - m[0] - m[4] + m[8]) * half;
let r_i_4 = m[7] - m[5];
let r_j_4 = m[2] - m[6];
let r_k_4 = m[3] - m[1];
if r_i_4 < -V::epsilon() {
i = -i;
}
if r_j_4 < -V::epsilon() {
j = -j;
}
if r_k_4 < -V::epsilon() {
k = -k;
}
[i, j, k, r]
}
#[must_use]
pub fn look_at<V: Float>(dirn: &[V; 3], up: &[V; 3]) -> [V; 4] {
let m = matrix::look_at3(dirn, up);
of_rotation(&m)
}
#[must_use]
#[inline]
pub fn invert<V: Float>(a: &[V; 4]) -> [V; 4] {
let l = vector::length_sq(a);
let r_l = {
if l < V::epsilon() {
V::ZERO
} else {
V::ONE / l
}
};
[-a[0] * r_l, -a[1] * r_l, -a[2] * r_l, a[3] * r_l]
}
#[must_use]
#[inline]
pub fn conjugate<V: Num>(a: &[V; 4]) -> [V; 4] {
[-a[0], -a[1], -a[2], a[3]]
}
#[must_use]
#[inline]
pub fn normalize<V: Float>(a: [V; 4]) -> [V; 4] {
vector::normalize(a)
}
#[must_use]
#[inline]
pub fn rotate_x<V: Float>(a: &[V; 4], angle: V) -> [V; 4] {
let (s, c) = V::sin_cos(angle * f_const!(V, 1, 2));
let i = a[0] * c + a[3] * s;
let j = a[1] * c + a[2] * s;
let k = a[2] * c - a[1] * s;
let r = a[3] * c - a[0] * s;
[i, j, k, r]
}
#[must_use]
#[inline]
pub fn rotate_y<V: Float>(a: &[V; 4], angle: V) -> [V; 4] {
let (s, c) = V::sin_cos(angle * f_const!(V, 1, 2));
let i = a[0] * c - a[2] * s;
let j = a[1] * c + a[3] * s;
let k = a[2] * c + a[0] * s;
let r = a[3] * c - a[1] * s;
[i, j, k, r]
}
#[must_use]
#[inline]
pub fn rotate_z<V: Float>(a: &[V; 4], angle: V) -> [V; 4] {
let (s, c) = V::sin_cos(angle * f_const!(V, 1, 2));
let i = a[0] * c + a[1] * s;
let j = a[1] * c - a[0] * s;
let k = a[2] * c + a[3] * s;
let r = a[3] * c - a[2] * s;
[i, j, k, r]
}
#[must_use]
#[inline]
pub fn multiply<V: Num>(a: &[V; 4], b: &[V; 4]) -> [V; 4] {
let i = a[0] * b[3] + a[3] * b[0] + a[1] * b[2] - a[2] * b[1];
let j = a[1] * b[3] + a[3] * b[1] + a[2] * b[0] - a[0] * b[2];
let k = a[2] * b[3] + a[3] * b[2] + a[0] * b[1] - a[1] * b[0];
let r = a[3] * b[3] - a[0] * b[0] - a[1] * b[1] - a[2] * b[2];
[i, j, k, r]
}
#[must_use]
#[inline]
pub fn divide<V: Float>(a: &[V; 4], b: &[V; 4]) -> [V; 4] {
let l2 = vector::length_sq(b);
if l2 < V::epsilon() {
[V::ZERO; 4]
} else {
let i = a[0] * b[3] - a[3] * b[0] - a[1] * b[2] + a[2] * b[1];
let j = a[1] * b[3] - a[3] * b[1] - a[2] * b[0] + a[0] * b[2];
let k = a[2] * b[3] - a[3] * b[2] - a[0] * b[1] + a[1] * b[0];
let r = a[3] * b[3] + a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
[i / l2, j / l2, k / l2, r / l2]
}
}
#[must_use]
#[inline]
pub fn nlerp<V: Float>(t: V, in0: &[V; 4], in1: &[V; 4]) -> [V; 4] {
normalize(vector::mix(in0, in1, t))
}
#[inline]
pub fn distance_sq<V: Float>(a: &[V; 4], b: &[V; 4]) -> V {
V::ONE - (a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3]).abs()
}
#[inline]
pub fn distance<V: Float>(a: &[V; 4], b: &[V; 4]) -> V {
distance_sq(a, b).sqrt()
}
#[must_use]
pub fn to_euler<V: Float>(q: &[V; 4]) -> (V, V, V) {
let zero = V::ZERO;
let one = V::ONE;
let two = one + one;
let almost_half = f_const!(V, 499_999, 1_000_000);
let halfpi = V::from_f32(1.570796326_f32).unwrap();
let i = q[0];
let j = q[1];
let k = q[2];
let r = q[3];
let test = i * j + r * k;
let (heading, attitude, bank) = {
if test > almost_half {
(two * V::atan2(i, r), halfpi, zero)
} else if test < -almost_half {
(-two * V::atan2(i, r), -halfpi, zero)
} else {
let i2 = i * i;
let j2 = j * j;
let k2 = k * k;
(
V::atan2(two * j * r - two * i * k, V::ONE - two * j2 - two * k2),
V::asin(two * test),
V::atan2(two * i * r - two * j * k, V::ONE - two * i2 - two * k2),
)
}
};
(bank, heading, attitude)
}
#[must_use]
pub fn apply3<V: Float>(q: &[V; 4], v: &[V; 3]) -> [V; 3] {
let one = V::ONE;
let two = one + one;
let (r, i, j, k) = as_rijk(q);
let x = (r * r + i * i - j * j - k * k) * v[0]
+ two * (i * k + r * j) * v[2]
+ two * (i * j - r * k) * v[1];
let y = (r * r - i * i + j * j - k * k) * v[1]
+ two * (j * i + r * k) * v[0]
+ two * (j * k - r * i) * v[2];
let z = (r * r - i * i - j * j + k * k) * v[2]
+ two * (k * j + r * i) * v[1]
+ two * (k * i - r * j) * v[0];
[x, y, z]
}
#[must_use]
pub fn apply4<V: Float>(q: &[V; 4], v: &[V; 4]) -> [V; 4] {
let one = V::ONE;
let two = one + one;
let (r, i, j, k) = as_rijk(q);
let x = (r * r + i * i - j * j - k * k) * v[0]
+ two * (i * k + r * j) * v[2]
+ two * (i * j - r * k) * v[1];
let y = (r * r - i * i + j * j - k * k) * v[1]
+ two * (j * i + r * k) * v[0]
+ two * (j * k - r * i) * v[2];
let z = (r * r - i * i - j * j + k * k) * v[2]
+ two * (k * j + r * i) * v[1]
+ two * (k * i - r * j) * v[0];
[x, y, z, v[3]]
}
#[must_use]
pub fn weighted_average_pair<V: Float>(qa: &[V; 4], w_a: V, qb: &[V; 4], w_b: V) -> [V; 4] {
let four = V::from_u8(4).unwrap();
let (ra, ia, ja, ka) = as_rijk(qa);
let (rb, ib, jb, kb) = as_rijk(qb);
let w_diff = w_a - w_b;
let q1_q2 = ra * rb + ia * ib + ja * jb + ka * kb;
let z_sq = w_diff * w_diff + four * w_a * w_b * q1_q2 * q1_q2;
let z = z_sq.sqrt();
let rw_a_sq = w_a * (z + w_diff) / z / (z + w_a + w_b);
let rw_b_sq = w_b * (z - w_diff) / z / (z + w_a + w_b);
let rw_a = rw_a_sq.sqrt();
let rw_b = rw_b_sq.sqrt() * q1_q2.signum();
of_rijk(
rw_a * ra + rw_b * rb,
rw_a * ia + rw_b * ib,
rw_a * ja + rw_b * jb,
rw_a * ka + rw_b * kb,
)
}
#[must_use]
pub fn weighted_average_many<I: Iterator<Item = (V, [V; 4])>, V: Float>(
mut values_iter: I,
) -> [V; 4] {
let (mut weight_ih, mut value_ih) = values_iter
.next()
.expect("weighted_average_many MUST be invoked with at least one quaternion");
let mut next_values = Vec::new();
loop {
if let Some((second_weight, second_value)) = values_iter.next() {
let w12 = weight_ih + second_weight;
let av = weighted_average_pair(
&value_ih,
weight_ih / w12,
&second_value,
second_weight / w12,
);
next_values.push((w12, av));
} else {
if next_values.is_empty() {
return value_ih;
} else {
next_values.push((weight_ih, value_ih));
break;
}
}
if let Some((w, v)) = values_iter.next() {
weight_ih = w;
value_ih = v;
} else {
break;
}
}
if next_values.len() == 1 {
next_values[0].1
} else {
let values_iter = next_values.into_iter();
weighted_average_many(values_iter)
}
}
#[must_use]
pub fn rotation_of_vec_to_vec<V: Float>(a: &[V; 3], b: &[V; 3]) -> [V; 4] {
let obtuse = vector::dot(a, b) < V::ZERO;
let cp = vector::cross_product3(a, b);
let sa = vector::length(&cp);
let angle = sa.asin();
let angle = if obtuse {
V::from_f32(3.141592653_f32).unwrap() - angle
} else {
angle
};
of_axis_angle(&cp, angle)
}