pub type Vec3 = [f64; 3];
#[inline]
pub fn dot(a: Vec3, b: Vec3) -> f64 {
a[0] * b[0] + a[1] * b[1] + a[2] * b[2]
}
#[inline]
pub fn difference(a: Vec3, b: Vec3) -> Vec3 {
[a[0] - b[0], a[1] - b[1], a[2] - b[2]]
}
#[inline]
pub fn norm(a: Vec3) -> f64 {
dot(a, a).sqrt()
}
pub fn unit(a: Vec3) -> Option<Vec3> {
let n = norm(a);
(n > 0.0 && n.is_finite()).then(|| [a[0] / n, a[1] / n, a[2] / n])
}
#[inline]
pub fn from_spherical(zenith_rad: f64, azimuth_rad: f64) -> Vec3 {
let (st, ct) = zenith_rad.sin_cos();
let (sp, cp) = azimuth_rad.sin_cos();
[st * cp, st * sp, ct]
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_spherical_direction_has_unit_norm() {
for (t, p) in [(0.3, -2.0), (1.2, 0.7), (3.0, 3.1)] {
let r = from_spherical(t, p);
assert!((norm(r) - 1.0).abs() < 1e-15);
assert!((r[2] - f64::cos(t)).abs() < 1e-15 && (r[1].atan2(r[0]) - p).abs() < 1e-12);
}
}
#[test]
fn a_direction_has_unit_norm_and_a_zero_vector_has_none() {
let u = unit([3.0, -4.0, 12.0]).unwrap();
assert!((norm(u) - 1.0).abs() < 1e-15);
assert_eq!(u, [3.0 / 13.0, -4.0 / 13.0, 12.0 / 13.0]);
assert_eq!(unit([0.0; 3]), None);
assert_eq!(unit([f64::INFINITY, 0.0, 0.0]), None);
assert_eq!(unit([f64::NAN, 0.0, 0.0]), None);
}
}