Repository: bitshifter/glam-rs (rust)

Issue:
".fract" for vector types
(Minor convenience thing, absolutely not urgent in any way).

In GLSL, fract is quite commonly used, in things like this hash function in https://www.shadertoy.com/view/tlGfzd:

```glsl
// by Dave_Hoskins
float hash12(vec2 p)
{
    vec3 p3  = fract(vec3(p.xyx) * .1031);
    p3 += dot(p3, p3.yzx + 33.33);
    return fract((p3.x + p3.y) * p3.z);
}
```

It just returns the fractional value, or `x - floor(x)`:  (https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/fract.xhtml).

Having this in glam would be convenient when translating GLSL shaders to rust-gpu, for example.

Pull request description:
Add `fract()` to float vector types.
Fixes #144.

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b2be6d4..e323c93 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,8 @@ The format is based on [Keep a Changelog], and this project adheres to
 
 ### Added
 
+* Added `fract()` method to float vector types which return a vector containing
+  `self - self.floor()`.
 * Added optional support for the `approx` crate. Note that all glam types
   implement their own `abs_diff_eq()` method without requiring the `approx`
   dependency.
diff --git a/src/core/traits/scalar.rs b/src/core/traits/scalar.rs
index fc1a399..1b2c18e 100644
--- a/src/core/traits/scalar.rs
+++ b/src/core/traits/scalar.rs
@@ -278,8 +278,7 @@ impl FloatEx for f64 {
     }
     #[inline(always)]
     fn acos_approx(self) -> Self {
-        // TODO: clamp range
-        f64::acos(self)
+        f64::acos(self.max(-1.0).min(1.0))
     }
 }
 
@@ -383,4 +382,8 @@ fn test_scalar_acos() {
     // input is clamped to -1.0..1.0
     assert_approx_eq!(2.0_f32.acos_approx(), 0.0);
     assert_approx_eq!((-2.0_f32).acos_approx(), core::f32::consts::PI);
+
+    // input is clamped to -1.0..1.0
+    assert_eq!(2.0_f64.acos_approx(), 0.0);
+    assert_eq!((-2.0_f64).acos_approx(), core::f64::consts::PI);
 }
diff --git a/src/mat3.rs b/src/mat3.rs
index e961fb7..f71bf3f 100644
--- a/src/mat3.rs
+++ b/src/mat3.rs
@@ -29,7 +29,8 @@ macro_rules! define_mat3_struct {
         ///
         /// This 3x3 matrix type features convenience methods for creating and using linear and
         /// affine transformations. If you are primarily dealing with 2D affine transformations the
-        /// [`Affine2`] type is much faster and more space efficient than using a 3x3 matrix.
+        /// [`Affine2`][crate::Affine2] type is much faster and more space efficient than using a
+        /// 3x3 matrix.
         ///
         /// Linear transformations including 3D rotation and scale can be created using methods
         /// such as [`Self::from_diagonal()`], [`Self::from_quat()`], [`Self::from_axis_angle()`],
diff --git a/src/mat4.rs b/src/mat4.rs
index fe2b526..cddded6 100644
--- a/src/mat4.rs
+++ b/src/mat4.rs
@@ -741,7 +741,8 @@ type InnerF32 = Columns4<XYZW<f32>>;
 ///
 /// This 4x4 matrix type features convenience methods for creating and using affine transforms and
 /// perspective projections. If you are primarily dealing with 3D affine transformations
-/// condidering using [`Affine3A`] which is faster tha a 4x4 matrix for some affine operations.
+/// condidering using [`Affine3A`][crate::Affine3A] which is faster tha a 4x4 matrix for some
+/// affine operations.
 ///
 /// Affine transformations including 3D translation, rotation and scale can be created
 /// using methods such as [`Self::from_translation()`], [`Self::from_quat()`],
@@ -813,7 +814,8 @@ type InnerF64 = Columns4<XYZW<f64>>;
 ///
 /// This 4x4 matrix type features convenience methods for creating and using affine transforms and
 /// perspective projections. If you are primarily dealing with 3D affine transformations
-/// condidering using [`DAffine3`] which is faster tha a 4x4 matrix for some affine operations.
+/// condidering using [`DAffine3`][crate::DAffine3] which is faster tha a 4x4 matrix for some
+/// affine operations.
 ///
 /// Affine transformations including 3D translation, rotation and scale can be created
 /// using methods such as [`Self::from_translation()`], [`Self::from_quat()`],
diff --git a/src/vec.rs b/src/vec.rs
index ed6202a..8e98d8f 100644
--- a/src/vec.rs
+++ b/src/vec.rs
@@ -329,6 +329,15 @@ macro_rules! impl_vecn_float_methods {
             Self($flttrait::ceil(self.0))
         }
 
+        /// Returns a vector containing the fractional part of the vector, e.g. `self -
+        /// self.floor()`.
+        ///
+        /// Note that this is fast but not precise for large numbers.
+        #[inline(always)]
+        pub fn fract(self) -> Self {
+            self - self.floor()
+        }
+
         /// Returns a vector containing `e^self` (the exponential function) for each element of
         /// `self`.
         #[inline(always)]


Test patch:
diff --git a/tests/vec2.rs b/tests/vec2.rs
index e78af1b..dc7cab9 100644
--- a/tests/vec2.rs
+++ b/tests/vec2.rs
@@ -557,6 +557,16 @@ macro_rules! impl_vec2_float_tests {
             );
         }
 
+        #[test]
+        fn test_fract() {
+            assert_approx_eq!($vec2::new(1.35, -1.5).fract(), $vec2::new(0.35, 0.5));
+            assert_approx_eq!(
+                $vec2::new(-2000000.123, 1000000.123).fract(),
+                $vec2::new(0.877, 0.123),
+                0.002
+            );
+        }
+
         #[test]
         fn test_ceil() {
             assert_eq!($vec2::new(1.35, -1.5).ceil(), $vec2::new(2.0, -1.0));
diff --git a/tests/vec3.rs b/tests/vec3.rs
index 1afe6cd..54cdc44 100644
--- a/tests/vec3.rs
+++ b/tests/vec3.rs
@@ -602,6 +602,18 @@ macro_rules! impl_vec3_float_tests {
             );
         }
 
+        #[test]
+        fn test_fract() {
+            assert_approx_eq!(
+                $vec3::new(1.35, 1.5, -1.5).fract(),
+                $vec3::new(0.35, 0.5, 0.5)
+            );
+            assert_approx_eq!(
+                $vec3::new(-200000.123, 1000000.123, 1000.9).fract(),
+                $vec3::new(0.877, 0.123, 0.9),
+                0.002
+            );
+        }
         #[test]
         fn test_ceil() {
             assert_eq!(
diff --git a/tests/vec4.rs b/tests/vec4.rs
index 8137cc2..c67e595 100644
--- a/tests/vec4.rs
+++ b/tests/vec4.rs
@@ -668,6 +668,19 @@ macro_rules! impl_vec4_float_tests {
             );
         }
 
+        #[test]
+        fn test_fract() {
+            assert_approx_eq!(
+                $vec4::new(1.35, 1.5, -1.5, 1.999).fract(),
+                $vec4::new(0.35, 0.5, 0.5, 0.999)
+            );
+            assert_approx_eq!(
+                $vec4::new(-0.0, -200000.123, 1000000.123, 1000.9).fract(),
+                $vec4::new(0.0, 0.877, 0.123, 0.9),
+                0.002
+            );
+        }
+
         #[test]
         fn test_ceil() {
             assert_eq!(