Merge pull request #780 from dimforge/misc
Add various utility functions
This commit is contained in:
commit
f0e29ba39f
16
CHANGELOG.md
16
CHANGELOG.md
|
@ -4,6 +4,22 @@ documented here.
|
|||
|
||||
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]
|
||||
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
|
||||
|
|
18
Cargo.toml
18
Cargo.toml
|
@ -36,23 +36,23 @@ libm-force = [ "simba/libm_force" ]
|
|||
|
||||
|
||||
[dependencies]
|
||||
typenum = "1.11"
|
||||
generic-array = "0.13"
|
||||
typenum = "1.12"
|
||||
generic-array = "0.14"
|
||||
rand = { version = "0.7", default-features = false }
|
||||
num-traits = { version = "0.2", default-features = false }
|
||||
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.2", default-features = false }
|
||||
num-complex = { version = "0.3", default-features = false }
|
||||
num-rational = { version = "0.3", default-features = false }
|
||||
approx = { version = "0.4", default-features = false }
|
||||
simba = { version = "0.3", default-features = false }
|
||||
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 }
|
||||
serde = { version = "1.0", features = [ "derive" ], optional = true }
|
||||
abomonation = { version = "0.7", optional = true }
|
||||
mint = { version = "0.5", optional = true }
|
||||
quickcheck = { version = "0.9", optional = true }
|
||||
pest = { version = "2.0", optional = true }
|
||||
pest_derive = { version = "2.0", optional = true }
|
||||
pest = { version = "2", optional = true }
|
||||
pest_derive = { version = "2", optional = true }
|
||||
matrixcompare-core = { version = "0.1", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
|
|
|
@ -23,6 +23,6 @@ abomonation-serialize = [ "nalgebra/abomonation-serialize" ]
|
|||
|
||||
[dependencies]
|
||||
num-traits = { version = "0.2", default-features = false }
|
||||
approx = { version = "0.3", default-features = false }
|
||||
simba = { version = "0.2", default-features = false }
|
||||
approx = { version = "0.4", default-features = false }
|
||||
simba = { version = "0.3", default-features = false }
|
||||
nalgebra = { path = "..", version = "0.22", default-features = false }
|
||||
|
|
|
@ -23,7 +23,7 @@ accelerate = ["lapack-src/accelerate"]
|
|||
intel-mkl = ["lapack-src/intel-mkl"]
|
||||
|
||||
[dependencies]
|
||||
nalgebra = { version = "0.22", path = ".." }
|
||||
nalgebra = { version = "0.22" } # , path = ".." }
|
||||
num-traits = "0.2"
|
||||
num-complex = { version = "0.2", default-features = false }
|
||||
simba = "0.2"
|
||||
|
@ -34,7 +34,7 @@ lapack-src = { version = "0.5", default-features = false }
|
|||
# clippy = "*"
|
||||
|
||||
[dev-dependencies]
|
||||
nalgebra = { version = "0.22", path = "..", features = [ "arbitrary" ] }
|
||||
nalgebra = { version = "0.22", features = [ "arbitrary" ] } # path = ".." }
|
||||
quickcheck = "0.9"
|
||||
approx = "0.3"
|
||||
rand = "0.7"
|
||||
|
|
|
@ -1019,6 +1019,12 @@ where
|
|||
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.
|
||||
#[inline]
|
||||
pub fn x() -> Self
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::allocator::Allocator;
|
||||
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;
|
||||
|
||||
|
@ -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>
|
||||
where
|
||||
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.
|
||||
fn inverse_transform_point(&self, p: &Point<N, D>) -> Point<N, D>
|
||||
where
|
||||
|
@ -74,6 +81,14 @@ where
|
|||
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]
|
||||
fn inverse_transform_point(&self, p: &Point<N, D>) -> Point<N, D>
|
||||
where
|
||||
|
|
|
@ -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, VectorN};
|
||||
use crate::geometry::{AbstractRotation, Point, Translation};
|
||||
use crate::base::{DefaultAllocator, MatrixN, Scalar, Unit, VectorN};
|
||||
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)]
|
||||
|
@ -350,6 +352,237 @@ where
|
|||
pub fn inverse_transform_vector(&self, v: &VectorN<N, D>) -> VectorN<N, D> {
|
||||
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
|
||||
|
|
|
@ -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>>
|
||||
where
|
||||
D: DimNameAdd<U1>,
|
||||
|
|
|
@ -102,6 +102,45 @@ impl<N: Scalar, D: DimName> Point<N, D>
|
|||
where
|
||||
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
|
||||
/// end of it.
|
||||
///
|
||||
|
|
|
@ -1542,6 +1542,26 @@ where
|
|||
pub fn inverse_transform_vector(&self, v: &Vector3<N>) -> Vector3<N> {
|
||||
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> {
|
||||
|
|
|
@ -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>
|
||||
where
|
||||
N: From<[<N as SimdValue>::Element; 2]>,
|
||||
|
|
|
@ -19,7 +19,7 @@ use simba::simd::SimdRealField;
|
|||
|
||||
use crate::base::allocator::Allocator;
|
||||
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;
|
||||
|
||||
/// A rotation matrix.
|
||||
|
@ -441,6 +441,25 @@ where
|
|||
pub fn inverse_transform_vector(&self, v: &VectorN<N, D>) -> VectorN<N, D> {
|
||||
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> {}
|
||||
|
|
|
@ -236,6 +236,31 @@ 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:
|
||||
///
|
||||
/// ```
|
||||
/// # #[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
|
||||
|
@ -862,6 +887,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
|
||||
|
|
|
@ -360,6 +360,43 @@ where
|
|||
pub fn inverse_transform_vector(&self, v: &Vector2<N>) -> Vector2<N> {
|
||||
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> {
|
||||
|
|
Loading…
Reference in New Issue