using System;
using System.Runtime.CompilerServices;
using UnityEngine;
namespace Unity.XR.CoreUtils
{
///
/// Math utilities
///
public static class MathUtility
{
// constants used in approximate equality checks
internal static readonly float EpsilonScaled = Mathf.Epsilon * 8;
///
/// A faster drop-in replacement for Mathf.Approximately(a, b).
/// Compares two floating point values and returns true if they are similar.
/// As an optimization, this method does not take into account the magnitude of the values it is comparing.
/// This method may not provide the same results as Mathf.Approximately for extremely large values
///
/// The first float being compared
/// The second float being compared
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool Approximately(float a, float b)
{
var d = b - a;
var absDiff = d >= 0f ? d : -d;
return absDiff < EpsilonScaled;
}
///
/// A slightly faster way to do Approximately(a, 0f).
///
/// The floating point value to compare with 0
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool ApproximatelyZero(float a)
{
return (a >= 0f ? a : -a) < EpsilonScaled;
}
///
/// Constrain a value between a minimum and a maximum
///
/// The input number
/// The minimum output
/// The maximum output
/// The number, clamped between and
public static double Clamp(double input, double min, double max)
{
if (input > max)
return max;
return input < min ? min : input;
}
///
/// Finds the shortest angle distance between two angle values
///
/// The start value
/// The end value
/// Half of the max angle
/// The max angle value
/// The angle distance between start and end
public static double ShortestAngleDistance(double start, double end, double halfMax, double max)
{
var angleDelta = end - start;
var angleSign = Math.Sign(angleDelta);
angleDelta = Math.Abs(angleDelta) % max;
if (angleDelta > halfMax)
angleDelta = -(max - angleDelta);
return angleDelta * angleSign;
}
///
/// Finds the shortest angle distance between two angle values
///
/// The start value
/// The end value
/// Half of the max angle
/// The max angle value
/// The angle distance between start and end
public static float ShortestAngleDistance(float start, float end, float halfMax, float max)
{
var angleDelta = end - start;
var angleSign = Mathf.Sign(angleDelta);
angleDelta = Math.Abs(angleDelta) % max;
if (angleDelta > halfMax)
angleDelta = -(max - angleDelta);
return angleDelta * angleSign;
}
///
/// Is the float value infinity or NaN?
///
/// The float value
/// True if the value is infinity or NaN (not a number), otherwise false
public static bool IsUndefined(this float value)
{
return float.IsInfinity(value) || float.IsNaN(value);
}
///
/// Checks if a vector is aligned with one of the axis vectors
///
/// The vector
/// True if the vector is aligned with any axis, otherwise false
public static bool IsAxisAligned(this Vector3 v)
{
return ApproximatelyZero(v.x * v.y) && ApproximatelyZero(v.y * v.z) && ApproximatelyZero(v.z * v.x);
}
///
/// Check if a value is a positive power of two
///
/// The value to check
/// True if the value is a positive power of two, false otherwise
public static bool IsPositivePowerOfTwo(int value)
{
return value > 0 && (value & (value - 1)) == 0;
}
///
/// Return the index of the first flag bit set to true
///
/// The flags value to check
/// The index of the first active flag
public static int FirstActiveFlagIndex(int value)
{
if (value == 0)
return 0;
const int bits = sizeof(int) * 8;
for (var i = 0; i < bits; i++)
if ((value & 1 << i) != 0)
return i;
return 0;
}
}
}