Merge pull request #780 from dimforge/misc

Add various utility functions
This commit is contained in:
Sébastien Crozet 2020-10-25 15:33:29 +01:00 committed by GitHub
commit f0e29ba39f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 495 additions and 18 deletions

View File

@ -4,6 +4,22 @@ documented here.
This project adheres to [Semantic Versioning](https://semver.org/). This project adheres to [Semantic Versioning](https://semver.org/).
## [0.23.0] - WIP
### Added
* The `.inverse_transform_unit_vector(v)` was added to `Rotation2/3`, `Isometry2/3`, `UnitQuaternion`, and `UnitComplex`.
It applies the corresponding rotation to a unit vector `Unit<Vector2/3>`.
* The `Point.map(f)` and `Point.apply(f)` to apply a function to each component of the point, similarly to `Vector.map(f)`
and `Vector.apply(f)`.
* The `Quaternion::from([N; 4])` conversion to build a quaternion from an array of four elements.
* The `Isometry::from(Translation)` conversion to build an isometry from a translation.
* The `Vector::ith_axis(i)` which build a unit vector, e.g., `Unit<Vector3<f32>>` with its i-th component set to 1.0 and the
others set to zero.
* The `Isometry.lerp_slerp` and `Isometry.try_lerp_slerp` methods to interpolate between two isometries using linear
interpolation for the translational part, and spherical interpolation for the rotational part.
* The `Rotation2.slerp`, `Rotation3.slerp`, and `UnitQuaternion.slerp` method for
spherical interpolation.
## [0.22.0] ## [0.22.0]
In this release, we are using the new version 0.2 of simba. One major change of that version is that the In this release, we are using the new version 0.2 of simba. One major change of that version is that the
use of `libm` is now opt-in when building targetting `no-std` environment. If you are using floating-point use of `libm` is now opt-in when building targetting `no-std` environment. If you are using floating-point

View File

@ -36,23 +36,23 @@ libm-force = [ "simba/libm_force" ]
[dependencies] [dependencies]
typenum = "1.11" typenum = "1.12"
generic-array = "0.13" generic-array = "0.14"
rand = { version = "0.7", default-features = false } rand = { version = "0.7", default-features = false }
num-traits = { version = "0.2", default-features = false } num-traits = { version = "0.2", default-features = false }
num-complex = { version = "0.2", default-features = false } num-complex = { version = "0.3", default-features = false }
num-rational = { version = "0.2", default-features = false } num-rational = { version = "0.3", default-features = false }
approx = { version = "0.3", default-features = false } approx = { version = "0.4", default-features = false }
simba = { version = "0.2", default-features = false } simba = { version = "0.3", default-features = false }
alga = { version = "0.9", default-features = false, optional = true } alga = { version = "0.9", default-features = false, optional = true }
rand_distr = { version = "0.2", optional = true } rand_distr = { version = "0.3", optional = true }
matrixmultiply = { version = "0.2", optional = true } matrixmultiply = { version = "0.2", optional = true }
serde = { version = "1.0", features = [ "derive" ], optional = true } serde = { version = "1.0", features = [ "derive" ], optional = true }
abomonation = { version = "0.7", optional = true } abomonation = { version = "0.7", optional = true }
mint = { version = "0.5", optional = true } mint = { version = "0.5", optional = true }
quickcheck = { version = "0.9", optional = true } quickcheck = { version = "0.9", optional = true }
pest = { version = "2.0", optional = true } pest = { version = "2", optional = true }
pest_derive = { version = "2.0", optional = true } pest_derive = { version = "2", optional = true }
matrixcompare-core = { version = "0.1", optional = true } matrixcompare-core = { version = "0.1", optional = true }
[dev-dependencies] [dev-dependencies]

View File

@ -23,6 +23,6 @@ abomonation-serialize = [ "nalgebra/abomonation-serialize" ]
[dependencies] [dependencies]
num-traits = { version = "0.2", default-features = false } num-traits = { version = "0.2", default-features = false }
approx = { version = "0.3", default-features = false } approx = { version = "0.4", default-features = false }
simba = { version = "0.2", default-features = false } simba = { version = "0.3", default-features = false }
nalgebra = { path = "..", version = "0.22", default-features = false } nalgebra = { path = "..", version = "0.22", default-features = false }

View File

@ -23,7 +23,7 @@ accelerate = ["lapack-src/accelerate"]
intel-mkl = ["lapack-src/intel-mkl"] intel-mkl = ["lapack-src/intel-mkl"]
[dependencies] [dependencies]
nalgebra = { version = "0.22", path = ".." } nalgebra = { version = "0.22" } # , path = ".." }
num-traits = "0.2" num-traits = "0.2"
num-complex = { version = "0.2", default-features = false } num-complex = { version = "0.2", default-features = false }
simba = "0.2" simba = "0.2"
@ -34,7 +34,7 @@ lapack-src = { version = "0.5", default-features = false }
# clippy = "*" # clippy = "*"
[dev-dependencies] [dev-dependencies]
nalgebra = { version = "0.22", path = "..", features = [ "arbitrary" ] } nalgebra = { version = "0.22", features = [ "arbitrary" ] } # path = ".." }
quickcheck = "0.9" quickcheck = "0.9"
approx = "0.3" approx = "0.3"
rand = "0.7" rand = "0.7"

View File

@ -1019,6 +1019,12 @@ where
res res
} }
/// The column unit vector with `N::one()` as its i-th component.
#[inline]
pub fn ith_axis(i: usize) -> Unit<Self> {
Unit::new_unchecked(Self::ith(i, N::one()))
}
/// The column vector with a 1 as its first component, and zero elsewhere. /// The column vector with a 1 as its first component, and zero elsewhere.
#[inline] #[inline]
pub fn x() -> Self pub fn x() -> Self

