|
|
|
@ -1,6 +1,9 @@ |
|
|
|
// Copyright (c) The Avalonia Project. All rights reserved.
|
|
|
|
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|
|
|
|
|
|
|
using System; |
|
|
|
using System.Runtime.InteropServices; |
|
|
|
|
|
|
|
namespace Avalonia.Utilities |
|
|
|
{ |
|
|
|
/// <summary>
|
|
|
|
@ -8,6 +11,86 @@ namespace Avalonia.Utilities |
|
|
|
/// </summary>
|
|
|
|
public static class MathUtilities |
|
|
|
{ |
|
|
|
/// <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) * double.Epsilon; |
|
|
|
double 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.
|
|
|
|
/// <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>
|
|
|
|
/// 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.
|
|
|
|
/// <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>
|
|
|
|
/// 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.
|
|
|
|
/// <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>
|
|
|
|
/// 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>
|
|
|
|
/// 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 * double.Epsilon; |
|
|
|
} |
|
|
|
|
|
|
|
/// <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 * double.Epsilon; |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Clamps a value between a minimum and maximum value.
|
|
|
|
/// </summary>
|
|
|
|
@ -31,6 +114,39 @@ namespace Avalonia.Utilities |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Calculates the value to be used for layout rounding at high DPI.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="value">Input value to be rounded.</param>
|
|
|
|
/// <param name="dpiScale">Ratio of screen's DPI to layout DPI</param>
|
|
|
|
/// <returns>Adjusted value that will produce layout rounding on screen at high dpi.</returns>
|
|
|
|
/// <remarks>This is a layout helper method. It takes DPI into account and also does not return
|
|
|
|
/// the rounded value if it is unacceptable for layout, e.g. Infinity or NaN. It's a helper associated with
|
|
|
|
/// UseLayoutRounding property and should not be used as a general rounding utility.</remarks>
|
|
|
|
public static double RoundLayoutValue(double value, double dpiScale) |
|
|
|
{ |
|
|
|
double newValue; |
|
|
|
|
|
|
|
// If DPI == 1, don't use DPI-aware rounding.
|
|
|
|
if (!MathUtilities.AreClose(dpiScale, 1.0)) |
|
|
|
{ |
|
|
|
newValue = Math.Round(value * dpiScale) / dpiScale; |
|
|
|
// If rounding produces a value unacceptable to layout (NaN, Infinity or MaxValue), use the original value.
|
|
|
|
if (double.IsNaN(newValue) || |
|
|
|
double.IsInfinity(newValue) || |
|
|
|
MathUtilities.AreClose(newValue, double.MaxValue)) |
|
|
|
{ |
|
|
|
newValue = value; |
|
|
|
} |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
newValue = Math.Round(value); |
|
|
|
} |
|
|
|
|
|
|
|
return newValue; |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Clamps a value between a minimum and maximum value.
|
|
|
|
/// </summary>
|
|
|
|
@ -54,4 +170,4 @@ namespace Avalonia.Utilities |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |