A cross-platform UI framework for .NET
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

367 lines
14 KiB

using System;
namespace Avalonia.Utilities
{
/// <summary>
/// Provides math utilities not provided in System.Math.
/// </summary>
#if !BUILDTASK
public
#endif
static class MathUtilities
{
// smallest such that 1.0+DoubleEpsilon != 1.0
internal static readonly double DoubleEpsilon = 2.2204460492503131e-016;
private const float FloatEpsilon = 1.192092896e-07F;
/// <summary>
/// AreClose - Returns whether or not two doubles are "close". That is, whether or
/// not they are within epsilon of each other.
/// </summary>
/// <param name="value1"> The first double to compare. </param>
/// <param name="value2"> The second double to compare. </param>
public static bool AreClose(double value1, double value2)
{
//in case they are Infinities (then epsilon check does not work)
if (value1 == value2) return true;
double eps = (Math.Abs(value1) + Math.Abs(value2) + 10.0) * DoubleEpsilon;
double delta = value1 - value2;
return (-eps < delta) && (eps > delta);
}
/// <summary>
/// AreClose - Returns whether or not two doubles are "close". That is, whether or
/// not they are within epsilon of each other.
/// </summary>
/// <param name="value1"> The first double to compare. </param>
/// <param name="value2"> The second double to compare. </param>
/// <param name="eps"> The fixed epsilon value used to compare.</param>
public static bool AreClose(double value1, double value2, double eps)
{
//in case they are Infinities (then epsilon check does not work)
if (value1 == value2) return true;
double delta = value1 - value2;
return (-eps < delta) && (eps > delta);
}
/// <summary>
/// AreClose - Returns whether or not two floats are "close". That is, whether or
/// not they are within epsilon of each other.
/// </summary>
/// <param name="value1"> The first float to compare. </param>
/// <param name="value2"> The second float to compare. </param>
public static bool AreClose(float value1, float value2)
{
//in case they are Infinities (then epsilon check does not work)
if (value1 == value2) return true;
float eps = (Math.Abs(value1) + Math.Abs(value2) + 10.0f) * FloatEpsilon;
float delta = value1 - value2;
return (-eps < delta) && (eps > delta);
}
/// <summary>
/// LessThan - Returns whether or not the first double is less than the second double.
/// That is, whether or not the first is strictly less than *and* not within epsilon of
/// the other number.
/// </summary>
/// <param name="value1"> The first double to compare. </param>
/// <param name="value2"> The second double to compare. </param>
public static bool LessThan(double value1, double value2)
{
return (value1 < value2) && !AreClose(value1, value2);
}
/// <summary>
/// LessThan - Returns whether or not the first float is less than the second float.
/// That is, whether or not the first is strictly less than *and* not within epsilon of
/// the other number.
/// </summary>
/// <param name="value1"> The first single float to compare. </param>
/// <param name="value2"> The second single float to compare. </param>
public static bool LessThan(float value1, float value2)
{
return (value1 < value2) && !AreClose(value1, value2);
}
/// <summary>
/// GreaterThan - Returns whether or not the first double is greater than the second double.
/// That is, whether or not the first is strictly greater than *and* not within epsilon of
/// the other number.
/// </summary>
/// <param name="value1"> The first double to compare. </param>
/// <param name="value2"> The second double to compare. </param>
public static bool GreaterThan(double value1, double value2)
{
return (value1 > value2) && !AreClose(value1, value2);
}
/// <summary>
/// GreaterThan - Returns whether or not the first float is greater than the second float.
/// That is, whether or not the first is strictly greater than *and* not within epsilon of
/// the other number.
/// </summary>
/// <param name="value1"> The first float to compare. </param>
/// <param name="value2"> The second float to compare. </param>
public static bool GreaterThan(float value1, float value2)
{
return (value1 > value2) && !AreClose(value1, value2);
}
/// <summary>
/// LessThanOrClose - Returns whether or not the first double is less than or close to
/// the second double. That is, whether or not the first is strictly less than or within
/// epsilon of the other number.
/// </summary>
/// <param name="value1"> The first double to compare. </param>
/// <param name="value2"> The second double to compare. </param>
public static bool LessThanOrClose(double value1, double value2)
{
return (value1 < value2) || AreClose(value1, value2);
}
/// <summary>
/// LessThanOrClose - Returns whether or not the first float is less than or close to
/// the second float. That is, whether or not the first is strictly less than or within
/// epsilon of the other number.
/// </summary>
/// <param name="value1"> The first float to compare. </param>
/// <param name="value2"> The second float to compare. </param>
public static bool LessThanOrClose(float value1, float value2)
{
return (value1 < value2) || AreClose(value1, value2);
}
/// <summary>
/// GreaterThanOrClose - Returns whether or not the first double is greater than or close to
/// the second double. That is, whether or not the first is strictly greater than or within
/// epsilon of the other number.
/// </summary>
/// <param name="value1"> The first double to compare. </param>
/// <param name="value2"> The second double to compare. </param>
public static bool GreaterThanOrClose(double value1, double value2)
{
return (value1 > value2) || AreClose(value1, value2);
}
/// <summary>
/// GreaterThanOrClose - Returns whether or not the first float is greater than or close to
/// the second float. That is, whether or not the first is strictly greater than or within
/// epsilon of the other number.
/// </summary>
/// <param name="value1"> The first float to compare. </param>
/// <param name="value2"> The second float to compare. </param>
public static bool GreaterThanOrClose(float value1, float value2)
{
return (value1 > value2) || AreClose(value1, value2);
}
/// <summary>
/// IsOne - Returns whether or not the double is "close" to 1. Same as AreClose(double, 1),
/// but this is faster.
/// </summary>
/// <param name="value"> The double to compare to 1. </param>
public static bool IsOne(double value)
{
return Math.Abs(value - 1.0) < 10.0 * DoubleEpsilon;
}
/// <summary>
/// IsOne - Returns whether or not the float is "close" to 1. Same as AreClose(float, 1),
/// but this is faster.
/// </summary>
/// <param name="value"> The float to compare to 1. </param>
public static bool IsOne(float value)
{
return Math.Abs(value - 1.0f) < 10.0f * FloatEpsilon;
}
/// <summary>
/// IsZero - Returns whether or not the double is "close" to 0. Same as AreClose(double, 0),
/// but this is faster.
/// </summary>
/// <param name="value"> The double to compare to 0. </param>
public static bool IsZero(double value)
{
return Math.Abs(value) < 10.0 * DoubleEpsilon;
}
/// <summary>
/// IsZero - Returns whether or not the float is "close" to 0. Same as AreClose(float, 0),
/// but this is faster.
/// </summary>
/// <param name="value"> The float to compare to 0. </param>
public static bool IsZero(float value)
{
return Math.Abs(value) < 10.0f * FloatEpsilon;
}
/// <summary>
/// Clamps a value between a minimum and maximum value.
/// </summary>
/// <param name="val">The value.</param>
/// <param name="min">The minimum value.</param>
/// <param name="max">The maximum value.</param>
/// <returns>The clamped value.</returns>
public static double Clamp(double val, double min, double max)
{
if (min > max)
{
ThrowCannotBeGreaterThanException(min, max);
}
if (val < min)
{
return min;
}
else if (val > max)
{
return max;
}
else
{
return val;
}
}
/// <summary>
/// Clamps a value between a minimum and maximum value.
/// </summary>
/// <param name="val">The value.</param>
/// <param name="min">The minimum value.</param>
/// <param name="max">The maximum value.</param>
/// <returns>The clamped value.</returns>
public static decimal Clamp(decimal val, decimal min, decimal max)
{
if (min > max)
{
ThrowCannotBeGreaterThanException(min, max);
}
if (val < min)
{
return min;
}
else if (val > max)
{
return max;
}
else
{
return val;
}
}
/// <summary>
/// Clamps a value between a minimum and maximum value.
/// </summary>
/// <param name="value">The value.</param>
/// <param name="min">The minimum value.</param>
/// <param name="max">The maximum value.</param>
/// <returns>The clamped value.</returns>
public static float Clamp(float value, float min, float max)
{
var amax = Math.Max(min, max);
var amin = Math.Min(min, max);
return Math.Min(Math.Max(value, amin), amax);
}
/// <summary>
/// Clamps a value between a minimum and maximum value.
/// </summary>
/// <param name="val">The value.</param>
/// <param name="min">The minimum value.</param>
/// <param name="max">The maximum value.</param>
/// <returns>The clamped value.</returns>
public static int Clamp(int val, int min, int max)
{
if (min > max)
{
ThrowCannotBeGreaterThanException(min, max);
}
if (val < min)
{
return min;
}
else if (val > max)
{
return max;
}
else
{
return val;
}
}
/// <summary>
/// Converts an angle in degrees to radians.
/// </summary>
/// <param name="angle">The angle in degrees.</param>
/// <returns>The angle in radians.</returns>
public static double Deg2Rad(double angle)
{
return angle * (Math.PI / 180d);
}
/// <summary>
/// Converts an angle in gradians to radians.
/// </summary>
/// <param name="angle">The angle in gradians.</param>
/// <returns>The angle in radians.</returns>
public static double Grad2Rad(double angle)
{
return angle * (Math.PI / 200d);
}
/// <summary>
/// Converts an angle in turns to radians.
/// </summary>
/// <param name="angle">The angle in turns.</param>
/// <returns>The angle in radians.</returns>
public static double Turn2Rad(double angle)
{
return angle * 2 * Math.PI;
}
/// <summary>
/// Calculates the point of an angle on an ellipse.
/// </summary>
/// <param name="centre">The centre point of the ellipse.</param>
/// <param name="radiusX">The x radius of the ellipse.</param>
/// <param name="radiusY">The y radius of the ellipse.</param>
/// <param name="angle">The angle in radians.</param>
/// <returns>A point on the ellipse.</returns>
public static Point GetEllipsePoint(Point centre, double radiusX, double radiusY, double angle)
{
return new Point(radiusX * Math.Cos(angle) + centre.X, radiusY * Math.Sin(angle) + centre.Y);
}
/// <summary>
/// Gets the minimum and maximum from the specified numbers.
/// </summary>
/// <param name="a">The first number.</param>
/// <param name="b">The second number.</param>
/// <returns>A tuple containing the minimum and maximum of the two specified numbers.</returns>
public static (double min, double max) GetMinMax(double a, double b)
{
return a < b ? (a, b) : (b, a);
}
/// <summary>
/// Gets the minimum and maximum from the specified number and the difference with that number.
/// </summary>
/// <param name="initialValue">The initial value to use.</param>
/// <param name="delta">The difference for <paramref name="initialValue"/>.</param>
/// <returns>A tuple containing the minimum and maximum of the specified number and the difference with that number.</returns>
public static (double min, double max) GetMinMaxFromDelta(double initialValue, double delta)
{
return GetMinMax(initialValue, initialValue + delta);
}
private static void ThrowCannotBeGreaterThanException<T>(T min, T max)
{
throw new ArgumentException($"{min} cannot be greater than {max}.");
}
}
}