Line | Branch | Exec | Source |
---|---|---|---|
1 | // Copyright Contributors to the OpenVDB Project | ||
2 | // SPDX-License-Identifier: MPL-2.0 | ||
3 | |||
4 | /// @author Nick Avramoussis, Francisco Gochez, Dan Bailey | ||
5 | /// | ||
6 | /// @file PointDelete.h | ||
7 | /// | ||
8 | /// @brief Methods for deleting points based on group membership | ||
9 | |||
10 | #ifndef OPENVDB_POINTS_POINT_DELETE_HAS_BEEN_INCLUDED | ||
11 | #define OPENVDB_POINTS_POINT_DELETE_HAS_BEEN_INCLUDED | ||
12 | |||
13 | #include "PointDataGrid.h" | ||
14 | #include "PointGroup.h" | ||
15 | #include "IndexIterator.h" | ||
16 | #include "IndexFilter.h" | ||
17 | |||
18 | #include <openvdb/tools/Prune.h> | ||
19 | #include <openvdb/tree/LeafManager.h> | ||
20 | |||
21 | #include <memory> | ||
22 | #include <string> | ||
23 | #include <vector> | ||
24 | |||
25 | |||
26 | namespace openvdb { | ||
27 | OPENVDB_USE_VERSION_NAMESPACE | ||
28 | namespace OPENVDB_VERSION_NAME { | ||
29 | namespace points { | ||
30 | |||
31 | |||
32 | /// @brief Delete points that are members of specific groups | ||
33 | /// | ||
34 | /// @details This method will delete points which are members of any of the supplied groups and | ||
35 | /// will optionally drop the groups from the tree. An invert flag can be used to | ||
36 | /// delete points that belong to none of the groups. | ||
37 | /// | ||
38 | /// @param pointTree the point tree | ||
39 | /// @param groups the groups from which to delete points | ||
40 | /// @param invert if enabled, points not belonging to any of the groups will be deleted | ||
41 | /// @param drop if enabled and invert is disabled, the groups will be dropped from the tree | ||
42 | /// | ||
43 | /// @note If the invert flag is true, none of the groups will be dropped after deleting points | ||
44 | /// regardless of the value of the drop parameter. | ||
45 | |||
46 | template <typename PointDataTreeT> | ||
47 | inline void deleteFromGroups(PointDataTreeT& pointTree, | ||
48 | const std::vector<std::string>& groups, | ||
49 | bool invert = false, | ||
50 | bool drop = true); | ||
51 | |||
52 | /// @brief Delete points that are members of a group | ||
53 | /// | ||
54 | /// @details This method will delete points which are members of the supplied group and will | ||
55 | /// optionally drop the group from the tree. An invert flag can be used to | ||
56 | /// delete points that belong to none of the groups. | ||
57 | /// | ||
58 | /// @param pointTree the point tree with the group to delete | ||
59 | /// @param group the name of the group to delete | ||
60 | /// @param invert if enabled, points not belonging to any of the groups will be deleted | ||
61 | /// @param drop if enabled and invert is disabled, the group will be dropped from the tree | ||
62 | /// | ||
63 | /// @note If the invert flag is true, the group will not be dropped after deleting points | ||
64 | /// regardless of the value of the drop parameter. | ||
65 | |||
66 | template <typename PointDataTreeT> | ||
67 | inline void deleteFromGroup(PointDataTreeT& pointTree, | ||
68 | const std::string& group, | ||
69 | bool invert = false, | ||
70 | bool drop = true); | ||
71 | |||
72 | |||
73 | //////////////////////////////////////// | ||
74 | |||
75 | /// @cond OPENVDB_DOCS_INTERNAL | ||
76 | |||
77 | namespace point_delete_internal { | ||
78 | |||
79 | |||
80 | struct VectorWrapper | ||
81 | { | ||
82 | using T = std::vector<std::pair<Index, Index>>; | ||
83 | |||
84 |
1/2✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
|
15 | VectorWrapper(const T& _data) : data(_data) { } |
85 |
2/2✓ Branch 0 taken 22 times.
✓ Branch 1 taken 15 times.
|
37 | operator bool() const { return index < data.size(); } |
86 | 22 | VectorWrapper& operator++() { index++; return *this; } | |
87 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
|
22 | Index sourceIndex() const { assert(*this); return data[index].first; } |
88 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
|
22 | Index targetIndex() const { assert(*this); return data[index].second; } |
89 | |||
90 | private: | ||
91 | const T& data; | ||
92 | T::size_type index = 0; | ||
93 | }; // struct VectorWrapper | ||
94 | |||
95 | |||
96 | template <typename PointDataTreeT, typename FilterT> | ||
97 | struct DeleteByFilterOp | ||
98 | { | ||
99 | using LeafManagerT = tree::LeafManager<PointDataTreeT>; | ||
100 | using LeafRangeT = typename LeafManagerT::LeafRange; | ||
101 | using LeafNodeT = typename PointDataTreeT::LeafNodeType; | ||
102 | using ValueType = typename LeafNodeT::ValueType; | ||
103 | |||
104 | 8 | DeleteByFilterOp(const FilterT& filter, | |
105 | const AttributeArray::ScopedRegistryLock* lock) | ||
106 | : mFilter(filter) | ||
107 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | , mLock(lock) { } |
108 | |||
109 | 15 | void operator()(const LeafRangeT& range) const | |
110 | { | ||
111 | 45 | for (auto leaf = range.begin(); leaf != range.end(); ++leaf) { | |
112 | |||
113 | const size_t newSize = | ||
114 |
1/2✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
|
15 | iterCount(leaf->template beginIndexAll<FilterT>(mFilter)); |
115 | |||
116 | // if all points are being deleted, clear the leaf attributes | ||
117 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 11 times.
|
15 | if (newSize == 0) { |
118 | 4 | leaf->clearAttributes(/*updateValueMask=*/true, mLock); | |
119 | 8 | continue; | |
120 | } | ||
121 | |||
122 | // early exit if no points are being deleted | ||
123 | |||
124 | 11 | const size_t currentSize = leaf->getLastValue(); | |
125 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 7 times.
|
11 | if (newSize == currentSize) continue; |
126 | |||
127 | const AttributeSet& existingAttributeSet = leaf->attributeSet(); | ||
128 | 7 | AttributeSet* newAttributeSet = new AttributeSet( | |
129 |
1/2✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
|
7 | existingAttributeSet, static_cast<Index>(newSize), mLock); |
130 | const size_t attributeSetSize = existingAttributeSet.size(); | ||
131 | |||
132 | // cache the attribute arrays for efficiency | ||
133 | |||
134 | std::vector<AttributeArray*> newAttributeArrays; | ||
135 | std::vector<const AttributeArray*> existingAttributeArrays; | ||
136 | |||
137 |
2/2✓ Branch 0 taken 15 times.
✓ Branch 1 taken 7 times.
|
22 | for (size_t i = 0; i < attributeSetSize; i++) { |
138 |
1/2✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
|
15 | AttributeArray* newArray = newAttributeSet->get(i); |
139 |
1/2✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
|
15 | const AttributeArray* existingArray = existingAttributeSet.getConst(i); |
140 | |||
141 |
2/4✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
|
15 | if (!newArray->hasConstantStride() || !existingArray->hasConstantStride()) { |
142 | ✗ | OPENVDB_THROW(openvdb::NotImplementedError, | |
143 | "Transfer of attribute values for dynamic arrays not currently supported."); | ||
144 | } | ||
145 | |||
146 |
3/6✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 15 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 15 times.
|
15 | if (newArray->stride() != existingArray->stride()) { |
147 | ✗ | OPENVDB_THROW(openvdb::LookupError, | |
148 | "Cannot transfer attribute values with mis-matching strides."); | ||
149 | } | ||
150 | |||
151 |
1/2✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
|
15 | newAttributeArrays.push_back(newArray); |
152 |
1/2✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
|
15 | existingAttributeArrays.push_back(existingArray); |
153 | } | ||
154 | |||
155 | Index attributeIndex = 0; | ||
156 | std::vector<ValueType> endOffsets; | ||
157 | |||
158 |
1/2✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
|
7 | endOffsets.reserve(LeafNodeT::NUM_VALUES); |
159 | |||
160 | // now construct new attribute arrays which exclude data from deleted points | ||
161 | |||
162 | std::vector<std::pair<Index, Index>> indexMapping; | ||
163 |
1/2✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
|
7 | indexMapping.reserve(newSize); |
164 | |||
165 |
2/2✓ Branch 1 taken 3584 times.
✓ Branch 2 taken 7 times.
|
3598 | for (auto voxel = leaf->cbeginValueAll(); voxel; ++voxel) { |
166 |
4/6✓ Branch 1 taken 3584 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3584 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 10 times.
✓ Branch 7 taken 3584 times.
|
3594 | for (auto iter = leaf->beginIndexVoxel(voxel.getCoord(), mFilter); |
167 | 10 | iter; ++iter) { | |
168 |
2/4✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
|
10 | indexMapping.emplace_back(*iter, attributeIndex++); |
169 | } | ||
170 |
0/2✗ Branch 1 not taken.
✗ Branch 2 not taken.
|
3584 | endOffsets.push_back(static_cast<ValueType>(attributeIndex)); |
171 | } | ||
172 | |||
173 |
2/2✓ Branch 0 taken 15 times.
✓ Branch 1 taken 7 times.
|
22 | for (size_t i = 0; i < attributeSetSize; i++) { |
174 | VectorWrapper indexMappingWrapper(indexMapping); | ||
175 |
1/2✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
|
15 | newAttributeArrays[i]->copyValues(*(existingAttributeArrays[i]), indexMappingWrapper); |
176 | } | ||
177 | |||
178 |
1/2✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
|
7 | leaf->replaceAttributeSet(newAttributeSet); |
179 |
1/2✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
|
7 | leaf->setOffsets(endOffsets); |
180 | } | ||
181 | 15 | } | |
182 | |||
183 | private: | ||
184 | const FilterT& mFilter; | ||
185 | const AttributeArray::ScopedRegistryLock* mLock; | ||
186 | }; // struct DeleteByFilterOp | ||
187 | |||
188 | } // namespace point_delete_internal | ||
189 | |||
190 | /// @endcond | ||
191 | |||
192 | //////////////////////////////////////// | ||
193 | |||
194 | |||
195 | template <typename PointDataTreeT> | ||
196 | 8 | inline void deleteFromGroups(PointDataTreeT& pointTree, | |
197 | const std::vector<std::string>& groups, | ||
198 | bool invert, | ||
199 | bool drop) | ||
200 | { | ||
201 | const typename PointDataTreeT::LeafCIter leafIter = pointTree.cbeginLeaf(); | ||
202 | |||
203 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | if (!leafIter) return; |
204 | |||
205 | const openvdb::points::AttributeSet& attributeSet = leafIter->attributeSet(); | ||
206 | const AttributeSet::Descriptor& descriptor = attributeSet.descriptor(); | ||
207 | 8 | std::vector<std::string> availableGroups; | |
208 | |||
209 | // determine which of the requested groups exist, and early exit | ||
210 | // if none are present in the tree | ||
211 | |||
212 |
2/2✓ Branch 0 taken 13 times.
✓ Branch 1 taken 8 times.
|
21 | for (const auto& groupName : groups) { |
213 |
2/4✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 13 times.
✗ Branch 4 not taken.
|
13 | if (descriptor.hasGroup(groupName)) { |
214 |
1/2✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
|
13 | availableGroups.push_back(groupName); |
215 | } | ||
216 | } | ||
217 | |||
218 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
|
8 | if (availableGroups.empty()) return; |
219 | |||
220 | 8 | std::vector<std::string> empty; | |
221 | 8 | std::unique_ptr<MultiGroupFilter> filter; | |
222 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 7 times.
|
8 | if (invert) { |
223 |
2/4✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
|
1 | filter.reset(new MultiGroupFilter(groups, empty, leafIter->attributeSet())); |
224 | } | ||
225 | else { | ||
226 |
2/4✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
|
7 | filter.reset(new MultiGroupFilter(empty, groups, leafIter->attributeSet())); |
227 | } | ||
228 | |||
229 | { // acquire registry lock to avoid locking when appending attributes in parallel | ||
230 | |||
231 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | AttributeArray::ScopedRegistryLock lock; |
232 | |||
233 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | tree::LeafManager<PointDataTreeT> leafManager(pointTree); |
234 | point_delete_internal::DeleteByFilterOp<PointDataTreeT, MultiGroupFilter> deleteOp( | ||
235 | *filter, &lock); | ||
236 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | tbb::parallel_for(leafManager.leafRange(), deleteOp); |
237 | } | ||
238 | |||
239 | // remove empty leaf nodes | ||
240 | |||
241 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | tools::pruneInactive(pointTree); |
242 | |||
243 | // drop the now-empty groups if requested (unless invert = true) | ||
244 | |||
245 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2 times.
|
8 | if (drop && !invert) { |
246 |
1/2✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
|
6 | dropGroups(pointTree, availableGroups); |
247 | } | ||
248 | } | ||
249 | |||
250 | template <typename PointDataTreeT> | ||
251 | 5 | inline void deleteFromGroup(PointDataTreeT& pointTree, | |
252 | const std::string& group, | ||
253 | bool invert, | ||
254 | bool drop) | ||
255 | { | ||
256 | 10 | std::vector<std::string> groups(1, group); | |
257 | |||
258 |
1/2✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
|
5 | deleteFromGroups(pointTree, groups, invert, drop); |
259 | 5 | } | |
260 | |||
261 | |||
262 | } // namespace points | ||
263 | } // namespace OPENVDB_VERSION_NAME | ||
264 | } // namespace openvdb | ||
265 | |||
266 | #endif // OPENVDB_POINTS_POINT_DELETE_HAS_BEEN_INCLUDED | ||
267 |