From c5dad7f9605021e3efd91f8c5a846d4319ec8afd Mon Sep 17 00:00:00 2001 From: sebcrozet Date: Sun, 5 Apr 2020 17:53:27 +0200 Subject: [PATCH] Re-add all the alga trait impls behind a feature. --- Cargo.toml | 4 +- src/base/matrix_alga.rs | 452 ++++++++++++++++++++++++++++++ src/base/mod.rs | 2 + src/geometry/isometry_alga.rs | 210 ++++++++++++++ src/geometry/mod.rs | 16 ++ src/geometry/point_alga.rs | 84 ++++++ src/geometry/quaternion_alga.rs | 313 +++++++++++++++++++++ src/geometry/rotation_alga.rs | 291 +++++++++++++++++++ src/geometry/similarity_alga.rs | 196 +++++++++++++ src/geometry/transform_alga.rs | 149 ++++++++++ src/geometry/translation_alga.rs | 215 ++++++++++++++ src/geometry/unit_complex_alga.rs | 185 ++++++++++++ src/sparse/cs_matrix.rs | 40 ++- 13 files changed, 2142 insertions(+), 15 deletions(-) create mode 100644 src/base/matrix_alga.rs create mode 100755 src/geometry/isometry_alga.rs create mode 100644 src/geometry/point_alga.rs create mode 100755 src/geometry/quaternion_alga.rs create mode 100755 src/geometry/rotation_alga.rs create mode 100755 src/geometry/similarity_alga.rs create mode 100755 src/geometry/transform_alga.rs create mode 100755 src/geometry/translation_alga.rs create mode 100755 src/geometry/unit_complex_alga.rs diff --git a/Cargo.toml b/Cargo.toml index d97581aa..7692ac55 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,7 +40,7 @@ num-complex = { version = "0.2", default-features = false } num-rational = { version = "0.2", default-features = false } approx = { version = "0.3", default-features = false } simba = { version = "0.1", default-features = false } -#alga = { version = "0.9", default-features = false } +alga = { version = "0.9", default-features = false, optional = true } rand_distr = { version = "0.2", optional = true } matrixmultiply = { version = "0.2", optional = true } serde = { version = "1.0", optional = true } @@ -73,4 +73,4 @@ path = "benches/lib.rs" lto = true #[patch.crates-io] -#alga = { path = "../alga/alga" } \ No newline at end of file +#simba = { path = "../simba" } \ No newline at end of file diff --git a/src/base/matrix_alga.rs b/src/base/matrix_alga.rs new file mode 100644 index 00000000..18182382 --- /dev/null +++ b/src/base/matrix_alga.rs @@ -0,0 +1,452 @@ +#[cfg(all(feature = "alloc", not(feature = "std")))] +use alloc::vec::Vec; + +use num::{One, Zero}; + +use alga::general::{ + AbstractGroup, AbstractGroupAbelian, AbstractLoop, AbstractMagma, AbstractModule, + AbstractMonoid, AbstractQuasigroup, AbstractSemigroup, Additive, ClosedAdd, ClosedMul, + ClosedNeg, ComplexField, Field, Identity, JoinSemilattice, Lattice, MeetSemilattice, Module, + Multiplicative, RingCommutative, TwoSidedInverse, +}; +use alga::linear::{ + FiniteDimInnerSpace, FiniteDimVectorSpace, InnerSpace, NormedSpace, VectorSpace, +}; + +use crate::base::allocator::Allocator; +use crate::base::dimension::{Dim, DimName}; +use crate::base::storage::{Storage, StorageMut}; +use crate::base::{DefaultAllocator, MatrixMN, MatrixN, Scalar}; + +/* + * + * Additive structures. + * + */ +impl Identity for MatrixMN +where + N: Scalar + Zero, + DefaultAllocator: Allocator, +{ + #[inline] + fn identity() -> Self { + Self::from_element(N::zero()) + } +} + +impl AbstractMagma for MatrixMN +where + N: Scalar + ClosedAdd, + DefaultAllocator: Allocator, +{ + #[inline] + fn operate(&self, other: &Self) -> Self { + self + other + } +} + +impl TwoSidedInverse for MatrixMN +where + N: Scalar + ClosedNeg, + DefaultAllocator: Allocator, +{ + #[inline] + #[must_use = "Did you mean to use two_sided_inverse_mut()?"] + fn two_sided_inverse(&self) -> Self { + -self + } + + #[inline] + fn two_sided_inverse_mut(&mut self) { + *self = -self.clone() + } +} + +macro_rules! inherit_additive_structure( + ($($marker: ident<$operator: ident> $(+ $bounds: ident)*),* $(,)*) => {$( + impl $marker<$operator> for MatrixMN + where N: Scalar + $marker<$operator> $(+ $bounds)*, + DefaultAllocator: Allocator { } + )*} +); + +inherit_additive_structure!( + AbstractSemigroup + ClosedAdd, + AbstractMonoid + Zero + ClosedAdd, + AbstractQuasigroup + ClosedAdd + ClosedNeg, + AbstractLoop + Zero + ClosedAdd + ClosedNeg, + AbstractGroup + Zero + ClosedAdd + ClosedNeg, + AbstractGroupAbelian + Zero + ClosedAdd + ClosedNeg +); + +impl AbstractModule for MatrixMN +where + N: Scalar + RingCommutative, + DefaultAllocator: Allocator, +{ + type AbstractRing = N; + + #[inline] + fn multiply_by(&self, n: N) -> Self { + self * n + } +} + +impl Module for MatrixMN +where + N: Scalar + RingCommutative, + DefaultAllocator: Allocator, +{ + type Ring = N; +} + +impl VectorSpace for MatrixMN +where + N: Scalar + Field, + DefaultAllocator: Allocator, +{ + type Field = N; +} + +impl FiniteDimVectorSpace for MatrixMN +where + N: Scalar + Field, + DefaultAllocator: Allocator, +{ + #[inline] + fn dimension() -> usize { + R::dim() * C::dim() + } + + #[inline] + fn canonical_basis_element(i: usize) -> Self { + assert!(i < Self::dimension(), "Index out of bound."); + + let mut res = Self::zero(); + unsafe { + *res.data.get_unchecked_linear_mut(i) = N::one(); + } + + res + } + + #[inline] + fn dot(&self, other: &Self) -> N { + self.dot(other) + } + + #[inline] + unsafe fn component_unchecked(&self, i: usize) -> &N { + self.data.get_unchecked_linear(i) + } + + #[inline] + unsafe fn component_unchecked_mut(&mut self, i: usize) -> &mut N { + self.data.get_unchecked_linear_mut(i) + } +} + +impl< + N: ComplexField + simba::scalar::ComplexField::RealField>, + R: DimName, + C: DimName, + > NormedSpace for MatrixMN +where + ::RealField: simba::scalar::RealField, + DefaultAllocator: Allocator, +{ + type RealField = ::RealField; + type ComplexField = N; + + #[inline] + fn norm_squared(&self) -> ::RealField { + self.norm_squared() + } + + #[inline] + fn norm(&self) -> ::RealField { + self.norm() + } + + #[inline] + #[must_use = "Did you mean to use normalize_mut()?"] + fn normalize(&self) -> Self { + self.normalize() + } + + #[inline] + fn normalize_mut(&mut self) -> ::RealField { + self.normalize_mut() + } + + #[inline] + #[must_use = "Did you mean to use try_normalize_mut()?"] + fn try_normalize(&self, min_norm: ::RealField) -> Option { + self.try_normalize(min_norm) + } + + #[inline] + fn try_normalize_mut( + &mut self, + min_norm: ::RealField, + ) -> Option<::RealField> { + self.try_normalize_mut(min_norm) + } +} + +impl< + N: ComplexField + simba::scalar::ComplexField::RealField>, + R: DimName, + C: DimName, + > InnerSpace for MatrixMN +where + ::RealField: simba::scalar::RealField, + DefaultAllocator: Allocator, +{ + #[inline] + fn angle(&self, other: &Self) -> ::RealField { + self.angle(other) + } + + #[inline] + fn inner_product(&self, other: &Self) -> N { + self.dotc(other) + } +} + +// FIXME: specialization will greatly simplify this implementation in the future. +// In particular: +// − use `x()` instead of `::canonical_basis_element` +// − use `::new(x, y, z)` instead of `::from_slice` +impl< + N: ComplexField + simba::scalar::ComplexField::RealField>, + R: DimName, + C: DimName, + > FiniteDimInnerSpace for MatrixMN +where + ::RealField: simba::scalar::RealField, + DefaultAllocator: Allocator, +{ + #[inline] + fn orthonormalize(vs: &mut [Self]) -> usize { + let mut nbasis_elements = 0; + + for i in 0..vs.len() { + { + let (elt, basis) = vs[..i + 1].split_last_mut().unwrap(); + + for basis_element in &basis[..nbasis_elements] { + *elt -= &*basis_element * elt.dot(basis_element) + } + } + + if vs[i] + .try_normalize_mut(::RealField::zero()) + .is_some() + { + // FIXME: this will be efficient on dynamically-allocated vectors but for + // statically-allocated ones, `.clone_from` would be better. + vs.swap(nbasis_elements, i); + nbasis_elements += 1; + + // All the other vectors will be dependent. + if nbasis_elements == Self::dimension() { + break; + } + } + } + + nbasis_elements + } + + #[inline] + fn orthonormal_subspace_basis(vs: &[Self], mut f: F) + where + F: FnMut(&Self) -> bool, + { + // FIXME: is this necessary? + assert!( + vs.len() <= Self::dimension(), + "The given set of vectors has no chance of being a free family." + ); + + match Self::dimension() { + 1 => { + if vs.len() == 0 { + let _ = f(&Self::canonical_basis_element(0)); + } + } + 2 => { + if vs.len() == 0 { + let _ = f(&Self::canonical_basis_element(0)) + && f(&Self::canonical_basis_element(1)); + } else if vs.len() == 1 { + let v = &vs[0]; + let res = Self::from_column_slice(&[-v[1], v[0]]); + + let _ = f(&res.normalize()); + } + + // Otherwise, nothing. + } + 3 => { + if vs.len() == 0 { + let _ = f(&Self::canonical_basis_element(0)) + && f(&Self::canonical_basis_element(1)) + && f(&Self::canonical_basis_element(2)); + } else if vs.len() == 1 { + let v = &vs[0]; + let mut a; + + if ComplexField::norm1(v[0]) > ComplexField::norm1(v[1]) { + a = Self::from_column_slice(&[v[2], N::zero(), -v[0]]); + } else { + a = Self::from_column_slice(&[N::zero(), -v[2], v[1]]); + }; + + let _ = a.normalize_mut(); + + if f(&a.cross(v)) { + let _ = f(&a); + } + } else if vs.len() == 2 { + let _ = f(&vs[0].cross(&vs[1]).normalize()); + } + } + _ => { + #[cfg(any(feature = "std", feature = "alloc"))] + { + // XXX: use a GenericArray instead. + let mut known_basis = Vec::new(); + + for v in vs.iter() { + known_basis.push(v.normalize()) + } + + for i in 0..Self::dimension() - vs.len() { + let mut elt = Self::canonical_basis_element(i); + + for v in &known_basis { + elt -= v * elt.dot(v) + } + + if let Some(subsp_elt) = + elt.try_normalize(::RealField::zero()) + { + if !f(&subsp_elt) { + return; + }; + + known_basis.push(subsp_elt); + } + } + } + #[cfg(all(not(feature = "std"), not(feature = "alloc")))] + { + panic!("Cannot compute the orthogonal subspace basis of a vector with a dimension greater than 3 \ + if #![no_std] is enabled and the 'alloc' feature is not enabled.") + } + } + } + } +} + +/* + * + * + * Multiplicative structures. + * + * + */ +impl Identity for MatrixN +where + N: Scalar + Zero + One, + DefaultAllocator: Allocator, +{ + #[inline] + fn identity() -> Self { + Self::identity() + } +} + +impl AbstractMagma for MatrixN +where + N: Scalar + Zero + One + ClosedAdd + ClosedMul, + DefaultAllocator: Allocator, +{ + #[inline] + fn operate(&self, other: &Self) -> Self { + self * other + } +} + +macro_rules! impl_multiplicative_structure( + ($($marker: ident<$operator: ident> $(+ $bounds: ident)*),* $(,)*) => {$( + impl $marker<$operator> for MatrixN + where N: Scalar + Zero + One + ClosedAdd + ClosedMul + $marker<$operator> $(+ $bounds)*, + DefaultAllocator: Allocator { } + )*} +); + +impl_multiplicative_structure!( + AbstractSemigroup, + AbstractMonoid + One +); + +/* + * + * Ordering + * + */ +impl MeetSemilattice for MatrixMN +where + N: Scalar + MeetSemilattice, + DefaultAllocator: Allocator, +{ + #[inline] + fn meet(&self, other: &Self) -> Self { + self.zip_map(other, |a, b| a.meet(&b)) + } +} + +impl JoinSemilattice for MatrixMN +where + N: Scalar + JoinSemilattice, + DefaultAllocator: Allocator, +{ + #[inline] + fn join(&self, other: &Self) -> Self { + self.zip_map(other, |a, b| a.join(&b)) + } +} + +impl Lattice for MatrixMN +where + N: Scalar + Lattice, + DefaultAllocator: Allocator, +{ + #[inline] + fn meet_join(&self, other: &Self) -> (Self, Self) { + let shape = self.data.shape(); + assert!( + shape == other.data.shape(), + "Matrix meet/join error: mismatched dimensions." + ); + + let mut mres = unsafe { Self::new_uninitialized_generic(shape.0, shape.1) }; + let mut jres = unsafe { Self::new_uninitialized_generic(shape.0, shape.1) }; + + for i in 0..shape.0.value() * shape.1.value() { + unsafe { + let mj = self + .data + .get_unchecked_linear(i) + .meet_join(other.data.get_unchecked_linear(i)); + *mres.data.get_unchecked_linear_mut(i) = mj.0; + *jres.data.get_unchecked_linear_mut(i) = mj.1; + } + } + + (mres, jres) + } +} diff --git a/src/base/mod.rs b/src/base/mod.rs index 0f380523..0e8ea72d 100644 --- a/src/base/mod.rs +++ b/src/base/mod.rs @@ -21,6 +21,8 @@ mod conversion; mod edition; pub mod indexing; mod matrix; +#[cfg(feature = "alga")] +mod matrix_alga; mod matrix_simba; mod matrix_slice; mod norm; diff --git a/src/geometry/isometry_alga.rs b/src/geometry/isometry_alga.rs new file mode 100755 index 00000000..3cb965a8 --- /dev/null +++ b/src/geometry/isometry_alga.rs @@ -0,0 +1,210 @@ +use alga::general::{ + AbstractGroup, AbstractLoop, AbstractMagma, AbstractMonoid, AbstractQuasigroup, + AbstractSemigroup, Id, Identity, Multiplicative, RealField, TwoSidedInverse, +}; +use alga::linear::Isometry as AlgaIsometry; +use alga::linear::{ + AffineTransformation, DirectIsometry, ProjectiveTransformation, Rotation, Similarity, + Transformation, +}; + +use crate::base::allocator::Allocator; +use crate::base::dimension::DimName; +use crate::base::{DefaultAllocator, VectorN}; + +use crate::geometry::{AbstractRotation, Isometry, Point, Translation}; + +/* + * + * Algebraic structures. + * + */ +impl Identity + for Isometry +where + R: Rotation> + AbstractRotation, + DefaultAllocator: Allocator, +{ + #[inline] + fn identity() -> Self { + Self::identity() + } +} + +impl TwoSidedInverse + for Isometry +where + R: Rotation> + AbstractRotation, + DefaultAllocator: Allocator, +{ + #[inline] + #[must_use = "Did you mean to use two_sided_inverse_mut()?"] + fn two_sided_inverse(&self) -> Self { + self.inverse() + } + + #[inline] + fn two_sided_inverse_mut(&mut self) { + self.inverse_mut() + } +} + +impl AbstractMagma + for Isometry +where + R: Rotation> + AbstractRotation, + DefaultAllocator: Allocator, +{ + #[inline] + fn operate(&self, rhs: &Self) -> Self { + self * rhs + } +} + +macro_rules! impl_multiplicative_structures( + ($($marker: ident<$operator: ident>),* $(,)*) => {$( + impl $marker<$operator> for Isometry + where R: Rotation> + AbstractRotation, + DefaultAllocator: Allocator { } + )*} +); + +impl_multiplicative_structures!( + AbstractSemigroup, + AbstractMonoid, + AbstractQuasigroup, + AbstractLoop, + AbstractGroup +); + +/* + * + * Transformation groups. + * + */ +impl Transformation> + for Isometry +where + R: Rotation> + AbstractRotation, + DefaultAllocator: Allocator, +{ + #[inline] + fn transform_point(&self, pt: &Point) -> Point { + self.transform_point(pt) + } + + #[inline] + fn transform_vector(&self, v: &VectorN) -> VectorN { + self.transform_vector(v) + } +} + +impl ProjectiveTransformation> + for Isometry +where + R: Rotation> + AbstractRotation, + DefaultAllocator: Allocator, +{ + #[inline] + fn inverse_transform_point(&self, pt: &Point) -> Point { + self.inverse_transform_point(pt) + } + + #[inline] + fn inverse_transform_vector(&self, v: &VectorN) -> VectorN { + self.inverse_transform_vector(v) + } +} + +impl AffineTransformation> + for Isometry +where + R: Rotation> + AbstractRotation, + DefaultAllocator: Allocator, +{ + type Rotation = R; + type NonUniformScaling = Id; + type Translation = Translation; + + #[inline] + fn decompose(&self) -> (Self::Translation, R, Id, R) { + ( + self.translation.clone(), + self.rotation.clone(), + Id::new(), + >::identity(), + ) + } + + #[inline] + fn append_translation(&self, t: &Self::Translation) -> Self { + t * self + } + + #[inline] + fn prepend_translation(&self, t: &Self::Translation) -> Self { + self * t + } + + #[inline] + fn append_rotation(&self, r: &Self::Rotation) -> Self { + let shift = Transformation::transform_vector(r, &self.translation.vector); + Isometry::from_parts(Translation::from(shift), r.clone() * self.rotation.clone()) + } + + #[inline] + fn prepend_rotation(&self, r: &Self::Rotation) -> Self { + Isometry::from_parts(self.translation.clone(), self.rotation.prepend_rotation(r)) + } + + #[inline] + fn append_scaling(&self, _: &Self::NonUniformScaling) -> Self { + self.clone() + } + + #[inline] + fn prepend_scaling(&self, _: &Self::NonUniformScaling) -> Self { + self.clone() + } + + #[inline] + fn append_rotation_wrt_point(&self, r: &Self::Rotation, p: &Point) -> Option { + let mut res = self.clone(); + res.append_rotation_wrt_point_mut(r, p); + Some(res) + } +} + +impl Similarity> + for Isometry +where + R: Rotation> + AbstractRotation, + DefaultAllocator: Allocator, +{ + type Scaling = Id; + + #[inline] + fn translation(&self) -> Translation { + self.translation.clone() + } + + #[inline] + fn rotation(&self) -> R { + self.rotation.clone() + } + + #[inline] + fn scaling(&self) -> Id { + Id::new() + } +} + +macro_rules! marker_impl( + ($($Trait: ident),*) => {$( + impl $Trait> for Isometry + where R: Rotation> + AbstractRotation, + DefaultAllocator: Allocator { } + )*} +); + +marker_impl!(AlgaIsometry, DirectIsometry); diff --git a/src/geometry/mod.rs b/src/geometry/mod.rs index 28c03edb..6f991f72 100644 --- a/src/geometry/mod.rs +++ b/src/geometry/mod.rs @@ -6,6 +6,8 @@ mod op_macros; mod abstract_rotation; mod point; +#[cfg(feature = "alga")] +mod point_alga; mod point_alias; mod point_construction; mod point_conversion; @@ -14,6 +16,8 @@ mod point_ops; mod point_simba; mod rotation; +#[cfg(feature = "alga")] +mod rotation_alga; mod rotation_alias; mod rotation_construction; mod rotation_conversion; @@ -22,6 +26,8 @@ mod rotation_simba; // FIXME: implement Rotation methods. mod rotation_specialization; mod quaternion; +#[cfg(feature = "alga")] +mod quaternion_alga; mod quaternion_construction; mod quaternion_conversion; mod quaternion_coordinates; @@ -29,12 +35,16 @@ mod quaternion_ops; mod quaternion_simba; mod unit_complex; +#[cfg(feature = "alga")] +mod unit_complex_alga; mod unit_complex_construction; mod unit_complex_conversion; mod unit_complex_ops; mod unit_complex_simba; mod translation; +#[cfg(feature = "alga")] +mod translation_alga; mod translation_alias; mod translation_construction; mod translation_conversion; @@ -43,6 +53,8 @@ mod translation_ops; mod translation_simba; mod isometry; +#[cfg(feature = "alga")] +mod isometry_alga; mod isometry_alias; mod isometry_construction; mod isometry_conversion; @@ -50,6 +62,8 @@ mod isometry_ops; mod isometry_simba; mod similarity; +#[cfg(feature = "alga")] +mod similarity_alga; mod similarity_alias; mod similarity_construction; mod similarity_conversion; @@ -59,6 +73,8 @@ mod similarity_simba; mod swizzle; mod transform; +#[cfg(feature = "alga")] +mod transform_alga; mod transform_alias; mod transform_construction; mod transform_conversion; diff --git a/src/geometry/point_alga.rs b/src/geometry/point_alga.rs new file mode 100644 index 00000000..5a7cace5 --- /dev/null +++ b/src/geometry/point_alga.rs @@ -0,0 +1,84 @@ +use alga::general::{Field, JoinSemilattice, Lattice, MeetSemilattice, RealField}; +use alga::linear::{AffineSpace, EuclideanSpace}; + +use crate::base::allocator::Allocator; +use crate::base::dimension::DimName; +use crate::base::{DefaultAllocator, Scalar, VectorN}; + +use crate::geometry::Point; + +impl AffineSpace for Point +where + N: Scalar + Field, + DefaultAllocator: Allocator, +{ + type Translation = VectorN; +} + +impl EuclideanSpace for Point +where + DefaultAllocator: Allocator, +{ + type Coordinates = VectorN; + type RealField = N; + + #[inline] + fn origin() -> Self { + Self::origin() + } + + #[inline] + fn coordinates(&self) -> Self::Coordinates { + self.coords.clone() + } + + #[inline] + fn from_coordinates(coords: Self::Coordinates) -> Self { + Self::from(coords) + } + + #[inline] + fn scale_by(&self, n: N) -> Self { + self * n + } +} + +/* + * + * Ordering + * + */ +impl MeetSemilattice for Point +where + N: Scalar + MeetSemilattice, + DefaultAllocator: Allocator, +{ + #[inline] + fn meet(&self, other: &Self) -> Self { + Self::from(self.coords.meet(&other.coords)) + } +} + +impl JoinSemilattice for Point +where + N: Scalar + JoinSemilattice, + DefaultAllocator: Allocator, +{ + #[inline] + fn join(&self, other: &Self) -> Self { + Self::from(self.coords.join(&other.coords)) + } +} + +impl Lattice for Point +where + N: Scalar + Lattice, + DefaultAllocator: Allocator, +{ + #[inline] + fn meet_join(&self, other: &Self) -> (Self, Self) { + let (meet, join) = self.coords.meet_join(&other.coords); + + (Self::from(meet), Self::from(join)) + } +} diff --git a/src/geometry/quaternion_alga.rs b/src/geometry/quaternion_alga.rs new file mode 100755 index 00000000..087c8a58 --- /dev/null +++ b/src/geometry/quaternion_alga.rs @@ -0,0 +1,313 @@ +use num::Zero; + +use alga::general::{ + AbstractGroup, AbstractGroupAbelian, AbstractLoop, AbstractMagma, AbstractModule, + AbstractMonoid, AbstractQuasigroup, AbstractSemigroup, Additive, Id, Identity, Module, + Multiplicative, RealField, TwoSidedInverse, +}; +use alga::linear::{ + AffineTransformation, DirectIsometry, FiniteDimVectorSpace, Isometry, NormedSpace, + OrthogonalTransformation, ProjectiveTransformation, Rotation, Similarity, Transformation, + VectorSpace, +}; + +use crate::base::{Vector3, Vector4}; +use crate::geometry::{Point3, Quaternion, UnitQuaternion}; + +impl Identity for Quaternion { + #[inline] + fn identity() -> Self { + Self::identity() + } +} + +impl Identity for Quaternion { + #[inline] + fn identity() -> Self { + Self::zero() + } +} + +impl AbstractMagma for Quaternion { + #[inline] + fn operate(&self, rhs: &Self) -> Self { + self * rhs + } +} + +impl AbstractMagma for Quaternion { + #[inline] + fn operate(&self, rhs: &Self) -> Self { + self + rhs + } +} + +impl TwoSidedInverse for Quaternion { + #[inline] + fn two_sided_inverse(&self) -> Self { + -self + } +} + +macro_rules! impl_structures( + ($Quaternion: ident; $($marker: ident<$operator: ident>),* $(,)*) => {$( + impl $marker<$operator> for $Quaternion { } + )*} +); + +impl_structures!( + Quaternion; + AbstractSemigroup, + AbstractMonoid, + + AbstractSemigroup, + AbstractQuasigroup, + AbstractMonoid, + AbstractLoop, + AbstractGroup, + AbstractGroupAbelian +); + +/* + * + * Vector space. + * + */ +impl AbstractModule for Quaternion { + type AbstractRing = N; + + #[inline] + fn multiply_by(&self, n: N) -> Self { + self * n + } +} + +impl Module for Quaternion { + type Ring = N; +} + +impl VectorSpace for Quaternion { + type Field = N; +} + +impl FiniteDimVectorSpace for Quaternion { + #[inline] + fn dimension() -> usize { + 4 + } + + #[inline] + fn canonical_basis_element(i: usize) -> Self { + Self::from(Vector4::canonical_basis_element(i)) + } + + #[inline] + fn dot(&self, other: &Self) -> N { + self.coords.dot(&other.coords) + } + + #[inline] + unsafe fn component_unchecked(&self, i: usize) -> &N { + self.coords.component_unchecked(i) + } + + #[inline] + unsafe fn component_unchecked_mut(&mut self, i: usize) -> &mut N { + self.coords.component_unchecked_mut(i) + } +} + +impl NormedSpace for Quaternion { + type RealField = N; + type ComplexField = N; + + #[inline] + fn norm_squared(&self) -> N { + self.coords.norm_squared() + } + + #[inline] + fn norm(&self) -> N { + self.as_vector().norm() + } + + #[inline] + fn normalize(&self) -> Self { + let v = self.coords.normalize(); + Self::from(v) + } + + #[inline] + fn normalize_mut(&mut self) -> N { + self.coords.normalize_mut() + } + + #[inline] + fn try_normalize(&self, min_norm: N) -> Option { + if let Some(v) = self.coords.try_normalize(min_norm) { + Some(Self::from(v)) + } else { + None + } + } + + #[inline] + fn try_normalize_mut(&mut self, min_norm: N) -> Option { + self.coords.try_normalize_mut(min_norm) + } +} + +/* + * + * Implementations for UnitQuaternion. + * + */ +impl Identity for UnitQuaternion { + #[inline] + fn identity() -> Self { + Self::identity() + } +} + +impl AbstractMagma for UnitQuaternion { + #[inline] + fn operate(&self, rhs: &Self) -> Self { + self * rhs + } +} + +impl TwoSidedInverse + for UnitQuaternion +{ + #[inline] + fn two_sided_inverse(&self) -> Self { + self.inverse() + } + + #[inline] + fn two_sided_inverse_mut(&mut self) { + self.inverse_mut() + } +} + +impl_structures!( + UnitQuaternion; + AbstractSemigroup, + AbstractQuasigroup, + AbstractMonoid, + AbstractLoop, + AbstractGroup +); + +impl Transformation> for UnitQuaternion { + #[inline] + fn transform_point(&self, pt: &Point3) -> Point3 { + self.transform_point(pt) + } + + #[inline] + fn transform_vector(&self, v: &Vector3) -> Vector3 { + self.transform_vector(v) + } +} + +impl ProjectiveTransformation> + for UnitQuaternion +{ + #[inline] + fn inverse_transform_point(&self, pt: &Point3) -> Point3 { + self.inverse_transform_point(pt) + } + + #[inline] + fn inverse_transform_vector(&self, v: &Vector3) -> Vector3 { + self.inverse_transform_vector(v) + } +} + +impl AffineTransformation> + for UnitQuaternion +{ + type Rotation = Self; + type NonUniformScaling = Id; + type Translation = Id; + + #[inline] + fn decompose(&self) -> (Id, Self, Id, Self) { + (Id::new(), self.clone(), Id::new(), Self::identity()) + } + + #[inline] + fn append_translation(&self, _: &Self::Translation) -> Self { + self.clone() + } + + #[inline] + fn prepend_translation(&self, _: &Self::Translation) -> Self { + self.clone() + } + + #[inline] + fn append_rotation(&self, r: &Self::Rotation) -> Self { + r * self + } + + #[inline] + fn prepend_rotation(&self, r: &Self::Rotation) -> Self { + self * r + } + + #[inline] + fn append_scaling(&self, _: &Self::NonUniformScaling) -> Self { + self.clone() + } + + #[inline] + fn prepend_scaling(&self, _: &Self::NonUniformScaling) -> Self { + self.clone() + } +} + +impl Similarity> for UnitQuaternion { + type Scaling = Id; + + #[inline] + fn translation(&self) -> Id { + Id::new() + } + + #[inline] + fn rotation(&self) -> Self { + self.clone() + } + + #[inline] + fn scaling(&self) -> Id { + Id::new() + } +} + +macro_rules! marker_impl( + ($($Trait: ident),*) => {$( + impl $Trait> for UnitQuaternion { } + )*} +); + +marker_impl!(Isometry, DirectIsometry, OrthogonalTransformation); + +impl Rotation> for UnitQuaternion { + #[inline] + fn powf(&self, n: N) -> Option { + Some(self.powf(n)) + } + + #[inline] + fn rotation_between(a: &Vector3, b: &Vector3) -> Option { + Self::rotation_between(a, b) + } + + #[inline] + fn scaled_rotation_between(a: &Vector3, b: &Vector3, s: N) -> Option { + Self::scaled_rotation_between(a, b, s) + } +} diff --git a/src/geometry/rotation_alga.rs b/src/geometry/rotation_alga.rs new file mode 100755 index 00000000..f37ebbd1 --- /dev/null +++ b/src/geometry/rotation_alga.rs @@ -0,0 +1,291 @@ +use alga::general::{ + AbstractGroup, AbstractLoop, AbstractMagma, AbstractMonoid, AbstractQuasigroup, + AbstractSemigroup, Id, Identity, Multiplicative, RealField, TwoSidedInverse, +}; +use alga::linear::{ + self, AffineTransformation, DirectIsometry, Isometry, OrthogonalTransformation, + ProjectiveTransformation, Similarity, Transformation, +}; + +use crate::base::allocator::Allocator; +use crate::base::dimension::DimName; +use crate::base::{DefaultAllocator, VectorN}; + +use crate::geometry::{Point, Rotation}; + +/* + * + * Algebraic structures. + * + */ +impl Identity + for Rotation +where + DefaultAllocator: Allocator, +{ + #[inline] + fn identity() -> Self { + Self::identity() + } +} + +impl TwoSidedInverse + for Rotation +where + DefaultAllocator: Allocator, +{ + #[inline] + #[must_use = "Did you mean to use two_sided_inverse_mut()?"] + fn two_sided_inverse(&self) -> Self { + self.transpose() + } + + #[inline] + fn two_sided_inverse_mut(&mut self) { + self.transpose_mut() + } +} + +impl AbstractMagma + for Rotation +where + DefaultAllocator: Allocator, +{ + #[inline] + fn operate(&self, rhs: &Self) -> Self { + self * rhs + } +} + +macro_rules! impl_multiplicative_structures( + ($($marker: ident<$operator: ident>),* $(,)*) => {$( + impl $marker<$operator> for Rotation + where DefaultAllocator: Allocator { } + )*} +); + +impl_multiplicative_structures!( + AbstractSemigroup, + AbstractMonoid, + AbstractQuasigroup, + AbstractLoop, + AbstractGroup +); + +/* + * + * Transformation groups. + * + */ +impl Transformation> + for Rotation +where + DefaultAllocator: Allocator + Allocator, +{ + #[inline] + fn transform_point(&self, pt: &Point) -> Point { + self.transform_point(pt) + } + + #[inline] + fn transform_vector(&self, v: &VectorN) -> VectorN { + self.transform_vector(v) + } +} + +impl ProjectiveTransformation> + for Rotation +where + DefaultAllocator: Allocator + Allocator, +{ + #[inline] + fn inverse_transform_point(&self, pt: &Point) -> Point { + self.inverse_transform_point(pt) + } + + #[inline] + fn inverse_transform_vector(&self, v: &VectorN) -> VectorN { + self.inverse_transform_vector(v) + } +} + +impl AffineTransformation> + for Rotation +where + DefaultAllocator: Allocator + Allocator, +{ + type Rotation = Self; + type NonUniformScaling = Id; + type Translation = Id; + + #[inline] + fn decompose(&self) -> (Id, Self, Id, Self) { + (Id::new(), self.clone(), Id::new(), Self::identity()) + } + + #[inline] + fn append_translation(&self, _: &Self::Translation) -> Self { + self.clone() + } + + #[inline] + fn prepend_translation(&self, _: &Self::Translation) -> Self { + self.clone() + } + + #[inline] + fn append_rotation(&self, r: &Self::Rotation) -> Self { + r * self + } + + #[inline] + fn prepend_rotation(&self, r: &Self::Rotation) -> Self { + self * r + } + + #[inline] + fn append_scaling(&self, _: &Self::NonUniformScaling) -> Self { + self.clone() + } + + #[inline] + fn prepend_scaling(&self, _: &Self::NonUniformScaling) -> Self { + self.clone() + } +} + +impl Similarity> for Rotation +where + DefaultAllocator: Allocator + Allocator, +{ + type Scaling = Id; + + #[inline] + fn translation(&self) -> Id { + Id::new() + } + + #[inline] + fn rotation(&self) -> Self { + self.clone() + } + + #[inline] + fn scaling(&self) -> Id { + Id::new() + } +} + +macro_rules! marker_impl( + ($($Trait: ident),*) => {$( + impl $Trait> for Rotation + where DefaultAllocator: Allocator + + Allocator { } + )*} +); + +marker_impl!(Isometry, DirectIsometry, OrthogonalTransformation); + +/// Subgroups of the n-dimensional rotation group `SO(n)`. +impl linear::Rotation> + for Rotation +where + DefaultAllocator: Allocator + Allocator, +{ + #[inline] + fn powf(&self, _: N) -> Option { + // XXX: Add the general case. + // XXX: Use specialization for 2D and 3D. + unimplemented!() + } + + #[inline] + fn rotation_between(_: &VectorN, _: &VectorN) -> Option { + // XXX: Add the general case. + // XXX: Use specialization for 2D and 3D. + unimplemented!() + } + + #[inline] + fn scaled_rotation_between(_: &VectorN, _: &VectorN, _: N) -> Option { + // XXX: Add the general case. + // XXX: Use specialization for 2D and 3D. + unimplemented!() + } +} + +/* +impl Matrix for Rotation { + type Field = N; + type Row = Matrix; + type Column = Matrix; + type Transpose = Self; + + #[inline] + fn nrows(&self) -> usize { + self.submatrix.nrows() + } + + #[inline] + fn ncolumns(&self) -> usize { + self.submatrix.ncolumns() + } + + #[inline] + fn row(&self, i: usize) -> Self::Row { + self.submatrix.row(i) + } + + #[inline] + fn column(&self, i: usize) -> Self::Column { + self.submatrix.column(i) + } + + #[inline] + fn get(&self, i: usize, j: usize) -> Self::Field { + self.submatrix[(i, j)] + } + + #[inline] + unsafe fn get_unchecked(&self, i: usize, j: usize) -> Self::Field { + self.submatrix.at_fast(i, j) + } + + #[inline] + fn transpose(&self) -> Self::Transpose { + Rotation::from_matrix_unchecked(self.submatrix.transpose()) + } +} + +impl SquareMatrix for Rotation { + type Vector = Matrix; + + #[inline] + fn diagonal(&self) -> Self::Coordinates { + self.submatrix.diagonal() + } + + #[inline] + fn determinant(&self) -> Self::Field { + crate::one() + } + + #[inline] + fn try_inverse(&self) -> Option { + Some(::transpose(self)) + } + + #[inline] + fn try_inverse_mut(&mut self) -> bool { + self.transpose_mut(); + true + } + + #[inline] + fn transpose_mut(&mut self) { + self.submatrix.transpose_mut() + } +} + +impl InversibleSquareMatrix for Rotation { } +*/ diff --git a/src/geometry/similarity_alga.rs b/src/geometry/similarity_alga.rs new file mode 100755 index 00000000..58dd4563 --- /dev/null +++ b/src/geometry/similarity_alga.rs @@ -0,0 +1,196 @@ +use alga::general::{ + AbstractGroup, AbstractLoop, AbstractMagma, AbstractMonoid, AbstractQuasigroup, + AbstractSemigroup, Identity, Multiplicative, RealField, TwoSidedInverse, +}; +use alga::linear::Similarity as AlgaSimilarity; +use alga::linear::{AffineTransformation, ProjectiveTransformation, Rotation, Transformation}; + +use crate::base::allocator::Allocator; +use crate::base::dimension::DimName; +use crate::base::{DefaultAllocator, VectorN}; + +use crate::geometry::{AbstractRotation, Point, Similarity, Translation}; + +/* + * + * Algebraic structures. + * + */ +impl Identity + for Similarity +where + R: Rotation> + AbstractRotation, + DefaultAllocator: Allocator, +{ + #[inline] + fn identity() -> Self { + Self::identity() + } +} + +impl TwoSidedInverse + for Similarity +where + R: Rotation> + AbstractRotation, + DefaultAllocator: Allocator, +{ + #[inline] + #[must_use = "Did you mean to use two_sided_inverse_mut()?"] + fn two_sided_inverse(&self) -> Self { + self.inverse() + } + + #[inline] + fn two_sided_inverse_mut(&mut self) { + self.inverse_mut() + } +} + +impl AbstractMagma + for Similarity +where + R: Rotation> + AbstractRotation, + DefaultAllocator: Allocator, +{ + #[inline] + fn operate(&self, rhs: &Self) -> Self { + self * rhs + } +} + +macro_rules! impl_multiplicative_structures( + ($($marker: ident<$operator: ident>),* $(,)*) => {$( + impl $marker<$operator> for Similarity + where R: Rotation> + AbstractRotation, + DefaultAllocator: Allocator { } + )*} +); + +impl_multiplicative_structures!( + AbstractSemigroup, + AbstractMonoid, + AbstractQuasigroup, + AbstractLoop, + AbstractGroup +); + +/* + * + * Transformation groups. + * + */ +impl Transformation> + for Similarity +where + R: Rotation> + AbstractRotation, + DefaultAllocator: Allocator, +{ + #[inline] + fn transform_point(&self, pt: &Point) -> Point { + self.transform_point(pt) + } + + #[inline] + fn transform_vector(&self, v: &VectorN) -> VectorN { + self.transform_vector(v) + } +} + +impl ProjectiveTransformation> + for Similarity +where + R: Rotation> + AbstractRotation, + DefaultAllocator: Allocator, +{ + #[inline] + fn inverse_transform_point(&self, pt: &Point) -> Point { + self.inverse_transform_point(pt) + } + + #[inline] + fn inverse_transform_vector(&self, v: &VectorN) -> VectorN { + self.inverse_transform_vector(v) + } +} + +impl AffineTransformation> + for Similarity +where + R: Rotation> + AbstractRotation, + DefaultAllocator: Allocator, +{ + type NonUniformScaling = N; + type Rotation = R; + type Translation = Translation; + + #[inline] + fn decompose(&self) -> (Translation, R, N, R) { + ( + self.isometry.translation.clone(), + self.isometry.rotation.clone(), + self.scaling(), + >::identity(), + ) + } + + #[inline] + fn append_translation(&self, t: &Self::Translation) -> Self { + t * self + } + + #[inline] + fn prepend_translation(&self, t: &Self::Translation) -> Self { + self * t + } + + #[inline] + fn append_rotation(&self, r: &Self::Rotation) -> Self { + Similarity::from_isometry(self.isometry.append_rotation(r), self.scaling()) + } + + #[inline] + fn prepend_rotation(&self, r: &Self::Rotation) -> Self { + Similarity::from_isometry(self.isometry.prepend_rotation(r), self.scaling()) + } + + #[inline] + fn append_scaling(&self, s: &Self::NonUniformScaling) -> Self { + self.append_scaling(*s) + } + + #[inline] + fn prepend_scaling(&self, s: &Self::NonUniformScaling) -> Self { + self.prepend_scaling(*s) + } + + #[inline] + fn append_rotation_wrt_point(&self, r: &Self::Rotation, p: &Point) -> Option { + let mut res = self.clone(); + res.append_rotation_wrt_point_mut(r, p); + Some(res) + } +} + +impl AlgaSimilarity> + for Similarity +where + R: Rotation> + AbstractRotation, + DefaultAllocator: Allocator, +{ + type Scaling = N; + + #[inline] + fn translation(&self) -> Translation { + self.isometry.translation() + } + + #[inline] + fn rotation(&self) -> R { + self.isometry.rotation() + } + + #[inline] + fn scaling(&self) -> N { + self.scaling() + } +} diff --git a/src/geometry/transform_alga.rs b/src/geometry/transform_alga.rs new file mode 100755 index 00000000..3ba6eb7d --- /dev/null +++ b/src/geometry/transform_alga.rs @@ -0,0 +1,149 @@ +use alga::general::{ + AbstractGroup, AbstractLoop, AbstractMagma, AbstractMonoid, AbstractQuasigroup, + AbstractSemigroup, Identity, Multiplicative, RealField, TwoSidedInverse, +}; +use alga::linear::{ProjectiveTransformation, Transformation}; + +use crate::base::allocator::Allocator; +use crate::base::dimension::{DimNameAdd, DimNameSum, U1}; +use crate::base::{DefaultAllocator, VectorN}; + +use crate::geometry::{Point, SubTCategoryOf, TCategory, TProjective, Transform}; + +/* + * + * Algebraic structures. + * + */ +impl, C> Identity + for Transform +where + C: TCategory, + DefaultAllocator: Allocator, DimNameSum>, +{ + #[inline] + fn identity() -> Self { + Self::identity() + } +} + +impl, C> TwoSidedInverse + for Transform +where + C: SubTCategoryOf, + DefaultAllocator: Allocator, DimNameSum>, +{ + #[inline] + #[must_use = "Did you mean to use two_sided_inverse_mut()?"] + fn two_sided_inverse(&self) -> Self { + self.clone().inverse() + } + + #[inline] + fn two_sided_inverse_mut(&mut self) { + self.inverse_mut() + } +} + +impl, C> AbstractMagma + for Transform +where + C: TCategory, + DefaultAllocator: Allocator, DimNameSum>, +{ + #[inline] + fn operate(&self, rhs: &Self) -> Self { + self * rhs + } +} + +macro_rules! impl_multiplicative_structures( + ($($marker: ident<$operator: ident>),* $(,)*) => {$( + impl, C> $marker<$operator> for Transform + where C: TCategory, + DefaultAllocator: Allocator, DimNameSum> { } + )*} +); + +macro_rules! impl_inversible_multiplicative_structures( + ($($marker: ident<$operator: ident>),* $(,)*) => {$( + impl, C> $marker<$operator> for Transform + where C: SubTCategoryOf, + DefaultAllocator: Allocator, DimNameSum> { } + )*} +); + +impl_multiplicative_structures!( + AbstractSemigroup, + AbstractMonoid, +); + +impl_inversible_multiplicative_structures!( + AbstractQuasigroup, + AbstractLoop, + AbstractGroup +); + +/* + * + * Transformation groups. + * + */ +impl, C> Transformation> for Transform +where + N: RealField + simba::scalar::RealField, + C: TCategory, + DefaultAllocator: Allocator, DimNameSum> + + Allocator> + + Allocator + + Allocator, +{ + #[inline] + fn transform_point(&self, pt: &Point) -> Point { + self.transform_point(pt) + } + + #[inline] + fn transform_vector(&self, v: &VectorN) -> VectorN { + self.transform_vector(v) + } +} + +impl, C> ProjectiveTransformation> for Transform +where + N: RealField + simba::scalar::RealField, + C: SubTCategoryOf, + DefaultAllocator: Allocator, DimNameSum> + + Allocator> + + Allocator + + Allocator, +{ + #[inline] + fn inverse_transform_point(&self, pt: &Point) -> Point { + self.inverse_transform_point(pt) + } + + #[inline] + fn inverse_transform_vector(&self, v: &VectorN) -> VectorN { + self.inverse_transform_vector(v) + } +} + +// FIXME: we need to implement an SVD for this. +// +// impl, C> AffineTransformation> for Transform +// where N: RealField, +// C: SubTCategoryOf, +// DefaultAllocator: Allocator, DimNameSum> + +// Allocator + +// Allocator { +// type PreRotation = Rotation; +// type NonUniformScaling = VectorN; +// type PostRotation = Rotation; +// type Translation = Translation; +// +// #[inline] +// fn decompose(&self) -> (Self::Translation, Self::PostRotation, Self::NonUniformScaling, Self::PreRotation) { +// unimplemented!() +// } +// } diff --git a/src/geometry/translation_alga.rs b/src/geometry/translation_alga.rs new file mode 100755 index 00000000..aba6f1f4 --- /dev/null +++ b/src/geometry/translation_alga.rs @@ -0,0 +1,215 @@ +use alga::general::{ + AbstractGroup, AbstractLoop, AbstractMagma, AbstractMonoid, AbstractQuasigroup, + AbstractSemigroup, Id, Identity, Multiplicative, RealField, TwoSidedInverse, +}; +use alga::linear::Translation as AlgaTranslation; +use alga::linear::{ + AffineTransformation, DirectIsometry, Isometry, ProjectiveTransformation, Similarity, + Transformation, +}; + +use crate::base::allocator::Allocator; +use crate::base::dimension::DimName; +use crate::base::{DefaultAllocator, VectorN}; + +use crate::geometry::{Point, Translation}; + +/* + * + * Algebraic structures. + * + */ +impl Identity + for Translation +where + DefaultAllocator: Allocator, +{ + #[inline] + fn identity() -> Self { + Self::identity() + } +} + +impl TwoSidedInverse + for Translation +where + DefaultAllocator: Allocator, +{ + #[inline] + #[must_use = "Did you mean to use two_sided_inverse_mut()?"] + fn two_sided_inverse(&self) -> Self { + self.inverse() + } + + #[inline] + fn two_sided_inverse_mut(&mut self) { + self.inverse_mut() + } +} + +impl AbstractMagma + for Translation +where + DefaultAllocator: Allocator, +{ + #[inline] + fn operate(&self, rhs: &Self) -> Self { + self * rhs + } +} + +macro_rules! impl_multiplicative_structures( + ($($marker: ident<$operator: ident>),* $(,)*) => {$( + impl $marker<$operator> for Translation + where DefaultAllocator: Allocator { } + )*} +); + +impl_multiplicative_structures!( + AbstractSemigroup, + AbstractMonoid, + AbstractQuasigroup, + AbstractLoop, + AbstractGroup +); + +/* + * + * Transformation groups. + * + */ +impl Transformation> + for Translation +where + DefaultAllocator: Allocator, +{ + #[inline] + fn transform_point(&self, pt: &Point) -> Point { + self.transform_point(pt) + } + + #[inline] + fn transform_vector(&self, v: &VectorN) -> VectorN { + v.clone() + } +} + +impl ProjectiveTransformation> + for Translation +where + DefaultAllocator: Allocator, +{ + #[inline] + fn inverse_transform_point(&self, pt: &Point) -> Point { + self.inverse_transform_point(pt) + } + + #[inline] + fn inverse_transform_vector(&self, v: &VectorN) -> VectorN { + v.clone() + } +} + +impl AffineTransformation> + for Translation +where + DefaultAllocator: Allocator, +{ + type Rotation = Id; + type NonUniformScaling = Id; + type Translation = Self; + + #[inline] + fn decompose(&self) -> (Self, Id, Id, Id) { + (self.clone(), Id::new(), Id::new(), Id::new()) + } + + #[inline] + fn append_translation(&self, t: &Self::Translation) -> Self { + t * self + } + + #[inline] + fn prepend_translation(&self, t: &Self::Translation) -> Self { + self * t + } + + #[inline] + fn append_rotation(&self, _: &Self::Rotation) -> Self { + self.clone() + } + + #[inline] + fn prepend_rotation(&self, _: &Self::Rotation) -> Self { + self.clone() + } + + #[inline] + fn append_scaling(&self, _: &Self::NonUniformScaling) -> Self { + self.clone() + } + + #[inline] + fn prepend_scaling(&self, _: &Self::NonUniformScaling) -> Self { + self.clone() + } +} + +impl Similarity> + for Translation +where + DefaultAllocator: Allocator, +{ + type Scaling = Id; + + #[inline] + fn translation(&self) -> Self { + self.clone() + } + + #[inline] + fn rotation(&self) -> Id { + Id::new() + } + + #[inline] + fn scaling(&self) -> Id { + Id::new() + } +} + +macro_rules! marker_impl( + ($($Trait: ident),*) => {$( + impl $Trait> for Translation + where DefaultAllocator: Allocator { } + )*} +); + +marker_impl!(Isometry, DirectIsometry); + +/// Subgroups of the n-dimensional translation group `T(n)`. +impl AlgaTranslation> + for Translation +where + DefaultAllocator: Allocator, +{ + #[inline] + fn to_vector(&self) -> VectorN { + self.vector.clone() + } + + #[inline] + fn from_vector(v: VectorN) -> Option { + Some(Self::from(v)) + } + + #[inline] + fn powf(&self, n: N) -> Option { + Some(Self::from(&self.vector * n)) + } + + #[inline] + fn translation_between(a: &Point, b: &Point) -> Option { + Some(Self::from(b - a)) + } +} diff --git a/src/geometry/unit_complex_alga.rs b/src/geometry/unit_complex_alga.rs new file mode 100755 index 00000000..46e30535 --- /dev/null +++ b/src/geometry/unit_complex_alga.rs @@ -0,0 +1,185 @@ +use alga::general::{ + AbstractGroup, AbstractLoop, AbstractMagma, AbstractMonoid, AbstractQuasigroup, + AbstractSemigroup, Id, Identity, Multiplicative, RealField, TwoSidedInverse, +}; +use alga::linear::{ + AffineTransformation, DirectIsometry, Isometry, OrthogonalTransformation, + ProjectiveTransformation, Rotation, Similarity, Transformation, +}; + +use crate::base::allocator::Allocator; +use crate::base::dimension::U2; +use crate::base::{DefaultAllocator, Vector2}; +use crate::geometry::{Point2, UnitComplex}; + +/* + * + * Implementations for UnitComplex. + * + */ +impl Identity for UnitComplex { + #[inline] + fn identity() -> Self { + Self::identity() + } +} + +impl AbstractMagma for UnitComplex { + #[inline] + fn operate(&self, rhs: &Self) -> Self { + self * rhs + } +} + +impl TwoSidedInverse for UnitComplex { + #[inline] + #[must_use = "Did you mean to use two_sided_inverse_mut()?"] + fn two_sided_inverse(&self) -> Self { + self.inverse() + } + + #[inline] + fn two_sided_inverse_mut(&mut self) { + self.inverse_mut() + } +} + +macro_rules! impl_structures( + ($($marker: ident<$operator: ident>),* $(,)*) => {$( + impl $marker<$operator> for UnitComplex { + } + )*} +); + +impl_structures!( + AbstractSemigroup, + AbstractQuasigroup, + AbstractMonoid, + AbstractLoop, + AbstractGroup +); + +impl Transformation> for UnitComplex +where + DefaultAllocator: Allocator, +{ + #[inline] + fn transform_point(&self, pt: &Point2) -> Point2 { + self.transform_point(pt) + } + + #[inline] + fn transform_vector(&self, v: &Vector2) -> Vector2 { + self.transform_vector(v) + } +} + +impl ProjectiveTransformation> for UnitComplex +where + DefaultAllocator: Allocator, +{ + #[inline] + fn inverse_transform_point(&self, pt: &Point2) -> Point2 { + self.inverse_transform_point(pt) + } + + #[inline] + fn inverse_transform_vector(&self, v: &Vector2) -> Vector2 { + self.inverse_transform_vector(v) + } +} + +impl AffineTransformation> for UnitComplex +where + DefaultAllocator: Allocator, +{ + type Rotation = Self; + type NonUniformScaling = Id; + type Translation = Id; + + #[inline] + fn decompose(&self) -> (Id, Self, Id, Self) { + (Id::new(), self.clone(), Id::new(), Self::identity()) + } + + #[inline] + fn append_translation(&self, _: &Self::Translation) -> Self { + self.clone() + } + + #[inline] + fn prepend_translation(&self, _: &Self::Translation) -> Self { + self.clone() + } + + #[inline] + fn append_rotation(&self, r: &Self::Rotation) -> Self { + r * self + } + + #[inline] + fn prepend_rotation(&self, r: &Self::Rotation) -> Self { + self * r + } + + #[inline] + fn append_scaling(&self, _: &Self::NonUniformScaling) -> Self { + self.clone() + } + + #[inline] + fn prepend_scaling(&self, _: &Self::NonUniformScaling) -> Self { + self.clone() + } +} + +impl Similarity> for UnitComplex +where + DefaultAllocator: Allocator, +{ + type Scaling = Id; + + #[inline] + fn translation(&self) -> Id { + Id::new() + } + + #[inline] + fn rotation(&self) -> Self { + self.clone() + } + + #[inline] + fn scaling(&self) -> Id { + Id::new() + } +} + +macro_rules! marker_impl( + ($($Trait: ident),*) => {$( + impl $Trait> for UnitComplex + where DefaultAllocator: Allocator { } + )*} +); + +marker_impl!(Isometry, DirectIsometry, OrthogonalTransformation); + +impl Rotation> for UnitComplex +where + DefaultAllocator: Allocator, +{ + #[inline] + fn powf(&self, n: N) -> Option { + Some(self.powf(n)) + } + + #[inline] + fn rotation_between(a: &Vector2, b: &Vector2) -> Option { + Some(Self::rotation_between(a, b)) + } + + #[inline] + fn scaled_rotation_between(a: &Vector2, b: &Vector2, s: N) -> Option { + Some(Self::scaled_rotation_between(a, b, s)) + } +} diff --git a/src/sparse/cs_matrix.rs b/src/sparse/cs_matrix.rs index ffe87e5f..9ff656d4 100644 --- a/src/sparse/cs_matrix.rs +++ b/src/sparse/cs_matrix.rs @@ -55,7 +55,6 @@ pub trait CsStorageIter<'a, N, R, C = U1> { /// Iterates through all the row indices of the j-th column. fn column_row_indices(&'a self, j: usize) -> Self::ColumnRowIndices; - #[inline(always)] /// Iterates through all the entries of the j-th column. fn column_entries(&'a self, j: usize) -> Self::ColumnEntries; } @@ -106,7 +105,8 @@ pub trait CsStorageMut: /// A storage of column-compressed sparse matrix based on a Vec. #[derive(Clone, Debug, PartialEq)] pub struct CsVecStorage -where DefaultAllocator: Allocator +where + DefaultAllocator: Allocator, { pub(crate) shape: (R, C), pub(crate) p: VectorN, @@ -115,7 +115,8 @@ where DefaultAllocator: Allocator } impl CsVecStorage -where DefaultAllocator: Allocator +where + DefaultAllocator: Allocator, { /// The value buffer of this storage. pub fn values(&self) -> &[N] { @@ -136,7 +137,8 @@ where DefaultAllocator: Allocator impl CsVecStorage where DefaultAllocator: Allocator {} impl<'a, N: Scalar, R: Dim, C: Dim> CsStorageIter<'a, N, R, C> for CsVecStorage -where DefaultAllocator: Allocator +where + DefaultAllocator: Allocator, { type ColumnEntries = ColumnEntries<'a, N>; type ColumnRowIndices = iter::Cloned>; @@ -155,7 +157,8 @@ where DefaultAllocator: Allocator } impl CsStorage for CsVecStorage -where DefaultAllocator: Allocator +where + DefaultAllocator: Allocator, { #[inline] fn shape(&self) -> (R, C) { @@ -200,7 +203,8 @@ where DefaultAllocator: Allocator } impl<'a, N: Scalar, R: Dim, C: Dim> CsStorageIterMut<'a, N, R, C> for CsVecStorage -where DefaultAllocator: Allocator +where + DefaultAllocator: Allocator, { type ValuesMut = slice::IterMut<'a, N>; type ColumnEntriesMut = iter::Zip>, slice::IterMut<'a, N>>; @@ -220,8 +224,10 @@ where DefaultAllocator: Allocator } } -impl CsStorageMut for CsVecStorage where DefaultAllocator: Allocator -{} +impl CsStorageMut for CsVecStorage where + DefaultAllocator: Allocator +{ +} /* pub struct CsSliceStorage<'a, N: Scalar, R: Dim, C: DimAdd> { @@ -247,7 +253,8 @@ pub struct CsMatrix< pub type CsVector> = CsMatrix; impl CsMatrix -where DefaultAllocator: Allocator +where + DefaultAllocator: Allocator, { /// Creates a new compressed sparse column matrix with the specified dimension and /// `nvals` possible non-zero values. @@ -403,7 +410,9 @@ impl> CsMatrix { /// Computes the transpose of this sparse matrix. pub fn transpose(&self) -> CsMatrix - where DefaultAllocator: Allocator { + where + DefaultAllocator: Allocator, + { let (nrows, ncols) = self.data.shape(); let nvals = self.len(); @@ -442,10 +451,13 @@ impl> CsMatrix { } impl CsMatrix -where DefaultAllocator: Allocator +where + DefaultAllocator: Allocator, { pub(crate) fn sort(&mut self) - where DefaultAllocator: Allocator { + where + DefaultAllocator: Allocator, + { // Size = R let nrows = self.data.shape().0; let mut workspace = unsafe { VectorN::new_uninitialized_generic(nrows, U1) }; @@ -477,7 +489,9 @@ where DefaultAllocator: Allocator // Remove dupliate entries on a sorted CsMatrix. pub(crate) fn dedup(&mut self) - where N: Zero + ClosedAdd { + where + N: Zero + ClosedAdd, + { let mut curr_i = 0; for j in 0..self.ncols() {