1 // Copyright 2014 The Servo Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution.
3 //
4 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7 // option. This file may not be copied, modified, or distributed
8 // except according to those terms.
9 //! A type-checked scaling factor between units.
10 
11 use num::One;
12 
13 use num_traits::NumCast;
14 #[cfg(feature = "serde")]
15 use serde;
16 use core::fmt;
17 use core::ops::{Add, Div, Mul, Neg, Sub};
18 use core::hash::{Hash, Hasher};
19 use core::marker::PhantomData;
20 use core::cmp::Ordering;
21 use {Point2D, Rect, Size2D, Vector2D};
22 
23 /// A scaling factor between two different units of measurement.
24 ///
25 /// This is effectively a type-safe float, intended to be used in combination with other types like
26 /// `length::Length` to enforce conversion between systems of measurement at compile time.
27 ///
28 /// `Src` and `Dst` represent the units before and after multiplying a value by a `Scale`. They
29 /// may be types without values, such as empty enums.  For example:
30 ///
31 /// ```rust
32 /// use euclid::Scale;
33 /// use euclid::Length;
34 /// enum Mm {};
35 /// enum Inch {};
36 ///
37 /// let mm_per_inch: Scale<f32, Inch, Mm> = Scale::new(25.4);
38 ///
39 /// let one_foot: Length<f32, Inch> = Length::new(12.0);
40 /// let one_foot_in_mm: Length<f32, Mm> = one_foot * mm_per_inch;
41 /// ```
42 #[repr(C)]
43 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
44 #[cfg_attr(feature = "serde", serde(bound(serialize = "T: serde::Serialize", deserialize = "T: serde::Deserialize<'de>")))]
45 pub struct Scale<T, Src, Dst>(pub T, #[doc(hidden)] pub PhantomData<(Src, Dst)>);
46 
47 impl<T, Src, Dst> Scale<T, Src, Dst> {
48     #[inline]
new(x: T) -> Self49     pub const fn new(x: T) -> Self {
50         Scale(x, PhantomData)
51     }
52 
53     /// Returns the given point transformed by this scale.
54     ///
55     /// # Example
56     ///
57     /// ```rust
58     /// use euclid::{Scale, point2};
59     /// enum Mm {};
60     /// enum Cm {};
61     ///
62     /// let to_mm: Scale<i32, Cm, Mm> = Scale::new(10);
63     ///
64     /// assert_eq!(to_mm.transform_point(point2(42, -42)), point2(420, -420));
65     /// ```
66     #[inline]
transform_point(&self, point: Point2D<T, Src>) -> Point2D<T::Output, Dst> where T: Clone + Mul67     pub fn transform_point(&self, point: Point2D<T, Src>) -> Point2D<T::Output, Dst>
68         where T: Clone + Mul
69     {
70         Point2D::new(point.x * self.get(), point.y * self.get())
71     }
72 
73     /// Returns the given vector transformed by this scale.
74     ///
75     /// # Example
76     ///
77     /// ```rust
78     /// use euclid::{Scale, vec2};
79     /// enum Mm {};
80     /// enum Cm {};
81     ///
82     /// let to_mm: Scale<i32, Cm, Mm> = Scale::new(10);
83     ///
84     /// assert_eq!(to_mm.transform_vector(vec2(42, -42)), vec2(420, -420));
85     /// ```
86     #[inline]
transform_vector(&self, vec: Vector2D<T, Src>) -> Vector2D<T::Output, Dst> where T: Clone + Mul87     pub fn transform_vector(&self, vec: Vector2D<T, Src>) -> Vector2D<T::Output, Dst>
88         where T: Clone + Mul
89     {
90         Vector2D::new(vec.x * self.get(), vec.y * self.get())
91     }
92 
93     /// Returns the given vector transformed by this scale.
94     ///
95     /// # Example
96     ///
97     /// ```rust
98     /// use euclid::{Scale, size2};
99     /// enum Mm {};
100     /// enum Cm {};
101     ///
102     /// let to_mm: Scale<i32, Cm, Mm> = Scale::new(10);
103     ///
104     /// assert_eq!(to_mm.transform_size(size2(42, -42)), size2(420, -420));
105     /// ```
106     #[inline]
transform_size(&self, size: Size2D<T, Src>) -> Size2D<T::Output, Dst> where T: Clone + Mul107     pub fn transform_size(&self, size: Size2D<T, Src>) -> Size2D<T::Output, Dst>
108         where T: Clone + Mul
109     {
110         Size2D::new(size.width * self.get(), size.height * self.get())
111     }
112 
113     /// Returns the given rect transformed by this scale.
114     ///
115     /// # Example
116     ///
117     /// ```rust
118     /// use euclid::{Scale, rect};
119     /// enum Mm {};
120     /// enum Cm {};
121     ///
122     /// let to_mm: Scale<i32, Cm, Mm> = Scale::new(10);
123     ///
124     /// assert_eq!(to_mm.transform_rect(&rect(1, 2, 42, -42)), rect(10, 20, 420, -420));
125     /// ```
126     #[inline]
transform_rect(&self, rect: &Rect<T, Src>) -> Rect<T::Output, Dst> where T: Copy + Mul127     pub fn transform_rect(&self, rect: &Rect<T, Src>) -> Rect<T::Output, Dst>
128         where T: Copy + Mul
129     {
130         Rect::new(
131             self.transform_point(rect.origin),
132             self.transform_size(rect.size),
133         )
134     }
135 
136     /// Returns the inverse of this scale.
137     #[inline]
inverse(&self) -> Scale<T::Output, Dst, Src> where T: Clone + Neg138     pub fn inverse(&self) -> Scale<T::Output, Dst, Src>
139         where T: Clone + Neg
140     {
141         Scale::new(-self.get())
142     }
143 
144     /// Returns `true` if this scale has no effect.
145     ///
146     /// # Example
147     ///
148     /// ```rust
149     /// use euclid::Scale;
150     /// use euclid::num::One;
151     /// enum Mm {};
152     /// enum Cm {};
153     ///
154     /// let cm_per_mm: Scale<f32, Mm, Cm> = Scale::new(0.1);
155     /// let mm_per_mm: Scale<f32, Mm, Mm> = Scale::new(1.0);
156     ///
157     /// assert_eq!(cm_per_mm.is_identity(), false);
158     /// assert_eq!(mm_per_mm.is_identity(), true);
159     /// assert_eq!(mm_per_mm, Scale::one());
160     /// ```
161     #[inline]
is_identity(&self) -> bool where T: PartialEq + One162     pub fn is_identity(&self) -> bool
163         where T: PartialEq + One
164     {
165         self.0 == T::one()
166     }
167 }
168 
169 impl<T: Clone, Src, Dst> Scale<T, Src, Dst> {
170     #[inline]
get(&self) -> T171     pub fn get(&self) -> T {
172         self.0.clone()
173     }
174 
175     /// The inverse Scale (1.0 / self).
176     ///
177     /// # Example
178     ///
179     /// ```rust
180     /// use euclid::Scale;
181     /// enum Mm {};
182     /// enum Cm {};
183     ///
184     /// let cm_per_mm: Scale<f32, Cm, Mm> = Scale::new(0.1);
185     ///
186     /// assert_eq!(cm_per_mm.inv(), Scale::new(10.0));
187     /// ```
inv(&self) -> Scale<T::Output, Dst, Src> where T: One + Div188     pub fn inv(&self) -> Scale<T::Output, Dst, Src>
189     where
190         T: One + Div
191     {
192         let one: T = One::one();
193         Scale::new(one / self.0.clone())
194     }
195 }
196 
197 impl<T: NumCast + Clone, Src, Dst> Scale<T, Src, Dst> {
198     /// Cast from one numeric representation to another, preserving the units.
199     ///
200     /// # Panics
201     ///
202     /// If the source value cannot be represented by the target type `NewT`, then
203     /// method panics. Use `try_cast` if that must be case.
204     ///
205     /// # Example
206     ///
207     /// ```rust
208     /// use euclid::Scale;
209     /// enum Mm {};
210     /// enum Cm {};
211     ///
212     /// let to_mm: Scale<i32, Cm, Mm> = Scale::new(10);
213     ///
214     /// assert_eq!(to_mm.cast::<f32>(), Scale::new(10.0));
215     /// ```
216     /// That conversion will panic, because `i32` not enough to store such big numbers:
217     /// ```rust,should_panic
218     /// use euclid::Scale;
219     /// enum Mm {};// millimeter = 10^-2 meters
220     /// enum Em {};// exameter   = 10^18 meters
221     ///
222     /// // Panics
223     /// let to_em: Scale<i32, Mm, Em> = Scale::new(10e20).cast();
224     /// ```
225     #[inline]
cast<NewT: NumCast>(&self) -> Scale<NewT, Src, Dst>226     pub fn cast<NewT: NumCast>(&self) -> Scale<NewT, Src, Dst> {
227         self.try_cast().unwrap()
228     }
229 
230     /// Fallible cast from one numeric representation to another, preserving the units.
231     /// If the source value cannot be represented by the target type `NewT`, then `None`
232     /// is returned.
233     ///
234     /// # Example
235     ///
236     /// ```rust
237     /// use euclid::Scale;
238     /// enum Mm {};
239     /// enum Cm {};
240     /// enum Em {};// Exameter = 10^18 meters
241     ///
242     /// let to_mm: Scale<i32, Cm, Mm> = Scale::new(10);
243     /// let to_em: Scale<f32, Mm, Em> = Scale::new(10e20);
244     ///
245     /// assert_eq!(to_mm.try_cast::<f32>(), Some(Scale::new(10.0)));
246     /// // Integer to small to store that number
247     /// assert_eq!(to_em.try_cast::<i32>(), None);
248     /// ```
try_cast<NewT: NumCast>(&self) -> Option<Scale<NewT, Src, Dst>>249     pub fn try_cast<NewT: NumCast>(&self) -> Option<Scale<NewT, Src, Dst>> {
250         NumCast::from(self.get()).map(Scale::new)
251     }
252 }
253 
254 impl<Src, Dst> Scale<f32, Src, Dst> {
255     /// Identity scaling, could be used to safely transit from one space to another.
256     pub const ONE: Self = Scale(1.0, PhantomData);
257 }
258 
259 
260 // scale0 * scale1
261 // (A,B) * (B,C) = (A,C)
262 impl<T: Mul, A, B, C> Mul<Scale<T, B, C>> for Scale<T, A, B> {
263     type Output = Scale<T::Output, A, C>;
264 
265     #[inline]
mul(self, other: Scale<T, B, C>) -> Self::Output266     fn mul(self, other: Scale<T, B, C>) -> Self::Output {
267         Scale::new(self.0 * other.0)
268     }
269 }
270 
271 // scale0 + scale1
272 impl<T: Add, Src, Dst> Add for Scale<T, Src, Dst> {
273     type Output = Scale<T::Output, Src, Dst>;
274 
275     #[inline]
add(self, other: Scale<T, Src, Dst>) -> Self::Output276     fn add(self, other: Scale<T, Src, Dst>) -> Self::Output {
277         Scale::new(self.0 + other.0)
278     }
279 }
280 
281 // scale0 - scale1
282 impl<T: Sub, Src, Dst> Sub for Scale<T, Src, Dst> {
283     type Output = Scale<T::Output, Src, Dst>;
284 
285     #[inline]
sub(self, other: Scale<T, Src, Dst>) -> Self::Output286     fn sub(self, other: Scale<T, Src, Dst>) -> Self::Output {
287         Scale::new(self.0 - other.0)
288     }
289 }
290 
291 
292 // FIXME: Switch to `derive(PartialEq, Clone)` after this Rust issue is fixed:
293 // https://github.com/rust-lang/rust/issues/26925
294 
295 impl<T: PartialEq, Src, Dst> PartialEq for Scale<T, Src, Dst> {
eq(&self, other: &Scale<T, Src, Dst>) -> bool296     fn eq(&self, other: &Scale<T, Src, Dst>) -> bool {
297         self.0 == other.0
298     }
299 }
300 
301 impl<T: Eq, Src, Dst> Eq for Scale<T, Src, Dst> {}
302 
303 impl<T: PartialOrd, Src, Dst> PartialOrd for Scale<T, Src, Dst> {
partial_cmp(&self, other: &Self) -> Option<Ordering>304     fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
305         self.0.partial_cmp(&other.0)
306     }
307 }
308 
309 impl<T: Ord, Src, Dst> Ord for Scale<T, Src, Dst> {
cmp(&self, other: &Self) -> Ordering310     fn cmp(&self, other: &Self) -> Ordering {
311         self.0.cmp(&other.0)
312     }
313 }
314 
315 impl<T: Clone, Src, Dst> Clone for Scale<T, Src, Dst> {
clone(&self) -> Scale<T, Src, Dst>316     fn clone(&self) -> Scale<T, Src, Dst> {
317         Scale::new(self.get())
318     }
319 }
320 
321 impl<T: Copy, Src, Dst> Copy for Scale<T, Src, Dst> {}
322 
323 impl<T: fmt::Debug, Src, Dst> fmt::Debug for Scale<T, Src, Dst> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result324     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
325         self.0.fmt(f)
326     }
327 }
328 
329 impl<T: fmt::Display, Src, Dst> fmt::Display for Scale<T, Src, Dst> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result330     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
331         self.0.fmt(f)
332     }
333 }
334 
335 impl<T: Default, Src, Dst> Default for Scale<T, Src, Dst> {
default() -> Self336     fn default() -> Self {
337         Self::new(T::default())
338     }
339 }
340 
341 impl<T: Hash, Src, Dst> Hash for Scale<T, Src, Dst> {
hash<H: Hasher>(&self, state: &mut H)342     fn hash<H: Hasher>(&self, state: &mut H) {
343         self.0.hash(state)
344     }
345 }
346 
347 impl<T: One, Src, Dst> One for Scale<T, Src, Dst> {
348     #[inline]
one() -> Self349     fn one() -> Self {
350         Scale::new(T::one())
351     }
352 }
353 
354 #[cfg(test)]
355 mod tests {
356     use super::Scale;
357 
358     enum Inch {}
359     enum Cm {}
360     enum Mm {}
361 
362     #[test]
test_scale()363     fn test_scale() {
364         let mm_per_inch: Scale<f32, Inch, Mm> = Scale::new(25.4);
365         let cm_per_mm: Scale<f32, Mm, Cm> = Scale::new(0.1);
366 
367         let mm_per_cm: Scale<f32, Cm, Mm> = cm_per_mm.inv();
368         assert_eq!(mm_per_cm.get(), 10.0);
369 
370         let one: Scale<f32, Mm, Mm> = cm_per_mm * mm_per_cm;
371         assert_eq!(one.get(), 1.0);
372 
373         let one: Scale<f32, Cm, Cm> = mm_per_cm * cm_per_mm;
374         assert_eq!(one.get(), 1.0);
375 
376         let cm_per_inch: Scale<f32, Inch, Cm> = mm_per_inch * cm_per_mm;
377         //  mm     cm     cm
378         // ---- x ---- = ----
379         // inch    mm    inch
380         assert_eq!(cm_per_inch, Scale::new(2.54));
381 
382         let a: Scale<isize, Inch, Inch> = Scale::new(2);
383         let b: Scale<isize, Inch, Inch> = Scale::new(3);
384         assert_ne!(a, b);
385         assert_eq!(a, a.clone());
386         assert_eq!(a.clone() + b.clone(), Scale::new(5));
387         assert_eq!(a - b, Scale::new(-1));
388     }
389 }
390