From 46d60aacd8f0218c005b3b4be15caa15c8ff26af Mon Sep 17 00:00:00 2001 From: Falk David Date: Wed, 19 Jul 2023 16:37:50 +0200 Subject: [PATCH] Array Utils: Add `find_all_ranges` function This functions returns a vector of all the index ranges for which the span contains consecutive values that equal to the value given. Pull Request: https://projects.blender.org/blender/blender/pulls/110265 --- source/blender/blenlib/BLI_array_utils.hh | 25 ++++++++++ .../blenlib/tests/BLI_array_utils_test.cc | 50 +++++++++++++++++++ 2 files changed, 75 insertions(+) diff --git a/source/blender/blenlib/BLI_array_utils.hh b/source/blender/blenlib/BLI_array_utils.hh index 0f10337664d..fd37816cc22 100644 --- a/source/blender/blenlib/BLI_array_utils.hh +++ b/source/blender/blenlib/BLI_array_utils.hh @@ -169,4 +169,29 @@ inline BooleanMix booleans_mix_calc(const VArray &varray) return booleans_mix_calc(varray, varray.index_range()); } +/** + * Finds all the index ranges for which consecutive values in \a span equal \a value. + */ +template inline Vector find_all_ranges(const Span span, const T &value) +{ + if (span.is_empty()) { + return Vector(); + } + Vector ranges; + int64_t length = (span.first() == value) ? 1 : 0; + for (const int64_t i : span.index_range().drop_front(1)) { + if (span[i - 1] == value && span[i] != value) { + ranges.append(IndexRange(i - length, length)); + length = 0; + } + else if (span[i] == value) { + length++; + } + } + if (length > 0) { + ranges.append(IndexRange(span.size() - length, length)); + } + return ranges; +} + } // namespace blender::array_utils diff --git a/source/blender/blenlib/tests/BLI_array_utils_test.cc b/source/blender/blenlib/tests/BLI_array_utils_test.cc index 71da95bedd6..7d608f9ea5d 100644 --- a/source/blender/blenlib/tests/BLI_array_utils_test.cc +++ b/source/blender/blenlib/tests/BLI_array_utils_test.cc @@ -5,6 +5,7 @@ #include "testing/testing.h" #include "BLI_array_utils.h" +#include "BLI_array_utils.hh" #include "BLI_utildefines.h" #include "BLI_utildefines_stack.h" @@ -241,3 +242,52 @@ TEST(array_utils, DeduplicateOrdered3) } #undef DEDUPLICATE_ORDERED_TEST + +static void find_all_ranges_test(const blender::Span data, + const blender::Span data_cmp) +{ + using namespace blender; + Vector ranges = array_utils::find_all_ranges(data, true); + EXPECT_EQ(ranges.size(), data_cmp.size()); + EXPECT_EQ_ARRAY(data_cmp.data(), ranges.as_span().data(), data_cmp.size()); +} + +TEST(array_utils, FindAllRanges1) +{ + using namespace blender; + const std::array data = {false}; + Vector ranges = array_utils::find_all_ranges(Span(data.data(), data.size()), true); + EXPECT_EQ(ranges.size(), 0); +} + +TEST(array_utils, FindAllRanges2) +{ + using namespace blender; + const std::array data = {true, true, true}; + const std::array data_cmp = {IndexRange(0, 3)}; + find_all_ranges_test(data, data_cmp); +} + +TEST(array_utils, FindAllRanges3) +{ + using namespace blender; + const std::array data = {true, false}; + const std::array data_cmp = {IndexRange(0, 1)}; + find_all_ranges_test(data, data_cmp); +} + +TEST(array_utils, FindAllRanges4) +{ + using namespace blender; + const std::array data = {false, true}; + const std::array data_cmp = {IndexRange(1, 1)}; + find_all_ranges_test(data, data_cmp); +} + +TEST(array_utils, FindAllRanges5) +{ + using namespace blender; + const std::array data = {true, false, false, true, true, false, true}; + const std::array data_cmp = {IndexRange(0, 1), IndexRange(3, 2), IndexRange(6, 1)}; + find_all_ranges_test(data, data_cmp); +}