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.
This commit is contained in:
Hans Goudey 2023-05-24 09:54:25 -04:00
parent ba3f26fac5
commit c9dac7e77c
2 changed files with 63 additions and 0 deletions

View File

@ -141,4 +141,16 @@ inline void gather(const VArray<T> &src,
void invert_booleans(MutableSpan<bool> span);
enum class BooleanMix {
None,
AllFalse,
AllTrue,
Mixed,
};
BooleanMix booleans_mix_calc(const VArray<bool> &varray, IndexRange range_to_check);
inline BooleanMix booleans_mix_calc(const VArray<bool> &varray)
{
return booleans_mix_calc(varray, varray.index_range());
}
} // namespace blender::array_utils

View File

@ -52,4 +52,55 @@ void invert_booleans(MutableSpan<bool> span)
});
}
BooleanMix booleans_mix_calc(const VArray<bool> &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<const bool *>(info.data) ? BooleanMix::AllTrue : BooleanMix::AllFalse;
}
if (info.type == CommonVArrayInfo::Type::Span) {
const Span<bool> span(static_cast<const bool *>(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<bool> 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