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 crate::num::One; |
12 | |
13 | use crate::approxord::{max, min}; |
14 | use crate::{Box2D, Box3D, Point2D, Point3D, Rect, Size2D, Vector2D}; |
15 | |
16 | use core::cmp::Ordering; |
17 | use core::fmt; |
18 | use core::hash::{Hash, Hasher}; |
19 | use core::marker::PhantomData; |
20 | use core::ops::{Add, Div, Mul, Sub}; |
21 | |
22 | #[cfg (feature = "bytemuck" )] |
23 | use bytemuck::{Pod, Zeroable}; |
24 | use num_traits::NumCast; |
25 | #[cfg (feature = "serde" )] |
26 | use serde::{Deserialize, Serialize}; |
27 | |
28 | /// A scaling factor between two different units of measurement. |
29 | /// |
30 | /// This is effectively a type-safe float, intended to be used in combination with other types like |
31 | /// `length::Length` to enforce conversion between systems of measurement at compile time. |
32 | /// |
33 | /// `Src` and `Dst` represent the units before and after multiplying a value by a `Scale`. They |
34 | /// may be types without values, such as empty enums. For example: |
35 | /// |
36 | /// ```rust |
37 | /// use euclid::Scale; |
38 | /// use euclid::Length; |
39 | /// enum Mm {}; |
40 | /// enum Inch {}; |
41 | /// |
42 | /// let mm_per_inch: Scale<f32, Inch, Mm> = Scale::new(25.4); |
43 | /// |
44 | /// let one_foot: Length<f32, Inch> = Length::new(12.0); |
45 | /// let one_foot_in_mm: Length<f32, Mm> = one_foot * mm_per_inch; |
46 | /// ``` |
47 | #[repr (C)] |
48 | #[cfg_attr (feature = "serde" , derive(Serialize, Deserialize))] |
49 | #[cfg_attr ( |
50 | feature = "serde" , |
51 | serde(bound( |
52 | serialize = "T: serde::Serialize" , |
53 | deserialize = "T: serde::Deserialize<'de>" |
54 | )) |
55 | )] |
56 | pub struct Scale<T, Src, Dst>(pub T, #[doc (hidden)] pub PhantomData<(Src, Dst)>); |
57 | |
58 | impl<T, Src, Dst> Scale<T, Src, Dst> { |
59 | #[inline ] |
60 | pub const fn new(x: T) -> Self { |
61 | Scale(x, PhantomData) |
62 | } |
63 | |
64 | /// Creates an identity scale (1.0). |
65 | #[inline ] |
66 | pub fn identity() -> Self |
67 | where |
68 | T: One, |
69 | { |
70 | Scale::new(T::one()) |
71 | } |
72 | |
73 | /// Returns the given point transformed by this scale. |
74 | /// |
75 | /// # Example |
76 | /// |
77 | /// ```rust |
78 | /// use euclid::{Scale, point2}; |
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_point(point2(42, -42)), point2(420, -420)); |
85 | /// ``` |
86 | #[inline ] |
87 | pub fn transform_point(self, point: Point2D<T, Src>) -> Point2D<T::Output, Dst> |
88 | where |
89 | T: Copy + Mul, |
90 | { |
91 | Point2D::new(point.x * self.0, point.y * self.0) |
92 | } |
93 | |
94 | /// Returns the given point transformed by this scale. |
95 | #[inline ] |
96 | pub fn transform_point3d(self, point: Point3D<T, Src>) -> Point3D<T::Output, Dst> |
97 | where |
98 | T: Copy + Mul, |
99 | { |
100 | Point3D::new(point.x * self.0, point.y * self.0, point.z * self.0) |
101 | } |
102 | |
103 | /// Returns the given vector transformed by this scale. |
104 | /// |
105 | /// # Example |
106 | /// |
107 | /// ```rust |
108 | /// use euclid::{Scale, vec2}; |
109 | /// enum Mm {}; |
110 | /// enum Cm {}; |
111 | /// |
112 | /// let to_mm: Scale<i32, Cm, Mm> = Scale::new(10); |
113 | /// |
114 | /// assert_eq!(to_mm.transform_vector(vec2(42, -42)), vec2(420, -420)); |
115 | /// ``` |
116 | #[inline ] |
117 | pub fn transform_vector(self, vec: Vector2D<T, Src>) -> Vector2D<T::Output, Dst> |
118 | where |
119 | T: Copy + Mul, |
120 | { |
121 | Vector2D::new(vec.x * self.0, vec.y * self.0) |
122 | } |
123 | |
124 | /// Returns the given size transformed by this scale. |
125 | /// |
126 | /// # Example |
127 | /// |
128 | /// ```rust |
129 | /// use euclid::{Scale, size2}; |
130 | /// enum Mm {}; |
131 | /// enum Cm {}; |
132 | /// |
133 | /// let to_mm: Scale<i32, Cm, Mm> = Scale::new(10); |
134 | /// |
135 | /// assert_eq!(to_mm.transform_size(size2(42, -42)), size2(420, -420)); |
136 | /// ``` |
137 | #[inline ] |
138 | pub fn transform_size(self, size: Size2D<T, Src>) -> Size2D<T::Output, Dst> |
139 | where |
140 | T: Copy + Mul, |
141 | { |
142 | Size2D::new(size.width * self.0, size.height * self.0) |
143 | } |
144 | |
145 | /// Returns the given rect transformed by this scale. |
146 | /// |
147 | /// # Example |
148 | /// |
149 | /// ```rust |
150 | /// use euclid::{Scale, rect}; |
151 | /// enum Mm {}; |
152 | /// enum Cm {}; |
153 | /// |
154 | /// let to_mm: Scale<i32, Cm, Mm> = Scale::new(10); |
155 | /// |
156 | /// assert_eq!(to_mm.transform_rect(&rect(1, 2, 42, -42)), rect(10, 20, 420, -420)); |
157 | /// ``` |
158 | #[inline ] |
159 | pub fn transform_rect(self, rect: &Rect<T, Src>) -> Rect<T::Output, Dst> |
160 | where |
161 | T: Copy + Mul, |
162 | { |
163 | Rect::new( |
164 | self.transform_point(rect.origin), |
165 | self.transform_size(rect.size), |
166 | ) |
167 | } |
168 | |
169 | /// Returns the given box transformed by this scale. |
170 | #[inline ] |
171 | pub fn transform_box2d(self, b: &Box2D<T, Src>) -> Box2D<T::Output, Dst> |
172 | where |
173 | T: Copy + Mul, |
174 | { |
175 | Box2D { |
176 | min: self.transform_point(b.min), |
177 | max: self.transform_point(b.max), |
178 | } |
179 | } |
180 | |
181 | /// Returns the given box transformed by this scale. |
182 | #[inline ] |
183 | pub fn transform_box3d(self, b: &Box3D<T, Src>) -> Box3D<T::Output, Dst> |
184 | where |
185 | T: Copy + Mul, |
186 | { |
187 | Box3D { |
188 | min: self.transform_point3d(b.min), |
189 | max: self.transform_point3d(b.max), |
190 | } |
191 | } |
192 | |
193 | /// Returns `true` if this scale has no effect. |
194 | /// |
195 | /// # Example |
196 | /// |
197 | /// ```rust |
198 | /// use euclid::Scale; |
199 | /// use euclid::num::One; |
200 | /// enum Mm {}; |
201 | /// enum Cm {}; |
202 | /// |
203 | /// let cm_per_mm: Scale<f32, Mm, Cm> = Scale::new(0.1); |
204 | /// let mm_per_mm: Scale<f32, Mm, Mm> = Scale::new(1.0); |
205 | /// |
206 | /// assert_eq!(cm_per_mm.is_identity(), false); |
207 | /// assert_eq!(mm_per_mm.is_identity(), true); |
208 | /// assert_eq!(mm_per_mm, Scale::one()); |
209 | /// ``` |
210 | #[inline ] |
211 | pub fn is_identity(self) -> bool |
212 | where |
213 | T: PartialEq + One, |
214 | { |
215 | self.0 == T::one() |
216 | } |
217 | |
218 | /// Returns the underlying scalar scale factor. |
219 | #[inline ] |
220 | pub fn get(self) -> T { |
221 | self.0 |
222 | } |
223 | |
224 | /// The inverse Scale (1.0 / self). |
225 | /// |
226 | /// # Example |
227 | /// |
228 | /// ```rust |
229 | /// use euclid::Scale; |
230 | /// enum Mm {}; |
231 | /// enum Cm {}; |
232 | /// |
233 | /// let cm_per_mm: Scale<f32, Cm, Mm> = Scale::new(0.1); |
234 | /// |
235 | /// assert_eq!(cm_per_mm.inverse(), Scale::new(10.0)); |
236 | /// ``` |
237 | pub fn inverse(self) -> Scale<T::Output, Dst, Src> |
238 | where |
239 | T: One + Div, |
240 | { |
241 | let one: T = One::one(); |
242 | Scale::new(one / self.0) |
243 | } |
244 | } |
245 | |
246 | impl<T: PartialOrd, Src, Dst> Scale<T, Src, Dst> { |
247 | #[inline ] |
248 | pub fn min(self, other: Self) -> Self { |
249 | Self::new(min(self.0, y:other.0)) |
250 | } |
251 | |
252 | #[inline ] |
253 | pub fn max(self, other: Self) -> Self { |
254 | Self::new(max(self.0, y:other.0)) |
255 | } |
256 | |
257 | /// Returns the point each component of which clamped by corresponding |
258 | /// components of `start` and `end`. |
259 | /// |
260 | /// Shortcut for `self.max(start).min(end)`. |
261 | #[inline ] |
262 | pub fn clamp(self, start: Self, end: Self) -> Self |
263 | where |
264 | T: Copy, |
265 | { |
266 | self.max(start).min(end) |
267 | } |
268 | } |
269 | |
270 | impl<T: NumCast, Src, Dst> Scale<T, Src, Dst> { |
271 | /// Cast from one numeric representation to another, preserving the units. |
272 | /// |
273 | /// # Panics |
274 | /// |
275 | /// If the source value cannot be represented by the target type `NewT`, then |
276 | /// method panics. Use `try_cast` if that must be case. |
277 | /// |
278 | /// # Example |
279 | /// |
280 | /// ```rust |
281 | /// use euclid::Scale; |
282 | /// enum Mm {}; |
283 | /// enum Cm {}; |
284 | /// |
285 | /// let to_mm: Scale<i32, Cm, Mm> = Scale::new(10); |
286 | /// |
287 | /// assert_eq!(to_mm.cast::<f32>(), Scale::new(10.0)); |
288 | /// ``` |
289 | /// That conversion will panic, because `i32` not enough to store such big numbers: |
290 | /// ```rust,should_panic |
291 | /// use euclid::Scale; |
292 | /// enum Mm {};// millimeter = 10^-2 meters |
293 | /// enum Em {};// exameter = 10^18 meters |
294 | /// |
295 | /// // Panics |
296 | /// let to_em: Scale<i32, Mm, Em> = Scale::new(10e20).cast(); |
297 | /// ``` |
298 | #[inline ] |
299 | pub fn cast<NewT: NumCast>(self) -> Scale<NewT, Src, Dst> { |
300 | self.try_cast().unwrap() |
301 | } |
302 | |
303 | /// Fallible cast from one numeric representation to another, preserving the units. |
304 | /// If the source value cannot be represented by the target type `NewT`, then `None` |
305 | /// is returned. |
306 | /// |
307 | /// # Example |
308 | /// |
309 | /// ```rust |
310 | /// use euclid::Scale; |
311 | /// enum Mm {}; |
312 | /// enum Cm {}; |
313 | /// enum Em {};// Exameter = 10^18 meters |
314 | /// |
315 | /// let to_mm: Scale<i32, Cm, Mm> = Scale::new(10); |
316 | /// let to_em: Scale<f32, Mm, Em> = Scale::new(10e20); |
317 | /// |
318 | /// assert_eq!(to_mm.try_cast::<f32>(), Some(Scale::new(10.0))); |
319 | /// // Integer to small to store that number |
320 | /// assert_eq!(to_em.try_cast::<i32>(), None); |
321 | /// ``` |
322 | pub fn try_cast<NewT: NumCast>(self) -> Option<Scale<NewT, Src, Dst>> { |
323 | NumCast::from(self.0).map(Scale::new) |
324 | } |
325 | } |
326 | |
327 | #[cfg (feature = "arbitrary" )] |
328 | impl<'a, T, Src, Dst> arbitrary::Arbitrary<'a> for Scale<T, Src, Dst> |
329 | where |
330 | T: arbitrary::Arbitrary<'a>, |
331 | { |
332 | fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> { |
333 | Ok(Scale::new(arbitrary::Arbitrary::arbitrary(u)?)) |
334 | } |
335 | } |
336 | |
337 | #[cfg (feature = "bytemuck" )] |
338 | unsafe impl<T: Zeroable, Src, Dst> Zeroable for Scale<T, Src, Dst> {} |
339 | |
340 | #[cfg (feature = "bytemuck" )] |
341 | unsafe impl<T: Pod, Src: 'static, Dst: 'static> Pod for Scale<T, Src, Dst> {} |
342 | |
343 | // scale0 * scale1 |
344 | // (A,B) * (B,C) = (A,C) |
345 | impl<T: Mul, A, B, C> Mul<Scale<T, B, C>> for Scale<T, A, B> { |
346 | type Output = Scale<T::Output, A, C>; |
347 | |
348 | #[inline ] |
349 | fn mul(self, other: Scale<T, B, C>) -> Self::Output { |
350 | Scale::new(self.0 * other.0) |
351 | } |
352 | } |
353 | |
354 | // scale0 + scale1 |
355 | impl<T: Add, Src, Dst> Add for Scale<T, Src, Dst> { |
356 | type Output = Scale<T::Output, Src, Dst>; |
357 | |
358 | #[inline ] |
359 | fn add(self, other: Scale<T, Src, Dst>) -> Self::Output { |
360 | Scale::new(self.0 + other.0) |
361 | } |
362 | } |
363 | |
364 | // scale0 - scale1 |
365 | impl<T: Sub, Src, Dst> Sub for Scale<T, Src, Dst> { |
366 | type Output = Scale<T::Output, Src, Dst>; |
367 | |
368 | #[inline ] |
369 | fn sub(self, other: Scale<T, Src, Dst>) -> Self::Output { |
370 | Scale::new(self.0 - other.0) |
371 | } |
372 | } |
373 | |
374 | // FIXME: Switch to `derive(PartialEq, Clone)` after this Rust issue is fixed: |
375 | // https://github.com/rust-lang/rust/issues/26925 |
376 | |
377 | impl<T: PartialEq, Src, Dst> PartialEq for Scale<T, Src, Dst> { |
378 | fn eq(&self, other: &Scale<T, Src, Dst>) -> bool { |
379 | self.0 == other.0 |
380 | } |
381 | } |
382 | |
383 | impl<T: Eq, Src, Dst> Eq for Scale<T, Src, Dst> {} |
384 | |
385 | impl<T: PartialOrd, Src, Dst> PartialOrd for Scale<T, Src, Dst> { |
386 | fn partial_cmp(&self, other: &Self) -> Option<Ordering> { |
387 | self.0.partial_cmp(&other.0) |
388 | } |
389 | } |
390 | |
391 | impl<T: Ord, Src, Dst> Ord for Scale<T, Src, Dst> { |
392 | fn cmp(&self, other: &Self) -> Ordering { |
393 | self.0.cmp(&other.0) |
394 | } |
395 | } |
396 | |
397 | impl<T: Clone, Src, Dst> Clone for Scale<T, Src, Dst> { |
398 | fn clone(&self) -> Scale<T, Src, Dst> { |
399 | Scale::new(self.0.clone()) |
400 | } |
401 | } |
402 | |
403 | impl<T: Copy, Src, Dst> Copy for Scale<T, Src, Dst> {} |
404 | |
405 | impl<T: fmt::Debug, Src, Dst> fmt::Debug for Scale<T, Src, Dst> { |
406 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
407 | self.0.fmt(f) |
408 | } |
409 | } |
410 | |
411 | impl<T: Default, Src, Dst> Default for Scale<T, Src, Dst> { |
412 | fn default() -> Self { |
413 | Self::new(T::default()) |
414 | } |
415 | } |
416 | |
417 | impl<T: Hash, Src, Dst> Hash for Scale<T, Src, Dst> { |
418 | fn hash<H: Hasher>(&self, state: &mut H) { |
419 | self.0.hash(state) |
420 | } |
421 | } |
422 | |
423 | impl<T: One, Src, Dst> One for Scale<T, Src, Dst> { |
424 | #[inline ] |
425 | fn one() -> Self { |
426 | Scale::new(T::one()) |
427 | } |
428 | } |
429 | |
430 | #[cfg (test)] |
431 | mod tests { |
432 | use super::Scale; |
433 | |
434 | enum Inch {} |
435 | enum Cm {} |
436 | enum Mm {} |
437 | |
438 | #[test ] |
439 | fn test_scale() { |
440 | let mm_per_inch: Scale<f32, Inch, Mm> = Scale::new(25.4); |
441 | let cm_per_mm: Scale<f32, Mm, Cm> = Scale::new(0.1); |
442 | |
443 | let mm_per_cm: Scale<f32, Cm, Mm> = cm_per_mm.inverse(); |
444 | assert_eq!(mm_per_cm.get(), 10.0); |
445 | |
446 | let one: Scale<f32, Mm, Mm> = cm_per_mm * mm_per_cm; |
447 | assert_eq!(one.get(), 1.0); |
448 | |
449 | let one: Scale<f32, Cm, Cm> = mm_per_cm * cm_per_mm; |
450 | assert_eq!(one.get(), 1.0); |
451 | |
452 | let cm_per_inch: Scale<f32, Inch, Cm> = mm_per_inch * cm_per_mm; |
453 | // mm cm cm |
454 | // ---- x ---- = ---- |
455 | // inch mm inch |
456 | assert_eq!(cm_per_inch, Scale::new(2.54)); |
457 | |
458 | let a: Scale<isize, Inch, Inch> = Scale::new(2); |
459 | let b: Scale<isize, Inch, Inch> = Scale::new(3); |
460 | assert_ne!(a, b); |
461 | assert_eq!(a, a.clone()); |
462 | assert_eq!(a.clone() + b.clone(), Scale::new(5)); |
463 | assert_eq!(a - b, Scale::new(-1)); |
464 | |
465 | // Clamp |
466 | assert_eq!(Scale::identity().clamp(a, b), a); |
467 | assert_eq!(Scale::new(5).clamp(a, b), b); |
468 | let a = Scale::<f32, Inch, Inch>::new(2.0); |
469 | let b = Scale::<f32, Inch, Inch>::new(3.0); |
470 | let c = Scale::<f32, Inch, Inch>::new(2.5); |
471 | assert_eq!(c.clamp(a, b), c); |
472 | } |
473 | } |
474 | |