Fix the PartialEq impl for quaternions.
The double-covering property should only be taken into account for `UnitQuaternion` instead of `Quaternion` itself.
This commit is contained in:
parent
dda1ae75e6
commit
67603be6ed
@ -11,7 +11,8 @@ use abomonation::Abomonation;
|
||||
|
||||
use crate::allocator::Allocator;
|
||||
use crate::base::DefaultAllocator;
|
||||
use crate::{Dim, MatrixMN, RealField, Scalar, SimdComplexField, SimdRealField};
|
||||
use crate::storage::Storage;
|
||||
use crate::{Dim, Matrix, MatrixMN, RealField, Scalar, SimdComplexField, SimdRealField};
|
||||
|
||||
/// A wrapper that ensures the underlying algebraic entity has a unit norm.
|
||||
///
|
||||
@ -24,7 +25,7 @@ use crate::{Dim, MatrixMN, RealField, Scalar, SimdComplexField, SimdRealField};
|
||||
/// and [`UnitQuaternion`](crate::UnitQuaternion); both built on top of `Unit`. If you are interested
|
||||
/// in their documentation, read their dedicated pages directly.
|
||||
#[repr(transparent)]
|
||||
#[derive(Eq, PartialEq, Clone, Hash, Debug, Copy)]
|
||||
#[derive(Clone, Hash, Debug, Copy)]
|
||||
pub struct Unit<T> {
|
||||
pub(crate) value: T,
|
||||
}
|
||||
@ -64,6 +65,28 @@ impl<T: Abomonation> Abomonation for Unit<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<N, R, C, S> PartialEq for Unit<Matrix<N, R, C, S>>
|
||||
where
|
||||
N: Scalar + PartialEq,
|
||||
R: Dim,
|
||||
C: Dim,
|
||||
S: Storage<N, R, C>,
|
||||
{
|
||||
#[inline]
|
||||
fn eq(&self, rhs: &Self) -> bool {
|
||||
self.value.eq(&rhs.value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<N, R, C, S> Eq for Unit<Matrix<N, R, C, S>>
|
||||
where
|
||||
N: Scalar + Eq,
|
||||
R: Dim,
|
||||
C: Dim,
|
||||
S: Storage<N, R, C>,
|
||||
{
|
||||
}
|
||||
|
||||
/// Trait implemented by entities scan be be normalized and put in an `Unit` struct.
|
||||
pub trait Normed {
|
||||
/// The type of the norm.
|
||||
|
@ -13,7 +13,7 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
#[cfg(feature = "abomonation-serialize")]
|
||||
use abomonation::Abomonation;
|
||||
|
||||
use simba::scalar::RealField;
|
||||
use simba::scalar::{ClosedNeg, RealField};
|
||||
use simba::simd::{SimdBool, SimdOption, SimdRealField, SimdValue};
|
||||
|
||||
use crate::base::dimension::{U1, U3, U4};
|
||||
@ -23,17 +23,18 @@ use crate::base::{
|
||||
};
|
||||
|
||||
use crate::geometry::{Point3, Rotation};
|
||||
use std::ops::Neg;
|
||||
|
||||
/// A quaternion. See the type alias `UnitQuaternion = Unit<Quaternion>` for a quaternion
|
||||
/// that may be used as a rotation.
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
pub struct Quaternion<N: Scalar + SimdValue> {
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||
pub struct Quaternion<N: Scalar> {
|
||||
/// This quaternion as a 4D vector of coordinates in the `[ x, y, z, w ]` storage order.
|
||||
pub coords: Vector4<N>,
|
||||
}
|
||||
|
||||
impl<N: RealField> Default for Quaternion<N> {
|
||||
impl<N: Scalar + Zero> Default for Quaternion<N> {
|
||||
fn default() -> Self {
|
||||
Quaternion {
|
||||
coords: Vector4::zeros(),
|
||||
@ -42,7 +43,7 @@ impl<N: RealField> Default for Quaternion<N> {
|
||||
}
|
||||
|
||||
#[cfg(feature = "abomonation-serialize")]
|
||||
impl<N: SimdRealField> Abomonation for Quaternion<N>
|
||||
impl<N: Scalar> Abomonation for Quaternion<N>
|
||||
where
|
||||
Vector4<N>: Abomonation,
|
||||
{
|
||||
@ -59,36 +60,8 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: SimdRealField + Eq> Eq for Quaternion<N> where N::Element: SimdRealField {}
|
||||
|
||||
impl<N: SimdRealField> PartialEq for Quaternion<N>
|
||||
where
|
||||
N::Element: SimdRealField,
|
||||
{
|
||||
fn eq(&self, rhs: &Self) -> bool {
|
||||
self.coords == rhs.coords ||
|
||||
// Account for the double-covering of S², i.e. q = -q
|
||||
self.as_vector().iter().zip(rhs.as_vector().iter()).all(|(a, b)| *a == -*b)
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: SimdRealField + hash::Hash> hash::Hash for Quaternion<N> {
|
||||
fn hash<H: hash::Hasher>(&self, state: &mut H) {
|
||||
self.coords.hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: Scalar + Copy + SimdValue> Copy for Quaternion<N> {}
|
||||
|
||||
impl<N: Scalar + SimdValue> Clone for Quaternion<N> {
|
||||
#[inline]
|
||||
fn clone(&self) -> Self {
|
||||
Self::from(self.coords.clone())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde-serialize")]
|
||||
impl<N: SimdRealField> Serialize for Quaternion<N>
|
||||
impl<N: Scalar> Serialize for Quaternion<N>
|
||||
where
|
||||
Owned<N, U4>: Serialize,
|
||||
{
|
||||
@ -101,7 +74,7 @@ where
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde-serialize")]
|
||||
impl<'a, N: SimdRealField> Deserialize<'a> for Quaternion<N>
|
||||
impl<'a, N: Scalar> Deserialize<'a> for Quaternion<N>
|
||||
where
|
||||
Owned<N, U4>: Deserialize<'a>,
|
||||
{
|
||||
@ -980,6 +953,17 @@ impl<N: RealField + fmt::Display> fmt::Display for Quaternion<N> {
|
||||
/// A unit quaternions. May be used to represent a rotation.
|
||||
pub type UnitQuaternion<N> = Unit<Quaternion<N>>;
|
||||
|
||||
impl<N: Scalar + ClosedNeg + PartialEq> PartialEq for UnitQuaternion<N> {
|
||||
#[inline]
|
||||
fn eq(&self, rhs: &Self) -> bool {
|
||||
self.coords == rhs.coords ||
|
||||
// Account for the double-covering of S², i.e. q = -q
|
||||
self.coords.iter().zip(rhs.coords.iter()).all(|(a, b)| *a == -b.inlined_clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: Scalar + ClosedNeg + Eq> Eq for UnitQuaternion<N> {}
|
||||
|
||||
impl<N: SimdRealField> Normed for Quaternion<N> {
|
||||
type Norm = N::SimdRealField;
|
||||
|
||||
|
@ -184,14 +184,14 @@ impl<N1: RealField, N2: RealField + SupersetOf<N1>> SubsetOf<Matrix4<N2>> for Un
|
||||
}
|
||||
|
||||
#[cfg(feature = "mint")]
|
||||
impl<N: SimdRealField> From<mint::Quaternion<N>> for Quaternion<N> {
|
||||
impl<N: Scalar> From<mint::Quaternion<N>> for Quaternion<N> {
|
||||
fn from(q: mint::Quaternion<N>) -> Self {
|
||||
Self::new(q.s, q.v.x, q.v.y, q.v.z)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "mint")]
|
||||
impl<N: SimdRealField> Into<mint::Quaternion<N>> for Quaternion<N> {
|
||||
impl<N: Scalar> Into<mint::Quaternion<N>> for Quaternion<N> {
|
||||
fn into(self) -> mint::Quaternion<N> {
|
||||
mint::Quaternion {
|
||||
v: mint::Vector3 {
|
||||
@ -205,7 +205,7 @@ impl<N: SimdRealField> Into<mint::Quaternion<N>> for Quaternion<N> {
|
||||
}
|
||||
|
||||
#[cfg(feature = "mint")]
|
||||
impl<N: SimdRealField> Into<mint::Quaternion<N>> for UnitQuaternion<N> {
|
||||
impl<N: Scalar> Into<mint::Quaternion<N>> for UnitQuaternion<N> {
|
||||
fn into(self) -> mint::Quaternion<N> {
|
||||
mint::Quaternion {
|
||||
v: mint::Vector3 {
|
||||
@ -258,14 +258,14 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: Scalar + SimdValue> From<Vector4<N>> for Quaternion<N> {
|
||||
impl<N: Scalar> From<Vector4<N>> for Quaternion<N> {
|
||||
#[inline]
|
||||
fn from(coords: Vector4<N>) -> Self {
|
||||
Self { coords }
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: Scalar + SimdValue> From<[N; 4]> for Quaternion<N> {
|
||||
impl<N: Scalar> From<[N; 4]> for Quaternion<N> {
|
||||
#[inline]
|
||||
fn from(coords: [N; 4]) -> Self {
|
||||
Self {
|
||||
|
@ -4,8 +4,10 @@ use std::fmt;
|
||||
|
||||
use crate::base::{Matrix2, Matrix3, Normed, Unit, Vector1, Vector2};
|
||||
use crate::geometry::{Point2, Rotation2};
|
||||
use crate::Scalar;
|
||||
use simba::scalar::RealField;
|
||||
use simba::simd::SimdRealField;
|
||||
use std::cmp::{Eq, PartialEq};
|
||||
|
||||
/// A 2D rotation represented as a complex number with magnitude 1.
|
||||
///
|
||||
@ -29,6 +31,15 @@ use simba::simd::SimdRealField;
|
||||
/// * [Conversion to a matrix <span style="float:right;">`to_rotation_matrix`, `to_homogeneous`…</span>](#conversion-to-a-matrix)
|
||||
pub type UnitComplex<N> = Unit<Complex<N>>;
|
||||
|
||||
impl<N: Scalar + PartialEq> PartialEq for UnitComplex<N> {
|
||||
#[inline]
|
||||
fn eq(&self, rhs: &Self) -> bool {
|
||||
(**self).eq(&**rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: Scalar + Eq> Eq for UnitComplex<N> {}
|
||||
|
||||
impl<N: SimdRealField> Normed for Complex<N> {
|
||||
type Norm = N::SimdRealField;
|
||||
|
||||
|
@ -114,7 +114,6 @@ quickcheck!(
|
||||
*/
|
||||
fn unit_quaternion_double_covering(q: UnitQuaternion<f64>) -> bool {
|
||||
let mq = UnitQuaternion::new_unchecked(-q.into_inner());
|
||||
|
||||
mq == q && mq.angle() == q.angle() && mq.axis() == q.axis()
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user