// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved. using System; using System.ComponentModel; namespace FlaxEngine { /// /// A collection of common math functions. /// [HideInEditor] public static class Mathf { /// /// The value for which all absolute numbers smaller than are considered equal to zero. /// public const float Epsilon = 1e-6f; /// /// A value specifying the approximation of π which is 180 degrees. /// public const float Pi = (float)Math.PI; /// /// A value specifying the approximation of 2π which is 360 degrees. /// public const float TwoPi = (float)(2 * Math.PI); /// /// A value specifying the approximation of π/2 which is 90 degrees. /// public const float PiOverTwo = (float)(Math.PI / 2); /// /// A value specifying the approximation of π/4 which is 45 degrees. /// public const float PiOverFour = (float)(Math.PI / 4); /// /// Returns the absolute value of f. /// /// public static float Abs(float f) { return Math.Abs(f); } /// /// Returns the absolute value of value. /// /// public static int Abs(int value) { return Math.Abs(value); } /// /// Returns the arc-cosine of f - the angle in radians whose cosine is f. /// /// public static float Acos(float f) { return (float)Math.Acos(f); } /// /// Compares two floating point values if they are similar. /// /// /// public static bool Approximately(float a, float b) { return Abs(b - a) < Max(1E-06f * Max(Abs(a), Abs(b)), Epsilon * 8f); } /// /// Returns the arc-sine of f - the angle in radians whose sine is f. /// /// public static float Asin(float f) { return (float)Math.Asin(f); } /// /// Returns the arc-tangent of f - the angle in radians whose tangent is f. /// /// public static float Atan(float f) { return (float)Math.Atan(f); } /// /// Returns the angle in radians whose Tan is y/x. /// /// /// public static float Atan2(float y, float x) { return (float)Math.Atan2(y, x); } /// /// Returns the smallest integer greater to or equal to f. /// /// public static float Ceil(float f) { return (float)Math.Ceiling(f); } /// /// Returns the smallest integer greater to or equal to f. /// /// public static int CeilToInt(float f) { return (int)Math.Ceiling(f); } /// /// Clamps value between 0 and 1 and returns value. /// /// Value to clamp /// Result value public static float Saturate(float value) { if (value < 0f) return 0f; return value > 1f ? 1f : value; } /// /// Returns the cosine of angle f in radians. /// /// public static float Cos(float f) { return (float)Math.Cos(f); } /// /// Calculates the shortest difference between two given angles given in degrees. /// /// /// public static float DeltaAngle(float current, float target) { float t = Repeat(target - current, 360f); if (t > 180f) t = t - 360f; return t; } /// /// Returns e raised to the specified power. /// /// public static float Exp(float power) { return (float)Math.Exp(power); } /// /// Returns the largest integer smaller to or equal to f. /// /// public static float Floor(float f) { return (float)Math.Floor(f); } /// /// Returns the largest integer smaller to or equal to f. /// /// public static int FloorToInt(float f) { return (int)Math.Floor(f); } /// /// Remaps the specified value from the specified range to another. /// /// The value to remap. /// The source range minimum. /// The source range maximum. /// The destination range minimum. /// The destination range maximum. /// The remapped value. public static float Remap(float value, float fromMin, float fromMax, float toMin, float toMax) { return (value - fromMin) / (fromMax - fromMin) * (toMax - toMin) + toMin; } /// /// Calculates the linear parameter t that produces the interpolation value within the range [a, b]. /// /// /// /// public static float InverseLerp(float a, float b, float value) { if (a == b) return 0f; return Saturate((value - a) / (b - a)); } /// /// Same as Lerp but makes sure the values interpolate correctly when they wrap around 360 degrees. /// /// /// /// public static float LerpAngle(float a, float b, float t) { float c = Repeat(b - a, 360f); if (c > 180f) c = c - 360f; return a + c * Saturate(t); } /// /// Returns the logarithm of a specified number in a specified base. /// /// /// public static float Log(float f, float p) { return (float)Math.Log(f, p); } /// /// Returns the natural (base e) logarithm of a specified number. /// /// public static float Log(float f) { return (float)Math.Log(f); } /// /// Returns the base 10 logarithm of a specified number. /// /// public static float Log10(float f) { return (float)Math.Log10(f); } /// /// Returns largest of two or more values. /// /// /// public static float Max(float a, float b) { return a <= b ? b : a; } /// /// Returns largest of two or more values. /// /// /// public static double Max(double a, double b) { return a <= b ? b : a; } /// /// Returns largest of two or more values. /// /// /// public static long Max(long a, long b) { return a <= b ? b : a; } /// /// Returns largest of two or more values. /// /// /// public static ulong Max(ulong a, ulong b) { return a <= b ? b : a; } /// /// Returns largest of two or more values. /// /// public static float Max(params float[] values) { int length = values.Length; if (length == 0) return 0f; float t = values[0]; for (var i = 1; i < length; i++) if (values[i] > t) t = values[i]; return t; } /// /// Returns the largest of two or more values. /// /// /// public static int Max(int a, int b) { return a <= b ? b : a; } /// /// Returns the largest of two or more values. /// /// public static int Max(params int[] values) { int length = values.Length; if (length == 0) return 0; int t = values[0]; for (var i = 1; i < length; i++) if (values[i] > t) t = values[i]; return t; } /// /// Returns the smallest of two or more values. /// /// /// public static float Min(float a, float b) { return a >= b ? b : a; } /// /// Returns the smallest of two or more values. /// /// /// public static double Min(double a, double b) { return a >= b ? b : a; } /// /// Returns the smallest of two or more values. /// /// /// public static long Min(long a, long b) { return a >= b ? b : a; } /// /// Returns the smallest of two or more values. /// /// /// public static ulong Min(ulong a, ulong b) { return a >= b ? b : a; } /// /// Returns the smallest of two or more values. /// /// public static float Min(params float[] values) { int length = values.Length; if (length == 0) return 0f; float t = values[0]; for (var i = 1; i < length; i++) if (values[i] < t) t = values[i]; return t; } /// /// Returns the smallest of two or more values. /// /// /// public static int Min(int a, int b) { return a >= b ? b : a; } /// /// Returns the smallest of two or more values. /// /// public static int Min(params int[] values) { int length = values.Length; if (length == 0) return 0; int num = values[0]; for (var i = 1; i < length; i++) if (values[i] < num) num = values[i]; return num; } /// /// Moves a value current towards target. /// /// The current value. /// The value to move towards. /// The maximum change that should be applied to the value. public static float MoveTowards(float current, float target, float maxDelta) { if (Abs(target - current) <= maxDelta) return target; return current + Sign(target - current) * maxDelta; } /// /// Same as MoveTowards but makes sure the values interpolate correctly when they wrap around 360 degrees. /// /// /// /// public static float MoveTowardsAngle(float current, float target, float maxDelta) { float delta = DeltaAngle(current, target); if ((-maxDelta < delta) && (delta < maxDelta)) return target; target = current + delta; return MoveTowards(current, target, maxDelta); } /// /// PingPongs the value t, so that it is never larger than length and never smaller than 0. /// /// /// public static float PingPong(float t, float length) { t = Repeat(t, length * 2f); return length - Abs(t - length); } /// /// Returns f raised to power p. /// /// /// public static float Pow(float f, float p) { return (float)Math.Pow(f, p); } /// /// Loops the value t, so that it is never larger than length and never smaller than 0. /// /// /// public static float Repeat(float t, float length) { return t - Floor(t / length) * length; } /// /// Returns f rounded to the nearest integer. /// /// public static float Round(float f) { return (float)Math.Round(f); } /// /// Returns f rounded to the nearest integer. /// /// public static int RoundToInt(float f) { return (int)Math.Round(f); } /// /// Returns the sign of f. /// /// public static float Sign(float f) { return f < 0f ? -1f : 1f; } /// /// Returns the sine of angle f in radians. /// /// public static float Sin(float f) { return (float)Math.Sin(f); } /// /// Gradually changes a value towards a desired goal over time with smoothing. /// /// The current value. /// The target value. /// The current velocity. /// The smoothing time. Smaller values increase blending time. /// The maximum speed. /// The smoothed value. public static float SmoothDamp(float current, float target, ref float currentVelocity, float smoothTime, float maxSpeed) { return SmoothDamp(current, target, ref currentVelocity, smoothTime, maxSpeed, Time.DeltaTime); } /// /// Gradually changes a value towards a desired goal over time with smoothing. /// /// The current value. /// The target value. /// The current velocity. /// The smoothing time. Smaller values increase blending time. /// The smoothed value. public static float SmoothDamp(float current, float target, ref float currentVelocity, float smoothTime) { return SmoothDamp(current, target, ref currentVelocity, smoothTime, float.PositiveInfinity, Time.DeltaTime); } /// /// Gradually changes a value towards a desired goal over time with smoothing. /// /// The current value. /// The target value. /// The current velocity. /// The smoothing time. Smaller values increase blending time. /// The maximum speed. /// The delta time (in seconds) since last update. /// The smoothed value. public static float SmoothDamp(float current, float target, ref float currentVelocity, float smoothTime, [DefaultValue("float.PositiveInfinity")] float maxSpeed, [DefaultValue("Time.DeltaTime")] float deltaTime) { smoothTime = Max(0.0001f, smoothTime); float a = 2f / smoothTime; float b = a * deltaTime; float c = 1f / (1f + b + 0.48f * b * b + 0.235f * b * b * b); float d = current - target; float e = target; float f = maxSpeed * smoothTime; d = Clamp(d, -f, f); target = current - d; float g = (currentVelocity + a * d) * deltaTime; currentVelocity = (currentVelocity - a * g) * c; float h = target + (d + g) * c; if (e - current > 0f == h > e) { h = e; currentVelocity = (h - e) / deltaTime; } return h; } /// /// Gradually changes an angle towards a desired goal over time with smoothing. /// /// The current angle. /// The target angle. /// The current velocity. /// The smoothing time. Smaller values increase blending time. /// The maximum speed. /// The smoothed value. public static float SmoothDampAngle(float current, float target, ref float currentVelocity, float smoothTime, float maxSpeed) { return SmoothDampAngle(current, target, ref currentVelocity, smoothTime, maxSpeed, Time.DeltaTime); } /// /// Gradually changes an angle towards a desired goal over time with smoothing. /// /// The current angle. /// The target angle. /// The current velocity. /// The smoothing time. Smaller values increase blending time. /// The smoothed value. public static float SmoothDampAngle(float current, float target, ref float currentVelocity, float smoothTime) { return SmoothDampAngle(current, target, ref currentVelocity, smoothTime, float.PositiveInfinity, Time.DeltaTime); } /// /// Gradually changes an angle towards a desired goal over time with smoothing. /// /// The current angle. /// The target angle. /// The current velocity. /// The smoothing time. Smaller values increase blending time. /// The maximum speed. /// The delta time (in seconds) since last update. /// The smoothed value. public static float SmoothDampAngle(float current, float target, ref float currentVelocity, float smoothTime, [DefaultValue("float.PositiveInfinity")] float maxSpeed, [DefaultValue("Time.DeltaTime")] float deltaTime) { target = current + DeltaAngle(current, target); return SmoothDamp(current, target, ref currentVelocity, smoothTime, maxSpeed, deltaTime); } /// /// Interpolates between min and max with smoothing at the limits. /// /// /// /// public static float SmoothStep(float from, float to, float t) { t = Saturate(t); t = -2f * t * t * t + 3f * t * t; return to * t + from * (1f - t); } /// /// Performs a cubic interpolation. /// /// The first point. /// The tangent direction at first point. /// The second point. /// The tangent direction at second point. /// The distance along the spline. /// The interpolated value. public static float CubicInterp(float p0, float t0, float p1, float t1, float alpha) { float alpha2 = alpha * alpha; float alpha3 = alpha2 * alpha; return (((2 * alpha3) - (3 * alpha2) + 1) * p0) + ((alpha3 - (2 * alpha2) + alpha) * t0) + ((alpha3 - alpha2) * t1) + (((-2 * alpha3) + (3 * alpha2)) * p1); } /// /// Interpolate between A and B, applying an ease in function. Exponent controls the degree of the curve. /// public static float InterpEaseIn(float a, float b, float alpha, float exponent) { float modifiedAlpha = Pow(alpha, exponent); return Lerp(a, b, modifiedAlpha); } /// /// Interpolate between A and B, applying an ease out function. Exponent controls the degree of the curve. /// public static float InterpEaseOut(float a, float b, float alpha, float exponent) { float modifiedAlpha = 1.0f - Pow(1.0f - alpha, exponent); return Lerp(a, b, modifiedAlpha); } /// /// Interpolate between A and B, applying an ease in/out function. Exponent controls the degree of the curve. /// public static float InterpEaseInOut(float a, float b, float alpha, float exponent) { return Lerp(a, b, (alpha < 0.5f) ? InterpEaseIn(0.0f, 1.0f, alpha * 2.0f, exponent) * 0.5f : InterpEaseOut(0.0f, 1.0f, alpha * 2.0f - 1.0f, exponent) * 0.5f + 0.5f); } /// /// Interpolation between A and B, applying a sinusoidal in function. /// public static float InterpSinIn(float a, float b, float alpha) { float modifiedAlpha = -1.0f * Cos(alpha * PiOverTwo) + 1.0f; return Lerp(a, b, modifiedAlpha); } /// /// Interpolation between A and B, applying a sinusoidal out function. /// public static float InterpSinOut(float a, float b, float alpha) { float modifiedAlpha = Sin(alpha * PiOverTwo); return Lerp(a, b, modifiedAlpha); } /// /// Interpolation between A and B, applying a sinusoidal in/out function. /// public static float InterpSinInOut(float a, float b, float alpha) { return Lerp(a, b, (alpha < 0.5f) ? InterpSinIn(0.0f, 1.0f, alpha * 2.0f) * 0.5f : InterpSinOut(0.0f, 1.0f, alpha * 2.0f - 1.0f) * 0.5f + 0.5f); } /// /// Interpolation between A and B, applying an exponential in function. /// public static float InterpExpoIn(float a, float b, float alpha) { float modifiedAlpha = (alpha == 0.0f) ? 0.0f : Pow(2.0f, 10.0f * (alpha - 1.0f)); return Lerp(a, b, modifiedAlpha); } /// /// Interpolation between A and B, applying an exponential out function. /// public static float InterpExpoOut(float a, float b, float alpha) { float modifiedAlpha = (alpha == 1.0f) ? 1.0f : -Pow(2.0f, -10.0f * alpha) + 1.0f; return Lerp(a, b, modifiedAlpha); } /// /// Interpolation between A and B, applying an exponential in/out function. /// public static float InterpExpoInOut(float a, float b, float alpha) { return Lerp(a, b, (alpha < 0.5f) ? InterpExpoIn(0.0f, 1.0f, alpha * 2.0f) * 0.5f : InterpExpoOut(0.0f, 1.0f, alpha * 2.0f - 1.0f) * 0.5f + 0.5f); } /// /// Interpolation between A and B, applying a circular in function. /// public static float InterpCircularIn(float a, float b, float alpha) { float modifiedAlpha = -1.0f * (Sqrt(1.0f - alpha * alpha) - 1.0f); return Lerp(a, b, modifiedAlpha); } /// /// Interpolation between A and B, applying a circular out function. /// public static float InterpCircularOut(float a, float b, float alpha) { alpha -= 1.0f; float modifiedAlpha = Sqrt(1.0f - alpha * alpha); return Lerp(a, b, modifiedAlpha); } /// /// Interpolation between A and B, applying a circular in/out function. /// public static float InterpCircularInOut(float a, float b, float alpha) { return Lerp(a, b, (alpha < 0.5f) ? InterpCircularIn(0.0f, 1.0f, alpha * 2.0f) * 0.5f : InterpCircularOut(0.0f, 1.0f, alpha * 2.0f - 1.0f) * 0.5f + 0.5f); } /// /// Maps the specified value from the given range into another. /// /// The value to map from range [fromMin; fromMax]. /// The source range minimum value. /// The source range maximum value. /// The destination range minimum value. /// The destination range maximum value. /// The mapped value in range [toMin; toMax]. public static float Map(float value, float fromMin, float fromMax, float toMin, float toMax) { float t = (value - fromMin) / (fromMax - fromMin); return toMin + t * (toMax - toMin); } /// /// Determines whether the specified x is pow of 2. /// /// The x. /// true if the specified x is pow2; otherwise, false. public static bool IsPowerOfTwo(int x) { return ((x != 0) && (x & (x - 1)) == 0); } /// /// Get the next power of two for a size. /// /// The size. /// System.Int32. public static int NextPowerOfTwo(int size) { return 1 << (int)Math.Ceiling(Math.Log(size, 2)); } /// /// Get the next power of two for a size. /// /// The size. /// System.Int32. public static float NextPowerOfTwo(float size) { return (float)Math.Pow(2, Math.Ceiling(Math.Log(size, 2))); } /// /// Converts a float value from sRGB to linear. /// /// The sRGB value. /// A linear value. public static float SRgbToLinear(float sRgbValue) { if (sRgbValue < 0.04045f) return sRgbValue / 12.92f; return (float)Math.Pow((sRgbValue + 0.055) / 1.055, 2.4); } /// /// Converts a float value from linear to sRGB. /// /// The linear value. /// The encoded sRGB value. public static float LinearToSRgb(float linearValue) { if (linearValue < 0.0031308f) return linearValue * 12.92f; return (float)(1.055 * Math.Pow(linearValue, 1 / 2.4) - 0.055); } /// /// Returns square root of f. /// /// public static float Sqrt(float f) { return (float)Math.Sqrt(f); } /// /// Returns square of the given value. /// /// The value. /// The value * value. public static int Square(int f) { return f * f; } /// /// Returns square of the given value. /// /// The value. /// The value * value. public static double Square(double f) { return f * f; } /// /// Returns square of the given value. /// /// The value. /// The value * value. public static float Square(float f) { return f * f; } /// /// Returns the tangent of angle f in radians. /// /// public static float Tan(float f) { return (float)Math.Tan(f); } /// /// Checks if a and b are almost equals, taking into account the magnitude of floating point numbers (unlike /// method). See Remarks. /// See remarks. /// /// The left value to compare. /// The right value to compare. /// true if a almost equal to b, false otherwise /// /// The code is using the technique described by Bruce Dawson in /// /// Comparing /// Floating point numbers 2012 edition /// /// . /// public static unsafe bool NearEqual(float a, float b) { // Check if the numbers are really close -- needed // when comparing numbers near zero. if (IsZero(a - b)) return true; // Original from Bruce Dawson: http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ int aInt = *(int*)&a; int bInt = *(int*)&b; // Different signs means they do not match. if (aInt < 0 != bInt < 0) return false; // Find the difference in ULPs. int ulp = Math.Abs(aInt - bInt); // Choose of maxUlp = 4 // according to http://code.google.com/p/googletest/source/browse/trunk/include/gtest/internal/gtest-internal.h const int maxUlp = 4; return ulp <= maxUlp; } /// /// Checks if a and b are almost equals, taking into account the magnitude of floating point numbers . /// See remarks. /// /// The left value to compare. /// The right value to compare. /// true if a almost equal to b, false otherwise public static bool NearEqual(double a, double b) { return Math.Abs(a - b) < double.Epsilon * 10; } /// /// Determines whether the specified value is close to zero (0.0f). /// /// The floating value. /// true if the specified value is close to zero (0.0f); otherwise, false. public static bool IsZero(float a) { return Math.Abs(a) < Epsilon; } /// /// Determines whether the specified value is close to one (1.0f). /// /// The floating value. /// true if the specified value is close to one (1.0f); otherwise, false. public static bool IsOne(float a) { return IsZero(a - 1.0f); } /// /// Checks if a - b are almost equals within a float epsilon. /// /// The left value to compare. /// The right value to compare. /// Epsilon value /// true if a almost equal to b within a float epsilon, false otherwise public static bool WithinEpsilon(float a, float b, float epsilon) { float num = a - b; return (-epsilon <= num) && (num <= epsilon); } /// /// Determines whether the specified value is in a given range [min; max]. /// /// The value. /// The minimum. /// The maximum. /// /// true if the specified value is in a given range; otherwise, false. /// public static bool IsInRange(float value, float min, float max) { return value >= min && value <= max; } /// /// Determines whether the specified value is NOT in a given range [min; max]. /// /// The value. /// The minimum. /// The maximum. /// /// true if the specified value is NOT in a given range; otherwise, false. /// public static bool IsNotInRange(float value, float min, float max) { return value < min || value > max; } /// /// Determines whether the specified value is in a given range [min; max]. /// /// The value. /// The minimum. /// The maximum. /// /// true if the specified value is in a given range; otherwise, false. /// public static bool IsInRange(int value, int min, int max) { return value >= min && value <= max; } /// /// Determines whether the specified value is NOT in a given range [min; max]. /// /// The value. /// The minimum. /// The maximum. /// /// true if the specified value is NOT in a given range; otherwise, false. /// public static bool IsNotInRange(int value, int min, int max) { return value < min || value > max; } #region Angle units conversions /// /// Converts revolutions to degrees. /// public static float RevolutionsToDegrees = 360.0f; /// /// Converts revolutions to radians. /// public static float RevolutionsToRadians = TwoPi; /// /// Converts revolutions to gradians. /// public static float RevolutionsToGradians = 400.0f; /// /// Converts degrees to revolutions. /// public static float DegreesToRevolutions = (1.0f / 360.0f); /// /// Converts degrees to radians. /// public static float DegreesToRadians = (Pi / 180.0f); /// /// Converts radians to revolutions. /// public static float RadiansToRevolutions = (1.0f / TwoPi); /// /// Converts radians to gradians. /// public static float RadiansToGradians = (200.0f / Pi); /// /// Converts gradians to revolutions. /// public static float GradiansToRevolutions = (1.0f / 400.0f); /// /// Converts gradians to degrees. /// public static float GradiansToDegrees = (9.0f / 10.0f); /// /// Converts gradians to radians. /// public static float GradiansToRadians = (Pi / 200.0f); /// /// Converts radians to degrees. /// public static float RadiansToDegrees = (180.0f / Pi); #endregion /// /// Given a heading which may be outside the +/- PI range, 'unwind' it back into that range. /// /// Angle in radians to unwind. /// Valid angle in radians. public static float UnwindRadians(float angle) { // TODO: make it faster? while (angle > Pi) { angle -= TwoPi; } while (angle < -Pi) { angle += TwoPi; } return angle; } /// /// Utility to ensure angle is between +/- 180 degrees by unwinding /// /// Angle in degrees to unwind. /// Valid angle in degrees. public static float UnwindDegrees(float angle) { // TODO: make it faster? while (angle > 180.0f) { angle -= 360.0f; } while (angle < -180.0f) { angle += 360.0f; } return angle; } /// /// Clamps the specified value. /// /// The value. /// The min. /// The max. /// The result of clamping a value between min and max public static long Clamp(long value, long min, long max) { return value < min ? min : value > max ? max : value; } /// /// Clamps the specified value. /// /// The value. /// The min. /// The max. /// The result of clamping a value between min and max public static ulong Clamp(ulong value, ulong min, ulong max) { return value < min ? min : value > max ? max : value; } /// /// Clamps the specified value. /// /// The value. /// The min. /// The max. /// The result of clamping a value between min and max public static float Clamp(float value, float min, float max) { return value < min ? min : value > max ? max : value; } /// /// Clamps the specified value. /// /// The value. /// The min. /// The max. /// The result of clamping a value between min and max public static double Clamp(double value, double min, double max) { return value < min ? min : value > max ? max : value; } /// /// Clamps the specified value. /// /// The value. /// The min. /// The max. /// The result of clamping a value between min and max public static int Clamp(int value, int min, int max) { return value < min ? min : value > max ? max : value; } /// /// Interpolates between two values using a linear function by a given amount. /// /// /// See http://www.encyclopediaofmath.org/index.php/Linear_interpolation and /// http://fgiesen.wordpress.com/2012/08/15/linear-interpolation-past-present-and-future/ /// /// Value to interpolate from. /// Value to interpolate to. /// Interpolation amount. /// The result of linear interpolation of values based on the amount. public static double Lerp(double from, double to, double amount) { return from + (to - from) * amount; } /// /// Interpolates between two values using a linear function by a given amount. /// /// /// See http://www.encyclopediaofmath.org/index.php/Linear_interpolation and /// http://fgiesen.wordpress.com/2012/08/15/linear-interpolation-past-present-and-future/ /// /// Value to interpolate from. /// Value to interpolate to. /// Interpolation amount. /// The result of linear interpolation of values based on the amount. public static float Lerp(float from, float to, float amount) { return from + (to - from) * amount; } /// /// Interpolates between two values using a linear function by a given amount. /// /// /// See http://www.encyclopediaofmath.org/index.php/Linear_interpolation and /// http://fgiesen.wordpress.com/2012/08/15/linear-interpolation-past-present-and-future/ /// /// Value to interpolate from. /// Value to interpolate to. /// Interpolation amount. /// The result of linear interpolation of values based on the amount. public static int Lerp(int from, int to, float amount) { return (int)(from + (to - from) * amount); } /// /// Interpolates between two values using a linear function by a given amount. /// /// /// See http://www.encyclopediaofmath.org/index.php/Linear_interpolation and /// http://fgiesen.wordpress.com/2012/08/15/linear-interpolation-past-present-and-future/ /// /// Value to interpolate from. /// Value to interpolate to. /// Interpolation amount. /// The result of linear interpolation of values based on the amount. public static byte Lerp(byte from, byte to, float amount) { return (byte)(from + (to - from) * amount); } /// /// Performs smooth (cubic Hermite) interpolation between 0 and 1. /// /// /// See https://en.wikipedia.org/wiki/Smoothstep /// /// Value between 0 and 1 indicating interpolation amount. public static float SmoothStep(float amount) { return amount <= 0 ? 0 : amount >= 1 ? 1 : amount * amount * (3 - 2 * amount); } /// /// Performs a smooth(er) interpolation between 0 and 1 with 1st and 2nd order derivatives of zero at endpoints. /// /// /// See https://en.wikipedia.org/wiki/Smoothstep /// /// Value between 0 and 1 indicating interpolation amount. public static float SmootherStep(float amount) { return amount <= 0 ? 0 : amount >= 1 ? 1 : amount * amount * amount * (amount * (amount * 6 - 15) + 10); } /// /// Calculates the modulo of the specified value. /// /// The value. /// The modulo. /// The result of the modulo applied to value public static float Mod(float value, float modulo) { if (modulo == 0.0f) return value; return value % modulo; } /// /// Calculates the modulo 2*PI of the specified value. /// /// The value. /// The result of the modulo applied to value public static float Mod2PI(float value) { return Mod(value, TwoPi); } /// /// Wraps the specified value into a range [min, max] /// /// The value to wrap. /// The min. /// The max. /// Result of the wrapping. /// Is thrown when is greater than . public static int Wrap(int value, int min, int max) { if (min > max) throw new ArgumentException(string.Format("min {0} should be less than or equal to max {1}", min, max), nameof(min)); // Code from http://stackoverflow.com/a/707426/1356325 int rangeSize = max - min + 1; if (value < min) value += rangeSize * ((min - value) / rangeSize + 1); return min + (value - min) % rangeSize; } /// /// Wraps the specified value into a range [min, max] /// /// The value. /// The min. /// The max. /// Result of the wrapping. /// Is thrown when is greater than . public static float Wrap(float value, float min, float max) { if (NearEqual(min, max)) return min; double mind = min; double maxd = max; double valued = value; if (mind > maxd) throw new ArgumentException(string.Format("min {0} should be less than or equal to max {1}", min, max), nameof(min)); double rangeSize = maxd - mind; return (float)(mind + (valued - mind) - rangeSize * Math.Floor((valued - mind) / rangeSize)); } /// /// Gauss function. /// http://en.wikipedia.org/wiki/Gaussian_function#Two-dimensional_Gaussian_function /// /// Curve amplitude. /// Position X. /// Position Y /// Center X. /// Center Y. /// Curve sigma X. /// Curve sigma Y. /// The result of Gaussian function. public static float Gauss(float amplitude, float x, float y, float centerX, float centerY, float sigmaX, float sigmaY) { return (float)Gauss((double)amplitude, x, y, centerX, centerY, sigmaX, sigmaY); } /// /// Gauss function. /// http://en.wikipedia.org/wiki/Gaussian_function#Two-dimensional_Gaussian_function /// /// Curve amplitude. /// Position X. /// Position Y /// Center X. /// Center Y. /// Curve sigma X. /// Curve sigma Y. /// The result of Gaussian function. public static double Gauss(double amplitude, double x, double y, double centerX, double centerY, double sigmaX, double sigmaY) { double cx = x - centerX; double cy = y - centerY; double componentX = cx * cx / (2 * sigmaX * sigmaX); double componentY = cy * cy / (2 * sigmaY * sigmaY); return amplitude * Math.Exp(-(componentX + componentY)); } /// /// Converts the input alpha value from a linear 0-1 value into the output alpha described by blend mode. /// /// The alpha (normalized to 0-1). /// The mode. /// The output alpha (normalized to 0-1). public static float InterpolateAlphaBlend(float alpha, AlphaBlendMode mode) { switch (mode) { case AlphaBlendMode.Sinusoidal: alpha = (Sin(alpha * Pi - PiOverTwo) + 1.0f) / 2.0f; break; case AlphaBlendMode.Cubic: alpha = CubicInterp(0.0f, 0.0f, 1.0f, 0.0f, alpha); break; case AlphaBlendMode.QuadraticInOut: alpha = InterpEaseInOut(0.0f, 1.0f, alpha, 2); break; case AlphaBlendMode.CubicInOut: alpha = InterpEaseInOut(0.0f, 1.0f, alpha, 3); break; case AlphaBlendMode.HermiteCubic: alpha = SmoothStep(0.0f, 1.0f, alpha); break; case AlphaBlendMode.QuarticInOut: alpha = InterpEaseInOut(0.0f, 1.0f, alpha, 4); break; case AlphaBlendMode.QuinticInOut: alpha = InterpEaseInOut(0.0f, 1.0f, alpha, 5); break; case AlphaBlendMode.CircularIn: alpha = InterpCircularIn(0.0f, 1.0f, alpha); break; case AlphaBlendMode.CircularOut: alpha = InterpCircularOut(0.0f, 1.0f, alpha); break; case AlphaBlendMode.CircularInOut: alpha = InterpCircularInOut(0.0f, 1.0f, alpha); break; case AlphaBlendMode.ExpIn: alpha = InterpExpoIn(0.0f, 1.0f, alpha); break; case AlphaBlendMode.ExpOut: alpha = InterpExpoOut(0.0f, 1.0f, alpha); break; case AlphaBlendMode.ExpInOut: alpha = InterpExpoInOut(0.0f, 1.0f, alpha); break; } return Saturate(alpha); } } }