diff --git a/core/math/aabb.cpp b/core/math/aabb.cpp
index 026f1794457..fcf245d2ad0 100644
--- a/core/math/aabb.cpp
+++ b/core/math/aabb.cpp
@@ -76,6 +76,10 @@ bool AABB::is_equal_approx(const AABB &p_aabb) const {
return position.is_equal_approx(p_aabb.position) && size.is_equal_approx(p_aabb.size);
}
+bool AABB::is_finite() const {
+ return position.is_finite() && size.is_finite();
+}
+
AABB AABB::intersection(const AABB &p_aabb) const {
#ifdef MATH_CHECKS
if (unlikely(size.x < 0 || size.y < 0 || size.z < 0 || p_aabb.size.x < 0 || p_aabb.size.y < 0 || p_aabb.size.z < 0)) {
diff --git a/core/math/aabb.h b/core/math/aabb.h
index b9f777c6cf9..9d5837ad37f 100644
--- a/core/math/aabb.h
+++ b/core/math/aabb.h
@@ -63,6 +63,7 @@ struct _NO_DISCARD_ AABB {
bool operator!=(const AABB &p_rval) const;
bool is_equal_approx(const AABB &p_aabb) const;
+ bool is_finite() const;
_FORCE_INLINE_ bool intersects(const AABB &p_aabb) const; /// Both AABBs overlap
_FORCE_INLINE_ bool intersects_inclusive(const AABB &p_aabb) const; /// Both AABBs (or their faces) overlap
_FORCE_INLINE_ bool encloses(const AABB &p_aabb) const; /// p_aabb is completely inside this
diff --git a/core/math/basis.cpp b/core/math/basis.cpp
index 845686f3392..9b8188eed86 100644
--- a/core/math/basis.cpp
+++ b/core/math/basis.cpp
@@ -691,6 +691,10 @@ bool Basis::is_equal_approx(const Basis &p_basis) const {
return rows[0].is_equal_approx(p_basis.rows[0]) && rows[1].is_equal_approx(p_basis.rows[1]) && rows[2].is_equal_approx(p_basis.rows[2]);
}
+bool Basis::is_finite() const {
+ return rows[0].is_finite() && rows[1].is_finite() && rows[2].is_finite();
+}
+
bool Basis::operator==(const Basis &p_matrix) const {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
diff --git a/core/math/basis.h b/core/math/basis.h
index cc2924f5ff5..69bef5a7beb 100644
--- a/core/math/basis.h
+++ b/core/math/basis.h
@@ -134,6 +134,7 @@ struct _NO_DISCARD_ Basis {
}
bool is_equal_approx(const Basis &p_basis) const;
+ bool is_finite() const;
bool operator==(const Basis &p_matrix) const;
bool operator!=(const Basis &p_matrix) const;
diff --git a/core/math/math_funcs.h b/core/math/math_funcs.h
index 7fa674a23d5..0af529ad981 100644
--- a/core/math/math_funcs.h
+++ b/core/math/math_funcs.h
@@ -184,6 +184,9 @@ public:
#endif
}
+ static _ALWAYS_INLINE_ bool is_finite(double p_val) { return isfinite(p_val); }
+ static _ALWAYS_INLINE_ bool is_finite(float p_val) { return isfinite(p_val); }
+
static _ALWAYS_INLINE_ double abs(double g) { return absd(g); }
static _ALWAYS_INLINE_ float abs(float g) { return absf(g); }
static _ALWAYS_INLINE_ int abs(int g) { return g > 0 ? g : -g; }
diff --git a/core/math/plane.cpp b/core/math/plane.cpp
index 3b2eab4ae22..a5d2fe56281 100644
--- a/core/math/plane.cpp
+++ b/core/math/plane.cpp
@@ -176,6 +176,10 @@ bool Plane::is_equal_approx(const Plane &p_plane) const {
return normal.is_equal_approx(p_plane.normal) && Math::is_equal_approx(d, p_plane.d);
}
+bool Plane::is_finite() const {
+ return normal.is_finite() && Math::is_finite(d);
+}
+
Plane::operator String() const {
return "[N: " + normal.operator String() + ", D: " + String::num_real(d, false) + "]";
}
diff --git a/core/math/plane.h b/core/math/plane.h
index 73babfa4965..77da59fb274 100644
--- a/core/math/plane.h
+++ b/core/math/plane.h
@@ -74,6 +74,7 @@ struct _NO_DISCARD_ Plane {
Plane operator-() const { return Plane(-normal, -d); }
bool is_equal_approx(const Plane &p_plane) const;
bool is_equal_approx_any_side(const Plane &p_plane) const;
+ bool is_finite() const;
_FORCE_INLINE_ bool operator==(const Plane &p_plane) const;
_FORCE_INLINE_ bool operator!=(const Plane &p_plane) const;
diff --git a/core/math/quaternion.cpp b/core/math/quaternion.cpp
index 4a8d29e402d..6a5f29f3d8f 100644
--- a/core/math/quaternion.cpp
+++ b/core/math/quaternion.cpp
@@ -79,6 +79,10 @@ bool Quaternion::is_equal_approx(const Quaternion &p_quaternion) const {
return Math::is_equal_approx(x, p_quaternion.x) && Math::is_equal_approx(y, p_quaternion.y) && Math::is_equal_approx(z, p_quaternion.z) && Math::is_equal_approx(w, p_quaternion.w);
}
+bool Quaternion::is_finite() const {
+ return Math::is_finite(x) && Math::is_finite(y) && Math::is_finite(z) && Math::is_finite(w);
+}
+
real_t Quaternion::length() const {
return Math::sqrt(length_squared());
}
diff --git a/core/math/quaternion.h b/core/math/quaternion.h
index 178cfaca70f..7aa400aa8c0 100644
--- a/core/math/quaternion.h
+++ b/core/math/quaternion.h
@@ -55,6 +55,7 @@ struct _NO_DISCARD_ Quaternion {
}
_FORCE_INLINE_ real_t length_squared() const;
bool is_equal_approx(const Quaternion &p_quaternion) const;
+ bool is_finite() const;
real_t length() const;
void normalize();
Quaternion normalized() const;
diff --git a/core/math/rect2.cpp b/core/math/rect2.cpp
index 9e78ead816c..facf4eb3c45 100644
--- a/core/math/rect2.cpp
+++ b/core/math/rect2.cpp
@@ -38,6 +38,10 @@ bool Rect2::is_equal_approx(const Rect2 &p_rect) const {
return position.is_equal_approx(p_rect.position) && size.is_equal_approx(p_rect.size);
}
+bool Rect2::is_finite() const {
+ return position.is_finite() && size.is_finite();
+}
+
bool Rect2::intersects_segment(const Point2 &p_from, const Point2 &p_to, Point2 *r_pos, Point2 *r_normal) const {
#ifdef MATH_CHECKS
if (unlikely(size.x < 0 || size.y < 0)) {
diff --git a/core/math/rect2.h b/core/math/rect2.h
index 50dd2dc1df1..9863405d8e2 100644
--- a/core/math/rect2.h
+++ b/core/math/rect2.h
@@ -207,6 +207,7 @@ struct _NO_DISCARD_ Rect2 {
}
bool is_equal_approx(const Rect2 &p_rect) const;
+ bool is_finite() const;
bool operator==(const Rect2 &p_rect) const { return position == p_rect.position && size == p_rect.size; }
bool operator!=(const Rect2 &p_rect) const { return position != p_rect.position || size != p_rect.size; }
diff --git a/core/math/transform_2d.cpp b/core/math/transform_2d.cpp
index 2bfefe979ff..548a82d2545 100644
--- a/core/math/transform_2d.cpp
+++ b/core/math/transform_2d.cpp
@@ -168,6 +168,10 @@ bool Transform2D::is_equal_approx(const Transform2D &p_transform) const {
return columns[0].is_equal_approx(p_transform.columns[0]) && columns[1].is_equal_approx(p_transform.columns[1]) && columns[2].is_equal_approx(p_transform.columns[2]);
}
+bool Transform2D::is_finite() const {
+ return columns[0].is_finite() && columns[1].is_finite() && columns[2].is_finite();
+}
+
Transform2D Transform2D::looking_at(const Vector2 &p_target) const {
Transform2D return_trans = Transform2D(get_rotation(), get_origin());
Vector2 target_position = affine_inverse().xform(p_target);
diff --git a/core/math/transform_2d.h b/core/math/transform_2d.h
index f23f32867a5..2b11f36535e 100644
--- a/core/math/transform_2d.h
+++ b/core/math/transform_2d.h
@@ -98,6 +98,7 @@ struct _NO_DISCARD_ Transform2D {
void orthonormalize();
Transform2D orthonormalized() const;
bool is_equal_approx(const Transform2D &p_transform) const;
+ bool is_finite() const;
Transform2D looking_at(const Vector2 &p_target) const;
diff --git a/core/math/transform_3d.cpp b/core/math/transform_3d.cpp
index 6741ef4034d..3285cbd664b 100644
--- a/core/math/transform_3d.cpp
+++ b/core/math/transform_3d.cpp
@@ -174,6 +174,10 @@ bool Transform3D::is_equal_approx(const Transform3D &p_transform) const {
return basis.is_equal_approx(p_transform.basis) && origin.is_equal_approx(p_transform.origin);
}
+bool Transform3D::is_finite() const {
+ return basis.is_finite() && origin.is_finite();
+}
+
bool Transform3D::operator==(const Transform3D &p_transform) const {
return (basis == p_transform.basis && origin == p_transform.origin);
}
diff --git a/core/math/transform_3d.h b/core/math/transform_3d.h
index 44d6d826f35..cb347aa1c16 100644
--- a/core/math/transform_3d.h
+++ b/core/math/transform_3d.h
@@ -75,6 +75,7 @@ struct _NO_DISCARD_ Transform3D {
void orthogonalize();
Transform3D orthogonalized() const;
bool is_equal_approx(const Transform3D &p_transform) const;
+ bool is_finite() const;
bool operator==(const Transform3D &p_transform) const;
bool operator!=(const Transform3D &p_transform) const;
diff --git a/core/math/vector2.cpp b/core/math/vector2.cpp
index 56dbba393ae..53665871268 100644
--- a/core/math/vector2.cpp
+++ b/core/math/vector2.cpp
@@ -186,6 +186,10 @@ bool Vector2::is_zero_approx() const {
return Math::is_zero_approx(x) && Math::is_zero_approx(y);
}
+bool Vector2::is_finite() const {
+ return Math::is_finite(x) && Math::is_finite(y);
+}
+
Vector2::operator String() const {
return "(" + String::num_real(x, false) + ", " + String::num_real(y, false) + ")";
}
diff --git a/core/math/vector2.h b/core/math/vector2.h
index 75364f72f01..5775d8e735e 100644
--- a/core/math/vector2.h
+++ b/core/math/vector2.h
@@ -121,6 +121,7 @@ struct _NO_DISCARD_ Vector2 {
bool is_equal_approx(const Vector2 &p_v) const;
bool is_zero_approx() const;
+ bool is_finite() const;
Vector2 operator+(const Vector2 &p_v) const;
void operator+=(const Vector2 &p_v);
diff --git a/core/math/vector3.cpp b/core/math/vector3.cpp
index 55ba5091447..b106200c4a6 100644
--- a/core/math/vector3.cpp
+++ b/core/math/vector3.cpp
@@ -139,6 +139,10 @@ bool Vector3::is_zero_approx() const {
return Math::is_zero_approx(x) && Math::is_zero_approx(y) && Math::is_zero_approx(z);
}
+bool Vector3::is_finite() const {
+ return Math::is_finite(x) && Math::is_finite(y) && Math::is_finite(z);
+}
+
Vector3::operator String() const {
return "(" + String::num_real(x, false) + ", " + String::num_real(y, false) + ", " + String::num_real(z, false) + ")";
}
diff --git a/core/math/vector3.h b/core/math/vector3.h
index 62e810fb4df..19771eb312e 100644
--- a/core/math/vector3.h
+++ b/core/math/vector3.h
@@ -136,6 +136,7 @@ struct _NO_DISCARD_ Vector3 {
bool is_equal_approx(const Vector3 &p_v) const;
bool is_zero_approx() const;
+ bool is_finite() const;
/* Operators */
diff --git a/core/math/vector4.cpp b/core/math/vector4.cpp
index 9fd980aaff4..3b189f7ed4a 100644
--- a/core/math/vector4.cpp
+++ b/core/math/vector4.cpp
@@ -64,6 +64,10 @@ bool Vector4::is_zero_approx() const {
return Math::is_zero_approx(x) && Math::is_zero_approx(y) && Math::is_zero_approx(z) && Math::is_zero_approx(w);
}
+bool Vector4::is_finite() const {
+ return Math::is_finite(x) && Math::is_finite(y) && Math::is_finite(z) && Math::is_finite(w);
+}
+
real_t Vector4::length() const {
return Math::sqrt(length_squared());
}
diff --git a/core/math/vector4.h b/core/math/vector4.h
index ac7b6c3aeef..7c4bdc17889 100644
--- a/core/math/vector4.h
+++ b/core/math/vector4.h
@@ -71,6 +71,7 @@ struct _NO_DISCARD_ Vector4 {
_FORCE_INLINE_ real_t length_squared() const;
bool is_equal_approx(const Vector4 &p_vec4) const;
bool is_zero_approx() const;
+ bool is_finite() const;
real_t length() const;
void normalize();
Vector4 normalized() const;
diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp
index 6218c21cde0..e5e7ff357cd 100644
--- a/core/string/ustring.cpp
+++ b/core/string/ustring.cpp
@@ -4651,10 +4651,10 @@ String String::sprintf(const Array &values, bool *error) const {
double value = values[value_index];
bool is_negative = (value < 0);
String str = String::num(ABS(value), min_decimals);
- bool not_numeric = isinf(value) || isnan(value);
+ const bool is_finite = Math::is_finite(value);
// Pad decimals out.
- if (!not_numeric) {
+ if (is_finite) {
str = str.pad_decimals(min_decimals);
}
@@ -4662,7 +4662,7 @@ String String::sprintf(const Array &values, bool *error) const {
// Padding. Leave room for sign later if required.
int pad_chars_count = (is_negative || show_sign) ? min_chars - 1 : min_chars;
- String pad_char = (pad_with_zeros && !not_numeric) ? String("0") : String(" "); // Never pad NaN or inf with zeros
+ String pad_char = (pad_with_zeros && is_finite) ? String("0") : String(" "); // Never pad NaN or inf with zeros
if (left_justified) {
str = str.rpad(pad_chars_count, pad_char);
} else {
@@ -4713,10 +4713,10 @@ String String::sprintf(const Array &values, bool *error) const {
for (int i = 0; i < count; i++) {
double val = vec[i];
String number_str = String::num(ABS(val), min_decimals);
- bool not_numeric = isinf(val) || isnan(val);
+ const bool is_finite = Math::is_finite(val);
// Pad decimals out.
- if (!not_numeric) {
+ if (is_finite) {
number_str = number_str.pad_decimals(min_decimals);
}
@@ -4724,7 +4724,7 @@ String String::sprintf(const Array &values, bool *error) const {
// Padding. Leave room for sign later if required.
int pad_chars_count = val < 0 ? min_chars - 1 : min_chars;
- String pad_char = (pad_with_zeros && !not_numeric) ? String("0") : String(" "); // Never pad NaN or inf with zeros
+ String pad_char = (pad_with_zeros && is_finite) ? String("0") : String(" "); // Never pad NaN or inf with zeros
if (left_justified) {
number_str = number_str.rpad(pad_chars_count, pad_char);
} else {
diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp
index 1831f7b72a3..900e3d8e776 100644
--- a/core/variant/variant_call.cpp
+++ b/core/variant/variant_call.cpp
@@ -1606,6 +1606,7 @@ static void _register_variant_builtin_methods() {
bind_method(Vector2, is_normalized, sarray(), varray());
bind_method(Vector2, is_equal_approx, sarray("to"), varray());
bind_method(Vector2, is_zero_approx, sarray(), varray());
+ bind_method(Vector2, is_finite, sarray(), varray());
bind_method(Vector2, posmod, sarray("mod"), varray());
bind_method(Vector2, posmodv, sarray("modv"), varray());
bind_method(Vector2, project, sarray("b"), varray());
@@ -1653,6 +1654,7 @@ static void _register_variant_builtin_methods() {
bind_method(Rect2, has_area, sarray(), varray());
bind_method(Rect2, has_point, sarray("point"), varray());
bind_method(Rect2, is_equal_approx, sarray("rect"), varray());
+ bind_method(Rect2, is_finite, sarray(), varray());
bind_method(Rect2, intersects, sarray("b", "include_borders"), varray(false));
bind_method(Rect2, encloses, sarray("b"), varray());
bind_method(Rect2, intersection, sarray("b"), varray());
@@ -1695,6 +1697,7 @@ static void _register_variant_builtin_methods() {
bind_method(Vector3, is_normalized, sarray(), varray());
bind_method(Vector3, is_equal_approx, sarray("to"), varray());
bind_method(Vector3, is_zero_approx, sarray(), varray());
+ bind_method(Vector3, is_finite, sarray(), varray());
bind_method(Vector3, inverse, sarray(), varray());
bind_method(Vector3, clamp, sarray("min", "max"), varray());
bind_method(Vector3, snapped, sarray("step"), varray());
@@ -1759,6 +1762,7 @@ static void _register_variant_builtin_methods() {
bind_method(Vector4, inverse, sarray(), varray());
bind_method(Vector4, is_equal_approx, sarray("with"), varray());
bind_method(Vector4, is_zero_approx, sarray(), varray());
+ bind_method(Vector4, is_finite, sarray(), varray());
/* Vector4i */
@@ -1775,6 +1779,7 @@ static void _register_variant_builtin_methods() {
bind_method(Plane, normalized, sarray(), varray());
bind_method(Plane, center, sarray(), varray());
bind_method(Plane, is_equal_approx, sarray("to_plane"), varray());
+ bind_method(Plane, is_finite, sarray(), varray());
bind_method(Plane, is_point_over, sarray("point"), varray());
bind_method(Plane, distance_to, sarray("point"), varray());
bind_method(Plane, has_point, sarray("point", "tolerance"), varray(CMP_EPSILON));
@@ -1790,6 +1795,7 @@ static void _register_variant_builtin_methods() {
bind_method(Quaternion, normalized, sarray(), varray());
bind_method(Quaternion, is_normalized, sarray(), varray());
bind_method(Quaternion, is_equal_approx, sarray("to"), varray());
+ bind_method(Quaternion, is_finite, sarray(), varray());
bind_method(Quaternion, inverse, sarray(), varray());
bind_method(Quaternion, log, sarray(), varray());
bind_method(Quaternion, exp, sarray(), varray());
@@ -1909,6 +1915,7 @@ static void _register_variant_builtin_methods() {
bind_method(Transform2D, basis_xform_inv, sarray("v"), varray());
bind_method(Transform2D, interpolate_with, sarray("xform", "weight"), varray());
bind_method(Transform2D, is_equal_approx, sarray("xform"), varray());
+ bind_method(Transform2D, is_finite, sarray(), varray());
bind_method(Transform2D, set_rotation, sarray("rotation"), varray());
bind_method(Transform2D, set_scale, sarray("scale"), varray());
bind_method(Transform2D, set_skew, sarray("skew"), varray());
@@ -1929,6 +1936,7 @@ static void _register_variant_builtin_methods() {
bind_method(Basis, tdotz, sarray("with"), varray());
bind_method(Basis, slerp, sarray("to", "weight"), varray());
bind_method(Basis, is_equal_approx, sarray("b"), varray());
+ bind_method(Basis, is_finite, sarray(), varray());
bind_method(Basis, get_rotation_quaternion, sarray(), varray());
bind_static_method(Basis, looking_at, sarray("target", "up"), varray(Vector3(0, 1, 0)));
bind_static_method(Basis, from_scale, sarray("scale"), varray());
@@ -1943,6 +1951,7 @@ static void _register_variant_builtin_methods() {
bind_method(AABB, has_surface, sarray(), varray());
bind_method(AABB, has_point, sarray("point"), varray());
bind_method(AABB, is_equal_approx, sarray("aabb"), varray());
+ bind_method(AABB, is_finite, sarray(), varray());
bind_method(AABB, intersects, sarray("with"), varray());
bind_method(AABB, encloses, sarray("with"), varray());
bind_method(AABB, intersects_plane, sarray("plane"), varray());
@@ -1975,6 +1984,7 @@ static void _register_variant_builtin_methods() {
bind_method(Transform3D, looking_at, sarray("target", "up"), varray(Vector3(0, 1, 0)));
bind_method(Transform3D, interpolate_with, sarray("xform", "weight"), varray());
bind_method(Transform3D, is_equal_approx, sarray("xform"), varray());
+ bind_method(Transform3D, is_finite, sarray(), varray());
/* Projection */
diff --git a/core/variant/variant_utility.cpp b/core/variant/variant_utility.cpp
index 670b66d53e5..3843c32bcc3 100644
--- a/core/variant/variant_utility.cpp
+++ b/core/variant/variant_utility.cpp
@@ -310,6 +310,10 @@ struct VariantUtilityFunctions {
return Math::is_zero_approx(x);
}
+ static inline bool is_finite(double x) {
+ return Math::is_finite(x);
+ }
+
static inline double ease(float x, float curve) {
return Math::ease(x, curve);
}
@@ -1420,6 +1424,7 @@ void Variant::_register_variant_utility_functions() {
FUNCBINDR(is_equal_approx, sarray("a", "b"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(is_zero_approx, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH);
+ FUNCBINDR(is_finite, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(ease, sarray("x", "curve"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(step_decimals, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH);
diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml
index 1b575695825..0752ef6b257 100644
--- a/doc/classes/@GlobalScope.xml
+++ b/doc/classes/@GlobalScope.xml
@@ -476,6 +476,13 @@
Infinity values of the same sign are considered equal.
+
+
+
+
+ Returns whether [code]x[/code] is a finite value, i.e. it is not [constant @GDScript.NAN], positive infinity, or negative infinity.
+
+
diff --git a/doc/classes/AABB.xml b/doc/classes/AABB.xml
index 23dd41f275d..1ac3e6b64cb 100644
--- a/doc/classes/AABB.xml
+++ b/doc/classes/AABB.xml
@@ -205,6 +205,12 @@
Returns [code]true[/code] if this [AABB] and [param aabb] are approximately equal, by calling [method @GlobalScope.is_equal_approx] on each component.
+
+
+
+ Returns [code]true[/code] if this [AABB] is finite, by calling [method @GlobalScope.is_finite] on each component.
+
+
diff --git a/doc/classes/Basis.xml b/doc/classes/Basis.xml
index 6d9b679fbc8..f499be34a01 100644
--- a/doc/classes/Basis.xml
+++ b/doc/classes/Basis.xml
@@ -112,6 +112,12 @@
Returns [code]true[/code] if this basis and [param b] are approximately equal, by calling [code]is_equal_approx[/code] on each component.
+
+
+
+ Returns [code]true[/code] if this basis is finite, by calling [method @GlobalScope.is_finite] on each component.
+
+
diff --git a/doc/classes/Plane.xml b/doc/classes/Plane.xml
index e51e3753fc1..fbe8afa8d16 100644
--- a/doc/classes/Plane.xml
+++ b/doc/classes/Plane.xml
@@ -119,6 +119,12 @@
Returns [code]true[/code] if this plane and [param to_plane] are approximately equal, by running [method @GlobalScope.is_equal_approx] on each component.
+
+
+
+ Returns [code]true[/code] if this plane is finite, by calling [method @GlobalScope.is_finite] on each component.
+
+
diff --git a/doc/classes/Quaternion.xml b/doc/classes/Quaternion.xml
index f21ebf57e28..99dffeff9d5 100644
--- a/doc/classes/Quaternion.xml
+++ b/doc/classes/Quaternion.xml
@@ -115,6 +115,12 @@
Returns [code]true[/code] if this quaternion and [param to] are approximately equal, by running [method @GlobalScope.is_equal_approx] on each component.
+
+
+
+ Returns [code]true[/code] if this quaternion is finite, by calling [method @GlobalScope.is_finite] on each component.
+
+
diff --git a/doc/classes/Rect2.xml b/doc/classes/Rect2.xml
index ac012e9604d..28fe42afae2 100644
--- a/doc/classes/Rect2.xml
+++ b/doc/classes/Rect2.xml
@@ -165,6 +165,12 @@
Returns [code]true[/code] if this [Rect2] and [param rect] are approximately equal, by calling [code]is_equal_approx[/code] on each component.
+
+
+
+ Returns [code]true[/code] if this [Rect2] is finite, by calling [method @GlobalScope.is_finite] on each component.
+
+
diff --git a/doc/classes/Transform2D.xml b/doc/classes/Transform2D.xml
index 905b3d77af2..23d20a5a753 100644
--- a/doc/classes/Transform2D.xml
+++ b/doc/classes/Transform2D.xml
@@ -123,6 +123,12 @@
Returns [code]true[/code] if this transform and [code]transform[/code] are approximately equal, by calling [code]is_equal_approx[/code] on each component.
+
+
+
+ Returns [code]true[/code] if this transform is finite, by calling [method @GlobalScope.is_finite] on each component.
+
+
diff --git a/doc/classes/Transform3D.xml b/doc/classes/Transform3D.xml
index 18b4f9e6f99..b3145ea0227 100644
--- a/doc/classes/Transform3D.xml
+++ b/doc/classes/Transform3D.xml
@@ -82,6 +82,12 @@
Returns [code]true[/code] if this transform and [code]transform[/code] are approximately equal, by calling [code]is_equal_approx[/code] on each component.
+
+
+
+ Returns [code]true[/code] if this transform is finite, by calling [method @GlobalScope.is_finite] on each component.
+
+
diff --git a/doc/classes/Vector2.xml b/doc/classes/Vector2.xml
index e1852340c0d..5590f823365 100644
--- a/doc/classes/Vector2.xml
+++ b/doc/classes/Vector2.xml
@@ -206,6 +206,12 @@
Returns [code]true[/code] if this vector and [code]v[/code] are approximately equal, by running [method @GlobalScope.is_equal_approx] on each component.
+
+
+
+ Returns [code]true[/code] if this vector is finite, by calling [method @GlobalScope.is_finite] on each component.
+
+
diff --git a/doc/classes/Vector3.xml b/doc/classes/Vector3.xml
index 1ef84050cda..81e8dd22605 100644
--- a/doc/classes/Vector3.xml
+++ b/doc/classes/Vector3.xml
@@ -174,6 +174,12 @@
Returns [code]true[/code] if this vector and [param to] are approximately equal, by running [method @GlobalScope.is_equal_approx] on each component.
+
+
+
+ Returns [code]true[/code] if this vector is finite, by calling [method @GlobalScope.is_finite] on each component.
+
+
diff --git a/doc/classes/Vector4.xml b/doc/classes/Vector4.xml
index fdc93f82ec9..662d0bce3ae 100644
--- a/doc/classes/Vector4.xml
+++ b/doc/classes/Vector4.xml
@@ -135,6 +135,12 @@
Returns [code]true[/code] if this vector and [param with] are approximately equal, by running [method @GlobalScope.is_equal_approx] on each component.
+
+
+
+ Returns [code]true[/code] if this vector is finite, by calling [method @GlobalScope.is_finite] on each component.
+
+
diff --git a/servers/rendering/renderer_scene_cull.cpp b/servers/rendering/renderer_scene_cull.cpp
index 2b25e8962f5..d0cb46dee95 100644
--- a/servers/rendering/renderer_scene_cull.cpp
+++ b/servers/rendering/renderer_scene_cull.cpp
@@ -869,12 +869,7 @@ void RendererSceneCull::instance_set_transform(RID p_instance, const Transform3D
for (int i = 0; i < 4; i++) {
const Vector3 &v = i < 3 ? p_transform.basis.rows[i] : p_transform.origin;
- ERR_FAIL_COND(Math::is_inf(v.x));
- ERR_FAIL_COND(Math::is_nan(v.x));
- ERR_FAIL_COND(Math::is_inf(v.y));
- ERR_FAIL_COND(Math::is_nan(v.y));
- ERR_FAIL_COND(Math::is_inf(v.z));
- ERR_FAIL_COND(Math::is_nan(v.z));
+ ERR_FAIL_COND(!v.is_finite());
}
#endif
diff --git a/tests/core/math/test_aabb.h b/tests/core/math/test_aabb.h
index d5f54a139ef..ebaf441abf6 100644
--- a/tests/core/math/test_aabb.h
+++ b/tests/core/math/test_aabb.h
@@ -389,6 +389,27 @@ TEST_CASE("[AABB] Expanding") {
aabb.expand(Vector3(-20, 0, 0)).is_equal_approx(AABB(Vector3(-20, 0, -2.5), Vector3(22.5, 7, 6))),
"expand() with non-contained point should return the expected AABB.");
}
+
+TEST_CASE("[AABB] Finite number checks") {
+ const Vector3 x(0, 1, 2);
+ const Vector3 infinite(NAN, NAN, NAN);
+
+ CHECK_MESSAGE(
+ AABB(x, x).is_finite(),
+ "AABB with all components finite should be finite");
+
+ CHECK_FALSE_MESSAGE(
+ AABB(infinite, x).is_finite(),
+ "AABB with one component infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ AABB(x, infinite).is_finite(),
+ "AABB with one component infinite should not be finite.");
+
+ CHECK_FALSE_MESSAGE(
+ AABB(infinite, infinite).is_finite(),
+ "AABB with two components infinite should not be finite.");
+}
+
} // namespace TestAABB
#endif // TEST_AABB_H
diff --git a/tests/core/math/test_basis.h b/tests/core/math/test_basis.h
index b6493c57262..a65020597a5 100644
--- a/tests/core/math/test_basis.h
+++ b/tests/core/math/test_basis.h
@@ -334,6 +334,40 @@ TEST_CASE("[Basis] Set axis angle") {
bugNan.get_axis_angle(axis, angle);
CHECK(!Math::is_nan(angle));
}
+
+TEST_CASE("[Basis] Finite number checks") {
+ const Vector3 x(0, 1, 2);
+ const Vector3 infinite(NAN, NAN, NAN);
+
+ CHECK_MESSAGE(
+ Basis(x, x, x).is_finite(),
+ "Basis with all components finite should be finite");
+
+ CHECK_FALSE_MESSAGE(
+ Basis(infinite, x, x).is_finite(),
+ "Basis with one component infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Basis(x, infinite, x).is_finite(),
+ "Basis with one component infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Basis(x, x, infinite).is_finite(),
+ "Basis with one component infinite should not be finite.");
+
+ CHECK_FALSE_MESSAGE(
+ Basis(infinite, infinite, x).is_finite(),
+ "Basis with two components infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Basis(infinite, x, infinite).is_finite(),
+ "Basis with two components infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Basis(x, infinite, infinite).is_finite(),
+ "Basis with two components infinite should not be finite.");
+
+ CHECK_FALSE_MESSAGE(
+ Basis(infinite, infinite, infinite).is_finite(),
+ "Basis with three components infinite should not be finite.");
+}
+
} // namespace TestBasis
#endif // TEST_BASIS_H
diff --git a/tests/core/math/test_plane.h b/tests/core/math/test_plane.h
index d81a5af1ce4..84d9a0ff7dc 100644
--- a/tests/core/math/test_plane.h
+++ b/tests/core/math/test_plane.h
@@ -167,6 +167,29 @@ TEST_CASE("[Plane] Intersection") {
vec_out.is_equal_approx(Vector3(1, 1, 1)),
"intersects_segment() should modify vec_out to the expected result.");
}
+
+TEST_CASE("[Plane] Finite number checks") {
+ const Vector3 x(0, 1, 2);
+ const Vector3 infinite_vec(NAN, NAN, NAN);
+ const real_t y = 0;
+ const real_t infinite_y = NAN;
+
+ CHECK_MESSAGE(
+ Plane(x, y).is_finite(),
+ "Plane with all components finite should be finite");
+
+ CHECK_FALSE_MESSAGE(
+ Plane(x, infinite_y).is_finite(),
+ "Plane with one component infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Plane(infinite_vec, y).is_finite(),
+ "Plane with one component infinite should not be finite.");
+
+ CHECK_FALSE_MESSAGE(
+ Plane(infinite_vec, infinite_y).is_finite(),
+ "Plane with two components infinite should not be finite.");
+}
+
} // namespace TestPlane
#endif // TEST_PLANE_H
diff --git a/tests/core/math/test_quaternion.h b/tests/core/math/test_quaternion.h
index 63d30759bb2..d1912cbf42c 100644
--- a/tests/core/math/test_quaternion.h
+++ b/tests/core/math/test_quaternion.h
@@ -384,6 +384,63 @@ TEST_CASE("[Stress][Quaternion] Many vector xforms") {
}
}
+TEST_CASE("[Quaternion] Finite number checks") {
+ const real_t x = NAN;
+
+ CHECK_MESSAGE(
+ Quaternion(0, 1, 2, 3).is_finite(),
+ "Quaternion with all components finite should be finite");
+
+ CHECK_FALSE_MESSAGE(
+ Quaternion(x, 1, 2, 3).is_finite(),
+ "Quaternion with one component infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Quaternion(0, x, 2, 3).is_finite(),
+ "Quaternion with one component infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Quaternion(0, 1, x, 3).is_finite(),
+ "Quaternion with one component infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Quaternion(0, 1, 2, x).is_finite(),
+ "Quaternion with one component infinite should not be finite.");
+
+ CHECK_FALSE_MESSAGE(
+ Quaternion(x, x, 2, 3).is_finite(),
+ "Quaternion with two components infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Quaternion(x, 1, x, 3).is_finite(),
+ "Quaternion with two components infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Quaternion(x, 1, 2, x).is_finite(),
+ "Quaternion with two components infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Quaternion(0, x, x, 3).is_finite(),
+ "Quaternion with two components infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Quaternion(0, x, 2, x).is_finite(),
+ "Quaternion with two components infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Quaternion(0, 1, x, x).is_finite(),
+ "Quaternion with two components infinite should not be finite.");
+
+ CHECK_FALSE_MESSAGE(
+ Quaternion(0, x, x, x).is_finite(),
+ "Quaternion with three components infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Quaternion(x, 1, x, x).is_finite(),
+ "Quaternion with three components infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Quaternion(x, x, 2, x).is_finite(),
+ "Quaternion with three components infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Quaternion(x, x, x, 3).is_finite(),
+ "Quaternion with three components infinite should not be finite.");
+
+ CHECK_FALSE_MESSAGE(
+ Quaternion(x, x, x, x).is_finite(),
+ "Quaternion with four components infinite should not be finite.");
+}
+
} // namespace TestQuaternion
#endif // TEST_QUATERNION_H
diff --git a/tests/core/math/test_rect2.h b/tests/core/math/test_rect2.h
index 6323b214dbb..d784875c1c0 100644
--- a/tests/core/math/test_rect2.h
+++ b/tests/core/math/test_rect2.h
@@ -300,6 +300,27 @@ TEST_CASE("[Rect2] Merging") {
Rect2(0, 100, 1280, 720).merge(Rect2(-4000, -4000, 100, 100)).is_equal_approx(Rect2(-4000, -4000, 5280, 4820)),
"merge() with non-enclosed Rect2 should return the expected result.");
}
+
+TEST_CASE("[Rect2] Finite number checks") {
+ const Vector2 x(0, 1);
+ const Vector2 infinite(NAN, NAN);
+
+ CHECK_MESSAGE(
+ Rect2(x, x).is_finite(),
+ "Rect2 with all components finite should be finite");
+
+ CHECK_FALSE_MESSAGE(
+ Rect2(infinite, x).is_finite(),
+ "Rect2 with one component infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Rect2(x, infinite).is_finite(),
+ "Rect2 with one component infinite should not be finite.");
+
+ CHECK_FALSE_MESSAGE(
+ Rect2(infinite, infinite).is_finite(),
+ "Rect2 with two components infinite should not be finite.");
+}
+
} // namespace TestRect2
#endif // TEST_RECT2_H
diff --git a/tests/core/math/test_transform_2d.h b/tests/core/math/test_transform_2d.h
index 697bf63fc57..ab51bcd7da9 100644
--- a/tests/core/math/test_transform_2d.h
+++ b/tests/core/math/test_transform_2d.h
@@ -83,6 +83,40 @@ TEST_CASE("[Transform2D] rotation") {
CHECK(orig.rotated(phi) == R * orig);
CHECK(orig.rotated_local(phi) == orig * R);
}
+
+TEST_CASE("[Transform2D] Finite number checks") {
+ const Vector2 x(0, 1);
+ const Vector2 infinite(NAN, NAN);
+
+ CHECK_MESSAGE(
+ Transform2D(x, x, x).is_finite(),
+ "Transform2D with all components finite should be finite");
+
+ CHECK_FALSE_MESSAGE(
+ Transform2D(infinite, x, x).is_finite(),
+ "Transform2D with one component infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Transform2D(x, infinite, x).is_finite(),
+ "Transform2D with one component infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Transform2D(x, x, infinite).is_finite(),
+ "Transform2D with one component infinite should not be finite.");
+
+ CHECK_FALSE_MESSAGE(
+ Transform2D(infinite, infinite, x).is_finite(),
+ "Transform2D with two components infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Transform2D(infinite, x, infinite).is_finite(),
+ "Transform2D with two components infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Transform2D(x, infinite, infinite).is_finite(),
+ "Transform2D with two components infinite should not be finite.");
+
+ CHECK_FALSE_MESSAGE(
+ Transform2D(infinite, infinite, infinite).is_finite(),
+ "Transform2D with three components infinite should not be finite.");
+}
+
} // namespace TestTransform2D
#endif // TEST_TRANSFORM_2D_H
diff --git a/tests/core/math/test_transform_3d.h b/tests/core/math/test_transform_3d.h
index da166b43f73..d2730f35777 100644
--- a/tests/core/math/test_transform_3d.h
+++ b/tests/core/math/test_transform_3d.h
@@ -84,6 +84,29 @@ TEST_CASE("[Transform3D] rotation") {
CHECK(orig.rotated(axis, phi) == R * orig);
CHECK(orig.rotated_local(axis, phi) == orig * R);
}
+
+TEST_CASE("[Transform3D] Finite number checks") {
+ const Vector3 y(0, 1, 2);
+ const Vector3 infinite_vec(NAN, NAN, NAN);
+ const Basis x(y, y, y);
+ const Basis infinite_basis(infinite_vec, infinite_vec, infinite_vec);
+
+ CHECK_MESSAGE(
+ Transform3D(x, y).is_finite(),
+ "Transform3D with all components finite should be finite");
+
+ CHECK_FALSE_MESSAGE(
+ Transform3D(x, infinite_vec).is_finite(),
+ "Transform3D with one component infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Transform3D(infinite_basis, y).is_finite(),
+ "Transform3D with one component infinite should not be finite.");
+
+ CHECK_FALSE_MESSAGE(
+ Transform3D(infinite_basis, infinite_vec).is_finite(),
+ "Transform3D with two components infinite should not be finite.");
+}
+
} // namespace TestTransform3D
#endif // TEST_TRANSFORM_3D_H
diff --git a/tests/core/math/test_vector2.h b/tests/core/math/test_vector2.h
index 0d7f1163e49..a87b9ffc02c 100644
--- a/tests/core/math/test_vector2.h
+++ b/tests/core/math/test_vector2.h
@@ -465,6 +465,32 @@ TEST_CASE("[Vector2] Linear algebra methods") {
Math::is_equal_approx(Vector2(-a.x, a.y).dot(Vector2(b.x, -b.y)), (real_t)-57.3),
"Vector2 dot should return expected value.");
}
+
+TEST_CASE("[Vector2] Finite number checks") {
+ const double infinite[] = { NAN, INFINITY, -INFINITY };
+
+ CHECK_MESSAGE(
+ Vector2(0, 1).is_finite(),
+ "Vector2(0, 1) should be finite");
+
+ for (double x : infinite) {
+ CHECK_FALSE_MESSAGE(
+ Vector2(x, 1).is_finite(),
+ "Vector2 with one component infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Vector2(0, x).is_finite(),
+ "Vector2 with one component infinite should not be finite.");
+ }
+
+ for (double x : infinite) {
+ for (double y : infinite) {
+ CHECK_FALSE_MESSAGE(
+ Vector2(x, y).is_finite(),
+ "Vector2 with two components infinite should not be finite.");
+ }
+ }
+}
+
} // namespace TestVector2
#endif // TEST_VECTOR2_H
diff --git a/tests/core/math/test_vector3.h b/tests/core/math/test_vector3.h
index be271bad1ff..4932cd04db5 100644
--- a/tests/core/math/test_vector3.h
+++ b/tests/core/math/test_vector3.h
@@ -479,6 +479,51 @@ TEST_CASE("[Vector3] Linear algebra methods") {
Math::is_equal_approx(Vector3(-a.x, a.y, -a.z).dot(Vector3(b.x, -b.y, b.z)), (real_t)-75.24),
"Vector3 dot should return expected value.");
}
+
+TEST_CASE("[Vector3] Finite number checks") {
+ const double infinite[] = { NAN, INFINITY, -INFINITY };
+
+ CHECK_MESSAGE(
+ Vector3(0, 1, 2).is_finite(),
+ "Vector3(0, 1, 2) should be finite");
+
+ for (double x : infinite) {
+ CHECK_FALSE_MESSAGE(
+ Vector3(x, 1, 2).is_finite(),
+ "Vector3 with one component infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Vector3(0, x, 2).is_finite(),
+ "Vector3 with one component infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Vector3(0, 1, x).is_finite(),
+ "Vector3 with one component infinite should not be finite.");
+ }
+
+ for (double x : infinite) {
+ for (double y : infinite) {
+ CHECK_FALSE_MESSAGE(
+ Vector3(x, y, 2).is_finite(),
+ "Vector3 with two components infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Vector3(x, 1, y).is_finite(),
+ "Vector3 with two components infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Vector3(0, x, y).is_finite(),
+ "Vector3 with two components infinite should not be finite.");
+ }
+ }
+
+ for (double x : infinite) {
+ for (double y : infinite) {
+ for (double z : infinite) {
+ CHECK_FALSE_MESSAGE(
+ Vector3(x, y, z).is_finite(),
+ "Vector3 with three components infinite should not be finite.");
+ }
+ }
+ }
+}
+
} // namespace TestVector3
#endif // TEST_VECTOR3_H
diff --git a/tests/core/math/test_vector4.h b/tests/core/math/test_vector4.h
index 3f50f166358..b31db56f67b 100644
--- a/tests/core/math/test_vector4.h
+++ b/tests/core/math/test_vector4.h
@@ -314,6 +314,84 @@ TEST_CASE("[Vector4] Linear algebra methods") {
Math::is_equal_approx((vector1 * 2).dot(vector2 * 4), (real_t)-25.9 * 8),
"Vector4 dot product should work as expected.");
}
+
+TEST_CASE("[Vector4] Finite number checks") {
+ const double infinite[] = { NAN, INFINITY, -INFINITY };
+
+ CHECK_MESSAGE(
+ Vector4(0, 1, 2, 3).is_finite(),
+ "Vector4(0, 1, 2, 3) should be finite");
+
+ for (double x : infinite) {
+ CHECK_FALSE_MESSAGE(
+ Vector4(x, 1, 2, 3).is_finite(),
+ "Vector4 with one component infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Vector4(0, x, 2, 3).is_finite(),
+ "Vector4 with one component infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Vector4(0, 1, x, 3).is_finite(),
+ "Vector4 with one component infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Vector4(0, 1, 2, x).is_finite(),
+ "Vector4 with one component infinite should not be finite.");
+ }
+
+ for (double x : infinite) {
+ for (double y : infinite) {
+ CHECK_FALSE_MESSAGE(
+ Vector4(x, y, 2, 3).is_finite(),
+ "Vector4 with two components infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Vector4(x, 1, y, 3).is_finite(),
+ "Vector4 with two components infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Vector4(x, 1, 2, y).is_finite(),
+ "Vector4 with two components infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Vector4(0, x, y, 3).is_finite(),
+ "Vector4 with two components infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Vector4(0, x, 2, y).is_finite(),
+ "Vector4 with two components infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Vector4(0, 1, x, y).is_finite(),
+ "Vector4 with two components infinite should not be finite.");
+ }
+ }
+
+ for (double x : infinite) {
+ for (double y : infinite) {
+ for (double z : infinite) {
+ CHECK_FALSE_MESSAGE(
+ Vector4(0, x, y, z).is_finite(),
+ "Vector4 with three components infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Vector4(x, 1, y, z).is_finite(),
+ "Vector4 with three components infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Vector4(x, y, 2, z).is_finite(),
+ "Vector4 with three components infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Vector4(x, y, z, 3).is_finite(),
+ "Vector4 with three components infinite should not be finite.");
+ }
+ }
+ }
+
+ for (double x : infinite) {
+ for (double y : infinite) {
+ for (double z : infinite) {
+ for (double w : infinite) {
+ CHECK_FALSE_MESSAGE(
+ Vector4(x, y, z, w).is_finite(),
+ "Vector4 with four components infinite should not be finite.");
+ }
+ }
+ }
+ }
+}
+
} // namespace TestVector4
#endif // TEST_VECTOR4_H