OpenVDB  0.104.0
LeafNode.h
Go to the documentation of this file.
1 
2 //
3 // Copyright (c) 2012 DreamWorks Animation LLC
4 //
5 // All rights reserved. This software is distributed under the
6 // Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
7 //
8 // Redistributions of source code must retain the above copyright
9 // and license notice and the following restrictions and disclaimer.
10 //
11 // * Neither the name of DreamWorks Animation nor the names of
12 // its contributors may be used to endorse or promote products derived
13 // from this software without specific prior written permission.
14 //
15 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
20 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 // IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
27 // LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
28 //
30 
31 #ifndef OPENVDB_TREE_LEAFNODE_HAS_BEEN_INCLUDED
32 #define OPENVDB_TREE_LEAFNODE_HAS_BEEN_INCLUDED
33 
34 #include <iostream>
35 #include <algorithm> // for std::swap
36 #include <cstring> // for std::memcpy()
37 #include <boost/shared_ptr.hpp>
38 #include <boost/static_assert.hpp>
39 #include <boost/bind.hpp>
40 #include <tbb/blocked_range.h>
41 #include <tbb/parallel_for.h>
42 #include <openvdb/Types.h>
43 #include <openvdb/util/NodeMasks.h>
44 #include <openvdb/io/Compression.h> // for io::readData(), etc.
45 #include "Iterator.h"
46 #include "Util.h"
47 
48 
49 class TestLeaf;
50 template<typename> class TestLeafIO;
51 
52 namespace openvdb {
54 namespace OPENVDB_VERSION_NAME {
55 namespace tree {
56 
59 template<typename T, Index Log2Dim>
60 class LeafNode
61 {
62 public:
63  typedef T ValueType;
65  typedef boost::shared_ptr<LeafNode> Ptr;
67 
68  static const Index
69  LOG2DIM = Log2Dim, // needed by parent nodes
70  TOTAL = Log2Dim, // needed by parent nodes
71  DIM = 1 << TOTAL, // dimension along one coordinate direction
72  NUM_VALUES = 1 << 3 * Log2Dim,
73  NUM_VOXELS = NUM_VALUES, // total number of voxels represented by this node
74  SIZE = NUM_VALUES,
75  LEVEL = 0; // level 0 = leaf
76 
79  template<typename OtherValueType>
80  struct ValueConverter {
82  };
83 
84  class Buffer
85  {
86  public:
87  Buffer(): mData(new ValueType[SIZE]) {}
88  Buffer(const ValueType& val) : mData(new ValueType[SIZE]) { this->fill(val); }
89  Buffer(const Buffer& other) : mData(new ValueType[SIZE]) { *this = other; }
90  ~Buffer() { delete [] mData; }
91 
92  void fill(const ValueType& val)
93  {
94  ValueType* target = mData;
95  Index size = SIZE;
96  while (size--) *target++ = val;
97  }
98  // Fast access methods
99  const ValueType& getValue(Index i) const { assert(i < SIZE); return mData[i]; }
100  void setValue(Index i, const ValueType& val) { assert(i < SIZE); mData[i] = val; }
101  const ValueType& operator[](Index i) const { return this->getValue(i); }
102 
103  Buffer& operator=(const Buffer& other)
104  {
105  ValueType* target = mData;
106  const ValueType* source = other.mData;
107  Index size = SIZE;
108  while (size--) *target++ = *source++;
109  return *this;
110  }
111  bool operator==(const Buffer& other) const
112  {
113  const ValueType* target = mData;
114  const ValueType* source = other.mData;
115  Index size = SIZE;
116  while (size--) if(!math::isExactlyEqual(*target++, *source++)) return false;
117  return true;
118  }
119  bool operator!=(const Buffer& other) const { return !(other == *this); }
120 
121  void swap(Buffer& other)
122  {
123  ValueType* tmp = mData;
124  mData = other.mData;
125  other.mData = tmp;
126  }
127 
128  static Index memUsage() { return sizeof(ValueType*) + SIZE * sizeof(ValueType); }
129  static Index size() { return SIZE; }
130 
131  private:
134  ValueType& operator[](Index i) { assert(i < SIZE); return mData[i]; }
135 
136  friend class ::TestLeaf;
137  // Allow the parent LeafNode to access this Buffer's data pointer.
138  friend class LeafNode;
139 
140  ValueType* mData;
141  }; // class Buffer
142 
143 
145  LeafNode();
146 
151  explicit LeafNode(const Coord& coords,
152  const ValueType& value = zeroVal<ValueType>(),
153  bool active = false);
154 
156  LeafNode(const LeafNode&);
157 
159  template<typename OtherValueType>
161  const ValueType& offValue, const ValueType& onValue, TopologyCopy);
162 
164  template<typename OtherValueType>
166  const ValueType& background, TopologyCopy);
167 
169  ~LeafNode();
170 
171  //
172  // Statistics
173  //
175  static Index log2dim() { return Log2Dim; }
177  static Index dim() { return DIM; }
178  static Index size() { return SIZE; }
179  static Index numValues() { return SIZE; }
180  static Index getLevel() { return LEVEL; }
181  static void getNodeLog2Dims(std::vector<Index>& dims) { dims.push_back(Log2Dim); }
182  static Index getChildDim() { return 1; }
183 
184  static Index32 leafCount() { return 1; }
185  static Index32 nonLeafCount() { return 0; }
186 
188  Index64 onVoxelCount() const { return mValueMask.countOn(); }
190  Index64 offVoxelCount() const { return mValueMask.countOff(); }
191  Index64 onLeafVoxelCount() const { return onVoxelCount(); }
192  Index64 offLeafVoxelCount() const { return offVoxelCount(); }
194  bool isEmpty() const { return mValueMask.isOff(); }
196  bool isDense() const { return mValueMask.isOn(); }
197 
199  Index64 memUsage() const;
200 
202  void evalActiveVoxelBoundingBox(CoordBBox&) const;
203 
206  CoordBBox getNodeBoundingBox() const { return CoordBBox::createCube(mOrigin, DIM); }
207 
209  const Coord& getOrigin() const { return mOrigin; }
210  void getOrigin(Coord& origin) const { origin = mOrigin; }
211  void getOrigin(Int32& x, Int32& y, Int32& z) const { mOrigin.asXYZ(x, y, z); }
212 
214  static Index coord2offset(const Coord& xyz);
216  static void offset2coord(Index n, Coord &xyz);
218  Coord offset2globalCoord(Index n) const;
219 
221  std::string str() const;
222 
225  template<typename OtherType, Index OtherLog2Dim>
226  bool hasSameTopology(const LeafNode<OtherType, OtherLog2Dim>* other) const;
227 
229  bool operator==(const LeafNode& other) const;
230  bool operator!=(const LeafNode& other) const { return !(other == *this); }
231 
232 protected:
236 
237  // Type tags to disambiguate template instantiations
238  struct ValueOn {}; struct ValueOff {}; struct ValueAll {};
239  struct ChildOn {}; struct ChildOff {}; struct ChildAll {};
240 
241  template<typename MaskIterT, typename NodeT, typename ValueT, typename TagT>
242  struct ValueIter:
243  // Derives from SparseIteratorBase, but can also be used as a dense iterator,
244  // if MaskIterT is a dense mask iterator type.
245  public SparseIteratorBase<
246  MaskIterT, ValueIter<MaskIterT, NodeT, ValueT, TagT>, NodeT, ValueT>
247  {
249 
251  ValueIter(const MaskIterT& iter, NodeT* parent): BaseT(iter, parent) {}
252 
253  ValueT& getItem(Index pos) const { return this->parent().getValue(pos); }
254  ValueT& getValue() const { return this->parent().getValue(this->pos()); }
255 
256  // Note: setItem() can't be called on const iterators.
257  void setItem(Index pos, const ValueT& value) const
258  {
259  this->parent().setValueOnly(pos, value);
260  }
261  // Note: setValue() can't be called on const iterators.
262  void setValue(const ValueT& value) const
263  {
264  this->parent().setValueOnly(this->pos(), value);
265  }
266  };
267 
269  template<typename MaskIterT, typename NodeT, typename TagT>
270  struct ChildIter:
271  public SparseIteratorBase<MaskIterT, ChildIter<MaskIterT, NodeT, TagT>, NodeT, ValueType>
272  {
274  ChildIter(const MaskIterT& iter, NodeT* parent): SparseIteratorBase<
275  MaskIterT, ChildIter<MaskIterT, NodeT, TagT>, NodeT, ValueType>(iter, parent) {}
276  };
277 
278  template<typename NodeT, typename ValueT, typename TagT>
279  struct DenseIter: public DenseIteratorBase<
280  MaskDenseIterator, DenseIter<NodeT, ValueT, TagT>, NodeT, /*ChildT=*/void, ValueT>
281  {
284 
286  DenseIter(const MaskDenseIterator& iter, NodeT* parent): BaseT(iter, parent) {}
287 
288  bool getItem(Index pos, void*& child, NonConstValueT& value) const
289  {
290  value = this->parent().getValue(pos);
291  child = NULL;
292  return false; // no child
293  }
294 
295  // Note: setItem() can't be called on const iterators.
296  //void setItem(Index pos, void* child) const {}
297 
298  // Note: unsetItem() can't be called on const iterators.
299  void unsetItem(Index pos, const ValueT& value) const
300  {
301  this->parent().setValueOnly(pos, value);
302  }
303  };
304 
305 public:
318 
319  ValueOnCIter cbeginValueOn() const { return ValueOnCIter(mValueMask.beginOn(), this); }
320  ValueOnCIter beginValueOn() const { return ValueOnCIter(mValueMask.beginOn(), this); }
321  ValueOnIter beginValueOn() { return ValueOnIter(mValueMask.beginOn(), this); }
322  ValueOffCIter cbeginValueOff() const { return ValueOffCIter(mValueMask.beginOff(), this); }
323  ValueOffCIter beginValueOff() const { return ValueOffCIter(mValueMask.beginOff(), this); }
324  ValueOffIter beginValueOff() { return ValueOffIter(mValueMask.beginOff(), this); }
325  ValueAllCIter cbeginValueAll() const { return ValueAllCIter(mValueMask.beginDense(), this); }
326  ValueAllCIter beginValueAll() const { return ValueAllCIter(mValueMask.beginDense(), this); }
327  ValueAllIter beginValueAll() { return ValueAllIter(mValueMask.beginDense(), this); }
328 
329  ValueOnCIter cendValueOn() const { return ValueOnCIter(mValueMask.endOn(), this); }
330  ValueOnCIter endValueOn() const { return ValueOnCIter(mValueMask.endOn(), this); }
331  ValueOnIter endValueOn() { return ValueOnIter(mValueMask.endOn(), this); }
332  ValueOffCIter cendValueOff() const { return ValueOffCIter(mValueMask.endOff(), this); }
333  ValueOffCIter endValueOff() const { return ValueOffCIter(mValueMask.endOff(), this); }
334  ValueOffIter endValueOff() { return ValueOffIter(mValueMask.endOff(), this); }
335  ValueAllCIter cendValueAll() const { return ValueAllCIter(mValueMask.endDense(), this); }
336  ValueAllCIter endValueAll() const { return ValueAllCIter(mValueMask.endDense(), this); }
337  ValueAllIter endValueAll() { return ValueAllIter(mValueMask.endDense(), this); }
338 
339  // Note that [c]beginChildOn() and [c]beginChildOff() actually return end iterators,
340  // because leaf nodes have no children.
341  ChildOnCIter cbeginChildOn() const { return ChildOnCIter(mValueMask.endOn(), this); }
342  ChildOnCIter beginChildOn() const { return ChildOnCIter(mValueMask.endOn(), this); }
343  ChildOnIter beginChildOn() { return ChildOnIter(mValueMask.endOn(), this); }
344  ChildOffCIter cbeginChildOff() const { return ChildOffCIter(mValueMask.endOff(), this); }
345  ChildOffCIter beginChildOff() const { return ChildOffCIter(mValueMask.endOff(), this); }
346  ChildOffIter beginChildOff() { return ChildOffIter(mValueMask.endOff(), this); }
347  ChildAllCIter cbeginChildAll() const { return ChildAllCIter(mValueMask.beginDense(), this); }
348  ChildAllCIter beginChildAll() const { return ChildAllCIter(mValueMask.beginDense(), this); }
349  ChildAllIter beginChildAll() { return ChildAllIter(mValueMask.beginDense(), this); }
350 
351  ChildOnCIter cendChildOn() const { return ChildOnCIter(mValueMask.endOn(), this); }
352  ChildOnCIter endChildOn() const { return ChildOnCIter(mValueMask.endOn(), this); }
353  ChildOnIter endChildOn() { return ChildOnIter(mValueMask.endOn(), this); }
354  ChildOffCIter cendChildOff() const { return ChildOffCIter(mValueMask.endOff(), this); }
355  ChildOffCIter endChildOff() const { return ChildOffCIter(mValueMask.endOff(), this); }
356  ChildOffIter endChildOff() { return ChildOffIter(mValueMask.endOff(), this); }
357  ChildAllCIter cendChildAll() const { return ChildAllCIter(mValueMask.endDense(), this); }
358  ChildAllCIter endChildAll() const { return ChildAllCIter(mValueMask.endDense(), this); }
359  ChildAllIter endChildAll() { return ChildAllIter(mValueMask.endDense(), this); }
360 
361  //
362  // Buffer management
363  //
366  void swap(Buffer& other) { mBuffer.swap(other); }
368  OPENVDB_DEPRECATED const Buffer& getBuffer() const { return mBuffer; }
369  const Buffer& buffer() const { return mBuffer; }
370  Buffer& buffer() { return mBuffer; }
371 
372  //
373  // I/O methods
374  //
377  void readTopology(std::istream&, bool fromHalf = false);
380  void writeTopology(std::ostream&, bool toHalf = false) const;
381 
384  void readBuffers(std::istream&, bool fromHalf = false);
387  void writeBuffers(std::ostream&, bool toHalf = false) const;
388 
389  size_t streamingSize(bool toHalf = false) const;
390 
391  //
392  // Accessor methods
393  //
395  const ValueType& getValue(const Coord& xyz) const;
397  const ValueType& getValue(Index offset) const;
398 
402  bool probeValue(const Coord& xyz, ValueType& val) const;
406  bool probeValue(Index offset, ValueType& val) const;
407 
409  static Index getValueLevel(const Coord&) { return LEVEL; }
410 
412  void setActiveState(const Coord& xyz, bool on);
413 
415  void setValueOff(const Coord& xyz) { mValueMask.setOff(LeafNode::coord2offset(xyz)); }
417  void setValueOff(Index offset) { assert(offset < SIZE); mValueMask.setOff(offset); }
418 
420  void setValueOff(const Coord& xyz, const ValueType& val);
422  void setValueOff(Index offset, const ValueType& val);
423 
425  void setValueOn(const Coord& xyz) { mValueMask.setOn(LeafNode::coord2offset(xyz)); }
427  void setValueOn(Index offset) { assert(offset < SIZE); mValueMask.setOn(offset); }
429  void setValueOn(const Coord& xyz, const ValueType& val) {
430  this->setValueOn(LeafNode::coord2offset(xyz), val);
431  }
433  void setValue(const Coord& xyz, const ValueType& val) { this->setValueOn(xyz, val); };
435  void setValueOn(Index offset, const ValueType& val) {
436  mBuffer[offset] = val;
437  mValueMask.setOn(offset);
438  }
439 
442  void setValueOnMin(const Coord& xyz, const ValueType& val) {
443  this->setValueOnMin(LeafNode::coord2offset(xyz), val);
444  }
447  void setValueOnMin(Index offset, const ValueType& val) {
448  mBuffer[offset] = std::min(val, mBuffer[offset]);
449  mValueMask.setOn(offset);
450  }
451 
454  void setValueOnMax(const Coord& xyz, const ValueType& val) {
455  this->setValueOnMax(LeafNode::coord2offset(xyz), val);
456  }
459  void setValueOnMax(Index offset, const ValueType& val) {
460  mBuffer[offset] = std::max(val, mBuffer[offset]);
461  mValueMask.setOn(offset);
462  }
463 
466  void setValueOnSum(const Coord& xyz, const ValueType& val) {
467  this->setValueOnSum(LeafNode::coord2offset(xyz), val);
468  }
471  void setValueOnSum(Index offset, const ValueType& val) {
472  mBuffer[offset] += val;
473  mValueMask.setOn(offset);
474  }
476  OPENVDB_DEPRECATED void resetValue(const Coord& xyz, const ValueType& val) {
477  this->resetValue(LeafNode::coord2offset(xyz), val);
478  }
481  void setValueOnly(const Coord& xyz, const ValueType& val) {
482  this->setValueOnly(LeafNode::coord2offset(xyz), val);
483  }
488  void setValueOnly(Index offset, const ValueType& val) {
489  assert(offset<SIZE); mBuffer[offset] = val;
490  }
492  OPENVDB_DEPRECATED void resetValue(Index offset, const ValueType& val) {
493  mBuffer[offset] = val;
494  }
495 
497  void addValue(const ValueType& val);
499  void scaleValue(const ValueType& scale);
500 
503  OPENVDB_DEPRECATED void setValueMaskOn() { mValueMask.setOn(); }
506  OPENVDB_DEPRECATED void setValueMaskOff() { mValueMask.setOff(); }
507 
509  void setValuesOn() { mValueMask.setOn(); }
511  void setValuesOff() { mValueMask.setOff(); }
512 
514  bool isValueOn(const Coord& xyz) const { return this->isValueOn(LeafNode::coord2offset(xyz)); }
516  bool isValueOn(Index offset) const { return mValueMask.isOn(offset); }
517 
519  static bool hasActiveTiles() { return false; }
520 
523  void fill(const CoordBBox& bbox, const ValueType&, bool active = true);
524 
526  void fill(const ValueType& value);
527 
529  void fill(const ValueType& value, bool active);
530 
533  template<typename AccessorT>
534  const ValueType& getValueAndCache(const Coord& xyz, AccessorT&) const
535  {
536  return this->getValue(xyz);
537  }
538 
541  template<typename AccessorT>
542  bool isValueOnAndCache(const Coord& xyz, AccessorT&) const { return this->isValueOn(xyz); }
543 
546  template<typename AccessorT>
547  void setValueAndCache(const Coord& xyz, const ValueType& val, AccessorT&)
548  {
549  this->setValueOn(xyz, val);
550  }
551 
555  template<typename AccessorT>
556  void setValueOnlyAndCache(const Coord& xyz, const ValueType& val, AccessorT&)
557  {
558  this->setValueOnly(xyz, val);
559  }
560 
565  template<typename AccessorT>
566  void setValueOnSumAndCache(const Coord& xyz, const ValueType& val, AccessorT&)
567  {
568  this->setValueOnSum(xyz, val);
569  }
570 
573  template<typename AccessorT>
574  void setValueOffAndCache(const Coord& xyz, const ValueType& value, AccessorT&)
575  {
576  this->setValueOff(xyz, value);
577  }
578 
582  template<typename AccessorT>
583  void setActiveStateAndCache(const Coord& xyz, bool on, AccessorT&)
584  {
585  this->setActiveState(xyz, on);
586  }
587 
591  template<typename AccessorT>
592  bool probeValueAndCache(const Coord& xyz, ValueType& val, AccessorT&) const
593  {
594  return this->probeValue(xyz, val);
595  }
596 
600  template<typename AccessorT>
601  const ValueType& getValue(const Coord& xyz, bool& state, int& level, AccessorT&) const
602  {
603  const Index offset = this->coord2offset(xyz);
604  state = mValueMask.isOn(offset);
605  level = LEVEL;
606  return mBuffer[offset];
607  }
608  template<typename ProbeType, typename AccessorT>
609  OPENVDB_DEPRECATED const ValueType& probe(const Coord& xyz, ProbeType& p, AccessorT&) const
610  {
611  const Index offset = this->coord2offset(xyz);
612  p.setState(mValueMask.isOn(offset));
613  p.setLevel(LEVEL);
614  return mBuffer[offset];
615  }
616  template<bool State, bool Level, typename AccessorT>
617  OPENVDB_DEPRECATED const ValueType& probe(const Coord& xyz, bool& state, int& level, AccessorT&) const
618  {
619  const Index offset = this->coord2offset(xyz);
620  if (State) state = mValueMask.isOn(offset);
621  if (Level) level = LEVEL;
622  return mBuffer[offset];
623  }
624 
627  template<typename AccessorT>
628  static Index getValueLevelAndCache(const Coord&, AccessorT&) { return LEVEL; }
629 
631  template<typename AccessorT>
632  OPENVDB_DEPRECATED void updateCache(const Coord&, AccessorT&) const {}
633 
637  const ValueType& getFirstValue() const { return mBuffer[0]; }
639  const ValueType& getLastValue() const { return mBuffer[SIZE - 1]; }
640 
643  void resetBackground(const ValueType& oldBackground, const ValueType& newBackground);
644 
645  void signedFloodFill(const ValueType& background);
646 
647  void negate();
648 
649  void merge(const LeafNode&);
650  void merge(const LeafNode& other, const ValueType& /*bg*/, const ValueType& /*otherBG*/)
651  {
652  LeafNode::merge(other);
653  }
655 
660  template<typename OtherType>
661  void topologyUnion(const LeafNode<OtherType, Log2Dim>& other);
662 
671  template<typename OtherType>
672  void topologyIntersection(const LeafNode<OtherType, Log2Dim>& other, const ValueType&);
673 
674  template<typename CombineOp>
675  void combine(const LeafNode& other, CombineOp& op);
676  template<typename CombineOp>
677  void combine(const ValueType& value, bool valueIsActive, CombineOp& op);
678 
679  template<typename CombineOp>
680  void combine2(const LeafNode& other, const ValueType&, bool valueIsActive, CombineOp&);
681  template<typename CombineOp>
682  void combine2(const ValueType&, const LeafNode& other, bool valueIsActive, CombineOp&);
683  template<typename CombineOp>
684  void combine2(const LeafNode& b0, const LeafNode& b1, CombineOp&);
685 
691  template<typename BBoxOp> void visitActiveBBox(BBoxOp&) const;
692 
693  template<typename VisitorOp> void visit(VisitorOp&);
694  template<typename VisitorOp> void visit(VisitorOp&) const;
695 
696  template<typename OtherLeafNodeType, typename VisitorOp>
697  void visit2Node(OtherLeafNodeType& other, VisitorOp&);
698  template<typename OtherLeafNodeType, typename VisitorOp>
699  void visit2Node(OtherLeafNodeType& other, VisitorOp&) const;
700  template<typename IterT, typename VisitorOp>
701  void visit2(IterT& otherIter, VisitorOp&, bool otherIsLHS = false);
702  template<typename IterT, typename VisitorOp>
703  void visit2(IterT& otherIter, VisitorOp&, bool otherIsLHS = false) const;
704 
706 
707  template<typename PruneOp> void pruneOp(PruneOp&) {}
708  void prune(const ValueType& /*tolerance*/ = zeroVal<ValueType>()) {}
709  void pruneInactive(const ValueType&) {}
711 
712 
713  LeafNode* touchLeaf(const Coord&) { return this; }
714  template<typename AccessorT>
715  LeafNode* touchLeafAndCache(const Coord&, AccessorT&) { return this; }
716  LeafNode* probeLeaf(const Coord&) { return this; }
717  template<typename AccessorT>
718  LeafNode* probeLeafAndCache(const Coord&, AccessorT&) { return this; }
720 
721 
722  const LeafNode* probeConstLeaf(const Coord&) const { return this; }
723  template<typename AccessorT>
724  const LeafNode* probeConstLeafAndCache(const Coord&, AccessorT&) const { return this; }
726 
730  bool isConstant(ValueType& constValue, bool& state,
731  const ValueType& tolerance = zeroVal<ValueType>()) const;
733  bool isInactive() const { return mValueMask.isOff(); }
734 
735 protected:
736  friend class ::TestLeaf;
737  template<typename> friend class ::TestLeafIO;
738 
739  // During topology-only construction, access is needed
740  // to protected/private members of other template instances.
741  template<typename, Index> friend class LeafNode;
742 
749 
750  // Allow iterators to call mask accessor methods (see below).
755 
762 
763  // Mask accessors
764 public:
765  bool isValueMaskOn(Index n) const { return mValueMask.isOn(n); }
766  bool isValueMaskOn() const { return mValueMask.isOn(); }
767  bool isValueMaskOff(Index n) const { return mValueMask.isOff(n); }
768  bool isValueMaskOff() const { return mValueMask.isOff(); }
769  const NodeMaskType& getValueMask() const { return mValueMask; }
770  NodeMaskType& getValueMask() { return mValueMask; }
771  bool isChildMaskOn(Index) const { return false; } // leaf nodes have no children
772  bool isChildMaskOff(Index) const { return true; }
773  bool isChildMaskOff() const { return true; }
774 protected:
775  void setValueMask(Index n, bool on) { mValueMask.set(n, on); }
776  void setValueMaskOn(Index n) { setValueMask(n, true); }
777  void setValueMaskOff(Index n) { mValueMask.setOff(n); }
778 
780  static void evalNodeOrigin(Coord& xyz) { xyz &= ~(DIM - 1); }
781 
782  template<typename NodeT, typename VisitorOp, typename ChildAllIterT>
783  static inline void doVisit(NodeT&, VisitorOp&);
784 
785  template<typename NodeT, typename OtherNodeT, typename VisitorOp,
786  typename ChildAllIterT, typename OtherChildAllIterT>
787  static inline void doVisit2Node(NodeT& self, OtherNodeT& other, VisitorOp&);
788 
789  template<typename NodeT, typename VisitorOp,
790  typename ChildAllIterT, typename OtherChildAllIterT>
791  static inline void doVisit2(NodeT& self, OtherChildAllIterT&, VisitorOp&, bool otherIsLHS);
792 
793 private:
794 
795  // Disallow copying.
796  LeafNode& operator=(const LeafNode&);
797 
798 }; // end of LeafNode class
799 
800 
802 
803 template<typename T, Index Log2Dim>
804 inline
806  mValueMask(),//default is off!
807  mOrigin(0, 0, 0)
808 {
809 }
810 
811 
812 template<typename T, Index Log2Dim>
813 inline
814 LeafNode<T, Log2Dim>::LeafNode(const Coord& xyz, const T& val, bool active):
815  mBuffer(val),
816  mValueMask(active),
817  mOrigin(xyz & (~(DIM - 1)))
818 {
819 }
820 
821 template<typename T, Index Log2Dim>
822 template<typename OtherValueType>
823 inline
825  const ValueType& background, TopologyCopy):
826  mBuffer(background),
827  mValueMask(other.mValueMask),
828  mOrigin(other.mOrigin)
829 {
830 }
831 
832 template<typename T, Index Log2Dim>
833 template<typename OtherValueType>
834 inline
836  const ValueType& offValue, const ValueType& onValue, TopologyCopy):
837  mValueMask(other.mValueMask),
838  mOrigin(other.mOrigin)
839 {
840  for (Index i = 0; i < SIZE; ++i) {
841  mBuffer[i] = (mValueMask.isOn(i) ? onValue : offValue);
842  }
843 }
844 
845 template<typename T, Index Log2Dim>
846 inline
848  mBuffer(other.mBuffer),
849  mValueMask(other.mValueMask),
850  mOrigin(other.mOrigin)
851 {
852 }
853 
854 
855 template<typename T, Index Log2Dim>
856 inline
858 {
859 }
860 
861 
862 template<typename T, Index Log2Dim>
863 inline std::string
865 {
866  std::ostringstream ostr;
867  ostr << "LeafNode @" << mOrigin << ": " << mBuffer;
868  return ostr.str();
869 }
870 
871 
873 
874 
875 template<typename T, Index Log2Dim>
876 inline Index
878 {
879  assert ((xyz[0]&DIM-1u)<DIM && (xyz[1]&DIM-1u)<DIM && (xyz[2]&DIM-1u)<DIM);
880  return ((xyz[0]&DIM-1u)<<2*Log2Dim)
881  + ((xyz[1]&DIM-1u)<< Log2Dim)
882  + (xyz[2]&DIM-1u);
883 }
884 
885 
886 template<typename T, Index Log2Dim>
887 inline void
889 {
890  assert(n<(1<< 3*Log2Dim));
891  xyz.setX(n >> 2*Log2Dim);
892  n &= ((1<<2*Log2Dim)-1);
893  xyz.setY(n >> Log2Dim);
894  xyz.setZ(n & ((1<<Log2Dim)-1));
895 }
896 
897 
898 template<typename T, Index Log2Dim>
899 inline Coord
901 {
902  Coord local;
903  this->offset2coord(n, local);
904  return Coord(local + this->getOrigin());
905 }
906 
907 
909 
910 
911 template<typename ValueT, Index Log2Dim>
912 inline const ValueT&
914 {
915  return this->getValue(LeafNode::coord2offset(xyz));
916 }
917 
918 template<typename ValueT, Index Log2Dim>
919 inline const ValueT&
921 {
922  assert(offset < SIZE);
923  return mBuffer[offset];
924 }
925 
926 
927 template<typename T, Index Log2Dim>
928 inline bool
930 {
931  return this->probeValue(LeafNode::coord2offset(xyz), val);
932 }
933 
934 template<typename T, Index Log2Dim>
935 inline bool
937 {
938  assert(offset < SIZE);
939  val = mBuffer[offset];
940  return mValueMask.isOn(offset);
941 }
942 
943 
944 template<typename T, Index Log2Dim>
945 inline void
947 {
948  this->setValueOff(LeafNode::coord2offset(xyz), val);
949 }
950 
951 template<typename T, Index Log2Dim>
952 inline void
954 {
955  assert(offset < SIZE);
956  mBuffer[offset] = val;
957  mValueMask.setOff(offset);
958 }
959 
960 
961 template<typename T, Index Log2Dim>
962 inline void
964 {
965  mValueMask.set(this->coord2offset(xyz), on);
966 }
967 
968 
969 template<typename T, Index Log2Dim>
970 inline void
972 {
973  for (typename NodeMaskType::OnIterator iter = mValueMask.beginOn(); iter; ++iter) {
974  mBuffer[iter.pos()] += val;
975  }
976 }
977 
978 
979 template<typename T, Index Log2Dim>
980 inline void
982 {
983  for (typename NodeMaskType::OnIterator iter = mValueMask.beginOn(); iter; ++iter) {
984  mBuffer[iter.pos()] *= scale;
985  }
986 }
987 
988 
990 
991 
992 template<typename T, Index Log2Dim>
993 inline void
994 LeafNode<T, Log2Dim>::fill(const CoordBBox& bbox, const ValueType& value, bool active)
995 {
996  for (Int32 x = bbox.min().x(); x <= bbox.max().x(); ++x) {
997  const Index offsetX = (x&DIM-1u)<<2*Log2Dim;
998  for (Int32 y = bbox.min().y(); y <= bbox.max().y(); ++y) {
999  const Index offsetXY = offsetX + ((y&DIM-1u)<< Log2Dim);
1000  for (Int32 z = bbox.min().z(); z <= bbox.max().z(); ++z) {
1001  const Index offset = offsetXY + (z&DIM-1u);
1002  mBuffer[offset] = value;
1003  mValueMask.set(offset, active);
1004  }
1005  }
1006  }
1007 }
1008 
1009 template<typename T, Index Log2Dim>
1010 inline void
1012 {
1013  mBuffer.fill(value);
1014 }
1015 
1016 template<typename T, Index Log2Dim>
1017 inline void
1018 LeafNode<T, Log2Dim>::fill(const ValueType& value, bool active)
1019 {
1020  mBuffer.fill(value);
1021  mValueMask.set(active);
1022 }
1023 
1024 
1026 
1027 
1028 template<typename T, Index Log2Dim>
1029 inline void
1030 LeafNode<T, Log2Dim>::readTopology(std::istream& is, bool /*fromHalf*/)
1031 {
1032  mValueMask.load(is);
1033 }
1034 
1035 
1036 template<typename T, Index Log2Dim>
1037 inline void
1038 LeafNode<T, Log2Dim>::writeTopology(std::ostream& os, bool /*toHalf*/) const
1039 {
1040  mValueMask.save(os);
1041 }
1042 
1043 
1045 
1046 
1047 template<typename T, Index Log2Dim>
1048 inline void
1049 LeafNode<T,Log2Dim>::readBuffers(std::istream& is, bool fromHalf)
1050 {
1051  // Read in the value mask.
1052  mValueMask.load(is);
1053 
1054  int8_t numBuffers = 1;
1056  // Read in the origin.
1057  is.read(reinterpret_cast<char*>(&mOrigin), sizeof(Coord::ValueType) * 3);
1058 
1059  // Read in the number of buffers, which should now always be one.
1060  is.read(reinterpret_cast<char*>(&numBuffers), sizeof(int8_t));
1061  }
1062 
1063  io::readCompressedValues(is, mBuffer.mData, SIZE, mValueMask, fromHalf);
1064 
1065  if (numBuffers > 1) {
1066  // Read in and discard auxiliary buffers that were created with earlier
1067  // versions of the library. (Auxiliary buffers are not mask compressed.)
1068  const bool zipped = io::getDataCompression(is) & io::COMPRESS_ZIP;
1069  Buffer temp;
1070  for (int i = 1; i < numBuffers; ++i) {
1071  if (fromHalf) {
1072  io::HalfReader<io::RealToHalf<T>::isReal, T>::read(is, temp.mData, SIZE, zipped);
1073  } else {
1074  io::readData<T>(is, temp.mData, SIZE, zipped);
1075  }
1076  }
1077  }
1078 }
1079 
1080 
1081 template<typename T, Index Log2Dim>
1082 inline void
1083 LeafNode<T, Log2Dim>::writeBuffers(std::ostream& os, bool toHalf) const
1084 {
1085  // Write out the value mask.
1086  mValueMask.save(os);
1087 
1088  io::writeCompressedValues(os, mBuffer.mData, SIZE,
1089  mValueMask, /*childMask=*/NodeMaskType(), toHalf);
1090 }
1091 
1092 
1094 
1095 
1096 template<typename T, Index Log2Dim>
1097 inline bool
1099 {
1100  return mOrigin == other.mOrigin &&
1101  mValueMask == other.mValueMask &&
1102  mBuffer == other.mBuffer;
1103 }
1104 
1105 
1106 template<typename T, Index Log2Dim>
1107 inline Index64
1109 {
1110  return mBuffer.memUsage() + sizeof(mOrigin) + mValueMask.memUsage();
1111 }
1112 
1113 
1114 template<typename T, Index Log2Dim>
1115 inline void
1117 {
1118  const CoordBBox this_bbox = this->getNodeBoundingBox();
1119  if (bbox.isInside(this_bbox)) {
1120  // nothing to do
1121  } else if (this->isDense()) {
1122  bbox.expand(this_bbox);
1123  } else {
1124  for (ValueOnCIter iter=this->cbeginValueOn(); iter; ++iter) bbox.expand(iter.getCoord());
1125  }
1126 }
1127 
1128 
1129 template<typename T, Index Log2Dim>
1130 template<typename OtherType, Index OtherLog2Dim>
1131 inline bool
1133 {
1134  assert(other);
1135  return (Log2Dim == OtherLog2Dim && mValueMask == other->getValueMask());
1136 }
1137 
1138 
1139 template<typename T, Index Log2Dim>
1140 inline bool
1141 LeafNode<T, Log2Dim>::isConstant(T& constValue, bool& state, const T& tolerance) const
1142 {
1143  if (!mValueMask.isOn() && !mValueMask.isOff()) return false;
1144 
1145  state = mValueMask.isOn();
1146 
1147  bool allEqual = true;
1148  const T value = mBuffer[0];
1149  for (Index i = 1; allEqual && i < SIZE; ++i) {
1151  allEqual = math::isApproxEqual(mBuffer[i], value, tolerance);
1152  }
1153  if (allEqual) constValue = value;
1154  return allEqual;
1155 }
1156 
1157 
1161 template<typename T, Index Log2Dim>
1162 inline void
1164 {
1165  assert(!math::isZero(background)); // background must differ from -background
1166  const Index first = mValueMask.findFirstOn();
1167 
1168  if (first == SIZE) return; // empty
1169 
1170  const T zero = zeroVal<T>();
1171  bool inside = (mBuffer[first] < zero), xInside = inside, yInside = inside;
1172  for (Index i = 0; i != (1 << Log2Dim); ++i) {
1173  const Index x = i << (2 * Log2Dim);
1174  if (mValueMask.isOn(x)) {
1175  xInside = (mBuffer[x] < zero); // element(i, 0, 0)
1176  }
1177  yInside = xInside;
1178  for (Index j = 0; j != (1 << Log2Dim); ++j) {
1179  const Index xy = x + (j << Log2Dim);
1180  if (mValueMask.isOn(xy)) {
1181  yInside = (mBuffer[xy] < zero); // element(i, j, 0)
1182  }
1183  inside = yInside;
1184  for (Index k = 0; k != (1 << Log2Dim); ++k) {
1185  const Index xyz = xy + k; // element(i, j, k)
1186  if (mValueMask.isOn(xyz)) {
1187  inside = (mBuffer[xyz] < zero);
1188  } else if (inside) {
1189  mBuffer[xyz] = negative(background);
1190  } else { // outside
1191  mBuffer[xyz] = background;
1192  }
1193  }
1194  }
1195  }
1196 }
1197 
1198 
1199 template<typename T, Index Log2Dim>
1200 inline void
1201 LeafNode<T, Log2Dim>::resetBackground(const T& oldBackground, const T& newBackground)
1202 {
1203  if (math::isExactlyEqual(oldBackground, newBackground)) return;
1204  typename NodeMaskType::OffIterator iter;
1205  // For all inactive values...
1206  for (iter = this->mValueMask.beginOff(); iter; ++iter) {
1207  ValueType &inactiveValue = mBuffer[iter.pos()];
1208  if (math::isApproxEqual(inactiveValue, oldBackground)) {
1209  inactiveValue = newBackground;
1210  } else if (math::isApproxEqual(inactiveValue, negative(oldBackground))) {
1211  inactiveValue = negative(newBackground);
1212  }
1213  }
1214 }
1215 
1216 
1217 template<typename T, Index Log2Dim>
1218 inline void
1220 {
1221  typename NodeMaskType::OnIterator iter = other.mValueMask.beginOn();
1222  for (; iter; ++iter) {
1223  const Index n = iter.pos();
1224  if (mValueMask.isOn(n)) continue;
1225  mBuffer[n] = other.mBuffer[n];
1226  mValueMask.setOn(n);
1227  }
1228 }
1229 
1230 
1231 template<typename T, Index Log2Dim>
1232 template<typename OtherType>
1233 inline void
1235 {
1236  mValueMask |= other.getValueMask();
1237 }
1238 
1239 template<typename T, Index Log2Dim>
1240 template<typename OtherType>
1241 inline void
1243  const ValueType&)
1244 {
1245  mValueMask &= other.getValueMask();
1246 }
1247 
1248 template<typename T, Index Log2Dim>
1249 inline void
1251 {
1252  for (Index i = 0; i < SIZE; ++i) {
1253  mBuffer[i] = -mBuffer[i];
1254  }
1255 }
1256 
1257 
1259 
1260 
1261 template<typename T, Index Log2Dim>
1262 template<typename CombineOp>
1263 inline void
1264 LeafNode<T, Log2Dim>::combine(const LeafNode& other, CombineOp& op)
1265 {
1266  CombineArgs<T> args;
1267  for (Index i = 0; i < SIZE; ++i) {
1268  op(args.setARef(mBuffer[i])
1269  .setAIsActive(mValueMask.isOn(i))
1270  .setBRef(other.mBuffer[i])
1271  .setBIsActive(other.mValueMask.isOn(i))
1272  .setResultRef(mBuffer[i]));
1273  mValueMask.set(i, args.resultIsActive());
1274  }
1275 }
1276 
1277 
1278 template<typename T, Index Log2Dim>
1279 template<typename CombineOp>
1280 inline void
1281 LeafNode<T, Log2Dim>::combine(const ValueType& value, bool valueIsActive, CombineOp& op)
1282 {
1283  CombineArgs<T> args;
1284  args.setBRef(value).setBIsActive(valueIsActive);
1285  for (Index i = 0; i < SIZE; ++i) {
1286  op(args.setARef(mBuffer[i])
1287  .setAIsActive(mValueMask.isOn(i))
1288  .setResultRef(mBuffer[i]));
1289  mValueMask.set(i, args.resultIsActive());
1290  }
1291 }
1292 
1293 
1295 
1296 
1297 template<typename T, Index Log2Dim>
1298 template<typename CombineOp>
1299 inline void
1301  bool valueIsActive, CombineOp& op)
1302 {
1303  CombineArgs<T> args;
1304  args.setBRef(value).setBIsActive(valueIsActive);
1305  for (Index i = 0; i < SIZE; ++i) {
1306  op(args.setARef(other.mBuffer[i])
1307  .setAIsActive(other.mValueMask.isOn(i))
1308  .setResultRef(mBuffer[i]));
1309  mValueMask.set(i, args.resultIsActive());
1310  }
1311 }
1312 
1313 
1314 template<typename T, Index Log2Dim>
1315 template<typename CombineOp>
1316 inline void
1318  bool valueIsActive, CombineOp& op)
1319 {
1320  CombineArgs<T> args;
1321  args.setARef(value).setAIsActive(valueIsActive);
1322  for (Index i = 0; i < SIZE; ++i) {
1323  op(args.setBRef(other.mBuffer[i])
1324  .setBIsActive(other.mValueMask.isOn(i))
1325  .setResultRef(mBuffer[i]));
1326  mValueMask.set(i, args.resultIsActive());
1327  }
1328 }
1329 
1330 
1331 template<typename T, Index Log2Dim>
1332 template<typename CombineOp>
1333 inline void
1334 LeafNode<T, Log2Dim>::combine2(const LeafNode& b0, const LeafNode& b1, CombineOp& op)
1335 {
1336  CombineArgs<T> args;
1337  for (Index i = 0; i < SIZE; ++i) {
1338  mValueMask.set(i, b0.mValueMask.isOn(i) || b1.mValueMask.isOn(i));
1339  op(args.setARef(b0.mBuffer[i])
1340  .setAIsActive(b0.mValueMask.isOn(i))
1341  .setBRef(b1.mBuffer[i])
1342  .setBIsActive(b1.mValueMask.isOn(i))
1343  .setResultRef(mBuffer[i]));
1344  mValueMask.set(i, args.resultIsActive());
1345  }
1346 }
1347 
1348 
1350 
1351 
1352 template<typename T, Index Log2Dim>
1353 template<typename BBoxOp>
1354 inline void
1356 {
1357  if (op.template descent<LEVEL>()) {
1358  for (ValueOnCIter i=this->cbeginValueOn(); i; ++i) {
1359 #ifdef _MSC_VER
1360  op.operator()<LEVEL>(CoordBBox::createCube(i.getCoord(), 1));
1361 #else
1362  op.template operator()<LEVEL>(CoordBBox::createCube(i.getCoord(), 1));
1363 #endif
1364  }
1365  } else {
1366 #ifdef _MSC_VER
1367  op.operator()<LEVEL>(this->getNodeBoundingBox());
1368 #else
1369  op.template operator()<LEVEL>(this->getNodeBoundingBox());
1370 #endif
1371  }
1372 }
1373 
1374 
1375 template<typename T, Index Log2Dim>
1376 template<typename VisitorOp>
1377 inline void
1379 {
1380  doVisit<LeafNode, VisitorOp, ChildAllIter>(*this, op);
1381 }
1382 
1383 
1384 template<typename T, Index Log2Dim>
1385 template<typename VisitorOp>
1386 inline void
1387 LeafNode<T, Log2Dim>::visit(VisitorOp& op) const
1388 {
1389  doVisit<const LeafNode, VisitorOp, ChildAllCIter>(*this, op);
1390 }
1391 
1392 
1393 template<typename T, Index Log2Dim>
1394 template<typename NodeT, typename VisitorOp, typename ChildAllIterT>
1395 inline void
1396 LeafNode<T, Log2Dim>::doVisit(NodeT& self, VisitorOp& op)
1397 {
1398  for (ChildAllIterT iter = self.beginChildAll(); iter; ++iter) {
1399  op(iter);
1400  }
1401 }
1402 
1403 
1405 
1406 
1407 template<typename T, Index Log2Dim>
1408 template<typename OtherLeafNodeType, typename VisitorOp>
1409 inline void
1410 LeafNode<T, Log2Dim>::visit2Node(OtherLeafNodeType& other, VisitorOp& op)
1411 {
1412  doVisit2Node<LeafNode, OtherLeafNodeType, VisitorOp, ChildAllIter,
1413  typename OtherLeafNodeType::ChildAllIter>(*this, other, op);
1414 }
1415 
1416 
1417 template<typename T, Index Log2Dim>
1418 template<typename OtherLeafNodeType, typename VisitorOp>
1419 inline void
1420 LeafNode<T, Log2Dim>::visit2Node(OtherLeafNodeType& other, VisitorOp& op) const
1421 {
1422  doVisit2Node<const LeafNode, OtherLeafNodeType, VisitorOp, ChildAllCIter,
1423  typename OtherLeafNodeType::ChildAllCIter>(*this, other, op);
1424 }
1425 
1426 
1427 template<typename T, Index Log2Dim>
1428 template<
1429  typename NodeT,
1430  typename OtherNodeT,
1431  typename VisitorOp,
1432  typename ChildAllIterT,
1433  typename OtherChildAllIterT>
1434 inline void
1435 LeafNode<T, Log2Dim>::doVisit2Node(NodeT& self, OtherNodeT& other, VisitorOp& op)
1436 {
1437  // Allow the two nodes to have different ValueTypes, but not different dimensions.
1438  BOOST_STATIC_ASSERT(OtherNodeT::SIZE == NodeT::SIZE);
1439  BOOST_STATIC_ASSERT(OtherNodeT::LEVEL == NodeT::LEVEL);
1440 
1441  ChildAllIterT iter = self.beginChildAll();
1442  OtherChildAllIterT otherIter = other.beginChildAll();
1443 
1444  for ( ; iter && otherIter; ++iter, ++otherIter) {
1445  op(iter, otherIter);
1446  }
1447 }
1448 
1449 
1451 
1452 
1453 template<typename T, Index Log2Dim>
1454 template<typename IterT, typename VisitorOp>
1455 inline void
1456 LeafNode<T, Log2Dim>::visit2(IterT& otherIter, VisitorOp& op, bool otherIsLHS)
1457 {
1458  doVisit2<LeafNode, VisitorOp, ChildAllIter, IterT>(
1459  *this, otherIter, op, otherIsLHS);
1460 }
1461 
1462 
1463 template<typename T, Index Log2Dim>
1464 template<typename IterT, typename VisitorOp>
1465 inline void
1466 LeafNode<T, Log2Dim>::visit2(IterT& otherIter, VisitorOp& op, bool otherIsLHS) const
1467 {
1468  doVisit2<const LeafNode, VisitorOp, ChildAllCIter, IterT>(
1469  *this, otherIter, op, otherIsLHS);
1470 }
1471 
1472 
1473 template<typename T, Index Log2Dim>
1474 template<
1475  typename NodeT,
1476  typename VisitorOp,
1477  typename ChildAllIterT,
1478  typename OtherChildAllIterT>
1479 inline void
1480 LeafNode<T, Log2Dim>::doVisit2(NodeT& self, OtherChildAllIterT& otherIter,
1481  VisitorOp& op, bool otherIsLHS)
1482 {
1483  if (!otherIter) return;
1484 
1485  if (otherIsLHS) {
1486  for (ChildAllIterT iter = self.beginChildAll(); iter; ++iter) {
1487  op(otherIter, iter);
1488  }
1489  } else {
1490  for (ChildAllIterT iter = self.beginChildAll(); iter; ++iter) {
1491  op(iter, otherIter);
1492  }
1493  }
1494 }
1495 
1496 
1498 
1499 
1500 template<typename T, Index Log2Dim>
1501 inline std::ostream&
1502 operator<<(std::ostream& os, const typename LeafNode<T, Log2Dim>::Buffer& buf)
1503 {
1504  for (Index32 i = 0, N = buf.size(); i < N; ++i) os << buf.mData[i] << ", ";
1505  return os;
1506 }
1507 
1508 } // namespace tree
1509 } // namespace OPENVDB_VERSION_NAME
1510 } // namespace openvdb
1511 
1512 
1514 
1515 
1516 // Specialization for LeafNodes of type bool
1517 #include "LeafNodeBool.h"
1518 
1519 #endif // OPENVDB_TREE_LEAFNODE_HAS_BEEN_INCLUDED
1520 
1521 // Copyright (c) 2012 DreamWorks Animation LLC
1522 // All rights reserved. This software is distributed under the
1523 // Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )