#region License /* MoonWorks - Game Development Framework * Copyright 2021 Evan Hemsley */ /* Derived from code by Ethan Lee (Copyright 2009-2021). * Released under the Microsoft Public License. * See fna.LICENSE for details. * Derived from code by the Mono.Xna Team (Copyright 2006). * Released under the MIT License. See monoxna.LICENSE for details. */ #endregion namespace MoonWorks.Math { /// /// Contains commonly used precalculated values and mathematical operations. /// public static class MathHelper { #region Public Constants /// /// Represents the mathematical constant e(2.71828175). /// public const float E = (float) System.Math.E; /// /// Represents the log base ten of e(0.4342945). /// public const float Log10E = 0.4342945f; /// /// Represents the log base two of e(1.442695). /// public const float Log2E = 1.442695f; /// /// Represents the value of pi(3.14159274). /// public const float Pi = (float) System.Math.PI; /// /// Represents the value of pi divided by two(1.57079637). /// public const float PiOver2 = (float) (System.Math.PI / 2.0); /// /// Represents the value of pi divided by four(0.7853982). /// public const float PiOver4 = (float) (System.Math.PI / 4.0); /// /// Represents the value of pi times two(6.28318548). /// public const float TwoPi = (float) (System.Math.PI * 2.0); #endregion #region Internal Static Readonly Fields internal static readonly float MachineEpsilonFloat = GetMachineEpsilonFloat(); #endregion #region Public Static Methods /// /// Returns the Cartesian coordinate for one axis of a point that is defined by a /// given triangle and two normalized barycentric (areal) coordinates. /// /// /// The coordinate on one axis of vertex 1 of the defining triangle. /// /// /// The coordinate on the same axis of vertex 2 of the defining triangle. /// /// /// The coordinate on the same axis of vertex 3 of the defining triangle. /// /// /// The normalized barycentric (areal) coordinate b2, equal to the weighting factor /// for vertex 2, the coordinate of which is specified in value2. /// /// /// The normalized barycentric (areal) coordinate b3, equal to the weighting factor /// for vertex 3, the coordinate of which is specified in value3. /// /// /// Cartesian coordinate of the specified point with respect to the axis being used. /// public static float Barycentric( float value1, float value2, float value3, float amount1, float amount2 ) { return value1 + (value2 - value1) * amount1 + (value3 - value1) * amount2; } /// /// Performs a Catmull-Rom interpolation using the specified positions. /// /// The first position in the interpolation. /// The second position in the interpolation. /// The third position in the interpolation. /// The fourth position in the interpolation. /// Weighting factor. /// A position that is the result of the Catmull-Rom interpolation. public static float CatmullRom( float value1, float value2, float value3, float value4, float amount ) { /* Using formula from http://www.mvps.org/directx/articles/catmull/ * Internally using doubles not to lose precision. */ double amountSquared = amount * amount; double amountCubed = amountSquared * amount; return (float) ( 0.5 * ( ((2.0 * value2 + (value3 - value1) * amount) + ((2.0 * value1 - 5.0 * value2 + 4.0 * value3 - value4) * amountSquared) + (3.0 * value2 - value1 - 3.0 * value3 + value4) * amountCubed) ) ); } /// /// Restricts a value to be within a specified range. /// /// The value to clamp. /// /// The minimum value. If value is less than min, min /// will be returned. /// /// /// The maximum value. If value is greater than max, max /// will be returned. /// /// The clamped value. public static float Clamp(float value, float min, float max) { // First we check to see if we're greater than the max. value = (value > max) ? max : value; // Then we check to see if we're less than the min. value = (value < min) ? min : value; // There's no check to see if min > max. return value; } /// /// Calculates the absolute value of the difference of two values. /// /// Source value. /// Source value. /// Distance between the two values. public static float Distance(float value1, float value2) { return System.Math.Abs(value1 - value2); } /// /// Performs a Hermite spline interpolation. /// /// Source position. /// Source tangent. /// Source position. /// Source tangent. /// Weighting factor. /// The result of the Hermite spline interpolation. public static float Hermite( float value1, float tangent1, float value2, float tangent2, float amount ) { /* All transformed to double not to lose precision * Otherwise, for high numbers of param:amount the result is NaN instead * of Infinity. */ double v1 = value1, v2 = value2, t1 = tangent1, t2 = tangent2, s = amount; double result; double sCubed = s * s * s; double sSquared = s * s; if (WithinEpsilon(amount, 0f)) { result = value1; } else if (WithinEpsilon(amount, 1f)) { result = value2; } else { result = ( ((2 * v1 - 2 * v2 + t2 + t1) * sCubed) + ((3 * v2 - 3 * v1 - 2 * t1 - t2) * sSquared) + (t1 * s) + v1 ); } return (float) result; } /// /// Linearly interpolates between two values. /// /// Source value. /// Source value. /// /// Value between 0 and 1 indicating the weight of value2. /// /// Interpolated value. /// /// This method performs the linear interpolation based on the following formula. /// value1 + (value2 - value1) * amount /// Passing amount a value of 0 will cause value1 to be returned, a value of 1 will /// cause value2 to be returned. /// public static float Lerp(float value1, float value2, float amount) { return value1 + (value2 - value1) * amount; } /// /// Returns the greater of two values. /// /// Source value. /// Source value. /// The greater value. public static float Max(float value1, float value2) { return value1 > value2 ? value1 : value2; } /// /// Returns the lesser of two values. /// /// Source value. /// Source value. /// The lesser value. public static float Min(float value1, float value2) { return value1 < value2 ? value1 : value2; } /// /// Interpolates between two values using a cubic equation. /// /// Source value. /// Source value. /// Weighting value. /// Interpolated value. public static float SmoothStep(float value1, float value2, float amount) { /* It is expected that 0 < amount < 1. * If amount < 0, return value1. * If amount > 1, return value2. */ float result = MathHelper.Clamp(amount, 0f, 1f); result = MathHelper.Hermite(value1, 0f, value2, 0f, result); return result; } /// /// Converts radians to degrees. /// /// The angle in radians. /// The angle in degrees. /// /// This method uses double precision internally, though it returns single float. /// Factor = 180 / pi /// public static float ToDegrees(float radians) { return (float) (radians * 57.295779513082320876798154814105); } /// /// Converts degrees to radians. /// /// The angle in degrees. /// The angle in radians. /// /// This method uses double precision internally, though it returns single float. /// Factor = pi / 180 /// public static float ToRadians(float degrees) { return (float) (degrees * 0.017453292519943295769236907684886); } /// /// Reduces a given angle to a value between pi and -pi. /// /// The angle to reduce, in radians. /// The new angle, in radians. public static float WrapAngle(float angle) { if ((angle > -Pi) && (angle <= Pi)) { return angle; } angle %= TwoPi; if (angle <= -Pi) { return angle + TwoPi; } if (angle > Pi) { return angle - TwoPi; } return angle; } #endregion #region Internal Static Methods // FIXME: This could be an extension! ClampIntEXT? -flibit /// /// Restricts a value to be within a specified range. /// /// The value to clamp. /// /// The minimum value. If value is less than min, min /// will be returned. /// /// /// The maximum value. If value is greater than max, max /// will be returned. /// /// The clamped value. internal static int Clamp(int value, int min, int max) { value = (value > max) ? max : value; value = (value < min) ? min : value; return value; } internal static bool WithinEpsilon(float floatA, float floatB) { return System.Math.Abs(floatA - floatB) < MachineEpsilonFloat; } internal static int ClosestMSAAPower(int value) { /* Checking for the highest power of two _after_ than the given int: * http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 * Take result, divide by 2, get the highest power of two _before_! * -flibit */ if (value == 1) { // ... Except for 1, which is invalid for MSAA -flibit return 0; } int result = value - 1; result |= result >> 1; result |= result >> 2; result |= result >> 4; result |= result >> 8; result |= result >> 16; result += 1; if (result == value) { return result; } return result >> 1; } #endregion #region Private Static Methods /// /// Find the current machine's Epsilon for the float data type. /// (That is, the largest float, e, where e == 0.0f is true.) /// private static float GetMachineEpsilonFloat() { float machineEpsilon = 1.0f; float comparison; /* Keep halving the working value of machineEpsilon until we get a number that * when added to 1.0f will still evaluate as equal to 1.0f. */ do { machineEpsilon *= 0.5f; comparison = 1.0f + machineEpsilon; } while (comparison > 1.0f); return machineEpsilon; } #endregion } }