| 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 |