zng_unit/
distance_key.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
use crate::{Px, PxPoint};

use serde::{Deserialize, Serialize};

/// Comparable key that represents the absolute distance between two pixel points.
///
/// Computing the actual distance only for comparison is expensive, this key avoids the conversion to float and square-root operation.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, bytemuck::Zeroable, bytemuck::Pod)]
#[repr(transparent)]
#[serde(transparent)]
pub struct DistanceKey(u64);
impl DistanceKey {
    /// Value that is always greater than any distance key.
    pub const NONE_MAX: DistanceKey = DistanceKey(u64::MAX);

    /// Value that is always smaller than any distance key.
    pub const NONE_MIN: DistanceKey = DistanceKey(0);

    /// Maximum distance.
    pub const MAX: DistanceKey = DistanceKey((Px::MAX.0 as u64).pow(2));

    /// Minimum distance.
    pub const MIN: DistanceKey = DistanceKey(1);

    /// New distance key computed from two points.
    pub fn from_points(a: PxPoint, b: PxPoint) -> Self {
        let pa = ((a.x - b.x).0.unsigned_abs() as u64).pow(2);
        let pb = ((a.y - b.y).0.unsigned_abs() as u64).pow(2);

        Self((pa + pb) + 1)
    }

    /// New distance key from already computed actual distance.
    ///
    /// Note that computing the actual distance is slower then using [`from_points`] to compute just the distance key.
    ///
    /// [`from_points`]: Self::from_points
    pub fn from_distance(d: Px) -> Self {
        let p = (d.0.unsigned_abs() as u64).pow(2);
        Self(p + 1)
    }

    /// If the key is the [`NONE_MAX`] or [`NONE_MIN`].
    ///
    /// [`NONE_MAX`]: Self::NONE_MAX
    /// [`NONE_MIN`]: Self::NONE_MIN
    pub fn is_none(self) -> bool {
        self == Self::NONE_MAX || self == Self::NONE_MIN
    }

    /// Completes the distance calculation.
    pub fn distance(self) -> Option<Px> {
        if self.is_none() {
            None
        } else {
            let p = self.0 - 1;
            let d = (p as f64).sqrt();

            Some(Px(d.round() as i32))
        }
    }
}