Repository: bitshifter/glam-rs (rust)

Issue:
Lack of Serialize, Deserialize, PartialEq implementations
The following types are missing these implementations:

Serialize + Deserialize:
 - EulerRot
 - BVec3A
 - BVec4A
 - BVec2
 - BVec3
 - BVec4

PartialEq:
- EulerRot

Is there a reason for this or were they simply not needed yet ?

Pull request:
Add missing Serialize, Deserialize and PartialEq implementations
Fixes #313 

Patch:
diff --git a/src/euler.rs b/src/euler.rs
index ef1bdf3..5ee5ed6 100644
--- a/src/euler.rs
+++ b/src/euler.rs
@@ -16,9 +16,8 @@ use num_traits::Float;
 /// E.g. XYZ will first apply the z-axis rotation.
 ///
 /// YXZ can be used for yaw (y-axis), pitch (x-axis), roll (z-axis).
-///
-/// The two-axis rotations (e.g. ZYZ) are not fully tested and have to be treated with caution.
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
 pub enum EulerRot {
     /// Intrinsic three-axis rotation ZYX
     ZYX,
@@ -32,25 +31,6 @@ pub enum EulerRot {
     XYZ,
     /// Intrinsic three-axis rotation XZY
     XZY,
-
-    /// Intrinsic two-axis rotation ZYZ
-    #[deprecated(note = "Untested! Use at own risk!")]
-    ZYZ,
-    /// Intrinsic two-axis rotation ZXZ
-    #[deprecated(note = "Untested! Use at own risk!")]
-    ZXZ,
-    /// Intrinsic two-axis rotation YXY
-    #[deprecated(note = "Untested! Use at own risk!")]
-    YXY,
-    /// Intrinsic two-axis rotation YZY
-    #[deprecated(note = "Untested! Use at own risk!")]
-    YZY,
-    /// Intrinsic two-axis rotation XYX
-    #[deprecated(note = "Untested! Use at own risk!")]
-    XYX,
-    /// Intrinsic two-axis rotation XZX
-    #[deprecated(note = "Untested! Use at own risk!")]
-    XZX,
 }
 
 impl Default for EulerRot {
@@ -119,30 +99,6 @@ macro_rules! impl_from_quat {
                         .atan2(q.w * q.w - q.x * q.x - q.y * q.y + q.z * q.z),
                     XZY => (2.0 * (q.y * q.z + q.w * q.x))
                         .atan2(q.w * q.w - q.x * q.x + q.y * q.y - q.z * q.z),
-                    #[allow(deprecated)]
-                    ZYZ => {
-                        (2.0 * (q.y * q.z + q.w * q.x)).atan2_fixed(-2.0 * (q.x * q.z - q.w * q.y))
-                    }
-                    #[allow(deprecated)]
-                    ZXZ => {
-                        (2.0 * (q.x * q.z - q.w * q.y)).atan2_fixed(2.0 * (q.y * q.z + q.w * q.x))
-                    }
-                    #[allow(deprecated)]
-                    YXY => {
-                        (2.0 * (q.x * q.y + q.w * q.z)).atan2_fixed(-2.0 * (q.y * q.z - q.w * q.x))
-                    }
-                    #[allow(deprecated)]
-                    YZY => {
-                        (2.0 * (q.y * q.z - q.w * q.x)).atan2_fixed(2.0 * (q.x * q.y + q.w * q.z))
-                    }
-                    #[allow(deprecated)]
-                    XYX => {
-                        (2.0 * (q.x * q.y - q.w * q.z)).atan2_fixed(2.0 * (q.x * q.z + q.w * q.y))
-                    }
-                    #[allow(deprecated)]
-                    XZX => {
-                        (2.0 * (q.x * q.z + q.w * q.y)).atan2_fixed(-2.0 * (q.x * q.y - q.w * q.z))
-                    }
                 }
             }
 
@@ -162,18 +118,6 @@ macro_rules! impl_from_quat {
                     YZX => arc_clamp(2.0 * (q.x * q.y + q.w * q.z)).asin(),
                     XYZ => arc_clamp(2.0 * (q.x * q.z + q.w * q.y)).asin(),
                     XZY => arc_clamp(-2.0 * (q.x * q.y - q.w * q.z)).asin(),
-                    #[allow(deprecated)]
-                    ZYZ => arc_clamp(q.w * q.w - q.x * q.x - q.y * q.y + q.z * q.z).acos(),
-                    #[allow(deprecated)]
-                    ZXZ => arc_clamp(q.w * q.w - q.x * q.x - q.y * q.y + q.z * q.z).acos(),
-                    #[allow(deprecated)]
-                    YXY => arc_clamp(q.w * q.w - q.x * q.x + q.y * q.y - q.z * q.z).acos(),
-                    #[allow(deprecated)]
-                    YZY => arc_clamp(q.w * q.w - q.x * q.x + q.y * q.y - q.z * q.z).acos(),
-                    #[allow(deprecated)]
-                    XYX => arc_clamp(q.w * q.w + q.x * q.x - q.y * q.y - q.z * q.z).acos(),
-                    #[allow(deprecated)]
-                    XZX => arc_clamp(q.w * q.w + q.x * q.x - q.y * q.y - q.z * q.z).acos(),
                 }
             }
 
