use std::f32::consts::PI;
pub fn cartesian_to_spherical(cartesian: [f32; 3]) -> [f32; 3] {
let [x, y, z] = cartesian;
let r = (x * x + y * y + z * z).sqrt();
let theta = z.atan2((x * x + y * y).sqrt()); let phi = y.atan2(x);
[
(phi * (180.0 / PI) + 360.0) % 360.0, theta * (180.0 / PI), r, ]
}
pub fn spherical_to_cartesian(spherical: [f32; 3]) -> [f32; 3] {
let [azimuth, elevation, radius] = spherical;
let phi = azimuth * (PI / 180.0);
let theta = elevation * (PI / 180.0);
let horizontal_dist = theta.cos() * radius;
[
phi.cos() * horizontal_dist, phi.sin() * horizontal_dist, theta.sin() * radius, ]
}
pub fn radius(cartesian: &[f32; 3]) -> f32 {
(cartesian[0].powi(2) + cartesian[1].powi(2) + cartesian[2].powi(2)).sqrt()
}
#[allow(dead_code)]
pub fn convert_array_to_spherical(values: &mut [f32]) {
for chunk in values.chunks_exact_mut(3) {
let cart: [f32; 3] = [chunk[0], chunk[1], chunk[2]];
let sph = cartesian_to_spherical(cart);
chunk[0] = sph[0];
chunk[1] = sph[1];
chunk[2] = sph[2];
}
}
pub fn convert_array_to_cartesian(values: &mut [f32]) {
for chunk in values.chunks_exact_mut(3) {
let sph: [f32; 3] = [chunk[0], chunk[1], chunk[2]];
let cart = spherical_to_cartesian(sph);
chunk[0] = cart[0];
chunk[1] = cart[1];
chunk[2] = cart[2];
}
}
#[cfg(test)]
mod tests {
use super::*;
const EPSILON: f32 = 1e-5;
fn approx_eq(a: f32, b: f32) -> bool {
(a - b).abs() < EPSILON
}
#[test]
fn test_cartesian_to_spherical_front() {
let cart = [1.0, 0.0, 0.0];
let sph = cartesian_to_spherical(cart);
assert!(approx_eq(sph[0], 0.0), "azimuth: {} != 0", sph[0]);
assert!(approx_eq(sph[1], 0.0), "elevation: {} != 0", sph[1]);
assert!(approx_eq(sph[2], 1.0), "radius: {} != 1", sph[2]);
}
#[test]
fn test_cartesian_to_spherical_left() {
let cart = [0.0, 1.0, 0.0];
let sph = cartesian_to_spherical(cart);
assert!(approx_eq(sph[0], 90.0), "azimuth: {} != 90", sph[0]);
assert!(approx_eq(sph[1], 0.0), "elevation: {} != 0", sph[1]);
assert!(approx_eq(sph[2], 1.0), "radius: {} != 1", sph[2]);
}
#[test]
fn test_cartesian_to_spherical_up() {
let cart = [0.0, 0.0, 1.0];
let sph = cartesian_to_spherical(cart);
assert!(approx_eq(sph[1], 90.0), "elevation: {} != 90", sph[1]);
assert!(approx_eq(sph[2], 1.0), "radius: {} != 1", sph[2]);
}
#[test]
fn test_spherical_to_cartesian_front() {
let sph = [0.0, 0.0, 1.0];
let cart = spherical_to_cartesian(sph);
assert!(approx_eq(cart[0], 1.0), "x: {} != 1", cart[0]);
assert!(approx_eq(cart[1], 0.0), "y: {} != 0", cart[1]);
assert!(approx_eq(cart[2], 0.0), "z: {} != 0", cart[2]);
}
#[test]
fn test_spherical_to_cartesian_left() {
let sph = [90.0, 0.0, 1.0];
let cart = spherical_to_cartesian(sph);
assert!(approx_eq(cart[0], 0.0), "x: {} != 0", cart[0]);
assert!(approx_eq(cart[1], 1.0), "y: {} != 1", cart[1]);
assert!(approx_eq(cart[2], 0.0), "z: {} != 0", cart[2]);
}
#[test]
fn test_roundtrip_cartesian_spherical() {
let original = [0.5, 0.3, 0.7];
let spherical = cartesian_to_spherical(original);
let back = spherical_to_cartesian(spherical);
assert!(
approx_eq(original[0], back[0]),
"x: {} != {}",
original[0],
back[0]
);
assert!(
approx_eq(original[1], back[1]),
"y: {} != {}",
original[1],
back[1]
);
assert!(
approx_eq(original[2], back[2]),
"z: {} != {}",
original[2],
back[2]
);
}
#[test]
fn test_convert_array_to_spherical() {
let mut values = [1.0, 0.0, 0.0, 0.0, 1.0, 0.0]; convert_array_to_spherical(&mut values);
assert!(approx_eq(values[0], 0.0)); assert!(approx_eq(values[1], 0.0)); assert!(approx_eq(values[2], 1.0));
assert!(approx_eq(values[3], 90.0)); assert!(approx_eq(values[4], 0.0)); assert!(approx_eq(values[5], 1.0)); }
#[test]
fn test_radius() {
assert!(approx_eq(radius(&[1.0, 0.0, 0.0]), 1.0));
assert!(approx_eq(radius(&[0.0, 1.0, 0.0]), 1.0));
assert!(approx_eq(radius(&[0.0, 0.0, 1.0]), 1.0));
assert!(approx_eq(radius(&[1.0, 1.0, 1.0]), 3.0_f32.sqrt()));
}
}