use glam::Vec3;
use itertools::Itertools;
#[cfg(test)]
mod tests;
fn sphere_ln(q: &Vec3, p: &Vec3) -> Vec3 {
let r = p.angle_between(*q);
let k = if r == 0.0 {
1.0
} else {
r / r.sin()
};
k * (p - q * r.cos())
}
fn sphere_exp(q: &Vec3, dp: &Vec3) -> Vec3 {
let r = dp.length();
let k = if r == 0.0 {
1.0
} else {
r.sin() / r
};
q * r.cos() + k * dp
}
pub fn slerp_n<const N: usize>(w: &[f32; N], p: &[Vec3; N]) -> Vec3 {
let total_weight = w.iter().cloned().tree_reduce(|a, b| a + b);
debug_assert!(total_weight.is_some(), "Sum of weights must exist.");
debug_assert!((total_weight.unwrap() - 1.0) <= f32::EPSILON, "Sum of weights must be equal to 1.0.");
let mut q = w.iter()
.zip(p.iter())
.map(|(w_i, p_i)| w_i * p_i)
.tree_reduce(|v_i, v_j| v_i + v_j)
.unwrap()
.normalize();
loop {
let u = w.iter()
.zip(p.iter())
.map(|(w_i, p_i)| w_i * sphere_ln(&q, &p_i))
.tree_reduce(|p_i, p_j| p_i + p_j)
.unwrap();
q = sphere_exp(&q, &u);
if u.length() < 1.0e-6 {
return q;
}
}
}
pub fn slerp_3(w1: f32, p1: Vec3, w2: f32, p2: Vec3, w3: f32, p3: Vec3) -> Vec3 {
slerp_n(&[w1, w2, w3], &[p1, p2, p3])
}