/* Licensed under the MIT/X11 license. * Copyright (c) 2006-2008 the OpenTK Team. * This notice may not be removed from any source distribution. * See license.txt for licensing detailed licensing details. * * Contributions by Andy Gill, James Talton and Georg Wächter. */ using System; using System.Diagnostics.CodeAnalysis; namespace OpenTK { /// /// Contains common mathematical functions and constants. /// public static class MathHelper { /// /// Defines the value of Pi as a . /// public const float Pi = 3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117067982148086513282306647093844609550582231725359408128481117450284102701938521105559644622948954930382f; /// /// Defines the value of Pi divided by two as a . /// public const float PiOver2 = Pi / 2; /// /// Defines the value of Pi divided by three as a . /// public const float PiOver3 = Pi / 3; /// /// Definesthe value of Pi divided by four as a . /// public const float PiOver4 = Pi / 4; /// /// Defines the value of Pi divided by six as a . /// public const float PiOver6 = Pi / 6; /// /// Defines the value of Pi multiplied by two as a . /// public const float TwoPi = 2 * Pi; /// /// Defines the value of Pi multiplied by 3 and divided by two as a . /// public const float ThreePiOver2 = 3 * Pi / 2; /// /// Defines the value of E as a . /// public const float E = 2.71828182845904523536f; /// /// Defines the base-10 logarithm of E. /// public const float Log10E = 0.434294482f; /// /// Defines the base-2 logarithm of E. /// public const float Log2E = 1.442695041f; /// /// Returns the next power of two that is greater than or equal to the specified number. /// /// The specified number. /// The next power of two. public static long NextPowerOfTwo(long n) { if (n < 0) { throw new ArgumentOutOfRangeException("n", "Must be positive."); } return (long)System.Math.Pow(2, System.Math.Ceiling(System.Math.Log((double)n, 2))); } /// /// Returns the next power of two that is greater than or equal to the specified number. /// /// The specified number. /// The next power of two. public static int NextPowerOfTwo(int n) { if (n < 0) { throw new ArgumentOutOfRangeException("n", "Must be positive."); } return (int)System.Math.Pow(2, System.Math.Ceiling(System.Math.Log((double)n, 2))); } /// /// Returns the next power of two that is greater than or equal to the specified number. /// /// The specified number. /// The next power of two. public static float NextPowerOfTwo(float n) { if (n < 0) { throw new ArgumentOutOfRangeException("n", "Must be positive."); } return (float)System.Math.Pow(2, System.Math.Ceiling(System.Math.Log((double)n, 2))); } /// /// Returns the next power of two that is greater than or equal to the specified number. /// /// The specified number. /// The next power of two. public static double NextPowerOfTwo(double n) { if (n < 0) { throw new ArgumentOutOfRangeException("n", "Must be positive."); } return System.Math.Pow(2, System.Math.Ceiling(System.Math.Log((double)n, 2))); } /// Calculates the factorial of a given natural number. /// /// The number. /// n! public static long Factorial(int n) { long result = 1; for (; n > 1; n--) { result *= n; } return result; } /// /// Calculates the binomial coefficient above . /// /// The n. /// The k. /// n! / (k! * (n - k)!) public static long BinomialCoefficient(int n, int k) { return Factorial(n) / (Factorial(k) * Factorial(n - k)); } /// /// Returns an approximation of the inverse square root of left number. /// /// A number. /// An approximation of the inverse square root of the specified number, with an upper error bound of 0.001 /// /// This is an improved implementation of the the method known as Carmack's inverse square root /// which is found in the Quake III source code. This implementation comes from /// http://www.codemaestro.com/reviews/review00000105.html. For the history of this method, see /// http://www.beyond3d.com/content/articles/8/ /// public static float InverseSqrtFast(float x) { unsafe { float xhalf = 0.5f * x; int i = *(int*)&x; // Read bits as integer. i = 0x5f375a86 - (i >> 1); // Make an initial guess for Newton-Raphson approximation x = *(float*)&i; // Convert bits back to float x = x * (1.5f - xhalf * x * x); // Perform left single Newton-Raphson step. return x; } } /// /// Returns an approximation of the inverse square root of left number. /// /// A number. /// An approximation of the inverse square root of the specified number, with an upper error bound of 0.001 /// /// This is an improved implementation of the the method known as Carmack's inverse square root /// which is found in the Quake III source code. This implementation comes from /// http://www.codemaestro.com/reviews/review00000105.html. For the history of this method, see /// http://www.beyond3d.com/content/articles/8/ /// public static double InverseSqrtFast(double x) { return InverseSqrtFast((float)x); // TODO: The following code is wrong. Fix it, to improve precision. #if false unsafe { double xhalf = 0.5f * x; int i = *(int*)&x; // Read bits as integer. i = 0x5f375a86 - (i >> 1); // Make an initial guess for Newton-Raphson approximation x = *(float*)&i; // Convert bits back to float x = x * (1.5f - xhalf * x * x); // Perform left single Newton-Raphson step. return x; } #endif } /// /// Convert degrees to radians /// /// An angle in degrees /// The angle expressed in radians public static float DegreesToRadians(float degrees) { const float degToRad = (float)System.Math.PI / 180.0f; return degrees * degToRad; } /// /// Convert radians to degrees /// /// An angle in radians /// The angle expressed in degrees public static float RadiansToDegrees(float radians) { const float radToDeg = 180.0f / (float)System.Math.PI; return radians * radToDeg; } /// /// Convert degrees to radians /// /// An angle in degrees /// The angle expressed in radians public static double DegreesToRadians(double degrees) { const double degToRad = System.Math.PI / 180.0; return degrees * degToRad; } /// /// Convert radians to degrees /// /// An angle in radians /// The angle expressed in degrees public static double RadiansToDegrees(double radians) { const double radToDeg = 180.0 / System.Math.PI; return radians * radToDeg; } /// /// Swaps two double values. /// /// The first value. /// The second value. public static void Swap(ref double a, ref double b) { double temp = a; a = b; b = temp; } /// /// Swaps two float values. /// /// The first value. /// The second value. public static void Swap(ref float a, ref float b) { float temp = a; a = b; b = temp; } /// /// Clamps a number between a minimum and a maximum. /// /// The number to clamp. /// The minimum allowed value. /// The maximum allowed value. /// min, if n is lower than min; max, if n is higher than max; n otherwise. public static int Clamp(int n, int min, int max) { return Math.Max(Math.Min(n, max), min); } /// /// Clamps a number between a minimum and a maximum. /// /// The number to clamp. /// The minimum allowed value. /// The maximum allowed value. /// min, if n is lower than min; max, if n is higher than max; n otherwise. public static float Clamp(float n, float min, float max) { return Math.Max(Math.Min(n, max), min); } /// /// Clamps a number between a minimum and a maximum. /// /// The number to clamp. /// The minimum allowed value. /// The maximum allowed value. /// min, if n is lower than min; max, if n is higher than max; n otherwise. public static double Clamp(double n, double min, double max) { return Math.Max(Math.Min(n, max), min); } private static unsafe int FloatToInt32Bits(float f) { return *((int*)&f); } /// /// Approximates floating point equality with a maximum number of different bits. /// This is typically used in place of an epsilon comparison. /// see: https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ /// see: https://stackoverflow.com/questions/3874627/floating-point-comparison-functions-for-c-sharp /// /// the first value to compare /// >the second value to compare /// the number of floating point bits to check /// public static bool ApproximatelyEqual(float a, float b, int maxDeltaBits) { // we use longs here, otherwise we run into a two's complement problem, causing this to fail with -2 and 2.0 long aInt = FloatToInt32Bits(a); if (aInt < 0) { aInt = Int32.MinValue - aInt; } long bInt = FloatToInt32Bits(b); if (bInt < 0) { bInt = Int32.MinValue - bInt; } long intDiff = Math.Abs(aInt - bInt); return intDiff <= (1 << maxDeltaBits); } /// /// Approximates double-precision floating point equality by an epsilon (maximum error) value. /// This method is designed as a "fits-all" solution and attempts to handle as many cases as possible. /// /// The first float. /// The second float. /// The maximum error between the two. /// true if the values are approximately equal within the error margin; otherwise, false. [SuppressMessage("ReSharper", "CompareOfFloatsByEqualityOperator")] public static bool ApproximatelyEqualEpsilon(double a, double b, double epsilon) { const double doubleNormal = (1L << 52) * double.Epsilon; double absA = Math.Abs(a); double absB = Math.Abs(b); double diff = Math.Abs(a - b); if (a == b) { // Shortcut, handles infinities return true; } if (a == 0.0f || b == 0.0f || diff < doubleNormal) { // a or b is zero, or both are extremely close to it. // relative error is less meaningful here return diff < (epsilon * doubleNormal); } // use relative error return diff / Math.Min((absA + absB), double.MaxValue) < epsilon; } /// /// Approximates single-precision floating point equality by an epsilon (maximum error) value. /// This method is designed as a "fits-all" solution and attempts to handle as many cases as possible. /// /// The first float. /// The second float. /// The maximum error between the two. /// true if the values are approximately equal within the error margin; otherwise, false. [SuppressMessage("ReSharper", "CompareOfFloatsByEqualityOperator")] public static bool ApproximatelyEqualEpsilon(float a, float b, float epsilon) { const float floatNormal = (1 << 23) * float.Epsilon; float absA = Math.Abs(a); float absB = Math.Abs(b); float diff = Math.Abs(a - b); if (a == b) { // Shortcut, handles infinities return true; } if (a == 0.0f || b == 0.0f || diff < floatNormal) { // a or b is zero, or both are extremely close to it. // relative error is less meaningful here return diff < (epsilon * floatNormal); } // use relative error float relativeError = diff / Math.Min((absA + absB), float.MaxValue); return relativeError < epsilon; } /// /// Approximates equivalence between two single-precision floating-point numbers on a direct human scale. /// It is important to note that this does not approximate equality - instead, it merely checks whether or not /// two numbers could be considered equivalent to each other within a certain tolerance. The tolerance is /// inclusive. /// /// The first value to compare. /// The second value to compare· /// The tolerance within which the two values would be considered equivalent. /// Whether or not the values can be considered equivalent within the tolerance. [SuppressMessage("ReSharper", "CompareOfFloatsByEqualityOperator")] public static bool ApproximatelyEquivalent(float a, float b, float tolerance) { if (a == b) { // Early bailout, handles infinities return true; } float diff = Math.Abs(a - b); return diff <= tolerance; } /// /// Approximates equivalence between two double-precision floating-point numbers on a direct human scale. /// It is important to note that this does not approximate equality - instead, it merely checks whether or not /// two numbers could be considered equivalent to each other within a certain tolerance. The tolerance is /// inclusive. /// /// The first value to compare. /// The second value to compare· /// The tolerance within which the two values would be considered equivalent. /// Whether or not the values can be considered equivalent within the tolerance. [SuppressMessage("ReSharper", "CompareOfFloatsByEqualityOperator")] public static bool ApproximatelyEquivalent(double a, double b, double tolerance) { if (a == b) { // Early bailout, handles infinities return true; } double diff = Math.Abs(a - b); return diff <= tolerance; } } }