Opentk/src/OpenTK/Math/MathHelper.cs

430 lines
16 KiB
C#

#region --- License ---
/* 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.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Text;
namespace OpenTK
{
/// <summary>
/// Contains common mathematical functions and constants.
/// </summary>
public static class MathHelper
{
#region Fields
/// <summary>
/// Defines the value of Pi as a <see cref="System.Single"/>.
/// </summary>
public const float Pi = 3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117067982148086513282306647093844609550582231725359408128481117450284102701938521105559644622948954930382f;
/// <summary>
/// Defines the value of Pi divided by two as a <see cref="System.Single"/>.
/// </summary>
public const float PiOver2 = Pi / 2;
/// <summary>
/// Defines the value of Pi divided by three as a <see cref="System.Single"/>.
/// </summary>
public const float PiOver3 = Pi / 3;
/// <summary>
/// Definesthe value of Pi divided by four as a <see cref="System.Single"/>.
/// </summary>
public const float PiOver4 = Pi / 4;
/// <summary>
/// Defines the value of Pi divided by six as a <see cref="System.Single"/>.
/// </summary>
public const float PiOver6 = Pi / 6;
/// <summary>
/// Defines the value of Pi multiplied by two as a <see cref="System.Single"/>.
/// </summary>
public const float TwoPi = 2 * Pi;
/// <summary>
/// Defines the value of Pi multiplied by 3 and divided by two as a <see cref="System.Single"/>.
/// </summary>
public const float ThreePiOver2 = 3 * Pi / 2;
/// <summary>
/// Defines the value of E as a <see cref="System.Single"/>.
/// </summary>
public const float E = 2.71828182845904523536f;
/// <summary>
/// Defines the base-10 logarithm of E.
/// </summary>
public const float Log10E = 0.434294482f;
/// <summary>
/// Defines the base-2 logarithm of E.
/// </summary>
public const float Log2E = 1.442695041f;
#endregion
#region Public Members
#region NextPowerOfTwo
/// <summary>
/// Returns the next power of two that is greater than or equal to the specified number.
/// </summary>
/// <param name="n">The specified number.</param>
/// <returns>The next power of two.</returns>
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)));
}
/// <summary>
/// Returns the next power of two that is greater than or equal to the specified number.
/// </summary>
/// <param name="n">The specified number.</param>
/// <returns>The next power of two.</returns>
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)));
}
/// <summary>
/// Returns the next power of two that is greater than or equal to the specified number.
/// </summary>
/// <param name="n">The specified number.</param>
/// <returns>The next power of two.</returns>
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)));
}
/// <summary>
/// Returns the next power of two that is greater than or equal to the specified number.
/// </summary>
/// <param name="n">The specified number.</param>
/// <returns>The next power of two.</returns>
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)));
}
#endregion
#region Factorial
/// <summary>Calculates the factorial of a given natural number.
/// </summary>
/// <param name="n">The number.</param>
/// <returns>n!</returns>
public static long Factorial(int n)
{
long result = 1;
for (; n > 1; n--)
result *= n;
return result;
}
#endregion
#region BinomialCoefficient
/// <summary>
/// Calculates the binomial coefficient <paramref name="n"/> above <paramref name="k"/>.
/// </summary>
/// <param name="n">The n.</param>
/// <param name="k">The k.</param>
/// <returns>n! / (k! * (n - k)!)</returns>
public static long BinomialCoefficient(int n, int k)
{
return Factorial(n) / (Factorial(k) * Factorial(n - k));
}
#endregion
#region InverseSqrtFast
/// <summary>
/// Returns an approximation of the inverse square root of left number.
/// </summary>
/// <param name="x">A number.</param>
/// <returns>An approximation of the inverse square root of the specified number, with an upper error bound of 0.001</returns>
/// <remarks>
/// 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/
/// </remarks>
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;
}
}
/// <summary>
/// Returns an approximation of the inverse square root of left number.
/// </summary>
/// <param name="x">A number.</param>
/// <returns>An approximation of the inverse square root of the specified number, with an upper error bound of 0.001</returns>
/// <remarks>
/// 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/
/// </remarks>
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
}
#endregion
#region DegreesToRadians
/// <summary>
/// Convert degrees to radians
/// </summary>
/// <param name="degrees">An angle in degrees</param>
/// <returns>The angle expressed in radians</returns>
public static float DegreesToRadians(float degrees)
{
const float degToRad = (float)System.Math.PI / 180.0f;
return degrees * degToRad;
}
/// <summary>
/// Convert radians to degrees
/// </summary>
/// <param name="radians">An angle in radians</param>
/// <returns>The angle expressed in degrees</returns>
public static float RadiansToDegrees(float radians)
{
const float radToDeg = 180.0f / (float)System.Math.PI;
return radians * radToDeg;
}
/// <summary>
/// Convert degrees to radians
/// </summary>
/// <param name="degrees">An angle in degrees</param>
/// <returns>The angle expressed in radians</returns>
public static double DegreesToRadians(double degrees)
{
const double degToRad = System.Math.PI / 180.0;
return degrees * degToRad;
}
/// <summary>
/// Convert radians to degrees
/// </summary>
/// <param name="radians">An angle in radians</param>
/// <returns>The angle expressed in degrees</returns>
public static double RadiansToDegrees(double radians)
{
const double radToDeg = 180.0 / System.Math.PI;
return radians * radToDeg;
}
#endregion
#region Swap
/// <summary>
/// Swaps two double values.
/// </summary>
/// <param name="a">The first value.</param>
/// <param name="b">The second value.</param>
public static void Swap(ref double a, ref double b)
{
double temp = a;
a = b;
b = temp;
}
/// <summary>
/// Swaps two float values.
/// </summary>
/// <param name="a">The first value.</param>
/// <param name="b">The second value.</param>
public static void Swap(ref float a, ref float b)
{
float temp = a;
a = b;
b = temp;
}
#endregion
#region Clamp
/// <summary>
/// Clamps a number between a minimum and a maximum.
/// </summary>
/// <param name="n">The number to clamp.</param>
/// <param name="min">The minimum allowed value.</param>
/// <param name="max">The maximum allowed value.</param>
/// <returns>min, if n is lower than min; max, if n is higher than max; n otherwise.</returns>
public static int Clamp(int n, int min, int max)
{
return Math.Max(Math.Min(n, max), min);
}
/// <summary>
/// Clamps a number between a minimum and a maximum.
/// </summary>
/// <param name="n">The number to clamp.</param>
/// <param name="min">The minimum allowed value.</param>
/// <param name="max">The maximum allowed value.</param>
/// <returns>min, if n is lower than min; max, if n is higher than max; n otherwise.</returns>
public static float Clamp(float n, float min, float max)
{
return Math.Max(Math.Min(n, max), min);
}
/// <summary>
/// Clamps a number between a minimum and a maximum.
/// </summary>
/// <param name="n">The number to clamp.</param>
/// <param name="min">The minimum allowed value.</param>
/// <param name="max">The maximum allowed value.</param>
/// <returns>min, if n is lower than min; max, if n is higher than max; n otherwise.</returns>
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);
}
/// <summary>
/// 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
/// </summary>
/// <param name="a">the first value to compare</param>
/// <param name="b">>the second value to compare</param>
/// <param name="maxDeltaBits">the number of floating point bits to check</param>
/// <returns></returns>
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);
}
/// <summary>
/// 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.
/// </summary>
/// <param name="a">The first float.</param>
/// <param name="b">The second float.</param>
/// <param name="epsilon">The maximum error between the two.</param>
/// <returns><value>true</value> if the values are approximately equal within the error margin; otherwise, <value>false</value>.</returns>
[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;
}
/// <summary>
/// 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.
/// </summary>
/// <param name="a">The first float.</param>
/// <param name="b">The second float.</param>
/// <param name="epsilon">The maximum error between the two.</param>
/// <returns><value>true</value> if the values are approximately equal within the error margin; otherwise, <value>false</value>.</returns>
[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;
}
#endregion
#endregion
}
}