Skip to content

Commit cfdf290

Browse files
committed
Use approximate test when comparing properties, thoroughly
1 parent 42b054a commit cfdf290

File tree

7 files changed

+70
-40
lines changed

7 files changed

+70
-40
lines changed

core/array.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ void Array::clear() {
8989
_p->array.clear();
9090
}
9191

92-
bool Array::deep_equal(const Array &p_array, int p_recursion_count) const {
92+
bool Array::deep_equal(const Array &p_array, int p_recursion_count, bool p_approximate) const {
9393
// Cheap checks
9494
ERR_FAIL_COND_V_MSG(p_recursion_count > MAX_RECURSION, true, "Max recursion reached");
9595
if (_p == p_array._p) {
@@ -105,7 +105,7 @@ bool Array::deep_equal(const Array &p_array, int p_recursion_count) const {
105105
// Heavy O(n) check
106106
p_recursion_count++;
107107
for (int i = 0; i < size; i++) {
108-
if (!a1[i].deep_equal(a2[i], p_recursion_count)) {
108+
if (!a1[i].deep_equal(a2[i], p_recursion_count, p_approximate)) {
109109
return false;
110110
}
111111
}

core/array.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ class Array {
5656
bool empty() const;
5757
void clear();
5858

59-
bool deep_equal(const Array &p_array, int p_recursion_count = 0) const;
59+
bool deep_equal(const Array &p_array, int p_recursion_count = 0, bool p_approximate = false) const;
6060
bool operator==(const Array &p_array) const;
6161

6262
uint32_t hash() const;

core/dictionary.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ bool Dictionary::erase(const Variant &p_key) {
162162
return _p->variant_map.erase(p_key);
163163
}
164164

165-
bool Dictionary::deep_equal(const Dictionary &p_dictionary, int p_recursion_count) const {
165+
bool Dictionary::deep_equal(const Dictionary &p_dictionary, int p_recursion_count, bool p_approximate) const {
166166
// Cheap checks
167167
ERR_FAIL_COND_V_MSG(p_recursion_count > MAX_RECURSION, 0, "Max recursion reached");
168168
if (_p == p_dictionary._p) {
@@ -178,8 +178,8 @@ bool Dictionary::deep_equal(const Dictionary &p_dictionary, int p_recursion_coun
178178
p_recursion_count++;
179179
while (this_E && other_E) {
180180
if (
181-
!this_E.key().deep_equal(other_E.key(), p_recursion_count) ||
182-
!this_E.value().deep_equal(other_E.value(), p_recursion_count)) {
181+
!this_E.key().deep_equal(other_E.key(), p_recursion_count, p_approximate) ||
182+
!this_E.value().deep_equal(other_E.value(), p_recursion_count, p_approximate)) {
183183
return false;
184184
}
185185

core/dictionary.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ class Dictionary {
7272

7373
bool erase(const Variant &p_key);
7474

75-
bool deep_equal(const Dictionary &p_dictionary, int p_recursion_count = 0) const;
75+
bool deep_equal(const Dictionary &p_dictionary, int p_recursion_count = 0, bool p_approximate = false) const;
7676
bool operator==(const Dictionary &p_dictionary) const;
7777
bool operator!=(const Dictionary &p_dictionary) const;
7878

core/variant.cpp

Lines changed: 57 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -601,35 +601,69 @@ bool Variant::can_convert_strict(Variant::Type p_type_from, Variant::Type p_type
601601
return false;
602602
}
603603

604-
bool Variant::deep_equal(const Variant &p_variant, int p_recursion_count) const {
605-
ERR_FAIL_COND_V_MSG(p_recursion_count > MAX_RECURSION, true, "Max recursion reached");
604+
template <typename T>
605+
static bool _equal_approx_recursive(const Variant &p_a, const Variant &p_b, int p_recursion_count, bool p_approximate) {
606+
if (p_a.get_type() != p_b.get_type()) {
607+
return false;
608+
}
606609

607-
// Containers must be handled with recursivity checks
608-
switch (type) {
609-
case Variant::Type::DICTIONARY: {
610-
if (p_variant.type != Variant::Type::DICTIONARY) {
611-
return false;
612-
}
610+
const T &a_as_t = p_a.operator T();
611+
const T &b_as_t = p_b.operator T();
613612

614-
const Dictionary v1_as_d = Dictionary(*this);
615-
const Dictionary v2_as_d = Dictionary(p_variant);
613+
return a_as_t.deep_equal(b_as_t, p_recursion_count + 1, p_approximate);
614+
}
616615

617-
return v1_as_d.deep_equal(v2_as_d, p_recursion_count + 1);
618-
} break;
619-
case Variant::Type::ARRAY: {
620-
if (p_variant.type != Variant::Type::ARRAY) {
621-
return false;
622-
}
616+
template <typename T>
617+
static bool _equal_approx_primitive(const Variant &p_a, const Variant &p_b) {
618+
if (p_a.get_type() != p_b.get_type()) {
619+
return false;
620+
}
623621

624-
const Array v1_as_a = Array(*this);
625-
const Array v2_as_a = Array(p_variant);
622+
const T &a_as_t = p_a.operator T();
623+
const T &b_as_t = p_b.operator T();
626624

627-
return v1_as_a.deep_equal(v2_as_a, p_recursion_count + 1);
628-
} break;
629-
default: {
630-
return *this == p_variant;
631-
} break;
625+
return a_as_t.is_equal_approx(b_as_t);
626+
}
627+
628+
bool Variant::deep_equal(const Variant &p_variant, int p_recursion_count, bool p_approximate) const {
629+
ERR_FAIL_COND_V_MSG(p_recursion_count > MAX_RECURSION, true, "Max recursion reached");
630+
631+
// Containers must be handled with recursivity checks
632+
if (type == ARRAY) {
633+
return _equal_approx_recursive<Array>(*this, p_variant, p_recursion_count, p_approximate);
634+
} else if (type == DICTIONARY) {
635+
return _equal_approx_recursive<Dictionary>(*this, p_variant, p_recursion_count, p_approximate);
636+
} else if (p_approximate) {
637+
switch (type) {
638+
case REAL:
639+
return Math::is_equal_approx((double)*this, (double)p_variant);
640+
case VECTOR2:
641+
return _equal_approx_primitive<Vector2>(*this, p_variant);
642+
case RECT2:
643+
return _equal_approx_primitive<Rect2>(*this, p_variant);
644+
case VECTOR3:
645+
return _equal_approx_primitive<Vector3>(*this, p_variant);
646+
case TRANSFORM2D:
647+
return _equal_approx_primitive<Transform2D>(*this, p_variant);
648+
case PLANE:
649+
return _equal_approx_primitive<Plane>(*this, p_variant);
650+
case QUAT:
651+
return _equal_approx_primitive<Quat>(*this, p_variant);
652+
case AABB:
653+
return _equal_approx_primitive<::AABB>(*this, p_variant);
654+
case BASIS:
655+
return _equal_approx_primitive<Basis>(*this, p_variant);
656+
case TRANSFORM:
657+
return _equal_approx_primitive<Transform>(*this, p_variant);
658+
case DICTIONARY:
659+
return _equal_approx_recursive<Dictionary>(*this, p_variant, p_recursion_count, p_approximate);
660+
case ARRAY:
661+
return _equal_approx_recursive<Array>(*this, p_variant, p_recursion_count, p_approximate);
662+
default: {
663+
}
664+
}
632665
}
666+
return *this == p_variant;
633667
}
634668

635669
bool Variant::operator==(const Variant &p_variant) const {

core/variant.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -408,7 +408,7 @@ class Variant {
408408

409409
//argsVariant call()
410410

411-
bool deep_equal(const Variant &p_variant, int p_recursion_count = 0) const;
411+
bool deep_equal(const Variant &p_variant, int p_recursion_count = 0, bool p_approximate = false) const;
412412
bool operator==(const Variant &p_variant) const;
413413
bool operator!=(const Variant &p_variant) const;
414414
bool operator<(const Variant &p_variant) const;

scene/property_utils.cpp

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,11 @@
3737
#include "scene/resources/packed_scene.h"
3838

3939
bool PropertyUtils::is_property_value_different(const Variant &p_a, const Variant &p_b) {
40-
if (p_a.get_type() == Variant::REAL && p_b.get_type() == Variant::REAL) {
41-
//this must be done because, as some scenes save as text, there might be a tiny difference in floats due to numerical error
42-
return !Math::is_equal_approx((float)p_a, (float)p_b);
43-
} else {
44-
// For our purposes, treating null object as NIL is the right thing to do
45-
const Variant &a = p_a.get_type() == Variant::OBJECT && (Object *)p_a == nullptr ? Variant() : p_a;
46-
const Variant &b = p_b.get_type() == Variant::OBJECT && (Object *)p_b == nullptr ? Variant() : p_b;
47-
return !a.deep_equal(b);
48-
}
40+
// For our purposes, treating null object as NIL is the right thing to do
41+
const Variant &a = p_a.get_type() == Variant::OBJECT && (Object *)p_a == nullptr ? Variant() : p_a;
42+
const Variant &b = p_b.get_type() == Variant::OBJECT && (Object *)p_b == nullptr ? Variant() : p_b;
43+
// Approximmation must be used because, as some scenes save as text, there might be a tiny difference in floats due to numerical error
44+
return !a.deep_equal(b, 0, true);
4945
}
5046

5147
Variant PropertyUtils::get_property_default_value(const Object *p_object, const StringName &p_property, bool *r_is_valid, const Vector<SceneState::PackState> *p_states_stack_cache, bool p_update_exports, const Node *p_owner, bool *r_is_class_default) {

0 commit comments

Comments
 (0)