diff --git a/CHANGELOG.md b/CHANGELOG.md
index 512b4b3..4b5c2ae 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- X10D: Added `MathUtility.Bias(float, float)` and `MathUtility.Bias(double, double)`.
- X10D: Added `MathUtility.ExponentialDecay(float, float, float)` and `MathUtility.ExponentialDecay(double, double, double)`.
- X10D: Added `MathUtility.InverseLerp(float, float, float)` and `MathUtility.InverseLerp(double, double, double)`.
+- X10D: Added `MathUtility.Sawtooth(float)` and `MathUtility.Sawtooth(double)`.
- X10D: Added `MathUtility.ScaleRange(float, float, float, float, float)`
and `MathUtility.ScaleRange(double, double, double, double, double)`
- X10D: Added `MathUtility.Sigmoid(float)` and `MathUtility.Sigmoid(double)`.
diff --git a/X10D.Tests/src/Math/MathUtilityTests.cs b/X10D.Tests/src/Math/MathUtilityTests.cs
index 45c37ab..f58835c 100644
--- a/X10D.Tests/src/Math/MathUtilityTests.cs
+++ b/X10D.Tests/src/Math/MathUtilityTests.cs
@@ -163,6 +163,72 @@ public class MathUtilityTests
Assert.AreEqual(1.0f, floatResult);
}
+ [TestMethod]
+ public void Sawtooth_ShouldReturn0Point5_Given0Point5AsDouble()
+ {
+ const double value = 0.5;
+
+ const double expected = 0.5;
+ double actual = MathUtility.Sawtooth(value);
+
+ Assert.AreEqual(expected, actual, 1e-6);
+ }
+
+ [TestMethod]
+ public void Sawtooth_ShouldReturn0Point5_Given0Point5AsSingle()
+ {
+ const float value = 0.5f;
+
+ const float expected = 0.5f;
+ float actual = MathUtility.Sawtooth(value);
+
+ Assert.AreEqual(expected, actual, 1e-6f);
+ }
+
+ [TestMethod]
+ public void Sawtooth_ShouldReturn0Point5_Given1Point5AsDouble()
+ {
+ const double value = 1.5;
+
+ const double expected = 0.5;
+ double actual = MathUtility.Sawtooth(value);
+
+ Assert.AreEqual(expected, actual, 1e-6);
+ }
+
+ [TestMethod]
+ public void Sawtooth_ShouldReturn0Point5_Given1Point5AsSingle()
+ {
+ const float value = 1.5f;
+
+ const float expected = 0.5f;
+ float actual = MathUtility.Sawtooth(value);
+
+ Assert.AreEqual(expected, actual, 1e-6f);
+ }
+
+ [TestMethod]
+ public void Sawtooth_ShouldReturn0Point5_GivenNegative1Point5AsDouble()
+ {
+ const double value = -1.5;
+
+ const double expected = 0.5;
+ double actual = MathUtility.Sawtooth(value);
+
+ Assert.AreEqual(expected, actual, 1e-6);
+ }
+
+ [TestMethod]
+ public void Sawtooth_ShouldReturn0Point5_GivenNegative1Point5AsSingle()
+ {
+ const float value = -1.5f;
+
+ const float expected = 0.5f;
+ float actual = MathUtility.Sawtooth(value);
+
+ Assert.AreEqual(expected, actual, 1e-6f);
+ }
+
[TestMethod]
public void ScaleRangeDouble_ShouldScaleRange_GivenItsValues()
{
diff --git a/X10D/src/Math/MathUtility.cs b/X10D/src/Math/MathUtility.cs
index dc9b2f7..18d0009 100644
--- a/X10D/src/Math/MathUtility.cs
+++ b/X10D/src/Math/MathUtility.cs
@@ -287,6 +287,26 @@ public static class MathUtility
return System.Math.Pow(value, 1.0 / gamma);
}
+ ///
+ /// Returns the incremental sawtooth wave of a given value.
+ ///
+ /// The value to calculate.
+ /// The sawtooth wave of the given value.
+ public static float Sawtooth(float value)
+ {
+ return (value - MathF.Floor(value));
+ }
+
+ ///
+ /// Returns the incremental sawtooth wave of a given value.
+ ///
+ /// The value to calculate.
+ /// The sawtooth wave of the given value.
+ public static double Sawtooth(double value)
+ {
+ return (value - System.Math.Floor(value));
+ }
+
///
/// Converts a value from being a percentage of one range, to being the same percentage in a new range.
///