summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/extension/native_extension.cpp2
-rw-r--r--core/templates/cowdata.h32
-rw-r--r--core/templates/vector.h2
-rw-r--r--core/variant/variant_call.cpp27
-rw-r--r--doc/classes/PackedByteArray.xml23
-rw-r--r--doc/classes/PackedColorArray.xml23
-rw-r--r--doc/classes/PackedFloat32Array.xml23
-rw-r--r--doc/classes/PackedFloat64Array.xml23
-rw-r--r--doc/classes/PackedInt32Array.xml23
-rw-r--r--doc/classes/PackedInt64Array.xml23
-rw-r--r--doc/classes/PackedStringArray.xml23
-rw-r--r--doc/classes/PackedVector2Array.xml23
-rw-r--r--doc/classes/PackedVector3Array.xml23
-rw-r--r--doc/classes/ProgressBar.xml17
-rw-r--r--doc/classes/SceneTree.xml19
-rw-r--r--editor/editor_file_dialog.cpp2
-rw-r--r--editor/editor_file_dialog.h2
-rw-r--r--editor/filesystem_dock.cpp12
-rw-r--r--editor/filesystem_dock.h4
-rw-r--r--modules/text_server_adv/text_server_adv.cpp5
-rw-r--r--modules/text_server_fb/text_server_fb.cpp5
-rw-r--r--platform/windows/display_server_windows.cpp12
-rw-r--r--platform/windows/display_server_windows.h1
-rw-r--r--scene/2d/camera_2d.cpp6
-rw-r--r--scene/3d/camera_3d.cpp2
-rw-r--r--scene/3d/world_environment.cpp4
-rw-r--r--scene/gui/progress_bar.cpp61
-rw-r--r--scene/gui/progress_bar.h16
-rw-r--r--scene/main/scene_tree.cpp24
-rw-r--r--scene/main/scene_tree.h8
-rw-r--r--scene/main/shader_globals_override.cpp2
-rw-r--r--scene/main/viewport.cpp2
-rw-r--r--scene/resources/material.cpp2
-rw-r--r--tests/core/math/test_geometry_2d.h301
34 files changed, 724 insertions, 53 deletions
diff --git a/core/extension/native_extension.cpp b/core/extension/native_extension.cpp
index db1cbd53b4..5738b42049 100644
--- a/core/extension/native_extension.cpp
+++ b/core/extension/native_extension.cpp
@@ -426,7 +426,7 @@ Ref<Resource> NativeExtensionResourceLoader::load(const String &p_path, const St
return Ref<Resource>();
}
- if (!library_path.is_resource_file()) {
+ if (!library_path.is_resource_file() && !library_path.is_absolute_path()) {
library_path = p_path.get_base_dir().plus_file(library_path);
}
diff --git a/core/templates/cowdata.h b/core/templates/cowdata.h
index f1ac32928f..e760fc2176 100644
--- a/core/templates/cowdata.h
+++ b/core/templates/cowdata.h
@@ -183,6 +183,8 @@ public:
}
int find(const T &p_val, int p_from = 0) const;
+ int rfind(const T &p_val, int p_from = -1) const;
+ int count(const T &p_val) const;
_FORCE_INLINE_ CowData() {}
_FORCE_INLINE_ ~CowData();
@@ -350,6 +352,36 @@ int CowData<T>::find(const T &p_val, int p_from) const {
}
template <class T>
+int CowData<T>::rfind(const T &p_val, int p_from) const {
+ const int s = size();
+
+ if (p_from < 0) {
+ p_from = s + p_from;
+ }
+ if (p_from < 0 || p_from >= s) {
+ p_from = s - 1;
+ }
+
+ for (int i = p_from; i >= 0; i--) {
+ if (get(i) == p_val) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+template <class T>
+int CowData<T>::count(const T &p_val) const {
+ int amount = 0;
+ for (int i = 0; i < size(); i++) {
+ if (get(i) == p_val) {
+ amount++;
+ }
+ }
+ return amount;
+}
+
+template <class T>
void CowData<T>::_ref(const CowData *p_from) {
_ref(*p_from);
}
diff --git a/core/templates/vector.h b/core/templates/vector.h
index d87e76139b..2ac7c7630a 100644
--- a/core/templates/vector.h
+++ b/core/templates/vector.h
@@ -92,6 +92,8 @@ public:
_FORCE_INLINE_ const T &operator[](int p_index) const { return _cowdata.get(p_index); }
Error insert(int p_pos, T p_val) { return _cowdata.insert(p_pos, p_val); }
int find(const T &p_val, int p_from = 0) const { return _cowdata.find(p_val, p_from); }
+ int rfind(const T &p_val, int p_from = -1) const { return _cowdata.rfind(p_val, p_from); }
+ int count(const T &p_val) const { return _cowdata.count(p_val); }
void append_array(Vector<T> p_other);
diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp
index f4b2af5a94..882a89b8ba 100644
--- a/core/variant/variant_call.cpp
+++ b/core/variant/variant_call.cpp
@@ -1873,6 +1873,9 @@ static void _register_variant_builtin_methods() {
bind_method(PackedByteArray, sort, sarray(), varray());
bind_method(PackedByteArray, bsearch, sarray("value", "before"), varray(true));
bind_method(PackedByteArray, duplicate, sarray(), varray());
+ bind_method(PackedByteArray, find, sarray("value", "from"), varray(0));
+ bind_method(PackedByteArray, rfind, sarray("value", "from"), varray(-1));
+ bind_method(PackedByteArray, count, sarray("value"), varray());
bind_function(PackedByteArray, get_string_from_ascii, _VariantCall::func_PackedByteArray_get_string_from_ascii, sarray(), varray());
bind_function(PackedByteArray, get_string_from_utf8, _VariantCall::func_PackedByteArray_get_string_from_utf8, sarray(), varray());
@@ -1935,6 +1938,9 @@ static void _register_variant_builtin_methods() {
bind_method(PackedInt32Array, sort, sarray(), varray());
bind_method(PackedInt32Array, bsearch, sarray("value", "before"), varray(true));
bind_method(PackedInt32Array, duplicate, sarray(), varray());
+ bind_method(PackedInt32Array, find, sarray("value", "from"), varray(0));
+ bind_method(PackedInt32Array, rfind, sarray("value", "from"), varray(-1));
+ bind_method(PackedInt32Array, count, sarray("value"), varray());
/* Int64 Array */
@@ -1955,6 +1961,9 @@ static void _register_variant_builtin_methods() {
bind_method(PackedInt64Array, sort, sarray(), varray());
bind_method(PackedInt64Array, bsearch, sarray("value", "before"), varray(true));
bind_method(PackedInt64Array, duplicate, sarray(), varray());
+ bind_method(PackedInt64Array, find, sarray("value", "from"), varray(0));
+ bind_method(PackedInt64Array, rfind, sarray("value", "from"), varray(-1));
+ bind_method(PackedInt64Array, count, sarray("value"), varray());
/* Float32 Array */
@@ -1975,6 +1984,9 @@ static void _register_variant_builtin_methods() {
bind_method(PackedFloat32Array, sort, sarray(), varray());
bind_method(PackedFloat32Array, bsearch, sarray("value", "before"), varray(true));
bind_method(PackedFloat32Array, duplicate, sarray(), varray());
+ bind_method(PackedFloat32Array, find, sarray("value", "from"), varray(0));
+ bind_method(PackedFloat32Array, rfind, sarray("value", "from"), varray(-1));
+ bind_method(PackedFloat32Array, count, sarray("value"), varray());
/* Float64 Array */
@@ -1995,6 +2007,9 @@ static void _register_variant_builtin_methods() {
bind_method(PackedFloat64Array, sort, sarray(), varray());
bind_method(PackedFloat64Array, bsearch, sarray("value", "before"), varray(true));
bind_method(PackedFloat64Array, duplicate, sarray(), varray());
+ bind_method(PackedFloat64Array, find, sarray("value", "from"), varray(0));
+ bind_method(PackedFloat64Array, rfind, sarray("value", "from"), varray(-1));
+ bind_method(PackedFloat64Array, count, sarray("value"), varray());
/* String Array */
@@ -2015,6 +2030,9 @@ static void _register_variant_builtin_methods() {
bind_method(PackedStringArray, sort, sarray(), varray());
bind_method(PackedStringArray, bsearch, sarray("value", "before"), varray(true));
bind_method(PackedStringArray, duplicate, sarray(), varray());
+ bind_method(PackedStringArray, find, sarray("value", "from"), varray(0));
+ bind_method(PackedStringArray, rfind, sarray("value", "from"), varray(-1));
+ bind_method(PackedStringArray, count, sarray("value"), varray());
/* Vector2 Array */
@@ -2035,6 +2053,9 @@ static void _register_variant_builtin_methods() {
bind_method(PackedVector2Array, sort, sarray(), varray());
bind_method(PackedVector2Array, bsearch, sarray("value", "before"), varray(true));
bind_method(PackedVector2Array, duplicate, sarray(), varray());
+ bind_method(PackedVector2Array, find, sarray("value", "from"), varray(0));
+ bind_method(PackedVector2Array, rfind, sarray("value", "from"), varray(-1));
+ bind_method(PackedVector2Array, count, sarray("value"), varray());
/* Vector3 Array */
@@ -2055,6 +2076,9 @@ static void _register_variant_builtin_methods() {
bind_method(PackedVector3Array, sort, sarray(), varray());
bind_method(PackedVector3Array, bsearch, sarray("value", "before"), varray(true));
bind_method(PackedVector3Array, duplicate, sarray(), varray());
+ bind_method(PackedVector3Array, find, sarray("value", "from"), varray(0));
+ bind_method(PackedVector3Array, rfind, sarray("value", "from"), varray(-1));
+ bind_method(PackedVector3Array, count, sarray("value"), varray());
/* Color Array */
@@ -2075,6 +2099,9 @@ static void _register_variant_builtin_methods() {
bind_method(PackedColorArray, sort, sarray(), varray());
bind_method(PackedColorArray, bsearch, sarray("value", "before"), varray(true));
bind_method(PackedColorArray, duplicate, sarray(), varray());
+ bind_method(PackedColorArray, find, sarray("value", "from"), varray(0));
+ bind_method(PackedColorArray, rfind, sarray("value", "from"), varray(-1));
+ bind_method(PackedColorArray, count, sarray("value"), varray());
/* Register constants */
diff --git a/doc/classes/PackedByteArray.xml b/doc/classes/PackedByteArray.xml
index 61dc1eef00..5d0861bcf3 100644
--- a/doc/classes/PackedByteArray.xml
+++ b/doc/classes/PackedByteArray.xml
@@ -61,6 +61,13 @@
Returns a new [PackedByteArray] with the data compressed. Set the compression mode using one of [enum File.CompressionMode]'s constants.
</description>
</method>
+ <method name="count" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="int" />
+ <description>
+ Returns the number of times an element is in the array.
+ </description>
+ </method>
<method name="decode_double" qualifiers="const">
<return type="float" />
<argument index="0" name="byte_offset" type="int" />
@@ -257,6 +264,14 @@
Assigns the given value to all elements in the array. This can typically be used together with [method resize] to create an array with a given size and initialized elements.
</description>
</method>
+ <method name="find" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="int" />
+ <argument index="1" name="from" type="int" default="0" />
+ <description>
+ Searches the array for a value and returns its index or [code]-1[/code] if not found. Optionally, the initial search index can be passed.
+ </description>
+ </method>
<method name="get_string_from_ascii" qualifiers="const">
<return type="String" />
<description>
@@ -352,6 +367,14 @@
Reverses the order of the elements in the array.
</description>
</method>
+ <method name="rfind" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="int" />
+ <argument index="1" name="from" type="int" default="-1" />
+ <description>
+ Searches the array in reverse order. Optionally, a start search index can be passed. If negative, the start index is considered relative to the end of the array.
+ </description>
+ </method>
<method name="set">
<return type="void" />
<argument index="0" name="index" type="int" />
diff --git a/doc/classes/PackedColorArray.xml b/doc/classes/PackedColorArray.xml
index f880771c77..12a553af49 100644
--- a/doc/classes/PackedColorArray.xml
+++ b/doc/classes/PackedColorArray.xml
@@ -54,6 +54,13 @@
[b]Note:[/b] Calling [method bsearch] on an unsorted array results in unexpected behavior.
</description>
</method>
+ <method name="count" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="Color" />
+ <description>
+ Returns the number of times an element is in the array.
+ </description>
+ </method>
<method name="duplicate">
<return type="PackedColorArray" />
<description>
@@ -67,6 +74,14 @@
Assigns the given value to all elements in the array. This can typically be used together with [method resize] to create an array with a given size and initialized elements.
</description>
</method>
+ <method name="find" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="Color" />
+ <argument index="1" name="from" type="int" default="0" />
+ <description>
+ Searches the array for a value and returns its index or [code]-1[/code] if not found. Optionally, the initial search index can be passed.
+ </description>
+ </method>
<method name="has" qualifiers="const">
<return type="bool" />
<argument index="0" name="value" type="Color" />
@@ -115,6 +130,14 @@
Reverses the order of the elements in the array.
</description>
</method>
+ <method name="rfind" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="Color" />
+ <argument index="1" name="from" type="int" default="-1" />
+ <description>
+ Searches the array in reverse order. Optionally, a start search index can be passed. If negative, the start index is considered relative to the end of the array.
+ </description>
+ </method>
<method name="set">
<return type="void" />
<argument index="0" name="index" type="int" />
diff --git a/doc/classes/PackedFloat32Array.xml b/doc/classes/PackedFloat32Array.xml
index e2b877ad5f..0a114e6c06 100644
--- a/doc/classes/PackedFloat32Array.xml
+++ b/doc/classes/PackedFloat32Array.xml
@@ -55,6 +55,13 @@
[b]Note:[/b] Calling [method bsearch] on an unsorted array results in unexpected behavior.
</description>
</method>
+ <method name="count" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="float" />
+ <description>
+ Returns the number of times an element is in the array.
+ </description>
+ </method>
<method name="duplicate">
<return type="PackedFloat32Array" />
<description>
@@ -68,6 +75,14 @@
Assigns the given value to all elements in the array. This can typically be used together with [method resize] to create an array with a given size and initialized elements.
</description>
</method>
+ <method name="find" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="float" />
+ <argument index="1" name="from" type="int" default="0" />
+ <description>
+ Searches the array for a value and returns its index or [code]-1[/code] if not found. Optionally, the initial search index can be passed.
+ </description>
+ </method>
<method name="has" qualifiers="const">
<return type="bool" />
<argument index="0" name="value" type="float" />
@@ -116,6 +131,14 @@
Reverses the order of the elements in the array.
</description>
</method>
+ <method name="rfind" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="float" />
+ <argument index="1" name="from" type="int" default="-1" />
+ <description>
+ Searches the array in reverse order. Optionally, a start search index can be passed. If negative, the start index is considered relative to the end of the array.
+ </description>
+ </method>
<method name="set">
<return type="void" />
<argument index="0" name="index" type="int" />
diff --git a/doc/classes/PackedFloat64Array.xml b/doc/classes/PackedFloat64Array.xml
index be7a52b7f4..0327559f5b 100644
--- a/doc/classes/PackedFloat64Array.xml
+++ b/doc/classes/PackedFloat64Array.xml
@@ -55,6 +55,13 @@
[b]Note:[/b] Calling [method bsearch] on an unsorted array results in unexpected behavior.
</description>
</method>
+ <method name="count" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="float" />
+ <description>
+ Returns the number of times an element is in the array.
+ </description>
+ </method>
<method name="duplicate">
<return type="PackedFloat64Array" />
<description>
@@ -68,6 +75,14 @@
Assigns the given value to all elements in the array. This can typically be used together with [method resize] to create an array with a given size and initialized elements.
</description>
</method>
+ <method name="find" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="float" />
+ <argument index="1" name="from" type="int" default="0" />
+ <description>
+ Searches the array for a value and returns its index or [code]-1[/code] if not found. Optionally, the initial search index can be passed.
+ </description>
+ </method>
<method name="has" qualifiers="const">
<return type="bool" />
<argument index="0" name="value" type="float" />
@@ -116,6 +131,14 @@
Reverses the order of the elements in the array.
</description>
</method>
+ <method name="rfind" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="float" />
+ <argument index="1" name="from" type="int" default="-1" />
+ <description>
+ Searches the array in reverse order. Optionally, a start search index can be passed. If negative, the start index is considered relative to the end of the array.
+ </description>
+ </method>
<method name="set">
<return type="void" />
<argument index="0" name="index" type="int" />
diff --git a/doc/classes/PackedInt32Array.xml b/doc/classes/PackedInt32Array.xml
index 108273d859..f8b606d266 100644
--- a/doc/classes/PackedInt32Array.xml
+++ b/doc/classes/PackedInt32Array.xml
@@ -55,6 +55,13 @@
[b]Note:[/b] Calling [method bsearch] on an unsorted array results in unexpected behavior.
</description>
</method>
+ <method name="count" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="int" />
+ <description>
+ Returns the number of times an element is in the array.
+ </description>
+ </method>
<method name="duplicate">
<return type="PackedInt32Array" />
<description>
@@ -68,6 +75,14 @@
Assigns the given value to all elements in the array. This can typically be used together with [method resize] to create an array with a given size and initialized elements.
</description>
</method>
+ <method name="find" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="int" />
+ <argument index="1" name="from" type="int" default="0" />
+ <description>
+ Searches the array for a value and returns its index or [code]-1[/code] if not found. Optionally, the initial search index can be passed.
+ </description>
+ </method>
<method name="has" qualifiers="const">
<return type="bool" />
<argument index="0" name="value" type="int" />
@@ -116,6 +131,14 @@
Reverses the order of the elements in the array.
</description>
</method>
+ <method name="rfind" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="int" />
+ <argument index="1" name="from" type="int" default="-1" />
+ <description>
+ Searches the array in reverse order. Optionally, a start search index can be passed. If negative, the start index is considered relative to the end of the array.
+ </description>
+ </method>
<method name="set">
<return type="void" />
<argument index="0" name="index" type="int" />
diff --git a/doc/classes/PackedInt64Array.xml b/doc/classes/PackedInt64Array.xml
index c34f2fc75e..ea3e304d35 100644
--- a/doc/classes/PackedInt64Array.xml
+++ b/doc/classes/PackedInt64Array.xml
@@ -55,6 +55,13 @@
[b]Note:[/b] Calling [method bsearch] on an unsorted array results in unexpected behavior.
</description>
</method>
+ <method name="count" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="int" />
+ <description>
+ Returns the number of times an element is in the array.
+ </description>
+ </method>
<method name="duplicate">
<return type="PackedInt64Array" />
<description>
@@ -68,6 +75,14 @@
Assigns the given value to all elements in the array. This can typically be used together with [method resize] to create an array with a given size and initialized elements.
</description>
</method>
+ <method name="find" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="int" />
+ <argument index="1" name="from" type="int" default="0" />
+ <description>
+ Searches the array for a value and returns its index or [code]-1[/code] if not found. Optionally, the initial search index can be passed.
+ </description>
+ </method>
<method name="has" qualifiers="const">
<return type="bool" />
<argument index="0" name="value" type="int" />
@@ -116,6 +131,14 @@
Reverses the order of the elements in the array.
</description>
</method>
+ <method name="rfind" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="int" />
+ <argument index="1" name="from" type="int" default="-1" />
+ <description>
+ Searches the array in reverse order. Optionally, a start search index can be passed. If negative, the start index is considered relative to the end of the array.
+ </description>
+ </method>
<method name="set">
<return type="void" />
<argument index="0" name="index" type="int" />
diff --git a/doc/classes/PackedStringArray.xml b/doc/classes/PackedStringArray.xml
index 536f5f02eb..d0f1d5f753 100644
--- a/doc/classes/PackedStringArray.xml
+++ b/doc/classes/PackedStringArray.xml
@@ -55,6 +55,13 @@
[b]Note:[/b] Calling [method bsearch] on an unsorted array results in unexpected behavior.
</description>
</method>
+ <method name="count" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="String" />
+ <description>
+ Returns the number of times an element is in the array.
+ </description>
+ </method>
<method name="duplicate">
<return type="PackedStringArray" />
<description>
@@ -68,6 +75,14 @@
Assigns the given value to all elements in the array. This can typically be used together with [method resize] to create an array with a given size and initialized elements.
</description>
</method>
+ <method name="find" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="String" />
+ <argument index="1" name="from" type="int" default="0" />
+ <description>
+ Searches the array for a value and returns its index or [code]-1[/code] if not found. Optionally, the initial search index can be passed.
+ </description>
+ </method>
<method name="has" qualifiers="const">
<return type="bool" />
<argument index="0" name="value" type="String" />
@@ -116,6 +131,14 @@
Reverses the order of the elements in the array.
</description>
</method>
+ <method name="rfind" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="String" />
+ <argument index="1" name="from" type="int" default="-1" />
+ <description>
+ Searches the array in reverse order. Optionally, a start search index can be passed. If negative, the start index is considered relative to the end of the array.
+ </description>
+ </method>
<method name="set">
<return type="void" />
<argument index="0" name="index" type="int" />
diff --git a/doc/classes/PackedVector2Array.xml b/doc/classes/PackedVector2Array.xml
index 29423d1cde..8f3e5d173d 100644
--- a/doc/classes/PackedVector2Array.xml
+++ b/doc/classes/PackedVector2Array.xml
@@ -55,6 +55,13 @@
[b]Note:[/b] Calling [method bsearch] on an unsorted array results in unexpected behavior.
</description>
</method>
+ <method name="count" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="Vector2" />
+ <description>
+ Returns the number of times an element is in the array.
+ </description>
+ </method>
<method name="duplicate">
<return type="PackedVector2Array" />
<description>
@@ -68,6 +75,14 @@
Assigns the given value to all elements in the array. This can typically be used together with [method resize] to create an array with a given size and initialized elements.
</description>
</method>
+ <method name="find" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="Vector2" />
+ <argument index="1" name="from" type="int" default="0" />
+ <description>
+ Searches the array for a value and returns its index or [code]-1[/code] if not found. Optionally, the initial search index can be passed.
+ </description>
+ </method>
<method name="has" qualifiers="const">
<return type="bool" />
<argument index="0" name="value" type="Vector2" />
@@ -116,6 +131,14 @@
Reverses the order of the elements in the array.
</description>
</method>
+ <method name="rfind" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="Vector2" />
+ <argument index="1" name="from" type="int" default="-1" />
+ <description>
+ Searches the array in reverse order. Optionally, a start search index can be passed. If negative, the start index is considered relative to the end of the array.
+ </description>
+ </method>
<method name="set">
<return type="void" />
<argument index="0" name="index" type="int" />
diff --git a/doc/classes/PackedVector3Array.xml b/doc/classes/PackedVector3Array.xml
index 6d460cecab..1207293c32 100644
--- a/doc/classes/PackedVector3Array.xml
+++ b/doc/classes/PackedVector3Array.xml
@@ -54,6 +54,13 @@
[b]Note:[/b] Calling [method bsearch] on an unsorted array results in unexpected behavior.
</description>
</method>
+ <method name="count" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="Vector3" />
+ <description>
+ Returns the number of times an element is in the array.
+ </description>
+ </method>
<method name="duplicate">
<return type="PackedVector3Array" />
<description>
@@ -67,6 +74,14 @@
Assigns the given value to all elements in the array. This can typically be used together with [method resize] to create an array with a given size and initialized elements.
</description>
</method>
+ <method name="find" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="Vector3" />
+ <argument index="1" name="from" type="int" default="0" />
+ <description>
+ Searches the array for a value and returns its index or [code]-1[/code] if not found. Optionally, the initial search index can be passed.
+ </description>
+ </method>
<method name="has" qualifiers="const">
<return type="bool" />
<argument index="0" name="value" type="Vector3" />
@@ -115,6 +130,14 @@
Reverses the order of the elements in the array.
</description>
</method>
+ <method name="rfind" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="Vector3" />
+ <argument index="1" name="from" type="int" default="-1" />
+ <description>
+ Searches the array in reverse order. Optionally, a start search index can be passed. If negative, the start index is considered relative to the end of the array.
+ </description>
+ </method>
<method name="set">
<return type="void" />
<argument index="0" name="index" type="int" />
diff --git a/doc/classes/ProgressBar.xml b/doc/classes/ProgressBar.xml
index 1e9ab7c375..8a781c51fb 100644
--- a/doc/classes/ProgressBar.xml
+++ b/doc/classes/ProgressBar.xml
@@ -9,10 +9,27 @@
<tutorials>
</tutorials>
<members>
+ <member name="fill_mode" type="int" setter="set_fill_mode" getter="get_fill_mode" default="0">
+ The fill direction. See [enum FillMode] for possible values.
+ </member>
<member name="percent_visible" type="bool" setter="set_percent_visible" getter="is_percent_visible" default="true">
If [code]true[/code], the fill percentage is displayed on the bar.
</member>
</members>
+ <constants>
+ <constant name="FILL_BEGIN_TO_END" value="0" enum="FillMode">
+ The progress bar fills from begin to end horizontally, according to the language direction. If [method Control.is_layout_rtl] returns [code]false[/code], it fills from left to right, and if it returns [code]true[/code], it fills from right to left.
+ </constant>
+ <constant name="FILL_END_TO_BEGIN" value="1" enum="FillMode">
+ The progress bar fills from end to begin horizontally, according to the language direction. If [method Control.is_layout_rtl] returns [code]false[/code], it fills from right to left, and if it returns [code]true[/code], it fills from left to right.
+ </constant>
+ <constant name="FILL_TOP_TO_BOTTOM" value="2" enum="FillMode">
+ The progress fills from top to bottom.
+ </constant>
+ <constant name="FILL_BOTTOM_TO_TOP" value="3" enum="FillMode">
+ The progress fills from bottom to top.
+ </constant>
+ </constants>
<theme_items>
<theme_item name="font_color" data_type="color" type="Color" default="Color(0.95, 0.95, 0.95, 1)">
The color of the text.
diff --git a/doc/classes/SceneTree.xml b/doc/classes/SceneTree.xml
index 970f5125cd..3cd2ab0903 100644
--- a/doc/classes/SceneTree.xml
+++ b/doc/classes/SceneTree.xml
@@ -18,9 +18,9 @@
<argument index="0" name="group" type="StringName" />
<argument index="1" name="method" type="StringName" />
<description>
- Calls [code]method[/code] on each member of the given group. You can pass arguments to [code]method[/code] by specifying them at the end of the method call. This method is equivalent of calling [method call_group_flags] with [constant GROUP_CALL_DEFAULT] flag.
+ Calls [code]method[/code] on each member of the given group. You can pass arguments to [code]method[/code] by specifying them at the end of the method call.
[b]Note:[/b] Due to design limitations, [method call_group] will fail silently if one of the arguments is [code]null[/code].
- [b]Note:[/b] [method call_group] will always call methods with an one-frame delay, in a way similar to [method Object.call_deferred]. To call methods immediately, use [method call_group_flags] with the [constant GROUP_CALL_REALTIME] flag.
+ [b]Note:[/b] [method call_group] will call methods immediately on all members at once, which can cause stuttering if an expensive method is called on lots of members. To wait for one frame after [method call_group] was called, use [method call_group_flags] with the [constant GROUP_CALL_DEFERRED] flag.
</description>
</method>
<method name="call_group_flags" qualifiers="vararg">
@@ -30,11 +30,12 @@
<argument index="2" name="method" type="StringName" />
<description>
Calls [code]method[/code] on each member of the given group, respecting the given [enum GroupCallFlags]. You can pass arguments to [code]method[/code] by specifying them at the end of the method call.
- [b]Note:[/b] Due to design limitations, [method call_group_flags] will fail silently if one of the arguments is [code]null[/code].
[codeblock]
- # Call the method immediately and in reverse order.
- get_tree().call_group_flags(SceneTree.GROUP_CALL_REALTIME | SceneTree.GROUP_CALL_REVERSE, "bases", "destroy")
+ # Call the method in a deferred manner and in reverse order.
+ get_tree().call_group_flags(SceneTree.GROUP_CALL_DEFERRED | SceneTree.GROUP_CALL_REVERSE)
[/codeblock]
+ [b]Note:[/b] Due to design limitations, [method call_group_flags] will fail silently if one of the arguments is [code]null[/code].
+ [b]Note:[/b] Group call flags are used to control the method calling behavior. By default, methods will be called immediately in a way similar to [method call_group]. However, if the [constant GROUP_CALL_DEFERRED] flag is present in the [code]flags[/code] argument, methods will be called with a one-frame delay in a way similar to [method Object.set_deferred].
</description>
</method>
<method name="change_scene">
@@ -139,6 +140,7 @@
<argument index="1" name="notification" type="int" />
<description>
Sends the given notification to all members of the [code]group[/code].
+ [b]Note:[/b] [method notify_group] will immediately notify all members at once, which can cause stuttering if an expensive method is called as a result of sending the notification lots of members. To wait for one frame, use [method notify_group_flags] with the [constant GROUP_CALL_DEFERRED] flag.
</description>
</method>
<method name="notify_group_flags">
@@ -148,6 +150,7 @@
<argument index="2" name="notification" type="int" />
<description>
Sends the given notification to all members of the [code]group[/code], respecting the given [enum GroupCallFlags].
+ [b]Note:[/b] Group call flags are used to control the notification sending behavior. By default, notifications will be sent immediately in a way similar to [method notify_group]. However, if the [constant GROUP_CALL_DEFERRED] flag is present in the [code]flags[/code] argument, notifications will be sent with a one-frame delay in a way similar to using [code]Object.call_deferred("notification", ...)[/code].
</description>
</method>
<method name="queue_delete">
@@ -189,6 +192,7 @@
<argument index="2" name="value" type="Variant" />
<description>
Sets the given [code]property[/code] to [code]value[/code] on all members of the given group.
+ [b]Note:[/b] [method set_group] will set the property immediately on all members at once, which can cause stuttering if a property with an expensive setter is set on lots of members. To wait for one frame, use [method set_group_flags] with the [constant GROUP_CALL_DEFERRED] flag.
</description>
</method>
<method name="set_group_flags">
@@ -199,6 +203,7 @@
<argument index="3" name="value" type="Variant" />
<description>
Sets the given [code]property[/code] to [code]value[/code] on all members of the given group, respecting the given [enum GroupCallFlags].
+ [b]Note:[/b] Group call flags are used to control the property setting behavior. By default, properties will be set immediately in a way similar to [method set_group]. However, if the [constant GROUP_CALL_DEFERRED] flag is present in the [code]flags[/code] argument, properties will be set with a one-frame delay in a way similar to [method Object.call_deferred].
</description>
</method>
<method name="set_multiplayer">
@@ -297,8 +302,8 @@
<constant name="GROUP_CALL_REVERSE" value="1" enum="GroupCallFlags">
Call a group in reverse scene order.
</constant>
- <constant name="GROUP_CALL_REALTIME" value="2" enum="GroupCallFlags">
- Call a group immediately (calls are normally made on idle).
+ <constant name="GROUP_CALL_DEFERRED" value="2" enum="GroupCallFlags">
+ Call a group with a one-frame delay (idle frame, not physics).
</constant>
<constant name="GROUP_CALL_UNIQUE" value="4" enum="GroupCallFlags">
Call a group only once even if the call is executed many times.
diff --git a/editor/editor_file_dialog.cpp b/editor/editor_file_dialog.cpp
index 84d656fc20..13084e2aab 100644
--- a/editor/editor_file_dialog.cpp
+++ b/editor/editor_file_dialog.cpp
@@ -529,7 +529,7 @@ void EditorFileDialog::_multi_selected(int p_item, bool p_selected) {
get_ok_button()->set_disabled(_is_open_should_be_disabled());
}
-void EditorFileDialog::_items_clear_selection(int p_item, const Vector2 &p_pos, MouseButton p_mouse_button_index) {
+void EditorFileDialog::_items_clear_selection(const Vector2 &p_pos, MouseButton p_mouse_button_index) {
if (p_mouse_button_index != MouseButton::LEFT) {
return;
}
diff --git a/editor/editor_file_dialog.h b/editor/editor_file_dialog.h
index d9a58a5b7f..5f2e29b690 100644
--- a/editor/editor_file_dialog.h
+++ b/editor/editor_file_dialog.h
@@ -165,7 +165,7 @@ private:
void _item_selected(int p_item);
void _multi_selected(int p_item, bool p_selected);
- void _items_clear_selection(int p_item, const Vector2 &p_pos, MouseButton p_mouse_button_index);
+ void _items_clear_selection(const Vector2 &p_pos, MouseButton p_mouse_button_index);
void _item_dc_selected(int p_item);
void _item_list_item_rmb_clicked(int p_item, const Vector2 &p_pos, MouseButton p_mouse_button_index);
diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp
index 47c016803c..aae8cf25b6 100644
--- a/editor/filesystem_dock.cpp
+++ b/editor/filesystem_dock.cpp
@@ -2637,7 +2637,7 @@ void FileSystemDock::_tree_empty_selected() {
tree->deselect_all();
}
-void FileSystemDock::_file_list_rmb_clicked(int p_item, const Vector2 &p_pos, MouseButton p_mouse_button_index) {
+void FileSystemDock::_file_list_item_clicked(int p_item, const Vector2 &p_pos, MouseButton p_mouse_button_index) {
if (p_mouse_button_index != MouseButton::RIGHT) {
return;
}
@@ -2665,7 +2665,11 @@ void FileSystemDock::_file_list_rmb_clicked(int p_item, const Vector2 &p_pos, Mo
}
}
-void FileSystemDock::_file_list_rmb_pressed(const Vector2 &p_pos) {
+void FileSystemDock::_file_list_empty_clicked(const Vector2 &p_pos, MouseButton p_mouse_button_index) {
+ if (p_mouse_button_index != MouseButton::RIGHT) {
+ return;
+ }
+
// Right click on empty space for file list.
if (searched_string.length() > 0) {
return;
@@ -3132,10 +3136,10 @@ FileSystemDock::FileSystemDock() {
files->set_v_size_flags(SIZE_EXPAND_FILL);
files->set_select_mode(ItemList::SELECT_MULTI);
files->set_drag_forwarding(this);
- files->connect("item_clicked", callable_mp(this, &FileSystemDock::_file_list_rmb_clicked));
+ files->connect("item_clicked", callable_mp(this, &FileSystemDock::_file_list_item_clicked));
files->connect("gui_input", callable_mp(this, &FileSystemDock::_file_list_gui_input));
files->connect("multi_selected", callable_mp(this, &FileSystemDock::_file_multi_selected));
- files->connect("rmb_clicked", callable_mp(this, &FileSystemDock::_file_list_rmb_pressed));
+ files->connect("empty_clicked", callable_mp(this, &FileSystemDock::_file_list_empty_clicked));
files->set_custom_minimum_size(Size2(0, 15 * EDSCALE));
files->set_allow_rmb_select(true);
file_list_vb->add_child(files);
diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h
index f6c4e271bf..fc24b3e9fd 100644
--- a/editor/filesystem_dock.h
+++ b/editor/filesystem_dock.h
@@ -260,8 +260,8 @@ private:
void _file_and_folders_fill_popup(PopupMenu *p_popup, Vector<String> p_paths, bool p_display_path_dependent_options = true);
void _tree_rmb_select(const Vector2 &p_pos);
void _tree_rmb_empty(const Vector2 &p_pos);
- void _file_list_rmb_clicked(int p_item, const Vector2 &p_pos, MouseButton p_mouse_button_index);
- void _file_list_rmb_pressed(const Vector2 &p_pos);
+ void _file_list_item_clicked(int p_item, const Vector2 &p_pos, MouseButton p_mouse_button_index);
+ void _file_list_empty_clicked(const Vector2 &p_pos, MouseButton p_mouse_button_index);
void _tree_empty_selected();
struct FileInfo {
diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp
index ba249aff7a..07421b7275 100644
--- a/modules/text_server_adv/text_server_adv.cpp
+++ b/modules/text_server_adv/text_server_adv.cpp
@@ -2172,6 +2172,11 @@ void TextServerAdvanced::font_set_scale(const RID &p_font_rid, int64_t p_size, d
Vector2i size = _get_size(fd, p_size);
ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+#ifdef MODULE_FREETYPE_ENABLED
+ if (fd->cache[size]->face) {
+ return; // Do not override scale for dynamic fonts, it's calculated automatically.
+ }
+#endif
fd->cache[size]->scale = p_scale;
}
diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp
index 8ae56aa64d..257c569a25 100644
--- a/modules/text_server_fb/text_server_fb.cpp
+++ b/modules/text_server_fb/text_server_fb.cpp
@@ -1334,6 +1334,11 @@ void TextServerFallback::font_set_scale(const RID &p_font_rid, int64_t p_size, d
Vector2i size = _get_size(fd, p_size);
ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+#ifdef MODULE_FREETYPE_ENABLED
+ if (fd->cache[size]->face) {
+ return; // Do not override scale for dynamic fonts, it's calculated automatically.
+ }
+#endif
fd->cache[size]->scale = p_scale;
}
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index b548277f95..93cab85441 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -1106,6 +1106,10 @@ void DisplayServerWindows::_update_window_style(WindowID p_window, bool p_repain
SetWindowLongPtr(wd.hWnd, GWL_STYLE, style);
SetWindowLongPtr(wd.hWnd, GWL_EXSTYLE, style_ex);
+ if (icon.is_valid()) {
+ set_icon(icon);
+ }
+
SetWindowPos(wd.hWnd, wd.always_on_top ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | ((wd.no_focus || wd.is_popup) ? SWP_NOACTIVATE : 0));
if (p_repaint) {
@@ -1895,9 +1899,11 @@ void DisplayServerWindows::set_icon(const Ref<Image> &p_icon) {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND(!p_icon.is_valid());
- Ref<Image> icon = p_icon->duplicate();
- if (icon->get_format() != Image::FORMAT_RGBA8) {
- icon->convert(Image::FORMAT_RGBA8);
+ if (icon != p_icon) {
+ icon = p_icon->duplicate();
+ if (icon->get_format() != Image::FORMAT_RGBA8) {
+ icon->convert(Image::FORMAT_RGBA8);
+ }
}
int w = icon->get_width();
int h = icon->get_height();
diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h
index c039b29c54..febc8a2043 100644
--- a/platform/windows/display_server_windows.h
+++ b/platform/windows/display_server_windows.h
@@ -399,6 +399,7 @@ class DisplayServerWindows : public DisplayServer {
HHOOK mouse_monitor = nullptr;
List<WindowID> popup_list;
uint64_t time_since_popup = 0;
+ Ref<Image> icon;
WindowID _create_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect);
WindowID window_id_counter = MAIN_WINDOW_ID;
diff --git a/scene/2d/camera_2d.cpp b/scene/2d/camera_2d.cpp
index efde8d8a2b..f61dbc071d 100644
--- a/scene/2d/camera_2d.cpp
+++ b/scene/2d/camera_2d.cpp
@@ -57,7 +57,7 @@ void Camera2D::_update_scroll() {
Size2 screen_size = _get_camera_screen_size();
Point2 screen_offset = (anchor_mode == ANCHOR_MODE_DRAG_CENTER ? (screen_size * 0.5) : Point2());
- get_tree()->call_group_flags(SceneTree::GROUP_CALL_REALTIME, group_name, "_camera_moved", xform, screen_offset);
+ get_tree()->call_group(group_name, "_camera_moved", xform, screen_offset);
};
}
@@ -421,7 +421,7 @@ bool Camera2D::is_current() const {
void Camera2D::make_current() {
if (is_inside_tree()) {
- get_tree()->call_group_flags(SceneTree::GROUP_CALL_REALTIME, group_name, "_make_current", this);
+ get_tree()->call_group(group_name, "_make_current", this);
} else {
current = true;
}
@@ -430,7 +430,7 @@ void Camera2D::make_current() {
void Camera2D::clear_current() {
if (is_inside_tree()) {
- get_tree()->call_group_flags(SceneTree::GROUP_CALL_REALTIME, group_name, "_make_current", (Object *)nullptr);
+ get_tree()->call_group(group_name, "_make_current", (Object *)nullptr);
} else {
current = false;
}
diff --git a/scene/3d/camera_3d.cpp b/scene/3d/camera_3d.cpp
index 4f05e80377..4c53776bba 100644
--- a/scene/3d/camera_3d.cpp
+++ b/scene/3d/camera_3d.cpp
@@ -217,8 +217,6 @@ void Camera3D::make_current() {
}
get_viewport()->_camera_3d_set(this);
-
- //get_scene()->call_group(SceneMainLoop::GROUP_CALL_REALTIME,camera_group,"_camera_make_current",this);
}
void Camera3D::clear_current(bool p_enable_next) {
diff --git a/scene/3d/world_environment.cpp b/scene/3d/world_environment.cpp
index f638644628..fe9d9ae4dd 100644
--- a/scene/3d/world_environment.cpp
+++ b/scene/3d/world_environment.cpp
@@ -71,7 +71,7 @@ void WorldEnvironment::_update_current_environment() {
} else {
get_viewport()->find_world_3d()->set_environment(Ref<Environment>());
}
- get_tree()->call_group("_world_environment_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id()), "update_configuration_warnings");
+ get_tree()->call_group_flags(SceneTree::GROUP_CALL_DEFERRED, "_world_environment_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id()), "update_configuration_warnings");
}
void WorldEnvironment::_update_current_camera_effects() {
@@ -82,7 +82,7 @@ void WorldEnvironment::_update_current_camera_effects() {
get_viewport()->find_world_3d()->set_camera_effects(Ref<CameraEffects>());
}
- get_tree()->call_group("_world_camera_effects_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id()), "update_configuration_warnings");
+ get_tree()->call_group_flags(SceneTree::GROUP_CALL_DEFERRED, "_world_camera_effects_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id()), "update_configuration_warnings");
}
void WorldEnvironment::set_environment(const Ref<Environment> &p_environment) {
diff --git a/scene/gui/progress_bar.cpp b/scene/gui/progress_bar.cpp
index 50ffb3ca67..f36682942f 100644
--- a/scene/gui/progress_bar.cpp
+++ b/scene/gui/progress_bar.cpp
@@ -62,15 +62,42 @@ void ProgressBar::_notification(int p_what) {
Color font_color = get_theme_color(SNAME("font_color"));
draw_style_box(bg, Rect2(Point2(), get_size()));
+
float r = get_as_ratio();
- int mp = fg->get_minimum_size().width;
- int p = r * (get_size().width - mp);
- if (p > 0) {
- if (is_layout_rtl()) {
- draw_style_box(fg, Rect2(Point2(p, 0), Size2(fg->get_minimum_size().width, get_size().height)));
- } else {
- draw_style_box(fg, Rect2(Point2(0, 0), Size2(p + fg->get_minimum_size().width, get_size().height)));
- }
+
+ switch (mode) {
+ case FILL_BEGIN_TO_END:
+ case FILL_END_TO_BEGIN: {
+ int mp = fg->get_minimum_size().width;
+ int p = round(r * (get_size().width - mp));
+ // We want FILL_BEGIN_TO_END to map to right to left when UI layout is RTL,
+ // and left to right otherwise. And likewise for FILL_END_TO_BEGIN.
+ bool right_to_left = is_layout_rtl() ? (mode == FILL_BEGIN_TO_END) : (mode == FILL_END_TO_BEGIN);
+ if (p > 0) {
+ if (right_to_left) {
+ int p_remaining = round((1.0 - r) * (get_size().width - mp));
+ draw_style_box(fg, Rect2(Point2(p_remaining, 0), Size2(p + fg->get_minimum_size().width, get_size().height)));
+ } else {
+ draw_style_box(fg, Rect2(Point2(0, 0), Size2(p + fg->get_minimum_size().width, get_size().height)));
+ }
+ }
+ } break;
+ case FILL_TOP_TO_BOTTOM:
+ case FILL_BOTTOM_TO_TOP: {
+ int mp = fg->get_minimum_size().height;
+ int p = round(r * (get_size().height - mp));
+
+ if (p > 0) {
+ if (mode == FILL_TOP_TO_BOTTOM) {
+ draw_style_box(fg, Rect2(Point2(0, 0), Size2(get_size().width, p + fg->get_minimum_size().height)));
+ } else {
+ int p_remaining = round((1.0 - r) * (get_size().height - mp));
+ draw_style_box(fg, Rect2(Point2(0, p_remaining), Size2(get_size().width, p + fg->get_minimum_size().height)));
+ }
+ }
+ } break;
+ case FILL_MODE_MAX:
+ break;
}
if (percent_visible) {
@@ -88,6 +115,16 @@ void ProgressBar::_notification(int p_what) {
}
}
+void ProgressBar::set_fill_mode(int p_fill) {
+ ERR_FAIL_INDEX(p_fill, FILL_MODE_MAX);
+ mode = (FillMode)p_fill;
+ update();
+}
+
+int ProgressBar::get_fill_mode() {
+ return mode;
+}
+
void ProgressBar::set_percent_visible(bool p_visible) {
percent_visible = p_visible;
update();
@@ -98,10 +135,18 @@ bool ProgressBar::is_percent_visible() const {
}
void ProgressBar::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_fill_mode", "mode"), &ProgressBar::set_fill_mode);
+ ClassDB::bind_method(D_METHOD("get_fill_mode"), &ProgressBar::get_fill_mode);
ClassDB::bind_method(D_METHOD("set_percent_visible", "visible"), &ProgressBar::set_percent_visible);
ClassDB::bind_method(D_METHOD("is_percent_visible"), &ProgressBar::is_percent_visible);
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "fill_mode", PROPERTY_HINT_ENUM, "Begin to End,End to Begin,Top to Bottom,Bottom to Top"), "set_fill_mode", "get_fill_mode");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "percent_visible"), "set_percent_visible", "is_percent_visible");
+
+ BIND_ENUM_CONSTANT(FILL_BEGIN_TO_END);
+ BIND_ENUM_CONSTANT(FILL_END_TO_BEGIN);
+ BIND_ENUM_CONSTANT(FILL_TOP_TO_BOTTOM);
+ BIND_ENUM_CONSTANT(FILL_BOTTOM_TO_TOP);
}
ProgressBar::ProgressBar() {
diff --git a/scene/gui/progress_bar.h b/scene/gui/progress_bar.h
index 2d89163f78..5ba21ad7d5 100644
--- a/scene/gui/progress_bar.h
+++ b/scene/gui/progress_bar.h
@@ -43,11 +43,27 @@ protected:
static void _bind_methods();
public:
+ enum FillMode {
+ FILL_BEGIN_TO_END,
+ FILL_END_TO_BEGIN,
+ FILL_TOP_TO_BOTTOM,
+ FILL_BOTTOM_TO_TOP,
+ FILL_MODE_MAX
+ };
+
+ void set_fill_mode(int p_fill);
+ int get_fill_mode();
+
void set_percent_visible(bool p_visible);
bool is_percent_visible() const;
Size2 get_minimum_size() const override;
ProgressBar();
+
+private:
+ FillMode mode = FILL_BEGIN_TO_END;
};
+VARIANT_ENUM_CAST(ProgressBar::FillMode);
+
#endif // PROGRESS_BAR_H
diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp
index a151d3cb33..9d80b3cc0f 100644
--- a/scene/main/scene_tree.cpp
+++ b/scene/main/scene_tree.cpp
@@ -181,7 +181,7 @@ void SceneTree::_flush_ugc() {
argptrs[i] = &E->get()[i];
}
- call_group_flagsp(GROUP_CALL_REALTIME, E->key().group, E->key().call, argptrs, E->get().size());
+ call_group_flagsp(GROUP_CALL_DEFAULT, E->key().group, E->key().call, argptrs, E->get().size());
unique_group_calls.erase(E);
}
@@ -220,7 +220,7 @@ void SceneTree::call_group_flagsp(uint32_t p_call_flags, const StringName &p_gro
return;
}
- if (p_call_flags & GROUP_CALL_UNIQUE && !(p_call_flags & GROUP_CALL_REALTIME)) {
+ if (p_call_flags & GROUP_CALL_UNIQUE && p_call_flags & GROUP_CALL_DEFERRED) {
ERR_FAIL_COND(ugc_locked);
UGCall ug;
@@ -254,7 +254,7 @@ void SceneTree::call_group_flagsp(uint32_t p_call_flags, const StringName &p_gro
continue;
}
- if (p_call_flags & GROUP_CALL_REALTIME) {
+ if (!(p_call_flags & GROUP_CALL_DEFERRED)) {
Callable::CallError ce;
nodes[i]->callp(p_function, p_args, p_argcount, ce);
} else {
@@ -268,7 +268,7 @@ void SceneTree::call_group_flagsp(uint32_t p_call_flags, const StringName &p_gro
continue;
}
- if (p_call_flags & GROUP_CALL_REALTIME) {
+ if (!(p_call_flags & GROUP_CALL_DEFERRED)) {
Callable::CallError ce;
nodes[i]->callp(p_function, p_args, p_argcount, ce);
} else {
@@ -307,7 +307,7 @@ void SceneTree::notify_group_flags(uint32_t p_call_flags, const StringName &p_gr
continue;
}
- if (p_call_flags & GROUP_CALL_REALTIME) {
+ if (!(p_call_flags & GROUP_CALL_DEFERRED)) {
nodes[i]->notification(p_notification);
} else {
MessageQueue::get_singleton()->push_notification(nodes[i], p_notification);
@@ -320,7 +320,7 @@ void SceneTree::notify_group_flags(uint32_t p_call_flags, const StringName &p_gr
continue;
}
- if (p_call_flags & GROUP_CALL_REALTIME) {
+ if (!(p_call_flags & GROUP_CALL_DEFERRED)) {
nodes[i]->notification(p_notification);
} else {
MessageQueue::get_singleton()->push_notification(nodes[i], p_notification);
@@ -358,7 +358,7 @@ void SceneTree::set_group_flags(uint32_t p_call_flags, const StringName &p_group
continue;
}
- if (p_call_flags & GROUP_CALL_REALTIME) {
+ if (!(p_call_flags & GROUP_CALL_DEFERRED)) {
nodes[i]->set(p_name, p_value);
} else {
MessageQueue::get_singleton()->push_set(nodes[i], p_name, p_value);
@@ -371,7 +371,7 @@ void SceneTree::set_group_flags(uint32_t p_call_flags, const StringName &p_group
continue;
}
- if (p_call_flags & GROUP_CALL_REALTIME) {
+ if (!(p_call_flags & GROUP_CALL_DEFERRED)) {
nodes[i]->set(p_name, p_value);
} else {
MessageQueue::get_singleton()->push_set(nodes[i], p_name, p_value);
@@ -390,7 +390,7 @@ void SceneTree::notify_group(const StringName &p_group, int p_notification) {
}
void SceneTree::set_group(const StringName &p_group, const String &p_name, const Variant &p_value) {
- set_group_flags(0, p_group, p_name, p_value);
+ set_group_flags(GROUP_CALL_DEFAULT, p_group, p_name, p_value);
}
void SceneTree::initialize() {
@@ -413,7 +413,7 @@ bool SceneTree::physics_process(double p_time) {
emit_signal(SNAME("physics_frame"));
_notify_group_pause(SNAME("physics_process_internal"), Node::NOTIFICATION_INTERNAL_PHYSICS_PROCESS);
- call_group_flags(GROUP_CALL_REALTIME, SNAME("_picking_viewports"), SNAME("_process_picking"));
+ call_group(SNAME("_picking_viewports"), SNAME("_process_picking"));
_notify_group_pause(SNAME("physics_process"), Node::NOTIFICATION_PHYSICS_PROCESS);
_flush_ugc();
MessageQueue::get_singleton()->flush(); //small little hack
@@ -944,7 +944,7 @@ void SceneTree::_call_group(const Variant **p_args, int p_argcount, Callable::Ca
StringName group = *p_args[0];
StringName method = *p_args[1];
- call_group_flagsp(0, group, method, p_args + 2, p_argcount - 2);
+ call_group_flagsp(GROUP_CALL_DEFAULT, group, method, p_args + 2, p_argcount - 2);
}
int64_t SceneTree::get_frame() const {
@@ -1277,7 +1277,7 @@ void SceneTree::_bind_methods() {
BIND_ENUM_CONSTANT(GROUP_CALL_DEFAULT);
BIND_ENUM_CONSTANT(GROUP_CALL_REVERSE);
- BIND_ENUM_CONSTANT(GROUP_CALL_REALTIME);
+ BIND_ENUM_CONSTANT(GROUP_CALL_DEFERRED);
BIND_ENUM_CONSTANT(GROUP_CALL_UNIQUE);
}
diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h
index 9d7757e0a3..d633fb38d0 100644
--- a/scene/main/scene_tree.h
+++ b/scene/main/scene_tree.h
@@ -151,7 +151,6 @@ private:
int collision_debug_contacts;
void _change_scene(Node *p_to);
- //void _call_group(uint32_t p_call_flags,const StringName& p_group,const StringName& p_function,const Variant& p_arg1,const Variant& p_arg2);
List<Ref<SceneTreeTimer>> timers;
List<Ref<Tween>> tweens;
@@ -225,7 +224,7 @@ public:
enum GroupCallFlags {
GROUP_CALL_DEFAULT = 0,
GROUP_CALL_REVERSE = 1,
- GROUP_CALL_REALTIME = 2,
+ GROUP_CALL_DEFERRED = 2,
GROUP_CALL_UNIQUE = 4,
};
@@ -235,17 +234,20 @@ public:
void notify_group_flags(uint32_t p_call_flags, const StringName &p_group, int p_notification);
void set_group_flags(uint32_t p_call_flags, const StringName &p_group, const String &p_name, const Variant &p_value);
+ // `notify_group()` is immediate by default since Godot 4.0.
void notify_group(const StringName &p_group, int p_notification);
+ // `set_group()` is immediate by default since Godot 4.0.
void set_group(const StringName &p_group, const String &p_name, const Variant &p_value);
template <typename... VarArgs>
+ // `call_group()` is immediate by default since Godot 4.0.
void call_group(const StringName &p_group, const StringName &p_function, VarArgs... p_args) {
Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported.
const Variant *argptrs[sizeof...(p_args) + 1];
for (uint32_t i = 0; i < sizeof...(p_args); i++) {
argptrs[i] = &args[i];
}
- call_group_flagsp(0, p_group, p_function, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args));
+ call_group_flagsp(GROUP_CALL_DEFAULT, p_group, p_function, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args));
}
template <typename... VarArgs>
diff --git a/scene/main/shader_globals_override.cpp b/scene/main/shader_globals_override.cpp
index 7c689bd436..ed08c45a01 100644
--- a/scene/main/shader_globals_override.cpp
+++ b/scene/main/shader_globals_override.cpp
@@ -267,7 +267,7 @@ void ShaderGlobalsOverride::_notification(int p_what) {
remove_from_group(SceneStringNames::get_singleton()->shader_overrides_group_active);
remove_from_group(SceneStringNames::get_singleton()->shader_overrides_group);
- get_tree()->call_group(SceneStringNames::get_singleton()->shader_overrides_group, "_activate"); //another may want to activate when this is removed
+ get_tree()->call_group_flags(SceneTree::GROUP_CALL_DEFERRED, SceneStringNames::get_singleton()->shader_overrides_group, "_activate"); //another may want to activate when this is removed
active = false;
} break;
}
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index d7e58ed707..e4037c2843 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -2186,7 +2186,7 @@ void Viewport::_gui_control_grab_focus(Control *p_control) {
// No need for change.
return;
}
- get_tree()->call_group_flags(SceneTree::GROUP_CALL_REALTIME, "_viewports", "_gui_remove_focus_for_window", (Node *)get_base_window());
+ get_tree()->call_group("_viewports", "_gui_remove_focus_for_window", (Node *)get_base_window());
gui.key_focus = p_control;
emit_signal(SNAME("gui_focus_changed"), p_control);
p_control->notification(Control::NOTIFICATION_FOCUS_ENTER);
diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp
index 16fce5e08a..27e1590940 100644
--- a/scene/resources/material.cpp
+++ b/scene/resources/material.cpp
@@ -84,7 +84,7 @@ void Material::inspect_native_shader_code() {
SceneTree *st = Object::cast_to<SceneTree>(OS::get_singleton()->get_main_loop());
RID shader = get_shader_rid();
if (st && shader.is_valid()) {
- st->call_group("_native_shader_source_visualizer", "_inspect_shader", shader);
+ st->call_group_flags(SceneTree::GROUP_CALL_DEFERRED, "_native_shader_source_visualizer", "_inspect_shader", shader);
}
}
diff --git a/tests/core/math/test_geometry_2d.h b/tests/core/math/test_geometry_2d.h
index 3487e4d7e8..db4e6e2177 100644
--- a/tests/core/math/test_geometry_2d.h
+++ b/tests/core/math/test_geometry_2d.h
@@ -135,7 +135,7 @@ TEST_CASE("[Geometry2D] Line intersection") {
"Parallel lines should not intersect.");
}
-TEST_CASE("[Geometry2D] Segment intersection.") {
+TEST_CASE("[Geometry2D] Segment intersection") {
Vector2 r;
CHECK(Geometry2D::segment_intersects_segment(Vector2(-1, 1), Vector2(1, -1), Vector2(1, 1), Vector2(-1, -1), &r));
@@ -148,6 +148,10 @@ TEST_CASE("[Geometry2D] Segment intersection.") {
Geometry2D::segment_intersects_segment(Vector2(-1, 1), Vector2(1, -1), Vector2(0, 1), Vector2(2, -1), &r),
"Parallel segments should not intersect.");
+ CHECK_FALSE_MESSAGE(
+ Geometry2D::segment_intersects_segment(Vector2(1, 2), Vector2(3, 2), Vector2(0, 2), Vector2(-2, 2), &r),
+ "Non-overlapping collinear segments should not intersect.");
+
CHECK_MESSAGE(
Geometry2D::segment_intersects_segment(Vector2(0, 0), Vector2(0, 1), Vector2(0, 0), Vector2(1, 0), &r),
"Touching segments should intersect.");
@@ -159,11 +163,114 @@ TEST_CASE("[Geometry2D] Segment intersection.") {
CHECK(r.is_equal_approx(Vector2(0, 0)));
}
+TEST_CASE("[Geometry2D] Segment intersection with circle") {
+ real_t minus_one = -1.0;
+ real_t zero = 0.0;
+ real_t one_quarter = 0.25;
+ real_t three_quarters = 0.75;
+ real_t one = 1.0;
+
+ CHECK_MESSAGE(
+ Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(0, 0), Vector2(4, 0), Vector2(0, 0), 1.0), one_quarter),
+ "Segment from inside to outside of circle should intersect it.");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(4, 0), Vector2(0, 0), Vector2(0, 0), 1.0), three_quarters),
+ "Segment from outside to inside of circle should intersect it.");
+
+ CHECK_MESSAGE(
+ Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(-2, 0), Vector2(2, 0), Vector2(0, 0), 1.0), one_quarter),
+ "Segment running through circle should intersect it.");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(2, 0), Vector2(-2, 0), Vector2(0, 0), 1.0), one_quarter),
+ "Segment running through circle should intersect it.");
+
+ CHECK_MESSAGE(
+ Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(0, 0), Vector2(1, 0), Vector2(0, 0), 1.0), one),
+ "Segment starting inside the circle and ending on the circle should intersect it");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(1, 0), Vector2(0, 0), Vector2(0, 0), 1.0), zero),
+ "Segment starting on the circle and going inwards should intersect it");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(1, 0), Vector2(2, 0), Vector2(0, 0), 1.0), zero),
+ "Segment starting on the circle and going outwards should intersect it");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(2, 0), Vector2(1, 0), Vector2(0, 0), 1.0), one),
+ "Segment starting outside the circle and ending on the circle intersect it");
+
+ CHECK_MESSAGE(
+ Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(-1, 0), Vector2(1, 0), Vector2(0, 0), 2.0), minus_one),
+ "Segment completely within the circle should not intersect it");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(1, 0), Vector2(-1, 0), Vector2(0, 0), 2.0), minus_one),
+ "Segment completely within the circle should not intersect it");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(2, 0), Vector2(3, 0), Vector2(0, 0), 1.0), minus_one),
+ "Segment completely outside the circle should not intersect it");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(3, 0), Vector2(2, 0), Vector2(0, 0), 1.0), minus_one),
+ "Segment completely outside the circle should not intersect it");
+}
+
+TEST_CASE("[Geometry2D] Segment intersection with polygon") {
+ Vector<Point2> a;
+
+ a.push_back(Point2(-2, 2));
+ a.push_back(Point2(3, 4));
+ a.push_back(Point2(1, 1));
+ a.push_back(Point2(2, -2));
+ a.push_back(Point2(-1, -1));
+
+ CHECK_MESSAGE(
+ Geometry2D::is_segment_intersecting_polygon(Vector2(0, 2), Vector2(2, 2), a),
+ "Segment from inside to outside of polygon should intersect it.");
+ CHECK_MESSAGE(
+ Geometry2D::is_segment_intersecting_polygon(Vector2(2, 2), Vector2(0, 2), a),
+ "Segment from outside to inside of polygon should intersect it.");
+
+ CHECK_MESSAGE(
+ Geometry2D::is_segment_intersecting_polygon(Vector2(2, 4), Vector2(3, 3), a),
+ "Segment running through polygon should intersect it.");
+ CHECK_MESSAGE(
+ Geometry2D::is_segment_intersecting_polygon(Vector2(3, 3), Vector2(2, 4), a),
+ "Segment running through polygon should intersect it.");
+
+ CHECK_MESSAGE(
+ Geometry2D::is_segment_intersecting_polygon(Vector2(0, 0), Vector2(1, 1), a),
+ "Segment starting inside the polygon and ending on the polygon should intersect it");
+ CHECK_MESSAGE(
+ Geometry2D::is_segment_intersecting_polygon(Vector2(1, 1), Vector2(0, 0), a),
+ "Segment starting on the polygon and going inwards should intersect it");
+ CHECK_MESSAGE(
+ Geometry2D::is_segment_intersecting_polygon(Vector2(-2, 2), Vector2(-2, -1), a),
+ "Segment starting on the polygon and going outwards should intersect it");
+ CHECK_MESSAGE(
+ Geometry2D::is_segment_intersecting_polygon(Vector2(-2, 1), Vector2(-2, 2), a),
+ "Segment starting outside the polygon and ending on the polygon intersect it");
+
+ CHECK_FALSE_MESSAGE(
+ Geometry2D::is_segment_intersecting_polygon(Vector2(-1, 2), Vector2(1, -1), a),
+ "Segment completely within the polygon should not intersect it");
+ CHECK_FALSE_MESSAGE(
+ Geometry2D::is_segment_intersecting_polygon(Vector2(1, -1), Vector2(-1, 2), a),
+ "Segment completely within the polygon should not intersect it");
+ CHECK_FALSE_MESSAGE(
+ Geometry2D::is_segment_intersecting_polygon(Vector2(2, 2), Vector2(2, -1), a),
+ "Segment completely outside the polygon should not intersect it");
+ CHECK_FALSE_MESSAGE(
+ Geometry2D::is_segment_intersecting_polygon(Vector2(2, -1), Vector2(2, 2), a),
+ "Segment completely outside the polygon should not intersect it");
+}
+
TEST_CASE("[Geometry2D] Closest point to segment") {
Vector2 s[] = { Vector2(-4, -4), Vector2(4, 4) };
CHECK(Geometry2D::get_closest_point_to_segment(Vector2(4.1, 4.1), s).is_equal_approx(Vector2(4, 4)));
CHECK(Geometry2D::get_closest_point_to_segment(Vector2(-4.1, -4.1), s).is_equal_approx(Vector2(-4, -4)));
CHECK(Geometry2D::get_closest_point_to_segment(Vector2(-1, 1), s).is_equal_approx(Vector2(0, 0)));
+
+ Vector2 t[] = { Vector2(1, -2), Vector2(1, -2) };
+ CHECK_MESSAGE(
+ Geometry2D::get_closest_point_to_segment(Vector2(-3, 4), t).is_equal_approx(Vector2(1, -2)),
+ "Line segment is only a single point. This point should be the closest.");
}
TEST_CASE("[Geometry2D] Closest point to uncapped segment") {
@@ -186,6 +293,30 @@ TEST_CASE("[Geometry2D] Closest points between segments") {
Geometry2D::get_closest_points_between_segments(Vector2(-1, 1), Vector2(1, -1), Vector2(1, 1), Vector2(-1, -1), c1, c2);
CHECK(c1.is_equal_approx(Vector2(0, 0)));
CHECK(c2.is_equal_approx(Vector2(0, 0)));
+
+ Geometry2D::get_closest_points_between_segments(Vector2(-3, 4), Vector2(-3, 4), Vector2(-4, 3), Vector2(-2, 3), c1, c2);
+ CHECK_MESSAGE(
+ c1.is_equal_approx(Vector2(-3, 4)),
+ "1st line segment is only a point, this point should be the closest point to the 2nd line segment.");
+ CHECK_MESSAGE(
+ c2.is_equal_approx(Vector2(-3, 3)),
+ "1st line segment is only a point, this should not matter when determining the closest point on the 2nd line segment.");
+
+ Geometry2D::get_closest_points_between_segments(Vector2(-4, 3), Vector2(-2, 3), Vector2(-3, 4), Vector2(-3, 4), c1, c2);
+ CHECK_MESSAGE(
+ c1.is_equal_approx(Vector2(-3, 3)),
+ "2nd line segment is only a point, this should not matter when determining the closest point on the 1st line segment.");
+ CHECK_MESSAGE(
+ c2.is_equal_approx(Vector2(-3, 4)),
+ "2nd line segment is only a point, this point should be the closest point to the 1st line segment.");
+
+ Geometry2D::get_closest_points_between_segments(Vector2(5, -4), Vector2(5, -4), Vector2(-2, 1), Vector2(-2, 1), c1, c2);
+ CHECK_MESSAGE(
+ c1.is_equal_approx(Vector2(5, -4)),
+ "Both line segments are only a point. On the 1st line segment, that point should be the closest point to the 2nd line segment.");
+ CHECK_MESSAGE(
+ c2.is_equal_approx(Vector2(-2, 1)),
+ "Both line segments are only a point. On the 2nd line segment, that point should be the closest point to the 1st line segment.");
}
TEST_CASE("[Geometry2D] Make atlas") {
@@ -562,6 +693,174 @@ TEST_CASE("[Geometry2D] Clip polyline with polygon") {
CHECK(r[1][1].is_equal_approx(Vector2(55, 70)));
}
}
+
+TEST_CASE("[Geometry2D] Convex hull") {
+ Vector<Point2> a;
+ Vector<Point2> r;
+
+ a.push_back(Point2(-4, -8));
+ a.push_back(Point2(-10, -4));
+ a.push_back(Point2(8, 2));
+ a.push_back(Point2(-6, 10));
+ a.push_back(Point2(-12, 4));
+ a.push_back(Point2(10, -8));
+ a.push_back(Point2(4, 8));
+
+ SUBCASE("[Geometry2D] No points") {
+ r = Geometry2D::convex_hull(Vector<Vector2>());
+
+ CHECK_MESSAGE(r.is_empty(), "The convex hull should be empty if there are no input points.");
+ }
+
+ SUBCASE("[Geometry2D] Single point") {
+ Vector<Point2> b;
+ b.push_back(Point2(4, -3));
+
+ r = Geometry2D::convex_hull(b);
+ REQUIRE_MESSAGE(r.size() == 1, "Convex hull should contain 1 point.");
+ CHECK(r[0].is_equal_approx(b[0]));
+ }
+
+ SUBCASE("[Geometry2D] All points form the convex hull") {
+ r = Geometry2D::convex_hull(a);
+ REQUIRE_MESSAGE(r.size() == 8, "Convex hull should contain 8 points.");
+ CHECK(r[0].is_equal_approx(Point2(-12, 4)));
+ CHECK(r[1].is_equal_approx(Point2(-10, -4)));
+ CHECK(r[2].is_equal_approx(Point2(-4, -8)));
+ CHECK(r[3].is_equal_approx(Point2(10, -8)));
+ CHECK(r[4].is_equal_approx(Point2(8, 2)));
+ CHECK(r[5].is_equal_approx(Point2(4, 8)));
+ CHECK(r[6].is_equal_approx(Point2(-6, 10)));
+ CHECK(r[7].is_equal_approx(Point2(-12, 4)));
+ }
+
+ SUBCASE("[Geometry2D] Add extra points inside original convex hull") {
+ a.push_back(Point2(-4, -8));
+ a.push_back(Point2(0, 0));
+ a.push_back(Point2(0, 8));
+ a.push_back(Point2(-10, -3));
+ a.push_back(Point2(9, -4));
+ a.push_back(Point2(6, 4));
+
+ r = Geometry2D::convex_hull(a);
+ REQUIRE_MESSAGE(r.size() == 8, "Convex hull should contain 8 points.");
+ CHECK(r[0].is_equal_approx(Point2(-12, 4)));
+ CHECK(r[1].is_equal_approx(Point2(-10, -4)));
+ CHECK(r[2].is_equal_approx(Point2(-4, -8)));
+ CHECK(r[3].is_equal_approx(Point2(10, -8)));
+ CHECK(r[4].is_equal_approx(Point2(8, 2)));
+ CHECK(r[5].is_equal_approx(Point2(4, 8)));
+ CHECK(r[6].is_equal_approx(Point2(-6, 10)));
+ CHECK(r[7].is_equal_approx(Point2(-12, 4)));
+ }
+
+ SUBCASE("[Geometry2D] Add extra points on border of original convex hull") {
+ a.push_back(Point2(9, -3));
+ a.push_back(Point2(-2, -8));
+
+ r = Geometry2D::convex_hull(a);
+ REQUIRE_MESSAGE(r.size() == 8, "Convex hull should contain 8 points.");
+ CHECK(r[0].is_equal_approx(Point2(-12, 4)));
+ CHECK(r[1].is_equal_approx(Point2(-10, -4)));
+ CHECK(r[2].is_equal_approx(Point2(-4, -8)));
+ CHECK(r[3].is_equal_approx(Point2(10, -8)));
+ CHECK(r[4].is_equal_approx(Point2(8, 2)));
+ CHECK(r[5].is_equal_approx(Point2(4, 8)));
+ CHECK(r[6].is_equal_approx(Point2(-6, 10)));
+ CHECK(r[7].is_equal_approx(Point2(-12, 4)));
+ }
+
+ SUBCASE("[Geometry2D] Add extra points outside border of original convex hull") {
+ a.push_back(Point2(-11, -1));
+ a.push_back(Point2(7, 6));
+
+ r = Geometry2D::convex_hull(a);
+ REQUIRE_MESSAGE(r.size() == 10, "Convex hull should contain 10 points.");
+ CHECK(r[0].is_equal_approx(Point2(-12, 4)));
+ CHECK(r[1].is_equal_approx(Point2(-11, -1)));
+ CHECK(r[2].is_equal_approx(Point2(-10, -4)));
+ CHECK(r[3].is_equal_approx(Point2(-4, -8)));
+ CHECK(r[4].is_equal_approx(Point2(10, -8)));
+ CHECK(r[5].is_equal_approx(Point2(8, 2)));
+ CHECK(r[6].is_equal_approx(Point2(7, 6)));
+ CHECK(r[7].is_equal_approx(Point2(4, 8)));
+ CHECK(r[8].is_equal_approx(Point2(-6, 10)));
+ CHECK(r[9].is_equal_approx(Point2(-12, 4)));
+ }
+}
+
+TEST_CASE("[Geometry2D] Bresenham line") {
+ Vector<Vector2i> r;
+
+ SUBCASE("[Geometry2D] Single point") {
+ r = Geometry2D::bresenham_line(Point2i(0, 0), Point2i(0, 0));
+
+ REQUIRE_MESSAGE(r.size() == 1, "The Bresenham line should contain exactly one point.");
+ CHECK(r[0] == Vector2i(0, 0));
+ }
+
+ SUBCASE("[Geometry2D] Line parallel to x-axis") {
+ r = Geometry2D::bresenham_line(Point2i(1, 2), Point2i(5, 2));
+
+ REQUIRE_MESSAGE(r.size() == 5, "The Bresenham line should contain exactly five points.");
+ CHECK(r[0] == Vector2i(1, 2));
+ CHECK(r[1] == Vector2i(2, 2));
+ CHECK(r[2] == Vector2i(3, 2));
+ CHECK(r[3] == Vector2i(4, 2));
+ CHECK(r[4] == Vector2i(5, 2));
+ }
+
+ SUBCASE("[Geometry2D] 45 degree line from the origin") {
+ r = Geometry2D::bresenham_line(Point2i(0, 0), Point2i(4, 4));
+
+ REQUIRE_MESSAGE(r.size() == 5, "The Bresenham line should contain exactly five points.");
+ CHECK(r[0] == Vector2i(0, 0));
+ CHECK(r[1] == Vector2i(1, 1));
+ CHECK(r[2] == Vector2i(2, 2));
+ CHECK(r[3] == Vector2i(3, 3));
+ CHECK(r[4] == Vector2i(4, 4));
+ }
+
+ SUBCASE("[Geometry2D] Sloped line going up one unit") {
+ r = Geometry2D::bresenham_line(Point2i(0, 0), Point2i(4, 1));
+
+ REQUIRE_MESSAGE(r.size() == 5, "The Bresenham line should contain exactly five points.");
+ CHECK(r[0] == Vector2i(0, 0));
+ CHECK(r[1] == Vector2i(1, 0));
+ CHECK(r[2] == Vector2i(2, 0));
+ CHECK(r[3] == Vector2i(3, 1));
+ CHECK(r[4] == Vector2i(4, 1));
+ }
+
+ SUBCASE("[Geometry2D] Sloped line going up two units") {
+ r = Geometry2D::bresenham_line(Point2i(0, 0), Point2i(4, 2));
+
+ REQUIRE_MESSAGE(r.size() == 5, "The Bresenham line should contain exactly five points.");
+ CHECK(r[0] == Vector2i(0, 0));
+ CHECK(r[1] == Vector2i(1, 0));
+ CHECK(r[2] == Vector2i(2, 1));
+ CHECK(r[3] == Vector2i(3, 1));
+ CHECK(r[4] == Vector2i(4, 2));
+ }
+
+ SUBCASE("[Geometry2D] Long sloped line") {
+ r = Geometry2D::bresenham_line(Point2i(0, 0), Point2i(11, 5));
+
+ REQUIRE_MESSAGE(r.size() == 12, "The Bresenham line should contain exactly twelve points.");
+ CHECK(r[0] == Vector2i(0, 0));
+ CHECK(r[1] == Vector2i(1, 0));
+ CHECK(r[2] == Vector2i(2, 1));
+ CHECK(r[3] == Vector2i(3, 1));
+ CHECK(r[4] == Vector2i(4, 2));
+ CHECK(r[5] == Vector2i(5, 2));
+ CHECK(r[6] == Vector2i(6, 3));
+ CHECK(r[7] == Vector2i(7, 3));
+ CHECK(r[8] == Vector2i(8, 4));
+ CHECK(r[9] == Vector2i(9, 4));
+ CHECK(r[10] == Vector2i(10, 5));
+ CHECK(r[11] == Vector2i(11, 5));
+ }
+}
} // namespace TestGeometry2D
#endif // TEST_GEOMETRY_2D_H