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