// Copyright 2014 The Servo Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution. // // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! A type-checked scaling factor between units.
use core::cmp::Ordering; use core::fmt; use core::hash::{Hash, Hasher}; use core::marker::PhantomData; use core::ops::{Add, Div, Mul, Sub};
#[cfg(feature = "bytemuck")] use bytemuck::{Pod, Zeroable}; #[cfg(feature = "malloc_size_of")] use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; use num_traits::NumCast; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize};
/// A scaling factor between two different units of measurement. /// /// This is effectively a type-safe float, intended to be used in combination with other types like /// `length::Length` to enforce conversion between systems of measurement at compile time. /// /// `Src` and `Dst` represent the units before and after multiplying a value by a `Scale`. They /// may be types without values, such as empty enums. For example: /// /// ```rust /// use euclid::Scale; /// use euclid::Length; /// enum Mm {}; /// enum Inch {}; /// /// let mm_per_inch: Scale<f32, Inch, Mm> = Scale::new(25.4); /// /// let one_foot: Length<f32, Inch> = Length::new(12.0); /// let one_foot_in_mm: Length<f32, Mm> = one_foot * mm_per_inch; /// ``` #[repr(C)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(
feature = "serde",
serde(bound(
serialize = "T: serde::Serialize",
deserialize = "T: serde::Deserialize<'de>"
))
)] pubstruct Scale<T, Src, Dst>(pub T, #[doc(hidden)] pub PhantomData<(Src, Dst)>);
/// Creates an identity scale (1.0). #[inline] pubfn identity() -> Self where
T: One,
{
Scale::new(T::one())
}
/// Returns the given point transformed by this scale. /// /// # Example /// /// ```rust /// use euclid::{Scale, point2}; /// enum Mm {}; /// enum Cm {}; /// /// let to_mm: Scale<i32, Cm, Mm> = Scale::new(10); /// /// assert_eq!(to_mm.transform_point(point2(42, -42)), point2(420, -420)); /// ``` #[inline] pubfn transform_point(self, point: Point2D<T, Src>) -> Point2D<T::Output, Dst> where
T: Copy + Mul,
{
Point2D::new(point.x * self.0, point.y * self.0)
}
/// Returns the given point transformed by this scale. #[inline] pubfn transform_point3d(self, point: Point3D<T, Src>) -> Point3D<T::Output, Dst> where
T: Copy + Mul,
{
Point3D::new(point.x * self.0, point.y * self.0, point.z * self.0)
}
/// Returns the given vector transformed by this scale. /// /// # Example /// /// ```rust /// use euclid::{Scale, vec2}; /// enum Mm {}; /// enum Cm {}; /// /// let to_mm: Scale<i32, Cm, Mm> = Scale::new(10); /// /// assert_eq!(to_mm.transform_vector(vec2(42, -42)), vec2(420, -420)); /// ``` #[inline] pubfn transform_vector(self, vec: Vector2D<T, Src>) -> Vector2D<T::Output, Dst> where
T: Copy + Mul,
{
Vector2D::new(vec.x * self.0, vec.y * self.0)
}
/// Returns the given size transformed by this scale. /// /// # Example /// /// ```rust /// use euclid::{Scale, size2}; /// enum Mm {}; /// enum Cm {}; /// /// let to_mm: Scale<i32, Cm, Mm> = Scale::new(10); /// /// assert_eq!(to_mm.transform_size(size2(42, -42)), size2(420, -420)); /// ``` #[inline] pubfn transform_size(self, size: Size2D<T, Src>) -> Size2D<T::Output, Dst> where
T: Copy + Mul,
{
Size2D::new(size.width * self.0, size.height * self.0)
}
/// Returns the given rect transformed by this scale. /// /// # Example /// /// ```rust /// use euclid::{Scale, rect}; /// enum Mm {}; /// enum Cm {}; /// /// let to_mm: Scale<i32, Cm, Mm> = Scale::new(10); /// /// assert_eq!(to_mm.transform_rect(&rect(1, 2, 42, -42)), rect(10, 20, 420, -420)); /// ``` #[inline] pubfn transform_rect(self, rect: &Rect<T, Src>) -> Rect<T::Output, Dst> where
T: Copy + Mul,
{
Rect::new( self.transform_point(rect.origin), self.transform_size(rect.size),
)
}
/// Returns the given box transformed by this scale. #[inline] pubfn transform_box2d(self, b: &Box2D<T, Src>) -> Box2D<T::Output, Dst> where
T: Copy + Mul,
{
Box2D {
min: self.transform_point(b.min),
max: self.transform_point(b.max),
}
}
/// Returns the given box transformed by this scale. #[inline] pubfn transform_box3d(self, b: &Box3D<T, Src>) -> Box3D<T::Output, Dst> where
T: Copy + Mul,
{
Box3D {
min: self.transform_point3d(b.min),
max: self.transform_point3d(b.max),
}
}
/// Returns `true` if this scale has no effect. /// /// # Example /// /// ```rust /// use euclid::Scale; /// use euclid::num::One; /// enum Mm {}; /// enum Cm {}; /// /// let cm_per_mm: Scale<f32, Mm, Cm> = Scale::new(0.1); /// let mm_per_mm: Scale<f32, Mm, Mm> = Scale::new(1.0); /// /// assert_eq!(cm_per_mm.is_identity(), false); /// assert_eq!(mm_per_mm.is_identity(), true); /// assert_eq!(mm_per_mm, Scale::one()); /// ``` #[inline] pubfn is_identity(self) -> bool where
T: PartialEq + One,
{ self.0 == T::one()
}
/// Returns the underlying scalar scale factor. #[inline] pubfn get(self) -> T { self.0
}
/// The inverse Scale (1.0 / self). /// /// # Example /// /// ```rust /// use euclid::Scale; /// enum Mm {}; /// enum Cm {}; /// /// let cm_per_mm: Scale<f32, Cm, Mm> = Scale::new(0.1); /// /// assert_eq!(cm_per_mm.inverse(), Scale::new(10.0)); /// ``` pubfn inverse(self) -> Scale<T::Output, Dst, Src> where
T: One + Div,
{ let one: T = One::one();
Scale::new(one / self.0)
}
/// Returns the same transform with a different source unit. #[inline] pubfn with_source<NewSrc>(self) -> Scale<T, NewSrc, Dst> {
Scale::new(self.0)
}
/// Returns the same transform with a different destination unit. #[inline] pubfn with_destination<NewDst>(self) -> Scale<T, Src, NewDst> {
Scale::new(self.0)
}
}
/// Returns the point each component of which clamped by corresponding /// components of `start` and `end`. /// /// Shortcut for `self.max(start).min(end)`. #[inline] pubfn clamp(self, start: Self, end: Self) -> Self where
T: Copy,
{ self.max(start).min(end)
}
}
impl<T: NumCast, Src, Dst> Scale<T, Src, Dst> { /// Cast from one numeric representation to another, preserving the units. /// /// # Panics /// /// If the source value cannot be represented by the target type `NewT`, then /// method panics. Use `try_cast` if that must be case. /// /// # Example /// /// ```rust /// use euclid::Scale; /// enum Mm {}; /// enum Cm {}; /// /// let to_mm: Scale<i32, Cm, Mm> = Scale::new(10); /// /// assert_eq!(to_mm.cast::<f32>(), Scale::new(10.0)); /// ``` /// That conversion will panic, because `i32` not enough to store such big numbers: /// ```rust,should_panic /// use euclid::Scale; /// enum Mm {};// millimeter = 10^-2 meters /// enum Em {};// exameter = 10^18 meters /// /// // Panics /// let to_em: Scale<i32, Mm, Em> = Scale::new(10e20).cast(); /// ``` #[inline] pubfn cast<NewT: NumCast>(self) -> Scale<NewT, Src, Dst> { self.try_cast().unwrap()
}
/// Fallible cast from one numeric representation to another, preserving the units. /// If the source value cannot be represented by the target type `NewT`, then `None` /// is returned. /// /// # Example /// /// ```rust /// use euclid::Scale; /// enum Mm {}; /// enum Cm {}; /// enum Em {};// Exameter = 10^18 meters /// /// let to_mm: Scale<i32, Cm, Mm> = Scale::new(10); /// let to_em: Scale<f32, Mm, Em> = Scale::new(10e20); /// /// assert_eq!(to_mm.try_cast::<f32>(), Some(Scale::new(10.0))); /// // Integer to small to store that number /// assert_eq!(to_em.try_cast::<i32>(), None); /// ``` pubfn try_cast<NewT: NumCast>(self) -> Option<Scale<NewT, Src, Dst>> {
NumCast::from(self.0).map(Scale::new)
}
}
impl<T: One, Src, Dst> One for Scale<T, Src, Dst> { #[inline] fn one() -> Self {
Scale::new(T::one())
}
}
#[cfg(test)] mod tests { usesuper::Scale;
enum Inch {} enum Cm {} enum Mm {}
#[test] fn test_scale() { let mm_per_inch: Scale<f32, Inch, Mm> = Scale::new(25.4); let cm_per_mm: Scale<f32, Mm, Cm> = Scale::new(0.1);
let mm_per_cm: Scale<f32, Cm, Mm> = cm_per_mm.inverse();
assert_eq!(mm_per_cm.get(), 10.0);
let one: Scale<f32, Mm, Mm> = cm_per_mm * mm_per_cm;
assert_eq!(one.get(), 1.0);
let one: Scale<f32, Cm, Cm> = mm_per_cm * cm_per_mm;
assert_eq!(one.get(), 1.0);
let cm_per_inch: Scale<f32, Inch, Cm> = mm_per_inch * cm_per_mm; // mm cm cm // ---- x ---- = ---- // inch mm inch
assert_eq!(cm_per_inch, Scale::new(2.54));
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.