Repository: bitshifter/glam-rs
Pull request #343 (2022-09-29 01:57:13)

Add more const fn's
Because I'm bored-ish and this is kinda fun.

FYI I'm now testing with `cargo run --release -p codegen -- -f && ./build_and_test_features.sh && ./build_and_test_wasm32_chrome.sh` so I'm not testing coresimd (famous last words)

Wondering if this kind of thing is wanted or if the extra complexity isn't welcome :thinking: 


Patch:
diff --git a/Cargo.toml b/Cargo.toml
index 1240ad5..eaf4bc7 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -50,7 +50,7 @@ bytemuck = { version = "1.5", optional = true, default-features = false }
 mint = { version = "0.5.8", optional = true, default-features = false }
 num-traits = { version = "0.2.14", optional = true, default-features = false }
 rand = { version = "0.8", optional = true, default-features = false }
-serde = { version = "1.0", optional = true, features = ["derive"] }
+serde = { version = "1.0", optional = true, default-features = false, features = ["derive"] }
 rkyv = { version = "0.7", optional = true }
 bytecheck = { version = "0.6", optional = true, default-features = false}
 
diff --git a/codegen/templates/mat.rs.tera b/codegen/templates/mat.rs.tera
index 4b36697..2fb7856 100644
--- a/codegen/templates/mat.rs.tera
+++ b/codegen/templates/mat.rs.tera
@@ -302,14 +302,28 @@ impl {{ self_t }} {
     /// Creates a `[{{ scalar_t }}; {{ size }}]` array storing data in column major order.
     /// If you require data in row major order `transpose` the matrix first.
     #[inline]
-    pub fn to_cols_array(&self) -> [{{ scalar_t }}; {{ size }}] {
-        [
-            {% for axis in axes %}
-                {% for c in components %}
-                    self.{{ axis }}.{{ c }},
+    pub const fn to_cols_array(&self) -> [{{ scalar_t }}; {{ size }}] {
+        {% if self_t == "Mat2" and not is_scalar %}
+            unsafe { *(self as *const Self as *const [f32; 4]) }
+        {% else %}
+            {% if dim >= 2 and not is_scalar %}
+                {% for axis in axes %}
+                    let [{% for c in components %} {{ axis }}_{{ c }}, {% endfor %}] = self.{{ axis }}.to_array();
                 {%- endfor %}
-            {%- endfor %}
-        ]
+            {% endif %}
+
+            [
+                {% for axis in axes %}
+                    {% for c in components %}
+                        {% if dim >= 2 and not is_scalar %}
+                            {{ axis }}_{{ c }},
+                        {% else %}
+                            self.{{ axis }}.{{ c }},
+                        {% endif %}
+                    {%- endfor %}
+                {%- endfor %}
+            ]
+        {% endif %}
     }
 
     /// Creates a {{ nxn }} matrix from a `[[{{ scalar_t }}; {{ dim }}]; {{ dim }}]` {{ dim }}D array stored in column major order.
@@ -327,50 +341,50 @@ impl {{ self_t }} {
     /// Creates a `[[{{ scalar_t }}; {{ dim }}]; {{ dim }}]` {{ dim }}D array storing data in column major order.
     /// If you require data in row major order `transpose` the matrix first.
     #[inline]
-    pub fn to_cols_array_2d(&self) -> [[{{ scalar_t }}; {{ dim }}]; {{ dim }}] {
-        [
-            {% for axis in axes %}
+    pub const fn to_cols_array_2d(&self) -> [[{{ scalar_t }}; {{ dim }}]; {{ dim }}] {
+        {% if self_t == "Mat2" and not is_scalar %}
+            unsafe { *(self as *const Self as *const [[f32; 2]; 2]) }
+        {% else %}
+            [
+                {% for axis in axes %}
                 self.{{ axis }}.to_array(),
-            {%- endfor %}
-        ]
-    }
-{% if dim == 4 and vecn_t != "DVec4" %}
-    /// Creates a {{ nxn }} matrix with its diagonal set to `diagonal` and all other entries set to 0.
-    #[doc(alias = "scale")]
-    #[inline]
-    pub const fn from_diagonal(diagonal: {{ vecn_t }}) -> Self {
-        // diagonal.x, diagonal.y etc can't be done in a const-context
-        let [x, y, z, w] = diagonal.to_array();
-        Self::new(
-            {% for i in range(end = dim) %}
-                {% for j in range(end = dim) %}
-                    {% if i == j %}
-                        {{ components[i] }},
-                    {% else %}
-                        0.0,
-                    {% endif %}
                 {%- endfor %}
-            {%- endfor %}
-        )
+            ]
+        {% endif %}
     }
-{% else %}
+
     /// Creates a {{ nxn }} matrix with its diagonal set to `diagonal` and all other entries set to 0.
     #[doc(alias = "scale")]
     #[inline]
     pub const fn from_diagonal(diagonal: {{ vecn_t }}) -> Self {
-        Self::new(
-            {% for i in range(end = dim) %}
-                {% for j in range(end = dim) %}
-                    {% if i == j %}
-                        diagonal.{{ components[i] }},
-                    {% else %}
-                        0.0,
-                    {% endif %}
+        {% if self_t == "Mat4" and not is_scalar %}
+            // diagonal.x, diagonal.y etc can't be done in a const-context
+            let [x, y, z, w] = diagonal.to_array();
+            Self::new(
+                {% for i in range(end = dim) %}
+                    {% for j in range(end = dim) %}
+                        {% if i == j %}
+                            {{ components[i] }},
+                        {% else %}
+                            0.0,
+                        {% endif %}
+                    {%- endfor %}
                 {%- endfor %}
-            {%- endfor %}
-        )
+            )
+        {% else %}
+            Self::new(
+                {% for i in range(end = dim) %}
+                    {% for j in range(end = dim) %}
+                        {% if i == j %}
+                            diagonal.{{ components[i] }},
+                        {% else %}
+                            0.0,
+                        {% endif %}
+                    {%- endfor %}
+                {%- endfor %}
+            )
+        {% endif %}
     }
-{% endif %}
 
 {% if dim == 2 %}
     /// Creates a {{ nxn }} matrix containing the combining non-uniform `scale` and rotation of
diff --git a/codegen/templates/vec.rs.tera b/codegen/templates/vec.rs.tera
index a8ca85b..425262e 100644
--- a/codegen/templates/vec.rs.tera
+++ b/codegen/templates/vec.rs.tera
@@ -430,6 +430,20 @@ impl {{ self_t }} {
         {% endif %}
     }
 
+    /// Returns a vector where every component is the dot product of `self` and `rhs` 
+    #[inline]
+    pub fn dot_into_vec(self, rhs: Self) -> Self {
+        {% if is_sse2 %}
+            Self(unsafe { dot{{ dim }}_into_m128(self.0, rhs.0) })
+        {% 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) })
+        {% else %}
+            Self::splat(self.dot(rhs))
+        {% endif %}
+    }
+
 {% if dim == 3 %}
     /// Computes the cross product of `self` and `rhs`.
     #[inline]
@@ -797,11 +811,61 @@ impl {{ self_t }} {
             }
         {% elif is_coresimd %}
             Self(self.0.signum())
-        {% else %}
-            let mask = self.cmpge(Self::ZERO);
-            let result = Self::select(mask, Self::ONE, Self::NEG_ONE);
-            let mask = self.is_nan_mask();
-            Self::select(mask, self, result)
+        {% elif is_sse2 %}
+            unsafe {
+                let result = Self(_mm_or_ps(_mm_and_ps(self.0, Self::NEG_ONE.0), Self::ONE.0));
+                let mask = self.is_nan_mask();
+                Self::select(mask, self, result)
+            }
+        {% elif is_wasm32 %}
+            unsafe {
+                let result = Self(v128_or(v128_and(self.0, Self::NEG_ONE.0), Self::ONE.0));
+                let mask = self.is_nan_mask();
+                Self::select(mask, self, result)
+            }
+        {% endif %}
+    }
+
+    /// Returns a bitmask with the lowest {{ dim }} bits set to the sign bits from the elements of `self`.
+    ///
+    /// A negative element results in a `1` bit and a positive element in a `0` bit.  Element `x` goes
+    /// into the first lowest bit, element `y` into the second, etc.
+    #[inline]
+    pub fn is_negative_bitmask(self) -> u32 {
+        {% if is_scalar and is_float %}
+            {% for c in components %}
+                {% if loop.first %}
+                    (self.{{ c }}.is_sign_negative() as u32) |
+                {% else %}
+                    (self.{{ c }}.is_sign_negative() as u32) << {{ loop.index0 }} {% if not loop.last %} | {% endif %}
+                {% endif %}
+            {% endfor %}
+        {% elif is_scalar %}
+            {% for c in components %}
+                {% if loop.first %}
+                    (self.{{ c }}.is_negative() as u32) |
+                {% else %}
+                    (self.{{ c }}.is_negative() as u32) << {{ loop.index0 }} {% if not loop.last %} | {% endif %}
+                {% endif %}
+            {% endfor %}
+        {% elif is_sse2 %}
+            {% if dim == 3 %}
+                unsafe { (_mm_movemask_ps(self.0) as u32) & 0x7 }
+            {% elif dim == 4 %}
+                unsafe { _mm_movemask_ps(self.0) as u32 }
+            {% endif %}
+        {% elif is_wasm32 %}
+            {% if dim == 3 %}
+                (u32x4_bitmask(self.0) & 0x7) as u32
+            {% elif dim == 4 %}
+                u32x4_bitmask(self.0) as u32
+            {% endif %}
+        {% elif is_coresimd %}
+            {% if dim == 3 %}
+                (self.0.is_sign_negative().to_bitmask() & 0x7) as u32
+            {% elif dim == 4 %}
+                self.0.is_sign_negative().to_bitmask() as u32
+            {% endif %}
         {% endif %}
     }
 {% endif %}
@@ -2021,7 +2085,7 @@ impl Neg for {{ self_t }} {
                 {%- endfor %}
             }
         {% elif is_sse2 %}
-            Self(unsafe { _mm_sub_ps(Self::ZERO.0, self.0) })
+            Self(unsafe { _mm_xor_ps(_mm_set1_ps(-0.0), self.0) })
         {% elif is_wasm32 %}
             Self(f32x4_neg(self.0))
         {% elif is_coresimd %}
diff --git a/codegen/templates/vec_mask.rs.tera b/codegen/templates/vec_mask.rs.tera
index cca5010..c89d325 100644
--- a/codegen/templates/vec_mask.rs.tera
+++ b/codegen/templates/vec_mask.rs.tera
@@ -71,13 +71,12 @@ pub struct {{ self_t }}(pub(crate) {{ simd_t }});
 
 const MASK: [u32; 2] = [0, 0xff_ff_ff_ff];
 
-const FALSE: {{ self_t }} = {{ self_t }}::new(
-    {% for c in components %}
-        false,
-    {%- endfor %}
-);
-
 impl {{ self_t }} {
+    /// All false.
+    pub const FALSE: Self = Self::splat(false);
+
+    /// All true.
+    pub const TRUE: Self = Self::splat(true);
 
     /// Creates a new vector mask.
     #[inline(always)]
@@ -125,7 +124,17 @@ impl {{ self_t }} {
         {% endif %}
     }
 
-    /// Returns a bitmask with the lowest two bits set from the elements of `self`.
+    /// Creates a vector with all elements set to `v`.
+    #[inline]
+    pub const fn splat(v: bool) -> Self {
+        Self::new(
+            {% for c in components %}
+                v,
+            {%- endfor %}
+        )
+    }
+
+    /// Returns a bitmask with the lowest {{ dim }} bits set from the elements of `self`.
     ///
     /// A true element results in a `1` bit and a false element in a `0` bit.  Element `x` goes
     /// into the first lowest bit, element `y` into the second, etc.
@@ -266,7 +275,7 @@ impl {{ self_t }} {
 impl Default for {{ self_t }} {
     #[inline]
     fn default() -> Self {
-        FALSE
+        Self::FALSE
     }
 }
 
diff --git a/src/bool/bvec2.rs b/src/bool/bvec2.rs
index 073dd12..e69fcfc 100644
--- a/src/bool/bvec2.rs
+++ b/src/bool/bvec2.rs
@@ -14,16 +14,26 @@ pub struct BVec2 {
 
 const MASK: [u32; 2] = [0, 0xff_ff_ff_ff];
 
-const FALSE: BVec2 = BVec2::new(false, false);
-
 impl BVec2 {
+    /// All false.
+    pub const FALSE: Self = Self::splat(false);
+
+    /// All true.
+    pub const TRUE: Self = Self::splat(true);
+
     /// Creates a new vector mask.
     #[inline(always)]
     pub const fn new(x: bool, y: bool) -> Self {
         Self { x, y }
     }
 
-    /// Returns a bitmask with the lowest two bits set from the elements of `self`.
+    /// Creates a vector with all elements set to `v`.
+    #[inline]
+    pub const fn splat(v: bool) -> Self {
+        Self::new(v, v)
+    }
+
+    /// Returns a bitmask with the lowest 2 bits set from the elements of `self`.
     ///
     /// A true element results in a `1` bit and a false element in a `0` bit.  Element `x` goes
     /// into the first lowest bit, element `y` into the second, etc.
@@ -58,7 +68,7 @@ impl BVec2 {
 impl Default for BVec2 {
     #[inline]
     fn default() -> Self {
-        FALSE
+        Self::FALSE
     }
 }
 
diff --git a/src/bool/bvec3.rs b/src/bool/bvec3.rs
index b97ed58..91331e2 100644
--- a/src/bool/bvec3.rs
+++ b/src/bool/bvec3.rs
@@ -15,16 +15,26 @@ pub struct BVec3 {
 
 const MASK: [u32; 2] = [0, 0xff_ff_ff_ff];
 
-const FALSE: BVec3 = BVec3::new(false, false, false);
-
 impl BVec3 {
+    /// All false.
+    pub const FALSE: Self = Self::splat(false);
+
+    /// All true.
+    pub const TRUE: Self = Self::splat(true);
+
     /// Creates a new vector mask.
     #[inline(always)]
     pub const fn new(x: bool, y: bool, z: bool) -> Self {
         Self { x, y, z }
     }
 
-    /// Returns a bitmask with the lowest two bits set from the elements of `self`.
+    /// Creates a vector with all elements set to `v`.
+    #[inline]
+    pub const fn splat(v: bool) -> Self {
+        Self::new(v, v, v)
+    }
+
+    /// Returns a bitmask with the lowest 3 bits set from the elements of `self`.
     ///
     /// A true element results in a `1` bit and a false element in a `0` bit.  Element `x` goes
     /// into the first lowest bit, element `y` into the second, etc.
@@ -63,7 +73,7 @@ impl BVec3 {
 impl Default for BVec3 {
     #[inline]
     fn default() -> Self {
-        FALSE
+        Self::FALSE
     }
 }
 
diff --git a/src/bool/bvec4.rs b/src/bool/bvec4.rs
index f4d366b..7754cd4 100644
--- a/src/bool/bvec4.rs
+++ b/src/bool/bvec4.rs
@@ -16,16 +16,26 @@ pub struct BVec4 {
 
 const MASK: [u32; 2] = [0, 0xff_ff_ff_ff];
 
-const FALSE: BVec4 = BVec4::new(false, false, false, false);
-
 impl BVec4 {
+    /// All false.
+    pub const FALSE: Self = Self::splat(false);
+
+    /// All true.
+    pub const TRUE: Self = Self::splat(true);
+
     /// Creates a new vector mask.
     #[inline(always)]
     pub const fn new(x: bool, y: bool, z: bool, w: bool) -> Self {
         Self { x, y, z, w }
     }
 
-    /// Returns a bitmask with the lowest two bits set from the elements of `self`.
+    /// Creates a vector with all elements set to `v`.
+    #[inline]
+    pub const fn splat(v: bool) -> Self {
+        Self::new(v, v, v, v)
+    }
+
+    /// Returns a bitmask with the lowest 4 bits set from the elements of `self`.
     ///
     /// A true element results in a `1` bit and a false element in a `0` bit.  Element `x` goes
     /// into the first lowest bit, element `y` into the second, etc.
@@ -65,7 +75,7 @@ impl BVec4 {
 impl Default for BVec4 {
     #[inline]
     fn default() -> Self {
-        FALSE
+        Self::FALSE
     }
 }
 
diff --git a/src/bool/coresimd/bvec3a.rs b/src/bool/coresimd/bvec3a.rs
index ca592c5..79f3a57 100644
--- a/src/bool/coresimd/bvec3a.rs
+++ b/src/bool/coresimd/bvec3a.rs
@@ -21,9 +21,13 @@ pub struct BVec3A(pub(crate) mask32x4);
 
 const MASK: [u32; 2] = [0, 0xff_ff_ff_ff];
 
-const FALSE: BVec3A = BVec3A::new(false, false, false);
-
 impl BVec3A {
+    /// All false.
+    pub const FALSE: Self = Self::splat(false);
+
+    /// All true.
+    pub const TRUE: Self = Self::splat(true);
+
     /// Creates a new vector mask.
     #[inline(always)]
     pub const fn new(x: bool, y: bool, z: bool) -> Self {
@@ -35,7 +39,13 @@ impl BVec3A {
         }
     }
 
-    /// Returns a bitmask with the lowest two bits set from the elements of `self`.
+    /// Creates a vector with all elements set to `v`.
+    #[inline]
+    pub const fn splat(v: bool) -> Self {
+        Self::new(v, v, v)
+    }
+
+    /// Returns a bitmask with the lowest 3 bits set from the elements of `self`.
     ///
     /// A true element results in a `1` bit and a false element in a `0` bit.  Element `x` goes
     /// into the first lowest bit, element `y` into the second, etc.
@@ -76,7 +86,7 @@ impl BVec3A {
 impl Default for BVec3A {
     #[inline]
     fn default() -> Self {
-        FALSE
+        Self::FALSE
     }
 }
 
diff --git a/src/bool/coresimd/bvec4a.rs b/src/bool/coresimd/bvec4a.rs
index 0e70217..381ee0b 100644
--- a/src/bool/coresimd/bvec4a.rs
+++ b/src/bool/coresimd/bvec4a.rs
@@ -21,9 +21,13 @@ pub struct BVec4A(pub(crate) mask32x4);
 
 const MASK: [u32; 2] = [0, 0xff_ff_ff_ff];
 
-const FALSE: BVec4A = BVec4A::new(false, false, false, false);
-
 impl BVec4A {
+    /// All false.
+    pub const FALSE: Self = Self::splat(false);
+
+    /// All true.
+    pub const TRUE: Self = Self::splat(true);
+
     /// Creates a new vector mask.
     #[inline(always)]
     pub const fn new(x: bool, y: bool, z: bool, w: bool) -> Self {
@@ -40,7 +44,13 @@ impl BVec4A {
         }
     }
 
-    /// Returns a bitmask with the lowest two bits set from the elements of `self`.
+    /// Creates a vector with all elements set to `v`.
+    #[inline]
+    pub const fn splat(v: bool) -> Self {
+        Self::new(v, v, v, v)
+    }
+
+    /// Returns a bitmask with the lowest 4 bits set from the elements of `self`.
     ///
     /// A true element results in a `1` bit and a false element in a `0` bit.  Element `x` goes
     /// into the first lowest bit, element `y` into the second, etc.
@@ -87,7 +97,7 @@ impl BVec4A {
 impl Default for BVec4A {
     #[inline]
     fn default() -> Self {
-        FALSE
+        Self::FALSE
     }
 }
 
diff --git a/src/bool/scalar/bvec3a.rs b/src/bool/scalar/bvec3a.rs
index 307e4c1..dbae395 100644
--- a/src/bool/scalar/bvec3a.rs
+++ b/src/bool/scalar/bvec3a.rs
@@ -15,9 +15,13 @@ pub struct BVec3A {
 
 const MASK: [u32; 2] = [0, 0xff_ff_ff_ff];
 
-const FALSE: BVec3A = BVec3A::new(false, false, false);
-
 impl BVec3A {
+    /// All false.
+    pub const FALSE: Self = Self::splat(false);
+
+    /// All true.
+    pub const TRUE: Self = Self::splat(true);
+
     /// Creates a new vector mask.
     #[inline(always)]
     pub const fn new(x: bool, y: bool, z: bool) -> Self {
@@ -28,7 +32,13 @@ impl BVec3A {
         }
     }
 
-    /// Returns a bitmask with the lowest two bits set from the elements of `self`.
+    /// Creates a vector with all elements set to `v`.
+    #[inline]
+    pub const fn splat(v: bool) -> Self {
+        Self::new(v, v, v)
+    }
+
+    /// Returns a bitmask with the lowest 3 bits set from the elements of `self`.
     ///
     /// A true element results in a `1` bit and a false element in a `0` bit.  Element `x` goes
     /// into the first lowest bit, element `y` into the second, etc.
@@ -67,7 +77,7 @@ impl BVec3A {
 impl Default for BVec3A {
     #[inline]
     fn default() -> Self {
-        FALSE
+        Self::FALSE
     }
 }
 
diff --git a/src/bool/scalar/bvec4a.rs b/src/bool/scalar/bvec4a.rs
index 0e133d1..c12d127 100644
--- a/src/bool/scalar/bvec4a.rs
+++ b/src/bool/scalar/bvec4a.rs
@@ -16,9 +16,13 @@ pub struct BVec4A {
 
 const MASK: [u32; 2] = [0, 0xff_ff_ff_ff];
 
-const FALSE: BVec4A = BVec4A::new(false, false, false, false);
-
 impl BVec4A {
+    /// All false.
+    pub const FALSE: Self = Self::splat(false);
+
+    /// All true.
+    pub const TRUE: Self = Self::splat(true);
+
     /// Creates a new vector mask.
     #[inline(always)]
     pub const fn new(x: bool, y: bool, z: bool, w: bool) -> Self {
@@ -30,7 +34,13 @@ impl BVec4A {
         }
     }
 
-    /// Returns a bitmask with the lowest two bits set from the elements of `self`.
+    /// Creates a vector with all elements set to `v`.
+    #[inline]
+    pub const fn splat(v: bool) -> Self {
+        Self::new(v, v, v, v)
+    }
+
+    /// Returns a bitmask with the lowest 4 bits set from the elements of `self`.
     ///
     /// A true element results in a `1` bit and a false element in a `0` bit.  Element `x` goes
     /// into the first lowest bit, element `y` into the second, etc.
@@ -70,7 +80,7 @@ impl BVec4A {
 impl Default for BVec4A {
     #[inline]
     fn default() -> Self {
-        FALSE
+        Self::FALSE
     }
 }
 
diff --git a/src/bool/sse2/bvec3a.rs b/src/bool/sse2/bvec3a.rs
index ed061e7..0fdbdf6 100644
--- a/src/bool/sse2/bvec3a.rs
+++ b/src/bool/sse2/bvec3a.rs
@@ -24,9 +24,13 @@ pub struct BVec3A(pub(crate) __m128);
 
 const MASK: [u32; 2] = [0, 0xff_ff_ff_ff];
 
-const FALSE: BVec3A = BVec3A::new(false, false, false);
-
 impl BVec3A {
+    /// All false.
+    pub const FALSE: Self = Self::splat(false);
+
+    /// All true.
+    pub const TRUE: Self = Self::splat(true);
+
     /// Creates a new vector mask.
     #[inline(always)]
     pub const fn new(x: bool, y: bool, z: bool) -> Self {
@@ -38,7 +42,13 @@ impl BVec3A {
         }
     }
 
-    /// Returns a bitmask with the lowest two bits set from the elements of `self`.
+    /// Creates a vector with all elements set to `v`.
+    #[inline]
+    pub const fn splat(v: bool) -> Self {
+        Self::new(v, v, v)
+    }
+
+    /// Returns a bitmask with the lowest 3 bits set from the elements of `self`.
     ///
     /// A true element results in a `1` bit and a false element in a `0` bit.  Element `x` goes
     /// into the first lowest bit, element `y` into the second, etc.
@@ -79,7 +89,7 @@ impl BVec3A {
 impl Default for BVec3A {
     #[inline]
     fn default() -> Self {
-        FALSE
+        Self::FALSE
     }
 }
 
diff --git a/src/bool/sse2/bvec4a.rs b/src/bool/sse2/bvec4a.rs
index 73e27d5..fc9d08c 100644
--- a/src/bool/sse2/bvec4a.rs
+++ b/src/bool/sse2/bvec4a.rs
@@ -24,9 +24,13 @@ pub struct BVec4A(pub(crate) __m128);
 
 const MASK: [u32; 2] = [0, 0xff_ff_ff_ff];
 
-const FALSE: BVec4A = BVec4A::new(false, false, false, false);
-
 impl BVec4A {
+    /// All false.
+    pub const FALSE: Self = Self::splat(false);
+
+    /// All true.
+    pub const TRUE: Self = Self::splat(true);
+
     /// Creates a new vector mask.
     #[inline(always)]
     pub const fn new(x: bool, y: bool, z: bool, w: bool) -> Self {
@@ -43,7 +47,13 @@ impl BVec4A {
         }
     }
 
-    /// Returns a bitmask with the lowest two bits set from the elements of `self`.
+    /// Creates a vector with all elements set to `v`.
+    #[inline]
+    pub const fn splat(v: bool) -> Self {
+        Self::new(v, v, v, v)
+    }
+
+    /// Returns a bitmask with the lowest 4 bits set from the elements of `self`.
     ///
     /// A true element results in a `1` bit and a false element in a `0` bit.  Element `x` goes
     /// into the first lowest bit, element `y` into the second, etc.
@@ -90,7 +100,7 @@ impl BVec4A {
 impl Default for BVec4A {
     #[inline]
     fn default() -> Self {
-        FALSE
+        Self::FALSE
     }
 }
 
diff --git a/src/bool/wasm32/bvec3a.rs b/src/bool/wasm32/bvec3a.rs
index 9d343f2..755246a 100644
--- a/src/bool/wasm32/bvec3a.rs
+++ b/src/bool/wasm32/bvec3a.rs
@@ -16,9 +16,13 @@ pub struct BVec3A(pub(crate) v128);
 
 const MASK: [u32; 2] = [0, 0xff_ff_ff_ff];
 
-const FALSE: BVec3A = BVec3A::new(false, false, false);
-
 impl BVec3A {
+    /// All false.
+    pub const FALSE: Self = Self::splat(false);
+
+    /// All true.
+    pub const TRUE: Self = Self::splat(true);
+
     /// Creates a new vector mask.
     #[inline(always)]
     pub const fn new(x: bool, y: bool, z: bool) -> Self {
@@ -30,7 +34,13 @@ impl BVec3A {
         ))
     }
 
-    /// Returns a bitmask with the lowest two bits set from the elements of `self`.
+    /// Creates a vector with all elements set to `v`.
+    #[inline]
+    pub const fn splat(v: bool) -> Self {
+        Self::new(v, v, v)
+    }
+
+    /// Returns a bitmask with the lowest 3 bits set from the elements of `self`.
     ///
     /// A true element results in a `1` bit and a false element in a `0` bit.  Element `x` goes
     /// into the first lowest bit, element `y` into the second, etc.
@@ -71,7 +81,7 @@ impl BVec3A {
 impl Default for BVec3A {
     #[inline]
     fn default() -> Self {
-        FALSE
+        Self::FALSE
     }
 }
 
diff --git a/src/bool/wasm32/bvec4a.rs b/src/bool/wasm32/bvec4a.rs
index 555570e..cdec83f 100644
--- a/src/bool/wasm32/bvec4a.rs
+++ b/src/bool/wasm32/bvec4a.rs
@@ -16,9 +16,13 @@ pub struct BVec4A(pub(crate) v128);
 
 const MASK: [u32; 2] = [0, 0xff_ff_ff_ff];
 
-const FALSE: BVec4A = BVec4A::new(false, false, false, false);
-
 impl BVec4A {
+    /// All false.
+    pub const FALSE: Self = Self::splat(false);
+
+    /// All true.
+    pub const TRUE: Self = Self::splat(true);
+
     /// Creates a new vector mask.
     #[inline(always)]
     pub const fn new(x: bool, y: bool, z: bool, w: bool) -> Self {
@@ -30,7 +34,13 @@ impl BVec4A {
         ))
     }
 
-    /// Returns a bitmask with the lowest two bits set from the elements of `self`.
+    /// Creates a vector with all elements set to `v`.
+    #[inline]
+    pub const fn splat(v: bool) -> Self {
+        Self::new(v, v, v, v)
+    }
+
+    /// Returns a bitmask with the lowest 4 bits set from the elements of `self`.
     ///
     /// A true element results in a `1` bit and a false element in a `0` bit.  Element `x` goes
     /// into the first lowest bit, element `y` into the second, etc.
@@ -77,7 +87,7 @@ impl BVec4A {
 impl Default for BVec4A {
     #[inline]
     fn default() -> Self {
-        FALSE
+        Self::FALSE
     }
 }
 
diff --git a/src/f32/coresimd/mat2.rs b/src/f32/coresimd/mat2.rs
index 2e63446..276816f 100644
--- a/src/f32/coresimd/mat2.rs
+++ b/src/f32/coresimd/mat2.rs
@@ -56,8 +56,8 @@ impl Mat2 {
     /// Creates a `[f32; 4]` array storing data in column major order.
     /// If you require data in row major order `transpose` the matrix first.
     #[inline]
-    pub fn to_cols_array(&self) -> [f32; 4] {
-        [self.x_axis.x, self.x_axis.y, self.y_axis.x, self.y_axis.y]
+    pub const fn to_cols_array(&self) -> [f32; 4] {
+        unsafe { *(self as *const Self as *const [f32; 4]) }
     }
 
     /// Creates a 2x2 matrix from a `[[f32; 2]; 2]` 2D array stored in column major order.
@@ -71,8 +71,8 @@ impl Mat2 {
     /// Creates a `[[f32; 2]; 2]` 2D array storing data in column major order.
     /// If you require data in row major order `transpose` the matrix first.
     #[inline]
-    pub fn to_cols_array_2d(&self) -> [[f32; 2]; 2] {
-        [self.x_axis.to_array(), self.y_axis.to_array()]
+    pub const fn to_cols_array_2d(&self) -> [[f32; 2]; 2] {
+        unsafe { *(self as *const Self as *const [[f32; 2]; 2]) }
     }
 
     /// Creates a 2x2 matrix with its diagonal set to `diagonal` and all other entries set to 0.
diff --git a/src/f32/coresimd/mat3a.rs b/src/f32/coresimd/mat3a.rs
index 8c14461..bda6d27 100644
--- a/src/f32/coresimd/mat3a.rs
+++ b/src/f32/coresimd/mat3a.rs
@@ -101,17 +101,14 @@ impl Mat3A {
     /// Creates a `[f32; 9]` array storing data in column major order.
     /// If you require data in row major order `transpose` the matrix first.
     #[inline]
-    pub fn to_cols_array(&self) -> [f32; 9] {
+    pub const fn to_cols_array(&self) -> [f32; 9] {
+        let [x_axis_x, x_axis_y, x_axis_z] = self.x_axis.to_array();
+        let [y_axis_x, y_axis_y, y_axis_z] = self.y_axis.to_array();
+        let [z_axis_x, z_axis_y, z_axis_z] = self.z_axis.to_array();
+
         [
-            self.x_axis.x,
-            self.x_axis.y,
-            self.x_axis.z,
-            self.y_axis.x,
-            self.y_axis.y,
-            self.y_axis.z,
-            self.z_axis.x,
-            self.z_axis.y,
-            self.z_axis.z,
+            x_axis_x, x_axis_y, x_axis_z, y_axis_x, y_axis_y, y_axis_z, z_axis_x, z_axis_y,
+            z_axis_z,
         ]
     }
 
@@ -130,7 +127,7 @@ impl Mat3A {
     /// Creates a `[[f32; 3]; 3]` 3D array storing data in column major order.
     /// If you require data in row major order `transpose` the matrix first.
     #[inline]
-    pub fn to_cols_array_2d(&self) -> [[f32; 3]; 3] {
+    pub const fn to_cols_array_2d(&self) -> [[f32; 3]; 3] {
         [
             self.x_axis.to_array(),
             self.y_axis.to_array(),
diff --git a/src/f32/coresimd/mat4.rs b/src/f32/coresimd/mat4.rs
index 9963972..d8a86f4 100644
--- a/src/f32/coresimd/mat4.rs
+++ b/src/f32/coresimd/mat4.rs
@@ -119,24 +119,15 @@ impl Mat4 {
     /// Creates a `[f32; 16]` array storing data in column major order.
     /// If you require data in row major order `transpose` the matrix first.
     #[inline]
-    pub fn to_cols_array(&self) -> [f32; 16] {
+    pub const fn to_cols_array(&self) -> [f32; 16] {
+        let [x_axis_x, x_axis_y, x_axis_z, x_axis_w] = self.x_axis.to_array();
+        let [y_axis_x, y_axis_y, y_axis_z, y_axis_w] = self.y_axis.to_array();
+        let [z_axis_x, z_axis_y, z_axis_z, z_axis_w] = self.z_axis.to_array();
+        let [w_axis_x, w_axis_y, w_axis_z, w_axis_w] = self.w_axis.to_array();
+
         [
-            self.x_axis.x,
-            self.x_axis.y,
-            self.x_axis.z,
-            self.x_axis.w,
-            self.y_axis.x,
-            self.y_axis.y,
-            self.y_axis.z,
-            self.y_axis.w,
-            self.z_axis.x,
-            self.z_axis.y,
-            self.z_axis.z,
-            self.z_axis.w,
-            self.w_axis.x,
-            self.w_axis.y,
-            self.w_axis.z,
-            self.w_axis.w,
+            x_axis_x, x_axis_y, x_axis_z, x_axis_w, y_axis_x, y_axis_y, y_axis_z, y_axis_w,
+            z_axis_x, z_axis_y, z_axis_z, z_axis_w, w_axis_x, w_axis_y, w_axis_z, w_axis_w,
         ]
     }
 
@@ -156,7 +147,7 @@ impl Mat4 {
     /// Creates a `[[f32; 4]; 4]` 4D array storing data in column major order.
     /// If you require data in row major order `transpose` the matrix first.
     #[inline]
-    pub fn to_cols_array_2d(&self) -> [[f32; 4]; 4] {
+    pub const fn to_cols_array_2d(&self) -> [[f32; 4]; 4] {
         [
             self.x_axis.to_array(),
             self.y_axis.to_array(),
diff --git a/src/f32/coresimd/vec3a.rs b/src/f32/coresimd/vec3a.rs
index 5215abb..71f5c64 100644
--- a/src/f32/coresimd/vec3a.rs
+++ b/src/f32/coresimd/vec3a.rs
@@ -148,6 +148,12 @@ impl Vec3A {
         dot3(self.0, rhs.0)
     }
 
+    /// 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) })
+    }
+
     /// Computes the cross product of `self` and `rhs`.
     #[inline]
     pub fn cross(self, rhs: Self) -> Self {
@@ -286,6 +292,15 @@ impl Vec3A {
         Self(self.0.signum())
     }
 
+    /// Returns a bitmask with the lowest 3 bits set to the sign bits from the elements of `self`.
+    ///
+    /// A negative element results in a `1` bit and a positive element in a `0` bit.  Element `x` goes
+    /// into the first lowest bit, element `y` into the second, etc.
+    #[inline]
+    pub fn is_negative_bitmask(self) -> u32 {
+        (self.0.is_sign_negative().to_bitmask() & 0x7) as u32
+    }
+
     /// 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]
diff --git a/src/f32/coresimd/vec4.rs b/src/f32/coresimd/vec4.rs
index 03e5abe..fa92f93 100644
--- a/src/f32/coresimd/vec4.rs
+++ b/src/f32/coresimd/vec4.rs
@@ -141,6 +141,12 @@ impl Vec4 {
         dot4(self.0, rhs.0)
     }
 
+    /// 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) })
+    }
+
     /// Returns a vector containing the minimum values for each element of `self` and `rhs`.
     ///
     /// In other words this computes `[self.x.min(rhs.x), self.y.min(rhs.y), ..]`.
@@ -262,6 +268,15 @@ impl Vec4 {
         Self(self.0.signum())
     }
 
+    /// Returns a bitmask with the lowest 4 bits set to the sign bits from the elements of `self`.
+    ///
+    /// A negative element results in a `1` bit and a positive element in a `0` bit.  Element `x` goes
+    /// into the first lowest bit, element `y` into the second, etc.
+    #[inline]
+    pub fn is_negative_bitmask(self) -> u32 {
+        self.0.is_sign_negative().to_bitmask() as u32
+    }
+
     /// 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]
diff --git a/src/f32/mat3.rs b/src/f32/mat3.rs
index 1839e29..6c2ed4d 100644
--- a/src/f32/mat3.rs
+++ b/src/f32/mat3.rs
@@ -99,7 +99,7 @@ impl Mat3 {
     /// Creates a `[f32; 9]` array storing data in column major order.
     /// If you require data in row major order `transpose` the matrix first.
     #[inline]
-    pub fn to_cols_array(&self) -> [f32; 9] {
+    pub const fn to_cols_array(&self) -> [f32; 9] {
         [
             self.x_axis.x,
             self.x_axis.y,
@@ -128,7 +128,7 @@ impl Mat3 {
     /// Creates a `[[f32; 3]; 3]` 3D array storing data in column major order.
     /// If you require data in row major order `transpose` the matrix first.
     #[inline]
-    pub fn to_cols_array_2d(&self) -> [[f32; 3]; 3] {
+    pub const fn to_cols_array_2d(&self) -> [[f32; 3]; 3] {
         [
             self.x_axis.to_array(),
             self.y_axis.to_array(),
diff --git a/src/f32/scalar/mat2.rs b/src/f32/scalar/mat2.rs
index da98c68..03d082e 100644
--- a/src/f32/scalar/mat2.rs
+++ b/src/f32/scalar/mat2.rs
@@ -65,7 +65,7 @@ impl Mat2 {
     /// Creates a `[f32; 4]` array storing data in column major order.
     /// If you require data in row major order `transpose` the matrix first.
     #[inline]
-    pub fn to_cols_array(&self) -> [f32; 4] {
+    pub const fn to_cols_array(&self) -> [f32; 4] {
         [self.x_axis.x, self.x_axis.y, self.y_axis.x, self.y_axis.y]
     }
 
@@ -80,7 +80,7 @@ impl Mat2 {
     /// Creates a `[[f32; 2]; 2]` 2D array storing data in column major order.
     /// If you require data in row major order `transpose` the matrix first.
     #[inline]
-    pub fn to_cols_array_2d(&self) -> [[f32; 2]; 2] {
+    pub const fn to_cols_array_2d(&self) -> [[f32; 2]; 2] {
         [self.x_axis.to_array(), self.y_axis.to_array()]
     }
 
diff --git a/src/f32/scalar/mat3a.rs b/src/f32/scalar/mat3a.rs
index e72c8f0..ea56981 100644
--- a/src/f32/scalar/mat3a.rs
+++ b/src/f32/scalar/mat3a.rs
@@ -99,7 +99,7 @@ impl Mat3A {
     /// Creates a `[f32; 9]` array storing data in column major order.
     /// If you require data in row major order `transpose` the matrix first.
     #[inline]
-    pub fn to_cols_array(&self) -> [f32; 9] {
+    pub const fn to_cols_array(&self) -> [f32; 9] {
         [
             self.x_axis.x,
             self.x_axis.y,
@@ -128,7 +128,7 @@ impl Mat3A {
     /// Creates a `[[f32; 3]; 3]` 3D array storing data in column major order.
     /// If you require data in row major order `transpose` the matrix first.
     #[inline]
-    pub fn to_cols_array_2d(&self) -> [[f32; 3]; 3] {
+    pub const fn to_cols_array_2d(&self) -> [[f32; 3]; 3] {
         [
             self.x_axis.to_array(),
             self.y_axis.to_array(),
diff --git a/src/f32/scalar/mat4.rs b/src/f32/scalar/mat4.rs
index f2de316..580eb27 100644
--- a/src/f32/scalar/mat4.rs
+++ b/src/f32/scalar/mat4.rs
@@ -124,7 +124,7 @@ impl Mat4 {
     /// Creates a `[f32; 16]` array storing data in column major order.
     /// If you require data in row major order `transpose` the matrix first.
     #[inline]
-    pub fn to_cols_array(&self) -> [f32; 16] {
+    pub const fn to_cols_array(&self) -> [f32; 16] {
         [
             self.x_axis.x,
             self.x_axis.y,
@@ -161,7 +161,7 @@ impl Mat4 {
     /// Creates a `[[f32; 4]; 4]` 4D array storing data in column major order.
     /// If you require data in row major order `transpose` the matrix first.
     #[inline]
-    pub fn to_cols_array_2d(&self) -> [[f32; 4]; 4] {
+    pub const fn to_cols_array_2d(&self) -> [[f32; 4]; 4] {
         [
             self.x_axis.to_array(),
             self.y_axis.to_array(),
@@ -174,10 +174,9 @@ impl Mat4 {
     #[doc(alias = "scale")]
     #[inline]
     pub const fn from_diagonal(diagonal: Vec4) -> Self {
-        // diagonal.x, diagonal.y etc can't be done in a const-context
-        let [x, y, z, w] = diagonal.to_array();
         Self::new(
-            x, 0.0, 0.0, 0.0, 0.0, y, 0.0, 0.0, 0.0, 0.0, z, 0.0, 0.0, 0.0, 0.0, w,
+            diagonal.x, 0.0, 0.0, 0.0, 0.0, diagonal.y, 0.0, 0.0, 0.0, 0.0, diagonal.z, 0.0, 0.0,
+            0.0, 0.0, diagonal.w,
         )
     }
 
diff --git a/src/f32/scalar/vec3a.rs b/src/f32/scalar/vec3a.rs
index 71b9ebd..26b82b6 100644
--- a/src/f32/scalar/vec3a.rs
+++ b/src/f32/scalar/vec3a.rs
@@ -159,6 +159,12 @@ impl Vec3A {
         (self.x * rhs.x) + (self.y * rhs.y) + (self.z * rhs.z)
     }
 
+    /// 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::splat(self.dot(rhs))
+    }
+
     /// Computes the cross product of `self` and `rhs`.
     #[inline]
     pub fn cross(self, rhs: Self) -> Self {
@@ -306,6 +312,17 @@ impl Vec3A {
         }
     }
 
+    /// Returns a bitmask with the lowest 3 bits set to the sign bits from the elements of `self`.
+    ///
+    /// A negative element results in a `1` bit and a positive element in a `0` bit.  Element `x` goes
+    /// into the first lowest bit, element `y` into the second, etc.
+    #[inline]
+    pub fn is_negative_bitmask(self) -> u32 {
+        (self.x.is_sign_negative() as u32)
+            | (self.y.is_sign_negative() as u32) << 1
+            | (self.z.is_sign_negative() as u32) << 2
+    }
+
     /// 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]
diff --git a/src/f32/scalar/vec4.rs b/src/f32/scalar/vec4.rs
index 7facd6c..6abcccd 100644
--- a/src/f32/scalar/vec4.rs
+++ b/src/f32/scalar/vec4.rs
@@ -162,6 +162,12 @@ impl Vec4 {
         (self.x * rhs.x) + (self.y * rhs.y) + (self.z * rhs.z) + (self.w * rhs.w)
     }
 
+    /// 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::splat(self.dot(rhs))
+    }
+
     /// Returns a vector containing the minimum values for each element of `self` and `rhs`.
     ///
     /// In other words this computes `[self.x.min(rhs.x), self.y.min(rhs.y), ..]`.
@@ -333,6 +339,18 @@ impl Vec4 {
         }
     }
 
+    /// Returns a bitmask with the lowest 4 bits set to the sign bits from the elements of `self`.
+    ///
+    /// A negative element results in a `1` bit and a positive element in a `0` bit.  Element `x` goes
+    /// into the first lowest bit, element `y` into the second, etc.
+    #[inline]
+    pub fn is_negative_bitmask(self) -> u32 {
+        (self.x.is_sign_negative() as u32)
+            | (self.y.is_sign_negative() as u32) << 1
+            | (self.z.is_sign_negative() as u32) << 2
+            | (self.w.is_sign_negative() as u32) << 3
+    }
+
     /// 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]
diff --git a/src/f32/sse2/mat2.rs b/src/f32/sse2/mat2.rs
index fa7b607..311448b 100644
--- a/src/f32/sse2/mat2.rs
+++ b/src/f32/sse2/mat2.rs
@@ -74,8 +74,8 @@ impl Mat2 {
     /// Creates a `[f32; 4]` array storing data in column major order.
     /// If you require data in row major order `transpose` the matrix first.
     #[inline]
-    pub fn to_cols_array(&self) -> [f32; 4] {
-        [self.x_axis.x, self.x_axis.y, self.y_axis.x, self.y_axis.y]
+    pub const fn to_cols_array(&self) -> [f32; 4] {
+        unsafe { *(self as *const Self as *const [f32; 4]) }
     }
 
     /// Creates a 2x2 matrix from a `[[f32; 2]; 2]` 2D array stored in column major order.
@@ -89,8 +89,8 @@ impl Mat2 {
     /// Creates a `[[f32; 2]; 2]` 2D array storing data in column major order.
     /// If you require data in row major order `transpose` the matrix first.
     #[inline]
-    pub fn to_cols_array_2d(&self) -> [[f32; 2]; 2] {
-        [self.x_axis.to_array(), self.y_axis.to_array()]
+    pub const fn to_cols_array_2d(&self) -> [[f32; 2]; 2] {
+        unsafe { *(self as *const Self as *const [[f32; 2]; 2]) }
     }
 
     /// Creates a 2x2 matrix with its diagonal set to `diagonal` and all other entries set to 0.
diff --git a/src/f32/sse2/mat3a.rs b/src/f32/sse2/mat3a.rs
index 25fde99..14912dd 100644
--- a/src/f32/sse2/mat3a.rs
+++ b/src/f32/sse2/mat3a.rs
@@ -104,17 +104,14 @@ impl Mat3A {
     /// Creates a `[f32; 9]` array storing data in column major order.
     /// If you require data in row major order `transpose` the matrix first.
     #[inline]
-    pub fn to_cols_array(&self) -> [f32; 9] {
+    pub const fn to_cols_array(&self) -> [f32; 9] {
+        let [x_axis_x, x_axis_y, x_axis_z] = self.x_axis.to_array();
+        let [y_axis_x, y_axis_y, y_axis_z] = self.y_axis.to_array();
+        let [z_axis_x, z_axis_y, z_axis_z] = self.z_axis.to_array();
+
         [
-            self.x_axis.x,
-            self.x_axis.y,
-            self.x_axis.z,
-            self.y_axis.x,
-            self.y_axis.y,
-            self.y_axis.z,
-            self.z_axis.x,
-            self.z_axis.y,
-            self.z_axis.z,
+            x_axis_x, x_axis_y, x_axis_z, y_axis_x, y_axis_y, y_axis_z, z_axis_x, z_axis_y,
+            z_axis_z,
         ]
     }
 
@@ -133,7 +130,7 @@ impl Mat3A {
     /// Creates a `[[f32; 3]; 3]` 3D array storing data in column major order.
     /// If you require data in row major order `transpose` the matrix first.
     #[inline]
-    pub fn to_cols_array_2d(&self) -> [[f32; 3]; 3] {
+    pub const fn to_cols_array_2d(&self) -> [[f32; 3]; 3] {
         [
             self.x_axis.to_array(),
             self.y_axis.to_array(),
diff --git a/src/f32/sse2/mat4.rs b/src/f32/sse2/mat4.rs
index b6a0c62..24e16ad 100644
--- a/src/f32/sse2/mat4.rs
+++ b/src/f32/sse2/mat4.rs
@@ -122,24 +122,15 @@ impl Mat4 {
     /// Creates a `[f32; 16]` array storing data in column major order.
     /// If you require data in row major order `transpose` the matrix first.
     #[inline]
-    pub fn to_cols_array(&self) -> [f32; 16] {
+    pub const fn to_cols_array(&self) -> [f32; 16] {
+        let [x_axis_x, x_axis_y, x_axis_z, x_axis_w] = self.x_axis.to_array();
+        let [y_axis_x, y_axis_y, y_axis_z, y_axis_w] = self.y_axis.to_array();
+        let [z_axis_x, z_axis_y, z_axis_z, z_axis_w] = self.z_axis.to_array();
+        let [w_axis_x, w_axis_y, w_axis_z, w_axis_w] = self.w_axis.to_array();
+
         [
-            self.x_axis.x,
-            self.x_axis.y,
-            self.x_axis.z,
-            self.x_axis.w,
-            self.y_axis.x,
-            self.y_axis.y,
-            self.y_axis.z,
-            self.y_axis.w,
-            self.z_axis.x,
-            self.z_axis.y,
-            self.z_axis.z,
-            self.z_axis.w,
-            self.w_axis.x,
-            self.w_axis.y,
-            self.w_axis.z,
-            self.w_axis.w,
+            x_axis_x, x_axis_y, x_axis_z, x_axis_w, y_axis_x, y_axis_y, y_axis_z, y_axis_w,
+            z_axis_x, z_axis_y, z_axis_z, z_axis_w, w_axis_x, w_axis_y, w_axis_z, w_axis_w,
         ]
     }
 
@@ -159,7 +150,7 @@ impl Mat4 {
     /// Creates a `[[f32; 4]; 4]` 4D array storing data in column major order.
     /// If you require data in row major order `transpose` the matrix first.
     #[inline]
-    pub fn to_cols_array_2d(&self) -> [[f32; 4]; 4] {
+    pub const fn to_cols_array_2d(&self) -> [[f32; 4]; 4] {
         [
             self.x_axis.to_array(),
             self.y_axis.to_array(),
diff --git a/src/f32/sse2/vec3a.rs b/src/f32/sse2/vec3a.rs
index 1ead0df..56b8c8d 100644
--- a/src/f32/sse2/vec3a.rs
+++ b/src/f32/sse2/vec3a.rs
@@ -160,6 +160,12 @@ impl Vec3A {
         unsafe { dot3(self.0, rhs.0) }
     }
 
+    /// 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_m128(self.0, rhs.0) })
+    }
+
     /// Computes the cross product of `self` and `rhs`.
     #[inline]
     pub fn cross(self, rhs: Self) -> Self {
@@ -306,10 +312,20 @@ impl Vec3A {
     /// - `NAN` if the number is `NAN`
     #[inline]
     pub fn signum(self) -> Self {
-        let mask = self.cmpge(Self::ZERO);
-        let result = Self::select(mask, Self::ONE, Self::NEG_ONE);
-        let mask = self.is_nan_mask();
-        Self::select(mask, self, result)
+        unsafe {
+            let result = Self(_mm_or_ps(_mm_and_ps(self.0, Self::NEG_ONE.0), Self::ONE.0));
+            let mask = self.is_nan_mask();
+            Self::select(mask, self, result)
+        }
+    }
+
+    /// Returns a bitmask with the lowest 3 bits set to the sign bits from the elements of `self`.
+    ///
+    /// A negative element results in a `1` bit and a positive element in a `0` bit.  Element `x` goes
+    /// into the first lowest bit, element `y` into the second, etc.
+    #[inline]
+    pub fn is_negative_bitmask(self) -> u32 {
+        unsafe { (_mm_movemask_ps(self.0) as u32) & 0x7 }
     }
 
     /// Returns `true` if, and only if, all elements are finite.  If any element is either
@@ -987,7 +1003,7 @@ impl Neg for Vec3A {
     type Output = Self;
     #[inline]
     fn neg(self) -> Self {
-        Self(unsafe { _mm_sub_ps(Self::ZERO.0, self.0) })
+        Self(unsafe { _mm_xor_ps(_mm_set1_ps(-0.0), self.0) })
     }
 }
 
diff --git a/src/f32/sse2/vec4.rs b/src/f32/sse2/vec4.rs
index ab645ca..f72e027 100644
--- a/src/f32/sse2/vec4.rs
+++ b/src/f32/sse2/vec4.rs
@@ -153,6 +153,12 @@ impl Vec4 {
         unsafe { dot4(self.0, rhs.0) }
     }
 
+    /// 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_m128(self.0, rhs.0) })
+    }
+
     /// Returns a vector containing the minimum values for each element of `self` and `rhs`.
     ///
     /// In other words this computes `[self.x.min(rhs.x), self.y.min(rhs.y), ..]`.
@@ -281,10 +287,20 @@ impl Vec4 {
     /// - `NAN` if the number is `NAN`
     #[inline]
     pub fn signum(self) -> Self {
-        let mask = self.cmpge(Self::ZERO);
-        let result = Self::select(mask, Self::ONE, Self::NEG_ONE);
-        let mask = self.is_nan_mask();
-        Self::select(mask, self, result)
+        unsafe {
+            let result = Self(_mm_or_ps(_mm_and_ps(self.0, Self::NEG_ONE.0), Self::ONE.0));
+            let mask = self.is_nan_mask();
+            Self::select(mask, self, result)
+        }
+    }
+
+    /// Returns a bitmask with the lowest 4 bits set to the sign bits from the elements of `self`.
+    ///
+    /// A negative element results in a `1` bit and a positive element in a `0` bit.  Element `x` goes
+    /// into the first lowest bit, element `y` into the second, etc.
+    #[inline]
+    pub fn is_negative_bitmask(self) -> u32 {
+        unsafe { _mm_movemask_ps(self.0) as u32 }
     }
 
     /// Returns `true` if, and only if, all elements are finite.  If any element is either
@@ -900,7 +916,7 @@ impl Neg for Vec4 {
     type Output = Self;
     #[inline]
     fn neg(self) -> Self {
-        Self(unsafe { _mm_sub_ps(Self::ZERO.0, self.0) })
+        Self(unsafe { _mm_xor_ps(_mm_set1_ps(-0.0), self.0) })
     }
 }
 
diff --git a/src/f32/vec2.rs b/src/f32/vec2.rs
index 7c275eb..13181e1 100644
--- a/src/f32/vec2.rs
+++ b/src/f32/vec2.rs
@@ -125,6 +125,12 @@ impl Vec2 {
         (self.x * rhs.x) + (self.y * rhs.y)
     }
 
+    /// 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::splat(self.dot(rhs))
+    }
+
     /// Returns a vector containing the minimum values for each element of `self` and `rhs`.
     ///
     /// In other words this computes `[self.x.min(rhs.x), self.y.min(rhs.y), ..]`.
@@ -258,6 +264,15 @@ impl Vec2 {
         }
     }
 
+    /// Returns a bitmask with the lowest 2 bits set to the sign bits from the elements of `self`.
+    ///
+    /// A negative element results in a `1` bit and a positive element in a `0` bit.  Element `x` goes
+    /// into the first lowest bit, element `y` into the second, etc.
+    #[inline]
+    pub fn is_negative_bitmask(self) -> u32 {
+        (self.x.is_sign_negative() as u32) | (self.y.is_sign_negative() as u32) << 1
+    }
+
     /// 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]
diff --git a/src/f32/vec3.rs b/src/f32/vec3.rs
index df0ae09..5606263 100644
--- a/src/f32/vec3.rs
+++ b/src/f32/vec3.rs
@@ -153,6 +153,12 @@ impl Vec3 {
         (self.x * rhs.x) + (self.y * rhs.y) + (self.z * rhs.z)
     }
 
+    /// 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::splat(self.dot(rhs))
+    }
+
     /// Computes the cross product of `self` and `rhs`.
     #[inline]
     pub fn cross(self, rhs: Self) -> Self {
@@ -300,6 +306,17 @@ impl Vec3 {
         }
     }
 
+    /// Returns a bitmask with the lowest 3 bits set to the sign bits from the elements of `self`.
+    ///
+    /// A negative element results in a `1` bit and a positive element in a `0` bit.  Element `x` goes
+    /// into the first lowest bit, element `y` into the second, etc.
+    #[inline]
+    pub fn is_negative_bitmask(self) -> u32 {
+        (self.x.is_sign_negative() as u32)
+            | (self.y.is_sign_negative() as u32) << 1
+            | (self.z.is_sign_negative() as u32) << 2
+    }
+
     /// 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]
diff --git a/src/f32/wasm32/mat2.rs b/src/f32/wasm32/mat2.rs
index ef721b3..7869ff5 100644
--- a/src/f32/wasm32/mat2.rs
+++ b/src/f32/wasm32/mat2.rs
@@ -56,8 +56,8 @@ impl Mat2 {
     /// Creates a `[f32; 4]` array storing data in column major order.
     /// If you require data in row major order `transpose` the matrix first.
     #[inline]
-    pub fn to_cols_array(&self) -> [f32; 4] {
-        [self.x_axis.x, self.x_axis.y, self.y_axis.x, self.y_axis.y]
+    pub const fn to_cols_array(&self) -> [f32; 4] {
+        unsafe { *(self as *const Self as *const [f32; 4]) }
     }
 
     /// Creates a 2x2 matrix from a `[[f32; 2]; 2]` 2D array stored in column major order.
@@ -71,8 +71,8 @@ impl Mat2 {
     /// Creates a `[[f32; 2]; 2]` 2D array storing data in column major order.
     /// If you require data in row major order `transpose` the matrix first.
     #[inline]
-    pub fn to_cols_array_2d(&self) -> [[f32; 2]; 2] {
-        [self.x_axis.to_array(), self.y_axis.to_array()]
+    pub const fn to_cols_array_2d(&self) -> [[f32; 2]; 2] {
+        unsafe { *(self as *const Self as *const [[f32; 2]; 2]) }
     }
 
     /// Creates a 2x2 matrix with its diagonal set to `diagonal` and all other entries set to 0.
diff --git a/src/f32/wasm32/mat3a.rs b/src/f32/wasm32/mat3a.rs
index f5d974a..573ea1f 100644
--- a/src/f32/wasm32/mat3a.rs
+++ b/src/f32/wasm32/mat3a.rs
@@ -101,17 +101,14 @@ impl Mat3A {
     /// Creates a `[f32; 9]` array storing data in column major order.
     /// If you require data in row major order `transpose` the matrix first.
     #[inline]
-    pub fn to_cols_array(&self) -> [f32; 9] {
+    pub const fn to_cols_array(&self) -> [f32; 9] {
+        let [x_axis_x, x_axis_y, x_axis_z] = self.x_axis.to_array();
+        let [y_axis_x, y_axis_y, y_axis_z] = self.y_axis.to_array();
+        let [z_axis_x, z_axis_y, z_axis_z] = self.z_axis.to_array();
+
         [
-            self.x_axis.x,
-            self.x_axis.y,
-            self.x_axis.z,
-            self.y_axis.x,
-            self.y_axis.y,
-            self.y_axis.z,
-            self.z_axis.x,
-            self.z_axis.y,
-            self.z_axis.z,
+            x_axis_x, x_axis_y, x_axis_z, y_axis_x, y_axis_y, y_axis_z, z_axis_x, z_axis_y,
+            z_axis_z,
         ]
     }
 
@@ -130,7 +127,7 @@ impl Mat3A {
     /// Creates a `[[f32; 3]; 3]` 3D array storing data in column major order.
     /// If you require data in row major order `transpose` the matrix first.
     #[inline]
-    pub fn to_cols_array_2d(&self) -> [[f32; 3]; 3] {
+    pub const fn to_cols_array_2d(&self) -> [[f32; 3]; 3] {
         [
             self.x_axis.to_array(),
             self.y_axis.to_array(),
diff --git a/src/f32/wasm32/mat4.rs b/src/f32/wasm32/mat4.rs
index 9ce0c23..433e893 100644
--- a/src/f32/wasm32/mat4.rs
+++ b/src/f32/wasm32/mat4.rs
@@ -119,24 +119,15 @@ impl Mat4 {
     /// Creates a `[f32; 16]` array storing data in column major order.
     /// If you require data in row major order `transpose` the matrix first.
     #[inline]
-    pub fn to_cols_array(&self) -> [f32; 16] {
+    pub const fn to_cols_array(&self) -> [f32; 16] {
+        let [x_axis_x, x_axis_y, x_axis_z, x_axis_w] = self.x_axis.to_array();
+        let [y_axis_x, y_axis_y, y_axis_z, y_axis_w] = self.y_axis.to_array();
+        let [z_axis_x, z_axis_y, z_axis_z, z_axis_w] = self.z_axis.to_array();
+        let [w_axis_x, w_axis_y, w_axis_z, w_axis_w] = self.w_axis.to_array();
+
         [
-            self.x_axis.x,
-            self.x_axis.y,
-            self.x_axis.z,
-            self.x_axis.w,
-            self.y_axis.x,
-            self.y_axis.y,
-            self.y_axis.z,
-            self.y_axis.w,
-            self.z_axis.x,
-            self.z_axis.y,
-            self.z_axis.z,
-            self.z_axis.w,
-            self.w_axis.x,
-            self.w_axis.y,
-            self.w_axis.z,
-            self.w_axis.w,
+            x_axis_x, x_axis_y, x_axis_z, x_axis_w, y_axis_x, y_axis_y, y_axis_z, y_axis_w,
+            z_axis_x, z_axis_y, z_axis_z, z_axis_w, w_axis_x, w_axis_y, w_axis_z, w_axis_w,
         ]
     }
 
@@ -156,7 +147,7 @@ impl Mat4 {
     /// Creates a `[[f32; 4]; 4]` 4D array storing data in column major order.
     /// If you require data in row major order `transpose` the matrix first.
     #[inline]
-    pub fn to_cols_array_2d(&self) -> [[f32; 4]; 4] {
+    pub const fn to_cols_array_2d(&self) -> [[f32; 4]; 4] {
         [
             self.x_axis.to_array(),
             self.y_axis.to_array(),
diff --git a/src/f32/wasm32/vec3a.rs b/src/f32/wasm32/vec3a.rs
index cec3c79..08a6269 100644
--- a/src/f32/wasm32/vec3a.rs
+++ b/src/f32/wasm32/vec3a.rs
@@ -152,6 +152,12 @@ impl Vec3A {
         dot3(self.0, rhs.0)
     }
 
+    /// 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_v128(self.0, rhs.0) })
+    }
+
     /// Computes the cross product of `self` and `rhs`.
     #[inline]
     pub fn cross(self, rhs: Self) -> Self {
@@ -287,10 +293,20 @@ impl Vec3A {
     /// - `NAN` if the number is `NAN`
     #[inline]
     pub fn signum(self) -> Self {
-        let mask = self.cmpge(Self::ZERO);
-        let result = Self::select(mask, Self::ONE, Self::NEG_ONE);
-        let mask = self.is_nan_mask();
-        Self::select(mask, self, result)
+        unsafe {
+            let result = Self(v128_or(v128_and(self.0, Self::NEG_ONE.0), Self::ONE.0));
+            let mask = self.is_nan_mask();
+            Self::select(mask, self, result)
+        }
+    }
+
+    /// Returns a bitmask with the lowest 3 bits set to the sign bits from the elements of `self`.
+    ///
+    /// A negative element results in a `1` bit and a positive element in a `0` bit.  Element `x` goes
+    /// into the first lowest bit, element `y` into the second, etc.
+    #[inline]
+    pub fn is_negative_bitmask(self) -> u32 {
+        (u32x4_bitmask(self.0) & 0x7) as u32
     }
 
     /// Returns `true` if, and only if, all elements are finite.  If any element is either
diff --git a/src/f32/wasm32/vec4.rs b/src/f32/wasm32/vec4.rs
index bc79cc8..de16f66 100644
--- a/src/f32/wasm32/vec4.rs
+++ b/src/f32/wasm32/vec4.rs
@@ -145,6 +145,12 @@ impl Vec4 {
         dot4(self.0, rhs.0)
     }
 
+    /// 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_v128(self.0, rhs.0) })
+    }
+
     /// Returns a vector containing the minimum values for each element of `self` and `rhs`.
     ///
     /// In other words this computes `[self.x.min(rhs.x), self.y.min(rhs.y), ..]`.
@@ -269,10 +275,20 @@ impl Vec4 {
     /// - `NAN` if the number is `NAN`
     #[inline]
     pub fn signum(self) -> Self {
-        let mask = self.cmpge(Self::ZERO);
-        let result = Self::select(mask, Self::ONE, Self::NEG_ONE);
-        let mask = self.is_nan_mask();
-        Self::select(mask, self, result)
+        unsafe {
+            let result = Self(v128_or(v128_and(self.0, Self::NEG_ONE.0), Self::ONE.0));
+            let mask = self.is_nan_mask();
+            Self::select(mask, self, result)
+        }
+    }
+
+    /// Returns a bitmask with the lowest 4 bits set to the sign bits from the elements of `self`.
+    ///
+    /// A negative element results in a `1` bit and a positive element in a `0` bit.  Element `x` goes
+    /// into the first lowest bit, element `y` into the second, etc.
+    #[inline]
+    pub fn is_negative_bitmask(self) -> u32 {
+        u32x4_bitmask(self.0) as u32
     }
 
     /// Returns `true` if, and only if, all elements are finite.  If any element is either
diff --git a/src/f64/dmat2.rs b/src/f64/dmat2.rs
index 3f79adf..bd52c8c 100644
--- a/src/f64/dmat2.rs
+++ b/src/f64/dmat2.rs
@@ -61,7 +61,7 @@ impl DMat2 {
     /// Creates a `[f64; 4]` array storing data in column major order.
     /// If you require data in row major order `transpose` the matrix first.
     #[inline]
-    pub fn to_cols_array(&self) -> [f64; 4] {
+    pub const fn to_cols_array(&self) -> [f64; 4] {
         [self.x_axis.x, self.x_axis.y, self.y_axis.x, self.y_axis.y]
     }
 
@@ -76,7 +76,7 @@ impl DMat2 {
     /// Creates a `[[f64; 2]; 2]` 2D array storing data in column major order.
     /// If you require data in row major order `transpose` the matrix first.
     #[inline]
-    pub fn to_cols_array_2d(&self) -> [[f64; 2]; 2] {
+    pub const fn to_cols_array_2d(&self) -> [[f64; 2]; 2] {
         [self.x_axis.to_array(), self.y_axis.to_array()]
     }
 
diff --git a/src/f64/dmat3.rs b/src/f64/dmat3.rs
index adf6cbf..600223c 100644
--- a/src/f64/dmat3.rs
+++ b/src/f64/dmat3.rs
@@ -99,7 +99,7 @@ impl DMat3 {
     /// Creates a `[f64; 9]` array storing data in column major order.
     /// If you require data in row major order `transpose` the matrix first.
     #[inline]
-    pub fn to_cols_array(&self) -> [f64; 9] {
+    pub const fn to_cols_array(&self) -> [f64; 9] {
         [
             self.x_axis.x,
             self.x_axis.y,
@@ -128,7 +128,7 @@ impl DMat3 {
     /// Creates a `[[f64; 3]; 3]` 3D array storing data in column major order.
     /// If you require data in row major order `transpose` the matrix first.
     #[inline]
-    pub fn to_cols_array_2d(&self) -> [[f64; 3]; 3] {
+    pub const fn to_cols_array_2d(&self) -> [[f64; 3]; 3] {
         [
             self.x_axis.to_array(),
             self.y_axis.to_array(),
diff --git a/src/f64/dmat4.rs b/src/f64/dmat4.rs
index cac5d55..cc4283c 100644
--- a/src/f64/dmat4.rs
+++ b/src/f64/dmat4.rs
@@ -118,7 +118,7 @@ impl DMat4 {
     /// Creates a `[f64; 16]` array storing data in column major order.
     /// If you require data in row major order `transpose` the matrix first.
     #[inline]
-    pub fn to_cols_array(&self) -> [f64; 16] {
+    pub const fn to_cols_array(&self) -> [f64; 16] {
         [
             self.x_axis.x,
             self.x_axis.y,
@@ -155,7 +155,7 @@ impl DMat4 {
     /// Creates a `[[f64; 4]; 4]` 4D array storing data in column major order.
     /// If you require data in row major order `transpose` the matrix first.
     #[inline]
-    pub fn to_cols_array_2d(&self) -> [[f64; 4]; 4] {
+    pub const fn to_cols_array_2d(&self) -> [[f64; 4]; 4] {
         [
             self.x_axis.to_array(),
             self.y_axis.to_array(),
diff --git a/src/f64/dvec2.rs b/src/f64/dvec2.rs
index 205dca2..42039e9 100644
--- a/src/f64/dvec2.rs
+++ b/src/f64/dvec2.rs
@@ -125,6 +125,12 @@ impl DVec2 {
         (self.x * rhs.x) + (self.y * rhs.y)
     }
 
+    /// 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::splat(self.dot(rhs))
+    }
+
     /// Returns a vector containing the minimum values for each element of `self` and `rhs`.
     ///
     /// In other words this computes `[self.x.min(rhs.x), self.y.min(rhs.y), ..]`.
@@ -258,6 +264,15 @@ impl DVec2 {
         }
     }
 
+    /// Returns a bitmask with the lowest 2 bits set to the sign bits from the elements of `self`.
+    ///
+    /// A negative element results in a `1` bit and a positive element in a `0` bit.  Element `x` goes
+    /// into the first lowest bit, element `y` into the second, etc.
+    #[inline]
+    pub fn is_negative_bitmask(self) -> u32 {
+        (self.x.is_sign_negative() as u32) | (self.y.is_sign_negative() as u32) << 1
+    }
+
     /// 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]
diff --git a/src/f64/dvec3.rs b/src/f64/dvec3.rs
index 2251a28..0260d42 100644
--- a/src/f64/dvec3.rs
+++ b/src/f64/dvec3.rs
@@ -153,6 +153,12 @@ impl DVec3 {
         (self.x * rhs.x) + (self.y * rhs.y) + (self.z * rhs.z)
     }
 
+    /// 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::splat(self.dot(rhs))
+    }
+
     /// Computes the cross product of `self` and `rhs`.
     #[inline]
     pub fn cross(self, rhs: Self) -> Self {
@@ -300,6 +306,17 @@ impl DVec3 {
         }
     }
 
+    /// Returns a bitmask with the lowest 3 bits set to the sign bits from the elements of `self`.
+    ///
+    /// A negative element results in a `1` bit and a positive element in a `0` bit.  Element `x` goes
+    /// into the first lowest bit, element `y` into the second, etc.
+    #[inline]
+    pub fn is_negative_bitmask(self) -> u32 {
+        (self.x.is_sign_negative() as u32)
+            | (self.y.is_sign_negative() as u32) << 1
+            | (self.z.is_sign_negative() as u32) << 2
+    }
+
     /// 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]
diff --git a/src/f64/dvec4.rs b/src/f64/dvec4.rs
index 132b8a8..50d49b4 100644
--- a/src/f64/dvec4.rs
+++ b/src/f64/dvec4.rs
@@ -154,6 +154,12 @@ impl DVec4 {
         (self.x * rhs.x) + (self.y * rhs.y) + (self.z * rhs.z) + (self.w * rhs.w)
     }
 
+    /// 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::splat(self.dot(rhs))
+    }
+
     /// Returns a vector containing the minimum values for each element of `self` and `rhs`.
     ///
     /// In other words this computes `[self.x.min(rhs.x), self.y.min(rhs.y), ..]`.
@@ -325,6 +331,18 @@ impl DVec4 {
         }
     }
 
+    /// Returns a bitmask with the lowest 4 bits set to the sign bits from the elements of `self`.
+    ///
+    /// A negative element results in a `1` bit and a positive element in a `0` bit.  Element `x` goes
+    /// into the first lowest bit, element `y` into the second, etc.
+    #[inline]
+    pub fn is_negative_bitmask(self) -> u32 {
+        (self.x.is_sign_negative() as u32)
+            | (self.y.is_sign_negative() as u32) << 1
+            | (self.z.is_sign_negative() as u32) << 2
+            | (self.w.is_sign_negative() as u32) << 3
+    }
+
     /// 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]
diff --git a/src/i32/ivec2.rs b/src/i32/ivec2.rs
index 4ad537b..599f4f5 100644
--- a/src/i32/ivec2.rs
+++ b/src/i32/ivec2.rs
@@ -119,6 +119,12 @@ impl IVec2 {
         (self.x * rhs.x) + (self.y * rhs.y)
     }
 
+    /// 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::splat(self.dot(rhs))
+    }
+
     /// Returns a vector containing the minimum values for each element of `self` and `rhs`.
     ///
     /// In other words this computes `[self.x.min(rhs.x), self.y.min(rhs.y), ..]`.
@@ -252,6 +258,15 @@ impl IVec2 {
         }
     }
 
+    /// Returns a bitmask with the lowest 2 bits set to the sign bits from the elements of `self`.
+    ///
+    /// A negative element results in a `1` bit and a positive element in a `0` bit.  Element `x` goes
+    /// into the first lowest bit, element `y` into the second, etc.
+    #[inline]
+    pub fn is_negative_bitmask(self) -> u32 {
+        (self.x.is_negative() as u32) | (self.y.is_negative() as u32) << 1
+    }
+
     /// Returns a vector that is equal to `self` rotated by 90 degrees.
     #[inline]
     pub fn perp(self) -> Self {
diff --git a/src/i32/ivec3.rs b/src/i32/ivec3.rs
index 0019b51..7a87a30 100644
--- a/src/i32/ivec3.rs
+++ b/src/i32/ivec3.rs
@@ -147,6 +147,12 @@ impl IVec3 {
         (self.x * rhs.x) + (self.y * rhs.y) + (self.z * rhs.z)
     }
 
+    /// 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::splat(self.dot(rhs))
+    }
+
     /// Computes the cross product of `self` and `rhs`.
     #[inline]
     pub fn cross(self, rhs: Self) -> Self {
@@ -294,6 +300,17 @@ impl IVec3 {
         }
     }
 
+    /// Returns a bitmask with the lowest 3 bits set to the sign bits from the elements of `self`.
+    ///
+    /// A negative element results in a `1` bit and a positive element in a `0` bit.  Element `x` goes
+    /// into the first lowest bit, element `y` into the second, etc.
+    #[inline]
+    pub fn is_negative_bitmask(self) -> u32 {
+        (self.x.is_negative() as u32)
+            | (self.y.is_negative() as u32) << 1
+            | (self.z.is_negative() as u32) << 2
+    }
+
     /// Casts all elements of `self` to `f32`.
     #[inline]
     pub fn as_vec3(&self) -> crate::Vec3 {
diff --git a/src/i32/ivec4.rs b/src/i32/ivec4.rs
index 53e90ac..157cf05 100644
--- a/src/i32/ivec4.rs
+++ b/src/i32/ivec4.rs
@@ -148,6 +148,12 @@ impl IVec4 {
         (self.x * rhs.x) + (self.y * rhs.y) + (self.z * rhs.z) + (self.w * rhs.w)
     }
 
+    /// 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::splat(self.dot(rhs))
+    }
+
     /// Returns a vector containing the minimum values for each element of `self` and `rhs`.
     ///
     /// In other words this computes `[self.x.min(rhs.x), self.y.min(rhs.y), ..]`.
@@ -319,6 +325,18 @@ impl IVec4 {
         }
     }
 
+    /// Returns a bitmask with the lowest 4 bits set to the sign bits from the elements of `self`.
+    ///
+    /// A negative element results in a `1` bit and a positive element in a `0` bit.  Element `x` goes
+    /// into the first lowest bit, element `y` into the second, etc.
+    #[inline]
+    pub fn is_negative_bitmask(self) -> u32 {
+        (self.x.is_negative() as u32)
+            | (self.y.is_negative() as u32) << 1
+            | (self.z.is_negative() as u32) << 2
+            | (self.w.is_negative() as u32) << 3
+    }
+
     /// Casts all elements of `self` to `f32`.
     #[inline]
     pub fn as_vec4(&self) -> crate::Vec4 {
diff --git a/src/u32/uvec2.rs b/src/u32/uvec2.rs
index b422600..ac3d7df 100644
--- a/src/u32/uvec2.rs
+++ b/src/u32/uvec2.rs
@@ -110,6 +110,12 @@ impl UVec2 {
         (self.x * rhs.x) + (self.y * rhs.y)
     }
 
+    /// 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::splat(self.dot(rhs))
+    }
+
     /// Returns a vector containing the minimum values for each element of `self` and `rhs`.
     ///
     /// In other words this computes `[self.x.min(rhs.x), self.y.min(rhs.y), ..]`.
diff --git a/src/u32/uvec3.rs b/src/u32/uvec3.rs
index 18a5bbd..192886e 100644
--- a/src/u32/uvec3.rs
+++ b/src/u32/uvec3.rs
@@ -135,6 +135,12 @@ impl UVec3 {
         (self.x * rhs.x) + (self.y * rhs.y) + (self.z * rhs.z)
     }
 
+    /// 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::splat(self.dot(rhs))
+    }
+
     /// Computes the cross product of `self` and `rhs`.
     #[inline]
     pub fn cross(self, rhs: Self) -> Self {
diff --git a/src/u32/uvec4.rs b/src/u32/uvec4.rs
index aa07f0c..decccf3 100644
--- a/src/u32/uvec4.rs
+++ b/src/u32/uvec4.rs
@@ -133,6 +133,12 @@ impl UVec4 {
         (self.x * rhs.x) + (self.y * rhs.y) + (self.z * rhs.z) + (self.w * rhs.w)
     }
 
+    /// 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::splat(self.dot(rhs))
+    }
+
     /// Returns a vector containing the minimum values for each element of `self` and `rhs`.
     ///
     /// In other words this computes `[self.x.min(rhs.x), self.y.min(rhs.y), ..]`.


Test patch:
diff --git a/tests/vec2.rs b/tests/vec2.rs
index f165982..3ca9acc 100644
--- a/tests/vec2.rs
+++ b/tests/vec2.rs
@@ -268,6 +268,11 @@ macro_rules! impl_vec2_tests {
             );
         });
 
+        glam_test!(test_mask_splat, {
+            assert_eq!($mask::splat(false), $mask::new(false, false));
+            assert_eq!($mask::splat(true), $mask::new(true, true));
+        });
+
         glam_test!(test_mask_bitmask, {
             assert_eq!($mask::new(false, false).bitmask(), 0b00);
             assert_eq!($mask::new(true, false).bitmask(), 0b01);
@@ -473,6 +478,8 @@ macro_rules! impl_vec2_signed_tests {
         glam_test!(test_neg, {
             let a = $new(1 as $t, 2 as $t);
             assert_eq!($new(-1 as $t, -2 as $t), (-a));
+            assert_eq!($new(-0.0 as $t, -0.0 as $t), -$new(0.0 as $t, 0.0 as $t));
+            assert_eq!($new(0.0 as $t, -0.0 as $t), -$new(-0.0 as $t, 0.0 as $t));
         });
 
         glam_test!(test_perp, {
@@ -554,6 +561,7 @@ macro_rules! impl_vec2_float_tests {
             assert_eq!(13.0, (-5.0 * x).distance(12.0 * y));
             assert_eq!(x, (2.0 * x).normalize());
             assert_eq!(1.0 * 3.0 + 2.0 * 4.0, $new(1.0, 2.0).dot($new(3.0, 4.0)));
+            assert_eq!($new(8.0, 8.0), $new(1.0, 2.0).dot_into_vec($new(4.0, 2.0)));
             assert_eq!(2.0 * 2.0 + 3.0 * 3.0, $new(2.0, 3.0).length_squared());
             assert_eq!(
                 (2.0 as $t * 2.0 + 3.0 * 3.0).sqrt(),
@@ -590,7 +598,7 @@ macro_rules! impl_vec2_float_tests {
 
         glam_test!(test_sign, {
             assert_eq!($vec2::ZERO.signum(), $vec2::ONE);
-            assert_eq!(-$vec2::ZERO.signum(), -$vec2::ONE);
+            assert_eq!((-$vec2::ZERO).signum(), -$vec2::ONE);
             assert_eq!($vec2::ONE.signum(), $vec2::ONE);
             assert_eq!((-$vec2::ONE).signum(), -$vec2::ONE);
             assert_eq!($vec2::splat(INFINITY).signum(), $vec2::ONE);
@@ -598,6 +606,17 @@ macro_rules! impl_vec2_float_tests {
             assert!($vec2::splat(NAN).signum().is_nan_mask().all());
         });
 
+        glam_test!(test_is_negative_bitmask, {
+            assert_eq!($vec2::ZERO.is_negative_bitmask(), 0b00);
+            assert_eq!((-$vec2::ZERO).is_negative_bitmask(), 0b11);
+            assert_eq!($vec2::ONE.is_negative_bitmask(), 0b00);
+            assert_eq!((-$vec2::ONE).is_negative_bitmask(), 0b11);
+            assert_eq!($vec2::new(-0.1, 0.2).is_negative_bitmask(), 0b01);
+            assert_eq!($vec2::new(0.8, 0.3).is_negative_bitmask(), 0b00);
+            assert_eq!($vec2::new(0.3, -0.4).is_negative_bitmask(), 0b10);
+            assert_eq!($vec2::new(-0.2, -0.6).is_negative_bitmask(), 0b11);
+        });
+
         glam_test!(test_abs, {
             assert_eq!($vec2::ZERO.abs(), $vec2::ZERO);
             assert_eq!($vec2::ONE.abs(), $vec2::ONE);
diff --git a/tests/vec3.rs b/tests/vec3.rs
index 80956d7..92af042 100644
--- a/tests/vec3.rs
+++ b/tests/vec3.rs
@@ -316,6 +316,11 @@ macro_rules! impl_vec3_tests {
             );
         });
 
+        glam_test!(test_mask_splat, {
+            assert_eq!($mask::splat(false), $mask::new(false, false, false));
+            assert_eq!($mask::splat(true), $mask::new(true, true, true));
+        });
+
         glam_test!(test_mask_bitmask, {
             assert_eq!($mask::new(false, false, false).bitmask(), 0b000);
             assert_eq!($mask::new(true, false, false).bitmask(), 0b001);
@@ -518,6 +523,14 @@ macro_rules! impl_vec3_signed_tests {
         glam_test!(test_neg, {
             let a = $new(1 as $t, 2 as $t, 3 as $t);
             assert_eq!((-1 as $t, -2 as $t, -3 as $t), (-a).into());
+            assert_eq!(
+                $new(-0.0 as $t, -0.0 as $t, -0.0 as $t),
+                -$new(0.0 as $t, 0.0 as $t, 0.0 as $t)
+            );
+            assert_eq!(
+                $new(0.0 as $t, -0.0 as $t, -0.0 as $t),
+                -$new(-0.0 as $t, 0.0 as $t, 0.0 as $t)
+            );
         });
 
         glam_test!(test_dot_signed, {
@@ -598,6 +611,10 @@ macro_rules! impl_vec3_float_tests {
                 1.0 * 4.0 + 2.0 * 5.0 + 3.0 * 6.0,
                 $new(1.0, 2.0, 3.0).dot($new(4.0, 5.0, 6.0))
             );
+            assert_eq!(
+                $new(14.0, 14.0, 14.0),
+                $new(0.0, 4.0, 6.0).dot_into_vec($new(3.0, 2.0, 1.0))
+            );
             assert_eq!(
                 2.0 * 2.0 + 3.0 * 3.0 + 4.0 * 4.0,
                 $new(2.0, 3.0, 4.0).length_squared()
@@ -643,7 +660,7 @@ macro_rules! impl_vec3_float_tests {
 
         glam_test!(test_signum, {
             assert_eq!($vec3::ZERO.signum(), $vec3::ONE);
-            assert_eq!(-$vec3::ZERO.signum(), -$vec3::ONE);
+            assert_eq!((-$vec3::ZERO).signum(), -$vec3::ONE);
             assert_eq!($vec3::ONE.signum(), $vec3::ONE);
             assert_eq!((-$vec3::ONE).signum(), -$vec3::ONE);
             assert_eq!($vec3::splat(INFINITY).signum(), $vec3::ONE);
@@ -651,6 +668,18 @@ macro_rules! impl_vec3_float_tests {
             assert!($vec3::splat(NAN).signum().is_nan_mask().all());
         });
 
+        glam_test!(test_is_negative_bitmask, {
+            assert_eq!($vec3::ZERO.is_negative_bitmask(), 0b000);
+            assert_eq!((-$vec3::ZERO).is_negative_bitmask(), 0b111);
+            assert_eq!($vec3::ONE.is_negative_bitmask(), 0b000);
+            assert_eq!((-$vec3::ONE).is_negative_bitmask(), 0b111);
+            assert_eq!($vec3::new(-0.1, 0.2, 0.3).is_negative_bitmask(), 0b001);
+            assert_eq!($vec3::new(0.8, 0.3, 0.1).is_negative_bitmask(), 0b000);
+            assert_eq!($vec3::new(0.1, 0.5, -0.3).is_negative_bitmask(), 0b100);
+            assert_eq!($vec3::new(0.3, -0.4, 0.1).is_negative_bitmask(), 0b010);
+            assert_eq!($vec3::new(-0.2, 0.6, -0.5).is_negative_bitmask(), 0b101);
+        });
+
         glam_test!(test_abs, {
             assert_eq!($vec3::ZERO.abs(), $vec3::ZERO);
             assert_eq!($vec3::ONE.abs(), $vec3::ONE);
diff --git a/tests/vec4.rs b/tests/vec4.rs
index 60b282a..9d06371 100644
--- a/tests/vec4.rs
+++ b/tests/vec4.rs
@@ -386,6 +386,11 @@ macro_rules! impl_vec4_tests {
             );
         });
 
+        glam_test!(test_mask_splat, {
+            assert_eq!($mask::splat(false), $mask::new(false, false, false, false));
+            assert_eq!($mask::splat(true), $mask::new(true, true, true, true));
+        });
+
         glam_test!(test_mask_bitmask, {
             assert_eq!($mask::new(false, false, false, false).bitmask(), 0b0000);
             assert_eq!($mask::new(false, false, true, true).bitmask(), 0b1100);
@@ -593,6 +598,14 @@ macro_rules! impl_vec4_signed_tests {
         glam_test!(test_neg, {
             let a = $new(1 as $t, 2 as $t, 3 as $t, 4 as $t);
             assert_eq!((-1 as $t, -2 as $t, -3 as $t, -4 as $t), (-a).into());
+            assert_eq!(
+                $new(-0.0 as $t, -0.0 as $t, -0.0 as $t, -0.0 as $t),
+                -$new(0.0 as $t, 0.0 as $t, 0.0 as $t, 0.0 as $t)
+            );
+            assert_eq!(
+                $new(0.0 as $t, -0.0 as $t, -0.0 as $t, -0.0 as $t),
+                -$new(-0.0 as $t, 0.0 as $t, 0.0 as $t, 0.0 as $t)
+            );
         });
 
         glam_test!(test_dot_signed, {
@@ -676,6 +689,10 @@ macro_rules! impl_vec4_float_tests {
                 1.0 * 5.0 + 2.0 * 6.0 + 3.0 * 7.0 + 4.0 * 8.0,
                 $new(1.0, 2.0, 3.0, 4.0).dot($new(5.0, 6.0, 7.0, 8.0))
             );
+            assert_eq!(
+                $new(28.0, 28.0, 28.0, 28.0),
+                $new(0.0, 5.0, 3.0, 6.0).dot_into_vec($new(7.0, 2.0, 4.0, 1.0))
+            );
             assert_eq!(
                 2.0 * 2.0 + 3.0 * 3.0 + 4.0 * 4.0 + 5.0 * 5.0,
                 $new(2.0, 3.0, 4.0, 5.0).length_squared()
@@ -725,7 +742,7 @@ macro_rules! impl_vec4_float_tests {
 
         glam_test!(test_signum, {
             assert_eq!($vec4::ZERO.signum(), $vec4::ONE);
-            assert_eq!(-$vec4::ZERO.signum(), -$vec4::ONE);
+            assert_eq!((-$vec4::ZERO).signum(), -$vec4::ONE);
             assert_eq!($vec4::ONE.signum(), $vec4::ONE);
             assert_eq!((-$vec4::ONE).signum(), -$vec4::ONE);
             assert_eq!($vec4::splat(INFINITY).signum(), $vec4::ONE);
@@ -733,6 +750,33 @@ macro_rules! impl_vec4_float_tests {
             assert!($vec4::splat(NAN).signum().is_nan_mask().all());
         });
 
+        glam_test!(test_is_negative_bitmask, {
+            assert_eq!($vec4::ZERO.is_negative_bitmask(), 0b0000);
+            assert_eq!((-$vec4::ZERO).is_negative_bitmask(), 0b1111);
+            assert_eq!($vec4::ONE.is_negative_bitmask(), 0b0000);
+            assert_eq!((-$vec4::ONE).is_negative_bitmask(), 0b1111);
+            assert_eq!(
+                $vec4::new(-0.1, 0.2, 0.3, -0.4).is_negative_bitmask(),
+                0b1001
+            );
+            assert_eq!(
+                $vec4::new(0.8, 0.3, 0.1, -0.0).is_negative_bitmask(),
+                0b1000
+            );
+            assert_eq!(
+                $vec4::new(0.1, 0.5, -0.3, 0.7).is_negative_bitmask(),
+                0b0100
+            );
+            assert_eq!(
+                $vec4::new(0.3, -0.4, 0.1, 0.6).is_negative_bitmask(),
+                0b0010
+            );
+            assert_eq!(
+                $vec4::new(0.2, -0.6, 0.5, -0.3).is_negative_bitmask(),
+                0b1010
+            );
+        });
+
         glam_test!(test_abs, {
             assert_eq!($vec4::ZERO.abs(), $vec4::ZERO);
             assert_eq!($vec4::ONE.abs(), $vec4::ONE);