style/values/
distance.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5//! Machinery to compute distances between animatable values.
6
7use app_units::Au;
8use euclid::default::Size2D;
9use std::iter::Sum;
10use std::ops::Add;
11
12/// A trait to compute squared distances between two animatable values.
13///
14/// This trait is derivable with `#[derive(ComputeSquaredDistance)]`. The derived
15/// implementation uses a `match` expression with identical patterns for both
16/// `self` and `other`, calling `ComputeSquaredDistance::compute_squared_distance`
17/// on each fields of the values.
18///
19/// If a variant is annotated with `#[animation(error)]`, the corresponding
20/// `match` arm returns an error.
21///
22/// Trait bounds for type parameter `Foo` can be opted out of with
23/// `#[animation(no_bound(Foo))]` on the type definition, trait bounds for
24/// fields can be opted into with `#[distance(field_bound)]` on the field.
25pub trait ComputeSquaredDistance {
26    /// Computes the squared distance between two animatable values.
27    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()>;
28}
29
30/// A distance between two animatable values.
31#[derive(Add, Clone, Copy, Debug, From, PartialEq, PartialOrd)]
32pub struct SquaredDistance {
33    value: f64,
34}
35
36impl SquaredDistance {
37    /// Returns a squared distance from its square root.
38    #[inline]
39    pub fn from_sqrt(sqrt: f64) -> Self {
40        Self { value: sqrt * sqrt }
41    }
42}
43
44impl ComputeSquaredDistance for u16 {
45    #[inline]
46    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
47        Ok(SquaredDistance::from_sqrt(
48            ((*self as f64) - (*other as f64)).abs(),
49        ))
50    }
51}
52
53impl ComputeSquaredDistance for i16 {
54    #[inline]
55    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
56        Ok(SquaredDistance::from_sqrt((*self - *other).abs() as f64))
57    }
58}
59
60impl ComputeSquaredDistance for i32 {
61    #[inline]
62    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
63        Ok(SquaredDistance::from_sqrt((*self - *other).abs() as f64))
64    }
65}
66
67impl ComputeSquaredDistance for f32 {
68    #[inline]
69    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
70        Ok(SquaredDistance::from_sqrt((*self - *other).abs() as f64))
71    }
72}
73
74impl ComputeSquaredDistance for f64 {
75    #[inline]
76    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
77        Ok(SquaredDistance::from_sqrt((*self - *other).abs()))
78    }
79}
80
81impl ComputeSquaredDistance for Au {
82    #[inline]
83    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
84        self.0.compute_squared_distance(&other.0)
85    }
86}
87
88impl<T> ComputeSquaredDistance for Box<T>
89where
90    T: ComputeSquaredDistance,
91{
92    #[inline]
93    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
94        (**self).compute_squared_distance(&**other)
95    }
96}
97
98impl<T> ComputeSquaredDistance for Option<T>
99where
100    T: ComputeSquaredDistance,
101{
102    #[inline]
103    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
104        match (self.as_ref(), other.as_ref()) {
105            (Some(this), Some(other)) => this.compute_squared_distance(other),
106            (None, None) => Ok(SquaredDistance::from_sqrt(0.)),
107            _ => Err(()),
108        }
109    }
110}
111
112impl<T> ComputeSquaredDistance for Size2D<T>
113where
114    T: ComputeSquaredDistance,
115{
116    #[inline]
117    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
118        Ok(self.width.compute_squared_distance(&other.width)?
119            + self.height.compute_squared_distance(&other.height)?)
120    }
121}
122
123impl SquaredDistance {
124    /// Returns the square root of this squared distance.
125    #[inline]
126    pub fn sqrt(self) -> f64 {
127        self.value.sqrt()
128    }
129}
130
131impl Sum for SquaredDistance {
132    fn sum<I>(iter: I) -> Self
133    where
134        I: Iterator<Item = Self>,
135    {
136        iter.fold(SquaredDistance::from_sqrt(0.), Add::add)
137    }
138}