session_rust/
tolerance.rs

1use once_cell::sync::Lazy;
2use serde::{Deserialize, Serialize};
3use std::f64::consts::PI as STD_PI;
4
5/// Mathematical constants
6pub const PI: f64 = STD_PI;
7pub const TO_DEGREES: f64 = 180.0 / STD_PI;
8pub const TO_RADIANS: f64 = STD_PI / 180.0;
9
10/// Scale factor
11pub const SCALE: f64 = 1e6;
12
13/// Tolerance settings for geometric operations
14#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct Tolerance {
16    pub unit: String,
17    absolute: Option<f64>,
18    relative: Option<f64>,
19    angular: Option<f64>,
20    approximation: Option<f64>,
21    precision: Option<i32>,
22    lineardeflection: Option<f64>,
23    angulardeflection: Option<f64>,
24}
25
26impl Tolerance {
27    /// Default tolerance values
28    pub const ABSOLUTE: f64 = 1e-9;
29    pub const RELATIVE: f64 = 1e-6;
30    pub const ANGULAR: f64 = 1e-6;
31    pub const APPROXIMATION: f64 = 1e-3;
32    pub const PRECISION: i32 = 3;
33    pub const LINEARDEFLECTION: f64 = 1e-3;
34    pub const ANGULARDEFLECTION: f64 = 1e-1;
35
36    /// Angle tolerance in degrees
37    pub const ANGLE_TOLERANCE_DEGREES: f64 = 0.11;
38
39    /// Zero tolerance for comparisons
40    pub const ZERO_TOLERANCE: f64 = 1e-12;
41
42    pub fn new(unit: &str) -> Self {
43        Self {
44            unit: unit.to_string(),
45            absolute: None,
46            relative: None,
47            angular: None,
48            approximation: None,
49            precision: None,
50            lineardeflection: None,
51            angulardeflection: None,
52        }
53    }
54
55    pub fn reset(&mut self) {
56        self.absolute = None;
57        self.relative = None;
58        self.angular = None;
59        self.approximation = None;
60        self.precision = None;
61        self.lineardeflection = None;
62        self.angulardeflection = None;
63    }
64
65    pub fn absolute(&self) -> f64 {
66        self.absolute.unwrap_or(Self::ABSOLUTE)
67    }
68
69    pub fn set_absolute(&mut self, value: f64) {
70        self.absolute = Some(value);
71    }
72
73    pub fn relative(&self) -> f64 {
74        self.relative.unwrap_or(Self::RELATIVE)
75    }
76
77    pub fn set_relative(&mut self, value: f64) {
78        self.relative = Some(value);
79    }
80
81    pub fn angular(&self) -> f64 {
82        self.angular.unwrap_or(Self::ANGULAR)
83    }
84
85    pub fn set_angular(&mut self, value: f64) {
86        self.angular = Some(value);
87    }
88
89    pub fn approximation(&self) -> f64 {
90        self.approximation.unwrap_or(Self::APPROXIMATION)
91    }
92
93    pub fn set_approximation(&mut self, value: f64) {
94        self.approximation = Some(value);
95    }
96
97    pub fn precision(&self) -> i32 {
98        self.precision.unwrap_or(Self::PRECISION)
99    }
100
101    pub fn set_precision(&mut self, value: i32) {
102        self.precision = Some(value);
103    }
104
105    pub fn lineardeflection(&self) -> f64 {
106        self.lineardeflection.unwrap_or(Self::LINEARDEFLECTION)
107    }
108
109    pub fn set_lineardeflection(&mut self, value: f64) {
110        self.lineardeflection = Some(value);
111    }
112
113    pub fn angulardeflection(&self) -> f64 {
114        self.angulardeflection.unwrap_or(Self::ANGULARDEFLECTION)
115    }
116
117    pub fn set_angulardeflection(&mut self, value: f64) {
118        self.angulardeflection = Some(value);
119    }
120
121    pub fn tolerance(&self, truevalue: f64, rtol: f64, atol: f64) -> f64 {
122        rtol * truevalue.abs() + atol
123    }
124
125    pub fn compare(&self, a: f64, b: f64, rtol: f64, atol: f64) -> bool {
126        (a - b).abs() <= self.tolerance(b, rtol, atol)
127    }
128
129    pub fn is_zero(&self, a: f64, tol: Option<f64>) -> bool {
130        let tol = tol.unwrap_or(self.absolute());
131        a.abs() <= tol
132    }
133
134    pub fn is_positive(&self, a: f64, tol: Option<f64>) -> bool {
135        let tol = tol.unwrap_or(self.absolute());
136        a > tol
137    }
138
139    pub fn is_negative(&self, a: f64, tol: Option<f64>) -> bool {
140        let tol = tol.unwrap_or(self.absolute());
141        a < -tol
142    }
143
144    pub fn is_between(&self, value: f64, minval: f64, maxval: f64, atol: Option<f64>) -> bool {
145        let atol = atol.unwrap_or(self.absolute());
146        minval - atol <= value && value <= maxval + atol
147    }
148
149    pub fn is_close(&self, a: f64, b: f64, rtol: Option<f64>, atol: Option<f64>) -> bool {
150        let rtol = rtol.unwrap_or(self.relative());
151        let atol = atol.unwrap_or(self.absolute());
152        self.compare(a, b, rtol, atol)
153    }
154
155    pub fn is_allclose(&self, a: &[f64], b: &[f64], rtol: Option<f64>, atol: Option<f64>) -> bool {
156        let rtol = rtol.unwrap_or(self.relative());
157        let atol = atol.unwrap_or(self.absolute());
158        a.iter()
159            .zip(b.iter())
160            .all(|(x, y)| self.compare(*x, *y, rtol, atol))
161    }
162
163    pub fn is_angle_zero(&self, a: f64, tol: Option<f64>) -> bool {
164        let tol = tol.unwrap_or(self.angular());
165        a.abs() <= tol
166    }
167
168    pub fn is_angles_close(&self, a: f64, b: f64, tol: Option<f64>) -> bool {
169        let tol = tol.unwrap_or(self.angular());
170        (a - b).abs() <= tol
171    }
172
173    pub fn geometric_key(&self, xyz: [f64; 3], precision: Option<i32>) -> String {
174        let precision = precision.unwrap_or_else(|| self.precision());
175        let [mut x, mut y, mut z] = xyz;
176
177        if precision == -1 {
178            return format!("{},{},{}", x as i64, y as i64, z as i64);
179        }
180
181        if precision < -1 {
182            let p = (-precision - 1) as u32;
183            let factor = 10_f64.powi(p as i32);
184            return format!(
185                "{},{},{}",
186                ((x / factor).round() * factor) as i64,
187                ((y / factor).round() * factor) as i64,
188                ((z / factor).round() * factor) as i64
189            );
190        }
191
192        let minzero = format!("-{:.prec$}", 0.0, prec = precision as usize);
193        if format!("{:.prec$}", x, prec = precision as usize) == minzero {
194            x = 0.0;
195        }
196        if format!("{:.prec$}", y, prec = precision as usize) == minzero {
197            y = 0.0;
198        }
199        if format!("{:.prec$}", z, prec = precision as usize) == minzero {
200            z = 0.0;
201        }
202
203        format!(
204            "{:.prec$},{:.prec$},{:.prec$}",
205            x,
206            y,
207            z,
208            prec = precision as usize
209        )
210    }
211
212    pub fn geometric_key_xy(&self, xy: [f64; 2], precision: Option<i32>) -> String {
213        let precision = precision.unwrap_or_else(|| self.precision());
214        let [mut x, mut y] = xy;
215
216        if precision == -1 {
217            return format!("{},{}", x as i64, y as i64);
218        }
219
220        if precision < -1 {
221            let p = (-precision - 1) as u32;
222            let factor = 10_f64.powi(p as i32);
223            return format!(
224                "{},{}",
225                ((x / factor).round() * factor) as i64,
226                ((y / factor).round() * factor) as i64
227            );
228        }
229
230        let minzero = format!("-{:.prec$}", 0.0, prec = precision as usize);
231        if format!("{:.prec$}", x, prec = precision as usize) == minzero {
232            x = 0.0;
233        }
234        if format!("{:.prec$}", y, prec = precision as usize) == minzero {
235            y = 0.0;
236        }
237
238        format!("{:.prec$},{:.prec$}", x, y, prec = precision as usize)
239    }
240
241    pub fn format_number(&self, number: f64, precision: Option<i32>) -> String {
242        let precision = precision.unwrap_or_else(|| self.precision());
243
244        if precision == -1 {
245            return format!("{}", number.round() as i64);
246        }
247
248        if precision < -1 {
249            let p = (-precision - 1) as u32;
250            let factor = 10_f64.powi(p as i32);
251            return format!("{}", ((number / factor).round() * factor) as i64);
252        }
253
254        format!("{:.prec$}", number, prec = precision as usize)
255    }
256
257    pub fn precision_from_tolerance(&self, tol: Option<f64>) -> i32 {
258        let tol = tol.unwrap_or_else(|| self.absolute());
259        if tol < 1.0 {
260            let s = format!("{tol:e}");
261            if let Some(exp_pos) = s.find("e-") {
262                if let Ok(exp) = s[exp_pos + 2..].parse::<i32>() {
263                    return exp;
264                }
265            }
266        }
267        0
268    }
269}
270
271impl Default for Tolerance {
272    fn default() -> Self {
273        Self::new("M")
274    }
275}
276
277pub static TOL: Lazy<Tolerance> = Lazy::new(Tolerance::default);
278
279#[cfg(test)]
280#[path = "tolerance_test.rs"]
281mod tolerance_test;