use super::{matrixr_op, vector_op};
use crate::{Float, Num};
pub fn identity<V: Num, const D2: usize, const D: usize>() -> [V; D2] {
let mut r = [V::zero(); D2];
for i in 0..D {
r[i * (D + 1)] = V::one();
}
r
}
pub fn identity2<V: Num>() -> [V; 4] {
identity::<V, 4, 2>()
}
pub fn identity3<V: Num>() -> [V; 9] {
identity::<V, 9, 3>()
}
pub fn identity4<V: Num>() -> [V; 16] {
identity::<V, 16, 4>()
}
pub fn determinant2<V: Num>(m: &[V; 4]) -> V {
m[0] * m[3] - m[1] * m[2]
}
pub fn inverse2<V: Num>(m: &[V; 4]) -> [V; 4] {
let d = determinant2(m);
let r_d = {
if d != V::ZERO {
V::one() / d
} else {
V::zero()
}
};
[m[3] * r_d, -m[1] * r_d, -m[2] * r_d, m[0] * r_d]
}
pub fn determinant3<V: Num>(m: &[V; 9]) -> V {
m[0] * (m[4] * m[8] - m[5] * m[7])
+ m[1] * (m[5] * m[6] - m[3] * m[8])
+ m[2] * (m[3] * m[7] - m[4] * m[6])
}
pub fn inverse3<V: Num>(m: &[V; 9]) -> [V; 9] {
let mut r = [V::zero(); 9];
let d = determinant3(m);
let r_d = {
if d != V::ZERO {
V::one() / d
} else {
V::zero()
}
};
#[allow(clippy::identity_op)]
{
r[0] = (m[3 + 1] * m[6 + 2] - m[3 + 2] * m[6 + 1]) * r_d;
r[3] = (m[3 + 2] * m[6 + 0] - m[3 + 0] * m[6 + 2]) * r_d;
r[6] = (m[3 + 0] * m[6 + 1] - m[3 + 1] * m[6 + 0]) * r_d;
r[1] = (m[6 + 1] * m[0 + 2] - m[6 + 2] * m[0 + 1]) * r_d;
r[4] = (m[6 + 2] * m[0 + 0] - m[6 + 0] * m[0 + 2]) * r_d;
r[7] = (m[6 + 0] * m[0 + 1] - m[6 + 1] * m[0 + 0]) * r_d;
r[2] = (m[0 + 1] * m[3 + 2] - m[0 + 2] * m[3 + 1]) * r_d;
r[5] = (m[0 + 2] * m[3 + 0] - m[0 + 0] * m[3 + 2]) * r_d;
r[8] = (m[0 + 0] * m[3 + 1] - m[0 + 1] * m[3 + 0]) * r_d;
}
r
}
pub fn from_quat3<V: Num>(q: [V; 4]) -> [V; 9] {
let zero = V::zero();
let one = V::one();
let two = one + one;
let mut r = [zero; 9];
let x = q[0];
let y = q[1];
let z = q[2];
let w = q[3];
#[allow(clippy::identity_op)]
{
r[0 + 0] = one - two * y * y - two * z * z;
r[0 + 1] = two * x * y + two * z * w;
r[0 + 2] = two * x * z - two * y * w;
r[3 + 1] = one - two * z * z - two * x * x;
r[3 + 2] = two * y * z + two * x * w;
r[3 + 0] = two * x * y - two * z * w;
r[6 + 2] = one - two * x * x - two * y * y;
r[6 + 0] = two * z * x + two * y * w;
r[6 + 1] = two * y * z - two * x * w;
}
r
}
pub fn determinant4<V: Num>(m: &[V; 16]) -> V {
#[allow(clippy::identity_op)]
{
let det0 = m[0]
* (m[4 + 1] * (m[8 + 2] * m[12 + 3] - m[8 + 3] * m[12 + 2])
+ (m[4 + 2] * (m[8 + 3] * m[12 + 1] - m[8 + 1] * m[12 + 3]))
+ (m[4 + 3] * (m[8 + 1] * m[12 + 2] - m[8 + 2] * m[12 + 1])));
let det1 = m[1]
* (m[4 + 2] * (m[8 + 3] * m[12 + 0] - m[8 + 0] * m[12 + 3])
+ (m[4 + 3] * (m[8 + 0] * m[12 + 2] - m[8 + 2] * m[12 + 0]))
+ (m[4 + 0] * (m[8 + 2] * m[12 + 3] - m[8 + 3] * m[12 + 2])));
let det2 = m[2]
* (m[4 + 3] * (m[8 + 0] * m[12 + 1] - m[8 + 1] * m[12 + 0])
+ (m[4 + 0] * (m[8 + 1] * m[12 + 3] - m[8 + 3] * m[12 + 1]))
+ (m[4 + 1] * (m[8 + 3] * m[12 + 0] - m[8 + 0] * m[12 + 3])));
let det3 = m[3]
* (m[4 + 0] * (m[8 + 1] * m[12 + 2] - m[8 + 2] * m[12 + 1])
+ (m[4 + 1] * (m[8 + 2] * m[12 + 0] - m[8 + 0] * m[12 + 2]))
+ (m[4 + 2] * (m[8 + 0] * m[12 + 1] - m[8 + 1] * m[12 + 0])));
det0 - det1 + det2 - det3
}
}
pub fn inverse4<V: Num>(m: &[V; 16]) -> [V; 16] {
let d = determinant4(m);
let mut r = [V::ZERO; 16];
if d != V::ZERO {
let r_d = V::one() / d;
for j in 0..4 {
let a = ((j + 1) & 3) * 4;
let b = ((j + 2) & 3) * 4;
let c = ((j + 3) & 3) * 4;
for i in 0..4 {
let x = (i + 1) & 3;
let y = (i + 2) & 3;
let z = (i + 3) & 3;
let sc = if (i + j) & 1 == 0 {
V::one()
} else {
-V::one()
};
r[i * 4 + j] = ((m[a + x] * m[b + y] - m[b + x] * m[a + y]) * m[c + z]
+ (m[a + y] * m[b + z] - m[b + y] * m[a + z]) * m[c + x]
+ (m[a + z] * m[b + x] - m[b + z] * m[a + x]) * m[c + y])
* sc
* r_d;
}
}
}
r
}
pub fn multiply2<V: Num>(a: &[V; 2 * 2], b: &[V; 2 * 2]) -> [V; 2 * 2] {
matrixr_op::multiply::<V, 4, 4, 4, 2, 2, 2>(a, b)
}
pub fn transform_vec2<V: Num>(m: &[V; 4], v: &[V; 2]) -> [V; 2] {
matrixr_op::transform_vec::<V, 4, 2, 2>(m, v)
}
pub fn multiply3<V: Num>(a: &[V; 9], b: &[V; 9]) -> [V; 9] {
matrixr_op::multiply::<V, 9, 9, 9, 3, 3, 3>(a, b)
}
pub fn transform_vec3<V: Num>(m: &[V; 9], v: &[V; 3]) -> [V; 3] {
matrixr_op::transform_vec::<V, 9, 3, 3>(m, v)
}
pub fn look_at3<V: Float>(dirn: &[V; 3], up: &[V; 3]) -> [V; 9] {
let d = vector_op::normalize(*dirn);
let du = vector_op::dot(&d, up);
let u = [up[0] - d[0] * du, up[1] - d[1] * du, up[2] - d[2] * du];
let u = vector_op::normalize(u);
[
u[2] * d[1] - u[1] * d[2],
u[0] * d[2] - u[2] * d[0],
u[1] * d[0] - u[0] * d[1],
u[0],
u[1],
u[2],
-d[0],
-d[1],
-d[2],
]
}
pub fn multiply4<V: Num>(a: &[V; 16], b: &[V; 16]) -> [V; 16] {
matrixr_op::multiply::<V, 16, 16, 16, 4, 4, 4>(a, b)
}
pub fn transform_vec4<V: Num>(m: &[V; 16], v: &[V; 4]) -> [V; 4] {
matrixr_op::transform_vec::<V, 16, 4, 4>(m, v)
}
pub fn translate4<V: Num>(m: &[V; 16], v: &[V; 4]) -> [V; 16] {
let mut r = *m;
#[allow(clippy::identity_op)]
{
r[12] = m[0] * v[0] + m[4 + 0] * v[1] + m[8 + 0] * v[2] + m[12 + 0];
r[13] = m[1] * v[0] + m[4 + 1] * v[1] + m[8 + 1] * v[2] + m[12 + 1];
r[14] = m[2] * v[0] + m[4 + 2] * v[1] + m[8 + 2] * v[2] + m[12 + 2];
r[15] = m[3] * v[0] + m[4 + 3] * v[1] + m[8 + 3] * v[2] + m[12 + 3];
}
r
}
pub fn look_at4<V: Float>(eye: &[V; 3], centre: &[V; 3], up: &[V; 3]) -> [V; 16] {
let dirn = vector_op::sub(*centre, eye, V::one());
let d = vector_op::normalize(dirn);
let du = vector_op::dot(&d, up);
let u = [up[0] - d[0] * du, up[1] - d[1] * du, up[2] - d[2] * du];
let u = vector_op::normalize(u);
[
u[2] * d[1] - u[1] * d[2],
u[0] * d[2] - u[2] * d[0],
u[1] * d[0] - u[0] * d[1],
V::zero(),
u[0],
u[1],
u[2],
V::zero(),
-d[0],
-d[1],
-d[2],
V::zero(),
V::zero(),
V::zero(),
V::zero(),
V::one(),
]
}
pub fn perspective4<V: Float>(fov: V, aspect: V, near: V, far: V) -> [V; 16] {
let zero = V::zero();
let one = V::one();
let two = one + one;
let mut r = [zero; 16];
let f = one / V::tan(fov / two);
r[0] = f / aspect;
r[5] = f;
r[11] = -one;
let nf = one / (near - far);
r[10] = (far + near) * nf;
r[14] = two * far * near * nf;
r
}
pub fn from_quat4<V: Num>(q: [V; 4]) -> [V; 16] {
let zero = V::zero();
let one = V::one();
let two = one + one;
let mut r = [zero; 16];
let x = q[0];
let y = q[1];
let z = q[2];
let w = q[3];
#[allow(clippy::identity_op)]
{
r[0 + 0] = one - two * y * y - two * z * z;
r[0 + 1] = two * x * y + two * z * w;
r[0 + 2] = two * x * z - two * y * w;
r[4 + 1] = one - two * z * z - two * x * x;
r[4 + 2] = two * y * z + two * x * w;
r[4 + 0] = two * x * y - two * z * w;
r[8 + 2] = one - two * x * x - two * y * y;
r[8 + 0] = two * z * x + two * y * w;
r[8 + 1] = two * y * z - two * x * w;
r[15] = V::one();
}
r
}
#[track_caller]
pub fn lup_decompose<V: Num + std::cmp::PartialOrd>(
order: usize,
matrix: &[V],
lu: &mut [V],
pivot: &mut [usize],
) -> V {
assert!(
pivot.len() >= order,
"Pivot must be at least the order of the matrix"
);
assert_eq!(
matrix.len(),
order * order,
"Matrix provided must be square and of the correct order"
);
assert_eq!(
lu.len(),
order * order,
"Resulting LU decomposition provided must be square and of the correct order"
);
for i in 0..order {
pivot[i] = i;
}
let mut det = V::one();
lu.copy_from_slice(matrix);
for d in 0..(order - 1) {
let mut max_lu_rc = V::zero();
let mut r_max = 0;
for r in d..order {
let t = lu[r * order + d];
if t < V::zero() {
if (max_lu_rc < V::zero() && t < max_lu_rc)
|| (max_lu_rc >= V::zero() && t < -max_lu_rc)
{
max_lu_rc = t;
r_max = r;
}
} else {
if (max_lu_rc > V::zero() && t > max_lu_rc)
|| (max_lu_rc <= V::zero() && t > -max_lu_rc)
{
max_lu_rc = t;
r_max = r;
}
}
}
if max_lu_rc == V::zero() {
return max_lu_rc;
}
if r_max != d {
det = -det;
let p = pivot[r_max];
pivot[r_max] = pivot[d];
pivot[d] = p;
for c in 0..order {
let v = lu[r_max * order + c];
lu[r_max * order + c] = lu[d * order + c];
lu[d * order + c] = v;
}
}
det = det * max_lu_rc;
for r in (d + 1)..order {
let scale = lu[r * order + d] / max_lu_rc;
lu[r * order + d] = scale;
for c in (d + 1)..order {
lu[r * order + c] = lu[r * order + c] - scale * lu[d * order + c];
}
}
}
det * lu[order * order - 1]
}
pub fn lu_split<V: Num>(order: usize, lu: &mut [V], lower: &mut [V]) {
assert_eq!(
lu.len(),
order * order,
"LU decomposition provided must be square and of the correct order"
);
assert_eq!(
lower.len(),
order * order,
"Lower matrix provided must be square and of the correct order"
);
lower.copy_from_slice(lu);
for r in 0..order {
for c in 0..order {
if r == c {
lower[r * order + c] = V::one();
} else if r < c {
lower[r * order + c] = V::zero();
} else {
lu[r * order + c] = V::zero();
}
}
}
}
#[track_caller]
pub fn lup_invert<V: Num>(
n: usize,
lu: &[V],
pivot: &[usize],
result: &mut [V],
temp_row: &mut [V],
temp_row2: &mut [V],
) -> bool {
assert_eq!(
lu.len(),
n * n,
"Inverting a square {n} by {n} matrix expected LU len to match",
);
assert_eq!(
pivot.len(),
n,
"Inverting a square {n} by {n} matrix expected pivot len to be n",
);
assert_eq!(
result.len(),
n * n,
"Inverting a square {n} by {n} matrix expected result len to match",
);
assert_eq!(temp_row.len(), n, "Temp row 1 must be length `n`");
assert_eq!(temp_row2.len(), n, "Temp row 2 must be length `n`");
for c in 0..n {
for r in 0..n {
temp_row[r] = V::zero();
}
temp_row[c] = V::one();
for r in 0..n {
for i in (r + 1)..n {
temp_row[i] = temp_row[i] - lu[i * n + r] * temp_row[r];
}
}
temp_row2.copy_from_slice(temp_row);
let mut r = n - 1;
loop {
let urr = lu[r * n + r];
if urr == V::zero() {
return false;
}
temp_row2[r] = temp_row2[r] / urr;
for i in 0..r {
temp_row2[i] = temp_row2[i] - lu[i * n + r] * temp_row2[r]
}
if r == 0 {
break;
}
r -= 1;
}
for r in 0..n {
result[r * n + pivot[c]] = temp_row2[r];
}
}
true
}
pub fn unpivot<V: Num>(order: usize, matrix: &[V], pivot: &[usize], result: &mut [V]) {
for r in 0..order {
for c in 0..order {
let p = pivot[r];
result[r * order + c] = matrix[p * order + c];
}
}
}
pub fn invert<V: Num + std::cmp::PartialOrd, const D2: usize, const D: usize>(
matrix: &mut [V; D2],
) -> bool {
let mut lu = [V::zero(); D2];
let mut pivot = [0; D];
let mut tr0 = [V::zero(); D];
let mut tr1 = [V::zero(); D];
if lup_decompose(D, matrix, &mut lu, &mut pivot) == V::zero() {
false
} else {
lup_invert(D, &lu, &pivot, matrix, &mut tr0, &mut tr1)
}
}