Merge pull request #64268 from timothyqiu/is-finite

Add `is_finite` method for checking built-in types
This commit is contained in:
Rémi Verschelde 2022-10-11 09:25:46 +02:00
commit 58eff50bf1
46 changed files with 504 additions and 12 deletions

View file

@ -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)) {

View file

@ -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

View file

@ -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++) {

View file

@ -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;

View file

@ -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; }

View file

@ -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) + "]";
}

View file

@ -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;

View file

@ -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());
}

View file

@ -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;

View file

@ -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)) {

View file

@ -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; }

View file

@ -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);

View file

@ -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;

View file

@ -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);
}

View file

@ -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;

View file

@ -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) + ")";
}

View file

@ -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);

View file

@ -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) + ")";
}

View file

@ -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 */

View file

@ -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());
}

View file

@ -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;

View file

@ -4654,10 +4654,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);
}
@ -4665,7 +4665,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 {
@ -4716,10 +4716,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);
}
@ -4727,7 +4727,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 {

View file

@ -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 */

View file

@ -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);

View file

@ -476,6 +476,13 @@
Infinity values of the same sign are considered equal.
</description>
</method>
<method name="is_finite">
<return type="bool" />
<param index="0" name="x" type="float" />
<description>
Returns whether [code]x[/code] is a finite value, i.e. it is not [constant @GDScript.NAN], positive infinity, or negative infinity.
</description>
</method>
<method name="is_inf">
<return type="bool" />
<param index="0" name="x" type="float" />

View file

@ -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.
</description>
</method>
<method name="is_finite" qualifiers="const">
<return type="bool" />
<description>
Returns [code]true[/code] if this [AABB] is finite, by calling [method @GlobalScope.is_finite] on each component.
</description>
</method>
<method name="merge" qualifiers="const">
<return type="AABB" />
<param index="0" name="with" type="AABB" />

View file

@ -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.
</description>
</method>
<method name="is_finite" qualifiers="const">
<return type="bool" />
<description>
Returns [code]true[/code] if this basis is finite, by calling [method @GlobalScope.is_finite] on each component.
</description>
</method>
<method name="looking_at" qualifiers="static">
<return type="Basis" />
<param index="0" name="target" type="Vector3" />

View file

@ -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.
</description>
</method>
<method name="is_finite" qualifiers="const">
<return type="bool" />
<description>
Returns [code]true[/code] if this plane is finite, by calling [method @GlobalScope.is_finite] on each component.
</description>
</method>
<method name="is_point_over" qualifiers="const">
<return type="bool" />
<param index="0" name="point" type="Vector3" />

View file

@ -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.
</description>
</method>
<method name="is_finite" qualifiers="const">
<return type="bool" />
<description>
Returns [code]true[/code] if this quaternion is finite, by calling [method @GlobalScope.is_finite] on each component.
</description>
</method>
<method name="is_normalized" qualifiers="const">
<return type="bool" />
<description>

View file

@ -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.
</description>
</method>
<method name="is_finite" qualifiers="const">
<return type="bool" />
<description>
Returns [code]true[/code] if this [Rect2] is finite, by calling [method @GlobalScope.is_finite] on each component.
</description>
</method>
<method name="merge" qualifiers="const">
<return type="Rect2" />
<param index="0" name="b" type="Rect2" />

View file

@ -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.
</description>
</method>
<method name="is_finite" qualifiers="const">
<return type="bool" />
<description>
Returns [code]true[/code] if this transform is finite, by calling [method @GlobalScope.is_finite] on each component.
</description>
</method>
<method name="looking_at" qualifiers="const">
<return type="Transform2D" />
<param index="0" name="target" type="Vector2" default="Vector2(0, 0)" />

View file

@ -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.
</description>
</method>
<method name="is_finite" qualifiers="const">
<return type="bool" />
<description>
Returns [code]true[/code] if this transform is finite, by calling [method @GlobalScope.is_finite] on each component.
</description>
</method>
<method name="looking_at" qualifiers="const">
<return type="Transform3D" />
<param index="0" name="target" type="Vector3" />

View file

@ -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.
</description>
</method>
<method name="is_finite" qualifiers="const">
<return type="bool" />
<description>
Returns [code]true[/code] if this vector is finite, by calling [method @GlobalScope.is_finite] on each component.
</description>
</method>
<method name="is_normalized" qualifiers="const">
<return type="bool" />
<description>

View file

@ -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.
</description>
</method>
<method name="is_finite" qualifiers="const">
<return type="bool" />
<description>
Returns [code]true[/code] if this vector is finite, by calling [method @GlobalScope.is_finite] on each component.
</description>
</method>
<method name="is_normalized" qualifiers="const">
<return type="bool" />
<description>

View file

@ -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.
</description>
</method>
<method name="is_finite" qualifiers="const">
<return type="bool" />
<description>
Returns [code]true[/code] if this vector is finite, by calling [method @GlobalScope.is_finite] on each component.
</description>
</method>
<method name="is_normalized" qualifiers="const">
<return type="bool" />
<description>

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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