Add missing slerp implementations.

This commit is contained in:
Crozet Sébastien 2020-10-25 14:00:47 +01:00
parent 22b8fc9225
commit 93f361cba8
5 changed files with 239 additions and 48 deletions

View File

@ -17,6 +17,8 @@ This project adheres to [Semantic Versioning](https://semver.org/).
others set to zero. others set to zero.
* The `Isometry.lerp_slerp` and `Isometry.try_lerp_slerp` methods to interpolate between two isometries using linear * 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. 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

View File

@ -35,14 +35,6 @@ pub trait AbstractRotation<N: Scalar, D: DimName>: PartialEq + ClosedMul + Clone
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
DefaultAllocator: Allocator<N, D>; DefaultAllocator: Allocator<N, D>;
/// Perfom a spherical interpolation between two rolations.
fn slerp(&self, other: &Self, t: N) -> Self
where
DefaultAllocator: Allocator<N, D>;
/// Attempts to perfom a spherical interpolation between two rolations.
fn try_slerp(&self, other: &Self, t: N, epsilon: N) -> Option<Self>
where
DefaultAllocator: Allocator<N, D>;
} }
impl<N: SimdRealField, D: DimName> AbstractRotation<N, D> for Rotation<N, D> impl<N: SimdRealField, D: DimName> AbstractRotation<N, D> for Rotation<N, D>
@ -104,22 +96,6 @@ where
{ {
self.inverse_transform_point(p) self.inverse_transform_point(p)
} }
#[inline]
fn slerp(&self, other: &Self, t: N) -> Self
where
DefaultAllocator: Allocator<N, D>,
{
self.slerp(other, t)
}
#[inline]
fn try_slerp(&self, other: &Self, t: N, epsilon: N) -> Option<Self>
where
DefaultAllocator: Allocator<N, D>,
{
self.try_slerp(other, t, epsilon)
}
} }
impl<N: SimdRealField> AbstractRotation<N, U3> for UnitQuaternion<N> impl<N: SimdRealField> AbstractRotation<N, U3> for UnitQuaternion<N>
@ -160,16 +136,6 @@ where
fn inverse_transform_point(&self, p: &Point<N, U3>) -> Point<N, U3> { fn inverse_transform_point(&self, p: &Point<N, U3>) -> Point<N, U3> {
self.inverse_transform_point(p) self.inverse_transform_point(p)
} }
#[inline]
fn slerp(&self, other: &Self, t: N) -> Self {
self.slerp(other, t)
}
#[inline]
fn try_slerp(&self, other: &Self, t: N, epsilon: N) -> Option<Self> {
self.try_slerp(other, t, epsilon)
}
} }
impl<N: SimdRealField> AbstractRotation<N, U2> for UnitComplex<N> impl<N: SimdRealField> AbstractRotation<N, U2> for UnitComplex<N>
@ -210,14 +176,4 @@ where
fn inverse_transform_point(&self, p: &Point<N, U2>) -> Point<N, U2> { fn inverse_transform_point(&self, p: &Point<N, U2>) -> Point<N, U2> {
self.inverse_transform_point(p) self.inverse_transform_point(p)
} }
#[inline]
fn slerp(&self, other: &Self, t: N) -> Self {
self.slerp(other, t)
}
#[inline]
fn try_slerp(&self, other: &Self, t: N, epsilon: N) -> Option<Self> {
self.try_slerp(other, t, epsilon)
}
} }

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, Unit, 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)]
@ -373,7 +375,9 @@ where
pub fn inverse_transform_unit_vector(&self, v: &Unit<VectorN<N, D>>) -> Unit<VectorN<N, D>> { pub fn inverse_transform_unit_vector(&self, v: &Unit<VectorN<N, D>>) -> Unit<VectorN<N, D>> {
self.rotation.inverse_transform_unit_vector(v) 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, /// Interpolates between two isometries using a linear interpolation for the translation part,
/// and a spherical interpolation for the rotation part. /// and a spherical interpolation for the rotation part.
/// ///
@ -383,7 +387,7 @@ where
/// # Examples: /// # Examples:
/// ///
/// ``` /// ```
/// # use nalgebra::geometry::{Translation3, UnitQuaternion}; /// # use nalgebra::geometry::{Vector3, Translation3, UnitQuaternion};
/// ///
/// let t1 = Translation3::new(1.0, 2.0, 3.0); /// let t1 = Translation3::new(1.0, 2.0, 3.0);
/// let t2 = Translation3::new(3.0, 6.0, 9.0); /// let t2 = Translation3::new(3.0, 6.0, 9.0);
@ -416,7 +420,7 @@ where
/// # Examples: /// # Examples:
/// ///
/// ``` /// ```
/// # use nalgebra::geometry::{Translation3, UnitQuaternion}; /// # use nalgebra::geometry::{Vector3, Translation3, UnitQuaternion};
/// ///
/// let t1 = Translation3::new(1.0, 2.0, 3.0); /// let t1 = Translation3::new(1.0, 2.0, 3.0);
/// let t2 = Translation3::new(3.0, 6.0, 9.0); /// let t2 = Translation3::new(3.0, 6.0, 9.0);
@ -441,6 +445,144 @@ where
} }
} }
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::geometry::{Vector3, Translation3, Rotation3};
///
/// let t1 = Translation3::new(1.0, 2.0, 3.0);
/// let t2 = Translation3::new(3.0, 6.0, 9.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 = 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::geometry::{Vector3, Translation3, Rotation3};
///
/// let t1 = Translation3::new(1.0, 2.0, 3.0);
/// let t2 = Translation3::new(3.0, 6.0, 9.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 = 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, 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:
///
/// ```
/// # use nalgebra::geometry::{Vector2, Translation2, UnitComplex, Isometry2};
///
/// let t1 = Translation2::new(1.0, 2.0);
/// let t2 = Translation2::new(3.0, 6.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_eq!(iso3.rotation.angle(), std::f32::consts::FRAC_PI_2);
/// ```
#[inline]
pub fn 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:
///
/// ```
/// # use nalgebra::geometry::{Vector2, Translation2, Rotation2, Isometry2};
///
/// let t1 = Translation2::new(1.0, 2.0);
/// let t2 = Translation2::new(3.0, 6.0);
/// let q1 = Rotation2::new(std::f32::consts::FRAC_PI_4);
/// let q2 = Rotation2::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_eq!(iso3.rotation.angle(), std::f32::consts::FRAC_PI_2);
/// ```
#[inline]
pub fn 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
// and makes it hard to use it, e.g., for Transform × Isometry implementation. // and makes it hard to use it, e.g., for Transform × Isometry implementation.
// This is OK since all constructors of the isometry enforce the Rotation bound already (and // This is OK since all constructors of the isometry enforce the Rotation bound already (and

View File

@ -236,6 +236,30 @@ 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:
///
/// ```
/// # 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_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 +886,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

@ -376,6 +376,26 @@ where
pub fn inverse_transform_unit_vector(&self, v: &Unit<Vector2<N>>) -> Unit<Vector2<N>> { pub fn inverse_transform_unit_vector(&self, v: &Unit<Vector2<N>>) -> Unit<Vector2<N>> {
self.inverse() * v self.inverse() * v
} }
/// Spherical linear interpolation between two rotations represented as unit complex numbers.
///
/// # Examples:
///
/// ```
/// # 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_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> {