View File

@ -1,6 +1,6 @@
use crate::allocator::Allocator; use crate::allocator::Allocator;
use crate::geometry::{Rotation, UnitComplex, UnitQuaternion}; use crate::geometry::{Rotation, UnitComplex, UnitQuaternion};
use crate::{DefaultAllocator, DimName, Point, Scalar, SimdRealField, VectorN, U2, U3}; use crate::{DefaultAllocator, DimName, Point, Scalar, SimdRealField, Unit, VectorN, U2, U3};
use simba::scalar::ClosedMul; use simba::scalar::ClosedMul;
@ -24,6 +24,13 @@ pub trait AbstractRotation<N: Scalar, D: DimName>: PartialEq + ClosedMul + Clone
fn inverse_transform_vector(&self, v: &VectorN<N, D>) -> VectorN<N, D> fn inverse_transform_vector(&self, v: &VectorN<N, D>) -> VectorN<N, D>
where where
DefaultAllocator: Allocator<N, D>; DefaultAllocator: Allocator<N, D>;
/// Apply the inverse rotation to the given unit vector.
fn inverse_transform_unit_vector(&self, v: &Unit<VectorN<N, D>>) -> Unit<VectorN<N, D>>
where
DefaultAllocator: Allocator<N, D>,
{
Unit::new_unchecked(self.inverse_transform_vector(&**v))
}
/// Apply the inverse rotation to the given point. /// Apply the inverse rotation to the given point.
fn inverse_transform_point(&self, p: &Point<N, D>) -> Point<N, D> fn inverse_transform_point(&self, p: &Point<N, D>) -> Point<N, D>
where where
@ -74,6 +81,14 @@ where
self.inverse_transform_vector(v) self.inverse_transform_vector(v)
} }
#[inline]
fn inverse_transform_unit_vector(&self, v: &Unit<VectorN<N, D>>) -> Unit<VectorN<N, D>>
where
DefaultAllocator: Allocator<N, D>,
{
self.inverse_transform_unit_vector(v)
}
#[inline] #[inline]
fn inverse_transform_point(&self, p: &Point<N, D>) -> Point<N, D> fn inverse_transform_point(&self, p: &Point<N, D>) -> Point<N, D>
where where

View File

