zng_unit/
distance_key.rs

1use crate::{Px, PxPoint};
2
3use serde::{Deserialize, Serialize};
4
5/// Comparable key that represents the absolute distance between two pixel points.
6///
7/// Computing the actual distance only for comparison is expensive, this key avoids the conversion to float and square-root operation.
8#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, bytemuck::Zeroable, bytemuck::Pod)]
9#[repr(transparent)]
10#[serde(transparent)]
11pub struct DistanceKey(u64);
12impl DistanceKey {
13    /// Value that is always greater than any distance key.
14    pub const NONE_MAX: DistanceKey = DistanceKey(u64::MAX);
15
16    /// Value that is always smaller than any distance key.
17    pub const NONE_MIN: DistanceKey = DistanceKey(0);
18
19    /// Maximum distance.
20    pub const MAX: DistanceKey = DistanceKey((Px::MAX.0 as u64).pow(2));
21
22    /// Minimum distance.
23    pub const MIN: DistanceKey = DistanceKey(1);
24
25    /// New distance key computed from two points.
26    pub fn from_points(a: PxPoint, b: PxPoint) -> Self {
27        let pa = ((a.x - b.x).0.unsigned_abs() as u64).pow(2);
28        let pb = ((a.y - b.y).0.unsigned_abs() as u64).pow(2);
29
30        Self((pa + pb) + 1)
31    }
32
33    /// New distance key from already computed actual distance.
34    ///
35    /// Note that computing the actual distance is slower then using [`from_points`] to compute just the distance key.
36    ///
37    /// [`from_points`]: Self::from_points
38    pub fn from_distance(d: Px) -> Self {
39        let p = (d.0.unsigned_abs() as u64).pow(2);
40        Self(p + 1)
41    }
42
43    /// If the key is the [`NONE_MAX`] or [`NONE_MIN`].
44    ///
45    /// [`NONE_MAX`]: Self::NONE_MAX
46    /// [`NONE_MIN`]: Self::NONE_MIN
47    pub fn is_none(self) -> bool {
48        self == Self::NONE_MAX || self == Self::NONE_MIN
49    }
50
51    /// Completes the distance calculation.
52    pub fn distance(self) -> Option<Px> {
53        if self.is_none() {
54            None
55        } else {
56            let p = self.0 - 1;
57            let d = (p as f64).sqrt();
58
59            Some(Px(d.round() as i32))
60        }
61    }
62}