diff --git a/src/lib.rs b/src/lib.rs index 9c10add5..5ed1613d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -133,7 +133,7 @@ pub use traits::{ POrdering, PntAsVec, Repeat, - Rotate, Rotation, RotationMatrix, RotationWithTranslation, + Rotate, Rotation, RotationMatrix, RotationWithTranslation, RotationTo, Row, Shape, SquareMat, @@ -569,6 +569,21 @@ pub fn append_rotation_wrt_center + Copy, RotationWithTranslation::append_rotation_wrt_center(m, amount) } +/* + * RotationTo + */ +/// Computes the angle of the rotation needed to transfom `a` to `b`. +#[inline(always)] +pub fn angle_between(a: &V, b: &V) -> V::AngleType { + a.angle_to(b) +} + +/// Computes the rotation needed to transform `a` to `b`. +#[inline(always)] +pub fn rotation_between(a: &V, b: &V) -> V::DeltaRotationType { + a.rotation_to(b) +} + /* * RotationMatrix */ diff --git a/src/structs/quat.rs b/src/structs/quat.rs index 24f70bc1..604a1e20 100644 --- a/src/structs/quat.rs +++ b/src/structs/quat.rs @@ -12,7 +12,7 @@ use structs::{Vec3, Pnt3, Rot3, Mat3}; use traits::operations::{ApproxEq, Inv, POrd, POrdering, Axpy}; use traits::structure::{Cast, Indexable, Iterable, IterableMut, Dim, Shape, BaseFloat, BaseNum, Bounded, Repeat}; -use traits::geometry::{Norm, Rotation, Rotate, Transform}; +use traits::geometry::{Norm, Rotation, Rotate, RotationTo, Transform}; #[cfg(feature="arbitrary")] use quickcheck::{Arbitrary, Gen}; @@ -456,6 +456,24 @@ impl> Rotate> for UnitQuat { } } +impl> RotationTo for UnitQuat { + type AngleType = N; + type DeltaRotationType = UnitQuat; + + #[inline] + fn angle_to(&self, other: &Self) -> N { + let delta = self.rotation_to(other); + let _2 = ::one::() + ::one(); + + _2 * delta.q.vector().norm().atan2(delta.q.w) + } + + #[inline] + fn rotation_to(&self, other: &Self) -> UnitQuat { + *other / *self + } +} + impl> Transform> for UnitQuat { #[inline] fn transform(&self, v: &Vec3) -> Vec3 { diff --git a/src/structs/rot.rs b/src/structs/rot.rs index dca149b4..ebf24217 100644 --- a/src/structs/rot.rs +++ b/src/structs/rot.rs @@ -5,8 +5,8 @@ use std::ops::{Mul, Neg, Index}; use rand::{Rand, Rng}; use num::{Zero, One}; -use traits::geometry::{Rotate, Rotation, AbsoluteRotate, RotationMatrix, Transform, ToHomogeneous, - Norm, Cross}; +use traits::geometry::{Rotate, Rotation, AbsoluteRotate, RotationMatrix, RotationTo, Transform, + ToHomogeneous, Norm, Cross}; use traits::structure::{Cast, Dim, Row, Col, BaseFloat, BaseNum, Eye, Diag}; use traits::operations::{Absolute, Inv, Transpose, ApproxEq}; use structs::vec::{Vec1, Vec2, Vec3, Vec4}; @@ -71,6 +71,21 @@ impl Rotation> for Rot2 { } } +impl RotationTo for Rot2 { + type AngleType = N; + type DeltaRotationType = Rot2; + + #[inline] + fn angle_to(&self, other: &Self) -> N { + self.rotation_to(other).rotation().norm() + } + + #[inline] + fn rotation_to(&self, other: &Self) -> Rot2 { + *other * ::inv(self).unwrap() + } +} + impl Rand for Rot2 { #[inline] fn rand(rng: &mut R) -> Rot2 { @@ -283,6 +298,22 @@ Rotation> for Rot3 { } } +impl RotationTo for Rot3 { + type AngleType = N; + type DeltaRotationType = Rot3; + + #[inline] + fn angle_to(&self, other: &Self) -> N { + // FIXME: refactor to avoid the normalization of the rotation axisangle vector. + self.rotation_to(other).rotation().norm() + } + + #[inline] + fn rotation_to(&self, other: &Self) -> Rot3 { + *other * ::inv(self).unwrap() + } +} + impl Rand for Rot3 { #[inline] fn rand(rng: &mut R) -> Rot3 { diff --git a/src/structs/spec/vec.rs b/src/structs/spec/vec.rs index fcb0fa1c..09e8f63a 100644 --- a/src/structs/spec/vec.rs +++ b/src/structs/spec/vec.rs @@ -1,9 +1,50 @@ use std::ops::{Sub, Mul, Neg}; use num::{Zero, One}; use traits::structure::{Cast, Row, Basis, BaseFloat}; -use traits::geometry::{Norm, Cross, CrossMatrix, UniformSphereSample}; +use traits::geometry::{Norm, Cross, CrossMatrix, RotationTo, UniformSphereSample}; use structs::vec::{Vec1, Vec2, Vec3, Vec4}; use structs::mat::Mat3; +use structs::rot::{Rot2, Rot3}; + +impl RotationTo for Vec2 { + type AngleType = N; + type DeltaRotationType = Rot2; + + #[inline] + fn angle_to(&self, other: &Self) -> N { + ::cross(self, other).x.atan2(::dot(self, other)) + } + + #[inline] + fn rotation_to(&self, other: &Self) -> Rot2 { + Rot2::new(Vec1::new(self.angle_to(other))) + } +} + +impl RotationTo for Vec3 { + type AngleType = N; + type DeltaRotationType = Rot3; + + #[inline] + fn angle_to(&self, other: &Self) -> N { + ::cross(self, other).norm().atan2(::dot(self, other)) + } + + #[inline] + fn rotation_to(&self, other: &Self) -> Rot3 { + let mut axis = ::cross(self, other); + let norm = axis.normalize_mut(); + + if ::is_zero(&norm) { + ::one() + } + else { + let axis_angle = axis * norm.atan2(::dot(self, other)); + + Rot3::new(axis_angle) + } + } +} impl + Sub> Cross for Vec2 { type CrossProductType = Vec1; diff --git a/src/structs/vec_macros.rs b/src/structs/vec_macros.rs index 8b92dd09..6c7079f0 100644 --- a/src/structs/vec_macros.rs +++ b/src/structs/vec_macros.rs @@ -79,7 +79,7 @@ macro_rules! at_fast_impl( // However, f32/f64 does not implement Ord… macro_rules! ord_impl( ($t: ident, $comp0: ident, $($compN: ident),*) => ( - impl POrd for $t { + impl POrd for $t { #[inline] fn inf(&self, other: &$t) -> $t { $t::new(self.$comp0.min(other.$comp0) @@ -291,7 +291,7 @@ macro_rules! container_impl( macro_rules! basis_impl( ($t: ident, $dim: expr) => ( - impl> Basis for $t { + impl> Basis for $t { #[inline] fn canonical_basis) -> bool>(mut f: F) { for i in 0..$dim { @@ -547,7 +547,7 @@ macro_rules! translation_impl( macro_rules! norm_impl( ($t: ident, $($compN: ident),+) => ( - impl Norm for $t { + impl Norm for $t { #[inline] fn sqnorm(&self) -> N { Dot::dot(self, self) diff --git a/src/traits/geometry.rs b/src/traits/geometry.rs index 29b3a51a..ffc2db7e 100644 --- a/src/traits/geometry.rs +++ b/src/traits/geometry.rs @@ -64,6 +64,22 @@ pub trait Rotation { fn set_rotation(&mut self, V); } +/// Trait of object that can be rotated to be superimposed with another one of the same nature. +pub trait RotationTo { + /// Type of the angle between two elements. + type AngleType; + + /// Type of the rotation between two elements. + type DeltaRotationType; + + /// Computes an angle nedded to transform the first element to the second one using a + /// rotation. + fn angle_to(&self, other: &Self) -> Self::AngleType; + + /// Computes the smallest rotation needed to transform the first element to the second one. + fn rotation_to(&self, other: &Self) -> Self::DeltaRotationType; +} + /// Trait of objects able to rotate other objects. /// /// This is typically implemented by matrices which rotate vectors. diff --git a/src/traits/mod.rs b/src/traits/mod.rs index 30506892..1594064d 100644 --- a/src/traits/mod.rs +++ b/src/traits/mod.rs @@ -1,8 +1,9 @@ //! Mathematical traits. pub use traits::geometry::{AbsoluteRotate, Cross, CrossMatrix, Dot, FromHomogeneous, Norm, Orig, - Rotate, Rotation, RotationMatrix, RotationWithTranslation, ToHomogeneous, - Transform, Transformation, Translate, Translation, UniformSphereSample}; + Rotate, Rotation, RotationMatrix, RotationWithTranslation, RotationTo, + ToHomogeneous, Transform, Transformation, Translate, Translation, + UniformSphereSample}; pub use traits::structure::{FloatVec, FloatPnt, Basis, Cast, Col, Dim, Indexable, Iterable, IterableMut, Mat, SquareMat, Row, NumVec, NumPnt, PntAsVec, ColSlice,