mirror of
https://github.com/oliverbooth/X10D
synced 2024-11-22 08:18:47 +00:00
feat: add generic math where possible in MathUtility
This commit is contained in:
parent
b91aad6305
commit
21271314af
@ -1,6 +1,5 @@
|
|||||||
#if NET7_0_OR_GREATER
|
#if NET7_0_OR_GREATER
|
||||||
using System.Diagnostics.Contracts;
|
using System.Diagnostics.Contracts;
|
||||||
using System.Globalization;
|
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using X10D.CompilerServices;
|
using X10D.CompilerServices;
|
||||||
@ -31,28 +30,5 @@ public static class BinaryIntegerExtensions
|
|||||||
TInteger root = TInteger.Abs(value).Mod(nine);
|
TInteger root = TInteger.Abs(value).Mod(nine);
|
||||||
return int.CreateChecked(root == TInteger.Zero ? nine : root);
|
return int.CreateChecked(root == TInteger.Zero ? nine : root);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Performs a modulo operation which supports a negative dividend.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="dividend">The dividend.</param>
|
|
||||||
/// <param name="divisor">The divisor.</param>
|
|
||||||
/// <returns>The result of <c>dividend mod divisor</c>.</returns>
|
|
||||||
/// <remarks>
|
|
||||||
/// The <c>%</c> operator (commonly called the modulo operator) in C# is not defined to be modulo, but is instead
|
|
||||||
/// remainder. This quirk inherently makes it difficult to use modulo in a negative context, as <c>x % y</c> where x is
|
|
||||||
/// negative will return a negative value, akin to <c>-(x % y)</c>, even if precedence is forced. This method provides a
|
|
||||||
/// modulo operation which supports negative dividends.
|
|
||||||
/// </remarks>
|
|
||||||
/// <author>ShreevatsaR, https://stackoverflow.com/a/1082938/1467293</author>
|
|
||||||
/// <license>CC-BY-SA 2.5</license>
|
|
||||||
[Pure]
|
|
||||||
[MethodImpl(CompilerResources.MethodImplOptions)]
|
|
||||||
public static TInteger Mod<TInteger>(this TInteger dividend, TInteger divisor)
|
|
||||||
where TInteger : IBinaryInteger<TInteger>
|
|
||||||
{
|
|
||||||
TInteger r = dividend % divisor;
|
|
||||||
return r < TInteger.Zero ? r + divisor : r;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System.Diagnostics.Contracts;
|
using System.Diagnostics.Contracts;
|
||||||
|
using System.Numerics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using X10D.CompilerServices;
|
using X10D.CompilerServices;
|
||||||
|
|
||||||
@ -12,6 +13,7 @@ public static class MathUtility
|
|||||||
private const double DefaultGamma = 2.2;
|
private const double DefaultGamma = 2.2;
|
||||||
private const float DefaultGammaF = 2.2f;
|
private const float DefaultGammaF = 2.2f;
|
||||||
|
|
||||||
|
#if !NET7_0_OR_GREATER
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Applies a simple bias function to value.
|
/// Applies a simple bias function to value.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -40,80 +42,6 @@ public static class MathUtility
|
|||||||
return value / ((1.0 / bias - 2.0) * (1.0 - value) + 1.0);
|
return value / ((1.0 / bias - 2.0) * (1.0 - value) + 1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Calculates exponential decay for a value.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value">The value to decay.</param>
|
|
||||||
/// <param name="alpha">A factor by which to scale the decay.</param>
|
|
||||||
/// <param name="decay">The decay amount.</param>
|
|
||||||
/// <returns>The exponentially decayed value.</returns>
|
|
||||||
public static float ExponentialDecay(float value, float alpha, float decay)
|
|
||||||
{
|
|
||||||
return value * MathF.Exp(-decay * alpha);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Calculates exponential decay for a value.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value">The value to decay.</param>
|
|
||||||
/// <param name="alpha">A factor by which to scale the decay.</param>
|
|
||||||
/// <param name="decay">The decay amount.</param>
|
|
||||||
/// <returns>The exponentially decayed value.</returns>
|
|
||||||
public static double ExponentialDecay(double value, double alpha, double decay)
|
|
||||||
{
|
|
||||||
return value * System.Math.Exp(-decay * alpha);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Converts a gamma-encoded value to a linear value using a gamma value of <c>2.2</c>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value">The gamma-encoded value to convert. Expected range is [0, 1].</param>
|
|
||||||
/// <returns>The linear value.</returns>
|
|
||||||
[Pure]
|
|
||||||
[MethodImpl(CompilerResources.MethodImplOptions)]
|
|
||||||
public static float GammaToLinear(float value)
|
|
||||||
{
|
|
||||||
return GammaToLinear(value, DefaultGammaF);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Converts a gamma-encoded value to a linear value using the specified gamma value.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value">The gamma-encoded value to convert. Expected range is [0, 1].</param>
|
|
||||||
/// <param name="gamma">The gamma value to use for decoding.</param>
|
|
||||||
/// <returns>The linear value.</returns>
|
|
||||||
[Pure]
|
|
||||||
[MethodImpl(CompilerResources.MethodImplOptions)]
|
|
||||||
public static float GammaToLinear(float value, float gamma)
|
|
||||||
{
|
|
||||||
return MathF.Pow(value, 1.0f / gamma);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Converts a gamma-encoded value to a linear value using a gamma value of <c>2.2</c>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value">The gamma-encoded value to convert. Expected range is [0, 1].</param>
|
|
||||||
/// <returns>The linear value.</returns>
|
|
||||||
[Pure]
|
|
||||||
[MethodImpl(CompilerResources.MethodImplOptions)]
|
|
||||||
public static double GammaToLinear(double value)
|
|
||||||
{
|
|
||||||
return GammaToLinear(value, DefaultGamma);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Converts a gamma-encoded value to a linear value using the specified gamma value.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value">The gamma-encoded value to convert. Expected range is [0, 1].</param>
|
|
||||||
/// <param name="gamma">The gamma value to use for decoding.</param>
|
|
||||||
/// <returns>The linear value.</returns>
|
|
||||||
[Pure]
|
|
||||||
[MethodImpl(CompilerResources.MethodImplOptions)]
|
|
||||||
public static double GammaToLinear(double value, double gamma)
|
|
||||||
{
|
|
||||||
return System.Math.Pow(value, 1.0 / gamma);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the linear interpolation inverse of a value, such that it determines where a value lies between two other
|
/// Returns the linear interpolation inverse of a value, such that it determines where a value lies between two other
|
||||||
/// values.
|
/// values.
|
||||||
@ -190,6 +118,167 @@ public static class MathUtility
|
|||||||
return ((1.0 - alpha) * value) + (alpha * target);
|
return ((1.0 - alpha) * value) + (alpha * target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Performs smooth Hermite interpolation from one value to a target using a specified alpha.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The interpolation source.</param>
|
||||||
|
/// <param name="target">The interpolation target.</param>
|
||||||
|
/// <param name="alpha">The interpolation alpha.</param>
|
||||||
|
/// <returns>The interpolation result.</returns>
|
||||||
|
public static float SmoothStep(float value, float target, float alpha)
|
||||||
|
{
|
||||||
|
alpha = System.Math.Clamp(alpha, 0.0f, 1.0f);
|
||||||
|
alpha = -2.0f * alpha * alpha * alpha + 3.0f * alpha * alpha;
|
||||||
|
return target * alpha + value * (1.0f - alpha);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Performs smooth Hermite interpolation from one value to a target using a specified alpha.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The interpolation source.</param>
|
||||||
|
/// <param name="target">The interpolation target.</param>
|
||||||
|
/// <param name="alpha">The interpolation alpha.</param>
|
||||||
|
/// <returns>The interpolation result.</returns>
|
||||||
|
public static double SmoothStep(double value, double target, double alpha)
|
||||||
|
{
|
||||||
|
alpha = System.Math.Clamp(alpha, 0.0, 1.0);
|
||||||
|
alpha = -2.0 * alpha * alpha * alpha + 3.0 * alpha * alpha;
|
||||||
|
return target * alpha + value * (1.0 - alpha);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts a value from being a percentage of one range, to being the same percentage in a new range.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The value to convert.</param>
|
||||||
|
/// <param name="oldMin">The old minimum value.</param>
|
||||||
|
/// <param name="oldMax">The old maximum value.</param>
|
||||||
|
/// <param name="newMin">The new minimum value.</param>
|
||||||
|
/// <param name="newMax">The new maximum value.</param>
|
||||||
|
/// <returns>The scaled value.</returns>
|
||||||
|
[Pure]
|
||||||
|
[MethodImpl(CompilerResources.MethodImplOptions)]
|
||||||
|
public static float ScaleRange(float value, float oldMin, float oldMax, float newMin, float newMax)
|
||||||
|
{
|
||||||
|
float oldRange = oldMax - oldMin;
|
||||||
|
float newRange = newMax - newMin;
|
||||||
|
float alpha = (value - oldMin) / oldRange;
|
||||||
|
return (alpha * newRange) + newMin;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts a value from being a percentage of one range, to being the same percentage in a new range.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The value to convert.</param>
|
||||||
|
/// <param name="oldMin">The old minimum value.</param>
|
||||||
|
/// <param name="oldMax">The old maximum value.</param>
|
||||||
|
/// <param name="newMin">The new minimum value.</param>
|
||||||
|
/// <param name="newMax">The new maximum value.</param>
|
||||||
|
/// <returns>The scaled value.</returns>
|
||||||
|
[Pure]
|
||||||
|
[MethodImpl(CompilerResources.MethodImplOptions)]
|
||||||
|
public static double ScaleRange(double value, double oldMin, double oldMax, double newMin, double newMax)
|
||||||
|
{
|
||||||
|
double oldRange = oldMax - oldMin;
|
||||||
|
double newRange = newMax - newMin;
|
||||||
|
double alpha = (value - oldMin) / oldRange;
|
||||||
|
return (alpha * newRange) + newMin;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the incremental sawtooth wave of a given value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The value to calculate.</param>
|
||||||
|
/// <returns>The sawtooth wave of the given value.</returns>
|
||||||
|
public static float Sawtooth(float value)
|
||||||
|
{
|
||||||
|
return (value - MathF.Floor(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the incremental sawtooth wave of a given value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The value to calculate.</param>
|
||||||
|
/// <returns>The sawtooth wave of the given value.</returns>
|
||||||
|
public static double Sawtooth(double value)
|
||||||
|
{
|
||||||
|
return (value - System.Math.Floor(value));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calculates exponential decay for a value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The value to decay.</param>
|
||||||
|
/// <param name="alpha">A factor by which to scale the decay.</param>
|
||||||
|
/// <param name="decay">The decay amount.</param>
|
||||||
|
/// <returns>The exponentially decayed value.</returns>
|
||||||
|
public static float ExponentialDecay(float value, float alpha, float decay)
|
||||||
|
{
|
||||||
|
return value * MathF.Exp(-decay * alpha);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calculates exponential decay for a value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The value to decay.</param>
|
||||||
|
/// <param name="alpha">A factor by which to scale the decay.</param>
|
||||||
|
/// <param name="decay">The decay amount.</param>
|
||||||
|
/// <returns>The exponentially decayed value.</returns>
|
||||||
|
public static double ExponentialDecay(double value, double alpha, double decay)
|
||||||
|
{
|
||||||
|
return value * System.Math.Exp(-decay * alpha);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts a gamma-encoded value to a linear value using a gamma value of <c>2.2</c>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The gamma-encoded value to convert. Expected range is [0, 1].</param>
|
||||||
|
/// <returns>The linear value.</returns>
|
||||||
|
[Pure]
|
||||||
|
[MethodImpl(CompilerResources.MethodImplOptions)]
|
||||||
|
public static float GammaToLinear(float value)
|
||||||
|
{
|
||||||
|
return GammaToLinear(value, DefaultGammaF);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts a gamma-encoded value to a linear value using the specified gamma value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The gamma-encoded value to convert. Expected range is [0, 1].</param>
|
||||||
|
/// <param name="gamma">The gamma value to use for decoding.</param>
|
||||||
|
/// <returns>The linear value.</returns>
|
||||||
|
[Pure]
|
||||||
|
[MethodImpl(CompilerResources.MethodImplOptions)]
|
||||||
|
public static float GammaToLinear(float value, float gamma)
|
||||||
|
{
|
||||||
|
return MathF.Pow(value, 1.0f / gamma);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts a gamma-encoded value to a linear value using a gamma value of <c>2.2</c>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The gamma-encoded value to convert. Expected range is [0, 1].</param>
|
||||||
|
/// <returns>The linear value.</returns>
|
||||||
|
[Pure]
|
||||||
|
[MethodImpl(CompilerResources.MethodImplOptions)]
|
||||||
|
public static double GammaToLinear(double value)
|
||||||
|
{
|
||||||
|
return GammaToLinear(value, DefaultGamma);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts a gamma-encoded value to a linear value using the specified gamma value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The gamma-encoded value to convert. Expected range is [0, 1].</param>
|
||||||
|
/// <param name="gamma">The gamma value to use for decoding.</param>
|
||||||
|
/// <returns>The linear value.</returns>
|
||||||
|
[Pure]
|
||||||
|
[MethodImpl(CompilerResources.MethodImplOptions)]
|
||||||
|
public static double GammaToLinear(double value, double gamma)
|
||||||
|
{
|
||||||
|
return System.Math.Pow(value, 1.0 / gamma);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Converts a linear value to a gamma-encoded value using a gamma value of <c>2.2</c>.
|
/// Converts a linear value to a gamma-encoded value using a gamma value of <c>2.2</c>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -288,64 +377,6 @@ public static class MathUtility
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns the incremental sawtooth wave of a given value.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value">The value to calculate.</param>
|
|
||||||
/// <returns>The sawtooth wave of the given value.</returns>
|
|
||||||
public static float Sawtooth(float value)
|
|
||||||
{
|
|
||||||
return (value - MathF.Floor(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns the incremental sawtooth wave of a given value.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value">The value to calculate.</param>
|
|
||||||
/// <returns>The sawtooth wave of the given value.</returns>
|
|
||||||
public static double Sawtooth(double value)
|
|
||||||
{
|
|
||||||
return (value - System.Math.Floor(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Converts a value from being a percentage of one range, to being the same percentage in a new range.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value">The value to convert.</param>
|
|
||||||
/// <param name="oldMin">The old minimum value.</param>
|
|
||||||
/// <param name="oldMax">The old maximum value.</param>
|
|
||||||
/// <param name="newMin">The new minimum value.</param>
|
|
||||||
/// <param name="newMax">The new maximum value.</param>
|
|
||||||
/// <returns>The scaled value.</returns>
|
|
||||||
[Pure]
|
|
||||||
[MethodImpl(CompilerResources.MethodImplOptions)]
|
|
||||||
public static float ScaleRange(float value, float oldMin, float oldMax, float newMin, float newMax)
|
|
||||||
{
|
|
||||||
float oldRange = oldMax - oldMin;
|
|
||||||
float newRange = newMax - newMin;
|
|
||||||
float alpha = (value - oldMin) / oldRange;
|
|
||||||
return (alpha * newRange) + newMin;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Converts a value from being a percentage of one range, to being the same percentage in a new range.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value">The value to convert.</param>
|
|
||||||
/// <param name="oldMin">The old minimum value.</param>
|
|
||||||
/// <param name="oldMax">The old maximum value.</param>
|
|
||||||
/// <param name="newMin">The new minimum value.</param>
|
|
||||||
/// <param name="newMax">The new maximum value.</param>
|
|
||||||
/// <returns>The scaled value.</returns>
|
|
||||||
[Pure]
|
|
||||||
[MethodImpl(CompilerResources.MethodImplOptions)]
|
|
||||||
public static double ScaleRange(double value, double oldMin, double oldMax, double newMin, double newMax)
|
|
||||||
{
|
|
||||||
double oldRange = oldMax - oldMin;
|
|
||||||
double newRange = newMax - newMin;
|
|
||||||
double alpha = (value - oldMin) / oldRange;
|
|
||||||
return (alpha * newRange) + newMin;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Calculates the sigmoid function for the given input value.
|
/// Calculates the sigmoid function for the given input value.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -374,18 +405,92 @@ public static class MathUtility
|
|||||||
return 1.0f / (1.0f + System.Math.Exp(-value));
|
return 1.0f / (1.0f + System.Math.Exp(-value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if NET7_0_OR_GREATER
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Performs smooth Hermite interpolation from one value to a target using a specified alpha.
|
/// Applies a simple bias function to value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The value to which the bias function will be applied.</param>
|
||||||
|
/// <param name="bias">The bias value. Valid values range from 0-1.</param>
|
||||||
|
/// <returns>The biased result.</returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// If <paramref name="bias" /> is less than 0.5, <paramref name="value" /> will be shifted downward; otherwise, upward.
|
||||||
|
/// </remarks>
|
||||||
|
public static TNumber Bias<TNumber>(TNumber value, TNumber bias)
|
||||||
|
where TNumber : INumber<TNumber>
|
||||||
|
{
|
||||||
|
TNumber identity = TNumber.MultiplicativeIdentity;
|
||||||
|
return value / ((identity / bias - TNumber.CreateChecked(2)) * (identity - value) + identity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the linear interpolation inverse of a value, such that it determines where a value lies between two other
|
||||||
|
/// values.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="alpha">The value whose lerp inverse is to be found.</param>
|
||||||
|
/// <param name="start">The start of the range.</param>
|
||||||
|
/// <param name="end">The end of the range.</param>
|
||||||
|
/// <returns>A value determined by <c>(alpha - start) / (end - start)</c>.</returns>
|
||||||
|
[Pure]
|
||||||
|
[MethodImpl(CompilerResources.MethodImplOptions)]
|
||||||
|
public static TNumber InverseLerp<TNumber>(TNumber alpha, TNumber start, TNumber end)
|
||||||
|
where TNumber : INumber<TNumber>
|
||||||
|
{
|
||||||
|
if (start == end)
|
||||||
|
{
|
||||||
|
return TNumber.Zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (alpha - start) / (end - start);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Linearly interpolates from one value to a target using a specified alpha.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="value">The interpolation source.</param>
|
/// <param name="value">The interpolation source.</param>
|
||||||
/// <param name="target">The interpolation target.</param>
|
/// <param name="target">The interpolation target.</param>
|
||||||
/// <param name="alpha">The interpolation alpha.</param>
|
/// <param name="alpha">The interpolation alpha.</param>
|
||||||
/// <returns>The interpolation result.</returns>
|
/// <returns>
|
||||||
public static float SmoothStep(float value, float target, float alpha)
|
/// The interpolation result as determined by <c>(1 - alpha) * value + alpha * target</c>.
|
||||||
|
/// </returns>
|
||||||
|
[Pure]
|
||||||
|
[MethodImpl(CompilerResources.MethodImplOptions)]
|
||||||
|
public static TNumber Lerp<TNumber>(TNumber value, TNumber target, TNumber alpha)
|
||||||
|
where TNumber : INumber<TNumber>
|
||||||
{
|
{
|
||||||
alpha = System.Math.Clamp(alpha, 0.0f, 1.0f);
|
// rookie mistake: a + t * (b - a)
|
||||||
alpha = -2.0f * alpha * alpha * alpha + 3.0f * alpha * alpha;
|
// "precise" method: (1 - t) * a + t * b
|
||||||
return target * alpha + value * (1.0f - alpha);
|
return ((TNumber.MultiplicativeIdentity - alpha) * value) + (alpha * target);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the incremental sawtooth wave of a given value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The value to calculate.</param>
|
||||||
|
/// <returns>The sawtooth wave of the given value.</returns>
|
||||||
|
public static TNumber Sawtooth<TNumber>(TNumber value)
|
||||||
|
where TNumber : IFloatingPoint<TNumber>
|
||||||
|
{
|
||||||
|
return (value - TNumber.Floor(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts a value from being a percentage of one range, to being the same percentage in a new range.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The value to convert.</param>
|
||||||
|
/// <param name="oldMin">The old minimum value.</param>
|
||||||
|
/// <param name="oldMax">The old maximum value.</param>
|
||||||
|
/// <param name="newMin">The new minimum value.</param>
|
||||||
|
/// <param name="newMax">The new maximum value.</param>
|
||||||
|
/// <returns>The scaled value.</returns>
|
||||||
|
[Pure]
|
||||||
|
[MethodImpl(CompilerResources.MethodImplOptions)]
|
||||||
|
public static TNumber ScaleRange<TNumber>(TNumber value, TNumber oldMin, TNumber oldMax, TNumber newMin, TNumber newMax)
|
||||||
|
where TNumber : INumber<TNumber>
|
||||||
|
{
|
||||||
|
TNumber oldRange = oldMax - oldMin;
|
||||||
|
TNumber newRange = newMax - newMin;
|
||||||
|
TNumber alpha = (value - oldMin) / oldRange;
|
||||||
|
return (alpha * newRange) + newMin;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -395,10 +500,16 @@ public static class MathUtility
|
|||||||
/// <param name="target">The interpolation target.</param>
|
/// <param name="target">The interpolation target.</param>
|
||||||
/// <param name="alpha">The interpolation alpha.</param>
|
/// <param name="alpha">The interpolation alpha.</param>
|
||||||
/// <returns>The interpolation result.</returns>
|
/// <returns>The interpolation result.</returns>
|
||||||
public static double SmoothStep(double value, double target, double alpha)
|
public static TNumber SmoothStep<TNumber>(TNumber value, TNumber target, TNumber alpha)
|
||||||
|
where TNumber : INumber<TNumber>
|
||||||
{
|
{
|
||||||
alpha = System.Math.Clamp(alpha, 0.0, 1.0);
|
TNumber one = TNumber.One;
|
||||||
alpha = -2.0 * alpha * alpha * alpha + 3.0 * alpha * alpha;
|
TNumber two = one + one;
|
||||||
return target * alpha + value * (1.0 - alpha);
|
TNumber three = two + one;
|
||||||
|
|
||||||
|
alpha = TNumber.Clamp(alpha, TNumber.Zero, TNumber.One);
|
||||||
|
alpha = -two * alpha * alpha * alpha + three * alpha * alpha;
|
||||||
|
return target * alpha + value * (one - alpha);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
104
X10D/src/Math/NumberExtensions.cs
Normal file
104
X10D/src/Math/NumberExtensions.cs
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
#if NET7_0_OR_GREATER
|
||||||
|
using System.Diagnostics.Contracts;
|
||||||
|
using System.Numerics;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using X10D.CompilerServices;
|
||||||
|
|
||||||
|
namespace X10D.Math;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Math-related extension methods for <see cref="INumber{TSelf}" />.
|
||||||
|
/// </summary>
|
||||||
|
public static class NumberExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a value indicating whether the current value is evenly divisible by 2.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The value whose parity to check.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// <see langword="true" /> if <paramref name="value" /> is evenly divisible by 2, or <see langword="false" />
|
||||||
|
/// otherwise.
|
||||||
|
/// </returns>
|
||||||
|
[Pure]
|
||||||
|
[MethodImpl(CompilerResources.MethodImplOptions)]
|
||||||
|
public static bool IsEven<TNumber>(this TNumber value)
|
||||||
|
where TNumber : INumber<TNumber>
|
||||||
|
{
|
||||||
|
return value % TNumber.CreateChecked(2) == TNumber.Zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a value indicating whether the current value is not evenly divisible by 2.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The value whose parity to check.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// <see langword="true" /> if <paramref name="value" /> is not evenly divisible by 2, or <see langword="false" />
|
||||||
|
/// otherwise.
|
||||||
|
/// </returns>
|
||||||
|
[Pure]
|
||||||
|
[MethodImpl(CompilerResources.MethodImplOptions)]
|
||||||
|
public static bool IsOdd<TNumber>(this TNumber value)
|
||||||
|
where TNumber : INumber<TNumber>
|
||||||
|
{
|
||||||
|
return !value.IsEven();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Performs a modulo operation which supports a negative dividend.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dividend">The dividend.</param>
|
||||||
|
/// <param name="divisor">The divisor.</param>
|
||||||
|
/// <returns>The result of <c>dividend mod divisor</c>.</returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// The <c>%</c> operator (commonly called the modulo operator) in C# is not defined to be modulo, but is instead
|
||||||
|
/// remainder. This quirk inherently makes it difficult to use modulo in a negative context, as <c>x % y</c> where x is
|
||||||
|
/// negative will return a negative value, akin to <c>-(x % y)</c>, even if precedence is forced. This method provides a
|
||||||
|
/// modulo operation which supports negative dividends.
|
||||||
|
/// </remarks>
|
||||||
|
/// <author>ShreevatsaR, https://stackoverflow.com/a/1082938/1467293</author>
|
||||||
|
/// <license>CC-BY-SA 2.5</license>
|
||||||
|
[Pure]
|
||||||
|
[MethodImpl(CompilerResources.MethodImplOptions)]
|
||||||
|
public static TNumber Mod<TNumber>(this TNumber dividend, TNumber divisor)
|
||||||
|
where TNumber : INumber<TNumber>
|
||||||
|
{
|
||||||
|
TNumber r = dividend % divisor;
|
||||||
|
return r < TNumber.Zero ? r + divisor : r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns an integer that indicates the sign of this number.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">A signed number.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// A number that indicates the sign of <paramref name="value" />, as shown in the following table.
|
||||||
|
///
|
||||||
|
/// <list type="table">
|
||||||
|
/// <listheader>
|
||||||
|
/// <term>Return value</term>
|
||||||
|
/// <description>Meaning</description>
|
||||||
|
/// </listheader>
|
||||||
|
///
|
||||||
|
/// <item>
|
||||||
|
/// <term>-1</term>
|
||||||
|
/// <description><paramref name="value" /> is less than zero.</description>
|
||||||
|
/// </item>
|
||||||
|
/// <item>
|
||||||
|
/// <term>0</term>
|
||||||
|
/// <description><paramref name="value" /> is equal to zero.</description>
|
||||||
|
/// </item>
|
||||||
|
/// <item>
|
||||||
|
/// <term>1</term>
|
||||||
|
/// <description><paramref name="value" /> is greater than zero.</description>
|
||||||
|
/// </item>
|
||||||
|
/// </list>
|
||||||
|
/// </returns>
|
||||||
|
[Pure]
|
||||||
|
[MethodImpl(CompilerResources.MethodImplOptions)]
|
||||||
|
public static int Sign<TNumber>(this TNumber value)
|
||||||
|
where TNumber : INumber<TNumber>
|
||||||
|
{
|
||||||
|
return TNumber.Sign(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
Loading…
Reference in New Issue
Block a user