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