Browse Source

Merge pull request #18992 from aaronfranke/mono-equal-approx

[Core] [Mono] Improve and use approximate equality methods
Rémi Verschelde 6 năm trước cách đây
mục cha
commit
18e88c8563
36 tập tin đã thay đổi với 198 bổ sung126 xóa
  1. 2 2
      core/math/delaunay.h
  2. 1 1
      core/math/geometry.cpp
  3. 5 5
      core/math/geometry.h
  4. 13 6
      core/math/math_funcs.h
  5. 2 2
      core/math/plane.cpp
  6. 2 2
      core/math/plane.h
  7. 4 4
      core/math/vector2.h
  8. 12 11
      core/math/vector3.h
  9. 20 0
      doc/classes/@GDScript.xml
  10. 1 1
      editor/editor_inspector.cpp
  11. 1 1
      editor/plugins/curve_editor_plugin.cpp
  12. 2 2
      editor/plugins/spatial_editor_plugin.cpp
  13. 5 5
      modules/csg/csg.cpp
  14. 23 0
      modules/gdscript/gdscript_functions.cpp
  15. 2 0
      modules/gdscript/gdscript_functions.h
  16. 8 8
      modules/mono/glue/Managed/Files/Color.cs
  17. 14 0
      modules/mono/glue/Managed/Files/Mathf.cs
  18. 2 2
      modules/mono/glue/Managed/Files/MathfEx.cs
  19. 1 1
      modules/mono/glue/Managed/Files/Plane.cs
  20. 1 1
      modules/mono/glue/Managed/Files/Quat.cs
  21. 15 11
      modules/mono/glue/Managed/Files/Vector2.cs
  22. 12 11
      modules/mono/glue/Managed/Files/Vector3.cs
  23. 6 6
      scene/2d/navigation_2d.cpp
  24. 4 4
      scene/3d/path.cpp
  25. 3 3
      scene/3d/voxel_light_baker.cpp
  26. 1 1
      scene/gui/graph_edit.cpp
  27. 1 1
      scene/main/scene_tree.cpp
  28. 19 19
      scene/resources/animation.cpp
  29. 1 1
      scene/resources/packed_scene.cpp
  30. 3 3
      servers/physics/collision_solver_sat.cpp
  31. 3 3
      servers/physics/joints/cone_twist_joint_sw.cpp
  32. 1 1
      servers/physics/joints/generic_6dof_joint_sw.cpp
  33. 3 3
      servers/physics/joints/hinge_joint_sw.cpp
  34. 2 2
      servers/physics_2d/collision_solver_2d_sat.cpp
  35. 1 1
      servers/visual/visual_server_canvas.h
  36. 2 2
      servers/visual/visual_server_scene.cpp

+ 2 - 2
core/math/delaunay.h

