Add rotation_between_axis/scaled_rotation_between_axis.

This commit is contained in:
Sébastien Crozet 2018-02-02 12:26:25 +01:00
parent dc41b55e5a
commit 52598de44c
2 changed files with 55 additions and 14 deletions

View File

@ -12,6 +12,9 @@ This project adheres to [Semantic Versioning](http://semver.org/).
`::from_element(...)`. `::from_element(...)`.
* The `.iamin()` methods that returns the index of the vector entry with * The `.iamin()` methods that returns the index of the vector entry with
smallest absolute value. smallest absolute value.
* `UnitQuaternion::scaled_rotation_between_axis` and
`UnitQuaternion::rotation_between_axis` that take Unit vectors instead of
Vector as arguments.

View File

@ -207,25 +207,63 @@ impl<N: Real> UnitQuaternion<N> {
where SB: Storage<N, U3>, where SB: Storage<N, U3>,
SC: Storage<N, U3> { SC: Storage<N, U3> {
// FIXME: code duplication with Rotation. // FIXME: code duplication with Rotation.
if let (Some(na), Some(nb)) = (a.try_normalize(N::zero()), b.try_normalize(N::zero())) { if let (Some(na), Some(nb)) = (Unit::try_new(a.clone_owned(), N::zero()),
Unit::try_new(b.clone_owned(), N::zero())) {
Self::scaled_rotation_between_axis(&na, &nb, s)
}
else {
Some(Self::identity())
}
}
/// The unit quaternion needed to make `a` and `b` be collinear and point toward the same
/// direction.
#[inline]
pub fn rotation_between_axis<SB, SC>(a: &Unit<Vector<N, U3, SB>>, b: &Unit<Vector<N, U3, SC>>) -> Option<Self>
where SB: Storage<N, U3>,
SC: Storage<N, U3> {
Self::scaled_rotation_between_axis(a, b, N::one())
}
/// The smallest rotation needed to make `a` and `b` collinear and point toward the same
/// direction, raised to the power `s`.
#[inline]
pub fn scaled_rotation_between_axis<SB, SC>(na: &Unit<Vector<N, U3, SB>>,
nb: &Unit<Vector<N, U3, SC>>,
s: N)
-> Option<Self>
where SB: Storage<N, U3>,
SC: Storage<N, U3> {
// FIXME: code duplication with Rotation.
let c = na.cross(&nb); let c = na.cross(&nb);
if let Some(axis) = Unit::try_new(c, N::default_epsilon()) { if let Some(axis) = Unit::try_new(c, N::default_epsilon()) {
return Some(Self::from_axis_angle(&axis, na.dot(&nb).acos() * s)) let cos = na.dot(&nb);
}
// Zero or PI. // The cosinus may be out of [-1, 1] because of innacuracies.
if na.dot(&nb) < N::zero() { if cos <= -N::one() {
return None
}
else if cos >= N::one() {
return Some(Self::identity())
}
else {
return Some(Self::from_axis_angle(&axis, cos.acos() * s))
}
}
else if na.dot(&nb) < N::zero() {
// PI // PI
// //
// The rotation axis is undefined but the angle not zero. This is not a // The rotation axis is undefined but the angle not zero. This is not a
// simple rotation. // simple rotation.
return None; return None;
} }
} else {
// Zero
Some(Self::identity()) Some(Self::identity())
} }
}
/// Creates an unit quaternion that corresponds to the local frame of an observer standing at the /// Creates an unit quaternion that corresponds to the local frame of an observer standing at the