forked from M-Labs/nalgebra
Add missing slerp implementations.
This commit is contained in:
parent
22b8fc9225
commit
93f361cba8
@ -17,6 +17,8 @@ This project adheres to [Semantic Versioning](https://semver.org/).
|
||||
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]
|
||||
In this release, we are using the new version 0.2 of simba. One major change of that version is that the
|
||||
|
@ -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>
|
||||
where
|
||||
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>
|
||||
@ -104,22 +96,6 @@ where
|
||||
{
|
||||
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>
|
||||
@ -160,16 +136,6 @@ where
|
||||
fn inverse_transform_point(&self, p: &Point<N, U3>) -> Point<N, U3> {
|
||||
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>
|
||||
@ -210,14 +176,4 @@ where
|
||||
fn inverse_transform_point(&self, p: &Point<N, U2>) -> Point<N, U2> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -14,10 +14,12 @@ use simba::scalar::{RealField, SubsetOf};
|
||||
use simba::simd::SimdRealField;
|
||||
|
||||
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::{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.
|
||||
#[repr(C)]
|
||||
@ -373,7 +375,9 @@ where
|
||||
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.
|
||||
///
|
||||
@ -383,7 +387,7 @@ where
|
||||
/// # Examples:
|
||||
///
|
||||
/// ```
|
||||
/// # use nalgebra::geometry::{Translation3, UnitQuaternion};
|
||||
/// # use nalgebra::geometry::{Vector3, Translation3, UnitQuaternion};
|
||||
///
|
||||
/// let t1 = Translation3::new(1.0, 2.0, 3.0);
|
||||
/// let t2 = Translation3::new(3.0, 6.0, 9.0);
|
||||
@ -416,7 +420,7 @@ where
|
||||
/// # Examples:
|
||||
///
|
||||
/// ```
|
||||
/// # use nalgebra::geometry::{Translation3, UnitQuaternion};
|
||||
/// # use nalgebra::geometry::{Vector3, Translation3, UnitQuaternion};
|
||||
///
|
||||
/// let t1 = Translation3::new(1.0, 2.0, 3.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
|
||||
// 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
|
||||
|
@ -236,6 +236,30 @@ impl<N: SimdRealField> Rotation2<N> {
|
||||
pub fn scaled_axis(&self) -> VectorN<N, U1> {
|
||||
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
|
||||
@ -862,6 +886,53 @@ where
|
||||
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
|
||||
|
@ -376,6 +376,26 @@ where
|
||||
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:
|
||||
///
|
||||
/// ```
|
||||
/// # 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> {
|
||||
|
Loading…
Reference in New Issue
Block a user