Repository: bitshifter/glam-rs
Pull request #201 (2021-06-19 11:50:25)

Issue 196


Patch:
diff --git a/src/affine3.rs b/src/affine3.rs
index 448916b..866b3ba 100644
--- a/src/affine3.rs
+++ b/src/affine3.rs
@@ -259,6 +259,11 @@ macro_rules! impl_affine3_methods {
             ///
             /// The transform is expected to be non-degenerate and without shearing, or the output
             /// will be invalid.
+            ///
+            /// # Panics
+            ///
+            /// Will panic if the determinant `self.matrix3` is zero or if the resulting scale
+            /// vector containts any zero elements when `glam_assert` is enabled.
             #[inline(always)]
             pub fn to_scale_rotation_translation(&self) -> ($vec3, $quat, $vec3) {
                 // TODO: migrate to core module
@@ -303,6 +308,10 @@ macro_rules! impl_affine3_methods {
             /// a focal point.
             ///
             /// For a view coordinate system with `+X=right`, `+Y=up` and `+Z=forward`.
+            ///
+            /// # Panics
+            ///
+            /// Will panic if `up` is not normalized when `glam_assert` is enabled.
             #[inline]
             pub fn look_at_lh(eye: $vec3, center: $vec3, up: $vec3) -> Self {
                 glam_assert!(up.is_normalized());
@@ -313,6 +322,10 @@ macro_rules! impl_affine3_methods {
             /// a focal point.
             ///
             /// For a view coordinate system with `+X=right`, `+Y=up` and `+Z=back`.
+            ///
+            /// # Panics
+            ///
+            /// Will panic if `up` is not normalized when `glam_assert` is enabled.
             #[inline]
             pub fn look_at_rh(eye: $vec3, center: $vec3, up: $vec3) -> Self {
                 glam_assert!(up.is_normalized());
diff --git a/src/core/sse2/matrix.rs b/src/core/sse2/matrix.rs
index 3fcf3a5..44a33cd 100644
--- a/src/core/sse2/matrix.rs
+++ b/src/core/sse2/matrix.rs
@@ -129,6 +129,7 @@ impl FloatMatrix2x2<f32, XY<f32>> for __m128 {
             let sub = _mm_sub_ps(prod, _mm_shuffle_ps(prod, prod, 0b01_01_01_01));
             let det = _mm_shuffle_ps(sub, sub, 0b00_00_00_00);
             let tmp = _mm_div_ps(SIGN, det);
+            glam_assert!(tmp.is_finite());
             let dbca = _mm_shuffle_ps(abcd, abcd, 0b00_10_01_11);
             _mm_mul_ps(dbca, tmp)
         }
diff --git a/src/core/sse2/quaternion.rs b/src/core/sse2/quaternion.rs
index cac7bb6..6e172c0 100644
--- a/src/core/sse2/quaternion.rs
+++ b/src/core/sse2/quaternion.rs
@@ -76,6 +76,8 @@ impl Quaternion<f32> for __m128 {
 
     #[inline]
     fn mul_quaternion(self, other: Self) -> Self {
+        glam_assert!(FloatVector4::is_normalized(self));
+        glam_assert!(FloatVector4::is_normalized(other));
         unsafe {
             // Based on https://github.com/nfrechette/rtm `rtm::quat_mul`
             let lhs = self;
diff --git a/src/core/sse2/vector.rs b/src/core/sse2/vector.rs
index 8b3bc44..2f6f467 100644
--- a/src/core/sse2/vector.rs
+++ b/src/core/sse2/vector.rs
@@ -689,8 +689,10 @@ impl FloatVector3<f32> for __m128 {
     #[inline]
     fn normalize(self) -> Self {
         unsafe {
-            let dot = Vector3::dot_into_vec(self, self);
-            _mm_div_ps(self, _mm_sqrt_ps(dot))
+            let length = _mm_sqrt_ps(Vector3::dot_into_vec(self, self));
+            let normalized = _mm_div_ps(self, length);
+            glam_assert!(FloatVector3::is_finite(normalized));
+            normalized
         }
     }
 }
@@ -780,7 +782,9 @@ impl FloatVector4<f32> for __m128 {
     fn normalize(self) -> Self {
         unsafe {
             let dot = Vector4::dot_into_vec(self, self);
-            _mm_div_ps(self, _mm_sqrt_ps(dot))
+            let normalized = _mm_div_ps(self, _mm_sqrt_ps(dot));
+            glam_assert!(FloatVector4::is_finite(normalized));
+            normalized
         }
     }
 }
diff --git a/src/core/traits/matrix.rs b/src/core/traits/matrix.rs
index 2b44210..aee7193 100644
--- a/src/core/traits/matrix.rs
+++ b/src/core/traits/matrix.rs
@@ -718,7 +718,6 @@ pub trait FloatMatrix4x4<T: FloatEx, V4: FloatVector4<T> + Quaternion<T>>:
 
     #[inline]
     fn from_quaternion(rotation: V4) -> Self {
-        glam_assert!(rotation.is_normalized());
         let (x_axis, y_axis, z_axis) = Self::quaternion_to_axes(rotation);
         Self::from_cols(x_axis, y_axis, z_axis, V4::W)
     }
@@ -750,7 +749,6 @@ pub trait FloatMatrix4x4<T: FloatEx, V4: FloatVector4<T> + Quaternion<T>>:
 
     #[inline]
     fn from_scale_quaternion_translation(scale: XYZ<T>, rotation: V4, translation: XYZ<T>) -> Self {
-        glam_assert!(rotation.is_normalized());
         let (x_axis, y_axis, z_axis) = Self::quaternion_to_axes(rotation);
         Self::from_cols(
             x_axis.mul_scalar(scale.x),
@@ -762,7 +760,6 @@ pub trait FloatMatrix4x4<T: FloatEx, V4: FloatVector4<T> + Quaternion<T>>:
 
     #[inline]
     fn from_quaternion_translation(rotation: V4, translation: XYZ<T>) -> Self {
-        glam_assert!(rotation.is_normalized());
         let (x_axis, y_axis, z_axis) = Self::quaternion_to_axes(rotation);
         Self::from_cols(x_axis, y_axis, z_axis, V4::from_xyz(translation, T::ONE))
     }
diff --git a/src/core/traits/projection.rs b/src/core/traits/projection.rs
index abf2f8d..8fae4a4 100644
--- a/src/core/traits/projection.rs
+++ b/src/core/traits/projection.rs
@@ -83,6 +83,7 @@ pub trait ProjectionMatrix<T: FloatEx, V4: FloatVector4<T> + Quaternion<T>>:
     /// Creates an infinite right-handed perspective projection matrix with
     /// [0,1] depth range.
     fn perspective_infinite_rh(fov_y_radians: T, aspect_ratio: T, z_near: T) -> Self {
+        glam_assert!(z_near > T::ZERO);
         let f = T::ONE / (T::HALF * fov_y_radians).tan();
         Self::from_cols(
             V4::new(f / aspect_ratio, T::ZERO, T::ZERO, T::ZERO),
@@ -95,6 +96,7 @@ pub trait ProjectionMatrix<T: FloatEx, V4: FloatVector4<T> + Quaternion<T>>:
     /// Creates an infinite reverse right-handed perspective projection matrix
     /// with [0,1] depth range.
     fn perspective_infinite_reverse_rh(fov_y_radians: T, aspect_ratio: T, z_near: T) -> Self {
+        glam_assert!(z_near > T::ZERO);
         let f = T::ONE / (T::HALF * fov_y_radians).tan();
         Self::from_cols(
             V4::new(f / aspect_ratio, T::ZERO, T::ZERO, T::ZERO),
diff --git a/src/core/traits/vector.rs b/src/core/traits/vector.rs
index 3e11080..a782dfe 100644
--- a/src/core/traits/vector.rs
+++ b/src/core/traits/vector.rs
@@ -549,7 +549,9 @@ pub trait FloatVector2<T: FloatEx>: SignedVector2<T> {
 
     #[inline]
     fn normalize(self) -> Self {
-        self.mul_scalar(self.length_recip())
+        let normalized = self.mul_scalar(self.length_recip());
+        glam_assert!(normalized.is_finite());
+        normalized
     }
 
     #[inline(always)]
@@ -645,7 +647,9 @@ pub trait FloatVector3<T: FloatEx>: SignedVector3<T> {
 
     #[inline]
     fn normalize(self) -> Self {
-        self.mul_scalar(self.length_recip())
+        let normalized = self.mul_scalar(self.length_recip());
+        glam_assert!(normalized.is_finite());
+        normalized
     }
 
     #[inline(always)]
@@ -770,7 +774,9 @@ pub trait FloatVector4<T: FloatEx>: SignedVector4<T> {
 
     #[inline]
     fn normalize(self) -> Self {
-        self.mul_scalar(self.length_recip())
+        let normalized = self.mul_scalar(self.length_recip());
+        glam_assert!(normalized.is_finite());
+        normalized
     }
 
     #[inline(always)]
diff --git a/src/mat2.rs b/src/mat2.rs
index f6dba19..6fb4ebd 100644
--- a/src/mat2.rs
+++ b/src/mat2.rs
@@ -188,6 +188,10 @@ macro_rules! impl_mat2_methods {
         /// Returns the inverse of `self`.
         ///
         /// If the matrix is not invertible the returned matrix will be invalid.
+        ///
+        /// # Panics
+        ///
+        /// Will panic if the determinant of `self` is zero when `glam_assert` is enabled.
         #[must_use]
         #[inline(always)]
         pub fn inverse(&self) -> Self {
diff --git a/src/mat3.rs b/src/mat3.rs
index 6ac20ff..a38ebb8 100644
--- a/src/mat3.rs
+++ b/src/mat3.rs
@@ -110,6 +110,10 @@ macro_rules! impl_mat3_methods {
 
         #[inline(always)]
         /// Creates a 3D rotation matrix from the given quaternion.
+        ///
+        /// # Panics
+        ///
+        /// Will panic if `rotation` is not normalized when `glam_assert` is enabled.
         pub fn from_quat(rotation: $quat) -> Self {
             // TODO: SIMD?
             Self($inner::from_quaternion(rotation.0.into()))
@@ -117,6 +121,10 @@ macro_rules! impl_mat3_methods {
 
         /// Creates a 3D rotation matrix from a normalized rotation `axis` and `angle` (in
         /// radians).
+        ///
+        /// # Panics
+        ///
+        /// Will panic if `axis` is not normalized when `glam_assert` is enabled.
         #[inline(always)]
         pub fn from_axis_angle(axis: $vec3, angle: $t) -> Self {
             Self(FloatMatrix3x3::from_axis_angle(axis.0, angle))
@@ -194,6 +202,10 @@ macro_rules! impl_mat3_methods {
         ///
         /// The resulting matrix can be used to transform 2D points and vectors. See
         /// [`Self::transform_point2()`] and [`Self::transform_vector2()`].
+        ///
+        /// # Panics
+        ///
+        /// Will panic if all elements of `scale` are zero when `glam_assert` is enabled.
         #[inline(always)]
         pub fn from_scale(scale: $vec2) -> Self {
             Self(Matrix3x3::from_scale(scale.0))
@@ -302,6 +314,10 @@ macro_rules! impl_mat3_methods {
         /// Returns the inverse of `self`.
         ///
         /// If the matrix is not invertible the returned matrix will be invalid.
+        ///
+        /// # Panics
+        ///
+        /// Will panic if the determinant of `self` is zero when `glam_assert` is enabled.
         #[must_use]
         #[inline(always)]
         pub fn inverse(&self) -> Self {
diff --git a/src/mat4.rs b/src/mat4.rs
index 51b80f4..e1eedac 100644
--- a/src/mat4.rs
+++ b/src/mat4.rs
@@ -118,6 +118,10 @@ macro_rules! impl_mat4_methods {
         ///
         /// The resulting matrix can be used to transform 3D points and vectors. See
         /// [`Self::transform_point3()`] and [`Self::transform_vector3()`].
+        ///
+        /// # Panics
+        ///
+        /// Will panic if `rotation` is not normalized when `glam_assert` is enabled.
         #[inline(always)]
         pub fn from_scale_rotation_translation(
             scale: $vec3,
@@ -135,6 +139,10 @@ macro_rules! impl_mat4_methods {
         ///
         /// The resulting matrix can be used to transform 3D points and vectors. See
         /// [`Self::transform_point3()`] and [`Self::transform_vector3()`].
+        ///
+        /// # Panics
+        ///
+        /// Will panic if `rotation` is not normalized when `glam_assert` is enabled.
         #[inline(always)]
         pub fn from_rotation_translation(rotation: $quat, translation: $vec3) -> Self {
             Self($inner::from_quaternion_translation(
@@ -145,6 +153,11 @@ macro_rules! impl_mat4_methods {
 
         /// Extracts `scale`, `rotation` and `translation` from `self`. The input matrix is
         /// expected to be a 3D affine transformation matrix otherwise the output will be invalid.
+        ///
+        /// # Panics
+        ///
+        /// Will panic if the determinant of `self` is zero or if the resulting scale vector
+        /// contains any zero elements when `glam_assert` is enabled.
         #[inline(always)]
         pub fn to_scale_rotation_translation(&self) -> ($vec3, $quat, $vec3) {
             let (scale, rotation, translation) = self.0.to_scale_quaternion_translation();
@@ -155,6 +168,10 @@ macro_rules! impl_mat4_methods {
         ///
         /// The resulting matrix can be used to transform 3D points and vectors. See
         /// [`Self::transform_point3()`] and [`Self::transform_vector3()`].
+        ///
+        /// # Panics
+        ///
+        /// Will panic if `rotation` is not normalized when `glam_assert` is enabled.
         #[inline(always)]
         pub fn from_quat(rotation: $quat) -> Self {
             Self($inner::from_quaternion(rotation.0))
@@ -174,6 +191,10 @@ macro_rules! impl_mat4_methods {
         ///
         /// The resulting matrix can be used to transform 3D points and vectors. See
         /// [`Self::transform_point3()`] and [`Self::transform_vector3()`].
+        ///
+        /// # Panics
+        ///
+        /// Will panic if `axis` is not normalized when `glam_assert` is enabled.
         #[inline(always)]
         pub fn from_axis_angle(axis: $vec3, angle: $t) -> Self {
             Self($inner::from_axis_angle(axis.0, angle))
@@ -233,6 +254,10 @@ macro_rules! impl_mat4_methods {
         ///
         /// The resulting matrix can be used to transform 3D points and vectors. See
         /// [`Self::transform_point3()`] and [`Self::transform_vector3()`].
+        ///
+        /// # Panics
+        ///
+        /// Will panic if all elements of `scale` are zero when `glam_assert` is enabled.
         #[inline(always)]
         pub fn from_scale(scale: $vec3) -> Self {
             Self($inner::from_scale(scale.0))
@@ -350,6 +375,10 @@ macro_rules! impl_mat4_methods {
         /// Returns the inverse of `self`.
         ///
         /// If the matrix is not invertible the returned matrix will be invalid.
+        ///
+        /// # Panics
+        ///
+        /// Will panic if the determinant of `self` is zero when `glam_assert` is enabled.
         #[must_use]
         #[inline(always)]
         pub fn inverse(&self) -> Self {
@@ -359,6 +388,10 @@ macro_rules! impl_mat4_methods {
         /// Creates a left-handed view matrix using a camera position, an up direction, and a focal
         /// point.
         /// For a view coordinate system with `+X=right`, `+Y=up` and `+Z=forward`.
+        ///
+        /// # Panics
+        ///
+        /// Will panic if `up` is not normalized when `glam_assert` is enabled.
         #[inline(always)]
         pub fn look_at_lh(eye: $vec3, center: $vec3, up: $vec3) -> Self {
             Self($inner::look_at_lh(eye.0, center.0, up.0))
@@ -367,6 +400,10 @@ macro_rules! impl_mat4_methods {
         /// Creates a right-handed view matrix using a camera position, an up direction, and a focal
         /// point.
         /// For a view coordinate system with `+X=right`, `+Y=up` and `+Z=back`.
+        ///
+        /// # Panics
+        ///
+        /// Will panic if `up` is not normalized when `glam_assert` is enabled.
         #[inline(always)]
         pub fn look_at_rh(eye: $vec3, center: $vec3, up: $vec3) -> Self {
             Self($inner::look_at_rh(eye.0, center.0, up.0))
@@ -374,7 +411,7 @@ macro_rules! impl_mat4_methods {
 
         /// Creates a right-handed perspective projection matrix with [-1,1] depth range.
         /// This is the same as the OpenGL `gluPerspective` function.
-        /// See https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/gluPerspective.xml
+        /// See <https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/gluPerspective.xml>
         #[inline(always)]
         pub fn perspective_rh_gl(
             fov_y_radians: $t,
@@ -391,6 +428,11 @@ macro_rules! impl_mat4_methods {
         }
 
         /// Creates a left-handed perspective projection matrix with `[0,1]` depth range.
+        ///
+        /// # Panics
+        ///
+        /// Will panic if `z_near` or `z_far` are less than or equal to zero when `glam_assert` is
+        /// enabled.
         #[inline(always)]
         pub fn perspective_lh(fov_y_radians: $t, aspect_ratio: $t, z_near: $t, z_far: $t) -> Self {
             Self($inner::perspective_lh(
@@ -402,6 +444,11 @@ macro_rules! impl_mat4_methods {
         }
 
         /// Creates a right-handed perspective projection matrix with `[0,1]` depth range.
+        ///
+        /// # Panics
+        ///
+        /// Will panic if `z_near` or `z_far` are less than or equal to zero when `glam_assert` is
+        /// enabled.
         #[inline(always)]
         pub fn perspective_rh(fov_y_radians: $t, aspect_ratio: $t, z_near: $t, z_far: $t) -> Self {
             Self($inner::perspective_rh(
@@ -413,6 +460,10 @@ macro_rules! impl_mat4_methods {
         }
 
         /// Creates an infinite left-handed perspective projection matrix with `[0,1]` depth range.
+        ///
+        /// # Panics
+        ///
+        /// Will panic if `z_near` is less than or equal to zero when `glam_assert` is enabled.
         #[inline(always)]
         pub fn perspective_infinite_lh(fov_y_radians: $t, aspect_ratio: $t, z_near: $t) -> Self {
             Self($inner::perspective_infinite_lh(
@@ -423,6 +474,10 @@ macro_rules! impl_mat4_methods {
         }
 
         /// Creates an infinite left-handed perspective projection matrix with `[0,1]` depth range.
+        ///
+        /// # Panics
+        ///
+        /// Will panic if `z_near` is less than or equal to zero when `glam_assert` is enabled.
         #[inline(always)]
         pub fn perspective_infinite_reverse_lh(
             fov_y_radians: $t,
@@ -465,7 +520,7 @@ macro_rules! impl_mat4_methods {
         /// Creates a right-handed orthographic projection matrix with `[-1,1]` depth
         /// range.  This is the same as the OpenGL `glOrtho` function in OpenGL.
         /// See
-        /// https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glOrtho.xml
+        /// <https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glOrtho.xml>
         #[inline(always)]
         pub fn orthographic_rh_gl(
             left: $t,
@@ -555,6 +610,10 @@ macro_rules! impl_mat4_methods {
         /// This method assumes that `self` contains a valid affine transform. It does not perform
         /// a persective divide, if `self` contains a perspective transform, or if you are unsure,
         /// the [`Self::project_point3()`] method should be used instead.
+        ///
+        /// # Panics
+        ///
+        /// Will panic if the 3rd row of `self` is not `(0, 0, 0, 1)` when `glam_assert` is enabled.
         #[inline]
         pub fn transform_point3(&self, other: $vec3) -> $vec3 {
             glam_assert!(self.row(3) == $vec4::W);
@@ -567,6 +626,10 @@ macro_rules! impl_mat4_methods {
         /// `0.0`.
         ///
         /// This method assumes that `self` contains a valid affine transform.
+        ///
+        /// # Panics
+        ///
+        /// Will panic if the 3rd row of `self` is not `(0, 0, 0, 1)` when `glam_assert` is enabled.
         #[inline]
         pub fn transform_vector3(&self, other: $vec3) -> $vec3 {
             glam_assert!(self.row(3) == $vec4::W);
diff --git a/src/quat.rs b/src/quat.rs
index d0fd26e..76a6800 100644
--- a/src/quat.rs
+++ b/src/quat.rs
@@ -114,6 +114,10 @@ macro_rules! impl_quat_methods {
 
         /// Create a quaternion for a normalized rotation `axis` and `angle` (in radians).
         /// The axis must be normalized (unit-length).
+        ///
+        /// # Panics
+        ///
+        /// Will panic if `axis` is not normalized when `glam_assert` is enabled.
         #[inline(always)]
         pub fn from_axis_angle(axis: $vec3, angle: $t) -> Self {
             Self($inner::from_axis_angle(axis.0, angle))
@@ -196,9 +200,8 @@ macro_rules! impl_quat_methods {
             ))
         }
 
-        /// Gets the minimal rotation for transforming `from` to `to`.
-        /// The rotation is in the plane spanned by the two vectors.
-        /// Will rotate at most 180 degrees.
+        /// Gets the minimal rotation for transforming `from` to `to`.  The rotation is in the
+        /// plane spanned by the two vectors.  Will rotate at most 180 degrees.
         ///
         /// The input vectors must be normalized (unit-length).
         ///
@@ -206,6 +209,10 @@ macro_rules! impl_quat_methods {
         ///
         /// For near-singular cases (from≈to and from≈-to) the current implementation
         /// is only accurate to about 0.001 (for `f32`).
+        ///
+        /// # Panics
+        ///
+        /// Will panic if `from` or `to` are not normalized when `glam_assert` is enabled.
         #[cfg(feature = "std")]
         pub fn from_rotation_arc(from: $vec3, to: $vec3) -> Self {
             glam_assert!(from.is_normalized());
@@ -226,15 +233,19 @@ macro_rules! impl_quat_methods {
             }
         }
 
-        /// Gets the minimal rotation for transforming `from` to either `to` or `-to`.
-        /// This means that the resulting quaternion will rotate `from` so that it is colinear with `to`.
+        /// Gets the minimal rotation for transforming `from` to either `to` or `-to`.  This means
+        /// that the resulting quaternion will rotate `from` so that it is colinear with `to`.
         ///
-        /// The rotation is in the plane spanned by the two vectors.
-        /// Will rotate at most 90 degrees.
+        /// The rotation is in the plane spanned by the two vectors.  Will rotate at most 90
+        /// degrees.
         ///
         /// The input vectors must be normalized (unit-length).
         ///
         /// `to.dot(from_rotation_arc_colinear(from, to) * from).abs() ≈ 1`.
+        ///
+        /// # Panics
+        ///
+        /// Will panic if `from` or `to` are not normalized when `glam_assert` is enabled.
         #[cfg(feature = "std")]
         pub fn from_rotation_arc_colinear(from: $vec3, to: $vec3) -> Self {
             if from.dot(to) < 0.0 {
@@ -277,6 +288,10 @@ macro_rules! impl_quat_methods {
         /// Typically quaternion inverse returns the conjugate of a normalized quaternion.
         /// Because `self` is assumed to already be unit length this method *does not* normalize
         /// before returning the conjugate.
+        ///
+        /// # Panics
+        ///
+        /// Will panic if `self` is not normalized when `glam_assert` is enabled.
         #[must_use]
         #[inline(always)]
         pub fn inverse(self) -> Self {
@@ -319,6 +334,10 @@ macro_rules! impl_quat_methods {
         /// Returns `self` normalized to length 1.0.
         ///
         /// For valid results, `self` must _not_ be of length zero.
+        ///
+        /// Panics
+        ///
+        /// Will panic if `self` is zero length when `glam_assert` is enabled.
         #[must_use]
         #[inline(always)]
         pub fn normalize(self) -> Self {
@@ -354,6 +373,10 @@ macro_rules! impl_quat_methods {
         /// for transforming this quaternion into another.
         ///
         /// Both quaternions must be normalized.
+        ///
+        /// # Panics
+        ///
+        /// Will panic if `self` or `other` are not normalized when `glam_assert` is enabled.
         pub fn angle_between(self, other: Self) -> $t {
             glam_assert!(self.is_normalized() && other.is_normalized());
             use crate::core::traits::scalar::FloatEx;
@@ -379,6 +402,10 @@ macro_rules! impl_quat_methods {
         ///
         /// When `s` is `0.0`, the result will be equal to `self`.  When `s`
         /// is `1.0`, the result will be equal to `other`.
+        ///
+        /// # Panics
+        ///
+        /// Will panic if `self` or `end` are not normalized when `glam_assert` is enabled.
         #[inline(always)]
         #[cfg_attr(docsrs, doc(alias = "mix"))]
         pub fn lerp(self, end: Self, s: $t) -> Self {
@@ -397,20 +424,33 @@ macro_rules! impl_quat_methods {
         /// one will take the short way. In order to correct for this, the `dot`
         /// product between `self` and `end` should be positive. If the `dot`
         /// product is negative, slerp between `-self` and `end`.
+        ///
+        /// # Panics
+        ///
+        /// Will panic if `self` or `end` are not normalized when `glam_assert` is enabled.
         #[inline(always)]
         pub fn slerp(self, end: Self, s: $t) -> Self {
             Self(self.0.slerp(end.0, s))
         }
 
         /// Multiplies a quaternion and a 3D vector, returning the rotated vector.
+        ///
+        /// # Panics
+        ///
+        /// Will panic if `self` is not normalized when `glam_assert` is enabled.
         #[inline(always)]
         pub fn mul_vec3(self, other: $vec3) -> $vec3 {
             $vec3(self.0.mul_vector3(other.0))
         }
 
-        /// Multiplies two quaternions.
-        /// If they each represent a rotation, the result will represent the combined rotation.
+        /// Multiplies two quaternions. If they each represent a rotation, the result will
+        /// represent the combined rotation.
+        ///
         /// Note that due to floating point rounding the result may not be perfectly normalized.
+        ///
+        /// # Panics
+        ///
+        /// Will panic if `self` or `other` are not normalized when `glam_assert` is enabled.
         #[inline(always)]
         pub fn mul_quat(self, other: Self) -> Self {
             Self(self.0.mul_quaternion(other.0))
@@ -451,10 +491,11 @@ macro_rules! impl_quat_traits {
         impl Add<$quat> for $quat {
             type Output = Self;
             /// Adds two quaternions.
+            ///
             /// The sum is not guaranteed to be normalized.
             ///
-            /// NB: Addition is not the same as combining the rotations represented by the two quaternions!
-            /// That corresponds to multiplication.
+            /// Note that addition is not the same as combining the rotations represented by the
+            /// two quaternions! That corresponds to multiplication.
             #[inline]
             fn add(self, other: Self) -> Self {
                 Self(self.0.add(other.0))
@@ -464,6 +505,7 @@ macro_rules! impl_quat_traits {
         impl Sub<$quat> for $quat {
             type Output = Self;
             /// Subtracts the other quaternion from self.
+            ///
             /// The difference is not guaranteed to be normalized.
             #[inline]
             fn sub(self, other: Self) -> Self {
@@ -474,6 +516,7 @@ macro_rules! impl_quat_traits {
         impl Mul<$t> for $quat {
             type Output = Self;
             /// Multiplies a quaternion by a scalar value.
+            ///
             /// The product is not guaranteed to be normalized.
             #[inline]
             fn mul(self, other: $t) -> Self {
@@ -493,6 +536,15 @@ macro_rules! impl_quat_traits {
 
         impl Mul<$quat> for $quat {
             type Output = Self;
+            /// Multiplies two quaternions. If they each represent a rotation, the result will
+            /// represent the combined rotation.
+            ///
+            /// Note that due to floating point rounding the result may not be perfectly
+            /// normalized.
+            ///
+            /// # Panics
+            ///
+            /// Will panic if `self` or `other` are not normalized when `glam_assert` is enabled.
             #[inline]
             fn mul(self, other: Self) -> Self {
                 Self(self.0.mul_quaternion(other.0))
@@ -500,6 +552,15 @@ macro_rules! impl_quat_traits {
         }
 
         impl MulAssign<$quat> for $quat {
+            /// Multiplies two quaternions. If they each represent a rotation, the result will
+            /// represent the combined rotation.
+            ///
+            /// Note that due to floating point rounding the result may not be perfectly
+            /// normalized.
+            ///
+            /// # Panics
+            ///
+            /// Will panic if `self` or `other` are not normalized when `glam_assert` is enabled.
             #[inline]
             fn mul_assign(&mut self, other: Self) {
                 self.0 = self.0.mul_quaternion(other.0);
@@ -508,6 +569,11 @@ macro_rules! impl_quat_traits {
 
         impl Mul<$vec3> for $quat {
             type Output = $vec3;
+            /// Multiplies a quaternion and a 3D vector, returning the rotated vector.
+            ///
+            /// # Panics
+            ///
+            /// Will panic if `self` is not normalized when `glam_assert` is enabled.
             #[inline]
             fn mul(self, other: $vec3) -> Self::Output {
                 $vec3(self.0.mul_vector3(other.0))
diff --git a/src/vec.rs b/src/vec.rs
index df2a8dc..7332590 100644
--- a/src/vec.rs
+++ b/src/vec.rs
@@ -44,8 +44,9 @@ macro_rules! impl_vecn_common_methods {
         ///
         /// Each element in `min` must be less-or-equal to the corresponing element in `max`.
         ///
-        /// If the `glam-assert` feature is enabled, the function will panic if the contract is not
-        /// met, otherwise the behavior is undefined.
+        /// # Panics
+        ///
+        /// Will panic if `min` is greater than `max` when `glam_assert` is enabled.
         #[inline(always)]
         pub fn clamp(self, min: Self, max: Self) -> Self {
             Self($vectrait::clamp(self.0, min.0, max.0))
@@ -260,6 +261,10 @@ macro_rules! impl_vecn_float_methods {
         /// For valid results, `self` must _not_ be of length zero, nor very close to zero.
         ///
         /// See also [`Self::try_normalize`] and [`Self::normalize_or_zero`].
+        ///
+        /// Panics
+        ///
+        /// Will panic if `self` is zero length when `glam_assert` is enabled.
         #[must_use]
         #[inline(always)]
         pub fn normalize(self) -> Self {
diff --git a/src/vec3.rs b/src/vec3.rs
index e381270..4f77e68 100644
--- a/src/vec3.rs
+++ b/src/vec3.rs
@@ -202,6 +202,10 @@ macro_rules! impl_vec3_float_methods {
 
         /// Returns any unit-length vector that is orthogonal to the given one.
         /// The input vector must be finite and non-zero.
+        ///
+        /// # Panics
+        ///
+        /// Will panic if `self` is not normalized when `glam_assert` is enabled.
         #[inline]
         #[cfg(feature = "std")]
         pub fn any_orthonormal_vector(&self) -> Self {
@@ -215,6 +219,10 @@ macro_rules! impl_vec3_float_methods {
 
         /// Given a unit-length vector return two other vectors that together form an orthonormal basis.
         /// That is, all three vectors are orthogonal to each other and are normalized.
+        ///
+        /// # Panics
+        ///
+        /// Will panic if `self` is not normalized when `glam_assert` is enabled.
         #[inline]
         #[cfg(feature = "std")]
         pub fn any_orthonormal_pair(&self) -> (Self, Self) {


Test patch:
diff --git a/tests/affine2.rs b/tests/affine2.rs
index 221e9f2..6bcf352 100644
--- a/tests/affine2.rs
+++ b/tests/affine2.rs
@@ -92,6 +92,8 @@ macro_rules! impl_affine2_tests {
             let m_inv = m.inverse();
             assert_approx_eq!($affine2::IDENTITY, m * m_inv, 1.0e-5);
             assert_approx_eq!($affine2::IDENTITY, m_inv * m, 1.0e-5);
+
+            should_glam_assert!({ $affine2::ZERO.inverse() });
         }
 
         #[test]
@@ -126,6 +128,9 @@ macro_rules! impl_affine2_tests {
                 m,
                 $affine2::from_cols(MATRIX2D[0].into(), MATRIX2D[1].into(), MATRIX2D[2].into())
             );
+
+            should_panic!({ $affine2::from_cols_slice(&[0.0; 5]) });
+            should_panic!({ $affine2::IDENTITY.write_cols_to_slice(&mut [0.0; 5]) });
         }
 
         #[cfg(feature = "std")]
diff --git a/tests/affine3.rs b/tests/affine3.rs
index d9ccddb..9351a6e 100644
--- a/tests/affine3.rs
+++ b/tests/affine3.rs
@@ -49,6 +49,9 @@ macro_rules! impl_affine3_tests {
             let rot_z1 = $affine3::from_rotation_z(deg(180.0));
             let rot_z2 = $affine3::from_axis_angle($vec3::Z, deg(180.0));
             assert_approx_eq!(rot_z1, rot_z2, eps);
+
+            should_glam_assert!({ $affine3::from_axis_angle($vec3::ZERO, 0.0) });
+            should_glam_assert!({ $affine3::from_quat($quat::IDENTITY * 2.0) });
         }
 
         #[test]
@@ -111,6 +114,8 @@ macro_rules! impl_affine3_tests {
             let m_inv = m.inverse();
             assert_approx_eq!($affine3::IDENTITY, m * m_inv, 1.0e-5);
             assert_approx_eq!($affine3::IDENTITY, m_inv * m, 1.0e-5);
+
+            should_glam_assert!({ $affine3::ZERO.inverse() });
         }
 
         #[test]
@@ -200,6 +205,9 @@ macro_rules! impl_affine3_tests {
             let point = $vec3::new(1.0, 0.0, 0.0);
             assert_approx_eq!(lh.transform_point3(point), $vec3::new(0.0, 1.0, 5.0));
             assert_approx_eq!(rh.transform_point3(point), $vec3::new(0.0, 1.0, -5.0));
+
+            should_glam_assert!({ $affine3::look_at_lh($vec3::ONE, $vec3::ZERO, $vec3::ZERO) });
+            should_glam_assert!({ $affine3::look_at_rh($vec3::ONE, $vec3::ZERO, $vec3::ZERO) });
         }
 
         #[test]
@@ -249,6 +257,9 @@ macro_rules! impl_affine3_tests {
                     MATRIX2D[3].into()
                 )
             );
+
+            should_panic!({ $affine3::from_cols_slice(&[0.0; 11]) });
+            should_panic!({ $affine3::IDENTITY.write_cols_to_slice(&mut [0.0; 11]) });
         }
 
         #[cfg(feature = "std")]
diff --git a/tests/mat2.rs b/tests/mat2.rs
index 93bdd7f..6608451 100644
--- a/tests/mat2.rs
+++ b/tests/mat2.rs
@@ -51,6 +51,13 @@ macro_rules! impl_mat2_tests {
             *m.col_mut(1) = m.col(1).yx();
             assert_eq!($vec2::new(2.0, 1.0), m.col(0));
             assert_eq!($vec2::new(4.0, 3.0), m.col(1));
+
+            should_panic!({ $mat2::ZERO.col(2) });
+            should_panic!({
+                let mut m = $mat2::ZERO;
+                m.col_mut(2);
+            });
+            should_panic!({ $mat2::ZERO.row(2) });
         }
 
         #[test]
@@ -147,6 +154,8 @@ macro_rules! impl_mat2_tests {
             assert_approx_eq!($mat2::IDENTITY, m * m_inv);
             assert_approx_eq!($mat2::IDENTITY, m_inv * m);
             assert_approx_eq!(m_inv, rot_inv * scale_inv);
+
+            should_glam_assert!({ $mat2::ZERO.inverse() });
         }
 
         #[test]
@@ -191,6 +200,9 @@ macro_rules! impl_mat2_tests {
             let mut out: [$t; 4] = Default::default();
             m.write_cols_to_slice(&mut out);
             assert_eq!(MATRIX1D, out);
+
+            should_panic!({ $mat2::from_cols_slice(&[0.0; 3]) });
+            should_panic!({ $mat2::IDENTITY.write_cols_to_slice(&mut [0.0; 3]) });
         }
 
         #[cfg(feature = "std")]
diff --git a/tests/mat3.rs b/tests/mat3.rs
index c435d90..86f9fc0 100644
--- a/tests/mat3.rs
+++ b/tests/mat3.rs
@@ -2,7 +2,7 @@
 mod support;
 
 macro_rules! impl_mat3_tests {
-    ($t:ident, $const_new:ident, $newmat3:ident, $mat3:ident, $mat4:ident, $newvec3:ident, $vec3:ident, $vec2:ident) => {
+    ($t:ident, $const_new:ident, $newmat3:ident, $mat3:ident, $mat4:ident, $quat:ident, $newvec3:ident, $vec3:ident, $vec2:ident) => {
         use core::$t::INFINITY;
         use core::$t::NAN;
         use core::$t::NEG_INFINITY;
@@ -77,6 +77,13 @@ macro_rules! impl_mat3_tests {
             assert_eq!($newvec3(3.0, 2.0, 1.0), m.col(0));
             assert_eq!($newvec3(6.0, 5.0, 4.0), m.col(1));
             assert_eq!($newvec3(9.0, 8.0, 7.0), m.col(2));
+
+            should_panic!({ $mat3::ZERO.col(3) });
+            should_panic!({
+                let mut m = $mat3::ZERO;
+                m.col_mut(3);
+            });
+            should_panic!({ $mat3::ZERO.row(3) });
         }
 
         #[test]
@@ -105,12 +112,23 @@ macro_rules! impl_mat3_tests {
             let rot_x1 = $mat3::from_rotation_x(deg(180.0));
             let rot_x2 = $mat3::from_axis_angle($vec3::X, deg(180.0));
             assert_approx_eq!(rot_x1, rot_x2);
+            let rot_x3 = $mat3::from_quat($quat::from_rotation_x(deg(180.0)));
+            assert_approx_eq!(rot_x1, rot_x3);
+
             let rot_y1 = $mat3::from_rotation_y(deg(180.0));
             let rot_y2 = $mat3::from_axis_angle($vec3::Y, deg(180.0));
             assert_approx_eq!(rot_y1, rot_y2);
+            let rot_y3 = $mat3::from_quat($quat::from_rotation_y(deg(180.0)));
+            assert_approx_eq!(rot_y1, rot_y3);
+
             let rot_z1 = $mat3::from_rotation_z(deg(180.0));
             let rot_z2 = $mat3::from_axis_angle($vec3::Z, deg(180.0));
             assert_approx_eq!(rot_z1, rot_z2);
+            let rot_z3 = $mat3::from_quat($quat::from_rotation_z(deg(180.0)));
+            assert_approx_eq!(rot_z1, rot_z3);
+
+            should_glam_assert!({ $mat3::from_axis_angle($vec3::ZERO, 0.0) });
+            should_glam_assert!({ $mat3::from_quat($quat::from_xyzw(0.0, 0.0, 0.0, 0.0)) });
         }
 
         #[test]
@@ -140,6 +158,8 @@ macro_rules! impl_mat3_tests {
             assert_eq!($vec2::new(2.0, 0.0), m.transform_point2($vec2::X));
             assert_eq!($vec2::new(0.0, 4.0), m.transform_point2($vec2::Y));
 
+            should_glam_assert!({ $mat3::from_scale($vec2::ZERO) });
+
             let m = $mat3::from_scale_angle_translation(
                 $vec2::new(0.5, 1.5),
                 $t::to_radians(90.0),
@@ -260,6 +280,8 @@ macro_rules! impl_mat3_tests {
             assert_approx_eq!($mat3::IDENTITY, m * m_inv);
             assert_approx_eq!($mat3::IDENTITY, m_inv * m);
             assert_approx_eq!(m_inv, rotz_inv * scale_inv);
+
+            should_glam_assert!({ $mat3::ZERO.inverse() });
         }
 
         #[test]
@@ -308,6 +330,9 @@ macro_rules! impl_mat3_tests {
             let mut out: [$t; 9] = Default::default();
             m.write_cols_to_slice(&mut out);
             assert_eq!(MATRIX1D, out);
+
+            should_panic!({ $mat3::from_cols_slice(&[0.0; 8]) });
+            should_panic!({ $mat3::IDENTITY.write_cols_to_slice(&mut [0.0; 8]) });
         }
 
         #[cfg(feature = "std")]
@@ -336,7 +361,7 @@ macro_rules! impl_mat3_tests {
 
 mod mat3 {
     use super::support::deg;
-    use glam::{const_mat3, mat3, swizzles::*, vec3, vec3a, Mat3, Mat4, Vec2, Vec3, Vec3A};
+    use glam::{const_mat3, mat3, swizzles::*, vec3, vec3a, Mat3, Mat4, Quat, Vec2, Vec3, Vec3A};
 
     #[test]
     fn test_align() {
@@ -365,12 +390,12 @@ mod mat3 {
         );
     }
 
-    impl_mat3_tests!(f32, const_mat3, mat3, Mat3, Mat4, vec3, Vec3, Vec2);
+    impl_mat3_tests!(f32, const_mat3, mat3, Mat3, Mat4, Quat, vec3, Vec3, Vec2);
 }
 
 mod mat3a {
     use super::support::deg;
-    use glam::{const_mat3a, mat3a, swizzles::*, vec3a, Mat3A, Mat4, Vec2, Vec3, Vec3A};
+    use glam::{const_mat3a, mat3a, swizzles::*, vec3a, Mat3A, Mat4, Quat, Vec2, Vec3, Vec3A};
 
     #[test]
     fn test_align() {
@@ -395,12 +420,22 @@ mod mat3a {
         );
     }
 
-    impl_mat3_tests!(f32, const_mat3a, mat3a, Mat3A, Mat4, vec3a, Vec3, Vec2);
+    impl_mat3_tests!(
+        f32,
+        const_mat3a,
+        mat3a,
+        Mat3A,
+        Mat4,
+        Quat,
+        vec3a,
+        Vec3,
+        Vec2
+    );
 }
 
 mod dmat3 {
     use super::support::deg;
-    use glam::{const_dmat3, dmat3, dvec3, swizzles::*, DMat3, DMat4, DVec2, DVec3};
+    use glam::{const_dmat3, dmat3, dvec3, swizzles::*, DMat3, DMat4, DQuat, DVec2, DVec3};
 
     #[test]
     fn test_align() {
@@ -409,5 +444,15 @@ mod dmat3 {
         assert_eq!(8, mem::align_of::<DMat3>());
     }
 
-    impl_mat3_tests!(f64, const_dmat3, dmat3, DMat3, DMat4, dvec3, DVec3, DVec2);
+    impl_mat3_tests!(
+        f64,
+        const_dmat3,
+        dmat3,
+        DMat3,
+        DMat4,
+        DQuat,
+        dvec3,
+        DVec3,
+        DVec2
+    );
 }
diff --git a/tests/mat4.rs b/tests/mat4.rs
index 2f4d4cd..f35edb2 100644
--- a/tests/mat4.rs
+++ b/tests/mat4.rs
@@ -116,6 +116,13 @@ macro_rules! impl_mat4_tests {
             assert_eq!($newvec4(8.0, 7.0, 6.0, 5.0), m.col(1));
             assert_eq!($newvec4(12.0, 11.0, 10.0, 9.0), m.col(2));
             assert_eq!($newvec4(16.0, 15.0, 14.0, 13.0), m.col(3));
+
+            should_panic!({ $mat4::ZERO.col(4) });
+            should_panic!({
+                let mut m = $mat4::ZERO;
+                m.col_mut(4);
+            });
+            should_panic!({ $mat4::ZERO.row(4) });
         }
 
         #[test]
@@ -171,6 +178,11 @@ macro_rules! impl_mat4_tests {
             let rot_z1 = $mat4::from_rotation_z(deg(180.0));
             let rot_z2 = $mat4::from_axis_angle($vec3::Z, deg(180.0));
             assert_approx_eq!(rot_z1, rot_z2);
+
+            assert_approx_eq!($mat4::IDENTITY, $mat4::from_quat($quat::IDENTITY));
+
+            should_glam_assert!({ $mat4::from_axis_angle($vec3::ZERO, 0.0) });
+            should_glam_assert!({ $mat4::from_quat($quat::from_xyzw(0.0, 0.0, 0.0, 0.0)) });
         }
 
         #[test]
@@ -206,6 +218,9 @@ macro_rules! impl_mat4_tests {
                 $newvec3(4.0, 2.0, 1.0),
                 m.project_point3($newvec3(2.0, 2.0, 2.0))
             );
+
+            should_glam_assert!({ $mat4::ZERO.transform_vector3($vec3::X) });
+            should_glam_assert!({ $mat4::ZERO.transform_point3($vec3::X) });
         }
 
         #[test]
@@ -247,6 +262,8 @@ macro_rules! impl_mat4_tests {
                 m.transform_point3($vec3::new(1.0, 1.0, 1.0)),
                 $vec3::new(2.0, 4.0, 8.0)
             );
+
+            should_glam_assert!({ $mat4::from_scale($vec3::ZERO) });
         }
 
         #[test]
@@ -323,6 +340,8 @@ macro_rules! impl_mat4_tests {
             let m_inv = m.inverse();
             assert_approx_eq!($mat4::IDENTITY, m * m_inv, 1.0e-5);
             assert_approx_eq!($mat4::IDENTITY, m_inv * m, 1.0e-5);
+
+            should_glam_assert!({ $mat4::ZERO.inverse() });
         }
 
         #[test]
@@ -400,6 +419,20 @@ macro_rules! impl_mat4_tests {
                 $mat4::from_scale_rotation_translation(out_scale, out_rotation, out_translation),
                 1e-6
             );
+
+            should_glam_assert!({
+                $mat4::from_scale_rotation_translation(
+                    $vec3::ONE,
+                    $quat::from_xyzw(0.0, 0.0, 0.0, 0.0),
+                    $vec3::ZERO,
+                )
+            });
+            should_glam_assert!({
+                $mat4::from_rotation_translation($quat::from_xyzw(0.0, 0.0, 0.0, 0.0), $vec3::ZERO)
+            });
+            // TODO: should check scale
+            // should_glam_assert!({ $mat4::from_scale_rotation_translation($vec3::ZERO, $quat::IDENTITY, $vec3::ZERO) });
+            should_glam_assert!({ $mat4::ZERO.to_scale_rotation_translation() });
         }
 
         #[test]
@@ -412,6 +445,9 @@ macro_rules! impl_mat4_tests {
             let point = $vec3::new(1.0, 0.0, 0.0);
             assert_approx_eq!(lh.transform_point3(point), $vec3::new(0.0, 1.0, 5.0));
             assert_approx_eq!(rh.transform_point3(point), $vec3::new(0.0, 1.0, -5.0));
+
+            should_glam_assert!({ $mat4::look_at_lh($vec3::ONE, $vec3::ZERO, $vec3::ZERO) });
+            should_glam_assert!({ $mat4::look_at_rh($vec3::ONE, $vec3::ZERO, $vec3::ZERO) });
         }
 
         #[test]
@@ -438,6 +474,9 @@ macro_rules! impl_mat4_tests {
             let original = $vec3::new(5.0, 5.0, 5.0);
             let projected = projection * original.extend(1.0);
             assert_approx_eq!($vec4::new(2.5, 5.0, 0.0, 5.0), projected);
+
+            should_glam_assert!({ $mat4::perspective_lh(0.0, 1.0, 1.0, 0.0) });
+            should_glam_assert!({ $mat4::perspective_lh(0.0, 1.0, 0.0, 1.0) });
         }
 
         #[test]
@@ -451,6 +490,8 @@ macro_rules! impl_mat4_tests {
             let original = $vec3::new(5.0, 5.0, 5.0);
             let projected = projection * original.extend(1.0);
             assert_approx_eq!($vec4::new(2.5, 5.0, 0.0, 5.0), projected);
+
+            should_glam_assert!({ $mat4::perspective_infinite_lh(0.0, 1.0, 0.0) });
         }
 
         #[test]
@@ -464,6 +505,8 @@ macro_rules! impl_mat4_tests {
             let original = $vec3::new(5.0, 5.0, 5.0);
             let projected = projection * original.extend(1.0);
             assert_approx_eq!($vec4::new(2.5, 5.0, 5.0, 5.0), projected);
+
+            should_glam_assert!({ $mat4::perspective_infinite_reverse_lh(0.0, 1.0, 0.0) });
         }
 
         #[test]
@@ -477,6 +520,9 @@ macro_rules! impl_mat4_tests {
             let original = $vec3::new(5.0, 5.0, 5.0);
             let projected = projection * original.extend(1.0);
             assert_approx_eq!($vec4::new(2.5, 5.0, -15.0, -5.0), projected);
+
+            should_glam_assert!({ $mat4::perspective_rh(0.0, 1.0, 1.0, 0.0) });
+            should_glam_assert!({ $mat4::perspective_rh(0.0, 1.0, 0.0, 1.0) });
         }
 
         #[test]
@@ -490,6 +536,8 @@ macro_rules! impl_mat4_tests {
             let original = $vec3::new(5.0, 5.0, 5.0);
             let projected = projection * original.extend(1.0);
             assert_approx_eq!($vec4::new(2.5, 5.0, -10.0, -5.0), projected);
+
+            should_glam_assert!({ $mat4::perspective_infinite_rh(0.0, 1.0, 0.0) });
         }
 
         #[test]
@@ -503,6 +551,8 @@ macro_rules! impl_mat4_tests {
             let original = $vec3::new(5.0, 5.0, 5.0);
             let projected = projection * original.extend(1.0);
             assert_approx_eq!($vec4::new(2.5, 5.0, 5.0, -5.0), projected);
+
+            should_glam_assert!({ $mat4::perspective_infinite_reverse_rh(0.0, 1.0, 0.0) });
         }
 
         #[test]
@@ -590,6 +640,9 @@ macro_rules! impl_mat4_tests {
             let mut out: [$t; 16] = Default::default();
             m.write_cols_to_slice(&mut out);
             assert_eq!(MATRIX1D, out);
+
+            should_panic!({ $mat4::from_cols_slice(&[0.0; 15]) });
+            should_panic!({ $mat4::IDENTITY.write_cols_to_slice(&mut [0.0; 15]) });
         }
 
         #[cfg(feature = "std")]
diff --git a/tests/quat.rs b/tests/quat.rs
index f511a0e..4ebc88a 100644
--- a/tests/quat.rs
+++ b/tests/quat.rs
@@ -50,6 +50,8 @@ macro_rules! impl_quat_tests {
             assert_approx_eq!(q1.length_recip(), 0.5);
             assert_approx_eq!(q0, q1.normalize());
             assert_approx_eq!(q0.dot(q1), 2.0, 1.0e-6);
+
+            should_glam_assert!({ ($quat::IDENTITY * 0.0).normalize() });
         }
 
         #[test]
@@ -124,6 +126,9 @@ macro_rules! impl_quat_tests {
             let (axis, angle) = $quat::IDENTITY.to_axis_angle();
             assert_eq!(axis, $vec3::X);
             assert_eq!(angle, rad(0.0));
+
+            should_glam_assert!({ ($quat::IDENTITY * 2.0).inverse() });
+            should_glam_assert!({ $quat::from_axis_angle($vec3::ZERO, 0.0) });
         }
 
         #[test]
@@ -213,6 +218,11 @@ macro_rules! impl_quat_tests {
             assert_approx_eq!($vec3::Z, mrzx.mul_vec3($vec3::X));
             assert_approx_eq!(-$vec3::X, mrzx * $vec3::Y);
             assert_approx_eq!(-$vec3::X, mrzx.mul_vec3($vec3::Y));
+
+            should_glam_assert!({ ($quat::IDENTITY * 0.5).mul_vec3($vec3::X) });
+            should_glam_assert!({ ($quat::IDENTITY * 0.5) * $vec3::X });
+            should_glam_assert!({ ($quat::IDENTITY * 0.5).mul_quat($quat::IDENTITY) });
+            should_glam_assert!({ ($quat::IDENTITY * 0.5) * $quat::IDENTITY });
         }
 
         #[test]
@@ -251,6 +261,9 @@ macro_rules! impl_quat_tests {
             assert_approx_eq!(q0, q0.lerp(q1, 0.0));
             assert_approx_eq!(q1, q0.lerp(q1, 1.0));
             assert_approx_eq!($quat::from_rotation_y(deg(45.0)), q0.lerp(q1, 0.5));
+
+            should_glam_assert!({ $quat::lerp($quat::IDENTITY * 2.0, $quat::IDENTITY, 1.0) });
+            should_glam_assert!({ $quat::lerp($quat::IDENTITY, $quat::IDENTITY * 0.5, 1.0) });
         }
 
         #[test]
@@ -260,6 +273,9 @@ macro_rules! impl_quat_tests {
             assert_approx_eq!(q0, q0.slerp(q1, 0.0), 1.0e-3);
             assert_approx_eq!(q1, q0.slerp(q1, 1.0), 1.0e-3);
             assert_approx_eq!($quat::from_rotation_y(deg(45.0)), q0.slerp(q1, 0.5), 1.0e-3);
+
+            should_glam_assert!({ $quat::lerp($quat::IDENTITY * 2.0, $quat::IDENTITY, 1.0) });
+            should_glam_assert!({ $quat::lerp($quat::IDENTITY, $quat::IDENTITY * 0.5, 1.0) });
         }
 
         #[test]
@@ -315,6 +331,9 @@ macro_rules! impl_quat_tests {
             let mut d = [0.0, 0.0, 0.0, 0.0];
             b.write_to_slice(&mut d[..]);
             assert_eq!(a, d);
+
+            should_panic!({ $quat::IDENTITY.write_to_slice(&mut [0 as $t; 3]) });
+            should_panic!({ $quat::from_slice(&[0 as $t; 3]) });
         }
 
         #[test]
@@ -427,6 +446,11 @@ macro_rules! impl_quat_tests {
                     );
                 }
             }
+
+            should_glam_assert!({ $quat::from_rotation_arc($vec3::ZERO, $vec3::X) });
+            should_glam_assert!({ $quat::from_rotation_arc($vec3::X, $vec3::ZERO) });
+            should_glam_assert!({ $quat::from_rotation_arc_colinear($vec3::ZERO, $vec3::X) });
+            should_glam_assert!({ $quat::from_rotation_arc_colinear($vec3::X, $vec3::ZERO) });
         }
     };
 }
diff --git a/tests/support/macros.rs b/tests/support/macros.rs
index 9d92005..ea37ae4 100644
--- a/tests/support/macros.rs
+++ b/tests/support/macros.rs
@@ -1,3 +1,19 @@
+#[macro_export]
+macro_rules! should_panic {
+    ($block:block) => {{
+        #[cfg(feature = "std")]
+        assert!(std::panic::catch_unwind(|| $block).is_err());
+    }};
+}
+
+#[macro_export]
+macro_rules! should_glam_assert {
+    ($block:block) => {{
+        #[cfg(any(feature = "glam-assert", feature = "debug-glam-assert"))]
+        should_panic!($block);
+    }};
+}
+
 #[macro_export]
 macro_rules! assert_approx_eq {
     ($a:expr, $b:expr) => {{
@@ -52,6 +68,18 @@ macro_rules! impl_vec_float_normalize_tests {
             assert_eq!(from_x_y(MAX.sqrt(), 0.0).normalize(), from_x_y(1.0, 0.0));
             // assert_eq!(from_x_y(MAX, 0.0).normalize(), from_x_y(1.0, 0.0)); // normalize fails for huge vectors and returns zero
 
+            // We expect not to be able to normalize small numbers:
+            should_glam_assert!({ from_x_y(0.0, 0.0).normalize() });
+            should_glam_assert!({ from_x_y(MIN_POSITIVE, 0.0).normalize() });
+
+            // We expect not to be able to normalize non-finite vectors:
+            should_glam_assert!({ from_x_y(INFINITY, 0.0).normalize() });
+            should_glam_assert!({ from_x_y(NAN, 0.0).normalize() });
+        }
+
+        #[cfg(not(any(feature = "debug-glam-assert", feature = "glam-assert")))]
+        #[test]
+        fn test_normalize_no_glam_assert() {
             // We expect not to be able to normalize small numbers:
             assert!(!from_x_y(0.0, 0.0).normalize().is_finite());
             assert!(!from_x_y(MIN_POSITIVE, 0.0).normalize().is_finite());
diff --git a/tests/vec2.rs b/tests/vec2.rs
index dc7cab9..a6c6027 100644
--- a/tests/vec2.rs
+++ b/tests/vec2.rs
@@ -135,6 +135,8 @@ macro_rules! impl_vec2_tests {
             assert_eq!(vec(6, 6).clamp(min, max), vec(6, 6));
             assert_eq!(vec(7, 7).clamp(min, max), vec(6, 7));
             assert_eq!(vec(9, 9).clamp(min, max), vec(6, 8));
+
+            should_glam_assert!({ $vec2::clamp($vec2::ZERO, $vec2::ONE, $vec2::ZERO) });
         }
 
         #[test]
@@ -374,6 +376,9 @@ macro_rules! impl_vec2_tests {
             let mut a = [0 as $t, 0 as $t];
             v.write_to_slice(&mut a);
             assert_eq!(v, $vec2::from_slice(&a));
+
+            should_panic!({ $vec2::ONE.write_to_slice(&mut [0 as $t]) });
+            should_panic!({ $vec2::from_slice(&[0 as $t]) });
         }
 
         #[cfg(feature = "std")]
@@ -671,6 +676,14 @@ macro_rules! impl_vec2_float_tests {
                 $vec2::new(6.0, 8.0) // lengthened to length 10.0
             );
         }
+
+        #[cfg(any(feature = "glam-assert", feature = "debug-glam-assert"))]
+        #[test]
+        fn test_float_glam_assert() {
+            use std::panic::catch_unwind;
+
+            assert!(catch_unwind(|| $vec2::ZERO.normalize()).is_err());
+        }
     };
 }
 
diff --git a/tests/vec3.rs b/tests/vec3.rs
index 54cdc44..6ba963b 100644
--- a/tests/vec3.rs
+++ b/tests/vec3.rs
@@ -150,6 +150,8 @@ macro_rules! impl_vec3_tests {
             assert_eq!(vec(6, 6, 6).clamp(min, max), vec(6, 6, 6));
             assert_eq!(vec(7, 7, 7).clamp(min, max), vec(6, 7, 7));
             assert_eq!(vec(9, 9, 9).clamp(min, max), vec(6, 8, 8));
+
+            should_glam_assert!({ $vec3::clamp($vec3::ZERO, $vec3::ONE, $vec3::ZERO) });
         }
 
         #[test]
@@ -418,6 +420,9 @@ macro_rules! impl_vec3_tests {
             let mut a = [0 as $t, 0 as $t, 0 as $t];
             v.write_to_slice(&mut a);
             assert_eq!(v, $vec3::from_slice(&a));
+
+            should_panic!({ $vec3::ONE.write_to_slice(&mut [0 as $t; 2]) });
+            should_panic!({ $vec3::from_slice(&[0 as $t; 2]) });
         }
 
         #[cfg(feature = "std")]
diff --git a/tests/vec4.rs b/tests/vec4.rs
index c67e595..fb46487 100644
--- a/tests/vec4.rs
+++ b/tests/vec4.rs
@@ -185,6 +185,8 @@ macro_rules! impl_vec4_tests {
             assert_eq!(vec(6, 6, 6, 6).clamp(min, max), vec(6, 6, 6, 6));
             assert_eq!(vec(7, 7, 7, 7).clamp(min, max), vec(6, 6, 7, 7));
             assert_eq!(vec(9, 9, 9, 9).clamp(min, max), vec(6, 6, 8, 8));
+
+            should_glam_assert!({ $vec4::clamp($vec4::ZERO, $vec4::ONE, $vec4::ZERO) });
         }
 
         #[test]
@@ -259,6 +261,9 @@ macro_rules! impl_vec4_tests {
             let mut d = [0 as $t, 0 as $t, 0 as $t, 0 as $t];
             b.write_to_slice(&mut d[..]);
             assert_eq!(a, d);
+
+            should_panic!({ $vec4::ONE.write_to_slice(&mut [0 as $t; 3]) });
+            should_panic!({ $vec4::from_slice(&[0 as $t; 3]) });
         }
 
         // #[test]