arris_math/tolerance.rs
1//! The tolerance pair an algorithm takes.
2
3use crate::Precision;
4
5/// What a geometric algorithm needs to decide "the same" and "parallel":
6/// a length and an angle. Derived from the model's [`Precision`] by
7/// [`Precision::tolerance`] or from an entity's own tolerance by the
8/// operation that owns it — never a literal in an algorithm
9/// (`docs/DATA-MODEL.md` §Tolerances).
10///
11/// ```
12/// use arris_math::{Precision, Tolerance};
13///
14/// let tol: Tolerance = Precision::DEFAULT.tolerance();
15/// assert_eq!(tol.linear, Precision::DEFAULT.default_tolerance);
16/// assert_eq!(tol.angular, Precision::DEFAULT.angular_tolerance);
17/// ```
18#[derive(Debug, Clone, Copy, PartialEq)]
19pub struct Tolerance {
20 /// Two points closer than this are the same point; a distance below
21 /// it is zero.
22 pub linear: f64,
23 /// Two directions within this angle (radians) are parallel; two
24 /// surfaces meeting at less than it are tangent.
25 pub angular: f64,
26}
27
28impl Tolerance {
29 /// A tolerance from its two parts.
30 pub const fn new(linear: f64, angular: f64) -> Self {
31 Tolerance { linear, angular }
32 }
33
34 /// `true` when both parts are finite and positive.
35 pub fn is_consistent(&self) -> bool {
36 self.linear.is_finite()
37 && self.linear > 0.0
38 && self.angular.is_finite()
39 && self.angular > 0.0
40 }
41}
42
43impl Precision {
44 /// The tolerance an algorithm without an entity of its own uses:
45 /// `default_tolerance` for lengths, `angular_tolerance` for angles.
46 pub const fn tolerance(&self) -> Tolerance {
47 Tolerance::new(self.default_tolerance, self.angular_tolerance)
48 }
49}
50
51#[cfg(test)]
52mod tests {
53 use super::*;
54
55 #[test]
56 fn default_precision_gives_a_consistent_tolerance() {
57 assert!(Precision::DEFAULT.tolerance().is_consistent());
58 assert!(!Tolerance::new(0.0, 1e-12).is_consistent());
59 assert!(!Tolerance::new(1e-7, f64::NAN).is_consistent());
60 }
61}