Repository: bitshifter/glam-rs (rust)

Issue:
impl Add<Quat> and Sub<Quat> for Quat
In short: Through a somewhat complicated chain of traits and supertraits in multiple different libraries, I've found myself needing glam::Quat to implement addition and subtraction. I've started working on a PR.

In long: I'm trying to implement skeletal animation for the bevy engine. They use glam, and rotations are represented by your quaternions. We're looking to use the splines crate to handle interpolation between keyframes, which is a very generic crate. It does however require the supplied type to implement addition and subtraction, and through a shim I've managed to get it working, but at the cost of user convenience. The splines crate provides implementations of its traits for a number of external math crates, but I am unable to add support for glam because of the requirement of these ops.

I understand that adding support for addition and subtraction ops might mislead users who are trying to combine rotations, but perhaps with good documentation we can mitigate this risk and steer them towards correct usage.

Pull request description:
Implement Add<Quat> Sub<Quat>, Mul<f32> and Div<f32> for Quat
closes #82 

I tried to clarify in docs that these ops are not what you want to use most of the time. No editor I know of will show these docs when just using the `+` or `-` operator, but I couldn't think of a better way.

Patch:
diff --git a/src/f32/quat.rs b/src/f32/quat.rs
index 213adcd..adacb5e 100644
--- a/src/f32/quat.rs
+++ b/src/f32/quat.rs
@@ -6,7 +6,7 @@ use core::arch::x86_64::*;
 use core::{
     cmp::Ordering,
     fmt,
-    ops::{Mul, MulAssign, Neg},
+    ops::{Add, Div, Mul, MulAssign, Neg, Sub},
 };
 
 const IDENTITY: Quat = const_quat!([0.0, 0.0, 0.0, 1.0]);
@@ -456,6 +456,7 @@ impl Quat {
 
     #[inline]
     /// Multiplies two quaternions.
+    /// If they each represent a rotation, the result will represent the combined rotation.
     /// Note that due to floating point rounding the result may not be perfectly normalized.
     pub fn mul_quat(self, other: Self) -> Self {
         glam_assert!(self.is_normalized());
@@ -553,6 +554,49 @@ impl fmt::Display for Quat {
     }
 }
 
+impl Add<Quat> for Quat {
+    type Output = Self;
+    #[inline]
+    /// Adds two quaternions.
+    /// The sum is not guaranteed to be normalized.
+    ///
+    /// NB: Addition is not the same as combining the rotations represented by the two quaternions!
+    /// That corresponds to multiplication.
+    fn add(self, other: Self) -> Self {
+        Self(self.0 + other.0)
+    }
+}
+
+impl Sub<Quat> for Quat {
+    type Output = Self;
+    #[inline]
+    /// Subtracts the other quaternion from self.
+    /// The difference is not guaranteed to be normalized.
+    fn sub(self, other: Self) -> Self {
+        Self(self.0 - other.0)
+    }
+}
+
+impl Mul<f32> for Quat {
+    type Output = Self;
+    #[inline]
+    /// Multiplies a quaternion with an f32.
+    /// The product is not guaranteed to be normalized.
+    fn mul(self, other: f32) -> Self {
+        Self(self.0 * other)
+    }
+}
+
+impl Div<f32> for Quat {
+    type Output = Self;
+    #[inline]
+    /// Divides a quaternion by an f32.
+    /// The quotient is not guaranteed to be normalized.
+    fn div(self, other: f32) -> Self {
+        Self(self.0 / other)
+    }
+}
+
 impl Mul<Quat> for Quat {
     type Output = Self;
     #[inline]


Test patch:
diff --git a/tests/quat.rs b/tests/quat.rs
index b51edb6..6a279fd 100644
--- a/tests/quat.rs
+++ b/tests/quat.rs
@@ -330,6 +330,32 @@ fn test_quat_elements() {
     assert!(a.w() == w);
 }
 
+#[test]
+fn test_quat_addition() {
+    let a = Quat::from_xyzw(1.0, 2.0, 3.0, 4.0);
+    let b = Quat::from_xyzw(5.0, 6.0, 7.0, -9.0);
+    assert_eq!(a + b, Quat::from_xyzw(6.0, 8.0, 10.0, -5.0));
+}
+
+#[test]
+fn test_quat_subtraction() {
+    let a = Quat::from_xyzw(6.0, 8.0, 10.0, -5.0);
+    let b = Quat::from_xyzw(5.0, 6.0, 7.0, -9.0);
+    assert_eq!(a - b, Quat::from_xyzw(1.0, 2.0, 3.0, 4.0));
+}
+
+#[test]
+fn test_quat_f32_multiplication() {
+    let a = Quat::from_xyzw(1.0, 2.0, 3.0, 4.0);
+    assert_eq!(a * 2.0, Quat::from_xyzw(2.0, 4.0, 6.0, 8.0));
+}
+
+#[test]
+fn test_quat_f32_division() {
+    let a = Quat::from_xyzw(2.0, 4.0, 6.0, 8.0);
+    assert_eq!(a / 2.0, Quat::from_xyzw(1.0, 2.0, 3.0, 4.0));
+}
+
 #[cfg(feature = "serde")]
 #[test]
 fn test_quat_serde() {