Repository: bitshifter/glam-rs
Pull request #98 (2020-11-18 11:46:44)

Add `fn is_finite(&self) -> bool` member to all types
Having a function like this is super-useful to guard against non-finite values. For instance:

``` rust
let v = foo.cross(bar) / det;
let v = v.normalize():
if v.is_finite() {
   Some(v)
} else {
   None
}
```

or `debug_assert!(transform.is_finite());`

## Naming

There is already a `is_nan` which returns a mask. This sets a precedence that something called `is_finite` should also return a mask. However, `is_entirely_finite` doesn't feel like a great name to me.
Maybe functions that return a mask should have a `m` suffix or something instead?

Anyway, I'll be happy to change the name of this, this is just a first draft!


Patch:
diff --git a/src/f32/mat2.rs b/src/f32/mat2.rs
index 2f7b9b2..744b772 100644
--- a/src/f32/mat2.rs
+++ b/src/f32/mat2.rs
@@ -150,6 +150,13 @@ impl Mat2 {
     //     }
     // }
 
+    /// Returns `true` if, and only if, all elements are finite.
+    /// If any element is either `NaN`, positive or negative infinity, this will return `false`.
+    #[inline]
+    pub fn is_finite(&self) -> bool {
+        self.x_axis.is_finite() && self.y_axis.is_finite()
+    }
+
     /// Returns the transpose of `self`.
     #[inline]
     pub fn transpose(&self) -> Self {
diff --git a/src/f32/mat3.rs b/src/f32/mat3.rs
index 55f97be..6fe976c 100644
--- a/src/f32/mat3.rs
+++ b/src/f32/mat3.rs
@@ -255,6 +255,13 @@ impl Mat3 {
     //     }
     // }
 
+    /// Returns `true` if, and only if, all elements are finite.
+    /// If any element is either `NaN`, positive or negative infinity, this will return `false`.
+    #[inline]
+    pub fn is_finite(&self) -> bool {
+        self.x_axis.is_finite() && self.y_axis.is_finite() && self.z_axis.is_finite()
+    }
+
     /// Returns the transpose of `self`.
     #[inline]
     pub fn transpose(&self) -> Self {
diff --git a/src/f32/mat4.rs b/src/f32/mat4.rs
index aa3ddec..e467b3f 100644
--- a/src/f32/mat4.rs
+++ b/src/f32/mat4.rs
@@ -344,6 +344,16 @@ impl Mat4 {
     //     }
     // }
 
+    /// Returns `true` if, and only if, all elements are finite.
+    /// If any element is either `NaN`, positive or negative infinity, this will return `false`.
+    #[inline]
+    pub fn is_finite(&self) -> bool {
+        self.x_axis.is_finite()
+            && self.y_axis.is_finite()
+            && self.z_axis.is_finite()
+            && self.w_axis.is_finite()
+    }
+
     /// Returns the transpose of `self`.
     #[inline]
     pub fn transpose(&self) -> Self {
diff --git a/src/f32/quat.rs b/src/f32/quat.rs
index 7ac64a4..9640e89 100644
--- a/src/f32/quat.rs
+++ b/src/f32/quat.rs
@@ -287,6 +287,13 @@ impl Quat {
         Self(self.0.mul(inv_len))
     }
 
+    /// Returns `true` if, and only if, all elements are finite.
+    /// If any element is either `NaN`, positive or negative infinity, this will return `false`.
+    #[inline]
+    pub fn is_finite(&self) -> bool {
+        self.x.is_finite() && self.y.is_finite() && self.z.is_finite() && self.w.is_finite()
+    }
+
     /// Returns whether `self` of length `1.0` or not.
     ///
     /// Uses a precision threshold of `1e-6`.
diff --git a/src/f32/transform.rs b/src/f32/transform.rs
index 742cb40..d832957 100644
--- a/src/f32/transform.rs
+++ b/src/f32/transform.rs
@@ -193,6 +193,13 @@ impl TransformRT {
         }
     }
 
+    /// Returns `true` if, and only if, all elements are finite.
+    /// If any element is either `NaN`, positive or negative infinity, this will return `false`.
+    #[inline]
+    pub fn is_finite(&self) -> bool {
+        self.rotation.is_finite() && self.translation.is_finite()
+    }
+
     #[inline]
     pub fn inverse(&self) -> Self {
         let rotation = self.rotation.conjugate();
diff --git a/src/f32/vec2.rs b/src/f32/vec2.rs
index 6b9ac3d..4362be8 100644
--- a/src/f32/vec2.rs
+++ b/src/f32/vec2.rs
@@ -75,6 +75,13 @@ impl Vec2 {
         is_normalized!(self)
     }
 
+    /// Returns `true` if, and only if, all elements are finite.
+    /// If any element is either `NaN`, positive or negative infinity, this will return `false`.
+    #[inline]
+    pub fn is_finite(&self) -> bool {
+        self.x.is_finite() && self.y.is_finite()
+    }
+
     /// Returns true if the absolute difference of all elements between `self`
     /// and `other` is less than or equal to `max_abs_diff`.
     ///
diff --git a/src/f32/vec3.rs b/src/f32/vec3.rs
index 88dd70c..c0d8dba 100644
--- a/src/f32/vec3.rs
+++ b/src/f32/vec3.rs
@@ -393,6 +393,13 @@ impl Vec3 {
         self + ((other - self) * s)
     }
 
+    /// Returns `true` if, and only if, all elements are finite.
+    /// If any element is either `NaN`, positive or negative infinity, this will return `false`.
+    #[inline]
+    pub fn is_finite(&self) -> bool {
+        self.x.is_finite() && self.y.is_finite() && self.z.is_finite()
+    }
+
     /// Returns whether `self` of length `1.0` or not.
     ///
     /// Uses a precision threshold of `1e-6`.
diff --git a/src/f32/vec3a.rs b/src/f32/vec3a.rs
index 186e58e..8099412 100644
--- a/src/f32/vec3a.rs
+++ b/src/f32/vec3a.rs
@@ -668,6 +668,13 @@ impl Vec3A {
         is_normalized!(self)
     }
 
+    /// Returns `true` if, and only if, all elements are finite.
+    /// If any element is either `NaN`, positive or negative infinity, this will return `false`.
+    #[inline]
+    pub fn is_finite(&self) -> bool {
+        self.x.is_finite() && self.y.is_finite() && self.z.is_finite()
+    }
+
     /// Returns true if the absolute difference of all elements between `self`
     /// and `other` is less than or equal to `max_abs_diff`.
     ///
diff --git a/src/f32/vec4.rs b/src/f32/vec4.rs
index ae47db4..d0a39e8 100644
--- a/src/f32/vec4.rs
+++ b/src/f32/vec4.rs
@@ -713,6 +713,13 @@ impl Vec4 {
         self + ((other - self) * s)
     }
 
+    /// Returns `true` if, and only if, all elements are finite.
+    /// If any element is either `NaN`, positive or negative infinity, this will return `false`.
+    #[inline]
+    pub fn is_finite(&self) -> bool {
+        self.x.is_finite() && self.y.is_finite() && self.z.is_finite() && self.w.is_finite()
+    }
+
     /// Returns whether `self` is length `1.0` or not.
     ///
     /// Uses a precision threshold of `1e-6`.


Test patch:
diff --git a/tests/mat2.rs b/tests/mat2.rs
index a867544..bfc7710 100644
--- a/tests/mat2.rs
+++ b/tests/mat2.rs
@@ -198,3 +198,14 @@ fn test_product() {
     let two = Mat2::identity() + Mat2::identity();
     assert_eq!(vec![two, two].iter().product::<Mat2>(), two * two);
 }
+
+#[test]
+fn test_mat2_is_finite() {
+    use std::f32::INFINITY;
+    use std::f32::NAN;
+    use std::f32::NEG_INFINITY;
+    assert!(Mat2::identity().is_finite());
+    assert!(!(Mat2::identity() * INFINITY).is_finite());
+    assert!(!(Mat2::identity() * NEG_INFINITY).is_finite());
+    assert!(!(Mat2::identity() * NAN).is_finite());
+}
diff --git a/tests/mat3.rs b/tests/mat3.rs
index 659c8fa..10b5720 100644
--- a/tests/mat3.rs
+++ b/tests/mat3.rs
@@ -262,3 +262,14 @@ fn test_product() {
     let two = Mat3::identity() + Mat3::identity();
     assert_eq!(vec![two, two].iter().product::<Mat3>(), two * two);
 }
+
+#[test]
+fn test_mat3_is_finite() {
+    use std::f32::INFINITY;
+    use std::f32::NAN;
+    use std::f32::NEG_INFINITY;
+    assert!(Mat3::identity().is_finite());
+    assert!(!(Mat3::identity() * INFINITY).is_finite());
+    assert!(!(Mat3::identity() * NEG_INFINITY).is_finite());
+    assert!(!(Mat3::identity() * NAN).is_finite());
+}
diff --git a/tests/mat4.rs b/tests/mat4.rs
index 8f97ebd..4e58900 100644
--- a/tests/mat4.rs
+++ b/tests/mat4.rs
@@ -543,3 +543,14 @@ fn test_product() {
     let two = Mat4::identity() + Mat4::identity();
     assert_eq!(vec![two, two].iter().product::<Mat4>(), two * two);
 }
+
+#[test]
+fn test_mat4_is_finite() {
+    use std::f32::INFINITY;
+    use std::f32::NAN;
+    use std::f32::NEG_INFINITY;
+    assert!(Mat4::identity().is_finite());
+    assert!(!(Mat4::identity() * INFINITY).is_finite());
+    assert!(!(Mat4::identity() * NEG_INFINITY).is_finite());
+    assert!(!(Mat4::identity() * NAN).is_finite());
+}
diff --git a/tests/quat.rs b/tests/quat.rs
index e2875ad..5cb3f5e 100644
--- a/tests/quat.rs
+++ b/tests/quat.rs
@@ -402,3 +402,16 @@ fn test_product() {
     let two = quat(2.0, 2.0, 2.0, 2.0).normalize();
     assert_eq!(vec![two, two].iter().product::<Quat>(), two * two);
 }
+
+#[test]
+fn test_quat_is_finite() {
+    use std::f32::INFINITY;
+    use std::f32::NAN;
+    use std::f32::NEG_INFINITY;
+    assert!(Quat::from_xyzw(0.0, 0.0, 0.0, 0.0).is_finite());
+    assert!(Quat::from_xyzw(-1e-10, 1.0, 1e10, 42.0).is_finite());
+    assert!(!Quat::from_xyzw(INFINITY, 0.0, 0.0, 0.0).is_finite());
+    assert!(!Quat::from_xyzw(0.0, NAN, 0.0, 0.0).is_finite());
+    assert!(!Quat::from_xyzw(0.0, 0.0, NEG_INFINITY, 0.0).is_finite());
+    assert!(!Quat::from_xyzw(0.0, 0.0, 0.0, NAN).is_finite());
+}
diff --git a/tests/vec2.rs b/tests/vec2.rs
index 839a567..c080077 100644
--- a/tests/vec2.rs
+++ b/tests/vec2.rs
@@ -518,3 +518,16 @@ fn test_product() {
     let two = Vec2::new(2.0, 2.0);
     assert_eq!(vec![two, two].iter().product::<Vec2>(), two * two);
 }
+
+#[test]
+fn test_vec2_is_finite() {
+    use std::f32::INFINITY;
+    use std::f32::NAN;
+    use std::f32::NEG_INFINITY;
+    assert!(Vec2::new(0.0, 0.0).is_finite());
+    assert!(Vec2::new(-1e-10, 1e10).is_finite());
+    assert!(!Vec2::new(INFINITY, 0.0).is_finite());
+    assert!(!Vec2::new(0.0, NAN).is_finite());
+    assert!(!Vec2::new(0.0, NEG_INFINITY).is_finite());
+    assert!(!Vec2::new(INFINITY, NEG_INFINITY).is_finite());
+}
diff --git a/tests/vec3.rs b/tests/vec3.rs
index dca1d6e..ed44ec2 100644
--- a/tests/vec3.rs
+++ b/tests/vec3.rs
@@ -566,3 +566,16 @@ fn test_product() {
     let two = Vec3::new(2.0, 2.0, 2.0);
     assert_eq!(vec![two, two].iter().product::<Vec3>(), two * two);
 }
+
+#[test]
+fn test_vec3_is_finite() {
+    use std::f32::INFINITY;
+    use std::f32::NAN;
+    use std::f32::NEG_INFINITY;
+    assert!(Vec3::new(0.0, 0.0, 0.0).is_finite());
+    assert!(Vec3::new(-1e-10, 1.0, 1e10).is_finite());
+    assert!(!Vec3::new(INFINITY, 0.0, 0.0).is_finite());
+    assert!(!Vec3::new(0.0, NAN, 0.0).is_finite());
+    assert!(!Vec3::new(0.0, 0.0, NEG_INFINITY).is_finite());
+    assert!(!Vec3::splat(NAN).is_finite());
+}
diff --git a/tests/vec3a.rs b/tests/vec3a.rs
index 7cf581a..5db2d73 100644
--- a/tests/vec3a.rs
+++ b/tests/vec3a.rs
@@ -604,3 +604,26 @@ fn test_product() {
     let two = Vec3A::new(2.0, 2.0, 2.0);
     assert_eq!(vec![two, two].iter().product::<Vec3A>(), two * two);
 }
+
+#[test]
+fn test_vec3a_is_finite() {
+    use std::f32::INFINITY;
+    use std::f32::NAN;
+    use std::f32::NEG_INFINITY;
+    assert!(Vec3A::new(0.0, 0.0, 0.0).is_finite());
+    assert!(Vec3A::new(-1e-10, 1.0, 1e10).is_finite());
+    assert!(!Vec3A::new(INFINITY, 0.0, 0.0).is_finite());
+    assert!(!Vec3A::new(0.0, NAN, 0.0).is_finite());
+    assert!(!Vec3A::new(0.0, 0.0, NEG_INFINITY).is_finite());
+    assert!(!Vec3A::splat(NAN).is_finite());
+
+    {
+        // Check that the implicit fourth element (in simd) does not affect `is_finite()`:
+        let mut v = Vec3A::splat(NAN);
+        assert!(!v.is_finite());
+        v.x = 42.0;
+        v.y = 42.0;
+        v.z = 42.0;
+        assert!(v.is_finite());
+    }
+}
diff --git a/tests/vec4.rs b/tests/vec4.rs
index 62e8e2e..632fa34 100644
--- a/tests/vec4.rs
+++ b/tests/vec4.rs
@@ -638,3 +638,16 @@ fn test_product() {
     let two = Vec4::new(2.0, 2.0, 2.0, 2.0);
     assert_eq!(vec![two, two].iter().product::<Vec4>(), two * two);
 }
+
+#[test]
+fn test_vec4_is_finite() {
+    use std::f32::INFINITY;
+    use std::f32::NAN;
+    use std::f32::NEG_INFINITY;
+    assert!(Vec4::new(0.0, 0.0, 0.0, 0.0).is_finite());
+    assert!(Vec4::new(-1e-10, 1.0, 1e10, 42.0).is_finite());
+    assert!(!Vec4::new(INFINITY, 0.0, 0.0, 0.0).is_finite());
+    assert!(!Vec4::new(0.0, NAN, 0.0, 0.0).is_finite());
+    assert!(!Vec4::new(0.0, 0.0, NEG_INFINITY, 0.0).is_finite());
+    assert!(!Vec4::new(0.0, 0.0, 0.0, NAN).is_finite());
+}