diff --git a/PowerKit.Tests/Extensions/DoubleExtensionsTests.cs b/PowerKit.Tests/Extensions/DoubleExtensionsTests.cs
index 06a7e7e..ad80928 100644
--- a/PowerKit.Tests/Extensions/DoubleExtensionsTests.cs
+++ b/PowerKit.Tests/Extensions/DoubleExtensionsTests.cs
@@ -27,4 +27,17 @@ public void ParseOrDefault_Test()
double.ParseOrDefault("abc", -1.0).Should().Be(-1.0);
double.ParseOrDefault(null).Should().Be(0.0);
}
+
+ [Fact]
+ public void Wrap_Test()
+ {
+ // Act & assert
+ 5.0.Wrap(0.0, 10.0).Should().Be(5.0);
+ 13.0.Wrap(0.0, 10.0).Should().Be(3.0);
+ (-3.0).Wrap(0.0, 10.0).Should().Be(7.0);
+ (-10.0).Wrap(0.0, 10.0).Should().Be(0.0);
+ 0.0.Wrap(0.0, 10.0).Should().Be(0.0);
+ 10.0.Wrap(0.0, 10.0).Should().Be(0.0);
+ 23.0.Wrap(0.0, 10.0).Should().Be(3.0);
+ }
}
diff --git a/PowerKit.Tests/Extensions/SingleExtensionsTests.cs b/PowerKit.Tests/Extensions/SingleExtensionsTests.cs
index 0c847f2..36df17a 100644
--- a/PowerKit.Tests/Extensions/SingleExtensionsTests.cs
+++ b/PowerKit.Tests/Extensions/SingleExtensionsTests.cs
@@ -31,4 +31,17 @@ public void ParseOrDefault_Test()
float.ParseOrDefault("abc", -1.0f).Should().Be(-1.0f);
float.ParseOrDefault(null).Should().Be(0.0f);
}
+
+ [Fact]
+ public void Wrap_Test()
+ {
+ // Act & assert
+ 5.0f.Wrap(0.0f, 10.0f).Should().Be(5.0f);
+ 13.0f.Wrap(0.0f, 10.0f).Should().Be(3.0f);
+ (-3.0f).Wrap(0.0f, 10.0f).Should().Be(7.0f);
+ (-10.0f).Wrap(0.0f, 10.0f).Should().Be(0.0f);
+ 0.0f.Wrap(0.0f, 10.0f).Should().Be(0.0f);
+ 10.0f.Wrap(0.0f, 10.0f).Should().Be(0.0f);
+ 23.0f.Wrap(0.0f, 10.0f).Should().Be(3.0f);
+ }
}
diff --git a/PowerKit/Extensions/DoubleExtensions.cs b/PowerKit/Extensions/DoubleExtensions.cs
index 40e8e1f..2a434fd 100644
--- a/PowerKit/Extensions/DoubleExtensions.cs
+++ b/PowerKit/Extensions/DoubleExtensions.cs
@@ -66,4 +66,31 @@ public static double ParseOrDefault(
public static double ParseOrDefault(string? str, double defaultValue = default) =>
double.ParseOrDefault(str, CultureInfo.CurrentCulture, defaultValue);
}
+
+ extension(double value)
+ {
+ ///
+ /// Wraps the value into the half-open range [, ).
+ /// A value equal to wraps to .
+ ///
+ /// The inclusive lower bound of the range.
+ /// The exclusive upper bound of the range.
+ ///
+ /// This method requires to be greater than .
+ /// If is less than or equal to , the behavior is invalid.
+ ///
+ public double Wrap(double min, double max)
+ {
+ if (max <= min)
+ {
+ throw new ArgumentOutOfRangeException(
+ nameof(max),
+ "The maximum value must be greater than the minimum value."
+ );
+ }
+
+ var range = max - min;
+ return min + (((value - min) % range + range) % range);
+ }
+ }
}
diff --git a/PowerKit/Extensions/SingleExtensions.cs b/PowerKit/Extensions/SingleExtensions.cs
index a6bbd3f..2862b63 100644
--- a/PowerKit/Extensions/SingleExtensions.cs
+++ b/PowerKit/Extensions/SingleExtensions.cs
@@ -66,4 +66,31 @@ public static float ParseOrDefault(
public static float ParseOrDefault(string? str, float defaultValue = default) =>
float.ParseOrDefault(str, CultureInfo.CurrentCulture, defaultValue);
}
+
+ extension(float value)
+ {
+ ///
+ /// Wraps the value into the half-open range [, ),
+ /// cycling it back around when it exceeds the bounds.
+ ///
+ ///
+ /// The returned value is greater than or equal to and less than
+ /// for valid ranges. A value equal to
+ /// wraps to .
+ /// When is less than or equal to , this
+ /// method does not throw; it follows floating-point arithmetic and may return
+ /// .
+ ///
+ public float Wrap(float min, float max)
+ {
+ if (max <= min)
+ {
+ throw new ArgumentOutOfRangeException(nameof(max), "max must be greater than min.");
+ }
+
+ var range = max - min;
+ var normalized = ((value - min) % range + range) % range;
+ return min + normalized;
+ }
+ }
}