// Copyright (c) 2010-2014 SharpDX - Alexandre Mutel // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // ----------------------------------------------------------------------------- // Original code from SlimMath project. http://code.google.com/p/slimmath/ // Greetings to SlimDX Group. Original code published with the following license: // ----------------------------------------------------------------------------- /* * Copyright (c) 2007-2011 SlimDX Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ using System; namespace SharpDX { public static class MathUtil { /// /// The value for which all absolute numbers smaller than are considered equal to zero. /// public const float ZeroTolerance = 1e-6f; // Value a 8x higher than 1.19209290E-07F /// /// 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); /// /// 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 unsafe static 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); } /// /// 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) < ZeroTolerance; } /// /// 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)); } /// /// Converts revolutions to degrees. /// /// The value to convert. /// The converted value. public static float RevolutionsToDegrees(float revolution) { return revolution * 360.0f; } /// /// Converts revolutions to radians. /// /// The value to convert. /// The converted value. public static float RevolutionsToRadians(float revolution) { return revolution * TwoPi; } /// /// Converts revolutions to gradians. /// /// The value to convert. /// The converted value. public static float RevolutionsToGradians(float revolution) { return revolution * 400.0f; } /// /// Converts degrees to revolutions. /// /// The value to convert. /// The converted value. public static float DegreesToRevolutions(float degree) { return degree / 360.0f; } /// /// Converts degrees to radians. /// /// The value to convert. /// The converted value. public static float DegreesToRadians(float degree) { return degree * (Pi / 180.0f); } /// /// Converts radians to revolutions. /// /// The value to convert. /// The converted value. public static float RadiansToRevolutions(float radian) { return radian / TwoPi; } /// /// Converts radians to gradians. /// /// The value to convert. /// The converted value. public static float RadiansToGradians(float radian) { return radian * (200.0f / Pi); } /// /// Converts gradians to revolutions. /// /// The value to convert. /// The converted value. public static float GradiansToRevolutions(float gradian) { return gradian / 400.0f; } /// /// Converts gradians to degrees. /// /// The value to convert. /// The converted value. public static float GradiansToDegrees(float gradian) { return gradian * (9.0f / 10.0f); } /// /// Converts gradians to radians. /// /// The value to convert. /// The converted value. public static float GradiansToRadians(float gradian) { return gradian * (Pi / 200.0f); } /// /// Converts radians to degrees. /// /// The value to convert. /// The converted value. public static float RadiansToDegrees(float radian) { return radian * (180.0f / Pi); } /// /// 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 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 (1 - amount) * from + amount * to; } /// /// 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 (1 - amount) * from + amount * to; } /// /// 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)Lerp((float)from, (float)to, 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), "min"); // Code from http://stackoverflow.com/a/707426/1356325 int range_size = max - min + 1; if (value < min) value += range_size * ((min - value) / range_size + 1); return min + (value - min) % range_size; } /// /// 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), "min"); var range_size = maxd - mind; return (float)(mind + (valued - mind) - (range_size * Math.Floor((valued - mind) / range_size))); } /// /// 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) { var cx = x - centerX; var cy = y - centerY; var componentX = (cx * cx) / (2 * sigmaX * sigmaX); var componentY = (cy * cy) / (2 * sigmaY * sigmaY); return amplitude * Math.Exp(-(componentX + componentY)); } } }