@ -14,10 +14,12 @@ use simba::scalar::{RealField, SubsetOf};
use simba::simd::SimdRealField; use simba::simd::SimdRealField;
use crate::base::allocator::Allocator; use crate::base::allocator::Allocator;
use crate::base::dimension::{DimName, DimNameAdd, DimNameSum, U1}; use crate::base::dimension::{DimName, DimNameAdd, DimNameSum, U1, U2, U3};
use crate::base::storage::Owned; use crate::base::storage::Owned;
use crate::base::{DefaultAllocator, MatrixN, Scalar, VectorN}; use crate::base::{DefaultAllocator, MatrixN, Scalar, Unit, VectorN};
use crate::geometry::{AbstractRotation, Point, Translation}; use crate::geometry::{
AbstractRotation, Point, Rotation2, Rotation3, Translation, UnitComplex, UnitQuaternion,
};
/// A direct isometry, i.e., a rotation followed by a translation, aka. a rigid-body motion, aka. an element of a Special Euclidean (SE) group. /// A direct isometry, i.e., a rotation followed by a translation, aka. a rigid-body motion, aka. an element of a Special Euclidean (SE) group.
#[repr(C)] #[repr(C)]
@ -350,6 +352,237 @@ where
pub fn inverse_transform_vector(&self, v: &VectorN<N, D>) -> VectorN<N, D> { pub fn inverse_transform_vector(&self, v: &VectorN<N, D>) -> VectorN<N, D> {
self.rotation.inverse_transform_vector(v) self.rotation.inverse_transform_vector(v)
} }
/// Transform the given unit vector by the inverse of this isometry, ignoring the
/// translation component of the isometry. This may be
/// less expensive than computing the entire isometry inverse and then
/// transforming the point.
///
/// # Example
///
/// ```
/// # #[macro_use] extern crate approx;
/// # use std::f32;
/// # use nalgebra::{Isometry3, Translation3, UnitQuaternion, Vector3};
/// let tra = Translation3::new(0.0, 0.0, 3.0);
/// let rot = UnitQuaternion::from_scaled_axis(Vector3::z() * f32::consts::FRAC_PI_2);
/// let iso = Isometry3::from_parts(tra, rot);
///
/// let transformed_point = iso.inverse_transform_unit_vector(&Vector3::x_axis());
/// assert_relative_eq!(transformed_point, -Vector3::y_axis(), epsilon = 1.0e-6);
/// ```
#[inline]
pub fn inverse_transform_unit_vector(&self, v: &Unit<VectorN<N, D>>) -> Unit<VectorN<N, D>> {
self.rotation.inverse_transform_unit_vector(v)
}
}
impl<N: SimdRealField> Isometry<N, U3, UnitQuaternion<N>> {
/// Interpolates between two isometries using a linear interpolation for the translation part,
/// and a spherical interpolation for the rotation part.
///
/// Panics if the angle between both rotations is 180 degrees (in which case the interpolation
/// is not well-defined). Use `.try_lerp_slerp` instead to avoid the panic.
///
/// # Examples:
///
/// ```
/// # use nalgebra::{Vector3, Translation3, Isometry3, UnitQuaternion};
///
/// let t1 = Translation3::new(1.0, 2.0, 3.0);
/// let t2 = Translation3::new(4.0, 8.0, 12.0);
/// let q1 = UnitQuaternion::from_euler_angles(std::f32::consts::FRAC_PI_4, 0.0, 0.0);
/// let q2 = UnitQuaternion::from_euler_angles(-std::f32::consts::PI, 0.0, 0.0);
/// let iso1 = Isometry3::from_parts(t1, q1);
/// let iso2 = Isometry3::from_parts(t2, q2);
///
/// let iso3 = iso1.lerp_slerp(&iso2, 1.0 / 3.0);
///
/// assert_eq!(iso3.translation.vector, Vector3::new(2.0, 4.0, 6.0));
/// assert_eq!(iso3.rotation.euler_angles(), (std::f32::consts::FRAC_PI_2, 0.0, 0.0));
/// ```
#[inline]
pub fn lerp_slerp(&self, other: &Self, t: N) -> Self
where
N: RealField,
{
let tr = self.translation.vector.lerp(&other.translation.vector, t);
let rot = self.rotation.slerp(&other.rotation, t);
Self::from_parts(tr.into(), rot)
}
/// Attempts to interpolate between two isometries using a linear interpolation for the translation part,
/// and a spherical interpolation for the rotation part.
///
/// Retuns `None` if the angle between both rotations is 180 degrees (in which case the interpolation
/// is not well-defined).
///
/// # Examples:
///
/// ```
/// # use nalgebra::{Vector3, Translation3, Isometry3, UnitQuaternion};
///
/// let t1 = Translation3::new(1.0, 2.0, 3.0);
/// let t2 = Translation3::new(4.0, 8.0, 12.0);
/// let q1 = UnitQuaternion::from_euler_angles(std::f32::consts::FRAC_PI_4, 0.0, 0.0);
/// let q2 = UnitQuaternion::from_euler_angles(-std::f32::consts::PI, 0.0, 0.0);
/// let iso1 = Isometry3::from_parts(t1, q1);
/// let iso2 = Isometry3::from_parts(t2, q2);
///
/// let iso3 = iso1.lerp_slerp(&iso2, 1.0 / 3.0);
///
/// assert_eq!(iso3.translation.vector, Vector3::new(2.0, 4.0, 6.0));
/// assert_eq!(iso3.rotation.euler_angles(), (std::f32::consts::FRAC_PI_2, 0.0, 0.0));
/// ```
#[inline]
pub fn try_lerp_slerp(&self, other: &Self, t: N, epsilon: N) -> Option<Self>
where
N: RealField,
{
let tr = self.translation.vector.lerp(&other.translation.vector, t);
let rot = self.rotation.try_slerp(&other.rotation, t, epsilon)?;
Some(Self::from_parts(tr.into(), rot))
}
}
impl<N: SimdRealField> Isometry<N, U3, Rotation3<N>> {
/// Interpolates between two isometries using a linear interpolation for the translation part,
/// and a spherical interpolation for the rotation part.
///
/// Panics if the angle between both rotations is 180 degrees (in which case the interpolation
/// is not well-defined). Use `.try_lerp_slerp` instead to avoid the panic.
///
/// # Examples:
///
/// ```
/// # use nalgebra::{Vector3, Translation3, Rotation3, IsometryMatrix3};
///
/// let t1 = Translation3::new(1.0, 2.0, 3.0);
/// let t2 = Translation3::new(4.0, 8.0, 12.0);
/// let q1 = Rotation3::from_euler_angles(std::f32::consts::FRAC_PI_4, 0.0, 0.0);
/// let q2 = Rotation3::from_euler_angles(-std::f32::consts::PI, 0.0, 0.0);
/// let iso1 = IsometryMatrix3::from_parts(t1, q1);
/// let iso2 = IsometryMatrix3::from_parts(t2, q2);
///
/// let iso3 = iso1.lerp_slerp(&iso2, 1.0 / 3.0);
///
/// assert_eq!(iso3.translation.vector, Vector3::new(2.0, 4.0, 6.0));
/// assert_eq!(iso3.rotation.euler_angles(), (std::f32::consts::FRAC_PI_2, 0.0, 0.0));
/// ```
#[inline]
pub fn lerp_slerp(&self, other: &Self, t: N) -> Self
where
N: RealField,
{
let tr = self.translation.vector.lerp(&other.translation.vector, t);
let rot = self.rotation.slerp(&other.rotation, t);
Self::from_parts(tr.into(), rot)
}
/// Attempts to interpolate between two isometries using a linear interpolation for the translation part,
/// and a spherical interpolation for the rotation part.
///
/// Retuns `None` if the angle between both rotations is 180 degrees (in which case the interpolation
/// is not well-defined).
///
/// # Examples:
///
/// ```
/// # use nalgebra::{Vector3, Translation3, Rotation3, IsometryMatrix3};
///
/// let t1 = Translation3::new(1.0, 2.0, 3.0);
/// let t2 = Translation3::new(4.0, 8.0, 12.0);
/// let q1 = Rotation3::from_euler_angles(std::f32::consts::FRAC_PI_4, 0.0, 0.0);
/// let q2 = Rotation3::from_euler_angles(-std::f32::consts::PI, 0.0, 0.0);
/// let iso1 = IsometryMatrix3::from_parts(t1, q1);
/// let iso2 = IsometryMatrix3::from_parts(t2, q2);
///
/// let iso3 = iso1.lerp_slerp(&iso2, 1.0 / 3.0);
///
/// assert_eq!(iso3.translation.vector, Vector3::new(2.0, 4.0, 6.0));
/// assert_eq!(iso3.rotation.euler_angles(), (std::f32::consts::FRAC_PI_2, 0.0, 0.0));
/// ```
#[inline]
pub fn try_lerp_slerp(&self, other: &Self, t: N, epsilon: N) -> Option<Self>
where
N: RealField,
{
let tr = self.translation.vector.lerp(&other.translation.vector, t);
let rot = self.rotation.try_slerp(&other.rotation, t, epsilon)?;
Some(Self::from_parts(tr.into(), rot))
}
}
impl<N: SimdRealField> Isometry<N, U2, UnitComplex<N>> {
/// Interpolates between two isometries using a linear interpolation for the translation part,
/// and a spherical interpolation for the rotation part.
///
/// Panics if the angle between both rotations is 180 degrees (in which case the interpolation
/// is not well-defined). Use `.try_lerp_slerp` instead to avoid the panic.
///
/// # Examples:
///
/// ```
/// # #[macro_use] extern crate approx;
/// # use nalgebra::{Vector2, Translation2, UnitComplex, Isometry2};
///
/// let t1 = Translation2::new(1.0, 2.0);
/// let t2 = Translation2::new(4.0, 8.0);
/// let q1 = UnitComplex::new(std::f32::consts::FRAC_PI_4);
/// let q2 = UnitComplex::new(-std::f32::consts::PI);
/// let iso1 = Isometry2::from_parts(t1, q1);
/// let iso2 = Isometry2::from_parts(t2, q2);
///
/// let iso3 = iso1.lerp_slerp(&iso2, 1.0 / 3.0);
///
/// assert_eq!(iso3.translation.vector, Vector2::new(2.0, 4.0));
/// assert_relative_eq!(iso3.rotation.angle(), std::f32::consts::FRAC_PI_2);
/// ```
#[inline]
pub fn lerp_slerp(&self, other: &Self, t: N) -> Self
where
N: RealField,
{
let tr = self.translation.vector.lerp(&other.translation.vector, t);
let rot = self.rotation.slerp(&other.rotation, t);
Self::from_parts(tr.into(), rot)
}
}
impl<N: SimdRealField> Isometry<N, U2, Rotation2<N>> {
/// Interpolates between two isometries using a linear interpolation for the translation part,
/// and a spherical interpolation for the rotation part.
///
/// Panics if the angle between both rotations is 180 degrees (in which case the interpolation
/// is not well-defined). Use `.try_lerp_slerp` instead to avoid the panic.
///
/// # Examples:
///
/// ```
/// # #[macro_use] extern crate approx;
/// # use nalgebra::{Vector2, Translation2, Rotation2, IsometryMatrix2};
///
/// let t1 = Translation2::new(1.0, 2.0);
/// let t2 = Translation2::new(4.0, 8.0);
/// let q1 = Rotation2::new(std::f32::consts::FRAC_PI_4);
/// let q2 = Rotation2::new(-std::f32::consts::PI);
/// let iso1 = IsometryMatrix2::from_parts(t1, q1);
/// let iso2 = IsometryMatrix2::from_parts(t2, q2);
///
/// let iso3 = iso1.lerp_slerp(&iso2, 1.0 / 3.0);
///
/// assert_eq!(iso3.translation.vector, Vector2::new(2.0, 4.0));
/// assert_relative_eq!(iso3.rotation.angle(), std::f32::consts::FRAC_PI_2);
/// ```
#[inline]
pub fn lerp_slerp(&self, other: &Self, t: N) -> Self
where
N: RealField,
{
let tr = self.translation.vector.lerp(&other.translation.vector, t);
let rot = self.rotation.slerp(&other.rotation, t);
Self::from_parts(tr.into(), rot)
}
} }
// NOTE: we don't require `R: Rotation<...>` here because this is not useful for the implementation // NOTE: we don't require `R: Rotation<...>` here because this is not useful for the implementation

