summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJcrespo <jcrespo@wikimedia.org>2023-06-18 14:47:01 +0200
committerJaime Crespo <jcrespo@wikimedia.org>2023-09-01 01:27:56 +0200
commit528a76486c5475bdbb5cc9d1152d349205a366f6 (patch)
tree72977a33dd2770ab21c2da28c42e890100f42089
parent549fcce5f8f7beace3e5c90e9bbe4335d4fd1476 (diff)
downloadredot-engine-528a76486c5475bdbb5cc9d1152d349205a366f6.tar.gz
Add inverse hyperbolic functions `asinh()`, `acosh()` & `atanh()`
GDScript has the following built-in trigonometry functions: - `sin()` - `cos()` - `tan()` - `asin()` - `acos()` - `atan()` - `atan()` - `sinh()` - `cosh()` - `tanh()` However, it lacks the hyperbolic arc (also known as inverse hyperbolic) functions: - `asinh()` - `acosh()` - `atanh()` Implement them by just exposing the C++ Math library, but clamping its values to the closest real defined value. For the cosine, clamp input values lower than 1 to 1. In the case of the tangent, where the limit value is infinite, clamp it to -inf or +inf. References #78377 Fixes godotengine/godot-proposals#7110
-rw-r--r--core/math/math_funcs.h11
-rw-r--r--core/variant/variant_utility.cpp16
-rw-r--r--core/variant/variant_utility.h3
-rw-r--r--doc/classes/@GlobalScope.xml39
-rw-r--r--tests/core/math/test_math_funcs.h31
5 files changed, 100 insertions, 0 deletions
diff --git a/core/math/math_funcs.h b/core/math/math_funcs.h
index f96d3a909f..934c75b5d3 100644
--- a/core/math/math_funcs.h
+++ b/core/math/math_funcs.h
@@ -88,6 +88,17 @@ public:
static _ALWAYS_INLINE_ double atan2(double p_y, double p_x) { return ::atan2(p_y, p_x); }
static _ALWAYS_INLINE_ float atan2(float p_y, float p_x) { return ::atan2f(p_y, p_x); }
+ static _ALWAYS_INLINE_ double asinh(double p_x) { return ::asinh(p_x); }
+ static _ALWAYS_INLINE_ float asinh(float p_x) { return ::asinhf(p_x); }
+
+ // Always does clamping so always safe to use.
+ static _ALWAYS_INLINE_ double acosh(double p_x) { return p_x < 1 ? 0 : ::acosh(p_x); }
+ static _ALWAYS_INLINE_ float acosh(float p_x) { return p_x < 1 ? 0 : ::acoshf(p_x); }
+
+ // Always does clamping so always safe to use.
+ static _ALWAYS_INLINE_ double atanh(double p_x) { return p_x <= -1 ? -INFINITY : (p_x >= 1 ? INFINITY : ::atanh(p_x)); }
+ static _ALWAYS_INLINE_ float atanh(float p_x) { return p_x <= -1 ? -INFINITY : (p_x >= 1 ? INFINITY : ::atanhf(p_x)); }
+
static _ALWAYS_INLINE_ double sqrt(double p_x) { return ::sqrt(p_x); }
static _ALWAYS_INLINE_ float sqrt(float p_x) { return ::sqrtf(p_x); }
diff --git a/core/variant/variant_utility.cpp b/core/variant/variant_utility.cpp
index 4f6bcb58b3..b51df89bec 100644
--- a/core/variant/variant_utility.cpp
+++ b/core/variant/variant_utility.cpp
@@ -81,6 +81,18 @@ double VariantUtilityFunctions::atan2(double y, double x) {
return Math::atan2(y, x);
}
+double VariantUtilityFunctions::asinh(double arg) {
+ return Math::asinh(arg);
+}
+
+double VariantUtilityFunctions::acosh(double arg) {
+ return Math::acosh(arg);
+}
+
+double VariantUtilityFunctions::atanh(double arg) {
+ return Math::atanh(arg);
+}
+
double VariantUtilityFunctions::sqrt(double x) {
return Math::sqrt(x);
}
@@ -1502,6 +1514,10 @@ void Variant::_register_variant_utility_functions() {
FUNCBINDR(atan2, sarray("y", "x"), Variant::UTILITY_FUNC_TYPE_MATH);
+ FUNCBINDR(asinh, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH);
+ FUNCBINDR(acosh, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH);
+ FUNCBINDR(atanh, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH);
+
FUNCBINDR(sqrt, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(fmod, sarray("x", "y"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(fposmod, sarray("x", "y"), Variant::UTILITY_FUNC_TYPE_MATH);
diff --git a/core/variant/variant_utility.h b/core/variant/variant_utility.h
index 78f66987cb..66883fb140 100644
--- a/core/variant/variant_utility.h
+++ b/core/variant/variant_utility.h
@@ -45,6 +45,9 @@ struct VariantUtilityFunctions {
static double acos(double arg);
static double atan(double arg);
static double atan2(double y, double x);
+ static double asinh(double arg);
+ static double acosh(double arg);
+ static double atanh(double arg);
static double sqrt(double x);
static double fmod(double b, double r);
static double fposmod(double b, double r);
diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml
index ae40051d95..fa3f6e434e 100644
--- a/doc/classes/@GlobalScope.xml
+++ b/doc/classes/@GlobalScope.xml
@@ -72,6 +72,19 @@
[/codeblock]
</description>
</method>
+ <method name="acosh">
+ <return type="float" />
+ <param index="0" name="x" type="float" />
+ <description>
+ Returns the hyperbolic arc (also called inverse) cosine of [param x], returning a value in radians. Use it to get the angle from an angle's cosine in hyperbolic space if [param x] is larger or equal to 1. For values of [param x] lower than 1, it will return 0, in order to prevent [method acosh] from returning [constant @GDScript.NAN].
+ [codeblock]
+ var a = acosh(2) # Returns 1.31695789692482
+ cosh(a) # Returns 2
+
+ var b = acosh(-1) # Returns 0
+ [/codeblock]
+ </description>
+ </method>
<method name="asin">
<return type="float" />
<param index="0" name="x" type="float" />
@@ -83,6 +96,17 @@
[/codeblock]
</description>
</method>
+ <method name="asinh">
+ <return type="float" />
+ <param index="0" name="x" type="float" />
+ <description>
+ Returns the hyperbolic arc (also called inverse) sine of [param x], returning a value in radians. Use it to get the angle from an angle's sine in hyperbolic space.
+ [codeblock]
+ var a = asinh(0.9) # Returns 0.8088669356527824
+ sinh(a) # Returns 0.9
+ [/codeblock]
+ </description>
+ </method>
<method name="atan">
<return type="float" />
<param index="0" name="x" type="float" />
@@ -107,6 +131,21 @@
[/codeblock]
</description>
</method>
+ <method name="atanh">
+ <return type="float" />
+ <param index="0" name="x" type="float" />
+ <description>
+ Returns the hyperbolic arc (also called inverse) tangent of [param x], returning a value in radians. Use it to get the angle from an angle's tangent in hyperbolic space if [param x] is between -1 and 1 (non-inclusive).
+ In mathematics, the inverse hyperbolic tangent is only defined for -1 &lt; [param x] &lt; 1 in the real set, so values equal or lower to -1 for [param x] return negative [constant @GDScript.INF] and values equal or higher than 1 return positive [constant @GDScript.INF] in order to prevent [method atanh] from returning [constant @GDScript.NAN].
+ [codeblock]
+ var a = atanh(0.9) # Returns 1.47221948958322
+ tanh(a) # Returns 0.9
+
+ var b = atanh(-2) # Returns -inf
+ tanh(b) # Returns -1
+ [/codeblock]
+ </description>
+ </method>
<method name="bezier_derivative">
<return type="float" />
<param index="0" name="start" type="float" />
diff --git a/tests/core/math/test_math_funcs.h b/tests/core/math/test_math_funcs.h
index b6cb9620f1..e3504ef1e5 100644
--- a/tests/core/math/test_math_funcs.h
+++ b/tests/core/math/test_math_funcs.h
@@ -175,6 +175,37 @@ TEST_CASE_TEMPLATE("[Math] asin/acos/atan", T, float, double) {
CHECK(Math::atan((T)450.0) == doctest::Approx((T)1.5685741082));
}
+TEST_CASE_TEMPLATE("[Math] asinh/acosh/atanh", T, float, double) {
+ CHECK(Math::asinh((T)-2.0) == doctest::Approx((T)-1.4436354751));
+ CHECK(Math::asinh((T)-0.1) == doctest::Approx((T)-0.0998340788));
+ CHECK(Math::asinh((T)0.1) == doctest::Approx((T)0.0998340788));
+ CHECK(Math::asinh((T)0.5) == doctest::Approx((T)0.4812118250));
+ CHECK(Math::asinh((T)1.0) == doctest::Approx((T)0.8813735870));
+ CHECK(Math::asinh((T)2.0) == doctest::Approx((T)1.4436354751));
+
+ CHECK(Math::acosh((T)-2.0) == doctest::Approx((T)0.0));
+ CHECK(Math::acosh((T)-0.1) == doctest::Approx((T)0.0));
+ CHECK(Math::acosh((T)0.1) == doctest::Approx((T)0.0));
+ CHECK(Math::acosh((T)0.5) == doctest::Approx((T)0.0));
+ CHECK(Math::acosh((T)1.0) == doctest::Approx((T)0.0));
+ CHECK(Math::acosh((T)2.0) == doctest::Approx((T)1.3169578969));
+ CHECK(Math::acosh((T)450.0) == doctest::Approx((T)6.8023935287));
+
+ CHECK(Math::is_inf(Math::atanh((T)-2.0)));
+ CHECK(Math::atanh((T)-2.0) < (T)0.0);
+ CHECK(Math::is_inf(Math::atanh((T)-1.0)));
+ CHECK(Math::atanh((T)-1.0) < (T)0.0);
+ CHECK(Math::atanh((T)-0.1) == doctest::Approx((T)-0.1003353477));
+ CHECK(Math::atanh((T)0.1) == doctest::Approx((T)0.1003353477));
+ CHECK(Math::atanh((T)0.5) == doctest::Approx((T)0.5493061443));
+ CHECK(Math::is_inf(Math::atanh((T)1.0)));
+ CHECK(Math::atanh((T)1.0) > (T)0.0);
+ CHECK(Math::is_inf(Math::atanh((T)1.5)));
+ CHECK(Math::atanh((T)1.5) > (T)0.0);
+ CHECK(Math::is_inf(Math::atanh((T)450.0)));
+ CHECK(Math::atanh((T)450.0) > (T)0.0);
+}
+
TEST_CASE_TEMPLATE("[Math] sinc/sincn/atan2", T, float, double) {
CHECK(Math::sinc((T)-0.1) == doctest::Approx((T)0.9983341665));
CHECK(Math::sinc((T)0.1) == doctest::Approx((T)0.9983341665));