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::allocator::Allocator;
|
||||||
use crate::base::DefaultAllocator;
|
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.
|
/// 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
|
/// and [`UnitQuaternion`](crate::UnitQuaternion); both built on top of `Unit`. If you are interested
|
||||||
/// in their documentation, read their dedicated pages directly.
|
/// in their documentation, read their dedicated pages directly.
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
#[derive(Eq, PartialEq, Clone, Hash, Debug, Copy)]
|
#[derive(Clone, Hash, Debug, Copy)]
|
||||||
pub struct Unit<T> {
|
pub struct Unit<T> {
|
||||||
pub(crate) value: 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.
|
/// Trait implemented by entities scan be be normalized and put in an `Unit` struct.
|
||||||
pub trait Normed {
|
pub trait Normed {
|
||||||
/// The type of the norm.
|
/// The type of the norm.
|
||||||
|
|
|
@ -13,7 +13,7 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
#[cfg(feature = "abomonation-serialize")]
|
#[cfg(feature = "abomonation-serialize")]
|
||||||
use abomonation::Abomonation;
|
use abomonation::Abomonation;
|
||||||
|
|
||||||
use simba::scalar::RealField;
|
use simba::scalar::{ClosedNeg, RealField};
|
||||||
use simba::simd::{SimdBool, SimdOption, SimdRealField, SimdValue};
|
use simba::simd::{SimdBool, SimdOption, SimdRealField, SimdValue};
|
||||||
|
|
||||||
use crate::base::dimension::{U1, U3, U4};
|
use crate::base::dimension::{U1, U3, U4};
|
||||||
|
@ -23,17 +23,18 @@ use crate::base::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::geometry::{Point3, Rotation};
|
use crate::geometry::{Point3, Rotation};
|
||||||
|
use std::ops::Neg;
|
||||||
|
|
||||||
/// A quaternion. See the type alias `UnitQuaternion = Unit<Quaternion>` for a quaternion
|
/// A quaternion. See the type alias `UnitQuaternion = Unit<Quaternion>` for a quaternion
|
||||||
/// that may be used as a rotation.
|
/// that may be used as a rotation.
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||||
pub struct Quaternion<N: Scalar + SimdValue> {
|
pub struct Quaternion<N: Scalar> {
|
||||||
/// This quaternion as a 4D vector of coordinates in the `[ x, y, z, w ]` storage order.
|
/// This quaternion as a 4D vector of coordinates in the `[ x, y, z, w ]` storage order.
|
||||||
pub coords: Vector4<N>,
|
pub coords: Vector4<N>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<N: RealField> Default for Quaternion<N> {
|
impl<N: Scalar + Zero> Default for Quaternion<N> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Quaternion {
|
Quaternion {
|
||||||
coords: Vector4::zeros(),
|
coords: Vector4::zeros(),
|
||||||
|
@ -42,7 +43,7 @@ impl<N: RealField> Default for Quaternion<N> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "abomonation-serialize")]
|
#[cfg(feature = "abomonation-serialize")]
|
||||||
impl<N: SimdRealField> Abomonation for Quaternion<N>
|
impl<N: Scalar> Abomonation for Quaternion<N>
|
||||||
where
|
where
|
||||||
Vector4<N>: Abomonation,
|
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")]
|
#[cfg(feature = "serde-serialize")]
|
||||||
impl<N: SimdRealField> Serialize for Quaternion<N>
|
impl<N: Scalar> Serialize for Quaternion<N>
|
||||||
where
|
where
|
||||||
Owned<N, U4>: Serialize,
|
Owned<N, U4>: Serialize,
|
||||||
{
|
{
|
||||||
|
@ -101,7 +74,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "serde-serialize")]
|
#[cfg(feature = "serde-serialize")]
|
||||||
impl<'a, N: SimdRealField> Deserialize<'a> for Quaternion<N>
|
impl<'a, N: Scalar> Deserialize<'a> for Quaternion<N>
|
||||||
where
|
where
|
||||||
Owned<N, U4>: Deserialize<'a>,
|
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.
|
/// A unit quaternions. May be used to represent a rotation.
|
||||||
pub type UnitQuaternion<N> = Unit<Quaternion<N>>;
|
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> {
|
impl<N: SimdRealField> Normed for Quaternion<N> {
|
||||||
type Norm = N::SimdRealField;
|
type Norm = N::SimdRealField;
|
||||||
|
|
||||||
|
|
|
@ -184,14 +184,14 @@ impl<N1: RealField, N2: RealField + SupersetOf<N1>> SubsetOf<Matrix4<N2>> for Un
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "mint")]
|
#[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 {
|
fn from(q: mint::Quaternion<N>) -> Self {
|
||||||
Self::new(q.s, q.v.x, q.v.y, q.v.z)
|
Self::new(q.s, q.v.x, q.v.y, q.v.z)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "mint")]
|
#[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> {
|
fn into(self) -> mint::Quaternion<N> {
|
||||||
mint::Quaternion {
|
mint::Quaternion {
|
||||||
v: mint::Vector3 {
|
v: mint::Vector3 {
|
||||||
|
@ -205,7 +205,7 @@ impl<N: SimdRealField> Into<mint::Quaternion<N>> for Quaternion<N> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "mint")]
|
#[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> {
|
fn into(self) -> mint::Quaternion<N> {
|
||||||
mint::Quaternion {
|
mint::Quaternion {
|
||||||
v: mint::Vector3 {
|
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]
|
#[inline]
|
||||||
fn from(coords: Vector4<N>) -> Self {
|
fn from(coords: Vector4<N>) -> Self {
|
||||||
Self { coords }
|
Self { coords }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<N: Scalar + SimdValue> From<[N; 4]> for Quaternion<N> {
|
impl<N: Scalar> From<[N; 4]> for Quaternion<N> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(coords: [N; 4]) -> Self {
|
fn from(coords: [N; 4]) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
|
|
@ -4,8 +4,10 @@ use std::fmt;
|
||||||
|
|
||||||
use crate::base::{Matrix2, Matrix3, Normed, Unit, Vector1, Vector2};
|
use crate::base::{Matrix2, Matrix3, Normed, Unit, Vector1, Vector2};
|
||||||
use crate::geometry::{Point2, Rotation2};
|
use crate::geometry::{Point2, Rotation2};
|
||||||
|
use crate::Scalar;
|
||||||
use simba::scalar::RealField;
|
use simba::scalar::RealField;
|
||||||
use simba::simd::SimdRealField;
|
use simba::simd::SimdRealField;
|
||||||
|
use std::cmp::{Eq, PartialEq};
|
||||||
|
|
||||||
/// A 2D rotation represented as a complex number with magnitude 1.
|
/// 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)
|
/// * [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>>;
|
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> {
|
impl<N: SimdRealField> Normed for Complex<N> {
|
||||||
type Norm = N::SimdRealField;
|
type Norm = N::SimdRealField;
|
||||||
|
|
||||||
|
|
|
@ -114,7 +114,6 @@ quickcheck!(
|
||||||
*/
|
*/
|
||||||
fn unit_quaternion_double_covering(q: UnitQuaternion<f64>) -> bool {
|
fn unit_quaternion_double_covering(q: UnitQuaternion<f64>) -> bool {
|
||||||
let mq = UnitQuaternion::new_unchecked(-q.into_inner());
|
let mq = UnitQuaternion::new_unchecked(-q.into_inner());
|
||||||
|
|
||||||
mq == q && mq.angle() == q.angle() && mq.axis() == q.axis()
|
mq == q && mq.angle() == q.angle() && mq.axis() == q.axis()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue