1use super::Decimal;
4use crate::{Point, Vector};
5
6#[inline]
11pub fn min(val1: f64, val2: f64) -> f64 {
12 if val1 != val2 {
13 if !val1.is_nan() {
14 return if val1 < val2 { val1 } else { val2 };
15 }
16 return val1;
17 }
18 if val1.is_sign_negative() {
19 val1
20 } else {
21 val2
22 }
23}
24
25#[inline]
27pub fn max(val1: f64, val2: f64) -> f64 {
28 if val1 != val2 {
29 if !val1.is_nan() {
30 return if val2 < val1 { val1 } else { val2 };
31 }
32 return val1;
33 }
34 if val2.is_sign_negative() {
35 val1
36 } else {
37 val2
38 }
39}
40
41#[inline]
43pub fn min_f32(val1: f32, val2: f32) -> f32 {
44 if val1 != val2 {
45 if !val1.is_nan() {
46 return if val1 < val2 { val1 } else { val2 };
47 }
48 return val1;
49 }
50 if val1.is_sign_negative() {
51 val1
52 } else {
53 val2
54 }
55}
56
57#[inline]
59pub fn max_f32(val1: f32, val2: f32) -> f32 {
60 if val1 != val2 {
61 if !val1.is_nan() {
62 return if val2 < val1 { val1 } else { val2 };
63 }
64 return val1;
65 }
66 if val2.is_sign_negative() {
67 val1
68 } else {
69 val2
70 }
71}
72
73#[inline]
75pub fn equals(a: f64, b: f64) -> bool {
76 a == b || (a.is_nan() && b.is_nan())
77}
78
79pub struct MathUtilities;
81
82impl MathUtilities {
83 pub const DOUBLE_EPSILON: f64 = f64::EPSILON; pub const FLOAT_EPSILON: f32 = f32::EPSILON; #[inline]
91 pub fn are_close(value1: f64, value2: f64) -> bool {
92 if value1 == value2 {
94 return true;
95 }
96 let eps = (value1.abs() + value2.abs() + 10.0) * Self::DOUBLE_EPSILON;
97 let delta = value1 - value2;
98 (-eps < delta) && (eps > delta)
99 }
100
101 #[inline]
103 pub fn are_close_eps(value1: f64, value2: f64, eps: f64) -> bool {
104 if value1 == value2 {
106 return true;
107 }
108 let delta = value1 - value2;
109 (-eps < delta) && (eps > delta)
110 }
111
112 #[inline]
114 pub fn are_close_f32(value1: f32, value2: f32) -> bool {
115 if value1 == value2 {
117 return true;
118 }
119 let eps = (value1.abs() + value2.abs() + 10.0) * Self::FLOAT_EPSILON;
120 let delta = value1 - value2;
121 (-eps < delta) && (eps > delta)
122 }
123
124 #[inline]
126 pub fn less_than(value1: f64, value2: f64) -> bool {
127 (value1 < value2) && !Self::are_close(value1, value2)
128 }
129
130 #[inline]
132 pub fn less_than_f32(value1: f32, value2: f32) -> bool {
133 (value1 < value2) && !Self::are_close_f32(value1, value2)
134 }
135
136 #[inline]
138 pub fn greater_than(value1: f64, value2: f64) -> bool {
139 (value1 > value2) && !Self::are_close(value1, value2)
140 }
141
142 #[inline]
144 pub fn greater_than_f32(value1: f32, value2: f32) -> bool {
145 (value1 > value2) && !Self::are_close_f32(value1, value2)
146 }
147
148 #[inline]
150 pub fn less_than_or_close(value1: f64, value2: f64) -> bool {
151 (value1 < value2) || Self::are_close(value1, value2)
152 }
153
154 #[inline]
156 pub fn less_than_or_close_f32(value1: f32, value2: f32) -> bool {
157 (value1 < value2) || Self::are_close_f32(value1, value2)
158 }
159
160 #[inline]
162 pub fn greater_than_or_close(value1: f64, value2: f64) -> bool {
163 (value1 > value2) || Self::are_close(value1, value2)
164 }
165
166 #[inline]
168 pub fn greater_than_or_close_f32(value1: f32, value2: f32) -> bool {
169 (value1 > value2) || Self::are_close_f32(value1, value2)
170 }
171
172 #[inline]
174 pub fn is_one(value: f64) -> bool {
175 (value - 1.0).abs() < 10.0 * Self::DOUBLE_EPSILON
176 }
177
178 #[inline]
180 pub fn is_one_f32(value: f32) -> bool {
181 (value - 1.0).abs() < 10.0 * Self::FLOAT_EPSILON
182 }
183
184 #[inline]
186 pub fn is_zero(value: f64) -> bool {
187 value.abs() < 10.0 * Self::DOUBLE_EPSILON
188 }
189
190 #[inline]
192 pub fn is_zero_f32(value: f32) -> bool {
193 value.abs() < 10.0 * Self::FLOAT_EPSILON
194 }
195
196 #[inline]
201 pub fn clamp(val: f64, min: f64, max: f64) -> f64 {
202 if min > max {
203 throw_cannot_be_greater_than(min, max);
204 }
205
206 if val < min {
207 min
208 } else if val > max {
209 max
210 } else {
211 val
212 }
213 }
214
215 #[inline]
218 pub fn clamp_f32(value: f32, min: f32, max: f32) -> f32 {
219 let amax = max_f32(min, max);
220 let amin = min_f32(min, max);
221 min_f32(max_f32(value, amin), amax)
222 }
223
224 #[inline]
229 pub fn clamp_i32(val: i32, min: i32, max: i32) -> i32 {
230 if min > max {
231 throw_cannot_be_greater_than(min, max);
232 }
233
234 if val < min {
235 min
236 } else if val > max {
237 max
238 } else {
239 val
240 }
241 }
242
243 #[inline]
248 pub fn clamp_decimal(val: Decimal, min: Decimal, max: Decimal) -> Decimal {
249 if min > max {
250 throw_cannot_be_greater_than(min, max);
251 }
252
253 if val < min {
254 min
255 } else if val > max {
256 max
257 } else {
258 val
259 }
260 }
261
262 #[inline]
264 pub fn deg2rad(angle: f64) -> f64 {
265 angle * (std::f64::consts::PI / 180.0)
266 }
267
268 #[inline]
270 pub fn grad2rad(angle: f64) -> f64 {
271 angle * (std::f64::consts::PI / 200.0)
272 }
273
274 #[inline]
276 pub fn turn2rad(angle: f64) -> f64 {
277 angle * 2.0 * std::f64::consts::PI
278 }
279
280 #[inline]
282 pub fn get_ellipse_point(centre: Point, radius_x: f64, radius_y: f64, angle: f64) -> Point {
283 Point::new(
284 radius_x * angle.cos() + centre.x,
285 radius_y * angle.sin() + centre.y,
286 )
287 }
288
289 #[inline]
291 pub fn get_min_max(a: f64, b: f64) -> (f64, f64) {
292 if a < b {
293 (a, b)
294 } else {
295 (b, a)
296 }
297 }
298
299 #[inline]
301 pub fn get_min_max_from_delta(initial_value: f64, delta: f64) -> (f64, f64) {
302 Self::get_min_max(initial_value, initial_value + delta)
303 }
304
305 #[inline]
307 pub fn is_negative_or_non_finite(d: f64) -> bool {
308 d.to_bits() >= 0x7FF0_0000_0000_0000
309 }
310
311 #[inline]
312 pub fn is_finite(d: f64) -> bool {
313 d.is_finite()
314 }
315
316 pub fn which_polygon_side_intersects(
320 c_poly: u32,
321 p_pt_poly: &[Vector],
322 pt_current: Vector,
323 vec_edge: Vector,
324 ) -> i32 {
325 let mut n_positive = 0u32;
326 let mut n_negative = 0u32;
327 let mut n_zero = 0u32;
328
329 let vec_edge_normal = Vector::new(-vec_edge.y, vec_edge.x);
330
331 for pt in &p_pt_poly[..c_poly as usize] {
332 let vec_current = pt_current - *pt;
333 let r_dot = Vector::dot(vec_current, vec_edge_normal);
334
335 if r_dot > 0.0 {
336 n_positive += 1;
337 } else if r_dot < 0.0 {
338 n_negative += 1;
339 } else {
340 n_zero += 1;
341 }
342
343 if (n_positive > 0 && n_negative > 0) || (n_zero > 0) {
344 return 0;
345 }
346 }
347
348 if n_positive > 0 {
349 1
350 } else {
351 -1
352 }
353 }
354
355 pub fn do_polygons_intersect(
357 c_poly_a: u32,
358 p_pt_poly_a: &[Vector],
359 c_poly_b: u32,
360 p_pt_poly_b: &[Vector],
361 ) -> bool {
362 let (na, nb) = (c_poly_a as usize, c_poly_b as usize);
363
364 for i in 0..na {
365 let vec_edge = p_pt_poly_a[(i + 1) % na] - p_pt_poly_a[i];
366 if Self::which_polygon_side_intersects(c_poly_b, p_pt_poly_b, p_pt_poly_a[i], vec_edge)
367 < 0
368 {
369 return false;
370 }
371 }
372
373 for i in 0..nb {
374 let vec_edge = p_pt_poly_b[(i + 1) % nb] - p_pt_poly_b[i];
375 if Self::which_polygon_side_intersects(c_poly_a, p_pt_poly_a, p_pt_poly_b[i], vec_edge)
376 < 0
377 {
378 return false;
379 }
380 }
381
382 true
383 }
384
385 pub fn is_entirely_contained(
387 c_poly_a: u32,
388 p_pt_poly_a: &[Vector],
389 c_poly_b: u32,
390 p_pt_poly_b: &[Vector],
391 ) -> bool {
392 let nb = c_poly_b as usize;
393
394 for i in 0..nb {
395 let vec_edge = p_pt_poly_b[(i + 1) % nb] - p_pt_poly_b[i];
396 if Self::which_polygon_side_intersects(c_poly_a, p_pt_poly_a, p_pt_poly_b[i], vec_edge)
397 <= 0
398 {
399 return false;
402 }
403 }
404
405 true
406 }
407}
408
409#[cold]
410#[inline(never)]
411fn throw_cannot_be_greater_than<T: std::fmt::Display>(min: T, max: T) -> ! {
412 panic!("{min} cannot be greater than {max}.");
413}
414
415#[cfg(test)]
416mod tests {
417 use super::*;
418
419 const ANY_VALUE: f64 = 42.42;
420
421 struct Fixture {
422 calculated_any_value: f64,
423 one: f64,
424 zero: f64,
425 }
426
427 fn fixture() -> Fixture {
428 let mut calculated_any_value = 0.0;
429 let mut one = 0.0;
430 let mut zero = 1.0;
431
432 const N: i32 = 10;
433 let dx_any = ANY_VALUE / N as f64;
434 let dx_one = 1.0 / N as f64;
435 let dx_zero = zero / N as f64;
436
437 for _ in 0..N {
438 calculated_any_value += dx_any;
439 one += dx_one;
440 zero -= dx_zero;
441 }
442
443 Fixture {
444 calculated_any_value,
445 one,
446 zero,
447 }
448 }
449
450 fn round(value: f64, digits: i32) -> f64 {
452 let power = 10f64.powi(digits);
453 (value * power).round_ties_even() / power
454 }
455
456 #[test]
457 fn two_equivalent_double_values_are_close() {
458 let f = fixture();
459 let actual = MathUtilities::are_close(ANY_VALUE, f.calculated_any_value);
460
461 assert!(actual);
462 assert_eq!(ANY_VALUE, round(f.calculated_any_value, 14));
463 }
464
465 #[test]
466 fn two_equivalent_single_values_are_close() {
467 let f = fixture();
468 let expected_value = ANY_VALUE as f32;
469 let actual_value = f.calculated_any_value as f32;
470
471 let actual = MathUtilities::are_close_f32(expected_value, actual_value);
472
473 assert!(actual);
474 assert_eq!(
475 round(expected_value as f64, 5) as f32,
476 round(actual_value as f64, 4) as f32
477 );
478 }
479
480 #[test]
481 fn calculated_double_one_is_one() {
482 let f = fixture();
483 let actual = MathUtilities::is_one(f.one);
484
485 assert!(actual);
486 assert_eq!(1.0, round(f.one, 15));
487 }
488
489 #[test]
490 fn calculated_single_one_is_one() {
491 let f = fixture();
492 let actual_value = f.one as f32;
493
494 let actual = MathUtilities::is_one_f32(actual_value);
495
496 assert!(actual);
497 assert_eq!(1.0f32, round(actual_value as f64, 7) as f32);
498 }
499
500 #[test]
501 fn calculated_double_zero_is_zero() {
502 let f = fixture();
503 let actual = MathUtilities::is_zero(f.zero);
504
505 assert!(actual);
506 assert_eq!(0.0, round(f.zero, 15));
507 }
508
509 #[test]
510 fn calculated_single_zero_is_zero() {
511 let f = fixture();
512 let actual_value = f.zero as f32;
513
514 let actual = MathUtilities::is_zero_f32(actual_value);
515
516 assert!(actual);
517 assert_eq!(0.0f32, round(actual_value as f64, 7) as f32);
518 }
519
520 #[test]
521 fn float_clamp_input_nan_return_nan() {
522 let clamp = MathUtilities::clamp(f64::NAN, 0.0, 1.0);
523 assert!(clamp.is_nan());
524 }
525
526 #[test]
527 fn float_clamp_input_negative_infinity_return_min() {
528 const MIN: f64 = 0.0;
529 const MAX: f64 = 1.0;
530
531 let actual = MathUtilities::clamp(f64::NEG_INFINITY, MIN, MAX);
532 assert_eq!(MIN, actual);
533 }
534
535 #[test]
536 fn float_clamp_input_positive_infinity_return_max() {
537 const MIN: f64 = 0.0;
538 const MAX: f64 = 1.0;
539
540 let actual = MathUtilities::clamp(f64::INFINITY, MIN, MAX);
541 assert_eq!(MAX, actual);
542 }
543
544 #[test]
545 fn double_float_zero_less_than_one() {
546 assert!(MathUtilities::less_than(0.0, 1.0));
547 }
548
549 #[test]
550 fn single_float_zero_less_than_one() {
551 assert!(MathUtilities::less_than_f32(0.0, 1.0));
552 }
553
554 #[test]
555 fn double_float_one_not_less_than_zero() {
556 assert!(!MathUtilities::less_than(1.0, 0.0));
557 }
558
559 #[test]
560 fn single_float_one_not_less_than_zero() {
561 assert!(!MathUtilities::less_than_f32(1.0, 0.0));
562 }
563
564 #[test]
565 fn double_float_zero_not_greater_than_one() {
566 assert!(!MathUtilities::greater_than(0.0, 1.0));
567 }
568
569 #[test]
570 fn single_float_zero_not_greater_than_one() {
571 assert!(!MathUtilities::greater_than_f32(0.0, 1.0));
572 }
573
574 #[test]
575 fn double_float_one_greater_than_zero() {
576 assert!(MathUtilities::greater_than(1.0, 0.0));
577 }
578
579 #[test]
580 fn single_float_one_greater_than_zero() {
581 assert!(MathUtilities::greater_than_f32(1.0, 0.0));
582 }
583
584 #[test]
585 fn double_float_one_less_than_or_close_one() {
586 assert!(MathUtilities::less_than_or_close(1.0, 1.0));
587 }
588
589 #[test]
590 fn single_float_one_less_than_or_close_one() {
591 assert!(MathUtilities::less_than_or_close_f32(1.0, 1.0));
592 }
593
594 #[test]
595 fn double_float_one_greater_than_or_close_one() {
596 assert!(MathUtilities::greater_than_or_close(1.0, 1.0));
597 }
598
599 #[test]
600 fn single_float_one_greater_than_or_close_one() {
601 assert!(MathUtilities::greater_than_or_close_f32(1.0, 1.0));
602 }
603
604 #[test]
607 fn min_max_propagate_nan_and_order_zeros() {
608 assert!(min(f64::NAN, 1.0).is_nan());
609 assert!(min(1.0, f64::NAN).is_nan());
610 assert!(max(f64::NAN, 1.0).is_nan());
611 assert!(max(1.0, f64::NAN).is_nan());
612 assert!(min(0.0, -0.0).is_sign_negative());
613 assert!(max(-0.0, 0.0).is_sign_positive());
614 assert_eq!(min(1.0, 2.0), 1.0);
615 assert_eq!(max(1.0, 2.0), 2.0);
616 }
617
618 #[test]
619 fn negative_or_non_finite() {
620 assert!(MathUtilities::is_negative_or_non_finite(-1.0));
621 assert!(MathUtilities::is_negative_or_non_finite(-0.0));
622 assert!(MathUtilities::is_negative_or_non_finite(f64::NAN));
623 assert!(MathUtilities::is_negative_or_non_finite(f64::INFINITY));
624 assert!(!MathUtilities::is_negative_or_non_finite(0.0));
625 assert!(!MathUtilities::is_negative_or_non_finite(1.0));
626 }
627
628 #[test]
629 fn polygon_intersection() {
630 let square = |x: f64, y: f64, s: f64| {
631 [
632 Vector::new(x, y),
633 Vector::new(x, y + s),
634 Vector::new(x + s, y + s),
635 Vector::new(x + s, y),
636 ]
637 };
638 let a = square(0.0, 0.0, 10.0);
639 let b = square(5.0, 5.0, 10.0);
640 let c = square(20.0, 20.0, 5.0);
641 let d = square(2.0, 2.0, 2.0);
642
643 assert!(MathUtilities::do_polygons_intersect(4, &a, 4, &b));
644 assert!(!MathUtilities::do_polygons_intersect(4, &a, 4, &c));
645 assert!(MathUtilities::is_entirely_contained(4, &d, 4, &a));
646 assert!(!MathUtilities::is_entirely_contained(4, &b, 4, &a));
647 }
648
649 #[test]
651 fn decimal_clamp_returns_the_value_or_the_nearest_bound() {
652 let decimal = |text: &str| Decimal::parse(text).expect("a decimal");
653 let (min, max) = (decimal("-1.5"), decimal("2.5"));
654
655 assert_eq!(decimal("1"), MathUtilities::clamp_decimal(decimal("1"), min, max));
656 assert_eq!(min, MathUtilities::clamp_decimal(decimal("-2"), min, max));
657 assert_eq!(max, MathUtilities::clamp_decimal(decimal("3"), min, max));
658 }
659
660 #[test]
662 #[should_panic(expected = "cannot be greater than")]
663 fn decimal_clamp_throws_when_min_is_greater_than_max() {
664 MathUtilities::clamp_decimal(Decimal::ZERO, Decimal::ONE, Decimal::ZERO);
665 }
666}