// Copyright 2013 The Servo Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use super::UnknownUnit; use crate::approxord::{max, min}; use crate::length::Length; use crate::num::*; use crate::scale::Scale; use crate::vector::{vec2, BoolVector2D, Vector2D}; use crate::vector::{vec3, BoolVector3D, Vector3D}; #[cfg(feature = "mint")] use mint; use core::cmp::{Eq, PartialEq}; use core::fmt; use core::hash::Hash; use core::iter::Sum; use core::marker::PhantomData; use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}; use num_traits::{NumCast, Signed, Float}; #[cfg(feature = "serde")] use serde; #[cfg(feature = "bytemuck")] use bytemuck::{Zeroable, Pod}; /// A 2d size tagged with a unit. #[repr(C)] pub struct Size2D { /// The extent of the element in the `U` units along the `x` axis (usually horizontal). pub width: T, /// The extent of the element in the `U` units along the `y` axis (usually vertical). pub height: T, #[doc(hidden)] pub _unit: PhantomData, } impl Copy for Size2D {} impl Clone for Size2D { fn clone(&self) -> Self { Size2D { width: self.width.clone(), height: self.height.clone(), _unit: PhantomData, } } } #[cfg(feature = "serde")] impl<'de, T, U> serde::Deserialize<'de> for Size2D where T: serde::Deserialize<'de>, { /// Deserializes 2d size from tuple of width and height. fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { let (width, height) = serde::Deserialize::deserialize(deserializer)?; Ok(Size2D { width, height, _unit: PhantomData, }) } } #[cfg(feature = "serde")] impl serde::Serialize for Size2D where T: serde::Serialize, { /// Serializes 2d size to tuple of width and height. fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, { (&self.width, &self.height).serialize(serializer) } } #[cfg(feature = "arbitrary")] impl<'a, T, U> arbitrary::Arbitrary<'a> for Size2D where T: arbitrary::Arbitrary<'a>, { fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { let (width, height) = arbitrary::Arbitrary::arbitrary(u)?; Ok(Size2D { width, height, _unit: PhantomData, }) } } #[cfg(feature = "bytemuck")] unsafe impl Zeroable for Size2D {} #[cfg(feature = "bytemuck")] unsafe impl Pod for Size2D {} impl Eq for Size2D where T: Eq {} impl PartialEq for Size2D where T: PartialEq, { fn eq(&self, other: &Self) -> bool { self.width == other.width && self.height == other.height } } impl Hash for Size2D where T: Hash, { fn hash(&self, h: &mut H) { self.width.hash(h); self.height.hash(h); } } impl fmt::Debug for Size2D { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(&self.width, f)?; write!(f, "x")?; fmt::Debug::fmt(&self.height, f) } } impl Default for Size2D { fn default() -> Self { Size2D::new(Default::default(), Default::default()) } } impl Size2D { /// The same as [`Zero::zero()`] but available without importing trait. /// /// [`Zero::zero()`]: ./num/trait.Zero.html#tymethod.zero #[inline] pub fn zero() -> Self where T: Zero, { Size2D::new(Zero::zero(), Zero::zero()) } /// Constructor taking scalar values. #[inline] pub const fn new(width: T, height: T) -> Self { Size2D { width, height, _unit: PhantomData, } } /// Constructor taking scalar strongly typed lengths. #[inline] pub fn from_lengths(width: Length, height: Length) -> Self { Size2D::new(width.0, height.0) } /// Constructor setting all components to the same value. #[inline] pub fn splat(v: T) -> Self where T: Clone, { Size2D { width: v.clone(), height: v, _unit: PhantomData, } } /// Tag a unitless value with units. #[inline] pub fn from_untyped(p: Size2D) -> Self { Size2D::new(p.width, p.height) } } impl Size2D { /// Return this size as an array of two elements (width, then height). #[inline] pub fn to_array(self) -> [T; 2] { [self.width, self.height] } /// Return this size as a tuple of two elements (width, then height). #[inline] pub fn to_tuple(self) -> (T, T) { (self.width, self.height) } /// Return this size as a vector with width and height. #[inline] pub fn to_vector(self) -> Vector2D { vec2(self.width, self.height) } /// Drop the units, preserving only the numeric value. #[inline] pub fn to_untyped(self) -> Size2D { self.cast_unit() } /// Cast the unit #[inline] pub fn cast_unit(self) -> Size2D { Size2D::new(self.width, self.height) } /// Rounds each component to the nearest integer value. /// /// This behavior is preserved for negative values (unlike the basic cast). /// /// ```rust /// # use euclid::size2; /// enum Mm {} /// /// assert_eq!(size2::<_, Mm>(-0.1, -0.8).round(), size2::<_, Mm>(0.0, -1.0)) /// ``` #[inline] #[must_use] pub fn round(self) -> Self where T: Round, { Size2D::new(self.width.round(), self.height.round()) } /// Rounds each component to the smallest integer equal or greater than the original value. /// /// This behavior is preserved for negative values (unlike the basic cast). /// /// ```rust /// # use euclid::size2; /// enum Mm {} /// /// assert_eq!(size2::<_, Mm>(-0.1, -0.8).ceil(), size2::<_, Mm>(0.0, 0.0)) /// ``` #[inline] #[must_use] pub fn ceil(self) -> Self where T: Ceil, { Size2D::new(self.width.ceil(), self.height.ceil()) } /// Rounds each component to the biggest integer equal or lower than the original value. /// /// This behavior is preserved for negative values (unlike the basic cast). /// /// ```rust /// # use euclid::size2; /// enum Mm {} /// /// assert_eq!(size2::<_, Mm>(-0.1, -0.8).floor(), size2::<_, Mm>(-1.0, -1.0)) /// ``` #[inline] #[must_use] pub fn floor(self) -> Self where T: Floor, { Size2D::new(self.width.floor(), self.height.floor()) } /// Returns result of multiplication of both components pub fn area(self) -> T::Output where T: Mul, { self.width * self.height } /// Linearly interpolate each component between this size and another size. /// /// # Example /// /// ```rust /// use euclid::size2; /// use euclid::default::Size2D; /// /// let from: Size2D<_> = size2(0.0, 10.0); /// let to: Size2D<_> = size2(8.0, -4.0); /// /// assert_eq!(from.lerp(to, -1.0), size2(-8.0, 24.0)); /// assert_eq!(from.lerp(to, 0.0), size2( 0.0, 10.0)); /// assert_eq!(from.lerp(to, 0.5), size2( 4.0, 3.0)); /// assert_eq!(from.lerp(to, 1.0), size2( 8.0, -4.0)); /// assert_eq!(from.lerp(to, 2.0), size2(16.0, -18.0)); /// ``` #[inline] pub fn lerp(self, other: Self, t: T) -> Self where T: One + Sub + Mul + Add, { let one_t = T::one() - t; self * one_t + other * t } } impl Size2D { /// Cast from one numeric representation to another, preserving the units. /// /// When casting from floating point to integer coordinates, the decimals are truncated /// as one would expect from a simple cast, but this behavior does not always make sense /// geometrically. Consider using `round()`, `ceil()` or `floor()` before casting. #[inline] pub fn cast(self) -> Size2D { self.try_cast().unwrap() } /// Fallible cast from one numeric representation to another, preserving the units. /// /// When casting from floating point to integer coordinates, the decimals are truncated /// as one would expect from a simple cast, but this behavior does not always make sense /// geometrically. Consider using `round()`, `ceil()` or `floor()` before casting. pub fn try_cast(self) -> Option> { match (NumCast::from(self.width), NumCast::from(self.height)) { (Some(w), Some(h)) => Some(Size2D::new(w, h)), _ => None, } } // Convenience functions for common casts /// Cast into an `f32` size. #[inline] pub fn to_f32(self) -> Size2D { self.cast() } /// Cast into an `f64` size. #[inline] pub fn to_f64(self) -> Size2D { self.cast() } /// Cast into an `uint` size, truncating decimals if any. /// /// When casting from floating point sizes, it is worth considering whether /// to `round()`, `ceil()` or `floor()` before the cast in order to obtain /// the desired conversion behavior. #[inline] pub fn to_usize(self) -> Size2D { self.cast() } /// Cast into an `u32` size, truncating decimals if any. /// /// When casting from floating point sizes, it is worth considering whether /// to `round()`, `ceil()` or `floor()` before the cast in order to obtain /// the desired conversion behavior. #[inline] pub fn to_u32(self) -> Size2D { self.cast() } /// Cast into an `u64` size, truncating decimals if any. /// /// When casting from floating point sizes, it is worth considering whether /// to `round()`, `ceil()` or `floor()` before the cast in order to obtain /// the desired conversion behavior. #[inline] pub fn to_u64(self) -> Size2D { self.cast() } /// Cast into an `i32` size, truncating decimals if any. /// /// When casting from floating point sizes, it is worth considering whether /// to `round()`, `ceil()` or `floor()` before the cast in order to obtain /// the desired conversion behavior. #[inline] pub fn to_i32(self) -> Size2D { self.cast() } /// Cast into an `i64` size, truncating decimals if any. /// /// When casting from floating point sizes, it is worth considering whether /// to `round()`, `ceil()` or `floor()` before the cast in order to obtain /// the desired conversion behavior. #[inline] pub fn to_i64(self) -> Size2D { self.cast() } } impl Size2D { /// Returns true if all members are finite. #[inline] pub fn is_finite(self) -> bool { self.width.is_finite() && self.height.is_finite() } } impl Size2D { /// Computes the absolute value of each component. /// /// For `f32` and `f64`, `NaN` will be returned for component if the component is `NaN`. /// /// For signed integers, `::MIN` will be returned for component if the component is `::MIN`. pub fn abs(self) -> Self { size2(self.width.abs(), self.height.abs()) } /// Returns `true` if both components is positive and `false` any component is zero or negative. pub fn is_positive(self) -> bool { self.width.is_positive() && self.height.is_positive() } } impl Size2D { /// Returns the size each component of which are minimum of this size and another. #[inline] pub fn min(self, other: Self) -> Self { size2(min(self.width, other.width), min(self.height, other.height)) } /// Returns the size each component of which are maximum of this size and another. #[inline] pub fn max(self, other: Self) -> Self { size2(max(self.width, other.width), max(self.height, other.height)) } /// Returns the size each component of which clamped by corresponding /// components of `start` and `end`. /// /// Shortcut for `self.max(start).min(end)`. #[inline] pub fn clamp(self, start: Self, end: Self) -> Self where T: Copy, { self.max(start).min(end) } // Returns true if this size is larger or equal to the other size in all dimensions. #[inline] pub fn contains(self, other: Self) -> bool { self.width >= other.width && self.height >= other.height } /// Returns vector with results of "greater then" operation on each component. pub fn greater_than(self, other: Self) -> BoolVector2D { BoolVector2D { x: self.width > other.width, y: self.height > other.height, } } /// Returns vector with results of "lower then" operation on each component. pub fn lower_than(self, other: Self) -> BoolVector2D { BoolVector2D { x: self.width < other.width, y: self.height < other.height, } } /// Returns `true` if any component of size is zero, negative, or NaN. pub fn is_empty(self) -> bool where T: Zero, { let zero = T::zero(); // The condition is experessed this way so that we return true in // the presence of NaN. !(self.width > zero && self.height > zero) } } impl Size2D { /// Returns vector with results of "equal" operation on each component. pub fn equal(self, other: Self) -> BoolVector2D { BoolVector2D { x: self.width == other.width, y: self.height == other.height, } } /// Returns vector with results of "not equal" operation on each component. pub fn not_equal(self, other: Self) -> BoolVector2D { BoolVector2D { x: self.width != other.width, y: self.height != other.height, } } } impl Round for Size2D { /// See [`Size2D::round()`](#method.round). #[inline] fn round(self) -> Self { self.round() } } impl Ceil for Size2D { /// See [`Size2D::ceil()`](#method.ceil). #[inline] fn ceil(self) -> Self { self.ceil() } } impl Floor for Size2D { /// See [`Size2D::floor()`](#method.floor). #[inline] fn floor(self) -> Self { self.floor() } } impl Zero for Size2D { #[inline] fn zero() -> Self { Size2D::new(Zero::zero(), Zero::zero()) } } impl Neg for Size2D { type Output = Size2D; #[inline] fn neg(self) -> Self::Output { Size2D::new(-self.width, -self.height) } } impl Add for Size2D { type Output = Size2D; #[inline] fn add(self, other: Self) -> Self::Output { Size2D::new(self.width + other.width, self.height + other.height) } } impl, U> Add<&Self> for Size2D { type Output = Self; fn add(self, other: &Self) -> Self { Size2D::new(self.width + other.width, self.height + other.height) } } impl + Zero, U> Sum for Size2D { fn sum>(iter: I) -> Self { iter.fold(Self::zero(), Add::add) } } impl<'a, T: 'a + Add + Copy + Zero, U: 'a> Sum<&'a Self> for Size2D { fn sum>(iter: I) -> Self { iter.fold(Self::zero(), Add::add) } } impl AddAssign for Size2D { #[inline] fn add_assign(&mut self, other: Self) { self.width += other.width; self.height += other.height; } } impl Sub for Size2D { type Output = Size2D; #[inline] fn sub(self, other: Self) -> Self::Output { Size2D::new(self.width - other.width, self.height - other.height) } } impl SubAssign for Size2D { #[inline] fn sub_assign(&mut self, other: Self) { self.width -= other.width; self.height -= other.height; } } impl Mul for Size2D { type Output = Size2D; #[inline] fn mul(self, scale: T) -> Self::Output { Size2D::new(self.width * scale, self.height * scale) } } impl MulAssign for Size2D { #[inline] fn mul_assign(&mut self, other: T) { self.width *= other; self.height *= other; } } impl Mul> for Size2D { type Output = Size2D; #[inline] fn mul(self, scale: Scale) -> Self::Output { Size2D::new(self.width * scale.0, self.height * scale.0) } } impl MulAssign> for Size2D { #[inline] fn mul_assign(&mut self, other: Scale) { *self *= other.0; } } impl Div for Size2D { type Output = Size2D; #[inline] fn div(self, scale: T) -> Self::Output { Size2D::new(self.width / scale, self.height / scale) } } impl DivAssign for Size2D { #[inline] fn div_assign(&mut self, other: T) { self.width /= other; self.height /= other; } } impl Div> for Size2D { type Output = Size2D; #[inline] fn div(self, scale: Scale) -> Self::Output { Size2D::new(self.width / scale.0, self.height / scale.0) } } impl DivAssign> for Size2D { #[inline] fn div_assign(&mut self, other: Scale) { *self /= other.0; } } /// Shorthand for `Size2D::new(w, h)`. #[inline] pub const fn size2(w: T, h: T) -> Size2D { Size2D::new(w, h) } #[cfg(feature = "mint")] impl From> for Size2D { #[inline] fn from(v: mint::Vector2) -> Self { Size2D { width: v.x, height: v.y, _unit: PhantomData, } } } #[cfg(feature = "mint")] impl Into> for Size2D { #[inline] fn into(self) -> mint::Vector2 { mint::Vector2 { x: self.width, y: self.height, } } } impl From> for Size2D { #[inline] fn from(v: Vector2D) -> Self { size2(v.x, v.y) } } impl Into<[T; 2]> for Size2D { #[inline] fn into(self) -> [T; 2] { [self.width, self.height] } } impl From<[T; 2]> for Size2D { #[inline] fn from([w, h]: [T; 2]) -> Self { size2(w, h) } } impl Into<(T, T)> for Size2D { #[inline] fn into(self) -> (T, T) { (self.width, self.height) } } impl From<(T, T)> for Size2D { #[inline] fn from(tuple: (T, T)) -> Self { size2(tuple.0, tuple.1) } } #[cfg(test)] mod size2d { use crate::default::Size2D; #[cfg(feature = "mint")] use mint; #[test] pub fn test_area() { let p = Size2D::new(1.5, 2.0); assert_eq!(p.area(), 3.0); } #[cfg(feature = "mint")] #[test] pub fn test_mint() { let s1 = Size2D::new(1.0, 2.0); let sm: mint::Vector2<_> = s1.into(); let s2 = Size2D::from(sm); assert_eq!(s1, s2); } mod ops { use crate::default::Size2D; use crate::scale::Scale; pub enum Mm {} pub enum Cm {} pub type Size2DMm = crate::Size2D; pub type Size2DCm = crate::Size2D; #[test] pub fn test_neg() { assert_eq!(-Size2D::new(1.0, 2.0), Size2D::new(-1.0, -2.0)); assert_eq!(-Size2D::new(0.0, 0.0), Size2D::new(-0.0, -0.0)); assert_eq!(-Size2D::new(-1.0, -2.0), Size2D::new(1.0, 2.0)); } #[test] pub fn test_add() { let s1 = Size2D::new(1.0, 2.0); let s2 = Size2D::new(3.0, 4.0); assert_eq!(s1 + s2, Size2D::new(4.0, 6.0)); assert_eq!(s1 + &s2, Size2D::new(4.0, 6.0)); let s1 = Size2D::new(1.0, 2.0); let s2 = Size2D::new(0.0, 0.0); assert_eq!(s1 + s2, Size2D::new(1.0, 2.0)); assert_eq!(s1 + &s2, Size2D::new(1.0, 2.0)); let s1 = Size2D::new(1.0, 2.0); let s2 = Size2D::new(-3.0, -4.0); assert_eq!(s1 + s2, Size2D::new(-2.0, -2.0)); assert_eq!(s1 + &s2, Size2D::new(-2.0, -2.0)); let s1 = Size2D::new(0.0, 0.0); let s2 = Size2D::new(0.0, 0.0); assert_eq!(s1 + s2, Size2D::new(0.0, 0.0)); assert_eq!(s1 + &s2, Size2D::new(0.0, 0.0)); } #[test] pub fn test_add_assign() { let mut s = Size2D::new(1.0, 2.0); s += Size2D::new(3.0, 4.0); assert_eq!(s, Size2D::new(4.0, 6.0)); let mut s = Size2D::new(1.0, 2.0); s += Size2D::new(0.0, 0.0); assert_eq!(s, Size2D::new(1.0, 2.0)); let mut s = Size2D::new(1.0, 2.0); s += Size2D::new(-3.0, -4.0); assert_eq!(s, Size2D::new(-2.0, -2.0)); let mut s = Size2D::new(0.0, 0.0); s += Size2D::new(0.0, 0.0); assert_eq!(s, Size2D::new(0.0, 0.0)); } #[test] pub fn test_sum() { let sizes = [ Size2D::new(0.0, 1.0), Size2D::new(1.0, 2.0), Size2D::new(2.0, 3.0) ]; let sum = Size2D::new(3.0, 6.0); assert_eq!(sizes.iter().sum::>(), sum); } #[test] pub fn test_sub() { let s1 = Size2D::new(1.0, 2.0); let s2 = Size2D::new(3.0, 4.0); assert_eq!(s1 - s2, Size2D::new(-2.0, -2.0)); let s1 = Size2D::new(1.0, 2.0); let s2 = Size2D::new(0.0, 0.0); assert_eq!(s1 - s2, Size2D::new(1.0, 2.0)); let s1 = Size2D::new(1.0, 2.0); let s2 = Size2D::new(-3.0, -4.0); assert_eq!(s1 - s2, Size2D::new(4.0, 6.0)); let s1 = Size2D::new(0.0, 0.0); let s2 = Size2D::new(0.0, 0.0); assert_eq!(s1 - s2, Size2D::new(0.0, 0.0)); } #[test] pub fn test_sub_assign() { let mut s = Size2D::new(1.0, 2.0); s -= Size2D::new(3.0, 4.0); assert_eq!(s, Size2D::new(-2.0, -2.0)); let mut s = Size2D::new(1.0, 2.0); s -= Size2D::new(0.0, 0.0); assert_eq!(s, Size2D::new(1.0, 2.0)); let mut s = Size2D::new(1.0, 2.0); s -= Size2D::new(-3.0, -4.0); assert_eq!(s, Size2D::new(4.0, 6.0)); let mut s = Size2D::new(0.0, 0.0); s -= Size2D::new(0.0, 0.0); assert_eq!(s, Size2D::new(0.0, 0.0)); } #[test] pub fn test_mul_scalar() { let s1: Size2D = Size2D::new(3.0, 5.0); let result = s1 * 5.0; assert_eq!(result, Size2D::new(15.0, 25.0)); } #[test] pub fn test_mul_assign_scalar() { let mut s1 = Size2D::new(3.0, 5.0); s1 *= 5.0; assert_eq!(s1, Size2D::new(15.0, 25.0)); } #[test] pub fn test_mul_scale() { let s1 = Size2DMm::new(1.0, 2.0); let cm_per_mm: Scale = Scale::new(0.1); let result = s1 * cm_per_mm; assert_eq!(result, Size2DCm::new(0.1, 0.2)); } #[test] pub fn test_mul_assign_scale() { let mut s1 = Size2DMm::new(1.0, 2.0); let scale: Scale = Scale::new(0.1); s1 *= scale; assert_eq!(s1, Size2DMm::new(0.1, 0.2)); } #[test] pub fn test_div_scalar() { let s1: Size2D = Size2D::new(15.0, 25.0); let result = s1 / 5.0; assert_eq!(result, Size2D::new(3.0, 5.0)); } #[test] pub fn test_div_assign_scalar() { let mut s1: Size2D = Size2D::new(15.0, 25.0); s1 /= 5.0; assert_eq!(s1, Size2D::new(3.0, 5.0)); } #[test] pub fn test_div_scale() { let s1 = Size2DCm::new(0.1, 0.2); let cm_per_mm: Scale = Scale::new(0.1); let result = s1 / cm_per_mm; assert_eq!(result, Size2DMm::new(1.0, 2.0)); } #[test] pub fn test_div_assign_scale() { let mut s1 = Size2DMm::new(0.1, 0.2); let scale: Scale = Scale::new(0.1); s1 /= scale; assert_eq!(s1, Size2DMm::new(1.0, 2.0)); } #[test] pub fn test_nan_empty() { use std::f32::NAN; assert!(Size2D::new(NAN, 2.0).is_empty()); assert!(Size2D::new(0.0, NAN).is_empty()); assert!(Size2D::new(NAN, -2.0).is_empty()); } } } /// A 3d size tagged with a unit. #[repr(C)] pub struct Size3D { /// The extent of the element in the `U` units along the `x` axis. pub width: T, /// The extent of the element in the `U` units along the `y` axis. pub height: T, /// The extent of the element in the `U` units along the `z` axis. pub depth: T, #[doc(hidden)] pub _unit: PhantomData, } impl Copy for Size3D {} impl Clone for Size3D { fn clone(&self) -> Self { Size3D { width: self.width.clone(), height: self.height.clone(), depth: self.depth.clone(), _unit: PhantomData, } } } #[cfg(feature = "serde")] impl<'de, T, U> serde::Deserialize<'de> for Size3D where T: serde::Deserialize<'de>, { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { let (width, height, depth) = serde::Deserialize::deserialize(deserializer)?; Ok(Size3D { width, height, depth, _unit: PhantomData, }) } } #[cfg(feature = "serde")] impl serde::Serialize for Size3D where T: serde::Serialize, { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, { (&self.width, &self.height, &self.depth).serialize(serializer) } } #[cfg(feature = "bytemuck")] unsafe impl Zeroable for Size3D {} #[cfg(feature = "bytemuck")] unsafe impl Pod for Size3D {} impl Eq for Size3D where T: Eq {} impl PartialEq for Size3D where T: PartialEq, { fn eq(&self, other: &Self) -> bool { self.width == other.width && self.height == other.height && self.depth == other.depth } } impl Hash for Size3D where T: Hash, { fn hash(&self, h: &mut H) { self.width.hash(h); self.height.hash(h); self.depth.hash(h); } } impl fmt::Debug for Size3D { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(&self.width, f)?; write!(f, "x")?; fmt::Debug::fmt(&self.height, f)?; write!(f, "x")?; fmt::Debug::fmt(&self.depth, f) } } impl Default for Size3D { fn default() -> Self { Size3D::new(Default::default(), Default::default(), Default::default()) } } impl Size3D { /// The same as [`Zero::zero()`] but available without importing trait. /// /// [`Zero::zero()`]: ./num/trait.Zero.html#tymethod.zero pub fn zero() -> Self where T: Zero, { Size3D::new(Zero::zero(), Zero::zero(), Zero::zero()) } /// Constructor taking scalar values. #[inline] pub const fn new(width: T, height: T, depth: T) -> Self { Size3D { width, height, depth, _unit: PhantomData, } } /// Constructor taking scalar strongly typed lengths. #[inline] pub fn from_lengths(width: Length, height: Length, depth: Length) -> Self { Size3D::new(width.0, height.0, depth.0) } /// Constructor setting all components to the same value. #[inline] pub fn splat(v: T) -> Self where T: Clone, { Size3D { width: v.clone(), height: v.clone(), depth: v, _unit: PhantomData, } } /// Tag a unitless value with units. #[inline] pub fn from_untyped(p: Size3D) -> Self { Size3D::new(p.width, p.height, p.depth) } } impl Size3D { /// Return this size as an array of three elements (width, then height, then depth). #[inline] pub fn to_array(self) -> [T; 3] { [self.width, self.height, self.depth] } /// Return this size as an array of three elements (width, then height, then depth). #[inline] pub fn to_tuple(self) -> (T, T, T) { (self.width, self.height, self.depth) } /// Return this size as a vector with width, height and depth. #[inline] pub fn to_vector(self) -> Vector3D { vec3(self.width, self.height, self.depth) } /// Drop the units, preserving only the numeric value. #[inline] pub fn to_untyped(self) -> Size3D { self.cast_unit() } /// Cast the unit #[inline] pub fn cast_unit(self) -> Size3D { Size3D::new(self.width, self.height, self.depth) } /// Rounds each component to the nearest integer value. /// /// This behavior is preserved for negative values (unlike the basic cast). /// /// ```rust /// # use euclid::size3; /// enum Mm {} /// /// assert_eq!(size3::<_, Mm>(-0.1, -0.8, 0.4).round(), size3::<_, Mm>(0.0, -1.0, 0.0)) /// ``` #[inline] #[must_use] pub fn round(self) -> Self where T: Round, { Size3D::new(self.width.round(), self.height.round(), self.depth.round()) } /// Rounds each component to the smallest integer equal or greater than the original value. /// /// This behavior is preserved for negative values (unlike the basic cast). /// /// ```rust /// # use euclid::size3; /// enum Mm {} /// /// assert_eq!(size3::<_, Mm>(-0.1, -0.8, 0.4).ceil(), size3::<_, Mm>(0.0, 0.0, 1.0)) /// ``` #[inline] #[must_use] pub fn ceil(self) -> Self where T: Ceil, { Size3D::new(self.width.ceil(), self.height.ceil(), self.depth.ceil()) } /// Rounds each component to the biggest integer equal or lower than the original value. /// /// This behavior is preserved for negative values (unlike the basic cast). /// /// ```rust /// # use euclid::size3; /// enum Mm {} /// /// assert_eq!(size3::<_, Mm>(-0.1, -0.8, 0.4).floor(), size3::<_, Mm>(-1.0, -1.0, 0.0)) /// ``` #[inline] #[must_use] pub fn floor(self) -> Self where T: Floor, { Size3D::new(self.width.floor(), self.height.floor(), self.depth.floor()) } /// Returns result of multiplication of all components pub fn volume(self) -> T where T: Mul, { self.width * self.height * self.depth } /// Linearly interpolate between this size and another size. /// /// # Example /// /// ```rust /// use euclid::size3; /// use euclid::default::Size3D; /// /// let from: Size3D<_> = size3(0.0, 10.0, -1.0); /// let to: Size3D<_> = size3(8.0, -4.0, 0.0); /// /// assert_eq!(from.lerp(to, -1.0), size3(-8.0, 24.0, -2.0)); /// assert_eq!(from.lerp(to, 0.0), size3( 0.0, 10.0, -1.0)); /// assert_eq!(from.lerp(to, 0.5), size3( 4.0, 3.0, -0.5)); /// assert_eq!(from.lerp(to, 1.0), size3( 8.0, -4.0, 0.0)); /// assert_eq!(from.lerp(to, 2.0), size3(16.0, -18.0, 1.0)); /// ``` #[inline] pub fn lerp(self, other: Self, t: T) -> Self where T: One + Sub + Mul + Add, { let one_t = T::one() - t; self * one_t + other * t } } impl Size3D { /// Cast from one numeric representation to another, preserving the units. /// /// When casting from floating point to integer coordinates, the decimals are truncated /// as one would expect from a simple cast, but this behavior does not always make sense /// geometrically. Consider using `round()`, `ceil()` or `floor()` before casting. #[inline] pub fn cast(self) -> Size3D { self.try_cast().unwrap() } /// Fallible cast from one numeric representation to another, preserving the units. /// /// When casting from floating point to integer coordinates, the decimals are truncated /// as one would expect from a simple cast, but this behavior does not always make sense /// geometrically. Consider using `round()`, `ceil()` or `floor()` before casting. pub fn try_cast(self) -> Option> { match ( NumCast::from(self.width), NumCast::from(self.height), NumCast::from(self.depth), ) { (Some(w), Some(h), Some(d)) => Some(Size3D::new(w, h, d)), _ => None, } } // Convenience functions for common casts /// Cast into an `f32` size. #[inline] pub fn to_f32(self) -> Size3D { self.cast() } /// Cast into an `f64` size. #[inline] pub fn to_f64(self) -> Size3D { self.cast() } /// Cast into an `uint` size, truncating decimals if any. /// /// When casting from floating point sizes, it is worth considering whether /// to `round()`, `ceil()` or `floor()` before the cast in order to obtain /// the desired conversion behavior. #[inline] pub fn to_usize(self) -> Size3D { self.cast() } /// Cast into an `u32` size, truncating decimals if any. /// /// When casting from floating point sizes, it is worth considering whether /// to `round()`, `ceil()` or `floor()` before the cast in order to obtain /// the desired conversion behavior. #[inline] pub fn to_u32(self) -> Size3D { self.cast() } /// Cast into an `i32` size, truncating decimals if any. /// /// When casting from floating point sizes, it is worth considering whether /// to `round()`, `ceil()` or `floor()` before the cast in order to obtain /// the desired conversion behavior. #[inline] pub fn to_i32(self) -> Size3D { self.cast() } /// Cast into an `i64` size, truncating decimals if any. /// /// When casting from floating point sizes, it is worth considering whether /// to `round()`, `ceil()` or `floor()` before the cast in order to obtain /// the desired conversion behavior. #[inline] pub fn to_i64(self) -> Size3D { self.cast() } } impl Size3D { /// Returns true if all members are finite. #[inline] pub fn is_finite(self) -> bool { self.width.is_finite() && self.height.is_finite() && self.depth.is_finite() } } impl Size3D { /// Computes the absolute value of each component. /// /// For `f32` and `f64`, `NaN` will be returned for component if the component is `NaN`. /// /// For signed integers, `::MIN` will be returned for component if the component is `::MIN`. pub fn abs(self) -> Self { size3(self.width.abs(), self.height.abs(), self.depth.abs()) } /// Returns `true` if all components is positive and `false` any component is zero or negative. pub fn is_positive(self) -> bool { self.width.is_positive() && self.height.is_positive() && self.depth.is_positive() } } impl Size3D { /// Returns the size each component of which are minimum of this size and another. #[inline] pub fn min(self, other: Self) -> Self { size3( min(self.width, other.width), min(self.height, other.height), min(self.depth, other.depth), ) } /// Returns the size each component of which are maximum of this size and another. #[inline] pub fn max(self, other: Self) -> Self { size3( max(self.width, other.width), max(self.height, other.height), max(self.depth, other.depth), ) } /// Returns the size each component of which clamped by corresponding /// components of `start` and `end`. /// /// Shortcut for `self.max(start).min(end)`. #[inline] pub fn clamp(self, start: Self, end: Self) -> Self where T: Copy, { self.max(start).min(end) } // Returns true if this size is larger or equal to the other size in all dimensions. #[inline] pub fn contains(self, other: Self) -> bool { self.width >= other.width && self.height >= other.height && self.depth >= other.depth } /// Returns vector with results of "greater than" operation on each component. pub fn greater_than(self, other: Self) -> BoolVector3D { BoolVector3D { x: self.width > other.width, y: self.height > other.height, z: self.depth > other.depth, } } /// Returns vector with results of "lower than" operation on each component. pub fn lower_than(self, other: Self) -> BoolVector3D { BoolVector3D { x: self.width < other.width, y: self.height < other.height, z: self.depth < other.depth, } } /// Returns `true` if any component of size is zero, negative or NaN. pub fn is_empty(self) -> bool where T: Zero, { let zero = T::zero(); !(self.width > zero && self.height > zero && self.depth <= zero) } } impl Size3D { /// Returns vector with results of "equal" operation on each component. pub fn equal(self, other: Self) -> BoolVector3D { BoolVector3D { x: self.width == other.width, y: self.height == other.height, z: self.depth == other.depth, } } /// Returns vector with results of "not equal" operation on each component. pub fn not_equal(self, other: Self) -> BoolVector3D { BoolVector3D { x: self.width != other.width, y: self.height != other.height, z: self.depth != other.depth, } } } impl Round for Size3D { /// See [`Size3D::round()`](#method.round). #[inline] fn round(self) -> Self { self.round() } } impl Ceil for Size3D { /// See [`Size3D::ceil()`](#method.ceil). #[inline] fn ceil(self) -> Self { self.ceil() } } impl Floor for Size3D { /// See [`Size3D::floor()`](#method.floor). #[inline] fn floor(self) -> Self { self.floor() } } impl Zero for Size3D { #[inline] fn zero() -> Self { Size3D::new(Zero::zero(), Zero::zero(), Zero::zero()) } } impl Neg for Size3D { type Output = Size3D; #[inline] fn neg(self) -> Self::Output { Size3D::new(-self.width, -self.height, -self.depth) } } impl Add for Size3D { type Output = Size3D; #[inline] fn add(self, other: Self) -> Self::Output { Size3D::new( self.width + other.width, self.height + other.height, self.depth + other.depth, ) } } impl, U> Add<&Self> for Size3D { type Output = Self; fn add(self, other: &Self) -> Self { Size3D::new( self.width + other.width, self.height + other.height, self.depth + other.depth, ) } } impl + Zero, U> Sum for Size3D { fn sum>(iter: I) -> Self { iter.fold(Self::zero(), Add::add) } } impl<'a, T: 'a + Add + Copy + Zero, U: 'a> Sum<&'a Self> for Size3D { fn sum>(iter: I) -> Self { iter.fold(Self::zero(), Add::add) } } impl AddAssign for Size3D { #[inline] fn add_assign(&mut self, other: Self) { self.width += other.width; self.height += other.height; self.depth += other.depth; } } impl Sub for Size3D { type Output = Size3D; #[inline] fn sub(self, other: Self) -> Self::Output { Size3D::new( self.width - other.width, self.height - other.height, self.depth - other.depth, ) } } impl SubAssign for Size3D { #[inline] fn sub_assign(&mut self, other: Self) { self.width -= other.width; self.height -= other.height; self.depth -= other.depth; } } impl Mul for Size3D { type Output = Size3D; #[inline] fn mul(self, scale: T) -> Self::Output { Size3D::new( self.width * scale, self.height * scale, self.depth * scale, ) } } impl MulAssign for Size3D { #[inline] fn mul_assign(&mut self, other: T) { self.width *= other; self.height *= other; self.depth *= other; } } impl Mul> for Size3D { type Output = Size3D; #[inline] fn mul(self, scale: Scale) -> Self::Output { Size3D::new( self.width * scale.0, self.height * scale.0, self.depth * scale.0, ) } } impl MulAssign> for Size3D { #[inline] fn mul_assign(&mut self, other: Scale) { *self *= other.0; } } impl Div for Size3D { type Output = Size3D; #[inline] fn div(self, scale: T) -> Self::Output { Size3D::new( self.width / scale, self.height / scale, self.depth / scale, ) } } impl DivAssign for Size3D { #[inline] fn div_assign(&mut self, other: T) { self.width /= other; self.height /= other; self.depth /= other; } } impl Div> for Size3D { type Output = Size3D; #[inline] fn div(self, scale: Scale) -> Self::Output { Size3D::new( self.width / scale.0, self.height / scale.0, self.depth / scale.0, ) } } impl DivAssign> for Size3D { #[inline] fn div_assign(&mut self, other: Scale) { *self /= other.0; } } #[cfg(feature = "mint")] impl From> for Size3D { #[inline] fn from(v: mint::Vector3) -> Self { size3(v.x, v.y, v.z) } } #[cfg(feature = "mint")] impl Into> for Size3D { #[inline] fn into(self) -> mint::Vector3 { mint::Vector3 { x: self.width, y: self.height, z: self.depth, } } } impl From> for Size3D { #[inline] fn from(v: Vector3D) -> Self { size3(v.x, v.y, v.z) } } impl Into<[T; 3]> for Size3D { #[inline] fn into(self) -> [T; 3] { [self.width, self.height, self.depth] } } impl From<[T; 3]> for Size3D { #[inline] fn from([w, h, d]: [T; 3]) -> Self { size3(w, h, d) } } impl Into<(T, T, T)> for Size3D { #[inline] fn into(self) -> (T, T, T) { (self.width, self.height, self.depth) } } impl From<(T, T, T)> for Size3D { #[inline] fn from(tuple: (T, T, T)) -> Self { size3(tuple.0, tuple.1, tuple.2) } } /// Shorthand for `Size3D::new(w, h, d)`. #[inline] pub const fn size3(w: T, h: T, d: T) -> Size3D { Size3D::new(w, h, d) } #[cfg(test)] mod size3d { mod ops { use crate::default::Size3D; use crate::scale::Scale; pub enum Mm {} pub enum Cm {} pub type Size3DMm = crate::Size3D; pub type Size3DCm = crate::Size3D; #[test] pub fn test_neg() { assert_eq!(-Size3D::new(1.0, 2.0, 3.0), Size3D::new(-1.0, -2.0, -3.0)); assert_eq!(-Size3D::new(0.0, 0.0, 0.0), Size3D::new(-0.0, -0.0, -0.0)); assert_eq!(-Size3D::new(-1.0, -2.0, -3.0), Size3D::new(1.0, 2.0, 3.0)); } #[test] pub fn test_add() { let s1 = Size3D::new(1.0, 2.0, 3.0); let s2 = Size3D::new(4.0, 5.0, 6.0); assert_eq!(s1 + s2, Size3D::new(5.0, 7.0, 9.0)); assert_eq!(s1 + &s2, Size3D::new(5.0, 7.0, 9.0)); let s1 = Size3D::new(1.0, 2.0, 3.0); let s2 = Size3D::new(0.0, 0.0, 0.0); assert_eq!(s1 + s2, Size3D::new(1.0, 2.0, 3.0)); assert_eq!(s1 + &s2, Size3D::new(1.0, 2.0, 3.0)); let s1 = Size3D::new(1.0, 2.0, 3.0); let s2 = Size3D::new(-4.0, -5.0, -6.0); assert_eq!(s1 + s2, Size3D::new(-3.0, -3.0, -3.0)); assert_eq!(s1 + &s2, Size3D::new(-3.0, -3.0, -3.0)); let s1 = Size3D::new(0.0, 0.0, 0.0); let s2 = Size3D::new(0.0, 0.0, 0.0); assert_eq!(s1 + s2, Size3D::new(0.0, 0.0, 0.0)); assert_eq!(s1 + &s2, Size3D::new(0.0, 0.0, 0.0)); } #[test] pub fn test_sum() { let sizes = [ Size3D::new(0.0, 1.0, 2.0), Size3D::new(1.0, 2.0, 3.0), Size3D::new(2.0, 3.0, 4.0) ]; let sum = Size3D::new(3.0, 6.0, 9.0); assert_eq!(sizes.iter().sum::>(), sum); } #[test] pub fn test_add_assign() { let mut s = Size3D::new(1.0, 2.0, 3.0); s += Size3D::new(4.0, 5.0, 6.0); assert_eq!(s, Size3D::new(5.0, 7.0, 9.0)); let mut s = Size3D::new(1.0, 2.0, 3.0); s += Size3D::new(0.0, 0.0, 0.0); assert_eq!(s, Size3D::new(1.0, 2.0, 3.0)); let mut s = Size3D::new(1.0, 2.0, 3.0); s += Size3D::new(-4.0, -5.0, -6.0); assert_eq!(s, Size3D::new(-3.0, -3.0, -3.0)); let mut s = Size3D::new(0.0, 0.0, 0.0); s += Size3D::new(0.0, 0.0, 0.0); assert_eq!(s, Size3D::new(0.0, 0.0, 0.0)); } #[test] pub fn test_sub() { let s1 = Size3D::new(1.0, 2.0, 3.0); let s2 = Size3D::new(4.0, 5.0, 6.0); assert_eq!(s1 - s2, Size3D::new(-3.0, -3.0, -3.0)); let s1 = Size3D::new(1.0, 2.0, 3.0); let s2 = Size3D::new(0.0, 0.0, 0.0); assert_eq!(s1 - s2, Size3D::new(1.0, 2.0, 3.0)); let s1 = Size3D::new(1.0, 2.0, 3.0); let s2 = Size3D::new(-4.0, -5.0, -6.0); assert_eq!(s1 - s2, Size3D::new(5.0, 7.0, 9.0)); let s1 = Size3D::new(0.0, 0.0, 0.0); let s2 = Size3D::new(0.0, 0.0, 0.0); assert_eq!(s1 - s2, Size3D::new(0.0, 0.0, 0.0)); } #[test] pub fn test_sub_assign() { let mut s = Size3D::new(1.0, 2.0, 3.0); s -= Size3D::new(4.0, 5.0, 6.0); assert_eq!(s, Size3D::new(-3.0, -3.0, -3.0)); let mut s = Size3D::new(1.0, 2.0, 3.0); s -= Size3D::new(0.0, 0.0, 0.0); assert_eq!(s, Size3D::new(1.0, 2.0, 3.0)); let mut s = Size3D::new(1.0, 2.0, 3.0); s -= Size3D::new(-4.0, -5.0, -6.0); assert_eq!(s, Size3D::new(5.0, 7.0, 9.0)); let mut s = Size3D::new(0.0, 0.0, 0.0); s -= Size3D::new(0.0, 0.0, 0.0); assert_eq!(s, Size3D::new(0.0, 0.0, 0.0)); } #[test] pub fn test_mul_scalar() { let s1: Size3D = Size3D::new(3.0, 5.0, 7.0); let result = s1 * 5.0; assert_eq!(result, Size3D::new(15.0, 25.0, 35.0)); } #[test] pub fn test_mul_assign_scalar() { let mut s1: Size3D = Size3D::new(3.0, 5.0, 7.0); s1 *= 5.0; assert_eq!(s1, Size3D::new(15.0, 25.0, 35.0)); } #[test] pub fn test_mul_scale() { let s1 = Size3DMm::new(1.0, 2.0, 3.0); let cm_per_mm: Scale = Scale::new(0.1); let result = s1 * cm_per_mm; assert_eq!(result, Size3DCm::new(0.1, 0.2, 0.3)); } #[test] pub fn test_mul_assign_scale() { let mut s1 = Size3DMm::new(1.0, 2.0, 3.0); let scale: Scale = Scale::new(0.1); s1 *= scale; assert_eq!(s1, Size3DMm::new(0.1, 0.2, 0.3)); } #[test] pub fn test_div_scalar() { let s1: Size3D = Size3D::new(15.0, 25.0, 35.0); let result = s1 / 5.0; assert_eq!(result, Size3D::new(3.0, 5.0, 7.0)); } #[test] pub fn test_div_assign_scalar() { let mut s1: Size3D = Size3D::new(15.0, 25.0, 35.0); s1 /= 5.0; assert_eq!(s1, Size3D::new(3.0, 5.0, 7.0)); } #[test] pub fn test_div_scale() { let s1 = Size3DCm::new(0.1, 0.2, 0.3); let cm_per_mm: Scale = Scale::new(0.1); let result = s1 / cm_per_mm; assert_eq!(result, Size3DMm::new(1.0, 2.0, 3.0)); } #[test] pub fn test_div_assign_scale() { let mut s1 = Size3DMm::new(0.1, 0.2, 0.3); let scale: Scale = Scale::new(0.1); s1 /= scale; assert_eq!(s1, Size3DMm::new(1.0, 2.0, 3.0)); } #[test] pub fn test_nan_empty() { use std::f32::NAN; assert!(Size3D::new(NAN, 2.0, 3.0).is_empty()); assert!(Size3D::new(0.0, NAN, 0.0).is_empty()); assert!(Size3D::new(1.0, 2.0, NAN).is_empty()); } } }