mirror of
https://projects.blender.org/blender/blender.git
synced 2025-01-22 07:22:12 -05:00
BLI: add IndexMask::from_ranges constructor function
This can be useful when going from a selection of curves to the corresponding selection of points. The simple implementation given here should work quite well in the majority of cases. There is still optimization potential for some cases involving masks with many gaps. The implementation is also single threaded for now. Using multi-threading is possible, but should ideally be done in some way to still lets us exploit the fact that the index ranges are already in sorted order. Pull Request: https://projects.blender.org/blender/blender/pulls/133323
This commit is contained in:
parent
890455affe
commit
fb7eef9271
4 changed files with 100 additions and 8 deletions
|
@ -13,6 +13,7 @@
|
|||
#include "BLI_index_mask_fwd.hh"
|
||||
#include "BLI_index_ranges_builder_fwd.hh"
|
||||
#include "BLI_linear_allocator.hh"
|
||||
#include "BLI_offset_indices.hh"
|
||||
#include "BLI_offset_span.hh"
|
||||
#include "BLI_task.hh"
|
||||
#include "BLI_unique_sorted_indices.hh"
|
||||
|
@ -208,6 +209,11 @@ class IndexMask : private IndexMaskData {
|
|||
static IndexMask from_bools(const IndexMask &universe,
|
||||
const VArray<bool> &bools,
|
||||
IndexMaskMemory &memory);
|
||||
/** Construct a mask from the ranges referenced by the offset indices. */
|
||||
template<typename T>
|
||||
static IndexMask from_ranges(OffsetIndices<T> offsets,
|
||||
const IndexMask &mask,
|
||||
IndexMaskMemory &memory);
|
||||
/**
|
||||
* Constructs a mask by repeating the indices in the given mask with a stride.
|
||||
* For example, with an input mask containing `{3, 5}` and a stride of 10 the resulting mask
|
||||
|
@ -581,6 +587,13 @@ template<typename T> void build_reverse_map(const IndexMask &mask, MutableSpan<T
|
|||
int64_t consolidate_index_mask_segments(MutableSpan<IndexMaskSegment> segments,
|
||||
IndexMaskMemory &memory);
|
||||
|
||||
/**
|
||||
* Adds index mask segments to the the vector for the given range. Ranges shorter than
|
||||
* #max_segment_size fit into a single segment. Larger ranges are split into multiple segments.
|
||||
*/
|
||||
template<int64_t N>
|
||||
void index_range_to_mask_segments(const IndexRange range, Vector<IndexMaskSegment, N> &r_segments);
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name #RawMaskIterator Inline Methods
|
||||
* \{ */
|
||||
|
@ -1081,6 +1094,20 @@ inline bool operator!=(const IndexMask &a, const IndexMask &b)
|
|||
return !(a == b);
|
||||
}
|
||||
|
||||
template<int64_t N>
|
||||
inline void index_range_to_mask_segments(const IndexRange range,
|
||||
Vector<IndexMaskSegment, N> &r_segments)
|
||||
{
|
||||
const std::array<int16_t, max_segment_size> &static_indices_array = get_static_indices_array();
|
||||
|
||||
const int64_t full_size = range.size();
|
||||
for (int64_t i = 0; i < full_size; i += max_segment_size) {
|
||||
const int64_t size = std::min(i + max_segment_size, full_size) - i;
|
||||
r_segments.append(
|
||||
IndexMaskSegment(range.first() + i, Span(static_indices_array).take_front(size)));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::index_mask
|
||||
|
||||
namespace blender {
|
||||
|
|
|
@ -642,6 +642,19 @@ IndexMask IndexMask::from_bools(const IndexMask &universe,
|
|||
universe, GrainSize(512), memory, [&](const int64_t index) { return bools[index]; });
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
IndexMask IndexMask::from_ranges(OffsetIndices<T> offsets,
|
||||
const IndexMask &mask,
|
||||
IndexMaskMemory &memory)
|
||||
{
|
||||
Vector<IndexMaskSegment, 16> segments;
|
||||
mask.foreach_range([&](const IndexRange mask_range) {
|
||||
const IndexRange range = offsets[mask_range];
|
||||
index_range_to_mask_segments(range, segments);
|
||||
});
|
||||
return IndexMask::from_segments(segments, memory);
|
||||
}
|
||||
|
||||
IndexMask IndexMask::from_union(const IndexMask &mask_a,
|
||||
const IndexMask &mask_b,
|
||||
IndexMaskMemory &memory)
|
||||
|
@ -1188,5 +1201,11 @@ template IndexMask IndexMask::from_indices(Span<int32_t>, IndexMaskMemory &);
|
|||
template IndexMask IndexMask::from_indices(Span<int64_t>, IndexMaskMemory &);
|
||||
template void IndexMask::to_indices(MutableSpan<int32_t>) const;
|
||||
template void IndexMask::to_indices(MutableSpan<int64_t>) const;
|
||||
template IndexMask IndexMask::from_ranges(OffsetIndices<int32_t>,
|
||||
const IndexMask &,
|
||||
IndexMaskMemory &);
|
||||
template IndexMask IndexMask::from_ranges(OffsetIndices<int64_t>,
|
||||
const IndexMask &,
|
||||
IndexMaskMemory &);
|
||||
|
||||
} // namespace blender::index_mask
|
||||
|
|
|
@ -974,18 +974,11 @@ static IndexMaskSegment evaluate_exact_with_indices(const Expr &root_expression,
|
|||
static Vector<IndexMaskSegment> build_result_mask_segments(
|
||||
const Span<EvaluatedSegment> evaluated_segments)
|
||||
{
|
||||
const std::array<int16_t, max_segment_size> &static_indices_array = get_static_indices_array();
|
||||
|
||||
Vector<IndexMaskSegment> result_mask_segments;
|
||||
for (const EvaluatedSegment &evaluated_segment : evaluated_segments) {
|
||||
switch (evaluated_segment.type) {
|
||||
case EvaluatedSegment::Type::Full: {
|
||||
const int64_t full_size = evaluated_segment.bounds.size();
|
||||
for (int64_t i = 0; i < full_size; i += max_segment_size) {
|
||||
const int64_t size = std::min(i + max_segment_size, full_size) - i;
|
||||
result_mask_segments.append(IndexMaskSegment(
|
||||
evaluated_segment.bounds.first() + i, Span(static_indices_array).take_front(size)));
|
||||
}
|
||||
index_range_to_mask_segments(evaluated_segment.bounds, result_mask_segments);
|
||||
break;
|
||||
}
|
||||
case EvaluatedSegment::Type::Copy: {
|
||||
|
|
|
@ -1222,4 +1222,57 @@ TEST(index_mask, SliceAndShift)
|
|||
}
|
||||
}
|
||||
|
||||
TEST(index_mask, IndexRangeToMaskSegments)
|
||||
{
|
||||
auto test_range = [](const IndexRange range) {
|
||||
Vector<IndexMaskSegment> segments;
|
||||
index_range_to_mask_segments(range, segments);
|
||||
IndexMaskMemory memory;
|
||||
const IndexMask mask = IndexMask::from_segments(segments, memory);
|
||||
const std::optional<IndexRange> new_range = mask.to_range();
|
||||
EXPECT_TRUE(new_range.has_value());
|
||||
EXPECT_EQ(range, *new_range);
|
||||
};
|
||||
|
||||
test_range(IndexRange::from_begin_size(1'000, 0));
|
||||
|
||||
test_range(IndexRange::from_begin_end_inclusive(0, 10));
|
||||
test_range(IndexRange::from_begin_end_inclusive(0, 10'000));
|
||||
test_range(IndexRange::from_begin_end_inclusive(0, 100'000));
|
||||
test_range(IndexRange::from_begin_end_inclusive(0, 1'000'000));
|
||||
|
||||
test_range(IndexRange::from_begin_end_inclusive(50'000, 1'000'000));
|
||||
test_range(IndexRange::from_begin_end_inclusive(999'999, 1'000'000));
|
||||
test_range(IndexRange::from_begin_end_inclusive(1'000'000, 1'000'000));
|
||||
}
|
||||
|
||||
TEST(index_mask, FromRanges)
|
||||
{
|
||||
IndexMaskMemory memory;
|
||||
Array<int> data = {5, 100, 400, 500, 100'000, 200'000};
|
||||
OffsetIndices<int> offsets(data);
|
||||
|
||||
{
|
||||
const IndexMask mask = IndexMask::from_ranges(offsets, offsets.index_range(), memory);
|
||||
EXPECT_EQ(mask.size(), 199'995);
|
||||
EXPECT_EQ(*mask.to_range(), IndexRange::from_begin_end(5, 200'000));
|
||||
}
|
||||
{
|
||||
const IndexMask mask = IndexMask::from_ranges(offsets, IndexRange(0), memory);
|
||||
EXPECT_TRUE(mask.is_empty());
|
||||
}
|
||||
{
|
||||
const IndexMask mask = IndexMask::from_ranges(offsets, IndexRange(1), memory);
|
||||
EXPECT_EQ(*mask.to_range(), IndexRange::from_begin_end(5, 100));
|
||||
}
|
||||
{
|
||||
const IndexMask offsets_mask = IndexMask::from_indices(Span<int>({1, 4}), memory);
|
||||
const IndexMask mask = IndexMask::from_ranges(offsets, offsets_mask, memory);
|
||||
EXPECT_EQ(mask,
|
||||
IndexMask::from_initializers({IndexRange::from_begin_end(100, 400),
|
||||
IndexRange::from_begin_end(100'000, 200'000)},
|
||||
memory));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::index_mask::tests
|
||||
|
|
Loading…
Reference in a new issue