From 164b42a9c8d174d4103da7324926c1649f27107d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Botero?= <0xafbf@gmail.com> Date: Sun, 29 Jan 2023 11:45:22 -0500 Subject: [PATCH] Added component-wise `min` and `max` functions for vectors --- core/math/vector3.h | 8 +++ core/math/vector3i.h | 8 +++ core/math/vector4.h | 8 +++ core/math/vector4i.h | 8 +++ core/variant/variant_utility.cpp | 92 +++++++++++++++++++++++++------- doc/classes/@GlobalScope.xml | 6 ++- tests/core/math/test_vector3.h | 8 +++ tests/core/math/test_vector3i.h | 7 +++ tests/core/math/test_vector4.h | 8 +++ tests/core/math/test_vector4i.h | 8 +++ 10 files changed, 141 insertions(+), 20 deletions(-) diff --git a/core/math/vector3.h b/core/math/vector3.h index bd8739d0240e..18943a820f2d 100644 --- a/core/math/vector3.h +++ b/core/math/vector3.h @@ -76,6 +76,14 @@ struct _NO_DISCARD_ Vector3 { return x < y ? (y < z ? Vector3::AXIS_Z : Vector3::AXIS_Y) : (x < z ? Vector3::AXIS_Z : Vector3::AXIS_X); } + Vector3 min(const Vector3 &p_vector3) const { + return Vector3(MIN(x, p_vector3.x), MIN(y, p_vector3.y), MIN(z, p_vector3.z)); + } + + Vector3 max(const Vector3 &p_vector3) const { + return Vector3(MAX(x, p_vector3.x), MAX(y, p_vector3.y), MAX(z, p_vector3.z)); + } + _FORCE_INLINE_ real_t length() const; _FORCE_INLINE_ real_t length_squared() const; diff --git a/core/math/vector3i.h b/core/math/vector3i.h index 96f9beef122f..53d3829a99d1 100644 --- a/core/math/vector3i.h +++ b/core/math/vector3i.h @@ -69,6 +69,14 @@ struct _NO_DISCARD_ Vector3i { Vector3i::Axis min_axis_index() const; Vector3i::Axis max_axis_index() const; + Vector3i min(const Vector3i &p_vector3i) const { + return Vector3i(MIN(x, p_vector3i.x), MIN(y, p_vector3i.y), MIN(z, p_vector3i.z)); + } + + Vector3i max(const Vector3i &p_vector3i) const { + return Vector3i(MAX(x, p_vector3i.x), MAX(y, p_vector3i.y), MAX(z, p_vector3i.z)); + } + _FORCE_INLINE_ int64_t length_squared() const; _FORCE_INLINE_ double length() const; diff --git a/core/math/vector4.h b/core/math/vector4.h index 0509261f3367..f16b04031759 100644 --- a/core/math/vector4.h +++ b/core/math/vector4.h @@ -68,6 +68,14 @@ struct _NO_DISCARD_ Vector4 { Vector4::Axis min_axis_index() const; Vector4::Axis max_axis_index() const; + Vector4 min(const Vector4 &p_vector4) const { + return Vector4(MIN(x, p_vector4.x), MIN(y, p_vector4.y), MIN(z, p_vector4.z), MIN(w, p_vector4.w)); + } + + Vector4 max(const Vector4 &p_vector4) const { + return Vector4(MAX(x, p_vector4.x), MAX(y, p_vector4.y), MAX(z, p_vector4.z), MAX(w, p_vector4.w)); + } + _FORCE_INLINE_ real_t length_squared() const; bool is_equal_approx(const Vector4 &p_vec4) const; bool is_zero_approx() const; diff --git a/core/math/vector4i.h b/core/math/vector4i.h index d38a9de6f1c5..b815aa8e7686 100644 --- a/core/math/vector4i.h +++ b/core/math/vector4i.h @@ -71,6 +71,14 @@ struct _NO_DISCARD_ Vector4i { Vector4i::Axis min_axis_index() const; Vector4i::Axis max_axis_index() const; + Vector4i min(const Vector4i &p_vector4i) const { + return Vector4i(MIN(x, p_vector4i.x), MIN(y, p_vector4i.y), MIN(z, p_vector4i.z), MIN(w, p_vector4i.w)); + } + + Vector4i max(const Vector4i &p_vector4i) const { + return Vector4i(MAX(x, p_vector4i.x), MAX(y, p_vector4i.y), MAX(z, p_vector4i.z), MAX(w, p_vector4i.w)); + } + _FORCE_INLINE_ int64_t length_squared() const; _FORCE_INLINE_ double length() const; diff --git a/core/variant/variant_utility.cpp b/core/variant/variant_utility.cpp index 042ebe368afd..d5f6ae138d20 100644 --- a/core/variant/variant_utility.cpp +++ b/core/variant/variant_utility.cpp @@ -540,23 +540,51 @@ struct VariantUtilityFunctions { r_error.expected = 2; return Variant(); } - Variant base = *p_args[0]; - Variant ret; + const Variant &base = *p_args[0]; + const Variant::Type base_type = base.get_type(); + Variant ret = base; + for (int i = 1; i < p_argcount; i++) { - bool valid; - Variant::evaluate(Variant::OP_LESS, base, *p_args[i], ret, valid); - if (!valid) { + const Variant &x = *p_args[i]; + if (x.get_type() != base_type) { r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.expected = base.get_type(); + r_error.expected = base_type; r_error.argument = i; return Variant(); } - if (ret.booleanize()) { - base = *p_args[i]; + switch (base_type) { + case Variant::INT: { + ret = MAX(VariantInternalAccessor::get(&ret), VariantInternalAccessor::get(&x)); + } break; + case Variant::FLOAT: { + ret = MAX(VariantInternalAccessor::get(&ret), VariantInternalAccessor::get(&x)); + } break; + case Variant::VECTOR2: { + ret = VariantInternalAccessor::get(&ret).max(VariantInternalAccessor::get(&x)); + } break; + case Variant::VECTOR2I: { + ret = VariantInternalAccessor::get(&ret).max(VariantInternalAccessor::get(&x)); + } break; + case Variant::VECTOR3: { + ret = VariantInternalAccessor::get(&ret).max(VariantInternalAccessor::get(&x)); + } break; + case Variant::VECTOR3I: { + ret = VariantInternalAccessor::get(&ret).max(VariantInternalAccessor::get(&x)); + } break; + case Variant::VECTOR4: { + ret = VariantInternalAccessor::get(&ret).max(VariantInternalAccessor::get(&x)); + } break; + case Variant::VECTOR4I: { + ret = VariantInternalAccessor::get(&ret).max(VariantInternalAccessor::get(&x)); + } break; + default: { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; + return Variant(); + } } } r_error.error = Callable::CallError::CALL_OK; - return base; + return ret; } static inline double maxf(double x, double y) { @@ -573,23 +601,51 @@ struct VariantUtilityFunctions { r_error.expected = 2; return Variant(); } - Variant base = *p_args[0]; - Variant ret; + const Variant &base = *p_args[0]; + const Variant::Type base_type = base.get_type(); + Variant ret = base; + for (int i = 1; i < p_argcount; i++) { - bool valid; - Variant::evaluate(Variant::OP_GREATER, base, *p_args[i], ret, valid); - if (!valid) { + const Variant &x = *p_args[i]; + if (x.get_type() != base_type) { r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.expected = base.get_type(); + r_error.expected = base_type; r_error.argument = i; return Variant(); } - if (ret.booleanize()) { - base = *p_args[i]; + switch (base_type) { + case Variant::INT: { + ret = MIN(VariantInternalAccessor::get(&ret), VariantInternalAccessor::get(&x)); + } break; + case Variant::FLOAT: { + ret = MIN(VariantInternalAccessor::get(&ret), VariantInternalAccessor::get(&x)); + } break; + case Variant::VECTOR2: { + ret = VariantInternalAccessor::get(&ret).min(VariantInternalAccessor::get(&x)); + } break; + case Variant::VECTOR2I: { + ret = VariantInternalAccessor::get(&ret).min(VariantInternalAccessor::get(&x)); + } break; + case Variant::VECTOR3: { + ret = VariantInternalAccessor::get(&ret).min(VariantInternalAccessor::get(&x)); + } break; + case Variant::VECTOR3I: { + ret = VariantInternalAccessor::get(&ret).min(VariantInternalAccessor::get(&x)); + } break; + case Variant::VECTOR4: { + ret = VariantInternalAccessor::get(&ret).min(VariantInternalAccessor::get(&x)); + } break; + case Variant::VECTOR4I: { + ret = VariantInternalAccessor::get(&ret).min(VariantInternalAccessor::get(&x)); + } break; + default: { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; + return Variant(); + } } } r_error.error = Callable::CallError::CALL_OK; - return base; + return ret; } static inline double minf(double x, double y) { diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml index 485c04da6d51..46d46e112bc9 100644 --- a/doc/classes/@GlobalScope.xml +++ b/doc/classes/@GlobalScope.xml @@ -656,9 +656,10 @@ - Returns the maximum of the given values. This function can take any number of arguments. + Returns the maximum of the given values. This function can take any number of arguments. It does component-wise comparison for vectors. [codeblock] max(1, 7, 3, -6, 5) # Returns 7 + max(Vector2(2, 4), Vector2(8, 1)) # Returns Vector2(8, 4) [/codeblock] @@ -689,9 +690,10 @@ - Returns the minimum of the given values. This function can take any number of arguments. + Returns the minimum of the given values. This function can take any number of arguments. It does component-wise comparison for vectors. [codeblock] min(1, 7, 3, -6, 5) # Returns -6 + min(Vector2(2, 4), Vector2(8, 1)) # Returns Vector2(2, 1) [/codeblock] diff --git a/tests/core/math/test_vector3.h b/tests/core/math/test_vector3.h index c3488954cee0..ca0aa028824b 100644 --- a/tests/core/math/test_vector3.h +++ b/tests/core/math/test_vector3.h @@ -354,6 +354,14 @@ TEST_CASE("[Vector3] Other methods") { CHECK_MESSAGE( vector.snapped(Vector3(0.25, 0.25, 0.25)) == Vector3(1.25, 3.5, 5.5), "Vector3 snapped to 0.25 should give exact results."); + + CHECK_MESSAGE( + Vector3(1.2, 2.5, 2.0).is_equal_approx(vector.min(Vector3(3.0, 2.5, 2.0))), + "Vector3 min should return expected value."); + + CHECK_MESSAGE( + Vector3(5.3, 3.4, 5.6).is_equal_approx(vector.max(Vector3(5.3, 2.0, 3.0))), + "Vector3 max should return expected value."); } TEST_CASE("[Vector3] Plane methods") { diff --git a/tests/core/math/test_vector3i.h b/tests/core/math/test_vector3i.h index 6eef129a367f..485a5007152b 100644 --- a/tests/core/math/test_vector3i.h +++ b/tests/core/math/test_vector3i.h @@ -130,6 +130,13 @@ TEST_CASE("[Vector3i] Operators") { TEST_CASE("[Vector3i] Other methods") { const Vector3i vector = Vector3i(1, 3, -7); + CHECK_MESSAGE( + vector.min(Vector3i(3, 2, 5)) == Vector3i(1, 2, -7), + "Vector3i min should return expected value."); + CHECK_MESSAGE( + vector.max(Vector3i(5, 2, 4)) == Vector3i(5, 3, 4), + "Vector3i max should return expected value."); + CHECK_MESSAGE( vector.snapped(Vector3i(4, 2, 5)) == Vector3i(0, 4, -5), "Vector3i snapped should work as expected."); diff --git a/tests/core/math/test_vector4.h b/tests/core/math/test_vector4.h index b85cc710e098..331e0fcfd5e6 100644 --- a/tests/core/math/test_vector4.h +++ b/tests/core/math/test_vector4.h @@ -255,6 +255,14 @@ TEST_CASE("[Vector4] Other methods") { CHECK_MESSAGE( vector.snapped(Vector4(0.25, 0.25, 0.25, 0.25)) == Vector4(1.25, 3.5, 5.5, 1.5), "Vector4 snapped to 0.25 should give exact results."); + + CHECK_MESSAGE( + Vector4(1.2, 2.5, 2.0, 1.6).is_equal_approx(vector.min(Vector4(3.0, 2.5, 2.0, 3.4))), + "Vector4 min should return expected value."); + + CHECK_MESSAGE( + Vector4(5.3, 3.4, 5.6, 4.2).is_equal_approx(vector.max(Vector4(5.3, 2.0, 3.0, 4.2))), + "Vector4 max should return expected value."); } TEST_CASE("[Vector4] Rounding methods") { diff --git a/tests/core/math/test_vector4i.h b/tests/core/math/test_vector4i.h index e5b47af7c4de..5fda6f17783f 100644 --- a/tests/core/math/test_vector4i.h +++ b/tests/core/math/test_vector4i.h @@ -133,6 +133,14 @@ TEST_CASE("[Vector4i] Operators") { TEST_CASE("[Vector3i] Other methods") { const Vector4i vector = Vector4i(1, 3, -7, 13); + CHECK_MESSAGE( + vector.min(Vector4i(3, 2, 5, 8)) == Vector4i(1, 2, -7, 8), + "Vector4i min should return expected value."); + + CHECK_MESSAGE( + vector.max(Vector4i(5, 2, 4, 8)) == Vector4i(5, 3, 4, 13), + "Vector4i max should return expected value."); + CHECK_MESSAGE( vector.snapped(Vector4i(4, 2, 5, 8)) == Vector4i(0, 4, -5, 16), "Vector4i snapped should work as expected.");