Skip to main content

bymsdfgen_core/math/
signed_distance.rs

1//! Signed distance with an alignment tiebreaker.
2//!
3//! Port of `core/SignedDistance.hpp`. The ordering compares by `|distance|` first
4//! and uses `dot` (cosine of the angle to the nearest edge) only to break exact
5//! ties, exactly as the original operators do.
6
7use std::cmp::Ordering;
8
9/// A signed distance together with the dot-product alignment used to
10/// disambiguate which edge is genuinely closest at corners.
11#[derive(Debug, Clone, Copy)]
12pub struct SignedDistance {
13    pub distance: f64,
14    pub dot: f64,
15}
16
17impl Default for SignedDistance {
18    #[inline]
19    fn default() -> Self {
20        // Matches the C++ default constructor: distance = -DBL_MAX, dot = 0.
21        SignedDistance {
22            distance: f64::MIN,
23            dot: 0.0,
24        }
25    }
26}
27
28impl SignedDistance {
29    #[inline]
30    pub const fn new(distance: f64, dot: f64) -> Self {
31        SignedDistance { distance, dot }
32    }
33
34    /// Total order on `(|distance|, dot)` — "closer" sorts as `Less`.
35    #[inline]
36    pub fn cmp_key(self, other: SignedDistance) -> Ordering {
37        let a = self.distance.abs();
38        let b = other.distance.abs();
39        match a.partial_cmp(&b) {
40            Some(Ordering::Equal) | None => {
41                self.dot.partial_cmp(&other.dot).unwrap_or(Ordering::Equal)
42            }
43            Some(ord) => ord,
44        }
45    }
46
47    #[inline]
48    pub fn is_closer_than(self, other: SignedDistance) -> bool {
49        self.cmp_key(other) == Ordering::Less
50    }
51}
52
53#[cfg(test)]
54mod tests {
55    use super::*;
56
57    #[test]
58    fn closer_by_magnitude() {
59        let a = SignedDistance::new(-1.0, 0.5);
60        let b = SignedDistance::new(2.0, 0.0);
61        assert!(a.is_closer_than(b));
62        assert!(!b.is_closer_than(a));
63    }
64
65    #[test]
66    fn tiebreak_by_dot() {
67        let a = SignedDistance::new(1.0, 0.2);
68        let b = SignedDistance::new(-1.0, 0.9);
69        // equal magnitude -> smaller dot wins
70        assert!(a.is_closer_than(b));
71    }
72}