View File

@ -151,6 +151,17 @@ where
} }
} }
impl<N: SimdRealField, D: DimName, R: AbstractRotation<N, D>> From<Translation<N, D>>
for Isometry<N, D, R>
where
DefaultAllocator: Allocator<N, D>,
{
#[inline]
fn from(tra: Translation<N, D>) -> Self {
Self::from_parts(tra, R::identity())
}
}
impl<N: SimdRealField, D: DimName, R> From<Isometry<N, D, R>> for MatrixN<N, DimNameSum<D, U1>> impl<N: SimdRealField, D: DimName, R> From<Isometry<N, D, R>> for MatrixN<N, DimNameSum<D, U1>>
where where
D: DimNameAdd<U1>, D: DimNameAdd<U1>,

View File

@ -102,6 +102,45 @@ impl<N: Scalar, D: DimName> Point<N, D>
where where
DefaultAllocator: Allocator<N, D>, DefaultAllocator: Allocator<N, D>,
{ {
/// Returns a point containing the result of `f` applied to each of its entries.
///
/// # Example
/// ```
/// # use nalgebra::{Point2, Point3};
/// let p = Point2::new(1.0, 2.0);
/// assert_eq!(p.map(|e| e * 10.0), Point2::new(10.0, 20.0));
///
/// // This works in any dimension.
/// let p = Point3::new(1.1, 2.1, 3.1);
/// assert_eq!(p.map(|e| e as u32), Point3::new(1, 2, 3));
/// ```
#[inline]
pub fn map<N2: Scalar, F: FnMut(N) -> N2>(&self, f: F) -> Point<N2, D>
where
DefaultAllocator: Allocator<N2, D>,
{
self.coords.map(f).into()
}
/// Replaces each component of `self` by the result of a closure `f` applied on it.
///
/// # Example
/// ```
/// # use nalgebra::{Point2, Point3};
/// let mut p = Point2::new(1.0, 2.0);
/// p.apply(|e| e * 10.0);
/// assert_eq!(p, Point2::new(10.0, 20.0));
///
/// // This works in any dimension.
/// let mut p = Point3::new(1.0, 2.0, 3.0);
/// p.apply(|e| e * 10.0);
/// assert_eq!(p, Point3::new(10.0, 20.0, 30.0));
/// ```
#[inline]
pub fn apply<F: FnMut(N) -> N>(&mut self, f: F) {
self.coords.apply(f)
}
/// Converts this point into a vector in homogeneous coordinates, i.e., appends a `1` at the /// Converts this point into a vector in homogeneous coordinates, i.e., appends a `1` at the
/// end of it. /// end of it.
/// ///

View File

@ -1542,6 +1542,26 @@ where
pub fn inverse_transform_vector(&self, v: &Vector3<N>) -> Vector3<N> { pub fn inverse_transform_vector(&self, v: &Vector3<N>) -> Vector3<N> {
self.inverse() * v self.inverse() * v
} }
/// Rotate a vector by the inverse of this unit quaternion. This may be
/// cheaper than inverting the unit quaternion and transforming the
/// vector.
///
/// # Example
///
/// ```
/// # #[macro_use] extern crate approx;
/// # use std::f32;
/// # use nalgebra::{UnitQuaternion, Vector3};
/// let rot = UnitQuaternion::from_axis_angle(&Vector3::z_axis(), f32::consts::FRAC_PI_2);
/// let transformed_vector = rot.inverse_transform_unit_vector(&Vector3::x_axis());
///
/// assert_relative_eq!(transformed_vector, -Vector3::y_axis(), epsilon = 1.0e-6);
/// ```
#[inline]
pub fn inverse_transform_unit_vector(&self, v: &Unit<Vector3<N>>) -> Unit<Vector3<N>> {
self.inverse() * v
}
} }
impl<N: RealField> Default for UnitQuaternion<N> { impl<N: RealField> Default for UnitQuaternion<N> {

View File

@ -265,6 +265,15 @@ impl<N: Scalar + SimdValue> From<Vector4<N>> for Quaternion<N> {
} }
} }
impl<N: Scalar + SimdValue> From<[N; 4]> for Quaternion<N> {
#[inline]
fn from(coords: [N; 4]) -> Self {
Self {
coords: coords.into(),
}
}
}
impl<N: Scalar + PrimitiveSimdValue> From<[Quaternion<N::Element>; 2]> for Quaternion<N> impl<N: Scalar + PrimitiveSimdValue> From<[Quaternion<N::Element>; 2]> for Quaternion<N>
where where
N: From<[<N as SimdValue>::Element; 2]>, N: From<[<N as SimdValue>::Element; 2]>,