@@ -193,30 +137,6 @@ macro_rules! impl_from_quat {
                         .atan2(q.w * q.w + q.x * q.x - q.y * q.y - q.z * q.z),
                     XZY => (2.0 * (q.x * q.z + q.w * q.y))
                         .atan2(q.w * q.w + q.x * q.x - q.y * q.y - q.z * q.z),
-                    #[allow(deprecated)]
-                    ZYZ => {
-                        (2.0 * (q.y * q.z - q.w * q.x)).atan2_fixed(2.0 * (q.x * q.z + q.w * q.y))
-                    }
-                    #[allow(deprecated)]
-                    ZXZ => {
-                        (2.0 * (q.x * q.z + q.w * q.y)).atan2_fixed(-2.0 * (q.y * q.z - q.w * q.x))
-                    }
-                    #[allow(deprecated)]
-                    YXY => {
-                        (2.0 * (q.x * q.y - q.w * q.z)).atan2_fixed(2.0 * (q.y * q.z + q.w * q.x))
-                    }
-                    #[allow(deprecated)]
-                    YZY => {
-                        (2.0 * (q.y * q.z + q.w * q.x)).atan2_fixed(-2.0 * (q.x * q.y - q.w * q.z))
-                    }
-                    #[allow(deprecated)]
-                    XYX => {
-                        (2.0 * (q.x * q.y + q.w * q.z)).atan2_fixed(-2.0 * (q.x * q.z - q.w * q.y))
-                    }
-                    #[allow(deprecated)]
-                    XZX => {
-                        (2.0 * (q.x * q.z - q.w * q.y)).atan2_fixed(2.0 * (q.x * q.y + q.w * q.z))
-                    }
                 }
             }
         }
@@ -250,18 +170,6 @@ macro_rules! impl_to_quat {
                     YZX => rot_y(u) * rot_z(v) * rot_x(w),
                     XYZ => rot_x(u) * rot_y(v) * rot_z(w),
                     XZY => rot_x(u) * rot_z(v) * rot_y(w),
-                    #[allow(deprecated)]
-                    ZYZ => rot_z(u) * rot_y(v) * rot_z(w),
-                    #[allow(deprecated)]
-                    ZXZ => rot_z(u) * rot_x(v) * rot_z(w),
-                    #[allow(deprecated)]
-                    YXY => rot_y(u) * rot_x(v) * rot_y(w),
-                    #[allow(deprecated)]
-                    YZY => rot_y(u) * rot_z(v) * rot_y(w),
-                    #[allow(deprecated)]
-                    XYX => rot_x(u) * rot_y(v) * rot_x(w),
-                    #[allow(deprecated)]
-                    XZX => rot_x(u) * rot_z(v) * rot_x(w),
                 }
                 .normalize()
             }
