summaryrefslogtreecommitdiffstats
path: root/third_party/rust/euclid/src/scale.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/euclid/src/scale.rs')
-rw-r--r--third_party/rust/euclid/src/scale.rs428
1 files changed, 428 insertions, 0 deletions
diff --git a/third_party/rust/euclid/src/scale.rs b/third_party/rust/euclid/src/scale.rs
new file mode 100644
index 0000000000..eac377a7d0
--- /dev/null
+++ b/third_party/rust/euclid/src/scale.rs
@@ -0,0 +1,428 @@
+// 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 crate::num::One;
+
+use crate::{Point2D, Point3D, Rect, Size2D, Vector2D, Box2D, Box3D};
+use core::cmp::Ordering;
+use core::fmt;
+use core::hash::{Hash, Hasher};
+use core::marker::PhantomData;
+use core::ops::{Add, Div, Mul, Sub};
+use num_traits::NumCast;
+#[cfg(feature = "serde")]
+use serde::{Deserialize, Serialize};
+#[cfg(feature = "bytemuck")]
+use bytemuck::{Zeroable, Pod};
+
+/// 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>"
+ ))
+)]
+pub struct Scale<T, Src, Dst>(pub T, #[doc(hidden)] pub PhantomData<(Src, Dst)>);
+
+impl<T, Src, Dst> Scale<T, Src, Dst> {
+ #[inline]
+ pub const fn new(x: T) -> Self {
+ Scale(x, PhantomData)
+ }
+
+ /// Creates an identity scale (1.0).
+ #[inline]
+ pub fn 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]
+ pub fn 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]
+ pub fn 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]
+ pub fn 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 vector 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]
+ pub fn 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]
+ pub fn 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]
+ pub fn 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]
+ pub fn 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]
+ pub fn is_identity(self) -> bool
+ where
+ T: PartialEq + One,
+ {
+ self.0 == T::one()
+ }
+
+ /// Returns the underlying scalar scale factor.
+ #[inline]
+ pub fn 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));
+ /// ```
+ pub fn inverse(self) -> Scale<T::Output, Dst, Src>
+ where
+ T: One + Div,
+ {
+ let one: T = One::one();
+ Scale::new(one / self.0)
+ }
+}
+
+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]
+ pub fn 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);
+ /// ```
+ pub fn try_cast<NewT: NumCast>(self) -> Option<Scale<NewT, Src, Dst>> {
+ NumCast::from(self.0).map(Scale::new)
+ }
+}
+
+#[cfg(feature = "bytemuck")]
+unsafe impl<T: Zeroable, Src, Dst> Zeroable for Scale<T, Src, Dst> {}
+
+#[cfg(feature = "bytemuck")]
+unsafe impl<T: Pod, Src: 'static, Dst: 'static> Pod for Scale<T, Src, Dst> {}
+
+// scale0 * scale1
+// (A,B) * (B,C) = (A,C)
+impl<T: Mul, A, B, C> Mul<Scale<T, B, C>> for Scale<T, A, B> {
+ type Output = Scale<T::Output, A, C>;
+
+ #[inline]
+ fn mul(self, other: Scale<T, B, C>) -> Self::Output {
+ Scale::new(self.0 * other.0)
+ }
+}
+
+// scale0 + scale1
+impl<T: Add, Src, Dst> Add for Scale<T, Src, Dst> {
+ type Output = Scale<T::Output, Src, Dst>;
+
+ #[inline]
+ fn add(self, other: Scale<T, Src, Dst>) -> Self::Output {
+ Scale::new(self.0 + other.0)
+ }
+}
+
+// scale0 - scale1
+impl<T: Sub, Src, Dst> Sub for Scale<T, Src, Dst> {
+ type Output = Scale<T::Output, Src, Dst>;
+
+ #[inline]
+ fn sub(self, other: Scale<T, Src, Dst>) -> Self::Output {
+ Scale::new(self.0 - other.0)
+ }
+}
+
+// FIXME: Switch to `derive(PartialEq, Clone)` after this Rust issue is fixed:
+// https://github.com/rust-lang/rust/issues/26925
+
+impl<T: PartialEq, Src, Dst> PartialEq for Scale<T, Src, Dst> {
+ fn eq(&self, other: &Scale<T, Src, Dst>) -> bool {
+ self.0 == other.0
+ }
+}
+
+impl<T: Eq, Src, Dst> Eq for Scale<T, Src, Dst> {}
+
+impl<T: PartialOrd, Src, Dst> PartialOrd for Scale<T, Src, Dst> {
+ fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+ self.0.partial_cmp(&other.0)
+ }
+}
+
+impl<T: Ord, Src, Dst> Ord for Scale<T, Src, Dst> {
+ fn cmp(&self, other: &Self) -> Ordering {
+ self.0.cmp(&other.0)
+ }
+}
+
+impl<T: Clone, Src, Dst> Clone for Scale<T, Src, Dst> {
+ fn clone(&self) -> Scale<T, Src, Dst> {
+ Scale::new(self.0.clone())
+ }
+}
+
+impl<T: Copy, Src, Dst> Copy for Scale<T, Src, Dst> {}
+
+impl<T: fmt::Debug, Src, Dst> fmt::Debug for Scale<T, Src, Dst> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ self.0.fmt(f)
+ }
+}
+
+impl<T: Default, Src, Dst> Default for Scale<T, Src, Dst> {
+ fn default() -> Self {
+ Self::new(T::default())
+ }
+}
+
+impl<T: Hash, Src, Dst> Hash for Scale<T, Src, Dst> {
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ self.0.hash(state)
+ }
+}
+
+impl<T: One, Src, Dst> One for Scale<T, Src, Dst> {
+ #[inline]
+ fn one() -> Self {
+ Scale::new(T::one())
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::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));
+
+ let a: Scale<isize, Inch, Inch> = Scale::new(2);
+ let b: Scale<isize, Inch, Inch> = Scale::new(3);
+ assert_ne!(a, b);
+ assert_eq!(a, a.clone());
+ assert_eq!(a.clone() + b.clone(), Scale::new(5));
+ assert_eq!(a - b, Scale::new(-1));
+ }
+}