diff --git a/src/Avalonia.Base/Utilities/MathUtilities.cs b/src/Avalonia.Base/Utilities/MathUtilities.cs
index dcb3ef4a2b..546133bb03 100644
--- a/src/Avalonia.Base/Utilities/MathUtilities.cs
+++ b/src/Avalonia.Base/Utilities/MathUtilities.cs
@@ -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
{
///
@@ -8,6 +11,86 @@ namespace Avalonia.Utilities
///
public static class MathUtilities
{
+ ///
+ /// AreClose - Returns whether or not two doubles are "close". That is, whether or
+ /// not they are within epsilon of each other.
+ ///
+ /// The first double to compare.
+ /// The second double to compare.
+ 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);
+ }
+
+ ///
+ /// 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.
+ /// The first double to compare.
+ /// The second double to compare.
+ public static bool LessThan(double value1, double value2)
+ {
+ return (value1 < value2) && !AreClose(value1, value2);
+ }
+
+ ///
+ /// 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.
+ /// The first double to compare.
+ /// The second double to compare.
+ public static bool GreaterThan(double value1, double value2)
+ {
+ return (value1 > value2) && !AreClose(value1, value2);
+ }
+
+ ///
+ /// 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.
+ /// The first double to compare.
+ /// The second double to compare.
+ public static bool LessThanOrClose(double value1, double value2)
+ {
+ return (value1 < value2) || AreClose(value1, value2);
+ }
+
+ ///
+ /// 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.
+ ///
+ /// The first double to compare.
+ /// The second double to compare.
+ public static bool GreaterThanOrClose(double value1, double value2)
+ {
+ return (value1 > value2) || AreClose(value1, value2);
+ }
+
+ ///
+ /// IsOne - Returns whether or not the double is "close" to 1. Same as AreClose(double, 1),
+ /// but this is faster.
+ ///
+ /// The double to compare to 1.
+ public static bool IsOne(double value)
+ {
+ return Math.Abs(value - 1.0) < 10.0 * double.Epsilon;
+ }
+
+ ///
+ /// IsZero - Returns whether or not the double is "close" to 0. Same as AreClose(double, 0),
+ /// but this is faster.
+ ///
+ /// The double to compare to 0.
+ public static bool IsZero(double value)
+ {
+ return Math.Abs(value) < 10.0 * double.Epsilon;
+ }
+
///
/// Clamps a value between a minimum and maximum value.
///
@@ -31,6 +114,39 @@ namespace Avalonia.Utilities
}
}
+ ///
+ /// Calculates the value to be used for layout rounding at high DPI.
+ ///
+ /// Input value to be rounded.
+ /// Ratio of screen's DPI to layout DPI
+ /// Adjusted value that will produce layout rounding on screen at high dpi.
+ /// 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.
+ 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;
+ }
+
///
/// Clamps a value between a minimum and maximum value.
///
@@ -54,4 +170,4 @@ namespace Avalonia.Utilities
}
}
}
-}
+}
\ No newline at end of file