View File

@ -19,7 +19,7 @@ use simba::simd::SimdRealField;
use crate::base::allocator::Allocator; use crate::base::allocator::Allocator;
use crate::base::dimension::{DimName, DimNameAdd, DimNameSum, U1}; use crate::base::dimension::{DimName, DimNameAdd, DimNameSum, U1};
use crate::base::{DefaultAllocator, MatrixN, Scalar, VectorN}; use crate::base::{DefaultAllocator, MatrixN, Scalar, Unit, VectorN};
use crate::geometry::Point; use crate::geometry::Point;
/// A rotation matrix. /// A rotation matrix.
@ -441,6 +441,25 @@ where
pub fn inverse_transform_vector(&self, v: &VectorN<N, D>) -> VectorN<N, D> { pub fn inverse_transform_vector(&self, v: &VectorN<N, D>) -> VectorN<N, D> {
self.matrix().tr_mul(v) self.matrix().tr_mul(v)
} }
/// Rotate the given vector by the inverse of this rotation. This may be
/// cheaper than inverting the rotation and then transforming the given
/// vector.
///
/// # Example
/// ```
/// # #[macro_use] extern crate approx;
/// # use std::f32;
/// # use nalgebra::{Rotation2, Rotation3, UnitQuaternion, Vector3};
/// let rot = Rotation3::new(Vector3::z() * f32::consts::FRAC_PI_2);
/// let transformed_vector = rot.inverse_transform_unit_vector(&Vector3::x_axis());
///
/// assert_relative_eq!(transformed_vector, -Vector3::y_axis(), epsilon = 1.0e-6);
/// ```
#[inline]
pub fn inverse_transform_unit_vector(&self, v: &Unit<VectorN<N, D>>) -> Unit<VectorN<N, D>> {
Unit::new_unchecked(self.inverse_transform_vector(&**v))
}
} }
impl<N: Scalar + Eq, D: DimName> Eq for Rotation<N, D> where DefaultAllocator: Allocator<N, D, D> {} impl<N: Scalar + Eq, D: DimName> Eq for Rotation<N, D> where DefaultAllocator: Allocator<N, D, D> {}

