diff --git a/src/geometry/mod.rs b/src/geometry/mod.rs index 37ca57f9..e7ff7304 100644 --- a/src/geometry/mod.rs +++ b/src/geometry/mod.rs @@ -48,6 +48,14 @@ mod translation_coordinates; mod translation_ops; mod translation_simba; +mod scale; +mod scale_alias; +mod scale_construction; +mod scale_conversion; +mod scale_coordinates; +mod scale_ops; +mod scale_simba; + mod isometry; mod isometry_alias; mod isometry_construction; @@ -95,6 +103,9 @@ pub use self::unit_complex::*; pub use self::translation::*; pub use self::translation_alias::*; +pub use self::scale::*; +pub use self::scale_alias::*; + pub use self::isometry::*; pub use self::isometry_alias::*; diff --git a/src/geometry/scale.rs b/src/geometry/scale.rs new file mode 100755 index 00000000..9f3e778c --- /dev/null +++ b/src/geometry/scale.rs @@ -0,0 +1,450 @@ +use approx::{AbsDiffEq, RelativeEq, UlpsEq}; +use num::{One, Zero}; +use std::fmt; +use std::hash; +#[cfg(feature = "abomonation-serialize")] +use std::io::{Result as IOResult, Write}; + +#[cfg(feature = "serde-serialize-no-std")] +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +#[cfg(feature = "abomonation-serialize")] +use abomonation::Abomonation; + +use crate::base::allocator::Allocator; +use crate::base::dimension::{DimNameAdd, DimNameSum, U1}; +use crate::base::storage::Owned; +use crate::base::{Const, DefaultAllocator, OMatrix, OVector, SVector, Scalar}; +use crate::ClosedDiv; +use crate::ClosedMul; + +use crate::geometry::Point; + +/// A scale which supports non-uniform scaling. +#[repr(C)] +pub struct Scale { + /// The scale coordinates, i.e., how much is multiplied to a point's coordinates when it is + /// scaled. + pub vector: SVector, +} + +impl fmt::Debug for Scale { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + self.vector.as_slice().fmt(formatter) + } +} + +impl hash::Hash for Scale +where + Owned>: hash::Hash, +{ + fn hash(&self, state: &mut H) { + self.vector.hash(state) + } +} + +impl Copy for Scale {} + +impl Clone for Scale +where + Owned>: Clone, +{ + #[inline] + fn clone(&self) -> Self { + Scale::from(self.vector.clone()) + } +} + +#[cfg(feature = "bytemuck")] +unsafe impl bytemuck::Zeroable for Scale +where + T: Scalar + bytemuck::Zeroable, + SVector: bytemuck::Zeroable, +{ +} + +#[cfg(feature = "bytemuck")] +unsafe impl bytemuck::Pod for Scale +where + T: Scalar + bytemuck::Pod, + SVector: bytemuck::Pod, +{ +} + +#[cfg(feature = "abomonation-serialize")] +impl Abomonation for Scale +where + T: Scalar, + SVector: Abomonation, +{ + unsafe fn entomb(&self, writer: &mut W) -> IOResult<()> { + self.vector.entomb(writer) + } + + fn extent(&self) -> usize { + self.vector.extent() + } + + unsafe fn exhume<'a, 'b>(&'a mut self, bytes: &'b mut [u8]) -> Option<&'b mut [u8]> { + self.vector.exhume(bytes) + } +} + +#[cfg(feature = "serde-serialize-no-std")] +impl Serialize for Scale +where + Owned>: Serialize, +{ + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.vector.serialize(serializer) + } +} + +#[cfg(feature = "serde-serialize-no-std")] +impl<'a, T: Scalar, const D: usize> Deserialize<'a> for Scale +where + Owned>: Deserialize<'a>, +{ + fn deserialize(deserializer: Des) -> Result + where + Des: Deserializer<'a>, + { + let matrix = SVector::::deserialize(deserializer)?; + + Ok(Scale::from(matrix)) + } +} + +#[cfg(feature = "rkyv-serialize-no-std")] +mod rkyv_impl { + use super::Scale; + use crate::base::SVector; + use rkyv::{offset_of, project_struct, Archive, Deserialize, Fallible, Serialize}; + + impl Archive for Scale { + type Archived = Scale; + type Resolver = as Archive>::Resolver; + + fn resolve( + &self, + pos: usize, + resolver: Self::Resolver, + out: &mut core::mem::MaybeUninit, + ) { + self.vector.resolve( + pos + offset_of!(Self::Archived, vector), + resolver, + project_struct!(out: Self::Archived => vector), + ); + } + } + + impl, S: Fallible + ?Sized, const D: usize> Serialize for Scale { + fn serialize(&self, serializer: &mut S) -> Result { + self.vector.serialize(serializer) + } + } + + impl Deserialize, _D> + for Scale + where + T::Archived: Deserialize, + { + fn deserialize(&self, deserializer: &mut _D) -> Result, _D::Error> { + Ok(Scale { + vector: self.vector.deserialize(deserializer)?, + }) + } + } +} + +impl Scale { + /// Inverts `self`. + /// + /// # Example + /// ``` + /// # use nalgebra::{Scale2, Scale3}; + /// let t = Scale3::new(1.0, 2.0, 3.0); + /// assert_eq!(t * t.try_inverse().unwrap(), Scale3::identity()); + /// assert_eq!(t.try_inverse().unwrap() * t, Scale3::identity()); + /// + /// // Work in all dimensions. + /// let t = Scale2::new(1.0, 2.0); + /// assert_eq!(t * t.try_inverse().unwrap(), Scale2::identity()); + /// assert_eq!(t.try_inverse().unwrap() * t, Scale2::identity()); + /// + /// // Returns None if any coordinate is 0. + /// let t = Scale2::new(0.0, 2.0); + /// assert_eq!(t.try_inverse(), None); + /// ``` + #[inline] + #[must_use = "Did you mean to use try_inverse_mut()?"] + pub fn try_inverse(&self) -> Option> + where + T: ClosedDiv + One + Zero, + { + for i in 0..D { + if self.vector[i] == T::zero() { + return None; + } + } + return Some(self.vector.map(|e| T::one() / e).into()); + } + + /// Inverts `self`. + /// + /// # Example + /// ``` + /// # use nalgebra::{Scale2, Scale3}; + /// + /// unsafe { + /// let t = Scale3::new(1.0, 2.0, 3.0); + /// assert_eq!(t * t.inverse_unchecked(), Scale3::identity()); + /// assert_eq!(t.inverse_unchecked() * t, Scale3::identity()); + /// + /// // Work in all dimensions. + /// let t = Scale2::new(1.0, 2.0); + /// assert_eq!(t * t.inverse_unchecked(), Scale2::identity()); + /// assert_eq!(t.inverse_unchecked() * t, Scale2::identity()); + /// } + /// ``` + #[inline] + #[must_use] + pub unsafe fn inverse_unchecked(&self) -> Scale + where + T: ClosedDiv + One, + { + return self.vector.map(|e| T::one() / e).into(); + } + + /// Inverts `self`. + /// + /// # Example + /// ``` + /// # use nalgebra::{Scale2, Scale3}; + /// let t = Scale3::new(1.0, 2.0, 3.0); + /// assert_eq!(t * t.pseudo_inverse(), Scale3::identity()); + /// assert_eq!(t.pseudo_inverse() * t, Scale3::identity()); + /// + /// // Work in all dimensions. + /// let t = Scale2::new(1.0, 2.0); + /// assert_eq!(t * t.pseudo_inverse(), Scale2::identity()); + /// assert_eq!(t.pseudo_inverse() * t, Scale2::identity()); + /// + /// // Inverts only non-zero coordinates. + /// let t = Scale2::new(0.0, 2.0); + /// assert_eq!(t * t.pseudo_inverse(), Scale2::new(0.0, 1.0)); + /// assert_eq!(t.pseudo_inverse() * t, Scale2::new(0.0, 1.0)); + /// ``` + #[inline] + #[must_use] + pub fn pseudo_inverse(&self) -> Scale + where + T: ClosedDiv + One + Zero, + { + return self + .vector + .map(|e| { + if e != T::zero() { + T::one() / e + } else { + T::zero() + } + }) + .into(); + } + + /// Converts this Scale into its equivalent homogeneous transformation matrix. + /// + /// # Example + /// ``` + /// # use nalgebra::{Scale2, Scale3, Matrix3, Matrix4}; + /// let t = Scale3::new(10.0, 20.0, 30.0); + /// let expected = Matrix4::new(10.0, 0.0, 0.0, 0.0, + /// 0.0, 20.0, 0.0, 0.0, + /// 0.0, 0.0, 30.0, 0.0, + /// 0.0, 0.0, 0.0, 1.0); + /// assert_eq!(t.to_homogeneous(), expected); + /// + /// let t = Scale2::new(10.0, 20.0); + /// let expected = Matrix3::new(10.0, 0.0, 0.0, + /// 0.0, 20.0, 0.0, + /// 0.0, 0.0, 1.0); + /// assert_eq!(t.to_homogeneous(), expected); + /// ``` + #[inline] + #[must_use] + pub fn to_homogeneous(&self) -> OMatrix, U1>, DimNameSum, U1>> + where + T: Zero + One + Clone, + Const: DimNameAdd, + DefaultAllocator: Allocator, U1>, DimNameSum, U1>> + + Allocator, U1>, U1>, + { + // TODO: use self.vector.push() instead. We can’t right now because + // that would require the DimAdd bound (but here we use DimNameAdd). + // This should be fixable once Rust gets a more complete support of + // const-generics. + let mut v = OVector::from_element(T::one()); + for i in 0..D { + v[i] = self.vector[i].clone(); + } + return OMatrix::from_diagonal(&v); + } + + /// Inverts `self` in-place. + /// + /// # Example + /// ``` + /// # use nalgebra::{Scale2, Scale3}; + /// let t = Scale3::new(1.0, 2.0, 3.0); + /// let mut inv_t = Scale3::new(1.0, 2.0, 3.0); + /// assert!(inv_t.try_inverse_mut()); + /// assert_eq!(t * inv_t, Scale3::identity()); + /// assert_eq!(inv_t * t, Scale3::identity()); + /// + /// // Work in all dimensions. + /// let t = Scale2::new(1.0, 2.0); + /// let mut inv_t = Scale2::new(1.0, 2.0); + /// assert!(inv_t.try_inverse_mut()); + /// assert_eq!(t * inv_t, Scale2::identity()); + /// assert_eq!(inv_t * t, Scale2::identity()); + /// + /// // Does not perform any operation if a coordinate is 0. + /// let mut t = Scale2::new(0.0, 2.0); + /// assert!(!t.try_inverse_mut()); + /// ``` + #[inline] + pub fn try_inverse_mut(&mut self) -> bool + where + T: ClosedDiv + One + Zero, + { + if let Some(v) = self.try_inverse() { + self.vector = v.vector; + true + } else { + false + } + } +} + +impl Scale { + /// Translate the given point. + /// + /// This is the same as the multiplication `self * pt`. + /// + /// # Example + /// ``` + /// # use nalgebra::{Scale3, Point3}; + /// let t = Scale3::new(1.0, 2.0, 3.0); + /// let transformed_point = t.transform_point(&Point3::new(4.0, 5.0, 6.0)); + /// assert_eq!(transformed_point, Point3::new(4.0, 10.0, 18.0)); + /// ``` + #[inline] + #[must_use] + pub fn transform_point(&self, pt: &Point) -> Point { + self * pt + } +} + +impl Scale { + /// Translate the given point by the inverse of this Scale. + /// + /// # Example + /// ``` + /// # use nalgebra::{Scale3, Point3}; + /// let t = Scale3::new(1.0, 2.0, 3.0); + /// let transformed_point = t.try_inverse_transform_point(&Point3::new(4.0, 6.0, 6.0)).unwrap(); + /// assert_eq!(transformed_point, Point3::new(4.0, 3.0, 2.0)); + /// + /// // Returns None if the inverse doesn't exist. + /// let t = Scale3::new(1.0, 0.0, 3.0); + /// let transformed_point = t.try_inverse_transform_point(&Point3::new(4.0, 6.0, 6.0)); + /// assert_eq!(transformed_point, None); + /// ``` + #[inline] + #[must_use] + pub fn try_inverse_transform_point(&self, pt: &Point) -> Option> { + self.try_inverse().map(|s| s * pt) + } +} + +impl Eq for Scale {} + +impl PartialEq for Scale { + #[inline] + fn eq(&self, right: &Scale) -> bool { + self.vector == right.vector + } +} + +impl AbsDiffEq for Scale +where + T::Epsilon: Clone, +{ + type Epsilon = T::Epsilon; + + #[inline] + fn default_epsilon() -> Self::Epsilon { + T::default_epsilon() + } + + #[inline] + fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool { + self.vector.abs_diff_eq(&other.vector, epsilon) + } +} + +impl RelativeEq for Scale +where + T::Epsilon: Clone, +{ + #[inline] + fn default_max_relative() -> Self::Epsilon { + T::default_max_relative() + } + + #[inline] + fn relative_eq( + &self, + other: &Self, + epsilon: Self::Epsilon, + max_relative: Self::Epsilon, + ) -> bool { + self.vector + .relative_eq(&other.vector, epsilon, max_relative) + } +} + +impl UlpsEq for Scale +where + T::Epsilon: Clone, +{ + #[inline] + fn default_max_ulps() -> u32 { + T::default_max_ulps() + } + + #[inline] + fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool { + self.vector.ulps_eq(&other.vector, epsilon, max_ulps) + } +} + +/* + * + * Display + * + */ +impl fmt::Display for Scale { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let precision = f.precision().unwrap_or(3); + + writeln!(f, "Scale {{")?; + write!(f, "{:.*}", precision, self.vector)?; + writeln!(f, "}}") + } +} diff --git a/src/geometry/scale_alias.rs b/src/geometry/scale_alias.rs new file mode 100644 index 00000000..87932442 --- /dev/null +++ b/src/geometry/scale_alias.rs @@ -0,0 +1,19 @@ +use crate::geometry::Scale; + +/// A 1-dimensional scale. +pub type Scale1 = Scale; + +/// A 2-dimensional scale. +pub type Scale2 = Scale; + +/// A 3-dimensional scale. +pub type Scale3 = Scale; + +/// A 4-dimensional scale. +pub type Scale4 = Scale; + +/// A 5-dimensional scale. +pub type Scale5 = Scale; + +/// A 6-dimensional scale. +pub type Scale6 = Scale; diff --git a/src/geometry/scale_construction.rs b/src/geometry/scale_construction.rs new file mode 100644 index 00000000..02cce69c --- /dev/null +++ b/src/geometry/scale_construction.rs @@ -0,0 +1,123 @@ +#[cfg(feature = "arbitrary")] +use crate::base::storage::Owned; +#[cfg(feature = "arbitrary")] +use quickcheck::{Arbitrary, Gen}; + +use num::One; +#[cfg(feature = "rand-no-std")] +use rand::{ + distributions::{Distribution, Standard}, + Rng, +}; + +use simba::scalar::{ClosedMul, SupersetOf}; + +use crate::base::{SVector, Scalar}; +use crate::geometry::Scale; + +impl Scale { + /// Creates a new identity scale. + /// + /// # Example + /// ``` + /// # use nalgebra::{Point2, Point3, Scale2, Scale3}; + /// let t = Scale2::identity(); + /// let p = Point2::new(1.0, 2.0); + /// assert_eq!(t * p, p); + /// + /// // Works in all dimensions. + /// let t = Scale3::identity(); + /// let p = Point3::new(1.0, 2.0, 3.0); + /// assert_eq!(t * p, p); + /// ``` + #[inline] + pub fn identity() -> Scale + where + T: One, + { + Scale::from(SVector::from_element(T::one())) + } + + /// Cast the components of `self` to another type. + /// + /// # Example + /// ``` + /// # use nalgebra::Scale2; + /// let tra = Scale2::new(1.0f64, 2.0); + /// let tra2 = tra.cast::(); + /// assert_eq!(tra2, Scale2::new(1.0f32, 2.0)); + /// ``` + pub fn cast(self) -> Scale + where + Scale: SupersetOf, + { + crate::convert(self) + } +} + +impl One for Scale { + #[inline] + fn one() -> Self { + Self::identity() + } +} + +#[cfg(feature = "rand-no-std")] +impl Distribution> for Standard +where + Standard: Distribution, +{ + /// Generate an arbitrary random variate for testing purposes. + #[inline] + fn sample(&self, rng: &mut G) -> Scale { + Scale::from(rng.gen::>()) + } +} + +#[cfg(feature = "arbitrary")] +impl Arbitrary for Scale +where + Owned>: Send, +{ + #[inline] + fn arbitrary(rng: &mut Gen) -> Self { + let v: SVector = Arbitrary::arbitrary(rng); + Self::from(v) + } +} + +/* + * + * Small Scale construction from components. + * + */ +macro_rules! componentwise_constructors_impl( + ($($doc: expr; $D: expr, $($args: ident:$irow: expr),*);* $(;)*) => {$( + impl Scale + { + #[doc = "Initializes this Scale from its components."] + #[doc = "# Example\n```"] + #[doc = $doc] + #[doc = "```"] + #[inline] + pub const fn new($($args: T),*) -> Self { + Self { vector: SVector::::new($($args),*) } + } + } + )*} +); + +componentwise_constructors_impl!( + "# use nalgebra::Scale1;\nlet t = Scale1::new(1.0);\nassert!(t.vector.x == 1.0);"; + 1, x:0; + "# use nalgebra::Scale2;\nlet t = Scale2::new(1.0, 2.0);\nassert!(t.vector.x == 1.0 && t.vector.y == 2.0);"; + 2, x:0, y:1; + "# use nalgebra::Scale3;\nlet t = Scale3::new(1.0, 2.0, 3.0);\nassert!(t.vector.x == 1.0 && t.vector.y == 2.0 && t.vector.z == 3.0);"; + 3, x:0, y:1, z:2; + "# use nalgebra::Scale4;\nlet t = Scale4::new(1.0, 2.0, 3.0, 4.0);\nassert!(t.vector.x == 1.0 && t.vector.y == 2.0 && t.vector.z == 3.0 && t.vector.w == 4.0);"; + 4, x:0, y:1, z:2, w:3; + "# use nalgebra::Scale5;\nlet t = Scale5::new(1.0, 2.0, 3.0, 4.0, 5.0);\nassert!(t.vector.x == 1.0 && t.vector.y == 2.0 && t.vector.z == 3.0 && t.vector.w == 4.0 && t.vector.a == 5.0);"; + 5, x:0, y:1, z:2, w:3, a:4; + "# use nalgebra::Scale6;\nlet t = Scale6::new(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);\nassert!(t.vector.x == 1.0 && t.vector.y == 2.0 && t.vector.z == 3.0 && t.vector.w == 4.0 && t.vector.a == 5.0 && t.vector.b == 6.0);"; + 6, x:0, y:1, z:2, w:3, a:4, b:5; +); diff --git a/src/geometry/scale_conversion.rs b/src/geometry/scale_conversion.rs new file mode 100644 index 00000000..2dc670a1 --- /dev/null +++ b/src/geometry/scale_conversion.rs @@ -0,0 +1,233 @@ +use num::{One, Zero}; + +use simba::scalar::{RealField, SubsetOf, SupersetOf}; +use simba::simd::PrimitiveSimdValue; + +use crate::base::allocator::Allocator; +use crate::base::dimension::{DimNameAdd, DimNameSum, U1}; +use crate::base::{Const, DefaultAllocator, OMatrix, OVector, SVector, Scalar}; + +use crate::geometry::{Scale, SuperTCategoryOf, TAffine, Transform}; +use crate::Point; + +/* + * This file provides the following conversions: + * ============================================= + * + * Scale -> Scale + * Scale -> Transform + * Scale -> Matrix (homogeneous) + */ + +impl SubsetOf> for Scale +where + T1: Scalar, + T2: Scalar + SupersetOf, +{ + #[inline] + fn to_superset(&self) -> Scale { + Scale::from(self.vector.to_superset()) + } + + #[inline] + fn is_in_subset(rot: &Scale) -> bool { + crate::is_convertible::<_, SVector>(&rot.vector) + } + + #[inline] + fn from_superset_unchecked(rot: &Scale) -> Self { + Scale { + vector: rot.vector.to_subset_unchecked(), + } + } +} + +impl SubsetOf> for Scale +where + T1: RealField, + T2: RealField + SupersetOf, + C: SuperTCategoryOf, + Const: DimNameAdd, + DefaultAllocator: Allocator, U1>, DimNameSum, U1>> + + Allocator, U1>, U1> + + Allocator, U1>, DimNameSum, U1>>, +{ + #[inline] + fn to_superset(&self) -> Transform { + Transform::from_matrix_unchecked(self.to_homogeneous().to_superset()) + } + + #[inline] + fn is_in_subset(t: &Transform) -> bool { + >::is_in_subset(t.matrix()) + } + + #[inline] + fn from_superset_unchecked(t: &Transform) -> Self { + Self::from_superset_unchecked(t.matrix()) + } +} + +impl + SubsetOf, U1>, DimNameSum, U1>>> for Scale +where + T1: RealField, + T2: RealField + SupersetOf, + Const: DimNameAdd, + DefaultAllocator: Allocator, U1>, DimNameSum, U1>> + + Allocator, U1>, U1> + + Allocator, U1>, DimNameSum, U1>>, +{ + #[inline] + fn to_superset(&self) -> OMatrix, U1>, DimNameSum, U1>> { + self.to_homogeneous().to_superset() + } + + #[inline] + fn is_in_subset(m: &OMatrix, U1>, DimNameSum, U1>>) -> bool { + if m[(D, D)] != T2::one() { + return false; + } + for i in 0..D + 1 { + for j in 0..D + 1 { + if i != j && m[(i, j)] != T2::zero() { + return false; + } + } + } + true + } + + #[inline] + fn from_superset_unchecked( + m: &OMatrix, U1>, DimNameSum, U1>>, + ) -> Self { + let v = m.fixed_slice::(0, 0).diagonal(); + Self { + vector: crate::convert_unchecked(v), + } + } +} + +impl From> + for OMatrix, U1>, DimNameSum, U1>> +where + Const: DimNameAdd, + DefaultAllocator: Allocator, U1>, DimNameSum, U1>> + + Allocator, U1>, U1> + + Allocator>, +{ + #[inline] + fn from(t: Scale) -> Self { + t.to_homogeneous() + } +} + +impl From>> for Scale { + #[inline] + fn from(vector: OVector>) -> Self { + Scale { vector } + } +} + +impl From<[T; D]> for Scale { + #[inline] + fn from(coords: [T; D]) -> Self { + Scale { + vector: coords.into(), + } + } +} + +impl From> for Scale { + #[inline] + fn from(pt: Point) -> Self { + Scale { vector: pt.coords } + } +} + +impl From> for [T; D] { + #[inline] + fn from(t: Scale) -> Self { + t.vector.into() + } +} + +impl From<[Scale; 2]> for Scale +where + T: From<[::Element; 2]>, + T::Element: Scalar, +{ + #[inline] + fn from(arr: [Scale; 2]) -> Self { + Self::from(OVector::from([ + arr[0].vector.clone(), + arr[1].vector.clone(), + ])) + } +} + +impl From<[Scale; 4]> for Scale +where + T: From<[::Element; 4]>, + T::Element: Scalar, +{ + #[inline] + fn from(arr: [Scale; 4]) -> Self { + Self::from(OVector::from([ + arr[0].vector.clone(), + arr[1].vector.clone(), + arr[2].vector.clone(), + arr[3].vector.clone(), + ])) + } +} + +impl From<[Scale; 8]> for Scale +where + T: From<[::Element; 8]>, + T::Element: Scalar, +{ + #[inline] + fn from(arr: [Scale; 8]) -> Self { + Self::from(OVector::from([ + arr[0].vector.clone(), + arr[1].vector.clone(), + arr[2].vector.clone(), + arr[3].vector.clone(), + arr[4].vector.clone(), + arr[5].vector.clone(), + arr[6].vector.clone(), + arr[7].vector.clone(), + ])) + } +} + +impl From<[Scale; 16]> + for Scale +where + T: From<[::Element; 16]>, + T::Element: Scalar, +{ + #[inline] + fn from(arr: [Scale; 16]) -> Self { + Self::from(OVector::from([ + arr[0].vector.clone(), + arr[1].vector.clone(), + arr[2].vector.clone(), + arr[3].vector.clone(), + arr[4].vector.clone(), + arr[5].vector.clone(), + arr[6].vector.clone(), + arr[7].vector.clone(), + arr[8].vector.clone(), + arr[9].vector.clone(), + arr[10].vector.clone(), + arr[11].vector.clone(), + arr[12].vector.clone(), + arr[13].vector.clone(), + arr[14].vector.clone(), + arr[15].vector.clone(), + ])) + } +} diff --git a/src/geometry/scale_coordinates.rs b/src/geometry/scale_coordinates.rs new file mode 100644 index 00000000..5158c62d --- /dev/null +++ b/src/geometry/scale_coordinates.rs @@ -0,0 +1,39 @@ +use std::ops::{Deref, DerefMut}; + +use crate::base::coordinates::{X, XY, XYZ, XYZW, XYZWA, XYZWAB}; +use crate::base::Scalar; + +use crate::geometry::Scale; + +/* + * + * Give coordinates to Scale{1 .. 6} + * + */ + +macro_rules! deref_impl( + ($D: expr, $Target: ident $(, $comps: ident)*) => { + impl Deref for Scale { + type Target = $Target; + + #[inline] + fn deref(&self) -> &Self::Target { + self.vector.deref() + } + } + + impl DerefMut for Scale { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + self.vector.deref_mut() + } + } + } +); + +deref_impl!(1, X, x); +deref_impl!(2, XY, x, y); +deref_impl!(3, XYZ, x, y, z); +deref_impl!(4, XYZW, x, y, z, w); +deref_impl!(5, XYZWA, x, y, z, w, a); +deref_impl!(6, XYZWAB, x, y, z, w, a, b); diff --git a/src/geometry/scale_ops.rs b/src/geometry/scale_ops.rs new file mode 100644 index 00000000..c056a301 --- /dev/null +++ b/src/geometry/scale_ops.rs @@ -0,0 +1,125 @@ +use std::ops::{Mul, MulAssign}; + +use simba::scalar::ClosedMul; + +use crate::base::constraint::{SameNumberOfColumns, SameNumberOfRows, ShapeConstraint}; +use crate::base::dimension::U1; +use crate::base::{Const, SVector, Scalar}; + +use crate::geometry::{Point, Scale}; + +// Scale × Scale +add_sub_impl!(Mul, mul, ClosedMul; + (Const, U1), (Const, U1) -> (Const, U1) + const D; for; where; + self: &'a Scale, right: &'b Scale, Output = Scale; + Scale::from(self.vector.component_mul(&right.vector)); + 'a, 'b); + +add_sub_impl!(Mul, mul, ClosedMul; + (Const, U1), (Const, U1) -> (Const, U1) + const D; for; where; + self: &'a Scale, right: Scale, Output = Scale; + Scale::from(self.vector.component_mul(&right.vector)); + 'a); + +add_sub_impl!(Mul, mul, ClosedMul; + (Const, U1), (Const, U1) -> (Const, U1) + const D; for; where; + self: Scale, right: &'b Scale, Output = Scale; + Scale::from(self.vector.component_mul(&right.vector)); + 'b); + +add_sub_impl!(Mul, mul, ClosedMul; + (Const, U1), (Const, U1) -> (Const, U1) + const D; for; where; + self: Scale, right: Scale, Output = Scale; + Scale::from(self.vector.component_mul(&right.vector)); ); + +// Scale × scalar +add_sub_impl!(Mul, mul, ClosedMul; + (Const, U1), (Const, U1) -> (Const, U1) + const D; for; where; + self: &'a Scale, right: T, Output = Scale; + Scale::from(&self.vector * right); + 'a); + +add_sub_impl!(Mul, mul, ClosedMul; + (Const, U1), (Const, U1) -> (Const, U1) + const D; for; where; + self: Scale, right: T, Output = Scale; + Scale::from(self.vector * right); ); + +// Scale × Point +add_sub_impl!(Mul, mul, ClosedMul; + (Const, U1), (Const, U1) -> (Const, U1) + const D; for; where; + self: &'a Scale, right: &'b Point, Output = Point; + Point::from(self.vector.component_mul(&right.coords)); + 'a, 'b); + +add_sub_impl!(Mul, mul, ClosedMul; + (Const, U1), (Const, U1) -> (Const, U1) + const D; for; where; + self: &'a Scale, right: Point, Output = Point; + Point::from(self.vector.component_mul(&right.coords)); + 'a); + +add_sub_impl!(Mul, mul, ClosedMul; + (Const, U1), (Const, U1) -> (Const, U1) + const D; for; where; + self: Scale, right: &'b Point, Output = Point; + Point::from(self.vector.component_mul(&right.coords)); + 'b); + +add_sub_impl!(Mul, mul, ClosedMul; + (Const, U1), (Const, U1) -> (Const, U1) + const D; for; where; + self: Scale, right: Point, Output = Point; + Point::from(self.vector.component_mul(&right.coords)); ); + +// Scale * Vector +add_sub_impl!(Mul, mul, ClosedMul; + (Const, U1), (Const, U1) -> (Const, U1) + const D; for; where; + self: &'a Scale, right: &'b SVector, Output = SVector; + SVector::from(self.vector.component_mul(&right)); + 'a, 'b); + +add_sub_impl!(Mul, mul, ClosedMul; + (Const, U1), (Const, U1) -> (Const, U1) + const D; for; where; + self: &'a Scale, right: SVector, Output = SVector; + SVector::from(self.vector.component_mul(&right)); + 'a); + +add_sub_impl!(Mul, mul, ClosedMul; + (Const, U1), (Const, U1) -> (Const, U1) + const D; for; where; + self: Scale, right: &'b SVector, Output = SVector; + SVector::from(self.vector.component_mul(&right)); + 'b); + +add_sub_impl!(Mul, mul, ClosedMul; + (Const, U1), (Const, U1) -> (Const, U1) + const D; for; where; + self: Scale, right: SVector, Output = SVector; + SVector::from(self.vector.component_mul(&right)); ); + +// Scale *= Scale +add_sub_assign_impl!(MulAssign, mul_assign, ClosedMul; + const D; + self: Scale, right: &'b Scale; + self.vector.component_mul_assign(&right.vector); + 'b); + +add_sub_assign_impl!(MulAssign, mul_assign, ClosedMul; + const D; + self: Scale, right: Scale; + self.vector.component_mul_assign(&right.vector); ); + +// Scale ×= scalar +add_sub_assign_impl!(MulAssign, mul_assign, ClosedMul; + const D; + self: Scale, right: T; + self.vector *= right; ); diff --git a/src/geometry/scale_simba.rs b/src/geometry/scale_simba.rs new file mode 100755 index 00000000..cb42b715 --- /dev/null +++ b/src/geometry/scale_simba.rs @@ -0,0 +1,49 @@ +use simba::simd::SimdValue; + +use crate::base::OVector; +use crate::Scalar; + +use crate::geometry::Scale; + +impl SimdValue for Scale +where + T::Element: Scalar, +{ + type Element = Scale; + type SimdBool = T::SimdBool; + + #[inline] + fn lanes() -> usize { + T::lanes() + } + + #[inline] + fn splat(val: Self::Element) -> Self { + OVector::splat(val.vector).into() + } + + #[inline] + fn extract(&self, i: usize) -> Self::Element { + self.vector.extract(i).into() + } + + #[inline] + unsafe fn extract_unchecked(&self, i: usize) -> Self::Element { + self.vector.extract_unchecked(i).into() + } + + #[inline] + fn replace(&mut self, i: usize, val: Self::Element) { + self.vector.replace(i, val.vector) + } + + #[inline] + unsafe fn replace_unchecked(&mut self, i: usize, val: Self::Element) { + self.vector.replace_unchecked(i, val.vector) + } + + #[inline] + fn select(self, cond: Self::SimdBool, other: Self) -> Self { + self.vector.select(cond, other.vector).into() + } +}