use crate::{Angle, NVector};
#[derive(PartialEq, Clone, Copy, Debug, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct ChordLength {
length2: f64,
}
impl ChordLength {
const MAX_CHORD_LENGTH_2: f64 = 4.0;
pub const NEGATIVE: ChordLength = Self { length2: -1.0 };
pub const ZERO: ChordLength = Self { length2: 0.0 };
pub const MAX: ChordLength = Self {
length2: Self::MAX_CHORD_LENGTH_2,
};
#[inline]
pub(crate) fn length2(&self) -> f64 {
self.length2
}
pub(crate) fn from_squared_length(length2: f64) -> Self {
Self { length2 }
}
pub fn new(p1: NVector, p2: NVector) -> Self {
let l2 = (p1.as_vec3() - p2.as_vec3()).squared_norm();
Self {
length2: l2.min(Self::MAX_CHORD_LENGTH_2),
}
}
pub fn from_angle(angle: Angle) -> Self {
let abs_angle: Angle = angle.abs();
if abs_angle == Angle::HALF_CIRCLE {
return Self::MAX;
}
let a = abs_angle.normalised_to(Angle::HALF_CIRCLE);
let l = 2.0 * (a.as_radians() * 0.5).sin();
Self { length2: l * l }
}
pub fn to_angle(&self) -> Angle {
if self.length2 < 0.0 {
return Angle::from_radians(-1.0);
}
Angle::from_radians(2.0 * (self.length2.sqrt() * 0.5).asin())
}
}
impl Eq for ChordLength {}
impl PartialOrd for ChordLength {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for ChordLength {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
let d = self.length2 - other.length2;
if d == 0.0 {
return std::cmp::Ordering::Equal;
}
if d < 0.0 {
return std::cmp::Ordering::Less;
}
std::cmp::Ordering::Greater
}
}
#[cfg(test)]
mod tests {
use std::ops::Neg;
use crate::{spherical::ChordLength, Angle, NVector};
#[test]
fn from_pos() {
let c = ChordLength::new(
NVector::from_lat_long_degrees(90.0, 0.0),
NVector::from_lat_long_degrees(-90.0, 0.0),
);
assert_eq!(Angle::HALF_CIRCLE, c.to_angle());
}
#[test]
fn symmetry() {
assert_eq!(
Angle::QUARTER_CIRCLE.round_d7(),
ChordLength::from_angle(Angle::QUARTER_CIRCLE)
.to_angle()
.round_d7()
);
}
#[test]
fn negative_to_angle() {
assert_eq!(Angle::from_radians(-1.0), ChordLength::NEGATIVE.to_angle());
}
#[test]
fn from_angle_range() {
assert_eq!(
ChordLength::MAX,
ChordLength::from_angle(Angle::HALF_CIRCLE),
);
assert_eq!(
ChordLength::from_angle(Angle::QUARTER_CIRCLE),
ChordLength::from_angle(Angle::HALF_CIRCLE + Angle::QUARTER_CIRCLE)
);
assert_eq!(
ChordLength::from_angle(Angle::QUARTER_CIRCLE),
ChordLength::from_angle(Angle::QUARTER_CIRCLE.neg())
);
}
#[test]
fn ord() {
let a = Angle::from_degrees(45.0);
assert_eq!(
::std::cmp::Ordering::Equal,
ChordLength::from_angle(a).cmp(&ChordLength::from_angle(a))
);
assert_eq!(
::std::cmp::Ordering::Equal,
ChordLength::NEGATIVE.cmp(&ChordLength::NEGATIVE)
);
assert_eq!(
::std::cmp::Ordering::Less,
ChordLength::NEGATIVE.cmp(&ChordLength::from_angle(a))
);
assert_eq!(
::std::cmp::Ordering::Greater,
ChordLength::from_angle(a).cmp(&ChordLength::NEGATIVE)
);
let b = Angle::from_degrees(90.0);
assert_eq!(
::std::cmp::Ordering::Less,
ChordLength::from_angle(a).cmp(&ChordLength::from_angle(b))
);
assert_eq!(
::std::cmp::Ordering::Greater,
ChordLength::from_angle(b).cmp(&ChordLength::from_angle(a))
);
}
}