diff --git a/src/features/impl_serde.rs b/src/features/impl_serde.rs
index c1f4a94..c3a44e0 100644
--- a/src/features/impl_serde.rs
+++ b/src/features/impl_serde.rs
@@ -46,7 +46,7 @@ macro_rules! impl_serde_vec2 {
 
         #[test]
         fn test_vec2_serde() {
-            let a = $vec2::new(1 as $t, 2 as $t);
+            let a = $vec2::new(V1, V2);
             let serialized = serde_json::to_string(&a).unwrap();
             assert_eq!(SX2, serialized);
             let deserialized = serde_json::from_str(&serialized).unwrap();
@@ -116,7 +116,7 @@ macro_rules! impl_serde_vec3 {
 
         #[test]
         fn $test_name() {
-            let a = $vec3::new(1 as $t, 2 as $t, 3 as $t);
+            let a = $vec3::new(V1, V2, V3);
             let serialized = serde_json::to_string(&a).unwrap();
             assert_eq!(SX3, serialized);
             let deserialized = serde_json::from_str(&serialized).unwrap();
@@ -189,7 +189,7 @@ macro_rules! impl_serde_vec4 {
 
         #[test]
         fn test_vec4_serde() {
-            let a = $vec4::new(1 as $t, 2 as $t, 3 as $t, 4 as $t);
+            let a = $vec4::new(V1, V2, V3, V4);
             let serialized = serde_json::to_string(&a).unwrap();
             assert_eq!(SX4, serialized);
             let deserialized = serde_json::from_str(&serialized).unwrap();
@@ -710,6 +710,38 @@ macro_rules! impl_serde_float_types {
     };
 }
 
+#[cfg(test)]
+mod test_f32 {
+    pub const V1: f32 = 1.0;
+    pub const V2: f32 = 2.0;
+    pub const V3: f32 = 3.0;
+    pub const V4: f32 = 4.0;
+}
+
+#[cfg(test)]
+mod test_f64 {
+    pub const V1: f64 = 1.0;
+    pub const V2: f64 = 2.0;
+    pub const V3: f64 = 3.0;
+    pub const V4: f64 = 4.0;
+}
+
+#[cfg(test)]
+mod test_i32 {
+    pub const V1: i32 = 1;
+    pub const V2: i32 = 2;
+    pub const V3: i32 = 3;
+    pub const V4: i32 = 4;
+}
+
+#[cfg(test)]
+mod test_u32 {
+    pub const V1: u32 = 1;
+    pub const V2: u32 = 2;
+    pub const V3: u32 = 3;
+    pub const V4: u32 = 4;
+}
+
 #[cfg(test)]
 mod test_float {
     pub const SX0: &str = "[]";
@@ -730,7 +762,184 @@ mod test_int {
     pub const SX5: &str = "[1,2,3,4,5]";
 }
 
+#[cfg(test)]
+mod test_bool_mask {
+    pub const SX0: &str = "[]";
+    pub const SX1: &str = "[true]";
+    pub const SX2: &str = "[true,true]";
+    pub const SX3: &str = "[true,true,true]";
+    pub const SX4: &str = "[true,true,true,true]";
+    pub const SX5: &str = "[true,true,true,true,true]";
+    pub const V1: bool = true;
+    pub const V2: bool = true;
+    pub const V3: bool = true;
+    pub const V4: bool = true;
+}
+
+mod bool {
+    #[cfg(test)]
+    use super::test_bool_mask::*;
+    use crate::{BVec2, BVec3, BVec4};
+    #[cfg(not(feature = "scalar-math"))]
+    use crate::{BVec3A, BVec4A};
+    use core::fmt;
+    use serde::{
+        de::{self, Deserialize, Deserializer, SeqAccess, Visitor},
+        ser::{Serialize, SerializeTupleStruct, Serializer},
+    };
+
+    impl_serde_vec2!(bool, BVec2);
+    impl_serde_vec3!(bool, BVec3);
+    impl_serde_vec4!(bool, BVec4);
+
+    #[cfg(not(feature = "scalar-math"))]
+    impl Serialize for BVec3A {
+        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+        where
+            S: Serializer,
+        {
+            let mut state = serializer.serialize_tuple_struct("BVec3A", 3)?;
+            let a: [bool; 3] = (*self).into();
+            state.serialize_field(&a[0])?;
+            state.serialize_field(&a[1])?;
+            state.serialize_field(&a[2])?;
+            state.end()
+        }
+    }
+
+    #[cfg(not(feature = "scalar-math"))]
+    impl<'de> Deserialize<'de> for BVec3A {
+        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+        where
+            D: Deserializer<'de>,
+        {
+            struct Vec3Visitor;
+
+            impl<'de> Visitor<'de> for Vec3Visitor {
+                type Value = BVec3A;
+
+                fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+                    formatter.write_str("struct BVec3A")
+                }
+
+                fn visit_seq<V>(self, mut seq: V) -> Result<BVec3A, V::Error>
+                where
+                    V: SeqAccess<'de>,
+                {
+                    let x = seq
+                        .next_element()?
+                        .ok_or_else(|| de::Error::invalid_length(0, &self))?;
+                    let y = seq
+                        .next_element()?
+                        .ok_or_else(|| de::Error::invalid_length(1, &self))?;
+                    let z = seq
+                        .next_element()?
+                        .ok_or_else(|| de::Error::invalid_length(2, &self))?;
+                    Ok(BVec3A::new(x, y, z))
+                }
+            }
+
+            deserializer.deserialize_tuple_struct(stringify!($vec3), 3, Vec3Visitor)
+        }
+    }
+
+    #[cfg(not(feature = "scalar-math"))]
+    #[test]
+    fn test_bvec3a_serde() {
+        let a = BVec3A::new(V1, V2, V3);
+        let serialized = serde_json::to_string(&a).unwrap();
+        assert_eq!(SX3, serialized);
+        let deserialized = serde_json::from_str(&serialized).unwrap();
+        assert_eq!(a, deserialized);
+        let deserialized = serde_json::from_str::<BVec3A>(SX0);
+        assert!(deserialized.is_err());
+        let deserialized = serde_json::from_str::<BVec3A>(SX1);
+        assert!(deserialized.is_err());
+        let deserialized = serde_json::from_str::<BVec3A>(SX2);
+        assert!(deserialized.is_err());
+        let deserialized = serde_json::from_str::<BVec3A>(SX4);
+        assert!(deserialized.is_err());
+    }
+
+    #[cfg(not(feature = "scalar-math"))]
+    impl Serialize for BVec4A {
+        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+        where
+            S: Serializer,
+        {
+            let mut state = serializer.serialize_tuple_struct(stringify!(BVec4A), 4)?;
+            let a: [bool; 4] = (*self).into();
+            state.serialize_field(&a[0])?;
+            state.serialize_field(&a[1])?;
+            state.serialize_field(&a[2])?;
+            state.serialize_field(&a[2])?;
+            state.end()
+        }
+    }
+
+    #[cfg(not(feature = "scalar-math"))]
+    impl<'de> Deserialize<'de> for BVec4A {
+        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+        where
+            D: Deserializer<'de>,
+        {
+            struct Vec4Visitor;
+
+            impl<'de> Visitor<'de> for Vec4Visitor {
+                type Value = BVec4A;
+
+                fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+                    formatter.write_str(concat!("struct ", stringify!(BVec4A)))
+                }
+
+                fn visit_seq<V>(self, mut seq: V) -> Result<BVec4A, V::Error>
+                where
+                    V: SeqAccess<'de>,
+                {
+                    let x = seq
+                        .next_element()?
+                        .ok_or_else(|| de::Error::invalid_length(0, &self))?;
+                    let y = seq
+                        .next_element()?
+                        .ok_or_else(|| de::Error::invalid_length(1, &self))?;
+                    let z = seq
+                        .next_element()?
+                        .ok_or_else(|| de::Error::invalid_length(2, &self))?;
+                    let w = seq
+                        .next_element()?
+                        .ok_or_else(|| de::Error::invalid_length(3, &self))?;
+                    Ok(BVec4A::new(x, y, z, w))
+                }
+            }
+
+            deserializer.deserialize_tuple_struct(stringify!(BVec4A), 4, Vec4Visitor)
+        }
+    }
+
+    #[cfg(not(feature = "scalar-math"))]
+    #[test]
+    fn test_bvec4a_serde() {
+        let a = BVec4A::new(V1, V2, V3, V4);
+        let serialized = serde_json::to_string(&a).unwrap();
+        assert_eq!(SX4, serialized);
+        let deserialized = serde_json::from_str(&serialized).unwrap();
+        assert_eq!(a, deserialized);
+        let deserialized = serde_json::from_str::<BVec4A>(SX0);
+        assert!(deserialized.is_err());
+        let deserialized = serde_json::from_str::<BVec4A>(SX1);
+        assert!(deserialized.is_err());
+        let deserialized = serde_json::from_str::<BVec4A>(SX2);
+        assert!(deserialized.is_err());
+        let deserialized = serde_json::from_str::<BVec4A>(SX3);
+        assert!(deserialized.is_err());
+        let deserialized = serde_json::from_str::<BVec4A>(SX5);
+        assert!(deserialized.is_err());
+    }
+}
+
 mod f32 {
+    #[cfg(test)]
+    use super::test_f32::*;
     #[cfg(test)]
     use super::test_float::*;
     use crate::{Affine2, Affine3A, Mat2, Mat3, Mat3A, Mat4, Quat, Vec2, Vec3, Vec3A, Vec4};
@@ -746,6 +955,8 @@ mod f32 {
 }
 
 mod f64 {
+    #[cfg(test)]
+    use super::test_f64::*;
     #[cfg(test)]
     use super::test_float::*;
     use crate::{DAffine2, DAffine3, DMat2, DMat3, DMat4, DQuat, DVec2, DVec3, DVec4};
@@ -761,6 +972,8 @@ mod f64 {
 }
 
 mod i32 {
+    #[cfg(test)]
+    use super::test_i32::*;
     #[cfg(test)]
     use super::test_int::*;
     use crate::{IVec2, IVec3, IVec4};
@@ -776,6 +989,8 @@ mod i32 {
 mod u32 {
     #[cfg(test)]
     use super::test_int::*;
+    #[cfg(test)]
+    use super::test_u32::*;
     use crate::{UVec2, UVec3, UVec4};
     use core::fmt;
     use serde::{


Test patch:
diff --git a/tests/euler.rs b/tests/euler.rs
index 3441d53..dc4de53 100644
--- a/tests/euler.rs
+++ b/tests/euler.rs
@@ -6,7 +6,7 @@ trait AngleDiff {
     type Output;
     fn angle_diff(self, other: Self) -> Self::Output;
 }
-#[macro_export]
+
 macro_rules! impl_angle_diff {
     ($t:ty, $pi:expr) => {
         impl AngleDiff for $t {
@@ -27,7 +27,6 @@ macro_rules! impl_angle_diff {
 impl_angle_diff!(f32, std::f32::consts::PI);
 impl_angle_diff!(f64, std::f64::consts::PI);
 
-#[macro_export]
 macro_rules! assert_approx_angle {
     ($a:expr, $b:expr, $eps:expr) => {{
         let (a, b) = ($a, $b);
@@ -45,7 +44,6 @@ macro_rules! assert_approx_angle {
     }};
 }
 
-#[macro_export]
 macro_rules! impl_3axis_test {
     ($name:ident, $t:ty, $quat:ident, $euler:path, $U:path, $V:path, $W:path, $vec:ident) => {
         glam_test!($name, {
@@ -85,48 +83,6 @@ macro_rules! impl_3axis_test {
     };
 }
 
-#[macro_export]
-macro_rules! impl_2axis_test {
-    ($name:ident, $t:ty, $quat:ident, $euler:path, $U:path, $V:path, $W:path, $vec:ident) => {
-        glam_test!($name, {
-            #[allow(deprecated)]
-            let euler = $euler;
-            assert!($U == $W); // First and last axis must be different for three axis
-            for u in (-176..=176).step_by(44) {
-                for v in (-176..=176).step_by(44) {
-                    for w in (-176..=176).step_by(44) {
-                        let u1 = (u as $t).to_radians();
-                        let v1 = (v as $t).to_radians();
-                        let w1 = (w as $t).to_radians();
-
-                        let q1: $quat = ($quat::from_axis_angle($U, u1)
-                            * $quat::from_axis_angle($V, v1)
-                            * $quat::from_axis_angle($W, w1))
-                        .normalize();
-
-                        // Test if the rotation is the expected
-                        let q2 = $quat::from_euler(euler, u1, v1, w1).normalize();
-                        assert_approx_eq!(q1, q2, 1e-5);
-
-                        // Test angle reconstruction
-                        let (u2, v2, w2) = q1.to_euler(euler);
-                        let _q3 = $quat::from_euler(euler, u2, v2, w2).normalize();
-
-                        // Disabled tests, since no generic tests for ambiguous results in the two-axis results...
-                        // assert_approx_angle!(u1, u2, 1e-4 as $t);
-                        // assert_approx_angle!(v1, v2, 1e-4 as $t);
-                        // assert_approx_angle!(w1, w2, 1e-4 as $t);
-
-                        // assert_approx_eq!(q1 * $vec::X, q3 * $vec::X, 1e-4);
-                        // assert_approx_eq!(q1 * $vec::Y, q3 * $vec::Y, 1e-4);
-                        // assert_approx_eq!(q1 * $vec::Z, q3 * $vec::Z, 1e-4);
-                    }
-                }
-            }
-        });
-    };
-}
-
 macro_rules! impl_all_quat_tests_three_axis {
     ($t:ty, $q:ident, $v:ident) => {
         impl_3axis_test!(test_euler_zyx, $t, $q, ER::ZYX, $v::Z, $v::Y, $v::X, $v);
@@ -138,17 +94,6 @@ macro_rules! impl_all_quat_tests_three_axis {
     };
 }
 
-macro_rules! impl_all_quat_tests_two_axis {
-    ($t:ty, $q:ident, $v:ident) => {
-        impl_2axis_test!(test_euler_zyz, $t, $q, ER::ZYZ, $v::Z, $v::Y, $v::Z, $v);
-        impl_2axis_test!(test_euler_zxz, $t, $q, ER::ZXZ, $v::Z, $v::X, $v::Z, $v);
-        impl_2axis_test!(test_euler_yxy, $t, $q, ER::YXY, $v::Y, $v::X, $v::Y, $v);
-        impl_2axis_test!(test_euler_yzy, $t, $q, ER::YZY, $v::Y, $v::Z, $v::Y, $v);
-        impl_2axis_test!(test_euler_xyx, $t, $q, ER::XYX, $v::X, $v::Y, $v::X, $v);
-        impl_2axis_test!(test_euler_xzx, $t, $q, ER::XZX, $v::X, $v::Z, $v::X, $v);
-    };
-}
-
 mod euler {
     use super::AngleDiff;
     use glam::*;
@@ -158,15 +103,11 @@ mod euler {
         use super::*;
 
         impl_all_quat_tests_three_axis!(f32, Quat, Vec3);
-
-        impl_all_quat_tests_two_axis!(f32, Quat, Vec3);
     }
 
     mod dquat {
         use super::*;
 
         impl_all_quat_tests_three_axis!(f64, DQuat, DVec3);
-
-        impl_all_quat_tests_two_axis!(f64, DQuat, DVec3);
     }
 }