diff --git a/core/array.cpp b/core/array.cpp index 108d9f7386e..ac30df08bcf 100644 --- a/core/array.cpp +++ b/core/array.cpp @@ -222,6 +222,63 @@ Array Array::duplicate(bool p_deep) const { return new_arr; } + +int Array::_fix_slice_index(int p_index, int p_arr_len, int p_top_mod) { + p_index = CLAMP(p_index, -p_arr_len, p_arr_len + p_top_mod); + if (p_index < 0) { + p_index = (p_index % p_arr_len + p_arr_len) % p_arr_len; // positive modulo + } + return p_index; +} + +int Array::_clamp_index(int p_index) const { + return CLAMP(p_index, -size() + 1, size() - 1); +} + +#define ARRAY_GET_DEEP(idx, is_deep) is_deep ? get(idx).duplicate(is_deep) : get(idx) + +Array Array::slice(int p_begin, int p_end, int p_step, bool p_deep) const { // like python, but inclusive on upper bound + Array new_arr; + + p_begin = Array::_fix_slice_index(p_begin, size(), -1); // can't start out of range + p_end = Array::_fix_slice_index(p_end, size(), 0); + + int x = p_begin; + int new_arr_i = 0; + + ERR_FAIL_COND_V(p_step == 0, new_arr); + if (Array::_clamp_index(p_begin) == Array::_clamp_index(p_end)) { // don't include element twice + new_arr.resize(1); + // new_arr[0] = 1; + new_arr[0] = ARRAY_GET_DEEP(Array::_clamp_index(p_begin), p_deep); + return new_arr; + } else { + int element_count = ceil((int)MAX(0, (p_end - p_begin) / p_step)) + 1; + if (element_count == 1) { // delta going in wrong direction to reach end + new_arr.resize(0); + return new_arr; + } + new_arr.resize(element_count); + } + + // if going backwards, have to have a different terminating condition + if (p_step < 0) { + while (x >= p_end) { + new_arr[new_arr_i] = ARRAY_GET_DEEP(Array::_clamp_index(x), p_deep); + x += p_step; + new_arr_i += 1; + } + } else if (p_step > 0) { + while (x <= p_end) { + new_arr[new_arr_i] = ARRAY_GET_DEEP(Array::_clamp_index(x), p_deep); + x += p_step; + new_arr_i += 1; + } + } + + return new_arr; +} + struct _ArrayVariantSort { _FORCE_INLINE_ bool operator()(const Variant &p_l, const Variant &p_r) const { diff --git a/core/array.h b/core/array.h index d4e937a486b..7a754d53ead 100644 --- a/core/array.h +++ b/core/array.h @@ -44,6 +44,9 @@ class Array { void _ref(const Array &p_from) const; void _unref() const; + int _clamp_index(int p_index) const; + static int _fix_slice_index(int p_index, int p_arr_len, int p_top_mod); + public: Variant &operator[](int p_idx); const Variant &operator[](int p_idx) const; @@ -91,6 +94,8 @@ public: Array duplicate(bool p_deep = false) const; + Array slice(int p_begin, int p_end, int p_step = 1, bool p_deep = false) const; + Variant min() const; Variant max() const; diff --git a/core/variant_call.cpp b/core/variant_call.cpp index 5e3876d6a4b..c288c50abfc 100644 --- a/core/variant_call.cpp +++ b/core/variant_call.cpp @@ -534,6 +534,7 @@ struct _VariantCall { VCALL_LOCALMEM2R(Array, bsearch); VCALL_LOCALMEM4R(Array, bsearch_custom); VCALL_LOCALMEM1R(Array, duplicate); + VCALL_LOCALMEM4R(Array, slice); VCALL_LOCALMEM0(Array, invert); VCALL_LOCALMEM0R(Array, max); VCALL_LOCALMEM0R(Array, min); @@ -1759,6 +1760,7 @@ void register_variant_methods() { ADDFUNC4R(ARRAY, INT, Array, bsearch_custom, NIL, "value", OBJECT, "obj", STRING, "func", BOOL, "before", varray(true)); ADDFUNC0NC(ARRAY, NIL, Array, invert, varray()); ADDFUNC1R(ARRAY, ARRAY, Array, duplicate, BOOL, "deep", varray(false)); + ADDFUNC4R(ARRAY, ARRAY, Array, slice, INT, "begin", INT, "end", INT, "step", BOOL, "deep", varray(1, false)); ADDFUNC0R(ARRAY, NIL, Array, max, varray()); ADDFUNC0R(ARRAY, NIL, Array, min, varray()); diff --git a/doc/classes/Array.xml b/doc/classes/Array.xml index 130908b8420..45d40ccceaa 100644 --- a/doc/classes/Array.xml +++ b/doc/classes/Array.xml @@ -124,6 +124,21 @@ [b]Note:[/b] Calling [method bsearch] on an unsorted array results in unexpected behavior. + + + + + + + + + + + + + Duplicates the subset described in the function and returns it in an array, deeply copying the array if [code]deep[/code] is true. Lower and upper index are inclusive, with the [code]step[/code] describing the change between indices while slicing. + + Clears the array. This is equivalent to using [method resize] with a size of [code]0[/code]. diff --git a/modules/gdnative/gdnative/array.cpp b/modules/gdnative/gdnative/array.cpp index 1ef8e9f9003..e97a75cca80 100644 --- a/modules/gdnative/gdnative/array.cpp +++ b/modules/gdnative/gdnative/array.cpp @@ -327,6 +327,15 @@ godot_array GDAPI godot_array_duplicate(const godot_array *p_self, const godot_b return res; } +godot_array GDAPI godot_array_slice(const godot_array *p_self, const godot_int p_begin, const godot_int p_end, const godot_int p_step, const godot_bool p_deep) { + const Array *self = (const Array *)p_self; + godot_array res; + Array *val = (Array *)&res; + memnew_placement(val, Array); + *val = self->slice(p_begin, p_end, p_step, p_deep); + return res; +} + godot_variant GDAPI godot_array_max(const godot_array *p_self) { const Array *self = (const Array *)p_self; godot_variant v; diff --git a/modules/gdnative/gdnative_api.json b/modules/gdnative/gdnative_api.json index 03258584ce7..55ba4ecc1ef 100644 --- a/modules/gdnative/gdnative_api.json +++ b/modules/gdnative/gdnative_api.json @@ -80,6 +80,17 @@ ["const godot_vector2 *", "p_self"], ["const godot_vector2 *", "p_to"] ] + }, + { + "name": "godot_array_slice", + "return_type": "godot_array", + "arguments": [ + ["const godot_array *", "p_self"], + ["const godot_int", "p_begin"], + ["const godot_int", "p_end"], + ["const godot_int", "p_step"], + ["const godot_bool", "p_deep"] + ] } ] }, diff --git a/modules/gdnative/include/gdnative/array.h b/modules/gdnative/include/gdnative/array.h index 10ef8a73d2a..2e3ce580335 100644 --- a/modules/gdnative/include/gdnative/array.h +++ b/modules/gdnative/include/gdnative/array.h @@ -132,6 +132,8 @@ void GDAPI godot_array_destroy(godot_array *p_self); godot_array GDAPI godot_array_duplicate(const godot_array *p_self, const godot_bool p_deep); +godot_array GDAPI godot_array_slice(const godot_array *p_self, const godot_int p_begin, const godot_int p_end, const godot_int p_delta, const godot_bool p_deep); + godot_variant GDAPI godot_array_max(const godot_array *p_self); godot_variant GDAPI godot_array_min(const godot_array *p_self);