From 3aecbd75a35478e1eb21d4672f2d7769d32b228d Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Thu, 12 Aug 2021 13:48:45 +0200 Subject: [PATCH 1/8] Remove unused include. --- Source/Engine/Core/Math/Math.h | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/Engine/Core/Math/Math.h b/Source/Engine/Core/Math/Math.h index e9bd6e58f..b184ca5ad 100644 --- a/Source/Engine/Core/Math/Math.h +++ b/Source/Engine/Core/Math/Math.h @@ -2,7 +2,6 @@ #pragma once -#include "Engine/Core/Core.h" #include "Engine/Core/Types/BaseTypes.h" #include From 328027300dfdd8ba2415c707fa629cc716d0003d Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Thu, 12 Aug 2021 13:49:21 +0200 Subject: [PATCH 2/8] Add Mathd implementation. --- Source/Engine/Core/Math/Mathd.h | 362 ++++++++++++++++++++++++++++++++ 1 file changed, 362 insertions(+) create mode 100644 Source/Engine/Core/Math/Mathd.h diff --git a/Source/Engine/Core/Math/Mathd.h b/Source/Engine/Core/Math/Mathd.h new file mode 100644 index 000000000..8da6f0330 --- /dev/null +++ b/Source/Engine/Core/Math/Mathd.h @@ -0,0 +1,362 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#pragma once + +#include +#include "Engine/Core/Types/BaseTypes.h" + +namespace Mathd +{ + /// + /// Computes the sine and cosine of a scalar float. + /// + /// The input angle (in radians). + /// The output sine value. + /// The output cosine value. + FLAXENGINE_API void SinCos(double angle, double& sine, double& cosine); + + static FORCE_INLINE double Trunc(double value) + { + return trunc(value); + } + + static FORCE_INLINE double Round(double value) + { + return round(value); + } + + static FORCE_INLINE double Floor(double value) + { + return floor(value); + } + + static FORCE_INLINE double Ceil(double value) + { + return ceil(value); + } + + static FORCE_INLINE double Sin(double value) + { + return sin(value); + } + + static FORCE_INLINE double Asin(double value) + { + return asin(value < -1. ? -1. : value < 1. ? value : 1.); + } + + static FORCE_INLINE double Sinh(double value) + { + return sinh(value); + } + + static FORCE_INLINE double Cos(double value) + { + return cos(value); + } + + static FORCE_INLINE double Acos(double value) + { + return acos(value < -1. ? -1. : value < 1. ? value : 1.); + } + + static FORCE_INLINE double Tan(double value) + { + return tan(value); + } + + static FORCE_INLINE double Atan(double value) + { + return atan(value); + } + + static FORCE_INLINE double Atan2(double y, double x) + { + return atan2(y, x); + } + + static FORCE_INLINE double InvSqrt(double value) + { + return 1.0f / sqrt(value); + } + + static FORCE_INLINE double Log(const double value) + { + return log(value); + } + + static FORCE_INLINE double Log2(const double value) + { + return log2(value); + } + + static FORCE_INLINE double Log10(const double value) + { + return log10(value); + } + + static FORCE_INLINE double Pow(const double base, const double exponent) + { + return pow(base, exponent); + } + + static FORCE_INLINE double Sqrt(const double value) + { + return sqrt(value); + } + + static FORCE_INLINE double Exp(const double value) + { + return exp(value); + } + + static FORCE_INLINE double Exp2(const double value) + { + return exp2(value); + } + + static FORCE_INLINE double Abs(const double value) + { + return fabs(value); + } + + static FORCE_INLINE double Mod(const double a, const double b) + { + return fmod(a, b); + } + + static FORCE_INLINE double ModF(double a, double* b) + { + return modf(a, b); + } + + /// + /// Returns signed fractional part of a double. + /// + /// Double point value to convert. + /// A double between [0 ; 1) for nonnegative input. A double between [-1; 0) for negative input. + static FORCE_INLINE double Fractional(double value) + { + return value - Trunc(value); + } + + static int64 TruncToInt(double value) + { + return (int64)value; + } + + static int64 FloorToInt(double value) + { + return TruncToInt(floor(value)); + } + + static FORCE_INLINE int64 RoundToInt(double value) + { + return FloorToInt(value + 0.5); + } + + static FORCE_INLINE int64 CeilToInt(double value) + { + return TruncToInt(ceil(value)); + } + + // Performs smooth (cubic Hermite) interpolation between 0 and 1 + // @param amount Value between 0 and 1 indicating interpolation amount + static double SmoothStep(double 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 + // @param amount Value between 0 and 1 indicating interpolation amount + static double SmootherStep(double amount) + { + return amount <= 0. ? 0. : amount >= 1. ? 1. : amount * amount * amount * (amount * (amount * 6. - 15.) + 10.); + } + + // Determines whether the specified value is close to zero (0.0) + // @param a The floating value + // @returns True if the specified value is close to zero (0.0). otherwise false + inline bool IsZero(double a) + { + return Abs(a) < ZeroTolerance; + } + + // Determines whether the specified value is close to one (1.0f) + // @param a The floating value + // @returns True if the specified value is close to one (1.0f). otherwise false + inline bool IsOne(double a) + { + return IsZero(a - 1.); + } + + // Returns a value indicating the sign of a number + // @returns A number that indicates the sign of value + inline double Sign(double v) + { + return v > 0. ? 1. : v < 0. ? -1. : 0.; + } + + /// + /// Compares the sign of two floating-point values. + /// + /// The first value. + /// The second value. + /// True if given values have the same sign (both positive or negative); otherwise false. + inline bool SameSign(const double a, const double b) + { + return a * b >= 0.; + } + + /// + /// Compares the sign of two floating-point values. + /// + /// The first value. + /// The second value. + /// True if given values don't have the same sign (first is positive and second is negative or vice versa); otherwise false. + inline bool NotSameSign(const double a, const double b) + { + return a * b < 0.; + } + + /// + /// Checks if a and b are not even almost equal, taking into account the magnitude of floating point numbers + /// + /// The left value to compare + /// The right value to compare + /// False if a almost equal to b, otherwise true + static bool NotNearEqual(double a, double b) + { + // Check if the numbers are really close - needed when comparing numbers near zero + if (IsZero(a - b)) + return false; + + // Original from Bruce Dawson: http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ + const int64 aInt = *(int64*)&a; + const int64 bInt = *(int64*)&b; + + // Different signs means they do not match + if (aInt < 0 != bInt < 0) + return true; + + // Find the difference in ULPs + const int64 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; + } + + + /// + /// Checks if a and b are almost equals within the given epsilon value. + /// + /// The left value to compare. + /// The right value to compare. + /// The comparision epsilon value. Should be 1e-4 or less. + /// True if a almost equal to b, otherwise false + static bool NearEqual(double a, double b, double eps) + { + return Abs(a - b) < eps; + } + + /// + /// Remaps the specified value from the specified range to another. + /// + /// The value to remap. + /// The source range minimum. + /// The source range maximum. + /// The destination range minimum. + /// The destination range maximum. + /// The remapped value. + static double Remap(double value, double fromMin, double fromMax, double toMin, double toMax) + { + return (value - fromMin) / (fromMax - fromMin) * (toMax - toMin) + toMin; + } + + static double ClampAxis(double angle) + { + angle = Mod(angle, 360.); + if (angle < 0.) + angle += 360.; + return angle; + } + + static double NormalizeAxis(double angle) + { + angle = ClampAxis(angle); + if (angle > 180.) + angle -= 360.; + return angle; + } + + // Find the smallest angle between two headings (in radians). + static double FindDeltaAngle(double a1, double a2) + { + double delta = a2 - a1; + if (delta > PI) + delta = delta - TWO_PI; + else if (delta < -PI) + delta = delta + TWO_PI; + return delta; + } + + // Given a heading which may be outside the +/- PI range, 'unwind' it back into that range + static double UnwindRadians(double a) + { + while (a > PI) + a -= TWO_PI; + while (a < -PI) + a += TWO_PI; + return a; + } + + // Utility to ensure angle is between +/- 180 degrees by unwinding + static double UnwindDegrees(double a) + { + while (a > 180.) + a -= 360.; + while (a < -180.) + a += 360.; + return a; + } + + /// + /// Returns value based on comparand. The main purpose of this function is to avoid branching based on floating point comparison which can be avoided via compiler intrinsics. + /// + /// + /// Please note that this doesn't define what happens in the case of NaNs as there might be platform specific differences. + /// + /// Comparand the results are based on. + /// The result value if comparand >= 0. + /// The result value if comparand < 0. + /// the valueGEZero if comparand >= 0, valueLTZero otherwise + static double DoubleSelect(double comparand, double valueGEZero, double valueLTZero) + { + return comparand >= 0. ? valueGEZero : valueLTZero; + } + + /// + /// Returns a smooth Hermite interpolation between 0 and 1 for the value X (where X ranges between A and B). Clamped to 0 for X <= A and 1 for X >= B. + /// + /// The minimum value of x. + /// The maximum value of x. + /// The x. + /// The smoothed value between 0 and 1. + static double SmoothStep(double a, double b, double x) + { + if (x < a) + return 0.; + if (x >= b) + return 1.; + const double fraction = (x - a) / (b - a); + return fraction * fraction * (3. - 2. * fraction); + } + +//TODO: When double vectors are implemented + // Rotates position about the given axis by the given angle, in radians, and returns the offset to position + //Vector3 FLAXENGINE_API RotateAboutAxis(const Vector3& normalizedRotationAxis, float angle, const Vector3& positionOnAxis, const Vector3& position); + + //Vector3 FLAXENGINE_API ExtractLargestComponent(const Vector3& v); +} From 2799136ccb4f44492e3c8b5518b38822d05b4216 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Thu, 12 Aug 2021 13:49:47 +0200 Subject: [PATCH 3/8] Remove Double operation from Math.h --- Source/Engine/Core/Math/Math.h | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/Source/Engine/Core/Math/Math.h b/Source/Engine/Core/Math/Math.h index b184ca5ad..462f71678 100644 --- a/Source/Engine/Core/Math/Math.h +++ b/Source/Engine/Core/Math/Math.h @@ -140,11 +140,6 @@ namespace Math return exp2f(value); } - static FORCE_INLINE double Abs(const double value) - { - return fabs(value); - } - static FORCE_INLINE float Abs(const float value) { return fabsf(value); @@ -160,11 +155,6 @@ namespace Math return value < 0 ? -value : value; } - static FORCE_INLINE double Mod(const double a, const double b) - { - return fmod(a, b); - } - static FORCE_INLINE float Mod(const float a, const float b) { return fmodf(a, b); @@ -434,14 +424,6 @@ namespace Math return amount <= 0 ? 0 : amount >= 1 ? 1 : amount * amount * amount * (amount * (amount * 6 - 15) + 10); } - // Determines whether the specified value is close to zero (0.0) - // @param a The floating value - // @returns True if the specified value is close to zero (0.0). otherwise false - inline bool IsZero(double a) - { - return Abs(a) < 1e-7; - } - // Determines whether the specified value is close to zero (0.0f) // @param a The floating value // @returns True if the specified value is close to zero (0.0f). otherwise false From b91800ad9de517f26a925be4fab67ec28ca5402e Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Thu, 12 Aug 2021 13:50:09 +0200 Subject: [PATCH 4/8] Use Mathd for double operation in Variant --- Source/Engine/Core/Types/Variant.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Source/Engine/Core/Types/Variant.cpp b/Source/Engine/Core/Types/Variant.cpp index 4b3cbd658..af3118858 100644 --- a/Source/Engine/Core/Types/Variant.cpp +++ b/Source/Engine/Core/Types/Variant.cpp @@ -6,6 +6,7 @@ #include "Engine/Core/Collections/Dictionary.h" #include "Engine/Content/Asset.h" #include "Engine/Core/Log.h" +#include "Engine/Core/Math/Mathd.h" #include "Engine/Core/Math/BoundingBox.h" #include "Engine/Core/Math/BoundingSphere.h" #include "Engine/Core/Math/Vector2.h" @@ -950,7 +951,7 @@ bool Variant::operator==(const Variant& other) const case VariantType::Float: return Math::NearEqual(AsFloat, other.AsFloat); case VariantType::Double: - return Math::Abs(AsDouble - other.AsDouble) < ZeroTolerance; + return Mathd::Abs(AsDouble - other.AsDouble) < ZeroTolerance; case VariantType::Pointer: return AsPointer == other.AsPointer; case VariantType::String: @@ -1103,7 +1104,7 @@ Variant::operator bool() const case VariantType::Float: return !Math::IsZero(AsFloat); case VariantType::Double: - return !Math::IsZero(AsDouble); + return !Mathd::IsZero(AsDouble); case VariantType::Pointer: return AsPointer != nullptr; case VariantType::String: @@ -2850,7 +2851,7 @@ Variant Variant::Cast(const Variant& v, const VariantType& to) switch (to.Type) { case VariantType::Bool: - return Variant(Math::Abs(v.AsDouble) > ZeroTolerance); + return Variant(Mathd::Abs(v.AsDouble) > ZeroTolerance); case VariantType::Int16: return Variant((int16)v.AsDouble); case VariantType::Int: From 69aac09be8ca47cba66446015f4a61eabf89da36 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Thu, 12 Aug 2021 13:50:34 +0200 Subject: [PATCH 5/8] Add Mathd.cs implementation --- Source/Engine/Core/Math/Mathd.cs | 1111 ++++++++++++++++++++++++++++++ 1 file changed, 1111 insertions(+) create mode 100644 Source/Engine/Core/Math/Mathd.cs diff --git a/Source/Engine/Core/Math/Mathd.cs b/Source/Engine/Core/Math/Mathd.cs new file mode 100644 index 000000000..42a65e023 --- /dev/null +++ b/Source/Engine/Core/Math/Mathd.cs @@ -0,0 +1,1111 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +using System; +using System.ComponentModel; + +namespace FlaxEngine +{ + /// + /// A collection of common math functions on double floating-points. + /// + [HideInEditor] + public static class Mathd + { + /// + /// The value for which all absolute numbers smaller than are considered equal to zero. + /// + public const double Epsilon = 1e-7f; + + /// + /// A value specifying the approximation of π which is 180 degrees. + /// + public const double Pi = Math.PI; + + /// + /// A value specifying the approximation of 2π which is 360 degrees. + /// + public const double TwoPi = 2.0 * Math.PI; + + /// + /// A value specifying the approximation of π/2 which is 90 degrees. + /// + public const double PiOverTwo = Math.PI / 2.0; + + /// + /// A value specifying the approximation of π/4 which is 45 degrees. + /// + public const double PiOverFour = Math.PI / 4.0; + + /// + /// A value specifying the golden mean + /// + public const double GoldenRatio = 1.6180339887; + + /// + /// Returns the absolute value of f. + /// + /// + public static double Abs(double f) + { + return Math.Abs(f); + } + + /// + /// Returns the arc-cosine of f - the angle in radians whose cosine is f. + /// + /// + public static double Acos(double f) + { + return Math.Acos(f); + } + + /// + /// Compares two floating point values if they are similar. + /// + /// + /// + public static bool Approximately(double a, double b) + { + return Abs(b - a) < Max(Epsilon * Max(Abs(a), Abs(b)), Epsilon * 8f); + } + + /// + /// Returns the arc-sine of f - the angle in radians whose sine is f. + /// + /// + public static double Asin(double f) + { + return Math.Asin(f); + } + + /// + /// Returns the arc-tangent of f - the angle in radians whose tangent is f. + /// + /// + public static double Atan(double f) + { + return Math.Atan(f); + } + + /// + /// Returns the angle in radians whose Tan is y/x. + /// + /// + /// + public static double Atan2(double y, double x) + { + return Math.Atan2(y, x); + } + + /// + /// Returns the smallest integer greater to or equal to f. + /// + /// + public static double Ceil(double f) + { + return Math.Ceiling(f); + } + + /// + /// Returns the smallest integer greater to or equal to f. + /// + /// + public static long CeilToInt(double f) + { + return (long)Math.Ceiling(f); + } + + /// + /// Clamps value between 0 and 1 and returns value. + /// + /// Value to clamp + /// Result value + public static double Saturate(double value) + { + if (value < 0d) + return 0d; + return value > 1d ? 1d : value; + } + + /// + /// Returns the cosine of angle f in radians. + /// + /// + public static double Cos(double f) + { + return Math.Cos(f); + } + + /// + /// Calculates the shortest difference between two given angles given in degrees. + /// + /// + /// + public static double DeltaAngle(double current, double target) + { + double t = Repeat(target - current, 360f); + if (t > 180d) + t -= 360d; + return t; + } + + /// + /// Returns e raised to the specified power. + /// + /// + public static double Exp(double power) + { + return Math.Exp(power); + } + + /// + /// Returns the largest integer smaller to or equal to f. + /// + /// + public static double Floor(double f) + { + return Math.Floor(f); + } + + /// + /// Returns the largest integer smaller to or equal to f. + /// + /// + public static long FloorToInt(double f) + { + return (long)Math.Floor(f); + } + + /// + /// Remaps the specified value from the specified range to another. + /// + /// The value to remap. + /// The source range minimum. + /// The source range maximum. + /// The destination range minimum. + /// The destination range maximum. + /// The remapped value. + public static double Remap(double value, double fromMin, double fromMax, double toMin, double toMax) + { + return (value - fromMin) / (fromMax - fromMin) * (toMax - toMin) + toMin; + } + + /// + /// Calculates the linear parameter t that produces the interpolation value within the range [a, b]. + /// + /// + /// + /// + public static double InverseLerp(double a, double b, double value) + { + if (a == b) + return 0d; + return Saturate((value - a) / (b - a)); + } + + /// + /// Same as Lerp but makes sure the values interpolate correctly when they wrap around 360 degrees. + /// + /// + /// + /// + public static double LerpAngle(double a, double b, double t) + { + double c = Repeat(b - a, 360d); + if (c > 180d) + c -= 360d; + return a + c * Saturate(t); + } + + /// + /// Returns the logarithm of a specified number in a specified base. + /// + /// + /// + public static double Log(double f, double p) + { + return Math.Log(f, p); + } + + /// + /// Returns the natural (base e) logarithm of a specified number. + /// + /// + public static double Log(double f) + { + return Math.Log(f); + } + + /// + /// Returns the base 10 logarithm of a specified number. + /// + /// + public static double Log10(double f) + { + return Math.Log10(f); + } + + /// + /// Returns largest of two or more values. + /// + /// + /// + public static double Max(double a, double b) + { + return a <= b ? b : a; + } + + + /// + /// Returns largest of two or more values. + /// + /// + public static double Max(params double[] values) + { + int length = values.Length; + if (length == 0) + return 0d; + + double t = values[0]; + for (var i = 1; i < length; i++) + if (values[i] > t) + t = values[i]; + return t; + } + + + /// + /// Returns the smallest of two or more values. + /// + /// + /// + public static double Min(double a, double b) + { + return a >= b ? b : a; + } + + + /// + /// Returns the smallest of two or more values. + /// + /// + public static double Min(params double[] values) + { + int length = values.Length; + if (length == 0) + return 0d; + + double t = values[0]; + for (var i = 1; i < length; i++) + if (values[i] < t) + t = values[i]; + + return t; + } + + /// + /// Moves a value current towards target. + /// + /// The current value. + /// The value to move towards. + /// The maximum change that should be applied to the value. + public static double MoveTowards(double current, double target, double maxDelta) + { + if (Abs(target - current) <= maxDelta) + return target; + return current + Sign(target - current) * maxDelta; + } + + /// + /// Same as MoveTowards but makes sure the values interpolate correctly when they wrap around 360 degrees. + /// + /// + /// + /// + public static double MoveTowardsAngle(double current, double target, double maxDelta) + { + double delta = DeltaAngle(current, target); + if ((-maxDelta < delta) && (delta < maxDelta)) + return target; + target = current + delta; + return MoveTowards(current, target, maxDelta); + } + + /// + /// PingPongs the value t, so that it is never larger than length and never smaller than 0. + /// + /// + /// + public static double PingPong(double t, double length) + { + t = Repeat(t, length * 2f); + return length - Abs(t - length); + } + + /// + /// Returns f raised to power p. + /// + /// + /// + public static double Pow(double f, double p) + { + return Math.Pow(f, p); + } + + /// + /// Loops the value t, so that it is never larger than length and never smaller than 0. + /// + /// + /// + public static double Repeat(double t, double length) + { + return t - Floor(t / length) * length; + } + + /// + /// Returns f rounded to the nearest integer. + /// + /// + public static double Round(double f) + { + return Math.Round(f); + } + + /// + /// Returns f rounded to the nearest integer. + /// + /// + public static int RoundToInt(double f) + { + return (int)Math.Round(f); + } + + /// + /// Returns f rounded to the nearest integer. + /// + /// + public static long RoundToLong(double f) + { + return (long)Math.Round(f); + } + + /// + /// Returns the sign of f. + /// + /// + public static double Sign(double f) + { + return f < 0d ? -1d : 1d; + } + + /// + /// Returns the sine of angle f in radians. + /// + /// + public static double Sin(double f) + { + return Math.Sin(f); + } + + /// + /// Gradually changes a value towards a desired goal over time with smoothing. + /// + /// The current value. + /// The target value. + /// The current velocity. + /// The smoothing time. Smaller values increase blending time. + /// The maximum speed. + /// The smoothed value. + public static double SmoothDamp(double current, double target, ref double currentVelocity, double smoothTime, double maxSpeed) + { + return SmoothDamp(current, target, ref currentVelocity, smoothTime, maxSpeed, Time.DeltaTime); + } + + /// + /// Gradually changes a value towards a desired goal over time with smoothing. + /// + /// The current value. + /// The target value. + /// The current velocity. + /// The smoothing time. Smaller values increase blending time. + /// The smoothed value. + public static double SmoothDamp(double current, double target, ref double currentVelocity, double smoothTime) + { + return SmoothDamp(current, target, ref currentVelocity, smoothTime, double.PositiveInfinity, Time.DeltaTime); + } + + /// + /// Gradually changes a value towards a desired goal over time with smoothing. + /// + /// The current value. + /// The target value. + /// The current velocity. + /// The smoothing time. Smaller values increase blending time. + /// The maximum speed. + /// The delta time (in seconds) since last update. + /// The smoothed value. + public static double SmoothDamp(double current, double target, ref double currentVelocity, double smoothTime, [DefaultValue("double.PositiveInfinity")] + double maxSpeed, [DefaultValue("Time.DeltaTime")] double deltaTime) + { + smoothTime = Max(0.0001d, smoothTime); + double a = 2d / smoothTime; + double b = a * deltaTime; + double c = 1d / (1d + b + 0.48d * b * b + 0.235d * b * b * b); + double d = current - target; + double e = target; + double f = maxSpeed * smoothTime; + d = Clamp(d, -f, f); + target = current - d; + double g = (currentVelocity + a * d) * deltaTime; + currentVelocity = (currentVelocity - a * g) * c; + double h = target + (d + g) * c; + if (e - current > 0d == h > e) + { + h = e; + currentVelocity = (h - e) / deltaTime; + } + return h; + } + + /// + /// Gradually changes an angle towards a desired goal over time with smoothing. + /// + /// The current angle. + /// The target angle. + /// The current velocity. + /// The smoothing time. Smaller values increase blending time. + /// The maximum speed. + /// The smoothed value. + public static double SmoothDampAngle(double current, double target, ref double currentVelocity, double smoothTime, double maxSpeed) + { + return SmoothDampAngle(current, target, ref currentVelocity, smoothTime, maxSpeed, Time.DeltaTime); + } + + /// + /// Gradually changes an angle towards a desired goal over time with smoothing. + /// + /// The current angle. + /// The target angle. + /// The current velocity. + /// The smoothing time. Smaller values increase blending time. + /// The smoothed value. + public static double SmoothDampAngle(double current, double target, ref double currentVelocity, double smoothTime) + { + return SmoothDampAngle(current, target, ref currentVelocity, smoothTime, double.PositiveInfinity, Time.DeltaTime); + } + + /// + /// Gradually changes an angle towards a desired goal over time with smoothing. + /// + /// The current angle. + /// The target angle. + /// The current velocity. + /// The smoothing time. Smaller values increase blending time. + /// The maximum speed. + /// The delta time (in seconds) since last update. + /// The smoothed value. + public static double SmoothDampAngle(double current, double target, ref double currentVelocity, double smoothTime, [DefaultValue("double.PositiveInfinity")] + double maxSpeed, [DefaultValue("Time.DeltaTime")] double deltaTime) + { + target = current + DeltaAngle(current, target); + return SmoothDamp(current, target, ref currentVelocity, smoothTime, maxSpeed, deltaTime); + } + + /// + /// Interpolates between min and max with smoothing at the limits. + /// + /// + /// + /// + public static double SmoothStep(double from, double to, double t) + { + t = Saturate(t); + t = -2d * t * t * t + 3d * t * t; + return to * t + from * (1d - t); + } + + /// + /// Performs a cubic interpolation. + /// + /// The first point. + /// The tangent direction at first point. + /// The second point. + /// The tangent direction at second point. + /// The distance along the spline. + /// The interpolated value. + public static double CubicInterp(double p0, double t0, double p1, double t1, double alpha) + { + double alpha2 = alpha * alpha; + double alpha3 = alpha2 * alpha; + return (((2d * alpha3) - (3d * alpha2) + 1d) * p0) + ((alpha3 - (2d * alpha2) + alpha) * t0) + ((alpha3 - alpha2) * t1) + (((-2d * alpha3) + (3d * alpha2)) * p1); + } + + /// + /// Interpolate between A and B, applying an ease in function. Exponent controls the degree of the curve. + /// + public static double InterpEaseIn(double a, double b, double alpha, double exponent) + { + double modifiedAlpha = Pow(alpha, exponent); + return Lerp(a, b, modifiedAlpha); + } + + /// + /// Interpolate between A and B, applying an ease out function. Exponent controls the degree of the curve. + /// + public static double InterpEaseOut(double a, double b, double alpha, double exponent) + { + double modifiedAlpha = 1d - Pow(1d - alpha, exponent); + return Lerp(a, b, modifiedAlpha); + } + + /// + /// Interpolate between A and B, applying an ease in/out function. Exponent controls the degree of the curve. + /// + public static double InterpEaseInOut(double a, double b, double alpha, double exponent) + { + return Lerp(a, b, (alpha < 0.5d) ? InterpEaseIn(0d, 1d, alpha * 2d, exponent) * 0.5d : InterpEaseOut(0d, 1d, alpha * 2d - 1d, exponent) * 0.5d + 0.5d); + } + + /// + /// Interpolation between A and B, applying a sinusoidal in function. + /// + public static double InterpSinIn(double a, double b, double alpha) + { + double modifiedAlpha = -1d * Cos(alpha * PiOverTwo) + 1d; + return Lerp(a, b, modifiedAlpha); + } + + /// + /// Interpolation between A and B, applying a sinusoidal out function. + /// + public static double InterpSinOut(double a, double b, double alpha) + { + double modifiedAlpha = Sin(alpha * PiOverTwo); + return Lerp(a, b, modifiedAlpha); + } + + /// + /// Interpolation between A and B, applying a sinusoidal in/out function. + /// + public static double InterpSinInOut(double a, double b, double alpha) + { + return Lerp(a, b, (alpha < 0.5d) ? InterpSinIn(0d, 1d, alpha * 2d) * 0.5d : InterpSinOut(0d, 1d, alpha * 2d - 1d) * 0.5d + 0.5d); + } + + /// + /// Interpolation between A and B, applying an exponential in function. + /// + public static double InterpExpoIn(double a, double b, double alpha) + { + double modifiedAlpha = (alpha == 0d) ? 0d : Pow(2d, 10d * (alpha - 1d)); + return Lerp(a, b, modifiedAlpha); + } + + /// + /// Interpolation between A and B, applying an exponential out function. + /// + public static double InterpExpoOut(double a, double b, double alpha) + { + double modifiedAlpha = (alpha == 1d) ? 1d : -Pow(2d, -10d * alpha) + 1d; + return Lerp(a, b, modifiedAlpha); + } + + /// + /// Interpolation between A and B, applying an exponential in/out function. + /// + public static double InterpExpoInOut(double a, double b, double alpha) + { + return Lerp(a, b, (alpha < 0.5d) ? InterpExpoIn(0d, 1d, alpha * 2d) * 0.5d : InterpExpoOut(0d, 1d, alpha * 2d - 1d) * 0.5d + 0.5d); + } + + /// + /// Interpolation between A and B, applying a circular in function. + /// + public static double InterpCircularIn(double a, double b, double alpha) + { + double modifiedAlpha = -1d * (Sqrt(1d - alpha * alpha) - 1d); + return Lerp(a, b, modifiedAlpha); + } + + /// + /// Interpolation between A and B, applying a circular out function. + /// + public static double InterpCircularOut(double a, double b, double alpha) + { + alpha -= 1d; + double modifiedAlpha = Sqrt(1d - alpha * alpha); + return Lerp(a, b, modifiedAlpha); + } + + /// + /// Interpolation between A and B, applying a circular in/out function. + /// + public static double InterpCircularInOut(double a, double b, double alpha) + { + return Lerp(a, b, (alpha < 0.5d) ? InterpCircularIn(0d, 1d, alpha * 2d) * 0.5d : InterpCircularOut(0d, 1d, alpha * 2d - 1d) * 0.5d + 0.5d); + } + + /// + /// Maps the specified value from the given range into another. + /// + /// The value to map from range [fromMin; fromMax]. + /// The source range minimum value. + /// The source range maximum value. + /// The destination range minimum value. + /// The destination range maximum value. + /// The mapped value in range [toMin; toMax]. + public static double Map(double value, double fromMin, double fromMax, double toMin, double toMax) + { + double t = (value - fromMin) / (fromMax - fromMin); + return toMin + t * (toMax - toMin); + } + + /// + /// Get the next power of two for a size. + /// + /// The size. + /// System.Int32. + public static double NextPowerOfTwo(double size) + { + return Math.Pow(2d, Math.Ceiling(Math.Log(size, 2d))); + } + + /// + /// Converts a float value from sRGB to linear. + /// + /// The sRGB value. + /// A linear value. + public static double SRgbToLinear(double sRgbValue) + { + if (sRgbValue < 0.04045d) + return sRgbValue / 12.92d; + return Math.Pow((sRgbValue + 0.055d) / 1.055d, 2.4d); + } + + /// + /// Converts a float value from linear to sRGB. + /// + /// The linear value. + /// The encoded sRGB value. + public static double LinearToSRgb(double linearValue) + { + if (linearValue < 0.0031308d) + return linearValue * 12.92d; + return 1.055d * Math.Pow(linearValue, 1d / 2.4d) - 0.055d; + } + + /// + /// Returns square root of f. + /// + /// + public static double Sqrt(double f) + { + return Math.Sqrt(f); + } + + /// + /// Returns square of the given value. + /// + /// The value. + /// The value * value. + public static double Square(double f) + { + return f * f; + } + + /// + /// Returns the tangent of angle f in radians. + /// + /// + public static double Tan(double f) + { + return Math.Tan(f); + } + + /// + /// 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 static unsafe bool NearEqual(double a, double 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/ + long aInt = *(long*)&a; + long bInt = *(long*)&b; + + // Different signs means they do not match. + if (aInt < 0 != bInt < 0) + return false; + + // Find the difference in ULPs. + long 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 long 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(double a) + { + return Math.Abs(a) < Epsilon; + } + + /// + /// 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(double a) + { + return IsZero(a - 1d); + } + + /// + /// 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(double a, double b, double epsilon) + { + double num = a - b; + return (-epsilon <= num) && (num <= epsilon); + } + + /// + /// Determines whether the specified value is in a given range [min; max]. + /// + /// The value. + /// The minimum. + /// The maximum. + /// + /// true if the specified value is in a given range; otherwise, false. + /// + public static bool IsInRange(double value, double min, double max) + { + return value >= min && value <= max; + } + + /// + /// Determines whether the specified value is NOT in a given range [min; max]. + /// + /// The value. + /// The minimum. + /// The maximum. + /// + /// true if the specified value is NOT in a given range; otherwise, false. + /// + public static bool IsNotInRange(double value, double min, double max) + { + return value < min || value > max; + } + + #region Angle units conversions + + /// + /// Converts revolutions to degrees. + /// + public static double RevolutionsToDegrees = 360d; + + /// + /// Converts revolutions to radians. + /// + public static double RevolutionsToRadians = TwoPi; + + /// + /// Converts revolutions to gradians. + /// + public static double RevolutionsToGradians = 400d; + + /// + /// Converts degrees to revolutions. + /// + public static double DegreesToRevolutions = (1d / 360d); + + /// + /// Converts degrees to radians. + /// + public static double DegreesToRadians = (Pi / 180d); + + /// + /// Converts radians to revolutions. + /// + public static double RadiansToRevolutions = (1d / TwoPi); + + /// + /// Converts radians to gradians. + /// + public static double RadiansToGradians = (200d / Pi); + + /// + /// Converts gradians to revolutions. + /// + public static double GradiansToRevolutions = (1d / 400d); + + /// + /// Converts gradians to degrees. + /// + public static double GradiansToDegrees = (9.0f / 10d); + + /// + /// Converts gradians to radians. + /// + public static double GradiansToRadians = (Pi / 200d); + + /// + /// Converts radians to degrees. + /// + public static double RadiansToDegrees = (180d / Pi); + + #endregion + + /// + /// Given a heading which may be outside the +/- PI range, 'unwind' it back into that range. + /// + /// Angle in radians to unwind. + /// Valid angle in radians. + public static double UnwindRadians(double angle) + { + // TODO: make it faster? + + while (angle > Pi) + { + angle -= TwoPi; + } + + while (angle < -Pi) + { + angle += TwoPi; + } + + return angle; + } + + /// + /// Utility to ensure angle is between +/- 180 degrees by unwinding + /// + /// Angle in degrees to unwind. + /// Valid angle in degrees. + public static double UnwindDegrees(double angle) + { + // TODO: make it faster? + + while (angle > 180.0f) + { + angle -= 360.0f; + } + + while (angle < -180.0f) + { + angle += 360.0f; + } + + return angle; + } + + /// + /// Clamps the specified value. + /// + /// The value. + /// The min. + /// The max. + /// The result of clamping a value between min and max + public static double Clamp(double value, double min, double 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 from + (to - from) * 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 double SmoothStep(double amount) + { + return amount <= 0d ? 0d + : amount >= 1d ? 1d + : amount * amount * (3d - 2d * 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 double SmootherStep(double amount) + { + return amount <= 0d ? 0d + : amount >= 1d ? 1d + : amount * amount * amount * (amount * (amount * 6d - 15d) + 10d); + } + + /// + /// Calculates the modulo of the specified value. + /// + /// The value. + /// The modulo. + /// The result of the modulo applied to value + public static double Mod(double value, double modulo) + { + if (modulo == 0d) + 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 double Mod2PI(double value) + { + return Mod(value, TwoPi); + } + + /// + /// 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 double Wrap(double value, double min, double 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), nameof(min)); + + double rangeSize = maxd - mind; + return mind + (valued - mind) - rangeSize * Math.Floor((valued - mind) / rangeSize); + } + + /// + /// 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) + { + double cx = x - centerX; + double cy = y - centerY; + + double componentX = cx * cx / (2 * sigmaX * sigmaX); + double componentY = cy * cy / (2 * sigmaY * sigmaY); + + return amplitude * Math.Exp(-(componentX + componentY)); + } + + /// + /// Converts the input alpha value from a linear 0-1 value into the output alpha described by blend mode. + /// + /// The alpha (normalized to 0-1). + /// The mode. + /// The output alpha (normalized to 0-1). + public static double InterpolateAlphaBlend(double alpha, AlphaBlendMode mode) + { + switch (mode) + { + case AlphaBlendMode.Sinusoidal: + alpha = (Sin(alpha * Pi - PiOverTwo) + 1d) / 2d; + break; + case AlphaBlendMode.Cubic: + alpha = CubicInterp(0d, 0d, 1d, 0d, alpha); + break; + case AlphaBlendMode.QuadraticInOut: + alpha = InterpEaseInOut(0d, 1d, alpha, 2d); + break; + case AlphaBlendMode.CubicInOut: + alpha = InterpEaseInOut(0d, 1d, alpha, 3d); + break; + case AlphaBlendMode.HermiteCubic: + alpha = SmoothStep(0d, 1d, alpha); + break; + case AlphaBlendMode.QuarticInOut: + alpha = InterpEaseInOut(0d, 1d, alpha, 4d); + break; + case AlphaBlendMode.QuinticInOut: + alpha = InterpEaseInOut(0d, 1d, alpha, 5d); + break; + case AlphaBlendMode.CircularIn: + alpha = InterpCircularIn(0d, 1d, alpha); + break; + case AlphaBlendMode.CircularOut: + alpha = InterpCircularOut(0d, 1d, alpha); + break; + case AlphaBlendMode.CircularInOut: + alpha = InterpCircularInOut(0d, 1d, alpha); + break; + case AlphaBlendMode.ExpIn: + alpha = InterpExpoIn(0d, 1d, alpha); + break; + case AlphaBlendMode.ExpOut: + alpha = InterpExpoOut(0d, 1d, alpha); + break; + case AlphaBlendMode.ExpInOut: + alpha = InterpExpoInOut(0d, 1d, alpha); + break; + } + + return Saturate(alpha); + } + } +} From 2c30f48900ea9ee88bef33da563852c2b2cc37a3 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Thu, 12 Aug 2021 13:51:06 +0200 Subject: [PATCH 6/8] Tweak --- Source/Engine/Core/Math/Mathf.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Core/Math/Mathf.cs b/Source/Engine/Core/Math/Mathf.cs index 93e0ff8a4..488c46777 100644 --- a/Source/Engine/Core/Math/Mathf.cs +++ b/Source/Engine/Core/Math/Mathf.cs @@ -6,7 +6,7 @@ using System.ComponentModel; namespace FlaxEngine { /// - /// A collection of common math functions. + /// A collection of common math functions on single floating-points. /// [HideInEditor] public static class Mathf From 2948bfbed069d6b5265812d418b18ec278a63464 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Thu, 12 Aug 2021 14:15:26 +0200 Subject: [PATCH 7/8] Backward compatibility (Mathd -> Math), fix and remove warnings. --- Source/Engine/Core/Math/Math.h | 1 + Source/Engine/Core/Math/Mathd.h | 2 +- Source/Engine/Core/Types/Variant.cpp | 6 +++--- Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp | 1 + 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Source/Engine/Core/Math/Math.h b/Source/Engine/Core/Math/Math.h index 462f71678..f138fadb3 100644 --- a/Source/Engine/Core/Math/Math.h +++ b/Source/Engine/Core/Math/Math.h @@ -2,6 +2,7 @@ #pragma once +#include "Engine/Core/Core.h" #include "Engine/Core/Types/BaseTypes.h" #include diff --git a/Source/Engine/Core/Math/Mathd.h b/Source/Engine/Core/Math/Mathd.h index 8da6f0330..6806ad0a9 100644 --- a/Source/Engine/Core/Math/Mathd.h +++ b/Source/Engine/Core/Math/Mathd.h @@ -5,7 +5,7 @@ #include #include "Engine/Core/Types/BaseTypes.h" -namespace Mathd +namespace Math { /// /// Computes the sine and cosine of a scalar float. diff --git a/Source/Engine/Core/Types/Variant.cpp b/Source/Engine/Core/Types/Variant.cpp index af3118858..683b195be 100644 --- a/Source/Engine/Core/Types/Variant.cpp +++ b/Source/Engine/Core/Types/Variant.cpp @@ -951,7 +951,7 @@ bool Variant::operator==(const Variant& other) const case VariantType::Float: return Math::NearEqual(AsFloat, other.AsFloat); case VariantType::Double: - return Mathd::Abs(AsDouble - other.AsDouble) < ZeroTolerance; + return Math::Abs(AsDouble - other.AsDouble) < ZeroTolerance; case VariantType::Pointer: return AsPointer == other.AsPointer; case VariantType::String: @@ -1104,7 +1104,7 @@ Variant::operator bool() const case VariantType::Float: return !Math::IsZero(AsFloat); case VariantType::Double: - return !Mathd::IsZero(AsDouble); + return !Math::IsZero(AsDouble); case VariantType::Pointer: return AsPointer != nullptr; case VariantType::String: @@ -2851,7 +2851,7 @@ Variant Variant::Cast(const Variant& v, const VariantType& to) switch (to.Type) { case VariantType::Bool: - return Variant(Mathd::Abs(v.AsDouble) > ZeroTolerance); + return Variant(Math::Abs(v.AsDouble) > ZeroTolerance); case VariantType::Int16: return Variant((int16)v.AsDouble); case VariantType::Int: diff --git a/Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp b/Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp index dd1873bab..80dadd8c7 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp +++ b/Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp @@ -4,6 +4,7 @@ #include "ModelTool.h" #include "Engine/Core/Log.h" +#include "Engine/Core/Math/Mathd.h" #include "Engine/Core/Math/Matrix.h" #include "Engine/Core/Collections/Sorting.h" #include "Engine/Platform/FileSystem.h" From 2d9627df3393033b726f7e340cf588e935623eed Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Thu, 12 Aug 2021 19:00:41 +0200 Subject: [PATCH 8/8] Tweak docs --- Source/Engine/Core/Math/Mathd.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Engine/Core/Math/Mathd.h b/Source/Engine/Core/Math/Mathd.h index 6806ad0a9..47b16b608 100644 --- a/Source/Engine/Core/Math/Mathd.h +++ b/Source/Engine/Core/Math/Mathd.h @@ -8,7 +8,7 @@ namespace Math { /// - /// Computes the sine and cosine of a scalar float. + /// Computes the sine and cosine of a scalar double. /// /// The input angle (in radians). /// The output sine value. @@ -198,7 +198,7 @@ namespace Math } /// - /// Compares the sign of two floating-point values. + /// Compares the sign of two double values. /// /// The first value. /// The second value. @@ -209,7 +209,7 @@ namespace Math } /// - /// Compares the sign of two floating-point values. + /// Compares the sign of two double values. /// /// The first value. /// The second value. @@ -220,7 +220,7 @@ namespace Math } /// - /// Checks if a and b are not even almost equal, taking into account the magnitude of floating point numbers + /// Checks if a and b are not even almost equal, taking into account the magnitude of double numbers /// /// The left value to compare /// The right value to compare