View File

@ -236,6 +236,31 @@ impl<N: SimdRealField> Rotation2<N> {
pub fn scaled_axis(&self) -> VectorN<N, U1> { pub fn scaled_axis(&self) -> VectorN<N, U1> {
Vector1::new(self.angle()) Vector1::new(self.angle())
} }
/// Spherical linear interpolation between two rotation matrices.
///
/// # Examples:
///
/// ```
/// # #[macro_use] extern crate approx;
/// # use nalgebra::geometry::Rotation2;
///
/// let rot1 = Rotation2::new(std::f32::consts::FRAC_PI_4);
/// let rot2 = Rotation2::new(-std::f32::consts::PI);
///
/// let rot = rot1.slerp(&rot2, 1.0 / 3.0);
///
/// assert_relative_eq!(rot.angle(), std::f32::consts::FRAC_PI_2);
/// ```
#[inline]
pub fn slerp(&self, other: &Self, t: N) -> Self
where
N::Element: SimdRealField,
{
let c1 = UnitComplex::from(*self);
let c2 = UnitComplex::from(*other);
c1.slerp(&c2, t).into()
}
} }
impl<N: SimdRealField> Distribution<Rotation2<N>> for Standard impl<N: SimdRealField> Distribution<Rotation2<N>> for Standard
@ -862,6 +887,53 @@ where
Self::identity() Self::identity()
} }
} }
/// Spherical linear interpolation between two rotation matrices.
///
/// Panics if the angle between both rotations is 180 degrees (in which case the interpolation
/// is not well-defined). Use `.try_slerp` instead to avoid the panic.
///
/// # Examples:
///
/// ```
/// # use nalgebra::geometry::Rotation3;
///
/// let q1 = Rotation3::from_euler_angles(std::f32::consts::FRAC_PI_4, 0.0, 0.0);
/// let q2 = Rotation3::from_euler_angles(-std::f32::consts::PI, 0.0, 0.0);
///
/// let q = q1.slerp(&q2, 1.0 / 3.0);
///
/// assert_eq!(q.euler_angles(), (std::f32::consts::FRAC_PI_2, 0.0, 0.0));
/// ```
#[inline]
pub fn slerp(&self, other: &Self, t: N) -> Self
where
N: RealField,
{
let q1 = UnitQuaternion::from(*self);
let q2 = UnitQuaternion::from(*other);
q1.slerp(&q2, t).into()
}
/// Computes the spherical linear interpolation between two rotation matrices or returns `None`
/// if both rotations are approximately 180 degrees apart (in which case the interpolation is
/// not well-defined).
///
/// # Arguments
/// * `self`: the first rotation to interpolate from.
/// * `other`: the second rotation to interpolate toward.
/// * `t`: the interpolation parameter. Should be between 0 and 1.
/// * `epsilon`: the value below which the sinus of the angle separating both rotations
/// must be to return `None`.
#[inline]
pub fn try_slerp(&self, other: &Self, t: N, epsilon: N) -> Option<Self>
where
N: RealField,
{
let q1 = Rotation3::from(*self);
let q2 = Rotation3::from(*other);
q1.try_slerp(&q2, t, epsilon).map(|q| q.into())
}
} }
impl<N: SimdRealField> Distribution<Rotation3<N>> for Standard impl<N: SimdRealField> Distribution<Rotation3<N>> for Standard

