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