1use once_cell::sync::Lazy;
2use serde::{Deserialize, Serialize};
3use std::f64::consts::PI as STD_PI;
4
5pub const PI: f64 = STD_PI;
7pub const TO_DEGREES: f64 = 180.0 / STD_PI;
8pub const TO_RADIANS: f64 = STD_PI / 180.0;
9
10pub const SCALE: f64 = 1e6;
12
13#[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 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 pub const ANGLE_TOLERANCE_DEGREES: f64 = 0.11;
38
39 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;