diff --git a/src/length.rs b/src/length.rs index 08604e5..443fb96 100644 --- a/src/length.rs +++ b/src/length.rs @@ -12,7 +12,7 @@ use scale::TypedScale; use num::Zero; use num_traits::{NumCast, Saturating}; -use num::One; +use num::{One, ValueOrLength}; #[cfg(feature = "serde")] use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::cmp::Ordering; @@ -254,6 +254,11 @@ where } } +impl ValueOrLength for Length { + #[inline] + fn value(self) -> T { self.0 } +} + #[cfg(test)] mod tests { use super::Length; diff --git a/src/lib.rs b/src/lib.rs index 7ba29e9..1d928a4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -50,9 +50,9 @@ //! // p.x is an f32. //! println!("p.x = {:?} ", p.x); //! // p.x is a Length. -//! println!("p.x_typed() = {:?} ", p.x_typed()); +//! println!("p.get_x() = {:?} ", p.get_x()); //! // Length::get returns the scalar value (f32). -//! assert_eq!(p.x, p.x_typed().get()); +//! assert_eq!(p.x, p.get_x().get()); //! ``` #[cfg(feature = "serde")] diff --git a/src/num.rs b/src/num.rs index 1df86d7..86ad761 100644 --- a/src/num.rs +++ b/src/num.rs @@ -10,6 +10,46 @@ use num_traits; +/// A trait for parameters that can be either a `Length` or the raw +/// scalar value `T`. +/// +/// This makes it possible to write methods that accept both, for example `TypedVector2D::new` +/// works with both strongly typed length arguments and direct scalar values: +/// +/// ``` +/// use euclid::{Length, TypedVector2D}; +/// struct WorldSpace; +/// struct ScreenSpace; +/// type WorldVec2 = TypedVector2D; +/// type WorldLength = Length; +/// type ScreenLength = Length; +/// // Convenient synatx: +/// let v1 = WorldVec2::new(1.0, 2.0); +/// // Statically checked synatx: +/// let v2 = WorldVec2::new(WorldLength::new(1.0), WorldLength::new(2.0)); +/// // This would give a compile time error because units do not match: +/// // let not_good = WorldVec2::new(ScreenLength:new(1.0), ScreenLength::new(2.0)); +/// ``` +pub trait ValueOrLength { + fn value(self) -> T; +} + +impl ValueOrLength for T { + #[inline] + fn value(self) -> T { self } +} + + +pub trait ValueOrScale { + fn value(self) -> T; +} + +impl ValueOrScale for T { + #[inline] + fn value(self) -> T { self } +} + + pub trait Zero { fn zero() -> Self; } diff --git a/src/point.rs b/src/point.rs index 23ba2ae..0b58cf1 100644 --- a/src/point.rs +++ b/src/point.rs @@ -32,11 +32,24 @@ define_matrix! { /// `Point2D` provides the same methods as `TypedPoint2D`. pub type Point2D = TypedPoint2D; +impl TypedPoint2D { + /// Constructor taking scalar values or `Length`. + #[inline] + pub fn new(x: N, y: N) -> Self + where N: ValueOrLength { + TypedPoint2D { + x: x.value(), + y: y.value(), + _unit: PhantomData, + } + } +} + impl TypedPoint2D { /// Constructor, setting all components to zero. #[inline] pub fn origin() -> Self { - point2(Zero::zero(), Zero::zero()) + point2(T::zero(), T::zero()) } #[inline] @@ -64,26 +77,11 @@ impl fmt::Display for TypedPoint2D { } impl TypedPoint2D { - /// Constructor taking scalar values directly. - #[inline] - pub fn new(x: T, y: T) -> Self { - TypedPoint2D { - x: x, - y: y, - _unit: PhantomData, - } - } - - /// Constructor taking properly typed Lengths instead of scalar values. - #[inline] - pub fn from_lengths(x: Length, y: Length) -> Self { - point2(x.0, y.0) - } - /// Create a 3d point from this one, using the specified z value. #[inline] - pub fn extend(&self, z: T) -> TypedPoint3D { - point3(self.x, self.y, z) + pub fn extend(&self, z: N) -> TypedPoint3D + where N: ValueOrLength { + point3(self.x, self.y, z.value()) } /// Cast this point into a vector. @@ -102,13 +100,13 @@ impl TypedPoint2D { /// Returns self.x as a Length carrying the unit. #[inline] - pub fn x_typed(&self) -> Length { + pub fn get_x(&self) -> Length { Length::new(self.x) } /// Returns self.y as a Length carrying the unit. #[inline] - pub fn y_typed(&self) -> Length { + pub fn get_y(&self) -> Length { Length::new(self.y) } @@ -285,7 +283,7 @@ impl TypedPoint2D { /// geometrically. Consider using `round()`, `ceil()` or `floor()` before casting. #[inline] pub fn cast(&self) -> Option> { - match (NumCast::from(self.x), NumCast::from(self.y)) { + match (NewT::from(self.x), NewT::from(self.y)) { (Some(x), Some(y)) => Some(point2(x, y)), _ => None, } @@ -397,7 +395,7 @@ impl TypedPoint3D { /// Constructor, setting all copmonents to zero. #[inline] pub fn origin() -> Self { - point3(Zero::zero(), Zero::zero(), Zero::zero()) + point3(T::zero(), T::zero(), T::zero()) } } @@ -438,24 +436,20 @@ impl fmt::Display for TypedPoint3D { } } -impl TypedPoint3D { - /// Constructor taking scalar values directly. +impl TypedPoint3D { + /// Constructor taking scalar values or `Length`. #[inline] - pub fn new(x: T, y: T, z: T) -> Self { + pub fn new>(x: N, y: N, z: N) -> Self { TypedPoint3D { - x: x, - y: y, - z: z, + x: x.value(), + y: y.value(), + z: z.value(), _unit: PhantomData, } } +} - /// Constructor taking properly typed Lengths instead of scalar values. - #[inline] - pub fn from_lengths(x: Length, y: Length, z: Length) -> Self { - point3(x.0, y.0, z.0) - } - +impl TypedPoint3D { /// Cast this point into a vector. /// /// Equivalent to substracting the origin to this point. @@ -484,19 +478,19 @@ impl TypedPoint3D { /// Returns self.x as a Length carrying the unit. #[inline] - pub fn x_typed(&self) -> Length { + pub fn get_x(&self) -> Length { Length::new(self.x) } /// Returns self.y as a Length carrying the unit. #[inline] - pub fn y_typed(&self) -> Length { + pub fn get_y(&self) -> Length { Length::new(self.y) } /// Returns self.z as a Length carrying the unit. #[inline] - pub fn z_typed(&self) -> Length { + pub fn get_z(&self) -> Length { Length::new(self.z) } @@ -640,9 +634,9 @@ impl TypedPoint3D { #[inline] pub fn cast(&self) -> Option> { match ( - NumCast::from(self.x), - NumCast::from(self.y), - NumCast::from(self.z), + NewT::from(self.x), + NewT::from(self.y), + NewT::from(self.z), ) { (Some(x), Some(y), Some(z)) => Some(point3(x, y, z)), _ => None, @@ -728,11 +722,11 @@ impl From<[T; 3]> for TypedPoint3D { } } -pub fn point2(x: T, y: T) -> TypedPoint2D { +pub fn point2(x: T, y: T) -> TypedPoint2D { TypedPoint2D::new(x, y) } -pub fn point3(x: T, y: T, z: T) -> TypedPoint3D { +pub fn point3(x: T, y: T, z: T) -> TypedPoint3D { TypedPoint3D::new(x, y, z) } diff --git a/src/rect.rs b/src/rect.rs index 620c446..fe98f57 100644 --- a/src/rect.rs +++ b/src/rect.rs @@ -133,22 +133,22 @@ where } #[inline] - pub fn max_x_typed(&self) -> Length { + pub fn get_max_x(&self) -> Length { Length::new(self.max_x()) } #[inline] - pub fn min_x_typed(&self) -> Length { + pub fn get_min_x(&self) -> Length { Length::new(self.min_x()) } #[inline] - pub fn max_y_typed(&self) -> Length { + pub fn get_max_y(&self) -> Length { Length::new(self.max_y()) } #[inline] - pub fn min_y_typed(&self) -> Length { + pub fn get_min_y(&self) -> Length { Length::new(self.min_y()) } @@ -199,22 +199,19 @@ where #[inline] #[cfg_attr(feature = "unstable", must_use)] - pub fn inflate(&self, width: T, height: T) -> Self { + pub fn inflate(&self, width: N, height: N) -> Self + where N: ValueOrLength { + let w = width.value(); + let h = height.value(); TypedRect::new( - TypedPoint2D::new(self.origin.x - width, self.origin.y - height), + TypedPoint2D::new(self.origin.x - w, self.origin.y - h), TypedSize2D::new( - self.size.width + width + width, - self.size.height + height + height, + self.size.width + w + w, + self.size.height + h + h, ), ) } - #[inline] - #[cfg_attr(feature = "unstable", must_use)] - pub fn inflate_typed(&self, width: Length, height: Length) -> Self { - self.inflate(width.get(), height.get()) - } - #[inline] pub fn top_right(&self) -> TypedPoint2D { TypedPoint2D::new(self.max_x(), self.origin.y) diff --git a/src/scale.rs b/src/scale.rs index 8aff0e5..1d2c7f7 100644 --- a/src/scale.rs +++ b/src/scale.rs @@ -8,8 +8,7 @@ // except according to those terms. //! A type-checked scaling factor between units. -use num::One; - +use num::{One, ValueOrScale}; use num_traits::NumCast; #[cfg(feature = "serde")] use serde::{Deserialize, Deserializer, Serialize, Serializer}; @@ -40,6 +39,11 @@ use {TypedPoint2D, TypedRect, TypedSize2D, TypedVector2D}; #[repr(C)] pub struct TypedScale(pub T, PhantomData<(Src, Dst)>); +impl ValueOrScale for TypedScale { + #[inline] + fn value(self) -> T { self.0 } +} + #[cfg(feature = "serde")] impl<'de, T, Src, Dst> Deserialize<'de> for TypedScale where diff --git a/src/side_offsets.rs b/src/side_offsets.rs index fc42437..a664742 100644 --- a/src/side_offsets.rs +++ b/src/side_offsets.rs @@ -12,7 +12,7 @@ use super::UnknownUnit; use length::Length; -use num::Zero; +use num::{Zero, ValueOrLength}; use std::fmt; use std::ops::Add; use std::marker::PhantomData; @@ -41,56 +41,47 @@ impl fmt::Debug for TypedSideOffsets2D { /// The default side offset type with no unit. pub type SideOffsets2D = TypedSideOffsets2D; -impl TypedSideOffsets2D { - /// Constructor taking a scalar for each side. - pub fn new(top: T, right: T, bottom: T, left: T) -> Self { + +impl TypedSideOffsets2D { + /// Constructor taking a scalar or a `Length` for each side. + pub fn new(top: N, right: N, bottom: N, left: N) -> Self + where N: ValueOrLength { TypedSideOffsets2D { - top: top, - right: right, - bottom: bottom, - left: left, + top: top.value(), + right: right.value(), + bottom: bottom.value(), + left: left.value(), _unit: PhantomData, } } +} - /// Constructor taking a typed Length for each side. - pub fn from_lengths( - top: Length, - right: Length, - bottom: Length, - left: Length, - ) -> Self { - TypedSideOffsets2D::new(top.0, right.0, bottom.0, left.0) - } - +impl TypedSideOffsets2D { /// Access self.top as a typed Length instead of a scalar value. - pub fn top_typed(&self) -> Length { + pub fn get_top(&self) -> Length { Length::new(self.top) } /// Access self.right as a typed Length instead of a scalar value. - pub fn right_typed(&self) -> Length { + pub fn get_right(&self) -> Length { Length::new(self.right) } /// Access self.bottom as a typed Length instead of a scalar value. - pub fn bottom_typed(&self) -> Length { + pub fn get_bottom(&self) -> Length { Length::new(self.bottom) } /// Access self.left as a typed Length instead of a scalar value. - pub fn left_typed(&self) -> Length { + pub fn get_left(&self) -> Length { Length::new(self.left) } /// Constructor setting the same value to all sides, taking a scalar value directly. - pub fn new_all_same(all: T) -> Self { - TypedSideOffsets2D::new(all, all, all, all) - } - - /// Constructor setting the same value to all sides, taking a typed Length. - pub fn from_length_all_same(all: Length) -> Self { - TypedSideOffsets2D::new_all_same(all.0) + pub fn new_all_same(all: N) -> Self + where N: ValueOrLength { + let v = all.value(); + TypedSideOffsets2D::new(v, v, v, v) } } @@ -106,11 +97,11 @@ where self.top + self.bottom } - pub fn horizontal_typed(&self) -> Length { + pub fn get_horizontal(&self) -> Length { Length::new(self.horizontal()) } - pub fn vertical_typed(&self) -> Length { + pub fn get_vertical(&self) -> Length { Length::new(self.vertical()) } } @@ -133,6 +124,6 @@ where impl TypedSideOffsets2D { /// Constructor, setting all sides to zero. pub fn zero() -> Self { - TypedSideOffsets2D::new(Zero::zero(), Zero::zero(), Zero::zero(), Zero::zero()) + TypedSideOffsets2D::new(T::zero(), T::zero(), T::zero(), T::zero()) } } diff --git a/src/size.rs b/src/size.rs index 6f8a5d3..2532bed 100644 --- a/src/size.rs +++ b/src/size.rs @@ -45,22 +45,16 @@ impl fmt::Display for TypedSize2D { impl TypedSize2D { /// Constructor taking scalar values. - pub fn new(width: T, height: T) -> Self { + pub fn new(width: N, height: N) -> Self + where N: ValueOrLength { TypedSize2D { - width: width, - height: height, + width: width.value(), + height: height.value(), _unit: PhantomData, } } } -impl TypedSize2D { - /// Constructor taking scalar strongly typed lengths. - pub fn from_lengths(width: Length, height: Length) -> Self { - TypedSize2D::new(width.get(), height.get()) - } -} - impl TypedSize2D { /// Rounds each component to the nearest integer value. /// @@ -134,13 +128,13 @@ impl TypedSize2D { impl TypedSize2D { pub fn zero() -> Self { - TypedSize2D::new(Zero::zero(), Zero::zero()) + TypedSize2D::new(T::zero(), T::zero()) } } impl Zero for TypedSize2D { fn zero() -> Self { - TypedSize2D::new(Zero::zero(), Zero::zero()) + TypedSize2D::new(T::zero(), T::zero()) } } @@ -179,13 +173,13 @@ impl, U1, U2> Div> for TypedS impl TypedSize2D { /// Returns self.width as a Length carrying the unit. #[inline] - pub fn width_typed(&self) -> Length { + pub fn get_width(&self) -> Length { Length::new(self.width) } /// Returns self.height as a Length carrying the unit. #[inline] - pub fn height_typed(&self) -> Length { + pub fn get_height(&self) -> Length { Length::new(self.height) } @@ -217,7 +211,7 @@ impl TypedSize2D { /// 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 cast(&self) -> Option> { - match (NumCast::from(self.width), NumCast::from(self.height)) { + match (NewT::from(self.width), NewT::from(self.height)) { (Some(w), Some(h)) => Some(TypedSize2D::new(w, h)), _ => None, } diff --git a/src/transform2d.rs b/src/transform2d.rs index d782a2f..d21001a 100644 --- a/src/transform2d.rs +++ b/src/transform2d.rs @@ -8,7 +8,7 @@ // except according to those terms. use super::{UnknownUnit, Angle}; -use num::{One, Zero}; +use num::{One, Zero, ValueOrScale}; use point::TypedPoint2D; use vector::{TypedVector2D, vec2}; use rect::TypedRect; @@ -223,11 +223,12 @@ where T: Copy + Clone + } /// Returns a scale transform. - pub fn create_scale(x: T, y: T) -> Self { + pub fn create_scale(x: N, y: N) -> Self + where N: ValueOrScale { let _0 = Zero::zero(); TypedTransform2D::row_major( - x, _0, - _0, y, + x.value(), _0, + _0, y.value(), _0, _0 ) } diff --git a/src/transform3d.rs b/src/transform3d.rs index 2876c6c..942c820 100644 --- a/src/transform3d.rs +++ b/src/transform3d.rs @@ -15,7 +15,7 @@ use vector::{TypedVector2D, TypedVector3D, vec2, vec3}; use rect::TypedRect; use transform2d::TypedTransform2D; use scale::TypedScale; -use num::{One, Zero}; +use num::{One, Zero, ValueOrScale}; use std::ops::{Add, Mul, Sub, Div, Neg}; use std::marker::PhantomData; use std::fmt; @@ -482,8 +482,12 @@ where T: Copy + Clone + } /// Create a 3d scale transform - pub fn create_scale(x: T, y: T, z: T) -> Self { - let (_0, _1): (T, T) = (Zero::zero(), One::one()); + pub fn create_scale(x: N, y: N, z: N) -> Self + where N: ValueOrScale { + let (_0, _1) = (T::zero(), T::one()); + let x = x.value(); + let y = y.value(); + let z = z.value(); TypedTransform3D::row_major( x, _0, _0, _0, _0, y, _0, _0, diff --git a/src/vector.rs b/src/vector.rs index c9e8ac0..eb42f0f 100644 --- a/src/vector.rs +++ b/src/vector.rs @@ -38,7 +38,7 @@ impl TypedVector2D { /// Constructor, setting all components to zero. #[inline] pub fn zero() -> Self { - TypedVector2D::new(Zero::zero(), Zero::zero()) + TypedVector2D::new(T::zero(), T::zero()) } /// Convert into a 3d vector. @@ -61,28 +61,24 @@ impl fmt::Display for TypedVector2D { } impl TypedVector2D { - /// Constructor taking scalar values directly. + /// Constructor taking scalar values or `Length`. #[inline] - pub fn new(x: T, y: T) -> Self { + pub fn new(x: N, y: N) -> Self + where N: ValueOrLength { TypedVector2D { - x: x, - y: y, + x: x.value(), + y: y.value(), _unit: PhantomData, } } } impl TypedVector2D { - /// Constructor taking properly typed Lengths instead of scalar values. - #[inline] - pub fn from_lengths(x: Length, y: Length) -> Self { - vec2(x.0, y.0) - } - /// Create a 3d vector from this one, using the specified z value. #[inline] - pub fn extend(&self, z: T) -> TypedVector3D { - vec3(self.x, self.y, z) + pub fn extend(&self, z: N) -> TypedVector3D + where N: ValueOrLength { + vec3(self.x, self.y, z.value()) } /// Cast this vector into a point. @@ -107,13 +103,13 @@ impl TypedVector2D { /// Returns self.x as a Length carrying the unit. #[inline] - pub fn x_typed(&self) -> Length { + pub fn get_x(&self) -> Length { Length::new(self.x) } /// Returns self.y as a Length carrying the unit. #[inline] - pub fn y_typed(&self) -> Length { + pub fn get_y(&self) -> Length { Length::new(self.y) } @@ -181,6 +177,14 @@ where { self.square_length().sqrt() } + + #[inline] + pub fn get_length(&self) -> Length + where + T: Float, + { + Length::new(self.length()) + } } impl TypedVector2D @@ -336,7 +340,7 @@ impl TypedVector2D { /// geometrically. Consider using `round()`, `ceil()` or `floor()` before casting. #[inline] pub fn cast(&self) -> Option> { - match (NumCast::from(self.x), NumCast::from(self.y)) { + match (NewT::from(self.x), NewT::from(self.y)) { (Some(x), Some(y)) => Some(TypedVector2D::new(x, y)), _ => None, } @@ -465,25 +469,20 @@ impl fmt::Display for TypedVector3D { } impl TypedVector3D { - /// Constructor taking scalar values directly. + /// Constructor taking scalar values or `Length`. #[inline] - pub fn new(x: T, y: T, z: T) -> Self { + pub fn new(x: N, y: N, z: N) -> Self + where N: ValueOrLength { TypedVector3D { - x: x, - y: y, - z: z, + x: x.value(), + y: y.value(), + z: z.value(), _unit: PhantomData, } } } impl TypedVector3D { - /// Constructor taking properly typed Lengths instead of scalar values. - #[inline] - pub fn from_lengths(x: Length, y: Length, z: Length) -> TypedVector3D { - vec3(x.0, y.0, z.0) - } - /// Cast this vector into a point. /// /// Equivalent to adding this vector to the origin. @@ -512,19 +511,19 @@ impl TypedVector3D { /// Returns self.x as a Length carrying the unit. #[inline] - pub fn x_typed(&self) -> Length { + pub fn get_x(&self) -> Length { Length::new(self.x) } /// Returns self.y as a Length carrying the unit. #[inline] - pub fn y_typed(&self) -> Length { + pub fn get_y(&self) -> Length { Length::new(self.y) } /// Returns self.z as a Length carrying the unit. #[inline] - pub fn z_typed(&self) -> Length { + pub fn get_z(&self) -> Length { Length::new(self.z) } @@ -590,6 +589,14 @@ impl + Add + Sub + Copy, U> { self.square_length().sqrt() } + + #[inline] + pub fn get_length(&self) -> Length + where + T: Float, + { + Length::new(self.length()) + } } impl TypedVector3D