Repository: bitshifter/glam-rs (rust)

Issue:
Implement Index / IndexMut for BVec4 et al.
Currently, the `Index` and `IndexMut` traits are implemented for `Vec4`, but not for `BVec4`. I'd like to request these traits be implemented so that there's better parity between `BVec4` and `Vec4`. It's possible this should be done for other types as well.

Pull request description:
Add BVec get and set methods for getting and setting individual elements.
Fixes #373 .

Patch:
diff --git a/codegen/templates/vec.rs.tera b/codegen/templates/vec.rs.tera
index 4ba2bb4..7a43064 100644
--- a/codegen/templates/vec.rs.tera
+++ b/codegen/templates/vec.rs.tera
@@ -485,7 +485,7 @@ impl {{ self_t }} {
         {% elif is_wasm32 %}
             Self(unsafe { dot{{ dim }}_into_v128(self.0, rhs.0) })
         {% elif is_coresimd %}
-            Self(unsafe { dot{{ dim }}_into_f32x4(self.0, rhs.0) })
+            Self(dot{{ dim }}_into_f32x4(self.0, rhs.0))
         {% else %}
             Self::splat(self.dot(rhs))
         {% endif %}
diff --git a/codegen/templates/vec_mask.rs.tera b/codegen/templates/vec_mask.rs.tera
index 42b6d27..998bd89 100644
--- a/codegen/templates/vec_mask.rs.tera
+++ b/codegen/templates/vec_mask.rs.tera
@@ -6,6 +6,11 @@
     {% set is_bool = true %}
 {% else %}
     {% set self_t = "BVec" ~ dim ~ "A" %}
+    {% if dim == 4 %}
+        {% set vec_t = "Vec4" %}
+    {% elif dim == 3 %}
+        {% set vec_t = "Vec3A" %}
+    {% endif %}
     {% set align = 16 %}
     {% set is_u32 = true %}
     {% if is_sse2 %}
@@ -217,6 +222,55 @@ impl {{ self_t }} {
         {% endif %}
     }
 
+    /// Tests the value at `index`.
+    ///
+    /// Panics if `index` is greater than {{ dim  - 1 }}.
+    #[inline]
+    pub fn test(&self, index: usize) -> bool {
+        {% if is_coresimd %}
+            self.0.test(index)
+        {% else %}
+            match index {
+                {% for c in components %}
+                    {%- if is_scalar and is_bool %}
+                        {{ loop.index0 }} => self.{{ c }},
+                    {%- elif is_scalar and is_u32 %}
+                        {{ loop.index0 }} => (self.{{ c }} & 0x1) != 0,
+                    {%- else %}
+                        {{ loop.index0 }} => (self.bitmask() & (1 << {{ loop.index0 }})) != 0,
+                    {%- endif %}
+                {%- endfor %}
+                _ => panic!("index out of bounds")
+            }
+        {% endif %}
+    }
+
+    /// Sets the element at `index`.
+    ///
+    /// Panics if `index` is greater than {{ dim  - 1 }}.
+    #[inline]
+    pub fn set(&mut self, index: usize, value: bool) {
+        {% if is_scalar %}
+            match index {
+                {% for c in components %}
+                    {%- if is_bool %}
+                        {{ loop.index0 }} => self.{{ c }} = value,
+                    {%- elif is_u32 %}
+                        {{ loop.index0 }} => self.{{ c }} = MASK[value as usize],
+                    {%- endif %}
+                {%- endfor %}
+                _ => panic!("index out of bounds")
+            }
+        {% elif is_coresimd %}
+            self.0.set(index, value)
+        {% else %}
+            use crate::{{ vec_t }};
+            let mut v = {{ vec_t }}(self.0);
+            v[index] = f32::from_bits(MASK[value as usize]);
+            *self = Self(v.0);
+        {% endif %}
+    }
+
     #[inline]
     fn into_bool_array(self) -> [bool; {{ dim }}] {
         {% if is_scalar and is_bool %}
diff --git a/src/bool/bvec2.rs b/src/bool/bvec2.rs
index e69fcfc..565b213 100644
--- a/src/bool/bvec2.rs
+++ b/src/bool/bvec2.rs
@@ -54,6 +54,30 @@ impl BVec2 {
         self.x && self.y
     }
 
+    /// Tests the value at `index`.
+    ///
+    /// Panics if `index` is greater than 1.
+    #[inline]
+    pub fn test(&self, index: usize) -> bool {
+        match index {
+            0 => self.x,
+            1 => self.y,
+            _ => panic!("index out of bounds"),
+        }
+    }
+
+    /// Sets the element at `index`.
+    ///
+    /// Panics if `index` is greater than 1.
+    #[inline]
+    pub fn set(&mut self, index: usize, value: bool) {
+        match index {
+            0 => self.x = value,
+            1 => self.y = value,
+            _ => panic!("index out of bounds"),
+        }
+    }
+
     #[inline]
     fn into_bool_array(self) -> [bool; 2] {
         [self.x, self.y]
diff --git a/src/bool/bvec3.rs b/src/bool/bvec3.rs
index 91331e2..18f3a4d 100644
--- a/src/bool/bvec3.rs
+++ b/src/bool/bvec3.rs
@@ -55,6 +55,32 @@ impl BVec3 {
         self.x && self.y && self.z
     }
 
+    /// Tests the value at `index`.
+    ///
+    /// Panics if `index` is greater than 2.
+    #[inline]
+    pub fn test(&self, index: usize) -> bool {
+        match index {
+            0 => self.x,
+            1 => self.y,
+            2 => self.z,
+            _ => panic!("index out of bounds"),
+        }
+    }
+
+    /// Sets the element at `index`.
+    ///
+    /// Panics if `index` is greater than 2.
+    #[inline]
+    pub fn set(&mut self, index: usize, value: bool) {
+        match index {
+            0 => self.x = value,
+            1 => self.y = value,
+            2 => self.z = value,
+            _ => panic!("index out of bounds"),
+        }
+    }
+
     #[inline]
     fn into_bool_array(self) -> [bool; 3] {
         [self.x, self.y, self.z]
diff --git a/src/bool/bvec4.rs b/src/bool/bvec4.rs
index 7754cd4..b76ff78 100644
--- a/src/bool/bvec4.rs
+++ b/src/bool/bvec4.rs
@@ -56,6 +56,34 @@ impl BVec4 {
         self.x && self.y && self.z && self.w
     }
 
+    /// Tests the value at `index`.
+    ///
+    /// Panics if `index` is greater than 3.
+    #[inline]
+    pub fn test(&self, index: usize) -> bool {
+        match index {
+            0 => self.x,
+            1 => self.y,
+            2 => self.z,
+            3 => self.w,
+            _ => panic!("index out of bounds"),
+        }
+    }
+
+    /// Sets the element at `index`.
+    ///
+    /// Panics if `index` is greater than 3.
+    #[inline]
+    pub fn set(&mut self, index: usize, value: bool) {
+        match index {
+            0 => self.x = value,
+            1 => self.y = value,
+            2 => self.z = value,
+            3 => self.w = value,
+            _ => panic!("index out of bounds"),
+        }
+    }
+
     #[inline]
     fn into_bool_array(self) -> [bool; 4] {
         [self.x, self.y, self.z, self.w]
diff --git a/src/bool/coresimd/bvec3a.rs b/src/bool/coresimd/bvec3a.rs
index b6ef6b0..0a6b06c 100644
--- a/src/bool/coresimd/bvec3a.rs
+++ b/src/bool/coresimd/bvec3a.rs
@@ -66,6 +66,22 @@ impl BVec3A {
         self.bitmask() == 0x7
     }
 
+    /// Tests the value at `index`.
+    ///
+    /// Panics if `index` is greater than 2.
+    #[inline]
+    pub fn test(&self, index: usize) -> bool {
+        self.0.test(index)
+    }
+
+    /// Sets the element at `index`.
+    ///
+    /// Panics if `index` is greater than 2.
+    #[inline]
+    pub fn set(&mut self, index: usize, value: bool) {
+        self.0.set(index, value)
+    }
+
     #[inline]
     fn into_bool_array(self) -> [bool; 3] {
         let bitmask = self.bitmask();
diff --git a/src/bool/coresimd/bvec4a.rs b/src/bool/coresimd/bvec4a.rs
index 983bdec..5631bd5 100644
--- a/src/bool/coresimd/bvec4a.rs
+++ b/src/bool/coresimd/bvec4a.rs
@@ -71,6 +71,22 @@ impl BVec4A {
         self.bitmask() == 0xf
     }
 
+    /// Tests the value at `index`.
+    ///
+    /// Panics if `index` is greater than 3.
+    #[inline]
+    pub fn test(&self, index: usize) -> bool {
+        self.0.test(index)
+    }
+
+    /// Sets the element at `index`.
+    ///
+    /// Panics if `index` is greater than 3.
+    #[inline]
+    pub fn set(&mut self, index: usize, value: bool) {
+        self.0.set(index, value)
+    }
+
     #[inline]
     fn into_bool_array(self) -> [bool; 4] {
         let bitmask = self.bitmask();
diff --git a/src/bool/scalar/bvec3a.rs b/src/bool/scalar/bvec3a.rs
index f35908c..d58e2d8 100644
--- a/src/bool/scalar/bvec3a.rs
+++ b/src/bool/scalar/bvec3a.rs
@@ -59,6 +59,32 @@ impl BVec3A {
         ((self.x & self.y & self.z) & 0x1) != 0
     }
 
+    /// Tests the value at `index`.
+    ///
+    /// Panics if `index` is greater than 2.
+    #[inline]
+    pub fn test(&self, index: usize) -> bool {
+        match index {
+            0 => (self.x & 0x1) != 0,
+            1 => (self.y & 0x1) != 0,
+            2 => (self.z & 0x1) != 0,
+            _ => panic!("index out of bounds"),
+        }
+    }
+
+    /// Sets the element at `index`.
+    ///
+    /// Panics if `index` is greater than 2.
+    #[inline]
+    pub fn set(&mut self, index: usize, value: bool) {
+        match index {
+            0 => self.x = MASK[value as usize],
+            1 => self.y = MASK[value as usize],
+            2 => self.z = MASK[value as usize],
+            _ => panic!("index out of bounds"),
+        }
+    }
+
     #[inline]
     fn into_bool_array(self) -> [bool; 3] {
         [
diff --git a/src/bool/scalar/bvec4a.rs b/src/bool/scalar/bvec4a.rs
index 28b0066..84153b1 100644
--- a/src/bool/scalar/bvec4a.rs
+++ b/src/bool/scalar/bvec4a.rs
@@ -61,6 +61,34 @@ impl BVec4A {
         ((self.x & self.y & self.z & self.w) & 0x1) != 0
     }
 
+    /// Tests the value at `index`.
+    ///
+    /// Panics if `index` is greater than 3.
+    #[inline]
+    pub fn test(&self, index: usize) -> bool {
+        match index {
+            0 => (self.x & 0x1) != 0,
+            1 => (self.y & 0x1) != 0,
+            2 => (self.z & 0x1) != 0,
+            3 => (self.w & 0x1) != 0,
+            _ => panic!("index out of bounds"),
+        }
+    }
+
+    /// Sets the element at `index`.
+    ///
+    /// Panics if `index` is greater than 3.
+    #[inline]
+    pub fn set(&mut self, index: usize, value: bool) {
+        match index {
+            0 => self.x = MASK[value as usize],
+            1 => self.y = MASK[value as usize],
+            2 => self.z = MASK[value as usize],
+            3 => self.w = MASK[value as usize],
+            _ => panic!("index out of bounds"),
+        }
+    }
+
     #[inline]
     fn into_bool_array(self) -> [bool; 4] {
         [
diff --git a/src/bool/sse2/bvec3a.rs b/src/bool/sse2/bvec3a.rs
index b9dfdce..e708bf6 100644
--- a/src/bool/sse2/bvec3a.rs
+++ b/src/bool/sse2/bvec3a.rs
@@ -69,6 +69,30 @@ impl BVec3A {
         self.bitmask() == 0x7
     }
 
+    /// Tests the value at `index`.
+    ///
+    /// Panics if `index` is greater than 2.
+    #[inline]
+    pub fn test(&self, index: usize) -> bool {
+        match index {
+            0 => (self.bitmask() & (1 << 0)) != 0,
+            1 => (self.bitmask() & (1 << 1)) != 0,
+            2 => (self.bitmask() & (1 << 2)) != 0,
+            _ => panic!("index out of bounds"),
+        }
+    }
+
+    /// Sets the element at `index`.
+    ///
+    /// Panics if `index` is greater than 2.
+    #[inline]
+    pub fn set(&mut self, index: usize, value: bool) {
+        use crate::Vec3A;
+        let mut v = Vec3A(self.0);
+        v[index] = f32::from_bits(MASK[value as usize]);
+        *self = Self(v.0);
+    }
+
     #[inline]
     fn into_bool_array(self) -> [bool; 3] {
         let bitmask = self.bitmask();
diff --git a/src/bool/sse2/bvec4a.rs b/src/bool/sse2/bvec4a.rs
index 6f2e163..ad9b6c9 100644
--- a/src/bool/sse2/bvec4a.rs
+++ b/src/bool/sse2/bvec4a.rs
@@ -74,6 +74,31 @@ impl BVec4A {
         self.bitmask() == 0xf
     }
 
+    /// Tests the value at `index`.
+    ///
+    /// Panics if `index` is greater than 3.
+    #[inline]
+    pub fn test(&self, index: usize) -> bool {
+        match index {
+            0 => (self.bitmask() & (1 << 0)) != 0,
+            1 => (self.bitmask() & (1 << 1)) != 0,
+            2 => (self.bitmask() & (1 << 2)) != 0,
+            3 => (self.bitmask() & (1 << 3)) != 0,
+            _ => panic!("index out of bounds"),
+        }
+    }
+
+    /// Sets the element at `index`.
+    ///
+    /// Panics if `index` is greater than 3.
+    #[inline]
+    pub fn set(&mut self, index: usize, value: bool) {
+        use crate::Vec4;
+        let mut v = Vec4(self.0);
+        v[index] = f32::from_bits(MASK[value as usize]);
+        *self = Self(v.0);
+    }
+
     #[inline]
     fn into_bool_array(self) -> [bool; 4] {
         let bitmask = self.bitmask();
diff --git a/src/bool/wasm32/bvec3a.rs b/src/bool/wasm32/bvec3a.rs
index 7f9faa5..6a7ddd5 100644
--- a/src/bool/wasm32/bvec3a.rs
+++ b/src/bool/wasm32/bvec3a.rs
@@ -60,6 +60,30 @@ impl BVec3A {
         self.bitmask() == 0x7
     }
 
+    /// Tests the value at `index`.
+    ///
+    /// Panics if `index` is greater than 2.
+    #[inline]
+    pub fn test(&self, index: usize) -> bool {
+        match index {
+            0 => (self.bitmask() & (1 << 0)) != 0,
+            1 => (self.bitmask() & (1 << 1)) != 0,
+            2 => (self.bitmask() & (1 << 2)) != 0,
+            _ => panic!("index out of bounds"),
+        }
+    }
+
+    /// Sets the element at `index`.
+    ///
+    /// Panics if `index` is greater than 2.
+    #[inline]
+    pub fn set(&mut self, index: usize, value: bool) {
+        use crate::Vec3A;
+        let mut v = Vec3A(self.0);
+        v[index] = f32::from_bits(MASK[value as usize]);
+        *self = Self(v.0);
+    }
+
     #[inline]
     fn into_bool_array(self) -> [bool; 3] {
         let bitmask = self.bitmask();
diff --git a/src/bool/wasm32/bvec4a.rs b/src/bool/wasm32/bvec4a.rs
index 838d146..4e8596b 100644
--- a/src/bool/wasm32/bvec4a.rs
+++ b/src/bool/wasm32/bvec4a.rs
@@ -60,6 +60,31 @@ impl BVec4A {
         self.bitmask() == 0xf
     }
 
+    /// Tests the value at `index`.
+    ///
+    /// Panics if `index` is greater than 3.
+    #[inline]
+    pub fn test(&self, index: usize) -> bool {
+        match index {
+            0 => (self.bitmask() & (1 << 0)) != 0,
+            1 => (self.bitmask() & (1 << 1)) != 0,
+            2 => (self.bitmask() & (1 << 2)) != 0,
+            3 => (self.bitmask() & (1 << 3)) != 0,
+            _ => panic!("index out of bounds"),
+        }
+    }
+
+    /// Sets the element at `index`.
+    ///
+    /// Panics if `index` is greater than 3.
+    #[inline]
+    pub fn set(&mut self, index: usize, value: bool) {
+        use crate::Vec4;
+        let mut v = Vec4(self.0);
+        v[index] = f32::from_bits(MASK[value as usize]);
+        *self = Self(v.0);
+    }
+
     #[inline]
     fn into_bool_array(self) -> [bool; 4] {
         let bitmask = self.bitmask();
diff --git a/src/f32/coresimd/vec3a.rs b/src/f32/coresimd/vec3a.rs
index 21c9466..8ba4966 100644
--- a/src/f32/coresimd/vec3a.rs
+++ b/src/f32/coresimd/vec3a.rs
@@ -162,7 +162,7 @@ impl Vec3A {
     /// Returns a vector where every component is the dot product of `self` and `rhs`.
     #[inline]
     pub fn dot_into_vec(self, rhs: Self) -> Self {
-        Self(unsafe { dot3_into_f32x4(self.0, rhs.0) })
+        Self(dot3_into_f32x4(self.0, rhs.0))
     }
 
     /// Computes the cross product of `self` and `rhs`.
diff --git a/src/f32/coresimd/vec4.rs b/src/f32/coresimd/vec4.rs
index 79435ac..8d0c4c6 100644
--- a/src/f32/coresimd/vec4.rs
+++ b/src/f32/coresimd/vec4.rs
@@ -154,7 +154,7 @@ impl Vec4 {
     /// Returns a vector where every component is the dot product of `self` and `rhs`.
     #[inline]
     pub fn dot_into_vec(self, rhs: Self) -> Self {
-        Self(unsafe { dot4_into_f32x4(self.0, rhs.0) })
+        Self(dot4_into_f32x4(self.0, rhs.0))
     }
 
     /// Returns a vector containing the minimum values for each element of `self` and `rhs`.
diff --git a/src/f32/math.rs b/src/f32/math.rs
index 36b891a..5ddf4f4 100644
--- a/src/f32/math.rs
+++ b/src/f32/math.rs
@@ -36,13 +36,7 @@ fn acos_approx_f32(v: f32) -> f32 {
 mod libm_math {
     #[inline(always)]
     pub(crate) fn abs(f: f32) -> f32 {
-        if f.is_sign_positive() {
-            return f;
-        }
-        if f.is_sign_negative() {
-            return -f;
-        }
-        f32::NAN
+        libm::fabsf(f)
     }
 
     #[inline(always)]
diff --git a/src/f64/math.rs b/src/f64/math.rs
index 3454342..90f0064 100644
--- a/src/f64/math.rs
+++ b/src/f64/math.rs
@@ -2,13 +2,7 @@
 mod libm_math {
     #[inline(always)]
     pub(crate) fn abs(f: f64) -> f64 {
-        if f.is_sign_positive() {
-            return f;
-        }
-        if f.is_sign_negative() {
-            return -f;
-        }
-        f64::NAN
+        libm::fabs(f)
     }
 
     #[inline(always)]


Test patch:
diff --git a/tests/vec2.rs b/tests/vec2.rs
index 195995a..95982fb 100644
--- a/tests/vec2.rs
+++ b/tests/vec2.rs
@@ -425,6 +425,30 @@ macro_rules! impl_vec2_tests {
             assert_ne!(b, c);
         });
 
+        glam_test!(test_mask_test, {
+            let a = $mask::new(true, false);
+            assert_eq!(a.test(0), true);
+            assert_eq!(a.test(1), false);
+
+            let b = $mask::new(false, true);
+            assert_eq!(b.test(0), false);
+            assert_eq!(b.test(1), true);
+        });
+
+        glam_test!(test_mask_set, {
+            let mut a = $mask::new(false, true);
+            a.set(0, true);
+            assert_eq!(a.test(0), true);
+            a.set(1, false);
+            assert_eq!(a.test(1), false);
+
+            let mut b = $mask::new(true, false);
+            b.set(0, false);
+            assert_eq!(b.test(0), false);
+            b.set(1, true);
+            assert_eq!(b.test(1), true);
+        });
+
         glam_test!(test_mask_hash, {
             use std::collections::hash_map::DefaultHasher;
             use std::hash::Hash;
diff --git a/tests/vec3.rs b/tests/vec3.rs
index f8faf1d..0c149f6 100644
--- a/tests/vec3.rs
+++ b/tests/vec3.rs
@@ -476,6 +476,36 @@ macro_rules! impl_vec3_tests {
             assert_ne!(b, c);
         });
 
+        glam_test!(test_mask_test, {
+            let a = $mask::new(true, false, true);
+            assert_eq!(a.test(0), true);
+            assert_eq!(a.test(1), false);
+            assert_eq!(a.test(2), true);
+
+            let b = $mask::new(false, true, false);
+            assert_eq!(b.test(0), false);
+            assert_eq!(b.test(1), true);
+            assert_eq!(b.test(2), false);
+        });
+
+        glam_test!(test_mask_set, {
+            let mut a = $mask::new(false, true, false);
+            a.set(0, true);
+            assert_eq!(a.test(0), true);
+            a.set(1, false);
+            assert_eq!(a.test(1), false);
+            a.set(2, true);
+            assert_eq!(a.test(2), true);
+
+            let mut b = $mask::new(true, false, true);
+            b.set(0, false);
+            assert_eq!(b.test(0), false);
+            b.set(1, true);
+            assert_eq!(b.test(1), true);
+            b.set(2, false);
+            assert_eq!(b.test(2), false);
+        });
+
         glam_test!(test_mask_hash, {
             use std::collections::hash_map::DefaultHasher;
             use std::hash::Hash;
diff --git a/tests/vec4.rs b/tests/vec4.rs
index e414752..d8a2c1e 100644
--- a/tests/vec4.rs
+++ b/tests/vec4.rs
@@ -561,6 +561,42 @@ macro_rules! impl_vec4_tests {
             assert_ne!(b, c);
         });
 
+        glam_test!(test_mask_test, {
+            let a = $mask::new(true, false, true, false);
+            assert_eq!(a.test(0), true);
+            assert_eq!(a.test(1), false);
+            assert_eq!(a.test(2), true);
+            assert_eq!(a.test(3), false);
+
+            let b = $mask::new(false, true, false, true);
+            assert_eq!(b.test(0), false);
+            assert_eq!(b.test(1), true);
+            assert_eq!(b.test(2), false);
+            assert_eq!(b.test(3), true);
+        });
+
+        glam_test!(test_mask_set, {
+            let mut a = $mask::new(false, true, false, true);
+            a.set(0, true);
+            assert_eq!(a.test(0), true);
+            a.set(1, false);
+            assert_eq!(a.test(1), false);
+            a.set(2, true);
+            assert_eq!(a.test(2), true);
+            a.set(3, false);
+            assert_eq!(a.test(3), false);
+
+            let mut b = $mask::new(true, false, true, false);
+            b.set(0, false);
+            assert_eq!(b.test(0), false);
+            b.set(1, true);
+            assert_eq!(b.test(1), true);
+            b.set(2, false);
+            assert_eq!(b.test(2), false);
+            b.set(3, true);
+            assert_eq!(b.test(3), true);
+        });
+
         glam_test!(test_mask_hash, {
             use std::collections::hash_map::DefaultHasher;
             use std::hash::Hash;