use core::fmt;
pub type Scalar = f64;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ToleranceError {
NotFiniteAndNonNegative,
}
impl fmt::Display for ToleranceError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::NotFiniteAndNonNegative => {
f.write_str("tolerance values must be finite and non-negative")
}
}
}
}
impl std::error::Error for ToleranceError {}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Tolerance {
linear: Scalar,
angular: Scalar,
}
impl Tolerance {
pub const ZERO: Self = Self {
linear: 0.0,
angular: 0.0,
};
pub const METRE: Self = Self {
linear: 1e-6,
angular: 1e-9,
};
pub const MILLIMETRE: Self = Self {
linear: 1e-3,
angular: 1e-9,
};
pub fn new(linear: Scalar, angular: Scalar) -> Result<Self, ToleranceError> {
if !linear.is_finite() || !angular.is_finite() || linear < 0.0 || angular < 0.0 {
return Err(ToleranceError::NotFiniteAndNonNegative);
}
Ok(Self { linear, angular })
}
#[inline]
pub const fn linear(self) -> Scalar {
self.linear
}
#[inline]
pub const fn angular(self) -> Scalar {
self.angular
}
#[inline]
pub fn eq(self, a: Scalar, b: Scalar) -> bool {
(a - b).abs() <= self.linear
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn rejects_invalid_policy() {
assert_eq!(
Tolerance::new(Scalar::NAN, 0.0),
Err(ToleranceError::NotFiniteAndNonNegative)
);
assert_eq!(
Tolerance::new(-1.0, 0.0),
Err(ToleranceError::NotFiniteAndNonNegative)
);
}
#[test]
fn policy_has_no_context_free_default() {
assert!(Tolerance::METRE.eq(1.0, 1.0 + 0.5e-6));
assert!(!Tolerance::METRE.eq(1.0, 1.0 + 2.0e-6));
}
}