use crate::{numbers::eq_zero, Vec3};
const TRIAGE_SIDE_EPS: f64 = 10.0 * f64::EPSILON;
pub(crate) fn angle_radians_between(v1: Vec3, v2: Vec3, vn: Option<Vec3>) -> f64 {
let p1xp2 = v1.cross_prod(v2);
let norm = p1xp2.norm();
let sin_o = match vn {
None => norm,
Some(v) => {
let d = p1xp2.dot_prod(v);
if d >= 0.0 {
norm
} else {
-norm
}
}
};
let cos_o = v1.dot_prod(v2);
sin_o.atan2(cos_o)
}
pub(crate) fn easting(v: Vec3) -> Vec3 {
if v.z().abs() == 1.0 {
return Vec3::UNIT_Y;
}
Vec3::new_unit(-v.y(), v.x(), 0.0)
}
pub(crate) fn side(v0: Vec3, v1: Vec3, v2: Vec3) -> i8 {
let triage_side = v0.dot_prod(v1.cross_prod(v2));
if triage_side.abs() <= TRIAGE_SIDE_EPS {
let side = v0.dot_prod(v1.orthogonal_to(v2));
if eq_zero(side) {
return 0;
}
if side > 0.0 {
1
} else {
-1
}
} else {
if triage_side > 0.0 {
1
} else {
-1
}
}
}
#[cfg(test)]
mod tests {
use std::f64::consts::PI;
use crate::{spherical::base::angle_radians_between, Vec3};
#[test]
fn angle_radians_between_signed() {
let z = Vec3::new(0.0, 0.0, -1.0);
assert_eq!(
angle_radians_between(Vec3::new(0.0, 1.0, 0.0), Vec3::new(1.0, 0.0, 0.0), Some(z)),
PI / 2.0
);
assert_eq!(
angle_radians_between(Vec3::new(1.0, 0.0, 0.0), Vec3::new(0.0, 1.0, 0.0), Some(z)),
-PI / 2.0
);
assert_eq!(
angle_radians_between(Vec3::new(1.0, 0.0, 0.0), Vec3::new(1.0, 1.0, 0.0), Some(z)),
-PI / 4.0
);
assert_eq!(
angle_radians_between(Vec3::new(1.0, 1.0, 0.0), Vec3::new(1.0, 0.0, 0.0), Some(z)),
PI / 4.0,
);
}
#[test]
fn angle_radians_between_unsigned() {
assert_eq!(
angle_radians_between(Vec3::new(0.0, 1.0, 0.0), Vec3::new(1.0, 0.0, 0.0), None),
PI / 2.0
);
assert_eq!(
angle_radians_between(Vec3::new(1.0, 0.0, 0.0), Vec3::new(1.0, 1.0, 0.0), None),
PI / 4.0
);
}
}