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
11use crate::num::One;
12
13use crate::approxord::{max, min};
14use crate::{Point2D, Point3D, Rect, Size2D, Vector2D, Box2D, Box3D};
15use core::cmp::Ordering;
16use core::fmt;
17use core::hash::{Hash, Hasher};
18use core::marker::PhantomData;
19use core::ops::{Add, Div, Mul, Sub};
20use num_traits::NumCast;
21#[cfg(feature = "serde")]
22use serde::{Deserialize, Serialize};
23#[cfg(feature = "bytemuck")]
24use 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)]
54pub struct Scale<T, Src, Dst>(pub T, #[doc(hidden)] pub PhantomData<(Src, Dst)>);
55
56impl<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
244impl<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
269impl<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")]
327unsafe impl<T: Zeroable, Src, Dst> Zeroable for Scale<T, Src, Dst> {}
328
329#[cfg(feature = "bytemuck")]
330unsafe impl<T: Pod, Src: 'static, Dst: 'static> Pod for Scale<T, Src, Dst> {}
331
332// scale0 * scale1
333// (A,B) * (B,C) = (A,C)
334impl<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
344impl<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
354impl<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
366impl<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
372impl<T: Eq, Src, Dst> Eq for Scale<T, Src, Dst> {}
373
374impl<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
380impl<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
386impl<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
392impl<T: Copy, Src, Dst> Copy for Scale<T, Src, Dst> {}
393
394impl<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
400impl<T: Default, Src, Dst> Default for Scale<T, Src, Dst> {
401 fn default() -> Self {
402 Self::new(T::default())
403 }
404}
405
406impl<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
412impl<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)]
420mod 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