View File

@ -360,6 +360,43 @@ where
pub fn inverse_transform_vector(&self, v: &Vector2<N>) -> Vector2<N> { pub fn inverse_transform_vector(&self, v: &Vector2<N>) -> Vector2<N> {
self.inverse() * v self.inverse() * v
} }
/// Rotate the given vector by the inverse of this unit complex number.
///
/// # Example
/// ```
/// # #[macro_use] extern crate approx;
/// # use nalgebra::{UnitComplex, Vector2};
/// # use std::f32;
/// let rot = UnitComplex::new(f32::consts::FRAC_PI_2);
/// let transformed_vector = rot.inverse_transform_unit_vector(&Vector2::x_axis());
/// assert_relative_eq!(transformed_vector, -Vector2::y_axis(), epsilon = 1.0e-6);
/// ```
#[inline]
pub fn inverse_transform_unit_vector(&self, v: &Unit<Vector2<N>>) -> Unit<Vector2<N>> {
self.inverse() * v
}
/// Spherical linear interpolation between two rotations represented as unit complex numbers.
///
/// # Examples:
///
/// ```
/// # #[macro_use] extern crate approx;
/// # use nalgebra::geometry::UnitComplex;
///
/// let rot1 = UnitComplex::new(std::f32::consts::FRAC_PI_4);
/// let rot2 = UnitComplex::new(-std::f32::consts::PI);
///
/// let rot = rot1.slerp(&rot2, 1.0 / 3.0);
///
/// assert_relative_eq!(rot.angle(), std::f32::consts::FRAC_PI_2);
/// ```
#[inline]
pub fn slerp(&self, other: &Self, t: N) -> Self {
Self::new(self.angle() * (N::one() - t) + other.angle() * t)
}
} }
impl<N: RealField + fmt::Display> fmt::Display for UnitComplex<N> { impl<N: RealField + fmt::Display> fmt::Display for UnitComplex<N> {