From c9dac7e77c78911e22edf105922f8ad377ee41f0 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 24 May 2023 09:54:25 -0400 Subject: [PATCH] BLI: Add utility to find true/false mix in boolean array This is useful for #108014, when determining whether there are any sharp faces and edges in a mesh. --- source/blender/blenlib/BLI_array_utils.hh | 12 +++++ source/blender/blenlib/intern/array_utils.cc | 51 ++++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/source/blender/blenlib/BLI_array_utils.hh b/source/blender/blenlib/BLI_array_utils.hh index 883ad1c1bf0..dc56492e909 100644 --- a/source/blender/blenlib/BLI_array_utils.hh +++ b/source/blender/blenlib/BLI_array_utils.hh @@ -141,4 +141,16 @@ inline void gather(const VArray &src, void invert_booleans(MutableSpan span); +enum class BooleanMix { + None, + AllFalse, + AllTrue, + Mixed, +}; +BooleanMix booleans_mix_calc(const VArray &varray, IndexRange range_to_check); +inline BooleanMix booleans_mix_calc(const VArray &varray) +{ + return booleans_mix_calc(varray, varray.index_range()); +} + } // namespace blender::array_utils diff --git a/source/blender/blenlib/intern/array_utils.cc b/source/blender/blenlib/intern/array_utils.cc index 4abf9ce5e34..12e27b5ab1c 100644 --- a/source/blender/blenlib/intern/array_utils.cc +++ b/source/blender/blenlib/intern/array_utils.cc @@ -52,4 +52,55 @@ void invert_booleans(MutableSpan span) }); } +BooleanMix booleans_mix_calc(const VArray &varray, const IndexRange range_to_check) +{ + if (varray.is_empty()) { + return BooleanMix::None; + } + const CommonVArrayInfo info = varray.common_info(); + if (info.type == CommonVArrayInfo::Type::Single) { + return *static_cast(info.data) ? BooleanMix::AllTrue : BooleanMix::AllFalse; + } + if (info.type == CommonVArrayInfo::Type::Span) { + const Span span(static_cast(info.data), varray.size()); + return threading::parallel_reduce( + range_to_check, + 4096, + BooleanMix::None, + [&](const IndexRange range, const BooleanMix init) { + if (init == BooleanMix::Mixed) { + return init; + } + + const Span slice = span.slice(range); + const bool first = slice.first(); + for (const bool value : slice.drop_front(1)) { + if (value != first) { + return BooleanMix::Mixed; + } + } + return first ? BooleanMix::AllTrue : BooleanMix::AllFalse; + }, + [&](BooleanMix a, BooleanMix b) { return (a == b) ? a : BooleanMix::Mixed; }); + } + return threading::parallel_reduce( + range_to_check, + 2048, + BooleanMix::None, + [&](const IndexRange range, const BooleanMix init) { + if (init == BooleanMix::Mixed) { + return init; + } + /* Alternatively, this could use #materialize to retrieve many values at once. */ + const bool first = varray[range.first()]; + for (const int64_t i : range.drop_front(1)) { + if (varray[i] != first) { + return BooleanMix::Mixed; + } + } + return first ? BooleanMix::AllTrue : BooleanMix::AllFalse; + }, + [&](BooleanMix a, BooleanMix b) { return (a == b) ? a : BooleanMix::Mixed; }); +} + } // namespace blender::array_utils