Repository: bitshifter/glam-rs
Pull request #471 (2024-02-19 16:17:54)

Element-wise absolute value for matrices
Pretty much exactly as the title says: add an `abs()` method to matrix types, that calls `abs()` on each column vector.

bitshifter: I'm curious, what is your use case for this PR?
bitshifter: All good, I'm just interested in how things in glam get used. I guess you are specifically implementing this bit https://pbr-book.org/4ed/Shapes/Managing_Rounding_Error#eq:transform-point-error?

Patch:
diff --git a/codegen/templates/mat.rs.tera b/codegen/templates/mat.rs.tera
index efe8ba9..25a8c14 100644
--- a/codegen/templates/mat.rs.tera
+++ b/codegen/templates/mat.rs.tera
@@ -2086,6 +2086,17 @@ impl {{ self_t }} {
         {% endfor %}
     }
 
+    /// Takes the absolute value of each element in `self`
+    #[inline]
+    #[must_use]
+    pub fn abs(&self) -> Self {
+        Self::from_cols(
+        {% for axis in axes %}
+            self.{{ axis }}.abs(),
+        {% endfor %}
+        )
+    }
+
     {% if scalar_t == "f32" %}
         #[inline]
         pub fn as_dmat{{ dim }}(&self) -> DMat{{ dim }} {
diff --git a/src/f32/coresimd/mat2.rs b/src/f32/coresimd/mat2.rs
index f8c0f91..f0e604e 100644
--- a/src/f32/coresimd/mat2.rs
+++ b/src/f32/coresimd/mat2.rs
@@ -309,6 +309,13 @@ impl Mat2 {
             && self.y_axis.abs_diff_eq(rhs.y_axis, max_abs_diff)
     }
 
+    /// Takes the absolute value of each element in `self`
+    #[inline]
+    #[must_use]
+    pub fn abs(&self) -> Self {
+        Self::from_cols(self.x_axis.abs(), self.y_axis.abs())
+    }
+
     #[inline]
     pub fn as_dmat2(&self) -> DMat2 {
         DMat2::from_cols(self.x_axis.as_dvec2(), self.y_axis.as_dvec2())
diff --git a/src/f32/coresimd/mat3a.rs b/src/f32/coresimd/mat3a.rs
index 3c0645d..733f97f 100644
--- a/src/f32/coresimd/mat3a.rs
+++ b/src/f32/coresimd/mat3a.rs
@@ -576,6 +576,13 @@ impl Mat3A {
             && self.z_axis.abs_diff_eq(rhs.z_axis, max_abs_diff)
     }
 
+    /// Takes the absolute value of each element in `self`
+    #[inline]
+    #[must_use]
+    pub fn abs(&self) -> Self {
+        Self::from_cols(self.x_axis.abs(), self.y_axis.abs(), self.z_axis.abs())
+    }
+
     #[inline]
     pub fn as_dmat3(&self) -> DMat3 {
         DMat3::from_cols(
diff --git a/src/f32/coresimd/mat4.rs b/src/f32/coresimd/mat4.rs
index 1117c12..78987f9 100644
--- a/src/f32/coresimd/mat4.rs
+++ b/src/f32/coresimd/mat4.rs
@@ -1219,6 +1219,18 @@ impl Mat4 {
             && self.w_axis.abs_diff_eq(rhs.w_axis, max_abs_diff)
     }
 
+    /// Takes the absolute value of each element in `self`
+    #[inline]
+    #[must_use]
+    pub fn abs(&self) -> Self {
+        Self::from_cols(
+            self.x_axis.abs(),
+            self.y_axis.abs(),
+            self.z_axis.abs(),
+            self.w_axis.abs(),
+        )
+    }
+
     #[inline]
     pub fn as_dmat4(&self) -> DMat4 {
         DMat4::from_cols(
diff --git a/src/f32/mat3.rs b/src/f32/mat3.rs
index 20b3c53..7c419cd 100644
--- a/src/f32/mat3.rs
+++ b/src/f32/mat3.rs
@@ -570,6 +570,13 @@ impl Mat3 {
             && self.z_axis.abs_diff_eq(rhs.z_axis, max_abs_diff)
     }
 
+    /// Takes the absolute value of each element in `self`
+    #[inline]
+    #[must_use]
+    pub fn abs(&self) -> Self {
+        Self::from_cols(self.x_axis.abs(), self.y_axis.abs(), self.z_axis.abs())
+    }
+
     #[inline]
     pub fn as_dmat3(&self) -> DMat3 {
         DMat3::from_cols(
diff --git a/src/f32/scalar/mat2.rs b/src/f32/scalar/mat2.rs
index 1205035..12767c7 100644
--- a/src/f32/scalar/mat2.rs
+++ b/src/f32/scalar/mat2.rs
@@ -304,6 +304,13 @@ impl Mat2 {
             && self.y_axis.abs_diff_eq(rhs.y_axis, max_abs_diff)
     }
 
+    /// Takes the absolute value of each element in `self`
+    #[inline]
+    #[must_use]
+    pub fn abs(&self) -> Self {
+        Self::from_cols(self.x_axis.abs(), self.y_axis.abs())
+    }
+
     #[inline]
     pub fn as_dmat2(&self) -> DMat2 {
         DMat2::from_cols(self.x_axis.as_dvec2(), self.y_axis.as_dvec2())
diff --git a/src/f32/scalar/mat3a.rs b/src/f32/scalar/mat3a.rs
index b443ae0..e48b1d4 100644
--- a/src/f32/scalar/mat3a.rs
+++ b/src/f32/scalar/mat3a.rs
@@ -574,6 +574,13 @@ impl Mat3A {
             && self.z_axis.abs_diff_eq(rhs.z_axis, max_abs_diff)
     }
 
+    /// Takes the absolute value of each element in `self`
+    #[inline]
+    #[must_use]
+    pub fn abs(&self) -> Self {
+        Self::from_cols(self.x_axis.abs(), self.y_axis.abs(), self.z_axis.abs())
+    }
+
     #[inline]
     pub fn as_dmat3(&self) -> DMat3 {
         DMat3::from_cols(
diff --git a/src/f32/scalar/mat4.rs b/src/f32/scalar/mat4.rs
index 5affa5a..4795384 100644
--- a/src/f32/scalar/mat4.rs
+++ b/src/f32/scalar/mat4.rs
@@ -1131,6 +1131,18 @@ impl Mat4 {
             && self.w_axis.abs_diff_eq(rhs.w_axis, max_abs_diff)
     }
 
+    /// Takes the absolute value of each element in `self`
+    #[inline]
+    #[must_use]
+    pub fn abs(&self) -> Self {
+        Self::from_cols(
+            self.x_axis.abs(),
+            self.y_axis.abs(),
+            self.z_axis.abs(),
+            self.w_axis.abs(),
+        )
+    }
+
     #[inline]
     pub fn as_dmat4(&self) -> DMat4 {
         DMat4::from_cols(
diff --git a/src/f32/sse2/mat2.rs b/src/f32/sse2/mat2.rs
index ab63731..7db655a 100644
--- a/src/f32/sse2/mat2.rs
+++ b/src/f32/sse2/mat2.rs
@@ -341,6 +341,13 @@ impl Mat2 {
             && self.y_axis.abs_diff_eq(rhs.y_axis, max_abs_diff)
     }
 
+    /// Takes the absolute value of each element in `self`
+    #[inline]
+    #[must_use]
+    pub fn abs(&self) -> Self {
+        Self::from_cols(self.x_axis.abs(), self.y_axis.abs())
+    }
+
     #[inline]
     pub fn as_dmat2(&self) -> DMat2 {
         DMat2::from_cols(self.x_axis.as_dvec2(), self.y_axis.as_dvec2())
diff --git a/src/f32/sse2/mat3a.rs b/src/f32/sse2/mat3a.rs
index 56f762e..141e6b4 100644
--- a/src/f32/sse2/mat3a.rs
+++ b/src/f32/sse2/mat3a.rs
@@ -581,6 +581,13 @@ impl Mat3A {
             && self.z_axis.abs_diff_eq(rhs.z_axis, max_abs_diff)
     }
 
+    /// Takes the absolute value of each element in `self`
+    #[inline]
+    #[must_use]
+    pub fn abs(&self) -> Self {
+        Self::from_cols(self.x_axis.abs(), self.y_axis.abs(), self.z_axis.abs())
+    }
+
     #[inline]
     pub fn as_dmat3(&self) -> DMat3 {
         DMat3::from_cols(
diff --git a/src/f32/sse2/mat4.rs b/src/f32/sse2/mat4.rs
index 22d42fb..8192681 100644
--- a/src/f32/sse2/mat4.rs
+++ b/src/f32/sse2/mat4.rs
@@ -1228,6 +1228,18 @@ impl Mat4 {
             && self.w_axis.abs_diff_eq(rhs.w_axis, max_abs_diff)
     }
 
+    /// Takes the absolute value of each element in `self`
+    #[inline]
+    #[must_use]
+    pub fn abs(&self) -> Self {
+        Self::from_cols(
+            self.x_axis.abs(),
+            self.y_axis.abs(),
+            self.z_axis.abs(),
+            self.w_axis.abs(),
+        )
+    }
+
     #[inline]
     pub fn as_dmat4(&self) -> DMat4 {
         DMat4::from_cols(
diff --git a/src/f32/wasm32/mat2.rs b/src/f32/wasm32/mat2.rs
index f2a4cc3..34dc808 100644
--- a/src/f32/wasm32/mat2.rs
+++ b/src/f32/wasm32/mat2.rs
@@ -315,6 +315,13 @@ impl Mat2 {
             && self.y_axis.abs_diff_eq(rhs.y_axis, max_abs_diff)
     }
 
+    /// Takes the absolute value of each element in `self`
+    #[inline]
+    #[must_use]
+    pub fn abs(&self) -> Self {
+        Self::from_cols(self.x_axis.abs(), self.y_axis.abs())
+    }
+
     #[inline]
     pub fn as_dmat2(&self) -> DMat2 {
         DMat2::from_cols(self.x_axis.as_dvec2(), self.y_axis.as_dvec2())
diff --git a/src/f32/wasm32/mat3a.rs b/src/f32/wasm32/mat3a.rs
index 96c039b..c29cecc 100644
--- a/src/f32/wasm32/mat3a.rs
+++ b/src/f32/wasm32/mat3a.rs
@@ -576,6 +576,13 @@ impl Mat3A {
             && self.z_axis.abs_diff_eq(rhs.z_axis, max_abs_diff)
     }
 
+    /// Takes the absolute value of each element in `self`
+    #[inline]
+    #[must_use]
+    pub fn abs(&self) -> Self {
+        Self::from_cols(self.x_axis.abs(), self.y_axis.abs(), self.z_axis.abs())
+    }
+
     #[inline]
     pub fn as_dmat3(&self) -> DMat3 {
         DMat3::from_cols(
diff --git a/src/f32/wasm32/mat4.rs b/src/f32/wasm32/mat4.rs
index 156e1de..c0eeeca 100644
--- a/src/f32/wasm32/mat4.rs
+++ b/src/f32/wasm32/mat4.rs
@@ -1219,6 +1219,18 @@ impl Mat4 {
             && self.w_axis.abs_diff_eq(rhs.w_axis, max_abs_diff)
     }
 
+    /// Takes the absolute value of each element in `self`
+    #[inline]
+    #[must_use]
+    pub fn abs(&self) -> Self {
+        Self::from_cols(
+            self.x_axis.abs(),
+            self.y_axis.abs(),
+            self.z_axis.abs(),
+            self.w_axis.abs(),
+        )
+    }
+
     #[inline]
     pub fn as_dmat4(&self) -> DMat4 {
         DMat4::from_cols(
diff --git a/src/f64/dmat2.rs b/src/f64/dmat2.rs
index dbc61c5..5608bfd 100644
--- a/src/f64/dmat2.rs
+++ b/src/f64/dmat2.rs
@@ -293,6 +293,13 @@ impl DMat2 {
             && self.y_axis.abs_diff_eq(rhs.y_axis, max_abs_diff)
     }
 
+    /// Takes the absolute value of each element in `self`
+    #[inline]
+    #[must_use]
+    pub fn abs(&self) -> Self {
+        Self::from_cols(self.x_axis.abs(), self.y_axis.abs())
+    }
+
     #[inline]
     pub fn as_mat2(&self) -> Mat2 {
         Mat2::from_cols(self.x_axis.as_vec2(), self.y_axis.as_vec2())
diff --git a/src/f64/dmat3.rs b/src/f64/dmat3.rs
index c271886..bbb24d7 100644
--- a/src/f64/dmat3.rs
+++ b/src/f64/dmat3.rs
@@ -567,6 +567,13 @@ impl DMat3 {
             && self.z_axis.abs_diff_eq(rhs.z_axis, max_abs_diff)
     }
 
+    /// Takes the absolute value of each element in `self`
+    #[inline]
+    #[must_use]
+    pub fn abs(&self) -> Self {
+        Self::from_cols(self.x_axis.abs(), self.y_axis.abs(), self.z_axis.abs())
+    }
+
     #[inline]
     pub fn as_mat3(&self) -> Mat3 {
         Mat3::from_cols(
diff --git a/src/f64/dmat4.rs b/src/f64/dmat4.rs
index e9012fe..f064aad 100644
--- a/src/f64/dmat4.rs
+++ b/src/f64/dmat4.rs
@@ -1095,6 +1095,18 @@ impl DMat4 {
             && self.w_axis.abs_diff_eq(rhs.w_axis, max_abs_diff)
     }
 
+    /// Takes the absolute value of each element in `self`
+    #[inline]
+    #[must_use]
+    pub fn abs(&self) -> Self {
+        Self::from_cols(
+            self.x_axis.abs(),
+            self.y_axis.abs(),
+            self.z_axis.abs(),
+            self.w_axis.abs(),
+        )
+    }
+
     #[inline]
     pub fn as_mat4(&self) -> Mat4 {
         Mat4::from_cols(


Test patch:
diff --git a/tests/mat4.rs b/tests/mat4.rs
index d3b9031..492f9fa 100644
--- a/tests/mat4.rs
+++ b/tests/mat4.rs
@@ -663,6 +663,28 @@ macro_rules! impl_mat4_tests {
             assert!(!($mat4::IDENTITY * NEG_INFINITY).is_finite());
             assert!(!($mat4::IDENTITY * NAN).is_finite());
         });
+
+        glam_test!(test_mat4_abs, {
+            let neg = $mat4::IDENTITY * -1.0;
+            assert_eq!(neg.abs(), $mat4::IDENTITY);
+
+            let partial_neg = $mat4::from_cols_array_2d(&[
+                [1.0, -2.0, 3.0, -4.0],
+                [-5.0, 6.0, -7.0, 8.0],
+                [-9.0, 10.0, 11.0, -12.0],
+                [13.0, -14.0, -15.0, 16.0],
+            ]);
+
+            assert_eq!(
+                partial_neg.abs(),
+                $mat4::from_cols_array_2d(&[
+                    [1.0, 2.0, 3.0, 4.0],
+                    [5.0, 6.0, 7.0, 8.0],
+                    [9.0, 10.0, 11.0, 12.0],
+                    [13.0, 14.0, 15.0, 16.0],
+                ])
+            );
+        });
     };
 }