@@ -80,11 +80,11 @@ public:
 	}
 
 	static bool edge_compare(const Vector<Vector2> &p_vertices, const Edge &p_a, const Edge &p_b) {
-		if (p_vertices[p_a.edge[0]].distance_to(p_vertices[p_b.edge[0]]) < CMP_EPSILON && p_vertices[p_a.edge[1]].distance_to(p_vertices[p_b.edge[1]]) < CMP_EPSILON) {
+		if (Math::is_zero_approx(p_vertices[p_a.edge[0]].distance_to(p_vertices[p_b.edge[0]])) && Math::is_zero_approx(p_vertices[p_a.edge[1]].distance_to(p_vertices[p_b.edge[1]]))) {
 			return true;
 		}
 
-		if (p_vertices[p_a.edge[0]].distance_to(p_vertices[p_b.edge[1]]) < CMP_EPSILON && p_vertices[p_a.edge[1]].distance_to(p_vertices[p_b.edge[0]]) < CMP_EPSILON) {
+		if (Math::is_zero_approx(p_vertices[p_a.edge[0]].distance_to(p_vertices[p_b.edge[1]])) && Math::is_zero_approx(p_vertices[p_a.edge[1]].distance_to(p_vertices[p_b.edge[0]]))) {
 			return true;
 		}
 

+ 1 - 1
core/math/geometry.cpp

@@ -836,7 +836,7 @@ Geometry::MeshData Geometry::build_convex_mesh(const PoolVector<Plane> &p_planes
 					Vector3 rel = edge1_A - edge0_A;
 
 					real_t den = clip.normal.dot(rel);
-					if (Math::abs(den) < CMP_EPSILON)
+					if (Math::is_zero_approx(den))
 						continue; // point too short
 
 					real_t dist = -(clip.normal.dot(edge0_A) - clip.d) / den;

+ 5 - 5
core/math/geometry.h

@@ -181,8 +181,8 @@ public:
 			}
 		}
 		// finally do the division to get sc and tc
-		sc = (Math::abs(sN) < CMP_EPSILON ? 0.0 : sN / sD);
-		tc = (Math::abs(tN) < CMP_EPSILON ? 0.0 : tN / tD);
+		sc = (Math::is_zero_approx(sN) ? 0.0 : sN / sD);
+		tc = (Math::is_zero_approx(tN) ? 0.0 : tN / tD);
 
 		// get the difference of the two closest points
 		Vector3 dP = w + (sc * u) - (tc * v); // = S1(sc) - S2(tc)
@@ -195,7 +195,7 @@ public:
 		Vector3 e2 = p_v2 - p_v0;
 		Vector3 h = p_dir.cross(e2);
 		real_t a = e1.dot(h);
-		if (a > -CMP_EPSILON && a < CMP_EPSILON) // parallel test
+		if (Math::is_zero_approx(a)) // parallel test
 			return false;
 
 		real_t f = 1.0 / a;
@@ -233,7 +233,7 @@ public:
 		Vector3 e2 = p_v2 - p_v0;
 		Vector3 h = rel.cross(e2);
 		real_t a = e1.dot(h);
-		if (a > -CMP_EPSILON && a < CMP_EPSILON) // parallel test
+		if (Math::is_zero_approx(a)) // parallel test
 			return false;
 
 		real_t f = 1.0 / a;
@@ -535,7 +535,7 @@ public:
 		// see http://paulbourke.net/geometry/pointlineplane/
 
 		const real_t denom = p_dir_b.y * p_dir_a.x - p_dir_b.x * p_dir_a.y;
-		if (Math::abs(denom) < CMP_EPSILON) { // parallel?
+		if (Math::is_zero_approx(denom)) { // parallel?
 			return false;
 		}
 

+ 13 - 6
core/math/math_funcs.h

@@ -272,13 +272,20 @@ public:
 		return diff < epsilon;
 	}
 
-	static _ALWAYS_INLINE_ bool is_equal_approx(real_t a, real_t b, real_t epsilon = CMP_EPSILON) {
-		// TODO: Comparing floats for approximate-equality is non-trivial.
-		// Using epsilon should cover the typical cases in Godot (where a == b is used to compare two reals), such as matrix and vector comparison operators.
-		// A proper implementation in terms of ULPs should eventually replace the contents of this function.
-		// See https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ for details.
+	static _ALWAYS_INLINE_ bool is_equal_approx(real_t a, real_t b) {
+		real_t tolerance = CMP_EPSILON * abs(a);
+		if (tolerance < CMP_EPSILON) {
+			tolerance = CMP_EPSILON;
+		}
+		return abs(a - b) < tolerance;
+	}
+
+	static _ALWAYS_INLINE_ bool is_equal_approx(real_t a, real_t b, real_t tolerance) {
+		return abs(a - b) < tolerance;
+	}
 
-		return abs(a - b) < epsilon;
+	static _ALWAYS_INLINE_ bool is_zero_approx(real_t s) {
+		return abs(s) < CMP_EPSILON;
 	}
 
 	static _ALWAYS_INLINE_ float absf(float g) {

+ 2 - 2
core/math/plane.cpp

@@ -110,7 +110,7 @@ bool Plane::intersects_ray(const Vector3 &p_from, const Vector3 &p_dir, Vector3
 	real_t den = normal.dot(segment);
 
 	//printf("den is %i\n",den);
-	if (Math::abs(den) <= CMP_EPSILON) {
+	if (Math::is_zero_approx(den)) {
 
 		return false;
 	}
@@ -135,7 +135,7 @@ bool Plane::intersects_segment(const Vector3 &p_begin, const Vector3 &p_end, Vec
 	real_t den = normal.dot(segment);
 
 	//printf("den is %i\n",den);
-	if (Math::abs(den) <= CMP_EPSILON) {
+	if (Math::is_zero_approx(den)) {
 
 		return false;
 	}

+ 2 - 2
core/math/plane.h

@@ -125,12 +125,12 @@ Plane::Plane(const Vector3 &p_point1, const Vector3 &p_point2, const Vector3 &p_
 
 bool Plane::operator==(const Plane &p_plane) const {
 
-	return normal == p_plane.normal && d == p_plane.d;
+	return normal == p_plane.normal && Math::is_equal_approx(d, p_plane.d);
 }
 
 bool Plane::operator!=(const Plane &p_plane) const {
 
-	return normal != p_plane.normal || d != p_plane.d;
+	return normal != p_plane.normal || !Math::is_equal_approx(d, p_plane.d);
 }
 
 #endif // PLANE_H

+ 4 - 4
core/math/vector2.h

@@ -106,8 +106,8 @@ struct Vector2 {
 	bool operator==(const Vector2 &p_vec2) const;
 	bool operator!=(const Vector2 &p_vec2) const;
 
-	bool operator<(const Vector2 &p_vec2) const { return (x == p_vec2.x) ? (y < p_vec2.y) : (x < p_vec2.x); }
-	bool operator<=(const Vector2 &p_vec2) const { return (x == p_vec2.x) ? (y <= p_vec2.y) : (x <= p_vec2.x); }
+	bool operator<(const Vector2 &p_vec2) const { return (Math::is_equal_approx(x, p_vec2.x)) ? (y < p_vec2.y) : (x < p_vec2.x); }
+	bool operator<=(const Vector2 &p_vec2) const { return (Math::is_equal_approx(x, p_vec2.x)) ? (y <= p_vec2.y) : (x < p_vec2.x); }
 
 	real_t angle() const;
 
@@ -213,11 +213,11 @@ _FORCE_INLINE_ Vector2 Vector2::operator-() const {
 
 _FORCE_INLINE_ bool Vector2::operator==(const Vector2 &p_vec2) const {
 
-	return x == p_vec2.x && y == p_vec2.y;
+	return Math::is_equal_approx(x, p_vec2.x) && Math::is_equal_approx(y, p_vec2.y);
 }
 _FORCE_INLINE_ bool Vector2::operator!=(const Vector2 &p_vec2) const {
 
-	return x != p_vec2.x || y != p_vec2.y;
+	return !Math::is_equal_approx(x, p_vec2.x) || !Math::is_equal_approx(y, p_vec2.y);
 }
 
 Vector2 Vector2::linear_interpolate(const Vector2 &p_b, real_t p_t) const {

+ 12 - 11
core/math/vector3.h

@@ -341,17 +341,17 @@ Vector3 Vector3::operator-() const {
 
 bool Vector3::operator==(const Vector3 &p_v) const {
 
-	return (x == p_v.x && y == p_v.y && z == p_v.z);
+	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::operator!=(const Vector3 &p_v) const {
-	return (x != p_v.x || y != p_v.y || z != p_v.z);
+	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::operator<(const Vector3 &p_v) const {
 
-	if (x == p_v.x) {
-		if (y == p_v.y)
+	if (Math::is_equal_approx(x, p_v.x)) {
+		if (Math::is_equal_approx(y, p_v.y))
 			return z < p_v.z;
 		else
 			return y < p_v.y;
@@ -362,8 +362,8 @@ bool Vector3::operator<(const Vector3 &p_v) const {
 
 bool Vector3::operator<=(const Vector3 &p_v) const {
 
-	if (x == p_v.x) {
-		if (y == p_v.y)
+	if (Math::is_equal_approx(x, p_v.x)) {
+		if (Math::is_equal_approx(y, p_v.y))
 			return z <= p_v.z;
 		else
 			return y < p_v.y;
@@ -402,13 +402,14 @@ real_t Vector3::length_squared() const {
 
 void Vector3::normalize() {
 
-	real_t l = length();
-	if (l == 0) {
+	real_t lengthsq = length_squared();
+	if (lengthsq == 0) {
 		x = y = z = 0;
 	} else {
-		x /= l;
-		y /= l;
-		z /= l;
+		real_t length = Math::sqrt(lengthsq);
+		x /= length;
+		y /= length;
+		z /= length;
 	}
 }
 

+ 20 - 0
doc/classes/@GDScript.xml

@@ -498,6 +498,17 @@
 				[/codeblock]
 			</description>
 		</method>
+		<method name="is_equal_approx">
+			<return type="bool">
+			</return>
+			<argument index="0" name="a" type="float">
+			</argument>
+			<argument index="1" name="b" type="float">
+			</argument>
+			<description>
+				Returns True/False whether [code]a[/code] and [code]b[/code] are approximately equal to each other.
+			</description>
+		</method>
 		<method name="is_inf">
 			<return type="bool">
 			</return>
@@ -525,6 +536,15 @@
 				Returns whether [code]s[/code] is a NaN (Not-A-Number) value.
 			</description>
 		</method>
+		<method name="is_zero_approx">
+			<return type="bool">
+			</return>
+			<argument index="0" name="s" type="float">
+			</argument>
+			<description>
+				Returns True/False whether [code]s[/code] is zero or almost zero.
+			</description>
+		</method>
 		<method name="len">
 			<return type="int">
 			</return>

+ 1 - 1
editor/editor_inspector.cpp

@@ -434,7 +434,7 @@ bool EditorPropertyRevert::is_node_property_different(Node *p_node, const Varian
 		float a = p_current;
 		float b = p_orig;
 
-		return Math::abs(a - b) > CMP_EPSILON; //this must be done because, as some scenes save as text, there might be a tiny difference in floats due to numerical error
+		return !Math::is_equal_approx(a, b); //this must be done because, as some scenes save as text, there might be a tiny difference in floats due to numerical error
 	}
 
 	return bool(Variant::evaluate(Variant::OP_NOT_EQUAL, p_current, p_orig));

+ 1 - 1
editor/plugins/curve_editor_plugin.cpp

@@ -194,7 +194,7 @@ void CurveEditor::on_gui_input(const Ref<InputEvent> &p_event) {
 					Vector2 dir = (control_pos - point_pos).normalized();
 
 					real_t tangent;
-					if (Math::abs(dir.x) > CMP_EPSILON)
+					if (!Math::is_zero_approx(dir.x))
 						tangent = dir.y / dir.x;
 					else
 						tangent = 9999 * (dir.y >= 0 ? 1 : -1);

+ 2 - 2
editor/plugins/spatial_editor_plugin.cpp

@@ -2473,7 +2473,7 @@ void SpatialEditorViewport::_draw() {
 				real_t max_speed = camera->get_zfar();
 				real_t scale_length = (max_speed - min_speed);
 
-				if (Math::abs(scale_length) > CMP_EPSILON) {
+				if (!Math::is_zero_approx(scale_length)) {
 					real_t logscale_t = 1.0 - Math::log(1 + freelook_speed - min_speed) / Math::log(1 + scale_length);
 
 					// There is no real maximum speed so that factor can become negative,
@@ -2491,7 +2491,7 @@ void SpatialEditorViewport::_draw() {
 				real_t max_distance = camera->get_zfar();
 				real_t scale_length = (max_distance - min_distance);
 
-				if (Math::abs(scale_length) > CMP_EPSILON) {
+				if (!Math::is_zero_approx(scale_length)) {
 					real_t logscale_t = 1.0 - Math::log(1 + cursor.distance - min_distance) / Math::log(1 + scale_length);
 
 					// There is no real maximum distance so that factor can become negative,

+ 5 - 5
modules/csg/csg.cpp

@@ -242,7 +242,7 @@ void CSGBrushOperation::BuildPoly::_clip_segment(const CSGBrush *p_brush, int p_
 	//check if edge and poly share a vertex, of so, assign it to segment_idx
 	for (int i = 0; i < points.size(); i++) {
 		for (int j = 0; j < 2; j++) {
-			if (segment[j].distance_to(points[i].point) < CMP_EPSILON) {
+			if (Math::is_zero_approx(segment[j].distance_to(points[i].point))) {
 				segment_idx[j] = i;
 				inserted_points.push_back(i);
 				break;
@@ -310,7 +310,7 @@ void CSGBrushOperation::BuildPoly::_clip_segment(const CSGBrush *p_brush, int p_
 			Vector2 edgeseg[2] = { points[edges[i].points[0]].point, points[edges[i].points[1]].point };
 			Vector2 closest = Geometry::get_closest_point_to_segment_2d(segment[j], edgeseg);
 
-			if (closest.distance_to(segment[j]) < CMP_EPSILON) {
+			if (Math::is_zero_approx(closest.distance_to(segment[j]))) {
 				//point rest of this edge
 				res = closest;
 				found = true;
@@ -439,7 +439,7 @@ void CSGBrushOperation::BuildPoly::clip(const CSGBrush *p_brush, int p_face, Mes
 
 	//transform A points to 2D
 
-	if (segment[0].distance_to(segment[1]) < CMP_EPSILON)
+	if (Math::is_zero_approx(segment[0].distance_to(segment[1])))
 		return; //too small
 
 	_clip_segment(p_brush, p_face, segment, mesh_merge, p_for_B);
@@ -461,10 +461,10 @@ void CSGBrushOperation::_collision_callback(const CSGBrush *A, int p_face_a, Map
 
 	{
 		//check if either is a degenerate
-		if (va[0].distance_to(va[1]) < CMP_EPSILON || va[0].distance_to(va[2]) < CMP_EPSILON || va[1].distance_to(va[2]) < CMP_EPSILON)
+		if (Math::is_zero_approx(va[0].distance_to(va[1])) || Math::is_zero_approx(va[0].distance_to(va[2])) || Math::is_zero_approx(va[1].distance_to(va[2])))
 			return;
 
-		if (vb[0].distance_to(vb[1]) < CMP_EPSILON || vb[0].distance_to(vb[2]) < CMP_EPSILON || vb[1].distance_to(vb[2]) < CMP_EPSILON)
+		if (Math::is_zero_approx(vb[0].distance_to(vb[1])) || Math::is_zero_approx(vb[0].distance_to(vb[2])) || Math::is_zero_approx(vb[1].distance_to(vb[2])))
 			return;
 	}
 

+ 23 - 0
modules/gdscript/gdscript_functions.cpp

@@ -68,6 +68,8 @@ const char *GDScriptFunctions::get_func_name(Function p_func) {
 		"exp",
 		"is_nan",
 		"is_inf",
+		"is_equal_approx",
+		"is_zero_approx",
 		"ease",
 		"decimals",
 		"stepify",
@@ -316,6 +318,17 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_
 			VALIDATE_ARG_NUM(0);
 			r_ret = Math::is_inf((double)*p_args[0]);
 		} break;
+		case MATH_ISEQUALAPPROX: {
+			VALIDATE_ARG_COUNT(2);
+			VALIDATE_ARG_NUM(0);
+			VALIDATE_ARG_NUM(1);
+			r_ret = Math::is_equal_approx((real_t)*p_args[0], (real_t)*p_args[1]);
+		} break;
+		case MATH_ISZEROAPPROX: {
+			VALIDATE_ARG_COUNT(1);
+			VALIDATE_ARG_NUM(0);
+			r_ret = Math::is_zero_approx((real_t)*p_args[0]);
+		} break;
 		case MATH_EASE: {
 			VALIDATE_ARG_COUNT(2);
 			VALIDATE_ARG_NUM(0);
@@ -1596,6 +1609,16 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) {
 			mi.return_val.type = Variant::BOOL;
 			return mi;
 		} break;
+		case MATH_ISEQUALAPPROX: {
+			MethodInfo mi("is_equal_approx", PropertyInfo(Variant::REAL, "a"), PropertyInfo(Variant::REAL, "b"));
+			mi.return_val.type = Variant::BOOL;
+			return mi;
+		} break;
+		case MATH_ISZEROAPPROX: {
+			MethodInfo mi("is_zero_approx", PropertyInfo(Variant::REAL, "s"));
+			mi.return_val.type = Variant::BOOL;
+			return mi;
+		} break;
 		case MATH_EASE: {
 			MethodInfo mi("ease", PropertyInfo(Variant::REAL, "s"), PropertyInfo(Variant::REAL, "curve"));
 			mi.return_val.type = Variant::REAL;

+ 2 - 0
modules/gdscript/gdscript_functions.h

@@ -59,6 +59,8 @@ public:
 		MATH_EXP,
 		MATH_ISNAN,
 		MATH_ISINF,
+		MATH_ISEQUALAPPROX,
+		MATH_ISZEROAPPROX,
 		MATH_EASE,
 		MATH_DECIMALS,
 		MATH_STEPIFY,

+ 8 - 8
modules/mono/glue/Managed/Files/Color.cs

@@ -168,7 +168,7 @@ namespace Godot
             int max = Mathf.Max(color.r8, Mathf.Max(color.g8, color.b8));
             int min = Mathf.Min(color.r8, Mathf.Min(color.g8, color.b8));
 
-            float delta = max - min;
+            int delta = max - min;
 
             if (delta == 0)
             {
@@ -591,11 +591,11 @@ namespace Godot
 
         public static bool operator <(Color left, Color right)
         {
-            if (left.r == right.r)
+            if (Mathf.IsEqualApprox(left.r, right.r))
             {
-                if (left.g == right.g)
+                if (Mathf.IsEqualApprox(left.g, right.g))
                 {
-                    if (left.b == right.b)
+                    if (Mathf.IsEqualApprox(left.b, right.b))
                         return left.a < right.a;
                     return left.b < right.b;
                 }
@@ -608,11 +608,11 @@ namespace Godot
 
         public static bool operator >(Color left, Color right)
         {
-            if (left.r == right.r)
+            if (Mathf.IsEqualApprox(left.r, right.r))
             {
-                if (left.g == right.g)
+                if (Mathf.IsEqualApprox(left.g, right.g))
                 {
-                    if (left.b == right.b)
+                    if (Mathf.IsEqualApprox(left.b, right.b))
                         return left.a > right.a;
                     return left.b > right.b;
                 }
@@ -635,7 +635,7 @@ namespace Godot
 
         public bool Equals(Color other)
         {
-            return r == other.r && g == other.g && b == other.b && a == other.a;
+            return Mathf.IsEqualApprox(r, other.r) && Mathf.IsEqualApprox(g, other.g) && Mathf.IsEqualApprox(b, other.b) && Mathf.IsEqualApprox(a, other.a);
         }
 
         public override int GetHashCode()

+ 14 - 0
modules/mono/glue/Managed/Files/Mathf.cs

@@ -143,6 +143,15 @@ namespace Godot
            return (weight - from) / (to - from);
         }
 
+        public static bool IsEqualApprox(real_t a, real_t b)
+        {
+            real_t tolerance = Epsilon * Abs(a);
+            if (tolerance < Epsilon) {
+                tolerance = Epsilon;
+            }
+            return Abs(a - b) < tolerance;
+        }
+
         public static bool IsInf(real_t s)
         {
            return real_t.IsInfinity(s);
@@ -153,6 +162,11 @@ namespace Godot
            return real_t.IsNaN(s);
         }
 
+        public static bool IsZeroApprox(real_t s)
+        {
+            return Abs(s) < Epsilon;
+        }
+
         public static real_t Lerp(real_t from, real_t to, real_t weight)
         {
             return from + (to - from) * weight;

+ 2 - 2
modules/mono/glue/Managed/Files/MathfEx.cs

@@ -36,9 +36,9 @@ namespace Godot
             return (int)Math.Round(s);
         }
 
-        public static bool IsEqualApprox(real_t a, real_t b, real_t ratio = Mathf.Epsilon)
+        public static bool IsEqualApprox(real_t a, real_t b, real_t tolerance)
         {
-            return Abs(a - b) < ratio;
+            return Abs(a - b) < tolerance;
         }
     }
 }

+ 1 - 1
modules/mono/glue/Managed/Files/Plane.cs

@@ -200,7 +200,7 @@ namespace Godot
 
         public bool Equals(Plane other)
         {
-            return _normal == other._normal && D == other.D;
+            return _normal == other._normal && Mathf.IsEqualApprox(D, other.D);
         }
 
         public override int GetHashCode()

+ 1 - 1
modules/mono/glue/Managed/Files/Quat.cs

@@ -358,7 +358,7 @@ namespace Godot
 
         public bool Equals(Quat other)
         {
-            return x == other.x && y == other.y && z == other.z && w == other.w;
+            return Mathf.IsEqualApprox(x, other.x) && Mathf.IsEqualApprox(y, other.y) && Mathf.IsEqualApprox(z, other.z) && Mathf.IsEqualApprox(w, other.w);
         }
 
         public override int GetHashCode()

+ 15 - 11
modules/mono/glue/Managed/Files/Vector2.cs

@@ -52,11 +52,15 @@ namespace Godot
 
         internal void Normalize()
         {
-            real_t length = x * x + y * y;
+            real_t lengthsq = LengthSquared();
 
-            if (length != 0f)
+            if (lengthsq == 0)
             {
-                length = Mathf.Sqrt(length);
+                x = y = 0f;
+            }
+            else
+            {
+                real_t length = Mathf.Sqrt(lengthsq);
                 x /= length;
                 y /= length;
             }
@@ -184,9 +188,9 @@ namespace Godot
 
         public Vector2 Normalized()
         {
-            var result = this;
-            result.Normalize();
-            return result;
+            var v = this;
+            v.Normalize();
+            return v;
         }
 
         public Vector2 Project(Vector2 onNormal)
@@ -343,7 +347,7 @@ namespace Godot
 
         public static bool operator <(Vector2 left, Vector2 right)
         {
-            if (left.x.Equals(right.x))
+            if (Mathf.IsEqualApprox(left.x, right.x))
             {
                 return left.y < right.y;
             }
@@ -353,7 +357,7 @@ namespace Godot
 
         public static bool operator >(Vector2 left, Vector2 right)
         {
-            if (left.x.Equals(right.x))
+            if (Mathf.IsEqualApprox(left.x, right.x))
             {
                 return left.y > right.y;
             }
@@ -363,7 +367,7 @@ namespace Godot
 
         public static bool operator <=(Vector2 left, Vector2 right)
         {
-            if (left.x.Equals(right.x))
+            if (Mathf.IsEqualApprox(left.x, right.x))
             {
                 return left.y <= right.y;
             }
@@ -373,7 +377,7 @@ namespace Godot
 
         public static bool operator >=(Vector2 left, Vector2 right)
         {
-            if (left.x.Equals(right.x))
+            if (Mathf.IsEqualApprox(left.x, right.x))
             {
                 return left.y >= right.y;
             }
@@ -393,7 +397,7 @@ namespace Godot
 
         public bool Equals(Vector2 other)
         {
-            return x == other.x && y == other.y;
+            return Mathf.IsEqualApprox(x, other.x) && Mathf.IsEqualApprox(y, other.y);
         }
 
         public override int GetHashCode()

+ 12 - 11
modules/mono/glue/Managed/Files/Vector3.cs

@@ -65,14 +65,15 @@ namespace Godot
 
         internal void Normalize()
         {
-            real_t length = Length();
+            real_t lengthsq = LengthSquared();
 
-            if (length == 0f)
+            if (lengthsq == 0)
             {
                 x = y = z = 0f;
             }
             else
             {
+                real_t length = Mathf.Sqrt(lengthsq);
                 x /= length;
                 y /= length;
                 z /= length;
@@ -397,9 +398,9 @@ namespace Godot
 
         public static bool operator <(Vector3 left, Vector3 right)
         {
-            if (left.x == right.x)
+            if (Mathf.IsEqualApprox(left.x, right.x))
             {
-                if (left.y == right.y)
+                if (Mathf.IsEqualApprox(left.y, right.y))
                     return left.z < right.z;
                 return left.y < right.y;
             }
@@ -409,9 +410,9 @@ namespace Godot
 
         public static bool operator >(Vector3 left, Vector3 right)
         {
-            if (left.x == right.x)
+            if (Mathf.IsEqualApprox(left.x, right.x))
             {
-                if (left.y == right.y)
+                if (Mathf.IsEqualApprox(left.y, right.y))
                     return left.z > right.z;
                 return left.y > right.y;
             }
@@ -421,9 +422,9 @@ namespace Godot
 
         public static bool operator <=(Vector3 left, Vector3 right)
         {
-            if (left.x == right.x)
+            if (Mathf.IsEqualApprox(left.x, right.x))
             {
-                if (left.y == right.y)
+                if (Mathf.IsEqualApprox(left.y, right.y))
                     return left.z <= right.z;
                 return left.y < right.y;
             }
@@ -433,9 +434,9 @@ namespace Godot
 
         public static bool operator >=(Vector3 left, Vector3 right)
         {
-            if (left.x == right.x)
+            if (Mathf.IsEqualApprox(left.x, right.x))
             {
-                if (left.y == right.y)
+                if (Mathf.IsEqualApprox(left.y, right.y))
                     return left.z >= right.z;
                 return left.y > right.y;
             }
@@ -455,7 +456,7 @@ namespace Godot
 
         public bool Equals(Vector3 other)
         {
-            return x == other.x && y == other.y && z == other.z;
+            return Mathf.IsEqualApprox(x, other.x) && Mathf.IsEqualApprox(y, other.y) && Mathf.IsEqualApprox(z, other.z);
         }
 
         public override int GetHashCode()

+ 6 - 6
scene/2d/navigation_2d.cpp

@@ -542,7 +542,7 @@ Vector<Vector2> Navigation2D::get_simple_path(const Vector2 &p_start, const Vect
 
 				if (CLOCK_TANGENT(apex_point, portal_left, left) >= 0) {
 					//process
-					if (portal_left.distance_squared_to(apex_point) < CMP_EPSILON || CLOCK_TANGENT(apex_point, left, portal_right) > 0) {
+					if (Math::is_zero_approx(portal_left.distance_squared_to(apex_point)) || CLOCK_TANGENT(apex_point, left, portal_right) > 0) {
 						left_poly = p;
 						portal_left = left;
 					} else {
@@ -552,7 +552,7 @@ Vector<Vector2> Navigation2D::get_simple_path(const Vector2 &p_start, const Vect
 						left_poly = p;
 						portal_left = apex_point;
 						portal_right = apex_point;
-						if (!path.size() || path[path.size() - 1].distance_to(apex_point) > CMP_EPSILON)
+						if (!path.size() || !Math::is_zero_approx(path[path.size() - 1].distance_to(apex_point)))
 							path.push_back(apex_point);
 						skip = true;
 					}
@@ -560,7 +560,7 @@ Vector<Vector2> Navigation2D::get_simple_path(const Vector2 &p_start, const Vect
 
 				if (!skip && CLOCK_TANGENT(apex_point, portal_right, right) <= 0) {
 					//process
-					if (portal_right.distance_squared_to(apex_point) < CMP_EPSILON || CLOCK_TANGENT(apex_point, right, portal_left) < 0) {
+					if (Math::is_zero_approx(portal_right.distance_squared_to(apex_point)) || CLOCK_TANGENT(apex_point, right, portal_left) < 0) {
 						right_poly = p;
 						portal_right = right;
 					} else {
@@ -570,7 +570,7 @@ Vector<Vector2> Navigation2D::get_simple_path(const Vector2 &p_start, const Vect
 						right_poly = p;
 						portal_right = apex_point;
 						portal_left = apex_point;
-						if (!path.size() || path[path.size() - 1].distance_to(apex_point) > CMP_EPSILON)
+						if (!path.size() || !Math::is_zero_approx(path[path.size() - 1].distance_to(apex_point)))
 							path.push_back(apex_point);
 					}
 				}
@@ -596,7 +596,7 @@ Vector<Vector2> Navigation2D::get_simple_path(const Vector2 &p_start, const Vect
 			}
 		}
 
-		if (!path.size() || path[path.size() - 1].distance_squared_to(begin_point) > CMP_EPSILON) {
+		if (!path.size() || !Math::is_zero_approx(path[path.size() - 1].distance_squared_to(begin_point))) {
 			path.push_back(begin_point); // Add the begin point
 		} else {
 			path.write[path.size() - 1] = begin_point; // Replace first midpoint by the exact begin point
@@ -604,7 +604,7 @@ Vector<Vector2> Navigation2D::get_simple_path(const Vector2 &p_start, const Vect
 
 		path.invert();
 
-		if (path.size() <= 1 || path[path.size() - 1].distance_squared_to(end_point) > CMP_EPSILON) {
+		if (path.size() <= 1 || !Math::is_zero_approx(path[path.size() - 1].distance_squared_to(end_point))) {
 			path.push_back(end_point); // Add the end point
 		} else {
 			path.write[path.size() - 1] = end_point; // Replace last midpoint by the exact end point

+ 4 - 4
scene/3d/path.cpp

@@ -173,7 +173,7 @@ void PathFollow::_update_transform() {
 		float dot = t_prev.dot(t_cur);
 		float angle = Math::acos(CLAMP(dot, -1, 1));
 
-		if (likely(Math::abs(angle) > CMP_EPSILON)) {
+		if (likely(!Math::is_zero_approx(angle))) {
 			if (rotation_mode == ROTATION_Y) {
 				// assuming we're referring to global Y-axis. is this correct?
 				axis.x = 0;
@@ -184,7 +184,7 @@ void PathFollow::_update_transform() {
 				// all components are allowed
 			}
 
-			if (likely(axis.length() > CMP_EPSILON)) {
+			if (likely(!Math::is_zero_approx(axis.length()))) {
 				t.rotate_basis(axis.normalized(), angle);
 			}
 		}
@@ -193,7 +193,7 @@ void PathFollow::_update_transform() {
 		float tilt_angle = c->interpolate_baked_tilt(o);
 		Vector3 tilt_axis = t_cur; // not sure what tilt is supposed to do, is this correct??
 
-		if (likely(Math::abs(tilt_angle) > CMP_EPSILON)) {
+		if (likely(!Math::is_zero_approx(Math::abs(tilt_angle)))) {
 			if (rotation_mode == ROTATION_Y) {
 				tilt_axis.x = 0;
 				tilt_axis.z = 0;
@@ -203,7 +203,7 @@ void PathFollow::_update_transform() {
 				// all components are allowed
 			}
 
-			if (likely(tilt_axis.length() > CMP_EPSILON)) {
+			if (likely(!Math::is_zero_approx(tilt_axis.length()))) {
 				t.rotate_basis(tilt_axis.normalized(), tilt_angle);
 			}
 		}

+ 3 - 3
scene/3d/voxel_light_baker.cpp

@@ -835,7 +835,7 @@ void VoxelLightBaker::plot_light_directional(const Vector3 &p_direction, const C
 
 	for (int i = 0; i < 3; i++) {
 
-		if (ABS(light_axis[i]) < CMP_EPSILON)
+		if (Math::is_zero_approx(light_axis[i]))
 			continue;
 		clip[clip_planes].normal[i] = 1.0;
 
@@ -978,7 +978,7 @@ void VoxelLightBaker::plot_light_omni(const Vector3 &p_pos, const Color &p_color
 
 		for (int c = 0; c < 3; c++) {
 
-			if (ABS(light_axis[c]) < CMP_EPSILON)
+			if (Math::is_zero_approx(light_axis[c]))
 				continue;
 			clip[clip_planes].normal[c] = 1.0;
 
@@ -1113,7 +1113,7 @@ void VoxelLightBaker::plot_light_spot(const Vector3 &p_pos, const Vector3 &p_axi
 
 		for (int c = 0; c < 3; c++) {
 
-			if (ABS(light_axis[c]) < CMP_EPSILON)
+			if (Math::is_zero_approx(light_axis[c]))
 				continue;
 			clip[clip_planes].normal[c] = 1.0;
 

+ 1 - 1
scene/gui/graph_edit.cpp

@@ -1053,7 +1053,7 @@ void GraphEdit::set_connection_activity(const StringName &p_from, int p_from_por
 
 		if (E->get().from == p_from && E->get().from_port == p_from_port && E->get().to == p_to && E->get().to_port == p_to_port) {
 
-			if (ABS(E->get().activity - p_activity) < CMP_EPSILON) {
+			if (Math::is_equal_approx(E->get().activity, p_activity)) {
 				//update only if changed
 				top_layer->update();
 				connections_layer->update();

+ 1 - 1
scene/main/scene_tree.cpp

@@ -1160,7 +1160,7 @@ void SceneTree::_update_root_rect() {
 		WARN_PRINT("Font oversampling only works with the resize modes 'Keep Width', 'Keep Height', and 'Expand'.");
 	}
 
-	if (stretch_aspect == STRETCH_ASPECT_IGNORE || ABS(viewport_aspect - video_mode_aspect) < CMP_EPSILON) {
+	if (stretch_aspect == STRETCH_ASPECT_IGNORE || Math::is_equal_approx(viewport_aspect, video_mode_aspect)) {
 		//same aspect or ignore aspect
 		viewport_size = desired_res;
 		screen_size = video_mode;

+ 19 - 19
scene/resources/animation.cpp

@@ -1477,7 +1477,7 @@ int Animation::_find(const Vector<K> &p_keys, float p_time) const {
 
 		middle = (low + high) / 2;
 
-		if (Math::abs(p_time - keys[middle].time) < CMP_EPSILON) { //match
+		if (Math::is_equal_approx(p_time, keys[middle].time)) { //match
 			return middle;
 		} else if (p_time < keys[middle].time)
 			high = middle - 1; //search low end of array
@@ -1680,10 +1680,10 @@ T Animation::_interpolate(const Vector<TKey<T> > &p_keys, float p_time, Interpol
 				float delta = p_keys[next].time - p_keys[idx].time;
 				float from = p_time - p_keys[idx].time;
 
-				if (Math::absf(delta) > CMP_EPSILON)
-					c = from / delta;
-				else
+				if (Math::is_zero_approx(delta))
 					c = 0;
+				else
+					c = from / delta;
 
 			} else {
 
@@ -1691,10 +1691,10 @@ T Animation::_interpolate(const Vector<TKey<T> > &p_keys, float p_time, Interpol
 				float delta = (length - p_keys[idx].time) + p_keys[next].time;
 				float from = p_time - p_keys[idx].time;
 
-				if (Math::absf(delta) > CMP_EPSILON)
-					c = from / delta;
-				else
+				if (Math::is_zero_approx(delta))
 					c = 0;
+				else
+					c = from / delta;
 			}
 
 		} else {
@@ -1707,10 +1707,10 @@ T Animation::_interpolate(const Vector<TKey<T> > &p_keys, float p_time, Interpol
 			float delta = endtime + p_keys[next].time;
 			float from = endtime + p_time;
 
-			if (Math::absf(delta) > CMP_EPSILON)
-				c = from / delta;
-			else
+			if (Math::is_zero_approx(delta))
 				c = 0;
+			else
+				c = from / delta;
 		}
 
 	} else { // no loop
@@ -1723,10 +1723,10 @@ T Animation::_interpolate(const Vector<TKey<T> > &p_keys, float p_time, Interpol
 				float delta = p_keys[next].time - p_keys[idx].time;
 				float from = p_time - p_keys[idx].time;
 
-				if (Math::absf(delta) > CMP_EPSILON)
-					c = from / delta;
-				else
+				if (Math::is_zero_approx(delta))
 					c = 0;
+				else
+					c = from / delta;
 
 			} else {
 
@@ -2774,9 +2774,9 @@ bool Animation::_transform_track_optimize_key(const TKey<TransformKey> &t0, cons
 		const Vector3 &v1 = t1.value.loc;
 		const Vector3 &v2 = t2.value.loc;
 
-		if (v0.distance_to(v2) < CMP_EPSILON) {
+		if (Math::is_zero_approx(v0.distance_to(v2))) {
 			//0 and 2 are close, let's see if 1 is close
-			if (v0.distance_to(v1) > CMP_EPSILON) {
+			if (!Math::is_zero_approx(v0.distance_to(v1))) {
 				//not close, not optimizable
 				return false;
 			}
@@ -2813,9 +2813,9 @@ bool Animation::_transform_track_optimize_key(const TKey<TransformKey> &t0, cons
 
 		//localize both to rotation from q0
 
-		if ((q0 - q2).length() < CMP_EPSILON) {
+		if (Math::is_zero_approx((q0 - q2).length())) {
 
-			if ((q0 - q1).length() > CMP_EPSILON)
+			if (!Math::is_zero_approx((q0 - q1).length()))
 				return false;
 
 		} else {
@@ -2863,9 +2863,9 @@ bool Animation::_transform_track_optimize_key(const TKey<TransformKey> &t0, cons
 		const Vector3 &v1 = t1.value.scale;
 		const Vector3 &v2 = t2.value.scale;
 
-		if (v0.distance_to(v2) < CMP_EPSILON) {
+		if (Math::is_zero_approx(v0.distance_to(v2))) {
 			//0 and 2 are close, let's see if 1 is close
-			if (v0.distance_to(v1) > CMP_EPSILON) {
+			if (!Math::is_zero_approx(v0.distance_to(v1))) {
 				//not close, not optimizable
 				return false;
 			}

+ 1 - 1
scene/resources/packed_scene.cpp

@@ -535,7 +535,7 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map
 					float a = value;
 					float b = original;
 
-					if (Math::abs(a - b) < CMP_EPSILON)
+					if (Math::is_equal_approx(a, b))
 						continue;
 				} else if (bool(Variant::evaluate(Variant::OP_EQUAL, value, original))) {
 

+ 3 - 3
servers/physics/collision_solver_sat.cpp

@@ -98,7 +98,7 @@ static void _generate_contacts_edge_edge(const Vector3 *p_points_A, int p_point_
 
 	Vector3 c = rel_A.cross(rel_B).cross(rel_B);
 
-	if (Math::abs(rel_A.dot(c)) < CMP_EPSILON) {
+	if (Math::is_zero_approx(rel_A.dot(c))) {
 
 		// should handle somehow..
 		//ERR_PRINT("TODO FIX");
@@ -678,7 +678,7 @@ static void _collision_box_box(const ShapeSW *p_a, const Transform &p_transform_
 
 			Vector3 axis = p_transform_a.basis.get_axis(i).cross(p_transform_b.basis.get_axis(j));
 
-			if (axis.length_squared() < CMP_EPSILON)
+			if (Math::is_zero_approx(axis.length_squared()))
 				continue;
 			axis.normalize();
 
@@ -767,7 +767,7 @@ static void _collision_box_capsule(const ShapeSW *p_a, const Transform &p_transf
 		// cylinder
 		Vector3 box_axis = p_transform_a.basis.get_axis(i);
 		Vector3 axis = box_axis.cross(cyl_axis);
-		if (axis.length_squared() < CMP_EPSILON)
+		if (Math::is_zero_approx(axis.length_squared()))
 			continue;
 
 		if (!separator.test_axis(axis.normalized()))

+ 3 - 3
servers/physics/joints/cone_twist_joint_sw.cpp

@@ -127,10 +127,10 @@ bool ConeTwistJointSW::setup(real_t p_timestep) {
 		Vector3 relPos = pivotBInW - pivotAInW;
 
 		Vector3 normal[3];
-		if (relPos.length_squared() > CMP_EPSILON) {
-			normal[0] = relPos.normalized();
-		} else {
+		if (Math::is_zero_approx(relPos.length_squared())) {
 			normal[0] = Vector3(real_t(1.0), 0, 0);
+		} else {
+			normal[0] = relPos.normalized();
 		}
 
 		plane_space(normal[0], normal[1], normal[2]);

+ 1 - 1
servers/physics/joints/generic_6dof_joint_sw.cpp

@@ -107,7 +107,7 @@ real_t G6DOFRotationalLimitMotorSW::solveAngularLimits(
 	// correction velocity
 	real_t motor_relvel = m_limitSoftness * (target_velocity - m_damping * rel_vel);
 
-	if (motor_relvel < CMP_EPSILON && motor_relvel > -CMP_EPSILON) {
+	if (Math::is_zero_approx(motor_relvel)) {
 		return 0.0f; //no need for applying force
 	}
 

+ 3 - 3
servers/physics/joints/hinge_joint_sw.cpp

@@ -167,10 +167,10 @@ bool HingeJointSW::setup(real_t p_step) {
 		Vector3 relPos = pivotBInW - pivotAInW;
 
 		Vector3 normal[3];
-		if (relPos.length_squared() > CMP_EPSILON) {
-			normal[0] = relPos.normalized();
-		} else {
+		if (Math::is_zero_approx(relPos.length_squared())) {
 			normal[0] = Vector3(real_t(1.0), 0, 0);
+		} else {
+			normal[0] = relPos.normalized();
 		}
 
 		plane_space(normal[0], normal[1], normal[2]);

+ 2 - 2
servers/physics_2d/collision_solver_2d_sat.cpp

@@ -237,8 +237,8 @@ public:
 
 		Vector2 axis = p_axis;
 
-		if (Math::abs(axis.x) < CMP_EPSILON &&
-				Math::abs(axis.y) < CMP_EPSILON) {
+		if (Math::is_zero_approx(axis.x) &&
+				Math::is_zero_approx(axis.y)) {
 			// strange case, try an upwards separator
 			axis = Vector2(0.0, 1.0);
 		}

+ 1 - 1
servers/visual/visual_server_canvas.h

@@ -82,7 +82,7 @@ public:
 
 		_FORCE_INLINE_ bool operator()(const Item *p_left, const Item *p_right) const {
 
-			if (Math::abs(p_left->ysort_pos.y - p_right->ysort_pos.y) < CMP_EPSILON)
+			if (Math::is_equal_approx(p_left->ysort_pos.y, p_right->ysort_pos.y))
 				return p_left->ysort_pos.x < p_right->ysort_pos.x;
 			else
 				return p_left->ysort_pos.y < p_right->ysort_pos.y;

+ 2 - 2
servers/visual/visual_server_scene.cpp

@@ -2654,7 +2654,7 @@ void VisualServerScene::_bake_gi_probe_light(const GIProbeDataHeader *header, co
 
 			for (int i = 0; i < 3; i++) {
 
-				if (ABS(light_axis[i]) < CMP_EPSILON)
+				if (Math::is_zero_approx(light_axis[i]))
 					continue;
 				clip[clip_planes].normal[i] = 1.0;
 
@@ -2789,7 +2789,7 @@ void VisualServerScene::_bake_gi_probe_light(const GIProbeDataHeader *header, co
 
 				for (int c = 0; c < 3; c++) {
 
-					if (ABS(light_axis[c]) < CMP_EPSILON)
+					if (Math::is_zero_approx(light_axis[c]))
 						continue;
 					clip[clip_planes].normal[c] = 1.0;