OpenVDB  11.0.0
PointReplicateImpl.h
Go to the documentation of this file.
1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: MPL-2.0
3 
4 /// @author Nick Avramoussis
5 ///
6 /// @file PointReplicateImpl.h
7 ///
8 
9 #ifndef OPENVDB_POINTS_POINT_REPLICATE_IMPL_HAS_BEEN_INCLUDED
10 #define OPENVDB_POINTS_POINT_REPLICATE_IMPL_HAS_BEEN_INCLUDED
11 
12 namespace openvdb {
14 namespace OPENVDB_VERSION_NAME {
15 namespace points {
16 
17 template <typename PointDataGridT>
18 typename PointDataGridT::Ptr
19 replicate(const PointDataGridT& source,
20  const Index multiplier,
21  const std::vector<std::string>& attributes,
22  const std::string& scaleAttribute,
23  const std::string& replicationIndex)
24 {
25  // The copy iterator, used to transfer array values from the source grid
26  // to the target (replicated grid).
27  struct CopyIter
28  {
29 #ifdef __clang__
30  // Silence incorrect clang warning
31  _Pragma("clang diagnostic push")
32  _Pragma("clang diagnostic ignored \"-Wunused-local-typedef\"")
33  using GetIncrementCB = std::function<Index(const Index)>;
34  _Pragma("clang diagnostic pop")
35 #else
36  using GetIncrementCB = std::function<Index(const Index)>;
37 #endif
38 
39  CopyIter(const Index end, const GetIncrementCB& cb)
40  : mIt(0), mEnd(0), mSource(0), mSourceEnd(end), mCallback(cb) {
41  mEnd = mCallback(mSource);
42  }
43 
44  operator bool() const { return mSource < mSourceEnd; }
45 
46  CopyIter& operator++()
47  {
48  ++mIt;
49  // If we have hit the end for current source idx, increment the source idx
50  // moving end to the new position for the next source with a non zero
51  // number of new values
52  while (mIt >= mEnd) {
53  ++mSource;
54  if (*this) mEnd += mCallback(mSource);
55  else return *this;
56  }
57 
58  return *this;
59  }
60 
61  Index sourceIndex() const { assert(*this); return mSource; }
62  Index targetIndex() const { assert(*this); return mIt; }
63 
64  private:
65  Index mIt, mEnd, mSource;
66  const Index mSourceEnd;
67  const GetIncrementCB mCallback;
68  }; // struct CopyIter
69 
70 
71  // We want the topology and index values of the leaf nodes, but we
72  // DON'T want to copy the arrays. This should only shallow copy the
73  // descriptor and arrays
74  PointDataGrid::Ptr points = source.deepCopy();
75 
76  auto iter = source.tree().cbeginLeaf();
77  if (!iter) return points;
78 
79  const AttributeSet::Descriptor& sourceDescriptor =
80  iter->attributeSet().descriptor();
81 
82  // verify position
83 
84  const size_t ppos = sourceDescriptor.find("P");
85  assert(ppos != AttributeSet::INVALID_POS);
86 
87  // build new dummy attribute set
88 
90  std::vector<size_t> attribsToDrop;
91  if (!attributes.empty()) {
92  for (const auto& nameIdxPair : sourceDescriptor.map()) {
93  if (std::find(attributes.begin(), attributes.end(), nameIdxPair.first) != attributes.end()) continue;
94  if (nameIdxPair.first == "P") continue;
95  attribsToDrop.emplace_back(nameIdxPair.second);
96  }
97  set.reset(new AttributeSet(iter->attributeSet(), 1));
98  }
99  else {
100  set.reset(new AttributeSet(AttributeSet::Descriptor::create(sourceDescriptor.type(ppos))));
101  }
102 
103  if (!replicationIndex.empty()) {
104  // don't copy replicationIndex attribute if it exists
105  // as it'll be overwritten and we create it after
106  const size_t replIdxIdx = sourceDescriptor.find(replicationIndex);
107  if (replIdxIdx != AttributeSet::INVALID_POS) attribsToDrop.emplace_back(replIdxIdx);
108  }
109  set->dropAttributes(attribsToDrop);
110 
111  // validate replication attributes
112 
113  size_t replicationIdx = AttributeSet::INVALID_POS;
114  if (!replicationIndex.empty()) {
115  set->appendAttribute(replicationIndex, TypedAttributeArray<int32_t>::attributeType());
116  replicationIdx = set->descriptor().find(replicationIndex);
117  }
118 
119  AttributeSet::DescriptorPtr descriptor = set->descriptorPtr();
120 
121  const size_t scaleIdx = !scaleAttribute.empty() ?
122  sourceDescriptor.find(scaleAttribute) : AttributeSet::INVALID_POS;
123 
124  openvdb::tree::LeafManager<const PointDataTree> sourceManager(source.tree());
125  openvdb::tree::LeafManager<openvdb::points::PointDataTree> manager(points->tree());
126 
127  manager.foreach(
128  [&](PointDataTree::LeafNodeType& leaf, size_t pos)
129  {
130  using ValueType = PointDataTree::ValueType;
131 
132  const auto& sourceLeaf = sourceManager.leaf(pos);
133  // @note This really shoudn't return uint64_t as AttributeArray's size is
134  // limited to the max of a uint32_t...
135  assert(sourceLeaf.pointCount() < Index64(std::numeric_limits<Index>::max()));
136  const Index sourceCount = static_cast<Index>(sourceLeaf.pointCount());
137 
138  Index uniformMultiplier = multiplier;
139  AttributeHandle<float>::UniquePtr scaleHandle(nullptr);
140  bool useScale = scaleIdx != AttributeSet::INVALID_POS;
141  if (useScale) {
142  scaleHandle = std::make_unique<AttributeHandle<float>>
143  (sourceLeaf.constAttributeArray(scaleIdx));
144  }
145  // small lambda that returns the amount of points to generate
146  // based on a scale. Should only be called if useScale is true,
147  // otherwise the scaleHandle will be reset or null
148  auto getPointsToGenerate = [&](const Index index) -> Index {
149  const float scale = std::max(0.0f, scaleHandle->get(index));
150  return static_cast<Index>
151  (math::Round(static_cast<float>(multiplier) * scale));
152  };
153 
154  // if uniform, update the multiplier and don't bother using the scale attribute
155 
156  if (useScale && scaleHandle->isUniform()) {
157  uniformMultiplier = getPointsToGenerate(0);
158  scaleHandle.reset();
159  useScale = false;
160  }
161 
162  // get the new count and build the new offsets - do this in this loop so we
163  // don't have to cache the offset vector. Note that the leaf offsets become
164  // invalid until leaf.replaceAttributeSet is called and should not be used
165 
166  Index total = 0;
167 
168  if (useScale) {
169  for (auto iter = sourceLeaf.cbeginValueAll(); iter; ++iter) {
170  for (auto piter = sourceLeaf.beginIndexVoxel(iter.getCoord());
171  piter; ++piter) { total += getPointsToGenerate(*piter); }
172  leaf.setOffsetOnly(iter.pos(), total);
173  }
174  }
175  else {
176  total = uniformMultiplier * sourceCount;
177 
178  // with a uniform multiplier, just multiply each voxel value
179  auto* data = leaf.buffer().data();
180  for (size_t i = 0; i < leaf.buffer().size(); ++i) {
181  const ValueType::IntType value = data[i];
182  data[i] = value * uniformMultiplier;
183  }
184  }
185 
186  // turn voxels off if no points
187  leaf.updateValueMask();
188  const AttributeSet& sourceSet = sourceLeaf.attributeSet();
189 
190  std::unique_ptr<openvdb::points::AttributeSet> newSet
191  (new AttributeSet(*set, total));
192 
193  auto copy = [&](const std::string& name)
194  {
195  const auto* sourceArray = sourceSet.getConst(name);
196  assert(sourceArray);
197 
198  // manually expand so that copyValues() doesn't expand and fill the array -
199  // we don't want to unnecessarily zero initialize the target values as we know
200  // we're going to write to all of them.
201  auto* array = newSet->get(name);
202  assert(array);
203  array->expand(/*fill*/false);
204 
205  if (useScale) {
206  const CopyIter iter(sourceCount, [&](const Index i) { return getPointsToGenerate(i); });
207  array->copyValues(*sourceArray, iter);
208  }
209  else {
210  const CopyIter iter(sourceCount, [&](const Index) { return uniformMultiplier; });
211  array->copyValues(*sourceArray, iter);
212  }
213  };
214 
215  copy("P");
216  for (const auto& iter : descriptor->map()) {
217  if (iter.first == "P") continue;
218  if (iter.first == replicationIndex) continue;
219  copy(iter.first);
220  }
221 
222  // assign the replication idx if requested
223 
224  if (replicationIdx != AttributeSet::INVALID_POS && total > 0) {
226  idxHandle(*newSet->get(replicationIdx), /*expand*/false);
227  idxHandle.expand(/*fill*/false);
228  assert(idxHandle.size() == total);
229 
230 
231  Index offset = 0;
232 
233  if (useScale) {
234  for (Index i = 0; i < sourceCount; ++i) {
235  const Index pointRepCount = getPointsToGenerate(i);
236  for (Index j = 0; j < pointRepCount; ++j) {
237  idxHandle.set(offset++, j);
238  }
239  }
240  }
241  else {
242  while (offset < total) {
243  for (Index j = 0; j < uniformMultiplier; ++j) {
244  idxHandle.set(offset++, j);
245  }
246  }
247  }
248  }
249 
250  leaf.replaceAttributeSet(newSet.release(), /*mismatch*/true);
251  });
252 
253  if (!scaleAttribute.empty()) {
254  tools::pruneInactive(points->tree());
255  }
256 
257  return points;
258 }
259 
260 template <typename PointDataGridT>
261 typename PointDataGridT::Ptr
262 replicate(const PointDataGridT& source,
263  const Index multiplier,
264  const std::string& scaleAttribute,
265  const std::string& replicationIndex)
266 {
267  auto iter = source.tree().cbeginLeaf();
268  if (!iter) return source.deepCopy();
269 
270  const openvdb::points::AttributeSet::Descriptor& sourceDescriptor =
271  iter->attributeSet().descriptor();
272 
273  std::vector<std::string> attribs;
274  attribs.reserve(sourceDescriptor.map().size());
275  for (const auto& namepos : sourceDescriptor.map()) {
276  attribs.emplace_back(namepos.first);
277  }
278 
279  return replicate(source, multiplier, attribs, scaleAttribute, replicationIndex);
280 }
281 
282 
283 } // namespace points
284 } // namespace OPENVDB_VERSION_NAME
285 } // namespace openvdb
286 
287 #endif // OPENVDB_POINTS_POINT_REPLICATE_IMPL_HAS_BEEN_INCLUDED
ValueType get(Index n, Index m=0) const
Definition: AttributeArray.h:2185
MatType scale(const Vec3< typename MatType::value_type > &s)
Return a matrix that scales by s.
Definition: Mat.h:615
const AttributeArray * getConst(const std::string &name) const
Return a pointer to the attribute array whose name is name or a null pointer if no match is found...
bool isUniform() const
Definition: AttributeArray.h:2211
Ordered collection of uniquely-named attribute arrays.
Definition: AttributeSet.h:38
void expand(bool fill=true)
If this array is uniform, replace it with an array of length size().
Definition: AttributeArray.h:2254
PointDataGridT::Ptr replicate(const PointDataGridT &source, const Index multiplier, const std::vector< std::string > &attributes, const std::string &scaleAttribute="", const std::string &replicationIndex="")
Replicates points provided in a source grid into a new grid, transfering and creating attributes foun...
Definition: PointReplicateImpl.h:19
uint64_t Index64
Definition: Types.h:53
std::unique_ptr< Handle > UniquePtr
Definition: AttributeArray.h:837
typename RootNodeType::LeafNodeType LeafNodeType
Definition: Tree.h:186
Definition: Exceptions.h:13
float Round(float x)
Return x rounded to the nearest integer.
Definition: Math.h:819
Typed class for storing attribute data.
Definition: AttributeArray.h:544
std::shared_ptr< AttributeSet > Ptr
Definition: AttributeSet.h:43
SharedPtr< Grid > Ptr
Definition: Grid.h:573
Index32 Index
Definition: Types.h:54
void pruneInactive(TreeT &tree, bool threaded=true, size_t grainSize=1)
Reduce the memory footprint of a tree by replacing with background tiles any nodes whose values are a...
Definition: Prune.h:355
std::shared_ptr< Descriptor > DescriptorPtr
Definition: AttributeSet.h:49
const std::enable_if<!VecTraits< T >::IsVec, T >::type & max(const T &a, const T &b)
Definition: Composite.h:110
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h.in:121
Write-able version of AttributeHandle.
Definition: AttributeArray.h:903
typename RootNodeType::ValueType ValueType
Definition: Tree.h:184
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h.in:212