[Core] Add `is_same` to types that have float components

Compares `NaN` as equal.

Added to:
* `AABB`
* `Basis`
* `Color`
* `Plane`
* `Projection`
* `Quaternion`
* `Rect2`
* `Transform2D`
* `Transform3D`
* `Vector2`
* `Vector3`
* `Vector4`

And added as a method in `Math`
This commit is contained in:
A Thousand Ships 2024-09-27 19:28:41 +02:00
parent 75cb3539b1
commit 86b9d7355f
No known key found for this signature in database
GPG Key ID: 2033189A662F8BD7
27 changed files with 99 additions and 87 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_same(const AABB &p_aabb) const {
return position.is_same(p_aabb.position) && size.is_same(p_aabb.size);
}
bool AABB::is_finite() const {
return position.is_finite() && size.is_finite();
}

View File

@ -63,6 +63,7 @@ struct [[nodiscard]] AABB {
bool operator!=(const AABB &p_rval) const;
bool is_equal_approx(const AABB &p_aabb) const;
bool is_same(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

View File

@ -694,6 +694,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_same(const Basis &p_basis) const {
return rows[0].is_same(p_basis.rows[0]) && rows[1].is_same(p_basis.rows[1]) && rows[2].is_same(p_basis.rows[2]);
}
bool Basis::is_finite() const {
return rows[0].is_finite() && rows[1].is_finite() && rows[2].is_finite();
}

View File

@ -121,6 +121,7 @@ struct [[nodiscard]] Basis {
}
bool is_equal_approx(const Basis &p_basis) const;
bool is_same(const Basis &p_basis) const;
bool is_finite() const;
bool operator==(const Basis &p_matrix) const;

View File

@ -259,6 +259,10 @@ bool Color::is_equal_approx(const Color &p_color) const {
return Math::is_equal_approx(r, p_color.r) && Math::is_equal_approx(g, p_color.g) && Math::is_equal_approx(b, p_color.b) && Math::is_equal_approx(a, p_color.a);
}
bool Color::is_same(const Color &p_color) const {
return Math::is_same(r, p_color.r) && Math::is_same(g, p_color.g) && Math::is_same(b, p_color.b) && Math::is_same(a, p_color.a);
}
Color Color::clamp(const Color &p_min, const Color &p_max) const {
return Color(
CLAMP(r, p_min.r, p_max.r),

View File

@ -94,6 +94,7 @@ struct [[nodiscard]] Color {
void operator/=(float p_scalar);
bool is_equal_approx(const Color &p_color) const;
bool is_same(const Color &p_color) const;
Color clamp(const Color &p_min = Color(0, 0, 0, 0), const Color &p_max = Color(1, 1, 1, 1)) const;
void invert();

View File

@ -594,6 +594,10 @@ public:
return abs(s) < (float)CMP_EPSILON;
}
static _ALWAYS_INLINE_ bool is_same(float a, float b) {
return (a == b) || (is_nan(a) && is_nan(b));
}
static _ALWAYS_INLINE_ bool is_equal_approx(double a, double b) {
// Check for exact equality first, required to handle "infinity" values.
if (a == b) {
@ -620,6 +624,10 @@ public:
return abs(s) < CMP_EPSILON;
}
static _ALWAYS_INLINE_ bool is_same(double a, double b) {
return (a == b) || (is_nan(a) && is_nan(b));
}
static _ALWAYS_INLINE_ float absf(float g) {
union {
float f;

View File

@ -172,6 +172,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_same(const Plane &p_plane) const {
return normal.is_same(p_plane.normal) && Math::is_same(d, p_plane.d);
}
bool Plane::is_finite() const {
return normal.is_finite() && Math::is_finite(d);
}

View File

@ -73,6 +73,7 @@ struct [[nodiscard]] Plane {
Plane operator-() const { return Plane(-normal, -d); }
bool is_equal_approx(const Plane &p_plane) const;
bool is_same(const Plane &p_plane) const;
bool is_equal_approx_any_side(const Plane &p_plane) const;
bool is_finite() const;

View File

@ -699,6 +699,10 @@ void Projection::flip_y() {
}
}
bool Projection::is_same(const Projection &p_cam) const {
return columns[0].is_same(p_cam.columns[0]) && columns[1].is_same(p_cam.columns[1]) && columns[2].is_same(p_cam.columns[2]) && columns[3].is_same(p_cam.columns[3]);
}
Projection::Projection() {
set_identity();
}

View File

@ -133,6 +133,8 @@ struct [[nodiscard]] Projection {
void flip_y();
bool is_same(const Projection &p_cam) const;
bool operator==(const Projection &p_cam) const {
for (uint32_t i = 0; i < 4; i++) {
for (uint32_t j = 0; j < 4; j++) {

View File

@ -66,6 +66,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_same(const Quaternion &p_quaternion) const {
return Math::is_same(x, p_quaternion.x) && Math::is_same(y, p_quaternion.y) && Math::is_same(z, p_quaternion.z) && Math::is_same(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);
}

View File

@ -54,6 +54,7 @@ struct [[nodiscard]] Quaternion {
}
_FORCE_INLINE_ real_t length_squared() const;
bool is_equal_approx(const Quaternion &p_quaternion) const;
bool is_same(const Quaternion &p_quaternion) const;
bool is_finite() const;
real_t length() const;
void normalize();

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_same(const Rect2 &p_rect) const {
return position.is_same(p_rect.position) && size.is_same(p_rect.size);
}
bool Rect2::is_finite() const {
return position.is_finite() && size.is_finite();
}

View File

@ -203,6 +203,7 @@ struct [[nodiscard]] Rect2 {
}
bool is_equal_approx(const Rect2 &p_rect) const;
bool is_same(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; }

View File

@ -180,6 +180,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_same(const Transform2D &p_transform) const {
return columns[0].is_same(p_transform.columns[0]) && columns[1].is_same(p_transform.columns[1]) && columns[2].is_same(p_transform.columns[2]);
}
bool Transform2D::is_finite() const {
return columns[0].is_finite() && columns[1].is_finite() && columns[2].is_finite();
}

View File

@ -101,6 +101,7 @@ struct [[nodiscard]] Transform2D {
Transform2D orthonormalized() const;
bool is_conformal() const;
bool is_equal_approx(const Transform2D &p_transform) const;
bool is_same(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_same(const Transform3D &p_transform) const {
return basis.is_same(p_transform.basis) && origin.is_same(p_transform.origin);
}
bool Transform3D::is_finite() const {
return basis.is_finite() && origin.is_finite();
}

View File

@ -75,6 +75,7 @@ struct [[nodiscard]] Transform3D {
void orthogonalize();
Transform3D orthogonalized() const;
bool is_equal_approx(const Transform3D &p_transform) const;
bool is_same(const Transform3D &p_transform) const;
bool is_finite() const;
bool operator==(const Transform3D &p_transform) const;

View File

@ -194,6 +194,10 @@ bool Vector2::is_equal_approx(const Vector2 &p_v) const {
return Math::is_equal_approx(x, p_v.x) && Math::is_equal_approx(y, p_v.y);
}
bool Vector2::is_same(const Vector2 &p_v) const {
return Math::is_same(x, p_v.x) && Math::is_same(y, p_v.y);
}
bool Vector2::is_zero_approx() const {
return Math::is_zero_approx(x) && Math::is_zero_approx(y);
}

View File

@ -129,6 +129,7 @@ struct [[nodiscard]] Vector2 {
Vector2 reflect(const Vector2 &p_normal) const;
bool is_equal_approx(const Vector2 &p_v) const;
bool is_same(const Vector2 &p_v) const;
bool is_zero_approx() const;
bool is_finite() const;

View File

@ -156,6 +156,10 @@ bool Vector3::is_equal_approx(const Vector3 &p_v) const {
return Math::is_equal_approx(x, p_v.x) && Math::is_equal_approx(y, p_v.y) && Math::is_equal_approx(z, p_v.z);
}
bool Vector3::is_same(const Vector3 &p_v) const {
return Math::is_same(x, p_v.x) && Math::is_same(y, p_v.y) && Math::is_same(z, p_v.z);
}
bool Vector3::is_zero_approx() const {
return Math::is_zero_approx(x) && Math::is_zero_approx(y) && Math::is_zero_approx(z);
}

View File

@ -155,6 +155,7 @@ struct [[nodiscard]] Vector3 {
_FORCE_INLINE_ Vector3 reflect(const Vector3 &p_normal) const;
bool is_equal_approx(const Vector3 &p_v) const;
bool is_same(const Vector3 &p_v) const;
bool is_zero_approx() const;
bool is_finite() const;

View File

@ -62,6 +62,10 @@ bool Vector4::is_equal_approx(const Vector4 &p_vec4) const {
return Math::is_equal_approx(x, p_vec4.x) && Math::is_equal_approx(y, p_vec4.y) && Math::is_equal_approx(z, p_vec4.z) && Math::is_equal_approx(w, p_vec4.w);
}
bool Vector4::is_same(const Vector4 &p_vec4) const {
return Math::is_same(x, p_vec4.x) && Math::is_same(y, p_vec4.y) && Math::is_same(z, p_vec4.z) && Math::is_same(w, p_vec4.w);
}
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);
}

View File

@ -89,6 +89,7 @@ struct [[nodiscard]] Vector4 {
_FORCE_INLINE_ real_t length_squared() const;
bool is_equal_approx(const Vector4 &p_vec4) const;
bool is_zero_approx() const;
bool is_same(const Vector4 &p_vec4) const;
bool is_finite() const;
real_t length() const;
void normalize();

View File

@ -409,116 +409,98 @@ struct HashMapComparatorDefault {
template <>
struct HashMapComparatorDefault<float> {
static bool compare(const float &p_lhs, const float &p_rhs) {
return (p_lhs == p_rhs) || (Math::is_nan(p_lhs) && Math::is_nan(p_rhs));
return Math::is_same(p_lhs, p_rhs);
}
};
template <>
struct HashMapComparatorDefault<double> {
static bool compare(const double &p_lhs, const double &p_rhs) {
return (p_lhs == p_rhs) || (Math::is_nan(p_lhs) && Math::is_nan(p_rhs));
return Math::is_same(p_lhs, p_rhs);
}
};
template <>
struct HashMapComparatorDefault<Color> {
static bool compare(const Color &p_lhs, const Color &p_rhs) {
return ((p_lhs.r == p_rhs.r) || (Math::is_nan(p_lhs.r) && Math::is_nan(p_rhs.r))) && ((p_lhs.g == p_rhs.g) || (Math::is_nan(p_lhs.g) && Math::is_nan(p_rhs.g))) && ((p_lhs.b == p_rhs.b) || (Math::is_nan(p_lhs.b) && Math::is_nan(p_rhs.b))) && ((p_lhs.a == p_rhs.a) || (Math::is_nan(p_lhs.a) && Math::is_nan(p_rhs.a)));
return p_lhs.is_same(p_rhs);
}
};
template <>
struct HashMapComparatorDefault<Vector2> {
static bool compare(const Vector2 &p_lhs, const Vector2 &p_rhs) {
return ((p_lhs.x == p_rhs.x) || (Math::is_nan(p_lhs.x) && Math::is_nan(p_rhs.x))) && ((p_lhs.y == p_rhs.y) || (Math::is_nan(p_lhs.y) && Math::is_nan(p_rhs.y)));
return p_lhs.is_same(p_rhs);
}
};
template <>
struct HashMapComparatorDefault<Vector3> {
static bool compare(const Vector3 &p_lhs, const Vector3 &p_rhs) {
return ((p_lhs.x == p_rhs.x) || (Math::is_nan(p_lhs.x) && Math::is_nan(p_rhs.x))) && ((p_lhs.y == p_rhs.y) || (Math::is_nan(p_lhs.y) && Math::is_nan(p_rhs.y))) && ((p_lhs.z == p_rhs.z) || (Math::is_nan(p_lhs.z) && Math::is_nan(p_rhs.z)));
return p_lhs.is_same(p_rhs);
}
};
template <>
struct HashMapComparatorDefault<Vector4> {
static bool compare(const Vector4 &p_lhs, const Vector4 &p_rhs) {
return ((p_lhs.x == p_rhs.x) || (Math::is_nan(p_lhs.x) && Math::is_nan(p_rhs.x))) && ((p_lhs.y == p_rhs.y) || (Math::is_nan(p_lhs.y) && Math::is_nan(p_rhs.y))) && ((p_lhs.z == p_rhs.z) || (Math::is_nan(p_lhs.z) && Math::is_nan(p_rhs.z))) && ((p_lhs.w == p_rhs.w) || (Math::is_nan(p_lhs.w) && Math::is_nan(p_rhs.w)));
return p_lhs.is_same(p_rhs);
}
};
template <>
struct HashMapComparatorDefault<Rect2> {
static bool compare(const Rect2 &p_lhs, const Rect2 &p_rhs) {
return HashMapComparatorDefault<Vector2>().compare(p_lhs.position, p_rhs.position) && HashMapComparatorDefault<Vector2>().compare(p_lhs.size, p_rhs.size);
return p_lhs.is_same(p_rhs);
}
};
template <>
struct HashMapComparatorDefault<AABB> {
static bool compare(const AABB &p_lhs, const AABB &p_rhs) {
return HashMapComparatorDefault<Vector3>().compare(p_lhs.position, p_rhs.position) && HashMapComparatorDefault<Vector3>().compare(p_lhs.size, p_rhs.size);
return p_lhs.is_same(p_rhs);
}
};
template <>
struct HashMapComparatorDefault<Plane> {
static bool compare(const Plane &p_lhs, const Plane &p_rhs) {
return HashMapComparatorDefault<Vector3>().compare(p_lhs.normal, p_rhs.normal) && ((p_lhs.d == p_rhs.d) || (Math::is_nan(p_lhs.d) && Math::is_nan(p_rhs.d)));
return p_lhs.is_same(p_rhs);
}
};
template <>
struct HashMapComparatorDefault<Transform2D> {
static bool compare(const Transform2D &p_lhs, const Transform2D &p_rhs) {
for (int i = 0; i < 3; ++i) {
if (!HashMapComparatorDefault<Vector2>().compare(p_lhs.columns[i], p_rhs.columns[i])) {
return false;
}
}
return true;
return p_lhs.is_same(p_rhs);
}
};
template <>
struct HashMapComparatorDefault<Basis> {
static bool compare(const Basis &p_lhs, const Basis &p_rhs) {
for (int i = 0; i < 3; ++i) {
if (!HashMapComparatorDefault<Vector3>().compare(p_lhs.rows[i], p_rhs.rows[i])) {
return false;
}
}
return true;
return p_lhs.is_same(p_rhs);
}
};
template <>
struct HashMapComparatorDefault<Transform3D> {
static bool compare(const Transform3D &p_lhs, const Transform3D &p_rhs) {
return HashMapComparatorDefault<Basis>().compare(p_lhs.basis, p_rhs.basis) && HashMapComparatorDefault<Vector3>().compare(p_lhs.origin, p_rhs.origin);
return p_lhs.is_same(p_rhs);
}
};
template <>
struct HashMapComparatorDefault<Projection> {
static bool compare(const Projection &p_lhs, const Projection &p_rhs) {
for (int i = 0; i < 4; ++i) {
if (!HashMapComparatorDefault<Vector4>().compare(p_lhs.columns[i], p_rhs.columns[i])) {
return false;
}
}
return true;
return p_lhs.is_same(p_rhs);
}
};
template <>
struct HashMapComparatorDefault<Quaternion> {
static bool compare(const Quaternion &p_lhs, const Quaternion &p_rhs) {
return ((p_lhs.x == p_rhs.x) || (Math::is_nan(p_lhs.x) && Math::is_nan(p_rhs.x))) && ((p_lhs.y == p_rhs.y) || (Math::is_nan(p_lhs.y) && Math::is_nan(p_rhs.y))) && ((p_lhs.z == p_rhs.z) || (Math::is_nan(p_lhs.z) && Math::is_nan(p_rhs.z))) && ((p_lhs.w == p_rhs.w) || (Math::is_nan(p_lhs.w) && Math::is_nan(p_rhs.w)));
return p_lhs.is_same(p_rhs);
}
};

View File

@ -3255,31 +3255,19 @@ uint32_t Variant::recursive_hash(int recursion_count) const {
(hash_compare_scalar_base(p_lhs, p_rhs, true))
#define hash_compare_vector2(p_lhs, p_rhs) \
(hash_compare_scalar((p_lhs).x, (p_rhs).x) && \
hash_compare_scalar((p_lhs).y, (p_rhs).y))
(p_lhs).is_same(p_rhs)
#define hash_compare_vector3(p_lhs, p_rhs) \
(hash_compare_scalar((p_lhs).x, (p_rhs).x) && \
hash_compare_scalar((p_lhs).y, (p_rhs).y) && \
hash_compare_scalar((p_lhs).z, (p_rhs).z))
(p_lhs).is_same(p_rhs)
#define hash_compare_vector4(p_lhs, p_rhs) \
(hash_compare_scalar((p_lhs).x, (p_rhs).x) && \
hash_compare_scalar((p_lhs).y, (p_rhs).y) && \
hash_compare_scalar((p_lhs).z, (p_rhs).z) && \
hash_compare_scalar((p_lhs).w, (p_rhs).w))
(p_lhs).is_same(p_rhs)
#define hash_compare_quaternion(p_lhs, p_rhs) \
(hash_compare_scalar((p_lhs).x, (p_rhs).x) && \
hash_compare_scalar((p_lhs).y, (p_rhs).y) && \
hash_compare_scalar((p_lhs).z, (p_rhs).z) && \
hash_compare_scalar((p_lhs).w, (p_rhs).w))
(p_lhs).is_same(p_rhs)
#define hash_compare_color(p_lhs, p_rhs) \
(hash_compare_scalar((p_lhs).r, (p_rhs).r) && \
hash_compare_scalar((p_lhs).g, (p_rhs).g) && \
hash_compare_scalar((p_lhs).b, (p_rhs).b) && \
hash_compare_scalar((p_lhs).a, (p_rhs).a))
(p_lhs).is_same(p_rhs)
#define hash_compare_packed_array(p_lhs, p_rhs, p_type, p_compare_func) \
const Vector<p_type> &l = PackedArrayRef<p_type>::get_array(p_lhs); \
@ -3350,13 +3338,7 @@ bool Variant::hash_compare(const Variant &p_variant, int recursion_count, bool s
Transform2D *l = _data._transform2d;
Transform2D *r = p_variant._data._transform2d;
for (int i = 0; i < 3; i++) {
if (!hash_compare_vector2(l->columns[i], r->columns[i])) {
return false;
}
}
return true;
return l->is_same(*r);
} break;
case VECTOR3: {
@ -3388,17 +3370,14 @@ bool Variant::hash_compare(const Variant &p_variant, int recursion_count, bool s
const Plane *l = reinterpret_cast<const Plane *>(_data._mem);
const Plane *r = reinterpret_cast<const Plane *>(p_variant._data._mem);
return hash_compare_vector3(l->normal, r->normal) &&
hash_compare_scalar(l->d, r->d);
return l->is_same(*r);
} break;
case AABB: {
const ::AABB *l = _data._aabb;
const ::AABB *r = p_variant._data._aabb;
return hash_compare_vector3(l->position, r->position) &&
hash_compare_vector3(l->size, r->size);
return l->is_same(*r);
} break;
case QUATERNION: {
@ -3412,38 +3391,20 @@ bool Variant::hash_compare(const Variant &p_variant, int recursion_count, bool s
const Basis *l = _data._basis;
const Basis *r = p_variant._data._basis;
for (int i = 0; i < 3; i++) {
if (!hash_compare_vector3(l->rows[i], r->rows[i])) {
return false;
}
}
return true;
return l->is_same(*r);
} break;
case TRANSFORM3D: {
const Transform3D *l = _data._transform3d;
const Transform3D *r = p_variant._data._transform3d;
for (int i = 0; i < 3; i++) {
if (!hash_compare_vector3(l->basis.rows[i], r->basis.rows[i])) {
return false;
}
}
return hash_compare_vector3(l->origin, r->origin);
return l->is_same(*r);
} break;
case PROJECTION: {
const Projection *l = _data._projection;
const Projection *r = p_variant._data._projection;
for (int i = 0; i < 4; i++) {
if (!hash_compare_vector4(l->columns[i], r->columns[i])) {
return false;
}
}
return true;
return l->is_same(*r);
} break;
case COLOR: {