Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt28
-rw-r--r--extern/CMakeLists.txt4
-rw-r--r--extern/openvdb/CMakeLists.txt134
-rw-r--r--extern/openvdb/COPYRIGHT25
-rw-r--r--extern/openvdb/LICENSE373
-rw-r--r--extern/openvdb/README16
-rw-r--r--extern/openvdb/internal/openvdb/Exceptions.h112
-rw-r--r--extern/openvdb/internal/openvdb/Grid.cc487
-rw-r--r--extern/openvdb/internal/openvdb/Grid.h1215
-rw-r--r--extern/openvdb/internal/openvdb/Metadata.h41
-rw-r--r--extern/openvdb/internal/openvdb/Platform.cc38
-rw-r--r--extern/openvdb/internal/openvdb/Platform.h174
-rw-r--r--extern/openvdb/internal/openvdb/PlatformConfig.h57
-rw-r--r--extern/openvdb/internal/openvdb/Types.h374
-rw-r--r--extern/openvdb/internal/openvdb/doc/api_0_98_0.txt196
-rw-r--r--extern/openvdb/internal/openvdb/doc/changes.txt741
-rw-r--r--extern/openvdb/internal/openvdb/doc/doc.txt417
-rw-r--r--extern/openvdb/internal/openvdb/doc/examplecode.txt1018
-rw-r--r--extern/openvdb/internal/openvdb/doc/faq.txt213
-rw-r--r--extern/openvdb/internal/openvdb/doc/math.txt416
-rw-r--r--extern/openvdb/internal/openvdb/io/Archive.cc746
-rw-r--r--extern/openvdb/internal/openvdb/io/Archive.h266
-rw-r--r--extern/openvdb/internal/openvdb/io/Compression.cc145
-rw-r--r--extern/openvdb/internal/openvdb/io/Compression.h566
-rw-r--r--extern/openvdb/internal/openvdb/io/File.cc594
-rw-r--r--extern/openvdb/internal/openvdb/io/File.h220
-rw-r--r--extern/openvdb/internal/openvdb/io/GridDescriptor.cc232
-rw-r--r--extern/openvdb/internal/openvdb/io/GridDescriptor.h135
-rw-r--r--extern/openvdb/internal/openvdb/io/Stream.cc140
-rw-r--r--extern/openvdb/internal/openvdb/io/Stream.h112
-rw-r--r--extern/openvdb/internal/openvdb/math/BBox.h392
-rw-r--r--extern/openvdb/internal/openvdb/math/Coord.h477
-rw-r--r--extern/openvdb/internal/openvdb/math/FiniteDifference.h2280
-rw-r--r--extern/openvdb/internal/openvdb/math/Hermite.cc167
-rw-r--r--extern/openvdb/internal/openvdb/math/Hermite.h493
-rw-r--r--extern/openvdb/internal/openvdb/math/LegacyFrustum.h192
-rw-r--r--extern/openvdb/internal/openvdb/math/Maps.cc283
-rw-r--r--extern/openvdb/internal/openvdb/math/Maps.h2478
-rw-r--r--extern/openvdb/internal/openvdb/math/Mat.h975
-rw-r--r--extern/openvdb/internal/openvdb/math/Mat3.h821
-rw-r--r--extern/openvdb/internal/openvdb/math/Mat4.h1369
-rw-r--r--extern/openvdb/internal/openvdb/math/Math.h652
-rw-r--r--extern/openvdb/internal/openvdb/math/Operators.h2086
-rw-r--r--extern/openvdb/internal/openvdb/math/Proximity.cc377
-rw-r--r--extern/openvdb/internal/openvdb/math/Proximity.h134
-rw-r--r--extern/openvdb/internal/openvdb/math/QuantizedUnitVec.cc98
-rw-r--r--extern/openvdb/internal/openvdb/math/QuantizedUnitVec.h164
-rw-r--r--extern/openvdb/internal/openvdb/math/Quat.h658
-rw-r--r--extern/openvdb/internal/openvdb/math/Stats.h236
-rw-r--r--extern/openvdb/internal/openvdb/math/Stencils.h1466
-rw-r--r--extern/openvdb/internal/openvdb/math/Transform.cc490
-rw-r--r--extern/openvdb/internal/openvdb/math/Transform.h295
-rw-r--r--extern/openvdb/internal/openvdb/math/Tuple.h232
-rw-r--r--extern/openvdb/internal/openvdb/math/Vec2.h518
-rw-r--r--extern/openvdb/internal/openvdb/math/Vec3.h620
-rw-r--r--extern/openvdb/internal/openvdb/math/Vec4.h540
-rw-r--r--extern/openvdb/internal/openvdb/metadata/MetaMap.cc204
-rw-r--r--extern/openvdb/internal/openvdb/metadata/MetaMap.h252
-rw-r--r--extern/openvdb/internal/openvdb/metadata/Metadata.cc168
-rw-r--r--extern/openvdb/internal/openvdb/metadata/Metadata.h414
-rw-r--r--extern/openvdb/internal/openvdb/metadata/StringMetadata.h75
-rw-r--r--extern/openvdb/internal/openvdb/openvdb.cc134
-rw-r--r--extern/openvdb/internal/openvdb/openvdb.h98
-rw-r--r--extern/openvdb/internal/openvdb/tools/Composite.h555
-rw-r--r--extern/openvdb/internal/openvdb/tools/Dense.h418
-rw-r--r--extern/openvdb/internal/openvdb/tools/Filter.h325
-rw-r--r--extern/openvdb/internal/openvdb/tools/GridOperators.h738
-rw-r--r--extern/openvdb/internal/openvdb/tools/GridTransformer.h986
-rw-r--r--extern/openvdb/internal/openvdb/tools/Interpolation.h554
-rw-r--r--extern/openvdb/internal/openvdb/tools/LevelSetAdvect.h712
-rw-r--r--extern/openvdb/internal/openvdb/tools/LevelSetFilter.h411
-rw-r--r--extern/openvdb/internal/openvdb/tools/LevelSetFracture.h360
-rw-r--r--extern/openvdb/internal/openvdb/tools/LevelSetRebuild.h347
-rw-r--r--extern/openvdb/internal/openvdb/tools/LevelSetSphere.h221
-rw-r--r--extern/openvdb/internal/openvdb/tools/LevelSetTracker.h516
-rw-r--r--extern/openvdb/internal/openvdb/tools/LevelSetUtil.h480
-rw-r--r--extern/openvdb/internal/openvdb/tools/MeshToVolume.h2465
-rw-r--r--extern/openvdb/internal/openvdb/tools/Morphology.h359
-rw-r--r--extern/openvdb/internal/openvdb/tools/ParticlesToLevelSet.h613
-rw-r--r--extern/openvdb/internal/openvdb/tools/PointAdvect.h524
-rw-r--r--extern/openvdb/internal/openvdb/tools/PointScatter.h328
-rw-r--r--extern/openvdb/internal/openvdb/tools/Statistics.h370
-rw-r--r--extern/openvdb/internal/openvdb/tools/ValueTransformer.h588
-rw-r--r--extern/openvdb/internal/openvdb/tools/VolumeToMesh.h2710
-rw-r--r--extern/openvdb/internal/openvdb/tree/InternalNode.h2507
-rw-r--r--extern/openvdb/internal/openvdb/tree/Iterator.h276
-rw-r--r--extern/openvdb/internal/openvdb/tree/LeafManager.h522
-rw-r--r--extern/openvdb/internal/openvdb/tree/LeafNode.h1659
-rw-r--r--extern/openvdb/internal/openvdb/tree/LeafNodeBool.h1494
-rw-r--r--extern/openvdb/internal/openvdb/tree/NodeUnion.h137
-rw-r--r--extern/openvdb/internal/openvdb/tree/RootNode.h2649
-rw-r--r--extern/openvdb/internal/openvdb/tree/Tree.h2017
-rw-r--r--extern/openvdb/internal/openvdb/tree/TreeIterator.h1360
-rw-r--r--extern/openvdb/internal/openvdb/tree/Util.h124
-rw-r--r--extern/openvdb/internal/openvdb/tree/ValueAccessor.h2325
-rw-r--r--extern/openvdb/internal/openvdb/util/Formats.cc121
-rw-r--r--extern/openvdb/internal/openvdb/util/Formats.h140
-rw-r--r--extern/openvdb/internal/openvdb/util/MapsUtil.h329
-rw-r--r--extern/openvdb/internal/openvdb/util/Name.h72
-rw-r--r--extern/openvdb/internal/openvdb/util/NodeMasks.h1282
-rw-r--r--extern/openvdb/internal/openvdb/util/NullInterrupter.h90
-rw-r--r--extern/openvdb/internal/openvdb/util/Util.cc77
-rw-r--r--extern/openvdb/internal/openvdb/util/Util.h165
-rw-r--r--extern/openvdb/internal/openvdb/util/logging.h69
-rw-r--r--extern/openvdb/internal/openvdb/version.h139
105 files changed, 61207 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index acd01f43fcd..ba44fc4bb35 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -263,6 +263,7 @@ mark_as_advanced(PYTHON_NUMPY_PATH)
option(WITH_CYCLES "Enable cycles Render Engine" ON)
option(WITH_CYCLES_TEST "Build cycles test application" OFF)
option(WITH_CYCLES_OSL "Build Cycles with OSL support" OFF)
+option(WITH_CYCLES_OPENVDB "Build Cycles with OpenVDB support (http://www.openvdb.org)" ON)
option(WITH_CYCLES_CUDA_BINARIES "Build cycles CUDA binaries" OFF)
set(CYCLES_CUDA_BINARIES_ARCH sm_20 sm_21 sm_30 sm_35 CACHE STRING "CUDA architectures to build binaries for")
mark_as_advanced(CYCLES_CUDA_BINARIES_ARCH)
@@ -848,6 +849,14 @@ if(UNIX AND NOT APPLE)
endif()
endif()
+ if(WITH_CYCLES_OPENVDB)
+ find_package_wrapper(OpenVDB)
+ if(NOT OpenVDB_FOUND)
+ set(WITH_CYCLES_OPENVDB OFF)
+ message(STATUS "OpenVDB was not found. Turning option off.")
+ endif()
+ endif()
+
# OpenSuse needs lutil, ArchLinux not, for now keep, can avoid by using --as-needed
set(PLATFORM_LINKLIBS "-lutil -lc -lm -lpthread -lstdc++")
@@ -1250,6 +1259,19 @@ elseif(WIN32)
set(OPENIMAGEIO_DEFINITIONS "-DUSE_TBB=0")
endif()
+ if(WITH_CYCLES_OPENVDB) #requires tbb and glfw
+ set(TBB ${LIBDIR}/tbb)
+ set(TBB_INCLUDE_DIRS ${TBB}/include)
+ #set(TBB_LIBRARIES optimized OpenImageIO debug OpenImageIO_d)
+ set(TBB_LIBPATH ${TBB}/lib)
+ set(TBB_DEFINITIONS "")
+
+ set(GLFW ${LIBDIR}/glfw)
+ set(GLFW_INCLUDE_DIRS ${GLFW}/include)
+ set(GLFW_LIBPATH ${GLFW}/lib)
+ set(GLFW_DEFINITIONS "")
+ endif()
+
if(WITH_LLVM)
set(LLVM_DIRECTORY ${LIBDIR}/llvm CACHE PATH "Path to the LLVM installation")
file(GLOB LLVM_LIBRARY ${LLVM_DIRECTORY}/lib/*.lib)
@@ -1874,6 +1896,11 @@ if(WITH_IMAGE_REDCODE)
set(REDCODE_INC ${REDCODE})
endif()
+
+if(WITH_CYCLES_OPENVDB)
+ set(OPENVDB_INCLUDE_DIRS "${CMAKE_SOURCE_DIR}/extern/openvdb")
+endif()
+
#-----------------------------------------------------------------------------
# Blender WebPlugin
@@ -2215,6 +2242,7 @@ if(FIRST_RUN)
info_cfg_option(WITH_INTERNATIONAL)
info_cfg_option(WITH_INPUT_NDOF)
info_cfg_option(WITH_CYCLES)
+ info_cfg_option(WITH_CYCLES_OPENVDB)
info_cfg_option(WITH_FREESTYLE)
info_cfg_option(WITH_OPENCOLORIO)
diff --git a/extern/CMakeLists.txt b/extern/CMakeLists.txt
index f6de8737fa7..47e3378ff05 100644
--- a/extern/CMakeLists.txt
+++ b/extern/CMakeLists.txt
@@ -79,3 +79,7 @@ if(WITH_GHOST_XDND)
add_subdirectory(xdnd)
endif()
endif()
+
+if(WITH_CYCLES_OPENVDB)
+ add_subdirectory(openvdb)
+endif()
diff --git a/extern/openvdb/CMakeLists.txt b/extern/openvdb/CMakeLists.txt
new file mode 100644
index 00000000000..7eb33cf2dc8
--- /dev/null
+++ b/extern/openvdb/CMakeLists.txt
@@ -0,0 +1,134 @@
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# The Original Code is Copyright (C) 2006, Blender Foundation
+# All rights reserved.
+#
+# The Original Code is: all of this file.
+#
+# Contributor(s): Rafael Campos.
+#
+# ***** END GPL LICENSE BLOCK *****
+
+set(INC
+ internal
+)
+
+set(INC_SYS
+ ${BOOST_INCLUDE_DIR}
+ ${OPENEXR_INCLUDE_DIR}
+ ${TBB_INCLUDE_DIRS}
+ ${ZLIB_INCLUDE_DIRS}
+)
+
+set(SRC
+ internal/openvdb/Grid.cc
+ internal/openvdb/io/Archive.cc
+ internal/openvdb/io/Compression.cc
+ internal/openvdb/io/File.cc
+ internal/openvdb/io/GridDescriptor.cc
+ internal/openvdb/io/Stream.cc
+ internal/openvdb/math/Hermite.cc
+ internal/openvdb/math/Maps.cc
+ internal/openvdb/math/Proximity.cc
+ internal/openvdb/math/QuantizedUnitVec.cc
+ internal/openvdb/math/Transform.cc
+ internal/openvdb/metadata/Metadata.cc
+ internal/openvdb/metadata/MetaMap.cc
+ internal/openvdb/openvdb.cc
+ internal/openvdb/Platform.cc
+ internal/openvdb/util/Formats.cc
+ internal/openvdb/util/Util.cc
+
+ internal/openvdb/Exceptions.h
+ internal/openvdb/Grid.h
+ internal/openvdb/io/Archive.h
+ internal/openvdb/io/Compression.h
+ internal/openvdb/io/File.h
+ internal/openvdb/io/GridDescriptor.h
+ internal/openvdb/io/Stream.h
+ internal/openvdb/math/BBox.h
+ internal/openvdb/math/Coord.h
+ internal/openvdb/math/FiniteDifference.h
+ internal/openvdb/math/Hermite.h
+ internal/openvdb/math/LegacyFrustum.h
+ internal/openvdb/math/Maps.h
+ internal/openvdb/math/Mat.h
+ internal/openvdb/math/Mat3.h
+ internal/openvdb/math/Mat4.h
+ internal/openvdb/math/Math.h
+ internal/openvdb/math/Operators.h
+ internal/openvdb/math/Proximity.h
+ internal/openvdb/math/QuantizedUnitVec.h
+ internal/openvdb/math/Quat.h
+ internal/openvdb/math/Stats.h
+ internal/openvdb/math/Stencils.h
+ internal/openvdb/math/Transform.h
+ internal/openvdb/math/Tuple.h
+ internal/openvdb/math/Vec2.h
+ internal/openvdb/math/Vec3.h
+ internal/openvdb/math/Vec4.h
+ internal/openvdb/Metadata.h
+ internal/openvdb/metadata/Metadata.h
+ internal/openvdb/metadata/MetaMap.h
+ internal/openvdb/metadata/StringMetadata.h
+ internal/openvdb/openvdb.h
+ internal/openvdb/Platform.h
+ internal/openvdb/PlatformConfig.h
+ internal/openvdb/tools/Composite.h
+ internal/openvdb/tools/Dense.h
+ internal/openvdb/tools/Filter.h
+ internal/openvdb/tools/GridOperators.h
+ internal/openvdb/tools/GridTransformer.h
+ internal/openvdb/tools/Interpolation.h
+ internal/openvdb/tools/LevelSetAdvect.h
+ internal/openvdb/tools/LevelSetFilter.h
+ internal/openvdb/tools/LevelSetFracture.h
+ internal/openvdb/tools/LevelSetRebuild.h
+ internal/openvdb/tools/LevelSetSphere.h
+ internal/openvdb/tools/LevelSetTracker.h
+ internal/openvdb/tools/LevelSetUtil.h
+ internal/openvdb/tools/MeshToVolume.h
+ internal/openvdb/tools/Morphology.h
+ internal/openvdb/tools/ParticlesToLevelSet.h
+ internal/openvdb/tools/PointAdvect.h
+ internal/openvdb/tools/PointScatter.h
+ internal/openvdb/tools/Statistics.h
+ internal/openvdb/tools/ValueTransformer.h
+ internal/openvdb/tools/VolumeToMesh.h
+ internal/openvdb/tree/InternalNode.h
+ internal/openvdb/tree/Iterator.h
+ internal/openvdb/tree/LeafManager.h
+ internal/openvdb/tree/LeafNode.h
+ internal/openvdb/tree/LeafNodeBool.h
+ internal/openvdb/tree/NodeUnion.h
+ internal/openvdb/tree/RootNode.h
+ internal/openvdb/tree/Tree.h
+ internal/openvdb/tree/TreeIterator.h
+ internal/openvdb/tree/Util.h
+ internal/openvdb/tree/ValueAccessor.h
+ internal/openvdb/Types.h
+ internal/openvdb/util/Formats.h
+ internal/openvdb/util/logging.h
+ internal/openvdb/util/MapsUtil.h
+ internal/openvdb/util/Name.h
+ internal/openvdb/util/NodeMasks.h
+ internal/openvdb/util/NullInterrupter.h
+ internal/openvdb/util/Util.h
+ internal/openvdb/version.h
+)
+
+blender_add_lib(extern_openvdb "${SRC}" "${INC}" "${INC_SYS}") \ No newline at end of file
diff --git a/extern/openvdb/COPYRIGHT b/extern/openvdb/COPYRIGHT
new file mode 100644
index 00000000000..4bd6195ad42
--- /dev/null
+++ b/extern/openvdb/COPYRIGHT
@@ -0,0 +1,25 @@
+Copyright (c) 2012-2013 DreamWorks Animation LLC
+
+All rights reserved. This software is distributed under the
+Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+
+Redistributions of source code must retain the above copyright
+and license notice and the following restrictions and disclaimer.
+
+* Neither the name of DreamWorks Animation nor the names of
+its contributors may be used to endorse or promote products derived
+from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
diff --git a/extern/openvdb/LICENSE b/extern/openvdb/LICENSE
new file mode 100644
index 00000000000..14e2f777f6c
--- /dev/null
+++ b/extern/openvdb/LICENSE
@@ -0,0 +1,373 @@
+Mozilla Public License Version 2.0
+==================================
+
+1. Definitions
+--------------
+
+1.1. "Contributor"
+ means each individual or legal entity that creates, contributes to
+ the creation of, or owns Covered Software.
+
+1.2. "Contributor Version"
+ means the combination of the Contributions of others (if any) used
+ by a Contributor and that particular Contributor's Contribution.
+
+1.3. "Contribution"
+ means Covered Software of a particular Contributor.
+
+1.4. "Covered Software"
+ means Source Code Form to which the initial Contributor has attached
+ the notice in Exhibit A, the Executable Form of such Source Code
+ Form, and Modifications of such Source Code Form, in each case
+ including portions thereof.
+
+1.5. "Incompatible With Secondary Licenses"
+ means
+
+ (a) that the initial Contributor has attached the notice described
+ in Exhibit B to the Covered Software; or
+
+ (b) that the Covered Software was made available under the terms of
+ version 1.1 or earlier of the License, but not also under the
+ terms of a Secondary License.
+
+1.6. "Executable Form"
+ means any form of the work other than Source Code Form.
+
+1.7. "Larger Work"
+ means a work that combines Covered Software with other material, in
+ a separate file or files, that is not Covered Software.
+
+1.8. "License"
+ means this document.
+
+1.9. "Licensable"
+ means having the right to grant, to the maximum extent possible,
+ whether at the time of the initial grant or subsequently, any and
+ all of the rights conveyed by this License.
+
+1.10. "Modifications"
+ means any of the following:
+
+ (a) any file in Source Code Form that results from an addition to,
+ deletion from, or modification of the contents of Covered
+ Software; or
+
+ (b) any new file in Source Code Form that contains any Covered
+ Software.
+
+1.11. "Patent Claims" of a Contributor
+ means any patent claim(s), including without limitation, method,
+ process, and apparatus claims, in any patent Licensable by such
+ Contributor that would be infringed, but for the grant of the
+ License, by the making, using, selling, offering for sale, having
+ made, import, or transfer of either its Contributions or its
+ Contributor Version.
+
+1.12. "Secondary License"
+ means either the GNU General Public License, Version 2.0, the GNU
+ Lesser General Public License, Version 2.1, the GNU Affero General
+ Public License, Version 3.0, or any later versions of those
+ licenses.
+
+1.13. "Source Code Form"
+ means the form of the work preferred for making modifications.
+
+1.14. "You" (or "Your")
+ means an individual or a legal entity exercising rights under this
+ License. For legal entities, "You" includes any entity that
+ controls, is controlled by, or is under common control with You. For
+ purposes of this definition, "control" means (a) the power, direct
+ or indirect, to cause the direction or management of such entity,
+ whether by contract or otherwise, or (b) ownership of more than
+ fifty percent (50%) of the outstanding shares or beneficial
+ ownership of such entity.
+
+2. License Grants and Conditions
+--------------------------------
+
+2.1. Grants
+
+Each Contributor hereby grants You a world-wide, royalty-free,
+non-exclusive license:
+
+(a) under intellectual property rights (other than patent or trademark)
+ Licensable by such Contributor to use, reproduce, make available,
+ modify, display, perform, distribute, and otherwise exploit its
+ Contributions, either on an unmodified basis, with Modifications, or
+ as part of a Larger Work; and
+
+(b) under Patent Claims of such Contributor to make, use, sell, offer
+ for sale, have made, import, and otherwise transfer either its
+ Contributions or its Contributor Version.
+
+2.2. Effective Date
+
+The licenses granted in Section 2.1 with respect to any Contribution
+become effective for each Contribution on the date the Contributor first
+distributes such Contribution.
+
+2.3. Limitations on Grant Scope
+
+The licenses granted in this Section 2 are the only rights granted under
+this License. No additional rights or licenses will be implied from the
+distribution or licensing of Covered Software under this License.
+Notwithstanding Section 2.1(b) above, no patent license is granted by a
+Contributor:
+
+(a) for any code that a Contributor has removed from Covered Software;
+ or
+
+(b) for infringements caused by: (i) Your and any other third party's
+ modifications of Covered Software, or (ii) the combination of its
+ Contributions with other software (except as part of its Contributor
+ Version); or
+
+(c) under Patent Claims infringed by Covered Software in the absence of
+ its Contributions.
+
+This License does not grant any rights in the trademarks, service marks,
+or logos of any Contributor (except as may be necessary to comply with
+the notice requirements in Section 3.4).
+
+2.4. Subsequent Licenses
+
+No Contributor makes additional grants as a result of Your choice to
+distribute the Covered Software under a subsequent version of this
+License (see Section 10.2) or under the terms of a Secondary License (if
+permitted under the terms of Section 3.3).
+
+2.5. Representation
+
+Each Contributor represents that the Contributor believes its
+Contributions are its original creation(s) or it has sufficient rights
+to grant the rights to its Contributions conveyed by this License.
+
+2.6. Fair Use
+
+This License is not intended to limit any rights You have under
+applicable copyright doctrines of fair use, fair dealing, or other
+equivalents.
+
+2.7. Conditions
+
+Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
+in Section 2.1.
+
+3. Responsibilities
+-------------------
+
+3.1. Distribution of Source Form
+
+All distribution of Covered Software in Source Code Form, including any
+Modifications that You create or to which You contribute, must be under
+the terms of this License. You must inform recipients that the Source
+Code Form of the Covered Software is governed by the terms of this
+License, and how they can obtain a copy of this License. You may not
+attempt to alter or restrict the recipients' rights in the Source Code
+Form.
+
+3.2. Distribution of Executable Form
+
+If You distribute Covered Software in Executable Form then:
+
+(a) such Covered Software must also be made available in Source Code
+ Form, as described in Section 3.1, and You must inform recipients of
+ the Executable Form how they can obtain a copy of such Source Code
+ Form by reasonable means in a timely manner, at a charge no more
+ than the cost of distribution to the recipient; and
+
+(b) You may distribute such Executable Form under the terms of this
+ License, or sublicense it under different terms, provided that the
+ license for the Executable Form does not attempt to limit or alter
+ the recipients' rights in the Source Code Form under this License.
+
+3.3. Distribution of a Larger Work
+
+You may create and distribute a Larger Work under terms of Your choice,
+provided that You also comply with the requirements of this License for
+the Covered Software. If the Larger Work is a combination of Covered
+Software with a work governed by one or more Secondary Licenses, and the
+Covered Software is not Incompatible With Secondary Licenses, this
+License permits You to additionally distribute such Covered Software
+under the terms of such Secondary License(s), so that the recipient of
+the Larger Work may, at their option, further distribute the Covered
+Software under the terms of either this License or such Secondary
+License(s).
+
+3.4. Notices
+
+You may not remove or alter the substance of any license notices
+(including copyright notices, patent notices, disclaimers of warranty,
+or limitations of liability) contained within the Source Code Form of
+the Covered Software, except that You may alter any license notices to
+the extent required to remedy known factual inaccuracies.
+
+3.5. Application of Additional Terms
+
+You may choose to offer, and to charge a fee for, warranty, support,
+indemnity or liability obligations to one or more recipients of Covered
+Software. However, You may do so only on Your own behalf, and not on
+behalf of any Contributor. You must make it absolutely clear that any
+such warranty, support, indemnity, or liability obligation is offered by
+You alone, and You hereby agree to indemnify every Contributor for any
+liability incurred by such Contributor as a result of warranty, support,
+indemnity or liability terms You offer. You may include additional
+disclaimers of warranty and limitations of liability specific to any
+jurisdiction.
+
+4. Inability to Comply Due to Statute or Regulation
+---------------------------------------------------
+
+If it is impossible for You to comply with any of the terms of this
+License with respect to some or all of the Covered Software due to
+statute, judicial order, or regulation then You must: (a) comply with
+the terms of this License to the maximum extent possible; and (b)
+describe the limitations and the code they affect. Such description must
+be placed in a text file included with all distributions of the Covered
+Software under this License. Except to the extent prohibited by statute
+or regulation, such description must be sufficiently detailed for a
+recipient of ordinary skill to be able to understand it.
+
+5. Termination
+--------------
+
+5.1. The rights granted under this License will terminate automatically
+if You fail to comply with any of its terms. However, if You become
+compliant, then the rights granted under this License from a particular
+Contributor are reinstated (a) provisionally, unless and until such
+Contributor explicitly and finally terminates Your grants, and (b) on an
+ongoing basis, if such Contributor fails to notify You of the
+non-compliance by some reasonable means prior to 60 days after You have
+come back into compliance. Moreover, Your grants from a particular
+Contributor are reinstated on an ongoing basis if such Contributor
+notifies You of the non-compliance by some reasonable means, this is the
+first time You have received notice of non-compliance with this License
+from such Contributor, and You become compliant prior to 30 days after
+Your receipt of the notice.
+
+5.2. If You initiate litigation against any entity by asserting a patent
+infringement claim (excluding declaratory judgment actions,
+counter-claims, and cross-claims) alleging that a Contributor Version
+directly or indirectly infringes any patent, then the rights granted to
+You by any and all Contributors for the Covered Software under Section
+2.1 of this License shall terminate.
+
+5.3. In the event of termination under Sections 5.1 or 5.2 above, all
+end user license agreements (excluding distributors and resellers) which
+have been validly granted by You or Your distributors under this License
+prior to termination shall survive termination.
+
+************************************************************************
+* *
+* 6. Disclaimer of Warranty *
+* ------------------------- *
+* *
+* Covered Software is provided under this License on an "as is" *
+* basis, without warranty of any kind, either expressed, implied, or *
+* statutory, including, without limitation, warranties that the *
+* Covered Software is free of defects, merchantable, fit for a *
+* particular purpose or non-infringing. The entire risk as to the *
+* quality and performance of the Covered Software is with You. *
+* Should any Covered Software prove defective in any respect, You *
+* (not any Contributor) assume the cost of any necessary servicing, *
+* repair, or correction. This disclaimer of warranty constitutes an *
+* essential part of this License. No use of any Covered Software is *
+* authorized under this License except under this disclaimer. *
+* *
+************************************************************************
+
+************************************************************************
+* *
+* 7. Limitation of Liability *
+* -------------------------- *
+* *
+* Under no circumstances and under no legal theory, whether tort *
+* (including negligence), contract, or otherwise, shall any *
+* Contributor, or anyone who distributes Covered Software as *
+* permitted above, be liable to You for any direct, indirect, *
+* special, incidental, or consequential damages of any character *
+* including, without limitation, damages for lost profits, loss of *
+* goodwill, work stoppage, computer failure or malfunction, or any *
+* and all other commercial damages or losses, even if such party *
+* shall have been informed of the possibility of such damages. This *
+* limitation of liability shall not apply to liability for death or *
+* personal injury resulting from such party's negligence to the *
+* extent applicable law prohibits such limitation. Some *
+* jurisdictions do not allow the exclusion or limitation of *
+* incidental or consequential damages, so this exclusion and *
+* limitation may not apply to You. *
+* *
+************************************************************************
+
+8. Litigation
+-------------
+
+Any litigation relating to this License may be brought only in the
+courts of a jurisdiction where the defendant maintains its principal
+place of business and such litigation shall be governed by laws of that
+jurisdiction, without reference to its conflict-of-law provisions.
+Nothing in this Section shall prevent a party's ability to bring
+cross-claims or counter-claims.
+
+9. Miscellaneous
+----------------
+
+This License represents the complete agreement concerning the subject
+matter hereof. If any provision of this License is held to be
+unenforceable, such provision shall be reformed only to the extent
+necessary to make it enforceable. Any law or regulation which provides
+that the language of a contract shall be construed against the drafter
+shall not be used to construe this License against a Contributor.
+
+10. Versions of the License
+---------------------------
+
+10.1. New Versions
+
+Mozilla Foundation is the license steward. Except as provided in Section
+10.3, no one other than the license steward has the right to modify or
+publish new versions of this License. Each version will be given a
+distinguishing version number.
+
+10.2. Effect of New Versions
+
+You may distribute the Covered Software under the terms of the version
+of the License under which You originally received the Covered Software,
+or under the terms of any subsequent version published by the license
+steward.
+
+10.3. Modified Versions
+
+If you create software not governed by this License, and you want to
+create a new license for such software, you may create and use a
+modified version of this License if you rename the license and remove
+any references to the name of the license steward (except to note that
+such modified license differs from this License).
+
+10.4. Distributing Source Code Form that is Incompatible With Secondary
+Licenses
+
+If You choose to distribute Source Code Form that is Incompatible With
+Secondary Licenses under the terms of this version of the License, the
+notice described in Exhibit B of this License must be attached.
+
+Exhibit A - Source Code Form License Notice
+-------------------------------------------
+
+ This Source Code Form is subject to the terms of the Mozilla Public
+ License, v. 2.0. If a copy of the MPL was not distributed with this
+ file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+If it is not possible or desirable to put the notice in a particular
+file, then You may include the notice in a location (such as a LICENSE
+file in a relevant directory) where a recipient would be likely to look
+for such a notice.
+
+You may add additional accurate notices of copyright ownership.
+
+Exhibit B - "Incompatible With Secondary Licenses" Notice
+---------------------------------------------------------
+
+ This Source Code Form is "Incompatible With Secondary Licenses", as
+ defined by the Mozilla Public License, v. 2.0.
diff --git a/extern/openvdb/README b/extern/openvdb/README
new file mode 100644
index 00000000000..44507466dd3
--- /dev/null
+++ b/extern/openvdb/README
@@ -0,0 +1,16 @@
+========================================================================
+ OpenVDB
+========================================================================
+
+The OpenVDB library comprises a hierarchical data structure and a suite
+of tools for the efficient manipulation of sparse, possibly time-varying,
+volumetric data discretized on a three-dimensional grid.
+
+For instructions on library installation and dependencies see INSTALL
+
+For documentation of the library and code examples see:
+www.openvdb.org/documentation/doxygen
+
+For more details visit the project's home page:
+www.openvdb.org
+
diff --git a/extern/openvdb/internal/openvdb/Exceptions.h b/extern/openvdb/internal/openvdb/Exceptions.h
new file mode 100644
index 00000000000..dc076423d99
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/Exceptions.h
@@ -0,0 +1,112 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+
+#ifndef OPENVDB_EXCEPTIONS_HAS_BEEN_INCLUDED
+#define OPENVDB_EXCEPTIONS_HAS_BEEN_INCLUDED
+
+#include <exception>
+#include <string>
+#include <iostream>
+#include <openvdb/version.h>
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+
+class OPENVDB_API Exception: public std::exception
+{
+public:
+ virtual const char* what() const throw()
+ {
+ try { return mMessage.c_str(); } catch (...) {};
+ return NULL;
+ }
+
+ virtual ~Exception() throw() {}
+
+protected:
+ Exception() throw() {}
+ explicit Exception(const char* eType, const std::string* const msg = NULL) throw()
+ {
+ try {
+ if (eType) mMessage = eType;
+ if (msg) mMessage += ": " + (*msg);
+ } catch (...) {}
+ }
+
+private:
+ std::string mMessage;
+};
+
+
+#define OPENVDB_EXCEPTION(_classname) \
+class OPENVDB_API _classname: public Exception \
+{ \
+public: \
+ _classname() throw() : Exception( #_classname ) {} \
+ explicit _classname(const std::string &msg) throw() : Exception( #_classname , &msg) {} \
+}
+
+
+OPENVDB_EXCEPTION(ArithmeticError);
+OPENVDB_EXCEPTION(IllegalValueException);
+OPENVDB_EXCEPTION(IndexError);
+OPENVDB_EXCEPTION(IoError);
+OPENVDB_EXCEPTION(KeyError);
+OPENVDB_EXCEPTION(LookupError);
+OPENVDB_EXCEPTION(NotImplementedError);
+OPENVDB_EXCEPTION(ReferenceError);
+OPENVDB_EXCEPTION(RuntimeError);
+OPENVDB_EXCEPTION(TypeError);
+OPENVDB_EXCEPTION(ValueError);
+
+
+#undef OPENVDB_EXCEPTION
+
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+
+#define OPENVDB_THROW(exception, message) \
+{ \
+ std::string _openvdb_throw_msg; \
+ try { \
+ std::ostringstream _openvdb_throw_os; \
+ _openvdb_throw_os << message; \
+ _openvdb_throw_msg = _openvdb_throw_os.str(); \
+ } catch (...) {} \
+ throw exception(_openvdb_throw_msg); \
+} // OPENVDB_THROW
+
+#endif // OPENVDB_EXCEPTIONS_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/Grid.cc b/extern/openvdb/internal/openvdb/Grid.cc
new file mode 100644
index 00000000000..7154c4914ee
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/Grid.cc
@@ -0,0 +1,487 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+
+#include "Grid.h"
+
+#include <openvdb/Metadata.h>
+#include <boost/algorithm/string/case_conv.hpp>
+#include <boost/algorithm/string/trim.hpp>
+#include <tbb/mutex.h>
+
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+
+/// @note For Houdini compatibility, boolean-valued metadata names
+/// should begin with "is_".
+const char
+ * const GridBase::META_GRID_CLASS = "class",
+ * const GridBase::META_GRID_CREATOR = "creator",
+ * const GridBase::META_GRID_NAME = "name",
+ * const GridBase::META_SAVE_HALF_FLOAT = "is_saved_as_half_float",
+ * const GridBase::META_IS_LOCAL_SPACE = "is_local_space",
+ * const GridBase::META_VECTOR_TYPE = "vector_type",
+ * const GridBase::META_FILE_BBOX_MIN = "file_bbox_min",
+ * const GridBase::META_FILE_BBOX_MAX = "file_bbox_max",
+ * const GridBase::META_FILE_COMPRESSION = "file_compression",
+ * const GridBase::META_FILE_MEM_BYTES = "file_mem_bytes",
+ * const GridBase::META_FILE_VOXEL_COUNT = "file_voxel_count";
+
+namespace {
+/// @todo Remove (deprecated in favor of META_SAVE_HALF_FLOAT)
+const char *SAVE_FLOAT_AS_HALF = "write as 16-bit float";
+}
+
+
+////////////////////////////////////////
+
+
+namespace {
+
+typedef std::map<Name, GridBase::GridFactory> GridFactoryMap;
+typedef GridFactoryMap::const_iterator GridFactoryMapCIter;
+
+typedef tbb::mutex Mutex;
+typedef Mutex::scoped_lock Lock;
+
+struct LockedGridRegistry {
+ LockedGridRegistry() {}
+ ~LockedGridRegistry() {}
+ Mutex mMutex;
+ GridFactoryMap mMap;
+};
+
+// Declare this at file scope to ensure thread-safe initialization.
+Mutex sInitGridRegistryMutex;
+
+
+// Global function for accessing the registry
+LockedGridRegistry*
+getGridRegistry()
+{
+ Lock lock(sInitGridRegistryMutex);
+
+ static LockedGridRegistry* registry = NULL;
+
+ if (registry == NULL) {
+
+#ifdef __ICC
+// Disable ICC "assignment to statically allocated variable" warning.
+// This assignment is mutex-protected and therefore thread-safe.
+__pragma(warning(disable:1711))
+#endif
+
+ registry = new LockedGridRegistry();
+
+#ifdef __ICC
+__pragma(warning(default:1711))
+#endif
+
+ }
+
+ return registry;
+}
+
+} // unnamed namespace
+
+
+bool
+GridBase::isRegistered(const Name& name)
+{
+ LockedGridRegistry* registry = getGridRegistry();
+ Lock lock(registry->mMutex);
+
+ return (registry->mMap.find(name) != registry->mMap.end());
+}
+
+
+void
+GridBase::registerGrid(const Name& name, GridFactory factory)
+{
+ LockedGridRegistry* registry = getGridRegistry();
+ Lock lock(registry->mMutex);
+
+ if (registry->mMap.find(name) != registry->mMap.end()) {
+ OPENVDB_THROW(KeyError, "Grid type " << name << " is already registered");
+ }
+
+ registry->mMap[name] = factory;
+}
+
+
+void
+GridBase::unregisterGrid(const Name& name)
+{
+ LockedGridRegistry* registry = getGridRegistry();
+ Lock lock(registry->mMutex);
+
+ registry->mMap.erase(name);
+}
+
+
+GridBase::Ptr
+GridBase::createGrid(const Name& name)
+{
+ LockedGridRegistry* registry = getGridRegistry();
+ Lock lock(registry->mMutex);
+
+ GridFactoryMapCIter iter = registry->mMap.find(name);
+
+ if (iter == registry->mMap.end()) {
+ OPENVDB_THROW(LookupError, "Cannot create grid of unregistered type " << name);
+ }
+
+ return (iter->second)();
+}
+
+
+void
+GridBase::clearRegistry()
+{
+ LockedGridRegistry* registry = getGridRegistry();
+ Lock lock(registry->mMutex);
+
+ registry->mMap.clear();
+}
+
+
+////////////////////////////////////////
+
+
+GridClass
+GridBase::stringToGridClass(const std::string& s)
+{
+ GridClass ret = GRID_UNKNOWN;
+ std::string str = s;
+ boost::trim(str);
+ boost::to_lower(str);
+ if (str == gridClassToString(GRID_LEVEL_SET)) {
+ ret = GRID_LEVEL_SET;
+ } else if (str == gridClassToString(GRID_FOG_VOLUME)) {
+ ret = GRID_FOG_VOLUME;
+ } else if (str == gridClassToString(GRID_STAGGERED)) {
+ ret = GRID_STAGGERED;
+ }
+ return ret;
+}
+
+
+std::string
+GridBase::gridClassToString(GridClass cls)
+{
+ std::string ret;
+ switch (cls) {
+ case GRID_UNKNOWN: ret = "unknown"; break;
+ case GRID_LEVEL_SET: ret = "level set"; break;
+ case GRID_FOG_VOLUME: ret = "fog volume"; break;
+ case GRID_STAGGERED: ret = "staggered"; break;
+ }
+ return ret;
+}
+
+std::string
+GridBase::gridClassToMenuName(GridClass cls)
+{
+ std::string ret;
+ switch (cls) {
+ case GRID_UNKNOWN: ret = "Other"; break;
+ case GRID_LEVEL_SET: ret = "Level Set"; break;
+ case GRID_FOG_VOLUME: ret = "Fog Volume"; break;
+ case GRID_STAGGERED: ret = "Staggered Vector Field"; break;
+ }
+ return ret;
+}
+
+
+
+GridClass
+GridBase::getGridClass() const
+{
+ GridClass cls = GRID_UNKNOWN;
+ if (StringMetadata::ConstPtr s = this->getMetadata<StringMetadata>(META_GRID_CLASS)) {
+ cls = stringToGridClass(s->value());
+ }
+ return cls;
+}
+
+
+void
+GridBase::setGridClass(GridClass cls)
+{
+ this->insertMeta(META_GRID_CLASS, StringMetadata(gridClassToString(cls)));
+}
+
+
+void
+GridBase::clearGridClass()
+{
+ this->removeMeta(META_GRID_CLASS);
+}
+
+
+////////////////////////////////////////
+
+
+VecType
+GridBase::stringToVecType(const std::string& s)
+{
+ VecType ret = VEC_INVARIANT;
+ std::string str = s;
+ boost::trim(str);
+ boost::to_lower(str);
+ if (str == vecTypeToString(VEC_COVARIANT)) {
+ ret = VEC_COVARIANT;
+ } else if (str == vecTypeToString(VEC_COVARIANT_NORMALIZE)) {
+ ret = VEC_COVARIANT_NORMALIZE;
+ } else if (str == vecTypeToString(VEC_CONTRAVARIANT_RELATIVE)) {
+ ret = VEC_CONTRAVARIANT_RELATIVE;
+ } else if (str == vecTypeToString(VEC_CONTRAVARIANT_ABSOLUTE)) {
+ ret = VEC_CONTRAVARIANT_ABSOLUTE;
+ }
+ return ret;
+}
+
+
+std::string
+GridBase::vecTypeToString(VecType typ)
+{
+ std::string ret;
+ switch (typ) {
+ case VEC_INVARIANT: ret = "invariant"; break;
+ case VEC_COVARIANT: ret = "covariant"; break;
+ case VEC_COVARIANT_NORMALIZE: ret = "covariant normalize"; break;
+ case VEC_CONTRAVARIANT_RELATIVE: ret = "contravariant relative"; break;
+ case VEC_CONTRAVARIANT_ABSOLUTE: ret = "contravariant absolute"; break;
+ }
+ return ret;
+}
+
+
+std::string
+GridBase::vecTypeExamples(VecType typ)
+{
+ std::string ret;
+ switch (typ) {
+ case VEC_INVARIANT: ret = "Tuple/Color/UVW"; break;
+ case VEC_COVARIANT: ret = "Gradient/Normal"; break;
+ case VEC_COVARIANT_NORMALIZE: ret = "Unit Normal"; break;
+ case VEC_CONTRAVARIANT_RELATIVE: ret = "Displacement/Velocity/Acceleration"; break;
+ case VEC_CONTRAVARIANT_ABSOLUTE: ret = "Position"; break;
+ }
+ return ret;
+}
+
+
+std::string
+GridBase::vecTypeDescription(VecType typ)
+{
+ std::string ret;
+ switch (typ) {
+ case VEC_INVARIANT:
+ ret = "Does not transform";
+ break;
+ case VEC_COVARIANT:
+ ret = "Apply the inverse-transpose transform matrix but ignore translation";
+ break;
+ case VEC_COVARIANT_NORMALIZE:
+ ret = "Apply the inverse-transpose transform matrix but ignore translation"
+ " and renormalize vectors";
+ break;
+ case VEC_CONTRAVARIANT_RELATIVE:
+ ret = "Apply the forward transform matrix but ignore translation";
+ break;
+ case VEC_CONTRAVARIANT_ABSOLUTE:
+ ret = "Apply the forward transform matrix, including translation";
+ break;
+ }
+ return ret;
+}
+
+
+VecType
+GridBase::getVectorType() const
+{
+ VecType typ = VEC_INVARIANT;
+ if (StringMetadata::ConstPtr s = this->getMetadata<StringMetadata>(META_VECTOR_TYPE)) {
+ typ = stringToVecType(s->value());
+ }
+ return typ;
+}
+
+
+void
+GridBase::setVectorType(VecType typ)
+{
+ this->insertMeta(META_VECTOR_TYPE, StringMetadata(vecTypeToString(typ)));
+}
+
+
+void
+GridBase::clearVectorType()
+{
+ this->removeMeta(META_VECTOR_TYPE);
+}
+
+
+////////////////////////////////////////
+
+
+std::string
+GridBase::getName() const
+{
+ if (Metadata::ConstPtr meta = (*this)[META_GRID_NAME]) return meta->str();
+ return "";
+}
+
+
+void
+GridBase::setName(const std::string& name)
+{
+ this->removeMeta(META_GRID_NAME);
+ this->insertMeta(META_GRID_NAME, StringMetadata(name));
+}
+
+
+////////////////////////////////////////
+
+
+std::string
+GridBase::getCreator() const
+{
+ if (Metadata::ConstPtr meta = (*this)[META_GRID_CREATOR]) return meta->str();
+ return "";
+}
+
+
+void
+GridBase::setCreator(const std::string& creator)
+{
+ this->removeMeta(META_GRID_CREATOR);
+ this->insertMeta(META_GRID_CREATOR, StringMetadata(creator));
+}
+
+
+////////////////////////////////////////
+
+
+bool
+GridBase::saveFloatAsHalf() const
+{
+ bool saveAsHalf = false;
+ if (Metadata::ConstPtr meta = (*this)[META_SAVE_HALF_FLOAT]) {
+ saveAsHalf = meta->asBool();
+ } else if ((*this)[SAVE_FLOAT_AS_HALF]) {
+ // Old behavior: saveAsHalf is true if metadata named
+ // SAVE_FLOAT_AS_HALF exists, regardless of its value.
+ saveAsHalf = true;
+ }
+ return saveAsHalf;
+}
+
+
+void
+GridBase::setSaveFloatAsHalf(bool saveAsHalf)
+{
+ this->removeMeta(META_SAVE_HALF_FLOAT);
+ this->insertMeta(META_SAVE_HALF_FLOAT, BoolMetadata(saveAsHalf));
+
+ // Remove the old, deprecated metadata.
+ this->removeMeta(SAVE_FLOAT_AS_HALF);
+}
+
+
+////////////////////////////////////////
+
+
+bool
+GridBase::isInWorldSpace() const
+{
+ bool local = false;
+ if (Metadata::ConstPtr meta = (*this)[META_IS_LOCAL_SPACE]) {
+ local = meta->asBool();
+ }
+ return !local;
+}
+
+
+void
+GridBase::setIsInWorldSpace(bool world)
+{
+ this->removeMeta(META_IS_LOCAL_SPACE);
+ this->insertMeta(META_IS_LOCAL_SPACE, BoolMetadata(!world));
+}
+
+
+////////////////////////////////////////
+
+
+void
+GridBase::addStatsMetadata()
+{
+ const CoordBBox bbox = this->evalActiveVoxelBoundingBox();
+ this->removeMeta(META_FILE_BBOX_MIN);
+ this->removeMeta(META_FILE_BBOX_MAX);
+ this->removeMeta(META_FILE_MEM_BYTES);
+ this->removeMeta(META_FILE_VOXEL_COUNT);
+ this->insertMeta(META_FILE_BBOX_MIN, Vec3IMetadata(bbox.min().asVec3i()));
+ this->insertMeta(META_FILE_BBOX_MAX, Vec3IMetadata(bbox.max().asVec3i()));
+ this->insertMeta(META_FILE_MEM_BYTES, Int64Metadata(this->memUsage()));
+ this->insertMeta(META_FILE_VOXEL_COUNT, Int64Metadata(this->activeVoxelCount()));
+}
+
+
+MetaMap::Ptr
+GridBase::getStatsMetadata() const
+{
+ static const char* const sFields[] = {
+ META_FILE_BBOX_MIN,
+ META_FILE_BBOX_MAX,
+ META_FILE_MEM_BYTES,
+ META_FILE_VOXEL_COUNT,
+ NULL
+ };
+
+ /// @todo Check that the fields are of the correct type?
+ MetaMap::Ptr ret(new MetaMap);
+ for (int i = 0; sFields[i] != NULL; ++i) {
+ if (Metadata::ConstPtr m = (*this)[sFields[i]]) {
+ ret->insertMeta(sFields[i], *m);
+ }
+ }
+ return ret;
+}
+
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/Grid.h b/extern/openvdb/internal/openvdb/Grid.h
new file mode 100644
index 00000000000..e6f6914f531
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/Grid.h
@@ -0,0 +1,1215 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+
+#ifndef OPENVDB_GRID_HAS_BEEN_INCLUDED
+#define OPENVDB_GRID_HAS_BEEN_INCLUDED
+
+#include <iostream>
+#include <set>
+#include <vector>
+#include <boost/static_assert.hpp>
+#include <boost/type_traits/remove_const.hpp>
+#include <boost/type_traits/is_floating_point.hpp>
+#include <openvdb/Types.h>
+#include <openvdb/util/Name.h>
+#include <openvdb/math/Transform.h>
+#include <openvdb/tree/Tree.h>
+#include <openvdb/metadata/MetaMap.h>
+#include <openvdb/Exceptions.h>
+
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+
+typedef tree::TreeBase TreeBase;
+
+template<typename> class Grid; // forward declaration
+
+
+/// @brief Create a new grid of type @c GridType with a given background value.
+///
+/// @note Calling createGrid<GridType>(background) is equivalent to calling
+/// GridType::create(background).
+template<typename GridType>
+inline typename GridType::Ptr createGrid(const typename GridType::ValueType& background);
+
+
+/// @brief Create a new grid of type @c GridType with background value zero.
+///
+/// @note Calling createGrid<GridType>() is equivalent to calling GridType::create().
+template<typename GridType>
+inline typename GridType::Ptr createGrid();
+
+
+/// @brief Create a new grid of the appropriate type that wraps the given tree.
+///
+/// @note This function can be called without specifying the template argument,
+/// i.e., as createGrid(tree).
+template<typename TreePtrType>
+inline typename Grid<typename TreePtrType::element_type>::Ptr createGrid(TreePtrType);
+
+
+/// @brief Create a new grid of type @c GridType classified as a "Level Set",
+/// i.e., a narrow-band level set.
+///
+/// @note @c GridType::ValueType must be a floating-point scalar.
+///
+/// @param voxelSize the size of a voxel in world units
+/// @param halfWidth the half width of the narrow band in voxel units
+///
+/// @details The voxel size and the narrow band half width define the grid's
+/// background value as halfWidth*voxelWidth. The transform is linear
+/// with a uniform scaling only corresponding to the specified voxel size.
+///
+/// @note It is generally advisable to specify a half-width of the narrow band
+/// that is larger than one voxel unit, otherwise zero crossings are not guaranteed.
+template<typename GridType>
+typename GridType::Ptr createLevelSet(
+ double voxelSize = 1.0, double halfWidth = LEVEL_SET_HALF_WIDTH);
+
+
+////////////////////////////////////////
+
+
+/// @brief Abstract base class for typed grids
+class OPENVDB_API GridBase: public MetaMap
+{
+public:
+ typedef boost::shared_ptr<GridBase> Ptr;
+ typedef boost::shared_ptr<const GridBase> ConstPtr;
+
+ typedef Ptr (*GridFactory)();
+
+
+ virtual ~GridBase() {}
+
+ /// @brief Return a new grid of the same type as this grid and whose
+ /// metadata and transform are deep copies of this grid's.
+ virtual GridBase::Ptr copyGrid(CopyPolicy treePolicy = CP_SHARE) const = 0;
+
+ /// Return a new grid whose metadata, transform and tree are deep copies of this grid's.
+ virtual GridBase::Ptr deepCopyGrid() const = 0;
+
+
+ //
+ // Registry methods
+ //
+ /// Create a new grid of the given (registered) type.
+ static Ptr createGrid(const Name& type);
+
+ /// Return @c true if the given grid type name is registered.
+ static bool isRegistered(const Name &type);
+
+ /// Clear the grid type registry.
+ static void clearRegistry();
+
+
+ //
+ // Grid type methods
+ //
+ /// Return the name of this grid's type.
+ virtual Name type() const = 0;
+ /// Return the name of the type of a voxel's value (e.g., "float" or "vec3d").
+ virtual Name valueType() const = 0;
+
+ /// Return @c true if this grid is of the same type as the template parameter.
+ template<typename GridType>
+ bool isType() const { return (this->type() == GridType::gridType()); }
+
+ //@{
+ /// @brief Return the result of downcasting a GridBase pointer to a Grid pointer
+ /// of the specified type, or return a null pointer if the types are incompatible.
+ template<typename GridType>
+ static typename GridType::Ptr grid(const GridBase::Ptr&);
+ template<typename GridType>
+ static typename GridType::ConstPtr grid(const GridBase::ConstPtr&);
+ template<typename GridType>
+ static typename GridType::ConstPtr constGrid(const GridBase::Ptr&);
+ template<typename GridType>
+ static typename GridType::ConstPtr constGrid(const GridBase::ConstPtr&);
+ //@}
+
+ //@{
+ /// @brief Return a pointer to this grid's tree, which might be
+ /// shared with other grids. The pointer is guaranteed to be non-null.
+ TreeBase::Ptr baseTreePtr();
+ TreeBase::ConstPtr baseTreePtr() const { return this->constBaseTreePtr(); }
+ virtual TreeBase::ConstPtr constBaseTreePtr() const = 0;
+ //@}
+
+ //@{
+ /// @brief Return a reference to this grid's tree, which might be
+ /// shared with other grids.
+ /// @note Calling setTree() on this grid invalidates all references
+ /// previously returned by this method.
+ TreeBase& baseTree() { return const_cast<TreeBase&>(this->constBaseTree()); }
+ const TreeBase& baseTree() const { return this->constBaseTree(); }
+ const TreeBase& constBaseTree() const { return *(this->constBaseTreePtr()); }
+ //@}
+
+ /// @brief Associate the given tree with this grid, in place of its existing tree.
+ /// @throw ValueError if the tree pointer is null
+ /// @throw TypeError if the tree is not of the appropriate type
+ /// @note Invalidates all references previously returned by baseTree()
+ /// or constBaseTree().
+ virtual void setTree(TreeBase::Ptr) = 0;
+
+ /// Set a new tree with the same background value as the previous tree.
+ virtual void newTree() = 0;
+
+ /// Return @c true if this grid contains only background voxels.
+ virtual bool empty() const = 0;
+ /// Empty this grid, setting all voxels to the background.
+ virtual void clear() = 0;
+
+ /// @brief Reduce the memory footprint of this grid by increasing its sparseness
+ /// either losslessly (@a tolerance = 0) or lossily (@a tolerance > 0).
+ /// @details With @a tolerance > 0, sparsify regions where voxels have the same
+ /// active state and have values that differ by no more than the tolerance
+ /// (converted to this grid's value type).
+ virtual void pruneGrid(float tolerance = 0.0) = 0;
+
+
+ //
+ // Metadata
+ //
+ /// Return this grid's user-specified name.
+ std::string getName() const;
+ /// Specify a name for this grid.
+ void setName(const std::string&);
+
+ /// Return the user-specified description of this grid's creator.
+ std::string getCreator() const;
+ /// Provide a description of this grid's creator.
+ void setCreator(const std::string&);
+
+ /// @brief Return @c true if this grid should be written out with floating-point
+ /// voxel values (including components of vectors) quantized to 16 bits.
+ bool saveFloatAsHalf() const;
+ void setSaveFloatAsHalf(bool);
+
+ /// Return the class of volumetric data (level set, fog volume, etc.) stored in this grid.
+ GridClass getGridClass() const;
+ /// Specify the class of volumetric data (level set, fog volume, etc.) stored in this grid.
+ void setGridClass(GridClass);
+ /// Remove the setting specifying the class of this grid's volumetric data.
+ void clearGridClass();
+
+ /// Return the metadata string value for the given class of volumetric data.
+ static std::string gridClassToString(GridClass);
+ /// Return a formatted string version of the grid class.
+ static std::string gridClassToMenuName(GridClass);
+ /// @brief Return the class of volumetric data specified by the given string.
+ /// @details If the string is not one of the ones returned by gridClassToString(),
+ /// return @c GRID_UNKNOWN.
+ static GridClass stringToGridClass(const std::string&);
+
+ /// @brief Return the type of vector data (invariant, covariant, etc.) stored
+ /// in this grid, assuming that this grid contains a vector-valued tree.
+ VecType getVectorType() const;
+ /// @brief Specify the type of vector data (invariant, covariant, etc.) stored
+ /// in this grid, assuming that this grid contains a vector-valued tree.
+ void setVectorType(VecType);
+ /// Remove the setting specifying the type of vector data stored in this grid.
+ void clearVectorType();
+
+ /// Return the metadata string value for the given type of vector data.
+ static std::string vecTypeToString(VecType);
+ /// Return a string listing examples of the given type of vector data
+ /// (e.g., "Gradient/Normal", given VEC_COVARIANT).
+ static std::string vecTypeExamples(VecType);
+ /// @brief Return a string describing how the given type of vector data is affected
+ /// by transformations (e.g., "Does not transform", given VEC_INVARIANT).
+ static std::string vecTypeDescription(VecType);
+ static VecType stringToVecType(const std::string&);
+
+ /// Return @c true if this grid's voxel values are in world space and should be
+ /// affected by transformations, @c false if they are in local space and should
+ /// not be affected by transformations.
+ bool isInWorldSpace() const;
+ /// Specify whether this grid's voxel values are in world space or in local space.
+ void setIsInWorldSpace(bool);
+
+ // Standard metadata field names
+ // (These fields should normally not be accessed directly, but rather
+ // via the accessor methods above, when available.)
+ // Note: Visual C++ requires these declarations to be separate statements.
+ static const char* const META_GRID_CLASS;
+ static const char* const META_GRID_CREATOR;
+ static const char* const META_GRID_NAME;
+ static const char* const META_SAVE_HALF_FLOAT;
+ static const char* const META_IS_LOCAL_SPACE;
+ static const char* const META_VECTOR_TYPE;
+ static const char* const META_FILE_BBOX_MIN;
+ static const char* const META_FILE_BBOX_MAX;
+ static const char* const META_FILE_COMPRESSION;
+ static const char* const META_FILE_MEM_BYTES;
+ static const char* const META_FILE_VOXEL_COUNT;
+
+
+ //
+ // Statistics
+ //
+ /// Return the number of active voxels.
+ virtual Index64 activeVoxelCount() const = 0;
+
+ /// Return the axis-aligned bounding box of all active voxels. If
+ /// the grid is empty a default bbox is returned.
+ virtual CoordBBox evalActiveVoxelBoundingBox() const = 0;
+
+ /// Return the dimensions of the axis-aligned bounding box of all active voxels.
+ virtual Coord evalActiveVoxelDim() const = 0;
+
+ /// Return the number of bytes of memory used by this grid.
+ virtual Index64 memUsage() const = 0;
+
+ /// @brief Add metadata to this grid comprising the current values
+ /// of statistics like the active voxel count and bounding box.
+ /// @note This metadata is not automatically kept up-to-date with
+ /// changes to this grid.
+ void addStatsMetadata();
+ /// @brief Return a new MetaMap containing just the metadata that
+ /// was added to this grid with addStatsMetadata().
+ /// @details If addStatsMetadata() was never called on this grid,
+ /// return an empty MetaMap.
+ MetaMap::Ptr getStatsMetadata() const;
+
+
+ //
+ // Transform methods
+ //
+ //@{
+ /// @brief Return a pointer to this grid's transform, which might be
+ /// shared with other grids.
+ math::Transform::Ptr transformPtr() { return mTransform; }
+ math::Transform::ConstPtr transformPtr() const { return mTransform; }
+ math::Transform::ConstPtr constTransformPtr() const { return mTransform; }
+ //@}
+ //@{
+ /// @brief Return a reference to this grid's transform, which might be
+ /// shared with other grids.
+ /// @note Calling setTransform() on this grid invalidates all references
+ /// previously returned by this method.
+ math::Transform& transform() { return *mTransform; }
+ const math::Transform& transform() const { return *mTransform; }
+ const math::Transform& constTransform() const { return *mTransform; }
+ //@}
+ /// @brief Associate the given transform with this grid, in place of
+ /// its existing transform.
+ /// @throw ValueError if the transform pointer is null
+ /// @note Invalidates all references previously returned by transform()
+ /// or constTransform().
+ void setTransform(math::Transform::Ptr);
+
+ /// Return the size of this grid's voxels.
+ Vec3d voxelSize() const { return transform().voxelSize(); }
+ /// @brief Return the size of this grid's voxel at position (x, y, z).
+ /// @note Frustum and perspective transforms have position-dependent voxel size.
+ Vec3d voxelSize(const Vec3d& xyz) const { return transform().voxelSize(xyz); }
+ /// Return true if the voxels in world space are uniformly sized cubes
+ bool hasUniformVoxels() const { return mTransform->hasUniformScale(); }
+ //@{
+ /// Apply this grid's transform to the given coordinates.
+ Vec3d indexToWorld(const Vec3d& xyz) const { return transform().indexToWorld(xyz); }
+ Vec3d indexToWorld(const Coord& ijk) const { return transform().indexToWorld(ijk); }
+ //@}
+ /// Apply the inverse of this grid's transform to the given coordinates.
+ Vec3d worldToIndex(const Vec3d& xyz) const { return transform().worldToIndex(xyz); }
+
+
+ //
+ // I/O methods
+ //
+ /// @brief Read the grid topology from a stream.
+ /// This will read only the grid structure, not the actual data buffers.
+ virtual void readTopology(std::istream&) = 0;
+ /// @brief Write the grid topology to a stream.
+ /// This will write only the grid structure, not the actual data buffers.
+ virtual void writeTopology(std::ostream&) const = 0;
+
+ /// Read all data buffers for this grid.
+ virtual void readBuffers(std::istream&) = 0;
+ /// Write out all data buffers for this grid.
+ virtual void writeBuffers(std::ostream&) const = 0;
+
+ /// Read in the transform for this grid.
+ void readTransform(std::istream& is) { transform().read(is); }
+ /// Write out the transform for this grid.
+ void writeTransform(std::ostream& os) const { transform().write(os); }
+
+ /// Output a human-readable description of this grid.
+ virtual void print(std::ostream& = std::cout, int verboseLevel = 1) const = 0;
+
+
+protected:
+ /// @brief Initialize with an identity linear transform.
+ GridBase(): mTransform(math::Transform::createLinearTransform()) {}
+
+ /// @brief Deep copy another grid's metadata and transform.
+ GridBase(const GridBase& other): MetaMap(other), mTransform(other.mTransform->copy()) {}
+
+ /// @brief Copy another grid's metadata but share its transform.
+ GridBase(const GridBase& other, ShallowCopy): MetaMap(other), mTransform(other.mTransform) {}
+
+ /// Register a grid type along with a factory function.
+ static void registerGrid(const Name& type, GridFactory);
+ /// Remove a grid type from the registry.
+ static void unregisterGrid(const Name& type);
+
+
+private:
+ math::Transform::Ptr mTransform;
+}; // class GridBase
+
+
+////////////////////////////////////////
+
+
+typedef std::vector<GridBase::Ptr> GridPtrVec;
+typedef GridPtrVec::iterator GridPtrVecIter;
+typedef GridPtrVec::const_iterator GridPtrVecCIter;
+typedef boost::shared_ptr<GridPtrVec> GridPtrVecPtr;
+
+typedef std::vector<GridBase::ConstPtr> GridCPtrVec;
+typedef GridCPtrVec::iterator GridCPtrVecIter;
+typedef GridCPtrVec::const_iterator GridCPtrVecCIter;
+typedef boost::shared_ptr<GridCPtrVec> GridCPtrVecPtr;
+
+typedef std::set<GridBase::Ptr> GridPtrSet;
+typedef GridPtrSet::iterator GridPtrSetIter;
+typedef GridPtrSet::const_iterator GridPtrSetCIter;
+typedef boost::shared_ptr<GridPtrSet> GridPtrSetPtr;
+
+typedef std::set<GridBase::ConstPtr> GridCPtrSet;
+typedef GridCPtrSet::iterator GridCPtrSetIter;
+typedef GridCPtrSet::const_iterator GridCPtrSetCIter;
+typedef boost::shared_ptr<GridCPtrSet> GridCPtrSetPtr;
+
+
+/// @brief Predicate functor that returns @c true for grids that have a specified name
+struct OPENVDB_API GridNamePred
+{
+ GridNamePred(const Name& name): name(name) {}
+ bool operator()(const GridBase::ConstPtr& g) const { return g && g->getName() == name; }
+ Name name;
+};
+
+/// Return the first grid in the given container whose name is @a name.
+template<typename GridPtrContainerT>
+inline typename GridPtrContainerT::value_type
+findGridByName(const GridPtrContainerT& container, const Name& name)
+{
+ typedef typename GridPtrContainerT::value_type GridPtrT;
+ typename GridPtrContainerT::const_iterator it =
+ std::find_if(container.begin(), container.end(), GridNamePred(name));
+ return (it == container.end() ? GridPtrT() : *it);
+}
+
+/// Return the first grid in the given map whose name is @a name.
+template<typename KeyT, typename GridPtrT>
+inline GridPtrT
+findGridByName(const std::map<KeyT, GridPtrT>& container, const Name& name)
+{
+ typedef std::map<KeyT, GridPtrT> GridPtrMapT;
+ for (typename GridPtrMapT::const_iterator it = container.begin(), end = container.end();
+ it != end; ++it)
+ {
+ const GridPtrT& grid = it->second;
+ if (grid && grid->getName() == name) return grid;
+ }
+ return GridPtrT();
+}
+//@}
+
+
+////////////////////////////////////////
+
+
+/// @brief Container class that associates a tree with a transform and metadata
+template<typename _TreeType>
+class Grid: public GridBase
+{
+public:
+ typedef boost::shared_ptr<Grid> Ptr;
+ typedef boost::shared_ptr<const Grid> ConstPtr;
+
+ typedef _TreeType TreeType;
+ typedef typename _TreeType::Ptr TreePtrType;
+ typedef typename _TreeType::ConstPtr ConstTreePtrType;
+ typedef typename _TreeType::ValueType ValueType;
+
+ typedef typename tree::ValueAccessor<_TreeType> Accessor;
+ typedef typename tree::ValueAccessor<const _TreeType> ConstAccessor;
+
+ typedef typename _TreeType::ValueOnIter ValueOnIter;
+ typedef typename _TreeType::ValueOnCIter ValueOnCIter;
+ typedef typename _TreeType::ValueOffIter ValueOffIter;
+ typedef typename _TreeType::ValueOffCIter ValueOffCIter;
+ typedef typename _TreeType::ValueAllIter ValueAllIter;
+ typedef typename _TreeType::ValueAllCIter ValueAllCIter;
+
+ /// @brief ValueConverter<T>::Type is the type of a grid having the same
+ /// hierarchy as this grid but a different value type, T.
+ ///
+ /// For example, FloatGrid::ValueConverter<double>::Type is equivalent to DoubleGrid.
+ /// @note If the source grid type is a template argument, it might be necessary
+ /// to write "typename SourceGrid::template ValueConverter<T>::Type".
+ template<typename OtherValueType>
+ struct ValueConverter {
+ typedef Grid<typename TreeType::template ValueConverter<OtherValueType>::Type> Type;
+ };
+
+ /// Return a new grid with the given background value.
+ static Ptr create(const ValueType& background);
+ /// Return a new grid with background value zero.
+ static Ptr create();
+ /// @brief Return a new grid that contains the given tree.
+ /// @throw ValueError if the tree pointer is null
+ static Ptr create(TreePtrType);
+ /// @brief Return a new, empty grid with the same transform and metadata as the
+ /// given grid and with background value zero.
+ static Ptr create(const GridBase& other);
+
+
+ /// Construct a new grid with background value zero.
+ Grid();
+ /// Construct a new grid with the given background value.
+ explicit Grid(const ValueType& background);
+ /// @brief Construct a new grid that shares the given tree and associates with it
+ /// an identity linear transform.
+ /// @throw ValueError if the tree pointer is null
+ explicit Grid(TreePtrType);
+ /// Deep copy another grid's metadata, transform and tree.
+ Grid(const Grid&);
+ /// Deep copy another grid's metadata, but share its tree and transform.
+ Grid(const Grid&, ShallowCopy);
+ /// @brief Deep copy another grid's metadata and transform, but construct a new tree
+ /// with background value zero.
+ Grid(const GridBase&);
+
+ virtual ~Grid() {}
+
+ //@{
+ /// @brief Return a new grid of the same type as this grid and whose
+ /// metadata and transform are deep copies of this grid's.
+ /// @details If @a treePolicy is @c CP_NEW, give the new grid a new, empty tree;
+ /// if @c CP_SHARE, the new grid shares this grid's tree and transform;
+ /// if @c CP_COPY, the new grid's tree is a deep copy of this grid's tree and transform
+ Ptr copy(CopyPolicy treePolicy = CP_SHARE) const;
+ virtual GridBase::Ptr copyGrid(CopyPolicy treePolicy = CP_SHARE) const;
+ //@}
+ //@{
+ /// Return a new grid whose metadata, transform and tree are deep copies of this grid's.
+ Ptr deepCopy() const { return Ptr(new Grid(*this)); }
+ virtual GridBase::Ptr deepCopyGrid() const { return this->deepCopy(); }
+ //@}
+
+ /// Return the name of this grid's type.
+ virtual Name type() const { return this->gridType(); }
+ /// Return the name of this type of grid.
+ static Name gridType() { return TreeType::treeType(); }
+
+
+ //
+ // Voxel access methods
+ //
+ /// Return the name of the type of a voxel's value (e.g., "float" or "vec3d").
+ virtual Name valueType() const { return tree().valueType(); }
+
+ /// Return this grid's background value.
+ const ValueType& background() const { return mTree->background(); }
+ /// Replace this grid's background value.
+ void setBackground(const ValueType& val) { tree().setBackground(val); }
+
+ /// Return @c true if this grid contains only inactive background voxels.
+ virtual bool empty() const { return tree().empty(); }
+ /// Empty this grid, so that all voxels become inactive background voxels.
+ virtual void clear() { tree().clear(); }
+
+ /// Return an accessor that provides random read and write access to this grid's voxels.
+ Accessor getAccessor() { return Accessor(tree()); }
+ //@{
+ /// Return an accessor that provides random read-only access to this grid's voxels.
+ ConstAccessor getAccessor() const { return ConstAccessor(tree()); }
+ ConstAccessor getConstAccessor() const { return ConstAccessor(tree()); }
+ //@}
+
+ //@{
+ /// Return an iterator over all of this grid's active values (tile and voxel).
+ ValueOnIter beginValueOn() { return tree().beginValueOn(); }
+ ValueOnCIter beginValueOn() const { return tree().cbeginValueOn(); }
+ ValueOnCIter cbeginValueOn() const { return tree().cbeginValueOn(); }
+ //@}
+ //@{
+ /// Return an iterator over all of this grid's inactive values (tile and voxel).
+ ValueOffIter beginValueOff() { return tree().beginValueOff(); }
+ ValueOffCIter beginValueOff() const { return tree().cbeginValueOff(); }
+ ValueOffCIter cbeginValueOff() const { return tree().cbeginValueOff(); }
+ //@}
+ //@{
+ /// Return an iterator over all of this grid's values (tile and voxel).
+ ValueAllIter beginValueAll() { return tree().beginValueAll(); }
+ ValueAllCIter beginValueAll() const { return tree().cbeginValueAll(); }
+ ValueAllCIter cbeginValueAll() const { return tree().cbeginValueAll(); }
+ //@}
+
+ /// Return the minimum and maximum active values in this grid.
+ void evalMinMax(ValueType& minVal, ValueType& maxVal) const;
+
+ /// @brief Set all voxels within a given axis-aligned box to a constant value.
+ /// @param bbox inclusive coordinates of opposite corners of an axis-aligned box
+ /// @param value the value to which to set voxels within the box
+ /// @param active if true, mark voxels within the box as active,
+ /// otherwise mark them as inactive
+ /// @note This operation generates a sparse, but not always optimally sparse,
+ /// representation of the filled box. Follow fill operations with a prune()
+ /// operation for optimal sparseness.
+ void fill(const CoordBBox& bbox, const ValueType& value, bool active = true);
+
+ /// @brief Set the values of all inactive voxels and tiles of a narrow-band
+ /// level set from the signs of the active voxels, setting outside values to
+ /// +background and inside values to -background.
+ ///
+ /// @note This operation should only be used on closed, narrow-band level sets!
+ void signedFloodFill() { tree().signedFloodFill(); }
+
+ /// @brief Set the values of all inactive voxels and tiles of a narrow-band
+ /// level set from the signs of the active voxels, setting outside values to
+ /// @a outside and inside values to @a inside.
+ /// @details Also, set this grid's background value to @a outside.
+ ///
+ /// @note This operation should only be used on closed, narrow-band level sets!
+ /// Also, @a inside should be negative, and @a outside should be larger than @a inside.
+ void signedFloodFill(const ValueType& outside, const ValueType& inside);
+
+ /// @brief Reduce the memory footprint of this grid by increasing its sparseness
+ /// either losslessly (@a tolerance = 0) or lossily (@a tolerance > 0).
+ /// @details With @a tolerance > 0, sparsify regions where voxels have the same
+ /// active state and have values that differ by no more than the tolerance.
+ void prune(const ValueType& tolerance = zeroVal<ValueType>()) { tree().prune(tolerance); }
+ /// Reduce the memory footprint of this grid by increasing its sparseness.
+ virtual void pruneGrid(float tolerance = 0.0);
+
+ /// @brief Transfer active voxels from another grid to this grid
+ /// wherever those voxels coincide with inactive voxels in this grid.
+ /// @note This operation always empties the other tree.
+ void merge(Grid& other) { tree().merge(other.tree()); }
+
+ /// @brief Union this grid's set of active values with the active values
+ /// of the other grid, whose value type may be different.
+ /// @details The resulting state of a value is active if the corresponding value
+ /// was already active OR if it is active in the other grid. Also, a resulting
+ /// value maps to a voxel if the corresponding value already mapped to a voxel
+ /// OR if it is a voxel in the other grid. Thus, a resulting value can only
+ /// map to a tile if the corresponding value already mapped to a tile
+ /// AND if it is a tile value in other grid.
+ ///
+ /// @note This operation modifies only active states, not values.
+ /// Specifically, active tiles and voxels in this grid are not changed, and
+ /// tiles or voxels that were inactive in this grid but active in the other grid
+ /// are marked as active in this grid but left with their original values.
+ template<typename OtherTreeType>
+ void topologyUnion(const Grid<OtherTreeType>& other) { tree().topologyUnion(other.tree()); }
+ /// @todo topologyIntersection
+ /// @todo topologyDifference
+
+ //
+ // Statistics
+ //
+ /// Return the number of active voxels.
+ virtual Index64 activeVoxelCount() const { return tree().activeVoxelCount(); }
+ /// Return the axis-aligned bounding box of all active voxels.
+ virtual CoordBBox evalActiveVoxelBoundingBox() const;
+ /// Return the dimensions of the axis-aligned bounding box of all active voxels.
+ virtual Coord evalActiveVoxelDim() const;
+
+ /// Return the number of bytes of memory used by this grid.
+ /// @todo Add transform().memUsage()
+ virtual Index64 memUsage() const { return tree().memUsage(); }
+
+
+ //
+ // Tree methods
+ //
+ //@{
+ /// @brief Return a pointer to this grid's tree, which might be
+ /// shared with other grids. The pointer is guaranteed to be non-null.
+ TreePtrType treePtr() { return mTree; }
+ ConstTreePtrType treePtr() const { return mTree; }
+ ConstTreePtrType constTreePtr() const { return mTree; }
+ virtual TreeBase::ConstPtr constBaseTreePtr() const { return mTree; }
+ //@}
+ //@{
+ /// @brief Return a reference to this grid's tree, which might be
+ /// shared with other grids.
+ /// @note Calling setTree() on this grid invalidates all references
+ /// previously returned by this method.
+ TreeType& tree() { return *mTree; }
+ const TreeType& tree() const { return *mTree; }
+ const TreeType& constTree() const { return *mTree; }
+ //@}
+
+ /// @brief Associate the given tree with this grid, in place of its existing tree.
+ /// @throw ValueError if the tree pointer is null
+ /// @throw TypeError if the tree is not of type TreeType
+ /// @note Invalidates all references previously returned by baseTree(),
+ /// constBaseTree(), tree() or constTree().
+ virtual void setTree(TreeBase::Ptr);
+
+ /// @brief Associate a new, empty tree with this grid, in place of its existing tree.
+ /// @note The new tree has the same background value as the existing tree.
+ virtual void newTree();
+
+
+ //
+ // I/O methods
+ //
+ /// @brief Read the grid topology from a stream.
+ /// This will read only the grid structure, not the actual data buffers.
+ virtual void readTopology(std::istream&);
+ /// @brief Write the grid topology to a stream.
+ /// This will write only the grid structure, not the actual data buffers.
+ virtual void writeTopology(std::ostream&) const;
+
+ /// Read all data buffers for this grid.
+ virtual void readBuffers(std::istream&);
+ /// Write out all data buffers for this grid.
+ virtual void writeBuffers(std::ostream&) const;
+
+ /// Output a human-readable description of this grid.
+ virtual void print(std::ostream& = std::cout, int verboseLevel = 1) const;
+
+
+ //
+ // Registry methods
+ //
+ /// Return @c true if this grid type is registered.
+ static bool isRegistered() { return GridBase::isRegistered(Grid::gridType()); }
+ /// Register this grid type along with a factory function.
+ static void registerGrid() { GridBase::registerGrid(Grid::gridType(), Grid::factory); }
+ /// Remove this grid type from the registry.
+ static void unregisterGrid() { GridBase::unregisterGrid(Grid::gridType()); }
+
+
+private:
+ /// Disallow assignment, since it wouldn't be obvious whether the copy is deep or shallow.
+ Grid& operator=(const Grid& other);
+
+ /// Helper function for use with registerGrid()
+ static GridBase::Ptr factory() { return Grid::create(); }
+
+ TreePtrType mTree;
+}; // class Grid
+
+
+////////////////////////////////////////
+
+
+/// @brief Cast a generic grid pointer to a pointer to a grid of a concrete class.
+///
+/// Return a null pointer if the input pointer is null or if it
+/// points to a grid that is not of type @c GridType.
+///
+/// @note Calling gridPtrCast<GridType>(grid) is equivalent to calling
+/// GridBase::grid<GridType>(grid).
+template<typename GridType>
+inline typename GridType::Ptr
+gridPtrCast(const GridBase::Ptr& grid)
+{
+ return GridBase::grid<GridType>(grid);
+}
+
+
+/// @brief Cast a generic const grid pointer to a const pointer to a grid
+/// of a concrete class.
+///
+/// Return a null pointer if the input pointer is null or if it
+/// points to a grid that is not of type @c GridType.
+///
+/// @note Calling gridConstPtrCast<GridType>(grid) is equivalent to calling
+/// GridBase::constGrid<GridType>(grid).
+template<typename GridType>
+inline typename GridType::ConstPtr
+gridConstPtrCast(const GridBase::ConstPtr& grid)
+{
+ return GridBase::constGrid<GridType>(grid);
+}
+
+
+////////////////////////////////////////
+
+
+/// @{
+/// @brief Return a pointer to a deep copy of the given grid, provided that
+/// the grid's concrete type is @c GridType.
+///
+/// Return a null pointer if the input pointer is null or if it
+/// points to a grid that is not of type @c GridType.
+template<typename GridType>
+inline typename GridType::Ptr
+deepCopyTypedGrid(const GridBase::ConstPtr& grid)
+{
+ if (!grid || !grid->isType<GridType>()) return typename GridType::Ptr();
+ return gridPtrCast<GridType>(grid->deepCopyGrid());
+}
+
+
+template<typename GridType>
+inline typename GridType::Ptr
+deepCopyTypedGrid(const GridBase& grid)
+{
+ if (!grid.isType<GridType>()) return typename GridType::Ptr();
+ return gridPtrCast<GridType>(grid.deepCopyGrid());
+}
+/// @}
+
+
+////////////////////////////////////////
+
+
+//@{
+/// @brief This adapter allows code that is templated on a Tree type to
+/// accept either a Tree type or a Grid type.
+template<typename _TreeType>
+struct TreeAdapter
+{
+ typedef _TreeType TreeType;
+ typedef typename boost::remove_const<TreeType>::type NonConstTreeType;
+ typedef typename TreeType::Ptr TreePtrType;
+ typedef typename TreeType::ConstPtr ConstTreePtrType;
+ typedef typename NonConstTreeType::Ptr NonConstTreePtrType;
+ typedef Grid<TreeType> GridType;
+ typedef Grid<NonConstTreeType> NonConstGridType;
+ typedef typename GridType::Ptr GridPtrType;
+ typedef typename NonConstGridType::Ptr NonConstGridPtrType;
+ typedef typename GridType::ConstPtr ConstGridPtrType;
+ typedef typename TreeType::ValueType ValueType;
+
+ static TreeType& tree(TreeType& t) { return t; }
+ static TreeType& tree(GridType& g) { return g.tree(); }
+ static const TreeType& tree(const TreeType& t) { return t; }
+ static const TreeType& tree(const GridType& g) { return g.tree(); }
+ static const TreeType& constTree(TreeType& t) { return t; }
+ static const TreeType& constTree(GridType& g) { return g.constTree(); }
+ static const TreeType& constTree(const TreeType& t) { return t; }
+ static const TreeType& constTree(const GridType& g) { return g.constTree(); }
+};
+
+
+/// Partial specialization for Grid types
+template<typename _TreeType>
+struct TreeAdapter<Grid<_TreeType> >
+{
+ typedef _TreeType TreeType;
+ typedef typename boost::remove_const<TreeType>::type NonConstTreeType;
+ typedef typename TreeType::Ptr TreePtrType;
+ typedef typename TreeType::ConstPtr ConstTreePtrType;
+ typedef typename NonConstTreeType::Ptr NonConstTreePtrType;
+ typedef Grid<TreeType> GridType;
+ typedef Grid<NonConstTreeType> NonConstGridType;
+ typedef typename GridType::Ptr GridPtrType;
+ typedef typename NonConstGridType::Ptr NonConstGridPtrType;
+ typedef typename GridType::ConstPtr ConstGridPtrType;
+ typedef typename TreeType::ValueType ValueType;
+
+ static TreeType& tree(TreeType& t) { return t; }
+ static TreeType& tree(GridType& g) { return g.tree(); }
+ static const TreeType& tree(const TreeType& t) { return t; }
+ static const TreeType& tree(const GridType& g) { return g.tree(); }
+ static const TreeType& constTree(TreeType& t) { return t; }
+ static const TreeType& constTree(GridType& g) { return g.constTree(); }
+ static const TreeType& constTree(const TreeType& t) { return t; }
+ static const TreeType& constTree(const GridType& g) { return g.constTree(); }
+};
+//@}
+
+
+////////////////////////////////////////
+
+
+template<typename GridType>
+inline typename GridType::Ptr
+GridBase::grid(const GridBase::Ptr& grid)
+{
+ // The string comparison on type names is slower than a dynamic_pointer_cast, but
+ // it is safer when pointers cross dso boundaries, as they do in many Houdini nodes.
+ if (grid && grid->type() == GridType::gridType()) {
+ return boost::static_pointer_cast<GridType>(grid);
+ }
+ return typename GridType::Ptr();
+}
+
+
+template<typename GridType>
+inline typename GridType::ConstPtr
+GridBase::grid(const GridBase::ConstPtr& grid)
+{
+ return boost::const_pointer_cast<const GridType>(
+ GridBase::grid<GridType>(boost::const_pointer_cast<GridBase>(grid)));
+}
+
+
+template<typename GridType>
+inline typename GridType::ConstPtr
+GridBase::constGrid(const GridBase::Ptr& grid)
+{
+ return boost::const_pointer_cast<const GridType>(GridBase::grid<GridType>(grid));
+}
+
+
+template<typename GridType>
+inline typename GridType::ConstPtr
+GridBase::constGrid(const GridBase::ConstPtr& grid)
+{
+ return boost::const_pointer_cast<const GridType>(
+ GridBase::grid<GridType>(boost::const_pointer_cast<GridBase>(grid)));
+}
+
+
+inline TreeBase::Ptr
+GridBase::baseTreePtr()
+{
+ return boost::const_pointer_cast<TreeBase>(this->constBaseTreePtr());
+}
+
+
+inline void
+GridBase::setTransform(math::Transform::Ptr xform)
+{
+ if (!xform) OPENVDB_THROW(ValueError, "Transform pointer is null");
+ mTransform = xform;
+}
+
+
+////////////////////////////////////////
+
+
+template<typename TreeT>
+inline Grid<TreeT>::Grid(): mTree(new TreeType)
+{
+}
+
+
+template<typename TreeT>
+inline Grid<TreeT>::Grid(const ValueType &background): mTree(new TreeType(background))
+{
+}
+
+
+template<typename TreeT>
+inline Grid<TreeT>::Grid(TreePtrType tree): mTree(tree)
+{
+ if (!tree) OPENVDB_THROW(ValueError, "Tree pointer is null");
+}
+
+
+template<typename TreeT>
+inline Grid<TreeT>::Grid(const Grid& other):
+ GridBase(other),
+ mTree(boost::static_pointer_cast<TreeType>(other.mTree->copy()))
+{
+}
+
+
+template<typename TreeT>
+inline Grid<TreeT>::Grid(const Grid& other, ShallowCopy):
+ GridBase(other, ShallowCopy()),
+ mTree(other.mTree)
+{
+}
+
+
+template<typename TreeT>
+inline Grid<TreeT>::Grid(const GridBase& other):
+ GridBase(other),
+ mTree(new TreeType)
+{
+}
+
+
+//static
+template<typename TreeT>
+inline typename Grid<TreeT>::Ptr
+Grid<TreeT>::create()
+{
+ return Grid::create(zeroVal<ValueType>());
+}
+
+
+//static
+template<typename TreeT>
+inline typename Grid<TreeT>::Ptr
+Grid<TreeT>::create(const ValueType& background)
+{
+ return Ptr(new Grid(background));
+}
+
+
+//static
+template<typename TreeT>
+inline typename Grid<TreeT>::Ptr
+Grid<TreeT>::create(TreePtrType tree)
+{
+ return Ptr(new Grid(tree));
+}
+
+
+//static
+template<typename TreeT>
+inline typename Grid<TreeT>::Ptr
+Grid<TreeT>::create(const GridBase& other)
+{
+ return Ptr(new Grid(other));
+}
+
+
+////////////////////////////////////////
+
+
+template<typename TreeT>
+inline typename Grid<TreeT>::Ptr
+Grid<TreeT>::copy(CopyPolicy treePolicy) const
+{
+ Ptr ret;
+ switch (treePolicy) {
+ case CP_NEW:
+ ret.reset(new Grid(*this, ShallowCopy()));
+ ret->newTree();
+ break;
+ case CP_COPY:
+ ret.reset(new Grid(*this));
+ break;
+ case CP_SHARE:
+ ret.reset(new Grid(*this, ShallowCopy()));
+ break;
+ }
+ return ret;
+}
+
+
+template<typename TreeT>
+inline GridBase::Ptr
+Grid<TreeT>::copyGrid(CopyPolicy treePolicy) const
+{
+ return this->copy(treePolicy);
+}
+
+
+////////////////////////////////////////
+
+
+template<typename TreeT>
+inline void
+Grid<TreeT>::setTree(TreeBase::Ptr tree)
+{
+ if (!tree) OPENVDB_THROW(ValueError, "Tree pointer is null");
+ if (tree->type() != TreeType::treeType()) {
+ OPENVDB_THROW(TypeError, "Cannot assign a tree of type "
+ + tree->type() + " to a grid of type " + this->type());
+ }
+ mTree = boost::static_pointer_cast<TreeType>(tree);
+}
+
+
+template<typename TreeT>
+inline void
+Grid<TreeT>::newTree()
+{
+ mTree.reset(new TreeType(this->background()));
+}
+
+
+template<typename TreeT>
+inline void
+Grid<TreeT>::fill(const CoordBBox& bbox, const ValueType& value, bool active)
+{
+ tree().fill(bbox, value, active);
+}
+
+
+template<typename TreeT>
+inline void
+Grid<TreeT>::signedFloodFill(const ValueType& outside, const ValueType& inside)
+{
+ tree().signedFloodFill(outside, inside);
+}
+
+
+template<typename TreeT>
+inline void
+Grid<TreeT>::pruneGrid(float tolerance)
+{
+ this->prune(ValueType(zeroVal<ValueType>() + tolerance));
+}
+
+
+template<typename TreeT>
+inline void
+Grid<TreeT>::evalMinMax(ValueType& minVal, ValueType& maxVal) const
+{
+ tree().evalMinMax(minVal, maxVal);
+}
+
+
+template<typename TreeT>
+inline CoordBBox
+Grid<TreeT>::evalActiveVoxelBoundingBox() const
+{
+ CoordBBox bbox;
+ tree().evalActiveVoxelBoundingBox(bbox);
+ return bbox;
+}
+
+
+template<typename TreeT>
+inline Coord
+Grid<TreeT>::evalActiveVoxelDim() const
+{
+ Coord dim;
+ const bool nonempty = tree().evalActiveVoxelDim(dim);
+ return (nonempty ? dim : Coord());
+}
+
+
+////////////////////////////////////////
+
+
+/// @internal Consider using the stream tagging mechanism (see io::Archive)
+/// to specify the float precision, but note that the setting is per-grid.
+
+template<typename TreeT>
+inline void
+Grid<TreeT>::readTopology(std::istream& is)
+{
+ tree().readTopology(is, saveFloatAsHalf());
+}
+
+
+template<typename TreeT>
+inline void
+Grid<TreeT>::writeTopology(std::ostream& os) const
+{
+ tree().writeTopology(os, saveFloatAsHalf());
+}
+
+
+template<typename TreeT>
+inline void
+Grid<TreeT>::readBuffers(std::istream& is)
+{
+ tree().readBuffers(is, saveFloatAsHalf());
+}
+
+
+template<typename TreeT>
+inline void
+Grid<TreeT>::writeBuffers(std::ostream& os) const
+{
+ tree().writeBuffers(os, saveFloatAsHalf());
+}
+
+
+template<typename TreeT>
+inline void
+Grid<TreeT>::print(std::ostream& os, int verboseLevel) const
+{
+ tree().print(os, verboseLevel);
+
+ if (metaCount() > 0) {
+ os << "Additional metadata:" << std::endl;
+ for (ConstMetaIterator it = beginMeta(), end = endMeta(); it != end; ++it) {
+ os << " " << it->first;
+ if (it->second) {
+ const std::string value = it->second->str();
+ if (!value.empty()) os << ": " << value;
+ }
+ os << "\n";
+ }
+ }
+
+ os << "Transform:" << std::endl;
+ transform().print(os, /*indent=*/" ");
+ os << std::endl;
+}
+
+
+////////////////////////////////////////
+
+
+template<typename GridType>
+inline typename GridType::Ptr
+createGrid(const typename GridType::ValueType& background)
+{
+ return GridType::create(background);
+}
+
+
+template<typename GridType>
+inline typename GridType::Ptr
+createGrid()
+{
+ return GridType::create();
+}
+
+
+template<typename TreePtrType>
+inline typename Grid<typename TreePtrType::element_type>::Ptr
+createGrid(TreePtrType tree)
+{
+ typedef typename TreePtrType::element_type TreeType;
+ return Grid<TreeType>::create(tree);
+}
+
+
+template<typename GridType>
+typename GridType::Ptr
+createLevelSet(double voxelSize, double halfWidth)
+{
+ typedef typename GridType::ValueType ValueType;
+
+ // GridType::ValueType is required to be a floating-point scalar.
+ BOOST_STATIC_ASSERT(boost::is_floating_point<ValueType>::value);
+
+ typename GridType::Ptr grid = GridType::create(
+ /*background=*/static_cast<ValueType>(voxelSize * halfWidth));
+ grid->setTransform(math::Transform::createLinearTransform(voxelSize));
+ grid->setGridClass(GRID_LEVEL_SET);
+ return grid;
+}
+
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_GRID_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/Metadata.h b/extern/openvdb/internal/openvdb/Metadata.h
new file mode 100644
index 00000000000..4668bd1926f
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/Metadata.h
@@ -0,0 +1,41 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+
+#ifndef OPENVDB_METADATA_HAS_BEEN_INCLUDED
+#define OPENVDB_METADATA_HAS_BEEN_INCLUDED
+
+#include <openvdb/metadata/Metadata.h>
+#include <openvdb/metadata/StringMetadata.h>
+
+#endif // OPENVDB_METADATA_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/Platform.cc b/extern/openvdb/internal/openvdb/Platform.cc
new file mode 100644
index 00000000000..89d73e41601
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/Platform.cc
@@ -0,0 +1,38 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+
+// For Windows, we need these includes to ensure all OPENVDB_API
+// functions/classes are compiled into the shared library.
+#include "openvdb.h"
+#include "Exceptions.h"
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/Platform.h b/extern/openvdb/internal/openvdb/Platform.h
new file mode 100644
index 00000000000..052dfddb4f7
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/Platform.h
@@ -0,0 +1,174 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+///
+/// @file Platform.h
+
+#ifndef OPENVDB_PLATFORM_HAS_BEEN_INCLUDED
+#define OPENVDB_PLATFORM_HAS_BEEN_INCLUDED
+
+#include "PlatformConfig.h"
+
+/// Use OPENVDB_DEPRECATED to mark functions as deprecated.
+/// It should be placed right before the signature of the function,
+/// e.g., "OPENVDB_DEPRECATED void functionName();".
+#ifdef OPENVDB_DEPRECATED
+#undef OPENVDB_DEPRECATED
+#endif
+#ifdef _MSC_VER
+ #define OPENVDB_DEPRECATED __declspec(deprecated)
+#else
+ #define OPENVDB_DEPRECATED __attribute__ ((deprecated))
+#endif
+
+/// Macro for determining if GCC version is >= than X.Y
+#if defined(__GNUC__)
+ #define OPENVDB_CHECK_GCC(MAJOR, MINOR) \
+ (__GNUC__ > MAJOR || (__GNUC__ == MAJOR && __GNUC_MINOR__ >= MINOR))
+#else
+ #define OPENVDB_CHECK_GCC(MAJOR, MINOR) 0
+#endif
+
+/// For compilers that need templated function specializations to have
+/// storage qualifiers, we need to declare the specializations as static inline.
+/// Otherwise, we'll get linker errors about multiply defined symbols.
+#if defined(__GNUC__) && OPENVDB_CHECK_GCC(4, 4)
+ #define OPENVDB_STATIC_SPECIALIZATION
+#else
+ #define OPENVDB_STATIC_SPECIALIZATION static
+#endif
+
+
+/// Bracket code with OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN/_END,
+/// as in the following example, to inhibit ICC remarks about unreachable code:
+/// @code
+/// template<typename NodeType>
+/// void processNode(NodeType& node)
+/// {
+/// OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN
+/// if (NodeType::LEVEL == 0) return; // ignore leaf nodes
+/// int i = 0;
+/// ...
+/// OPENVDB_NO_UNREACHABLE_CODE_WARNING_END
+/// }
+/// @endcode
+/// In the above, <tt>NodeType::LEVEL == 0</tt> is a compile-time constant expression,
+/// so for some template instantiations, the line below it is unreachable.
+#if defined(__INTEL_COMPILER)
+ // Disable ICC remark #111 ("statement is unreachable") and
+ // remark #185 ("dynamic initialization in unreachable code").
+ #define OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN \
+ _Pragma("warning (push)") \
+ _Pragma("warning (disable:111)") \
+ _Pragma("warning (disable:185)")
+ #define OPENVDB_NO_UNREACHABLE_CODE_WARNING_END \
+ _Pragma("warning (pop)")
+#else
+ #define OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN
+ #define OPENVDB_NO_UNREACHABLE_CODE_WARNING_END
+#endif
+
+
+/// Visual C++ does not have constants like M_PI unless this is defined.
+/// @note This is needed even though the core library is built with this but
+/// hcustom 12.1 doesn't define it. So this is needed for HDK operators.
+#ifndef _USE_MATH_DEFINES
+ #define _USE_MATH_DEFINES
+#endif
+
+/// Visual C++ does not have round
+#ifdef _MSC_VER
+ #include <boost/math/special_functions/round.hpp>
+ using boost::math::round;
+#endif
+
+/// Visual C++ uses _copysign() instead of copysign()
+#ifdef _MSC_VER
+ #include <float.h>
+ static inline double copysign(double x, double y) { return _copysign(x, y); }
+#endif
+
+/// Visual C++ does not have stdint.h which defines types like uint64_t.
+/// So for portability we instead include boost/cstdint.hpp.
+#include <boost/cstdint.hpp>
+using boost::int8_t;
+using boost::int16_t;
+using boost::int32_t;
+using boost::int64_t;
+using boost::uint8_t;
+using boost::uint16_t;
+using boost::uint32_t;
+using boost::uint64_t;
+
+/// Helper macros for defining library symbol visibility
+#ifdef OPENVDB_EXPORT
+#undef OPENVDB_EXPORT
+#endif
+#ifdef OPENVDB_IMPORT
+#undef OPENVDB_IMPORT
+#endif
+#ifdef __GNUC__
+ #define OPENVDB_EXPORT __attribute__((visibility("default")))
+ #define OPENVDB_IMPORT __attribute__((visibility("default")))
+#endif
+#ifdef _WIN32
+ #ifdef OPENVDB_DLL
+ #define OPENVDB_EXPORT __declspec(dllexport)
+ #define OPENVDB_IMPORT __declspec(dllimport)
+ #else
+ #define OPENVDB_EXPORT
+ #define OPENVDB_IMPORT
+ #endif
+#endif
+
+/// All classes and public free standing functions must be explicitly marked
+/// as \<lib\>_API to be exported. The \<lib\>_PRIVATE macros are defined when
+/// building that particular library.
+#ifdef OPENVDB_API
+#undef OPENVDB_API
+#endif
+#ifdef OPENVDB_PRIVATE
+ #define OPENVDB_API OPENVDB_EXPORT
+#else
+ #define OPENVDB_API OPENVDB_IMPORT
+#endif
+#ifdef OPENVDB_HOUDINI_API
+#undef OPENVDB_HOUDINI_API
+#endif
+#ifdef OPENVDB_HOUDINI_PRIVATE
+ #define OPENVDB_HOUDINI_API OPENVDB_EXPORT
+#else
+ #define OPENVDB_HOUDINI_API OPENVDB_IMPORT
+#endif
+
+#endif // OPENVDB_PLATFORM_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/PlatformConfig.h b/extern/openvdb/internal/openvdb/PlatformConfig.h
new file mode 100644
index 00000000000..0a15061c44e
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/PlatformConfig.h
@@ -0,0 +1,57 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+///
+/// @file PlatformConfig.h
+
+#ifndef OPENVDB_PLATFORMCONFIG_HAS_BEEN_INCLUDED
+#define OPENVDB_PLATFORMCONFIG_HAS_BEEN_INCLUDED
+
+// Windows specific configuration
+#ifdef _WIN32
+
+ // By default, assume we're building OpenVDB as a DLL if we're dynamically
+ // linking in the CRT, unless OPENVDB_STATICLIB is defined.
+ #if defined(_DLL) && !defined(OPENVDB_STATICLIB) && !defined(OPENVDB_DLL)
+ #define OPENVDB_DLL
+ #endif
+
+ // By default, assume that we're dynamically linking OpenEXR, unless
+ // OPENVDB_OPENEXR_STATICLIB is defined.
+ #if !defined(OPENVDB_OPENEXR_STATICLIB) && !defined(OPENEXR_DLL)
+ #define OPENEXR_DLL
+ #endif
+
+#endif // _WIN32
+
+#endif // OPENVDB_PLATFORMCONFIG_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/Types.h b/extern/openvdb/internal/openvdb/Types.h
new file mode 100644
index 00000000000..94e106b163b
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/Types.h
@@ -0,0 +1,374 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+
+#ifndef OPENVDB_TYPES_HAS_BEEN_INCLUDED
+#define OPENVDB_TYPES_HAS_BEEN_INCLUDED
+
+#include "version.h"
+#include "Platform.h"
+#include <OpenEXR/half.h>
+#include <openvdb/math/Math.h>
+#include <openvdb/math/BBox.h>
+#include <openvdb/math/Quat.h>
+#include <openvdb/math/Vec2.h>
+#include <openvdb/math/Vec3.h>
+#include <openvdb/math/Vec4.h>
+#include <openvdb/math/Mat3.h>
+#include <openvdb/math/Mat4.h>
+#include <openvdb/math/Coord.h>
+#include <openvdb/math/Hermite.h>
+
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+
+// One-dimensional scalar types
+typedef uint32_t Uint;
+typedef uint32_t Index32;
+typedef uint64_t Index64;
+typedef Index32 Index;
+typedef int16_t Int16;
+typedef int32_t Int32;
+typedef int64_t Int64;
+typedef Int32 Int;
+typedef unsigned char Byte;
+typedef double Real;
+
+// Two-dimensional vector types
+typedef math::Vec2i Vec2i;
+typedef math::Vec2s Vec2s;
+typedef math::Vec2d Vec2d;
+typedef math::Vec2<Real> Vec2R;
+typedef math::Vec2<Index> Vec2I;
+typedef math::Vec2<half> Vec2H;
+
+// Three-dimensional vector types
+typedef math::Vec3<Real> Vec3R;
+typedef math::Vec3<Index32> Vec3I;
+typedef math::Vec3<Int32> Vec3i;
+typedef math::Vec3<float> Vec3f;
+typedef math::Vec3s Vec3s;
+typedef math::Vec3<double> Vec3d;
+typedef math::Vec3<half> Vec3H;
+
+typedef math::Coord Coord;
+typedef math::CoordBBox CoordBBox;
+typedef math::BBox<Vec3d> BBoxd;
+
+// Four-dimensional vector types
+typedef math::Vec4<Real> Vec4R;
+typedef math::Vec4<Index32> Vec4I;
+typedef math::Vec4<Int32> Vec4i;
+typedef math::Vec4<float> Vec4f;
+typedef math::Vec4s Vec4s;
+typedef math::Vec4<double> Vec4d;
+typedef math::Vec4<half> Vec4H;
+
+// Three-dimensional matrix types
+typedef math::Mat3<Real> Mat3R;
+
+// Four-dimensional matrix types
+typedef math::Mat4<Real> Mat4R;
+typedef math::Mat4<double> Mat4d;
+typedef math::Mat4<float> Mat4s;
+
+// Compressed Hermite data
+typedef math::Hermite Hermite;
+
+// Quaternions
+typedef math::Quat<Real> QuatR;
+
+
+////////////////////////////////////////
+
+
+template<typename T> struct VecTraits { static const bool IsVec = false; };
+template<typename T> struct VecTraits<math::Vec2<T> > { static const bool IsVec = true; };
+template<typename T> struct VecTraits<math::Vec3<T> > { static const bool IsVec = true; };
+template<typename T> struct VecTraits<math::Vec4<T> > { static const bool IsVec = true; };
+
+
+////////////////////////////////////////
+
+
+// Add new items to the *end* of this list, and update NUM_GRID_CLASSES.
+enum GridClass {
+ GRID_UNKNOWN = 0,
+ GRID_LEVEL_SET,
+ GRID_FOG_VOLUME,
+ GRID_STAGGERED
+};
+enum { NUM_GRID_CLASSES = GRID_STAGGERED + 1 };
+
+static const Real LEVEL_SET_HALF_WIDTH = 3;
+
+/// The type of a vector determines how transforms are applied to it:
+/// <dl>
+/// <dt><b>Invariant</b>
+/// <dd>Does not transform (e.g., tuple, uvw, color)
+///
+/// <dt><b>Covariant</b>
+/// <dd>Apply inverse-transpose transformation: @e w = 0, ignores translation
+/// (e.g., gradient/normal)
+///
+/// <dt><b>Covariant Normalize</b>
+/// <dd>Apply inverse-transpose transformation: @e w = 0, ignores translation,
+/// vectors are renormalized (e.g., unit normal)
+///
+/// <dt><b>Contravariant Relative</b>
+/// <dd>Apply "regular" transformation: @e w = 0, ignores translation
+/// (e.g., displacement, velocity, acceleration)
+///
+/// <dt><b>Contravariant Absolute</b>
+/// <dd>Apply "regular" transformation: @e w = 1, vector translates (e.g., position)
+/// </dl>
+enum VecType {
+ VEC_INVARIANT = 0,
+ VEC_COVARIANT,
+ VEC_COVARIANT_NORMALIZE,
+ VEC_CONTRAVARIANT_RELATIVE,
+ VEC_CONTRAVARIANT_ABSOLUTE
+};
+enum { NUM_VEC_TYPES = VEC_CONTRAVARIANT_ABSOLUTE + 1 };
+
+
+////////////////////////////////////////
+
+
+template<typename T> const char* typeNameAsString() { return typeid(T).name(); }
+template<> inline const char* typeNameAsString<bool>() { return "bool"; }
+template<> inline const char* typeNameAsString<float>() { return "float"; }
+template<> inline const char* typeNameAsString<double>() { return "double"; }
+template<> inline const char* typeNameAsString<int32_t>() { return "int32"; }
+template<> inline const char* typeNameAsString<uint32_t>() { return "uint32"; }
+template<> inline const char* typeNameAsString<int64_t>() { return "int64"; }
+template<> inline const char* typeNameAsString<Hermite>() { return "Hermite"; }
+template<> inline const char* typeNameAsString<Vec2i>() { return "vec2i"; }
+template<> inline const char* typeNameAsString<Vec2s>() { return "vec2s"; }
+template<> inline const char* typeNameAsString<Vec2d>() { return "vec2d"; }
+template<> inline const char* typeNameAsString<Vec3i>() { return "vec3i"; }
+template<> inline const char* typeNameAsString<Vec3f>() { return "vec3s"; }
+template<> inline const char* typeNameAsString<Vec3d>() { return "vec3d"; }
+template<> inline const char* typeNameAsString<std::string>() { return "string"; }
+template<> inline const char* typeNameAsString<Mat4s>() { return "mat4s"; }
+template<> inline const char* typeNameAsString<Mat4d>() { return "mat4d"; }
+
+
+////////////////////////////////////////
+
+
+/// @brief This struct collects both input and output arguments to "grid combiner" functors
+/// used with the tree::TypedGrid::combineExtended() and combine2Extended() methods.
+/// ValueType is the value type of the two grids being combined.
+///
+/// @see openvdb/tree/Tree.h for usage information.
+///
+/// Setter methods return references to this object, to facilitate the following usage:
+/// @code
+/// CombineArgs<float> args;
+/// myCombineOp(args.setARef(aVal).setBRef(bVal).setAIsActive(true).setBIsActive(false));
+/// @endcode
+template<typename ValueType>
+class CombineArgs
+{
+public:
+ typedef ValueType ValueT;
+
+ CombineArgs():
+ mAValPtr(NULL), mBValPtr(NULL), mResultValPtr(&mResultVal),
+ mAIsActive(false), mBIsActive(false), mResultIsActive(false)
+ {}
+
+ /// Use this constructor when the result value is stored externally.
+ CombineArgs(const ValueType& a, const ValueType& b, ValueType& result,
+ bool aOn = false, bool bOn = false):
+ mAValPtr(&a), mBValPtr(&b), mResultValPtr(&result),
+ mAIsActive(aOn), mBIsActive(bOn)
+ { updateResultActive(); }
+
+ /// Use this constructor when the result value should be stored in this struct.
+ CombineArgs(const ValueType& a, const ValueType& b, bool aOn = false, bool bOn = false):
+ mAValPtr(&a), mBValPtr(&b), mResultValPtr(&mResultVal),
+ mAIsActive(aOn), mBIsActive(bOn)
+ { updateResultActive(); }
+
+ /// Get the A input value.
+ const ValueType& a() const { return *mAValPtr; }
+ /// Get the B input value.
+ const ValueType& b() const { return *mBValPtr; }
+ //@{
+ /// Get the output value.
+ const ValueType& result() const { return *mResultValPtr; }
+ ValueType& result() { return *mResultValPtr; }
+ //@}
+
+ /// Set the output value.
+ CombineArgs& setResult(const ValueType& val) { *mResultValPtr = val; return *this; }
+
+ /// Redirect the A value to a new external source.
+ CombineArgs& setARef(const ValueType& a) { mAValPtr = &a; return *this; }
+ /// Redirect the B value to a new external source.
+ CombineArgs& setBRef(const ValueType& b) { mBValPtr = &b; return *this; }
+ /// Redirect the result value to a new external destination.
+ CombineArgs& setResultRef(ValueType& val) { mResultValPtr = &val; return *this; }
+
+ /// @return true if the A value is active
+ bool aIsActive() const { return mAIsActive; }
+ /// @return true if the B value is active
+ bool bIsActive() const { return mBIsActive; }
+ /// @return true if the output value is active
+ bool resultIsActive() const { return mResultIsActive; }
+
+ /// Set the active state of the A value.
+ CombineArgs& setAIsActive(bool b) { mAIsActive = b; updateResultActive(); return *this; }
+ /// Set the active state of the B value.
+ CombineArgs& setBIsActive(bool b) { mBIsActive = b; updateResultActive(); return *this; }
+ /// Set the active state of the output value.
+ CombineArgs& setResultIsActive(bool b) { mResultIsActive = b; return *this; }
+
+protected:
+ /// By default, the result value is active if either of the input values is active,
+ /// but this behavior can be overridden by calling setResultIsActive().
+ void updateResultActive() { mResultIsActive = mAIsActive || mBIsActive; }
+
+ const ValueType* mAValPtr; // pointer to input value from A grid
+ const ValueType* mBValPtr; // pointer to input value from B grid
+ ValueType mResultVal; // computed output value (unused if stored externally)
+ ValueType* mResultValPtr; // pointer to either mResultVal or an external value
+ bool mAIsActive, mBIsActive; // active states of A and B values
+ bool mResultIsActive; // computed active state (default: A active || B active)
+};
+
+
+/// This struct adapts a "grid combiner" functor to swap the A and B grid values
+/// (e.g., so that if the original functor computes a + 2 * b, the adapted functor
+/// will compute b + 2 * a).
+template<typename ValueType, typename CombineOp>
+struct SwappedCombineOp
+{
+ SwappedCombineOp(CombineOp& op): op(op) {}
+
+ void operator()(CombineArgs<ValueType>& args)
+ {
+ CombineArgs<ValueType> swappedArgs(args.b(), args.a(), args.result(),
+ args.bIsActive(), args.aIsActive());
+ op(swappedArgs);
+ }
+
+ CombineOp& op;
+};
+
+
+////////////////////////////////////////
+
+
+/// In copy constructors, members stored as shared pointers can be handled
+/// in several ways:
+/// <dl>
+/// <dt><b>CP_NEW</b>
+/// <dd>Don't copy the member; default construct a new member object instead.
+///
+/// <dt><b>CP_SHARE</b>
+/// <dd>Copy the shared pointer, so that the original and new objects share
+/// the same member.
+///
+/// <dt><b>CP_COPY</b>
+/// <dd>Create a deep copy of the member.
+/// </dl>
+enum CopyPolicy { CP_NEW, CP_SHARE, CP_COPY };
+
+
+// Dummy class that distinguishes shallow copy constructors from
+// deep copy constructors
+class ShallowCopy {};
+// Dummy class that distinguishes topology copy constructors from
+// deep copy constructors
+class TopologyCopy {};
+
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+
+#if defined(__ICC)
+
+// Use these defines to bracket a region of code that has safe static accesses.
+// Keep the region as small as possible.
+#define OPENVDB_START_THREADSAFE_STATIC_REFERENCE __pragma(warning(disable:1710))
+#define OPENVDB_FINISH_THREADSAFE_STATIC_REFERENCE __pragma(warning(default:1710))
+#define OPENVDB_START_THREADSAFE_STATIC_WRITE __pragma(warning(disable:1711))
+#define OPENVDB_FINISH_THREADSAFE_STATIC_WRITE __pragma(warning(default:1711))
+#define OPENVDB_START_THREADSAFE_STATIC_ADDRESS __pragma(warning(disable:1712))
+#define OPENVDB_FINISH_THREADSAFE_STATIC_ADDRESS __pragma(warning(default:1712))
+
+// Use these defines to bracket a region of code that has unsafe static accesses.
+// Keep the region as small as possible.
+#define OPENVDB_START_NON_THREADSAFE_STATIC_REFERENCE __pragma(warning(disable:1710))
+#define OPENVDB_FINISH_NON_THREADSAFE_STATIC_REFERENCE __pragma(warning(default:1710))
+#define OPENVDB_START_NON_THREADSAFE_STATIC_WRITE __pragma(warning(disable:1711))
+#define OPENVDB_FINISH_NON_THREADSAFE_STATIC_WRITE __pragma(warning(default:1711))
+#define OPENVDB_START_NON_THREADSAFE_STATIC_ADDRESS __pragma(warning(disable:1712))
+#define OPENVDB_FINISH_NON_THREADSAFE_STATIC_ADDRESS __pragma(warning(default:1712))
+
+// Simpler version for one-line cases
+#define OPENVDB_THREADSAFE_STATIC_REFERENCE(CODE) \
+ __pragma(warning(disable:1710)); CODE; __pragma(warning(default:1710))
+#define OPENVDB_THREADSAFE_STATIC_WRITE(CODE) \
+ __pragma(warning(disable:1711)); CODE; __pragma(warning(default:1711))
+#define OPENVDB_THREADSAFE_STATIC_ADDRESS(CODE) \
+ __pragma(warning(disable:1712)); CODE; __pragma(warning(default:1712))
+
+#else // GCC does not support these compiler warnings
+
+#define OPENVDB_START_THREADSAFE_STATIC_REFERENCE
+#define OPENVDB_FINISH_THREADSAFE_STATIC_REFERENCE
+#define OPENVDB_START_THREADSAFE_STATIC_WRITE
+#define OPENVDB_FINISH_THREADSAFE_STATIC_WRITE
+#define OPENVDB_START_THREADSAFE_STATIC_ADDRESS
+#define OPENVDB_FINISH_THREADSAFE_STATIC_ADDRESS
+
+#define OPENVDB_START_NON_THREADSAFE_STATIC_REFERENCE
+#define OPENVDB_FINISH_NON_THREADSAFE_STATIC_REFERENCE
+#define OPENVDB_START_NON_THREADSAFE_STATIC_WRITE
+#define OPENVDB_FINISH_NON_THREADSAFE_STATIC_WRITE
+#define OPENVDB_START_NON_THREADSAFE_STATIC_ADDRESS
+#define OPENVDB_FINISH_NON_THREADSAFE_STATIC_ADDRESS
+
+#define OPENVDB_THREADSAFE_STATIC_REFERENCE(CODE) CODE
+#define OPENVDB_THREADSAFE_STATIC_WRITE(CODE) CODE
+#define OPENVDB_THREADSAFE_STATIC_ADDRESS(CODE) CODE
+
+#endif // defined(__ICC)
+
+#endif // OPENVDB_TYPES_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/doc/api_0_98_0.txt b/extern/openvdb/internal/openvdb/doc/api_0_98_0.txt
new file mode 100644
index 00000000000..de5b29f642b
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/doc/api_0_98_0.txt
@@ -0,0 +1,196 @@
+/**
+
+@page api_0_98_0 Porting to OpenVDB 0.98.0
+
+Starting in OpenVDB 0.98.0, @vdblink::tree::Tree Tree@endlink and
+@vdblink::math::Transform Transform@endlink objects (and
+@vdblink::Grid Grid@endlink objects in the context of Houdini SOPs)
+are passed and accessed primarily by reference
+rather than by shared pointer.
+(This is partly for performance reasons; the overhead of copying shared
+pointers, especially in a threaded environment, can be significant.)
+Furthermore, those objects now exhibit copy-on-write semantics, so that
+in most cases it is no longer necessary to make explicit deep copies.
+These changes were, for the most part, requested and implemented by
+Side&nbsp;Effects.
+
+Accessor methods like @vdblink::Grid::tree() Grid::tree@endlink,
+@vdblink::Grid::transform() Grid::transform@endlink and
+@c GEO_PrimVDB::getGrid that used to return shared pointers now return const
+references.
+Variants like @c Grid::constTree, which returned const shared
+pointers, have been removed, and new read/write accessors have been added.
+The latter, including @c Grid::treeRW(), @c Grid::transformRW() and
+@c GEO_PrimVDB::getGridRW, return non-const references, but they also ensure
+that ownership of the returned object is exclusive to the container
+(that is, they ensure that the grid has exclusive ownership of the tree or
+transform and that the primitive has exclusive ownership of the grid).
+The logic is as follows: if a grid, for example, has sole ownership of a
+tree, then @c Grid::treeRW() returns a non-const reference to that tree;
+but if the tree is shared (with other grids, perhaps), then
+@c Grid::treeRW() assigns the grid a new, deep copy of the tree
+and returns a non-const reference to the new tree.
+
+Shared pointers to @c Tree, @c Transform and @c Grid objects can still be
+requested from their respective containers via @c Grid::sharedTree(),
+@c Grid::sharedTransform() and @c GEO_PrimVDB::getSharedGrid,
+but their use is now discouraged.
+
+For Houdini SOPs, there are additional changes. First, VDB primitives are
+now normally processed in-place. That is, rather than extract a
+primitive's grid, make a deep copy of it and construct a new primitive to
+hold the copy, one now requests read/write access to a primitive's grid and
+then modifies the resulting grid. (SOPs that generate primitives or that
+replace grids of one type with another type can still do so using the old
+approach, however.)
+
+Second, grid metadata such as a grid's class (level set, fog volume, etc.),
+value type (@c float, @c vec3s, etc.), background value and so on (the full
+list is hardcoded into @c GEO_PrimVDB) is now exposed via "intrinsic"
+attributes of grid primitives, rather than via primitive attributes as before.
+As a result, this information no longer appears in a SOP's geometry
+spreadsheet, nor does any extra metadata that a SOP might add to a grid during
+processing. The metadata is still preserved in the @c Grid objects, though.
+
+Third, @c openvdb_houdini::processTypedGrid, which passes a shared grid
+pointer to a functor that also takes a shared pointer, is now deprecated in
+favor of @c openvdb_houdini::UTvdbProcessTypedGrid, which accepts shared
+pointers, raw pointers or references to grids (provided that the functor
+accepts an argument of the same kind). Below is a comparison of the old
+and new usage in the typical case of a SOP that processes input grids:
+
+
+<b>Old API</b> (0.97.0 and earlier)
+
+@code
+ struct MyGridProcessor
+ {
+ template<typename GridT>
+ void operator()(typename GridT::Ptr grid) const
+ {
+ // Process the grid's tree.
+(1) grid->tree()->pruneInactive();
+ }
+ };
+
+ SOP_OpenVDB_Example::cookMySop(OP_Context& context)
+ {
+ ...
+ duplicateSource(0, context);
+
+ // Process each VDB primitive that belongs to the selected group.
+ for (openvdb_houdini::VdbPrimIterator it(gdp, group); it; ++it) {
+
+ openvdb_houdini::GU_PrimVDB* vdbPrim = *it;
+
+ // Deep copy the primitive's grid if it is going to be modified.
+ openvdb_houdini::GridPtr grid = vdbPrim->getGrid()->deepCopyGrid();
+ // Otherwise, retrieve a read-only grid pointer.
+ //openvdb_houdini::GridCPtr grid = vdbPrim->getGrid();
+
+ // Process the grid.
+ MyGridProcessor proc;
+(2) openvdb_houdini::processTypedGrid(grid, proc);
+
+ // Create a new VDB primitive that contains the modified grid,
+ // and in the output detail replace the original primitive with
+ // the new one.
+(3) openvdb_houdini::replaceVdbPrimitive(*gdp, grid, *vdbPrim);
+ }
+ }
+@endcode
+
+
+<b>New API</b> (0.98.0 and later)
+
+@code
+ struct MyGridProcessor {
+ template<typename GridT>
+ void operator()(GridT& grid) const
+ {
+ // Request write access to the grid's tree, then process the tree.
+(1) grid.treeRW().pruneInactive();
+ }
+ };
+
+ SOP_OpenVDB_Example::cookMySop(OP_Context& context)
+ {
+ ...
+ duplicateSource(0, context);
+
+ // Process each VDB primitive that belongs to the selected group.
+ for (openvdb_houdini::VdbPrimIterator it(gdp, group); it; ++it) {
+
+ openvdb_houdini::GU_PrimVDB* vdbPrim = *it;
+
+ // Get write access to the grid associated with the primitive.
+ // If the grid is shared with other primitives, this will
+ // make a deep copy of it.
+ openvdb_houdini::Grid& grid = vdbPrim->getGridRW();
+
+ // If the grid is not going to be modified, get read-only access.
+ //const openvdb_houdini::Grid& grid = vdbPrim->getGrid();
+
+ // Process the grid.
+ MyGridProcessor proc;
+(2) openvdb_houdini::UTvdbProcessTypedGrid(
+(4) vdbPrim->getStorageType(), grid, proc);
+ }
+ }
+@endcode
+
+
+@b Notes
+<ol>
+<li>
+In the old API, @vdblink::Grid::tree() Grid::tree@endlink returned either
+a shared, non-const pointer to a tree or a shared const pointer, depending
+on whether the grid itself was non-const or const. In the new API,
+@vdblink::Grid::tree() Grid::tree@endlink always returns a const reference,
+and @c Grid::treeRW() always returns a non-const reference.
+
+<li>
+@c openvdb_houdini::processTypedGrid (old API) accepts only shared pointers
+to grids (or shared pointers to const grids), together with functors that
+accept shared pointers to grids. @c openvdb_houdini::UTvdbProcessTypedGrid
+(new API) accepts const and non-const references, shared pointers and raw
+pointers, together with matching functors. That is, all of the following
+are valid pairs of grid and functor arguments to @c UTvdbProcessTypedGrid():
+@code
+openvdb_houdini::Grid& grid = vdbPrim->getGridRW();
+struct MyProc { template<class GridT> operator()(GridT&) {...} };
+
+const openvdb_houdini::Grid& grid = vdbPrim->getGrid();
+struct MyProc { template<class GridT> operator()(const GridT&) {...} };
+
+openvdb_houdini::GridPtr grid = vdbPrim->getSharedGrid();
+struct MyProc { template<class GridT> operator()(typename GridT::Ptr) {...} };
+
+openvdb_houdini::GridCPtr grid = vdbPrim->getSharedConstGrid();
+struct MyProc { template<class GridT> operator()(typename GridT::ConstPtr) {...} };
+
+openvdb_houdini::Grid* grid = &vdbPrim->getGridRW();
+struct MyProc { template<class GridT> operator()(GridT*) {...} };
+
+const openvdb_houdini::Grid* grid = &vdbPrim->getGrid();
+struct MyProc { template<class GridT> operator()(const GridT*) {...} };
+@endcode
+
+<li>
+In the old API, input grid primitives were (usually) deleted after
+processing, and a new primitive was created for each output grid.
+@c openvdb_houdini::replaceVdbPrimitive() did both operations in one step
+and had the side effect of transferring the output grid's metadata to
+primitive attributes. In the new API, @c replaceVdbPrimitive() is rarely
+needed, because input grids can usually be processed in-place, and
+most commonly-used metadata is exposed via intrinsic attributes, which
+don't need to be manually updated.
+
+<li>
+The first argument to @c openvdb_houdini::UTvdbProcessTypedGrid() is the
+grid's storage type, which can be retrieved either by passing the grid
+to @c openvdb_houdini::UTvdbGetGridType() or by calling
+@c GEO_PrimVDB::getStorageType() on the primitive.
+</ol>
+
+*/
diff --git a/extern/openvdb/internal/openvdb/doc/changes.txt b/extern/openvdb/internal/openvdb/doc/changes.txt
new file mode 100644
index 00000000000..553f5ac67a8
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/doc/changes.txt
@@ -0,0 +1,741 @@
+/**
+
+@page changes Release Notes
+
+
+@htmlonly <a name="v1_1_1_changes"></a>@endhtmlonly
+@par
+<B>Version 1.1.1</B> - <I>May 10 2013</I>
+- Added a simple @vdblink::tools::Dense dense grid class@endlink and tools
+ to copy data from dense voxel arrays into OpenVDB grids and vice-versa.
+- Starting with Houdini 12.5.396, plugins built with this version
+ of OpenVDB can coexist with native Houdini OpenVDB nodes.
+- The level set fracture tool now smooths seam line edges during
+ mesh extraction, eliminating staircase artifacts.
+- Significantly improved the performance of the
+ @vdblink::util::leafTopologyIntersection()
+ leafTopologyIntersection@endlink and
+ @vdblink::util::leafTopologyDifference() leafTopologyDifference@endlink
+ utilities and added a @vdblink::tree::LeafNode::topologyDifference()
+ LeafNode::topologyDifference@endlink method.
+- Added convenience functions that provide simplified interfaces
+ to the @vdblink::tools::meshToLevelSet() mesh to volume@endlink
+ and @vdblink::tools::volumeToMesh() volume to mesh@endlink converters.
+- Added a @vdblink::tools::accumulate() tools::accumulate@endlink function
+ that is similar to @vdblink::tools::foreach() tools::foreach@endlink
+ but can be used to accumulate the results of computations over the values
+ of a grid.
+- Added @vdblink::tools::statistics() tools::statistics@endlink,
+ @vdblink::tools::opStatistics() tools::opStatistics@endlink and
+ @vdblink::tools::histogram() tools::histogram@endlink, which efficiently
+ compute statistics (mean, variance, etc.) and histograms of grid values
+ (using @vdblink::math::Stats math::Stats@endlink and
+ @vdblink::math::Histogram math::Histogram@endlink).
+- Modified @vdblink::math::CoordBBox CoordBBox@endlink to adhere to
+ TBB&rsquo;s splittable type requirements, so that, for example,
+ a @c CoordBBox can be used as a blocked iteration range.
+- Added @vdblink::tree::Tree::addTile() Tree::addTile@endlink,
+ @vdblink::tree::Tree::addLeaf() Tree::addLeaf@endlink and
+ @vdblink::tree::Tree::stealNode() Tree::stealNode@endlink, for fine
+ control over tree construction.
+- Addressed a numerical stability issue when performing Gaussian
+ filtering of level set grids.
+- Changed the return type of @vdblink::math::CoordBBox::volume()
+ CoordBBox::volume@endlink to reduce the risk of overflow.
+- When the input mesh is self-intersecting, the mesh to volume converter
+ now produces a level set with a monotonic gradient field.
+- Fixed a threading bug in the mesh to volume converter that caused it
+ to produce different results for the same input.
+- Fixed a bug in the particle to level set converter that prevented
+ particles with zero velocity from being rasterized in Trail mode.
+- Added an optional input to the Create SOP into which to merge
+ newly-created grids.
+- Fixed a bug in the Resample SOP that caused it to produce incorrect
+ narrow-band widths when resampling level set grids.
+- Fixed a bug in the To Polygons SOP that caused intermittent crashes
+ when the optional reference input was connected.
+- Fixed a bug in the Advect Level Set SOP that caused a crash
+ when the velocity input was connected but empty.
+- The Scatter and Sample Point SOPs now warn instead of erroring
+ when given empty grids.
+- Fixed a crash in @c vdb_view when stepping through multiple grids
+ after changing render modes.
+- @c vdb_view can now render fog volumes and vector fields, and it now
+ features interactively adjustable clipping planes that enable
+ one to view the interior of a volume.
+
+
+@htmlonly <a name="v1_1_0_changes"></a>@endhtmlonly
+@par
+<B>Version 1.1.0</B> - <I>April 4 2013</I>
+- The @vdblink::tools::resampleToMatch() resampleToMatch@endlink tool,
+ the Resample SOP and the Combine SOP now use level set rebuild to correctly
+ and safely resample level sets. Previously, scaling a level set would
+ invalidate the signed distance field, leading to holes and other artifacts.
+- Added a mask-based topological
+ @vdblink::tools::erodeVoxels erosion tool@endlink, and rewrote and
+ simplified the @vdblink::tools::dilateVoxels dilation tool@endlink.
+- The @vdblink::tools::LevelSetAdvection LevelSetAdvection@endlink tool
+ can now advect forward or backward in time.
+- @vdblink::tree::Tree::pruneLevelSet() Tree::pruneLevelSet@endlink now
+ replaces each pruned node with a tile having the inside or outside
+ background value, instead of arbitrarily selecting one of the node's
+ tile or voxel values.
+- When a grid is saved to a file with
+ @vdblink::Grid::saveFloatAsHalf() saveFloatAsHalf@endlink set to @c true,
+ the grid's background value is now also quantized to 16 bits.
+ (Not quantizing the background value caused a mismatch with the values
+ of background tiles.)
+- As with @vdblink::tools::foreach() tools::foreach@endlink, it is now
+ possible to specify whether functors passed to
+ @vdblink::tools::transformValues() tools::transformValues@endlink
+ should be shared across threads.
+- @vdblink::tree::LeafManager tree::LeafManager@endlink can now be
+ instantiated with a @const tree, although buffer swapping with @const trees
+ is disabled.
+- Added a @vdblink::Grid::signedFloodFill() Grid::signedFloodFill@endlink
+ overload that allows one to specify inside and outside values.
+- Fixed a bug in @vdblink::Grid::setBackground() Grid::setBackground@endlink
+ so that now only the values of inactive voxels change.
+- Fixed @vdblink::Grid::topologyUnion() Grid::topologyUnion@endlink so that
+ it actually unions tree topology, instead of just the active states
+ of tiles and voxels. The previous behavior broke multithreaded code
+ that relied on input and output grids having compatible tree topology.
+- @vdblink::math::Transform math::Transform@endlink now includes an
+ @vdblink::math::Transform::isIdentity() isIdentity@endlink predicate
+ and methods to @vdblink::math::Transform::preMult(const Mat4d&) pre-@endlink
+ and @vdblink::math::Transform::postMult(const Mat4d&) postmultiply@endlink
+ by a matrix.
+- Modified the @link NodeMasks.h node mask@endlink classes to permit
+ octree-like tree configurations (i.e., with a branching factor of two)
+ and to use 64-bit operations instead of 32-bit operations.
+- Implemented a new, more efficient
+ @vdblink::math::closestPointOnTriangleToPoint() closest point
+ on triangle@endlink algorithm.
+- Implemented a new vertex normal scheme in the volume to mesh
+ converter, and resolved some overlapping polygon issues.
+- The volume to mesh converter now meshes not just active voxels
+ but also active tiles.
+- Fixed a bug in the mesh to volume converter that caused unsigned
+ distance field conversion to produce empty grids.
+- Fixed a bug in the level set fracture tool whereby the cutter overlap
+ toggle was ignored.
+- Fixed an infinite loop bug in @c vdb_view.
+- Updated @c vdb_view to use the faster and less memory-intensive
+ OpenVDB volume to mesh converter instead of marching cubes,
+ and rewrote the shader to be OpenGL 3.2 and GLSL 1.2 compatible.
+- Given multiple input files or a file containing multiple grids,
+ @c vdb_view now displays one grid at a time. The left and right
+ arrow keys cycle between grids.
+- The To Polygons SOP now has an option to associate the input grid&rsquo;s
+ name with each output polygon.
+
+
+@htmlonly <a name="v1_0_0_changes"></a>@endhtmlonly
+@par
+<B>Version 1.0.0</B> - <I>March 14 2013</I>
+- @vdblink::tools::levelSetRebuild() tools::levelSetRebuild@endlink
+ now throws an exception when given a non-scalar or non-floating-point grid.
+- The tools in tools/GridOperators.h are now interruptible, as is
+ the Analysis SOP.
+- Added a
+ @vdblink::tree::LeafManager::LeafRange::Iterator leaf node iterator@endlink
+ and a TBB-compatible
+ @vdblink::tree::LeafManager::LeafRange range class@endlink
+ to the LeafManager.
+- Modified the @vdblink::tools::VolumeToMesh VolumeToMesh@endlink tool
+ to handle surface topology issues around fracture seam lines.
+- Modified the Makefile to allow @c vdb_view to compile on OS X systems
+ (provided that GLFW is available).
+- Fixed a bug in the Create SOP that resulted in "invalid parameter name"
+ warnings.
+- The Combine SOP now optionally resamples the A grid into the B grid's
+ index space (or vice-versa) if the A and B transforms differ.
+- The Vector Split and Vector Merge SOPs now skip inactive voxels
+ by default, but they can optionally be made to include inactive voxels,
+ as they did before.
+- The @vdblink::tools::LevelSetFracture LevelSetFracture@endlink tool now
+ supports custom rotations for each cutter instance, and the Fracture SOP
+ now uses quaternions to generate uniformly-distributed random rotations.
+
+
+@htmlonly <a name="v0_104_0_changes"></a>@endhtmlonly
+@par
+<B>Version 0.104.0</B> - <I>February 15 2013</I>
+- Added a @vdblink::tools::levelSetRebuild() tool@endlink and a SOP
+ to rebuild a level set from any scalar volume.
+- @c .vdb files are now saved using a mask-based compression scheme
+ that is an order of magnitude faster than Zip and produces comparable
+ file sizes for level set and fog volume grids. (Zip compression
+ is still enabled by default for other classes of grids).
+- The @vdblink::tools::Filter Filter@endlink and
+ @vdblink::tools::LevelSetFilter LevelSetFilter@endlink tools now
+ include a Gaussian filter, and mean (box) filtering is now 10-50x faster.
+- The isosurface @vdblink::tools::VolumeToMesh meshing tool@endlink
+ is now more robust (to level sets with one voxel wide narrow bands,
+ for example).
+- Mesh to volume conversion is on average 1.5x faster and up to 5.5x
+ faster for high-resolution meshes where the polygon/voxel size ratio
+ is small.
+- Added @vdblink::createLevelSet() createLevelSet@endlink and
+ @vdblink::createLevelSetSphere() createLevelSetSphere@endlink
+ factory functions for level set grids.
+- @vdblink::tree::ValueAccessor tree::ValueAccessor@endlink is now faster
+ for trees of height 2, 3 and 4 (the latter is the default), and it now
+ allows one to specify, via a template argument, the number of node levels
+ to be cached, which can also improve performance in special cases.
+- Added a toggle to @vdblink::tools::foreach() tools::foreach@endlink
+ to specify whether or not the functor should be shared across threads.
+- Added @vdblink::Mat4SMetadata Mat4s@endlink and
+ @vdblink::Mat4DMetadata Mat4d@endlink metadata types.
+- Added explicit pre- and postmultiplication methods to the @c Transform,
+ @c Map and @c Mat4 classes and deprecated the old accumulation methods.
+- Modified @vdblink::math::NonlinearFrustumMap NonlinearFrustumMap@endlink
+ to be more compatible with Houdini&rsquo;s frustum transform.
+- Fixed a @vdblink::tools::GridTransformer GridTransformer@endlink bug
+ that caused it to translate the output grid incorrectly in some cases.
+- Fixed a bug in the tree-level
+ @vdblink::tree::LeafIteratorBase LeafIterator@endlink that resulted in
+ intermittent crashes in
+ @vdblink::tools::dilateVoxels() tools::dilateVoxels@endlink.
+- The @c Hermite data type and Hermite grids are no longer supported.
+- Added tools/GridOperators.h, which includes new, cleaner implementations
+ of the @vdblink::tools::cpt() closest point transform@endlink,
+ @vdblink::tools::curl() curl@endlink,
+ @vdblink::tools::divergence() divergence@endlink,
+ @vdblink::tools::gradient() gradient@endlink,
+ @vdblink::tools::laplacian() Laplacian@endlink,
+ @vdblink::tools::magnitude() magnitude@endlink,
+ @vdblink::tools::meanCurvature() mean curvature@endlink and
+ @vdblink::tools::normalize() normalize@endlink tools.
+- Interrupt support has been improved in several tools, including
+ @vdblink::tools::ParticlesToLevelSet tools::ParticlesToLevelSet@endlink.
+- Simplified the API of the @vdblink::math::BaseStencil Stencil@endlink class
+ and added an @vdblink::math::BaseStencil::intersects() intersects@endlink
+ method to test for intersection with a specified isovalue.
+- Renamed @c voxelDimensions to @c voxelSize in transform classes
+ and elsewhere.
+- Deprecated @c houdini_utils::ParmFactory::setChoiceList in favor of
+ @c houdini_utils::ParmFactory::setChoiceListItems, which requires
+ a list of <I>token, label</I> string pairs.
+- Made various changes for Visual C++ compatibility.
+ <I>[Contributed by SESI]</I>
+- Fixed a bug in @c houdini_utils::getNodeChain() that caused the
+ Offset Level Set, Smooth Level Set and Renormalize Level Set SOPs
+ to ignore frame changes.
+ <I>[Contributed by SESI]</I>
+- The From Particles SOP now provides the option to write into
+ an existing grid.
+- Added a SOP to edit grid metadata.
+- The Fracture SOP now supports multiple cutter objects.
+- Added a To Polygons SOP that complements the Fracture SOP and allows
+ for elimination of seam lines, generation of correct vertex normals
+ and grouping of polygons when surfacing fracture fragments, using
+ the original level set or mesh as a reference.
+
+
+@htmlonly <a name="v0_103_1_changes"></a>@endhtmlonly
+@par
+<B>Version 0.103.1</B> - <I>January 15 2013</I>
+- @vdblink::tree::ValueAccessor tree::ValueAccessor@endlink read operations
+ are now faster for four-level trees.
+ (Preliminary benchmark tests suggest a 30-40% improvement.)
+- For vector-valued grids, @vdblink::tools::compMin() tools::compMin@endlink
+ and @vdblink::tools::compMax() tools::compMax@endlink now compare
+ vector magnitudes instead of individual components.
+- Migrated grid sampling code to a new file, Interpolation.h,
+ and deprecated old files and classes.
+- Added a level-set @vdblink::tools::LevelSetFracture fracture tool@endlink
+ and a Fracture SOP.
+- Added @vdblink::tools::sdfInteriorMask() tools::sdfInteriorMask@endlink,
+ which creates a mask of the interior region of a level set grid.
+- Fixed a bug in the mesh to volume converter that produced unexpected
+ nonzero values for voxels at the intersection of two polygons,
+ and another bug that produced narrow-band widths that didn't respect
+ the background value when the half-band width was less than three voxels.
+- @c houdini_utils::ParmFactory can now correctly generate ramp multi-parms.
+- Made various changes for Visual C++ compatibility.
+ <I>[Contributed by SESI]</I>
+- The Convert SOP can now convert between signed distance fields and
+ fog volumes and from volumes to meshes.
+ <I>[Contributed by SESI]</I>
+- For level sets, the From Mesh and From Particles SOPs now match
+ the reference grid's narrow-band width.
+- The Scatter SOP can now optionally scatter points in the interior
+ of a level set.
+
+
+@htmlonly <a name="v0_103_0_changes"></a>@endhtmlonly
+@par
+<B>Version 0.103.0</B> - <I>December 21 2012</I>
+- The mesh to volume converter is now 60% faster at generating
+ level sets with wide bands, and the From Mesh SOP is now interruptible.
+- Fixed a threading bug in the recently-added
+ @vdblink::tools::compReplace() compReplace@endlink tool
+ that caused it to produce incorrect output.
+- Added a @vdblink::tree::Tree::probeConstLeaf() probeConstLeaf@endlink
+ method to the @vdblink::tree::Tree::probeConstLeaf() Tree@endlink,
+ @vdblink::tree::ValueAccessor::probeConstLeaf() ValueAccessor@endlink
+ and @vdblink::tree::RootNode::probeConstLeaf() node@endlink classes.
+- The Houdini VDB primitive doesn't create a @c name attribute
+ unnecessarily (i.e., if its grid's name is empty), but it now
+ correctly allows the name to be changed to the empty string.
+- Fixed a crash in the Vector Merge SOP when fewer than three grids
+ were merged.
+- The From Particles SOP now features a "maximum half-width" parameter
+ to help avoid runaway computations.
+
+
+@htmlonly <a name="v0_102_0_changes"></a>@endhtmlonly
+@par
+<B>Version 0.102.0</B> - <I>December 13 2012</I>
+- Added @vdblink::tools::compReplace() tools::compReplace@endlink,
+ which copies the active values of one grid into another, and added
+ a "Replace A With Active B" mode to the Combine SOP.
+- @vdblink::Grid::signedFloodFill() Grid::signedFloodFill@endlink
+ no longer enters an infinite loop when filling an empty grid.
+- Fixed a bug in the particle to level set converter that sometimes
+ produced level sets with holes, and fixed a bug in the SOP that
+ could result in random output.
+- Fixed an issue in the frustum preview feature of the Create SOP
+ whereby rendering very large frustums could cause high CPU usage.
+- Added streamline support to the constrained advection scheme
+ in the Advect Points SOP.
+- Added an Advect Level Set SOP.
+
+
+@htmlonly <a name="v0_101_1_changes"></a>@endhtmlonly
+@par
+<B>Version 0.101.1</B> - <I>December 11 2012</I> (DWA internal release)
+- Partially reverted the Houdini VDB primitive's grid accessor methods
+ to their pre-0.98.0 behavior. A primitive's grid can once again
+ be accessed by shared pointer, but now also by reference.
+ Accessor methods for grid metadata have also been added, and the
+ primitive now ensures that metadata and transforms are never shared.
+- Fixed an intermittent crash in the From Particles SOP.
+
+
+@htmlonly <a name="v0_101_0_changes"></a>@endhtmlonly
+@par
+<B>Version 0.101.0</B> - <I>December 6 2012</I> (DWA internal release)
+- Partially reverted the @vdblink::Grid Grid@endlink's
+ @vdblink::Grid::tree() tree@endlink and
+ @vdblink::Grid::transform() transform@endlink accessor methods
+ to their pre-0.98.0 behavior, eliminating copy-on-write but
+ preserving their return-by-reference semantics. These methods
+ are now supplemented with a suite of
+ @vdblink::Grid::treePtr() shared@endlink
+ @vdblink::Grid::baseTreePtr() pointer@endlink
+ @vdblink::Grid::transformPtr() accessors@endlink.
+- Restructured the @vdblink::tools::MeshToVolume
+ mesh to volume converter@endlink for a 40% speedup
+ and to be more robust to non-manifold geometry, to better preserve
+ sharp features, to support arbitrary tree configurations and
+ to respect narrow-band limits.
+- Added a @c getNodeBoundingBox method to
+ @vdblink::tree::RootNode::getNodeBoundingBox() RootNode@endlink,
+ @vdblink::tree::InternalNode::getNodeBoundingBox() InternalNode@endlink
+ and @vdblink::tree::LeafNode::getNodeBoundingBox() LeafNode@endlink
+ that returns the index space spanned by a node.
+- Made various changes for Visual C++ compatibility.
+ <I>[Contributed by SESI]</I>
+- Renamed the Reshape Level Set SOP to Offset Level Set.
+- Fixed a crash in the Convert SOP and added support for conversion
+ of empty grids.
+
+
+@htmlonly <a name="v0_100_0_changes"></a>@endhtmlonly
+@par
+<B>Version 0.100.0</B> - <I>November 30 2012</I> (DWA internal release)
+- Greatly improved the performance of the level set to fog volume
+ @vdblink::tools::sdfToFogVolume() converter@endlink.
+- Improved the performance of the
+ @vdblink::tools::Filter::median() median filter@endlink
+ and of level set @vdblink::tools::csgUnion() CSG@endlink operations.
+- Reintroduced
+ @vdblink::tree::Tree::pruneLevelSet() Tree::pruneLevelSet@endlink,
+ a specialized @vdblink::tree::Tree::pruneInactive() pruneInactive@endlink
+ for level-set grids.
+- Added utilities to the @c houdini_utils library to facilitate the
+ collection of a chain of adjacent nodes of a particular type
+ so that they can be cooked in a single step. (For example,
+ adjacent @c xform SOPs could be collapsed by composing their
+ transformation matrices into a single matrix.)
+- Added pruning and flood-filling options to the Convert SOP.
+- Reimplemented the Filter SOP, omitting level-set-specific filters
+ and adding node chaining (to reduce memory usage when applying
+ several filters in sequence).
+- Added a toggle to the Read SOP to read grid metadata and
+ transforms only.
+- Changed the attribute transfer scheme on the From Mesh and
+ From Particles SOPs to allow for custom grid names and
+ vector type metadata.
+
+
+@htmlonly <a name="v0_99_0_changes"></a>@endhtmlonly
+@par
+<B>Version 0.99.0</B> - <I>November 21 2012</I>
+- Added @vdblink::Grid Grid@endlink methods that return non-<TT>const</TT>
+ tree and transform references without triggering deep copies,
+ as well as @c const methods that return @c const shared pointers.
+- Added @c Grid methods to @vdblink::Grid::addStatsMetadata populate@endlink
+ a grid's metadata with statistics like the active voxel count, and to
+ @vdblink::Grid::getStatsMetadata retrieve@endlink that metadata.
+ By default, statistics are now computed and added to grids
+ whenever they are written to <TT>.vdb</TT> files.
+- Added @vdblink::io::File::readGridMetadata io::File::readGridMetadata@endlink
+ and @vdblink::io::File::readAllGridMetadata
+ io::File::readAllGridMetadata@endlink methods to read just the
+ grid metadata and transforms from a <TT>.vdb</TT> file.
+- Fixed numerical precision issues in the
+ @vdblink::tools::csgUnion csgUnion@endlink,
+ @vdblink::tools::csgIntersection csgIntersection@endlink
+ and @vdblink::tools::csgDifference csgDifference@endlink
+ tools, and added toggles to optionally disable postprocess pruning.
+- Fixed an issue in @c vdb_view with the ordering of GL vertex buffer calls.
+ <I>[Contributed by Bill Katz]</I>
+- Fixed an intermittent crash in the
+ @vdblink::tools::ParticlesToLevelSet ParticlesToLevelSet@endlink tool,
+ as well as a race condition that could cause data corruption.
+- The @vdblink::tools::ParticlesToLevelSetAndId ParticlesToLevelSet@endlink
+ tool and From Particles SOP can now transfer arbitrary point attribute
+ values from the input particles to output voxels.
+- Fixed a bug in the Convert SOP whereby the names of primitives
+ were lost during conversion, and another bug that resulted in
+ arithmetic errors when converting empty grids.
+- Fixed a bug in the Combine SOP that caused the Operation selection
+ to be lost.
+
+
+@htmlonly <a name="v0_98_0_changes"></a>@endhtmlonly
+@par
+<B>Version 0.98.0</B> - <I>November 16 2012</I>
+- @vdblink::tree::Tree Tree@endlink and
+ @vdblink::math::Transform Transform@endlink objects (and
+ @vdblink::Grid Grid@endlink objects in the context of Houdini SOPs)
+ are now passed and accessed primarily by reference rather than by
+ shared pointer. See @subpage api_0_98_0 "Porting to OpenVDB 0.98.0"
+ for details about this important API change.
+ <I>[Contributed by SESI]</I>
+- Reimplemented @vdblink::math::CoordBBox CoordBBox@endlink to address several
+ off-by-one bugs related to bounding box dimensions.
+- Fixed an off-by-one bug in @vdblink::Grid::evalActiveVoxelBoundingBox()
+ evalActiveVoxelBoundingBox@endlink.
+- Introduced the @vdblink::tree::LeafManager LeafManager@endlink class,
+ which will eventually replace the @c LeafArray class. @c LeafManager supports
+ dynamic buffers stored as a structure of arrays (SOA), unlike @c LeafArray,
+ which supports only static buffers stored as an array of structures (AOS).
+- Improved the performance of the
+ @vdblink::tools::LevelSetFilter LevelSetFilter@endlink and
+ @vdblink::tools::LevelSetTracker LevelSetTracker@endlink tools by rewriting
+ them to use the new @vdblink::tree::LeafManager LeafManager@endlink class.
+- Added @vdblink::tree::Tree::setValueOnly() Tree::setValueOnly@endlink and
+ @vdblink::tree::ValueAccessor::setValueOnly()
+ ValueAccessor::setValueOnly@endlink methods, which change the value of
+ a voxel without changing its active state, and
+ @vdblink::tree::Tree::probeLeaf() Tree::probeLeaf@endlink and
+ @vdblink::tree::ValueAccessor::probeLeaf() ValueAccessor::probeLeaf@endlink
+ methods that return the leaf node that contains a given voxel (unless
+ the voxel is represented by a tile).
+- Added a @vdblink::tools::LevelSetAdvection LevelSetAdvection@endlink tool
+ that propagates and tracks narrow-band level sets.
+- Introduced a new @vdblink::tools::GridSampler GridSampler@endlink class
+ that supports world-space (or index-space) sampling of grid values.
+- Changed the interpretation of the
+ @vdblink::math::NonlinearFrustumMap NonlinearFrustumMap@endlink's
+ @em taper parameter to be the ratio of the near and far plane depths.
+- Added a @c ParmFactory::setChoiceList() overload that accepts
+ (@em token, @em label) string pairs, and a @c setDefault() overload that
+ accepts an STL string.
+- Fixed a crash in the Combine SOP in Copy B mode.
+- Split the Level Set Filter SOP into three separate SOPs,
+ Level Set Smooth, Level Set Reshape and Level Set Renormalize.
+ When two or more of these nodes are connected in sequence, they interact
+ to reduce memory usage: the last node in the sequence performs
+ all of the operations in one step.
+- The Advect Points SOP can now output polyline streamlines
+ that trace the paths of the points.
+- Added an option to the Analysis SOP to specify names for output grids.
+- Added camera-derived frustum transform support to the Create SOP.
+
+
+@htmlonly <a name="v0_97_0_changes"></a>@endhtmlonly
+@par
+<B>Version 0.97.0</B> - <I>October 18 2012</I>
+- Added a narrow-band @vdblink::tools::LevelSetTracker level set
+ interface tracking tool@endlink (up to fifth-order in space but currently
+ only first-order in time, with higher temporal orders to be added soon).
+- Added a @vdblink::tools::LevelSetFilter level set filter tool@endlink
+ to perform unrestricted surface smoothing (e.g., Laplacian flow),
+ filtering (e.g., mean value) and morphological operations (e.g.,
+ morphological opening).
+- Added adaptivity to the @vdblink::tools::VolumeToMesh
+ level set meshing tool@endlink for faster mesh extraction with fewer
+ polygons, without postprocessing.
+- Added a @vdblink::tree::ValueAccessor::touchLeaf()
+ ValueAccessor::touchLeaf@endlink method that creates (if necessary)
+ and returns the leaf node containing a given voxel. It can be used
+ to preallocate leaf nodes over which to run parallel algorithms.
+- Fixed a bug in @vdblink::Grid::merge() Grid::merge@endlink whereby
+ active tiles were sometimes lost.
+- Added @vdblink::tree::LeafManager LeafManager@endlink, which is similar
+ to @c LeafArray but supports a dynamic buffer count and allocates buffers
+ more efficiently. Useful for temporal integration (e.g., for level set
+ propagation and interface tracking), @c LeafManager is meant to replace
+ @c LeafArray, which will be deprecated in the next release.
+- Added a @vdblink::tree::LeafNode::fill() LeafNode::fill@endlink method
+ to efficiently populate leaf nodes with constant values.
+- Added a @vdblink::tree::Tree::visitActiveBBox() Tree::visitActiveBBox@endlink
+ method that applies a functor to the bounding boxes of all active tiles
+ and leaf nodes and that can be used to improve the performance of
+ ray intersection tests, rendering of bounding boxes, etc.
+- Added a @vdblink::tree::Tree::voxelizeActiveTiles()
+ Tree::voxelizeActiveTiles@endlink method to densify active tiles.
+ While convenient and fast, this can produce large dense grids, so use
+ it with caution.
+- Repackaged @c Tree::pruneLevelSet() as a
+ @vdblink::tree::Tree::pruneOp() Tree::pruneOp()@endlink-compatible
+ functor. @vdblink::tree::LevelSetPrune LevelSetPrune@endlink is a
+ specialized @vdblink::tree::Tree::pruneInactive() pruneInactive@endlink
+ for level-set grids and is used in interface tracking.
+- Added a @vdblink::GridBase::pruneGrid() GridBase::pruneGrid@endlink method.
+- Added a @vdblink::Grid::hasUniformVoxels() Grid:hasUniformVoxels@endlink
+ method.
+- Renamed @c tools::dilate to
+ @vdblink::tools::dilateVoxels() dilateVoxels@endlink and improved its
+ performance. The new name reflects the fact that the current
+ implementation ignores active tiles.
+- Added a @vdblink::tools::resampleToMatch() tools::resampleToMatch@endlink
+ function that resamples an input grid into an output grid with a
+ different transform such that, after resampling, the input and output grids
+ coincide, but the output grid's transform is preserved.
+- Significantly improved the performance of depth-bounded value
+ iterators (@vdblink::tree::Tree::ValueOnIter ValueOnIter@endlink,
+ @vdblink::tree::Tree::ValueAllIter ValueAllIter@endlink, etc.)
+ when the depth bound excludes leaf nodes.
+- Exposed the value buffers inside leaf nodes with
+ @vdblink::tree::LeafNode::buffer() LeafNode::buffer@endlink.
+ This allows for very fast access (const and non-const) to voxel
+ values using linear array offsets instead of @ijk coordinates.
+- In openvdb_houdini/UT_VDBTools.h, added operators for use with
+ @c processTypedGrid that resample grids in several different ways.
+- Added a policy mechanism to @c houdini_utils::OpFactory that allows for
+ customization of operator names, icons, and Help URLs.
+- Renamed many of the Houdini SOPs to make the names more consistent.
+- Added an Advect Points SOP.
+- Added a Level Set Filter SOP that allows for unrestricted surface
+ deformations, unlike the older Filter SOP, which restricts surface
+ motion to the initial narrow band.
+- Added staggered vector sampling to the Sample Points SOP.
+- Added a minimum radius threshold to the particle voxelization tool
+ and SOP.
+- Merged the Composite and CSG SOPs into a single Combine SOP.
+- Added a tool and a SOP to efficiently generate narrow-band level set
+ representations of spheres.
+- In the Visualize SOP, improved the performance of tree topology
+ generation, which is now enabled by default.
+
+
+@htmlonly <a name="v0_96_0_changes"></a>@endhtmlonly
+@par
+<B>Version 0.96.0</B> - <I>September 24 2012</I>
+- Fixed a memory corruption bug in the mesh voxelizer tool.
+- Temporarily removed the optional clipping feature from the level set mesher.
+- Added "Staggered Vector Field" to the list of grid classes in the Create SOP.
+
+
+@htmlonly <a name="v0_95_0_changes"></a>@endhtmlonly
+@par
+<B>Version 0.95.0</B> - <I>September 20 2012</I>
+- Added a quad @vdblink::tools::VolumeToMesh meshing@endlink tool for
+ higher-quality level set meshing and updated the Visualizer SOP
+ to use it.
+- Fixed a precision error in the @vdblink::tools::MeshToVolume
+ mesh voxelizer@endlink and improved the quality of inside/outside
+ voxel classification. Output grids are now also
+ @vdblink::Grid::setGridClass() classified@endlink as either level sets
+ or fog volumes.
+- Modified the @vdblink::tools::GridResampler GridResampler@endlink
+ to use the signed flood fill optimization only on grids that are
+ tagged as level sets.
+- Added a @vdblink::math::Quat quaternion@endlink class to the
+ math library and a method to return the
+ @vdblink::math::Mat3::trace trace@endlink of a @c Mat3.
+- Fixed a bug in the
+ @vdblink::tree::ValueAccessor::ValueAccessor(const ValueAccessor&)
+ ValueAccessor@endlink copy constructor that caused the copy to reference
+ the original.
+- Fixed a bug in @vdblink::tree::RootNode::setActiveState()
+ RootNode::setActiveState@endlink that caused a crash
+ when marking a (virtual) background voxel as inactive.
+- Added a @c Tree::pruneLevelSet method that is similar to but faster than
+ @vdblink::tree::Tree::pruneInactive() pruneInactive@endlink
+ for level set grids.
+- Added fast leaf node voxel access
+ @vdblink::tree::LeafNode::getValue(Index) const methods@endlink
+ that index by linear offset (as returned by
+ @vdblink::tree::LeafNode::ValueOnIter::pos() ValueIter::pos@endlink)
+ instead of by @ijk coordinates.
+- Added a @vdblink::tree::Tree::touchLeaf() Tree::touchLeaf@endlink
+ method that can be used to preallocate a static tree topology over which
+ to safely perform multithreaded processing.
+- Added a grain size argument to @c LeafArray for finer control of parallelism.
+- Modified the Makefile to make it easier to omit the <TT>doc</TT>,
+ @c vdb_test and @c vdb_view targets.
+- Added utility functions (in <TT>houdini/UT_VDBUtils.h</TT>) to convert
+ between Houdini and OpenVDB matrix and vector types.
+ <I>[Contributed by SESI]</I>
+- Added accessors to @c GEO_PrimVDB that make it easier to directly access
+ voxel data and that are used by the HScript volume expression functions
+ in Houdini 12.5. <I>[Contributed by SESI]</I>
+- As of Houdini 12.1.77, the native transform SOP operates on OpenVDB
+ primitives. <I>[Contributed by SESI]</I>
+- Added a Convert SOP that converts OpenVDB grids to Houdini volumes
+ and vice-versa.
+
+
+@htmlonly <a name="v0_94_1_changes"></a>@endhtmlonly
+@par
+<B>Version 0.94.1</B> - <I>September 7 2012</I>
+- Fixed bugs in @vdblink::tree::RootNode RootNode@endlink and
+ @vdblink::tree::InternalNode InternalNode@endlink @c setValue*() and
+ @c fill() methods that could cause neighboring voxels to become inactive.
+- Fixed a bug in
+ @vdblink::tree::Tree::hasSameTopology() Tree::hasSameTopology@endlink
+ that caused false positives when only active states and not values differed.
+- Added a @vdblink::tree::Tree::hasActiveTiles() Tree::hasActiveTiles@endlink
+ method.
+- For better cross-platform consistency, substituted bitwise AND operations
+ for right shifts in the @vdblink::tree::ValueAccessor ValueAccessor@endlink
+ hash key computation.
+- @c vdb_view no longer aborts when asked to surface a vector-valued
+ grid&mdash;but it still doesn't render the surface.
+- Made various changes for Visual C++ compatibility.
+ <I>[Contributed by SESI]</I>
+- Added an option to the MeshVoxelizer SOP to convert both open and
+ closed surfaces to unsigned distance fields.
+- The Filter SOP now allows multiple filters to be applied in
+ user-specified order.
+
+
+@htmlonly <a name="v0_94_0_changes"></a>@endhtmlonly
+@par
+<B>Version 0.94.0</B> - <I>August 30 2012</I>
+- Added a @vdblink::Grid::topologyUnion() method@endlink to union
+ just the active states of voxels from one grid with those of
+ another grid of a possibly different type.
+- Fixed an incorrect scale factor in the Laplacian diffusion
+ @vdblink::tools::Filter::laplacian() filter@endlink.
+- Fixed a bug in @vdblink::tree::Tree::merge() Tree::merge@endlink
+ that could leave a tree with invalid value accessors.
+- Added @vdblink::tree::TreeValueIteratorBase::setActiveState()
+ TreeValueIteratorBase::setActiveState@endlink and deprecated
+ @c setValueOn.
+- Removed @c tools/FastSweeping.h. It will be replaced with a much more
+ efficient implementation in the near future.
+- ZIP compression of <TT>.vdb</TT> files is now
+ @vdblink::io::File::setCompressionEnabled() optional@endlink,
+ but enabled by default. <I>[Contributed by SESI]</I>
+- Made various changes for Clang and Visual C++ compatibility.
+ <I>[Contributed by SESI]</I>
+- The MeshVoxelizer SOP can now transfer arbitrary point and primitive
+ attribute values from the input mesh to output voxels.
+
+
+@htmlonly <a name="v0_93_0_changes"></a>@endhtmlonly
+@par
+<B>Version 0.93.0</B> - <I>August 24 2012</I>
+- Renamed symbols in math/Operators.h to avoid ambiguities that
+ GCC&nbsp;4.4 reports as errors.
+- Simplified the API for the stencil version of the
+ closest-point transform @vdblink::math::CPT operator@endlink.
+- Added logic to
+ @vdblink::io::Archive::readGrid() io::Archive::readGrid@endlink
+ to set the grid name metadata from the descriptor if the metadata
+ doesn't already exist.
+- Added guards to prevent nesting of @c openvdb_houdini::Interrupter::start()
+ and @c end() calls.
+
+
+@htmlonly <a name="v0_92_0_changes"></a>@endhtmlonly
+@par
+<B>Version 0.92.0</B> - <I>August 23 2012</I>
+- Added a Laplacian diffusion
+ @vdblink::tools::Filter::laplacian() filter@endlink.
+- Fixed a bug in the initialization of the sparse contour tracer
+ that caused mesh-to-volume conversion to fail in certain cases.
+- Fixed a bug in the curvature stencil that caused mean curvature
+ filtering to produce wrong results.
+- Increased the speed of the
+ @vdblink::tools::GridTransformer GridTransformer@endlink
+ by as much as 20% for fog volumes.
+- Added optional pruning to the Resample SOP.
+- Modified the PointSample SOP to allow it to work with ungrouped,
+ anonymous grids.
+- Fixed a crash in the LevelSetNoise SOP.
+
+
+@htmlonly <a name="v0_91_0_changes"></a>@endhtmlonly
+@par
+<B>Version 0.91.0</B> - <I>August 16 2012</I>
+- @vdblink::tools::GridTransformer tools::GridTransformer@endlink
+ and @vdblink::tools::GridResampler tools::GridResampler@endlink
+ now correctly (but not yet efficiently) process tiles in sparse grids.
+- Added an optional @vdblink::CopyPolicy CopyPolicy@endlink argument
+ to @vdblink::GridBase::copyGrid() GridBase::copyGrid@endlink
+ and to @vdblink::Grid::copy() Grid::copy@endlink that specifies
+ whether and how the grid's tree should be copied.
+- Added a @vdblink::GridBase::newTree() GridBase::newTree@endlink
+ method that replaces a grid's tree with a new, empty tree of the
+ correct type.
+- Fixed a crash in
+ @vdblink::tree::Tree::setValueOff(const Coord& xyz, const ValueType& value)
+ Tree::setValueOff@endlink when the new value was equal to the
+ background value.
+- Fixed bugs in @vdblink::tree::Tree::prune() Tree::prune@endlink
+ that could result in output tiles with incorrect active states.
+- Added @c librt to the link dependencies to address build failures
+ on Ubuntu systems.
+- Made various small changes to the Makefile and the source code
+ that should help with Mac OS&nbsp;X compatibility.
+- The Composite and Resample SOPs now correctly copy the input grid's
+ metadata to the output grid.
+
+
+@htmlonly <a name="v0_90_1_changes"></a>@endhtmlonly
+@par
+<B>Version 0.90.1</B> - <I>August 7 2012</I>
+- Fixed a bug in the
+ @vdblink::math::BBox::getCenter() BBox::getCenter()@endlink method.
+- Added missing header files to various files.
+- @vdblink::io::File::NameIterator::gridName()
+ io::File::NameIterator::gridName()@endlink now returns a unique name
+ of the form <TT>"name[1]"</TT>, <TT>"name[2]"</TT>, etc. if a file
+ contains multiple grids with the same name.
+- Fixed a bug in the Writer SOP that caused grid names to be discarded.
+- The Resample SOP now correctly sets the background value of the
+ output grid.
+
+
+@htmlonly <a name="v0_90_0_changes"></a>@endhtmlonly
+@par
+<B>Version 0.90.0</B> - <I>August 3 2012</I> (initial public release)
+- Added a basic GL viewer for OpenVDB files.
+- Greatly improved the performance of two commonly-used @c Tree methods,
+ @vdblink::tree::Tree::evalActiveVoxelBoundingBox()
+ evalActiveVoxelBoundingBox()@endlink
+ and @vdblink::tree::Tree::memUsage() memUsage()@endlink.
+- Eliminated the @c GridMap class. File I/O now uses STL containers
+ of grid pointers instead.
+- Refactored stencil-based tools (Gradient, Laplacian, etc.) and rewrote
+ some of them for generality and better performance. Most now behave
+ correctly for grids with nonlinear index-to-world transforms.
+- Added a @link FiniteDifference.h library@endlink of index-space finite
+ difference operators.
+- Added a @vdblink::math::Hermite "Hermite"@endlink grid type that compactly
+ stores each voxel's upwind normals and can be used to convert volumes
+ to and from polygonal meshes.
+- Added a @link PointScatter.h tool@endlink (and a Houdini SOP)
+ to scatter points randomly throughout a volume.
+
+*/
diff --git a/extern/openvdb/internal/openvdb/doc/doc.txt b/extern/openvdb/internal/openvdb/doc/doc.txt
new file mode 100644
index 00000000000..bee192bc91d
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/doc/doc.txt
@@ -0,0 +1,417 @@
+/**
+@mainpage OpenVDB
+
+The @b OpenVDB library comprises a hierarchical data structure and a suite
+of tools for the efficient manipulation of sparse, possibly time-varying,
+volumetric data discretized on a three-dimensional grid. It is based on
+VDB, which was developed by Ken Museth at DreamWorks Animation, and it
+offers an effectively infinite 3D index space, compact storage (both in
+memory and on disk), fast data access (both random and sequential), and
+a collection of algorithms specifically optimized for the data structure
+for common tasks such as filtering, CSG, compositing, sampling and
+voxelization from other geometric representations. The technical details
+of VDB are described in the paper <a href="http://www.museth.org/Ken/Publications.html">&ldquo;VDB:&nbsp;High-Resolution Sparse Volumes with Dynamic Topology&rdquo;</a>.
+
+@b OpenVDB is maintained by
+<A HREF="http://www.dreamworksanimation.com">DreamWorks Animation</A>
+and was developed primarily by
+- Ken Museth
+- Peter Cucka
+- Mihai Ald&eacute;n
+- David Hill
+
+See the @subpage overview "Overview" for an introduction to the library.
+
+See @subpage transformsAndMaps "Transforms and Maps" for more
+discussion of the transforms used in @b OpenVDB.
+
+See the @subpage faq "FAQ" for frequently asked questions about @b OpenVDB.
+
+See the @subpage codeExamples "Cookbook" to get started using @b OpenVDB.
+
+See the @subpage changes "Release Notes" for what's new in this version
+of @b OpenVDB.
+
+
+
+@page overview OpenVDB Overview
+
+@section Contents
+- @ref secOverview
+- @ref secTree
+ - @ref subsecTreeConfig
+- @ref secSparsity
+ - @ref subsecValues
+ - @ref subsecInactive
+- @ref secSpaceAndTrans
+ - @ref subsecVoxSpace
+ - @ref subsecWorSpace
+ - @ref subsecTrans
+- @ref secToolUtils
+- @ref secIterator
+ - @ref subsecTreeIter
+ - @ref subsecNodeIter
+ - @ref subsecValueAccessor
+ - @ref subsecTraversal
+<!-- - @ref secRegistry -->
+
+
+@section secOverview Introduction
+
+This document is a high-level summary of the terminology and basic
+components of the OpenVDB library and is organized around two key
+motivating concepts. First, OpenVDB is designed specifically to
+work efficiently with sparse volumetric data locally sampled at a high
+spatial frequency, although it will function well for dense volumetric
+data. From this follows the need for a memory efficient representation of
+this sparsity and the need for fast iterators (and other tools) that
+respect sparsity. Second, data storage is separated from data
+interpretation. OpenVDB uses unit-less three-dimensional integer
+coordinates to address the sparse data, but introduces a unit-less
+continuous index space for interpolation, along with a transform to
+place the data in physical space.
+
+When manipulating data in OpenVDB, the three essential objects are (1) the
+@vdblink::tree::Tree Tree@endlink, a B-tree-like three-dimensional data
+structure; (2) the @vdblink::math::Transform Transform@endlink, which relates
+voxel indices @ijk to physical locations @xyz in @ref subsecWorSpace
+"world" space; and (3) the @vdblink::Grid Grid@endlink, a container
+that associates a @c Tree with a @c Transform and additional metadata.
+For instancing purposes (i.e., placing copies
+of the same volume in multiple locations), the same tree may be referenced
+(via smart pointers) by several different <TT>Grid</TT>s, each having a
+unique transform.
+
+We now proceed to discuss the @c Tree and ideas of sparsity in some
+detail, followed by a briefer description of the different spaces and
+transforms as well as some of the tools that act on the sparse data.
+
+
+@section secTree The Tree
+
+In OpenVDB the @c Tree data structure exists to answer the question
+<I>What value is stored at location @ijk in three-dimensional index
+space?</I> Here <I>i</I>, <I>j</I> and <I>k</I> are arbitrary signed 32-bit
+integers and the data type of the associated value (<TT>float</TT>,
+<TT>bool</TT>, vector, etc.) is the same for all @ijk. While the @c Tree
+serves the same purpose as a large three-dimensional array, it is a
+specially designed data structure that, given sparse unique values,
+minimizes the overall memory footprint while retaining fast access times.
+This is accomplished, as the name suggests, via a tree-based acceleration
+structure comprising a @vdblink::tree::RootNode RootNode@endlink,
+@vdblink::tree::LeafNode LeafNodes@endlink and usually one or more levels
+of @vdblink::tree::InternalNode InternalNodes@endlink with prescribed
+branching factors.
+
+@subsection subsecTreeConfig Tree Configuration
+
+The tree-based acceleration structure can be configured in various ways,
+but with the restriction that for a given tree all the <TT>LeafNode</TT>s
+are at the same depth. Conceptually, the @c RootNode and
+@c InternalNodes increasingly subdivide the three-dimensional index
+space, and the <TT>LeafNode</TT>s hold the actual unique voxels.
+
+The type of a @c Tree encodes both the type of the data to be
+stored in the tree (<TT>float</TT>, <TT>bool</TT>, etc.) and the
+tree's node configuration. In practice a four-level (root,
+internal, internal, leaf) configuration is standard, and several
+common tree types are defined in openvdb.h. For example,
+@code
+typedef tree::Tree4<float, 5, 4, 3>::Type FloatTree;
+typedef tree::Tree4<bool, 5, 4, 3>::Type BoolTree;
+@endcode
+These predefined tree types share the same branching factors, which dictate
+the number of children of a given node. The branching factors (5, 4, 3)
+are specified as base two logarithms and should be read backwards from the
+leaf nodes up the tree.
+
+In the default tree configuration, each @c LeafNode holds a
+three-dimensional grid of 2<SUP>3</SUP> voxels on a side (i.e., an
+@f$8\times8\times8@f$ voxel grid). Internally, the @c LeafNode is said
+to be at "level 0" of the tree. At "level 1" of this tree is the first
+@c InternalNode, and it indexes a @f$2^4\times2^4\times2^4 =
+16\times16\times16@f$ grid, each entry of which is either a @c LeafNode
+or a constant value that represents an @f$8\times8\times8@f$ block of
+voxels. At "level 2" is the second @c InternalNode in this
+configuration; it in turn indexes a @f$2^5\times2^5\times2^5 =
+32\times32\times32@f$ grid of level-1 <TT>InternalNode</TT>s and/or values,
+and so the @c InternalNode at level 2 subsumes a three-dimensional
+block of voxels of size @f$32\times16\times8 = 4096@f$ on a side. Unlike
+the <TT>InternalNode</TT>s and <TT>LeafNode</TT>s, the @c RootNode ("level
+3" for the default configuration) is not explicitly restricted in the number
+of children it may have, so the overall index space is limited only by the
+range of the integer indices (by default 32-bit index precision).
+
+
+@section secSparsity Sparse Values and Voxels
+
+Like a tree's node configuration, the type of data held by a tree is
+determined at compile time. Conceptually the tree itself
+employs two different notions of data sparsity to reduce the memory footprint
+and at the same time accelerate access to its contents. The first is largely
+hidden from the user and concerns ways in which large regions of uniform values
+are compactly represented, and the second allows for fast sequential iteration,
+skipping user-specified "uninteresting" regions (that may or may not have
+uniform values).
+
+@subsection subsecValues Tile, Voxel, and Background Values
+
+Although the data in a tree is accessed and set on a per-voxel level
+(i.e., the value at @ijk) it need not be internally stored in that way.
+To reduce the memory footprint and accelerate data access, data values are
+stored in three distinct forms internal to the tree: <B>voxel values</B>,
+<B>tile values</B>, and a <B>background value</B>. A voxel value is a unique
+value indexed by the location of a voxel and is stored in the @c LeafNode
+responsible for that voxel. A tile value is a uniform value assigned to all
+voxels subsumed by a given node. (For example, a tile Value belonging to an
+@c InternalNode at level 1 is equivalent to a constant-value cube of voxels
+of the same size, @f$8\times8\times8@f$, as a @c LeafNode.) The tile value
+is returned when a request is made for the data associated with any @ijk
+location within the uniform tile. The background value is a unique value
+(stored at the root-level) that is returned when accessing any @ijk location
+that does not resolve to either a tile or a @c LeafNode.
+
+
+@subsection subsecInactive Active and Inactive Voxels
+
+Any voxel or tile can be classified as either @b active or @b inactive.
+The interpretation of this state is application-specific, but generally
+active voxels are "interesting", and inactive somehow less so. The locations
+of active values may be sparse in the overall voxel topology, and OpenVDB
+provides @ref secIterator "iterators" that access active values only (as
+well as iterators over inactive values, all values, and general topology).
+An example of active vs. inactive: the voxels used to store the distance
+values of a narrow-band level set (i.e., close to a given surface) will be
+marked as active while the other ("far") voxel locations will be marked as
+inactive and will generally represent regions of space with constant distance
+values (e.g., two constant distance values of opposite sign to distinguish
+the enclosed inside region from the infinite outside or background embedding).
+
+The @vdblink::tree::Tree::prune() prune()@endlink method replaces
+with tile values any nodes that subsume voxels with the same values
+and active states.
+The resulting tree represents the same volume, but more sparsely.
+
+@section secSpaceAndTrans Coordinate Systems and Transforms
+
+The sampled data in the tree is accessed using signed <B>index coordinates</B>
+@ijk, but associating each indicial coordinate with a specific physical
+location is a job for a @c Transform. A simple linear transform assumes
+a lattice-like structure with a fixed physical distance @f$\Delta@f$ between
+indices, so that @f$(x,y,z) = (\Delta i, \Delta j, \Delta k)@f$.
+
+@subsection subsecVoxSpace Index Space
+
+To simplify transformations between physical space and lattice index
+coordinates, a continuous generalization of the index lattice points called
+<B>index space</B> is used.
+For example, index space coordinate (1.0, 1.0, 1.0) corresponds to the same
+point as (1,1,1) in the index lattice, but (1.5,1.0,1.0) also has meaning as
+halfway between the index coordinates (1,1,1) and (2,1,1). Index space can
+be used in constructing interpolated data values: given an arbitrary
+location in physical space, one can use a transform to compute the point in
+index space (which need not fall on an exact integer index) that maps to that
+location and locally interpolate from values with neighboring index
+coordinates.
+
+@subsection subsecWorSpace World Space
+
+The interpretation of the data in a tree takes place in <B>world
+space</B>. For example, the tree might hold data sampled at discrete
+physical locations in world space. @c Transform methods such as
+@vdblink::math::Transform::indexToWorld() indexToWorld() @endlink
+and its inverse @vdblink::math::Transform::worldToIndex()
+worldToIndex() @endlink
+may be used to relate coordinates in the two continuous spaces. In
+addition, methods such as @vdblink::math::Transform::worldToIndexCellCentered()
+worldToIndexCellCentered()@endlink actually return lattice points.
+
+@subsection subsecTrans Transforms and Maps
+A @c Grid contains smart pointers to both a @c Tree object and a
+@vdblink::math::Transform Transform @endlink object. The transform
+provides a context for interpreting the information held in the tree
+by associating a location in world space with each entry in the tree.
+The actual implementation of the @c Transform is managed by a
+@vdblink::math::MapBase Map@endlink object, which is an
+encapsulation of a continuous, mostly invertible function of three
+variables. A @c Map is required to provide
+@vdblink::math::MapBase::applyMap() applyMap()@endlink and
+@vdblink::math::MapBase::applyInverseMap() applyInverseMap()@endlink
+methods to relate locations in its domain to its range and vice versa.
+A @c Map is also required to provide information about its local derivatives.
+For more on these classes, see the
+@subpage transformsAndMaps "Transforms and Maps" page.
+
+
+@section secToolUtils Utilities and Tools
+
+OpenVDB provides utility functions and classes for the manipulation
+of grids and the data they hold. Tools such as those found in
+Gradient.h and Laplacian.h compute vector quantities from scalar
+data, while those in Curl.h, Divergence.h, Magnitude.h and Normalize.h
+perform simple operations on vector data. Other tools perform filtering
+(Filter.h) and interpolation (LinearInterp.h, QuadraticInterp.h) as well as
+sampling (GridTransformer.h), compositing (Composite.h), and other
+transformations (ValueTransformer.h). OpenVDB also supports advanced finite
+difference computations though various local support stencils defined in
+Stencils.h as well as fast hierachical constructive-solid-geometry operations
+on narrow-band level sets.
+
+
+@section secIterator Iterators
+
+OpenVDB provides efficient, often multithreaded, implementations of a large
+variety of morphological, filtering and other algorithms that address common
+data manipulation tasks on three-dimensional grids. For more specialized
+tasks, OpenVDB provides lower-level data accessors that enable fast
+iteration over all or selected voxels and over the elements of a @c Tree.
+These take several forms: iterator classes of various types, functor-based
+@b visitor methods, and the
+@vdblink::tree::ValueAccessor ValueAccessor@endlink,
+an accelerator for indexed @ijk voxel lookups.
+
+Iterator classes follow a fairly consistent naming scheme. First, the
+@b CIter and @b Iter suffixes denote @const and non-@const iterators, i.e.,
+iterators that offer, respectively, read-only and read/write access to the
+underlying tree or node. Second, iterators over tile and voxel values are
+denoted either @b On, @b Off or @b All, indicating that they visit only
+active values, only inactive values, or both active and inactive values.
+So, for example, @c Tree::ValueOnCIter is a read-only iterator over all
+active values (both tile and voxel) of a tree, whereas
+@c LeafNode::ValueAllIter is a read/write iterator over all values, both
+active and inactive, of a single leaf node.
+
+OpenVDB iterators are not STL-compatible in that one can always request
+an iterator that points to the beginning of a collection of elements (nodes,
+voxels, etc.), but one usually cannot request an iterator that points to the
+end of the collection. (This is because finding the end might require a
+full tree traversal.) Instead, all OpenVDB iterators implement a @c test()
+method that returns @c true as long as the iterator is not exhausted and
+@c false as soon as it is. Typical usage is as follows:
+@code
+typedef openvdb::FloatGrid GridType;
+GridType grid = ...;
+for (GridType::ValueOnCIter iter = grid.cbeginValueOn(); iter.test(); ++iter) ...
+@endcode
+or more compactly
+@code
+for (GridType::ValueOnCIter iter = grid.cbeginValueOn(); iter; ++iter) ...
+@endcode
+Note that the naming scheme for methods that return "begin" iterators
+closely mirrors that of the iterators themselves. That is,
+@c Grid::cbeginValueOn() returns a @const iterator to the first of a grid's
+active values, whereas @c LeafNode::beginValueAll() returns a non-@const
+iterator to the first of a leaf node's values, both active and inactive.
+(Const overloads of @c begin*() methods are usually provided, so that if
+the @c Grid is itself @const, @c Grid::begin*() will actually return a
+@const iterator. This makes it more convenient to use these methods in
+templated code.)
+
+Finally, note that modifying the tree or node over which one is iterating
+typically does not invalidate the iterator, though it might first need
+to be incremented to point to the next existing element (for example,
+if one deletes a child node to which the iterator is currently pointing).
+
+@subsection subsecTreeIter Tree Iterators
+@anchor treeValueIterRef
+@par Tree::ValueIter
+Tree-level value iterators traverse an entire tree, visiting each value
+(tile or voxel) exactly once. (It is also possible to restrict the
+traversal to minimum and maximum levels of the tree.) In addition to the
+methods common to all OpenVDB iterators, such as @c test() and @c next(),
+a @c Tree::ValueIter provides methods that return the depth in the tree of
+the node within which the iterator is pointing (the root node has depth 0)
+and the @ijk axis-aligned bounding box of the tile or voxel to which it is
+pointing, and methods to get and set both the value and the active state of
+the tile or voxel. See the
+@vdblink::tree::TreeValueIteratorBase TreeValueIteratorBase @endlink
+class for the complete list.
+
+@anchor treeLeafIterRef
+@par Tree::LeafIter
+Narrow-band level sets are represented by three distinct regions of voxels:
+a thin band of active voxels whose values are signed distances; an
+@b outside (or background) region of inactive voxels having a constant,
+positive distance; and an @b inside region of inactive voxels having a
+constant, negative distance. By convention in OpenVDB, narrow-band voxels
+are stored only at the leaf level of a tree, so to facilitate the
+implementation of level set algorithms that operate on narrow-band voxels,
+OpenVDB provides an iterator that visits each @c LeafNode in a tree exactly
+once. See the @vdblink::tree::LeafIteratorBase LeafIteratorBase @endlink
+class for details.
+
+@anchor treeNodeIterRef
+@par Tree::NodeIter
+A node iterator traverses a tree in depth-first order, starting from its
+root, and visits each node exactly once. (It is also possible to restrict
+the traversal to minimum and maximum node depths&mdash;see the
+@vdblink::tree::NodeIteratorBase NodeIteratorBase @endlink
+class for details.) Like the tree-level value iterator, the node iterator
+provides methods that return the depth in the tree of the node to which the
+iterator is pointing (the root node has depth 0) and the @ijk axis-aligned
+bounding box of the voxels subsumed by the node and all of its children.
+@par
+Naturally, a node iterator also provides access to the node to which it is
+pointing, but this is complicated somewhat by the fact that nodes of the
+various types (@c RootNode, @c InternalNode and @c LeafNode) do not inherit
+from a common base class. For efficiency, OpenVDB generally avoids class
+inheritance and virtual functions in favor of templates, allowing the
+compiler to optimize away function calls. In particular, each node type is
+templated on the type of its children, so even two <TT>InternalNode</TT>s at
+different levels of a tree have distinct types. As a result, it is
+necessary to know the type of the node to which a node iterator is pointing
+in order to request access to that node. See the
+@ref sNodeIterator "Cookbook" for an example of how to do this.
+
+@subsection subsecNodeIter Node Iterators
+
+Less commonly used than tree-level iterators (but found in the
+implementations of some of the narrow-band level set algorithms referred to
+@ref treeLeafIterRef "above") are node-level iterators. A node
+<B>value iterator</B> visits the values (active, inactive or both) stored in
+a single @c RootNode, @c InternalNode or @c LeafNode, whereas a node
+<B>child iterator</B> visits the children of a single root or internal node.
+(Recall that non-leaf nodes store either a tile value or a child node at
+each grid position.)
+
+
+@subsection subsecValueAccessor Value Accessor
+
+When traversing a grid by @ijk index in a spatially coherent pattern,
+such as when iterating over neighboring voxels, request a
+@vdblink::tree::ValueAccessor ValueAccessor@endlink from the grid
+(with @vdblink::Grid::getAccessor() Grid::getAccessor()@endlink)
+and use the accessor's
+@vdblink::tree::ValueAccessor::getValue() getValue()@endlink and
+@vdblink::tree::ValueAccessor::setValue() setValue()@endlink methods, since
+these will usually be significantly faster (a factor of three is typical)
+than accessing voxels directly in the grid's tree.
+The accessor records the sequence of nodes
+visited during the most recent access; on the next access, rather than
+traversing the tree from the root node down, it performs an inverted
+traversal from the deepest recorded node up. For neighboring voxels, the
+traversal need only proceed as far as the voxels' common ancestor node,
+which more often than not is the first node in the sequence.
+
+Multiple accessors may be associated with a single grid. In fact, for
+multithreaded, read-only access to a grid, it is recommended that each
+thread be assigned its own accessor. A thread-safe, mutex-locked accessor
+is provided (see @vdblink::tree::ValueAccessorRW ValueAccessorRW@endlink),
+but the locking negates much of the performance benefit of inverted traversal;
+and because it is the accessor object that is thread-safe, not the grid,
+concurrent reads and writes are not safe unless all threads share a single
+accessor.
+
+All accessors associated with a grid must be cleared after any operation that
+removes nodes from the grid's tree, such as pruning, CSG or compositing.
+For those and other built-in operations, this is done automatically via
+a callback mechanism, but developers must be careful to call
+@vdblink::tree::Tree::clearAllAccessors() Tree::clearAllAccessors()@endlink
+whenever deleting nodes directly.
+
+
+@subsection subsecTraversal Tree Traversal
+
+<I>To be written</I>
+
+*/
diff --git a/extern/openvdb/internal/openvdb/doc/examplecode.txt b/extern/openvdb/internal/openvdb/doc/examplecode.txt
new file mode 100644
index 00000000000..b6b5ff09486
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/doc/examplecode.txt
@@ -0,0 +1,1018 @@
+/**
+
+@page codeExamples OpenVDB Cookbook
+
+This section provides code snippets and some complete programs that
+illustrate how to use OpenVDB and how to perform common tasks.
+
+
+@section Contents
+- @ref sHelloWorld
+ - @ref sCompilingHelloWorld
+- @ref sAllocatingGrids
+- @ref sPopulatingGrids
+- @ref sModifyingGrids
+- @ref sStreamIO
+- @ref sHandlingMetadata
+ - @ref sAddingMetadata
+ - @ref sGettingMetadata
+ - @ref sRemovingMetadata
+- @ref sIteration
+ - @ref sNodeIterator
+ - @ref sLeafIterator
+ - @ref sValueIterator
+- @ref sXformTools
+ - @ref sResamplingTools
+ - @ref sValueXformTools
+- @ref sCombiningGrids
+ - @ref sCsgTools
+ - @ref sCompTools
+ - @ref sCombineTools
+- @ref sGenericProg
+ - @ref sTypedGridMethods
+
+
+
+@section sHelloWorld "Hello, World" for OpenVDB
+This is a very simple example showing how to create a grid and access
+its voxels. OpenVDB supports both random access to voxels by coordinates
+and sequential access by means of iterators. This example illustrates both
+types of access:
+@code
+#include <openvdb/openvdb.h>
+#include <iostream>
+
+int main()
+{
+ // Initialize the OpenVDB library. This must be called at least
+ // once per program and may safely be called multiple times.
+ openvdb::initialize();
+
+ // Create an empty floating-point grid with background value 0.
+ openvdb::FloatGrid::Ptr grid = openvdb::FloatGrid::create();
+
+ std::cout << "Testing random access:" << std::endl;
+
+ // Get an accessor for coordinate-based access to voxels.
+ openvdb::FloatGrid::Accessor accessor = grid->getAccessor();
+
+ // Define a coordinate with large signed indices.
+ openvdb::Coord xyz(1000, -200000000, 30000000);
+
+ // Set the voxel value at (1000, -200000000, 30000000) to 1.
+ accessor.setValue(xyz, 1.0);
+
+ // Verify that the voxel value at (1000, -200000000, 30000000) is 1.
+ std::cout << "Grid" << xyz << " = " << accessor.getValue(xyz) << std::endl;
+
+ // Reset the coordinates to those of a different voxel.
+ xyz.reset(1000, 200000000, -30000000);
+
+ // Verify that the voxel value at (1000, 200000000, -30000000) is
+ // the background value, 0.
+ std::cout << "Grid" << xyz << " = " << accessor.getValue(xyz) << std::endl;
+
+ // Set the voxel value at (1000, 200000000, -30000000) to 2.
+ accessor.setValue(xyz, 2.0);
+
+ // Set the voxels at the two extremes of the available coordinate space.
+ // For 32-bit signed coordinates these are (-2147483648, -2147483648, -2147483648)
+ // and (2147483647, 2147483647, 2147483647).
+ accessor.setValue(openvdb::Coord::min(), 3.0f);
+ accessor.setValue(openvdb::Coord::max(), 4.0f);
+
+ std::cout << "Testing sequential access:" << std::endl;
+
+ // Print all active ("on") voxels by means of an iterator.
+ for (openvdb::FloatGrid::ValueOnCIter iter = grid->cbeginValueOn(); iter; ++iter) {
+ std::cout << "Grid" << iter.getCoord() << " = " << *iter << std::endl;
+ }
+}
+@endcode
+Output:
+@code
+Testing random access:
+Grid[1000, -200000000, 30000000] = 1
+Grid[1000, 200000000, -30000000] = 0
+Testing sequential access:
+Grid[-2147483648, -2147483648, -2147483648] = 3
+Grid[1000, -200000000, 30000000] = 1
+Grid[1000, 200000000, -30000000] = 2
+Grid[2147483647, 2147483647, 2147483647] = 4
+@endcode
+
+@subsection sCompilingHelloWorld Compiling
+See the @c Makefile and @c INSTALL file included in this distribution for
+details on how to build and install the OpenVDB library.
+By default, installation is into the directory tree rooted at
+<tt>/tmp/OpenVDB/</tt>, but this can be changed either by editing the value
+of the @c INSTALL_DIR variable in the makefile or by setting the desired
+value from the command line, as in the following example:
+@code
+make install INSTALL_DIR=/usr/local
+@endcode
+Once OpenVDB has been installed, the simplest way to compile a program
+like the &ldquo;Hello, World&rdquo; example above is to examine the
+commands that are used to build the @c vdb_print tool:
+@code
+rm vdb_print
+make verbose=yes vdb_print
+@endcode
+and then replace &ldquo;<tt>-o vdb_print</tt>&rdquo; with, for example,
+&ldquo;<tt>-o helloworld</tt>&rdquo;
+and &ldquo;<tt>cmd/openvdb_print/main.cc</tt>&rdquo;
+with &ldquo;<tt>helloworld.cc</tt>&rdquo;.
+
+
+
+@section sAllocatingGrids Creating and writing a grid
+This example is a complete program that illustrates some of the basic steps
+to create grids and write them to disk. (See @ref sPopulatingGrids,
+below, for the implementation of the @c makeSphere() function.)
+@code
+#include <openvdb/openvdb.h>
+
+int main()
+{
+ openvdb::initialize();
+
+ // Create a shared pointer to a newly-allocated grid of a built-in type:
+ // in this case, a FloatGrid, which stores one single-precision floating point
+ // value per voxel. Other built-in grid types include BoolGrid, DoubleGrid,
+ // Int32Grid and Vec3SGrid (see openvdb.h for the complete list).
+ // The grid comprises a sparse tree representation of voxel data,
+ // user-supplied metadata and a voxel space to world space transform,
+ // which defaults to the identity transform.
+ openvdb::FloatGrid::Ptr grid =
+ openvdb::FloatGrid::create(/*background value=*/2.0);
+
+ // Populate the grid with a sparse, narrow-band level set representation
+ // of a sphere with radius 50 voxels, located at (1.5, 2, 3) in index space.
+ makeSphere(*grid, /*radius=*/50.0, /*center=*/openvdb::Vec3f(1.5, 2, 3));
+
+ // Associate some metadata with the grid.
+ grid->insertMeta("radius", openvdb::FloatMetadata(50.0));
+
+ // Associate a scaling transform with the grid that sets the voxel size
+ // to 0.5 units in world space.
+ grid->setTransform(
+ openvdb::math::Transform::createLinearTransform(/*voxel size=*/0.5));
+
+ // Identify the grid as a level set.
+ grid->setGridClass(openvdb::GRID_LEVEL_SET);
+
+ // Name the grid "LevelSetSphere".
+ grid->setName("LevelSetSphere");
+
+ // Create a VDB file object.
+ openvdb::io::File file("mygrids.vdb");
+
+ // Add the grid pointer to a container.
+ openvdb::GridPtrVec grids;
+ grids.push_back(grid);
+
+ // Write out the contents of the container.
+ file.write(grids);
+ file.close();
+}
+@endcode
+
+The OpenVDB library includes optimized routines for many common tasks.
+For example, most of the steps given above are encapsulated in the function
+@vdblink::tools::createLevelSetSphere() tools::createLevelSetSphere()@endlink, so that
+the above can be written simply as follows:
+
+@code
+#include <openvdb/openvdb.h>
+#include <openvdb/tools/LevelSetSphere.h>
+
+int main()
+{
+ openvdb::initialize();
+
+ // Create a FloatGrid and populate it with a narrow-band
+ // signed distance field of a sphere.
+ openvdb::FloatGrid::Ptr grid =
+ openvdb::tools::createLevelSetSphere<openvdb::FloatGrid>(
+ /*radius=*/50.0, /*center=*/openvdb::Vec3f(1.5, 2, 3),
+ /*voxel size=*/0.5, /*width=*/4.0);
+
+ // Associate some metadata with the grid.
+ grid->insertMeta("radius", openvdb::FloatMetadata(50.0));
+
+ // Name the grid "LevelSetSphere".
+ grid->setName("LevelSetSphere");
+
+ // Create a VDB file object.
+ openvdb::io::File file("mygrids.vdb");
+
+ // Add the grid pointer to a container.
+ openvdb::GridPtrVec grids;
+ grids.push_back(grid);
+
+ // Write out the contents of the container.
+ file.write(grids);
+ file.close();
+}
+@endcode
+
+
+
+@section sPopulatingGrids Populating a grid with values
+The following code is templated so as to operate on grids containing values
+of any scalar type, provided that the value type supports negation and
+comparison. Note that this algorithm is only meant as an example and should
+never be used in production; use the much more efficient routines in
+tools/LevelSetSphere.h instead.
+
+See @ref sGenericProg for more on processing grids of arbitrary type.
+@anchor makeSphereCode
+@code
+// Populate the given grid with a narrow-band level set representation of a sphere.
+// The width of the narrow band is determined by the grid's background value.
+// (Example code only; use tools::createSphereSDF() in production.)
+template<class GridType>
+void
+makeSphere(GridType& grid, float radius, const openvdb::Vec3f& c)
+{
+ typedef typename GridType::ValueType ValueT;
+
+ // Distance value for the constant region exterior to the narrow band
+ const ValueT outside = grid.background();
+
+ // Distance value for the constant region interior to the narrow band
+ // (by convention, the signed distance is negative in the interior of
+ // a level set)
+ const ValueT inside = -outside;
+
+ // Use the background value as the width in voxels of the narrow band.
+ // (The narrow band is centered on the surface of the sphere, which
+ // has distance 0.)
+ int padding = int(openvdb::math::RoundUp(openvdb::math::Abs(outside)));
+ // The bounding box of the narrow band is 2*dim voxels on a side.
+ int dim = int(radius + padding);
+
+ // Get a voxel accessor.
+ typename GridType::Accessor accessor = grid.getAccessor();
+
+ // Compute the signed distance from the surface of the sphere of each
+ // voxel within the bounding box and insert the value into the grid
+ // if it is smaller in magnitude than the background value.
+ openvdb::Coord ijk;
+ int &i = ijk[0], &j = ijk[1], &k = ijk[2];
+ for (i = c[0] - dim; i < c[0] + dim; ++i) {
+ const float x2 = openvdb::math::Pow2(i - c[0]);
+ for (j = c[1] - dim; j < c[1] + dim; ++j) {
+ const float x2y2 = openvdb::math::Pow2(j - c[1]) + x2;
+ for (k = c[2] - dim; k < c[2] + dim; ++k) {
+
+ // The distance from the sphere surface in voxels
+ const float dist = openvdb::math::Sqrt(x2y2
+ + openvdb::math::Pow2(k - c[2])) - radius;
+
+ // Convert the floating-point distance to the grid's value type.
+ ValueT val = ValueT(dist);
+
+ // Only insert distances that are smaller in magnitude than
+ // the background value.
+ if (val < inside || outside < val) continue;
+
+ // Set the distance for voxel (i,j,k).
+ accessor.setValue(ijk, val);
+ }
+ }
+ }
+
+ // Propagate the outside/inside sign information from the narrow band
+ // throughout the grid.
+ grid.signedFloodFill();
+}
+@endcode
+
+
+
+@section sModifyingGrids Reading and modifying a grid
+@code
+#include <openvdb/openvdb.h>
+
+openvdb::initialize();
+
+// Create a VDB file object.
+openvdb::io::File file("mygrids.vdb");
+
+// Open the file. This reads the file header, but not any grids.
+file.open();
+
+// Loop over all grids in the file and retrieve a shared pointer
+// to the one named "LevelSetSphere". (This can also be done
+// more simply by calling file.readGrid("LevelSetSphere").)
+openvdb::GridBase::Ptr baseGrid;
+for (openvdb::io::File::NameIterator nameIter = file.beginName();
+ nameIter != file.endName(); ++nameIter)
+{
+ // Read in only the grid we are interested in.
+ if (nameIter.gridName() == "LevelSetSphere") {
+ baseGrid = file.readGrid(nameIter.gridName());
+ } else {
+ std::cout << "skipping grid " << nameIter.gridName() << std::endl;
+ }
+}
+
+file.close();
+
+// From the example above, "LevelSetSphere" is known to be a FloatGrid,
+// so cast the generic grid pointer to a FloatGrid pointer.
+openvdb::FloatGrid::Ptr grid = openvdb::gridPtrCast<openvdb::FloatGrid>(baseGrid);
+
+// Convert the level set sphere to a narrow-band fog volume, in which
+// interior voxels have value 1, exterior voxels have value 0, and
+// narrow-band voxels have values varying linearly from 0 to 1.
+
+const float outside = grid->background();
+const float width = 2.0 * outside;
+
+// Visit and update all of the grid's active values, which correspond to
+// voxels on the narrow band.
+for (openvdb::FloatGrid::ValueOnIter iter = grid->beginValueOn(); iter; ++iter) {
+ float dist = iter.getValue();
+ iter.setValue((outside - dist) / width);
+}
+
+// Visit all of the grid's inactive tile and voxel values and update the values
+// that correspond to the interior region.
+for (openvdb::FloatGrid::ValueOffIter iter = grid->beginValueOff(); iter; ++iter) {
+ if (iter.getValue() < 0.0) {
+ iter.setValue(1.0);
+ iter.setValueOff();
+ }
+}
+
+// Set exterior voxels to 0.
+grid->setBackground(0.0);
+@endcode
+
+
+
+@section sStreamIO Stream I/O
+The @vdblink::io::Stream io::Stream@endlink class allows grids
+to be written to and read from streams that do not support random access,
+with the restriction that all grids must be written or read at once.
+(With @vdblink::io::File io::File@endlink,
+grids can be read individually by name, provided that they were originally
+written with @c io::File, rather than streamed to a file.)
+
+@code
+#include <openvdb/openvdb.h>
+#include <openvdb/io/Stream.h>
+
+openvdb::initialize();
+
+openvdb::GridPtrVecPtr grids(new GridPtrVec);
+grids->push_back(...);
+
+// Stream the grids to a string.
+std::ostringstream ostr(std::ios_base::binary);
+openvdb::io::Stream().write(ostr, *grids);
+
+// Stream the grids to a file.
+std::ofstream ofile("mygrids.vdb", std::ios_base::binary);
+openvdb::io::Stream().write(ofile, *grids);
+
+// Stream in grids from a string.
+// Note that io::Stream::getGrids() returns a shared pointer
+// to an openvdb::GridPtrVec.
+std::istringstream istr(ostr.str(), std::ios_base::binary);
+openvdb::io::Stream strm(istr);
+grids = strm.getGrids();
+
+// Stream in grids from a file.
+std::ifstream ifile("mygrids.vdb", std::ios_base::binary);
+grids = openvdb::io::Stream(ifile).getGrids();
+@endcode
+
+
+
+@section sHandlingMetadata Handling metadata
+Metadata of various types (string, floating point, integer, etc.&mdash;see
+metadata/Metadata.h for more) can be attached both to individual <tt>Grid</tt>s
+and to files on disk.
+The examples that follow refer to <tt>Grid</tt>s, but the usage is the same
+for the @vdblink::MetaMap MetaMap@endlink that can optionally be supplied
+to a @vdblink::io::File::write() file@endlink or
+@vdblink::io::Stream::write() stream@endlink for writing.
+
+@subsection sAddingMetadata Adding metadata
+The @vdblink::Grid::insertMeta() Grid::insertMeta()@endlink method either
+adds a new (@em name, @em value) pair if the name is unique, or overwrites
+the existing value if the name matches an existing one. An existing value
+cannot be overwritten with a new value of a different type; the old metadata
+must be removed first.
+@code
+#include <openvdb/openvdb.h>
+
+openvdb::Vec3SGrid::Ptr grid = openvdb::Vec3SGrid::create();
+
+grid->insertMeta("vector type", openvdb::StringMetadata("covariant (gradient)"));
+grid->insertMeta("radius", openvdb::FloatMetadata(50.0));
+grid->insertMeta("center", openvdb::Vec3SMetadata(openvdb::Vec3S(10, 15, 10)));
+
+// OK, overwrites existing value:
+grid->insertMeta("center", openvdb::Vec3SMetadata(openvdb::Vec3S(10.5, 15, 30)));
+
+// Error (throws openvdb::TypeError), can't overwrite a value of type Vec3S
+// with a value of type float:
+grid->insertMeta("center", openvdb::FloatMetadata(0.0));
+@endcode
+
+@subsection sGettingMetadata Retrieving metadata
+Call @vdblink::Grid::metaValue() Grid::metaValue()@endlink to retrieve
+the value of metadata of a known type. For example,
+@code
+std::string s = grid->metaValue<std::string>("vector type");
+
+float r = grid->metaValue<float>("radius");
+
+// Error (throws openvdb::TypeError), can't read a value of type Vec3S as a float:
+float center = grid->metaValue<float>("center");
+@endcode
+
+@vdblink::Grid::beginMeta() Grid::beginMeta()@endlink and
+@vdblink::Grid::beginMeta() Grid::beginMeta()@endlink return STL @c std::map
+iterators over all of the metadata associated with a grid:
+@code
+for (openvdb::MetaMap::MetaIterator iter = grid->beginMeta();
+ iter != grid->endMeta(); ++iter)
+{
+ const std::string& name = iter->first;
+ openvdb::Metadata::Ptr value = iter->second;
+ std::string valueAsString = value->str();
+ std::cout << name << " = " << valueAsString << std::endl;
+}
+@endcode
+
+If the type of the metadata is not known, use the
+@vdblink::Grid::operator[]() index operator@endlink to retrieve
+a shared pointer to a generic @vdblink::Metadata Metadata@endlink object,
+then query its type:
+@code
+openvdb::Metadata::Ptr metadata = grid["center"];
+
+// See typenameAsString<T>() in Types.h for a list of strings that can be
+// returned by the typeName() method.
+std::cout << metadata->typeName() << std::endl; // prints "vec3s"
+
+// One way to process metadata of arbitrary types:
+if (metadata->typeName() == openvdb::StringMetadata::staticTypeName()) {
+ std::string s = static_cast<openvdb::StringMetadata&>(*metadata).value();
+} else if (metadata->typeName() == openvdb::FloatMetadata::staticTypeName()) {
+ float f = static_cast<openvdb::FloatMetadata&>(*metadata).value();
+} else if (metadata->typeName() == openvdb::Vec3SMetadata::staticTypeName()) {
+ openvdb::Vec3S v = static_cast<openvdb::Vec3SMetadata&>(*metadata).value();
+}
+@endcode
+
+@subsection sRemovingMetadata Removing metadata
+@vdblink::Grid::removeMeta() Grid::removeMeta()@endlink removes metadata
+by name. If the given name is not found, the call has no effect.
+@code
+grid->removeMeta("vector type");
+grid->removeMeta("center");
+grid->removeMeta("vector type"); // OK (no effect)
+@endcode
+
+
+
+@section sIteration Iteration
+
+@subsection sNodeIterator Node Iterator
+A @vdblink::tree::Tree::NodeIter Tree::NodeIter@endlink visits each node in
+a tree exactly once. In the following example, the tree is known to have a
+depth of 4; see the @ref treeNodeIterRef "Overview" for a discussion of
+why node iteration can be complicated when the tree depth is not known.
+There are techniques (beyond the scope of this Cookbook) for operating
+on trees of arbitrary depth.
+@code
+#include <openvdb/openvdb.h>
+
+typedef openvdb::FloatGrid GridType;
+typedef GridType::TreeType TreeType;
+typedef TreeType::RootNodeType RootType; // level 3 RootNode
+assert(RootType::LEVEL == 3);
+typedef RootType::ChildNodeType Int1Type; // level 2 InternalNode
+typedef Int1Type::ChildNodeType Int2Type; // level 1 InternalNode
+typedef TreeType::LeafNodeType LeafType; // level 0 LeafNode
+
+GridType::Ptr grid = ...;
+
+for (TreeType::NodeIter iter = grid->tree().beginNode(); iter; ++iter) {
+ switch (iter.getDepth()) {
+ case 0: { RootType* node = NULL; iter.getNode(node); if (node) ...; break; }
+ case 1: { Int1Type* node = NULL; iter.getNode(node); if (node) ...; break; }
+ case 2: { Int2Type* node = NULL; iter.getNode(node); if (node) ...; break; }
+ case 3: { LeafType* node = NULL; iter.getNode(node); if (node) ...; break; }
+ }
+}
+@endcode
+Calling @vdblink::Grid::tree() tree()@endlink instead would return a @c const
+reference to the existing tree, with no guarantee of exclusive ownership.
+
+
+@subsection sLeafIterator Leaf Node Iterator
+A @vdblink::tree::Tree::LeafIter Tree::LeafIter@endlink visits each leaf
+node in a tree exactly once.
+@code
+#include <openvdb/openvdb.h>
+
+typedef openvdb::FloatGrid GridType;
+typedef GridType::TreeType TreeType;
+
+GridType::Ptr grid = ...;
+
+// Iterate over references to const LeafNodes.
+for (TreeType::LeafCIter iter = grid->tree().cbeginLeaf(); iter; ++iter) {
+ const TreeType::LeafNodeType& leaf = *iter;
+ ...
+}
+// Iterate over references to non-const LeafNodes.
+for (TreeType::LeafIter iter = grid->tree().beginLeaf(); iter; ++iter) {
+ TreeType::LeafNodeType& leaf = *iter;
+ ...
+}
+// Iterate over pointers to const LeafNodes.
+for (TreeType::LeafCIter iter = grid->tree().cbeginLeaf(); iter; ++iter) {
+ const TreeType::LeafNodeType* leaf = iter.getLeaf();
+ ...
+}
+// Iterate over pointers to non-const LeafNodes.
+for (TreeType::LeafIter iter = grid->tree().beginLeaf(); iter; ++iter) {
+ TreeType::LeafNodeType* leaf = iter.getLeaf();
+ ...
+}
+@endcode
+See the @ref treeLeafIterRef "Overview" for more on leaf node iterators.
+
+
+@subsection sValueIterator Value Iterator
+A @vdblink::tree::Tree::ValueAllIter Tree::ValueIter@endlink visits each
+@ref subsecValues "value" (both tile and voxel) in a tree exactly once.
+Iteration can be unrestricted or can be restricted to only active values
+or only inactive values. Note that tree-level value iterators (unlike
+the node iterators described above) can be accessed either through a
+grid's tree or directly through the grid itself, as in the following example:
+@code
+#include <openvdb/openvdb.h>
+
+typedef openvdb::Vec3SGrid GridType;
+typedef GridType::TreeType TreeType;
+
+GridType::Ptr grid = ...;
+
+// Iterate over all active values but don't allow them to be changed.
+for (GridType::ValueOnCIter iter = grid->cbeginValueOn(); iter.test(); ++iter) {
+ const openvdb::Vec3f& value = *iter;
+
+ // Print the coordinates of all voxels whose vector value has
+ // a length greater than 10, and print the bounding box coordinates
+ // of all tiles whose vector value length is greater than 10.
+ if (value.length() > 10.0) {
+ if (iter.isVoxelValue()) {
+ std::cout << iter.getCoord() << std::endl;
+ } else {
+ openvdb::CoordBBox bbox;
+ iter.getBoundingBox(bbox);
+ std::cout << bbox << std::endl;
+ }
+ }
+}
+
+// Iterate over and normalize all inactive values.
+for (GridType::ValueOffIter iter = grid->beginValueOff(); iter.test(); ++iter) {
+ openvdb::Vec3f value = *iter;
+ value.normalize();
+ iter.setValue(value);
+}
+
+// Normalize the (inactive) background value as well.
+grid->setBackground(grid->background().unit());
+@endcode
+See the @ref treeValueIterRef "Overview" for more on value iterators.
+
+
+
+@section sXformTools Transforming grids
+
+@subsection sResamplingTools Geometric transformation
+A @vdblink::tools::GridTransformer GridTransformer@endlink applies a
+geometric transformation to an input grid using one of several sampling
+schemes, and stores the result in an output grid. The operation is
+multithreaded by default, though threading can be disabled by calling
+@vdblink::tools::GridTransformer::setThreaded() setThreaded(false)@endlink.
+A @c GridTransformer object can be reused to apply the same transformation
+to multiple input grids, optionally using different sampling schemes.
+@code
+#include <openvdb/openvdb.h>
+#include <openvdb/tools/GridTransformer.h>
+
+openvdb::FloatGrid::Ptr
+ sourceGrid = ...
+ targetGrid = ...;
+
+// Get the source and target grids' index space to world space transforms.
+const openvdb::Transform
+ &sourceXform = sourceGrid->transform(),
+ &targetXform = targetGrid->transform();
+// Compute a source grid to target grid transform.
+// (For this example, we assume that both grids' transforms are linear,
+// so that they can be represented as 4 x 4 matrices.)
+openvdb::Mat4R xform =
+ sourceXform.getBaseMap()->getAffineMap()->getMat4() *
+ targetXform.getBaseMap()->getAffineMap()->getMat4().inverse();
+
+// Create the transformer.
+openvdb::tools::GridTransformer transformer(xform);
+
+// Resample using nearest-neighbor interpolation.
+transformer.transformGrid<openvdb::tools::PointSampler, openvdb::FloatGrid>(
+ *sourceGrid, *targetGrid);
+
+// Resample using trilinear interpolation.
+transformer.transformGrid<openvdb::tools::BoxSampler, openvdb::FloatGrid>(
+ *sourceGrid, *targetGrid);
+
+// Resample using triquadratic interpolation.
+transformer.transformGrid<openvdb::tools::QuadraticSampler, openvdb::FloatGrid>(
+ *sourceGrid, *targetGrid);
+
+// Prune the target tree for optimal sparsity.
+targetGrid->tree().prune();
+@endcode
+
+
+@subsection sValueXformTools Value transformation
+
+This example uses @vdblink::tools::foreach() tools::foreach()@endlink to
+multiply all values (both tile and voxel and both active and inactive)
+of a scalar, floating-point grid by two:
+@code
+#include <openvdb/openvdb.h>
+#include <openvdb/tools/ValueTransformer.h>
+
+// Define a local function that doubles the value to which the given
+// value iterator points.
+struct Local {
+ static inline void op(const openvdb::FloatGrid::ValueAllIter& iter) {
+ iter.setValue(*iter * 2);
+ }
+};
+
+openvdb::FloatGrid::Ptr grid = ...;
+
+// Apply the function to all values.
+openvdb::tools::foreach(grid->beginValueAll(), Local::op);
+@endcode
+
+This example uses @vdblink::tools::foreach() tools::foreach()@endlink to
+rotate all active vectors of a vector-valued grid by 45 degrees about the
+@em y axis:
+@code
+#include <openvdb/openvdb.h>
+#include <openvdb/tools/ValueTransformer.h>
+
+// Define a functor that multiplies the vector to which the given
+// value iterator points by a fixed matrix.
+struct MatMul {
+ openvdb::math::Mat3s M;
+ MatMul(const openvdb::math::Mat3s& mat): M(mat) {}
+ inline void operator()(const openvdb::Vec3SGrid::ValueOnIter& iter) const {
+ iter.setValue(M.transform(*iter));
+ }
+};
+
+openvdb::Vec3SGrid::Ptr grid = ...;
+
+// Construct the rotation matrix.
+openvdb::math::Mat3s rot45 =
+ openvdb::math::rotation<openvdb::math::Mat3s>(openvdb::math::Y_AXIS, M_PI_4);
+
+// Apply the functor to all active values.
+openvdb::tools::foreach(grid->beginValueOn(), MatMul(rot45));
+@endcode
+
+@vdblink::tools::transformValues() tools::transformValues()@endlink is
+similar to @vdblink::tools::foreach() tools::foreach()@endlink, but it populates
+an output grid with transformed values from an input grid that may have a
+different value type. The following example populates a scalar,
+floating-point grid with the lengths of all active vectors from a
+vector-valued grid (see also
+@vdblink::tools::Magnitude tools::Magnitude@endlink):
+@code
+#include <openvdb/openvdb.h>
+#include <openvdb/tools/ValueTransformer.h>
+
+// Define a local function that, given an iterator pointing to a vector value
+// in an input grid, sets the corresponding tile or voxel in a scalar,
+// floating-point output grid to the length of the vector.
+struct Local {
+ static inline void op(
+ const openvdb::Vec3SGrid::ValueOnCIter& iter,
+ openvdb::FloatGrid::ValueAccessor& accessor)
+ {
+ if (iter.isVoxelValue()) { // set a single voxel
+ accessor.setValue(iter.getCoord(), iter->length());
+ } else { // fill an entire tile
+ openvdb::CoordBBox bbox;
+ iter.getBoundingBox(bbox);
+ accessor.getTree().fill(bbox, iter->length());
+ }
+ }
+};
+
+openvdb::Vec3SGrid::ConstPtr inGrid = ...;
+
+// Create a scalar grid to hold the transformed values.
+openvdb::FloatGrid::Ptr outGrid = openvdb::FloatGrid::create();
+
+// Populate the output grid with transformed values.
+openvdb::tools::transformValues(inGrid->cbeginValueOn(), *outGrid, Local::op);
+@endcode
+
+
+
+@section sCombiningGrids Combining grids
+
+The following examples show various ways in which a pair of grids can be
+combined in @ref subsecVoxSpace "index space". The assumption is that index
+coordinates @ijk in both grids correspond to the same physical, @ref
+subsecWorSpace "world space" location. When the grids have different
+transforms, it is usually necessary to first @ref sResamplingTools "resample"
+one grid into the other grid's @ref subsecVoxSpace "index space".
+
+@subsection sCsgTools Level set CSG operations
+The level set CSG functions in tools/Composite.h operate on pairs of grids
+of the same type, using sparse traversal for efficiency. These operations
+always leave the second grid empty.
+@code
+#include <openvdb/openvdb.h>
+#include <openvdb/tools/Composite.h>
+
+// Two grids of the same type containing level set volumes
+openvdb::FloatGrid::Ptr gridA(...), gridB(...);
+
+// Save copies of the two grids; CSG operations always modify
+// the A grid and leave the B grid empty.
+openvdb::FloatGrid::ConstPtr
+ copyOfGridA = gridA->deepCopy(),
+ copyOfGridB = gridB->deepCopy();
+
+// Compute the union (A u B) of the two level sets.
+openvdb::tools::csgUnion(*gridA, *gridB);
+
+// Restore the original level sets.
+gridA = copyOfGridA->deepCopy();
+gridB = copyOfGridB->deepCopy();
+
+// Compute the intersection (A n B) of the two level sets.
+openvdb::tools::csgIntersection(gridA, gridB);
+
+// Restore the original level sets.
+gridA = copyOfGridA->deepCopy();
+gridB = copyOfGridB->deepCopy();
+
+// Compute the difference (A / B) of the two level sets.
+openvdb::tools::csgDifference(gridA, gridB);
+@endcode
+
+
+@subsection sCompTools Compositing operations
+Like the @ref sCsgTools "CSG operations", the compositing functions in
+tools/Composite.h operate on pairs of grids of the same type, and they
+always leave the second grid empty.
+@code
+#include <openvdb/openvdb.h>
+#include <openvdb/tools/Composite.h>
+
+// Two grids of the same type
+openvdb::FloatGrid::Ptr gridA = ..., gridB = ...;
+
+// Save copies of the two grids; compositing operations always
+// modify the A grid and leave the B grid empty.
+openvdb::FloatGrid::ConstPtr
+ copyOfGridA = gridA->deepCopy(),
+ copyOfGridB = gridB->deepCopy();
+
+// At each voxel, compute a = max(a, b).
+openvdb::tools::compMax(*gridA, *gridB);
+
+// Restore the original grids.
+gridA = copyOfGridA->deepCopy();
+gridB = copyOfGridB->deepCopy();
+
+// At each voxel, compute a = min(a, b).
+openvdb::tools::compMin(*gridA, *gridB);
+
+// Restore the original grids.
+gridA = copyOfGridA->deepCopy();
+gridB = copyOfGridB->deepCopy();
+
+// At each voxel, compute a = a + b.
+openvdb::tools::compSum(*gridA, *gridB);
+
+// Restore the original grids.
+gridA = copyOfGridA->deepCopy();
+gridB = copyOfGridB->deepCopy();
+
+// At each voxel, compute a = a * b.
+openvdb::tools::compMul(*gridA, *gridB);
+@endcode
+
+
+@subsection sCombineTools Generic combination
+The @vdblink::tree::Tree::combine() Tree::combine()@endlink family of
+methods apply a user-supplied operator to pairs of corresponding values
+of two trees. These methods are efficient because they take into account
+the sparsity of the trees; they are not multithreaded, however.
+
+This example uses the @vdblink::tree::Tree::combine() Tree::combine()@endlink
+method to compute the difference between corresponding voxels of two
+floating-point grids:
+@code
+#include <openvdb/openvdb.h>
+
+// Define a local function that subtracts two floating-point values.
+struct Local {
+ static inline void diff(const float& a, const float& b, float& result) {
+ result = a - b;
+ }
+};
+
+openvdb::FloatGrid::Ptr aGrid = ..., bGrid = ...;
+
+// Compute the difference between corresponding voxels of aGrid and bGrid
+// and store the result in aGrid, leaving bGrid empty.
+aGrid->tree().combine(bGrid->tree(), Local::diff);
+@endcode
+
+Another @vdblink::tree::Tree::combine() Tree::combine()@endlink example,
+this time using a functor to preserve state:
+@code
+#include <openvdb/openvdb.h>
+
+// Define a functor that computes f * a + (1 - f) * b for pairs of
+// floating-point values a and b.
+struct Blend {
+ Blend(float f): frac(f) {}
+ inline void operator()(const float& a, const float& b, float& result) const {
+ result = frac * a + (1.0 - frac) * b;
+ }
+ float frac;
+};
+
+openvdb::FloatGrid::Ptr aGrid = ..., bGrid = ...;
+
+// Compute a = 0.25 * a + 0.75 * b for all corresponding voxels of
+// aGrid and bGrid. Store the result in aGrid and empty bGrid.
+aGrid->tree().combine(bGrid->tree(), Blend(0.25));
+@endcode
+
+The @vdblink::tree::Tree::combineExtended() Tree::combineExtended()@endlink
+method invokes a function of the form <tt>void f(CombineArgs\<T>& args)</tt>,
+where the @vdblink::CombineArgs CombineArgs@endlink object encapsulates an
+@em a and a @em b value and their active states as well as a result value
+and its active state. In the following example, voxel values in
+floating-point @c aGrid are replaced with corresponding values from
+floating-point @c bGrid (leaving @c bGrid empty) wherever the @em b values
+are larger. The active states of any transferred values are preserved.
+@code
+#include <openvdb/openvdb.h>
+
+// Define a local function that retrieves a and b values from a CombineArgs
+// struct and then sets the result member to the maximum of a and b.
+struct Local {
+ static inline void max(CombineArgs<float>& args) {
+ if (args.b() > args.a()) {
+ // Transfer the B value and its active state.
+ args.setResult(args.b());
+ args.setResultIsActive(args.bIsActive());
+ } else {
+ // Preserve the A value and its active state.
+ args.setResult(args.a());
+ args.setResultIsActive(args.aIsActive());
+ }
+ }
+};
+
+openvdb::FloatGrid::Ptr aGrid = ..., bGrid = ...;
+
+aGrid->tree().combineExtended(bGrid->tree(), Local::max);
+@endcode
+
+Like @c combine(), @vdblink::tree::Tree::combine2() Tree::combine2()@endlink
+applies an operation to pairs of corresponding values of two trees.
+However, @c combine2() writes the result to a third, output tree and does
+not modify either of the two input trees. (As a result, it is less
+space-efficient than the @c combine() method.) Here, the voxel differencing
+example above is repeated using @c combine2():
+@code #include
+<openvdb/openvdb.h>
+
+struct Local {
+ static inline void diff(const float& a, const float& b, float& result) {
+ result = a - b;
+ }
+};
+
+openvdb::FloatGrid::ConstPtr aGrid = ..., bGrid = ...;
+openvdb::FloatGrid::Ptr resultGrid = openvdb::FloatGrid::create();
+
+// Combine aGrid and bGrid and write the result into resultGrid.
+// The input grids are not modified.
+resultGrid->tree().combine2(aGrid->tree(), bGrid->tree(), Local::diff);
+@endcode
+An @vdblink::tree::Tree::combine2Extended() extended combine2()@endlink
+is also available.
+
+
+
+@section sGenericProg Generic programming
+
+@subsection sTypedGridMethods Calling Grid methods
+A common task is to perform some operation on all of the grids in a file,
+where the operation involves @vdblink::Grid Grid@endlink method calls
+and the grids are of different types.
+Only a handful of @c Grid methods, such as
+@vdblink::Grid::activeVoxelCount() activeVoxelCount()@endlink,
+are virtual and can be called through a @vdblink::GridBase GridBase@endlink
+pointer; most are not, because they require knowledge of the <tt>Grid</tt>'s
+value type.
+For example, one might want to @vdblink::tree::Tree::prune() prune()@endlink
+the trees of all of the grids in a file regardless of their type, but
+@c Tree::prune() is non-virtual because it accepts an optional pruning
+tolerance argument whose type is the grid's value type.
+
+The @c processTypedGrid() function below makes this kind of task easier.
+It is called with a @c GridBase pointer and a functor whose call operator
+accepts a pointer to a @c Grid of arbitrary type. The call operator should
+be templated on the grid type and, if necessary, overloaded for specific
+grid types.
+
+@code
+template<typename OpType>
+void processTypedGrid(openvdb::GridBase::Ptr grid, OpType& op)
+{
+#define CALL_OP(GridType) \
+ op.template operator()<GridType>(openvdb::gridPtrCast<GridType>(grid))
+
+ if (grid->isType<openvdb::BoolGrid>()) CALL_OP(openvdb::BoolGrid);
+ else if (grid->isType<openvdb::FloatGrid>()) CALL_OP(openvdb::FloatGrid);
+ else if (grid->isType<openvdb::DoubleGrid>()) CALL_OP(openvdb::DoubleGrid);
+ else if (grid->isType<openvdb::Int32Grid>()) CALL_OP(openvdb::Int32Grid);
+ else if (grid->isType<openvdb::Int64Grid>()) CALL_OP(openvdb::Int64Grid);
+ else if (grid->isType<openvdb::Vec3IGrid>()) CALL_OP(openvdb::Vec3IGrid);
+ else if (grid->isType<openvdb::Vec3SGrid>()) CALL_OP(openvdb::Vec3SGrid);
+ else if (grid->isType<openvdb::Vec3DGrid>()) CALL_OP(openvdb::Vec3DGrid);
+ else if (grid->isType<openvdb::StringGrid>()) CALL_OP(openvdb::StringGrid);
+
+#undef CALL_OP
+}
+@endcode
+
+The following example shows how to use @c processTypedGrid() to implement
+a generic pruning operation for grids of all built-in types:
+@code
+#include <openvdb.h>
+
+// Define a functor that prunes the trees of grids of arbitrary type
+// with a fixed pruning tolerance.
+struct PruneOp {
+ double tolerance;
+ PruneOp(double t): tolerance(t) {}
+
+ template<typename GridType>
+ void operator()(typename GridType::Ptr grid) const
+ {
+ grid->tree().prune(typename GridType::ValueType(tolerance));
+ }
+ // Overload to handle string-valued grids
+ void operator()(openvdb::StringGrid::Ptr grid) const
+ {
+ grid->tree().prune();
+ }
+};
+
+// Read all grids from a file.
+openvdb::io::File file("mygrids.vdb");
+file.open();
+openvdb::GridPtrVecPtr myGrids = file.getGrids();
+file.close();
+
+// Prune each grid with a tolerance of 1%.
+const PruneOp pruner(/*tolerance=*/0.01);
+for (openvdb::GridPtrVecIter iter = myGrids->begin();
+ iter != myGrids->end(); ++iter)
+{
+ openvdb::GridBase::Ptr grid = *iter;
+ processTypedGrid(grid, pruner);
+}
+@endcode
+
+*/
diff --git a/extern/openvdb/internal/openvdb/doc/faq.txt b/extern/openvdb/internal/openvdb/doc/faq.txt
new file mode 100644
index 00000000000..75f34d0c57b
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/doc/faq.txt
@@ -0,0 +1,213 @@
+/**
+
+@page faq Frequently Asked Questions
+
+@section Contents
+- @ref sWhatIsVDB
+- @ref sWhatLicense
+- @ref sWhyUseVDB
+- @ref sGeneralizedOctree
+- @ref sLevelSet
+- @ref sCustomizeVDB
+- @ref sAdaptiveGrid
+- @ref sMeaningOfVDB
+- @ref sAccessor
+- @ref sValue
+- @ref sState
+- @ref sVoxel
+- @ref sTile
+- @ref sBackground
+- @ref sThreadSafe
+- @ref sMaxRes
+- @ref sCompareVDB
+- @ref sReplaceDense
+- @ref sFuture
+- @ref sContribute
+
+@section sWhatIsVDB What is OpenVDB?
+OpenVDB is a library comprising a compact hierarchical data structure and a
+suite of tools for the efficient manipulation of sparse, possibly time-varying,
+volumetric data discretized on a three-dimensional grid. It is based on
+VDB, which was developed by Ken Museth at DreamWorks Animation, and it
+offers an effectively infinite 3D index space, compact storage (both in
+memory and on disk), fast data access (both random and sequential), and
+a collection of algorithms specifically optimized for the data structure
+for common tasks such as filtering, constructive solid geometry (CSG),
+discretization of partial differential equations, voxelization of polygons,
+skinning of particles, volumetric compositing and sampling. The technical
+details of VDB are described in the paper
+<a href="http://www.museth.org/Ken/Publications.html" target="_blank">"VDB: High-Resolution Sparse Volumes with Dynamic Topology"</a>.
+
+@section sWhatLicense What license is OpenVDB distributed under?
+OpenVDB is released under the Mozilla Public License Version 2.0, which is a
+free, open source, and detailed software license developed and maintained by
+the Mozilla Foundation. It is characterized as a hybridization of the modified
+BSD license and GNU General Public License (GPL) that seeks to balance the
+concerns of proprietary and open source developers. For more information
+about this license, see the
+<a href="http://www.mozilla.org/MPL" target="_blank">Mozilla FAQ</a>.
+
+@section sWhyUseVDB Why should I use OpenVDB?
+The typical reasons to adopt OpenVDB are if you are <b>storing sparse
+data</b> and/or if you are performing <b>sparse computations</b>. OpenVDB
+is also effectively unbounded, which makes it very convenient for
+applications where the topology of the data is dynamic or unknown. Unlike
+many existing sparse data structures, OpenVDB is also optimized for
+numerical simulations, multithreaded volume compositing, near real-time
+boolean CSG operations, fast random and sequential data access and
+voxelization of points and polygons.
+
+@section sCustomizeVDB Can I customize the configuration of OpenVDB?
+Yes! OpenVDB is specifically developed to be highly customizable. That is,
+the user can define the sizes of all the nodes at each level of a tree as
+well as the number of tree levels and the data type of values stored in the
+tree. However, note that since OpenVDB makes extensive use of C++
+templating, configurations are fixed at compile time rather than at run
+time. This is a fundamental design characteristic of OpenVDB, and it leads
+to very high performance, thanks to techniques like inlining and template
+metaprogramming.
+
+@section sGeneralizedOctree Is OpenVDB merely a generalized octree or N-tree?
+No! While OpenVDB can conceptually be configured as a (height-balanced)
+octree, it is much more than an octree or N-tree. Whereas octrees and
+N-trees have fixed branching factors of respectively two and N in each
+coordinate direction, OpenVDB's branching factors typically vary between
+tree levels and are only limited to be powers of two. To understand why
+and also learn about other unique features of OpenVDB, refer to the
+upcoming paper in <i>ACM&nbsp;Transactions on Graphics</i>.
+
+@section sLevelSet Is OpenVDB primarily for level set applications?
+No! Don't let the fact that OpenVDB can represent and operate so well on
+level sets mislead you into thinking that it is limited to or even focusing
+on this special type of sparse volumetric application. OpenVDB was developed
+for general-purpose volumetric processing and numerical simulation, and we
+have even had success using it for volumetric applications that traditionally
+call for blocked or dense grids. However, the fact remains that narrow-band
+level sets play an essential role in many volumetric applications, and this
+explains why OpenVDB includes so many tools and algorithms specifically
+for level sets.
+
+@section sAdaptiveGrid Is OpenVDB an adaptive grid?
+Let's first stress that the term "adaptive grid" is somewhat ambiguous. Some
+use it to mean a grid that can store data sampled at adaptive voxel sizes
+typically derived from a so-called "refinement oracle", whereas others mean
+multiple grids with different fixed voxel sizes all sampling the same data.
+An example of the former is an octree and of the latter is a mipmap. Since
+OpenVDB stores both data values and child nodes at each level of the tree it
+is adaptive only in the first sense, not the second. The level of adaptivity
+or refinement between the tree levels is defined by the branching factors of
+the nodes, which are fixed at compile time.
+
+@section sMeaningOfVDB What does "VDB" stand for?
+Over the years VDB has been interpreted to mean different things, none of
+which are very descriptive: "Voxel Data Base", "Volumetric Data Blocks",
+"Volumetric Dynamic B+tree", etc. In early presentations of VDB we even used
+a different name, "DB+Grid", which was abandoned to emphasize its
+distinction from similarly named, but different, existing sparse data
+structures like DT-Grid or DB-Grid. The simple truth is that "VDB" is just a
+name. :-)
+
+@section sAccessor Why are there no coordinate-based access methods on the grid?
+It might surprise you that the @c Grid class doesn't directly provide access
+to voxels via their @ijk coordinates. Instead, the recommended procedure
+is to ask the grid for a "value accessor", which is an accelerator object
+that performs bottom-up tree traversal using cached information from
+previous traversals. Caching greatly improves performance, but it is
+inherently not thread-safe. However, a single grid may have multiple
+value accessors, so each thread can safely be assigned its own value accessor.
+Uncached&mdash;and therefore slower, but thread-safe&mdash;random access is possible
+through a grid's tree, for example with a call like
+<tt>grid.tree()->getValue(ijk)</tt>.
+
+@section sValue How and where does OpenVDB store values?
+OpenVDB stores voxel data in a tree with a fixed maximum height (chosen
+at compile time), with a root node that has a dynamic branching factor,
+with internal nodes that have fixed branching factors, and with leaf nodes
+of fixed dimensions. Values can be stored in nodes at all levels of
+the tree. Values stored in leaf nodes correspond to individual voxels;
+all other values correspond to "tiles" (see below).
+
+@section sState What are active and inactive values?
+Every value in a grid has a binary state that we refer to as its
+"active state".
+The interpretation of this binary state is application-specific, but
+typically an active (on) value is "interesting" in some sense, and an
+inactive (off) value is less interesting or uninteresting.
+For example, the values in a narrow-band level set are all active, and
+values outside the narrow band are all inactive.
+Active states of values are stored in very compact bit masks that
+support sparse iteration, so visiting all active (or inactive) values
+in a grid can be done very efficiently.
+
+@section sVoxel How are voxels represented in OpenVDB?
+Values stored in nodes of type @c LeafNode (which, when they exist,
+have a fixed depth), correspond to individual voxels.
+These are the smallest addressable units of index space.
+
+@section sTile What are tiles?
+Values stored in nodes of type @c RootNode or @c InternalNode correspond to
+regions of index space with a constant value and active state and with
+power of two dimensions. We refer to such regions as "tiles". By
+construction, tiles have no child nodes.
+
+@section sBackground What is the background value?
+A tree's background value is the value that is returned whenever
+one accesses a region of index space that is not explicitly represented by
+voxels or tiles in the tree. Thus, you can think of the background value
+as the default value associated with an empty tree.
+Note that the background value is always inactive!
+
+@section sThreadSafe Is OpenVDB thread-safe?
+Yes and no. If you're asking if OpenVDB can safely be used in a
+multithreaded application then the answer is a resounding yes. In fact, many
+of the tools included in OpenVDB are multithreaded (using Intel's Threading
+Building Blocks library). However, like virtually all data structures that
+employ caching and allocate-on-insert, certain operations in OpenVDB are not
+thread-safe in the general sense. In particular, it is not safe to retrieve
+voxels from a grid while another thread is inserting voxels.
+For multithreaded insertion operations we typically assign a separate grid
+to each thread and then merge the grids as threads terminate.
+This technique works remarkably well: because OpenVDB
+is sparse and hierarchical, merging is very efficient.
+For more details, please consult the @subpage codeExamples and
+Appendix&nbsp;B in the <i>Transactions on Graphics</i> paper.
+
+@section sMaxRes Is OpenVDB unbounded?
+Yes, to within available memory and the 32-bit precision of the coordinates
+used to index voxels. And OpenVDB supports signed coordinates, unlike most
+existing sparse data structures, so there are almost no restrictions on the
+available grid resolution or the index range of OpenVDB grids.
+
+@section sCompareVDB How does OpenVDB compare to existing sparse data structures?
+OpenVDB is very different from existing sparse data structures that you are
+likely to have heard of. Foremost it is hierarchical (unlike DT-Grid and
+Field3D), supports simulations and dynamic topology (unlike GigaVoxels), is
+effectively unbounded (unlike Field3D), and offers fast random and sequential
+voxel access.
+
+@section sReplaceDense Does OpenVDB replace dense grids?
+This depends a lot on your application of dense grids and your configuration
+of OpenVDB. Clearly, if you are storing or processing sparse data, OpenVDB
+will offer a smaller memory footprint and faster (sparse) data processing.
+However, even in some cases where both data and computation are dense, OpenVDB
+can offer benefits like improved CPU cache performance due to its underlying
+blocking and hierarchical tree structure. Exceptions are of course algorithms
+that expect dense data to be laid out linearly in memory, or applications that
+use very small grids. The simple truth is only a benchmark comparison can tell
+you the preferred data structure, but for what it's worth it is our experience
+that for the volumetric applications we encounter in production, OpenVDB is
+almost always superior.
+
+@section sFuture What future improvements to OpenVDB are planned?
+We are continuing to expand the OpenVDB toolset and improve its performance.
+In the near future we hope to add Python bindings, real-time volume rendering,
+a particle acceleration data structure, and possibly support for out-of-core
+rendering and volume processing. We would very much like to hear your
+suggestions and preferences.
+
+@section sContribute How can I contribute to OpenVDB?
+If you have bug reports or ideas for improvements or new features,
+please contact us! We will work to accommodate your suggestions and
+source code contributions.
+
+*/
diff --git a/extern/openvdb/internal/openvdb/doc/math.txt b/extern/openvdb/internal/openvdb/doc/math.txt
new file mode 100644
index 00000000000..5bd11c2890c
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/doc/math.txt
@@ -0,0 +1,416 @@
+/**
+
+@page transformsAndMaps Transforms and Maps
+
+@section Contents
+- @ref sTransforms
+ - @ref sLinearTransforms
+ - @ref sFrustumTransforms
+ - @ref sCellVsVertex
+ - @ref sVoxels
+ - @ref sStaggered
+- @ref sMaps
+ - @ref sGettingMat4
+ - @ref sCostOfMaps
+ - @ref sGradientAndMaps
+
+@section sTransforms Transforms in OpenVDB
+The OpenVDB @vdblink::tree::Tree Tree@endlink is a sparse representation
+of a three-dimensional array of voxels, each element of which is addressed via
+discrete, three-dimensional index space coordinates, typically in the form of
+a @vdblink::math::Coord Coord@endlink.
+For example, the following code retrieves the floating-point value of
+a voxel with index coordinates&nbsp;(1,&nbsp;2,&nbsp;3):
+@code
+openvdb::FloatGrid grid = ...;
+openvdb::FloatGrid::Accessor accessor = grid.getAccessor();
+openvdb::Coord ijk(1,2,3);
+float value = accessor.getValue(ijk);
+@endcode
+
+A @vdblink::math::Transform Transform@endlink relates index space coordinates
+to world space coordinates that give a spatial context for the discretized data.Translation from index coordinates @ijk to world space coordinates @xyz is
+done with a call to the
+@vdblink::math::Transform::indexToWorld() indexToWorld@endlink
+method, and from world space coordinates to index space coordinates with
+a call to @vdblink::math::Transform::worldToIndex() worldToIndex@endlink:
+@code
+// Create a linear transform that scales i, j and k by 0.1
+openvdb::math::Transform::Ptr linearTransform =
+ openvdb::math::Transform::createLinearTransform(0.1);
+
+// Compute the location in world space that is the image of (1,2,3).
+// The result will be (0.1, 0.2, 0.3).
+openvdb::Coord ijk(1,2,3);
+openvdb::Vec3d worldSpacePoint = linearTransform->indexToWorld(ijk);
+
+// Compute the location in index space that is the pre-image of (0.1, 0.2, 0.3).
+// The result will be (1.0, 2.0, 3.0).
+openvdb::Vec3d indexSpacePoint = linearTransform->worldToIndex(worldSpacePoint);
+@endcode
+In the above example, there are two things to notice.
+First, world space locations are specified as three-dimensional,
+double-precision, floating-point vectors, and second, @c worldToIndex
+does not return discrete coordinates, but rather a floating-point vector.
+This is a reflection of the fact that not every location in a continuous
+world space, i.e., not every @xyz, is the image of discrete integral
+coordinates @ijk.
+
+@subsection sLinearTransforms Linear Transforms
+Currently two different types of transforms are supported: linear and
+frustum transforms.
+A linear transform can be composed of scale, translation, rotation, and shear;
+essentially those things that can be mathematically represented by an
+invertible, constant-coefficient, @f$3 \times 3@f$ matrix and a translation
+(mathematically, an affine map).
+An essential feature of a linear transformation is that it treats all regions
+of index space equally: a small box in index space about origin @ijk=(0,0,0)
+is mapped (sheared, scaled, rotated, etc.) in just the same way that
+a small box about any other index point is mapped.
+@code
+// Create a linear transform from a 4x4 matrix (identity in this example).
+openvdb::math::Mat4d mat = openvdb::math::Mat4d::identity();
+openvdb::math::Transform::Ptr linearTransform =
+ openvdb::math::Transform::createLinearTransform(mat);
+
+// Rotate the transform by 90 degrees about the X axis.
+// As a result the j-index will now map into the -z physical direction,
+// and the k-index will map to the +y physical direction.
+linearTransform->preRotate(M_PI/2, openvdb::math::X_AXIS);
+@endcode
+
+@subsection sFrustumTransforms Frustum Transforms
+The frustum transform is a nonlinear transform that treats different
+index points differently.
+And while the linear transform can be applied to any point in index
+or world space, the frustum transform is designed to operate on a subset
+of space. Specifically, it transforms a given box in index space to
+a tapered box in world space that is a frustum of a rectangular pyramid.
+@code
+// Create the bounding box that will be mapped by the transform into
+// the shape of a frustum.
+// The points (0,0,0), (100,0,0), (0,50,0) and (100,50,0) will map to
+// the corners of the near plane of the frustum, while the corners
+// of the far plane will be the images of (0,0,120), (100,0,120),
+// (0,50,120) and (100,50,120).
+const openvdb::math::BBoxd bbox(/*min=*/openvdb::math::Vec3d(0,0,0),
+ /*max=*/openvdb::math::Vec3d(100,50,120));
+
+// The far plane of the frustum will be twice as big as the near plane.
+const double taper = 2;
+
+// The depth of the frustum will be 10 times the x-width of the near plane.
+cosnt double depth = 10;
+
+// The x-width of the frustum in world space units
+const double xWidth = 100;
+
+// Construct a frustum transform that results in a frustum whose
+// near plane is centered on the origin in world space.
+openvdb::math::Transform::Ptr frustumTransform =
+ openvdb::math:::Transform::createFrustumTransform(
+ bbox, taper, depth, xWidth);
+
+// The frustum shape can be rotated, scaled, translated and even
+// sheared if desired. For example, the following call translates
+// the frustum by 10,15,0 in world space:
+frustumTransform->postTranslate(openvdb::math::Vec3d(10,15,0));
+
+// Compute the world space image of a given point within
+// the index space bounding box that defines the frustum.
+openvdb::Coord ijk(20,10,18);
+openvdb::Vec3d worldLocation = frustumTransform->indexToWorld(ijk);
+@endcode
+
+@subsection sCellVsVertex Cell-Centered vs. Vertex-Centered Transforms
+When partitioning world space with a regular grid, two popular
+configurations are cell-centered and vertex-centered grids. This is really
+a question of interpretation and transforms.
+
+The cell-centered interpretation imagines world space as divided
+into discrete cells (e.g., cubes) centered on the image of the
+index-space lattice points.
+So the physical location @xyz that is the image (result of @c indexToWorld)
+of a lattice point @ijk is the center of a cube.
+In the vertex-centered approach, the images of the lattice points form the
+vertices of cells, so the location @xyz would be a corner, not the
+center, of a cube.
+
+The link between transforms and cell-centered or vertex-centered
+partitioning of world space is tenuous.
+It boils down to this: the &ldquo;first&rdquo; cell vertex often is aligned
+with the origin.
+In the cell-centered case, when the cells are cubes of length @f$\Delta@f$
+on a side, the transform
+@f$(x,y,z) = (\Delta i + \Delta/2, \Delta j + \Delta/2, \Delta k + \Delta /2 )@f$
+will place the center of the first cube (i.e., the image of @f$(0,0,0)@f$)
+at the world space location of @f$(\Delta/2, \Delta/2, \Delta/2)@f$,
+and the cube will have walls coincident with the @f$x=0@f$, @f$y=0@f$
+and @f$z=0@f$ planes.
+Using the OpenVDB transforms to create a so-called cell-centered transform
+could be done like this:
+@code
+// -- Constructing a uniform, cell-centered transform --
+
+// The grid spacing
+const double delta = 0.1;
+
+// The offset to cell-center points
+const openvdb::math::Vec3d offset(delta/2., delta/2., delta/2.);
+
+// A linear transform with the correct spacing
+openvdb::math::Transform::Ptr transform =
+ openvdb::math:::Transform::createLinearTransform(delta);
+
+// Add the offset.
+transform->postTranslate(offset);
+@endcode
+In contrast, for the vertex-centered partitions of space the first
+vertex is just the image of the first lattice point @f$ijk = (0,0,0)@f$,
+and the transform would lack the offset used in the cell-centered case;
+so it would simply be @f$(x,y,z) = (\Delta i , \Delta j ,\Delta k)@f$
+@code
+// -- Constructing a uniform, vertex-centered transform --
+
+// The grid spacing
+const double delta = 0.1;
+
+// A linear transform with the correct spacing
+openvdb::math::Transform::Ptr transform =
+ openvdb::math:::Transform::createLinearTransform(delta);
+@endcode
+
+@subsection sVoxels Voxel Interpretations
+A similar and often related concept to cell- and vertex-centered
+partitioning of world space is the idea of a voxel.
+A voxel historically refers to the volumetric equivalent of a pixel
+and as such implies a small region of world space.
+A voxel could, for instance, be the image under transform of a vertex-centered
+(or cell-centered) box in index space.
+In this way the voxel can be seen as a generalization of regular grid cells.
+The interpretation of data stored in a grid can be related to the concept
+of a voxel but need not be.
+An application might interpret the grid value indexed by @ijk as the
+spatial average of a quantity in a defined world-space voxel centered
+on the image of that lattice point.
+But in other situations the value stored at @ijk might be a discrete sample
+taken from a single point in world space.
+
+The @vdblink::math::Transform Transform@endlink class does include
+methods such as @vdblink::math::Transform::voxelSize() voxelSize@endlink
+and @vdblink::math::Transform::voxelVolume() voxelVolume@endlink that suppose
+a particular interpretation of a voxel.
+They assume a voxel that is the image of a vertex-centered cube in index space,
+so the @c voxelSize methods return the lengths of line segments in world space
+that connect vertices:
+@code
+openvdb::Coord ijk(0,0,0);
+openvdb::Coord tmp0(1,0,0), tmp1(0,1,0), tmp2(0,0,1);
+
+openvdb::math::Vec3d size;
+size.x() = (xform.indexToWorld(ijk + tmp0) - xform.indexToWorld(ijk)).length();
+size.y() = (xform.indexToWorld(ijk + tmp1) - xform.indexToWorld(ijk)).length();
+size.z() = (xform.indexToWorld(ijk + tmp2) - xform.indexToWorld(ijk)).length();
+
+// The voxelSize() for the voxel at (0,0,0) is consistent with
+// the computation above.
+assert(xform.voxelSize(ijk) == size);
+@endcode
+In the case where the transform is linear, the result of @c voxelSize
+will be independent of the actual location @ijk, but the voxel size for a
+nonlinear transform such as a frustum will be spatially varying.
+The related @c voxelVolume can not in general be computed from the
+@c voxelSize, because there is no guarantee that a general transform
+will convert a cube-shaped voxel into another cube.
+As a result, the @c voxelVolume actually returns the determinant of the
+transform, which will be a correct measure of volume even if the voxel
+is sheared into a general parallelepiped.
+
+@subsection sStaggered Staggered Velocity Grids
+Staggered velocity data is often used in fluid simulations, and the
+relationship between data interpretation and transforms is
+somewhat peculiar when using a vector grid to hold
+staggered velocity data.
+In this case the lattice point @ijk identifies a cell in world space
+by mapping to the cell&rsquo;s center, but each element of the velocity
+vector is identified with a different face of the cell.
+The first element of the vector is identified with the image of the
+@f$(i-1/2,j,k)@f$ face, the second element with @f$(i,j-1/2,k)@f$,
+and the third element with @f$(i,j,k-1/2)@f$.
+
+
+@section sMaps Maps in OpenVDB Transforms
+The actual work of a @vdblink::math::Transform Transform@endlink is performed
+by an implementation object called a @vdblink::math::MapBase Map@endlink.
+The map in turn is a polymorphic object whose derived types are designed to
+optimally represent the most common transformations.
+Specifically, the @vdblink::math::Transform Transform@endlink holds a
+@vdblink::math::MapBase MapBase@endlink pointer to a derived type that
+has been simplified to minimize calculations.
+When the transform is updated by prepending or appending a linear operation
+(e.g., with @vdblink::math::Transform::preRotate() preRotate@endlink),
+the implementation map is recomputed and simplified if possible.
+For example, in many level-set-oriented applications the transform
+between index space and world space is simply a uniform scaling of the
+index points, i.e., @f$(x,y,z) = (\Delta i, \Delta j, \Delta k)@f$,
+where @f$\Delta@f$ has some world space units.
+For transforms such as this, the implementation is a
+@vdblink::math::UniformScaleMap UniformScaleMap@endlink.
+@code
+// Create a linear transform that scales i, j and k by 0.1
+openvdb::math::Transform::Ptr linearTransform =
+ openvdb::math::Transform::createLinearTransform(0.1);
+
+// Create an equivalent map.
+openvdb::math::UniformScaleMap uniformScale(0.1);
+
+// At this time the map holds a openvdb::math::UniformScaleMap.
+assert(linearTransform->mapType() == openvdb::math::UniformScaleMap::type());
+
+openvdb::Coord ijk(1,2,3);
+
+// Applying the transform...
+openvdb::math::Vec3d transformResult = linearTransform->indexToWorld(ijk);
+
+// ...is equivalent to applying the map.
+openvdb::math::Vec3d mapResult = uniformScale.applyMap(ijk);
+
+assert(mapResult == transformResult);
+@endcode
+
+There are specialized maps for a variety of common linear transforms:
+pure translation (@vdblink::math::TranslationMap TranslationMap@endlink),
+uniform scale (@vdblink::math::UniformScaleMap UniformScaleMap@endlink),
+uniform scale and translation
+(@vdblink::math::UniformScaleTranslateMap UniformScaleTranslateMap@endlink),
+non-uniform scale (@vdblink::math::ScaleMap ScaleMap@endlink), and
+non-uniform scale and translation
+(@vdblink::math::ScaleTranslateMap ScaleTranslateMap@endlink).
+A general affine map (@vdblink::math::AffineMap AffineMap@endlink) is used
+for all other cases (those that include non-degenerate rotation or shear).
+
+@subsection sGettingMat4 An Equivalent Matrix Representation
+The matrix representation used within OpenVDB adheres to the minority
+convention of right-multiplication of the matrix against a vector:
+@code
+// Example matrix transform that scales, next translates,
+// and finally rotates an incoming vector
+openvdb::math::Mat4d transform = openvdb::math::Mat4d::identity();
+transform.preScale(openvdb::math::Vec3d(2,3,2));
+transform.postTranslate(openvdb::math::Vec3d(1,0,0));
+transform.postRotate(openvdb::math::X_AXIS, M_PI/3.0);
+
+// Location of a point in index space
+openvdb::math::Vec3d indexSpace(1,2,3);
+
+// Apply the transform by right-multiplying the matrix.
+openvdb::math::Vec3d worldSpace = indexSpace * transform;
+@endcode
+Any linear map can produce an equivalent
+@vdblink::math::AffineMap AffineMap@endlink, which in turn can produce
+an equivalent @f$4 \times 4@f$ matrix.
+Starting with a linear transform one can produce a consistent matrix as follows:
+@code
+openvdb::math::Mat4d matrix;
+if (transform->isLinear()) {
+ // Get the equivalent 4x4 matrix.
+ matrix = transform->getBaseMap()->getAffineMap()->getMat4();
+}
+@endcode
+
+This could be used as an intermediate form when constructing new linear
+transforms by combining old ones.
+@code
+// Get the matrix equivalent to linearTransformA.
+openvdb::math::Mat4d matrixA =
+ linearTransformA->getBaseMap()->getAffineMap()->getMat4();
+
+// Invert the matrix equivalent to linearTransformB.
+openvdb::math::Mat4d matrixBinv =
+ (linearTransformB->getBaseMap()->getAffineMap()->getMat4()).inverse();
+
+// Create a new transform that maps the index space of linearTransformA
+// to the index space of linearTransformB.
+openvdb::math::Transform::Ptr linearTransformAtoB =
+ openvdb::math::Trasform::createLinearTransform(matrixA * matrixBinv);
+@endcode
+Notice that in the above example, the internal representation used by
+the transform will be simplified if possible to use one of the various
+map types.
+
+@subsection sCostOfMaps Working Directly with Maps
+Accessing a transform&rsquo;s map through virtual function calls
+introduces some overhead and precludes compile-time optimizations
+such as inlining.
+For this reason, the more computationally intensive OpenVDB tools are
+templated to work directly with any underlying map.
+This allows the tools direct access to the map&rsquo;s simplified
+representation and gives the compiler a free hand to inline.
+
+Maps themselves know nothing of index space and world space, but are
+simply functions @f$x_{range} = f(x_{domain})@f$ that map 3-vectors from
+one space (the domain) to another (the range), or from the range back to
+the domain (@f$x_{domain} = f^{-1}(x_{range})@f$), by means of the methods
+@vdblink::math::MapBase::applyMap() applyMap@endlink and
+@vdblink::math::MapBase::applyInverseMap() applyInverseMap@endlink.
+
+@code
+// Create a simple uniform scale map that scales by 10.
+openvdb::math::UniformScaleMap usm(10.0);
+
+// A point in the domain
+openvdb::math::Vec3d domainPoint(0,1,3);
+
+// The resulting range point
+openvdb::math::Vec3d rangePoint = usm.applyMap(domainPoint);
+
+// The map is inverted thus:
+assert(domainPoint == usm.applyInverseMap(rangePoint));
+@endcode
+
+In many tools, the actual map type is not known a priori and
+must be deduced at runtime prior to calling the appropriate
+map-specific or map-templated code. The type of map currently being
+used by a transform can be determined using the
+@vdblink::math::Transform::mapType() mapType@endlink method:
+@code
+// Test for a uniform scale map.
+if (transform->mapType() == openvdb::math::UniformScaleMap::type()) {
+
+ // This call would return a null pointer in the case of a map type mismatch.
+ openvdb::math::UniformScaleMap::ConstPtr usm =
+ transform->map<openvdb::math::UniformScaleMap>();
+
+ // Call a function that accepts UniformScaleMaps.
+ dofoo(*usm)
+}
+@endcode
+
+To simplify this process, the function
+@vdblink::math::processTypedMap processTypedMap@endlink has been provided.
+It accepts a transform and a functor templated on the map type.
+
+@subsection sGradientAndMaps Maps and Mathematical Operations
+In addition to supporting the mapping of a point from one space to another,
+maps also support mapping of local gradients.
+This results from use of the chain rule of calculus in computing the
+Jacobian of the map.
+Essentially, the gradient calculated in the domain of a map can be converted
+to the gradient in the range of the map, allowing one to compute a gradient
+in index space and then transform it to a gradient in world space.
+@code
+// Compute the gradient at a point in index space in a
+// floating-point grid using the second-order central difference.
+openvdb::FloatGrid grid = ...;
+openvdb::Coord ijk(2,3,5)
+openvdb::math::Vec3d isGrad =
+ openvdb::math::ISGradient<CD_2D>::result(grid, ijk);
+
+// Apply the inverse Jacobian transform to convert the result to the
+// gradient in the world space defined by a map that scales index space
+// to create voxels of size 0.1 x 0.2 x 0.1
+openvdb::math::ScaleMap scalemap(0.1, 0.2, 0.1);
+openvdb::math::Vec3d wsGrad = scalemap.applyIJT(isGrad);
+@endcode
+
+*/
diff --git a/extern/openvdb/internal/openvdb/io/Archive.cc b/extern/openvdb/internal/openvdb/io/Archive.cc
new file mode 100644
index 00000000000..9b178ef99f1
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/io/Archive.cc
@@ -0,0 +1,746 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+
+#include "Archive.h"
+
+#include <algorithm> // for std::find_if()
+#include <cstring> // for std::memcpy()
+#include <iostream>
+#include <map>
+#include <sstream>
+#include <boost/uuid/uuid_generators.hpp>
+#include <boost/uuid/uuid_io.hpp>
+#include <openvdb/Exceptions.h>
+#include <openvdb/Metadata.h>
+#include <openvdb/util/logging.h>
+#include "GridDescriptor.h"
+
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace io {
+
+// Indices into a stream's internal extensible array of values used by readers and writers
+struct StreamState
+{
+ static const long MAGIC_NUMBER;
+
+ StreamState();
+
+ int magicNumber;
+ int fileVersion;
+ int libraryMajorVersion;
+ int libraryMinorVersion;
+ int dataCompression;
+ int writeGridStatsMetadata;
+ int gridBackground;
+ int gridClass;
+}
+sStreamState;
+
+const long StreamState::MAGIC_NUMBER =
+ long((uint64_t(OPENVDB_MAGIC) << 32) | (uint64_t(OPENVDB_MAGIC)));
+
+const uint32_t Archive::DEFAULT_COMPRESSION_FLAGS = (COMPRESS_ZIP | COMPRESS_ACTIVE_MASK);
+
+
+////////////////////////////////////////
+
+
+StreamState::StreamState(): magicNumber(std::ios_base::xalloc())
+{
+ // Having reserved an entry (the one at index magicNumber) in the extensible array
+ // associated with every stream, store a magic number at that location in the
+ // array belonging to the cout stream.
+ std::cout.iword(magicNumber) = MAGIC_NUMBER;
+ std::cout.pword(magicNumber) = this;
+
+ // Search for a lower-numbered entry in cout's array that already contains the magic number.
+ /// @todo This assumes that the indices returned by xalloc() increase monotonically.
+ int existingArray = -1;
+ for (int i = 0; i < magicNumber; ++i) {
+ if (std::cout.iword(i) == MAGIC_NUMBER) {
+ existingArray = i;
+ break;
+ }
+ }
+
+ if (existingArray >= 0 && std::cout.pword(existingArray) != NULL) {
+ // If a lower-numbered entry was found to contain the magic number,
+ // a coexisting version of this library must have registered it.
+ // In that case, the corresponding pointer should point to an existing
+ // StreamState struct. Copy the other array indices from that StreamState
+ // into this one, so as to share state with the other library.
+ const StreamState& other =
+ *static_cast<const StreamState*>(std::cout.pword(existingArray));
+ fileVersion = other.fileVersion;
+ libraryMajorVersion = other.libraryMajorVersion;
+ libraryMinorVersion = other.libraryMinorVersion;
+ dataCompression = other.dataCompression;
+ writeGridStatsMetadata = other.writeGridStatsMetadata;
+ gridBackground = other.gridBackground;
+ gridClass = other.gridClass;
+ } else {
+ // Reserve storage for per-stream file format and library version numbers
+ // and other values of use to readers and writers. Each of the following
+ // values is an index into the extensible arrays associated with all streams.
+ // The indices are common to all streams, but the values stored at those indices
+ // are unique to each stream.
+ fileVersion = std::ios_base::xalloc();
+ libraryMajorVersion = std::ios_base::xalloc();
+ libraryMinorVersion = std::ios_base::xalloc();
+ dataCompression = std::ios_base::xalloc();
+ writeGridStatsMetadata = std::ios_base::xalloc();
+ gridBackground = std::ios_base::xalloc();
+ gridClass = std::ios_base::xalloc();
+ }
+}
+
+
+////////////////////////////////////////
+
+
+Archive::Archive():
+ mFileVersion(OPENVDB_FILE_VERSION),
+ mUuid(boost::uuids::nil_uuid()),
+ mInputHasGridOffsets(false),
+ mEnableInstancing(true),
+ mCompression(DEFAULT_COMPRESSION_FLAGS),
+ mEnableGridStats(true)
+{
+ mLibraryVersion.first = OPENVDB_LIBRARY_MAJOR_VERSION;
+ mLibraryVersion.second = OPENVDB_LIBRARY_MINOR_VERSION;
+}
+
+
+Archive::~Archive()
+{
+}
+
+
+////////////////////////////////////////
+
+
+std::string
+Archive::getUniqueTag() const
+{
+ /// @todo Once versions of Boost < 1.44.0 are no longer in use,
+ /// this can be replaced with "return boost::uuids::to_string(mUuid);".
+ std::ostringstream ostr;
+ ostr << mUuid;
+ return ostr.str();
+}
+
+
+bool
+Archive::isIdentical(const std::string& uuidStr) const
+{
+ return uuidStr == getUniqueTag();
+}
+
+
+////////////////////////////////////////
+
+
+uint32_t
+getFormatVersion(std::istream& is)
+{
+ return static_cast<uint32_t>(is.iword(sStreamState.fileVersion));
+}
+
+
+void
+Archive::setFormatVersion(std::istream& is)
+{
+ is.iword(sStreamState.fileVersion) = mFileVersion;
+}
+
+
+VersionId
+getLibraryVersion(std::istream& is)
+{
+ VersionId version;
+ version.first = static_cast<uint32_t>(is.iword(sStreamState.libraryMajorVersion));
+ version.second = static_cast<uint32_t>(is.iword(sStreamState.libraryMinorVersion));
+ return version;
+}
+
+
+void
+Archive::setLibraryVersion(std::istream& is)
+{
+ is.iword(sStreamState.libraryMajorVersion) = mLibraryVersion.first;
+ is.iword(sStreamState.libraryMinorVersion) = mLibraryVersion.second;
+}
+
+
+std::string
+getVersion(std::istream& is)
+{
+ VersionId version = getLibraryVersion(is);
+ std::ostringstream ostr;
+ ostr << version.first << "." << version.second << "/" << getFormatVersion(is);
+ return ostr.str();
+}
+
+
+void
+setCurrentVersion(std::istream& is)
+{
+ is.iword(sStreamState.fileVersion) = OPENVDB_FILE_VERSION;
+ is.iword(sStreamState.libraryMajorVersion) = OPENVDB_LIBRARY_MAJOR_VERSION;
+ is.iword(sStreamState.libraryMinorVersion) = OPENVDB_LIBRARY_MINOR_VERSION;
+}
+
+
+void
+setVersion(std::ios_base& strm, const VersionId& libraryVersion, uint32_t fileVersion)
+{
+ strm.iword(sStreamState.fileVersion) = fileVersion;
+ strm.iword(sStreamState.libraryMajorVersion) = libraryVersion.first;
+ strm.iword(sStreamState.libraryMinorVersion) = libraryVersion.second;
+}
+
+
+std::string
+Archive::version() const
+{
+ std::ostringstream ostr;
+ ostr << mLibraryVersion.first << "." << mLibraryVersion.second << "/" << mFileVersion;
+ return ostr.str();
+}
+
+
+////////////////////////////////////////
+
+
+uint32_t
+getDataCompression(std::ios_base& strm)
+{
+ return uint32_t(strm.iword(sStreamState.dataCompression));
+}
+
+
+void
+setDataCompression(std::ios_base& strm, uint32_t compression)
+{
+ strm.iword(sStreamState.dataCompression) = compression;
+}
+
+
+void
+Archive::setDataCompression(std::istream& is)
+{
+ io::setDataCompression(is, mCompression);
+}
+
+
+bool
+Archive::isCompressionEnabled() const
+{
+ return (mCompression & COMPRESS_ZIP);
+}
+
+
+void
+Archive::setCompressionEnabled(bool b)
+{
+ if (b) mCompression |= COMPRESS_ZIP;
+ else mCompression &= ~COMPRESS_ZIP;
+}
+
+
+void
+Archive::setGridCompression(std::ostream& os, const GridBase& grid) const
+{
+ // Start with the options that are enabled globally for this archive.
+ uint32_t compression = compressionFlags();
+
+ // Disable options that are inappropriate for the given grid.
+ switch (grid.getGridClass()) {
+ case GRID_LEVEL_SET:
+ case GRID_FOG_VOLUME:
+ // Zip compression is not used on level sets or fog volumes.
+ compression = compression & ~COMPRESS_ZIP;
+ break;
+ default:
+ break;
+ }
+ io::setDataCompression(os, compression);
+
+ os.write(reinterpret_cast<const char*>(&compression), sizeof(uint32_t));
+}
+
+
+void
+Archive::readGridCompression(std::istream& is)
+{
+ if (getFormatVersion(is) >= OPENVDB_FILE_VERSION_NODE_MASK_COMPRESSION) {
+ uint32_t compression = COMPRESS_NONE;
+ is.read(reinterpret_cast<char*>(&compression), sizeof(uint32_t));
+ io::setDataCompression(is, compression);
+ }
+}
+
+
+////////////////////////////////////////
+
+
+bool
+getWriteGridStatsMetadata(std::ostream& os)
+{
+ return os.iword(sStreamState.writeGridStatsMetadata) != 0;
+}
+
+
+void
+Archive::setWriteGridStatsMetadata(std::ostream& os)
+{
+ os.iword(sStreamState.writeGridStatsMetadata) = mEnableGridStats;
+}
+
+
+////////////////////////////////////////
+
+
+uint32_t
+getGridClass(std::ios_base& strm)
+{
+ const uint32_t val = strm.iword(sStreamState.gridClass);
+ if (val >= NUM_GRID_CLASSES) return GRID_UNKNOWN;
+ return val;
+}
+
+
+void
+setGridClass(std::ios_base& strm, uint32_t cls)
+{
+ strm.iword(sStreamState.gridClass) = long(cls);
+}
+
+
+const void*
+getGridBackgroundValuePtr(std::ios_base& strm)
+{
+ return strm.pword(sStreamState.gridBackground);
+}
+
+
+void
+setGridBackgroundValuePtr(std::ios_base& strm, const void* background)
+{
+ strm.pword(sStreamState.gridBackground) = const_cast<void*>(background);
+}
+
+
+////////////////////////////////////////
+
+
+bool
+Archive::readHeader(std::istream& is)
+{
+ // 1) Read the magic number for VDB.
+ int64_t magic;
+ is.read(reinterpret_cast<char*>(&magic), sizeof(int64_t));
+
+ if (magic != OPENVDB_MAGIC) {
+ OPENVDB_THROW(IoError, "not a VDB file");
+ }
+
+ // 2) Read the file format version number.
+ is.read(reinterpret_cast<char*>(&mFileVersion), sizeof(uint32_t));
+ if (mFileVersion > OPENVDB_FILE_VERSION) {
+ OPENVDB_LOG_WARN("unsupported VDB file format (expected version "
+ << OPENVDB_FILE_VERSION << " or earlier, got version " << mFileVersion << ")");
+ } else if (mFileVersion < 211) {
+ // Versions prior to 211 stored separate major, minor and patch numbers.
+ uint32_t version;
+ is.read(reinterpret_cast<char*>(&version), sizeof(uint32_t));
+ mFileVersion = 100 * mFileVersion + 10 * version;
+ is.read(reinterpret_cast<char*>(&version), sizeof(uint32_t));
+ mFileVersion += version;
+ }
+
+ // 3) Read the library version numbers (not stored prior to file format version 211).
+ mLibraryVersion.first = mLibraryVersion.second = 0;
+ if (mFileVersion >= 211) {
+ uint32_t version;
+ is.read(reinterpret_cast<char*>(&version), sizeof(uint32_t));
+ mLibraryVersion.first = version; // major version
+ is.read(reinterpret_cast<char*>(&version), sizeof(uint32_t));
+ mLibraryVersion.second = version; // minor version
+ }
+
+ // 4) Read the flag indicating whether the stream supports partial reading.
+ // (Versions prior to 212 have no flag because they always supported partial reading.)
+ mInputHasGridOffsets = true;
+ if (mFileVersion >= 212) {
+ char hasGridOffsets;
+ is.read(&hasGridOffsets, sizeof(char));
+ mInputHasGridOffsets = hasGridOffsets;
+ }
+
+ // 5) Read the flag that indicates whether data is compressed.
+ // (From version 222 on, compression information is stored per grid.)
+ mCompression = DEFAULT_COMPRESSION_FLAGS;
+ if (mFileVersion >= OPENVDB_FILE_VERSION_SELECTIVE_COMPRESSION &&
+ mFileVersion < OPENVDB_FILE_VERSION_NODE_MASK_COMPRESSION)
+ {
+ char isCompressed;
+ is.read(&isCompressed, sizeof(char));
+ mCompression = (isCompressed != 0 ? COMPRESS_ZIP : COMPRESS_NONE);
+ }
+
+ // 6) Read the 16-byte (128-bit) uuid.
+ boost::uuids::uuid oldUuid = mUuid;
+ if (mFileVersion >= OPENVDB_FILE_VERSION_BOOST_UUID) {
+ // UUID is stored as an ASCII string.
+ is >> mUuid;
+ } else {
+ // Older versions stored the UUID as a byte string.
+ char uuidBytes[16];
+ is.read(uuidBytes, 16);
+ std::memcpy(&mUuid.data[0], uuidBytes, std::min<size_t>(16, mUuid.size()));
+ }
+ return oldUuid != mUuid; // true if UUID in input stream differs from old UUID
+}
+
+
+void
+Archive::writeHeader(std::ostream& os, bool seekable) const
+{
+ using boost::uint32_t;
+ using boost::int64_t;
+
+ // 1) Write the magic number for VDB.
+ int64_t magic = OPENVDB_MAGIC;
+ os.write(reinterpret_cast<char*>(&magic), sizeof(int64_t));
+
+ // 2) Write the file format version number.
+ uint32_t version = OPENVDB_FILE_VERSION;
+ os.write(reinterpret_cast<char*>(&version), sizeof(uint32_t));
+
+ // 3) Write the library version numbers.
+ version = OPENVDB_LIBRARY_MAJOR_VERSION;
+ os.write(reinterpret_cast<char*>(&version), sizeof(uint32_t));
+ version = OPENVDB_LIBRARY_MINOR_VERSION;
+ os.write(reinterpret_cast<char*>(&version), sizeof(uint32_t));
+
+ // 4) Write a flag indicating that this stream contains no grid offsets.
+ char hasGridOffsets = seekable;
+ os.write(&hasGridOffsets, sizeof(char));
+
+ // 5) Write a flag indicating that this stream contains compressed leaf data.
+ // (Omitted as of version 222)
+
+ // 6) Generate a new random 16-byte (128-bit) uuid and write it to the stream.
+ boost::mt19937 ran;
+ ran.seed(time(NULL));
+ boost::uuids::basic_random_generator<boost::mt19937> gen(&ran);
+ mUuid = gen(); // mUuid is mutable
+ os << mUuid;
+}
+
+
+////////////////////////////////////////
+
+
+int32_t
+Archive::readGridCount(std::istream& is)
+{
+ int32_t gridCount = 0;
+ is.read(reinterpret_cast<char*>(&gridCount), sizeof(int32_t));
+ return gridCount;
+}
+
+
+////////////////////////////////////////
+
+
+void
+Archive::connectInstance(const GridDescriptor& gd, const NamedGridMap& grids) const
+{
+ if (!gd.isInstance() || grids.empty()) return;
+
+ NamedGridMap::const_iterator it = grids.find(gd.uniqueName());
+ if (it == grids.end()) return;
+ GridBase::Ptr grid = it->second;
+ if (!grid) return;
+
+ it = grids.find(gd.instanceParentName());
+ if (it != grids.end()) {
+ GridBase::Ptr parent = it->second;
+ if (mEnableInstancing) {
+ // Share the instance parent's tree.
+ grid->setTree(parent->baseTreePtr());
+ } else {
+ // Copy the instance parent's tree.
+ grid->setTree(parent->baseTree().copy());
+ }
+ } else {
+ OPENVDB_THROW(KeyError, "missing instance parent \""
+ << GridDescriptor::nameAsString(gd.instanceParentName())
+ << "\" for grid " << GridDescriptor::nameAsString(gd.uniqueName()));
+ }
+}
+
+
+////////////////////////////////////////
+
+
+void
+Archive::readGrid(GridBase::Ptr grid, const GridDescriptor& gd, std::istream& is)
+{
+ // Read the compression settings for this grid and tag the stream with them
+ // so that downstream functions can reference them.
+ readGridCompression(is);
+
+ io::setGridClass(is, GRID_UNKNOWN);
+ io::setGridBackgroundValuePtr(is, NULL);
+
+ grid->readMeta(is);
+
+ // Add a description of the compression settings to the grid as metadata.
+ /// @todo Would this be useful?
+ //const uint32_t compression = getDataCompression(is);
+ //grid->insertMeta(GridBase::META_FILE_COMPRESSION,
+ // StringMetadata(compressionToString(compression)));
+
+ const GridClass gridClass = grid->getGridClass();
+ io::setGridClass(is, gridClass);
+
+ if (getFormatVersion(is) >= OPENVDB_FILE_VERSION_GRID_INSTANCING) {
+ grid->readTransform(is);
+ if (!gd.isInstance()) {
+ grid->readTopology(is);
+ grid->readBuffers(is);
+ }
+ } else {
+ grid->readTopology(is);
+ grid->readTransform(is);
+ grid->readBuffers(is);
+ }
+ if (getFormatVersion(is) < OPENVDB_FILE_VERSION_NO_GRIDMAP) {
+ // Older versions of the library didn't store grid names as metadata,
+ // so when reading older files, copy the grid name from the descriptor
+ // to the grid's metadata.
+ if (grid->getName().empty()) {
+ grid->setName(gd.gridName());
+ }
+ }
+}
+
+
+void
+Archive::write(std::ostream& os, const GridPtrVec& grids, bool seekable,
+ const MetaMap& metadata) const
+{
+ this->write(os, GridCPtrVec(grids.begin(), grids.end()), seekable, metadata);
+}
+
+
+void
+Archive::write(std::ostream& os, const GridCPtrVec& grids, bool seekable,
+ const MetaMap& metadata) const
+{
+ // Set stream flags so that downstream functions can reference them.
+ io::setDataCompression(os, compressionFlags());
+ os.iword(sStreamState.writeGridStatsMetadata) = isGridStatsMetadataEnabled();
+
+ this->writeHeader(os, seekable);
+
+ metadata.writeMeta(os);
+
+ // Write the number of non-null grids.
+ int32_t gridCount = 0;
+ for (GridCPtrVecCIter i = grids.begin(), e = grids.end(); i != e; ++i) {
+ if (*i) ++gridCount;
+ }
+ os.write(reinterpret_cast<char*>(&gridCount), sizeof(int32_t));
+
+ typedef std::map<const TreeBase*, GridDescriptor> TreeMap;
+ typedef TreeMap::iterator TreeMapIter;
+ TreeMap treeMap;
+
+ std::set<std::string> uniqueNames;
+
+ // Write out the non-null grids.
+ for (GridCPtrVecCIter i = grids.begin(), e = grids.end(); i != e; ++i) {
+ if (const GridBase::ConstPtr& grid = *i) {
+
+ // Ensure that the grid's descriptor has a unique grid name.
+ std::string name = grid->getName();
+ for (int n = 1; uniqueNames.find(name) != uniqueNames.end(); ++n) {
+ name = GridDescriptor::addSuffix(grid->getName(), n);
+ }
+ uniqueNames.insert(name);
+
+ // Create a grid descriptor.
+ GridDescriptor gd(name, grid->type(), grid->saveFloatAsHalf());
+
+ // Check if this grid's tree is shared with a grid that has already been written.
+ const TreeBase* treePtr = &(grid->baseTree());
+ TreeMapIter mapIter = treeMap.find(treePtr);
+
+ bool isInstance = ((mapIter != treeMap.end())
+ && (mapIter->second.saveFloatAsHalf() == gd.saveFloatAsHalf()));
+
+ if (mEnableInstancing && isInstance) {
+ // This grid's tree is shared with another grid that has already been written.
+ // Get the name of the other grid.
+ gd.setInstanceParentName(mapIter->second.uniqueName());
+ // Write out this grid's descriptor and metadata, but not its tree.
+ writeGridInstance(gd, grid, os, seekable);
+
+ OPENVDB_LOG_DEBUG_RUNTIME("io::Archive::write(): "
+ << GridDescriptor::nameAsString(gd.uniqueName())
+ << " (" << std::hex << treePtr << std::dec << ")"
+ << " is an instance of "
+ << GridDescriptor::nameAsString(gd.instanceParentName()));
+ } else {
+ // Write out the grid descriptor and its associated grid.
+ writeGrid(gd, grid, os, seekable);
+ // Record the grid's tree pointer so that the tree doesn't get written
+ // more than once.
+ treeMap[treePtr] = gd;
+ }
+ }
+
+ // Some compression options (e.g., mask compression) are set per grid.
+ // Restore the original settings before writing the next grid.
+ io::setDataCompression(os, compressionFlags());
+ }
+}
+
+
+void
+Archive::writeGrid(GridDescriptor& gd, GridBase::ConstPtr grid,
+ std::ostream& os, bool seekable) const
+{
+ // Write out the Descriptor's header information (grid name and type)
+ gd.writeHeader(os);
+
+ // Save the curent stream position as postion to where the offsets for
+ // this GridDescriptor will be written to.
+ int64_t offsetPos = (seekable ? int64_t(os.tellp()) : 0);
+
+ // Write out the offset information. At this point it will be incorrect.
+ // But we need to write it out to move the stream head forward.
+ gd.writeStreamPos(os);
+
+ // Now we know the starting grid storage position.
+ if (seekable) gd.setGridPos(os.tellp());
+
+ // Save the compression settings for this grid.
+ setGridCompression(os, *grid);
+
+ // Save the grid's metadata and transform.
+ if (!getWriteGridStatsMetadata(os)) {
+ grid->writeMeta(os);
+ } else {
+ // Compute and add grid statistics metadata.
+ GridBase::Ptr copyOfGrid = grid->copyGrid(); // shallow copy
+ copyOfGrid->addStatsMetadata();
+ copyOfGrid->writeMeta(os);
+ }
+ grid->writeTransform(os);
+
+ // Save the grid's structure.
+ grid->writeTopology(os);
+
+ // Now we know the grid block storage position.
+ if (seekable) gd.setBlockPos(os.tellp());
+
+ // Save out the data blocks of the grid.
+ grid->writeBuffers(os);
+
+ // Now we know the end position of this grid.
+ if (seekable) gd.setEndPos(os.tellp());
+
+ if (seekable) {
+ // Now, go back to where the Descriptor's offset information is written
+ // and write the offsets again.
+ os.seekp(offsetPos, std::ios_base::beg);
+ gd.writeStreamPos(os);
+
+ // Now seek back to the end.
+ gd.seekToEnd(os);
+ }
+}
+
+
+void
+Archive::writeGridInstance(GridDescriptor& gd, GridBase::ConstPtr grid,
+ std::ostream& os, bool seekable) const
+{
+ // Write out the Descriptor's header information (grid name, type
+ // and instance parent name).
+ gd.writeHeader(os);
+
+ // Save the curent stream position as postion to where the offsets for
+ // this GridDescriptor will be written to.
+ int64_t offsetPos = (seekable ? int64_t(os.tellp()) : 0);
+
+ // Write out the offset information. At this point it will be incorrect.
+ // But we need to write it out to move the stream head forward.
+ gd.writeStreamPos(os);
+
+ // Now we know the starting grid storage position.
+ if (seekable) gd.setGridPos(os.tellp());
+
+ // Save the compression settings for this grid.
+ setGridCompression(os, *grid);
+
+ // Save the grid's metadata and transform.
+ grid->writeMeta(os);
+ grid->writeTransform(os);
+
+ // Now we know the end position of this grid.
+ if (seekable) gd.setEndPos(os.tellp());
+
+ if (seekable) {
+ // Now, go back to where the Descriptor's offset information is written
+ // and write the offsets again.
+ os.seekp(offsetPos, std::ios_base::beg);
+ gd.writeStreamPos(os);
+
+ // Now seek back to the end.
+ gd.seekToEnd(os);
+ }
+}
+
+} // namespace io
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/io/Archive.h b/extern/openvdb/internal/openvdb/io/Archive.h
new file mode 100644
index 00000000000..ecfc65b6df0
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/io/Archive.h
@@ -0,0 +1,266 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+
+#ifndef OPENVDB_IO_ARCHIVE_HAS_BEEN_INCLUDED
+#define OPENVDB_IO_ARCHIVE_HAS_BEEN_INCLUDED
+
+#include <openvdb/Platform.h>
+#include <iosfwd>
+#include <map>
+#include <string>
+#include <boost/uuid/uuid.hpp>
+#include <boost/cstdint.hpp>
+#include <openvdb/Grid.h>
+#include <openvdb/metadata/MetaMap.h>
+#include <openvdb/version.h> // for VersionId
+#include "Compression.h" // for COMPRESS_ZIP, etc.
+
+
+class TestFile;
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace io {
+
+class GridDescriptor;
+
+
+/// Return the file format version number associated with the given input stream.
+/// @sa File::setFormatVersion()
+OPENVDB_API uint32_t getFormatVersion(std::istream&);
+
+/// Return the library version number associated with the given input stream.
+/// @sa File::setLibraryVersion()
+OPENVDB_API VersionId getLibraryVersion(std::istream&);
+
+/// Return a string of the form "<major>.<minor>/<format>", giving the library
+/// and file format version numbers associated with the given input stream.
+OPENVDB_API std::string getVersion(std::istream&);
+
+/// Associate the current file format and library version numbers with the given input stream.
+OPENVDB_API void setCurrentVersion(std::istream&);
+
+/// @brief Associate specific file format and library version numbers with the given stream.
+/// @details This is typically called immediately after reading a header that contains
+/// the version numbers. Data read subsequently can then be interpreted appropriately.
+OPENVDB_API void setVersion(std::ios_base&, const VersionId& libraryVersion, uint32_t fileVersion);
+
+/// Return @c true if grid statistics (active voxel count and bounding box, etc.)
+/// should be computed and stored as grid metadata on output to the given stream.
+OPENVDB_API bool getWriteGridStatsMetadata(std::ostream&);
+
+/// @brief Return a bitwise OR of compression option flags (COMPRESS_ZIP,
+/// COMPRESS_ACTIVE_MASK, etc.) specifying whether and how input data is compressed
+/// or output data should be compressed.
+OPENVDB_API uint32_t getDataCompression(std::ios_base&);
+
+/// @brief Associate with the given stream a bitwise OR of compression option flags
+/// (COMPRESS_ZIP, COMPRESS_ACTIVE_MASK, etc.) specifying whether and how input data
+/// is compressed or output data should be compressed.
+OPENVDB_API void setDataCompression(std::ios_base&, uint32_t compressionFlags);
+
+/// @brief Return the class (GRID_LEVEL_SET, GRID_UNKNOWN, etc.) of the grid
+/// currently being read from or written to the given stream.
+OPENVDB_API uint32_t getGridClass(std::ios_base&);
+
+/// @brief Associate with the given stream the class (GRID_LEVEL_SET, GRID_UNKNOWN, etc.)
+/// of the grid currently being read or written.
+OPENVDB_API void setGridClass(std::ios_base&, uint32_t);
+
+/// @brief Return a pointer to the background value of the grid
+/// currently being read from or written to the given stream.
+OPENVDB_API const void* getGridBackgroundValuePtr(std::ios_base&);
+
+/// @brief Specify (a pointer to) the background value of the grid
+/// currently being read from or written to the given stream.
+/// @note The pointer must remain valid until the entire grid has been read or written.
+OPENVDB_API void setGridBackgroundValuePtr(std::ios_base&, const void* background);
+
+
+////////////////////////////////////////
+
+
+/// Grid serializer/unserializer
+class OPENVDB_API Archive
+{
+public:
+ static const uint32_t DEFAULT_COMPRESSION_FLAGS;
+
+ Archive();
+ virtual ~Archive();
+
+ /// @brief Return the UUID that was most recently written (or read,
+ /// if no UUID has been written yet).
+ std::string getUniqueTag() const;
+ /// @brief Return @c true if the given UUID matches this archive's UUID.
+ bool isIdentical(const std::string& uuidStr) const;
+
+ /// @brief Return the file format version number of the input stream.
+ uint32_t fileVersion() const { return mFileVersion; }
+ /// @brief Return the (major, minor) version number of the library that was
+ /// used to write the input stream.
+ VersionId libraryVersion() const { return mLibraryVersion; }
+ /// @brief Return a string of the form "<major>.<minor>/<format>", giving the
+ /// library and file format version numbers associated with the input stream.
+ std::string version() const;
+
+ /// @brief Return @c true if trees shared by multiple grids are written out
+ /// only once, @c false if they are written out once per grid.
+ bool isInstancingEnabled() const { return mEnableInstancing; }
+ /// @brief Specify whether trees shared by multiple grids should be
+ /// written out only once (@c true) or once per grid (@c false).
+ /// @note Instancing is enabled by default.
+ void setInstancingEnabled(bool b) { mEnableInstancing = b; }
+
+ /// Return @c true if the data stream is Zip-compressed.
+ bool isCompressionEnabled() const;
+ /// @brief Specify whether the data stream should be Zip-compressed.
+ /// @details Enabling Zip compression makes I/O slower, but saves space.
+ /// Disable it only if raw I/O speed is a concern.
+ void setCompressionEnabled(bool);
+
+ /// Return a bit mask specifying compression options for the data stream.
+ uint32_t compressionFlags() const { return mCompression; }
+ /// @brief Specify whether and how the data stream should be compressed.
+ /// [Mainly for internal use]
+ /// @param c bitwise OR (e.g., COMPRESS_ZIP | COMPRESS_ACTIVE_MASK) of
+ /// compression option flags (see Compression.h for the available flags)
+ /// @note Not all combinations of compression options are supported.
+ void setCompressionFlags(uint32_t c) { mCompression = c; }
+
+ /// @brief Return @c true if grid statistics (active voxel count and
+ /// bounding box, etc.) are computed and written as grid metadata.
+ bool isGridStatsMetadataEnabled() const { return mEnableGridStats; }
+ /// @brief Specify whether grid statistics (active voxel count and
+ /// bounding box, etc.) should be computed and written as grid metadata.
+ void setGridStatsMetadataEnabled(bool b) { mEnableGridStats = b; }
+
+protected:
+ /// @brief Return @c true if the input stream contains grid offsets
+ /// that allow for random access or partial reading.
+ bool inputHasGridOffsets() const { return mInputHasGridOffsets; }
+ void setInputHasGridOffsets(bool b) { mInputHasGridOffsets = b; }
+
+ /// @brief Tag the given input stream with the input file format version number.
+ ///
+ /// The tag can be retrieved with getFormatVersion().
+ /// @sa getFormatVersion()
+ void setFormatVersion(std::istream&);
+
+ /// @brief Tag the given input stream with the version number of
+ /// the library with which the input stream was created.
+ ///
+ /// The tag can be retrieved with getLibraryVersion().
+ /// @sa getLibraryVersion()
+ void setLibraryVersion(std::istream&);
+
+ /// @brief Tag the given input stream with flags indicating whether
+ /// the input stream contains compressed data and how it is compressed.
+ void setDataCompression(std::istream&);
+
+ /// @brief Tag an output stream with flags specifying only those
+ /// compression options that are applicable to the given grid.
+ void setGridCompression(std::ostream&, const GridBase&) const;
+ /// @brief Read in the compression flags for a grid and
+ /// tag the given input stream with those flags.
+ static void readGridCompression(std::istream&);
+
+ /// @brief Tag the given output stream with a flag indicating whether
+ /// to compute and write grid statistics metadata.
+ void setWriteGridStatsMetadata(std::ostream&);
+
+ /// Read in and return the number of grids on the input stream.
+ static int readGridCount(std::istream&);
+
+ /// Populate the given grid from the input stream.
+ static void readGrid(GridBase::Ptr, const GridDescriptor&, std::istream&);
+
+ typedef std::map<Name /*uniqueName*/, GridBase::Ptr> NamedGridMap;
+
+ /// @brief If the grid represented by the given grid descriptor
+ /// is an instance, connect it with its instance parent.
+ void connectInstance(const GridDescriptor&, const NamedGridMap&) const;
+
+ /// Write the given grid descriptor and grid to an output stream
+ /// and update the GridDescriptor offsets.
+ /// @param seekable if true, the output stream supports seek operations
+ void writeGrid(GridDescriptor&, GridBase::ConstPtr, std::ostream&, bool seekable) const;
+ /// Write the given grid descriptor and grid metadata to an output stream
+ /// and update the GridDescriptor offsets, but don't write the grid's tree,
+ /// since it is shared with another grid.
+ /// @param seekable if true, the output stream supports seek operations
+ void writeGridInstance(GridDescriptor&, GridBase::ConstPtr,
+ std::ostream&, bool seekable) const;
+
+ /// @brief Read the magic number, version numbers, UUID, etc. from the given input stream.
+ /// @return @c true if the input UUID differs from the previously-read UUID.
+ bool readHeader(std::istream&);
+ /// @brief Write the magic number, version numbers, UUID, etc. to the given output stream.
+ /// @param seekable if true, the output stream supports seek operations
+ /// @todo This method should not be const since it actually redefines the UUID!
+ void writeHeader(std::ostream&, bool seekable) const;
+
+ //@{
+ /// Write the given grids to an output stream.
+ void write(std::ostream&, const GridPtrVec&, bool seekable, const MetaMap& = MetaMap()) const;
+ void write(std::ostream&, const GridCPtrVec&, bool seekable, const MetaMap& = MetaMap()) const;
+ //@}
+
+private:
+ friend class ::TestFile;
+
+ /// The version of the file that was read
+ uint32_t mFileVersion;
+ /// The version of the library that was used to create the file that was read
+ VersionId mLibraryVersion;
+ /// 16-byte (128-bit) UUID
+ mutable boost::uuids::uuid mUuid;// needs to mutable since writeHeader is const!
+ /// Flag indicating whether the input stream contains grid offsets
+ /// and therefore supports partial reading
+ bool mInputHasGridOffsets;
+ /// Flag indicating whether a tree shared by multiple grids should be
+ /// written out only once (true) or once per grid (false)
+ bool mEnableInstancing;
+ /// Flags indicating whether and how the data stream is compressed
+ uint32_t mCompression;
+ /// Flag indicating whether grid statistics metadata should be written
+ bool mEnableGridStats;
+}; // class Archive
+
+} // namespace io
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_IO_ARCHIVE_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/io/Compression.cc b/extern/openvdb/internal/openvdb/io/Compression.cc
new file mode 100644
index 00000000000..85933c5039c
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/io/Compression.cc
@@ -0,0 +1,145 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+
+#define ZLIB_WINAPI
+
+#include "Compression.h"
+
+#include <boost/algorithm/string/join.hpp>
+#include <boost/shared_array.hpp>
+#include <zlib.h>
+#include <openvdb/Exceptions.h>
+#include <openvdb/util/logging.h>
+
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace io {
+
+std::string
+compressionToString(uint32_t flags)
+{
+ if (flags == COMPRESS_NONE) return "none";
+
+ std::vector<std::string> words;
+ if (flags & COMPRESS_ZIP) words.push_back("zipped");
+ if (flags & COMPRESS_ACTIVE_MASK) {
+ words.push_back("active values");
+ }
+ return boost::join(words, " ");
+}
+
+
+////////////////////////////////////////
+
+
+namespace {
+const int ZIP_COMPRESSION_LEVEL = Z_DEFAULT_COMPRESSION; ///< @todo use Z_BEST_SPEED?
+}
+
+void
+zipToStream(std::ostream& os, const char* data, size_t numBytes)
+{
+ // Get an upper bound on the size of the compressed data.
+ uLongf numZippedBytes = compressBound(numBytes);
+ // Compress the data.
+ boost::shared_array<Bytef> zippedData(new Bytef[numZippedBytes]);
+ int status = compress2(
+ /*dest=*/zippedData.get(), &numZippedBytes,
+ /*src=*/reinterpret_cast<const Bytef*>(data), numBytes,
+ /*level=*/ZIP_COMPRESSION_LEVEL);
+ if (status != Z_OK) {
+ std::string errDescr;
+ if (const char* s = zError(status)) errDescr = s;
+ if (!errDescr.empty()) errDescr = " (" + errDescr + ")";
+ OPENVDB_LOG_DEBUG("zlib compress2() returned error code " << status << errDescr);
+ }
+ if (status == Z_OK && numZippedBytes < numBytes) {
+ // Write the size of the compressed data.
+ Int64 outZippedBytes = numZippedBytes;
+ os.write(reinterpret_cast<char*>(&outZippedBytes), 8);
+ // Write the compressed data.
+ os.write(reinterpret_cast<char*>(zippedData.get()), outZippedBytes);
+ } else {
+ // Write the size of the uncompressed data.
+ Int64 negBytes = -numBytes;
+ os.write(reinterpret_cast<char*>(&negBytes), 8);
+ // Write the uncompressed data.
+ os.write(reinterpret_cast<const char*>(data), numBytes);
+ }
+}
+
+
+void
+unzipFromStream(std::istream& is, char* data, size_t numBytes)
+{
+ // Read the size of the compressed data.
+ // A negative size indicates uncompressed data.
+ Int64 numZippedBytes;
+ is.read(reinterpret_cast<char*>(&numZippedBytes), 8);
+
+ if (numZippedBytes <= 0) {
+ // Read the uncompressed data.
+ is.read(data, -numZippedBytes);
+ if (size_t(-numZippedBytes) != numBytes) {
+ OPENVDB_THROW(RuntimeError, "Expected to read a " << numBytes
+ << "-byte chunk, got a " << -numZippedBytes << "-byte chunk");
+ }
+ } else {
+ // Read the compressed data.
+ boost::shared_array<Bytef> zippedData(new Bytef[numZippedBytes]);
+ is.read(reinterpret_cast<char*>(zippedData.get()), numZippedBytes);
+ // Uncompress the data.
+ uLongf numUnzippedBytes = numBytes;
+ int status = uncompress(
+ /*dest=*/reinterpret_cast<Bytef*>(data), &numUnzippedBytes,
+ /*src=*/zippedData.get(), static_cast<uLongf>(numZippedBytes));
+ if (status != Z_OK) {
+ std::string errDescr;
+ if (const char* s = zError(status)) errDescr = s;
+ if (!errDescr.empty()) errDescr = " (" + errDescr + ")";
+ OPENVDB_LOG_DEBUG("zlib uncompress() returned error code " << status << errDescr);
+ }
+ if (numUnzippedBytes != numBytes) {
+ OPENVDB_THROW(RuntimeError, "Expected to decompress " << numBytes
+ << " byte" << (numBytes == 1 ? "" : "s") << ", got "
+ << numZippedBytes << " byte" << (numZippedBytes == 1 ? "" : "s"));
+ }
+ }
+}
+
+} // namespace io
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/io/Compression.h b/extern/openvdb/internal/openvdb/io/Compression.h
new file mode 100644
index 00000000000..81ab2d64ea2
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/io/Compression.h
@@ -0,0 +1,566 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+
+#ifndef OPENVDB_IO_COMPRESSION_HAS_BEEN_INCLUDED
+#define OPENVDB_IO_COMPRESSION_HAS_BEEN_INCLUDED
+
+#include <openvdb/Types.h>
+#include <openvdb/math/Math.h> // for negative()
+#include <boost/scoped_array.hpp>
+#include <algorithm>
+#include <iostream>
+#include <string>
+#include <vector>
+
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace io {
+
+/// @brief OR-able bit flags for compression options on input and output streams
+/// @details
+/// <dl>
+/// <dt><tt>COMPRESS_NONE</tt>
+/// <dd>On write, don't compress data.<br>
+/// On read, the input stream contains uncompressed data.
+///
+/// <dt><tt>COMPRESS_ZIP</tt>
+/// <dd>When writing grids other than level sets or fog volumes, apply ZIP compression
+/// to internal and leaf node value buffers.<br>
+/// On read of grids other than level sets or fog volumes, the value buffers
+/// of internal and leaf nodes are ZIP-compressed.
+///
+/// <dt><tt>COMPRESS_ACTIVE_MASK</tt>
+/// <dd>When writing a grid of any class, don't output a node's inactive values
+/// if it has two or fewer distinct values. Instead, output minimal information
+/// to permit the lossless reconstruction of inactive values.<br>
+/// On read, nodes might have been stored without inactive values.
+/// Where necessary, reconstruct inactive values from available information.
+/// </dl>
+enum {
+ COMPRESS_NONE = 0,
+ COMPRESS_ZIP = 0x1,
+ COMPRESS_ACTIVE_MASK = 0x2
+};
+
+/// Return a string describing the given compression flags.
+OPENVDB_API std::string compressionToString(uint32_t flags);
+
+
+////////////////////////////////////////
+
+
+/// @internal Per-node indicator byte that specifies what additional metadata
+/// is stored to permit reconstruction of inactive values
+enum {
+ /*0*/ NO_MASK_OR_INACTIVE_VALS, // no inactive vals, or all inactive vals are +background
+ /*1*/ NO_MASK_AND_MINUS_BG, // all inactive vals are -background
+ /*2*/ NO_MASK_AND_ONE_INACTIVE_VAL, // all inactive vals have the same non-background val
+ /*3*/ MASK_AND_NO_INACTIVE_VALS, // mask selects between -background and +background
+ /*4*/ MASK_AND_ONE_INACTIVE_VAL, // mask selects between backgd and one other inactive val
+ /*5*/ MASK_AND_TWO_INACTIVE_VALS // mask selects between two non-background inactive vals
+};
+
+
+////////////////////////////////////////
+
+
+/// @brief RealToHalf and its specializations define a mapping from
+/// floating-point data types to analogous half float types.
+template<typename T>
+struct RealToHalf {
+ enum { isReal = false }; // unless otherwise specified, type T is not a floating-point type
+ typedef T HalfT; // type T's half float analogue is T itself
+};
+template<> struct RealToHalf<float> { enum { isReal = true }; typedef half HalfT; };
+template<> struct RealToHalf<double> { enum { isReal = true }; typedef half HalfT; };
+template<> struct RealToHalf<Vec2s> { enum { isReal = true }; typedef Vec2H HalfT; };
+template<> struct RealToHalf<Vec2d> { enum { isReal = true }; typedef Vec2H HalfT; };
+template<> struct RealToHalf<Vec3s> { enum { isReal = true }; typedef Vec3H HalfT; };
+template<> struct RealToHalf<Vec3d> { enum { isReal = true }; typedef Vec3H HalfT; };
+
+
+/// Return the given value truncated to 16-bit float precision.
+template<typename T>
+inline T
+truncateRealToHalf(const T& val)
+{
+ return T(typename RealToHalf<T>::HalfT(val));
+}
+
+
+////////////////////////////////////////
+
+
+OPENVDB_API void zipToStream(std::ostream&, const char* data, size_t numBytes);
+OPENVDB_API void unzipFromStream(std::istream&, char* data, size_t numBytes);
+
+/// @brief Read data from a stream.
+/// @param is the input stream
+/// @param data the contiguous array of data to read in
+/// @param count the number of elements to read in
+/// @param compressed if @c true, assume the data is ZIP compressed and uncompress it
+/// This default implementation is instantiated only for types whose size
+/// can be determined by the sizeof() operator.
+template<typename T>
+inline void
+readData(std::istream& is, T* data, Index count, bool compressed)
+{
+ if (compressed) {
+ unzipFromStream(is, reinterpret_cast<char*>(data), sizeof(T) * count);
+ } else {
+ is.read(reinterpret_cast<char*>(data), sizeof(T) * count);
+ }
+}
+
+/// Specialization for std::string input
+template<>
+inline void
+readData<std::string>(std::istream& is, std::string* data, Index count, bool /*compressed*/)
+{
+ for (Index i = 0; i < count; ++i) {
+ size_t len = 0;
+ is >> len;
+ //data[i].resize(len);
+ //is.read(&(data[i][0]), len);
+
+ std::string buffer(len+1, ' ');
+ is.read(&buffer[0], len+1 );
+ data[i].assign(buffer, 0, len);
+ }
+}
+
+/// HalfReader wraps a static function, read(), that is analogous to readData(), above,
+/// except that it is partially specialized for floating-point types in order to promote
+/// 16-bit half float values to full float. A wrapper class is required because
+/// only classes, not functions, can be partially specialized.
+template<bool IsReal, typename T> struct HalfReader;
+/// Partial specialization for non-floating-point types (no half to float promotion)
+template<typename T>
+struct HalfReader</*IsReal=*/false, T> {
+ static inline void read(std::istream& is, T* data, Index count, bool compressed) {
+ readData(is, data, count, compressed);
+ }
+};
+/// Partial specialization for floating-point types
+template<typename T>
+struct HalfReader</*IsReal=*/true, T> {
+ typedef typename RealToHalf<T>::HalfT HalfT;
+ static inline void read(std::istream& is, T* data, Index count, bool compressed) {
+ if (count < 1) return;
+ std::vector<HalfT> halfData(count); // temp buffer into which to read half float values
+ readData<HalfT>(is, reinterpret_cast<HalfT*>(&halfData[0]), count, compressed);
+ // Copy half float values from the temporary buffer to the full float output array.
+ std::copy(halfData.begin(), halfData.end(), data);
+ }
+};
+
+
+/// Write data to a stream.
+/// @param os the output stream
+/// @param data the contiguous array of data to write
+/// @param count the number of elements to write out
+/// @param compress if @c true, apply ZIP compression to the data
+/// This default implementation is instantiated only for types whose size
+/// can be determined by the sizeof() operator.
+template<typename T>
+inline void
+writeData(std::ostream &os, const T *data, Index count, bool compress)
+{
+ if (compress) {
+ zipToStream(os, reinterpret_cast<const char*>(data), sizeof(T) * count);
+ } else {
+ os.write(reinterpret_cast<const char*>(data), sizeof(T) * count);
+ }
+}
+
+/// Specialization for std::string output
+/// @todo Add compression
+template<>
+inline void
+writeData<std::string>(std::ostream& os, const std::string* data, Index count, bool /*compress*/)
+{
+ for (Index i = 0; i < count; ++i) {
+ const size_t len = data[i].size();
+ os << len;
+ os.write(data[i].c_str(), len+1);
+ //os.write(&(data[i][0]), len );
+ }
+}
+
+/// HalfWriter wraps a static function, write(), that is analogous to writeData(), above,
+/// except that it is partially specialized for floating-point types in order to quantize
+/// floating-point values to 16-bit half float. A wrapper class is required because
+/// only classes, not functions, can be partially specialized.
+template<bool IsReal, typename T> struct HalfWriter;
+/// Partial specialization for non-floating-point types (no float to half quantization)
+template<typename T>
+struct HalfWriter</*IsReal=*/false, T> {
+ static inline void write(std::ostream& os, const T* data, Index count, bool compress) {
+ writeData(os, data, count, compress);
+ }
+};
+/// Partial specialization for floating-point types
+template<typename T>
+struct HalfWriter</*IsReal=*/true, T> {
+ typedef typename RealToHalf<T>::HalfT HalfT;
+ static inline void write(std::ostream& os, const T* data, Index count, bool compress) {
+ if (count < 1) return;
+ // Convert full float values to half float, then output the half float array.
+ std::vector<HalfT> halfData(count);
+ std::copy(data, data + count, halfData.begin());
+ writeData<HalfT>(os, reinterpret_cast<const HalfT*>(&halfData[0]), count, compress);
+ }
+};
+#ifdef _MSC_VER
+/// Specialization to avoid double to float warnings in MSVC
+template<>
+struct HalfWriter</*IsReal=*/true, double> {
+ typedef RealToHalf<double>::HalfT HalfT;
+ static inline void write(std::ostream& os, const double* data, Index count, bool compress) {
+ if (count < 1) return;
+ // Convert full float values to half float, then output the half float array.
+ std::vector<HalfT> halfData(count);
+ for (Index i = 0; i < count; ++i) halfData[i] = float(data[i]);
+ writeData<HalfT>(os, reinterpret_cast<const HalfT*>(&halfData[0]), count, compress);
+ }
+};
+#endif // _MSC_VER
+
+
+////////////////////////////////////////
+
+
+/// Populate the given buffer with @a destCount values of type @c ValueT
+/// read from the given stream, taking into account that the stream might
+/// have been compressed via one of several supported schemes.
+/// [Mainly for internal use]
+/// @param is a stream from which to read data (possibly compressed,
+/// depending on the stream's compression settings)
+/// @param destBuf a buffer into which to read values of type @c ValueT
+/// @param destCount the number of values to be stored in the buffer
+/// @param valueMask a bitmask (typically, a node's value mask) indicating
+/// which positions in the buffer correspond to active values
+/// @param fromHalf if true, read 16-bit half floats from the input stream
+/// and convert them to full floats
+template<typename ValueT, typename MaskT>
+inline void
+readCompressedValues(std::istream& is, ValueT* destBuf, Index destCount,
+ const MaskT& valueMask, bool fromHalf)
+{
+ // Get the stream's compression settings.
+ const uint32_t compression = getDataCompression(is);
+ const bool
+ zipped = compression & COMPRESS_ZIP,
+ maskCompressed = compression & COMPRESS_ACTIVE_MASK;
+
+ int8_t metadata = NO_MASK_OR_INACTIVE_VALS;
+ if (getFormatVersion(is) >= OPENVDB_FILE_VERSION_NODE_MASK_COMPRESSION) {
+ // Read the flag that specifies what, if any, additional metadata
+ // (selection mask and/or inactive value(s)) is saved.
+ is.read(reinterpret_cast<char*>(&metadata), /*bytes=*/1);
+ }
+
+ ValueT background = zeroVal<ValueT>();
+ if (const void* bgPtr = getGridBackgroundValuePtr(is)) {
+ background = *static_cast<const ValueT*>(bgPtr);
+ }
+ ValueT inactiveVal1 = background;
+ ValueT inactiveVal0 =
+ ((metadata == NO_MASK_OR_INACTIVE_VALS) ? background : negative(background));
+
+ if (metadata != NO_MASK_OR_INACTIVE_VALS &&
+ metadata != NO_MASK_AND_MINUS_BG &&
+ metadata != MASK_AND_NO_INACTIVE_VALS)
+ {
+ // Read one of at most two distinct inactive values.
+ is.read(reinterpret_cast<char*>(&inactiveVal0), sizeof(ValueT));
+ if (metadata == MASK_AND_TWO_INACTIVE_VALS) {
+ // Read the second of two distinct inactive values.
+ is.read(reinterpret_cast<char*>(&inactiveVal1), sizeof(ValueT));
+ }
+ }
+
+ MaskT selectionMask;
+ if (metadata != NO_MASK_OR_INACTIVE_VALS &&
+ metadata != NO_MASK_AND_MINUS_BG &&
+ metadata != NO_MASK_AND_ONE_INACTIVE_VAL)
+ {
+ // For use in mask compression (only), read the bitmask that selects
+ // between two distinct inactive values.
+ selectionMask.load(is);
+ }
+
+ ValueT* tempBuf = destBuf;
+ boost::scoped_array<ValueT> scopedTempBuf;
+
+ Index tempCount = destCount;
+ if (maskCompressed && getFormatVersion(is) >= OPENVDB_FILE_VERSION_NODE_MASK_COMPRESSION) {
+ tempCount = valueMask.countOn();
+ if (tempCount != destCount) {
+ // If this node has inactive voxels, allocate a temporary buffer
+ // into which to read just the active values.
+ scopedTempBuf.reset(new ValueT[tempCount]);
+ tempBuf = scopedTempBuf.get();
+ }
+ }
+
+ // Read in the buffer.
+ if (fromHalf) {
+ HalfReader<RealToHalf<ValueT>::isReal, ValueT>::read(is, tempBuf, tempCount, zipped);
+ } else {
+ readData<ValueT>(is, tempBuf, tempCount, zipped);
+ }
+
+ // If mask compression is enabled and the number of active values read into
+ // the temp buffer is smaller than the size of the destination buffer,
+ // then there are missing (inactive) values.
+ if (maskCompressed && tempCount != destCount) {
+ // Restore inactive values, using the background value and, if available,
+ // the inside/outside mask. (For fog volumes, the destination buffer is assumed
+ // to be initialized to background value zero, so inactive values can be ignored.)
+ for (Index destIdx = 0, tempIdx = 0; destIdx < MaskT::SIZE; ++destIdx) {
+ if (valueMask.isOn(destIdx)) {
+ // Copy a saved active value into this node's buffer.
+ destBuf[destIdx] = tempBuf[tempIdx];
+ ++tempIdx;
+ } else {
+ // Reconstruct an unsaved inactive value and copy it into this node's buffer.
+ destBuf[destIdx] = (selectionMask.isOn(destIdx) ? inactiveVal1 : inactiveVal0);
+ }
+ }
+ }
+}
+
+
+/// Write @a srcCount values of type @c ValueT to the given stream, optionally
+/// after compressing the values via one of several supported schemes.
+/// [Mainly for internal use]
+/// @param os a stream to which to write data (possibly compressed, depending
+/// on the stream's compression settings)
+/// @param srcBuf a buffer containing values of type @c ValueT to be written
+/// @param srcCount the number of values stored in the buffer
+/// @param valueMask a bitmask (typically, a node's value mask) indicating
+/// which positions in the buffer correspond to active values
+/// @param childMask a bitmask (typically, a node's child mask) indicating
+/// which positions in the buffer correspond to child node pointers
+/// @param toHalf if true, convert floating-point values to 16-bit half floats
+template<typename ValueT, typename MaskT>
+inline void
+writeCompressedValues(std::ostream& os, ValueT* srcBuf, Index srcCount,
+ const MaskT& valueMask, const MaskT& childMask, bool toHalf)
+{
+ struct Local {
+ // Comparison function for values
+ static inline bool eq(const ValueT& a, const ValueT& b) {
+ return math::isExactlyEqual(a, b);
+ }
+ };
+
+ // Get the stream's compression settings.
+ const uint32_t compress = getDataCompression(os);
+ const bool
+ zip = compress & COMPRESS_ZIP,
+ maskCompress = compress & COMPRESS_ACTIVE_MASK;
+
+ Index tempCount = srcCount;
+ ValueT* tempBuf = srcBuf;
+ boost::scoped_array<ValueT> scopedTempBuf;
+
+ int8_t metadata = NO_MASK_OR_INACTIVE_VALS;
+
+ if (!maskCompress) {
+ os.write(reinterpret_cast<const char*>(&metadata), /*bytes=*/1);
+ } else {
+ // A valid level set's inactive values are either +background (outside)
+ // or -background (inside), and a fog volume's inactive values are all zero.
+ // Rather than write out all of these values, we can store just the active values
+ // (given that the value mask specifies their positions) and, if necessary,
+ // an inside/outside bitmask.
+
+ const ValueT zero = zeroVal<ValueT>();
+ ValueT background = zero;
+ if (const void* bgPtr = getGridBackgroundValuePtr(os)) {
+ background = *static_cast<const ValueT*>(bgPtr);
+ }
+
+ /// @todo Consider all values, not just inactive values?
+ ValueT inactiveVal[2] = { background, background };
+ int numUniqueInactiveVals = 0;
+ for (typename MaskT::OffIterator it = valueMask.beginOff();
+ numUniqueInactiveVals < 3 && it; ++it)
+ {
+ const Index32 idx = it.pos();
+
+ // Skip inactive values that are actually child node pointers.
+ if (childMask.isOn(idx)) continue;
+
+ const ValueT& val = srcBuf[idx];
+ const bool unique = !(
+ (numUniqueInactiveVals > 0 && Local::eq(val, inactiveVal[0])) ||
+ (numUniqueInactiveVals > 1 && Local::eq(val, inactiveVal[1]))
+ );
+ if (unique) {
+ if (numUniqueInactiveVals < 2) inactiveVal[numUniqueInactiveVals] = val;
+ ++numUniqueInactiveVals;
+ }
+ }
+
+ metadata = NO_MASK_OR_INACTIVE_VALS;
+
+ if (numUniqueInactiveVals == 1) {
+ if (!Local::eq(inactiveVal[0], background)) {
+ if (Local::eq(inactiveVal[0], negative(background))) {
+ metadata = NO_MASK_AND_MINUS_BG;
+ } else {
+ metadata = NO_MASK_AND_ONE_INACTIVE_VAL;
+ }
+ }
+ } else if (numUniqueInactiveVals == 2) {
+ metadata = NO_MASK_OR_INACTIVE_VALS;
+ if (!Local::eq(inactiveVal[0], background) && !Local::eq(inactiveVal[1], background)) {
+ // If neither inactive value is equal to the background, both values
+ // need to be saved, along with a mask that selects between them.
+ metadata = MASK_AND_TWO_INACTIVE_VALS;
+
+ } else if (Local::eq(inactiveVal[1], background)) {
+ if (Local::eq(inactiveVal[0], negative(background))) {
+ // If the second inactive value is equal to the background and
+ // the first is equal to -background, neither value needs to be saved,
+ // but save a mask that selects between -background and +background.
+ metadata = MASK_AND_NO_INACTIVE_VALS;
+ } else {
+ // If the second inactive value is equal to the background, only
+ // the first value needs to be saved, along with a mask that selects
+ // between it and the background.
+ metadata = MASK_AND_ONE_INACTIVE_VAL;
+ }
+ } else if (Local::eq(inactiveVal[0], background)) {
+ if (Local::eq(inactiveVal[1], negative(background))) {
+ // If the first inactive value is equal to the background and
+ // the second is equal to -background, neither value needs to be saved,
+ // but save a mask that selects between -background and +background.
+ metadata = MASK_AND_NO_INACTIVE_VALS;
+ std::swap(inactiveVal[0], inactiveVal[1]);
+ } else {
+ // If the first inactive value is equal to the background, swap it
+ // with the second value and save only that value, along with a mask
+ // that selects between it and the background.
+ std::swap(inactiveVal[0], inactiveVal[1]);
+ metadata = MASK_AND_ONE_INACTIVE_VAL;
+ }
+ }
+ }
+
+ os.write(reinterpret_cast<const char*>(&metadata), /*bytes=*/1);
+
+ if (metadata != NO_MASK_OR_INACTIVE_VALS &&
+ metadata != NO_MASK_AND_MINUS_BG &&
+ metadata != MASK_AND_NO_INACTIVE_VALS)
+ {
+ if (!toHalf) {
+ // Write one of at most two distinct inactive values.
+ os.write(reinterpret_cast<const char*>(&inactiveVal[0]), sizeof(ValueT));
+ if (metadata == MASK_AND_TWO_INACTIVE_VALS) {
+ // Write the second of two distinct inactive values.
+ os.write(reinterpret_cast<const char*>(&inactiveVal[1]), sizeof(ValueT));
+ }
+ } else {
+ // Write one of at most two distinct inactive values.
+ ValueT truncatedVal = truncateRealToHalf(inactiveVal[0]);
+ os.write(reinterpret_cast<const char*>(&truncatedVal), sizeof(ValueT));
+ if (metadata == MASK_AND_TWO_INACTIVE_VALS) {
+ // Write the second of two distinct inactive values.
+ truncatedVal = truncateRealToHalf(inactiveVal[1]);
+ os.write(reinterpret_cast<const char*>(&truncatedVal), sizeof(ValueT));
+ }
+ }
+ }
+
+ if (metadata == NO_MASK_OR_INACTIVE_VALS && numUniqueInactiveVals > 2) {
+ // If there are more than two unique inactive values, the entire input buffer
+ // needs to be saved (both active and inactive values).
+ /// @todo Save the selection mask as long as most of the inactive values
+ /// are one of two values?
+ } else {
+ // Create a new array to hold just the active values.
+ scopedTempBuf.reset(new ValueT[srcCount]);
+ tempBuf = scopedTempBuf.get();
+
+ if (metadata == NO_MASK_OR_INACTIVE_VALS ||
+ metadata == NO_MASK_AND_MINUS_BG ||
+ metadata == NO_MASK_AND_ONE_INACTIVE_VAL)
+ {
+ // Copy active values to the contiguous array.
+ tempCount = 0;
+ for (typename MaskT::OnIterator it = valueMask.beginOn(); it; ++it, ++tempCount) {
+ tempBuf[tempCount] = srcBuf[it.pos()];
+ }
+ } else {
+ // Copy active values to a new, contiguous array and populate a bitmask
+ // that selects between two distinct inactive values.
+ MaskT selectionMask;
+ tempCount = 0;
+ for (Index srcIdx = 0; srcIdx < srcCount; ++srcIdx) {
+ if (valueMask.isOn(srcIdx)) { // active value
+ tempBuf[tempCount] = srcBuf[srcIdx];
+ ++tempCount;
+ } else { // inactive value
+ if (Local::eq(srcBuf[srcIdx], inactiveVal[1])) {
+ selectionMask.setOn(srcIdx); // inactive value 1
+ } // else inactive value 0
+ }
+ }
+ assert(tempCount == valueMask.countOn());
+
+ // Write out the mask that selects between two inactive values.
+ selectionMask.save(os);
+ }
+ }
+ }
+
+ // Write out the buffer.
+ if (toHalf) {
+ HalfWriter<RealToHalf<ValueT>::isReal, ValueT>::write(os, tempBuf, tempCount, zip);
+ } else {
+ writeData(os, tempBuf, tempCount, zip);
+ }
+}
+
+} // namespace io
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_IO_COMPRESSION_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/io/File.cc b/extern/openvdb/internal/openvdb/io/File.cc
new file mode 100644
index 00000000000..5ca4c76ad3b
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/io/File.cc
@@ -0,0 +1,594 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+//
+/// @file File.cc
+
+#include "File.h"
+
+#include <cassert>
+#include <sstream>
+#include <boost/cstdint.hpp>
+#include <openvdb/Exceptions.h>
+#include <openvdb/util/logging.h>
+
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace io {
+
+File::File(const std::string& filename):
+ mFilename(filename),
+ mIsOpen(false)
+{
+ setInputHasGridOffsets(true);
+}
+
+
+File::~File()
+{
+}
+
+
+////////////////////////////////////////
+
+
+bool
+File::open()
+{
+#if defined(_MSC_VER)
+ // The original C++ standard library specified that open() only _sets_
+ // the fail bit upon an error. It does not clear any bits upon success.
+ // This was later addressed by the Library Working Group (LWG) for DR #409
+ // and implemented by gcc 4.0. Visual Studio 2008 however is one of those
+ // which has not caught up.
+ // See: http://gcc.gnu.org/onlinedocs/libstdc++/ext/lwg-defects.html#22
+ mInStream.clear();
+#endif
+
+ // Open the file.
+ mInStream.open(mFilename.c_str(), std::ios_base::in | std::ios_base::binary);
+
+ if (mInStream.fail()) {
+ OPENVDB_THROW(IoError, "could not open file " << mFilename);
+ }
+
+ // Read in the file header.
+ bool newFile = false;
+ try {
+ newFile = Archive::readHeader(mInStream);
+ } catch (IoError& e) {
+ mInStream.close();
+ if (e.what() && std::string("not a VDB file") == e.what()) {
+ // Rethrow, adding the filename.
+ OPENVDB_THROW(IoError, mFilename << " is not a VDB file");
+ }
+ throw;
+ }
+
+ // Tag the input stream with the file format and library version numbers.
+ Archive::setFormatVersion(mInStream);
+ Archive::setLibraryVersion(mInStream);
+ Archive::setDataCompression(mInStream);
+
+ // Read in the VDB metadata.
+ mMeta = MetaMap::Ptr(new MetaMap);
+ mMeta->readMeta(mInStream);
+
+ if (!inputHasGridOffsets()) {
+ OPENVDB_LOG_WARN("file " << mFilename << " does not support partial reading");
+
+ mGrids.reset(new GridPtrVec);
+ mNamedGrids.clear();
+
+ // Stream in the entire contents of the file and append all grids to mGrids.
+ const boost::int32_t gridCount = readGridCount(mInStream);
+ for (boost::int32_t i = 0; i < gridCount; ++i) {
+ GridDescriptor gd;
+ gd.read(mInStream);
+
+ GridBase::Ptr grid = createGrid(gd);
+ Archive::readGrid(grid, gd, mInStream);
+
+ mGridDescriptors.insert(std::make_pair(gd.gridName(), gd));
+ mGrids->push_back(grid);
+ mNamedGrids[gd.uniqueName()] = grid;
+ }
+ // Connect instances (grids that share trees with other grids).
+ for (NameMapCIter it = mGridDescriptors.begin(); it != mGridDescriptors.end(); ++it) {
+ Archive::connectInstance(it->second, mNamedGrids);
+ }
+ } else {
+ // Read in just the grid descriptors.
+ readGridDescriptors(mInStream);
+ }
+
+ mIsOpen = true;
+ return newFile; // true if file is not identical to opened file
+}
+
+
+void
+File::close()
+{
+ // Close the stream.
+ if (mInStream.is_open()) {
+ mInStream.close();
+ }
+
+ // Reset all data.
+ mMeta.reset();
+ mGridDescriptors.clear();
+ mGrids.reset();
+ mNamedGrids.clear();
+
+ mIsOpen = false;
+ setInputHasGridOffsets(true);
+}
+
+
+////////////////////////////////////////
+
+
+bool
+File::hasGrid(const Name& name) const
+{
+ if (!isOpen()) {
+ OPENVDB_THROW(IoError, mFilename << " is not open for reading");
+ }
+ return (findDescriptor(name) != mGridDescriptors.end());
+}
+
+
+MetaMap::Ptr
+File::getMetadata() const
+{
+ if (!isOpen()) {
+ OPENVDB_THROW(IoError, mFilename << " is not open for reading");
+ }
+ // Return a deep copy of the file-level metadata, which was read
+ // when the file was opened.
+ return MetaMap::Ptr(new MetaMap(*mMeta));
+}
+
+
+GridPtrVecPtr
+File::getGrids() const
+{
+ if (!isOpen()) {
+ OPENVDB_THROW(IoError, mFilename << " is not open for reading");
+ }
+
+ GridPtrVecPtr ret;
+ if (!inputHasGridOffsets()) {
+ // If the input file doesn't have grid offsets, then all of the grids
+ // have already been streamed in and stored in mGrids.
+ ret = mGrids;
+ } else {
+ ret.reset(new GridPtrVec);
+
+ Archive::NamedGridMap namedGrids;
+
+ // Read all grids represented by the GridDescriptors.
+ for (NameMapCIter i = mGridDescriptors.begin(), e = mGridDescriptors.end(); i != e; ++i) {
+ const GridDescriptor& gd = i->second;
+ GridBase::Ptr grid = readGrid(gd);
+ ret->push_back(grid);
+ namedGrids[gd.uniqueName()] = grid;
+ }
+
+ // Connect instances (grids that share trees with other grids).
+ for (NameMapCIter i = mGridDescriptors.begin(), e = mGridDescriptors.end(); i != e; ++i) {
+ Archive::connectInstance(i->second, namedGrids);
+ }
+ }
+ return ret;
+}
+
+
+////////////////////////////////////////
+
+
+GridPtrVecPtr
+File::readAllGridMetadata()
+{
+ if (!isOpen()) {
+ OPENVDB_THROW(IoError, mFilename << " is not open for reading");
+ }
+
+ GridPtrVecPtr ret(new GridPtrVec);
+
+ if (!inputHasGridOffsets()) {
+ // If the input file doesn't have grid offsets, then all of the grids
+ // have already been streamed in and stored in mGrids.
+ for (size_t i = 0, N = mGrids->size(); i < N; ++i) {
+ // Return copies of the grids, but with empty trees.
+ ret->push_back((*mGrids)[i]->copyGrid(/*treePolicy=*/CP_NEW));
+ }
+ } else {
+ // Read just the metadata and transforms for all grids.
+ for (NameMapCIter i = mGridDescriptors.begin(), e = mGridDescriptors.end(); i != e; ++i) {
+ const GridDescriptor& gd = i->second;
+ GridBase::ConstPtr grid = readGridPartial(gd, /*readTopology=*/false);
+ // Return copies of the grids, but with empty trees.
+ // (As of 0.98.0, at least, it would suffice to just const cast
+ // the grid pointers returned by readGridPartial(), but shallow
+ // copying the grids helps to ensure future compatibility.)
+ ret->push_back(grid->copyGrid(/*treePolicy=*/CP_NEW));
+ }
+ }
+ return ret;
+}
+
+
+GridBase::Ptr
+File::readGridMetadata(const Name& name)
+{
+ if (!isOpen()) {
+ OPENVDB_THROW(IoError, mFilename << " is not open for reading.");
+ }
+
+ GridBase::ConstPtr ret;
+ if (!inputHasGridOffsets()) {
+ // Retrieve the grid from mGrids, which should already contain
+ // the entire contents of the file.
+ ret = readGrid(name);
+ } else {
+ NameMapCIter it = findDescriptor(name);
+ if (it == mGridDescriptors.end()) {
+ OPENVDB_THROW(KeyError, mFilename << " has no grid named \"" << name << "\"");
+ }
+
+ // Seek to and read in the grid from the file.
+ const GridDescriptor& gd = it->second;
+ ret = readGridPartial(gd, /*readTopology=*/false);
+ }
+ return ret->copyGrid(/*treePolicy=*/CP_NEW);
+}
+
+
+////////////////////////////////////////
+
+
+GridBase::ConstPtr
+File::readGridPartial(const Name& name)
+{
+ if (!isOpen()) {
+ OPENVDB_THROW(IoError, mFilename << " is not open for reading.");
+ }
+
+ GridBase::ConstPtr ret;
+ if (!inputHasGridOffsets()) {
+ // Retrieve the grid from mGrids, which should already contain
+ // the entire contents of the file.
+ if (GridBase::Ptr grid = readGrid(name)) {
+ ret = boost::const_pointer_cast<const GridBase>(grid);
+ }
+ } else {
+ NameMapCIter it = findDescriptor(name);
+ if (it == mGridDescriptors.end()) {
+ OPENVDB_THROW(KeyError, mFilename << " has no grid named \"" << name << "\"");
+ }
+
+ // Seek to and read in the grid from the file.
+ const GridDescriptor& gd = it->second;
+ ret = readGridPartial(gd, /*readTopology=*/true);
+
+ if (gd.isInstance()) {
+ NameMapCIter parentIt =
+ findDescriptor(GridDescriptor::nameAsString(gd.instanceParentName()));
+ if (parentIt == mGridDescriptors.end()) {
+ OPENVDB_THROW(KeyError, "missing instance parent \""
+ << GridDescriptor::nameAsString(gd.instanceParentName())
+ << "\" for grid " << GridDescriptor::nameAsString(gd.uniqueName())
+ << " in file " << mFilename);
+ }
+ if (GridBase::ConstPtr parent =
+ readGridPartial(parentIt->second, /*readTopology=*/true))
+ {
+ if (Archive::isInstancingEnabled()) {
+ // Share the instance parent's tree.
+ boost::const_pointer_cast<GridBase>(ret)->setTree(
+ boost::const_pointer_cast<GridBase>(parent)->baseTreePtr());
+ } else {
+ // Copy the instance parent's tree.
+ boost::const_pointer_cast<GridBase>(ret)->setTree(
+ parent->baseTree().copy());
+ }
+ }
+ }
+ }
+ return ret;
+}
+
+
+GridBase::Ptr
+File::readGrid(const Name& name)
+{
+ if (!isOpen()) {
+ OPENVDB_THROW(IoError, mFilename << " is not open for reading.");
+ }
+
+ GridBase::Ptr ret;
+ if (!inputHasGridOffsets()) {
+ // Retrieve the grid from mNamedGrids, which should already contain
+ // the entire contents of the file.
+
+ // Search by unique name.
+ Archive::NamedGridMap::const_iterator it =
+ mNamedGrids.find(GridDescriptor::stringAsUniqueName(name));
+ // If not found, search by grid name.
+ if (it == mNamedGrids.end()) it = mNamedGrids.find(name);
+ if (it == mNamedGrids.end()) {
+ OPENVDB_THROW(KeyError, mFilename << " has no grid named \"" << name << "\"");
+ }
+ ret = it->second;
+ } else {
+ NameMapCIter it = findDescriptor(name);
+ if (it == mGridDescriptors.end()) {
+ OPENVDB_THROW(KeyError, mFilename << " has no grid named \"" << name << "\"");
+ }
+
+ // Seek to and read in the grid from the file.
+ const GridDescriptor& gd = it->second;
+ ret = readGrid(gd);
+
+ if (gd.isInstance()) {
+ NameMapCIter parentIt =
+ findDescriptor(GridDescriptor::nameAsString(gd.instanceParentName()));
+ if (parentIt == mGridDescriptors.end()) {
+ OPENVDB_THROW(KeyError, "missing instance parent \""
+ << GridDescriptor::nameAsString(gd.instanceParentName())
+ << "\" for grid " << GridDescriptor::nameAsString(gd.uniqueName())
+ << " in file " << mFilename);
+ }
+ if (GridBase::Ptr parent = readGrid(parentIt->second)) {
+ if (Archive::isInstancingEnabled()) {
+ // Share the instance parent's tree.
+ ret->setTree(parent->baseTreePtr());
+ } else {
+ // Copy the instance parent's tree.
+ ret->setTree(parent->baseTree().copy());
+ }
+ }
+ }
+ }
+ return ret;
+}
+
+
+////////////////////////////////////////
+
+
+void
+File::writeGrids(const GridCPtrVec& grids, const MetaMap& metadata) const
+{
+ if (isOpen()) {
+ OPENVDB_THROW(IoError,
+ mFilename << " cannot be written because it is open for reading");
+ }
+
+ // Create a file stream and write it out.
+ std::ofstream file;
+ file.open(mFilename.c_str(),
+ std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
+
+ if (file.fail()) {
+ OPENVDB_THROW(IoError, "could not open " << mFilename << " for writing");
+ }
+
+ // Write out the vdb.
+ Archive::write(file, grids, /*seekable=*/true, metadata);
+
+ file.close();
+}
+
+
+////////////////////////////////////////
+
+
+void
+File::readGridDescriptors(std::istream& is)
+{
+ // This method should not be called for files that don't contain grid offsets.
+ assert(inputHasGridOffsets());
+
+ mGridDescriptors.clear();
+
+ for (boost::int32_t i = 0, N = readGridCount(is); i < N; ++i) {
+ // Read the grid descriptor.
+ GridDescriptor gd;
+ gd.read(is);
+
+ // Add the descriptor to the dictionary.
+ mGridDescriptors.insert(std::make_pair(gd.gridName(), gd));
+
+ // Skip forward to the next descriptor.
+ gd.seekToEnd(is);
+ }
+}
+
+
+////////////////////////////////////////
+
+
+File::NameMapCIter
+File::findDescriptor(const Name& name) const
+{
+ const Name uniqueName = GridDescriptor::stringAsUniqueName(name);
+
+ // Find all descriptors with the given grid name.
+ std::pair<NameMapCIter, NameMapCIter> range = mGridDescriptors.equal_range(name);
+
+ if (range.first == range.second) {
+ // If no descriptors were found with the given grid name, the name might have
+ // a suffix ("name[N]"). In that case, remove the "[N]" suffix and search again.
+ range = mGridDescriptors.equal_range(GridDescriptor::stripSuffix(uniqueName));
+ }
+
+ const size_t count = size_t(std::distance(range.first, range.second));
+ if (count > 1 && name == uniqueName) {
+ OPENVDB_LOG_WARN(mFilename << " has more than one grid named \"" << name << "\"");
+ }
+
+ NameMapCIter ret = mGridDescriptors.end();
+
+ if (count > 0) {
+ if (name == uniqueName) {
+ // If the given grid name is unique or if no "[N]" index was given,
+ // use the first matching descriptor.
+ ret = range.first;
+ } else {
+ // If the given grid name has a "[N]" index, find the descriptor
+ // with a matching unique name.
+ for (NameMapCIter it = range.first; it != range.second; ++it) {
+ const Name candidateName = it->second.uniqueName();
+ if (candidateName == uniqueName || candidateName == name) {
+ ret = it;
+ break;
+ }
+ }
+ }
+ }
+ return ret;
+}
+
+
+////////////////////////////////////////
+
+
+GridBase::Ptr
+File::createGrid(const GridDescriptor& gd) const
+{
+ // Create the grid.
+ if (!GridBase::isRegistered(gd.gridType())) {
+ OPENVDB_THROW(KeyError, "Cannot read grid "
+ << GridDescriptor::nameAsString(gd.uniqueName())
+ << " from " << mFilename << ": grid type "
+ << gd.gridType() << " is not registered");
+ }
+
+ GridBase::Ptr grid = GridBase::createGrid(gd.gridType());
+ if (grid) grid->setSaveFloatAsHalf(gd.saveFloatAsHalf());
+
+ return grid;
+}
+
+
+GridBase::ConstPtr
+File::readGridPartial(const GridDescriptor& gd, bool readTopology) const
+{
+ // This method should not be called for files that don't contain grid offsets.
+ assert(inputHasGridOffsets());
+
+ GridBase::Ptr grid = createGrid(gd);
+
+ // Seek to grid.
+ gd.seekToGrid(mInStream);
+
+ // Read the grid partially.
+ readGridPartial(grid, mInStream, gd.isInstance(), readTopology);
+
+ // Promote to a const grid.
+ GridBase::ConstPtr constGrid = grid;
+
+ return constGrid;
+}
+
+
+GridBase::Ptr
+File::readGrid(const GridDescriptor& gd) const
+{
+ // This method should not be called for files that don't contain grid offsets.
+ assert(inputHasGridOffsets());
+
+ GridBase::Ptr grid = createGrid(gd);
+
+ // Seek to where the grid is.
+ gd.seekToGrid(mInStream);
+
+ // Read in the grid.
+ Archive::readGrid(grid, gd, mInStream);
+
+ return grid;
+}
+
+
+void
+File::readGridPartial(GridBase::Ptr grid, std::istream& is,
+ bool isInstance, bool readTopology) const
+{
+ // This method should not be called for files that don't contain grid offsets.
+ assert(inputHasGridOffsets());
+
+ // This code needs to stay in sync with io::Archive::readGrid(), in terms of
+ // the order of operations.
+ readGridCompression(is);
+ grid->readMeta(is);
+ if (getFormatVersion(is) >= OPENVDB_FILE_VERSION_GRID_INSTANCING) {
+ grid->readTransform(is);
+ if (!isInstance && readTopology) {
+ grid->readTopology(is);
+ }
+ } else {
+ if (readTopology) {
+ grid->readTopology(is);
+ grid->readTransform(is);
+ }
+ }
+}
+
+
+////////////////////////////////////////
+
+
+File::NameIterator
+File::beginName() const
+{
+ if (!isOpen()) {
+ OPENVDB_THROW(IoError, mFilename << " is not open for reading");
+ }
+ return File::NameIterator(mGridDescriptors.begin());
+}
+
+
+File::NameIterator
+File::endName() const
+{
+ return File::NameIterator(mGridDescriptors.end());
+}
+
+} // namespace io
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/io/File.h b/extern/openvdb/internal/openvdb/io/File.h
new file mode 100644
index 00000000000..a00fd0d7858
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/io/File.h
@@ -0,0 +1,220 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+//
+/// @file File.h
+
+#ifndef OPENVDB_IO_FILE_HAS_BEEN_INCLUDED
+#define OPENVDB_IO_FILE_HAS_BEEN_INCLUDED
+
+#include <iostream>
+#include <fstream>
+#include <map>
+#include <string>
+#include "Archive.h"
+#include "GridDescriptor.h"
+
+
+class TestFile;
+class TestStream;
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace io {
+
+/// Grid archive associated with a file on disk
+class OPENVDB_API File: public Archive
+{
+public:
+ typedef std::multimap<Name, GridDescriptor> NameMap;
+ typedef NameMap::const_iterator NameMapCIter;
+
+ explicit File(const std::string& filename);
+ ~File();
+
+ const std::string& filename() const { return mFilename; }
+
+ /// Open the file, read the file header and the file-level metadata, and
+ /// populate the grid descriptors, but do not load any grids into memory.
+ /// @throw IoError if the file is not a valid VDB file.
+ /// @return @c true if the file's UUID has changed since it was last read.
+ bool open();
+
+ /// Return @c true if the file has been opened for reading, false otherwise.
+ bool isOpen() const { return mIsOpen; }
+
+ /// Close the file once we are done reading from it.
+ void close();
+
+ /// Return @c true if a grid of the given name exists in this file.
+ bool hasGrid(const Name&) const;
+
+ /// Return (in a newly created MetaMap) the file-level metadata.
+ MetaMap::Ptr getMetadata() const;
+
+ /// Read the entire contents of the file and return a list of grid pointers.
+ GridPtrVecPtr getGrids() const;
+
+ /// @brief Read just the grid metadata and transforms from the file and return a list
+ /// of pointers to grids that are empty except for their metadata and transforms.
+ /// @throw IoError if this file is not open for reading.
+ GridPtrVecPtr readAllGridMetadata();
+
+ /// @brief Read a grid's metadata and transform only.
+ /// @return A pointer to a grid that is empty except for its metadata and transform.
+ /// @throw IoError if this file is not open for reading.
+ /// @throw KeyError if no grid with the given name exists in this file.
+ GridBase::Ptr readGridMetadata(const Name&);
+
+ /// @brief Read a grid's metadata, topology, transform, etc., but not
+ /// any of its leaf node data blocks.
+ /// @return the grid pointer to the partially loaded grid.
+ /// @note This returns a @c const pointer, so that the grid can't be
+ /// changed before its data blocks have been loaded. A non-<tt>const</tt>
+ /// pointer is only returned when readGrid() is called.
+ GridBase::ConstPtr readGridPartial(const Name&);
+
+ /// Read an entire grid, including all of its data blocks.
+ GridBase::Ptr readGrid(const Name&);
+
+ /// @todo GridPtrVec readAllGridsPartial(const Name&)
+ /// @todo GridPtrVec readAllGrids(const Name&)
+
+ /// @brief Write the grids in the given container to the file whose name
+ /// was given in the constructor.
+ template<typename GridPtrContainerT>
+ void write(const GridPtrContainerT&, const MetaMap& = MetaMap()) const;
+
+ /// A const iterator that iterates over all names in the file. This is only
+ /// valid once the file has been opened.
+ class NameIterator
+ {
+ public:
+ NameIterator(const NameMapCIter& iter): mIter(iter) {}
+ ~NameIterator() {}
+
+ NameIterator& operator++() { mIter++; return *this; }
+
+ bool operator==(const NameIterator& iter) const { return mIter == iter.mIter; }
+ bool operator!=(const NameIterator& iter) const { return mIter != iter.mIter; }
+
+ Name operator*() const { return this->gridName(); }
+
+ Name gridName() const { return GridDescriptor::nameAsString(mIter->second.uniqueName()); }
+
+ private:
+ NameMapCIter mIter;
+ };
+
+ /// @return a NameIterator to iterate over all grid names in the file.
+ NameIterator beginName() const;
+
+ /// @return the ending iterator for all grid names in the file.
+ NameIterator endName() const;
+
+private:
+ /// Resets the input stream to the beginning.
+ void resetInStream() const { mInStream.seekg(0, std::ios::beg); }
+
+ /// Read in all grid descriptors that are stored in the given stream.
+ void readGridDescriptors(std::istream&);
+
+ /// @brief Return an iterator to the descriptor for the grid with the given name.
+ /// If the name is non-unique, return an iterator to the first matching descriptor.
+ NameMapCIter findDescriptor(const Name&) const;
+
+ /// Return a newly created, empty grid of the type specified by the given grid descriptor.
+ GridBase::Ptr createGrid(const GridDescriptor&) const;
+
+ /// Read in and return the partially-populated grid specified by the given grid descriptor.
+ GridBase::ConstPtr readGridPartial(const GridDescriptor&, bool readTopology) const;
+
+ /// Read in and return the grid specified by the given grid descriptor.
+ GridBase::Ptr readGrid(const GridDescriptor&) const;
+
+ /// Partially populate the given grid by reading its metadata and transform and,
+ /// if the grid is not an instance, its tree structure, but not the tree's leaf nodes.
+ void readGridPartial(GridBase::Ptr, std::istream&, bool isInstance, bool readTopology) const;
+
+ void writeGrids(const GridCPtrVec&, const MetaMap&) const;
+
+ // Disallow copying of instances of this class.
+ File(const File& other);
+ File& operator=(const File& other);
+
+ friend class ::TestFile;
+ friend class ::TestStream;
+
+
+ std::string mFilename;
+ /// The file-level metadata
+ MetaMap::Ptr mMeta;
+ /// The file stream that is open for reading
+ mutable std::ifstream mInStream;
+ /// Flag indicating if we have read in the global information (header,
+ /// metadata, and grid descriptors) for this VDB file
+ bool mIsOpen;
+ /// Grid descriptors for all grids stored in the file, indexed by grid name
+ NameMap mGridDescriptors;
+ /// All grids, indexed by unique name (used only when mHasGridOffsets is false)
+ Archive::NamedGridMap mNamedGrids;
+ /// All grids stored in the file (used only when mHasGridOffsets is false)
+ GridPtrVecPtr mGrids;
+};
+
+
+////////////////////////////////////////
+
+
+template<typename GridPtrContainerT>
+inline void
+File::write(const GridPtrContainerT& container, const MetaMap& metadata) const
+{
+ GridCPtrVec grids;
+ std::copy(container.begin(), container.end(), std::back_inserter(grids));
+ this->writeGrids(grids, metadata);
+}
+
+template<>
+inline void
+File::write<GridCPtrVec>(const GridCPtrVec& grids, const MetaMap& metadata) const
+{
+ this->writeGrids(grids, metadata);
+}
+
+} // namespace io
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_IO_FILE_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/io/GridDescriptor.cc b/extern/openvdb/internal/openvdb/io/GridDescriptor.cc
new file mode 100644
index 00000000000..e0dcbcabbe5
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/io/GridDescriptor.cc
@@ -0,0 +1,232 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+
+#include "GridDescriptor.h"
+
+#include <sstream>
+#include <boost/algorithm/string/predicate.hpp> // for boost::ends_with()
+#include <boost/algorithm/string/erase.hpp> // for boost::erase_last()
+#include <openvdb/Exceptions.h>
+
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace io {
+
+namespace {
+
+// In order not to break backward compatibility with existing VDB files,
+// grids stored using 16-bit half floats are flagged by adding the following
+// suffix to the grid's type name on output. The suffix is removed on input
+// and the grid's "save float as half" flag set accordingly.
+const char* HALF_FLOAT_TYPENAME_SUFFIX = "_HalfFloat";
+
+const char* SEP = "\x1e"; // ASCII "record separator"
+
+}
+
+
+GridDescriptor::GridDescriptor():
+ mSaveFloatAsHalf(false),
+ mGridPos(0),
+ mBlockPos(0),
+ mEndPos(0)
+{
+}
+
+GridDescriptor::GridDescriptor(const Name &name, const Name &type, bool half):
+ mGridName(stripSuffix(name)),
+ mUniqueName(name),
+ mGridType(type),
+ mSaveFloatAsHalf(half),
+ mGridPos(0),
+ mBlockPos(0),
+ mEndPos(0)
+{
+}
+
+GridDescriptor::~GridDescriptor()
+{
+}
+
+void
+GridDescriptor::writeHeader(std::ostream &os) const
+{
+ writeString(os, mUniqueName);
+
+ Name gridType = mGridType;
+ if (mSaveFloatAsHalf) gridType += HALF_FLOAT_TYPENAME_SUFFIX;
+ writeString(os, gridType);
+
+ writeString(os, mInstanceParentName);
+}
+
+void
+GridDescriptor::writeStreamPos(std::ostream &os) const
+{
+ os.write(reinterpret_cast<const char*>(&mGridPos), sizeof(boost::int64_t));
+ os.write(reinterpret_cast<const char*>(&mBlockPos), sizeof(boost::int64_t));
+ os.write(reinterpret_cast<const char*>(&mEndPos), sizeof(boost::int64_t));
+}
+
+GridBase::Ptr
+GridDescriptor::read(std::istream &is)
+{
+ // Read in the name.
+ mUniqueName = readString(is);
+ mGridName = stripSuffix(mUniqueName);
+
+ // Read in the grid type.
+ mGridType = readString(is);
+ if (boost::ends_with(mGridType, HALF_FLOAT_TYPENAME_SUFFIX)) {
+ mSaveFloatAsHalf = true;
+ boost::erase_last(mGridType, HALF_FLOAT_TYPENAME_SUFFIX);
+ }
+
+ if (getFormatVersion(is) >= OPENVDB_FILE_VERSION_GRID_INSTANCING) {
+ mInstanceParentName = readString(is);
+ }
+
+ // Create the grid of the type if it has been registered.
+ if (!GridBase::isRegistered(mGridType)) {
+ OPENVDB_THROW(LookupError, "Cannot read grid." <<
+ " Grid type " << mGridType << " is not registered.");
+ }
+ // else
+ GridBase::Ptr grid = GridBase::createGrid(mGridType);
+ if (grid) grid->setSaveFloatAsHalf(mSaveFloatAsHalf);
+
+ // Read in the offsets.
+ is.read(reinterpret_cast<char*>(&mGridPos), sizeof(boost::int64_t));
+ is.read(reinterpret_cast<char*>(&mBlockPos), sizeof(boost::int64_t));
+ is.read(reinterpret_cast<char*>(&mEndPos), sizeof(boost::int64_t));
+
+ return grid;
+}
+
+void
+GridDescriptor::seekToGrid(std::istream &is) const
+{
+ is.seekg(mGridPos, std::ios_base::beg);
+}
+
+void
+GridDescriptor::seekToBlocks(std::istream &is) const
+{
+ is.seekg(mBlockPos, std::ios_base::beg);
+}
+
+void
+GridDescriptor::seekToEnd(std::istream &is) const
+{
+ is.seekg(mEndPos, std::ios_base::beg);
+}
+
+
+void
+GridDescriptor::seekToGrid(std::ostream &os) const
+{
+ os.seekp(mGridPos, std::ios_base::beg);
+}
+
+void
+GridDescriptor::seekToBlocks(std::ostream &os) const
+{
+ os.seekp(mBlockPos, std::ios_base::beg);
+}
+
+void
+GridDescriptor::seekToEnd(std::ostream &os) const
+{
+ os.seekp(mEndPos, std::ios_base::beg);
+}
+
+
+////////////////////////////////////////
+
+
+// static
+Name
+GridDescriptor::addSuffix(const Name& name, int n)
+{
+ std::ostringstream ostr;
+ ostr << name << SEP << n;
+ return ostr.str();
+}
+
+
+// static
+Name
+GridDescriptor::stripSuffix(const Name& name)
+{
+ return name.substr(0, name.find(SEP));
+}
+
+
+// static
+std::string
+GridDescriptor::nameAsString(const Name& name)
+{
+ std::string::size_type pos = name.find(SEP);
+ if (pos == std::string::npos) return name;
+
+ return name.substr(0, pos) + "[" + name.substr(pos + 1) + "]";
+}
+
+
+//static
+Name
+GridDescriptor::stringAsUniqueName(const std::string& s)
+{
+ Name ret = s;
+ if (!ret.empty() && *ret.rbegin() == ']') { // found trailing ']'
+ std::string::size_type pos = ret.find("[");
+ // Replace "[N]" with SEP "N".
+ if (pos != std::string::npos) {
+ if (ret.substr(pos) == "[0]") {
+ // "name[0]" is equivalent to "name".
+ ret.erase(pos);
+ } else {
+ ret.resize(ret.size() - 1); // drop trailing ']'
+ ret.replace(ret.find("["), 1, SEP);
+ }
+ }
+ }
+ return ret;
+}
+
+} // namespace io
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/io/GridDescriptor.h b/extern/openvdb/internal/openvdb/io/GridDescriptor.h
new file mode 100644
index 00000000000..a974e4ef770
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/io/GridDescriptor.h
@@ -0,0 +1,135 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+
+#ifndef OPENVDB_IO_GRIDDESCRIPTOR_HAS_BEEN_INCLUDED
+#define OPENVDB_IO_GRIDDESCRIPTOR_HAS_BEEN_INCLUDED
+
+#include <openvdb/Grid.h>
+#include <iostream>
+#include <boost/cstdint.hpp>
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace io {
+
+/// This structure stores useful information that describes a grid on disk.
+/// It can be used to retrieve I/O information about the grid such as
+/// offsets into the file where the grid is located, its type, etc.
+class OPENVDB_API GridDescriptor
+{
+public:
+ GridDescriptor();
+ GridDescriptor(const Name& name, const Name& gridType, bool saveFloatAsHalf = false);
+
+ ~GridDescriptor();
+
+ const Name& gridType() const { return mGridType; }
+ const Name& gridName() const { return mGridName; }
+ const Name& uniqueName() const { return mUniqueName; }
+
+ const Name& instanceParentName() const { return mInstanceParentName; }
+ void setInstanceParentName(const Name& name) { mInstanceParentName = name; }
+ bool isInstance() const { return !mInstanceParentName.empty(); }
+
+ bool saveFloatAsHalf() const { return mSaveFloatAsHalf; }
+
+ void setGridPos(boost::int64_t pos) { mGridPos = pos; }
+ boost::int64_t getGridPos() const { return mGridPos; }
+
+ void setBlockPos(boost::int64_t pos) { mBlockPos = pos; }
+ boost::int64_t getBlockPos() const { return mBlockPos; }
+
+ void setEndPos(boost::int64_t pos) { mEndPos = pos; }
+ boost::int64_t getEndPos() const { return mEndPos; }
+
+ // These methods seek to the right position in the given stream.
+ void seekToGrid(std::istream&) const;
+ void seekToBlocks(std::istream&) const;
+ void seekToEnd(std::istream&) const;
+
+ void seekToGrid(std::ostream&) const;
+ void seekToBlocks(std::ostream&) const;
+ void seekToEnd(std::ostream&) const;
+
+ /// @brief Write out this descriptor's header information (all data except for
+ /// stream offsets).
+ void writeHeader(std::ostream&) const;
+
+ /// @brief Since positions into the stream are known at a later time, they are
+ /// written out separately.
+ void writeStreamPos(std::ostream&) const;
+
+ /// @brief Read a grid descriptor from the given stream.
+ /// @return an empty grid of the type specified by the grid descriptor.
+ GridBase::Ptr read(std::istream&);
+
+ /// @brief Append the number @a n to the given name (separated by an ASCII
+ /// "record separator" character) and return the resulting name.
+ static Name addSuffix(const Name&, int n);
+ /// @brief Strip from the given name any suffix that is separated by an ASCII
+ /// "record separator" character and return the resulting name.
+ static Name stripSuffix(const Name&);
+ /// @brief Given a name with suffix N, return "name[N]", otherwise just return "name".
+ /// Use this to produce a human-readable string from a descriptor's unique name.
+ static std::string nameAsString(const Name&);
+ /// @brief Given a string of the form "name[N]", return "name" with the suffix N
+ /// separated by an ASCII "record separator" character). Otherwise just return
+ /// the string as is.
+ static Name stringAsUniqueName(const std::string&);
+
+private:
+ /// Name of the grid
+ Name mGridName;
+ /// Unique name for this descriptor
+ Name mUniqueName;
+ /// If nonempty, the name of another grid that shares this grid's tree
+ Name mInstanceParentName;
+ /// The type of the grid
+ Name mGridType;
+ /// Are floats quantized to 16 bits on disk?
+ bool mSaveFloatAsHalf;
+ /// Location in the stream where the grid data is stored
+ boost::int64_t mGridPos;
+ /// Location in the stream where the grid blocks are stored
+ boost::int64_t mBlockPos;
+ /// Location in the stream where the next grid descriptor begins
+ boost::int64_t mEndPos;
+};
+
+} // namespace io
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_IO_GRIDDESCRIPTOR_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/io/Stream.cc b/extern/openvdb/internal/openvdb/io/Stream.cc
new file mode 100644
index 00000000000..70bf486cee7
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/io/Stream.cc
@@ -0,0 +1,140 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+
+#include "Stream.h"
+
+#include <iostream>
+#include <vector>
+#include <boost/cstdint.hpp>
+#include <openvdb/Exceptions.h>
+#include "GridDescriptor.h"
+
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace io {
+
+Stream::Stream(std::istream& is)
+{
+ if (!is) return;
+
+ readHeader(is);
+
+ // Tag the input stream with the library and file format version numbers
+ // and the compression options specified in the header.
+ io::setVersion(is, libraryVersion(), fileVersion());
+ io::setDataCompression(is, compressionFlags());
+
+ // Read in the VDB metadata.
+ mMeta.reset(new MetaMap);
+ mMeta->readMeta(is);
+
+ // Read in the number of grids.
+ const boost::int32_t gridCount = readGridCount(is);
+
+ // Read in all grids and insert them into mGrids.
+ mGrids.reset(new GridPtrVec);
+ std::vector<GridDescriptor> descriptors;
+ descriptors.reserve(gridCount);
+ Archive::NamedGridMap namedGrids;
+ for (boost::int32_t i = 0; i < gridCount; ++i) {
+ GridDescriptor gd;
+ gd.read(is);
+ descriptors.push_back(gd);
+ GridBase::Ptr grid = readGrid(gd, is);
+ mGrids->push_back(grid);
+ namedGrids[gd.uniqueName()] = grid;
+ }
+
+ // Connect instances (grids that share trees with other grids).
+ for (size_t i = 0, N = descriptors.size(); i < N; ++i) {
+ Archive::connectInstance(descriptors[i], namedGrids);
+ }
+}
+
+
+Stream::Stream()
+{
+}
+
+
+Stream::~Stream()
+{
+}
+
+
+////////////////////////////////////////
+
+
+GridBase::Ptr
+Stream::readGrid(const GridDescriptor& gd, std::istream& is) const
+{
+ GridBase::Ptr grid;
+
+ if (!GridBase::isRegistered(gd.gridType())) {
+ OPENVDB_THROW(TypeError, "can't read grid \""
+ << GridDescriptor::nameAsString(gd.uniqueName()) <<
+ "\" from input stream because grid type " << gd.gridType() << " is unknown");
+ } else {
+ grid = GridBase::createGrid(gd.gridType());
+ if (grid) grid->setSaveFloatAsHalf(gd.saveFloatAsHalf());
+
+ Archive::readGrid(grid, gd, is);
+ }
+ return grid;
+}
+
+
+void
+Stream::writeGrids(std::ostream& os, const GridCPtrVec& grids, const MetaMap& metadata) const
+{
+ Archive::write(os, grids, /*seekable=*/false, metadata);
+}
+
+
+////////////////////////////////////////
+
+
+MetaMap::Ptr
+Stream::getMetadata() const
+{
+ // Return a deep copy of the file-level metadata, which was read
+ // when this object was constructed.
+ return MetaMap::Ptr(new MetaMap(*mMeta));
+}
+
+} // namespace io
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/io/Stream.h b/extern/openvdb/internal/openvdb/io/Stream.h
new file mode 100644
index 00000000000..cd57779b245
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/io/Stream.h
@@ -0,0 +1,112 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+
+#ifndef OPENVDB_IO_STREAM_HAS_BEEN_INCLUDED
+#define OPENVDB_IO_STREAM_HAS_BEEN_INCLUDED
+
+#include <iosfwd>
+#include "Archive.h"
+
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace io {
+
+class GridDescriptor;
+
+
+/// Grid archive associated with arbitrary input and output streams (not necessarily files)
+class OPENVDB_API Stream: public Archive
+{
+public:
+ /// Read grids from an input stream.
+ explicit Stream(std::istream&);
+ Stream();
+ ~Stream();
+
+ /// Return the file-level metadata in a newly created MetaMap.
+ MetaMap::Ptr getMetadata() const;
+
+ /// Return pointers to the grids that were read from the input stream.
+ GridPtrVecPtr getGrids() { return mGrids; }
+
+ /// Write the grids in the given container to an output stream.
+ template<typename GridPtrContainerT>
+ void write(std::ostream&, const GridPtrContainerT&, const MetaMap& = MetaMap()) const;
+
+private:
+ /// Create a new grid of the type specified by the given descriptor,
+ /// then populate the grid from the given input stream.
+ /// @return the newly created grid.
+ GridBase::Ptr readGrid(const GridDescriptor&, std::istream&) const;
+
+ void writeGrids(std::ostream&, const GridCPtrVec&, const MetaMap&) const;
+
+ // Disallow copying of instances of this class.
+ Stream(const Stream&);
+ Stream& operator=(const Stream&);
+
+
+ MetaMap::Ptr mMeta;
+ GridPtrVecPtr mGrids;
+};
+
+
+////////////////////////////////////////
+
+
+template<typename GridPtrContainerT>
+inline void
+Stream::write(std::ostream& os, const GridPtrContainerT& container,
+ const MetaMap& metadata) const
+{
+ GridCPtrVec grids;
+ std::copy(container.begin(), container.end(), std::back_inserter(grids));
+ this->writeGrids(os, grids, metadata);
+}
+
+template<>
+inline void
+Stream::write<GridCPtrVec>(std::ostream& os, const GridCPtrVec& grids,
+ const MetaMap& metadata) const
+{
+ this->writeGrids(os, grids, metadata);
+}
+
+} // namespace io
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_IO_STREAM_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/math/BBox.h b/extern/openvdb/internal/openvdb/math/BBox.h
new file mode 100644
index 00000000000..0af43d5d732
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/math/BBox.h
@@ -0,0 +1,392 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+
+#ifndef OPENVDB_MATH_BBOX_HAS_BEEN_INCLUDED
+#define OPENVDB_MATH_BBOX_HAS_BEEN_INCLUDED
+
+#include "Math.h" // for isApproxEqual() and tolerance()
+#include "Vec3.h"
+#include <ostream>
+#include <algorithm> // for min/max
+#include <boost/type_traits/is_integral.hpp>
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace math {
+
+/// @brief Axis-aligned bounding box
+template<class _VectorType>
+class BBox
+{
+public:
+ typedef _VectorType VectorType;
+ typedef _VectorType ValueType;
+ typedef typename _VectorType::ValueType ElementType;
+
+ BBox();
+ BBox(const VectorType& xyzMin, const VectorType& xyzMax);
+ BBox(const VectorType& xyzMin, const VectorType& xyzMax, bool sorted);
+ /// @brief Contruct a cubical BBox from a minimum coordinate and a
+ /// single edge length.
+ /// @note inclusive for integral <tt>ElementType</tt>s
+ BBox(const VectorType& xyzMin, const ElementType& length);
+ explicit BBox(const ElementType* xyz, bool sorted = true);
+ BBox(const BBox& other);
+
+ void sort();
+
+ const VectorType& min() const { return mMin; }
+ const VectorType& max() const { return mMax; }
+
+ VectorType& min() { return mMin; }
+ VectorType& max() { return mMax; }
+
+ bool operator==(const BBox& rhs) const;
+ bool operator!=(const BBox& rhs) const { return !(*this == rhs); }
+
+ bool empty() const;
+ bool hasVolume() const { return !empty(); }
+ operator bool() const { return !empty(); }
+
+ bool isSorted() const;
+
+ Vec3d getCenter() const;
+
+ /// @note inclusive for integral <tt>ElementType</tt>s
+ VectorType extents() const;
+
+ ElementType volume() const { VectorType e = extents(); return e[0] * e[1] * e[2]; }
+
+ /// Return the index (0, 1 or 2) of the longest axis.
+ size_t maxExtent() const;
+
+ /// Return @c true if point (x, y, z) is inside this bounding box.
+ bool isInside(const VectorType& xyz) const;
+
+ /// Return @c true if the given bounding box is inside this bounding box.
+ bool isInside(const BBox&) const;
+
+ /// Return @c true if the given bounding box overlaps with this bounding box.
+ bool hasOverlap(const BBox&) const;
+
+ /// Pad this bounding box.
+ void expand(ElementType padding);
+ /// Expand this bounding box to enclose point (x, y, z).
+ void expand(const VectorType& xyz);
+ /// Union this bounding box with the given bounding box.
+ void expand(const BBox&);
+ // @brief Union this bbox with the cubical bbox defined from xyzMin and
+ // length
+ /// @note inclusive for integral <tt>ElementType</tt>s
+ void expand(const VectorType& xyzMin, const ElementType& length);
+ /// Translate this bounding box by \f$(t_x, t_y, t_z)\f$.
+ void translate(const VectorType& t);
+
+ /// Unserialize this bounding box from the given stream.
+ void read(std::istream& is) { mMin.read(is); mMax.read(is); }
+ /// Serialize this bounding box to the given stream.
+ void write(std::ostream& os) const { mMin.write(os); mMax.write(os); }
+
+private:
+ VectorType mMin, mMax;
+}; // class BBox
+
+
+////////////////////////////////////////
+
+
+template<class VectorType>
+inline
+BBox<VectorType>::BBox():
+ mMin(ElementType(0), ElementType(0), ElementType(0)),
+ mMax(ElementType(0), ElementType(0), ElementType(0))
+{
+}
+
+template<class VectorType>
+inline
+BBox<VectorType>::BBox(const VectorType& xyzMin, const VectorType& xyzMax):
+ mMin(xyzMin), mMax(xyzMax)
+{
+}
+
+template<class VectorType>
+inline
+BBox<VectorType>::BBox(const VectorType& xyzMin, const VectorType& xyzMax, bool sorted):
+ mMin(xyzMin), mMax(xyzMax)
+{
+ if (!sorted) this->sort();
+}
+
+template<class VectorType>
+inline
+BBox<VectorType>::BBox(const VectorType& xyzMin, const ElementType& length):
+ mMin(xyzMin), mMax(xyzMin)
+{
+ // min and max are inclusive for integral ElementType
+ const ElementType size = boost::is_integral<ElementType>::value ? length-1 : length;
+ mMax[0] += size;
+ mMax[1] += size;
+ mMax[2] += size;
+}
+
+template<class VectorType>
+inline
+BBox<VectorType>::BBox(const ElementType* xyz, bool sorted):
+ mMin(xyz[0], xyz[1], xyz[2]),
+ mMax(xyz[3], xyz[4], xyz[5])
+{
+ if (!sorted) this->sort();
+}
+
+
+template<class VectorType>
+inline
+BBox<VectorType>::BBox(const BBox& other):
+ mMin(other.mMin), mMax(other.mMax)
+{
+}
+
+
+////////////////////////////////////////
+
+
+template<class VectorType>
+inline bool
+BBox<VectorType>::empty() const
+{
+ if (boost::is_integral<ElementType>::value) {
+ // min and max are inclusive for integral ElementType
+ return (mMin[0] > mMax[0] || mMin[1] > mMax[1] || mMin[2] > mMax[2]);
+ }
+ return mMin[0] >= mMax[0] || mMin[1] >= mMax[1] || mMin[2] >= mMax[2];
+}
+
+
+template<class VectorType>
+inline bool
+BBox<VectorType>::operator==(const BBox& rhs) const
+{
+ if (boost::is_integral<ElementType>::value) {
+ return mMin == rhs.min() && mMax == rhs.max();
+ } else {
+ return math::isApproxEqual(mMin, rhs.min()) && math::isApproxEqual(mMax, rhs.max());
+ }
+}
+
+
+template<class VectorType>
+inline void
+BBox<VectorType>::sort()
+{
+ VectorType tMin(mMin), tMax(mMax);
+ for (size_t i = 0; i < 3; ++i) {
+ mMin[i] = std::min(tMin[i], tMax[i]);
+ mMax[i] = std::max(tMin[i], tMax[i]);
+ }
+}
+
+
+template<class VectorType>
+inline bool
+BBox<VectorType>::isSorted() const
+{
+ if (boost::is_integral<ElementType>::value) {
+ return (mMin[0] <= mMax[0] && mMin[1] <= mMax[1] && mMin[2] <= mMax[2]);
+ } else {
+ ElementType t = tolerance<ElementType>::value();
+ return (mMin[0] < (mMax[0] + t) && mMin[1] < (mMax[1] + t) && mMin[2] < (mMax[2] + t));
+ }
+}
+
+
+template<class VectorType>
+inline Vec3d
+BBox<VectorType>::getCenter() const
+{
+ return (Vec3d(mMin.asPointer()) + Vec3d(mMax.asPointer())) * 0.5;
+}
+
+
+template<class VectorType>
+inline VectorType
+BBox<VectorType>::extents() const
+{
+ if (boost::is_integral<ElementType>::value) {
+ return (mMax - mMin) + VectorType(1, 1, 1);
+ } else {
+ return (mMax - mMin);
+ }
+}
+
+
+template<class VectorType>
+inline size_t
+BBox<VectorType>::maxExtent() const
+{
+ VectorType e = extents();
+ if (e[0] > e[1] && e[0] > e[2]) return 0;
+ else if (e[1] > e[2]) return 1;
+ return 2;
+}
+
+
+////////////////////////////////////////
+
+
+template<class VectorType>
+inline bool
+BBox<VectorType>::isInside(const VectorType& xyz) const
+{
+ if (boost::is_integral<ElementType>::value) {
+ return xyz[0] >= mMin[0] && xyz[0] <= mMax[0] &&
+ xyz[1] >= mMin[1] && xyz[1] <= mMax[1] &&
+ xyz[2] >= mMin[2] && xyz[2] <= mMax[2];
+ } else {
+ ElementType t = tolerance<ElementType>::value();
+ return xyz[0] > (mMin[0]-t) && xyz[0] < (mMax[0]+t) &&
+ xyz[1] > (mMin[1]-t) && xyz[1] < (mMax[1]+t) &&
+ xyz[2] > (mMin[2]-t) && xyz[2] < (mMax[2]+t);
+ }
+}
+
+
+template<class VectorType>
+inline bool
+BBox<VectorType>::isInside(const BBox& b) const
+{
+ if (boost::is_integral<ElementType>::value) {
+ return b.min()[0] >= mMin[0] && b.max()[0] <= mMax[0] &&
+ b.min()[1] >= mMin[1] && b.max()[1] <= mMax[1] &&
+ b.min()[2] >= mMin[2] && b.max()[2] <= mMax[2];
+ } else {
+ ElementType t = tolerance<ElementType>::value();
+ return (b.min()[0]-t) > mMin[0] && (b.max()[0]+t) < mMax[0] &&
+ (b.min()[1]-t) > mMin[1] && (b.max()[1]+t) < mMax[1] &&
+ (b.min()[2]-t) > mMin[2] && (b.max()[2]+t) < mMax[2];
+ }
+}
+
+
+template<class VectorType>
+inline bool
+BBox<VectorType>::hasOverlap(const BBox& b) const
+{
+ if (boost::is_integral<ElementType>::value) {
+ return mMax[0] >= b.min()[0] && mMin[0] <= b.max()[0] &&
+ mMax[1] >= b.min()[1] && mMin[1] <= b.max()[1] &&
+ mMax[2] >= b.min()[2] && mMin[2] <= b.max()[2];
+ } else {
+ ElementType t = tolerance<ElementType>::value();
+ return mMax[0] > (b.min()[0]-t) && mMin[0] < (b.max()[0]+t) &&
+ mMax[1] > (b.min()[1]-t) && mMin[1] < (b.max()[1]+t) &&
+ mMax[2] > (b.min()[2]-t) && mMin[2] < (b.max()[2]+t);
+ }
+}
+
+
+////////////////////////////////////////
+
+
+template<class VectorType>
+inline void
+BBox<VectorType>::expand(ElementType dx)
+{
+ dx = std::abs(dx);
+ for (size_t i = 0; i < 3; ++i) {
+ mMin[i] -= dx;
+ mMax[i] += dx;
+ }
+}
+
+
+template<class VectorType>
+inline void
+BBox<VectorType>::expand(const VectorType& xyz)
+{
+ for (size_t i = 0; i < 3; ++i) {
+ mMin[i] = std::min(mMin[i], xyz[i]);
+ mMax[i] = std::max(mMax[i], xyz[i]);
+ }
+}
+
+
+template<class VectorType>
+inline void
+BBox<VectorType>::expand(const BBox& b)
+{
+ for (size_t i = 0; i < 3; ++i) {
+ mMin[i] = std::min(mMin[i], b.min()[i]);
+ mMax[i] = std::max(mMax[i], b.max()[i]);
+ }
+}
+
+template<class VectorType>
+inline void
+BBox<VectorType>::expand(const VectorType& xyzMin, const ElementType& length)
+{
+ const ElementType size = boost::is_integral<ElementType>::value ? length-1 : length;
+ for (size_t i = 0; i < 3; ++i) {
+ mMin[i] = std::min(mMin[i], xyzMin[i]);
+ mMax[i] = std::max(mMax[i], xyzMin[i] + size);
+ }
+}
+
+
+template<class VectorType>
+inline void
+BBox<VectorType>::translate(const VectorType& dx)
+{
+ mMin += dx;
+ mMax += dx;
+}
+
+
+////////////////////////////////////////
+
+
+template<class VectorType>
+inline std::ostream&
+operator<<(std::ostream& os, const BBox<VectorType>& b)
+{
+ os << b.min() << " -> " << b.max();
+ return os;
+}
+
+} // namespace math
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_MATH_BBOX_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/math/Coord.h b/extern/openvdb/internal/openvdb/math/Coord.h
new file mode 100644
index 00000000000..b33f15e4f1f
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/math/Coord.h
@@ -0,0 +1,477 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+
+#ifndef OPENVDB_MATH_COORD_HAS_BEEN_INCLUDED
+#define OPENVDB_MATH_COORD_HAS_BEEN_INCLUDED
+
+#define NOMINMAX
+
+#include <openvdb/Platform.h>
+#include "Math.h"
+#include "Vec3.h"
+
+namespace tbb { class split; } // forward declaration
+
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace math {
+
+/// @brief Signed (x, y, z) integer coordinates
+class Coord
+{
+public:
+ typedef int32_t Int32;
+ typedef uint32_t Index32;
+ typedef Vec3<Int32> Vec3i;
+ typedef Vec3<Index32> Vec3I;
+
+ typedef Int32 ValueType;
+ typedef std::numeric_limits<ValueType> Limits;
+
+ Coord()
+ { mVec[0] = mVec[1] = mVec[2] = 0; }
+ explicit Coord(Int32 xyz)
+ { mVec[0] = mVec[1] = mVec[2] = xyz; }
+ Coord(Int32 x, Int32 y, Int32 z)
+ { mVec[0] = x; mVec[1] = y; mVec[2] = z; }
+ Coord(Index32 x, Index32 y, Index32 z)
+ { mVec[0] = Int32(x); mVec[1] = Int32(y); mVec[2] = Int32(z); }
+ explicit Coord(const Vec3i& v)
+ { mVec[0] = v[0]; mVec[1] = v[1]; mVec[2] = v[2]; }
+ explicit Coord(const Vec3I& v)
+ { mVec[0] = Int32(v[0]); mVec[1] = Int32(v[1]); mVec[2] = Int32(v[2]); }
+ explicit Coord(const Int32* v)
+ { mVec[0] = v[0]; mVec[1] = v[1]; mVec[2] = v[2]; }
+
+ static const Coord& min() { static const Coord sMin(Limits::min()); return sMin; }
+ static const Coord& max() { static const Coord sMax(Limits::max()); return sMax; }
+
+ /// @brief Return @a xyz rounded to the closest integer coordinates
+ /// (cell centered conversion).
+ template<typename T> static Coord round(const Vec3<T>& xyz)
+ {
+ return Coord(static_cast<int>(Round(xyz[0])),
+ static_cast<int>(Round(xyz[1])),
+ static_cast<int>(Round(xyz[2])));
+ }
+ /// @brief Return the largest integer coordinates that are not greater
+ /// than @a xyz (node centered conversion).
+ template<typename T> static Coord floor(const Vec3<T>& xyz)
+ {
+ return Coord(static_cast<int>(Floor(xyz[0])),
+ static_cast<int>(Floor(xyz[1])),
+ static_cast<int>(Floor(xyz[2])));
+ }
+
+ /// @brief Return the largest integer coordinates that are not greater
+ /// than @a xyz+1 (node centered conversion).
+ template<typename T> static Coord ceil(const Vec3<T>& xyz)
+ {
+ return Coord(static_cast<int>(Ceil(xyz[0])),
+ static_cast<int>(Ceil(xyz[1])),
+ static_cast<int>(Ceil(xyz[2])));
+ }
+
+ Coord& reset(Int32 x, Int32 y, Int32 z)
+ {
+ mVec[0] = x; mVec[1] = y; mVec[2] = z;
+ this->dirty();
+ return *this;
+ }
+ Coord& reset(Index32 x, Index32 y, Index32 z)
+ {
+ return this->reset(Int32(x), Int32(y), Int32(z));
+ }
+ Coord& reset(Int32 xyz) { return this->reset(xyz, xyz, xyz); }
+ Coord& setX(Int32 x) { mVec[0] = x; dirty(); return *this; }
+ Coord& setY(Int32 y) { mVec[1] = y; dirty(); return *this; }
+ Coord& setZ(Int32 z) { mVec[2] = z; dirty(); return *this; }
+ Coord& offset(Int32 dx, Int32 dy, Int32 dz)
+ {
+ mVec[0]+=dx; mVec[1]+=dy; mVec[2]+=dz;
+ this->dirty();
+ return *this;
+ }
+ Coord& offset(Int32 n) { return this->offset(n, n, n); }
+ Coord offsetBy(Int32 dx, Int32 dy, Int32 dz) const
+ {
+ return Coord(mVec[0] + dx, mVec[1] + dy, mVec[2] + dz);
+ }
+ Coord offsetBy(Int32 n) const { return offsetBy(n, n, n); }
+
+ Coord& operator+=(const Coord& rhs)
+ {
+ mVec[0] += rhs[0]; mVec[1] += rhs[1]; mVec[2] += rhs[2]; return *this;
+ }
+ Coord& operator-=(const Coord& rhs)
+ {
+ mVec[0] -= rhs[0]; mVec[1] -= rhs[1]; mVec[2] -= rhs[2]; return *this;
+ }
+ Coord operator+(const Coord& rhs) const
+ {
+ return Coord(mVec[0] + rhs[0], mVec[1] + rhs[1], mVec[2] + rhs[2]);
+ }
+ Coord operator-(const Coord& rhs) const
+ {
+ return Coord(mVec[0] - rhs[0], mVec[1] - rhs[1], mVec[2] - rhs[2]);
+ }
+ Coord operator-() const { return Coord(-mVec[0], -mVec[1], -mVec[2]); }
+
+ Coord operator>> (size_t n) const { return Coord(mVec[0]>>n, mVec[1]>>n, mVec[2]>>n); }
+ Coord operator<< (size_t n) const { return Coord(mVec[0]<<n, mVec[1]<<n, mVec[2]<<n); }
+ Coord& operator<<=(size_t n) { mVec[0]<<=n; mVec[1]<<=n; mVec[2]<<=n; dirty(); return *this; }
+ Coord& operator>>=(size_t n) { mVec[0]>>=n; mVec[1]>>=n; mVec[2]>>=n; dirty(); return *this; }
+ Coord operator& (Int32 n) const { return Coord(mVec[0] & n, mVec[1] & n, mVec[2] & n); }
+ Coord operator| (Int32 n) const { return Coord(mVec[0] | n, mVec[1] | n, mVec[2] | n); }
+ Coord& operator&= (Int32 n) { mVec[0]&=n; mVec[1]&=n; mVec[2]&=n; dirty(); return *this; }
+ Coord& operator|= (Int32 n) { mVec[0]|=n; mVec[1]|=n; mVec[2]|=n; dirty(); return *this; }
+
+ Int32 x() const { return mVec[0]; }
+ Int32 y() const { return mVec[1]; }
+ Int32 z() const { return mVec[2]; }
+ Int32 operator[](size_t i) const { assert(i < 3); return mVec[i]; }
+ Int32& x() { dirty(); return mVec[0]; }
+ Int32& y() { dirty(); return mVec[1]; }
+ Int32& z() { dirty(); return mVec[2]; }
+ Int32& operator[](size_t i) { assert(i < 3); dirty(); return mVec[i]; }
+
+ const Int32* asPointer() const { return mVec; }
+ Int32* asPointer() { dirty(); return mVec; }
+ Vec3d asVec3d() const { return Vec3d(double(mVec[0]), double(mVec[1]), double(mVec[2])); }
+ Vec3s asVec3s() const { return Vec3s(float(mVec[0]), float(mVec[1]), float(mVec[2])); }
+ Vec3i asVec3i() const { return Vec3i(mVec); }
+ Vec3I asVec3I() const
+ {
+ return Vec3I(Index32(mVec[0]), Index32(mVec[1]), Index32(mVec[2]));
+ }
+ void asXYZ(Int32& x, Int32& y, Int32& z) const { x = mVec[0]; y = mVec[1]; z = mVec[2]; }
+
+ bool operator==(const Coord& rhs) const
+ {
+ return (mVec[0] == rhs.mVec[0] && mVec[1] == rhs.mVec[1] && mVec[2] == rhs.mVec[2]);
+ }
+ bool operator!=(const Coord& rhs) const { return !(*this == rhs); }
+
+ /// Lexicographic less than
+ bool operator<(const Coord& rhs) const
+ {
+ return this->x() < rhs.x() ? true : this->x() > rhs.x() ? false
+ : this->y() < rhs.y() ? true : this->y() > rhs.y() ? false
+ : this->z() < rhs.z() ? true : false;
+ }
+ /// Lexicographic less than or equal to
+ bool operator<=(const Coord& rhs) const
+ {
+ return this->x() < rhs.x() ? true : this->x() > rhs.x() ? false
+ : this->y() < rhs.y() ? true : this->y() > rhs.y() ? false
+ : this->z() <=rhs.z() ? true : false;
+ }
+ /// Lexicographic greater than
+ bool operator>(const Coord& rhs) const { return !(*this <= rhs); }
+ /// Lexicographic greater than or equal to
+ bool operator>=(const Coord& rhs) const { return !(*this < rhs); }
+
+ //HashType hash() { if (!mHash) { mHash = ...; } return mHash; }
+
+ /// Perform a component-wise minimum with the other Coord.
+ void minComponent(const Coord& other)
+ {
+ mVec[0] = std::min(mVec[0], other.mVec[0]);
+ mVec[1] = std::min(mVec[1], other.mVec[1]);
+ mVec[2] = std::min(mVec[2], other.mVec[2]);
+ }
+
+ /// Perform a component-wise maximum with the other Coord.
+ void maxComponent(const Coord& other)
+ {
+ mVec[0] = std::max(mVec[0], other.mVec[0]);
+ mVec[1] = std::max(mVec[1], other.mVec[1]);
+ mVec[2] = std::max(mVec[2], other.mVec[2]);
+ }
+
+ /// Return the component-wise minimum of the two Coords.
+ static inline Coord minComponent(const Coord& lhs, const Coord& rhs)
+ {
+ return Coord(std::min(lhs.x(), rhs.x()),
+ std::min(lhs.y(), rhs.y()),
+ std::min(lhs.z(), rhs.z()));
+ }
+
+ /// Return the component-wise maximum of the two Coords.
+ static inline Coord maxComponent(const Coord& lhs, const Coord& rhs)
+ {
+ return Coord(std::max(lhs.x(), rhs.x()),
+ std::max(lhs.y(), rhs.y()),
+ std::max(lhs.z(), rhs.z()));
+ }
+ static inline bool lessThan(const Coord& a, const Coord& b)
+ {
+ return (a[0] < b[0] || a[1] < b[1] || a[2] < b[2]);
+ }
+ void read(std::istream& is) { is.read(reinterpret_cast<char*>(mVec), sizeof(mVec)); }
+ void write(std::ostream& os) const
+ {
+ os.write(reinterpret_cast<const char*>(mVec), sizeof(mVec));
+ }
+
+private:
+ //no-op for now
+ void dirty() { /*mHash.clear();*/ }
+
+ Int32 mVec[3];
+ //HashType mHash;
+}; // class Coord
+
+
+////////////////////////////////////////
+
+
+/// @brief Axis-aligned bounding box of signed integer coordinates
+/// @note The range of the integer coordinates, [min, max], is inclusive.
+/// Thus, a bounding box with min = max is not empty but rather encloses
+/// a single coordinate.
+class CoordBBox
+{
+public:
+ typedef uint64_t Index64;
+ typedef Coord::ValueType ValueType;
+
+ /// @brief The default constructor produces an empty bounding box.
+ CoordBBox(): mMin(Coord::max()), mMax(Coord::min()) {}
+ /// @brief Construct a bounding box with the given @a min and @a max bounds.
+ CoordBBox(const Coord& min, const Coord& max): mMin(min), mMax(max) {}
+ /// @brief Splitting constructor for use in TBB ranges
+ /// @note The other bounding box is assumed to be divisible.
+ CoordBBox(CoordBBox& other, const tbb::split&): mMin(other.mMin), mMax(other.mMax)
+ {
+ assert(this->is_divisible());
+ const size_t n = this->maxExtent();
+ mMax[n] = (mMin[n] + mMax[n]) >> 1;
+ other.mMin[n] = mMax[n] + 1;
+ }
+
+ static CoordBBox createCube(const Coord& min, ValueType dim)
+ {
+ return CoordBBox(min, min.offsetBy(dim - 1));
+ }
+
+ /// Return an "infinite" bounding box, as defined by the Coord value range.
+ static CoordBBox inf() { return CoordBBox(Coord::min(), Coord::max()); }
+
+ const Coord& min() const { return mMin; }
+ const Coord& max() const { return mMax; }
+
+ Coord& min() { return mMin; }
+ Coord& max() { return mMax; }
+
+ void reset(const Coord& min, const Coord& max) { mMin = min; mMax = max; }
+ void resetToCube(const Coord& min, ValueType dim) { mMin = min; mMax = min.offsetBy(dim - 1); }
+
+ /// @note The start coordinate is inclusive.
+ Coord getStart() const { return mMin; }
+ /// @note The end coordinate is exclusive.
+ Coord getEnd() const { return mMax.offsetBy(1); }
+
+ bool operator==(const CoordBBox& rhs) const { return mMin == rhs.mMin && mMax == rhs.mMax; }
+ bool operator!=(const CoordBBox& rhs) const { return !(*this == rhs); }
+
+ bool empty() const { return (mMin[0] > mMax[0] || mMin[1] > mMax[1] || mMin[2] > mMax[2]); }
+ //@{
+ /// Return @c true if this bounding box is nonempty
+ operator bool() const { return !this->empty(); }
+ bool hasVolume() const { return !this->empty(); }
+ //@}
+
+ /// Return the floating-point position of the center of this bounding box.
+ Vec3d getCenter() const { return 0.5 * Vec3d((mMin + mMax).asPointer()); }
+
+ /// @brief Return the dimensions of the coordinates spanned by this bounding box.
+ /// @note Since coordinates are inclusive, a bounding box with min = max
+ /// has dimensions of (1, 1, 1).
+ Coord dim() const { return mMax.offsetBy(1) - mMin; }
+ /// @todo deprecate - use dim instead
+ Coord extents() const { return this->dim(); }
+ /// @brief Return the integer volume of coordinates spanned by this bounding box.
+ /// @note Since coordinates are inclusive, a bounding box with min = max has volume one.
+ Index64 volume() const
+ {
+ const Coord d = this->dim();
+ return Index64(d[0]) * Index64(d[1]) * Index64(d[2]);
+ }
+ /// Return @c true if this bounding box can be subdivided [mainly for use by TBB].
+ bool is_divisible() const { return mMin[0]<mMax[0] && mMin[1]<mMax[1] && mMin[2]<mMax[2]; }
+
+ /// Return the index (0, 1 or 2) of the longest axis.
+ size_t maxExtent() const
+ {
+ const Coord d = this->dim();
+ return (d[0] > d[1] && d[0] > d[2]) ? 0 : (d[1] > d[2]) ? 1 : 2;
+ }
+
+ /// Return @c true if point (x, y, z) is inside this bounding box.
+ bool isInside(const Coord& xyz) const
+ {
+ return !(Coord::lessThan(xyz,mMin) || Coord::lessThan(mMax,xyz));
+ }
+
+ /// Return @c true if the given bounding box is inside this bounding box.
+ bool isInside(const CoordBBox& b) const
+ {
+ return !(Coord::lessThan(b.mMin,mMin) || Coord::lessThan(mMax,b.mMax));
+ }
+
+ /// Return @c true if the given bounding box overlaps with this bounding box.
+ bool hasOverlap(const CoordBBox& b) const
+ {
+ return !(Coord::lessThan(mMax,b.mMin) || Coord::lessThan(b.mMax,mMin));
+ }
+
+ /// Pad this bounding box with the specified padding.
+ void expand(ValueType padding)
+ {
+ mMin.offset(-padding);
+ mMax.offset( padding);
+ }
+ /// Expand this bounding box to enclose point (x, y, z).
+ void expand(const Coord& xyz)
+ {
+ mMin.minComponent(xyz);
+ mMax.maxComponent(xyz);
+ }
+ /// Union this bounding box with the given bounding box.
+ void expand(const CoordBBox& bbox)
+ {
+ mMin.minComponent(bbox.min());
+ mMax.maxComponent(bbox.max());
+ }
+ /// Intersect this bounding box with the given bounding box.
+ void intersect(const CoordBBox& bbox)
+ {
+ mMin.maxComponent(bbox.min());
+ mMax.minComponent(bbox.max());
+ }
+ /// @brief Union this bounding box with the cubical bounding box
+ /// of the given size and with the given minimum coordinates.
+ void expand(const Coord& min, Coord::ValueType dim)
+ {
+ mMin.minComponent(min);
+ mMax.maxComponent(min.offsetBy(dim-1));
+ }
+ /// Translate this bounding box by @f$(t_x, t_y, t_z)@f$.
+ void translate(const Coord& t) { mMin += t; mMax += t; }
+
+ /// Unserialize this bounding box from the given stream.
+ void read(std::istream& is) { mMin.read(is); mMax.read(is); }
+ /// Serialize this bounding box to the given stream.
+ void write(std::ostream& os) const { mMin.write(os); mMax.write(os); }
+
+private:
+ Coord mMin, mMax;
+}; // class CoordBBox
+
+
+////////////////////////////////////////
+
+
+inline std::ostream& operator<<(std::ostream& os, const Coord& xyz)
+{
+ os << xyz.asVec3i(); return os;
+}
+
+
+//@{
+/// Allow a Coord to be added to or subtracted from a Vec3.
+template<typename T>
+inline Vec3<typename promote<T, typename Coord::ValueType>::type>
+operator+(const Vec3<T>& v0, const Coord& v1)
+{
+ Vec3<typename promote<T, typename Coord::ValueType>::type> result(v0);
+ result[0] += v1[0];
+ result[1] += v1[1];
+ result[2] += v1[2];
+ return result;
+}
+
+template<typename T>
+inline Vec3<typename promote<T, typename Coord::ValueType>::type>
+operator+(const Coord& v1, const Vec3<T>& v0)
+{
+ Vec3<typename promote<T, typename Coord::ValueType>::type> result(v0);
+ result[0] += v1[0];
+ result[1] += v1[1];
+ result[2] += v1[2];
+ return result;
+}
+//@}
+
+
+//@{
+/// Allow a Coord to be subtracted from a Vec3.
+template <typename T>
+inline Vec3<typename promote<T, Coord::ValueType>::type>
+operator-(const Vec3<T>& v0, const Coord& v1)
+{
+ Vec3<typename promote<T, Coord::ValueType>::type> result(v0);
+ result[0] -= v1[0];
+ result[1] -= v1[1];
+ result[2] -= v1[2];
+ return result;
+}
+
+template <typename T>
+inline Vec3<typename promote<T, Coord::ValueType>::type>
+operator-(const Coord& v1, const Vec3<T>& v0)
+{
+ Vec3<typename promote<T, Coord::ValueType>::type> result(v0);
+ result[0] -= v1[0];
+ result[1] -= v1[1];
+ result[2] -= v1[2];
+ return -result;
+}
+//@}
+
+inline std::ostream&
+operator<<(std::ostream& os, const CoordBBox& b)
+{
+ os << b.min() << " -> " << b.max();
+ return os;
+}
+
+} // namespace math
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_MATH_COORD_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/math/FiniteDifference.h b/extern/openvdb/internal/openvdb/math/FiniteDifference.h
new file mode 100644
index 00000000000..6e02a7432fc
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/math/FiniteDifference.h
@@ -0,0 +1,2280 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+//
+/// @file FiniteDifference.h
+
+#ifndef OPENVDB_MATH_FINITEDIFFERENCE_HAS_BEEN_INCLUDED
+#define OPENVDB_MATH_FINITEDIFFERENCE_HAS_BEEN_INCLUDED
+
+#include <openvdb/Types.h>
+#include "Math.h"
+#include "Coord.h"
+#include "Vec3.h"
+
+#include <boost/algorithm/string/case_conv.hpp>
+#include <boost/algorithm/string/trim.hpp>
+
+#ifdef DWA_OPENVDB
+#include <simd/Simd.h>
+#endif
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace math {
+
+
+////////////////////////////////////////
+
+
+/// @brief Different discrete schemes used in the first derivatives.
+// Add new items to the *end* of this list, and update NUM_DS_SCHEMES.
+enum DScheme {
+ UNKNOWN_DS = -1,
+ CD_2NDT = 0, // center difference, 2nd order, but the result must be divided by 2
+ CD_2ND, // center difference, 2nd order
+ CD_4TH, // center difference, 4th order
+ CD_6TH, // center difference, 6th order
+ FD_1ST, // forward difference, 1st order
+ FD_2ND, // forward difference, 2nd order
+ FD_3RD, // forward difference, 3rd order
+ BD_1ST, // backward difference, 1st order
+ BD_2ND, // backward difference, 2nd order
+ BD_3RD, // backward difference, 3rd order
+ FD_WENO5, // forward difference, weno5
+ BD_WENO5, // backward difference, weno5
+ FD_HJWENO5, // forward differene, HJ-weno5
+ BD_HJWENO5 // backward difference, HJ-weno5
+};
+
+enum { NUM_DS_SCHEMES = BD_HJWENO5 + 1 };
+
+
+inline std::string
+dsSchemeToString(DScheme dss)
+{
+ std::string ret;
+ switch (dss) {
+ case UNKNOWN_DS: ret = "unknown_ds"; break;
+ case CD_2NDT: ret = "cd_2ndt"; break;
+ case CD_2ND: ret = "cd_2nd"; break;
+ case CD_4TH: ret = "cd_4th"; break;
+ case CD_6TH: ret = "cd_6th"; break;
+ case FD_1ST: ret = "fd_1st"; break;
+ case FD_2ND: ret = "fd_2nd"; break;
+ case FD_3RD: ret = "fd_3rd"; break;
+ case BD_1ST: ret = "bd_1st"; break;
+ case BD_2ND: ret = "bd_2nd"; break;
+ case BD_3RD: ret = "bd_3rd"; break;
+ case FD_WENO5: ret = "fd_weno5"; break;
+ case BD_WENO5: ret = "bd_weno5"; break;
+ case FD_HJWENO5: ret = "fd_hjweno5"; break;
+ case BD_HJWENO5: ret = "bd_hjweno5"; break;
+ }
+ return ret;
+}
+
+inline DScheme
+stringToDScheme(const std::string& s)
+{
+ DScheme ret = UNKNOWN_DS;
+
+ std::string str = s;
+ boost::trim(str);
+ boost::to_lower(str);
+
+ if (str == dsSchemeToString(CD_2NDT)) {
+ ret = CD_2NDT;
+ } else if (str == dsSchemeToString(CD_2ND)) {
+ ret = CD_2ND;
+ } else if (str == dsSchemeToString(CD_4TH)) {
+ ret = CD_4TH;
+ } else if (str == dsSchemeToString(CD_6TH)) {
+ ret = CD_6TH;
+ } else if (str == dsSchemeToString(FD_1ST)) {
+ ret = FD_1ST;
+ } else if (str == dsSchemeToString(FD_2ND)) {
+ ret = FD_2ND;
+ } else if (str == dsSchemeToString(FD_3RD)) {
+ ret = FD_3RD;
+ } else if (str == dsSchemeToString(BD_1ST)) {
+ ret = BD_1ST;
+ } else if (str == dsSchemeToString(BD_2ND)) {
+ ret = BD_2ND;
+ } else if (str == dsSchemeToString(BD_3RD)) {
+ ret = BD_3RD;
+ } else if (str == dsSchemeToString(FD_WENO5)) {
+ ret = FD_WENO5;
+ } else if (str == dsSchemeToString(BD_WENO5)) {
+ ret = BD_WENO5;
+ } else if (str == dsSchemeToString(FD_HJWENO5)) {
+ ret = FD_HJWENO5;
+ } else if (str == dsSchemeToString(BD_HJWENO5)) {
+ ret = BD_HJWENO5;
+ }
+
+ return ret;
+}
+
+inline std::string
+dsSchemeToMenuName(DScheme dss)
+{
+ std::string ret;
+ switch (dss) {
+ case UNKNOWN_DS: ret = "Unknown DS scheme"; break;
+ case CD_2NDT: ret = "Twice 2nd-order center difference"; break;
+ case CD_2ND: ret = "2nd-order center difference"; break;
+ case CD_4TH: ret = "4th-order center difference"; break;
+ case CD_6TH: ret = "6th-order center difference"; break;
+ case FD_1ST: ret = "1st-order forward difference"; break;
+ case FD_2ND: ret = "2nd-order forward difference"; break;
+ case FD_3RD: ret = "3rd-order forward difference"; break;
+ case BD_1ST: ret = "1st-order backward difference"; break;
+ case BD_2ND: ret = "2nd-order backward difference"; break;
+ case BD_3RD: ret = "3rd-order backward difference"; break;
+ case FD_WENO5: ret = "5th-order WENO forward difference"; break;
+ case BD_WENO5: ret = "5th-order WENO backward difference"; break;
+ case FD_HJWENO5: ret = "5th-order HJ-WENO forward difference"; break;
+ case BD_HJWENO5: ret = "5th-order HJ-WENO backward difference"; break;
+ }
+ return ret;
+}
+
+
+
+////////////////////////////////////////
+
+
+/// @brief Different discrete schemes used in the second derivatives.
+// Add new items to the *end* of this list, and update NUM_DD_SCHEMES.
+enum DDScheme {
+ UNKNOWN_DD = -1,
+ CD_SECOND = 0, // center difference, 2nd order
+ CD_FOURTH, // center difference, 4th order
+ CD_SIXTH // center difference, 6th order
+};
+
+enum { NUM_DD_SCHEMES = CD_SIXTH + 1 };
+
+
+////////////////////////////////////////
+
+
+/// @brief Biased Gradients are limited to non-centered differences
+// Add new items to the *end* of this list, and update NUM_BIAS_SCHEMES.
+enum BiasedGradientScheme {
+ UNKNOWN_BIAS = -1,
+ FIRST_BIAS = 0, // uses FD_1ST & BD_1ST
+ SECOND_BIAS, // uses FD_2ND & BD_2ND
+ THIRD_BIAS, // uses FD_3RD & BD_3RD
+ WENO5_BIAS, // uses WENO5
+ HJWENO5_BIAS // uses HJWENO5
+};
+
+enum { NUM_BIAS_SCHEMES = HJWENO5_BIAS + 1 };
+
+inline std::string
+biasedGradientSchemeToString(BiasedGradientScheme bgs)
+{
+ std::string ret;
+ switch (bgs) {
+ case UNKNOWN_BIAS: ret = "unknown_bias"; break;
+ case FIRST_BIAS: ret = "first_bias"; break;
+ case SECOND_BIAS: ret = "second_bias"; break;
+ case THIRD_BIAS: ret = "third_bias"; break;
+ case WENO5_BIAS: ret = "weno5_bias"; break;
+ case HJWENO5_BIAS: ret = "hjweno5_bias"; break;
+ }
+ return ret;
+}
+
+inline BiasedGradientScheme
+stringToBiasedGradientScheme(const std::string& s)
+{
+ BiasedGradientScheme ret = UNKNOWN_BIAS;
+
+ std::string str = s;
+ boost::trim(str);
+ boost::to_lower(str);
+
+ if (str == biasedGradientSchemeToString(FIRST_BIAS)) {
+ ret = FIRST_BIAS;
+ } else if (str == biasedGradientSchemeToString(SECOND_BIAS)) {
+ ret = SECOND_BIAS;
+ } else if (str == biasedGradientSchemeToString(THIRD_BIAS)) {
+ ret = THIRD_BIAS;
+ } else if (str == biasedGradientSchemeToString(WENO5_BIAS)) {
+ ret = WENO5_BIAS;
+ } else if (str == biasedGradientSchemeToString(HJWENO5_BIAS)) {
+ ret = HJWENO5_BIAS;
+ }
+ return ret;
+}
+
+inline std::string
+biasedGradientSchemeToMenuName(BiasedGradientScheme bgs)
+{
+ std::string ret;
+ switch (bgs) {
+ case UNKNOWN_BIAS: ret = "Unknown biased gradient"; break;
+ case FIRST_BIAS: ret = "1st-order biased gradient"; break;
+ case SECOND_BIAS: ret = "2nd-order biased gradient"; break;
+ case THIRD_BIAS: ret = "3rd-order biased gradient"; break;
+ case WENO5_BIAS: ret = "5th-order WENO biased gradient"; break;
+ case HJWENO5_BIAS: ret = "5th-order HJ-WENO biased gradient"; break;
+ }
+ return ret;
+}
+
+////////////////////////////////////////
+
+
+/// @brief Temporal integrations schemes
+// Add new items to the *end* of this list, and update NUM_TEMPORAL_SCHEMES.
+enum TemporalIntegrationScheme {
+ UNKNOWN_TIS = -1,
+ TVD_RK1,//same as explicit Euler integration
+ TVD_RK2,
+ TVD_RK3
+};
+
+enum { NUM_TEMPORAL_SCHEMES = TVD_RK3 + 1 };
+
+inline std::string
+temporalIntegrationSchemeToString(TemporalIntegrationScheme tis)
+{
+ std::string ret;
+ switch (tis) {
+ case UNKNOWN_TIS: ret = "unknown_tis"; break;
+ case TVD_RK1: ret = "tvd_rk1"; break;
+ case TVD_RK2: ret = "tvd_rk2"; break;
+ case TVD_RK3: ret = "tvd_rk3"; break;
+ }
+ return ret;
+}
+
+inline TemporalIntegrationScheme
+stringToTemporalIntegrationScheme(const std::string& s)
+{
+ TemporalIntegrationScheme ret = UNKNOWN_TIS;
+
+ std::string str = s;
+ boost::trim(str);
+ boost::to_lower(str);
+
+ if (str == temporalIntegrationSchemeToString(TVD_RK1)) {
+ ret = TVD_RK1;
+ } else if (str == temporalIntegrationSchemeToString(TVD_RK2)) {
+ ret = TVD_RK2;
+ } else if (str == temporalIntegrationSchemeToString(TVD_RK3)) {
+ ret = TVD_RK3;
+ }
+
+ return ret;
+}
+
+inline std::string
+temporalIntegrationSchemeToMenuName(TemporalIntegrationScheme tis)
+{
+ std::string ret;
+ switch (tis) {
+ case UNKNOWN_TIS: ret = "Unknown temporal integration"; break;
+ case TVD_RK1: ret = "Forward Euler"; break;
+ case TVD_RK2: ret = "2nd-order Runge-Kutta"; break;
+ case TVD_RK3: ret = "3rd-order Runge-Kutta"; break;
+ }
+ return ret;
+}
+
+
+//@}
+
+
+/// @brief implimentation of nonimally fith-order finite-difference WENO.
+/// This function returns the numerical flux. See "High Order Finite Difference and
+/// Finite Volume WENO Schemes and Discontinuous Galerkin Methods for CFD" - Chi-Wang Shu
+/// ICASE Report No 2001-11 (page 6). Also see ICASE No 97-65 for a more complete reference
+/// (Shu, 1997)
+/// Given v1 = f(x-2dx), v2 = f(x-dx), v3 = f(x), v4 = f(x+dx), v5 = f(x+2dx),
+/// the returns and interpolated value f(x+dx/2) with the special property that
+/// ( f(x+dx/2) - f(x-dx/2) ) / dx = df/dx (x) + error,
+/// where the error is 5-order in smooth regions: O(dx) <= error <=O(dx^5)
+template<typename ValueType>
+inline ValueType
+WENO5(const ValueType& v1, const ValueType& v2, const ValueType& v3, const ValueType& v4, const ValueType& v5, float scale2 = 0.01)
+{
+ static const double C=13.0/12.0;
+ // Weno is formulated for non-dimensional equations, here the optional scale2
+ // is a reference value (squared) for the function being interpolated. For
+ // example if 'v' is of order 1000, then scale2 = 10^6 is ok. But in practice
+ // leave scale2 = 1.
+ const double eps=1e-6*scale2;
+ // {\tilde \omega_k} = \gamma_k / ( \beta_k + \epsilon)^2 in Shu's ICASE report)
+ const double A1=0.1/math::Pow2(C*math::Pow2(v1-2*v2+v3)+0.25*math::Pow2(v1-4*v2+3.0*v3)+eps),
+ A2=0.6/math::Pow2(C*math::Pow2(v2-2*v3+v4)+0.25*math::Pow2(v2-v4)+eps),
+ A3=0.3/math::Pow2(C*math::Pow2(v3-2*v4+v5)+0.25*math::Pow2(3.0*v3-4*v4+v5)+eps);
+
+ return ValueType(A1*(2.0*v1 - 7.0*v2 + 11.0*v3) +
+ A2*(5.0*v3 - v2 + 2.0*v4) +
+ A3*(2.0*v3 + 5.0*v4 - v5))/(6.0*(A1+A2+A3));
+}
+
+
+template <typename Real>
+inline Real GudonovsNormSqrd(bool isOutside,
+ Real dP_xm, Real dP_xp,
+ Real dP_ym, Real dP_yp,
+ Real dP_zm, Real dP_zp)
+{
+ using math::Max;
+ using math::Min;
+ using math::Pow2;
+
+ const Real zero(0);
+ Real dPLen2;
+ if (isOutside) { // outside
+ dPLen2 = Max(Pow2(Max(dP_xm, zero)), Pow2(Min(dP_xp,zero))); // (dP/dx)2
+ dPLen2 += Max(Pow2(Max(dP_ym, zero)), Pow2(Min(dP_yp,zero))); // (dP/dy)2
+ dPLen2 += Max(Pow2(Max(dP_zm, zero)), Pow2(Min(dP_zp,zero))); // (dP/dz)2
+ } else { // inside
+ dPLen2 = Max(Pow2(Min(dP_xm, zero)), Pow2(Max(dP_xp,zero))); // (dP/dx)2
+ dPLen2 += Max(Pow2(Min(dP_ym, zero)), Pow2(Max(dP_yp,zero))); // (dP/dy)2
+ dPLen2 += Max(Pow2(Min(dP_zm, zero)), Pow2(Max(dP_zp,zero))); // (dP/dz)2
+ }
+ return dPLen2; // |\nabla\phi|^2
+};
+
+
+template <typename Real>
+inline Real GudonovsNormSqrd(bool isOutside, const Vec3<Real>& gradient_m, const Vec3<Real>& gradient_p)
+{
+ return GudonovsNormSqrd<Real>(isOutside,
+ gradient_m[0], gradient_p[0],
+ gradient_m[1], gradient_p[1],
+ gradient_m[2], gradient_p[2]);
+}
+
+
+#ifdef DWA_OPENVDB
+inline simd::Float4 simdMin(const simd::Float4& a, const simd::Float4& b) {
+ return simd::Float4(_mm_min_ps(a.base(), b.base()));
+}
+inline simd::Float4 simdMax(const simd::Float4& a, const simd::Float4& b) {
+ return simd::Float4(_mm_max_ps(a.base(), b.base()));
+}
+
+inline float simdSum(const simd::Float4& v);
+
+inline simd::Float4 Pow2(const simd::Float4& v) { return v * v; }
+
+template<>
+inline simd::Float4
+WENO5<simd::Float4>(const simd::Float4& v1, const simd::Float4& v2, const simd::Float4& v3,
+ const simd::Float4& v4, const simd::Float4& v5, float scale2)
+{
+ using math::Pow2;
+ typedef simd::Float4 F4;
+ const F4
+ C(13.0 / 12.0),
+ eps(1e-6 * scale2),
+ two(2.0), three(3.0), four(4.0), five(5.0), fourth(0.25),
+ A1 = F4(0.1) / Pow2(C*Pow2(v1-two*v2+v3) + fourth*Pow2(v1-four*v2+three*v3) + eps),
+ A2 = F4(0.6) / Pow2(C*Pow2(v2-two*v3+v4) + fourth*Pow2(v2-v4) + eps),
+ A3 = F4(0.3) / Pow2(C*Pow2(v3-two*v4+v5) + fourth*Pow2(three*v3-four*v4+v5) + eps);
+ return (A1 * (two * v1 - F4(7.0) * v2 + F4(11.0) * v3) +
+ A2 * (five * v3 - v2 + two * v4) +
+ A3 * (two * v3 + five * v4 - v5)) / (F4(6.0) * (A1 + A2 + A3));
+}
+
+
+inline float
+simdSum(const simd::Float4& v)
+{
+ // temp = { v3+v3, v2+v2, v1+v3, v0+v2 }
+ __m128 temp = _mm_add_ps(v.base(), _mm_movehl_ps(v.base(), v.base()));
+ // temp = { v3+v3, v2+v2, v1+v3, (v0+v2)+(v1+v3) }
+ temp = _mm_add_ss(temp, _mm_shuffle_ps(temp, temp, 1));
+ return _mm_cvtss_f32(temp);
+}
+
+inline float
+GudonovsNormSqrd(bool isOutside, const simd::Float4& dP_m, const simd::Float4& dP_p)
+{
+ const simd::Float4 zero(0.0);
+ simd::Float4 v = isOutside
+ ? simdMax(math::Pow2(simdMax(dP_m, zero)), math::Pow2(simdMin(dP_p, zero)))
+ : simdMax(math::Pow2(simdMin(dP_m, zero)), math::Pow2(simdMax(dP_p, zero)));
+ return simdSum(v);//should be v[0]+v[1]+v[2]
+}
+#endif
+
+template<DScheme DiffScheme>
+struct D1
+{
+ // random access version
+ template<typename Accessor>
+ static typename Accessor::ValueType inX(const Accessor& grid, const Coord& ijk);
+
+ template<typename Accessor>
+ static typename Accessor::ValueType inY(const Accessor& grid, const Coord& ijk);
+
+ template<typename Accessor>
+ static typename Accessor::ValueType inZ(const Accessor& grid, const Coord& ijk);
+
+ // stencil access version
+ template<typename Stencil>
+ static typename Stencil::ValueType inX(const Stencil& S);
+
+ template<typename Stencil>
+ static typename Stencil::ValueType inY(const Stencil& S);
+
+ template<typename Stencil>
+ static typename Stencil::ValueType inZ(const Stencil& S);
+};
+
+template<>
+struct D1<CD_2NDT>
+{
+ // the difference opperator
+ template <typename ValueType>
+ static ValueType difference(const ValueType& xp1, const ValueType& xm1) {
+ return xp1 - xm1;
+ }
+
+ // random access version
+ template<typename Accessor>
+ static typename Accessor::ValueType inX(const Accessor& grid, const Coord& ijk)
+ {
+ return difference(grid.getValue(ijk.offsetBy(1, 0, 0)), grid.getValue(ijk.offsetBy(-1, 0, 0)));
+ }
+
+ template<typename Accessor>
+ static typename Accessor::ValueType inY(const Accessor& grid, const Coord& ijk)
+ {
+ return difference(grid.getValue(ijk.offsetBy(0, 1, 0)), grid.getValue(ijk.offsetBy( 0, -1, 0)));
+ }
+
+ template<typename Accessor>
+ static typename Accessor::ValueType inZ(const Accessor& grid, const Coord& ijk)
+ {
+ return difference(grid.getValue(ijk.offsetBy(0, 0, 1)), grid.getValue(ijk.offsetBy( 0, 0, -1)));
+ }
+
+ // stencil access version
+ template<typename Stencil>
+ static typename Stencil::ValueType inX(const Stencil& S)
+ {
+ return difference( S.template getValue< 1, 0, 0>(), S.template getValue<-1, 0, 0>());
+ }
+
+ template<typename Stencil>
+ static typename Stencil::ValueType inY(const Stencil& S)
+ {
+ return difference( S.template getValue< 0, 1, 0>(), S.template getValue< 0,-1, 0>());
+ }
+
+ template<typename Stencil>
+ static typename Stencil::ValueType inZ(const Stencil& S)
+ {
+ return difference( S.template getValue< 0, 0, 1>(), S.template getValue< 0, 0,-1>());
+ }
+};
+
+template<>
+struct D1<CD_2ND>
+{
+
+ // the difference opperator
+ template <typename ValueType>
+ static ValueType difference(const ValueType& xp1, const ValueType& xm1) {
+ return (xp1 - xm1)*ValueType(0.5);
+ }
+
+
+ // random access
+ template<typename Accessor>
+ static typename Accessor::ValueType inX(const Accessor& grid, const Coord& ijk)
+ {
+ return difference(grid.getValue(ijk.offsetBy(1, 0, 0)), grid.getValue(ijk.offsetBy(-1, 0, 0)));
+ }
+
+ template<typename Accessor>
+ static typename Accessor::ValueType inY(const Accessor& grid, const Coord& ijk)
+ {
+ return difference(grid.getValue(ijk.offsetBy(0, 1, 0)), grid.getValue(ijk.offsetBy( 0, -1, 0)));
+ }
+
+ template<typename Accessor>
+ static typename Accessor::ValueType inZ(const Accessor& grid, const Coord& ijk)
+ {
+ return difference(grid.getValue(ijk.offsetBy(0, 0, 1)), grid.getValue(ijk.offsetBy( 0, 0, -1)));
+ }
+
+
+ // stencil access version
+ template<typename Stencil>
+ static typename Stencil::ValueType inX(const Stencil& S)
+ {
+ typedef typename Stencil::ValueType ValueType;
+ return difference(S.template getValue< 1, 0, 0>(), S.template getValue<-1, 0, 0>());
+ }
+ template<typename Stencil>
+ static typename Stencil::ValueType inY(const Stencil& S)
+ {
+ typedef typename Stencil::ValueType ValueType;
+ return difference(S.template getValue< 0, 1, 0>(), S.template getValue< 0,-1, 0>());
+ }
+
+ template<typename Stencil>
+ static typename Stencil::ValueType inZ(const Stencil& S)
+ {
+ typedef typename Stencil::ValueType ValueType;
+ return difference(S.template getValue< 0, 0, 1>(), S.template getValue< 0, 0,-1>());
+ }
+
+};
+
+template<>
+struct D1<CD_4TH>
+{
+
+ // the difference opperator
+ template <typename ValueType>
+ static ValueType difference( const ValueType& xp2, const ValueType& xp1,
+ const ValueType& xm1, const ValueType& xm2 ) {
+ return ValueType(2./3.)*(xp1 - xm1) + ValueType(1./12.)*(xm2 - xp2) ;
+ }
+
+
+ // random access version
+ template<typename Accessor>
+ static typename Accessor::ValueType inX(const Accessor& grid, const Coord& ijk)
+ {
+ return difference( grid.getValue(ijk.offsetBy( 2,0,0)), grid.getValue(ijk.offsetBy( 1,0,0)),
+ grid.getValue(ijk.offsetBy(-1,0,0)), grid.getValue(ijk.offsetBy(-2,0,0)) );
+
+ }
+
+ template<typename Accessor>
+ static typename Accessor::ValueType inY(const Accessor& grid, const Coord& ijk)
+ {
+
+ return difference( grid.getValue(ijk.offsetBy( 0, 2, 0)), grid.getValue(ijk.offsetBy( 0, 1, 0)),
+ grid.getValue(ijk.offsetBy( 0,-1, 0)), grid.getValue(ijk.offsetBy( 0,-2, 0)) );
+
+ }
+
+ template<typename Accessor>
+ static typename Accessor::ValueType inZ(const Accessor& grid, const Coord& ijk)
+ {
+
+ return difference( grid.getValue(ijk.offsetBy( 0, 0, 2)), grid.getValue(ijk.offsetBy( 0, 0, 1)),
+ grid.getValue(ijk.offsetBy( 0, 0,-1)), grid.getValue(ijk.offsetBy( 0, 0,-2)) );
+ }
+
+
+ // stencil access version
+ template<typename Stencil>
+ static typename Stencil::ValueType inX(const Stencil& S)
+ {
+ return difference( S.template getValue< 2, 0, 0>(),
+ S.template getValue< 1, 0, 0>(),
+ S.template getValue<-1, 0, 0>(),
+ S.template getValue<-2, 0, 0>() );
+ }
+
+ template<typename Stencil>
+ static typename Stencil::ValueType inY(const Stencil& S)
+ {
+ return difference( S.template getValue< 0, 2, 0>(),
+ S.template getValue< 0, 1, 0>(),
+ S.template getValue< 0,-1, 0>(),
+ S.template getValue< 0,-2, 0>() );
+ }
+
+ template<typename Stencil>
+ static typename Stencil::ValueType inZ(const Stencil& S)
+ {
+ return difference( S.template getValue< 0, 0, 2>(),
+ S.template getValue< 0, 0, 1>(),
+ S.template getValue< 0, 0,-1>(),
+ S.template getValue< 0, 0,-2>() );
+ }
+};
+
+template<>
+struct D1<CD_6TH>
+{
+
+ // the difference opperator
+ template <typename ValueType>
+ static ValueType difference( const ValueType& xp3, const ValueType& xp2, const ValueType& xp1,
+ const ValueType& xm1, const ValueType& xm2, const ValueType& xm3 ) {
+ return ValueType(3./4.)*(xp1 - xm1) - ValueType(0.15)*(xp2 - xm2) + ValueType(1./60.)*(xp3-xm3);
+ }
+
+
+ // random access version
+ template<typename Accessor>
+ static typename Accessor::ValueType inX(const Accessor& grid, const Coord& ijk)
+ {
+ return difference( grid.getValue(ijk.offsetBy( 3,0,0)), grid.getValue(ijk.offsetBy( 2,0,0)),
+ grid.getValue(ijk.offsetBy( 1,0,0)), grid.getValue(ijk.offsetBy(-1,0,0)),
+ grid.getValue(ijk.offsetBy(-2,0,0)), grid.getValue(ijk.offsetBy(-3,0,0)));
+ }
+
+ template<typename Accessor>
+ static typename Accessor::ValueType inY(const Accessor& grid, const Coord& ijk)
+ {
+ return difference( grid.getValue(ijk.offsetBy( 0, 3,0)), grid.getValue(ijk.offsetBy( 0, 2,0)),
+ grid.getValue(ijk.offsetBy( 0, 1,0)), grid.getValue(ijk.offsetBy( 0,-1,0)),
+ grid.getValue(ijk.offsetBy( 0,-2,0)), grid.getValue(ijk.offsetBy( 0,-3,0)));
+ }
+
+ template<typename Accessor>
+ static typename Accessor::ValueType inZ(const Accessor& grid, const Coord& ijk)
+ {
+ return difference( grid.getValue(ijk.offsetBy( 0, 0, 3)), grid.getValue(ijk.offsetBy( 0, 0, 2)),
+ grid.getValue(ijk.offsetBy( 0, 0, 1)), grid.getValue(ijk.offsetBy( 0, 0,-1)),
+ grid.getValue(ijk.offsetBy( 0, 0,-2)), grid.getValue(ijk.offsetBy( 0, 0,-3)));
+ }
+
+ // stencil access version
+ template<typename Stencil>
+ static typename Stencil::ValueType inX(const Stencil& S)
+ {
+ return difference(S.template getValue< 3, 0, 0>(),
+ S.template getValue< 2, 0, 0>(),
+ S.template getValue< 1, 0, 0>(),
+ S.template getValue<-1, 0, 0>(),
+ S.template getValue<-2, 0, 0>(),
+ S.template getValue<-3, 0, 0>());
+ }
+
+ template<typename Stencil>
+ static typename Stencil::ValueType inY(const Stencil& S)
+ {
+
+ return difference( S.template getValue< 0, 3, 0>(),
+ S.template getValue< 0, 2, 0>(),
+ S.template getValue< 0, 1, 0>(),
+ S.template getValue< 0,-1, 0>(),
+ S.template getValue< 0,-2, 0>(),
+ S.template getValue< 0,-3, 0>());
+ }
+
+ template<typename Stencil>
+ static typename Stencil::ValueType inZ(const Stencil& S)
+ {
+
+ return difference( S.template getValue< 0, 0, 3>(),
+ S.template getValue< 0, 0, 2>(),
+ S.template getValue< 0, 0, 1>(),
+ S.template getValue< 0, 0,-1>(),
+ S.template getValue< 0, 0,-2>(),
+ S.template getValue< 0, 0,-3>());
+ }
+};
+
+
+template<>
+struct D1<FD_1ST>
+{
+
+ // the difference opperator
+ template <typename ValueType>
+ static ValueType difference(const ValueType& xp1, const ValueType& xp0) {
+ return xp1 - xp0;
+ }
+
+
+ // random access version
+ template<typename Accessor>
+ static typename Accessor::ValueType inX(const Accessor& grid, const Coord& ijk)
+ {
+ return difference(grid.getValue(ijk.offsetBy(1, 0, 0)), grid.getValue(ijk));
+ }
+
+ template<typename Accessor>
+ static typename Accessor::ValueType inY(const Accessor& grid, const Coord& ijk)
+ {
+ return difference(grid.getValue(ijk.offsetBy(0, 1, 0)), grid.getValue(ijk));
+ }
+
+ template<typename Accessor>
+ static typename Accessor::ValueType inZ(const Accessor& grid, const Coord& ijk)
+ {
+ return difference(grid.getValue(ijk.offsetBy(0, 0, 1)), grid.getValue(ijk));
+ }
+
+ // stencil access version
+ template<typename Stencil>
+ static typename Stencil::ValueType inX(const Stencil& S)
+ {
+ return difference(S.template getValue< 1, 0, 0>(), S.template getValue< 0, 0, 0>());
+ }
+
+ template<typename Stencil>
+ static typename Stencil::ValueType inY(const Stencil& S)
+ {
+ return difference(S.template getValue< 0, 1, 0>(), S.template getValue< 0, 0, 0>());
+ }
+
+ template<typename Stencil>
+ static typename Stencil::ValueType inZ(const Stencil& S)
+ {
+ return difference(S.template getValue< 0, 0, 1>(), S.template getValue< 0, 0, 0>());
+ }
+};
+
+
+template<>
+struct D1<FD_2ND>
+{
+
+ // the difference opperator
+ template <typename ValueType>
+ static ValueType difference(const ValueType& xp2, const ValueType& xp1, const ValueType& xp0) {
+ return ValueType(2)*xp1 -(ValueType(0.5)*xp2 + ValueType(3./2.)*xp0);
+ }
+
+
+ // random access version
+ template<typename Accessor>
+ static typename Accessor::ValueType inX(const Accessor& grid, const Coord& ijk)
+ {
+ return difference(grid.getValue(ijk.offsetBy(2,0,0)), grid.getValue(ijk.offsetBy(1,0,0)), grid.getValue(ijk));
+ }
+
+ template<typename Accessor>
+ static typename Accessor::ValueType inY(const Accessor& grid, const Coord& ijk)
+ {
+ return difference(grid.getValue(ijk.offsetBy(0,2,0)), grid.getValue(ijk.offsetBy(0,1,0)), grid.getValue(ijk));
+ }
+
+ template<typename Accessor>
+ static typename Accessor::ValueType inZ(const Accessor& grid, const Coord& ijk)
+ {
+ return difference(grid.getValue(ijk.offsetBy(0,0,2)), grid.getValue(ijk.offsetBy(0,0,1)), grid.getValue(ijk));
+ }
+
+
+ // stencil access version
+ template<typename Stencil>
+ static typename Stencil::ValueType inX(const Stencil& S)
+ {
+ return difference( S.template getValue< 2, 0, 0>(),
+ S.template getValue< 1, 0, 0>(),
+ S.template getValue< 0, 0, 0>() );
+ }
+
+ template<typename Stencil>
+ static typename Stencil::ValueType inY(const Stencil& S)
+ {
+ return difference( S.template getValue< 0, 2, 0>(),
+ S.template getValue< 0, 1, 0>(),
+ S.template getValue< 0, 0, 0>() );
+ }
+
+ template<typename Stencil>
+ static typename Stencil::ValueType inZ(const Stencil& S)
+ {
+ return difference( S.template getValue< 0, 0, 2>(),
+ S.template getValue< 0, 0, 1>(),
+ S.template getValue< 0, 0, 0>() );
+ }
+
+};
+
+
+template<>
+struct D1<FD_3RD>
+{
+
+ // the difference opperator
+ template <typename ValueType>
+ static ValueType difference(const ValueType& xp3, const ValueType& xp2, const ValueType& xp1, const ValueType& xp0) {
+ return ValueType(1./3.)*xp3 - ValueType(1.5)*xp2 + ValueType(3.)*xp1 - ValueType(11./6.)*xp0;
+ }
+
+
+ // random access version
+ template<typename Accessor>
+ static typename Accessor::ValueType inX(const Accessor& grid, const Coord& ijk)
+ {
+ return difference( grid.getValue(ijk.offsetBy(3,0,0)),
+ grid.getValue(ijk.offsetBy(2,0,0)),
+ grid.getValue(ijk.offsetBy(1,0,0)),
+ grid.getValue(ijk) );
+ }
+
+ template<typename Accessor>
+ static typename Accessor::ValueType inY(const Accessor& grid, const Coord& ijk)
+ {
+ return difference( grid.getValue(ijk.offsetBy(0,3,0)),
+ grid.getValue(ijk.offsetBy(0,2,0)),
+ grid.getValue(ijk.offsetBy(0,1,0)),
+ grid.getValue(ijk) );
+ }
+
+ template<typename Accessor>
+ static typename Accessor::ValueType inZ(const Accessor& grid, const Coord& ijk)
+ {
+ return difference( grid.getValue(ijk.offsetBy(0,0,3)),
+ grid.getValue(ijk.offsetBy(0,0,2)),
+ grid.getValue(ijk.offsetBy(0,0,1)),
+ grid.getValue(ijk) );
+ }
+
+
+ // stencil access version
+ template<typename Stencil>
+ static typename Stencil::ValueType inX(const Stencil& S)
+ {
+ return difference(S.template getValue< 3, 0, 0>(),
+ S.template getValue< 2, 0, 0>(),
+ S.template getValue< 1, 0, 0>(),
+ S.template getValue< 0, 0, 0>() );
+ }
+
+ template<typename Stencil>
+ static typename Stencil::ValueType inY(const Stencil& S)
+ {
+ return difference(S.template getValue< 0, 3, 0>(),
+ S.template getValue< 0, 2, 0>(),
+ S.template getValue< 0, 1, 0>(),
+ S.template getValue< 0, 0, 0>() );
+ }
+
+ template<typename Stencil>
+ static typename Stencil::ValueType inZ(const Stencil& S)
+ {
+ return difference( S.template getValue< 0, 0, 3>(),
+ S.template getValue< 0, 0, 2>(),
+ S.template getValue< 0, 0, 1>(),
+ S.template getValue< 0, 0, 0>() );
+ }
+};
+
+
+template<>
+struct D1<BD_1ST>
+{
+
+ // the difference opperator
+ template <typename ValueType>
+ static ValueType difference(const ValueType& xm1, const ValueType& xm0) {
+ return -D1<FD_1ST>::difference(xm1, xm0);
+ }
+
+
+ // random access version
+ template<typename Accessor>
+ static typename Accessor::ValueType inX(const Accessor& grid, const Coord& ijk)
+ {
+ return difference(grid.getValue(ijk.offsetBy(-1,0,0)), grid.getValue(ijk));
+ }
+
+ template<typename Accessor>
+ static typename Accessor::ValueType inY(const Accessor& grid, const Coord& ijk)
+ {
+ return difference(grid.getValue(ijk.offsetBy(0,-1,0)), grid.getValue(ijk));
+ }
+
+ template<typename Accessor>
+ static typename Accessor::ValueType inZ(const Accessor& grid, const Coord& ijk)
+ {
+ return difference(grid.getValue(ijk.offsetBy(0, 0,-1)), grid.getValue(ijk));
+ }
+
+
+ // stencil access version
+ template<typename Stencil>
+ static typename Stencil::ValueType inX(const Stencil& S)
+ {
+ return difference(S.template getValue<-1, 0, 0>(), S.template getValue< 0, 0, 0>());
+ }
+
+ template<typename Stencil>
+ static typename Stencil::ValueType inY(const Stencil& S)
+ {
+ return difference(S.template getValue< 0,-1, 0>(), S.template getValue< 0, 0, 0>());
+ }
+
+ template<typename Stencil>
+ static typename Stencil::ValueType inZ(const Stencil& S)
+ {
+ return difference(S.template getValue< 0, 0,-1>(), S.template getValue< 0, 0, 0>());
+ }
+};
+
+
+template<>
+struct D1<BD_2ND>
+{
+
+ // the difference opperator
+ template <typename ValueType>
+ static ValueType difference(const ValueType& xm2, const ValueType& xm1, const ValueType& xm0) {
+ return -D1<FD_2ND>::difference(xm2, xm1, xm0);
+ }
+
+
+ // random access version
+ template<typename Accessor>
+ static typename Accessor::ValueType inX(const Accessor& grid, const Coord& ijk)
+ {
+ return difference( grid.getValue(ijk.offsetBy(-2,0,0)),
+ grid.getValue(ijk.offsetBy(-1,0,0)),
+ grid.getValue(ijk) );
+ }
+
+ template<typename Accessor>
+ static typename Accessor::ValueType inY(const Accessor& grid, const Coord& ijk)
+ {
+ return difference( grid.getValue(ijk.offsetBy(0,-2,0)),
+ grid.getValue(ijk.offsetBy(0,-1,0)),
+ grid.getValue(ijk) );
+ }
+
+ template<typename Accessor>
+ static typename Accessor::ValueType inZ(const Accessor& grid, const Coord& ijk)
+ {
+ return difference( grid.getValue(ijk.offsetBy(0,0,-2)),
+ grid.getValue(ijk.offsetBy(0,0,-1)),
+ grid.getValue(ijk) );
+ }
+
+ // stencil access version
+ template<typename Stencil>
+ static typename Stencil::ValueType inX(const Stencil& S)
+ {
+ return difference( S.template getValue<-2, 0, 0>(),
+ S.template getValue<-1, 0, 0>(),
+ S.template getValue< 0, 0, 0>() );
+ }
+
+ template<typename Stencil>
+ static typename Stencil::ValueType inY(const Stencil& S)
+ {
+ return difference( S.template getValue< 0,-2, 0>(),
+ S.template getValue< 0,-1, 0>(),
+ S.template getValue< 0, 0, 0>() );
+ }
+
+ template<typename Stencil>
+ static typename Stencil::ValueType inZ(const Stencil& S)
+ {
+ return difference( S.template getValue< 0, 0,-2>(),
+ S.template getValue< 0, 0,-1>(),
+ S.template getValue< 0, 0, 0>() );
+ }
+};
+
+
+template<>
+struct D1<BD_3RD>
+{
+
+ // the difference opperator
+ template <typename ValueType>
+ static ValueType difference(const ValueType& xm3, const ValueType& xm2, const ValueType& xm1, const ValueType& xm0){
+ return -D1<FD_3RD>::difference(xm3, xm2, xm1, xm0);
+ }
+
+ // random access version
+ template<typename Accessor>
+ static typename Accessor::ValueType inX(const Accessor& grid, const Coord& ijk)
+ {
+ return difference( grid.getValue(ijk.offsetBy(-3,0,0)),
+ grid.getValue(ijk.offsetBy(-2,0,0)),
+ grid.getValue(ijk.offsetBy(-1,0,0)),
+ grid.getValue(ijk) );
+ }
+
+ template<typename Accessor>
+ static typename Accessor::ValueType inY(const Accessor& grid, const Coord& ijk)
+ {
+ return difference( grid.getValue(ijk.offsetBy( 0,-3,0)),
+ grid.getValue(ijk.offsetBy( 0,-2,0)),
+ grid.getValue(ijk.offsetBy( 0,-1,0)),
+ grid.getValue(ijk) );
+ }
+
+ template<typename Accessor>
+ static typename Accessor::ValueType inZ(const Accessor& grid, const Coord& ijk)
+ {
+ return difference( grid.getValue(ijk.offsetBy( 0, 0,-3)),
+ grid.getValue(ijk.offsetBy( 0, 0,-2)),
+ grid.getValue(ijk.offsetBy( 0, 0,-1)),
+ grid.getValue(ijk) );
+ }
+
+ // stencil access version
+ template<typename Stencil>
+ static typename Stencil::ValueType inX(const Stencil& S)
+ {
+ return difference( S.template getValue<-3, 0, 0>(),
+ S.template getValue<-2, 0, 0>(),
+ S.template getValue<-1, 0, 0>(),
+ S.template getValue< 0, 0, 0>() );
+ }
+
+ template<typename Stencil>
+ static typename Stencil::ValueType inY(const Stencil& S)
+ {
+ return difference( S.template getValue< 0,-3, 0>(),
+ S.template getValue< 0,-2, 0>(),
+ S.template getValue< 0,-1, 0>(),
+ S.template getValue< 0, 0, 0>() );
+ }
+
+ template<typename Stencil>
+ static typename Stencil::ValueType inZ(const Stencil& S)
+ {
+ return difference( S.template getValue< 0, 0,-3>(),
+ S.template getValue< 0, 0,-2>(),
+ S.template getValue< 0, 0,-1>(),
+ S.template getValue< 0, 0, 0>() );
+ }
+
+};
+
+template<>
+struct D1<FD_WENO5>
+{
+ // the difference opperator
+ template <typename ValueType>
+ static ValueType difference(const ValueType& xp3, const ValueType& xp2,
+ const ValueType& xp1, const ValueType& xp0,
+ const ValueType& xm1, const ValueType& xm2) {
+ return WENO5<ValueType>(xp3, xp2, xp1, xp0, xm1)
+ - WENO5<ValueType>(xp2, xp1, xp0, xm1, xm2);
+ }
+
+
+ // random access version
+ template<typename Accessor>
+ static typename Accessor::ValueType inX(const Accessor& grid, const Coord& ijk)
+ {
+ typedef typename Accessor::ValueType ValueType;
+ ValueType V[6];
+ V[0] = grid.getValue(ijk.offsetBy(3,0,0));
+ V[1] = grid.getValue(ijk.offsetBy(2,0,0));
+ V[2] = grid.getValue(ijk.offsetBy(1,0,0));
+ V[3] = grid.getValue(ijk);
+ V[4] = grid.getValue(ijk.offsetBy(-1,0,0));
+ V[5] = grid.getValue(ijk.offsetBy(-2,0,0));
+
+ return difference(V[0], V[1], V[2], V[3], V[4], V[5]);
+ }
+
+ template<typename Accessor>
+ static typename Accessor::ValueType inY(const Accessor& grid, const Coord& ijk)
+ {
+ typedef typename Accessor::ValueType ValueType;
+ ValueType V[6];
+ V[0] = grid.getValue(ijk.offsetBy(0,3,0));
+ V[1] = grid.getValue(ijk.offsetBy(0,2,0));
+ V[2] = grid.getValue(ijk.offsetBy(0,1,0));
+ V[3] = grid.getValue(ijk);
+ V[4] = grid.getValue(ijk.offsetBy(0,-1,0));
+ V[5] = grid.getValue(ijk.offsetBy(0,-2,0));
+
+ return difference(V[0], V[1], V[2], V[3], V[4], V[5]);
+ }
+
+ template<typename Accessor>
+ static typename Accessor::ValueType inZ(const Accessor& grid, const Coord& ijk)
+ {
+ typedef typename Accessor::ValueType ValueType;
+ ValueType V[6];
+ V[0] = grid.getValue(ijk.offsetBy(0,0,3));
+ V[1] = grid.getValue(ijk.offsetBy(0,0,2));
+ V[2] = grid.getValue(ijk.offsetBy(0,0,1));
+ V[3] = grid.getValue(ijk);
+ V[4] = grid.getValue(ijk.offsetBy(0,0,-1));
+ V[5] = grid.getValue(ijk.offsetBy(0,0,-2));
+
+ return difference(V[0], V[1], V[2], V[3], V[4], V[5]);
+ }
+
+ // stencil access version
+ template<typename Stencil>
+ static typename Stencil::ValueType inX(const Stencil& S)
+ {
+
+ return difference( S.template getValue< 3, 0, 0>(),
+ S.template getValue< 2, 0, 0>(),
+ S.template getValue< 1, 0, 0>(),
+ S.template getValue< 0, 0, 0>(),
+ S.template getValue<-1, 0, 0>(),
+ S.template getValue<-2, 0, 0>() );
+
+ }
+
+ template<typename Stencil>
+ static typename Stencil::ValueType inY(const Stencil& S)
+ {
+ return difference( S.template getValue< 0, 3, 0>(),
+ S.template getValue< 0, 2, 0>(),
+ S.template getValue< 0, 1, 0>(),
+ S.template getValue< 0, 0, 0>(),
+ S.template getValue< 0,-1, 0>(),
+ S.template getValue< 0,-2, 0>() );
+ }
+
+ template<typename Stencil>
+ static typename Stencil::ValueType inZ(const Stencil& S)
+ {
+
+ return difference( S.template getValue< 0, 0, 3>(),
+ S.template getValue< 0, 0, 2>(),
+ S.template getValue< 0, 0, 1>(),
+ S.template getValue< 0, 0, 0>(),
+ S.template getValue< 0, 0,-1>(),
+ S.template getValue< 0, 0,-2>() );
+ }
+};
+
+template<>
+struct D1<FD_HJWENO5>
+{
+
+ // the difference opperator
+ template <typename ValueType>
+ static ValueType difference(const ValueType& xp3, const ValueType& xp2,
+ const ValueType& xp1, const ValueType& xp0,
+ const ValueType& xm1, const ValueType& xm2) {
+ return WENO5<ValueType>(xp3 - xp2, xp2 - xp1, xp1 - xp0, xp0-xm1, xm1-xm2);
+ }
+
+ // random access version
+ template<typename Accessor>
+ static typename Accessor::ValueType inX(const Accessor& grid, const Coord& ijk)
+ {
+ typedef typename Accessor::ValueType ValueType;
+ ValueType V[6];
+ V[0] = grid.getValue(ijk.offsetBy(3,0,0));
+ V[1] = grid.getValue(ijk.offsetBy(2,0,0));
+ V[2] = grid.getValue(ijk.offsetBy(1,0,0));
+ V[3] = grid.getValue(ijk);
+ V[4] = grid.getValue(ijk.offsetBy(-1,0,0));
+ V[5] = grid.getValue(ijk.offsetBy(-2,0,0));
+
+ return difference(V[0], V[1], V[2], V[3], V[4], V[5]);
+
+ }
+
+ template<typename Accessor>
+ static typename Accessor::ValueType inY(const Accessor& grid, const Coord& ijk)
+ {
+ typedef typename Accessor::ValueType ValueType;
+ ValueType V[6];
+ V[0] = grid.getValue(ijk.offsetBy(0,3,0));
+ V[1] = grid.getValue(ijk.offsetBy(0,2,0));
+ V[2] = grid.getValue(ijk.offsetBy(0,1,0));
+ V[3] = grid.getValue(ijk);
+ V[4] = grid.getValue(ijk.offsetBy(0,-1,0));
+ V[5] = grid.getValue(ijk.offsetBy(0,-2,0));
+
+ return difference(V[0], V[1], V[2], V[3], V[4], V[5]);
+ }
+
+ template<typename Accessor>
+ static typename Accessor::ValueType inZ(const Accessor& grid, const Coord& ijk)
+ {
+ typedef typename Accessor::ValueType ValueType;
+ ValueType V[6];
+ V[0] = grid.getValue(ijk.offsetBy(0,0,3));
+ V[1] = grid.getValue(ijk.offsetBy(0,0,2));
+ V[2] = grid.getValue(ijk.offsetBy(0,0,1));
+ V[3] = grid.getValue(ijk);
+ V[4] = grid.getValue(ijk.offsetBy(0,0,-1));
+ V[5] = grid.getValue(ijk.offsetBy(0,0,-2));
+
+ return difference(V[0], V[1], V[2], V[3], V[4], V[5]);
+ }
+
+ // stencil access version
+ template<typename Stencil>
+ static typename Stencil::ValueType inX(const Stencil& S)
+ {
+
+ return difference( S.template getValue< 3, 0, 0>(),
+ S.template getValue< 2, 0, 0>(),
+ S.template getValue< 1, 0, 0>(),
+ S.template getValue< 0, 0, 0>(),
+ S.template getValue<-1, 0, 0>(),
+ S.template getValue<-2, 0, 0>() );
+
+ }
+
+ template<typename Stencil>
+ static typename Stencil::ValueType inY(const Stencil& S)
+ {
+ return difference( S.template getValue< 0, 3, 0>(),
+ S.template getValue< 0, 2, 0>(),
+ S.template getValue< 0, 1, 0>(),
+ S.template getValue< 0, 0, 0>(),
+ S.template getValue< 0,-1, 0>(),
+ S.template getValue< 0,-2, 0>() );
+ }
+
+ template<typename Stencil>
+ static typename Stencil::ValueType inZ(const Stencil& S)
+ {
+
+ return difference( S.template getValue< 0, 0, 3>(),
+ S.template getValue< 0, 0, 2>(),
+ S.template getValue< 0, 0, 1>(),
+ S.template getValue< 0, 0, 0>(),
+ S.template getValue< 0, 0,-1>(),
+ S.template getValue< 0, 0,-2>() );
+ }
+
+};
+
+template<>
+struct D1<BD_WENO5>
+{
+
+ template<typename ValueType>
+ static ValueType difference(const ValueType& xm3, const ValueType& xm2, const ValueType& xm1,
+ const ValueType& xm0, const ValueType& xp1, const ValueType& xp2) {
+ return -D1<FD_WENO5>::difference(xm3, xm2, xm1, xm0, xp1, xp2);
+ }
+
+
+ // random access version
+ template<typename Accessor>
+ static typename Accessor::ValueType inX(const Accessor& grid, const Coord& ijk)
+ {
+ typedef typename Accessor::ValueType ValueType;
+ ValueType V[6];
+ V[0] = grid.getValue(ijk.offsetBy(-3,0,0));
+ V[1] = grid.getValue(ijk.offsetBy(-2,0,0));
+ V[2] = grid.getValue(ijk.offsetBy(-1,0,0));
+ V[3] = grid.getValue(ijk);
+ V[4] = grid.getValue(ijk.offsetBy(1,0,0));
+ V[5] = grid.getValue(ijk.offsetBy(2,0,0));
+
+ return difference(V[0], V[1], V[2], V[3], V[4], V[5]);
+ }
+
+ template<typename Accessor>
+ static typename Accessor::ValueType inY(const Accessor& grid, const Coord& ijk)
+ {
+ typedef typename Accessor::ValueType ValueType;
+ ValueType V[6];
+ V[0] = grid.getValue(ijk.offsetBy(0,-3,0));
+ V[1] = grid.getValue(ijk.offsetBy(0,-2,0));
+ V[2] = grid.getValue(ijk.offsetBy(0,-1,0));
+ V[3] = grid.getValue(ijk);
+ V[4] = grid.getValue(ijk.offsetBy(0,1,0));
+ V[5] = grid.getValue(ijk.offsetBy(0,2,0));
+
+ return difference(V[0], V[1], V[2], V[3], V[4], V[5]);
+ }
+
+ template<typename Accessor>
+ static typename Accessor::ValueType inZ(const Accessor& grid, const Coord& ijk)
+ {
+ typedef typename Accessor::ValueType ValueType;
+ ValueType V[6];
+ V[0] = grid.getValue(ijk.offsetBy(0,0,-3));
+ V[1] = grid.getValue(ijk.offsetBy(0,0,-2));
+ V[2] = grid.getValue(ijk.offsetBy(0,0,-1));
+ V[3] = grid.getValue(ijk);
+ V[4] = grid.getValue(ijk.offsetBy(0,0,1));
+ V[5] = grid.getValue(ijk.offsetBy(0,0,2));
+
+ return difference(V[0], V[1], V[2], V[3], V[4], V[5]);
+ }
+
+ // stencil access version
+ template<typename Stencil>
+ static typename Stencil::ValueType inX(const Stencil& S)
+ {
+ typedef typename Stencil::ValueType ValueType;
+ ValueType V[6];
+ V[0] = S.template getValue<-3, 0, 0>();
+ V[1] = S.template getValue<-2, 0, 0>();
+ V[2] = S.template getValue<-1, 0, 0>();
+ V[3] = S.template getValue< 0, 0, 0>();
+ V[4] = S.template getValue< 1, 0, 0>();
+ V[5] = S.template getValue< 2, 0, 0>();
+
+ return difference(V[0], V[1], V[2], V[3], V[4], V[5]);
+ }
+
+ template<typename Stencil>
+ static typename Stencil::ValueType inY(const Stencil& S)
+ {
+ typedef typename Stencil::ValueType ValueType;
+ ValueType V[6];
+ V[0] = S.template getValue< 0,-3, 0>();
+ V[1] = S.template getValue< 0,-2, 0>();
+ V[2] = S.template getValue< 0,-1, 0>();
+ V[3] = S.template getValue< 0, 0, 0>();
+ V[4] = S.template getValue< 0, 1, 0>();
+ V[5] = S.template getValue< 0, 2, 0>();
+
+ return difference(V[0], V[1], V[2], V[3], V[4], V[5]);
+ }
+
+ template<typename Stencil>
+ static typename Stencil::ValueType inZ(const Stencil& S)
+ {
+ typedef typename Stencil::ValueType ValueType;
+ ValueType V[6];
+ V[0] = S.template getValue< 0, 0,-3>();
+ V[1] = S.template getValue< 0, 0,-2>();
+ V[2] = S.template getValue< 0, 0,-1>();
+ V[3] = S.template getValue< 0, 0, 0>();
+ V[4] = S.template getValue< 0, 0, 1>();
+ V[5] = S.template getValue< 0, 0, 2>();
+
+ return difference(V[0], V[1], V[2], V[3], V[4], V[5]);
+ }
+};
+
+
+template<>
+struct D1<BD_HJWENO5>
+{
+ template<typename ValueType>
+ static ValueType difference(const ValueType& xm3, const ValueType& xm2, const ValueType& xm1,
+ const ValueType& xm0, const ValueType& xp1, const ValueType& xp2) {
+ return -D1<FD_HJWENO5>::difference(xm3, xm2, xm1, xm0, xp1, xp2);
+ }
+
+ // random access version
+ template<typename Accessor>
+ static typename Accessor::ValueType inX(const Accessor& grid, const Coord& ijk)
+ {
+ typedef typename Accessor::ValueType ValueType;
+ ValueType V[6];
+ V[0] = grid.getValue(ijk.offsetBy(-3,0,0));
+ V[1] = grid.getValue(ijk.offsetBy(-2,0,0));
+ V[2] = grid.getValue(ijk.offsetBy(-1,0,0));
+ V[3] = grid.getValue(ijk);
+ V[4] = grid.getValue(ijk.offsetBy(1,0,0));
+ V[5] = grid.getValue(ijk.offsetBy(2,0,0));
+
+ return difference(V[0], V[1], V[2], V[3], V[4], V[5]);
+ }
+
+ template<typename Accessor>
+ static typename Accessor::ValueType inY(const Accessor& grid, const Coord& ijk)
+ {
+ typedef typename Accessor::ValueType ValueType;
+ ValueType V[6];
+ V[0] = grid.getValue(ijk.offsetBy(0,-3,0));
+ V[1] = grid.getValue(ijk.offsetBy(0,-2,0));
+ V[2] = grid.getValue(ijk.offsetBy(0,-1,0));
+ V[3] = grid.getValue(ijk);
+ V[4] = grid.getValue(ijk.offsetBy(0,1,0));
+ V[5] = grid.getValue(ijk.offsetBy(0,2,0));
+
+ return difference(V[0], V[1], V[2], V[3], V[4], V[5]);
+ }
+
+ template<typename Accessor>
+ static typename Accessor::ValueType inZ(const Accessor& grid, const Coord& ijk)
+ {
+ typedef typename Accessor::ValueType ValueType;
+ ValueType V[6];
+ V[0] = grid.getValue(ijk.offsetBy(0,0,-3));
+ V[1] = grid.getValue(ijk.offsetBy(0,0,-2));
+ V[2] = grid.getValue(ijk.offsetBy(0,0,-1));
+ V[3] = grid.getValue(ijk);
+ V[4] = grid.getValue(ijk.offsetBy(0,0,1));
+ V[5] = grid.getValue(ijk.offsetBy(0,0,2));
+
+ return difference(V[0], V[1], V[2], V[3], V[4], V[5]);
+ }
+
+ // stencil access version
+ template<typename Stencil>
+ static typename Stencil::ValueType inX(const Stencil& S)
+ {
+ typedef typename Stencil::ValueType ValueType;
+ ValueType V[6];
+ V[0] = S.template getValue<-3, 0, 0>();
+ V[1] = S.template getValue<-2, 0, 0>();
+ V[2] = S.template getValue<-1, 0, 0>();
+ V[3] = S.template getValue< 0, 0, 0>();
+ V[4] = S.template getValue< 1, 0, 0>();
+ V[5] = S.template getValue< 2, 0, 0>();
+
+ return difference(V[0], V[1], V[2], V[3], V[4], V[5]);
+ }
+
+ template<typename Stencil>
+ static typename Stencil::ValueType inY(const Stencil& S)
+ {
+ typedef typename Stencil::ValueType ValueType;
+ ValueType V[6];
+ V[0] = S.template getValue< 0,-3, 0>();
+ V[1] = S.template getValue< 0,-2, 0>();
+ V[2] = S.template getValue< 0,-1, 0>();
+ V[3] = S.template getValue< 0, 0, 0>();
+ V[4] = S.template getValue< 0, 1, 0>();
+ V[5] = S.template getValue< 0, 2, 0>();
+
+ return difference(V[0], V[1], V[2], V[3], V[4], V[5]);
+ }
+
+ template<typename Stencil>
+ static typename Stencil::ValueType inZ(const Stencil& S)
+ {
+ typedef typename Stencil::ValueType ValueType;
+ ValueType V[6];
+ V[0] = S.template getValue< 0, 0,-3>();
+ V[1] = S.template getValue< 0, 0,-2>();
+ V[2] = S.template getValue< 0, 0,-1>();
+ V[3] = S.template getValue< 0, 0, 0>();
+ V[4] = S.template getValue< 0, 0, 1>();
+ V[5] = S.template getValue< 0, 0, 2>();
+
+ return difference(V[0], V[1], V[2], V[3], V[4], V[5]);
+ }
+};
+
+
+template<DScheme DiffScheme>
+struct D1Vec
+{
+ // random access version
+ template<typename Accessor>
+ static typename Accessor::ValueType::value_type inX(const Accessor& grid, const Coord& ijk, int n)
+ {
+ return D1<DiffScheme>::inX(grid, ijk)[n];
+ }
+
+ template<typename Accessor>
+ static typename Accessor::ValueType::value_type inY(const Accessor& grid, const Coord& ijk, int n)
+ {
+ return D1<DiffScheme>::inY(grid, ijk)[n];
+ }
+ template<typename Accessor>
+ static typename Accessor::ValueType::value_type inZ(const Accessor& grid, const Coord& ijk, int n)
+ {
+ return D1<DiffScheme>::inZ(grid, ijk)[n];
+ }
+
+
+ // stencil access version
+ template<typename Stencil>
+ static typename Stencil::ValueType::value_type inX(const Stencil& S, int n)
+ {
+ return D1<DiffScheme>::inX(S)[n];
+ }
+
+ template<typename Stencil>
+ static typename Stencil::ValueType::value_type inY(const Stencil& S, int n)
+ {
+ return D1<DiffScheme>::inY(S)[n];
+ }
+
+ template<typename Stencil>
+ static typename Stencil::ValueType::value_type inZ(const Stencil& S, int n)
+ {
+ return D1<DiffScheme>::inZ(S)[n];
+ }
+};
+
+
+template<>
+struct D1Vec<CD_2NDT>
+{
+
+ // random access version
+ template<typename Accessor>
+ static typename Accessor::ValueType::value_type inX(const Accessor& grid, const Coord& ijk, int n)
+ {
+ return D1<CD_2NDT>::difference( grid.getValue(ijk.offsetBy( 1, 0, 0))[n],
+ grid.getValue(ijk.offsetBy(-1, 0, 0))[n] );
+ }
+
+ template<typename Accessor>
+ static typename Accessor::ValueType::value_type inY(const Accessor& grid, const Coord& ijk, int n)
+ {
+ return D1<CD_2NDT>::difference( grid.getValue(ijk.offsetBy(0, 1, 0))[n],
+ grid.getValue(ijk.offsetBy(0,-1, 0))[n] );
+ }
+
+ template<typename Accessor>
+ static typename Accessor::ValueType::value_type inZ(const Accessor& grid, const Coord& ijk, int n)
+ {
+ return D1<CD_2NDT>::difference( grid.getValue(ijk.offsetBy(0, 0, 1))[n],
+ grid.getValue(ijk.offsetBy(0, 0,-1))[n] );
+ }
+
+ // stencil access version
+ template<typename Stencil>
+ static typename Stencil::ValueType::value_type inX(const Stencil& S, int n)
+ {
+ return D1<CD_2NDT>::difference( S.template getValue< 1, 0, 0>()[n],
+ S.template getValue<-1, 0, 0>()[n] );
+ }
+
+ template<typename Stencil>
+ static typename Stencil::ValueType::value_type inY(const Stencil& S, int n)
+ {
+ return D1<CD_2NDT>::difference( S.template getValue< 0, 1, 0>()[n],
+ S.template getValue< 0,-1, 0>()[n] );
+ }
+
+ template<typename Stencil>
+ static typename Stencil::ValueType::value_type inZ(const Stencil& S, int n)
+ {
+ return D1<CD_2NDT>::difference( S.template getValue< 0, 0, 1>()[n],
+ S.template getValue< 0, 0,-1>()[n] );
+ }
+};
+
+template<>
+struct D1Vec<CD_2ND>
+{
+
+ // random access version
+ template<typename Accessor>
+ static typename Accessor::ValueType::value_type inX(const Accessor& grid, const Coord& ijk, int n)
+ {
+ return D1<CD_2ND>::difference( grid.getValue(ijk.offsetBy( 1, 0, 0))[n] ,
+ grid.getValue(ijk.offsetBy(-1, 0, 0))[n] );
+ }
+
+ template<typename Accessor>
+ static typename Accessor::ValueType::value_type inY(const Accessor& grid, const Coord& ijk, int n)
+ {
+ return D1<CD_2ND>::difference( grid.getValue(ijk.offsetBy(0, 1, 0))[n] ,
+ grid.getValue(ijk.offsetBy(0,-1, 0))[n] );
+ }
+
+ template<typename Accessor>
+ static typename Accessor::ValueType::value_type inZ(const Accessor& grid, const Coord& ijk, int n)
+ {
+ return D1<CD_2ND>::difference( grid.getValue(ijk.offsetBy(0, 0, 1))[n] ,
+ grid.getValue(ijk.offsetBy(0, 0,-1))[n] );
+ }
+
+
+ // stencil access version
+ template<typename Stencil>
+ static typename Stencil::ValueType::value_type inX(const Stencil& S, int n)
+ {
+ return D1<CD_2ND>::difference( S.template getValue< 1, 0, 0>()[n],
+ S.template getValue<-1, 0, 0>()[n] );
+ }
+
+ template<typename Stencil>
+ static typename Stencil::ValueType::value_type inY(const Stencil& S, int n)
+ {
+ return D1<CD_2ND>::difference( S.template getValue< 0, 1, 0>()[n],
+ S.template getValue< 0,-1, 0>()[n] );
+ }
+
+ template<typename Stencil>
+ static typename Stencil::ValueType::value_type inZ(const Stencil& S, int n)
+ {
+ return D1<CD_2ND>::difference( S.template getValue< 0, 0, 1>()[n],
+ S.template getValue< 0, 0,-1>()[n] );
+ }
+};
+
+
+template<>
+struct D1Vec<CD_4TH> {
+ // typedef typename Accessor::ValueType::value_type value_type;
+
+
+ // random access version
+ template<typename Accessor>
+ static typename Accessor::ValueType::value_type inX(const Accessor& grid, const Coord& ijk, int n) {
+ return D1<CD_4TH>::difference(grid.getValue(ijk.offsetBy(2, 0, 0))[n], grid.getValue(ijk.offsetBy( 1, 0, 0))[n],
+ grid.getValue(ijk.offsetBy(-1,0, 0))[n], grid.getValue(ijk.offsetBy(-2, 0, 0))[n]);
+ }
+
+ template<typename Accessor>
+ static typename Accessor::ValueType::value_type inY(const Accessor& grid, const Coord& ijk, int n) {
+ return D1<CD_4TH>::difference(grid.getValue(ijk.offsetBy( 0, 2, 0))[n], grid.getValue(ijk.offsetBy( 0, 1, 0))[n],
+ grid.getValue(ijk.offsetBy( 0,-1, 0))[n], grid.getValue(ijk.offsetBy( 0,-2, 0))[n]);
+ }
+
+ template<typename Accessor>
+ static typename Accessor::ValueType::value_type inZ(const Accessor& grid, const Coord& ijk, int n) {
+ return D1<CD_4TH>::difference(grid.getValue(ijk.offsetBy(0,0, 2))[n], grid.getValue(ijk.offsetBy( 0, 0, 1))[n],
+ grid.getValue(ijk.offsetBy(0,0,-1))[n], grid.getValue(ijk.offsetBy( 0, 0,-2))[n]);
+ }
+
+ // stencil access version
+ template<typename Stencil>
+ static typename Stencil::ValueType::value_type inX(const Stencil& S, int n) {
+ return D1<CD_4TH>::difference(S.template getValue< 2, 0, 0>()[n], S.template getValue< 1, 0, 0>()[n],
+ S.template getValue<-1, 0, 0>()[n], S.template getValue<-2, 0, 0>()[n] );
+ }
+
+ template<typename Stencil>
+ static typename Stencil::ValueType::value_type inY(const Stencil& S, int n) {
+ return D1<CD_4TH>::difference(S.template getValue< 0, 2, 0>()[n], S.template getValue< 0, 1, 0>()[n],
+ S.template getValue< 0,-1, 0>()[n], S.template getValue< 0,-2, 0>()[n]);
+ }
+
+ template<typename Stencil>
+ static typename Stencil::ValueType::value_type inZ(const Stencil& S, int n) {
+ return D1<CD_4TH>::difference(S.template getValue< 0, 0, 2>()[n], S.template getValue< 0, 0, 1>()[n],
+ S.template getValue< 0, 0,-1>()[n], S.template getValue< 0, 0,-2>()[n]);
+ }
+};
+
+template<>
+struct D1Vec<CD_6TH>
+{
+ //typedef typename Accessor::ValueType::value_type::value_type ValueType;
+
+ // random access version
+ template<typename Accessor>
+ static typename Accessor::ValueType::value_type inX(const Accessor& grid, const Coord& ijk, int n)
+ {
+ return D1<CD_6TH>::difference( grid.getValue(ijk.offsetBy( 3, 0, 0))[n], grid.getValue(ijk.offsetBy( 2, 0, 0))[n],
+ grid.getValue(ijk.offsetBy( 1, 0, 0))[n], grid.getValue(ijk.offsetBy(-1, 0, 0))[n],
+ grid.getValue(ijk.offsetBy(-2, 0, 0))[n], grid.getValue(ijk.offsetBy(-3, 0, 0))[n] );
+ }
+
+ template<typename Accessor>
+ static typename Accessor::ValueType::value_type inY(const Accessor& grid, const Coord& ijk, int n)
+ {
+
+ return D1<CD_6TH>::difference( grid.getValue(ijk.offsetBy( 0, 3, 0))[n], grid.getValue(ijk.offsetBy( 0, 2, 0))[n],
+ grid.getValue(ijk.offsetBy( 0, 1, 0))[n], grid.getValue(ijk.offsetBy( 0,-1, 0))[n],
+ grid.getValue(ijk.offsetBy( 0,-2, 0))[n], grid.getValue(ijk.offsetBy( 0,-3, 0))[n] );
+ }
+
+ template<typename Accessor>
+ static typename Accessor::ValueType::value_type inZ(const Accessor& grid, const Coord& ijk, int n)
+ {
+
+ return D1<CD_6TH>::difference( grid.getValue(ijk.offsetBy( 0, 0, 3))[n], grid.getValue(ijk.offsetBy( 0, 0, 2))[n],
+ grid.getValue(ijk.offsetBy( 0, 0, 1))[n], grid.getValue(ijk.offsetBy( 0, 0,-1))[n],
+ grid.getValue(ijk.offsetBy( 0, 0,-2))[n], grid.getValue(ijk.offsetBy( 0, 0,-3))[n] );
+ }
+
+
+ // stencil access version
+ template<typename Stencil>
+ static typename Stencil::ValueType::value_type inX(const Stencil& S, int n)
+ {
+ return D1<CD_6TH>::difference( S.template getValue< 3, 0, 0>()[n], S.template getValue< 2, 0, 0>()[n],
+ S.template getValue< 1, 0, 0>()[n], S.template getValue<-1, 0, 0>()[n],
+ S.template getValue<-2, 0, 0>()[n], S.template getValue<-3, 0, 0>()[n] );
+ }
+
+ template<typename Stencil>
+ static typename Stencil::ValueType::value_type inY(const Stencil& S, int n)
+ {
+ return D1<CD_6TH>::difference( S.template getValue< 0, 3, 0>()[n], S.template getValue< 0, 2, 0>()[n],
+ S.template getValue< 0, 1, 0>()[n], S.template getValue< 0,-1, 0>()[n],
+ S.template getValue< 0,-2, 0>()[n], S.template getValue< 0,-3, 0>()[n] );
+ }
+
+ template<typename Stencil>
+ static typename Stencil::ValueType::value_type inZ(const Stencil& S, int n)
+ {
+ return D1<CD_6TH>::difference( S.template getValue< 0, 0, 3>()[n], S.template getValue< 0, 0, 2>()[n],
+ S.template getValue< 0, 0, 1>()[n], S.template getValue< 0, 0,-1>()[n],
+ S.template getValue< 0, 0,-2>()[n], S.template getValue< 0, 0,-3>()[n] );
+ }
+};
+
+template<DDScheme DiffScheme>
+struct D2
+{
+
+ template<typename Accessor>
+ static typename Accessor::ValueType inX(const Accessor& grid, const Coord& ijk);
+ template<typename Accessor>
+ static typename Accessor::ValueType inY(const Accessor& grid, const Coord& ijk);
+ template<typename Accessor>
+ static typename Accessor::ValueType inZ(const Accessor& grid, const Coord& ijk);
+
+ // cross derivatives
+ template<typename Accessor>
+ static typename Accessor::ValueType inXandY(const Accessor& grid, const Coord& ijk);
+
+ template<typename Accessor>
+ static typename Accessor::ValueType inXandZ(const Accessor& grid, const Coord& ijk);
+
+ template<typename Accessor>
+ static typename Accessor::ValueType inYandZ(const Accessor& grid, const Coord& ijk);
+
+
+ // stencil access version
+ template<typename Stencil>
+ static typename Stencil::ValueType inX(const Stencil& S);
+ template<typename Stencil>
+ static typename Stencil::ValueType inY(const Stencil& S);
+ template<typename Stencil>
+ static typename Stencil::ValueType inZ(const Stencil& S);
+
+ // cross derivatives
+ template<typename Stencil>
+ static typename Stencil::ValueType inXandY(const Stencil& S);
+
+ template<typename Stencil>
+ static typename Stencil::ValueType inXandZ(const Stencil& S);
+
+ template<typename Stencil>
+ static typename Stencil::ValueType inYandZ(const Stencil& S);
+};
+
+template<>
+struct D2<CD_SECOND>
+{
+
+ // the difference opperator
+ template <typename ValueType>
+ static ValueType difference(const ValueType& xp1, const ValueType& xp0, const ValueType& xm1) {
+ return xp1 + xm1 - ValueType(2)*xp0;
+ }
+
+ template <typename ValueType>
+ static ValueType crossdifference(const ValueType& xpyp, const ValueType& xpym,
+ const ValueType& xmyp, const ValueType& xmym) {
+ return ValueType(0.25)*(xpyp + xmym - xpym - xmyp);
+ }
+
+ // random access version
+ template<typename Accessor>
+ static typename Accessor::ValueType inX(const Accessor& grid, const Coord& ijk)
+ {
+ return difference( grid.getValue(ijk.offsetBy( 1,0,0)), grid.getValue(ijk),
+ grid.getValue(ijk.offsetBy(-1,0,0)) );
+ }
+
+ template<typename Accessor>
+ static typename Accessor::ValueType inY(const Accessor& grid, const Coord& ijk)
+ {
+
+ return difference( grid.getValue(ijk.offsetBy(0, 1,0)), grid.getValue(ijk),
+ grid.getValue(ijk.offsetBy(0,-1,0)) );
+ }
+
+ template<typename Accessor>
+ static typename Accessor::ValueType inZ(const Accessor& grid, const Coord& ijk)
+ {
+ return difference( grid.getValue(ijk.offsetBy( 0,0, 1)), grid.getValue(ijk),
+ grid.getValue(ijk.offsetBy( 0,0,-1)) );
+ }
+
+ // cross derivatives
+ template<typename Accessor>
+ static typename Accessor::ValueType inXandY(const Accessor& grid, const Coord& ijk)
+ {
+ return crossdifference(grid.getValue(ijk.offsetBy(1, 1,0)), grid.getValue(ijk.offsetBy( 1,-1,0)),
+ grid.getValue(ijk.offsetBy(-1,1,0)), grid.getValue(ijk.offsetBy(-1,-1,0)));
+
+ }
+
+ template<typename Accessor>
+ static typename Accessor::ValueType inXandZ(const Accessor& grid, const Coord& ijk)
+ {
+ return crossdifference(grid.getValue(ijk.offsetBy(1,0, 1)), grid.getValue(ijk.offsetBy(1, 0,-1)),
+ grid.getValue(ijk.offsetBy(-1,0,1)), grid.getValue(ijk.offsetBy(-1,0,-1)) );
+ }
+
+ template<typename Accessor>
+ static typename Accessor::ValueType inYandZ(const Accessor& grid, const Coord& ijk)
+ {
+ return crossdifference(grid.getValue(ijk.offsetBy(0, 1,1)), grid.getValue(ijk.offsetBy(0, 1,-1)),
+ grid.getValue(ijk.offsetBy(0,-1,1)), grid.getValue(ijk.offsetBy(0,-1,-1)) );
+ }
+
+
+ // stencil access version
+ template<typename Stencil>
+ static typename Stencil::ValueType inX(const Stencil& S)
+ {
+ return difference( S.template getValue< 1, 0, 0>(), S.template getValue< 0, 0, 0>(),
+ S.template getValue<-1, 0, 0>() );
+ }
+
+ template<typename Stencil>
+ static typename Stencil::ValueType inY(const Stencil& S)
+ {
+ return difference( S.template getValue< 0, 1, 0>(), S.template getValue< 0, 0, 0>(),
+ S.template getValue< 0,-1, 0>() );
+ }
+
+ template<typename Stencil>
+ static typename Stencil::ValueType inZ(const Stencil& S)
+ {
+ return difference( S.template getValue< 0, 0, 1>(), S.template getValue< 0, 0, 0>(),
+ S.template getValue< 0, 0,-1>() );
+ }
+
+ // cross derivatives
+ template<typename Stencil>
+ static typename Stencil::ValueType inXandY(const Stencil& S)
+ {
+ return crossdifference(S.template getValue< 1, 1, 0>(), S.template getValue< 1,-1, 0>(),
+ S.template getValue<-1, 1, 0>(), S.template getValue<-1,-1, 0>() );
+ }
+
+ template<typename Stencil>
+ static typename Stencil::ValueType inXandZ(const Stencil& S)
+ {
+ return crossdifference(S.template getValue< 1, 0, 1>(), S.template getValue< 1, 0,-1>(),
+ S.template getValue<-1, 0, 1>(), S.template getValue<-1, 0,-1>() );
+ }
+
+ template<typename Stencil>
+ static typename Stencil::ValueType inYandZ(const Stencil& S)
+ {
+ return crossdifference(S.template getValue< 0, 1, 1>(), S.template getValue< 0, 1,-1>(),
+ S.template getValue< 0,-1, 1>(), S.template getValue< 0,-1,-1>() );
+ }
+};
+
+
+template<>
+struct D2<CD_FOURTH>
+{
+
+ // the difference opperator
+ template <typename ValueType>
+ static ValueType difference(const ValueType& xp2, const ValueType& xp1, const ValueType& xp0,
+ const ValueType& xm1, const ValueType& xm2) {
+ return ValueType(-1./12.)*(xp2 + xm2) + ValueType(4./3.)*(xp1 + xm1) -ValueType(2.5)*xp0;
+ }
+
+ template <typename ValueType>
+ static ValueType crossdifference(const ValueType& xp2yp2, const ValueType& xp2yp1,
+ const ValueType& xp2ym1, const ValueType& xp2ym2,
+ const ValueType& xp1yp2, const ValueType& xp1yp1,
+ const ValueType& xp1ym1, const ValueType& xp1ym2,
+ const ValueType& xm2yp2, const ValueType& xm2yp1,
+ const ValueType& xm2ym1, const ValueType& xm2ym2,
+ const ValueType& xm1yp2, const ValueType& xm1yp1,
+ const ValueType& xm1ym1, const ValueType& xm1ym2 ) {
+ ValueType tmp1 =
+ ValueType(2./3.0)*(xp1yp1 - xm1yp1 - xp1ym1 + xm1ym1)-
+ ValueType(1./12.)*(xp2yp1 - xm2yp1 - xp2ym1 + xm2ym1);
+ ValueType tmp2 =
+ ValueType(2./3.0)*(xp1yp2 - xm1yp2 - xp1ym2 + xm1ym2)-
+ ValueType(1./12.)*(xp2yp2 - xm2yp2 - xp2ym2 + xm2ym2);
+
+ return ValueType(2./3.)*tmp1 - ValueType(1./12.)*tmp2;
+ }
+
+
+
+ // random access version
+ template<typename Accessor>
+ static typename Accessor::ValueType inX(const Accessor& grid, const Coord& ijk)
+ {
+ return difference(grid.getValue(ijk.offsetBy(2,0,0)), grid.getValue(ijk.offsetBy( 1,0,0)),
+ grid.getValue(ijk),
+ grid.getValue(ijk.offsetBy(-1,0,0)), grid.getValue(ijk.offsetBy(-2, 0, 0)));
+ }
+
+ template<typename Accessor>
+ static typename Accessor::ValueType inY(const Accessor& grid, const Coord& ijk)
+ {
+ return difference(grid.getValue(ijk.offsetBy(0, 2,0)), grid.getValue(ijk.offsetBy(0, 1,0)),
+ grid.getValue(ijk),
+ grid.getValue(ijk.offsetBy(0,-1,0)), grid.getValue(ijk.offsetBy(0,-2, 0)));
+ }
+
+ template<typename Accessor>
+ static typename Accessor::ValueType inZ(const Accessor& grid, const Coord& ijk)
+ {
+ return difference(grid.getValue(ijk.offsetBy(0,0, 2)), grid.getValue(ijk.offsetBy(0, 0,1)),
+ grid.getValue(ijk),
+ grid.getValue(ijk.offsetBy(0,0,-1)), grid.getValue(ijk.offsetBy(0,0,-2)));
+ }
+
+ // cross derivatives
+ template<typename Accessor>
+ static typename Accessor::ValueType inXandY(const Accessor& grid, const Coord& ijk)
+ {
+ typedef typename Accessor::ValueType ValueType;
+ typename Accessor::ValueType tmp1 =
+ D1<CD_4TH>::inX(grid, ijk.offsetBy(0, 1, 0)) -
+ D1<CD_4TH>::inX(grid, ijk.offsetBy(0,-1, 0));
+ typename Accessor::ValueType tmp2 =
+ D1<CD_4TH>::inX(grid, ijk.offsetBy(0, 2, 0)) -
+ D1<CD_4TH>::inX(grid, ijk.offsetBy(0,-2, 0));
+ return ValueType(2./3.)*tmp1 - ValueType(1./12.)*tmp2;
+ }
+
+ template<typename Accessor>
+ static typename Accessor::ValueType inXandZ(const Accessor& grid, const Coord& ijk)
+ {
+ typedef typename Accessor::ValueType ValueType;
+ typename Accessor::ValueType tmp1 =
+ D1<CD_4TH>::inX(grid, ijk.offsetBy(0, 0, 1)) -
+ D1<CD_4TH>::inX(grid, ijk.offsetBy(0, 0,-1));
+ typename Accessor::ValueType tmp2 =
+ D1<CD_4TH>::inX(grid, ijk.offsetBy(0, 0, 2)) -
+ D1<CD_4TH>::inX(grid, ijk.offsetBy(0, 0,-2));
+ return ValueType(2./3.)*tmp1 - ValueType(1./12.)*tmp2;
+ }
+
+ template<typename Accessor>
+ static typename Accessor::ValueType inYandZ(const Accessor& grid, const Coord& ijk)
+ {
+ typedef typename Accessor::ValueType ValueType;
+ typename Accessor::ValueType tmp1 =
+ D1<CD_4TH>::inY(grid, ijk.offsetBy(0, 0, 1)) -
+ D1<CD_4TH>::inY(grid, ijk.offsetBy(0, 0,-1));
+ typename Accessor::ValueType tmp2 =
+ D1<CD_4TH>::inY(grid, ijk.offsetBy(0, 0, 2)) -
+ D1<CD_4TH>::inY(grid, ijk.offsetBy(0, 0,-2));
+ return ValueType(2./3.)*tmp1 - ValueType(1./12.)*tmp2;
+ }
+
+
+ // stencil access version
+ template<typename Stencil>
+ static typename Stencil::ValueType inX(const Stencil& S)
+ {
+ return difference(S.template getValue< 2, 0, 0>(), S.template getValue< 1, 0, 0>(),
+ S.template getValue< 0, 0, 0>(),
+ S.template getValue<-1, 0, 0>(), S.template getValue<-2, 0, 0>() );
+ }
+
+ template<typename Stencil>
+ static typename Stencil::ValueType inY(const Stencil& S)
+ {
+ return difference(S.template getValue< 0, 2, 0>(), S.template getValue< 0, 1, 0>(),
+ S.template getValue< 0, 0, 0>(),
+ S.template getValue< 0,-1, 0>(), S.template getValue< 0,-2, 0>() );
+ }
+
+ template<typename Stencil>
+ static typename Stencil::ValueType inZ(const Stencil& S)
+ {
+ return difference(S.template getValue< 0, 0, 2>(), S.template getValue< 0, 0, 1>(),
+ S.template getValue< 0, 0, 0>(),
+ S.template getValue< 0, 0,-1>(), S.template getValue< 0, 0,-2>() );
+ }
+
+ // cross derivatives
+ template<typename Stencil>
+ static typename Stencil::ValueType inXandY(const Stencil& S)
+ {
+ return crossdifference( S.template getValue< 2, 2, 0>(), S.template getValue< 2, 1, 0>(),
+ S.template getValue< 2,-1, 0>(), S.template getValue< 2,-2, 0>(),
+ S.template getValue< 1, 2, 0>(), S.template getValue< 1, 1, 0>(),
+ S.template getValue< 1,-1, 0>(), S.template getValue< 1,-2, 0>(),
+ S.template getValue<-2, 2, 0>(), S.template getValue<-2, 1, 0>(),
+ S.template getValue<-2,-1, 0>(), S.template getValue<-2,-2, 0>(),
+ S.template getValue<-1, 2, 0>(), S.template getValue<-1, 1, 0>(),
+ S.template getValue<-1,-1, 0>(), S.template getValue<-1,-2, 0>() );
+ }
+
+ template<typename Stencil>
+ static typename Stencil::ValueType inXandZ(const Stencil& S)
+ {
+ return crossdifference( S.template getValue< 2, 0, 2>(), S.template getValue< 2, 0, 1>(),
+ S.template getValue< 2, 0,-1>(), S.template getValue< 2, 0,-2>(),
+ S.template getValue< 1, 0, 2>(), S.template getValue< 1, 0, 1>(),
+ S.template getValue< 1, 0,-1>(), S.template getValue< 1, 0,-2>(),
+ S.template getValue<-2, 0, 2>(), S.template getValue<-2, 0, 1>(),
+ S.template getValue<-2, 0,-1>(), S.template getValue<-2, 0,-2>(),
+ S.template getValue<-1, 0, 2>(), S.template getValue<-1, 0, 1>(),
+ S.template getValue<-1, 0,-1>(), S.template getValue<-1, 0,-2>() );
+ }
+
+ template<typename Stencil>
+ static typename Stencil::ValueType inYandZ(const Stencil& S)
+ {
+ return crossdifference( S.template getValue< 0, 2, 2>(), S.template getValue< 0, 2, 1>(),
+ S.template getValue< 0, 2,-1>(), S.template getValue< 0, 2,-2>(),
+ S.template getValue< 0, 1, 2>(), S.template getValue< 0, 1, 1>(),
+ S.template getValue< 0, 1,-1>(), S.template getValue< 0, 1,-2>(),
+ S.template getValue< 0,-2, 2>(), S.template getValue< 0,-2, 1>(),
+ S.template getValue< 0,-2,-1>(), S.template getValue< 0,-2,-2>(),
+ S.template getValue< 0,-1, 2>(), S.template getValue< 0,-1, 1>(),
+ S.template getValue< 0,-1,-1>(), S.template getValue< 0,-1,-2>() );
+ }
+
+
+
+};
+
+template<>
+struct D2<CD_SIXTH>
+{
+
+ // the difference opperator
+ template <typename ValueType>
+ static ValueType difference(const ValueType& xp3, const ValueType& xp2, const ValueType& xp1,
+ const ValueType& xp0,
+ const ValueType& xm1, const ValueType& xm2, const ValueType& xm3) {
+ return ValueType(1./90.)*(xp3 + xm3) - ValueType(3./20.)*(xp2 + xm2)
+ + ValueType(1.5)*(xp1 + xm1) - ValueType(49./18.)*xp0;
+ }
+
+ template <typename ValueType>
+ static ValueType crossdifference( const ValueType& xp1yp1,const ValueType& xm1yp1,
+ const ValueType& xp1ym1,const ValueType& xm1ym1,
+ const ValueType& xp2yp1,const ValueType& xm2yp1,
+ const ValueType& xp2ym1,const ValueType& xm2ym1,
+ const ValueType& xp3yp1,const ValueType& xm3yp1,
+ const ValueType& xp3ym1,const ValueType& xm3ym1,
+ const ValueType& xp1yp2,const ValueType& xm1yp2,
+ const ValueType& xp1ym2,const ValueType& xm1ym2,
+ const ValueType& xp2yp2,const ValueType& xm2yp2,
+ const ValueType& xp2ym2,const ValueType& xm2ym2,
+ const ValueType& xp3yp2,const ValueType& xm3yp2,
+ const ValueType& xp3ym2,const ValueType& xm3ym2,
+ const ValueType& xp1yp3,const ValueType& xm1yp3,
+ const ValueType& xp1ym3,const ValueType& xm1ym3,
+ const ValueType& xp2yp3,const ValueType& xm2yp3,
+ const ValueType& xp2ym3,const ValueType& xm2ym3,
+ const ValueType& xp3yp3,const ValueType& xm3yp3,
+ const ValueType& xp3ym3,const ValueType& xm3ym3 )
+ {
+ ValueType tmp1 =
+ ValueType(0.7500)*(xp1yp1 - xm1yp1 - xp1ym1 + xm1ym1) -
+ ValueType(0.1500)*(xp2yp1 - xm2yp1 - xp2ym1 + xm2ym1) +
+ ValueType(1./60.)*(xp3yp1 - xm3yp1 - xp3ym1 + xm3ym1);
+
+ ValueType tmp2 =
+ ValueType(0.7500)*(xp1yp2 - xm1yp2 - xp1ym2 + xm1ym2) -
+ ValueType(0.1500)*(xp2yp2 - xm2yp2 - xp2ym2 + xm2ym2) +
+ ValueType(1./60.)*(xp3yp2 - xm3yp2 - xp3ym2 + xm3ym2);
+
+ ValueType tmp3 =
+ ValueType(0.7500)*(xp1yp3 - xm1yp3 - xp1ym3 + xm1ym3) -
+ ValueType(0.1500)*(xp2yp3 - xm2yp3 - xp2ym3 + xm2ym3) +
+ ValueType(1./60.)*(xp3yp3 - xm3yp3 - xp3ym3 + xm3ym3);
+
+ return ValueType(0.75)*tmp1 - ValueType(0.15)*tmp2 + ValueType(1./60)*tmp3;
+ }
+
+ // random access version
+
+ template<typename Accessor>
+ static typename Accessor::ValueType inX(const Accessor& grid, const Coord& ijk)
+ {
+ return difference(grid.getValue(ijk.offsetBy( 3, 0, 0)), grid.getValue(ijk.offsetBy( 2, 0, 0)),
+ grid.getValue(ijk.offsetBy( 1, 0, 0)), grid.getValue(ijk),
+ grid.getValue(ijk.offsetBy(-1, 0, 0)), grid.getValue(ijk.offsetBy(-2, 0, 0)),
+ grid.getValue(ijk.offsetBy(-3, 0, 0)) );
+
+ }
+
+ template<typename Accessor>
+ static typename Accessor::ValueType inY(const Accessor& grid, const Coord& ijk)
+ {
+
+ return difference(grid.getValue(ijk.offsetBy( 0, 3, 0)), grid.getValue(ijk.offsetBy( 0, 2, 0)),
+ grid.getValue(ijk.offsetBy( 0, 1, 0)), grid.getValue(ijk),
+ grid.getValue(ijk.offsetBy( 0,-1, 0)), grid.getValue(ijk.offsetBy( 0,-2, 0)),
+ grid.getValue(ijk.offsetBy( 0,-3, 0)) );
+
+ }
+
+ template<typename Accessor>
+ static typename Accessor::ValueType inZ(const Accessor& grid, const Coord& ijk)
+ {
+
+ return difference(grid.getValue(ijk.offsetBy( 0, 0, 3)), grid.getValue(ijk.offsetBy( 0, 0, 2)),
+ grid.getValue(ijk.offsetBy( 0, 0, 1)), grid.getValue(ijk),
+ grid.getValue(ijk.offsetBy( 0, 0,-1)), grid.getValue(ijk.offsetBy( 0, 0,-2)),
+ grid.getValue(ijk.offsetBy( 0, 0,-3)) );
+
+ }
+
+ template<typename Accessor>
+ static typename Accessor::ValueType inXandY(const Accessor& grid, const Coord& ijk)
+ {
+ typename Accessor::ValueType tmp1 =
+ D1<CD_6TH>::inX(grid, ijk.offsetBy(0, 1, 0)) -
+ D1<CD_6TH>::inX(grid, ijk.offsetBy(0,-1, 0));
+ typename Accessor::ValueType tmp2 =
+ D1<CD_6TH>::inX(grid, ijk.offsetBy(0, 2, 0)) -
+ D1<CD_6TH>::inX(grid, ijk.offsetBy(0,-2, 0));
+ typename Accessor::ValueType tmp3 =
+ D1<CD_6TH>::inX(grid, ijk.offsetBy(0, 3, 0)) -
+ D1<CD_6TH>::inX(grid, ijk.offsetBy(0,-3, 0));
+ return 0.75*tmp1 - 0.15*tmp2 + 1./60*tmp3;
+ }
+
+ template<typename Accessor>
+ static typename Accessor::ValueType inXandZ(const Accessor& grid, const Coord& ijk)
+ {
+ typename Accessor::ValueType tmp1 =
+ D1<CD_6TH>::inX(grid, ijk.offsetBy(0, 0, 1)) -
+ D1<CD_6TH>::inX(grid, ijk.offsetBy(0, 0,-1));
+ typename Accessor::ValueType tmp2 =
+ D1<CD_6TH>::inX(grid, ijk.offsetBy(0, 0, 2)) -
+ D1<CD_6TH>::inX(grid, ijk.offsetBy(0, 0,-2));
+ typename Accessor::ValueType tmp3 =
+ D1<CD_6TH>::inX(grid, ijk.offsetBy(0, 0, 3)) -
+ D1<CD_6TH>::inX(grid, ijk.offsetBy(0, 0,-3));
+ return 0.75*tmp1 - 0.15*tmp2 + 1./60*tmp3;
+ }
+
+ template<typename Accessor>
+ static typename Accessor::ValueType inYandZ(const Accessor& grid, const Coord& ijk)
+ {
+ typename Accessor::ValueType tmp1 =
+ D1<CD_6TH>::inY(grid, ijk.offsetBy(0, 0, 1)) -
+ D1<CD_6TH>::inY(grid, ijk.offsetBy(0, 0,-1));
+ typename Accessor::ValueType tmp2 =
+ D1<CD_6TH>::inY(grid, ijk.offsetBy(0, 0, 2)) -
+ D1<CD_6TH>::inY(grid, ijk.offsetBy(0, 0,-2));
+ typename Accessor::ValueType tmp3 =
+ D1<CD_6TH>::inY(grid, ijk.offsetBy(0, 0, 3)) -
+ D1<CD_6TH>::inY(grid, ijk.offsetBy(0, 0,-3));
+ return 0.75*tmp1 - 0.15*tmp2 + 1./60*tmp3;
+ }
+
+
+ // stencil access version
+ template<typename Stencil>
+ static typename Stencil::ValueType inX(const Stencil& S)
+ {
+ return difference( S.template getValue< 3, 0, 0>(), S.template getValue< 2, 0, 0>(),
+ S.template getValue< 1, 0, 0>(), S.template getValue< 0, 0, 0>(),
+ S.template getValue<-1, 0, 0>(), S.template getValue<-2, 0, 0>(),
+ S.template getValue<-3, 0, 0>() );
+ }
+
+ template<typename Stencil>
+ static typename Stencil::ValueType inY(const Stencil& S)
+ {
+ return difference( S.template getValue< 0, 3, 0>(), S.template getValue< 0, 2, 0>(),
+ S.template getValue< 0, 1, 0>(), S.template getValue< 0, 0, 0>(),
+ S.template getValue< 0,-1, 0>(), S.template getValue< 0,-2, 0>(),
+ S.template getValue< 0,-3, 0>() );
+
+ }
+
+ template<typename Stencil>
+ static typename Stencil::ValueType inZ(const Stencil& S)
+ {
+ return difference( S.template getValue< 0, 0, 3>(), S.template getValue< 0, 0, 2>(),
+ S.template getValue< 0, 0, 1>(), S.template getValue< 0, 0, 0>(),
+ S.template getValue< 0, 0,-1>(), S.template getValue< 0, 0,-2>(),
+ S.template getValue< 0, 0,-3>() );
+ }
+
+ template<typename Stencil>
+ static typename Stencil::ValueType inXandY(const Stencil& S)
+ {
+ return crossdifference( S.template getValue< 1, 1, 0>(), S.template getValue<-1, 1, 0>(),
+ S.template getValue< 1,-1, 0>(), S.template getValue<-1,-1, 0>(),
+ S.template getValue< 2, 1, 0>(), S.template getValue<-2, 1, 0>(),
+ S.template getValue< 2,-1, 0>(), S.template getValue<-2,-1, 0>(),
+ S.template getValue< 3, 1, 0>(), S.template getValue<-3, 1, 0>(),
+ S.template getValue< 3,-1, 0>(), S.template getValue<-3,-1, 0>(),
+ S.template getValue< 1, 2, 0>(), S.template getValue<-1, 2, 0>(),
+ S.template getValue< 1,-2, 0>(), S.template getValue<-1,-2, 0>(),
+ S.template getValue< 2, 2, 0>(), S.template getValue<-2, 2, 0>(),
+ S.template getValue< 2,-2, 0>(), S.template getValue<-2,-2, 0>(),
+ S.template getValue< 3, 2, 0>(), S.template getValue<-3, 2, 0>(),
+ S.template getValue< 3,-2, 0>(), S.template getValue<-3,-2, 0>(),
+ S.template getValue< 1, 3, 0>(), S.template getValue<-1, 3, 0>(),
+ S.template getValue< 1,-3, 0>(), S.template getValue<-1,-3, 0>(),
+ S.template getValue< 2, 3, 0>(), S.template getValue<-2, 3, 0>(),
+ S.template getValue< 2,-3, 0>(), S.template getValue<-2,-3, 0>(),
+ S.template getValue< 3, 3, 0>(), S.template getValue<-3, 3, 0>(),
+ S.template getValue< 3,-3, 0>(), S.template getValue<-3,-3, 0>() );
+
+ }
+
+ template<typename Stencil>
+ static typename Stencil::ValueType inXandZ(const Stencil& S)
+ {
+ return crossdifference( S.template getValue< 1, 0, 1>(), S.template getValue<-1, 0, 1>(),
+ S.template getValue< 1, 0,-1>(), S.template getValue<-1, 0,-1>(),
+ S.template getValue< 2, 0, 1>(), S.template getValue<-2, 0, 1>(),
+ S.template getValue< 2, 0,-1>(), S.template getValue<-2, 0,-1>(),
+ S.template getValue< 3, 0, 1>(), S.template getValue<-3, 0, 1>(),
+ S.template getValue< 3, 0,-1>(), S.template getValue<-3, 0,-1>(),
+ S.template getValue< 1, 0, 2>(), S.template getValue<-1, 0, 2>(),
+ S.template getValue< 1, 0,-2>(), S.template getValue<-1, 0,-2>(),
+ S.template getValue< 2, 0, 2>(), S.template getValue<-2, 0, 2>(),
+ S.template getValue< 2, 0,-2>(), S.template getValue<-2, 0,-2>(),
+ S.template getValue< 3, 0, 2>(), S.template getValue<-3, 0, 2>(),
+ S.template getValue< 3, 0,-2>(), S.template getValue<-3, 0,-2>(),
+ S.template getValue< 1, 0, 3>(), S.template getValue<-1, 0, 3>(),
+ S.template getValue< 1, 0,-3>(), S.template getValue<-1, 0,-3>(),
+ S.template getValue< 2, 0, 3>(), S.template getValue<-2, 0, 3>(),
+ S.template getValue< 2, 0,-3>(), S.template getValue<-2, 0,-3>(),
+ S.template getValue< 3, 0, 3>(), S.template getValue<-3, 0, 3>(),
+ S.template getValue< 3, 0,-3>(), S.template getValue<-3, 0,-3>() );
+
+ }
+
+ template<typename Stencil>
+ static typename Stencil::ValueType inYandZ(const Stencil& S)
+ {
+ return crossdifference( S.template getValue< 0, 1, 1>(), S.template getValue< 0,-1, 1>(),
+ S.template getValue< 0, 1,-1>(), S.template getValue< 0,-1,-1>(),
+ S.template getValue< 0, 2, 1>(), S.template getValue< 0,-2, 1>(),
+ S.template getValue< 0, 2,-1>(), S.template getValue< 0,-2,-1>(),
+ S.template getValue< 0, 3, 1>(), S.template getValue< 0,-3, 1>(),
+ S.template getValue< 0, 3,-1>(), S.template getValue< 0,-3,-1>(),
+ S.template getValue< 0, 1, 2>(), S.template getValue< 0,-1, 2>(),
+ S.template getValue< 0, 1,-2>(), S.template getValue< 0,-1,-2>(),
+ S.template getValue< 0, 2, 2>(), S.template getValue< 0,-2, 2>(),
+ S.template getValue< 0, 2,-2>(), S.template getValue< 0,-2,-2>(),
+ S.template getValue< 0, 3, 2>(), S.template getValue< 0,-3, 2>(),
+ S.template getValue< 0, 3,-2>(), S.template getValue< 0,-3,-2>(),
+ S.template getValue< 0, 1, 3>(), S.template getValue< 0,-1, 3>(),
+ S.template getValue< 0, 1,-3>(), S.template getValue< 0,-1,-3>(),
+ S.template getValue< 0, 2, 3>(), S.template getValue< 0,-2, 3>(),
+ S.template getValue< 0, 2,-3>(), S.template getValue< 0,-2,-3>(),
+ S.template getValue< 0, 3, 3>(), S.template getValue< 0,-3, 3>(),
+ S.template getValue< 0, 3,-3>(), S.template getValue< 0,-3,-3>() );
+ }
+
+};
+
+
+
+} // end math namespace
+} // namespace OPENVDB_VERSION_NAME
+} // end openvdb namespace
+
+#endif // OPENVDB_MATH_FINITEDIFFERENCE_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+
diff --git a/extern/openvdb/internal/openvdb/math/Hermite.cc b/extern/openvdb/internal/openvdb/math/Hermite.cc
new file mode 100644
index 00000000000..435ab1f82fc
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/math/Hermite.cc
@@ -0,0 +1,167 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+
+#include "Hermite.h"
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace math {
+
+
+////////////////////////////////////////
+
+// min and max on compressd data
+
+Hermite
+min(const Hermite& lhs, const Hermite& rhs)
+{
+ Hermite ret;
+
+ if(!lhs && !rhs) {
+
+ if(lhs.isInside()) ret = lhs;
+ else ret = rhs;
+
+ return ret;
+ }
+
+ ret.setIsInside(lhs.isInside() || rhs.isInside());
+
+ if(lhs.isGreaterX(rhs)) ret.setX(rhs);
+ else ret.setX(lhs);
+
+ if(lhs.isGreaterY(rhs)) ret.setY(rhs);
+ else ret.setY(lhs);
+
+ if(lhs.isGreaterZ(rhs)) ret.setZ(rhs);
+ else ret.setZ(lhs);
+
+ return ret;
+}
+
+Hermite
+max(const Hermite& lhs, const Hermite& rhs)
+{
+ Hermite ret;
+
+ if(!lhs && !rhs) {
+
+ if(!lhs.isInside()) ret = lhs;
+ else ret = rhs;
+
+ return ret;
+ }
+
+ ret.setIsInside(lhs.isInside() && rhs.isInside());
+
+ if(rhs.isGreaterX(lhs)) ret.setX(rhs);
+ else ret.setX(lhs);
+
+ if(rhs.isGreaterY(lhs)) ret.setY(rhs);
+ else ret.setY(lhs);
+
+ if(rhs.isGreaterZ(lhs)) ret.setZ(rhs);
+ else ret.setZ(lhs);
+
+ return ret;
+}
+
+
+////////////////////////////////////////
+
+// constructors
+
+Hermite::Hermite():
+ mXNormal(0),
+ mYNormal(0),
+ mZNormal(0),
+ mData(0)
+{
+}
+
+Hermite::Hermite(const Hermite& rhs):
+ mXNormal(rhs.mXNormal),
+ mYNormal(rhs.mYNormal),
+ mZNormal(rhs.mZNormal),
+ mData(rhs.mData)
+{
+}
+
+
+////////////////////////////////////////
+
+// string representation
+
+std::string
+Hermite::str() const
+{
+ std::ostringstream ss;
+
+ ss << "{ " << (isInside() ? "inside" : "outside");
+ if(hasOffsetX()) ss << " |x " << getOffsetX() << " " << getNormalX();
+ if(hasOffsetY()) ss << " |y " << getOffsetY() << " " << getNormalY();
+ if(hasOffsetZ()) ss << " |z " << getOffsetZ() << " " << getNormalZ();
+ ss << " }";
+
+ return ss.str();
+}
+
+
+////////////////////////////////////////
+
+
+void
+Hermite::read(std::istream& is)
+{
+ is.read(reinterpret_cast<char*>(&mXNormal), sizeof(uint16_t));
+ is.read(reinterpret_cast<char*>(&mYNormal), sizeof(uint16_t));
+ is.read(reinterpret_cast<char*>(&mZNormal), sizeof(uint16_t));
+ is.read(reinterpret_cast<char*>(&mData), sizeof(uint32_t));
+}
+
+void
+Hermite::write(std::ostream& os) const
+{
+ os.write(reinterpret_cast<const char*>(&mXNormal), sizeof(uint16_t));
+ os.write(reinterpret_cast<const char*>(&mYNormal), sizeof(uint16_t));
+ os.write(reinterpret_cast<const char*>(&mZNormal), sizeof(uint16_t));
+ os.write(reinterpret_cast<const char*>(&mData), sizeof(uint32_t));
+}
+
+
+} // namespace math
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/math/Hermite.h b/extern/openvdb/internal/openvdb/math/Hermite.h
new file mode 100644
index 00000000000..175478ce23d
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/math/Hermite.h
@@ -0,0 +1,493 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+
+#ifndef OPENVDB_MATH_HERMITE_HAS_BEEN_INCLUDED
+#define OPENVDB_MATH_HERMITE_HAS_BEEN_INCLUDED
+
+#include <openvdb/Platform.h>
+#include <openvdb/version.h>
+#include "QuantizedUnitVec.h"
+#include "Math.h"
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace math {
+
+
+// Forward declaration
+class Hermite;
+
+
+////////////////////////////////////////
+
+// Utility methods
+
+
+//@{
+/// min and max operations done directly on the compressed data.
+OPENVDB_API Hermite min(const Hermite&, const Hermite&);
+OPENVDB_API Hermite max(const Hermite&, const Hermite&);
+//@}
+
+
+////////////////////////////////////////
+
+
+/// @brief Quantized Hermite data object that stores compressed intersection
+/// information (offsets and normlas) for the up-wind edges of a voxel. (Size 10 bytes)
+class OPENVDB_API Hermite
+{
+public:
+
+ Hermite();
+ Hermite(const Hermite&);
+ const Hermite& operator=(const Hermite&);
+
+ /// clears all intersection data
+ void clear();
+
+ /// @return true if this Hermite objet has any edge intersection data.
+ operator bool() const;
+
+ /// equality operator
+ inline bool operator==(const Hermite&) const;
+ /// inequality operator
+ bool operator!=(const Hermite& rhs) const { return !(*this == rhs); }
+
+ /// unary negation operator, flips inside/outside state and normals.
+ Hermite operator-() const;
+
+ //@{
+ /// @brief methods to compress and store edge data.
+ /// @note @c offset is expected to be in the [0 to 1) range.
+ template <typename T>
+ void setX(T offset, const Vec3<T>&);
+
+ template <typename T>
+ void setY(T offset, const Vec3<T>&);
+
+ template <typename T>
+ void setZ(T offset, const Vec3<T>&);
+ //@}
+
+ /// @return true if the current Hermite object is classified
+ // as being inside a contour.
+ bool isInside() const { return MASK_SIGN & mData; }
+ /// Set the inside/outside state to reflect if this Hermite object
+ /// is located at a point in space that is inside/outside a contour.
+ void setIsInside(bool);
+
+ //@{
+ /// @return true if this Hermite object has intersection data
+ /// for the corresponding edge.
+ bool hasOffsetX() const { return mXNormal; };
+ bool hasOffsetY() const { return mYNormal; }
+ bool hasOffsetZ() const { return MASK_ZFLAG & mData; }
+ //@}
+
+ //@{
+ /// Edge offset greater-than comparisson operators
+ /// @note is @c this offset > than @c other offset
+ bool isGreaterX(const Hermite& other) const;
+ bool isGreaterY(const Hermite& other) const;
+ bool isGreaterZ(const Hermite& other) const;
+ //@}
+
+ //@{
+ /// Edge offset less-than comparisson operators
+ /// @note is @c this offset < than @c other offset
+ bool isLessX(const Hermite& other) const;
+ bool isLessY(const Hermite& other) const;
+ bool isLessZ(const Hermite& other) const;
+ //@}
+
+ //@{
+ /// @return uncompressed edge intersection offsets
+ float getOffsetX() const;
+ float getOffsetY() const;
+ float getOffsetZ() const;
+ //@}
+
+ //@{
+ /// @return uncompressed edge intersection normals
+ Vec3s getNormalX() const { return QuantizedUnitVec::unpack(mXNormal); }
+ Vec3s getNormalY() const { return QuantizedUnitVec::unpack(mYNormal); }
+ Vec3s getNormalZ() const { return QuantizedUnitVec::unpack(mZNormal); }
+ //@}
+
+ //@{
+ /// copy edge data from other Hermite object
+ /// @note copies data in the compressed form
+ void setX(const Hermite&);
+ void setY(const Hermite&);
+ void setZ(const Hermite&);
+ //@}
+
+ /// String representation.
+ std::string str() const;
+
+ /// Unserialize this transform from the given stream.
+ void read(std::istream&);
+ /// Serialize this transform to the given stream.
+ void write(std::ostream&) const;
+
+ //@{
+ /// Operators required by OpenVDB.
+ /// @note These methods don't perform meaningful operations on Hermite data.
+ bool operator< (const Hermite&) const { return false; };
+ bool operator> (const Hermite&) const { return false; };
+ template<class T> Hermite operator+(const T&) const { return *this; };
+ template<class T> Hermite operator-(const T&) const { return *this; };
+ //@}
+
+private:
+ /// Helper function that quantizes a [0, 1) offset using 10-bits.
+ template <typename T>
+ static uint32_t quantizeOffset(T offset);
+
+ /// Helper function that returns (signed) compressed-offsets,
+ /// used by comparisson operators.
+ static void getSignedOffsets(const Hermite& lhs, const Hermite& rhs,
+ const uint32_t bitmask, int& lhsV, int& rhsV);
+
+
+ // Bits masks
+ // 10000000000000000000000000000000
+ static const uint32_t MASK_SIGN = 0x80000000;
+ // 01000000000000000000000000000000
+ static const uint32_t MASK_ZFLAG = 0x40000000;
+ // 00111111111100000000000000000000
+ static const uint32_t MASK_XSLOT = 0x3FF00000;
+ // 00000000000011111111110000000000
+ static const uint32_t MASK_YSLOT = 0x000FFC00;
+ // 00000000000000000000001111111111
+ static const uint32_t MASK_ZSLOT = 0x000003FF;
+ // 00111111111111111111111111111111
+ static const uint32_t MASK_SLOTS = 0x3FFFFFFF;
+
+
+ uint16_t mXNormal, mYNormal, mZNormal;
+ uint32_t mData;
+
+}; // class Hermite
+
+
+////////////////////////////////////////
+
+// output-stream insertion operator
+
+inline std::ostream&
+operator<<(std::ostream& ostr, const Hermite& rhs)
+{
+ ostr << rhs.str();
+ return ostr;
+}
+
+
+////////////////////////////////////////
+
+// construction and assignment
+
+inline const Hermite&
+Hermite::operator=(const Hermite& rhs)
+{
+ mData = rhs.mData;
+ mXNormal = rhs.mXNormal;
+ mYNormal = rhs.mYNormal;
+ mZNormal = rhs.mZNormal;
+ return *this;
+}
+
+
+inline void
+Hermite::clear()
+{
+ mXNormal = 0;
+ mYNormal = 0;
+ mZNormal = 0;
+ mData = 0;
+}
+
+
+////////////////////////////////////////
+
+// bool operator and equality
+
+inline
+Hermite::operator bool() const
+{
+ if (0 != (mXNormal | mYNormal)) return true;
+ return hasOffsetZ();
+}
+
+
+inline bool
+Hermite::operator==(const Hermite& rhs) const
+{
+ if(mXNormal != rhs.mXNormal) return false;
+ if(mYNormal != rhs.mYNormal) return false;
+ if(mZNormal != rhs.mZNormal) return false;
+ return mData == rhs.mData;
+}
+
+
+////////////////////////////////////////
+
+// unary negation operator
+
+inline Hermite
+Hermite::operator-() const
+{
+ Hermite ret(*this);
+ QuantizedUnitVec::flipSignBits(ret.mXNormal);
+ QuantizedUnitVec::flipSignBits(ret.mYNormal);
+ QuantizedUnitVec::flipSignBits(ret.mZNormal);
+ ret.mData = (~MASK_SIGN & ret.mData) | (MASK_SIGN & ~ret.mData);
+ return ret;
+}
+
+
+////////////////////////////////////////
+
+// Helper funcions
+
+template <typename T>
+inline uint32_t
+Hermite::quantizeOffset(T offset)
+{
+ // the offset is expected to be normalized [0 to 1)
+ assert(offset < 1.0);
+ assert(offset > -1.0e-8);
+
+ // quantize the offset using 10-bits. (higher bits are masked out)
+ return uint32_t(1023 * offset) & MASK_ZSLOT;
+}
+
+inline void
+Hermite::getSignedOffsets(const Hermite& lhs, const Hermite& rhs,
+ const uint32_t bitmask, int& lhsV, int& rhsV)
+{
+ lhsV = bitmask & lhs.mData;
+ rhsV = bitmask & rhs.mData;
+
+ if(lhs.isInside()) lhsV = -lhsV;
+ if(rhs.isInside()) rhsV = -rhsV;
+}
+
+
+////////////////////////////////////////
+
+// compress and set edge data
+
+template <typename T>
+inline void
+Hermite::setX(T offset, const Vec3<T>& n)
+{
+ mData &= ~MASK_XSLOT; // clear xslot
+ mData |= quantizeOffset(offset) << 20;
+ mXNormal = QuantizedUnitVec::pack(n);
+}
+
+template <typename T>
+inline void
+Hermite::setY(T offset, const Vec3<T>& n)
+{
+ mData &= ~MASK_YSLOT; // clear yslot
+ mData |= quantizeOffset(offset) << 10;
+ mYNormal = QuantizedUnitVec::pack(n);
+}
+
+template <typename T>
+inline void
+Hermite::setZ(T offset, const Vec3<T>& n)
+{
+ mData &= ~MASK_ZSLOT; // clear zslot
+ mData |= MASK_ZFLAG | quantizeOffset(offset);
+ mZNormal = QuantizedUnitVec::pack(n);
+}
+
+
+////////////////////////////////////////
+
+// change inside/outside state
+
+inline void
+Hermite::setIsInside(bool isInside)
+{
+ mData &= ~MASK_SIGN; // clear sign-bit
+ mData |= uint32_t(isInside) * MASK_SIGN;
+}
+
+
+////////////////////////////////////////
+
+// Uncompress and return the edge intersection-offsets
+// 0.000977517 = 1.0 / 1023
+
+inline float
+Hermite::getOffsetX() const
+{
+ return float((mData >> 20) & MASK_ZSLOT) * 0.000977517;
+}
+
+inline float
+Hermite::getOffsetY() const
+{
+ return float((mData >> 10) & MASK_ZSLOT) * 0.000977517;
+}
+
+inline float
+Hermite::getOffsetZ() const
+{
+ return float(mData & MASK_ZSLOT) * 0.000977517;
+}
+
+
+////////////////////////////////////////
+
+// copy compressed edge data from other object
+
+inline void
+Hermite::setX(const Hermite& rhs)
+{
+ mData &= ~MASK_XSLOT; // clear xslot
+ mData |= MASK_XSLOT & rhs.mData; // copy xbits from rhs
+ mXNormal = rhs.mXNormal; // copy compressed normal
+
+ // Flip the copied normal if the rhs object has
+ // a different inside/outside state.
+ if(hasOffsetX() && isInside() != rhs.isInside())
+ QuantizedUnitVec::flipSignBits(mXNormal);
+}
+
+inline void
+Hermite::setY(const Hermite& rhs)
+{
+ mData &= ~MASK_YSLOT;
+ mData |= MASK_YSLOT & rhs.mData;
+ mYNormal = rhs.mYNormal;
+
+ if(hasOffsetY() && isInside() != rhs.isInside())
+ QuantizedUnitVec::flipSignBits(mYNormal);
+}
+
+inline void
+Hermite::setZ(const Hermite& rhs)
+{
+ mData &= ~MASK_ZSLOT;
+ mData |= (MASK_ZFLAG | MASK_ZSLOT) & rhs.mData;
+ mZNormal = rhs.mZNormal;
+ if(hasOffsetZ() && isInside() != rhs.isInside())
+ QuantizedUnitVec::flipSignBits(mZNormal);
+}
+
+
+////////////////////////////////////////
+
+// edge comparison operators
+
+inline bool
+Hermite::isGreaterX(const Hermite& rhs) const
+{
+ int lhsV, rhsV;
+ getSignedOffsets(*this, rhs, MASK_XSLOT, lhsV, rhsV);
+ return lhsV > rhsV;
+}
+
+inline bool
+Hermite::isGreaterY(const Hermite& rhs) const
+{
+ int lhsV, rhsV;
+ getSignedOffsets(*this, rhs, MASK_YSLOT, lhsV, rhsV);
+ return lhsV > rhsV;
+}
+
+inline bool
+Hermite::isGreaterZ(const Hermite& rhs) const
+{
+ int lhsV, rhsV;
+ getSignedOffsets(*this, rhs, MASK_ZSLOT, lhsV, rhsV);
+ return lhsV > rhsV;
+}
+
+inline bool
+Hermite::isLessX(const Hermite& rhs) const
+{
+ int lhsV, rhsV;
+ getSignedOffsets(*this, rhs, MASK_XSLOT, lhsV, rhsV);
+ return lhsV < rhsV;
+}
+
+inline bool
+Hermite::isLessY(const Hermite& rhs) const
+{
+ int lhsV, rhsV;
+ getSignedOffsets(*this, rhs, MASK_YSLOT, lhsV, rhsV);
+ return lhsV < rhsV;
+}
+
+inline bool
+Hermite::isLessZ(const Hermite& rhs) const
+{
+ int lhsV, rhsV;
+ getSignedOffsets(*this, rhs, MASK_ZSLOT, lhsV, rhsV);
+ return lhsV < rhsV;
+}
+
+
+////////////////////////////////////////
+
+
+inline bool
+isApproxEqual(const Hermite& lhs, const Hermite& rhs) { return lhs == rhs; }
+
+inline bool
+isApproxEqual(const Hermite& lhs, const Hermite& rhs, const Hermite& /*tolerance*/)
+ { return isApproxEqual(lhs, rhs); }
+
+
+} // namespace math
+
+
+////////////////////////////////////////
+
+
+template<> inline math::Hermite zeroVal<math::Hermite>() { return math::Hermite(); }
+
+
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_MATH_HERMITE_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/math/LegacyFrustum.h b/extern/openvdb/internal/openvdb/math/LegacyFrustum.h
new file mode 100644
index 00000000000..056158801ef
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/math/LegacyFrustum.h
@@ -0,0 +1,192 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+//
+/// @file math/LegacyFrustum.h
+
+#ifndef OPENVDB_MATH_LEGACYFRUSTUM_HAS_BEEN_INCLUDED
+#define OPENVDB_MATH_LEGACYFRUSTUM_HAS_BEEN_INCLUDED
+
+#include <iostream>
+#include "Coord.h"
+#include "Mat4.h"
+#include "Vec3.h"
+
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace math {
+namespace internal {
+
+/// @brief LegacyFrustum class used at DreamWorks for converting old vdb files.
+class LegacyFrustum
+{
+public:
+ LegacyFrustum(std::istream& is)
+ {
+ // First read in the old transform's base class.
+ // the "extents"
+ Coord tmpMin, tmpMax;
+ is.read(reinterpret_cast<char*>(&tmpMin), sizeof(Coord::ValueType) * 3);
+ is.read(reinterpret_cast<char*>(&tmpMax), sizeof(Coord::ValueType) * 3);
+
+ // set the extents
+ mExtents = CoordBBox(tmpMin, tmpMax);
+
+ // read the old-frustum class member data
+ //Mat4d tmpW2C;
+ Mat4d tmpW2C, tmpC2S, tmpS2C, tmpWorldToLocal;
+ Mat4d tmpS2U, tmpXYLocalToUnit, tmpZLocalToUnit;
+ Real tmpWindow[6];
+ Real tmpPadding;
+
+ //Mat4d tmpXYUnitToLocal, tmpZUnitToLocal
+
+ // read in each matrix.
+ is.read(reinterpret_cast<char*>(&tmpW2C),
+ sizeof(Mat4d::value_type) * Mat4d::size * Mat4d::size);
+ is.read(reinterpret_cast<char*>(&mC2W),
+ sizeof(Mat4d::value_type) * Mat4d::size * Mat4d::size);
+ is.read(reinterpret_cast<char*>(&tmpC2S),
+ sizeof(Mat4d::value_type) * Mat4d::size * Mat4d::size);
+ is.read(reinterpret_cast<char*>(&tmpS2C),
+ sizeof(Mat4d::value_type) * Mat4d::size * Mat4d::size);
+ is.read(reinterpret_cast<char*>(&tmpWorldToLocal),
+ sizeof(Mat4d::value_type) * Mat4d::size * Mat4d::size);
+ is.read(reinterpret_cast<char*>(&mLocalToWorld),
+ sizeof(Mat4d::value_type) * Mat4d::size * Mat4d::size);
+
+ is.read(reinterpret_cast<char*>(&tmpWindow[0]), sizeof(Real));
+ is.read(reinterpret_cast<char*>(&tmpWindow[1]), sizeof(Real));
+ is.read(reinterpret_cast<char*>(&tmpWindow[2]), sizeof(Real));
+ is.read(reinterpret_cast<char*>(&tmpWindow[3]), sizeof(Real));
+ is.read(reinterpret_cast<char*>(&tmpWindow[4]), sizeof(Real));
+ is.read(reinterpret_cast<char*>(&tmpWindow[5]), sizeof(Real));
+
+ is.read(reinterpret_cast<char*>(&tmpPadding), sizeof(Real));
+
+ is.read(reinterpret_cast<char*>(&tmpS2U),
+ sizeof(Mat4d::value_type) * Mat4d::size * Mat4d::size);
+ is.read(reinterpret_cast<char*>(&mXYUnitToLocal),
+ sizeof(Mat4d::value_type) * Mat4d::size * Mat4d::size);
+ is.read(reinterpret_cast<char*>(&tmpXYLocalToUnit),
+ sizeof(Mat4d::value_type) * Mat4d::size * Mat4d::size);
+ is.read(reinterpret_cast<char*>(&mZUnitToLocal),
+ sizeof(Mat4d::value_type) * Mat4d::size * Mat4d::size);
+ is.read(reinterpret_cast<char*>(&tmpZLocalToUnit),
+ sizeof(Mat4d::value_type) * Mat4d::size * Mat4d::size);
+
+
+ mNearPlane = tmpWindow[4];
+ mFarPlane = tmpWindow[5];
+
+ // Look up the world space corners of the
+ // frustum grid.
+ mFrNearOrigin = unitToLocalFrustum(Vec3R(0,0,0));
+ mFrFarOrigin = unitToLocalFrustum(Vec3R(0,0,1));
+
+ Vec3d frNearXTip = unitToLocalFrustum(Vec3R(1,0,0));
+ Vec3d frNearYTip = unitToLocalFrustum(Vec3R(0,1,0));
+ mFrNearXBasis = frNearXTip - mFrNearOrigin;
+ mFrNearYBasis = frNearYTip - mFrNearOrigin;
+
+ Vec3R frFarXTip = unitToLocalFrustum(Vec3R(1,0,1));
+ Vec3R frFarYTip = unitToLocalFrustum(Vec3R(0,1,1));
+ mFrFarXBasis = frFarXTip - mFrFarOrigin;
+ mFrFarYBasis = frFarYTip - mFrFarOrigin;
+ }
+
+ ~LegacyFrustum(){};
+
+ const Mat4d& getCamXForm() const {return mC2W; }
+
+ double getDepth() const {return (mFarPlane - mNearPlane); }
+ double getTaper() const {
+
+ return getNearPlaneWidth() / getFarPlaneWidth();
+ }
+
+ double getNearPlaneWidth() const {
+ double nearPlaneWidth = (unitToWorld(Vec3d(0,0,0)) - unitToWorld(Vec3d(1,0,0))).length();
+ return nearPlaneWidth;
+ }
+
+ double getFarPlaneWidth() const {
+ double farPlaneWidth = (unitToWorld(Vec3d(0,0,1)) - unitToWorld(Vec3d(1,0,1))).length();
+ return farPlaneWidth;
+ }
+
+ double getNearPlaneDist() const { return mNearPlane; }
+
+ const CoordBBox& getBBox() const {return mExtents; }
+
+ Vec3d unitToWorld(const Vec3d& in) const {return mLocalToWorld.transform( unitToLocal(in) ); }
+
+private:
+ LegacyFrustum(){};
+
+ Vec3d unitToLocal(const Vec3d& U) const {
+
+ // We first find the local space coordinates
+ // of the unit point projected onto the near
+ // and far planes of the frustum by using a
+ // linear combination of the planes basis vectors
+ Vec3d nearLS = ( U[0] * mFrNearXBasis ) + ( U[1] * mFrNearYBasis ) + mFrNearOrigin;
+ Vec3d farLS = ( U[0] * mFrFarXBasis ) + ( U[1] * mFrFarYBasis ) + mFrFarOrigin;
+
+ // then we lerp the two ws points in frustum z space
+ return U[2] * farLS + ( 1.0 - U[2] ) * nearLS;
+ }
+
+ Vec3d unitToLocalFrustum(const Vec3d& u) const {
+ Vec3d fzu = mZUnitToLocal.transformH(u);
+ Vec3d fu = u;
+ fu[2] = fzu.z();
+ return mXYUnitToLocal.transformH(fu);
+ }
+
+private:
+ Mat4d mC2W, mLocalToWorld, mXYUnitToLocal, mZUnitToLocal;
+ CoordBBox mExtents;
+ Vec3d mFrNearXBasis, mFrNearYBasis, mFrFarXBasis, mFrFarYBasis;
+ Vec3d mFrNearOrigin, mFrFarOrigin;
+ double mNearPlane, mFarPlane;
+};
+
+} // namespace internal
+} // namespace math
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_MATH_LEGACYFRUSTUM_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/math/Maps.cc b/extern/openvdb/internal/openvdb/math/Maps.cc
new file mode 100644
index 00000000000..e62a40266bf
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/math/Maps.cc
@@ -0,0 +1,283 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+
+#include "Maps.h"
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace math {
+
+
+////////////////////////////////////////
+
+
+MapRegistry* MapRegistry::mInstance = NULL;
+
+// Declare this at file scope to ensure thread-safe initialization.
+tbb::mutex sInitMapRegistryMutex;
+
+MapRegistry*
+MapRegistry::instance()
+{
+ Lock lock(sInitMapRegistryMutex);
+
+ if(mInstance == NULL) {
+ OPENVDB_START_THREADSAFE_STATIC_WRITE
+ mInstance = new MapRegistry();
+ OPENVDB_FINISH_THREADSAFE_STATIC_WRITE
+ return mInstance;
+ }
+
+ return mInstance;
+}
+
+MapBase::Ptr
+MapRegistry::createMap(const Name& name)
+{
+ Lock lock(instance()->mMutex);
+ MapDictionary::const_iterator iter = instance()->mMap.find(name);
+
+ if (iter == instance()->mMap.end()) {
+ OPENVDB_THROW(LookupError, "Cannot create map of unregistered type " << name);
+ }
+
+ return (iter->second)();
+}
+
+bool
+MapRegistry::isRegistered(const Name& name)
+{
+ Lock lock(instance()->mMutex);
+ return (instance()->mMap.find(name) != instance()->mMap.end());
+}
+
+void
+MapRegistry::registerMap(const Name& name, MapBase::MapFactory factory)
+{
+ Lock lock(instance()->mMutex);
+
+ if (instance()->mMap.find(name) != instance()->mMap.end()) {
+ OPENVDB_THROW(KeyError, "Map type " << name << " is already registered");
+ }
+
+ instance()->mMap[name] = factory;
+}
+
+void
+MapRegistry::unregisterMap(const Name& name)
+{
+ Lock lock(instance()->mMutex);
+ instance()->mMap.erase(name);
+}
+
+void
+MapRegistry::clear()
+{
+ Lock lock(instance()->mMutex);
+ instance()->mMap.clear();
+}
+
+
+////////////////////////////////////////
+
+// Utility methods for decomposition
+
+
+SymmetricMap::Ptr
+createSymmetricMap(const Mat3d& m)
+{
+ // test that the mat3 is a rotation || reflection
+ if (!isSymmetric(m)) {
+ OPENVDB_THROW(ArithmeticError,
+ "3x3 Matrix initializing symmetric map was not symmetric");
+ }
+ Vec3d eigenValues;
+ Mat3d Umatrix;
+
+ bool converged = math::diagonalizeSymmetricMatrix(m, Umatrix, eigenValues);
+ if (!converged) {
+ OPENVDB_THROW(ArithmeticError, "Diagonalization of the symmetric matrix failed");
+ }
+
+ UnitaryMap rotation(Umatrix);
+ ScaleMap diagonal(eigenValues);
+ CompoundMap<UnitaryMap, ScaleMap> first(rotation, diagonal);
+
+ UnitaryMap rotationInv(Umatrix.transpose());
+ return SymmetricMap::Ptr( new SymmetricMap(first, rotationInv));
+}
+
+
+PolarDecomposedMap::Ptr
+createPolarDecomposedMap(const Mat3d& m)
+{
+ // Because our internal libary left-multiplies vectors against matrices
+ // we are constructing M = Symmetric * Unitary instead of the more
+ // standard M = Unitary * Symmetric
+ Mat3d unitary, symmetric, mat3 = m.transpose();
+
+ // factor mat3 = U * S where U is unitary and S is symmetric
+ bool gotPolar = math::polarDecomposition(mat3, unitary, symmetric);
+ if (!gotPolar) {
+ OPENVDB_THROW(ArithmeticError, "Polar decomposition of transform failed");
+ }
+ // put the result in a polar map and then copy it into the output polar
+ UnitaryMap unitary_map(unitary.transpose());
+ SymmetricMap::Ptr symmetric_map = createSymmetricMap(symmetric);
+
+ return PolarDecomposedMap::Ptr(new PolarDecomposedMap(*symmetric_map, unitary_map));
+}
+
+
+FullyDecomposedMap::Ptr
+createFullyDecomposedMap(const Mat4d& m)
+{
+ if (!isAffine(m)) {
+ OPENVDB_THROW(ArithmeticError,
+ "4x4 Matrix initializing Decomposition map was not affine");
+ }
+
+ TranslationMap translate(m.getTranslation());
+ PolarDecomposedMap::Ptr polar = createPolarDecomposedMap(m.getMat3());
+
+ UnitaryAndTranslationMap rotationAndTranslate(polar->secondMap(), translate);
+
+ return FullyDecomposedMap::Ptr(new FullyDecomposedMap(polar->firstMap(), rotationAndTranslate));
+}
+
+
+MapBase::Ptr
+simplify(AffineMap::Ptr affine)
+{
+ if (affine->isScale()) { // can be simplified into a ScaleMap
+
+ Vec3d scale = affine->applyMap(Vec3d(1,1,1));
+ if (isApproxEqual(scale[0], scale[1]) && isApproxEqual(scale[0], scale[2])) {
+ return MapBase::Ptr(new UniformScaleMap(scale[0]));
+ } else {
+ return MapBase::Ptr(new ScaleMap(scale));
+ }
+
+ } else if (affine->isScaleTranslate()) { // can be simplified into a ScaleTranslateMap
+
+ Vec3d translate = affine->applyMap(Vec3d(0,0,0));
+ Vec3d scale = affine->applyMap(Vec3d(1,1,1)) - translate;
+
+ if (isApproxEqual(scale[0], scale[1]) && isApproxEqual(scale[0], scale[2])) {
+ return MapBase::Ptr(new UniformScaleTranslateMap(scale[0], translate));
+ } else {
+ return MapBase::Ptr(new ScaleTranslateMap(scale, translate));
+ }
+ }
+
+ // could not simplify the general Affine map.
+ return boost::static_pointer_cast<MapBase, AffineMap>(affine);
+}
+
+Mat4d
+approxInverse(const Mat4d& mat4d)
+{
+ if (std::abs(mat4d.det()) >= 3 * tolerance<double>::value()) {
+ try {
+ Mat4d result = mat4d.inverse();
+ return result;
+ } catch (ArithmeticError& ) {
+ // Mat4 code couldn't invert.
+ }
+ }
+
+ const Mat3d mat3 = mat4d.getMat3();
+ const Mat3d mat3T = mat3.transpose();
+ const Vec3d trans = mat4d.getTranslation();
+
+ // absolute tolerance used for the symmetric test.
+ const double tol = 1.e-6;
+
+ // only create the pseudoInverse for symmetric
+ bool symmetric = true;
+ for (int i = 0; i < 3; ++i ) {
+ for (int j = 0; j < 3; ++j ) {
+ if (!isApproxEqual(mat3[i][j], mat3T[i][j], tol)) {
+ symmetric = false;
+ }
+ }
+ }
+
+ if (!symmetric) {
+
+ // not symmetric, so just zero out the mat3 inverse and reverse the translation
+
+ Mat4d result = Mat4d::zero();
+ result.setTranslation(-trans);
+ result[3][3] = 1.f;
+ return result;
+
+ } else {
+
+ // compute the pseudo inverse
+
+ Mat3d eigenVectors;
+ Vec3d eigenValues;
+
+ diagonalizeSymmetricMatrix(mat3, eigenVectors, eigenValues);
+
+ Mat3d d = Mat3d::identity();
+ for (int i = 0; i < 3; ++i ) {
+ if (std::abs(eigenValues[i]) < 10.*tolerance<double>::value() ) {
+ d[i][i] = 0.f;
+ } else {
+ d[i][i] = 1.f/eigenValues[i];
+ }
+ }
+ // assemble the pseudo inverse
+
+ Mat3d pseudoInv = eigenVectors * d * eigenVectors.transpose();
+ Vec3d invTrans = -trans * pseudoInv;
+
+ Mat4d result = Mat4d::identity();
+ result.setMat3(pseudoInv);
+ result.setTranslation(invTrans);
+
+ return result;
+ }
+}
+
+////////////////////////////////////////
+
+
+} // namespace math
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/math/Maps.h b/extern/openvdb/internal/openvdb/math/Maps.h
new file mode 100644
index 00000000000..38b0875024b
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/math/Maps.h
@@ -0,0 +1,2478 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+//
+/// @file Maps.h
+
+#ifndef OPENVDB_MATH_MAPS_HAS_BEEN_INCLUDED
+#define OPENVDB_MATH_MAPS_HAS_BEEN_INCLUDED
+
+#include "Mat4.h"
+#include "Vec3.h"
+#include "BBox.h"
+#include "Coord.h"
+#include <openvdb/util/Name.h>
+#include <openvdb/Types.h>
+#include <boost/shared_ptr.hpp>
+#include <tbb/mutex.h>
+#include <map>
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace math {
+
+
+////////////////////////////////////////
+
+/// Forward declarations of the different map types
+
+class MapBase;
+class ScaleMap;
+class TranslationMap;
+class ScaleTranslateMap;
+class UniformScaleMap;
+class UniformScaleTranslateMap;
+class AffineMap;
+class UnitaryMap;
+class NonlinearFrustumMap;
+
+template<typename T1, typename T2> class CompoundMap;
+
+typedef CompoundMap<UnitaryMap, TranslationMap> UnitaryAndTranslationMap;
+typedef CompoundMap<CompoundMap<UnitaryMap, ScaleMap>, UnitaryMap> SpectralDecomposedMap;
+typedef SpectralDecomposedMap SymmetricMap;
+typedef CompoundMap<SymmetricMap, UnitaryAndTranslationMap> FullyDecomposedMap;
+typedef CompoundMap<SymmetricMap, UnitaryMap> PolarDecomposedMap;
+
+
+////////////////////////////////////////
+
+/// Map traits
+
+template<typename T> struct is_linear { static const bool value = false; };
+template<> struct is_linear<AffineMap> { static const bool value = true; };
+template<> struct is_linear<ScaleMap> { static const bool value = true; };
+template<> struct is_linear<UniformScaleMap> { static const bool value = true; };
+template<> struct is_linear<UnitaryMap> { static const bool value = true; };
+template<> struct is_linear<TranslationMap> { static const bool value = true; };
+template<> struct is_linear<ScaleTranslateMap> { static const bool value = true; };
+template<> struct is_linear<UniformScaleTranslateMap> { static const bool value = true; };
+
+template<typename T1, typename T2> struct is_linear<CompoundMap<T1, T2> > {
+ static const bool value = is_linear<T1>::value && is_linear<T2>::value;
+};
+
+
+template<typename T> struct is_uniform_scale { static const bool value = false; };
+template<> struct is_uniform_scale<UniformScaleMap> { static const bool value = true; };
+
+template<typename T> struct is_uniform_scale_translate { static const bool value = false; };
+template<> struct is_uniform_scale_translate<TranslationMap> { static const bool value = true; };
+template<> struct is_uniform_scale_translate<UniformScaleTranslateMap> {
+ static const bool value = true;
+};
+
+
+template<typename T> struct is_scale { static const bool value = false; };
+template<> struct is_scale<ScaleMap> { static const bool value = true; };
+
+template<typename T> struct is_scale_translate { static const bool value = false; };
+template<> struct is_scale_translate<ScaleTranslateMap> { static const bool value = true; };
+
+
+template<typename T> struct is_uniform_diagonal_jacobian {
+ static const bool value = is_uniform_scale<T>::value || is_uniform_scale_translate<T>::value;
+};
+
+template<typename T> struct is_diagonal_jacobian {
+ static const bool value = is_scale<T>::value || is_scale_translate<T>::value;
+};
+
+
+////////////////////////////////////////
+
+/// Utility methods
+
+/// @brief Create a SymmetricMap from a symmetric matrix.
+/// Decomposes the map into Rotation Diagonal Rotation^T
+OPENVDB_API boost::shared_ptr<SymmetricMap> createSymmetricMap(const Mat3d& m);
+
+
+/// @brief General decomposition of a Matrix into a Unitary (e.g. rotation)
+/// following a Symmetric (e.g. stretch & shear)
+OPENVDB_API boost::shared_ptr<FullyDecomposedMap> createFullyDecomposedMap(const Mat4d& m);
+
+
+/// @brief Decomposes a general linear into translation following polar decomposition.
+///
+/// T U S where:
+///
+/// T: Translation
+/// U: Unitary (rotation or reflection)
+/// S: Symmetric
+///
+/// @note: the Symmetric is automatically decomposed into Q D Q^T, where
+/// Q is rotation and D is diagonal.
+OPENVDB_API boost::shared_ptr<PolarDecomposedMap> createPolarDecomposedMap(const Mat3d& m);
+
+
+/// @brief reduces an AffineMap to a ScaleMap or a ScaleTranslateMap when it can
+OPENVDB_API boost::shared_ptr<MapBase> simplify(boost::shared_ptr<AffineMap> affine);
+
+/// @brief Returns the left pseudoInverse of the input matrix when the 3x3 part is symmetric
+/// otherwise it zeros the 3x3 and reverses the translation.
+OPENVDB_API Mat4d approxInverse(const Mat4d& mat);
+
+
+////////////////////////////////////////
+
+
+/// @brief Abstract base class for maps
+class OPENVDB_API MapBase
+{
+public:
+ typedef boost::shared_ptr<MapBase> Ptr;
+ typedef boost::shared_ptr<const MapBase> ConstPtr;
+ typedef Ptr (*MapFactory)();
+
+ virtual ~MapBase(){}
+
+ virtual boost::shared_ptr<AffineMap> getAffineMap() const = 0;
+
+ /// Return the name of this map's concrete type (e.g., @c "AffineMap").
+ virtual Name type() const = 0;
+
+ /// Return @c true if this map is of concrete type @c MapT (e.g., AffineMap).
+ template<typename MapT> bool isType() const { return this->type() == MapT::mapType(); }
+
+ /// Return @c true if this map is equal to the given map.
+ virtual bool isEqual(const MapBase& other) const = 0;
+
+ /// Return @c true if this map is linear.
+ virtual bool isLinear() const = 0;
+ /// Return @c true if the spacing between the image of latice is uniform in all directions
+ virtual bool hasUniformScale() const = 0;
+
+ virtual Vec3d applyMap(const Vec3d& in) const = 0;
+ virtual Vec3d applyInverseMap(const Vec3d& in) const = 0;
+
+ virtual Vec3d applyIJT(const Vec3d& in) const = 0;
+ virtual Vec3d applyIJT(const Vec3d& in, const Vec3d& pos) const = 0;
+ virtual Mat3d applyIJC(const Mat3d& m) const = 0;
+ virtual Mat3d applyIJC(const Mat3d& m, const Vec3d& v, const Vec3d& pos) const = 0;
+
+
+ virtual double determinant() const = 0;
+ virtual double determinant(const Vec3d&) const = 0;
+
+
+ //@{
+ /// @brief Method to return the local size of a voxel.
+ virtual Vec3d voxelSize() const = 0;
+ virtual Vec3d voxelSize(const Vec3d&) const = 0;
+ //@}
+
+ virtual void read(std::istream&) = 0;
+ virtual void write(std::ostream&) const = 0;
+
+ virtual std::string str() const = 0;
+
+ virtual MapBase::Ptr copy() const = 0;
+
+ //@{
+ /// @brief Methods to update the map
+ virtual MapBase::Ptr preRotate(double radians, Axis axis = X_AXIS) const = 0;
+ virtual MapBase::Ptr preTranslate(const Vec3d&) const = 0;
+ virtual MapBase::Ptr preScale(const Vec3d&) const = 0;
+ virtual MapBase::Ptr preShear(double shear, Axis axis0, Axis axis1) const = 0;
+
+ virtual MapBase::Ptr postRotate(double radians, Axis axis = X_AXIS) const = 0;
+ virtual MapBase::Ptr postTranslate(const Vec3d&) const = 0;
+ virtual MapBase::Ptr postScale(const Vec3d&) const = 0;
+ virtual MapBase::Ptr postShear(double shear, Axis axis0, Axis axis1) const = 0;
+ //@}
+
+protected:
+ MapBase() {}
+
+ template<typename MapT>
+ static bool isEqualBase(const MapT& self, const MapBase& other)
+ {
+ return other.isType<MapT>() && (self == *static_cast<const MapT*>(&other));
+ }
+};
+
+
+////////////////////////////////////////
+
+
+/// @brief Threadsafe singleton object for accessing the map type-name dictionary.
+/// Associates a map type-name with a factory function.
+class OPENVDB_API MapRegistry
+{
+public:
+ typedef std::map<Name, MapBase::MapFactory> MapDictionary;
+ typedef tbb::mutex Mutex;
+ typedef Mutex::scoped_lock Lock;
+
+ static MapRegistry* instance();
+
+ /// Create a new map of the given (registered) type name.
+ static MapBase::Ptr createMap(const Name&);
+
+ /// Return @c true if the given map type name is registered.
+ static bool isRegistered(const Name&);
+
+ /// Register a map type along with a factory function.
+ static void registerMap(const Name&, MapBase::MapFactory);
+
+ /// Remove a map type from the registry.
+ static void unregisterMap(const Name&);
+
+ /// Clear the map type registry.
+ static void clear();
+
+private:
+ MapRegistry() {}
+ static MapRegistry* mInstance;
+
+ mutable Mutex mMutex;
+ MapDictionary mMap;
+};
+
+
+////////////////////////////////////////
+
+
+/// @brief A general linear transform using homogeneous coordinates to perform
+/// rotation, scaling, shear and translation
+class OPENVDB_API AffineMap: public MapBase
+{
+public:
+ typedef boost::shared_ptr<AffineMap> Ptr;
+ typedef boost::shared_ptr<const AffineMap> ConstPtr;
+
+ AffineMap():
+ mMatrix(Mat4d::identity()),
+ mMatrixInv(Mat4d::identity()),
+ mJacobianInv(Mat3d::identity()),
+ mDeterminant(1),
+ mVoxelSize(Vec3d(1,1,1)),
+ mIsDiagonal(true),
+ mIsIdentity(true)
+ // the default constructor for translation is zero
+ {
+ }
+
+ AffineMap(const Mat3d& m)
+ {
+ Mat4d mat4(Mat4d::identity());
+ mat4.setMat3(m);
+ mMatrix = mat4;
+ updateAcceleration();
+ }
+
+ AffineMap(const Mat4d& m): mMatrix(m)
+ {
+ if (!isAffine(m)) {
+ OPENVDB_THROW(ArithmeticError,
+ "Tried to initialize an affine transform from a non-affine 4x4 matrix");
+ }
+ updateAcceleration();
+ }
+
+ AffineMap(const AffineMap& other):
+ MapBase(other),
+ mMatrix(other.mMatrix),
+ mMatrixInv(other.mMatrixInv),
+ mJacobianInv(other.mJacobianInv),
+ mDeterminant(other.mDeterminant),
+ mVoxelSize(other.mVoxelSize),
+ mIsDiagonal(other.mIsDiagonal),
+ mIsIdentity(other.mIsIdentity)
+ {
+ }
+
+ /// @brief constructor that merges the matrixes for two affine maps
+ AffineMap(const AffineMap& first, const AffineMap& second):
+ mMatrix(first.mMatrix * second.mMatrix)
+ {
+ updateAcceleration();
+ }
+
+ ~AffineMap() {}
+
+ /// Return a MapBase::Ptr to a new AffineMap
+ static MapBase::Ptr create() { return MapBase::Ptr(new AffineMap()); }
+ /// Return a MapBase::Ptr to a deep copy of this map
+ MapBase::Ptr copy() const { return MapBase::Ptr(new AffineMap(*this)); }
+
+ static bool isRegistered() { return MapRegistry::isRegistered(AffineMap::mapType()); }
+
+ static void registerMap()
+ {
+ MapRegistry::registerMap(
+ AffineMap::mapType(),
+ AffineMap::create);
+ }
+
+ Name type() const { return mapType(); }
+ static Name mapType() { return Name("AffineMap"); }
+
+ /// Return @c true (an AffineMap is always linear).
+ bool isLinear() const { return true; }
+
+ /// Return @c false ( test if this is unitary with translation )
+ bool hasUniformScale() const { return isUnitary(mMatrix.getMat3());}
+
+ virtual bool isEqual(const MapBase& other) const { return isEqualBase(*this, other); }
+
+ bool operator==(const AffineMap& other) const
+ {
+ // the Mat.eq() is approximate
+ if (!mMatrix.eq(other.mMatrix)) { return false; }
+ if (!mMatrixInv.eq(other.mMatrixInv)) { return false; }
+ return true;
+ }
+
+ bool operator!=(const AffineMap& other) const { return !(*this == other); }
+
+ AffineMap& operator=(const AffineMap& other)
+ {
+ mMatrix = other.mMatrix;
+ mMatrixInv = other.mMatrixInv;
+
+ mJacobianInv = other.mJacobianInv;
+ mDeterminant = other.mDeterminant;
+ mVoxelSize = other.mVoxelSize;
+ mIsDiagonal = other.mIsDiagonal;
+ mIsIdentity = other.mIsIdentity;
+ return *this;
+ }
+ /// Return the image of @c in under the map
+ Vec3d applyMap(const Vec3d& in) const { return in * mMatrix; }
+ /// Return the pre-image of @c in under the map
+ Vec3d applyInverseMap(const Vec3d& in) const {return in * mMatrixInv; }
+
+ /// Return the Jacobian of the map applied to @a in.
+ Vec3d applyJacobian(const Vec3d& in, const Vec3d&) const { return applyJacobian(in); }
+ /// Return the Jacobian of the map applied to @a in.
+ Vec3d applyJacobian(const Vec3d& in) const {
+ const double* m = mMatrix.asPointer();
+ return Vec3d( m[ 0] * in[0] + m[ 4] * in[1] + m[ 8] * in[2],
+ m[ 1] * in[0] + m[ 5] * in[1] + m[ 9] * in[2],
+ m[ 2] * in[0] + m[ 6] * in[1] + m[10] * in[2] );
+ }
+
+ /// Return the transpose of the inverse Jacobian of the map applied to @a in.
+ Vec3d applyIJT(const Vec3d& in, const Vec3d&) const { return applyIJT(in); }
+ /// Return the transpose of the inverse Jacobian of the map applied to @c in
+ Vec3d applyIJT(const Vec3d& in) const { return in * mJacobianInv; }
+ /// Return the Jacobian Curvature: zero for a linear map
+ Mat3d applyIJC(const Mat3d& m) const {
+ return mJacobianInv.transpose()* m * mJacobianInv;
+ }
+ Mat3d applyIJC(const Mat3d& in, const Vec3d& , const Vec3d& ) const {
+ return applyIJC(in);
+ }
+ /// Return the determinant of the Jacobian, ignores argument
+ double determinant(const Vec3d& ) const { return determinant(); }
+ /// Return the determinant of the Jacobian
+ double determinant() const { return mDeterminant; }
+
+ //@{
+ /// @brief Return the lengths of the images of the segments
+ /// (0,0,0)-(1,0,0), (0,0,0)-(0,1,0) and (0,0,0)-(0,0,1).
+ Vec3d voxelSize() const { return mVoxelSize; }
+ Vec3d voxelSize(const Vec3d&) const { return voxelSize(); }
+ //@}
+
+ /// Return @c true if the underlying matrix is approximately an identity
+ bool isIdentity() const { return mIsIdentity; }
+ /// Return @c true if the underylying matrix is diagonal
+ bool isDiagonal() const { return mIsDiagonal; }
+ /// Return @c true if the map is equivalent to a ScaleMap
+ bool isScale() const { return isDiagonal(); }
+ /// Return @c true if the map is equivalent to a ScaleTranslateMap
+ bool isScaleTranslate() const { return math::isDiagonal(mMatrix.getMat3()); }
+
+
+ // Methods that modify the existing affine map
+
+ //@{
+ /// @brief Methods that modify the existing
+ /// affine map by pre-applying the given operation.
+ void accumPreRotation(Axis axis, double radians)
+ {
+ mMatrix.preRotate(axis, radians);
+ updateAcceleration();
+ }
+ void accumPreScale(const Vec3d& v)
+ {
+ mMatrix.preScale(v);
+ updateAcceleration();
+ }
+ void accumPreTranslation(const Vec3d& v)
+ {
+ mMatrix.preTranslate(v);
+ updateAcceleration();
+ }
+ void accumPreShear(Axis axis0, Axis axis1, double shear)
+ {
+ mMatrix.preShear(axis0, axis1, shear);
+ updateAcceleration();
+ }
+ //@}
+
+
+ //@{
+ /// @brief Methods that modify the existing
+ /// affine map by post-applying the given operation.
+ void accumPostRotation(Axis axis, double radians)
+ {
+ mMatrix.postRotate(axis, radians);
+ updateAcceleration();
+ }
+ void accumPostScale(const Vec3d& v)
+ {
+ mMatrix.postScale(v);
+ updateAcceleration();
+ }
+ void accumPostTranslation(const Vec3d& v)
+ {
+ mMatrix.postTranslate(v);
+ updateAcceleration();
+ }
+ void accumPostShear(Axis axis0, Axis axis1, double shear)
+ {
+ mMatrix.postShear(axis0, axis1, shear);
+ updateAcceleration();
+ }
+ //@}
+
+
+ /// read serialization
+ void read(std::istream& is)
+ {
+ mMatrix.read(is);
+ updateAcceleration();
+ }
+
+ /// write serialization
+ void write(std::ostream& os) const
+ {
+ mMatrix.write(os);
+ }
+
+ /// string serialization, useful for debugging
+ std::string str() const
+ {
+ std::ostringstream buffer;
+ buffer << " - mat4:\n" << mMatrix.str() << std::endl;
+ buffer << " - voxel dimensions: " << mVoxelSize << std::endl;
+ return buffer.str();
+ }
+
+ /// on-demand decomposition of the affine map
+ boost::shared_ptr<FullyDecomposedMap> createDecomposedMap()
+ {
+ return createFullyDecomposedMap(mMatrix);
+ }
+
+ /// Return AffineMap::Ptr to a deep copy of the current AffineMap
+ AffineMap::Ptr getAffineMap() const { return AffineMap::Ptr(new AffineMap(*this)); }
+
+ /// Return AffineMap::Ptr to the inverse of this map
+ AffineMap::Ptr inverse() const { return AffineMap::Ptr(new AffineMap(mMatrixInv)); }
+
+
+ //@{
+ /// @brief Return a MapBase::Ptr to a new map that is the result
+ /// of prepending the appropraite operation.
+ MapBase::Ptr preRotate(double radians, Axis axis = X_AXIS) const
+ {
+ AffineMap::Ptr affineMap = getAffineMap();
+ affineMap->accumPreRotation(axis, radians);
+ return simplify(affineMap);
+ }
+ MapBase::Ptr preTranslate(const Vec3d& t) const
+ {
+ AffineMap::Ptr affineMap = getAffineMap();
+ affineMap->accumPreTranslation(t);
+ return boost::static_pointer_cast<MapBase, AffineMap>(affineMap);
+ }
+ MapBase::Ptr preScale(const Vec3d& s) const
+ {
+ AffineMap::Ptr affineMap = getAffineMap();
+ affineMap->accumPreScale(s);
+ return boost::static_pointer_cast<MapBase, AffineMap>(affineMap);
+ }
+ MapBase::Ptr preShear(double shear, Axis axis0, Axis axis1) const
+ {
+ AffineMap::Ptr affineMap = getAffineMap();
+ affineMap->accumPreShear(axis0, axis1, shear);
+ return simplify(affineMap);
+ }
+ //@}
+
+
+ //@{
+ /// @brief Return a MapBase::Ptr to a new map that is the result
+ /// of postfixing the appropraite operation.
+ MapBase::Ptr postRotate(double radians, Axis axis = X_AXIS) const
+ {
+ AffineMap::Ptr affineMap = getAffineMap();
+ affineMap->accumPostRotation(axis, radians);
+ return simplify(affineMap);
+ }
+ MapBase::Ptr postTranslate(const Vec3d& t) const
+ {
+ AffineMap::Ptr affineMap = getAffineMap();
+ affineMap->accumPostTranslation(t);
+ return boost::static_pointer_cast<MapBase, AffineMap>(affineMap);
+ }
+ MapBase::Ptr postScale(const Vec3d& s) const
+ {
+ AffineMap::Ptr affineMap = getAffineMap();
+ affineMap->accumPostScale(s);
+ return boost::static_pointer_cast<MapBase, AffineMap>(affineMap);
+ }
+ MapBase::Ptr postShear(double shear, Axis axis0, Axis axis1) const
+ {
+ AffineMap::Ptr affineMap = getAffineMap();
+ affineMap->accumPostShear(axis0, axis1, shear);
+ return simplify(affineMap);
+ }
+ //@}
+
+ /// Return the matrix representation of this AffineMap
+ Mat4d getMat4() const { return mMatrix;}
+ const Mat4d& getConstMat4() const {return mMatrix;}
+ const Mat3d& getConstJacobianInv() const {return mJacobianInv;}
+
+private:
+ void updateAcceleration() {
+ mDeterminant = mMatrix.getMat3().det();
+
+ if (std::abs(mDeterminant) < (3.0 * tolerance<double>::value())) {
+ OPENVDB_THROW(ArithmeticError,
+ "Tried to initialize an affine transform from a nearly singular matrix");
+ }
+ mMatrixInv = mMatrix.inverse();
+ mJacobianInv = mMatrixInv.getMat3().transpose();
+ mIsDiagonal = math::isDiagonal(mMatrix);
+ mIsIdentity = math::isIdentity(mMatrix);
+ Vec3d pos = applyMap(Vec3d(0,0,0));
+ mVoxelSize(0) = (applyMap(Vec3d(1,0,0)) - pos).length();
+ mVoxelSize(1) = (applyMap(Vec3d(0,1,0)) - pos).length();
+ mVoxelSize(2) = (applyMap(Vec3d(0,0,1)) - pos).length();
+ }
+
+ // the underlying matrix
+ Mat4d mMatrix;
+
+ // stored for acceleration
+ Mat4d mMatrixInv;
+ Mat3d mJacobianInv;
+ double mDeterminant;
+ Vec3d mVoxelSize;
+ bool mIsDiagonal, mIsIdentity;
+}; // class AffineMap
+
+
+////////////////////////////////////////
+
+
+/// @brief A specialized Affine transform that scales along the principal axis
+/// the scaling need not be uniform in the three-directions
+class OPENVDB_API ScaleMap: public MapBase
+{
+public:
+ typedef boost::shared_ptr<ScaleMap> Ptr;
+ typedef boost::shared_ptr<const ScaleMap> ConstPtr;
+
+ ScaleMap(): MapBase(), mScaleValues(Vec3d(1,1,1)), mVoxelSize(Vec3d(1,1,1)),
+ mInvScaleSqr(1,1,1), mInvTwiceScale(0.5,0.5,0.5){}
+
+ ScaleMap(const Vec3d& scale):
+ MapBase(),
+ mScaleValues(scale),
+ mVoxelSize(Vec3d(std::abs(scale(0)),std::abs(scale(1)), std::abs(scale(2))))
+ {
+ double determinant = scale[0]* scale[1] * scale[2];
+ if (std::abs(determinant) < 3.0 * tolerance<double>::value()) {
+ OPENVDB_THROW(ArithmeticError, "Non-zero scale values required");
+ }
+ mScaleValuesInverse = 1.0 / mScaleValues;
+ mInvScaleSqr = mScaleValuesInverse * mScaleValuesInverse;
+ mInvTwiceScale = mScaleValuesInverse / 2;
+ }
+
+ ScaleMap(const ScaleMap& other):
+ MapBase(),
+ mScaleValues(other.mScaleValues),
+ mVoxelSize(other.mVoxelSize),
+ mScaleValuesInverse(other.mScaleValuesInverse),
+ mInvScaleSqr(other.mInvScaleSqr),
+ mInvTwiceScale(other.mInvTwiceScale)
+ {
+ }
+
+ ~ScaleMap() {}
+
+ /// Return a MapBase::Ptr to a new ScaleMap
+ static MapBase::Ptr create() { return MapBase::Ptr(new ScaleMap()); }
+ /// Return a MapBase::Ptr to a deep copy of this map
+ MapBase::Ptr copy() const { return MapBase::Ptr(new ScaleMap(*this)); }
+
+ static bool isRegistered() { return MapRegistry::isRegistered(ScaleMap::mapType()); }
+
+ static void registerMap()
+ {
+ MapRegistry::registerMap(
+ ScaleMap::mapType(),
+ ScaleMap::create);
+ }
+
+ Name type() const { return mapType(); }
+ static Name mapType() { return Name("ScaleMap"); }
+
+ /// Return @c true (a ScaleMap is always linear).
+ bool isLinear() const { return true; }
+
+ /// Return @c true if the values have the same magitude (eg. -1, 1, -1 would be a rotation).
+ bool hasUniformScale() const {
+ bool value;
+ value = isApproxEqual(std::abs(mScaleValues.x()), std::abs(mScaleValues.y()), double(5e-7));
+ value = value && isApproxEqual(std::abs(mScaleValues.x()), std::abs(mScaleValues.z()), double(5e-7));
+ return value;
+ }
+
+ /// Return the image of @c in under the map
+ Vec3d applyMap(const Vec3d& in) const
+ {
+ return Vec3d(
+ in.x() * mScaleValues.x(),
+ in.y() * mScaleValues.y(),
+ in.z() * mScaleValues.z());
+ }
+ /// Return the pre-image of @c in under the map
+ Vec3d applyInverseMap(const Vec3d& in) const
+ {
+ return Vec3d(
+ in.x() * mScaleValuesInverse.x(),
+ in.y() * mScaleValuesInverse.y(),
+ in.z() * mScaleValuesInverse.z());
+ }
+ /// Return the Jacobian of the map applied to @a in.
+ Vec3d applyJacobian(const Vec3d& in, const Vec3d&) const { return applyJacobian(in); }
+ /// Return the Jacobian of the map applied to @a in.
+ Vec3d applyJacobian(const Vec3d& in) const {
+ return applyMap(in);
+ }
+
+
+ /// @brief Return the transpose of the inverse Jacobian of the map applied to @a in.
+ /// @details Ignores second argument
+ Vec3d applyIJT(const Vec3d& in, const Vec3d& ) const { return applyIJT(in);}
+ /// Return the transpose of the inverse Jacobian of the map applied to @c in
+ Vec3d applyIJT(const Vec3d& in) const { return applyInverseMap(in); }
+ /// Return the Jacobian Curvature: zero for a linear map
+ Mat3d applyIJC(const Mat3d& in) const {
+ Mat3d tmp;
+ for (int i=0; i<3; i++){
+ tmp.setRow(i, in.row(i)*mScaleValuesInverse(i));
+ }
+ for (int i=0; i<3; i++){
+ tmp.setCol(i, tmp.col(i)*mScaleValuesInverse(i));
+ }
+ return tmp;
+ }
+ Mat3d applyIJC(const Mat3d& in, const Vec3d&, const Vec3d& ) const {
+ return applyIJC(in);
+ }
+ /// Return the product of the scale values, ignores argument
+ double determinant(const Vec3d& ) const { return determinant(); }
+ /// Return the product of the scale values
+ double determinant() const { return mScaleValues.x()*mScaleValues.y()*mScaleValues.z(); }
+
+ /// Return the scale values that define the map
+ const Vec3d& getScale() const {return mScaleValues;}
+
+ /// Return the square of the scale. Used to optimize some finite difference calculations
+ const Vec3d& getInvScaleSqr() const {return mInvScaleSqr;}
+ /// Return 1/(2 scale). Used to optimize some finite difference calculations
+ const Vec3d& getInvTwiceScale() const {return mInvTwiceScale;}
+ /// Return 1/(scale)
+ const Vec3d& getInvScale() const {return mScaleValuesInverse; }
+
+ //@{
+ /// @brief Returns the lengths of the images
+ /// of the segments
+ /// \f$(0,0,0)-(1,0,0)\f$, \f$(0,0,0)-(0,1,0)\f$, \f$(0,0,0)-(0,0,1)\f$
+ /// this is equivalent to the absolute values of the scale values
+ Vec3d voxelSize() const { return mVoxelSize; }
+ Vec3d voxelSize(const Vec3d&) const { return voxelSize();}
+ //@}
+
+ /// read serialization
+ void read(std::istream& is)
+ {
+ mScaleValues.read(is);
+ mVoxelSize.read(is);
+ mScaleValuesInverse.read(is);
+ mInvScaleSqr.read(is);
+ mInvTwiceScale.read(is);
+ }
+ /// write serialization
+ void write(std::ostream& os) const
+ {
+ mScaleValues.write(os);
+ mVoxelSize.write(os);
+ mScaleValuesInverse.write(os);
+ mInvScaleSqr.write(os);
+ mInvTwiceScale.write(os);
+ }
+ /// string serialization, useful for debuging
+ std::string str() const
+ {
+ std::ostringstream buffer;
+ buffer << " - scale: " << mScaleValues << std::endl;
+ buffer << " - voxel dimensions: " << mVoxelSize << std::endl;
+ return buffer.str();
+ }
+
+ virtual bool isEqual(const MapBase& other) const { return isEqualBase(*this, other); }
+
+ bool operator==(const ScaleMap& other) const
+ {
+ // ::eq() uses a tolerance
+ if (!mScaleValues.eq(other.mScaleValues)) { return false; }
+ return true;
+ }
+
+ bool operator!=(const ScaleMap& other) const { return !(*this == other); }
+
+ /// Return a AffineMap equivalent to this map
+ AffineMap::Ptr getAffineMap() const
+ {
+ return AffineMap::Ptr(new AffineMap(math::scale<Mat4d>(mScaleValues)));
+ }
+
+
+
+ //@{
+ /// @brief Return a MapBase::Ptr to a new map that is the result
+ /// of prepending the appropraite operation to the existing map
+ MapBase::Ptr preRotate(double radians, Axis axis) const
+ {
+ AffineMap::Ptr affineMap = getAffineMap();
+ affineMap->accumPreRotation(axis, radians);
+ return simplify(affineMap);
+ }
+
+ MapBase::Ptr preTranslate(const Vec3d& tr) const;
+
+ MapBase::Ptr preScale(const Vec3d& v) const;
+
+ MapBase::Ptr preShear(double shear, Axis axis0, Axis axis1) const
+ {
+ AffineMap::Ptr affineMap = getAffineMap();
+ affineMap->accumPreShear(axis0, axis1, shear);
+ return simplify(affineMap);
+ }
+ //@}
+
+
+ //@{
+ /// @brief Return a MapBase::Ptr to a new map that is the result
+ /// of prepending the appropraite operation to the existing map.
+ MapBase::Ptr postRotate(double radians, Axis axis) const
+ {
+ AffineMap::Ptr affineMap = getAffineMap();
+ affineMap->accumPostRotation(axis, radians);
+ return simplify(affineMap);
+ }
+
+ MapBase::Ptr postTranslate(const Vec3d& tr) const;
+
+ MapBase::Ptr postScale(const Vec3d& v) const;
+
+ MapBase::Ptr postShear(double shear, Axis axis0, Axis axis1) const
+ {
+ AffineMap::Ptr affineMap = getAffineMap();
+ affineMap->accumPostShear(axis0, axis1, shear);
+ return simplify(affineMap);
+ }
+ //@}
+
+private:
+ Vec3d mScaleValues, mVoxelSize, mScaleValuesInverse, mInvScaleSqr, mInvTwiceScale;
+}; // class ScaleMap
+
+
+/// @brief A specialized Affine transform that scales along the principal axis
+/// the scaling is uniform in the three-directions
+class OPENVDB_API UniformScaleMap: public ScaleMap
+{
+public:
+ typedef boost::shared_ptr<UniformScaleMap> Ptr;
+ typedef boost::shared_ptr<const UniformScaleMap> ConstPtr;
+
+ UniformScaleMap(): ScaleMap(Vec3d(1,1,1)) {}
+ UniformScaleMap(double scale): ScaleMap(Vec3d(scale, scale, scale)) {}
+ UniformScaleMap(const UniformScaleMap& other): ScaleMap(other) {}
+ ~UniformScaleMap() {}
+
+ /// Return a MapBase::Ptr to a new UniformScaleMap
+ static MapBase::Ptr create() { return MapBase::Ptr(new UniformScaleMap()); }
+ /// Return a MapBase::Ptr to a deep copy of this map
+ MapBase::Ptr copy() const { return MapBase::Ptr(new UniformScaleMap(*this)); }
+
+ static bool isRegistered() { return MapRegistry::isRegistered(UniformScaleMap::mapType()); }
+ static void registerMap()
+ {
+ MapRegistry::registerMap(
+ UniformScaleMap::mapType(),
+ UniformScaleMap::create);
+ }
+
+ Name type() const { return mapType(); }
+ static Name mapType() { return Name("UniformScaleMap"); }
+
+ virtual bool isEqual(const MapBase& other) const { return isEqualBase(*this, other); }
+
+ bool operator==(const UniformScaleMap& other) const { return ScaleMap::operator==(other); }
+ bool operator!=(const UniformScaleMap& other) const { return !(*this == other); }
+
+ /// Return a MapBase::Ptr to a UniformScaleTraslateMap that is the result of
+ /// pre-translation on this map
+ MapBase::Ptr preTranslate(const Vec3d& tr) const;
+
+ /// Return a MapBase::Ptr to a UniformScaleTraslateMap that is the result of
+ /// post-translation on this map
+ MapBase::Ptr postTranslate(const Vec3d& tr) const;
+
+}; // class UniformScaleMap
+
+
+////////////////////////////////////////
+
+
+inline MapBase::Ptr
+ScaleMap::preScale(const Vec3d& v) const
+{
+ const Vec3d new_scale(v * mScaleValues);
+ if (isApproxEqual(new_scale[0],new_scale[1]) && isApproxEqual(new_scale[0],new_scale[2])) {
+ return MapBase::Ptr(new UniformScaleMap(new_scale[0]));
+ } else {
+ return MapBase::Ptr(new ScaleMap(new_scale));
+ }
+}
+
+
+inline MapBase::Ptr
+ScaleMap::postScale(const Vec3d& v) const
+{ // pre-post Scale are the same for a scale map
+ return preScale(v);
+}
+
+
+/// @brief A specialized linear transform that performs a translation
+class OPENVDB_API TranslationMap: public MapBase
+{
+public:
+ typedef boost::shared_ptr<TranslationMap> Ptr;
+ typedef boost::shared_ptr<const TranslationMap> ConstPtr;
+
+ // default constructor is a translation by zero.
+ TranslationMap(): MapBase(), mTranslation(Vec3d(0,0,0)) {}
+ TranslationMap(const Vec3d& t): MapBase(), mTranslation(t) {}
+ TranslationMap(const TranslationMap& other): MapBase(), mTranslation(other.mTranslation) {}
+
+ ~TranslationMap() {}
+
+ /// Return a MapBase::Ptr to a new TranslationMap
+ static MapBase::Ptr create() { return MapBase::Ptr(new TranslationMap()); }
+ /// Return a MapBase::Ptr to a deep copy of this map
+ MapBase::Ptr copy() const { return MapBase::Ptr(new TranslationMap(*this)); }
+
+ static bool isRegistered() { return MapRegistry::isRegistered(TranslationMap::mapType()); }
+
+ static void registerMap()
+ {
+ MapRegistry::registerMap(
+ TranslationMap::mapType(),
+ TranslationMap::create);
+ }
+
+ Name type() const { return mapType(); }
+ static Name mapType() { return Name("TranslationMap"); }
+
+ /// Return @c true (a TranslationMap is always linear).
+ bool isLinear() const { return true; }
+
+ /// Return @c false (by convention true)
+ bool hasUniformScale() const { return true; }
+
+ /// Return the image of @c in under the map
+ Vec3d applyMap(const Vec3d& in) const { return in + mTranslation; }
+ /// Return the pre-image of @c in under the map
+ Vec3d applyInverseMap(const Vec3d& in) const { return in - mTranslation; }
+ /// Return the Jacobian of the map applied to @a in.
+ Vec3d applyJacobian(const Vec3d& in, const Vec3d&) const { return applyJacobian(in); }
+ /// Return the Jacobian of the map applied to @a in.
+ Vec3d applyJacobian(const Vec3d& in) const {
+ return in;
+ }
+
+ /// @brief Return the transpose of the inverse Jacobian (Identity for TranslationMap)
+ /// of the map applied to @c in, ignores second argument
+ Vec3d applyIJT(const Vec3d& in, const Vec3d& ) const { return applyIJT(in);}
+ /// @brief Return the transpose of the inverse Jacobian (Identity for TranslationMap)
+ /// of the map applied to @c in
+ Vec3d applyIJT(const Vec3d& in) const {return in;}
+ /// Return the Jacobian Curvature: zero for a linear map
+ Mat3d applyIJC(const Mat3d& mat) const {return mat;}
+ Mat3d applyIJC(const Mat3d& mat, const Vec3d&, const Vec3d&) const {
+ return applyIJC(mat);
+ }
+
+ /// Return @c 1
+ double determinant(const Vec3d& ) const { return determinant(); }
+ /// Return @c 1
+ double determinant() const { return 1.0; }
+
+ /// Return \f$ (1,1,1) \f$
+ Vec3d voxelSize() const { return Vec3d(1,1,1);}
+ /// Return \f$ (1,1,1) \f$
+ Vec3d voxelSize(const Vec3d&) const { return voxelSize();}
+
+ /// Return the translation vector
+ const Vec3d& getTranslation() const { return mTranslation; }
+ /// read serialization
+ void read(std::istream& is) { mTranslation.read(is); }
+ /// write serialization
+ void write(std::ostream& os) const { mTranslation.write(os); }
+
+ /// string serialization, useful for debuging
+ std::string str() const
+ {
+ std::ostringstream buffer;
+ buffer << " - translation: " << mTranslation << std::endl;
+ return buffer.str();
+ }
+
+ virtual bool isEqual(const MapBase& other) const { return isEqualBase(*this, other); }
+
+ bool operator==(const TranslationMap& other) const
+ {
+ // ::eq() uses a tolerance
+ if (!mTranslation.eq(other.mTranslation)) { return false; }
+ return true;
+ }
+
+ bool operator!=(const TranslationMap& other) const { return !(*this == other); }
+
+ /// Return AffineMap::Ptr to an AffineMap equivalent to *this
+ AffineMap::Ptr getAffineMap() const
+ {
+ Mat4d matrix(Mat4d::identity());
+ matrix.setTranslation(mTranslation);
+
+ AffineMap::Ptr affineMap(new AffineMap(matrix));
+ return affineMap;
+ }
+
+ //@{
+ /// @brief Return a MapBase::Ptr to a new map that is the result
+ /// of prepending the appropriate operation.
+ MapBase::Ptr preRotate(double radians, Axis axis) const
+ {
+ AffineMap::Ptr affineMap = getAffineMap();
+ affineMap->accumPreRotation(axis, radians);
+ return simplify(affineMap);
+
+ }
+ MapBase::Ptr preTranslate(const Vec3d& t) const
+ {
+ return MapBase::Ptr(new TranslationMap(t + mTranslation));
+ }
+
+ MapBase::Ptr preScale(const Vec3d& v) const;
+
+ MapBase::Ptr preShear(double shear, Axis axis0, Axis axis1) const
+ {
+ AffineMap::Ptr affineMap = getAffineMap();
+ affineMap->accumPreShear(axis0, axis1, shear);
+ return simplify(affineMap);
+ }
+ //@}
+
+ //@{
+ /// @brief Return a MapBase::Ptr to a new map that is the result
+ /// of postfixing the appropriate operation.
+ MapBase::Ptr postRotate(double radians, Axis axis) const
+ {
+ AffineMap::Ptr affineMap = getAffineMap();
+ affineMap->accumPostRotation(axis, radians);
+ return simplify(affineMap);
+
+ }
+ MapBase::Ptr postTranslate(const Vec3d& t) const
+ { // post and pre are the same for this
+ return MapBase::Ptr(new TranslationMap(t + mTranslation));
+ }
+
+ MapBase::Ptr postScale(const Vec3d& v) const;
+
+ MapBase::Ptr postShear(double shear, Axis axis0, Axis axis1) const
+ {
+ AffineMap::Ptr affineMap = getAffineMap();
+ affineMap->accumPostShear(axis0, axis1, shear);
+ return simplify(affineMap);
+ }
+ //@}
+
+private:
+ Vec3d mTranslation;
+}; // class TranslationMap
+
+
+////////////////////////////////////////
+
+
+/// @brief A specialized Affine transform that scales along the principal axis
+/// the scaling need not be uniform in the three-directions, and then
+/// translates the result.
+class OPENVDB_API ScaleTranslateMap: public MapBase
+{
+public:
+ typedef boost::shared_ptr<ScaleTranslateMap> Ptr;
+ typedef boost::shared_ptr<const ScaleTranslateMap> ConstPtr;
+
+ ScaleTranslateMap():
+ MapBase(),
+ mTranslation(Vec3d(0,0,0)),
+ mScaleValues(Vec3d(1,1,1)),
+ mVoxelSize(Vec3d(1,1,1)),
+ mScaleValuesInverse(Vec3d(1,1,1)),
+ mInvScaleSqr(1,1,1),
+ mInvTwiceScale(0.5,0.5,0.5)
+ {
+ }
+
+ ScaleTranslateMap(const Vec3d& scale, const Vec3d& translate):
+ MapBase(),
+ mTranslation(translate),
+ mScaleValues(scale),
+ mVoxelSize(std::abs(scale(0)), std::abs(scale(1)), std::abs(scale(2)))
+ {
+ const double determinant = scale[0]* scale[1] * scale[2];
+ if (std::abs(determinant) < 3.0 * tolerance<double>::value()) {
+ OPENVDB_THROW(ArithmeticError, "Non-zero scale values required");
+ }
+ mScaleValuesInverse = 1.0 / mScaleValues;
+ mInvScaleSqr = mScaleValuesInverse * mScaleValuesInverse;
+ mInvTwiceScale = mScaleValuesInverse / 2;
+ }
+
+ ScaleTranslateMap(const ScaleMap& scale, const TranslationMap& translate):
+ MapBase(),
+ mTranslation(translate.getTranslation()),
+ mScaleValues(scale.getScale()),
+ mVoxelSize(std::abs(mScaleValues(0)),
+ std::abs(mScaleValues(1)),
+ std::abs(mScaleValues(2))),
+ mScaleValuesInverse(1.0 / scale.getScale())
+ {
+ mInvScaleSqr = mScaleValuesInverse * mScaleValuesInverse;
+ mInvTwiceScale = mScaleValuesInverse / 2;
+ }
+
+ ScaleTranslateMap(const ScaleTranslateMap& other):
+ MapBase(),
+ mTranslation(other.mTranslation),
+ mScaleValues(other.mScaleValues),
+ mVoxelSize(other.mVoxelSize),
+ mScaleValuesInverse(other.mScaleValuesInverse),
+ mInvScaleSqr(other.mInvScaleSqr),
+ mInvTwiceScale(other.mInvTwiceScale)
+ {}
+
+ ~ScaleTranslateMap() {}
+
+ /// Return a MapBase::Ptr to a new ScaleTranslateMap
+ static MapBase::Ptr create() { return MapBase::Ptr(new ScaleTranslateMap()); }
+ /// Return a MapBase::Ptr to a deep copy of this map
+ MapBase::Ptr copy() const { return MapBase::Ptr(new ScaleTranslateMap(*this)); }
+
+ static bool isRegistered() { return MapRegistry::isRegistered(ScaleTranslateMap::mapType()); }
+
+ static void registerMap()
+ {
+ MapRegistry::registerMap(
+ ScaleTranslateMap::mapType(),
+ ScaleTranslateMap::create);
+ }
+
+ Name type() const { return mapType(); }
+ static Name mapType() { return Name("ScaleTranslateMap"); }
+
+ /// Return @c true (a ScaleTranslateMap is always linear).
+ bool isLinear() const { return true; }
+
+ /// @brief Return @c true if the scale values have the same magnitude
+ /// (eg. -1, 1, -1 would be a rotation).
+ bool hasUniformScale() const {
+ bool value;
+ value = isApproxEqual(std::abs(mScaleValues.x()), std::abs(mScaleValues.y()), double(5e-7));
+ value = value && isApproxEqual(std::abs(mScaleValues.x()), std::abs(mScaleValues.z()), double(5e-7));
+
+ return value;
+ }
+
+ /// Return the image of @c under the map
+ Vec3d applyMap(const Vec3d& in) const
+ {
+ return Vec3d(
+ in.x() * mScaleValues.x() + mTranslation.x(),
+ in.y() * mScaleValues.y() + mTranslation.y(),
+ in.z() * mScaleValues.z() + mTranslation.z());
+ }
+ /// Return the pre-image of @c under the map
+ Vec3d applyInverseMap(const Vec3d& in) const
+ {
+ return Vec3d(
+ (in.x() - mTranslation.x() ) / mScaleValues.x(),
+ (in.y() - mTranslation.y() ) / mScaleValues.y(),
+ (in.z() - mTranslation.z() ) / mScaleValues.z());
+ }
+
+ /// Return the Jacobian of the map applied to @a in.
+ Vec3d applyJacobian(const Vec3d& in, const Vec3d&) const { return applyJacobian(in); }
+ /// Return the Jacobian of the map applied to @a in.
+ Vec3d applyJacobian(const Vec3d& in) const {
+ return in * mScaleValues;
+ }
+
+ /// @brief Return the transpose of the inverse Jacobian of the map applied to @a in
+ /// @details Ignores second argument
+ Vec3d applyIJT(const Vec3d& in, const Vec3d& ) const { return applyIJT(in);}
+ /// Return the transpose of the inverse Jacobian of the map applied to @c in
+ Vec3d applyIJT(const Vec3d& in) const
+ {
+ return Vec3d(
+ in.x() / mScaleValues.x(),
+ in.y() / mScaleValues.y(),
+ in.z() / mScaleValues.z());
+ }
+ /// Return the Jacobian Curvature: zero for a linear map
+ Mat3d applyIJC(const Mat3d& in) const {
+ Mat3d tmp;
+ for (int i=0; i<3; i++){
+ tmp.setRow(i, in.row(i)*mScaleValuesInverse(i));
+ }
+ for (int i=0; i<3; i++){
+ tmp.setCol(i, tmp.col(i)*mScaleValuesInverse(i));
+ }
+ return tmp;
+ }
+ Mat3d applyIJC(const Mat3d& in, const Vec3d&, const Vec3d& ) const {
+ return applyIJC(in);
+ }
+
+ /// Return the product of the scale values, ignores argument
+ double determinant(const Vec3d& ) const { return determinant(); }
+ /// Return the product of the scale values
+ double determinant() const { return mScaleValues.x()*mScaleValues.y()*mScaleValues.z(); }
+ /// Return the absolute values of the scale values
+ Vec3d voxelSize() const { return mVoxelSize;}
+ /// Return the absolute values of the scale values, ignores
+ ///argument
+ Vec3d voxelSize(const Vec3d&) const { return voxelSize();}
+
+ /// Returns the scale values
+ const Vec3d& getScale() const { return mScaleValues; }
+ /// Returns the translation
+ const Vec3d& getTranslation() const { return mTranslation; }
+
+ /// Return the square of the scale. Used to optimize some finite difference calculations
+ const Vec3d& getInvScaleSqr() const {return mInvScaleSqr;}
+ /// Return 1/(2 scale). Used to optimize some finite difference calculations
+ const Vec3d& getInvTwiceScale() const {return mInvTwiceScale;}
+ /// Return 1/(scale)
+ const Vec3d& getInvScale() const {return mScaleValuesInverse; }
+
+ /// read serialization
+ void read(std::istream& is)
+ {
+ mTranslation.read(is);
+ mScaleValues.read(is);
+ mVoxelSize.read(is);
+ mScaleValuesInverse.read(is);
+ mInvScaleSqr.read(is);
+ mInvTwiceScale.read(is);
+ }
+ /// write serialization
+ void write(std::ostream& os) const
+ {
+ mTranslation.write(os);
+ mScaleValues.write(os);
+ mVoxelSize.write(os);
+ mScaleValuesInverse.write(os);
+ mInvScaleSqr.write(os);
+ mInvTwiceScale.write(os);
+ }
+ /// string serialization, useful for debuging
+ std::string str() const
+ {
+ std::ostringstream buffer;
+ buffer << " - translation: " << mTranslation << std::endl;
+ buffer << " - scale: " << mScaleValues << std::endl;
+ buffer << " - voxel dimensions: " << mVoxelSize << std::endl;
+ return buffer.str();
+ }
+
+ virtual bool isEqual(const MapBase& other) const { return isEqualBase(*this, other); }
+
+ bool operator==(const ScaleTranslateMap& other) const
+ {
+ // ::eq() uses a tolerance
+ if (!mScaleValues.eq(other.mScaleValues)) { return false; }
+ if (!mTranslation.eq(other.mTranslation)) { return false; }
+ return true;
+ }
+
+ bool operator!=(const ScaleTranslateMap& other) const { return !(*this == other); }
+
+ /// Return AffineMap::Ptr to an AffineMap equivalent to *this
+ AffineMap::Ptr getAffineMap() const
+ {
+ AffineMap::Ptr affineMap(new AffineMap(math::scale<Mat4d>(mScaleValues)));
+ affineMap->accumPostTranslation(mTranslation);
+ return affineMap;
+ }
+
+ //@{
+ /// @brief Return a MapBase::Ptr to a new map that is the result
+ /// of prepending the appropraite operation.
+ MapBase::Ptr preRotate(double radians, Axis axis) const
+ {
+ AffineMap::Ptr affineMap = getAffineMap();
+ affineMap->accumPreRotation(axis, radians);
+ return simplify(affineMap);
+ }
+ MapBase::Ptr preTranslate(const Vec3d& t) const
+ {
+ const Vec3d& s = mScaleValues;
+ const Vec3d scaled_trans( t.x() * s.x(),
+ t.y() * s.y(),
+ t.z() * s.z() );
+ return MapBase::Ptr( new ScaleTranslateMap(mScaleValues, mTranslation + scaled_trans));
+ }
+
+ MapBase::Ptr preScale(const Vec3d& v) const;
+
+ MapBase::Ptr preShear(double shear, Axis axis0, Axis axis1) const
+ {
+ AffineMap::Ptr affineMap = getAffineMap();
+ affineMap->accumPreShear(axis0, axis1, shear);
+ return simplify(affineMap);
+ }
+ //@}
+
+ //@{
+ /// @brief Return a MapBase::Ptr to a new map that is the result
+ /// of postfixing the appropraite operation.
+ MapBase::Ptr postRotate(double radians, Axis axis) const
+ {
+ AffineMap::Ptr affineMap = getAffineMap();
+ affineMap->accumPostRotation(axis, radians);
+ return simplify(affineMap);
+ }
+ MapBase::Ptr postTranslate(const Vec3d& t) const
+ {
+ return MapBase::Ptr( new ScaleTranslateMap(mScaleValues, mTranslation + t));
+ }
+
+ MapBase::Ptr postScale(const Vec3d& v) const;
+
+ MapBase::Ptr postShear(double shear, Axis axis0, Axis axis1) const
+ {
+ AffineMap::Ptr affineMap = getAffineMap();
+ affineMap->accumPostShear(axis0, axis1, shear);
+ return simplify(affineMap);
+ }
+ //@}
+
+private:
+ Vec3d mTranslation, mScaleValues, mVoxelSize, mScaleValuesInverse,
+ mInvScaleSqr, mInvTwiceScale;
+}; // class ScaleTanslateMap
+
+
+inline MapBase::Ptr
+ScaleMap::postTranslate(const Vec3d& t) const
+{
+ return MapBase::Ptr(new ScaleTranslateMap(mScaleValues, t));
+}
+
+
+inline MapBase::Ptr
+ScaleMap::preTranslate(const Vec3d& t) const
+{
+
+ const Vec3d& s = mScaleValues;
+ const Vec3d scaled_trans( t.x() * s.x(),
+ t.y() * s.y(),
+ t.z() * s.z() );
+ return MapBase::Ptr(new ScaleTranslateMap(mScaleValues, scaled_trans));
+}
+
+
+/// @brief A specialized Affine transform that uniformaly scales along the principal axis
+/// and then translates the result.
+class OPENVDB_API UniformScaleTranslateMap: public ScaleTranslateMap
+{
+public:
+ typedef boost::shared_ptr<UniformScaleTranslateMap> Ptr;
+ typedef boost::shared_ptr<const UniformScaleTranslateMap> ConstPtr;
+
+ UniformScaleTranslateMap():ScaleTranslateMap(Vec3d(1,1,1), Vec3d(0,0,0)) {}
+ UniformScaleTranslateMap(double scale, const Vec3d& translate):
+ ScaleTranslateMap(Vec3d(scale,scale,scale), translate) {}
+ UniformScaleTranslateMap(const UniformScaleMap& scale, const TranslationMap& translate):
+ ScaleTranslateMap(scale.getScale(), translate.getTranslation()) {}
+
+ UniformScaleTranslateMap(const UniformScaleTranslateMap& other):ScaleTranslateMap(other) {}
+ ~UniformScaleTranslateMap() {}
+
+ /// Return a MapBase::Ptr to a new UniformScaleTranslateMap
+ static MapBase::Ptr create() { return MapBase::Ptr(new UniformScaleTranslateMap()); }
+ /// Return a MapBase::Ptr to a deep copy of this map
+ MapBase::Ptr copy() const { return MapBase::Ptr(new UniformScaleTranslateMap(*this)); }
+
+ static bool isRegistered()
+ {
+ return MapRegistry::isRegistered(UniformScaleTranslateMap::mapType());
+ }
+
+ static void registerMap()
+ {
+ MapRegistry::registerMap(
+ UniformScaleTranslateMap::mapType(),
+ UniformScaleTranslateMap::create);
+ }
+
+ Name type() const { return mapType(); }
+ static Name mapType() { return Name("UniformScaleTranslateMap"); }
+
+ virtual bool isEqual(const MapBase& other) const { return isEqualBase(*this, other); }
+
+ bool operator==(const UniformScaleTranslateMap& other) const
+ {
+ return ScaleTranslateMap::operator==(other);
+ }
+ bool operator!=(const UniformScaleTranslateMap& other) const { return !(*this == other); }
+
+ /// @brief Return a MapBase::Ptr to a UniformScaleTranslateMap that is
+ /// the result of prepending translation on this map.
+ MapBase::Ptr preTranslate(const Vec3d& t) const
+ {
+ const double scale = this->getScale().x();
+ const Vec3d new_trans = this->getTranslation() + scale * t;
+ return MapBase::Ptr( new UniformScaleTranslateMap(scale, new_trans));
+ }
+
+ /// @brief Return a MapBase::Ptr to a UniformScaleTranslateMap that is
+ /// the result of postfixing translation on this map.
+ MapBase::Ptr postTranslate(const Vec3d& t) const
+ {
+ const double scale = this->getScale().x();
+ return MapBase::Ptr( new UniformScaleTranslateMap(scale, this->getTranslation() + t));
+ }
+}; // class UniformScaleTanslateMap
+
+
+inline MapBase::Ptr
+UniformScaleMap::postTranslate(const Vec3d& t) const
+{
+ const double scale = this->getScale().x();
+ return MapBase::Ptr(new UniformScaleTranslateMap(scale, t));
+}
+
+
+inline MapBase::Ptr
+UniformScaleMap::preTranslate(const Vec3d& t) const
+{
+ const double scale = this->getScale().x();
+ return MapBase::Ptr(new UniformScaleTranslateMap(scale, scale*t));
+}
+
+inline MapBase::Ptr
+TranslationMap::preScale(const Vec3d& v) const
+{
+ if (isApproxEqual(v[0],v[1]) && isApproxEqual(v[0],v[2])) {
+ return MapBase::Ptr(new UniformScaleTranslateMap(v[0], mTranslation));
+ } else {
+ return MapBase::Ptr(new ScaleTranslateMap(v, mTranslation));
+ }
+}
+
+inline MapBase::Ptr
+TranslationMap::postScale(const Vec3d& v) const
+{
+ if (isApproxEqual(v[0],v[1]) && isApproxEqual(v[0],v[2])) {
+ return MapBase::Ptr(new UniformScaleTranslateMap(v[0], v[0]*mTranslation));
+ } else {
+ const Vec3d trans(mTranslation.x()*v.x(),
+ mTranslation.y()*v.y(),
+ mTranslation.z()*v.z());
+ return MapBase::Ptr(new ScaleTranslateMap(v, trans));
+ }
+}
+
+inline MapBase::Ptr
+ScaleTranslateMap::preScale(const Vec3d& v) const
+{
+ const Vec3d new_scale( v * mScaleValues );
+ if (isApproxEqual(new_scale[0],new_scale[1]) && isApproxEqual(new_scale[0],new_scale[2])) {
+ return MapBase::Ptr( new UniformScaleTranslateMap(new_scale[0], mTranslation));
+ } else {
+ return MapBase::Ptr( new ScaleTranslateMap(new_scale, mTranslation));
+ }
+}
+
+inline MapBase::Ptr
+ScaleTranslateMap::postScale(const Vec3d& v) const
+{
+ const Vec3d new_scale( v * mScaleValues );
+ const Vec3d new_trans( mTranslation.x()*v.x(),
+ mTranslation.y()*v.y(),
+ mTranslation.z()*v.z() );
+
+ if (isApproxEqual(new_scale[0],new_scale[1]) && isApproxEqual(new_scale[0],new_scale[2])) {
+ return MapBase::Ptr( new UniformScaleTranslateMap(new_scale[0], new_trans));
+ } else {
+ return MapBase::Ptr( new ScaleTranslateMap(new_scale, new_trans));
+ }
+}
+
+
+////////////////////////////////////////
+
+
+/// @brief A specialized linear transform that performs a unitary maping
+/// i.e. rotation and or reflection.
+class OPENVDB_API UnitaryMap: public MapBase
+{
+public:
+ typedef boost::shared_ptr<UnitaryMap> Ptr;
+ typedef boost::shared_ptr<const UnitaryMap> ConstPtr;
+
+ /// default constructor makes an Idenity.
+ UnitaryMap(): mAffineMap(Mat4d::identity())
+ {
+ }
+
+ UnitaryMap(const Vec3d& axis, double radians)
+ {
+ Mat3d matrix;
+ matrix.setToRotation(axis, radians);
+ mAffineMap = AffineMap(matrix);
+ }
+
+ UnitaryMap(Axis axis, double radians)
+ {
+ Mat4d matrix;
+ matrix.setToRotation(axis, radians);
+ mAffineMap = AffineMap(matrix);
+ }
+
+ UnitaryMap(const Mat3d& m)
+ {
+ // test that the mat3 is a rotation || reflection
+ if (!isUnitary(m)) {
+ OPENVDB_THROW(ArithmeticError, "Matrix initializing unitary map was not unitary");
+ }
+
+ Mat4d matrix(Mat4d::identity());
+ matrix.setMat3(m);
+ mAffineMap = AffineMap(matrix);
+ }
+
+ UnitaryMap(const Mat4d& m)
+ {
+ if (!isInvertible(m)) {
+ OPENVDB_THROW(ArithmeticError,
+ "4x4 Matrix initializing unitary map was not unitary: not invertible");
+ }
+
+ if (!isAffine(m)) {
+ OPENVDB_THROW(ArithmeticError,
+ "4x4 Matrix initializing unitary map was not unitary: not affine");
+ }
+
+ if (hasTranslation(m)) {
+ OPENVDB_THROW(ArithmeticError,
+ "4x4 Matrix initializing unitary map was not unitary: had translation");
+ }
+
+ if (!isUnitary(m.getMat3())) {
+ OPENVDB_THROW(ArithmeticError,
+ "4x4 Matrix initializing unitary map was not unitary");
+ }
+
+ mAffineMap = AffineMap(m);
+ }
+
+ UnitaryMap(const UnitaryMap& other):
+ MapBase(other),
+ mAffineMap(other.mAffineMap)
+ {
+ }
+
+ UnitaryMap(const UnitaryMap& first, const UnitaryMap& second):
+ mAffineMap(*(first.getAffineMap()), *(second.getAffineMap()))
+ {
+ }
+
+ ~UnitaryMap() {}
+ /// Return a MapBase::Ptr to a new UnitaryMap
+ static MapBase::Ptr create() { return MapBase::Ptr(new UnitaryMap()); }
+ /// Returns a MapBase::Ptr to a deep copy of *this
+ MapBase::Ptr copy() const { return MapBase::Ptr(new UnitaryMap(*this)); }
+
+ static bool isRegistered() { return MapRegistry::isRegistered(UnitaryMap::mapType()); }
+
+ static void registerMap()
+ {
+ MapRegistry::registerMap(
+ UnitaryMap::mapType(),
+ UnitaryMap::create);
+ }
+
+ /// Return @c UnitaryMap
+ Name type() const { return mapType(); }
+ /// Return @c UnitaryMap
+ static Name mapType() { return Name("UnitaryMap"); }
+
+ /// Return @c true (a UnitaryMap is always linear).
+ bool isLinear() const { return true; }
+
+ /// Return @c false (by convention true)
+ bool hasUniformScale() const { return true; }
+
+ virtual bool isEqual(const MapBase& other) const { return isEqualBase(*this, other); }
+
+ bool operator==(const UnitaryMap& other) const
+ {
+ // compare underlying linear map.
+ if (mAffineMap!=other.mAffineMap) return false;
+ return true;
+ }
+
+ bool operator!=(const UnitaryMap& other) const { return !(*this == other); }
+ /// Return the image of @c in under the map
+ Vec3d applyMap(const Vec3d& in) const { return mAffineMap.applyMap(in); }
+ /// Return the pre-image of @c in under the map
+ Vec3d applyInverseMap(const Vec3d& in) const { return mAffineMap.applyInverseMap(in); }
+
+ Vec3d applyJacobian(const Vec3d& in, const Vec3d&) const { return applyJacobian(in); }
+ /// Return the Jacobian of the map applied to @a in.
+ Vec3d applyJacobian(const Vec3d& in) const {
+ return applyMap(in);
+ }
+
+ /// @brief Return the transpose of the inverse Jacobian of the map applied to @a in
+ /// @details Ignores second argument
+ Vec3d applyIJT(const Vec3d& in, const Vec3d& ) const { return applyIJT(in);}
+ /// Return the transpose of the inverse Jacobian of the map applied to @c in
+ Vec3d applyIJT(const Vec3d& in) const { return mAffineMap.applyIJT(in); }
+ /// Return the Jacobian Curvature: zero for a linear map
+ Mat3d applyIJC(const Mat3d& in) const {
+ return mAffineMap.applyIJC(in);
+ }
+ Mat3d applyIJC(const Mat3d& in, const Vec3d&, const Vec3d& ) const {
+ return applyIJC(in);
+ }
+ /// Return the determinant of the Jacobian, ignores argument
+ double determinant(const Vec3d& ) const { return determinant(); }
+ /// Return the determinant of the Jacobian
+ double determinant() const { return mAffineMap.determinant(); }
+
+
+ /// @brief Returns the lengths of the images
+ /// of the segments
+ /// \f$(0,0,0)-(1,0,0)\f$, \f$(0,0,0)-(0,1,0)\f$,
+ /// \f$(0,0,0)-(0,0,1)\f$
+ Vec3d voxelSize() const { return mAffineMap.voxelSize();}
+ Vec3d voxelSize(const Vec3d&) const { return voxelSize();}
+
+ /// read serialization
+ void read(std::istream& is)
+ {
+ mAffineMap.read(is);
+ }
+
+ /// write serialization
+ void write(std::ostream& os) const
+ {
+ mAffineMap.write(os);
+ }
+ /// string serialization, useful for debuging
+ std::string str() const
+ {
+ std::ostringstream buffer;
+ buffer << mAffineMap.str();
+ return buffer.str();
+ }
+ /// Return AffineMap::Ptr to an AffineMap equivalent to *this
+ AffineMap::Ptr getAffineMap() const { return AffineMap::Ptr(new AffineMap(mAffineMap)); }
+
+ //@{
+ /// @brief Return a MapBase::Ptr to a new map that is the result
+ /// of prepending the appropraite operation.
+ MapBase::Ptr preRotate(double radians, Axis axis) const
+ {
+ UnitaryMap first(axis, radians);
+ UnitaryMap::Ptr unitaryMap(new UnitaryMap(first, *this));
+ return boost::static_pointer_cast<MapBase, UnitaryMap>(unitaryMap);
+ }
+ MapBase::Ptr preTranslate(const Vec3d& t) const
+ {
+ AffineMap::Ptr affineMap = getAffineMap();
+ affineMap->accumPreTranslation(t);
+ return simplify(affineMap);
+ }
+ MapBase::Ptr preScale(const Vec3d& v) const
+ {
+ AffineMap::Ptr affineMap = getAffineMap();
+ affineMap->accumPreScale(v);
+ return simplify(affineMap);
+ }
+ MapBase::Ptr preShear(double shear, Axis axis0, Axis axis1) const
+ {
+ AffineMap::Ptr affineMap = getAffineMap();
+ affineMap->accumPreShear(axis0, axis1, shear);
+ return simplify(affineMap);
+ }
+ //@}
+
+
+ //@{
+ /// @brief Return a MapBase::Ptr to a new map that is the result
+ /// of postfixing the appropraite operation.
+ MapBase::Ptr postRotate(double radians, Axis axis) const
+ {
+ UnitaryMap second(axis, radians);
+ UnitaryMap::Ptr unitaryMap(new UnitaryMap(*this, second));
+ return boost::static_pointer_cast<MapBase, UnitaryMap>(unitaryMap);
+ }
+ MapBase::Ptr postTranslate(const Vec3d& t) const
+ {
+ AffineMap::Ptr affineMap = getAffineMap();
+ affineMap->accumPostTranslation(t);
+ return simplify(affineMap);
+ }
+ MapBase::Ptr postScale(const Vec3d& v) const
+ {
+ AffineMap::Ptr affineMap = getAffineMap();
+ affineMap->accumPostScale(v);
+ return simplify(affineMap);
+ }
+ MapBase::Ptr postShear(double shear, Axis axis0, Axis axis1) const
+ {
+ AffineMap::Ptr affineMap = getAffineMap();
+ affineMap->accumPostShear(axis0, axis1, shear);
+ return simplify(affineMap);
+ }
+ //@}
+
+private:
+ AffineMap mAffineMap;
+}; // class UnitaryMap
+
+
+////////////////////////////////////////
+
+
+/// @brief This map is composed of three steps.
+/// Frist it will take a box of size (Lx X Ly X Lz) defined by an member data bounding box
+/// and map it into a frustum with near plane (1 X Ly/Lx) and precribed depth
+/// Then this frustum is transformed by an internal second map: most often a uniform scale,
+/// but other affects can be achieved by accumulating translation, shear and rotation: these
+/// are all applied to the second map
+class OPENVDB_API NonlinearFrustumMap: public MapBase
+{
+public:
+ typedef boost::shared_ptr<NonlinearFrustumMap> Ptr;
+ typedef boost::shared_ptr<const NonlinearFrustumMap> ConstPtr;
+
+ NonlinearFrustumMap():
+ MapBase(),
+ mBBox(Vec3d(0), Vec3d(1)),
+ mTaper(1),
+ mDepth(1)
+ {
+ init();
+ }
+
+ /// @brief Constructor that takes an index-space bounding box
+ /// to be mapped into a frustum with a given @a depth and @a taper
+ /// (defined as ratio of nearplane/farplane).
+ NonlinearFrustumMap(const BBoxd& bb, double taper, double depth):
+ MapBase(),mBBox(bb), mTaper(taper), mDepth(depth)
+ {
+ init();
+ }
+
+ /// @brief Constructor that takes an index-space bounding box
+ /// to be mapped into a frustum with a given @a depth and @a taper
+ /// (defined as ratio of nearplane/farplane).
+ /// @details This frustum is further modifed by the @a secondMap,
+ /// intended to be a simple translation and rotation and uniform scale
+ NonlinearFrustumMap(const BBoxd& bb, double taper, double depth,
+ const MapBase::Ptr& secondMap):
+ mBBox(bb), mTaper(taper), mDepth(depth)
+ {
+ if (!secondMap->isLinear() ) {
+ OPENVDB_THROW(ArithmeticError,
+ "The second map in the Frustum transfrom must be linear");
+ }
+ mSecondMap = *( secondMap->getAffineMap() );
+ init();
+ }
+
+ NonlinearFrustumMap(const NonlinearFrustumMap& other):
+ MapBase(),
+ mBBox(other.mBBox),
+ mTaper(other.mTaper),
+ mDepth(other.mDepth),
+ mSecondMap(other.mSecondMap),
+ mHasSimpleAffine(other.mHasSimpleAffine)
+ {
+ init();
+ }
+
+ /// @brief Constructor from a camera frustum
+ ///
+ /// @param position the tip of the frustum (i.e., the camera's position).
+ /// @param direction a vector pointing from @a position toward the near plane.
+ /// @param up a non-unit vector describing the direction and extent of
+ /// the frustum's intersection on the near plane. Together,
+ /// @a up must be orthogonal to @a direction.
+ /// @param aspect the aspect ratio of the frustum intersection with near plane
+ /// defined as width / height
+ /// @param z_near,depth the distance from @a position along @a direction to the
+ /// near and far planes of the frustum.
+ /// @param x_count the number of voxels, aligned with @a left,
+ /// across the face of the frustum
+ /// @param z_count the number of voxels, aligned with @a direction,
+ /// between the near and far planes
+ NonlinearFrustumMap(const Vec3d& position,
+ const Vec3d& direction,
+ const Vec3d& up,
+ double aspect /* width / height */,
+ double z_near, double depth,
+ Coord::ValueType x_count, Coord::ValueType z_count) {
+
+ /// @todo check that depth > 0
+ /// @todo check up.length > 0
+ /// @todo check that direction dot up = 0
+ if (!(depth > 0)) {
+ OPENVDB_THROW(ArithmeticError,
+ "The frustum depth must be non-zero and positive");
+ }
+ if (!(up.length() > 0)) {
+ OPENVDB_THROW(ArithmeticError,
+ "The frustum height must be non-zero and positive");
+ }
+ if (!(aspect > 0)) {
+ OPENVDB_THROW(ArithmeticError,
+ "The frustum aspect ratio must be non-zero and positive");
+ }
+ if (!(isApproxEqual(up.dot(direction), 0.))) {
+ OPENVDB_THROW(ArithmeticError,
+ "The frustum up orientation must be perpendicular to into-frustum direction");
+ }
+
+ double near_plane_height = 2 * up.length();
+ double near_plane_width = aspect * near_plane_height;
+
+ Coord::ValueType y_count = static_cast<int>(Round(x_count / aspect));
+
+ mBBox = BBoxd(Vec3d(0,0,0), Vec3d(x_count, y_count, z_count));
+ mDepth = depth / near_plane_width; // depth non-dimensionalized on width
+ double gamma = near_plane_width / z_near;
+ mTaper = 1./(mDepth*gamma + 1.);
+
+ Vec3d direction_unit = direction;
+ direction_unit.normalize();
+
+ Mat4d r1(Mat4d::identity());
+ r1.setToRotation(/*from*/Vec3d(0,0,1), /*to */direction_unit);
+ Mat4d r2(Mat4d::identity());
+ Vec3d temp = r1.inverse().transform(up);
+ r2.setToRotation(/*from*/Vec3d(0,1,0), /*to*/temp );
+ Mat4d scale = math::scale<Mat4d>(
+ Vec3d(near_plane_width, near_plane_width, near_plane_width));
+
+ // move the near plane to origin, rotate to align with axis, and scale down
+ // T_inv * R1_inv * R2_inv * scale_inv
+ Mat4d mat = scale * r2 * r1;
+ mat.setTranslation(position + z_near*direction_unit);
+
+ mSecondMap = AffineMap(mat);
+
+ init();
+ }
+
+ ~NonlinearFrustumMap(){}
+ /// Return a MapBase::Ptr to a new NonlinearFrustumMap
+ static MapBase::Ptr create() { return MapBase::Ptr(new NonlinearFrustumMap()); }
+ /// Return a MapBase::Ptr to a deep copy of this map
+ MapBase::Ptr copy() const { return MapBase::Ptr(new NonlinearFrustumMap(*this)); }
+
+ static bool isRegistered() { return MapRegistry::isRegistered(NonlinearFrustumMap::mapType()); }
+
+ static void registerMap()
+ {
+ MapRegistry::registerMap(
+ NonlinearFrustumMap::mapType(),
+ NonlinearFrustumMap::create);
+ }
+ /// Return @c NonlinearFrustumMap
+ Name type() const { return mapType(); }
+ /// Return @c NonlinearFrustumMap
+ static Name mapType() { return Name("NonlinearFrustumMap"); }
+
+ /// Return @c false (a NonlinearFrustumMap is never linear).
+ bool isLinear() const { return false; }
+
+ /// Return @c false (by convention false)
+ bool hasUniformScale() const { return false; }
+
+ /// Return @c true if the map is equivalent to an identity
+ bool isIdentity() const {
+
+ // The frustum can only be consistent with a linear map if the taper value is 1
+ if (!isApproxEqual(mTaper, double(1)) ) return false;
+
+ // There are various ways an identity can decomposed between the two parts of the
+ // map. Best to just check that the principle vectors are stationary.
+ const Vec3d e1(1,0,0);
+ if (!applyMap(e1).eq(e1)) return false;
+
+ const Vec3d e2(0,1,0);
+ if (!applyMap(e2).eq(e2)) return false;
+
+ const Vec3d e3(0,0,1);
+ if (!applyMap(e3).eq(e3)) return false;
+
+ return true;
+ }
+
+ virtual bool isEqual(const MapBase& other) const { return isEqualBase(*this, other); }
+
+ bool operator==(const NonlinearFrustumMap& other) const
+ {
+ if (mBBox!=other.mBBox) return false;
+ if (!isApproxEqual(mTaper, other.mTaper)) return false;
+ if (!isApproxEqual(mDepth, other.mDepth)) return false;
+
+ // Two linear transforms are equivalent iff they have the same translation
+ // and have the same affects on orthongal spanning basis check translation
+ Vec3d e(0,0,0);
+ if (!mSecondMap.applyMap(e).eq(other.mSecondMap.applyMap(e))) return false;
+ /// check spanning vectors
+ e(0) = 1;
+ if (!mSecondMap.applyMap(e).eq(other.mSecondMap.applyMap(e))) return false;
+ e(0) = 0;
+ e(1) = 1;
+ if (!mSecondMap.applyMap(e).eq(other.mSecondMap.applyMap(e))) return false;
+ e(1) = 0;
+ e(2) = 1;
+ if (!mSecondMap.applyMap(e).eq(other.mSecondMap.applyMap(e))) return false;
+ return true;
+ }
+
+ bool operator!=(const NonlinearFrustumMap& other) const { return !(*this == other); }
+
+ /// Return the image of @c in under the map
+ Vec3d applyMap(const Vec3d& in) const
+ {
+ return mSecondMap.applyMap(applyFrustumMap(in));
+ }
+
+ /// Return the pre-image of @c in under the map
+ Vec3d applyInverseMap(const Vec3d& in) const
+ {
+ return applyFrustumInverseMap(mSecondMap.applyInverseMap(in));
+ }
+ /// Return the Jacobian of the linear second map applied to @c in
+ Vec3d applyJacobian(const Vec3d& in) const { return mSecondMap.applyJacobian(in); }
+ /// Return the Jacobian of the linear second map applied to @c in
+ Vec3d applyJacobian(const Vec3d& in, const Vec3d& loc) const {
+
+ // Move the center of the x-face of the bbox
+ // to the origin in index space.
+ Vec3d centered(loc);
+ centered = centered - mBBox.min();
+ centered.x() -= mXo;
+ centered.y() -= mYo;
+
+ // scale the z-direction on depth / K count
+ const double zprime = centered.z()*mDepthOnLz;
+
+ const double scale = (mGamma * zprime + 1.) / mLx;
+ const double scale2 = mGamma * mDepthOnLz / mLx;
+
+
+ const Vec3d tmp(scale * in.x() + scale2 * centered.x()* in.z(),
+ scale * in.y() + scale2 * centered.y()* in.z(),
+ mDepthOnLz * in.z());
+
+ return mSecondMap.applyJacobian(tmp);
+ }
+
+ /// Return the transpose of the inverse Jacobian of the linear second map applied to @c in
+ Vec3d applyIJT(const Vec3d& in) const { return mSecondMap.applyIJT(in); }
+
+ // the Jacobian of the nonlinear part of the transform is a sparse matrix
+ // Jacobian^(-T) =
+ //
+ // (Lx)( 1/s 0 0 )
+ // ( 0 1/s 0 )
+ // ( -(x-xo)g/(sLx) -(y-yo)g/(sLx) Lz/(Depth Lx) )
+ /// Return the transpose of the inverse Jacobain (at @c locW applied to @c in.
+ /// @c ijk is the location in the pre-image space (e.g. index space)
+ Vec3d applyIJT(const Vec3d& d1_is, const Vec3d& ijk) const
+ {
+ const Vec3d loc = applyFrustumMap(ijk);
+ const double s = mGamma * loc.z() + 1.;
+
+ // verify that we aren't at the singularity
+ if (isApproxEqual(s, 0.)) {
+ OPENVDB_THROW(ArithmeticError, "Tried to evaluate the frustum transform"
+ " at the singular focal point (e.g. camera)");
+ }
+
+ const double sinv = 1.0/s; // 1/(z*gamma + 1)
+ const double pt0 = mLx * sinv; // Lx / (z*gamma +1)
+ const double pt1 = mGamma * pt0; // gamma * Lx / ( z*gamma +1)
+ const double pt2 = pt1 * sinv; // gamma * Lx / ( z*gamma +1)**2
+
+ const Mat3d& jacinv = mSecondMap.getConstJacobianInv();
+
+ // compute \frac{\partial E_i}{\partial x_j}
+ Mat3d gradE(Mat3d::zero());
+ for (int j = 0; j < 3; ++j ) {
+ gradE(0,j) = pt0 * jacinv(0,j) - pt2 * loc.x()*jacinv(2,j);
+ gradE(1,j) = pt0 * jacinv(1,j) - pt2 * loc.y()*jacinv(2,j);
+ gradE(2,j) = (1./mDepthOnLz) * jacinv(2,j);
+ }
+
+ Vec3d result;
+ for (int i = 0; i < 3; ++i) {
+ result(0) = d1_is(0) * gradE(0,i) + d1_is(1) * gradE(1,i) + d1_is(2) * gradE(2,i);
+ }
+
+ return result;
+
+ }
+
+ /// Return the Jacobian Curvature for the linear second map
+ Mat3d applyIJC(const Mat3d& in) const { return mSecondMap.applyIJC(in); }
+ /// Return the Jacobian Curvature: all the second derivatives in range space
+ /// @param d2_is second derivative matrix computed in index space
+ /// @param d1_is gradient computed in index space
+ /// @param ijk the index space location where the result is computed
+ Mat3d applyIJC(const Mat3d& d2_is, const Vec3d& d1_is, const Vec3d& ijk) const
+ {
+ const Vec3d loc = applyFrustumMap(ijk);
+
+ const double s = mGamma * loc.z() + 1.;
+
+ // verify that we aren't at the singularity
+ if (isApproxEqual(s, 0.)) {
+ OPENVDB_THROW(ArithmeticError, "Tried to evaluate the frustum transform"
+ " at the singular focal point (e.g. camera)");
+ }
+
+ // precompute
+ const double sinv = 1.0/s; // 1/(z*gamma + 1)
+ const double pt0 = mLx * sinv; // Lx / (z*gamma +1)
+ const double pt1 = mGamma * pt0; // gamma * Lx / ( z*gamma +1)
+ const double pt2 = pt1 * sinv; // gamma * Lx / ( z*gamma +1)**2
+ const double pt3 = pt2 * sinv; // gamma * Lx / ( z*gamma +1)**3
+
+
+ const Mat3d& jacinv = mSecondMap.getConstJacobianInv();
+
+ // compute \frac{\partial^2 E_i}{\partial x_j \partial x_k}
+
+ Mat3d matE0(Mat3d::zero());
+ Mat3d matE1(Mat3d::zero()); // matE2 = 0
+ for(int j = 0; j < 3; j++) {
+ for (int k = 0; k < 3; k++) {
+
+ const double pt4 = 2. * jacinv(2,j) * jacinv(2,k) * pt3;
+
+ matE0(j,k) = -(jacinv(0,j) * jacinv(2,k) + jacinv(2,j) * jacinv(0,k)) * pt2 +
+ pt4 * loc.x();
+
+ matE1(j,k) = -(jacinv(1,j) * jacinv(2,k) + jacinv(2,j) * jacinv(1,k)) * pt2 +
+ pt4 * loc.y();
+ }
+ }
+
+ // compute \frac{\partial E_i}{\partial x_j}
+ Mat3d gradE(Mat3d::zero());
+ for (int j = 0; j < 3; ++j ) {
+ gradE(0,j) = pt0 * jacinv(0,j) - pt2 * loc.x()*jacinv(2,j);
+ gradE(1,j) = pt0 * jacinv(1,j) - pt2 * loc.y()*jacinv(2,j);
+ gradE(2,j) = (1./mDepthOnLz) * jacinv(2,j);
+ }
+
+ Mat3d result(Mat3d::zero());
+ // compute \fac{\partial E_j}{\partial x_m} \fac{\partial E_i}{\partial x_n}
+ // \frac{\partial^2 input}{\partial E_i \partial E_j}
+ for (int m = 0; m < 3; ++m ) {
+ for ( int n = 0; n < 3; ++n) {
+ for (int i = 0; i < 3; ++i ) {
+ for (int j = 0; j < 3; ++j) {
+ result(m, n) += gradE(j, m) * gradE(i, n) * d2_is(i, j);
+ }
+ }
+ }
+ }
+
+ for (int m = 0; m < 3; ++m ) {
+ for ( int n = 0; n < 3; ++n) {
+ result(m, n) +=
+ matE0(m, n) * d1_is(0) + matE1(m, n) * d1_is(1);// + matE2(m, n) * d1_is(2);
+ }
+ }
+
+ return result;
+ }
+
+ /// Return the determinant of the Jacobian of linear second map
+ double determinant() const {return mSecondMap.determinant();} // no implementation
+
+ /// Return the determinate of the Jacobian evaluated at @c loc
+ /// @c loc is a location in the pre-image space (e.g., index space)
+ double determinant(const Vec3d& loc) const
+ {
+ double s = mGamma * loc.z() + 1.0;
+ double frustum_determinant = s * s * mDepthOnLzLxLx;
+ return mSecondMap.determinant() * frustum_determinant;
+ }
+
+ /// Return the size of a voxel at the center of the near plane
+ Vec3d voxelSize() const {
+ const Vec3d loc( 0.5*(mBBox.min().x() + mBBox.max().x()),
+ 0.5*(mBBox.min().y() + mBBox.max().y()),
+ mBBox.min().z());
+
+ return voxelSize(loc);
+
+ }
+
+ /// @brief Returns the lengths of the images of the three segments
+ /// from @a loc to @a loc + (1,0,0), from @a loc to @a loc + (0,1,0)
+ /// and from @a loc to @a loc + (0,0,1)
+ /// @param loc a location in the pre-image space (e.g., index space)
+ Vec3d voxelSize(const Vec3d& loc) const {
+ Vec3d out, pos = applyMap(loc);
+ out(0) = (applyMap(loc + Vec3d(1,0,0)) - pos).length();
+ out(1) = (applyMap(loc + Vec3d(0,1,0)) - pos).length();
+ out(2) = (applyMap(loc + Vec3d(0,0,1)) - pos).length();
+ return out;
+ }
+
+ AffineMap::Ptr getAffineMap() const
+ {
+ return mSecondMap.getAffineMap();
+ }
+
+ /// set the taper value, the ratio of nearplane width / far plane width
+ void setTaper(double t) { mTaper = t; init();}
+ /// Return the taper value.
+ double getTaper() const { return mTaper; }
+ /// set the frustum depth: distance between near and far plane = frustm depth * frustm x-width
+ void setDepth(double d) { mDepth = d; init();}
+ /// Return the unscaled frustm depth
+ double getDepth() const { return mDepth; }
+ // gamma a non-dimensional number: nearplane x-width / camera to near plane distance
+ double getGamma() const { return mGamma; }
+
+ /// Return the bounding box that defines the frustum in pre-image space
+ const BBoxd& getBBox() const { return mBBox; }
+
+ /// Return MapBase::Ptr& to the second map
+ const AffineMap& secondMap() const { return mSecondMap; }
+ /// Return @c true if the the bounding box in index space that defines the region that
+ /// is maped into the frustum is non-zero, otherwise @c false
+ bool isValid() const { return !mBBox.empty();}
+
+ /// Return @c true if the second map is a uniform scale, Rotation and translation
+ bool hasSimpleAffine() const { return mHasSimpleAffine; }
+
+ /// read serialization
+ void read(std::istream& is)
+ {
+ // for backward compatibility with earlier version
+ if (io::getFormatVersion(is) < OPENVDB_FILE_VERSION_FLOAT_FRUSTUM_BBOX ) {
+ CoordBBox bb;
+ bb.read(is);
+ mBBox = BBoxd(bb.min().asVec3d(), bb.max().asVec3d());
+ } else {
+ mBBox.read(is);
+ }
+
+ is.read(reinterpret_cast<char*>(&mTaper), sizeof(double));
+ is.read(reinterpret_cast<char*>(&mDepth), sizeof(double));
+
+ // Read the second maps type.
+ Name type = readString(is);
+
+ // Check if the map has been registered.
+ if(!MapRegistry::isRegistered(type)) {
+ OPENVDB_THROW(KeyError, "Map " << type << " is not registered");
+ }
+
+ // Create the second map of the type and then read it in.
+ MapBase::Ptr proxy = math::MapRegistry::createMap(type);
+ proxy->read(is);
+ mSecondMap = *(proxy->getAffineMap());
+ init();
+ }
+
+ /// write serialization
+ void write(std::ostream& os) const
+ {
+ mBBox.write(os);
+ os.write(reinterpret_cast<const char*>(&mTaper), sizeof(double));
+ os.write(reinterpret_cast<const char*>(&mDepth), sizeof(double));
+
+ writeString(os, mSecondMap.type());
+ mSecondMap.write(os);
+ }
+
+ /// string serialization, useful for debuging
+ std::string str() const
+ {
+ std::ostringstream buffer;
+ buffer << " - taper: " << mTaper << std::endl;
+ buffer << " - depth: " << mDepth << std::endl;
+ buffer << " SecondMap: "<< mSecondMap.type() << std::endl;
+ buffer << mSecondMap.str() << std::endl;
+ return buffer.str();
+ }
+
+ //@{
+ /// @brief Return a MapBase::Ptr to a new map that is the result
+ /// of prepending the appropriate operation to the linear part of this map
+ MapBase::Ptr preRotate(double radians, Axis axis = X_AXIS) const
+ {
+ return MapBase::Ptr(
+ new NonlinearFrustumMap(mBBox, mTaper, mDepth, mSecondMap.preRotate(radians, axis)));
+ }
+ MapBase::Ptr preTranslate(const Vec3d& t) const
+ {
+ return MapBase::Ptr(
+ new NonlinearFrustumMap(mBBox, mTaper, mDepth, mSecondMap.preTranslate(t)));
+ }
+ MapBase::Ptr preScale(const Vec3d& s) const
+ {
+ return MapBase::Ptr(
+ new NonlinearFrustumMap(mBBox, mTaper, mDepth, mSecondMap.preScale(s)));
+ }
+ MapBase::Ptr preShear(double shear, Axis axis0, Axis axis1) const
+ {
+ return MapBase::Ptr(new NonlinearFrustumMap(
+ mBBox, mTaper, mDepth, mSecondMap.preShear(shear, axis0, axis1)));
+ }
+ //@}
+
+ //@{
+ /// @brief Return a MapBase::Ptr to a new map that is the result
+ /// of postfixing the appropiate operation to the linear part of this map.
+ MapBase::Ptr postRotate(double radians, Axis axis = X_AXIS) const
+ {
+ return MapBase::Ptr(
+ new NonlinearFrustumMap(mBBox, mTaper, mDepth, mSecondMap.postRotate(radians, axis)));
+ }
+ MapBase::Ptr postTranslate(const Vec3d& t) const
+ {
+ return MapBase::Ptr(
+ new NonlinearFrustumMap(mBBox, mTaper, mDepth, mSecondMap.postTranslate(t)));
+ }
+ MapBase::Ptr postScale(const Vec3d& s) const
+ {
+ return MapBase::Ptr(
+ new NonlinearFrustumMap(mBBox, mTaper, mDepth, mSecondMap.postScale(s)));
+ }
+ MapBase::Ptr postShear(double shear, Axis axis0, Axis axis1) const
+ {
+ return MapBase::Ptr(new NonlinearFrustumMap(
+ mBBox, mTaper, mDepth, mSecondMap.postShear(shear, axis0, axis1)));
+ }
+ //@}
+
+
+private:
+ void init() {
+ // set up as a frustum
+ mLx = mBBox.extents().x();
+ mLy = mBBox.extents().y();
+ mLz = mBBox.extents().z();
+
+ if (isApproxEqual(mLx,0.) || isApproxEqual(mLy,0.) || isApproxEqual(mLz,0.) ) {
+ OPENVDB_THROW(ArithmeticError, "The index space bounding box"
+ " must have at least two index points in each direction.");
+ }
+
+ mXo = 0.5* mLx;
+ mYo = 0.5* mLy;
+
+ // mDepth is non-dimensionalized on near
+ mGamma = (1./mTaper - 1) / mDepth;
+
+ mDepthOnLz = mDepth/mLz;
+ mDepthOnLzLxLx = mDepthOnLz/(mLx * mLx);
+
+ /// test for shear and non-uniform scale
+ mHasSimpleAffine = true;
+ Vec3d tmp = mSecondMap.voxelSize();
+
+ /// false if there is non-uniform scale
+ if (!isApproxEqual(tmp(0), tmp(1))) { mHasSimpleAffine = false; return; }
+ if (!isApproxEqual(tmp(0), tmp(2))) { mHasSimpleAffine = false; return; }
+
+ Vec3d trans = mSecondMap.applyMap(Vec3d(0,0,0));
+ /// look for shear
+ Vec3d tmp1 = mSecondMap.applyMap(Vec3d(1,0,0)) - trans;
+ Vec3d tmp2 = mSecondMap.applyMap(Vec3d(0,1,0)) - trans;
+ Vec3d tmp3 = mSecondMap.applyMap(Vec3d(0,0,1)) - trans;
+
+ /// false if there is shear
+ if (!isApproxEqual(tmp1.dot(tmp2), 0., 1.e-7)) { mHasSimpleAffine = false; return; }
+ if (!isApproxEqual(tmp2.dot(tmp3), 0., 1.e-7)) { mHasSimpleAffine = false; return; }
+ if (!isApproxEqual(tmp3.dot(tmp1), 0., 1.e-7)) { mHasSimpleAffine = false; return; }
+ }
+
+ Vec3d applyFrustumMap(const Vec3d& in) const
+ {
+
+ // Move the center of the x-face of the bbox
+ // to the origin in index space.
+ Vec3d out(in);
+ out = out - mBBox.min();
+ out.x() -= mXo;
+ out.y() -= mYo;
+
+ // scale the z-direction on depth / K count
+ out.z() *= mDepthOnLz;
+
+ double scale = (mGamma * out.z() + 1.)/ mLx;
+
+ // scale the x-y on the length I count and apply tapper
+ out.x() *= scale ;
+ out.y() *= scale ;
+
+ return out;
+ }
+
+ Vec3d applyFrustumInverseMap(const Vec3d& in) const
+ {
+ // invert taper and resize: scale = 1/( (z+1)/2 (mt-1) + 1)
+ Vec3d out(in);
+ double invScale = mLx / (mGamma * out.z() + 1.);
+ out.x() *= invScale;
+ out.y() *= invScale;
+
+ out.x() += mXo;
+ out.y() += mYo;
+
+ out.z() /= mDepthOnLz;
+
+ // move back
+ out = out + mBBox.min();
+ return out;
+ }
+
+ // bounding box in index space used in Frustum transforms.
+ BBoxd mBBox;
+
+ // taper value used in constructing Frustums.
+ double mTaper;
+ double mDepth;
+
+ // defines the second map
+ AffineMap mSecondMap;
+
+ // these are derived from the above.
+ double mLx, mLy, mLz;
+ double mXo, mYo, mGamma, mDepthOnLz, mDepthOnLzLxLx;
+
+ // true: if the mSecondMap is linear and has no shear, and has no non-uniform scale
+ bool mHasSimpleAffine;
+}; // class NonlinearFrustumMap
+
+
+////////////////////////////////////////
+
+
+/// @brief Creates the composition of two maps, each of which could be a composition.
+/// In the case that each component of the composition classified as linear an
+/// acceleration AffineMap is stored.
+template<typename FirstMapType, typename SecondMapType>
+class CompoundMap
+{
+public:
+ typedef CompoundMap<FirstMapType, SecondMapType> MyType;
+
+ typedef boost::shared_ptr<MyType> Ptr;
+ typedef boost::shared_ptr<const MyType> ConstPtr;
+
+
+ CompoundMap() { updateAffineMatrix(); }
+
+ CompoundMap(const FirstMapType& f, const SecondMapType& s): mFirstMap(f), mSecondMap(s)
+ {
+ updateAffineMatrix();
+ }
+
+ CompoundMap(const MyType& other):
+ mFirstMap(other.mFirstMap),
+ mSecondMap(other.mSecondMap),
+ mAffineMap(other.mAffineMap)
+ {}
+
+ Name type() const { return mapType(); }
+ static Name mapType()
+ {
+ return (FirstMapType::mapType() + Name(":") + SecondMapType::mapType());
+ }
+
+ bool operator==(const MyType& other) const
+ {
+ if (mFirstMap != other.mFirstMap) return false;
+ if (mSecondMap != other.mSecondMap) return false;
+ if (mAffineMap != other.mAffineMap) return false;
+ return true;
+ }
+
+ bool operator!=(const MyType& other) const { return !(*this == other); }
+
+ MyType& operator=(const MyType& other)
+ {
+ mFirstMap = other.mFirstMap;
+ mSecondMap = other.mSecondMap;
+ mAffineMap = other.mAffineMap;
+ return *this;
+ }
+
+ bool isIdentity() const {
+ if (is_linear<MyType>::value) {
+ return mAffineMap.isIdentity();
+ } else {
+ return mFirstMap.isIdentity()&&mSecondMap.isIdentity();
+ }
+ }
+
+ bool isDiagonal() const {
+ if (is_linear<MyType>::value) {
+ return mAffineMap.isDiagonal();
+ } else {
+ return mFirstMap.isDiagonal()&&mSecondMap.isDiagonal();
+ }
+ }
+ AffineMap::Ptr getAffineMap() const
+ {
+ if (is_linear<MyType>::value) {
+ AffineMap::Ptr affine(new AffineMap(mAffineMap));
+ return affine;
+ } else {
+ OPENVDB_THROW(ArithmeticError,
+ "Constant affine matrix representation not possible for this nonlinear map");
+ }
+ }
+
+ // direct decompotion
+ const FirstMapType& firstMap() const { return mFirstMap; }
+ const SecondMapType& secondMap() const {return mSecondMap; }
+
+ void setFirstMap(const FirstMapType& first) { mFirstMap = first; updateAffineMatrix(); }
+ void setSecondMap(const SecondMapType& second) { mSecondMap = second; updateAffineMatrix(); }
+
+ void read(std::istream& is)
+ {
+ mAffineMap.read(is);
+ mFirstMap.read(is);
+ mSecondMap.read(is);
+ }
+ void write(std::ostream& os) const
+ {
+ mAffineMap.write(os);
+ mFirstMap.write(os);
+ mSecondMap.write(os);
+ }
+
+private:
+ void updateAffineMatrix()
+ {
+ if (is_linear<MyType>::value) {
+ // both maps need to be linear, these methods are only defined for linear maps
+ AffineMap::Ptr first = mFirstMap.getAffineMap();
+ AffineMap::Ptr second= mSecondMap.getAffineMap();
+ mAffineMap = AffineMap(*first, *second);
+ }
+ }
+
+ FirstMapType mFirstMap;
+ SecondMapType mSecondMap;
+ // used for acceleration
+ AffineMap mAffineMap;
+}; // class CompoundMap
+
+} // namespace math
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_MATH_MAPS_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/math/Mat.h b/extern/openvdb/internal/openvdb/math/Mat.h
new file mode 100644
index 00000000000..d1eb61b6fdd
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/math/Mat.h
@@ -0,0 +1,975 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+//
+/// @file Mat.h
+/// @author Joshua Schpok
+
+#ifndef OPENVDB_MATH_MAT_HAS_BEEN_INCLUDED
+#define OPENVDB_MATH_MAT_HAS_BEEN_INCLUDED
+
+#include <math.h>
+#include <cstdlib>
+#include <cstdio>
+#include <assert.h>
+#include <iostream>
+#include <sstream>
+#include <boost/format.hpp>
+#include <openvdb/Exceptions.h>
+#include "Math.h"
+
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace math {
+
+/// @class Mat "Mat.h"
+/// A base class for square matrices.
+template<unsigned SIZE, typename T>
+class Mat
+{
+public:
+ typedef T value_type;
+ typedef T ValueType;
+ enum SIZE_ { size = SIZE };
+
+ // Number of cols, rows, elements
+ static unsigned numRows() { return SIZE; }
+ static unsigned numColumns() { return SIZE; }
+ static unsigned numElements() { return SIZE*SIZE; }
+
+ /// Default ctor. Does nothing. Required because declaring a copy (or
+ /// other) constructor means the default constructor gets left out.
+ Mat() { }
+
+ /// Copy constructor. Used when the class signature matches exactly.
+ Mat(Mat const &src) {
+ for (unsigned i(0); i < numElements(); ++i) {
+ mm[i] = src.mm[i];
+ }
+ }
+
+ /// @return string representation of matrix
+ /// Since output is multiline, optional indentation argument prefixes
+ /// each newline with that much white space. It does not indent
+ /// the first line, since you might be calling this inline:
+ ///
+ /// cout << "matrix: " << mat.str(7)
+ ///
+ /// matrix: [[1 2]
+ /// [3 4]]
+ std::string
+ str(unsigned indentation = 0) const {
+
+ std::string ret;
+ std::string indent;
+
+ // We add +1 since we're indenting one for the first '['
+ indent.append(indentation+1, ' ');
+
+ ret.append("[");
+
+ // For each row,
+ for (unsigned i(0); i < SIZE; i++) {
+
+ ret.append("[");
+
+ // For each column
+ for (unsigned j(0); j < SIZE; j++) {
+
+ // Put a comma after everything except the last
+ if (j) ret.append(", ");
+ ret.append((boost::format("%1%") % mm[(i*SIZE)+j]).str());
+ }
+
+ ret.append("]");
+
+ // At the end of every row (except the last)...
+ if (i < SIZE-1 )
+ // ...suffix the row bracket with a comma, newline, and
+ // advance indentation
+ ret.append((boost::format(",\n%1%") % indent).str());
+ }
+
+ ret.append("]");
+
+ return ret;
+ }
+
+ /// Write a Mat to an output stream
+ friend std::ostream& operator<<(
+ std::ostream& ostr,
+ const Mat<SIZE, T>& m)
+ {
+ ostr << m.str();
+ return ostr;
+ }
+
+ void write(std::ostream& os) const {
+ os.write(reinterpret_cast<const char*>(&mm), sizeof(T)*SIZE*SIZE);
+ }
+
+ void read(std::istream& is) {
+ is.read(reinterpret_cast<char*>(&mm), sizeof(T)*SIZE*SIZE);
+ }
+
+
+protected:
+ T mm[SIZE*SIZE];
+};
+
+
+template<typename T> class Quat;
+template<typename T> class Vec3;
+
+/// Returns rotation matrix specified by the quaternion
+/// The quaternion is normalized and used to construct the matrix
+/// Note that the matrix is transposed to match post-multiplication
+/// symantics.
+template<class MatType>
+MatType rotation(const Quat<typename MatType::value_type> &q,
+ typename MatType::value_type eps = 1.0e-8)
+{
+ typedef typename MatType::value_type T;
+
+ T qdot(q.dot(q));
+ T s(0);
+
+ if (!isApproxEqual(qdot, T(0.0),eps)) {
+ s = 2.0 / qdot;
+ }
+
+ T x = s*q.x();
+ T y = s*q.y();
+ T z = s*q.z();
+ T wx = x*q.w();
+ T wy = y*q.w();
+ T wz = z*q.w();
+ T xx = x*q.x();
+ T xy = y*q.x();
+ T xz = z*q.x();
+ T yy = y*q.y();
+ T yz = z*q.y();
+ T zz = z*q.z();
+
+ MatType r;
+ r[0][0]=T(1) - (yy+zz); r[0][1]=xy + wz; r[0][2]=xz - wy;
+ r[1][0]=xy - wz; r[1][1]=T(1) - (xx+zz); r[1][2]=yz + wx;
+ r[2][0]=xz + wy; r[2][1]=yz - wx; r[2][2]=T(1) - (xx+yy);
+
+ if(MatType::numColumns() == 4) padMat4(r);
+ return r;
+}
+
+
+
+/// @brief Set the matrix to a rotation about the given axis.
+/// @param axis The axis (one of X, Y, Z) to rotate about.
+/// @param angle The rotation angle, in radians.
+template<class MatType>
+MatType rotation(Axis axis, typename MatType::value_type angle)
+{
+ typedef typename MatType::value_type T;
+ T c = static_cast<T>(cos(angle));
+ T s = static_cast<T>(sin(angle));
+
+ MatType result;
+ result.setIdentity();
+
+ switch (axis) {
+ case X_AXIS:
+ result[1][1] = c;
+ result[1][2] = s;
+ result[2][1] = -s;
+ result[2][2] = c;
+ return result;
+ case Y_AXIS:
+ result[0][0] = c;
+ result[0][2] = -s;
+ result[2][0] = s;
+ result[2][2] = c;
+ return result;
+ case Z_AXIS:
+ result[0][0] = c;
+ result[0][1] = s;
+ result[1][0] = -s;
+ result[1][1] = c;
+ return result;
+ default:
+ throw ValueError("Unrecognized rotation axis");
+ }
+}
+
+
+/// @return matrix to the rotation specified by axis and angle
+/// @note The axis must be unit vector
+template<class MatType>
+MatType rotation(
+ const Vec3<typename MatType::value_type> &_axis,
+ typename MatType::value_type angle)
+{
+ typedef typename MatType::value_type T;
+ T txy, txz, tyz, sx, sy, sz;
+
+ Vec3<T> axis(_axis.unit());
+
+ // compute trig properties of angle:
+ T c(cos(double(angle)));
+ T s(sin(double(angle)));
+ T t(1 - c);
+
+ MatType result;
+ // handle diagonal elements
+ result[0][0] = axis[0]*axis[0] * t + c;
+ result[1][1] = axis[1]*axis[1] * t + c;
+ result[2][2] = axis[2]*axis[2] * t + c;
+
+ txy = axis[0]*axis[1] * t;
+ sz = axis[2] * s;
+
+ txz = axis[0]*axis[2] * t;
+ sy = axis[1] * s;
+
+ tyz = axis[1]*axis[2] * t;
+ sx = axis[0] * s;
+
+ // right handed space
+ // Contribution from rotation about 'z'
+ result[0][1] = txy + sz;
+ result[1][0] = txy - sz;
+ // Contribution from rotation about 'y'
+ result[0][2] = txz - sy;
+ result[2][0] = txz + sy;
+ // Contribution from rotation about 'x'
+ result[1][2] = tyz + sx;
+ result[2][1] = tyz - sx;
+
+ if(MatType::numColumns() == 4) padMat4(result);
+ return MatType(result);
+}
+
+
+/// Return the euler angles composing this rotation matrix. Optional
+/// axes arguments describe in what order elementary rotations are
+/// applied. Note that in our convention, XYZ means Rz * Ry * Rx.
+/// Because we are using rows rather than columns to represent the
+/// local axes of a coordinate frame, the interpretation from a local
+/// reference point of view is to first rotate about the x axis, then
+/// about the newly rotated y axis, and finally by the new local z axis.
+/// From a fixed reference point of view, the interpretation is to
+/// rotate about the stationary world z, y, and x axes respectively.
+///
+/// Irrespective of the euler angle convention, in the case of distinct
+/// axes, eulerAngles() returns the x, y, and z angles in the corresponding
+/// x, y, z components of the returned Vec3. For the XZX convention, the
+/// left X value is returned in Vec3.x, and the right X value in Vec3.y.
+/// For the ZXZ convention the left Z value is returned in Vec3.z and
+/// the right Z value in Vec3.y
+///
+/// Examples of reconstructing r from its euler angle decomposition
+///
+/// v = eulerAngles(r, ZYX_ROTATION);
+/// rx.setToRotation(Vec3d(1,0,0), v[0]);
+/// ry.setToRotation(Vec3d(0,1,0), v[1]);
+/// rz.setToRotation(Vec3d(0,0,1), v[2]);
+/// r = rx * ry * rz;
+///
+/// v = eulerAngles(r, ZXZ_ROTATION);
+/// rz1.setToRotation(Vec3d(0,0,1), v[2]);
+/// rx.setToRotation (Vec3d(1,0,0), v[0]);
+/// rz2.setToRotation(Vec3d(0,0,1), v[1]);
+/// r = rz2 * rx * rz1;
+///
+/// v = eulerAngles(r, XZX_ROTATION);
+/// rx1.setToRotation (Vec3d(1,0,0), v[0]);
+/// rx2.setToRotation (Vec3d(1,0,0), v[1]);
+/// rz.setToRotation (Vec3d(0,0,1), v[2]);
+/// r = rx2 * rz * rx1;
+///
+template <class MatType>
+Vec3<typename MatType::value_type> eulerAngles(
+ const MatType& mat,
+ RotationOrder rotationOrder,
+ typename MatType::value_type eps=1.0e-8)
+{
+ typedef typename MatType::value_type ValueType;
+ typedef Vec3<ValueType> V;
+ ValueType phi, theta, psi;
+
+ switch(rotationOrder)
+ {
+ case XYZ_ROTATION:
+ if (isApproxEqual(mat[2][0], ValueType(1.0), eps)) {
+ theta = M_PI_2;
+ phi = 0.5 * atan2(mat[1][2], mat[1][1]);
+ psi = phi;
+ } else if (isApproxEqual(mat[2][0], ValueType(-1.0), eps)) {
+ theta = -M_PI_2;
+ phi = 0.5 * atan2(mat[1][2], mat[1][1]);
+ psi = -phi;
+ } else {
+ psi = atan2(-mat[1][0],mat[0][0]);
+ phi = atan2(-mat[2][1],mat[2][2]);
+ theta = atan2(mat[2][0],
+ sqrt( mat[2][1]*mat[2][1] +
+ mat[2][2]*mat[2][2]));
+ }
+ return V(phi, theta, psi);
+ case ZXY_ROTATION:
+ if (isApproxEqual(mat[1][2], ValueType(1.0), eps)) {
+ theta = M_PI_2;
+ phi = 0.5 * atan2(mat[0][1], mat[0][0]);
+ psi = phi;
+ } else if (isApproxEqual(mat[1][2], ValueType(-1.0), eps)) {
+ theta = -M_PI/2;
+ phi = 0.5 * atan2(mat[0][1],mat[2][1]);
+ psi = -phi;
+ } else {
+ psi = atan2(-mat[0][2], mat[2][2]);
+ phi = atan2(-mat[1][0], mat[1][1]);
+ theta = atan2(mat[1][2],
+ sqrt(mat[0][2] * mat[0][2] +
+ mat[2][2] * mat[2][2]));
+ }
+ return V(theta, psi, phi);
+
+ case YZX_ROTATION:
+ if (isApproxEqual(mat[0][1], ValueType(1.0), eps)) {
+ theta = M_PI_2;
+ phi = 0.5 * atan2(mat[2][0], mat[2][2]);
+ psi = phi;
+ } else if (isApproxEqual(mat[0][1], ValueType(-1.0), eps)) {
+ theta = -M_PI/2;
+ phi = 0.5 * atan2(mat[2][0], mat[1][0]);
+ psi = -phi;
+ } else {
+ psi = atan2(-mat[2][1], mat[1][1]);
+ phi = atan2(-mat[0][2], mat[0][0]);
+ theta = atan2(mat[0][1],
+ sqrt(mat[0][0] * mat[0][0] +
+ mat[0][2] * mat[0][2]));
+ }
+ return V(psi, phi, theta);
+
+ case XZX_ROTATION:
+
+ if (isApproxEqual(mat[0][0], ValueType(1.0), eps)) {
+ theta = 0.0;
+ phi = 0.5 * atan2(mat[1][2], mat[1][1]);
+ psi = phi;
+ } else if (isApproxEqual(mat[0][0], ValueType(-1.0), eps)) {
+ theta = M_PI;
+ psi = 0.5 * atan2(mat[2][1], -mat[1][1]);
+ phi = - psi;
+ } else {
+ psi = atan2(mat[2][0], -mat[1][0]);
+ phi = atan2(mat[0][2], mat[0][1]);
+ theta = atan2(sqrt(mat[0][1] * mat[0][1] +
+ mat[0][2] * mat[0][2]),
+ mat[0][0]);
+ }
+ return V(phi, psi, theta);
+
+ case ZXZ_ROTATION:
+
+ if (isApproxEqual(mat[2][2], ValueType(1.0), eps)) {
+ theta = 0.0;
+ phi = 0.5 * atan2(mat[0][1], mat[0][0]);
+ psi = phi;
+ } else if (isApproxEqual(mat[2][2], ValueType(-1.0), eps)) {
+ theta = M_PI;
+ phi = 0.5 * atan2(mat[0][1], mat[0][0]);
+ psi = -phi;
+ } else {
+ psi = atan2(mat[0][2], mat[1][2]);
+ phi = atan2(mat[2][0], -mat[2][1]);
+ theta = atan2(sqrt(mat[0][2] * mat[0][2] +
+ mat[1][2] * mat[1][2]),
+ mat[2][2]);
+ }
+ return V(theta, psi, phi);
+
+ case YXZ_ROTATION:
+
+ if (isApproxEqual(mat[2][1], ValueType(1.0), eps)) {
+ theta = - M_PI_2;
+ phi = 0.5 * atan2(-mat[1][0], mat[0][0]);
+ psi = phi;
+ } else if (isApproxEqual(mat[2][1], ValueType(-1.0), eps)) {
+ theta = M_PI_2;
+ phi = 0.5 * atan2(mat[1][0], mat[0][0]);
+ psi = -phi;
+ } else {
+ psi = atan2(mat[0][1], mat[1][1]);
+ phi = atan2(mat[2][0], mat[2][2]);
+ theta = atan2(-mat[2][1],
+ sqrt(mat[0][1] * mat[0][1] +
+ mat[1][1] * mat[1][1]));
+ }
+ return V(theta, phi, psi);
+
+ case ZYX_ROTATION:
+
+ if (isApproxEqual(mat[0][2], ValueType(1.0), eps)) {
+ theta = -M_PI_2;
+ phi = 0.5 * atan2(-mat[1][0], mat[1][1]);
+ psi = phi;
+ } else if (isApproxEqual(mat[0][2], ValueType(-1.0), eps)) {
+ theta = M_PI_2;
+ phi = 0.5 * atan2(mat[2][1], mat[2][0]);
+ psi = - phi;
+ } else {
+ psi = atan2(mat[1][2], mat[2][2]);
+ phi = atan2(mat[0][1], mat[0][0]);
+ theta = atan2(-mat[0][2],
+ sqrt(mat[0][1] * mat[0][1] +
+ mat[0][0] * mat[0][0]));
+ }
+ return V(psi, theta, phi);
+
+ case XZY_ROTATION:
+
+ if (isApproxEqual(mat[1][0], ValueType(-1.0), eps)) {
+ theta = M_PI_2;
+ psi = 0.5 * atan2(mat[2][1], mat[2][2]);
+ phi = - psi;
+ } else if (isApproxEqual(mat[1][0], ValueType(1.0), eps)) {
+ theta = - M_PI_2;
+ psi = 0.5 * atan2(- mat[2][1], mat[2][2]);
+ phi = psi;
+ } else {
+ psi = atan2(mat[2][0], mat[0][0]);
+ phi = atan2(mat[1][2], mat[1][1]);
+ theta = atan2(- mat[1][0],
+ sqrt(mat[1][1] * mat[1][1] +
+ mat[1][2] * mat[1][2]));
+ }
+ return V(phi, psi, theta);
+ }
+
+ OPENVDB_THROW(NotImplementedError, "Euler extraction sequence not implemented");
+}
+
+
+/// @brief Set the matrix to a rotation that maps v1 onto v2 about the cross
+/// product of v1 and v2.
+template<class MatType>
+MatType rotation(
+ const Vec3<typename MatType::value_type>& _v1,
+ const Vec3<typename MatType::value_type>& _v2,
+ typename MatType::value_type eps=1.0e-8)
+{
+ typedef typename MatType::value_type T;
+ Vec3<T> v1(_v1);
+ Vec3<T> v2(_v2);
+
+ // Check if v1 and v2 are unit length
+ if (!isApproxEqual(1.0, v1.dot(v1), eps)) {
+ v1.normalize();
+ }
+ if (!isApproxEqual(1.0, v2.dot(v2), eps)) {
+ v2.normalize();
+ }
+
+ Vec3<T> cross;
+ cross.cross(v1, v2);
+
+ if (isApproxEqual(cross[0], 0.0, eps) &&
+ isApproxEqual(cross[1], 0.0, eps) &&
+ isApproxEqual(cross[2], 0.0, eps)) {
+
+
+ // Given two unit vectors v1 and v2 that are nearly parallel, build a
+ // rotation matrix that maps v1 onto v2. First find which principal axis
+ // p is closest to perpendicular to v1. Find a reflection that exchanges
+ // v1 and p, and find a reflection that exchanges p2 and v2. The desired
+ // rotation matrix is the composition of these two reflections. See the
+ // paper "Efficiently Building a Matrix to Rotate One Vector to
+ // Another" by Tomas Moller and John Hughes in Journal of Graphics
+ // Tools Vol 4, No 4 for details.
+
+ Vec3<T> u, v, p(0.0, 0.0, 0.0);
+
+ double x = Abs(v1[0]);
+ double y = Abs(v1[1]);
+ double z = Abs(v1[2]);
+
+ if (x < y) {
+ if (z < x) {
+ p[2] = 1;
+ } else {
+ p[0] = 1;
+ }
+ } else {
+ if (z < y) {
+ p[2] = 1;
+ } else {
+ p[1] = 1;
+ }
+ }
+ u = p - v1;
+ v = p - v2;
+
+ double udot = u.dot(u);
+ double vdot = v.dot(v);
+
+ double a = -2 / udot;
+ double b = -2 / vdot;
+ double c = 4 * u.dot(v) / (udot * vdot);
+
+ MatType result;
+ result.setIdentity();
+
+ for (int j = 0; j < 3; j++) {
+ for (int i = 0; i < 3; i++)
+ result[i][j] =
+ a * u[i] * u[j] + b * v[i] * v[j] + c * v[j] * u[i];
+ }
+ result[0][0] += 1.0;
+ result[1][1] += 1.0;
+ result[2][2] += 1.0;
+
+ if(MatType::numColumns() == 4) padMat4(result);
+ return result;
+
+ } else {
+ double c = v1.dot(v2);
+ double a = (1.0 - c) / cross.dot(cross);
+
+ double a0 = a * cross[0];
+ double a1 = a * cross[1];
+ double a2 = a * cross[2];
+
+ double a01 = a0 * cross[1];
+ double a02 = a0 * cross[2];
+ double a12 = a1 * cross[2];
+
+ MatType r;
+
+ r[0][0] = c + a0 * cross[0];
+ r[0][1] = a01 + cross[2];
+ r[0][2] = a02 - cross[1],
+ r[1][0] = a01 - cross[2];
+ r[1][1] = c + a1 * cross[1];
+ r[1][2] = a12 + cross[0];
+ r[2][0] = a02 + cross[1];
+ r[2][1] = a12 - cross[0];
+ r[2][2] = c + a2 * cross[2];
+
+ if(MatType::numColumns() == 4) padMat4(r);
+ return r;
+
+ }
+}
+
+
+/// @return the matrix to a matrix that scales by v
+template<class MatType>
+MatType scale(const Vec3<typename MatType::value_type> &scaling)
+{
+ // Gets identity, then sets top 3 diagonal
+ // Inefficient by 3 sets.
+
+ MatType result;
+ result.setIdentity();
+ result[0][0] = scaling[0];
+ result[1][1] = scaling[1];
+ result[2][2] = scaling[2];
+
+ return result;
+}
+
+
+/// @return a Vec3 representing the lengths of the passed matrix's upper
+/// 3x3's rows.
+template<class MatType>
+Vec3<typename MatType::value_type>
+getScale(const MatType &mat)
+{
+ typedef Vec3<typename MatType::value_type> V;
+ return V(
+ V(mat[0][0], mat[0][1], mat[0][2]).length(),
+ V(mat[1][0], mat[1][1], mat[1][2]).length(),
+ V(mat[2][0], mat[2][1], mat[2][2]).length());
+}
+
+
+/// @return a copy of included matrix with its upper 3x3 rows normalized.
+/// This can be geometrically interpretted as a matrix with no scaling
+/// along its major axes.
+template<class MatType>
+MatType
+unit(const MatType &mat, typename MatType::value_type eps = 1.0e-8)
+{
+ Vec3<typename MatType::value_type> dud;
+ return unit(mat, eps, dud);
+}
+
+
+/// @return a copy of included matrix with its upper 3x3 rows normalized,
+/// and writes the length of each of these rows.
+/// This can be geometrically interpretted as a matrix with no scaling
+/// along its major axes, and the scaling in the input vector
+template<class MatType>
+MatType
+unit(
+ const MatType &in,
+ typename MatType::value_type eps,
+ Vec3<typename MatType::value_type>& scaling)
+{
+ typedef typename MatType::value_type T;
+ MatType result(in);
+
+ for (int i(0); i < 3; i++) {
+ try {
+ const Vec3<T> u(
+ Vec3<T>(in[i][0], in[i][1], in[i][2]).unit(eps, scaling[i]));
+ for (int j=0; j<3; j++) result[i][j] = u[j];
+ } catch (ArithmeticError&) {
+ for (int j=0; j<3; j++) result[i][j] = 0;
+ }
+ }
+ return result;
+}
+
+
+/// @brief Set the matrix to a shear along axis0 by a fraction of axis1.
+/// @param axis0 The fixed axis of the shear.
+/// @param axis1 The shear axis.
+/// @param shear The shear factor.
+template <class MatType>
+MatType
+shear(Axis axis0, Axis axis1, typename MatType::value_type shear)
+{
+ int index0 = static_cast<int>(axis0);
+ int index1 = static_cast<int>(axis1);
+
+ MatType result;
+ result.setIdentity();
+ if (axis0 == axis1) {
+ result[index1][index0] = shear + 1;
+ } else {
+ result[index1][index0] = shear;
+ }
+
+ return result;
+}
+
+
+/// @return a matrix as the cross product of the given vector
+template<class MatType>
+MatType
+skew(const Vec3<typename MatType::value_type> &skew)
+{
+ typedef typename MatType::value_type T;
+
+ MatType r;
+ r[0][0] = T(0); r[0][1] = skew.z(); r[0][2] = -skew.y();
+ r[1][0] = -skew.z(); r[1][1] = T(0); r[2][1] = skew.x();
+ r[2][0] = skew.y(); r[2][1] = -skew.x(); r[2][2] = T(0);
+
+ if(MatType::numColumns() == 4) padMat4(r);
+ return r;
+}
+
+
+/// Build an orientation matrix such that z points along direction,
+/// and y is along direction/vertical plane.
+template<class MatType>
+MatType
+aim(const Vec3<typename MatType::value_type>& direction,
+ const Vec3<typename MatType::value_type>& vertical)
+{
+ typedef typename MatType::value_type T;
+ Vec3<T> forward(direction.unit());
+ Vec3<T> horizontal(vertical.unit().cross(forward).unit());
+ Vec3<T> up(forward.cross(horizontal).unit());
+
+ MatType r;
+
+ r[0][0]=horizontal.x(); r[0][1]=horizontal.y(); r[0][2]=horizontal.z();
+ r[1][0]=up.x(); r[1][1]=up.y(); r[1][2]=up.z();
+ r[2][0]=forward.x(); r[2][1]=forward.y(); r[2][2]=forward.z();
+
+ if(MatType::numColumns() == 4) padMat4(r);
+ return r;
+}
+
+
+/// Write 0's along Mat4's last row and column, and a 1 on its diagonal
+/// Useful initialization when we're initializing juse the 3x3 block
+template<class MatType>
+static MatType&
+padMat4(MatType& dest)
+{
+ dest[0][3] = dest[1][3] = dest[2][3] = 0;
+ dest[3][2] = dest[3][1] = dest[3][0] = 0;
+ dest[3][3] = 1;
+
+ return dest;
+}
+
+
+/// Solve for A=B*B, given A
+///
+/// Denman-Beavers square root iteration
+template <typename MatType>
+inline void
+sqrtSolve(const MatType &aA, MatType &aB, double aTol=0.01)
+{
+ unsigned int iterations = (unsigned int)(log(aTol)/log(0.5));
+ MatType Y[2];
+ MatType Z[2];
+ MatType invY;
+ MatType invZ;
+
+ unsigned int current = 0;
+
+ Y[0]=aA;
+ Z[0] = MatType::identity();
+
+ unsigned int iteration;
+ for (iteration=0; iteration<iterations; iteration++)
+ {
+ unsigned int last = current;
+ current = !current;
+
+ invY = Y[last].inverse();
+ invZ = Z[last].inverse();
+
+ Y[current]=0.5*(Y[last]+invZ);
+ Z[current]=0.5*(Z[last]+invY);
+ }
+
+ MatType &R = Y[current];
+
+ aB=R;
+}
+
+
+template <typename MatType>
+inline void
+powSolve(const MatType &aA, MatType &aB, double aPower, double aTol=0.01)
+{
+ unsigned int iterations = (unsigned int)(log(aTol)/log(0.5));
+
+ const bool inverted = ( aPower < 0.0 );
+
+ if (inverted) {
+ aPower = -aPower;
+ }
+
+ unsigned int whole = (unsigned int)aPower;
+ double fraction = aPower - whole;
+
+ MatType R;
+ R = MatType::identity();
+
+ MatType partial = aA;
+
+ double contribution = 1.0;
+
+ unsigned int iteration;
+
+ for (iteration=0; iteration< iterations; iteration++)
+ {
+ sqrtSolve(partial, partial, aTol);
+ contribution *= 0.5;
+
+ if (fraction>=contribution)
+ {
+ R *= partial;
+ fraction-=contribution;
+ }
+ }
+
+ partial = aA;
+ while (whole)
+ {
+ if (whole & 1) {
+ R *= partial;
+ }
+ whole>>=1;
+ if(whole) {
+ partial*=partial;
+ }
+ }
+
+ if (inverted) {
+ aB = R.inverse();
+ }
+ else {
+ aB = R;
+ }
+}
+
+template <typename MatType>
+inline bool isIdentity(const MatType& m) {
+ typedef typename MatType::ValueType value_type;
+ return m.eq(MatType::identity());
+}
+
+
+template <typename MatType>
+inline bool isInvertible(const MatType& m) {
+ typedef typename MatType::ValueType value_type;
+ return !isApproxEqual(m.det(), (value_type)0);
+}
+/// Determine if a matrix is symmetric.
+/// This implicitly uses "isApproxEqual" to determine the equality
+template <typename MatType>
+inline bool isSymmetric(const MatType& m) {
+ return m.eq(m.transpose());
+}
+
+/// Determine is a matrix is Unitary (i.e. rotation or reflection)
+template <typename MatType>
+inline bool isUnitary(const MatType& m) {
+ typedef typename MatType::ValueType value_type;
+ if (!isApproxEqual(std::abs(m.det()), value_type(1.0))) return false;
+ // check that the matrix transpose is the inverse
+ MatType temp = m * m.transpose();
+ return temp.eq(MatType::identity(), toleranceValue<value_type>());
+}
+
+/// Determine if a matrix is diagonal
+template <typename MatType>
+inline bool isDiagonal(const MatType& mat) {
+ int n = MatType::size;
+ typename MatType::ValueType temp(0);
+ for (int i = 0; i < n; ++i) {
+ for (int j = 0; j < n; ++j) {
+ if (i != j) {
+ temp+=std::abs(mat(i,j));
+ }
+ }
+ }
+ return isApproxEqual(temp, typename MatType::ValueType(0.0));
+}
+
+/// takes a n by n matrix and returns the L_Infinty norm
+template<typename MatType>
+typename MatType::ValueType lInfinityNorm(const MatType& matrix)
+{
+ int n = MatType::size;
+ typename MatType::ValueType norm = 0;
+
+ for( int j = 0; j<n; ++j) {
+ typename MatType::ValueType column_sum = 0;
+
+ for (int i = 0; i<n; ++i) {
+ column_sum += fabs(matrix(i,j));
+ }
+ norm = std::max(norm, column_sum);
+ }
+
+ return norm;
+}
+
+/// takes an n by n matrix and returns the L_1 norm
+template<typename MatType>
+typename MatType::ValueType lOneNorm(const MatType& matrix)
+{
+ int n = MatType::size;
+ typename MatType::ValueType norm = 0;
+
+ for( int i = 0; i<n; ++i) {
+ typename MatType::ValueType row_sum = 0;
+
+ for (int j = 0; j<n; ++j) {
+ row_sum += fabs(matrix(i,j));
+ }
+ norm = std::max(norm, row_sum);
+ }
+
+ return norm;
+}
+
+
+///@brief Decompose an invertible 3x3 matrix into Unitary following
+/// a symmetric matrix (postitive semi-defininte Hermitian):
+/// i.e. M = U * S
+/// if the Unitary.det() = 1 it is a rotation, otherwise
+/// Unitary.det() = -1, meaning there is some part reflection.
+/// See "Computing the polar decomposition with applications"
+/// Higham, N.J. - SIAM J. Sc. Stat Comput 7(4):1160-1174
+template<typename MatType>
+bool polarDecomposition(const MatType& input, MatType& unitary, MatType& positive_hermitian, unsigned int MAX_ITERATIONS=100)
+{
+ /// tolerance
+ /// this should really be a trait of MatType::ValueType
+ unitary = input;
+ MatType new_unitary(input);
+ MatType unitary_inv;
+
+ if (fabs(unitary.det()) < tolerance<typename MatType::ValueType>::value()) return false;
+
+ unsigned int iteration(0);
+
+ typename MatType::ValueType linf_of_u;
+ typename MatType::ValueType l1nm_of_u;
+ typename MatType::ValueType linf_of_u_inv;
+ typename MatType::ValueType l1nm_of_u_inv;
+ typename MatType::ValueType l1_error = 100;
+ double gamma;
+
+ do {
+ unitary_inv = unitary.inverse();
+ linf_of_u = lInfinityNorm(unitary);
+ l1nm_of_u = lOneNorm(unitary);
+
+ linf_of_u_inv = lInfinityNorm(unitary_inv);
+ l1nm_of_u_inv = lOneNorm(unitary_inv);
+
+ gamma = sqrt( sqrt( (l1nm_of_u_inv * linf_of_u_inv ) / (l1nm_of_u * linf_of_u) ));
+
+ new_unitary = 0.5*(gamma * unitary + (1./gamma) * unitary_inv.transpose() );
+
+ l1_error = lInfinityNorm(unitary - new_unitary);
+ unitary = new_unitary;
+
+ /// this generally converges in less than ten iterations
+ if (iteration > MAX_ITERATIONS) return false;
+ iteration++;
+ } while (l1_error > tolerance<typename MatType::ValueType>::value());
+
+ positive_hermitian = unitary.transpose() * input;
+ return true;
+}
+
+} // namespace math
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_MATH_MAT_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/math/Mat3.h b/extern/openvdb/internal/openvdb/math/Mat3.h
new file mode 100644
index 00000000000..0575944ca87
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/math/Mat3.h
@@ -0,0 +1,821 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+
+#ifndef OPENVDB_MATH_MAT3_H_HAS_BEEN_INCLUDED
+#define OPENVDB_MATH_MAT3_H_HAS_BEEN_INCLUDED
+
+#include <iomanip>
+#include <assert.h>
+#include <math.h>
+#include <openvdb/Exceptions.h>
+#include "Vec3.h"
+#include "Mat.h"
+
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace math {
+
+template<typename T> class Vec3;
+template<typename T> class Mat4;
+template<typename T> class Quat;
+
+/// @class Mat3 Mat3.h
+/// @brief 3x3 matrix class.
+template<typename T>
+class Mat3: public Mat<3, T>
+{
+public:
+ /// Data type held by the matrix.
+ typedef T value_type;
+ typedef T ValueType;
+ typedef Mat<3, T> MyBase;
+ /// Trivial constructor, the matrix is NOT initialized
+ Mat3() {}
+
+ /// Constructor given the quaternion rotation, e.g. Mat3f m(q);
+ /// The quaternion is normalized and used to construct the matrix
+ Mat3(const Quat<T> &q)
+ { setToRotation(q); }
+
+
+ /// Constructor given array of elements, the ordering is in row major form:
+ /** @verbatim
+ a b c
+ d e f
+ g h i
+ @endverbatim */
+ template<typename Source>
+ Mat3(Source a, Source b, Source c,
+ Source d, Source e, Source f,
+ Source g, Source h, Source i)
+ {
+ MyBase::mm[0] = a;
+ MyBase::mm[1] = b;
+ MyBase::mm[2] = c;
+ MyBase::mm[3] = d;
+ MyBase::mm[4] = e;
+ MyBase::mm[5] = f;
+ MyBase::mm[6] = g;
+ MyBase::mm[7] = h;
+ MyBase::mm[8] = i;
+ } // constructor1Test
+
+ /// Construct matrix given basis vectors (columns)
+ template<typename Source>
+ Mat3(const Vec3<Source> &v1, const Vec3<Source> &v2, const Vec3<Source> &v3)
+ { setBasis(v1, v2, v3); }
+
+ /// Constructor given array of elements, the ordering is in row major form:\n
+ /// a[0] a[1] a[2]\n
+ /// a[3] a[4] a[5]\n
+ /// a[6] a[7] a[8]\n
+ template<typename Source>
+ Mat3(Source *a)
+ {
+ MyBase::mm[0] = a[0];
+ MyBase::mm[1] = a[1];
+ MyBase::mm[2] = a[2];
+ MyBase::mm[3] = a[3];
+ MyBase::mm[4] = a[4];
+ MyBase::mm[5] = a[5];
+ MyBase::mm[6] = a[6];
+ MyBase::mm[7] = a[7];
+ MyBase::mm[8] = a[8];
+ } // constructor1Test
+
+ /// Copy constructor
+ Mat3(const Mat<3, T> &m)
+ {
+ for (int i=0; i<3; ++i) {
+ for (int j=0; j<3; ++j) {
+ MyBase::mm[i*3 + j] = m[i][j];
+ }
+ }
+ }
+
+ /// Conversion constructor
+ template<typename Source>
+ explicit Mat3(const Mat3<Source> &m)
+ {
+ for (int i=0; i<3; ++i) {
+ for (int j=0; j<3; ++j) {
+ MyBase::mm[i*3 + j] = m[i][j];
+ }
+ }
+ }
+
+ /// Conversion from Mat4 (copies top left)
+ explicit Mat3(const Mat4<T> &m)
+ {
+ for (int i=0; i<3; ++i) {
+ for (int j=0; j<3; ++j) {
+ MyBase::mm[i*3 + j] = m[i][j];
+ }
+ }
+ }
+
+ /// Predefined constant for identity matrix
+ static const Mat3<T>& identity() {
+ return sIdentity;
+ }
+
+ /// Predefined constant for zero matrix
+ static const Mat3<T>& zero() {
+ return sZero;
+ }
+
+ /// Set ith row to vector v
+ void setRow(int i, const Vec3<T> &v)
+ {
+ // assert(i>=0 && i<3);
+ int i3 = i * 3;
+
+ MyBase::mm[i3+0] = v[0];
+ MyBase::mm[i3+1] = v[1];
+ MyBase::mm[i3+2] = v[2];
+ } // rowColumnTest
+
+ /// Get ith row, e.g. Vec3d v = m.row(1);
+ Vec3<T> row(int i) const
+ {
+ // assert(i>=0 && i<3);
+ return Vec3<T>((*this)(i,0), (*this)(i,1), (*this)(i,2));
+ } // rowColumnTest
+
+ /// Set jth column to vector v
+ void setCol(int j, const Vec3<T>& v)
+ {
+ // assert(j>=0 && j<3);
+ MyBase::mm[0+j] = v[0];
+ MyBase::mm[3+j] = v[1];
+ MyBase::mm[6+j] = v[2];
+ } // rowColumnTest
+
+ /// Get jth column, e.g. Vec3d v = m.col(0);
+ Vec3<T> col(int j) const
+ {
+ // assert(j>=0 && j<3);
+ return Vec3<T>((*this)(0,j), (*this)(1,j), (*this)(2,j));
+ } // rowColumnTest
+
+ // NB: The following two methods were changed to
+ // work around a gccWS5 compiler issue related to strict
+ // aliasing (see FX-475).
+
+ //@{
+ /// Array style reference to ith row
+ /// e.g. m[1][2] = 4;
+ T* operator[](int i) { return &(MyBase::mm[i*3]); }
+ const T* operator[](int i) const { return &(MyBase::mm[i*3]); }
+ //@}
+
+ T* asPointer() {return MyBase::mm;}
+ const T* asPointer() const {return MyBase::mm;}
+
+ /// Alternative indexed reference to the elements
+ /// Note that the indices are row first and column second.
+ /// e.g. m(0,0) = 1;
+ T& operator()(int i, int j)
+ {
+ // assert(i>=0 && i<3);
+ // assert(j>=0 && j<3);
+ return MyBase::mm[3*i+j];
+ } // trivial
+
+ /// Alternative indexed constant reference to the elements,
+ /// Note that the indices are row first and column second.
+ /// e.g. float f = m(1,0);
+ T operator()(int i, int j) const
+ {
+ // assert(i>=0 && i<3);
+ // assert(j>=0 && j<3);
+ return MyBase::mm[3*i+j];
+ } // trivial
+
+ /// Set the columns of "this" matrix to the vectors v1, v2, v3
+ void setBasis(const Vec3<T> &v1, const Vec3<T> &v2, const Vec3<T> &v3)
+ {
+ MyBase::mm[0] = v1[0];
+ MyBase::mm[1] = v1[1];
+ MyBase::mm[2] = v1[2];
+ MyBase::mm[3] = v2[0];
+ MyBase::mm[4] = v2[1];
+ MyBase::mm[5] = v2[2];
+ MyBase::mm[6] = v3[0];
+ MyBase::mm[7] = v3[1];
+ MyBase::mm[8] = v3[2];
+ } // setBasisTest
+
+ /// Set diagonal and symmetric triangular components
+ void setSymmetric(const Vec3<T> &vdiag, const Vec3<T> &vtri)
+ {
+ MyBase::mm[0] = vdiag[0];
+ MyBase::mm[1] = vtri[0];
+ MyBase::mm[2] = vtri[1];
+ MyBase::mm[3] = vtri[0];
+ MyBase::mm[4] = vdiag[1];
+ MyBase::mm[5] = vtri[2];
+ MyBase::mm[6] = vtri[1];
+ MyBase::mm[7] = vtri[2];
+ MyBase::mm[8] = vdiag[2];
+ } // setSymmetricTest
+
+ /// Returns matrix with prescribed diagonal and symmetric triangular
+ /// components
+ static Mat3 symmetric(const Vec3<T> &vdiag, const Vec3<T> &vtri)
+ {
+ return Mat3(
+ vdiag[0], vtri[0], vtri[1],
+ vtri[0], vdiag[1], vtri[2],
+ vtri[1], vtri[2], vdiag[2]
+ );
+ }
+
+ /// Set the matrix as cross product of the given vector
+ void setSkew(const Vec3<T> &v)
+ {*this = skew(v);}
+
+ /// @brief Set this matrix to the rotation matrix specified by the quaternion
+ /// @details The quaternion is normalized and used to construct the matrix.
+ /// Note that the matrix is transposed to match post-multiplication semantics.
+ void setToRotation(const Quat<T> &q)
+ {*this = rotation<Mat3<T> >(q);}
+
+ /// @brief Set this matrix to the rotation specified by @a axis and @a angle
+ /// @details The axis must be unit vector
+ void setToRotation(const Vec3<T> &axis, T angle)
+ {*this = rotation<Mat3<T> >(axis, angle);}
+
+ /// Set this matrix to zero
+ void setZero()
+ {
+ MyBase::mm[0] = 0;
+ MyBase::mm[1] = 0;
+ MyBase::mm[2] = 0;
+ MyBase::mm[3] = 0;
+ MyBase::mm[4] = 0;
+ MyBase::mm[5] = 0;
+ MyBase::mm[6] = 0;
+ MyBase::mm[7] = 0;
+ MyBase::mm[8] = 0;
+ } // trivial
+
+ /// Set "this" matrix to identity
+ void setIdentity()
+ {
+ MyBase::mm[0] = 1;
+ MyBase::mm[1] = 0;
+ MyBase::mm[2] = 0;
+ MyBase::mm[3] = 0;
+ MyBase::mm[4] = 1;
+ MyBase::mm[5] = 0;
+ MyBase::mm[6] = 0;
+ MyBase::mm[7] = 0;
+ MyBase::mm[8] = 1;
+ } // trivial
+
+ /// Assignment operator
+ template<typename Source>
+ const Mat3& operator=(const Mat3<Source> &m)
+ {
+ const Source *src = m.asPointer();
+
+ // don't suppress type conversion warnings
+ std::copy(src, (src + this->numElements()), MyBase::mm);
+ return *this;
+ } // opEqualToTest
+
+ /// Test if "this" is equivalent to m with tolerance of eps value
+ bool eq(const Mat3 &m, T eps=1.0e-8) const
+ {
+ return (isApproxEqual(MyBase::mm[0],m.mm[0],eps) &&
+ isApproxEqual(MyBase::mm[1],m.mm[1],eps) &&
+ isApproxEqual(MyBase::mm[2],m.mm[2],eps) &&
+ isApproxEqual(MyBase::mm[3],m.mm[3],eps) &&
+ isApproxEqual(MyBase::mm[4],m.mm[4],eps) &&
+ isApproxEqual(MyBase::mm[5],m.mm[5],eps) &&
+ isApproxEqual(MyBase::mm[6],m.mm[6],eps) &&
+ isApproxEqual(MyBase::mm[7],m.mm[7],eps) &&
+ isApproxEqual(MyBase::mm[8],m.mm[8],eps));
+ } // trivial
+
+ /// Negation operator, for e.g. m1 = -m2;
+ Mat3<T> operator-() const
+ {
+ return Mat3<T>(
+ -MyBase::mm[0], -MyBase::mm[1], -MyBase::mm[2],
+ -MyBase::mm[3], -MyBase::mm[4], -MyBase::mm[5],
+ -MyBase::mm[6], -MyBase::mm[7], -MyBase::mm[8]
+ );
+ } // trivial
+
+ /// Multiplication operator, e.g. M = scalar * M;
+ // friend Mat3 operator*(T scalar, const Mat3& m) {
+ // return m*scalar;
+ // }
+
+ /// @brief Returns m, where \f$m_{i,j} *= scalar\f$ for \f$i, j \in [0, 2]\f$
+ template <typename S>
+ const Mat3<T>& operator*=(S scalar)
+ {
+ MyBase::mm[0] *= scalar;
+ MyBase::mm[1] *= scalar;
+ MyBase::mm[2] *= scalar;
+ MyBase::mm[3] *= scalar;
+ MyBase::mm[4] *= scalar;
+ MyBase::mm[5] *= scalar;
+ MyBase::mm[6] *= scalar;
+ MyBase::mm[7] *= scalar;
+ MyBase::mm[8] *= scalar;
+ return *this;
+ }
+
+ /// @brief Returns m0, where \f$m0_{i,j} += m1_{i,j}\f$ for \f$i, j \in [0, 2]\f$
+ template <typename S>
+ const Mat3<T> &operator+=(const Mat3<S> &m1)
+ {
+ const S *s = m1.asPointer();
+
+ MyBase::mm[0] += s[0];
+ MyBase::mm[1] += s[1];
+ MyBase::mm[2] += s[2];
+ MyBase::mm[3] += s[3];
+ MyBase::mm[4] += s[4];
+ MyBase::mm[5] += s[5];
+ MyBase::mm[6] += s[6];
+ MyBase::mm[7] += s[7];
+ MyBase::mm[8] += s[8];
+ return *this;
+ }
+
+ /// @brief Returns m0, where \f$m0_{i,j} -= m1_{i,j}\f$ for \f$i, j \in [0, 2]\f$
+ template <typename S>
+ const Mat3<T> &operator-=(const Mat3<S> &m1)
+ {
+ const S *s = m1.asPointer();
+
+ MyBase::mm[0] -= s[0];
+ MyBase::mm[1] -= s[1];
+ MyBase::mm[2] -= s[2];
+ MyBase::mm[3] -= s[3];
+ MyBase::mm[4] -= s[4];
+ MyBase::mm[5] -= s[5];
+ MyBase::mm[6] -= s[6];
+ MyBase::mm[7] -= s[7];
+ MyBase::mm[8] -= s[8];
+ return *this;
+ }
+
+ /// @brief Returns m0, where \f$m0_{i,j} *= m1_{i,j}\f$ for \f$i, j \in [0, 2]\f$
+ template <typename S>
+ const Mat3<T> &operator*=(const Mat3<S> &m1)
+ {
+ Mat3<T> m0(*this);
+ const T* s0 = m0.asPointer();
+ const S* s1 = m1.asPointer();
+
+ MyBase::mm[0] = static_cast<T>(s0[0] * s1[0] +
+ s0[1] * s1[3] +
+ s0[2] * s1[6]);
+ MyBase::mm[1] = static_cast<T>(s0[0] * s1[1] +
+ s0[1] * s1[4] +
+ s0[2] * s1[7]);
+ MyBase::mm[2] = static_cast<T>(s0[0] * s1[2] +
+ s0[1] * s1[5] +
+ s0[2] * s1[8]);
+
+ MyBase::mm[3] = static_cast<T>(s0[3] * s1[0] +
+ s0[4] * s1[3] +
+ s0[5] * s1[6]);
+ MyBase::mm[4] = static_cast<T>(s0[3] * s1[1] +
+ s0[4] * s1[4] +
+ s0[5] * s1[7]);
+ MyBase::mm[5] = static_cast<T>(s0[3] * s1[2] +
+ s0[4] * s1[5] +
+ s0[5] * s1[8]);
+
+ MyBase::mm[6] = static_cast<T>(s0[6] * s1[0] +
+ s0[7] * s1[3] +
+ s0[8] * s1[6]);
+ MyBase::mm[7] = static_cast<T>(s0[6] * s1[1] +
+ s0[7] * s1[4] +
+ s0[8] * s1[7]);
+ MyBase::mm[8] = static_cast<T>(s0[6] * s1[2] +
+ s0[7] * s1[5] +
+ s0[8] * s1[8]);
+
+ return *this;
+ }
+
+ /// returns adjoint of m
+ Mat3 adjoint() const
+ {
+ return Mat3<T>(
+ MyBase::mm[4] * MyBase::mm[8] - MyBase::mm[5] * MyBase::mm[7],
+ MyBase::mm[2] * MyBase::mm[7] - MyBase::mm[1] * MyBase::mm[8],
+ MyBase::mm[1] * MyBase::mm[5] - MyBase::mm[2] * MyBase::mm[4],
+ MyBase::mm[5] * MyBase::mm[6] - MyBase::mm[3] * MyBase::mm[8],
+ MyBase::mm[0] * MyBase::mm[8] - MyBase::mm[2] * MyBase::mm[6],
+ MyBase::mm[2] * MyBase::mm[3] - MyBase::mm[0] * MyBase::mm[5],
+ MyBase::mm[3] * MyBase::mm[7] - MyBase::mm[4] * MyBase::mm[6],
+ MyBase::mm[1] * MyBase::mm[6] - MyBase::mm[0] * MyBase::mm[7],
+ MyBase::mm[0] * MyBase::mm[4] - MyBase::mm[1] * MyBase::mm[3]);
+ } // adjointTest
+
+ /// returns transpose of this
+ Mat3 transpose() const
+ {
+ return Mat3<T>(
+ MyBase::mm[0], MyBase::mm[3], MyBase::mm[6],
+ MyBase::mm[1], MyBase::mm[4], MyBase::mm[7],
+ MyBase::mm[2], MyBase::mm[5], MyBase::mm[8]);
+
+ } // transposeTest
+
+ /// returns inverse of this
+ /// throws FailedOperationException if singular
+ Mat3 inverse(T tolerance = 0) const
+ {
+ Mat3<T> inv(adjoint());
+
+ T det = inv.mm[0]*MyBase::mm[0] + inv.mm[1]*MyBase::mm[3] + inv.mm[2]*MyBase::mm[6];
+
+ // If the determinant is 0, m was singular and "this" will contain junk.
+ if (isApproxEqual(det,0.0,tolerance))
+ {
+ OPENVDB_THROW(ArithmeticError, "Inversion of singular 3x3 matrix");
+ }
+ return inv * (T(1)/det);
+ } // invertTest
+
+ /// Determinant of matrix
+ T det() const
+ {
+ T co00 = MyBase::mm[4]*MyBase::mm[8] - MyBase::mm[5]*MyBase::mm[7];
+ T co10 = MyBase::mm[5]*MyBase::mm[6] - MyBase::mm[3]*MyBase::mm[8];
+ T co20 = MyBase::mm[3]*MyBase::mm[7] - MyBase::mm[4]*MyBase::mm[6];
+ T d = MyBase::mm[0]*co00 + MyBase::mm[1]*co10 + MyBase::mm[2]*co20;
+ return d;
+ } // determinantTest
+
+ /// Trace of matrix
+ T trace() const
+ {
+ return MyBase::mm[0]+MyBase::mm[4]+MyBase::mm[8];
+ }
+
+ /// This function snaps a specific axis to a specific direction,
+ /// preserving scaling. It does this using minimum energy, thus
+ /// posing a unique solution if basis & direction arent parralel.
+ /// Direction need not be unit.
+ Mat3 snapBasis(Axis axis, const Vec3<T> &direction)
+ {
+ return snapBasis(*this, axis, direction);
+ }
+
+ /// Return the transformed vector by "this" matrix.
+ /// This function is equivalent to post-multiplying the matrix.
+ template<typename T0>
+ Vec3<T0> transform(const Vec3<T0> &v) const
+ {
+ return static_cast< Vec3<T0> >(v * *this);
+ } // xformVectorTest
+
+ /// Return the transformed vector by transpose of "this" matrix.
+ /// This function is equivalent to pre-multiplying the matrix.
+ template<typename T0>
+ Vec3<T0> pretransform(const Vec3<T0> &v) const
+ {
+ return static_cast< Vec3<T0> >(*this * v);
+ } // xformTVectorTest
+
+ /// This function snaps a specific axis to a specific direction,
+ /// preserving scaling. It does this using minimum energy, thus
+ /// posing a unique solution if basis & direction arent parralel.
+ /// Direction need not be unit.
+ template<typename T0>
+ Mat3 snappedBasis(Axis axis, const Vec3<T0>& direction) const
+ {
+ return snapBasis(*this, axis, direction);
+ }
+
+private:
+ static const Mat3<T> sIdentity;
+ static const Mat3<T> sZero;
+}; // class Mat3
+
+
+template <typename T>
+const Mat3<T> Mat3<T>::sIdentity = Mat3<T>(1, 0, 0,
+ 0, 1, 0,
+ 0, 0, 1);
+
+template <typename T>
+const Mat3<T> Mat3<T>::sZero = Mat3<T>(0, 0, 0,
+ 0, 0, 0,
+ 0, 0, 0);
+
+/// @relates Mat3
+/// @brief Equality operator, does exact floating point comparisons
+template <typename T0, typename T1>
+bool operator==(const Mat3<T0> &m0, const Mat3<T1> &m1)
+{
+ const T0 *t0 = m0.asPointer();
+ const T1 *t1 = m1.asPointer();
+
+ for (int i=0; i<9; ++i) {
+ if (!isExactlyEqual(t0[i], t1[i])) return false;
+ }
+ return true;
+}
+
+/// @relates Mat3
+/// @brief Inequality operator, does exact floating point comparisons
+template <typename T0, typename T1>
+bool operator!=(const Mat3<T0> &m0, const Mat3<T1> &m1) { return !(m0 == m1); }
+
+/// @relates Mat3
+/// @brief Returns M, where \f$M_{i,j} = m_{i,j} * scalar\f$ for \f$i, j \in [0, 2]\f$
+template <typename S, typename T>
+Mat3<typename promote<S, T>::type> operator*(S scalar, const Mat3<T> &m)
+{ return m*scalar; }
+
+/// @relates Mat3
+/// @brief Returns M, where \f$M_{i,j} = m_{i,j} * scalar\f$ for \f$i, j \in [0, 2]\f$
+template <typename S, typename T>
+Mat3<typename promote<S, T>::type> operator*(const Mat3<T> &m, S scalar)
+{
+ Mat3<typename promote<S, T>::type> result(m);
+ result *= scalar;
+ return result;
+}
+
+/// @relates Mat3
+/// @brief Returns M, where \f$M_{i,j} = m0_{i,j} + m1_{i,j}\f$ for \f$i, j \in [0, 2]\f$
+template <typename T0, typename T1>
+Mat3<typename promote<T0, T1>::type> operator+(const Mat3<T0> &m0, const Mat3<T1> &m1)
+{
+ Mat3<typename promote<T0, T1>::type> result(m0);
+ result += m1;
+ return result;
+}
+
+/// @relates Mat3
+/// @brief Returns M, where \f$M_{i,j} = m0_{i,j} - m1_{i,j}\f$ for \f$i, j \in [0, 2]\f$
+template <typename T0, typename T1>
+Mat3<typename promote<T0, T1>::type> operator-(const Mat3<T0> &m0, const Mat3<T1> &m1)
+{
+ Mat3<typename promote<T0, T1>::type> result(m0);
+ result -= m1;
+ return result;
+}
+
+
+/// @brief Matrix multiplication.
+///
+/// Returns M, where
+/// \f$M_{ij} = \sum_{n=0}^2\left(m0_{nj} + m1_{in}\right)\f$ for \f$i, j \in [0, 2]\f$
+template <typename T0, typename T1>
+Mat3<typename promote<T0, T1>::type>operator*(const Mat3<T0> &m0, const Mat3<T1> &m1)
+{
+ Mat3<typename promote<T0, T1>::type> result(m0);
+ result *= m1;
+ return result;
+}
+
+/// @relates Mat3
+/// @brief Returns v, where \f$v_{i} = \sum_{n=0}^2 m_{i,n} * v_n\f$ for \f$i \in [0, 2]\f$
+template<typename T, typename MT>
+inline Vec3<typename promote<T, MT>::type>
+operator*(const Mat3<MT> &_m, const Vec3<T> &_v)
+{
+ MT const *m = _m.asPointer();
+ return Vec3<typename promote<T, MT>::type>(
+ _v[0]*m[0] + _v[1]*m[1] + _v[2]*m[2],
+ _v[0]*m[3] + _v[1]*m[4] + _v[2]*m[5],
+ _v[0]*m[6] + _v[1]*m[7] + _v[2]*m[8]);
+}
+
+/// @relates Mat3
+/// @brief Returns v, where \f$v_{i} = \sum_{n=0}^2 m_{n,i} * v_n\f$ for \f$i \in [0, 2]\f$
+template<typename T, typename MT>
+inline Vec3<typename promote<T, MT>::type>
+operator*(const Vec3<T> &_v, const Mat3<MT> &_m)
+{
+ MT const *m = _m.asPointer();
+ return Vec3<typename promote<T, MT>::type>(
+ _v[0]*m[0] + _v[1]*m[3] + _v[2]*m[6],
+ _v[0]*m[1] + _v[1]*m[4] + _v[2]*m[7],
+ _v[0]*m[2] + _v[1]*m[5] + _v[2]*m[8]);
+}
+
+/// @relates Mat3
+/// @brief Returns v, where \f$v_{i} = \sum_{n=0}^2 m_{i,n} * v_n\f$ for \f$i \in [0, 2]\f$
+template<typename T, typename MT>
+inline Vec3<T> &operator *= (Vec3<T> &_v, const Mat3<MT> &_m)
+{
+ Vec3<T> mult = _v * _m;
+ _v = mult;
+ return _v;
+}
+
+/// this = outer product of v1, v2
+/// e.g. M = Mat3f::outerproduct(v1,v2);
+template <typename T>
+Mat3<T> outerProduct(const Vec3<T>& v1, const Vec3<T>& v2)
+{
+ Mat3<T> m;
+
+ m.setBasis(Vec3<T>(v1[0]*v2[0], v1[1]*v2[0], v1[2]*v2[0]),
+ Vec3<T>(v1[0]*v2[1], v1[1]*v2[1], v1[2]*v2[1]),
+ Vec3<T>(v1[0]*v2[2], v1[1]*v2[2], v1[2]*v2[2]));
+
+ return m;
+} // outerproductTest
+
+typedef Mat3<float> Mat3s;
+typedef Mat3<double> Mat3d;
+
+#if DWREAL_IS_DOUBLE == 1
+typedef Mat3d Mat3f;
+#else
+typedef Mat3s Mat3f;
+#endif // DWREAL_IS_DOUBLE
+
+
+/// Interpolate the rotation between m1 and m2 using Mat::powSolve.
+/// Unlike slerp, translation is not treated independently.
+/// This results in smoother animation results.
+template<typename T, typename T0>
+Mat3<T> powLerp(const Mat3<T0> &m1, const Mat3<T0> &m2, T t)
+{
+ Mat3<T> x = m1.inverse() * m2;
+ powSolve(x, x, t);
+ Mat3<T> m = m1 * x;
+ return m;
+}
+
+
+namespace {
+ template<typename T>
+ void pivot(int i, int j, Mat3<T>& S, Vec3<T>& D, Mat3<T>& Q)
+ {
+ const int& n = Mat3<T>::size; // should be 3
+ T temp;
+ /// scratch variables used in pivoting
+ double cotan_of_2_theta;
+ double tan_of_theta;
+ double cosin_of_theta;
+ double sin_of_theta;
+ double z;
+
+ double Sij = S(i,j);
+
+ double Sjj_minus_Sii = D[j] - D[i];
+
+ if (fabs(Sjj_minus_Sii) * (10*tolerance<T>::value()) > fabs(Sij)) {
+ tan_of_theta = Sij / Sjj_minus_Sii;
+ } else {
+ /// pivot on Sij
+ cotan_of_2_theta = 0.5*Sjj_minus_Sii / Sij ;
+
+ if (cotan_of_2_theta < 0.) {
+ tan_of_theta =
+ -1./(sqrt(1. + cotan_of_2_theta*cotan_of_2_theta) - cotan_of_2_theta);
+ } else {
+ tan_of_theta =
+ 1./(sqrt(1. + cotan_of_2_theta*cotan_of_2_theta) + cotan_of_2_theta);
+ }
+ }
+
+ cosin_of_theta = 1./sqrt( 1. + tan_of_theta * tan_of_theta);
+ sin_of_theta = cosin_of_theta * tan_of_theta;
+ z = tan_of_theta * Sij;
+ S(i,j) = 0;
+ D[i] -= z;
+ D[j] += z;
+ for (int k = 0; k < i; ++k) {
+ temp = S(k,i);
+ S(k,i) = cosin_of_theta * temp - sin_of_theta * S(k,j);
+ S(k,j)= sin_of_theta * temp + cosin_of_theta * S(k,j);
+ }
+ for (int k = i+1; k < j; ++k) {
+ temp = S(i,k);
+ S(i,k) = cosin_of_theta * temp - sin_of_theta * S(k,j);
+ S(k,j) = sin_of_theta * temp + cosin_of_theta * S(k,j);
+ }
+ for (int k = j+1; k < n; ++k) {
+ temp = S(i,k);
+ S(i,k) = cosin_of_theta * temp - sin_of_theta * S(j,k);
+ S(j,k) = sin_of_theta * temp + cosin_of_theta * S(j,k);
+ }
+ for (int k = 0; k < n; ++k)
+ {
+ temp = Q(k,i);
+ Q(k,i) = cosin_of_theta * temp - sin_of_theta*Q(k,j);
+ Q(k,j) = sin_of_theta * temp + cosin_of_theta*Q(k,j);
+ }
+ }
+}
+
+
+/// @brief Use Jacobi iterations to decompose a symmetric 3x3 matrix
+/// (diagonalize and compute eigenvectors)
+/// @details This is based on the "Efficient numerical diagonalization of Hermitian 3x3 matrices"
+/// Joachim Kopp. arXiv.org preprint: physics/0610206
+/// with the addition of largest pivot
+template<typename T>
+bool diagonalizeSymmetricMatrix(const Mat3<T>& input, Mat3<T>& Q, Vec3<T>& D,
+ unsigned int MAX_ITERATIONS=250)
+{
+ /// use Givens rotation matrix to eliminate off-diagonal entries.
+ /// initialize the rotation matrix as idenity
+ Q = Mat3<T>::identity();
+ int n = Mat3<T>::size; // should be 3
+
+ /// temp matrix. Assumed to be symmetric
+ Mat3<T> S(input);
+
+ for (int i = 0; i < n; ++i) {
+ D[i] = S(i,i);
+ }
+
+ unsigned int iterations(0);
+ /// Just iterate over all the non-diagonal enteries
+ /// using the largest as a pivot.
+ do {
+ /// check for absolute convergence
+ /// are symmetric off diagonals all zero
+ double er = 0;
+ for (int i = 0; i < n; ++i) {
+ for (int j = i+1; j < n; ++j) {
+ er += fabs(S(i,j));
+ }
+ }
+ if (std::abs(er) < tolerance<T>::value()) {
+ return true;
+ }
+ iterations++;
+
+ T max_element = 0;
+ int ip = 0;
+ int jp = 0;
+ /// loop over all the off-diagonals above the diagonal
+ for (int i = 0; i < n; ++i) {
+ for (int j = i+1; j < n; ++j){
+
+ if ( fabs(D[i]) * (10*tolerance<T>::value()) > fabs(S(i,j))) {
+ /// value too small to pivot on
+ S(i,j) = 0;
+ }
+ if (fabs(S(i,j)) > max_element) {
+ max_element = fabs(S(i,j));
+ ip = i;
+ jp = j;
+ }
+ }
+ }
+ pivot(ip, jp, S, D, Q);
+ } while (iterations < MAX_ITERATIONS);
+
+ return false;
+}
+
+} // namespace math
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_MATH_MAT3_H_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/math/Mat4.h b/extern/openvdb/internal/openvdb/math/Mat4.h
new file mode 100644
index 00000000000..460e34bbafb
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/math/Mat4.h
@@ -0,0 +1,1369 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+
+#ifndef OPENVDB_MATH_MAT4_H_HAS_BEEN_INCLUDED
+#define OPENVDB_MATH_MAT4_H_HAS_BEEN_INCLUDED
+
+#include <openvdb/Exceptions.h>
+#include <openvdb/Platform.h>
+#include <iomanip>
+#include <assert.h>
+#include <math.h>
+#include <algorithm>
+#include "Math.h"
+#include "Mat3.h"
+#include "Vec3.h"
+#include "Vec4.h"
+
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace math {
+
+template<typename T> class Vec4;
+
+
+/// @class Mat4 Mat4.h
+/// @brief 4x4 -matrix class.
+template<typename T>
+class Mat4: public Mat<4, T>
+{
+public:
+ /// Data type held by the matrix.
+ typedef T value_type;
+ typedef T ValueType;
+ typedef Mat<4, T> MyBase;
+
+ /// Trivial constructor, the matrix is NOT initialized
+ Mat4() {}
+
+ /// Constructor given array of elements, the ordering is in row major form:
+ /** @verbatim
+ a[ 0] a[1] a[ 2] a[ 3]
+ a[ 4] a[5] a[ 6] a[ 7]
+ a[ 8] a[9] a[10] a[11]
+ a[12] a[13] a[14] a[15]
+ @endverbatim */
+ template<typename Source>
+ Mat4(Source *a)
+ {
+ register int i;
+
+ for (i = 0; i < 16; i++) {
+ MyBase::mm[i] = a[i];
+ }
+ }
+
+ /// Constructor given array of elements, the ordering is in row major form:
+ /** @verbatim
+ a b c d
+ e f g h
+ i j k l
+ m n o p
+ @endverbatim */
+ template<typename Source>
+ Mat4(Source a, Source b, Source c, Source d,
+ Source e, Source f, Source g, Source h,
+ Source i, Source j, Source k, Source l,
+ Source m, Source n, Source o, Source p)
+ {
+ MyBase::mm[ 0] = a;
+ MyBase::mm[ 1] = b;
+ MyBase::mm[ 2] = c;
+ MyBase::mm[ 3] = d;
+
+ MyBase::mm[ 4] = e;
+ MyBase::mm[ 5] = f;
+ MyBase::mm[ 6] = g;
+ MyBase::mm[ 7] = h;
+
+ MyBase::mm[ 8] = i;
+ MyBase::mm[ 9] = j;
+ MyBase::mm[10] = k;
+ MyBase::mm[11] = l;
+
+ MyBase::mm[12] = m;
+ MyBase::mm[13] = n;
+ MyBase::mm[14] = o;
+ MyBase::mm[15] = p;
+ }
+
+ /// Construct matrix given basis vectors (columns)
+ template<typename Source>
+ Mat4(const Vec4<Source> &v1, const Vec4<Source> &v2,
+ const Vec4<Source> &v3, const Vec4<Source> &v4)
+ {
+ setBasis(v1, v2, v3, v4);
+ }
+
+ /// Copy constructor
+ Mat4(const Mat<4, T> &m)
+ {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ MyBase::mm[i*4 + j] = m[i][j];
+ }
+ }
+ }
+
+ /// Conversion constructor
+ template<typename Source>
+ explicit Mat4(const Mat4<Source> &m)
+ {
+ const Source *src = m.asPointer();
+
+ for (int i=0; i<16; ++i) {
+ MyBase::mm[i] = static_cast<T>(src[i]);
+ }
+ }
+
+ /// Predefined constant for identity matrix
+ static const Mat4<T>& identity() {
+ return sIdentity;
+ }
+
+ /// Predefined constant for zero matrix
+ static const Mat4<T>& zero() {
+ return sZero;
+ }
+
+ /// Set ith row to vector v
+ void setRow(int i, const Vec4<T> &v)
+ {
+ // assert(i>=0 && i<4);
+ int i4 = i * 4;
+ MyBase::mm[i4+0] = v[0];
+ MyBase::mm[i4+1] = v[1];
+ MyBase::mm[i4+2] = v[2];
+ MyBase::mm[i4+3] = v[3];
+ }
+
+ /// Get ith row, e.g. Vec4f v = m.row(1);
+ Vec4<T> row(int i) const
+ {
+ // assert(i>=0 && i<3);
+ return Vec4<T>((*this)(i,0), (*this)(i,1), (*this)(i,2), (*this)(i,3));
+ }
+
+ /// Set jth column to vector v
+ void setCol(int j, const Vec4<T>& v)
+ {
+ // assert(j>=0 && j<4);
+ MyBase::mm[ 0+j] = v[0];
+ MyBase::mm[ 4+j] = v[1];
+ MyBase::mm[ 8+j] = v[2];
+ MyBase::mm[12+j] = v[3];
+ }
+
+ /// Get jth column, e.g. Vec4f v = m.col(0);
+ Vec4<T> col(int j) const
+ {
+ // assert(j>=0 && j<4);
+ return Vec4<T>((*this)(0,j), (*this)(1,j), (*this)(2,j), (*this)(3,j));
+ }
+
+ //@{
+ /// Array style reference to ith row
+ /// e.g. m[1][3] = 4;
+ T* operator[](int i) { return &(MyBase::mm[i<<2]); }
+ const T* operator[](int i) const { return &(MyBase::mm[i<<2]); }
+ //@}
+
+ /// Direct access to the internal data
+ T* asPointer() {return MyBase::mm;}
+ const T* asPointer() const {return MyBase::mm;}
+
+ /// Alternative indexed reference to the elements
+ /// Note that the indices are row first and column second.
+ /// e.g. m(0,0) = 1;
+ T& operator()(int i, int j)
+ {
+ // assert(i>=0 && i<4);
+ // assert(j>=0 && j<4);
+ return MyBase::mm[4*i+j];
+ }
+
+ /// Alternative indexed constant reference to the elements,
+ /// Note that the indices are row first and column second.
+ /// e.g. float f = m(1,0);
+ T operator()(int i, int j) const
+ {
+ // assert(i>=0 && i<4);
+ // assert(j>=0 && j<4);
+ return MyBase::mm[4*i+j];
+ }
+
+ /// Set the columns of "this" matrix to the vectors v1, v2, v3, v4
+ void setBasis(const Vec4<T> &v1, const Vec4<T> &v2,
+ const Vec4<T> &v3, const Vec4<T> &v4)
+ {
+ MyBase::mm[ 0] = v1[0];
+ MyBase::mm[ 1] = v1[1];
+ MyBase::mm[ 2] = v1[2];
+ MyBase::mm[ 3] = v1[3];
+
+ MyBase::mm[ 4] = v2[0];
+ MyBase::mm[ 5] = v2[1];
+ MyBase::mm[ 6] = v2[2];
+ MyBase::mm[ 7] = v2[3];
+
+ MyBase::mm[ 8] = v3[0];
+ MyBase::mm[ 9] = v3[1];
+ MyBase::mm[10] = v3[2];
+ MyBase::mm[11] = v3[3];
+
+ MyBase::mm[12] = v4[0];
+ MyBase::mm[13] = v4[1];
+ MyBase::mm[14] = v4[2];
+ MyBase::mm[15] = v4[3];
+ }
+
+
+ // Set "this" matrix to zero
+ void setZero()
+ {
+ MyBase::mm[ 0] = 0;
+ MyBase::mm[ 1] = 0;
+ MyBase::mm[ 2] = 0;
+ MyBase::mm[ 3] = 0;
+ MyBase::mm[ 4] = 0;
+ MyBase::mm[ 5] = 0;
+ MyBase::mm[ 6] = 0;
+ MyBase::mm[ 7] = 0;
+ MyBase::mm[ 8] = 0;
+ MyBase::mm[ 9] = 0;
+ MyBase::mm[10] = 0;
+ MyBase::mm[11] = 0;
+ MyBase::mm[12] = 0;
+ MyBase::mm[13] = 0;
+ MyBase::mm[14] = 0;
+ MyBase::mm[15] = 0;
+ }
+
+ /// Set "this" matrix to identity
+ void setIdentity()
+ {
+ MyBase::mm[ 0] = 1;
+ MyBase::mm[ 1] = 0;
+ MyBase::mm[ 2] = 0;
+ MyBase::mm[ 3] = 0;
+
+ MyBase::mm[ 4] = 0;
+ MyBase::mm[ 5] = 1;
+ MyBase::mm[ 6] = 0;
+ MyBase::mm[ 7] = 0;
+
+ MyBase::mm[ 8] = 0;
+ MyBase::mm[ 9] = 0;
+ MyBase::mm[10] = 1;
+ MyBase::mm[11] = 0;
+
+ MyBase::mm[12] = 0;
+ MyBase::mm[13] = 0;
+ MyBase::mm[14] = 0;
+ MyBase::mm[15] = 1;
+ }
+
+
+ /// Set upper left to a Mat3
+ void setMat3(const Mat3<T> &m)
+ {
+ for (int i = 0; i < 3; i++)
+ for (int j=0; j < 3; j++)
+ MyBase::mm[i*4+j] = m[i][j];
+ }
+
+ Mat3<T> getMat3() const
+ {
+ Mat3<T> m;
+
+ for (int i = 0; i < 3; i++)
+ for (int j = 0; j < 3; j++)
+ m[i][j] = MyBase::mm[i*4+j];
+
+ return m;
+ }
+
+ /// Return the translation component
+ Vec3<T> getTranslation() const
+ {
+ return Vec3<T>(MyBase::mm[12], MyBase::mm[13], MyBase::mm[14]);
+ }
+
+ void setTranslation(const Vec3<T> &t)
+ {
+ MyBase::mm[12] = t[0];
+ MyBase::mm[13] = t[1];
+ MyBase::mm[14] = t[2];
+ }
+
+ /// Assignment operator
+ template<typename Source>
+ const Mat4& operator=(const Mat4<Source> &m)
+ {
+ const Source *src = m.asPointer();
+
+ // don't suppress warnings when assigning from different numerical types
+ std::copy(src, (src + this->numElements()), MyBase::mm);
+ return *this;
+ }
+
+ /// Test if "this" is equivalent to m with tolerance of eps value
+ bool eq(const Mat4 &m, T eps=1.0e-8) const
+ {
+ for (int i = 0; i < 16; i++) {
+ if (!isApproxEqual(MyBase::mm[i], m.mm[i], eps))
+ return false;
+ }
+ return true;
+ }
+
+ /// Negation operator, for e.g. m1 = -m2;
+ Mat4<T> operator-() const
+ {
+ return Mat4<T>(
+ -MyBase::mm[ 0], -MyBase::mm[ 1], -MyBase::mm[ 2], -MyBase::mm[ 3],
+ -MyBase::mm[ 4], -MyBase::mm[ 5], -MyBase::mm[ 6], -MyBase::mm[ 7],
+ -MyBase::mm[ 8], -MyBase::mm[ 9], -MyBase::mm[10], -MyBase::mm[11],
+ -MyBase::mm[12], -MyBase::mm[13], -MyBase::mm[14], -MyBase::mm[15]
+ );
+ } // trivial
+
+ /// Return m, where \f$m_{i,j} *= scalar\f$ for \f$i, j \in [0, 3]\f$
+ template <typename S>
+ const Mat4<T>& operator*=(S scalar)
+ {
+ MyBase::mm[ 0] *= scalar;
+ MyBase::mm[ 1] *= scalar;
+ MyBase::mm[ 2] *= scalar;
+ MyBase::mm[ 3] *= scalar;
+
+ MyBase::mm[ 4] *= scalar;
+ MyBase::mm[ 5] *= scalar;
+ MyBase::mm[ 6] *= scalar;
+ MyBase::mm[ 7] *= scalar;
+
+ MyBase::mm[ 8] *= scalar;
+ MyBase::mm[ 9] *= scalar;
+ MyBase::mm[10] *= scalar;
+ MyBase::mm[11] *= scalar;
+
+ MyBase::mm[12] *= scalar;
+ MyBase::mm[13] *= scalar;
+ MyBase::mm[14] *= scalar;
+ MyBase::mm[15] *= scalar;
+ return *this;
+ }
+
+ /// @brief Returns m0, where \f$m0_{i,j} += m1_{i,j}\f$ for \f$i, j \in [0, 3]\f$
+ template <typename S>
+ const Mat4<T> &operator+=(const Mat4<S> &m1)
+ {
+ const S* s = m1.asPointer();
+
+ MyBase::mm[ 0] += s[ 0];
+ MyBase::mm[ 1] += s[ 1];
+ MyBase::mm[ 2] += s[ 2];
+ MyBase::mm[ 3] += s[ 3];
+
+ MyBase::mm[ 4] += s[ 4];
+ MyBase::mm[ 5] += s[ 5];
+ MyBase::mm[ 6] += s[ 6];
+ MyBase::mm[ 7] += s[ 7];
+
+ MyBase::mm[ 8] += s[ 8];
+ MyBase::mm[ 9] += s[ 9];
+ MyBase::mm[10] += s[10];
+ MyBase::mm[11] += s[11];
+
+ MyBase::mm[12] += s[12];
+ MyBase::mm[13] += s[13];
+ MyBase::mm[14] += s[14];
+ MyBase::mm[15] += s[15];
+
+ return *this;
+ }
+
+ /// @brief Returns m0, where \f$m0_{i,j} -= m1_{i,j}\f$ for \f$i, j \in [0, 3]\f$
+ template <typename S>
+ const Mat4<T> &operator-=(const Mat4<S> &m1)
+ {
+ const S* s = m1.asPointer();
+
+ MyBase::mm[ 0] -= s[ 0];
+ MyBase::mm[ 1] -= s[ 1];
+ MyBase::mm[ 2] -= s[ 2];
+ MyBase::mm[ 3] -= s[ 3];
+
+ MyBase::mm[ 4] -= s[ 4];
+ MyBase::mm[ 5] -= s[ 5];
+ MyBase::mm[ 6] -= s[ 6];
+ MyBase::mm[ 7] -= s[ 7];
+
+ MyBase::mm[ 8] -= s[ 8];
+ MyBase::mm[ 9] -= s[ 9];
+ MyBase::mm[10] -= s[10];
+ MyBase::mm[11] -= s[11];
+
+ MyBase::mm[12] -= s[12];
+ MyBase::mm[13] -= s[13];
+ MyBase::mm[14] -= s[14];
+ MyBase::mm[15] -= s[15];
+
+ return *this;
+ }
+
+ /// Return m, where \f$m_{i,j} = \sum_{k} m0_{i,k}*m1_{k,j}\f$ for \f$i, j \in [0, 3]\f$
+ template <typename S>
+ const Mat4<T> &operator*=(const Mat4<S> &m1)
+ {
+ Mat4<T> m0(*this);
+
+ const T* s0 = m0.asPointer();
+ const S* s1 = m1.asPointer();
+
+ for (int i = 0; i < 4; i++) {
+ int i4 = 4 * i;
+ MyBase::mm[i4+0] = static_cast<T>(s0[i4+0] * s1[ 0] +
+ s0[i4+1] * s1[ 4] +
+ s0[i4+2] * s1[ 8] +
+ s0[i4+3] * s1[12]);
+
+ MyBase::mm[i4+1] = static_cast<T>(s0[i4+0] * s1[ 1] +
+ s0[i4+1] * s1[ 5] +
+ s0[i4+2] * s1[ 9] +
+ s0[i4+3] * s1[13]);
+
+ MyBase::mm[i4+2] = static_cast<T>(s0[i4+0] * s1[ 2] +
+ s0[i4+1] * s1[ 6] +
+ s0[i4+2] * s1[10] +
+ s0[i4+3] * s1[14]);
+
+ MyBase::mm[i4+3] = static_cast<T>(s0[i4+0] * s1[ 3] +
+ s0[i4+1] * s1[ 7] +
+ s0[i4+2] * s1[11] +
+ s0[i4+3] * s1[15]);
+ }
+ return *this;
+ }
+
+ /// @return transpose of this
+ Mat4 transpose() const
+ {
+ return Mat4<T>(
+ MyBase::mm[ 0], MyBase::mm[ 4], MyBase::mm[ 8], MyBase::mm[12],
+ MyBase::mm[ 1], MyBase::mm[ 5], MyBase::mm[ 9], MyBase::mm[13],
+ MyBase::mm[ 2], MyBase::mm[ 6], MyBase::mm[10], MyBase::mm[14],
+ MyBase::mm[ 3], MyBase::mm[ 7], MyBase::mm[11], MyBase::mm[15]
+ );
+ }
+
+
+ /// @return inverse of this
+ /// @throw ArithmeticError if singular
+ Mat4 inverse(T tolerance = 0) const
+ {
+ //
+ // inv [ A | b ] = [ E | f ] A: 3x3, b: 3x1, c': 1x3 d: 1x1
+ // [ c' | d ] [ g' | h ]
+ //
+ // If A is invertible use
+ //
+ // E = A^-1 + p*h*r
+ // p = A^-1 * b
+ // f = -p * h
+ // g' = -h * c'
+ // h = 1 / (d - c'*p)
+ // r' = c'*A^-1
+ //
+ // Otherwise use gauss-jordan elimination
+ //
+
+ //
+ // We create this alias to ourself so we can easily use own subscript
+ // operator.
+ const Mat4<T>& m(*this);
+
+ T m0011 = m[0][0] * m[1][1];
+ T m0012 = m[0][0] * m[1][2];
+ T m0110 = m[0][1] * m[1][0];
+ T m0210 = m[0][2] * m[1][0];
+ T m0120 = m[0][1] * m[2][0];
+ T m0220 = m[0][2] * m[2][0];
+
+ T detA = m0011 * m[2][2] - m0012 * m[2][1] - m0110 * m[2][2]
+ + m0210 * m[2][1] + m0120 * m[1][2] - m0220 * m[1][1];
+
+ bool hasPerspective =
+ (!isExactlyEqual(m[0][3], T(0.0)) ||
+ !isExactlyEqual(m[1][3], T(0.0)) ||
+ !isExactlyEqual(m[2][3], T(0.0)) ||
+ !isExactlyEqual(m[3][3], T(1.0)));
+
+ T det;
+ if (hasPerspective) {
+ det = m[0][3] * det3(m, 1,2,3, 0,2,1)
+ + m[1][3] * det3(m, 2,0,3, 0,2,1)
+ + m[2][3] * det3(m, 3,0,1, 0,2,1)
+ + m[3][3] * detA;
+ } else {
+ det = detA * m[3][3];
+ }
+
+ Mat4<T> inv;
+ bool invertible;
+
+ if (isApproxEqual(det,T(0.0),tolerance)) {
+ invertible = false;
+
+ } else if (isApproxEqual(detA,T(0.0),T(1e-8))) {
+ // det is too small to rely on inversion by subblocks
+ invertible = m.invert(inv, tolerance);
+
+ } else {
+ invertible = true;
+ detA = 1.0 / detA;
+
+ //
+ // Calculate A^-1
+ //
+ inv[0][0] = detA * ( m[1][1] * m[2][2] - m[1][2] * m[2][1]);
+ inv[0][1] = detA * (-m[0][1] * m[2][2] + m[0][2] * m[2][1]);
+ inv[0][2] = detA * ( m[0][1] * m[1][2] - m[0][2] * m[1][1]);
+
+ inv[1][0] = detA * (-m[1][0] * m[2][2] + m[1][2] * m[2][0]);
+ inv[1][1] = detA * ( m[0][0] * m[2][2] - m0220);
+ inv[1][2] = detA * ( m0210 - m0012);
+
+ inv[2][0] = detA * ( m[1][0] * m[2][1] - m[1][1] * m[2][0]);
+ inv[2][1] = detA * ( m0120 - m[0][0] * m[2][1]);
+ inv[2][2] = detA * ( m0011 - m0110);
+
+ if (hasPerspective) {
+ //
+ // Calculate r, p, and h
+ //
+ Vec3<T> r;
+ r[0] = m[3][0] * inv[0][0] + m[3][1] * inv[1][0]
+ + m[3][2] * inv[2][0];
+ r[1] = m[3][0] * inv[0][1] + m[3][1] * inv[1][1]
+ + m[3][2] * inv[2][1];
+ r[2] = m[3][0] * inv[0][2] + m[3][1] * inv[1][2]
+ + m[3][2] * inv[2][2];
+
+ Vec3<T> p;
+ p[0] = inv[0][0] * m[0][3] + inv[0][1] * m[1][3]
+ + inv[0][2] * m[2][3];
+ p[1] = inv[1][0] * m[0][3] + inv[1][1] * m[1][3]
+ + inv[1][2] * m[2][3];
+ p[2] = inv[2][0] * m[0][3] + inv[2][1] * m[1][3]
+ + inv[2][2] * m[2][3];
+
+ T h = m[3][3] - p.dot(Vec3<T>(m[3][0],m[3][1],m[3][2]));
+ if (isApproxEqual(h,T(0.0),tolerance)) {
+ invertible = false;
+
+ } else {
+ h = 1.0 / h;
+
+ //
+ // Calculate h, g, and f
+ //
+ inv[3][3] = h;
+ inv[3][0] = -h * r[0];
+ inv[3][1] = -h * r[1];
+ inv[3][2] = -h * r[2];
+
+ inv[0][3] = -h * p[0];
+ inv[1][3] = -h * p[1];
+ inv[2][3] = -h * p[2];
+
+ //
+ // Calculate E
+ //
+ p *= h;
+ inv[0][0] += p[0] * r[0];
+ inv[0][1] += p[0] * r[1];
+ inv[0][2] += p[0] * r[2];
+ inv[1][0] += p[1] * r[0];
+ inv[1][1] += p[1] * r[1];
+ inv[1][2] += p[1] * r[2];
+ inv[2][0] += p[2] * r[0];
+ inv[2][1] += p[2] * r[1];
+ inv[2][2] += p[2] * r[2];
+ }
+ } else {
+ // Equations are much simpler in the non-perspective case
+ inv[3][0] = - (m[3][0] * inv[0][0] + m[3][1] * inv[1][0]
+ + m[3][2] * inv[2][0]);
+ inv[3][1] = - (m[3][0] * inv[0][1] + m[3][1] * inv[1][1]
+ + m[3][2] * inv[2][1]);
+ inv[3][2] = - (m[3][0] * inv[0][2] + m[3][1] * inv[1][2]
+ + m[3][2] * inv[2][2]);
+ inv[0][3] = 0.0;
+ inv[1][3] = 0.0;
+ inv[2][3] = 0.0;
+ inv[3][3] = 1.0;
+ }
+ }
+
+ if (!invertible) OPENVDB_THROW(ArithmeticError, "Inversion of singular 4x4 matrix");
+ return inv;
+ }
+
+
+ /// Determinant of matrix
+ T det() const
+ {
+ const T *ap;
+ Mat3<T> submat;
+ T det;
+ T *sp;
+ int i, j, k, sign;
+
+ det = 0;
+ sign = 1;
+ for (i = 0; i < 4; i++) {
+ ap = &MyBase::mm[ 0];
+ sp = submat.asPointer();
+ for (j = 0; j < 4; j++) {
+ for (k = 0; k < 4; k++) {
+ if ((k != i) && (j != 0)) {
+ *sp++ = *ap;
+ }
+ ap++;
+ }
+ }
+
+ det += sign * MyBase::mm[i] * submat.det();
+ sign = -sign;
+ }
+
+ return det;
+ }
+
+ /// This function snaps a specific axis to a specific direction,
+ /// preserving scaling. It does this using minimum energy, thus
+ /// posing a unique solution if basis & direction arent parralel.
+ /// Direction need not be unit.
+ Mat4 snapBasis(Axis axis, const Vec3<T> &direction)
+ {return snapBasis(*this, axis, direction);}
+
+ /// Sets the matrix to a matrix that translates by v
+ static Mat4 translation(const Vec3d& v)
+ {
+ return Mat4(
+ T(1), T(0), T(0), T(0),
+ T(0), T(1), T(0), T(0),
+ T(0), T(0), T(1), T(0),
+ T(v.x()), T(v.y()),T(v.z()), T(1));
+ }
+
+ /// Sets the matrix to a matrix that translates by v
+ template <typename T0>
+ void setToTranslation(const Vec3<T0>& v)
+ {
+ MyBase::mm[ 0] = 1;
+ MyBase::mm[ 1] = 0;
+ MyBase::mm[ 2] = 0;
+ MyBase::mm[ 3] = 0;
+
+ MyBase::mm[ 4] = 0;
+ MyBase::mm[ 5] = 1;
+ MyBase::mm[ 6] = 0;
+ MyBase::mm[ 7] = 0;
+
+ MyBase::mm[ 8] = 0;
+ MyBase::mm[ 9] = 0;
+ MyBase::mm[10] = 1;
+ MyBase::mm[11] = 0;
+
+ MyBase::mm[12] = v.x();
+ MyBase::mm[13] = v.y();
+ MyBase::mm[14] = v.z();
+ MyBase::mm[15] = 1;
+ }
+
+ /// Left multiples by the specified translation, i.e. Trans * (*this)
+ template <typename T0>
+ void preTranslate(const Vec3<T0>& tr)
+ {
+ Vec3<T> tmp(tr.x(), tr.y(), tr.z());
+ Mat4<T> Tr = Mat4<T>::translation(tmp);
+
+ *this = Tr * (*this);
+
+ }
+
+ /// Right multiplies by the specified translation matrix, i.e. (*this) * Trans
+ template <typename T0>
+ void postTranslate(const Vec3<T0>& tr)
+ {
+ Vec3<T> tmp(tr.x(), tr.y(), tr.z());
+ Mat4<T> Tr = Mat4<T>::translation(tmp);
+
+ *this = (*this) * Tr;
+
+ }
+
+
+ /// Sets the matrix to a matrix that scales by v
+ template <typename T0>
+ void setToScale(const Vec3<T0>& v)
+ {
+ this->setIdentity();
+ MyBase::mm[ 0] = v.x();
+ MyBase::mm[ 5] = v.y();
+ MyBase::mm[10] = v.z();
+ }
+
+ // Left multiples by the specified scale matrix, i.e. Sc * (*this)
+ template <typename T0>
+ void preScale(const Vec3<T0>& v)
+ {
+ MyBase::mm[ 0] *= v.x();
+ MyBase::mm[ 1] *= v.x();
+ MyBase::mm[ 2] *= v.x();
+ MyBase::mm[ 3] *= v.x();
+
+ MyBase::mm[ 4] *= v.y();
+ MyBase::mm[ 5] *= v.y();
+ MyBase::mm[ 6] *= v.y();
+ MyBase::mm[ 7] *= v.y();
+
+ MyBase::mm[ 8] *= v.z();
+ MyBase::mm[ 9] *= v.z();
+ MyBase::mm[10] *= v.z();
+ MyBase::mm[11] *= v.z();
+ }
+
+
+
+ // Right multiples by the specified scale matrix, i.e. (*this) * Sc
+ template <typename T0>
+ void postScale(const Vec3<T0>& v)
+ {
+
+ MyBase::mm[ 0] *= v.x();
+ MyBase::mm[ 1] *= v.y();
+ MyBase::mm[ 2] *= v.z();
+
+ MyBase::mm[ 4] *= v.x();
+ MyBase::mm[ 5] *= v.y();
+ MyBase::mm[ 6] *= v.z();
+
+ MyBase::mm[ 8] *= v.x();
+ MyBase::mm[ 9] *= v.y();
+ MyBase::mm[10] *= v.z();
+
+ MyBase::mm[12] *= v.x();
+ MyBase::mm[13] *= v.y();
+ MyBase::mm[14] *= v.z();
+
+ }
+
+
+ /// @brief Sets the matrix to a rotation about the given axis.
+ /// @param axis The axis (one of X, Y, Z) to rotate about.
+ /// @param angle The rotation angle, in radians.
+ void setToRotation(Axis axis, T angle) {*this = rotation<Mat4<T> >(axis, angle);}
+
+ /// @brief Sets the matrix to a rotation about an arbitrary axis
+ /// @param axis The axis of rotation (cannot be zero-length)
+ /// @param angle The rotation angle, in radians.
+ void setToRotation(const Vec3<T>& axis, T angle) {*this = rotation<Mat4<T> >(axis, angle);}
+
+ /// @brief Sets the matrix to a rotation that maps v1 onto v2 about the cross
+ /// product of v1 and v2.
+ void setToRotation(const Vec3<T>& v1, const Vec3<T>& v2) {*this = rotation<Mat4<T> >(v1, v2);}
+
+
+ /// @brief Left multiplies by a rotation clock-wiseabout the given axis into this matrix.
+ /// @param axis The axis (one of X, Y, Z) of rotation.
+ /// @param angle The clock-wise rotation angle, in radians.
+ void preRotate(Axis axis, T angle)
+ {
+ T c = static_cast<T>(cos(angle));
+ T s = -static_cast<T>(sin(angle)); // the "-" makes it clockwise
+
+ switch (axis) {
+ case X_AXIS:
+ {
+ T a4, a5, a6, a7;
+
+ a4 = c * MyBase::mm[ 4] - s * MyBase::mm[ 8];
+ a5 = c * MyBase::mm[ 5] - s * MyBase::mm[ 9];
+ a6 = c * MyBase::mm[ 6] - s * MyBase::mm[10];
+ a7 = c * MyBase::mm[ 7] - s * MyBase::mm[11];
+
+
+ MyBase::mm[ 8] = s * MyBase::mm[ 4] + c * MyBase::mm[ 8];
+ MyBase::mm[ 9] = s * MyBase::mm[ 5] + c * MyBase::mm[ 9];
+ MyBase::mm[10] = s * MyBase::mm[ 6] + c * MyBase::mm[10];
+ MyBase::mm[11] = s * MyBase::mm[ 7] + c * MyBase::mm[11];
+
+ MyBase::mm[ 4] = a4;
+ MyBase::mm[ 5] = a5;
+ MyBase::mm[ 6] = a6;
+ MyBase::mm[ 7] = a7;
+ }
+ break;
+
+ case Y_AXIS:
+ {
+ T a0, a1, a2, a3;
+
+ a0 = c * MyBase::mm[ 0] + s * MyBase::mm[ 8];
+ a1 = c * MyBase::mm[ 1] + s * MyBase::mm[ 9];
+ a2 = c * MyBase::mm[ 2] + s * MyBase::mm[10];
+ a3 = c * MyBase::mm[ 3] + s * MyBase::mm[11];
+
+ MyBase::mm[ 8] = -s * MyBase::mm[ 0] + c * MyBase::mm[ 8];
+ MyBase::mm[ 9] = -s * MyBase::mm[ 1] + c * MyBase::mm[ 9];
+ MyBase::mm[10] = -s * MyBase::mm[ 2] + c * MyBase::mm[10];
+ MyBase::mm[11] = -s * MyBase::mm[ 3] + c * MyBase::mm[11];
+
+
+ MyBase::mm[ 0] = a0;
+ MyBase::mm[ 1] = a1;
+ MyBase::mm[ 2] = a2;
+ MyBase::mm[ 3] = a3;
+ }
+ break;
+
+ case Z_AXIS:
+ {
+ T a0, a1, a2, a3;
+
+ a0 = c * MyBase::mm[ 0] - s * MyBase::mm[ 4];
+ a1 = c * MyBase::mm[ 1] - s * MyBase::mm[ 5];
+ a2 = c * MyBase::mm[ 2] - s * MyBase::mm[ 6];
+ a3 = c * MyBase::mm[ 3] - s * MyBase::mm[ 7];
+
+ MyBase::mm[ 4] = s * MyBase::mm[ 0] + c * MyBase::mm[ 4];
+ MyBase::mm[ 5] = s * MyBase::mm[ 1] + c * MyBase::mm[ 5];
+ MyBase::mm[ 6] = s * MyBase::mm[ 2] + c * MyBase::mm[ 6];
+ MyBase::mm[ 7] = s * MyBase::mm[ 3] + c * MyBase::mm[ 7];
+
+ MyBase::mm[ 0] = a0;
+ MyBase::mm[ 1] = a1;
+ MyBase::mm[ 2] = a2;
+ MyBase::mm[ 3] = a3;
+ }
+ break;
+
+ default:
+ assert(axis==X_AXIS || axis==Y_AXIS || axis==Z_AXIS);
+ }
+ }
+
+
+ /// @brief Right multiplies by a rotation clock-wiseabout the given axis into this matrix.
+ /// @param axis The axis (one of X, Y, Z) of rotation.
+ /// @param angle The clock-wise rotation angle, in radians.
+ void postRotate(Axis axis, T angle)
+ {
+ T c = static_cast<T>(cos(angle));
+ T s = -static_cast<T>(sin(angle)); // the "-" makes it clockwise
+
+
+
+ switch (axis) {
+ case X_AXIS:
+ {
+ T a2, a6, a10, a14;
+
+ a2 = c * MyBase::mm[ 2] - s * MyBase::mm[ 1];
+ a6 = c * MyBase::mm[ 6] - s * MyBase::mm[ 5];
+ a10 = c * MyBase::mm[10] - s * MyBase::mm[ 9];
+ a14 = c * MyBase::mm[14] - s * MyBase::mm[13];
+
+
+ MyBase::mm[ 1] = c * MyBase::mm[ 1] + s * MyBase::mm[ 2];
+ MyBase::mm[ 5] = c * MyBase::mm[ 5] + s * MyBase::mm[ 6];
+ MyBase::mm[ 9] = c * MyBase::mm[ 9] + s * MyBase::mm[10];
+ MyBase::mm[13] = c * MyBase::mm[13] + s * MyBase::mm[14];
+
+ MyBase::mm[ 2] = a2;
+ MyBase::mm[ 6] = a6;
+ MyBase::mm[10] = a10;
+ MyBase::mm[14] = a14;
+ }
+ break;
+
+ case Y_AXIS:
+ {
+ T a2, a6, a10, a14;
+
+ a2 = c * MyBase::mm[ 2] + s * MyBase::mm[ 0];
+ a6 = c * MyBase::mm[ 6] + s * MyBase::mm[ 4];
+ a10 = c * MyBase::mm[10] + s * MyBase::mm[ 8];
+ a14 = c * MyBase::mm[14] + s * MyBase::mm[12];
+
+ MyBase::mm[ 0] = c * MyBase::mm[ 0] - s * MyBase::mm[ 2];
+ MyBase::mm[ 4] = c * MyBase::mm[ 4] - s * MyBase::mm[ 6];
+ MyBase::mm[ 8] = c * MyBase::mm[ 8] - s * MyBase::mm[10];
+ MyBase::mm[12] = c * MyBase::mm[12] - s * MyBase::mm[14];
+
+ MyBase::mm[ 2] = a2;
+ MyBase::mm[ 6] = a6;
+ MyBase::mm[10] = a10;
+ MyBase::mm[14] = a14;
+ }
+ break;
+
+ case Z_AXIS:
+ {
+ T a1, a5, a9, a13;
+
+ a1 = c * MyBase::mm[ 1] - s * MyBase::mm[ 0];
+ a5 = c * MyBase::mm[ 5] - s * MyBase::mm[ 4];
+ a9 = c * MyBase::mm[ 9] - s * MyBase::mm[ 8];
+ a13 = c * MyBase::mm[13] - s * MyBase::mm[12];
+
+ MyBase::mm[ 0] = c * MyBase::mm[ 0] + s * MyBase::mm[ 1];
+ MyBase::mm[ 4] = c * MyBase::mm[ 4] + s * MyBase::mm[ 5];
+ MyBase::mm[ 8] = c * MyBase::mm[ 8] + s * MyBase::mm[ 9];
+ MyBase::mm[12] = c * MyBase::mm[12] + s * MyBase::mm[13];
+
+ MyBase::mm[ 1] = a1;
+ MyBase::mm[ 5] = a5;
+ MyBase::mm[ 9] = a9;
+ MyBase::mm[13] = a13;
+
+ }
+ break;
+
+ default:
+ assert(axis==X_AXIS || axis==Y_AXIS || axis==Z_AXIS);
+ }
+ }
+
+ /// @brief Sets the matrix to a shear along axis0 by a fraction of axis1.
+ /// @param axis0 The fixed axis of the shear.
+ /// @param axis1 The shear axis.
+ /// @param shearby The shear factor.
+ void setToShear(Axis axis0, Axis axis1, T shearby)
+ {
+ *this = shear<Mat4<T> >(axis0, axis1, shearby);
+ }
+
+
+ /// @brief Left multiplies a shearing transformation into the matrix.
+ /// @see setToShear
+ void preShear(Axis axis0, Axis axis1, T shear)
+ {
+ int index0 = static_cast<int>(axis0);
+ int index1 = static_cast<int>(axis1);
+
+ // to row "index1" add a multiple of the index0 row
+ MyBase::mm[index1 * 4 + 0] += shear * MyBase::mm[index0 * 4 + 0];
+ MyBase::mm[index1 * 4 + 1] += shear * MyBase::mm[index0 * 4 + 1];
+ MyBase::mm[index1 * 4 + 2] += shear * MyBase::mm[index0 * 4 + 2];
+ MyBase::mm[index1 * 4 + 3] += shear * MyBase::mm[index0 * 4 + 3];
+ }
+
+
+ /// @brief Right multiplies a shearing transformation into the matrix.
+ /// @see setToShear
+ void postShear(Axis axis0, Axis axis1, T shear)
+ {
+ int index0 = static_cast<int>(axis0);
+ int index1 = static_cast<int>(axis1);
+
+ // to collumn "index0" add a multiple of the index1 row
+ MyBase::mm[index0 + 0] += shear * MyBase::mm[index1 + 0];
+ MyBase::mm[index0 + 4] += shear * MyBase::mm[index1 + 4];
+ MyBase::mm[index0 + 8] += shear * MyBase::mm[index1 + 8];
+ MyBase::mm[index0 + 12] += shear * MyBase::mm[index1 + 12];
+
+ }
+
+ /// Transform a Vec4 by post-multiplication.
+ template<typename T0>
+ Vec4<T0> transform(const Vec4<T0> &v) const
+ {
+ return static_cast< Vec4<T0> >(v * *this);
+ }
+
+ /// Transform a Vec3 by post-multiplication, without homogenous division.
+ template<typename T0>
+ Vec3<T0> transform(const Vec3<T0> &v) const
+ {
+ return static_cast< Vec3<T0> >(v * *this);
+ }
+
+ /// Transform a Vec4 by pre-multiplication.
+ template<typename T0>
+ Vec4<T0> pretransform(const Vec4<T0> &v) const
+ {
+ return static_cast< Vec4<T0> >(*this * v);
+ }
+
+ /// Transform a Vec3 by pre-multiplication, without homogenous division.
+ template<typename T0>
+ Vec3<T0> pretransform(const Vec3<T0> &v) const
+ {
+ return static_cast< Vec3<T0> >(*this * v);
+ }
+
+ /// Transform a Vec3 by post-multiplication, doing homogenous divison.
+ template<typename T0>
+ Vec3<T0> transformH(const Vec3<T0> &p) const
+ {
+ T0 w;
+
+ // w = p * (*this).col(3);
+ w = p[0] * MyBase::mm[ 3] + p[1] * MyBase::mm[ 7] + p[2] * MyBase::mm[11] + MyBase::mm[15];
+
+ if ( !isExactlyEqual(w , 0.0) ) {
+ return Vec3<T0>(static_cast<T0>((p[0] * MyBase::mm[ 0] + p[1] * MyBase::mm[ 4] +
+ p[2] * MyBase::mm[ 8] + MyBase::mm[12]) / w),
+ static_cast<T0>((p[0] * MyBase::mm[ 1] + p[1] * MyBase::mm[ 5] +
+ p[2] * MyBase::mm[ 9] + MyBase::mm[13]) / w),
+ static_cast<T0>((p[0] * MyBase::mm[ 2] + p[1] * MyBase::mm[ 6] +
+ p[2] * MyBase::mm[10] + MyBase::mm[14]) / w));
+ }
+
+ return Vec3<T0>(0, 0, 0);
+ }
+
+ /// Transform a Vec3 by pre-multiplication, doing homogenous division.
+ template<typename T0>
+ Vec3<T0> pretransformH(const Vec3<T0> &p) const
+ {
+ T0 w;
+
+ // w = p * (*this).col(3);
+ w = p[0] * MyBase::mm[12] + p[1] * MyBase::mm[13] + p[2] * MyBase::mm[14] + MyBase::mm[15];
+
+ if ( !isExactlyEqual(w , 0.0) ) {
+ return Vec3<T0>(static_cast<T0>((p[0] * MyBase::mm[ 0] + p[1] * MyBase::mm[ 1] +
+ p[2] * MyBase::mm[ 2] + MyBase::mm[ 3]) / w),
+ static_cast<T0>((p[0] * MyBase::mm[ 4] + p[1] * MyBase::mm[ 5] +
+ p[2] * MyBase::mm[ 6] + MyBase::mm[ 7]) / w),
+ static_cast<T0>((p[0] * MyBase::mm[ 8] + p[1] * MyBase::mm[ 9] +
+ p[2] * MyBase::mm[10] + MyBase::mm[11]) / w));
+ }
+
+ return Vec3<T0>(0, 0, 0);
+ }
+
+ /// Transform a Vec3 by post-multiplication, without translation.
+ template<typename T0>
+ Vec3<T0> transform3x3(const Vec3<T0> &v) const
+ {
+ return Vec3<T0>(
+ static_cast<T0>(v[0] * MyBase::mm[ 0] + v[1] * MyBase::mm[ 4] + v[2] * MyBase::mm[ 8]),
+ static_cast<T0>(v[0] * MyBase::mm[ 1] + v[1] * MyBase::mm[ 5] + v[2] * MyBase::mm[ 9]),
+ static_cast<T0>(v[0] * MyBase::mm[ 2] + v[1] * MyBase::mm[ 6] + v[2] * MyBase::mm[10]));
+ }
+
+
+private:
+ bool invert(Mat4<T> &inverse, T tolerance) const;
+
+ T det2(const Mat4<T> &a, int i0, int i1, int j0, int j1) const {
+ int i0row = i0 * 4;
+ int i1row = i1 * 4;
+ return a.mm[i0row+j0]*a.mm[i1row+j1] - a.mm[i0row+j1]*a.mm[i1row+j0];
+ }
+
+ T det3(const Mat4<T> &a, int i0, int i1, int i2,
+ int j0, int j1, int j2) const {
+ int i0row = i0 * 4;
+ return a.mm[i0row+j0]*det2(a, i1,i2, j1,j2) +
+ a.mm[i0row+j1]*det2(a, i1,i2, j2,j0) +
+ a.mm[i0row+j2]*det2(a, i1,i2, j0,j1);
+ }
+
+ static const Mat4<T> sIdentity;
+ static const Mat4<T> sZero;
+}; // class Mat4
+
+
+template <typename T>
+const Mat4<T> Mat4<T>::sIdentity = Mat4<T>(1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1);
+
+template <typename T>
+const Mat4<T> Mat4<T>::sZero = Mat4<T>(0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0);
+
+/// @relates Mat4
+/// @brief Equality operator, does exact floating point comparisons
+template <typename T0, typename T1>
+bool operator==(const Mat4<T0> &m0, const Mat4<T1> &m1)
+{
+ const T0 *t0 = m0.asPointer();
+ const T1 *t1 = m1.asPointer();
+
+ for (int i=0; i<16; ++i) if (!isExactlyEqual(t0[i], t1[i])) return false;
+ return true;
+}
+
+/// @relates Mat4
+/// @brief Inequality operator, does exact floating point comparisons
+template <typename T0, typename T1>
+bool operator!=(const Mat4<T0> &m0, const Mat4<T1> &m1) { return !(m0 == m1); }
+
+/// @relates Mat4
+/// @brief Returns M, where \f$M_{i,j} = m_{i,j} * scalar\f$ for \f$i, j \in [0, 3]\f$
+template <typename S, typename T>
+Mat4<typename promote<S, T>::type> operator*(S scalar, const Mat4<T> &m)
+{
+ return m*scalar;
+}
+
+/// @relates Mat4
+/// @brief Returns M, where \f$M_{i,j} = m_{i,j} * scalar\f$ for \f$i, j \in [0, 3]\f$
+template <typename S, typename T>
+Mat4<typename promote<S, T>::type> operator*(const Mat4<T> &m, S scalar)
+{
+ Mat4<typename promote<S, T>::type> result(m);
+ result *= scalar;
+ return result;
+}
+
+/// @relates Mat4
+/// @brief Returns v, where \f$v_{i} = \sum_{n=0}^3 m_{i,n} * v_n \f$ for \f$i \in [0, 3]\f$
+template<typename T, typename MT>
+inline Vec4<typename promote<T, MT>::type>
+operator*(const Mat4<MT> &_m,
+ const Vec4<T> &_v)
+{
+ MT const *m = _m.asPointer();
+ return Vec4<typename promote<T, MT>::type>(
+ _v[0]*m[0] + _v[1]*m[1] + _v[2]*m[2] + _v[3]*m[3],
+ _v[0]*m[4] + _v[1]*m[5] + _v[2]*m[6] + _v[3]*m[7],
+ _v[0]*m[8] + _v[1]*m[9] + _v[2]*m[10] + _v[3]*m[11],
+ _v[0]*m[12] + _v[1]*m[13] + _v[2]*m[14] + _v[3]*m[15]);
+}
+
+/// @relates Mat4
+/// @brief Returns v, where \f$v_{i} = \sum_{n=0}^3 m_{n,i} * v_n \f$ for \f$i \in [0, 3]\f$
+template<typename T, typename MT>
+inline Vec4<typename promote<T, MT>::type>
+operator*(const Vec4<T> &_v,
+ const Mat4<MT> &_m)
+{
+ MT const *m = _m.asPointer();
+ return Vec4<typename promote<T, MT>::type>(
+ _v[0]*m[0] + _v[1]*m[4] + _v[2]*m[8] + _v[3]*m[12],
+ _v[0]*m[1] + _v[1]*m[5] + _v[2]*m[9] + _v[3]*m[13],
+ _v[0]*m[2] + _v[1]*m[6] + _v[2]*m[10] + _v[3]*m[14],
+ _v[0]*m[3] + _v[1]*m[7] + _v[2]*m[11] + _v[3]*m[15]);
+}
+
+/// @relates Mat4
+/// @brief Returns v, where
+/// \f$v_{i} = \sum_{n=0}^3\left(m_{i,n} * v_n + m_{i,3}\right)\f$ for \f$i \in [0, 2]\f$
+template<typename T, typename MT>
+inline Vec3<typename promote<T, MT>::type>
+operator*(const Mat4<MT> &_m,
+ const Vec3<T> &_v)
+{
+ MT const *m = _m.asPointer();
+ return Vec3<typename promote<T, MT>::type>(
+ _v[0]*m[0] + _v[1]*m[1] + _v[2]*m[2] + m[3],
+ _v[0]*m[4] + _v[1]*m[5] + _v[2]*m[6] + m[7],
+ _v[0]*m[8] + _v[1]*m[9] + _v[2]*m[10] + m[11]);
+}
+
+/// @relates Mat4
+/// @brief Returns v, where
+/// \f$v_{i} = \sum_{n=0}^3\left(m_{n,i} * v_n + m_{3,i}\right)\f$ for \f$i \in [0, 2]\f$
+template<typename T, typename MT>
+inline Vec3<typename promote<T, MT>::type>
+operator*(const Vec3<T> &_v,
+ const Mat4<MT> &_m)
+{
+ MT const *m = _m.asPointer();
+ return Vec3<typename promote<T, MT>::type>(
+ _v[0]*m[0] + _v[1]*m[4] + _v[2]*m[8] + m[12],
+ _v[0]*m[1] + _v[1]*m[5] + _v[2]*m[9] + m[13],
+ _v[0]*m[2] + _v[1]*m[6] + _v[2]*m[10] + m[14]);
+}
+
+/// @relates Mat4
+/// @brief Returns M, where \f$M_{i,j} = m0_{i,j} + m1_{i,j}\f$ for \f$i, j \in [0, 3]\f$
+template <typename T0, typename T1>
+Mat4<typename promote<T0, T1>::type>
+operator+(const Mat4<T0> &m0, const Mat4<T1> &m1)
+{
+ Mat4<typename promote<T0, T1>::type> result(m0);
+ result += m1;
+ return result;
+}
+
+/// @relates Mat4
+/// @brief Returns M, where \f$M_{i,j} = m0_{i,j} - m1_{i,j}\f$ for \f$i, j \in [0, 3]\f$
+template <typename T0, typename T1>
+Mat4<typename promote<T0, T1>::type>
+operator-(const Mat4<T0> &m0, const Mat4<T1> &m1)
+{
+ Mat4<typename promote<T0, T1>::type> result(m0);
+ result -= m1;
+ return result;
+}
+
+/// @relates Mat4
+/// @brief Returns M, where
+/// \f$M_{ij} = \sum_{n=0}^3\left(m0_{nj} + m1_{in}\right)\f$ for \f$i, j \in [0, 3]\f$
+template <typename T0, typename T1>
+Mat4<typename promote<T0, T1>::type>
+operator*(const Mat4<T0> &m0, const Mat4<T1> &m1)
+{
+ Mat4<typename promote<T0, T1>::type> result(m0);
+ result *= m1;
+ return result;
+}
+
+
+/// Transform a Vec3 by pre-multiplication, without translation.
+/// Presumes this matrix is inverse of coordinate transform
+/// Synonymous to "pretransform3x3"
+template<typename T0, typename T1>
+Vec3<T1> transformNormal(const Mat4<T0> &m, const Vec3<T1> &n)
+{
+ return Vec3<T1>(
+ static_cast<T1>(m[0][0]*n[0] + m[0][1]*n[1] + m[0][2]*n[2]),
+ static_cast<T1>(m[1][0]*n[0] + m[1][1]*n[1] + m[1][2]*n[2]),
+ static_cast<T1>(m[2][0]*n[0] + m[2][1]*n[1] + m[2][2]*n[2]));
+}
+
+
+/// Invert via gauss-jordan elimination. Modified from dreamworks internal mx library
+template<typename T>
+bool Mat4<T>::invert(Mat4<T> &inverse, T tolerance) const
+{
+ Mat4<T> temp(*this);
+ inverse.setIdentity();
+
+ // Forward elimination step
+ double det = 1.0;
+ for (int i = 0; i < 4; ++i) {
+ int row = i;
+ double max = fabs(temp[i][i]);
+
+ for (int k = i+1; k < 4; ++k) {
+ if (fabs(temp[k][i]) > max) {
+ row = k;
+ max = fabs(temp[k][i]);
+ }
+ }
+
+ if (isExactlyEqual(max, 0.0)) return false;
+
+ // must move pivot to row i
+ if (row != i) {
+ det = -det;
+ for (int k = 0; k < 4; ++k) {
+ std::swap(temp[row][k], temp[i][k]);
+ std::swap(inverse[row][k], inverse[i][k]);
+ }
+ }
+
+ double pivot = temp[i][i];
+ det *= pivot;
+
+ // scale row i
+ for (int k = 0; k < 4; ++k) {
+ temp[i][k] /= pivot;
+ inverse[i][k] /= pivot;
+ }
+
+ // eliminate in rows below i
+ for (int j = i+1; j < 4; ++j) {
+ double t = temp[j][i];
+ if (!isExactlyEqual(t, 0.0)) {
+ // subtract scaled row i from row j
+ for (int k = 0; k < 4; ++k) {
+ temp[j][k] -= temp[i][k] * t;
+ inverse[j][k] -= inverse[i][k] * t;
+ }
+ }
+ }
+ }
+
+ // Backward elimination step
+ for (int i = 3; i > 0; --i) {
+ for (int j = 0; j < i; ++j) {
+ double t = temp[j][i];
+
+ if (!isExactlyEqual(t, 0.0)) {
+ for (int k = 0; k < 4; ++k) {
+ inverse[j][k] -= inverse[i][k]*t;
+ }
+ }
+ }
+ }
+ return det*det >= tolerance*tolerance;
+}
+
+template <typename T>
+inline bool isAffine(const Mat4<T>& m) {
+ return (m.col(3) == Vec4<T>(0, 0, 0, 1));
+}
+
+template <typename T>
+inline bool hasTranslation(const Mat4<T>& m) {
+ return (m.row(3) != Vec4<T>(0, 0, 0, 1));
+}
+
+
+typedef Mat4<float> Mat4s;
+typedef Mat4<double> Mat4d;
+
+#if DWREAL_IS_DOUBLE == 1
+typedef Mat4d Mat4f;
+#else
+typedef Mat4s Mat4f;
+#endif // DWREAL_IS_DOUBLE
+
+} // namespace math
+
+
+template<> inline math::Mat4s zeroVal<math::Mat4s>() { return math::Mat4s::identity(); }
+template<> inline math::Mat4d zeroVal<math::Mat4d>() { return math::Mat4d::identity(); }
+
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_UTIL_MAT4_H_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/math/Math.h b/extern/openvdb/internal/openvdb/math/Math.h
new file mode 100644
index 00000000000..9dbe7dc5483
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/math/Math.h
@@ -0,0 +1,652 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+//
+/// @author Ken Museth
+///
+/// @file Math.h
+
+#ifndef OPENVDB_MATH_HAS_BEEN_INCLUDED
+#define OPENVDB_MATH_HAS_BEEN_INCLUDED
+
+#include <assert.h>
+#include <algorithm> //for std::max
+#include <cmath> //for floor, ceil and sqrt
+#include <math.h> //for pow, fabs(float,double,long double) etc
+#include <cstdlib> //for srand, abs(int)
+#include <limits> //for std::numeric_limits<Type>::max()
+#include <string>
+#include <boost/numeric/conversion/conversion_traits.hpp>
+#include <openvdb/Platform.h>
+#include <openvdb/version.h>
+
+// Compile pragmas
+
+// Intel(r) compiler fires remark #1572: floating-point equality and inequality
+// comparisons are unrealiable when == or != is used with floating point operands.
+#if defined(__INTEL_COMPILER)
+ #define OPENVDB_NO_FP_EQUALITY_WARNING_BEGIN \
+ _Pragma("warning (push)") \
+ _Pragma("warning (disable:1572)")
+ #define OPENVDB_NO_FP_EQUALITY_WARNING_END \
+ _Pragma("warning (pop)")
+#else
+ // For GCC, #pragma GCC diagnostic ignored "-Wfloat-equal"
+ // isn't working until gcc 4.2+,
+ // Trying
+ // #pragma GCC system_header
+ // creates other problems, most notably "warning: will never be executed"
+ // in from templates, unsure of how to work around.
+ // If necessary, could use integer based comparisons for equality
+ #define OPENVDB_NO_FP_EQUALITY_WARNING_BEGIN
+ #define OPENVDB_NO_FP_EQUALITY_WARNING_END
+#endif
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+
+/// Return the value of type T that corresponds to zero.
+/// @note A zeroVal<T>() specialization must be defined for each ValueType T
+/// that cannot be constructed using the form T(0). For example, std::string(0)
+/// treats 0 as NULL and throws a std::logic_error.
+template<typename T> inline T zeroVal() { return T(0); }
+/// Return the std::string value that corresponds to zero.
+template<> inline std::string zeroVal<std::string>() { return ""; }
+/// Return the bool value that corresponds to zero.
+template<> inline bool zeroVal<bool>() { return false; }
+
+template<typename T> inline T toleranceValue() { return T(1e-8); }
+template<> inline float toleranceValue<float>() { return float(1e-6); }
+
+
+/// @todo These won't be needed if we eliminate StringGrids.
+//@{
+/// @brief Needed to support the <tt>(zeroVal<ValueType>() + val)</tt> idiom
+/// when @c ValueType is @c std::string
+inline std::string operator+(const std::string& s, bool) { return s; }
+inline std::string operator+(const std::string& s, int) { return s; }
+inline std::string operator+(const std::string& s, float) { return s; }
+inline std::string operator+(const std::string& s, double) { return s; }
+//@}
+
+
+/// Return the unary negation of the given value.
+/// @note A negative<T>() specialization must be defined for each ValueType T
+/// for which unary negation is not defined.
+template<typename T> inline T negative(const T& val) { return T(-val); }
+template<> inline bool negative(const bool& val) { return !val; }
+/// Return the "negation" of the given string.
+template<> inline std::string negative(const std::string& val) { return val; }
+
+
+namespace math {
+
+/// ==========> Random Values <==================
+
+/// Initialize random number generator
+inline void randSeed(unsigned int seed)
+{
+ srand(seed);
+}
+
+/// Return random value [0,1]
+inline double randUniform()
+{
+ return (double)(rand() / (RAND_MAX + 1.0));
+}
+
+/// Simple class to generate random intergers
+class RandomInt
+{
+ protected:
+ int my_min, my_range;
+ public:
+ RandomInt(unsigned int seed, int min, int max) : my_min(min), my_range(max-min+1) {
+ assert(min<max && "RandomInt: invalid arguments");
+ randSeed(seed);
+ }
+ void setRange(int min, int max) {my_min=min; my_range=max-min+1;}
+ int operator() (void) const {return rand() % my_range + my_min;}
+ int operator() (int min, int max) const {return rand() % (max-min+1) + min;}
+};
+
+
+// ==========> Clamp/Abs <==================
+
+/// Return @a x clamped to [@a min, @a max]
+template <typename Type>
+inline Type Clamp(Type x, Type min, Type max) {
+ assert(min<max);
+ return x > min ? x < max ? x : max : min;
+}
+
+/// Return @a x clamped to [0, 1]
+template <class Type>
+inline Type Clamp01(Type x) {
+ return x > Type(0) ? x < Type(1) ? x : Type(1) : Type(0);
+}
+/// Return @c true if @a x is outside [0,1]
+template <class Type>
+inline bool ClampTest01(Type &x) {
+ if (x>=Type(0) && x<=Type(1)) return false;
+ x = x< Type(0) ? Type(0) : Type(1);
+ return true;
+}
+
+/// Return 0 if x<min, 1 if x>max and else (3-2*t)*t*t, (x-min)/(max-min)
+template <class Type>
+inline Type SmoothUnitStep(Type x, Type min, Type max) {
+ assert(min<max);
+ const Type t = (x-min)/(max-min);
+ return t > 0 ? t < 1 ? (3-2*t)*t*t : Type(1) : Type(0);
+}
+
+/// Return the absolute value of a signed integer
+inline int32_t Abs(int32_t i)
+{
+ return abs(i);
+}
+
+/// Return the absolute value of a signed long integer
+inline int64_t Abs(int64_t i)
+{
+#ifdef _MSC_VER
+ return (i < int64_t(0) ? -i : i);
+#else
+ return abs(i);
+#endif
+}
+
+/// Return the absolute value of a float
+inline float Abs(float x)
+{
+ return fabs(x);
+}
+
+/// Return the absolute value of a double
+inline double Abs(double x)
+{
+ return fabs(x);
+}
+
+/// Return the absolute value of a long double
+inline long double Abs(long double x)
+{
+ return fabs(x);
+}
+
+/// Return the absolute value of a unsigned integer
+inline uint32_t Abs(uint32_t i)
+{
+ return i;
+}
+
+/// Return the absolute value of a unsigned integer
+inline uint64_t Abs(uint64_t i)
+{
+ return i;
+}
+////////////////////////////////////////
+
+
+template<typename Type>
+inline bool
+isZero(const Type& x)
+{
+ OPENVDB_NO_FP_EQUALITY_WARNING_BEGIN
+ return x == zeroVal<Type>();
+ OPENVDB_NO_FP_EQUALITY_WARNING_END
+}
+
+template<typename Type>
+inline bool
+isNegative(const Type& x)
+{
+ return x < zeroVal<Type>();
+}
+
+template<typename Type>
+inline bool
+isApproxEqual(const Type& a, const Type& b)
+{
+ const Type tolerance = Type(zeroVal<Type>() + toleranceValue<Type>());
+ return !(Abs(a - b) > tolerance);
+}
+
+template<typename Type>
+inline bool
+isApproxEqual(const Type& a, const Type& b, const Type& tolerance)
+{
+ return !(Abs(a - b) > tolerance);
+}
+
+#define OPENVDB_EXACT_IS_APPROX_EQUAL(T) \
+ template<> inline bool isApproxEqual<T>(const T& a, const T& b) { return a == b; } \
+ template<> inline bool isApproxEqual<T>(const T& a, const T& b, const T&) { return a == b; } \
+ /**/
+
+OPENVDB_EXACT_IS_APPROX_EQUAL(bool)
+OPENVDB_EXACT_IS_APPROX_EQUAL(std::string)
+
+
+template<typename T0, typename T1>
+inline bool
+isExactlyEqual(const T0& a, const T1& b)
+{
+ OPENVDB_NO_FP_EQUALITY_WARNING_BEGIN
+ return a == b;
+ OPENVDB_NO_FP_EQUALITY_WARNING_END
+}
+
+
+template<typename Type>
+inline bool
+isRelOrApproxEqual(const Type& a, const Type& b, const Type& absTol, const Type& relTol)
+{
+ // First check to see if we are inside the absolute tolerance
+ // Necessary for numbers close to 0
+ if (!(Abs(a - b) > absTol)) return true;
+
+ // Next check to see if we are inside the relative tolerance
+ // to handle large numbers that aren't within the abs tolerance
+ // but could be the closest floating point representation
+ double relError;
+ if (Abs(b) > Abs(a)) {
+ relError = Abs((a - b) / b);
+ } else {
+ relError = Abs((a - b) / a);
+ }
+ return (relError <= relTol);
+}
+
+template<>
+inline bool
+isRelOrApproxEqual(const bool& a, const bool& b, const bool&, const bool&)
+{
+ return (a == b);
+}
+
+
+////////////////////////////////////////
+
+
+// Avoid strict aliasing issues by using type punning
+// http://cellperformance.beyond3d.com/articles/2006/06/understanding-strict-aliasing.html
+// Using "casting through a union(2)"
+inline int32_t
+floatToInt32(const float aFloatValue)
+{
+ union FloatOrInt32 { float floatValue; int32_t int32Value; };
+ const FloatOrInt32* foi = reinterpret_cast<const FloatOrInt32*>(&aFloatValue);
+ return foi->int32Value;
+}
+
+inline int64_t
+doubleToInt64(const double aDoubleValue)
+{
+ union DoubleOrInt64 { double doubleValue; int64_t int64Value; };
+ const DoubleOrInt64* dol = reinterpret_cast<const DoubleOrInt64*>(&aDoubleValue);
+ return dol->int64Value;
+}
+
+
+// aUnitsInLastPlace is the allowed difference between the least significant digits
+// of the numbers' floating point representation
+// Please read refernce paper before trying to use isUlpsEqual
+// http://www.cygnus-software.com/papers/comparingFloats/comparingFloats.htm
+inline bool
+isUlpsEqual(const double aLeft, const double aRight, const int64_t aUnitsInLastPlace)
+{
+ int64_t longLeft = doubleToInt64(aLeft);
+ // Because of 2's complement, must restore lexicographical order
+ if (longLeft < 0) {
+ longLeft = INT64_C(0x8000000000000000) - longLeft;
+ }
+
+ int64_t longRight = doubleToInt64(aRight);
+ // Because of 2's complement, must restore lexicographical order
+ if (longRight < 0) {
+ longRight = INT64_C(0x8000000000000000) - longRight;
+ }
+
+ int64_t difference = labs(longLeft - longRight);
+ return (difference <= aUnitsInLastPlace);
+}
+
+inline bool
+isUlpsEqual(const float aLeft, const float aRight, const int32_t aUnitsInLastPlace)
+{
+ int32_t intLeft = floatToInt32(aLeft);
+ // Because of 2's complement, must restore lexicographical order
+ if (intLeft < 0) {
+ intLeft = 0x80000000 - intLeft;
+ }
+
+ int32_t intRight = floatToInt32(aRight);
+ // Because of 2's complement, must restore lexicographical order
+ if (intRight < 0) {
+ intRight = 0x80000000 - intRight;
+ }
+
+ int32_t difference = abs(intLeft - intRight);
+ return (difference <= aUnitsInLastPlace);
+}
+
+// ==========> Pow <==================
+
+/// Return x to the power of two, i.e. x*x
+template<typename Type>
+inline Type Pow2(Type x)
+{
+ return x*x;
+}
+
+/// Return x to the power of three, i.e. x*x*x
+template<typename Type>
+inline Type Pow3(Type x)
+{
+ return x*x*x;
+}
+
+/// Return x to the power of four, i.e. x*x*x*x
+template<typename Type>
+inline Type Pow4(Type x)
+{
+ return Pow2(Pow2(x));
+}
+
+/// Return x to the power of n, i.e. x^n
+template <typename Type>
+Type Pow(Type x, int n)
+{
+ Type ans = 1;
+ if (n < 0) {
+ n = -n;
+ x = Type(1)/x;
+ }
+ while(n--) ans *= x;
+ return ans;
+}
+
+/// Return b to the power of e, i.e. b^e
+inline float Pow(float b, float e)
+{
+ assert( b >= 0.0f && "Pow(float,float): base is negative" );
+ return powf(b,e);
+}
+
+/// Return b to the power of e, i.e. b^e
+inline double Pow(double b, double e)
+{
+ assert( b >= 0.0 && "Pow(double,double): base is negative" );
+ return pow(b,e);
+}
+
+// ==========> Max <==================
+
+/// Return the maximum of two values
+template< typename Type >
+inline const Type& Max( const Type& a, const Type& b )
+{
+ return std::max(a,b) ;
+}
+
+/// Return the maximum of three values
+template< typename Type >
+inline const Type& Max( const Type& a, const Type& b, const Type& c )
+{
+ return std::max( std::max(a,b), c ) ;
+}
+
+/// Return the maximum of four values
+template< typename Type >
+inline const Type& Max( const Type& a, const Type& b, const Type& c, const Type& d )
+{
+ return std::max( std::max(a,b), std::max(c,d) ) ;
+}
+
+/// Return the maximum of five values
+template< typename Type >
+inline const Type& Max( const Type& a, const Type& b, const Type& c,
+ const Type& d, const Type& e )
+{
+ return std::max( std::max(a,b), Max(c,d,e) ) ;
+}
+
+/// Return the maximum of six values
+template< typename Type >
+inline const Type& Max( const Type& a, const Type& b, const Type& c,
+ const Type& d, const Type& e, const Type& f )
+{
+ return std::max( Max(a,b,c), Max(d,e,f) ) ;
+}
+
+/// Return the maximum of seven values
+template< typename Type >
+inline const Type& Max( const Type& a, const Type& b, const Type& c, const Type& d,
+ const Type& e, const Type& f, const Type& g )
+{
+ return std::max( Max(a,b,c,d), Max(e,f,g) ) ;
+}
+
+/// Return the maximum of eight values
+template< typename Type >
+inline const Type& Max( const Type& a, const Type& b, const Type& c, const Type& d,
+ const Type& e, const Type& f, const Type& g, const Type& h )
+{
+ return std::max( Max(a,b,c,d), Max(e,f,g,h) ) ;
+}
+
+// ==========> Min <==================
+
+/// Return the minimum of two values
+template< typename Type >
+inline const Type& Min( const Type& a, const Type& b )
+{
+ return std::min(a,b) ;
+}
+
+/// Return the minimum of three values
+template< typename Type >
+inline const Type& Min( const Type& a, const Type& b, const Type& c )
+{
+ return std::min( std::min(a,b), c ) ;
+}
+
+/// Return the minimum of four values
+template< typename Type >
+inline const Type& Min( const Type& a, const Type& b, const Type& c, const Type& d )
+{
+ return std::min( std::min(a,b), std::min(c,d) ) ;
+}
+
+/// Return the minimum of five values
+template< typename Type >
+inline const Type& Min( const Type& a, const Type& b, const Type& c,
+ const Type& d, const Type& e )
+{
+ return std::min( std::min(a,b), Min(c,d,e) ) ;
+}
+
+/// Return the minimum of six values
+template< typename Type >
+inline const Type& Min( const Type& a, const Type& b, const Type& c,
+ const Type& d, const Type& e, const Type& f )
+{
+ return std::min( Min(a,b,c), Min(d,e,f) ) ;
+}
+
+/// Return the minimum of seven values
+template< typename Type >
+inline const Type& Min( const Type& a, const Type& b, const Type& c, const Type& d,
+ const Type& e, const Type& f, const Type& g )
+{
+ return std::min( Min(a,b,c,d), Min(e,f,g) ) ;
+}
+
+/// Return the minimum of eight values
+template< typename Type >
+inline const Type& Min( const Type& a, const Type& b, const Type& c, const Type& d,
+ const Type& e, const Type& f, const Type& g, const Type& h )
+{
+ return std::min( Min(a,b,c,d), Min(e,f,g,h) ) ;
+}
+
+/// Return the sign of a variable as an integer. The three cases are -1, 0 or 1
+template <typename Type>
+inline int Sign(const Type &x) {return ( (x)<0 ? -1 : (x)==0 ? 0 : 1);}
+
+
+/// Return square-root of a floating point
+inline float Sqrt(float x) {return sqrtf(x);}
+inline double Sqrt(double x){return sqrt(x);}
+inline long double Sqrt(long double x) {return sqrtl(x);}
+
+
+/// Return remainder of x/y = Mod
+inline int Mod(int i, int j) {return (i%j);};
+inline float Mod(float x, float y) {return fmodf(x,y);}
+inline double Mod(double x, double y){return fmod(x,y);}
+inline long double Mod(long double x, long double y) {return fmodl(x,y);}
+
+/// Return reminder of x/y
+template <typename Type>
+inline Type Reminder(Type x, Type y) {return Mod(x,y);}
+
+/// Return round up to nearest integer or base
+inline float RoundUp(float x) { return ceilf(x); }
+inline double RoundUp(double x) { return ceil(x); }
+inline long double RoundUp(long double x) { return ceill(x); }
+template <typename Type>
+inline Type RoundUp(Type x, Type base)
+{
+ Type reminder=Reminder(x,base);
+ return reminder ? x-reminder+base : x;
+}
+
+
+/// Return rounds down to nearest integer or base
+inline float RoundDown(float x) { return floorf(x); }
+inline double RoundDown(double x){ return floor(x); }
+inline long double RoundDown(long double x) { return floorl(x); }
+template <typename Type>
+inline Type RoundDown(Type x, Type base)
+{
+ Type reminder=Reminder(x,base);
+ if (!reminder)
+ return x;
+ else
+ return x-reminder;
+}
+
+/// Return integer part
+template <typename Type>
+inline Type IntegerPart(Type x)
+{
+ return (x > 0 ? RoundDown(x) : RoundUp(x));
+}
+
+/// Return fractional part
+template <typename Type>
+inline Type FractionalPart(Type x) { return Mod(x,Type(1)); }
+
+/// Return floor
+inline int Floor(float x) { return (int)RoundDown(x); }
+inline int Floor(double x) { return (int)RoundDown(x); }
+inline int Floor(long double x) { return (int)RoundDown(x); }
+
+/// Return ceil
+inline int Ceil(float x) { return (int)RoundUp(x); }
+inline int Ceil(double x) { return (int)RoundUp(x); }
+inline int Ceil(long double x) { return (int)RoundUp(x); }
+
+/// Return rounds off x to nearest integer value
+template <typename Type>
+inline Type Round(Type x) { return RoundDown(x+0.5); }
+
+/// Return chop of x
+template <typename Type>
+inline Type Chop(Type x, Type delta) { return (Abs(x) < delta ? 0 : x); }
+
+/// Return truncation of x to smoe digits
+template <typename Type>
+inline Type Truncate(Type x,unsigned int digits)
+{
+ Type tenth=Pow(10,digits);
+ return RoundDown(x*tenth+0.5)/tenth;
+}
+
+/// Return inverse of x
+template <typename Type>
+inline Type Inv(Type x)
+{
+ assert(x);
+ return Type(1)/x;
+}
+
+
+enum Axis {
+ X_AXIS = 0,
+ Y_AXIS = 1,
+ Z_AXIS = 2
+};
+
+// enum values are consistent with their historical mx analogs.
+enum RotationOrder {
+ XYZ_ROTATION = 0,
+ XZY_ROTATION,
+ YXZ_ROTATION,
+ YZX_ROTATION,
+ ZXY_ROTATION,
+ ZYX_ROTATION,
+ XZX_ROTATION,
+ ZXZ_ROTATION
+};
+
+
+template <typename S, typename T>
+struct promote {
+ typedef typename boost::numeric::conversion_traits<S, T>::supertype type;
+};
+
+
+template<typename T> struct tolerance { static T value() { return 0; } };
+template<> struct tolerance<float> { static float value() { return 1e-8f; } };
+template<> struct tolerance<double> { static double value() { return 1e-15; } };
+
+} // namespace math
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_MATH_MATH_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/math/Operators.h b/extern/openvdb/internal/openvdb/math/Operators.h
new file mode 100644
index 00000000000..ce99ffb864f
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/math/Operators.h
@@ -0,0 +1,2086 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+//
+/// @file Operators.h
+
+#ifndef OPENVDB_MATH_OPERATORS_HAS_BEEN_INCLUDED
+#define OPENVDB_MATH_OPERATORS_HAS_BEEN_INCLUDED
+
+#include "FiniteDifference.h"
+#include "Stencils.h"
+#include "Maps.h"
+#include "Transform.h"
+
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace math {
+
+// Simple tools to help determine when type conversions are needed
+template<typename Vec3T> struct is_vec3d { static const bool value = false; };
+template<> struct is_vec3d<Vec3d> { static const bool value = true; };
+
+template<typename T> struct is_double { static const bool value = false; };
+template<> struct is_double<double> { static const bool value = true; };
+
+
+/// @brief Adapter to associate a map with a world-space operator,
+/// giving it the same call signature as an index-space operator
+/// @todo For now, the operator's result type must be specified explicitly,
+/// but eventually it should be possible, via traits, to derive the result type
+/// from the operator type.
+template<typename MapType, typename OpType, typename ResultType>
+struct MapAdapter {
+ MapAdapter(const MapType& m): map(m) {}
+
+ template<typename AccessorType>
+ inline ResultType
+ result(const AccessorType& grid, const Coord& ijk) { return OpType::result(map, grid, ijk); }
+
+ template<typename StencilType>
+ inline ResultType
+ result(const StencilType& stencil) { return OpType::result(map, stencil); }
+
+ const MapType map;
+};
+
+
+/// Adapter for vector-valued index-space operators to return the vector magnitude
+template<typename OpType>
+struct ISOpMagnitude {
+ template<typename AccessorType>
+ static inline double result(const AccessorType& grid, const Coord& ijk) {
+ return double(OpType::result(grid, ijk).length());
+ }
+
+ template<typename StencilType>
+ static inline double result(const StencilType& stencil) {
+ return double(OpType::result(stencil).length());
+ }
+};
+
+/// Adapter for vector-valued world-space operators to return the vector magnitude
+template<typename OpType, typename MapT>
+struct OpMagnitude {
+ template<typename AccessorType>
+ static inline double result(const MapT& map, const AccessorType& grid, const Coord& ijk) {
+ return double(OpType::result(map, grid, ijk).length());
+ }
+
+ template<typename StencilType>
+ static inline double result(const MapT& map, const StencilType& stencil) {
+ return double(OpType::result(map, stencil).length());
+ }
+};
+
+
+namespace internal {
+
+// This additional layer is necessary for Visual C++ to compile.
+template<typename T>
+struct ReturnValue {
+ typedef typename T::ValueType ValueType;
+ typedef math::Vec3<ValueType> Vec3Type;
+};
+
+} // namespace internal
+
+// ---- Operators defined in index space
+
+
+//@{
+/// @brief Gradient operators defined in index space of various orders
+template<DScheme DiffScheme>
+struct ISGradient
+{
+ // random access version
+ template<typename Accessor> static Vec3<typename Accessor::ValueType>
+ result(const Accessor& grid, const Coord& ijk)
+ {
+ typedef typename Accessor::ValueType ValueType;
+ typedef Vec3<ValueType> Vec3Type;
+ return Vec3Type( D1<DiffScheme>::inX(grid, ijk),
+ D1<DiffScheme>::inY(grid, ijk),
+ D1<DiffScheme>::inZ(grid, ijk) );
+ }
+
+ // stencil access version
+ template<typename StencilT> static Vec3<typename StencilT::ValueType>
+ result(const StencilT& stencil)
+ {
+ typedef typename StencilT::ValueType ValueType;
+ typedef Vec3<ValueType> Vec3Type;
+ return Vec3Type( D1<DiffScheme>::inX(stencil),
+ D1<DiffScheme>::inY(stencil),
+ D1<DiffScheme>::inZ(stencil) );
+ }
+};
+//@}
+
+/// struct that relates the BiasedGradientScheme to the
+/// forward and backward difference methods used, as well as to
+/// the correct stencil type for index space use
+template<BiasedGradientScheme bgs>
+struct BIAS_SCHEME {
+ static const DScheme FD = FD_1ST;
+ static const DScheme BD = BD_1ST;
+
+ template<typename GridType>
+ struct ISStencil {
+ typedef SevenPointStencil<GridType> StencilType;
+ };
+};
+
+template<> struct BIAS_SCHEME<FIRST_BIAS>
+{
+ static const DScheme FD = FD_1ST;
+ static const DScheme BD = BD_1ST;
+
+ template<typename GridType>
+ struct ISStencil {
+ typedef SevenPointStencil<GridType> StencilType;
+ };
+};
+
+template<> struct BIAS_SCHEME<SECOND_BIAS>
+{
+ static const DScheme FD = FD_2ND;
+ static const DScheme BD = BD_2ND;
+
+ template<typename GridType>
+ struct ISStencil {
+ typedef ThirteenPointStencil<GridType> StencilType;
+ };
+};
+template<> struct BIAS_SCHEME<THIRD_BIAS>
+{
+ static const DScheme FD = FD_3RD;
+ static const DScheme BD = BD_3RD;
+
+ template<typename GridType>
+ struct ISStencil {
+ typedef NineteenPointStencil<GridType> StencilType;
+ };
+};
+template<> struct BIAS_SCHEME<WENO5_BIAS>
+{
+ static const DScheme FD = FD_WENO5;
+ static const DScheme BD = BD_WENO5;
+
+ template<typename GridType>
+ struct ISStencil {
+ typedef NineteenPointStencil<GridType> StencilType;
+ };
+};
+template<> struct BIAS_SCHEME<HJWENO5_BIAS>
+{
+ static const DScheme FD = FD_HJWENO5;
+ static const DScheme BD = BD_HJWENO5;
+
+ template<typename GridType>
+ struct ISStencil {
+ typedef NineteenPointStencil<GridType> StencilType;
+ };
+};
+
+
+//@{
+/// @brief Biased Gradient Operators, using upwinding defined by the @c Vec3Bias input
+
+template<BiasedGradientScheme GradScheme, typename Vec3Bias>
+struct ISGradientBiased
+{
+ static const DScheme FD = BIAS_SCHEME<GradScheme>::FD;
+ static const DScheme BD = BIAS_SCHEME<GradScheme>::BD;
+
+ // random access version
+ template<typename Accessor> static Vec3<typename Accessor::ValueType>
+ result(const Accessor& grid, const Coord& ijk, const Vec3Bias& V)
+ {
+ typedef typename Accessor::ValueType ValueType;
+ typedef Vec3<ValueType> Vec3Type;
+
+ return Vec3Type(V[0]<0 ? D1<FD>::inX(grid,ijk) : D1<BD>::inX(grid,ijk),
+ V[1]<0 ? D1<FD>::inY(grid,ijk) : D1<BD>::inY(grid,ijk),
+ V[2]<0 ? D1<FD>::inZ(grid,ijk) : D1<BD>::inZ(grid,ijk) );
+ }
+
+ // stencil access version
+ template<typename StencilT> static Vec3<typename StencilT::ValueType>
+ result(const StencilT& stencil, const Vec3Bias& V)
+ {
+ typedef typename StencilT::ValueType ValueType;
+ typedef Vec3<ValueType> Vec3Type;
+
+ return Vec3Type(V[0]<0 ? D1<FD>::inX(stencil) : D1<BD>::inX(stencil),
+ V[1]<0 ? D1<FD>::inY(stencil) : D1<BD>::inY(stencil),
+ V[2]<0 ? D1<FD>::inZ(stencil) : D1<BD>::inZ(stencil) );
+ }
+};
+
+
+template<BiasedGradientScheme GradScheme>
+struct ISGradientNormSqrd
+{
+ static const DScheme FD = BIAS_SCHEME<GradScheme>::FD;
+ static const DScheme BD = BIAS_SCHEME<GradScheme>::BD;
+
+
+ // random access version
+ template<typename Accessor>
+ static typename Accessor::ValueType
+ result(const Accessor& grid, const Coord& ijk)
+ {
+ typedef typename Accessor::ValueType ValueType;
+ typedef math::Vec3<ValueType> Vec3Type;
+
+ Vec3Type up = ISGradient<FD>::result(grid, ijk);
+ Vec3Type down = ISGradient<BD>::result(grid, ijk);
+ return math::GudonovsNormSqrd(grid.getValue(ijk)>0, down, up);
+ }
+
+ // stencil access version
+ template<typename StencilT>
+ static typename StencilT::ValueType
+ result(const StencilT& stencil)
+ {
+ typedef typename StencilT::ValueType ValueType;
+ typedef math::Vec3<ValueType> Vec3Type;
+
+ Vec3Type up = ISGradient<FD>::result(stencil);
+ Vec3Type down = ISGradient<BD>::result(stencil);
+ return math::GudonovsNormSqrd(stencil.template getValue<0, 0, 0>()>0, down, up);
+ }
+};
+
+#ifdef DWA_OPENVDB // for SIMD - note will do the computations in float
+template<>
+struct ISGradientNormSqrd<HJWENO5_BIAS>
+{
+ // random access version
+ template<typename Accessor>
+ static typename Accessor::ValueType
+ result(const Accessor& grid, const Coord& ijk)
+ {
+ typedef typename Accessor::ValueType ValueType;
+ typedef math::Vec3<ValueType> Vec3Type;
+
+ // SSE optimized
+ const simd::Float4
+ v1(grid.getValue(ijk.offsetBy(-2, 0, 0)) - grid.getValue(ijk.offsetBy(-3, 0, 0)),
+ grid.getValue(ijk.offsetBy( 0,-2, 0)) - grid.getValue(ijk.offsetBy( 0,-3, 0)),
+ grid.getValue(ijk.offsetBy( 0, 0,-2)) - grid.getValue(ijk.offsetBy( 0, 0,-3)), 0),
+ v2(grid.getValue(ijk.offsetBy(-1, 0, 0)) - grid.getValue(ijk.offsetBy(-2, 0, 0)),
+ grid.getValue(ijk.offsetBy( 0,-1, 0)) - grid.getValue(ijk.offsetBy( 0,-2, 0)),
+ grid.getValue(ijk.offsetBy( 0, 0,-1)) - grid.getValue(ijk.offsetBy( 0, 0,-2)), 0),
+ v3(grid.getValue(ijk ) - grid.getValue(ijk.offsetBy(-1, 0, 0)),
+ grid.getValue(ijk ) - grid.getValue(ijk.offsetBy( 0,-1, 0)),
+ grid.getValue(ijk ) - grid.getValue(ijk.offsetBy( 0, 0,-1)), 0),
+ v4(grid.getValue(ijk.offsetBy( 1, 0, 0)) - grid.getValue(ijk ),
+ grid.getValue(ijk.offsetBy( 0, 1, 0)) - grid.getValue(ijk ),
+ grid.getValue(ijk.offsetBy( 0, 0, 1)) - grid.getValue(ijk ), 0),
+ v5(grid.getValue(ijk.offsetBy( 2, 0, 0)) - grid.getValue(ijk.offsetBy( 1, 0, 0)),
+ grid.getValue(ijk.offsetBy( 0, 2, 0)) - grid.getValue(ijk.offsetBy( 0, 1, 0)),
+ grid.getValue(ijk.offsetBy( 0, 0, 2)) - grid.getValue(ijk.offsetBy( 0, 0, 1)), 0),
+ v6(grid.getValue(ijk.offsetBy( 3, 0, 0)) - grid.getValue(ijk.offsetBy( 2, 0, 0)),
+ grid.getValue(ijk.offsetBy( 0, 3, 0)) - grid.getValue(ijk.offsetBy( 0, 2, 0)),
+ grid.getValue(ijk.offsetBy( 0, 0, 3)) - grid.getValue(ijk.offsetBy( 0, 0, 2)), 0),
+ down = math::WENO5(v1, v2, v3, v4, v5),
+ up = math::WENO5(v6, v5, v4, v3, v2);
+
+ return math::GudonovsNormSqrd(grid.getValue(ijk)>0, down, up);
+ }
+
+ // stencil access version
+ template<typename StencilT>
+ static typename StencilT::ValueType
+ result(const StencilT& s)
+ {
+ typedef typename StencilT::ValueType ValueType;
+ typedef math::Vec3<ValueType> Vec3Type;
+
+ // SSE optimized
+ const simd::Float4
+ v1(s.template getValue<-2, 0, 0>() - s.template getValue<-3, 0, 0>(),
+ s.template getValue< 0,-2, 0>() - s.template getValue< 0,-3, 0>(),
+ s.template getValue< 0, 0,-2>() - s.template getValue< 0, 0,-3>(), 0),
+ v2(s.template getValue<-1, 0, 0>() - s.template getValue<-2, 0, 0>(),
+ s.template getValue< 0,-1, 0>() - s.template getValue< 0,-2, 0>(),
+ s.template getValue< 0, 0,-1>() - s.template getValue< 0, 0,-2>(), 0),
+ v3(s.template getValue< 0, 0, 0>() - s.template getValue<-1, 0, 0>(),
+ s.template getValue< 0, 0, 0>() - s.template getValue< 0,-1, 0>(),
+ s.template getValue< 0, 0, 0>() - s.template getValue< 0, 0,-1>(), 0),
+ v4(s.template getValue< 1, 0, 0>() - s.template getValue< 0, 0, 0>(),
+ s.template getValue< 0, 1, 0>() - s.template getValue< 0, 0, 0>(),
+ s.template getValue< 0, 0, 1>() - s.template getValue< 0, 0, 0>(), 0),
+ v5(s.template getValue< 2, 0, 0>() - s.template getValue< 1, 0, 0>(),
+ s.template getValue< 0, 2, 0>() - s.template getValue< 0, 1, 0>(),
+ s.template getValue< 0, 0, 2>() - s.template getValue< 0, 0, 1>(), 0),
+ v6(s.template getValue< 3, 0, 0>() - s.template getValue< 2, 0, 0>(),
+ s.template getValue< 0, 3, 0>() - s.template getValue< 0, 2, 0>(),
+ s.template getValue< 0, 0, 3>() - s.template getValue< 0, 0, 2>(), 0),
+ down = math::WENO5(v1, v2, v3, v4, v5),
+ up = math::WENO5(v6, v5, v4, v3, v2);
+
+ return math::GudonovsNormSqrd(s.template getValue<0, 0, 0>()>0, down, up);
+ }
+};
+#endif //DWA_OPENVDB // for SIMD - note will do the computations in float
+//@}
+
+
+//@{
+/// @brief Laplacian defined in index space, using various center-difference stencils
+template<DDScheme DiffScheme>
+struct ISLaplacian
+{
+ // random access version
+ template<typename Accessor>
+ static typename Accessor::ValueType result(const Accessor& grid, const Coord& ijk);
+
+ // stencil access version
+ template<typename StencilT>
+ static typename StencilT::ValueType result(const StencilT& stencil);
+};
+
+
+template<>
+struct ISLaplacian<CD_SECOND>
+{
+ // random access version
+ template<typename Accessor>
+ static typename Accessor::ValueType result(const Accessor& grid, const Coord& ijk)
+ {
+ return grid.getValue(ijk.offsetBy(1,0,0)) + grid.getValue(ijk.offsetBy(-1, 0, 0)) +
+ grid.getValue(ijk.offsetBy(0,1,0)) + grid.getValue(ijk.offsetBy(0, -1, 0)) +
+ grid.getValue(ijk.offsetBy(0,0,1)) + grid.getValue(ijk.offsetBy(0, 0,-1))
+ - 6*grid.getValue(ijk);
+ }
+
+ // stencil access version
+ template<typename StencilT>
+ static typename StencilT::ValueType result(const StencilT& stencil)
+ {
+ return stencil.template getValue< 1, 0, 0>() + stencil.template getValue<-1, 0, 0>() +
+ stencil.template getValue< 0, 1, 0>() + stencil.template getValue< 0,-1, 0>() +
+ stencil.template getValue< 0, 0, 1>() + stencil.template getValue< 0, 0,-1>()
+ - 6*stencil.template getValue< 0, 0, 0>();
+ }
+};
+
+template<>
+struct ISLaplacian<CD_FOURTH>
+{
+ // random access version
+ template<typename Accessor>
+ static typename Accessor::ValueType result(const Accessor& grid, const Coord& ijk)
+ {
+ return (-1./12.)*(
+ grid.getValue(ijk.offsetBy(2,0,0)) + grid.getValue(ijk.offsetBy(-2, 0, 0)) +
+ grid.getValue(ijk.offsetBy(0,2,0)) + grid.getValue(ijk.offsetBy( 0,-2, 0)) +
+ grid.getValue(ijk.offsetBy(0,0,2)) + grid.getValue(ijk.offsetBy( 0, 0,-2)) )
+ + (4./3.)*(
+ grid.getValue(ijk.offsetBy(1,0,0)) + grid.getValue(ijk.offsetBy(-1, 0, 0)) +
+ grid.getValue(ijk.offsetBy(0,1,0)) + grid.getValue(ijk.offsetBy( 0,-1, 0)) +
+ grid.getValue(ijk.offsetBy(0,0,1)) + grid.getValue(ijk.offsetBy( 0, 0,-1)) )
+ - 7.5*grid.getValue(ijk);
+ }
+
+ // stencil access version
+ template<typename StencilT>
+ static typename StencilT::ValueType result(const StencilT& stencil)
+ {
+ return (-1./12.)*(
+ stencil.template getValue< 2, 0, 0>() + stencil.template getValue<-2, 0, 0>() +
+ stencil.template getValue< 0, 2, 0>() + stencil.template getValue< 0,-2, 0>() +
+ stencil.template getValue< 0, 0, 2>() + stencil.template getValue< 0, 0,-2>() )
+ + (4./3.)*(
+ stencil.template getValue< 1, 0, 0>() + stencil.template getValue<-1, 0, 0>() +
+ stencil.template getValue< 0, 1, 0>() + stencil.template getValue< 0,-1, 0>() +
+ stencil.template getValue< 0, 0, 1>() + stencil.template getValue< 0, 0,-1>() )
+ - 7.5*stencil.template getValue< 0, 0, 0>();
+ }
+};
+
+template<>
+struct ISLaplacian<CD_SIXTH>
+{
+ // random access version
+ template<typename Accessor>
+ static typename Accessor::ValueType result(const Accessor& grid, const Coord& ijk)
+ {
+ return (1./90.)*(
+ grid.getValue(ijk.offsetBy(3,0,0)) + grid.getValue(ijk.offsetBy(-3, 0, 0)) +
+ grid.getValue(ijk.offsetBy(0,3,0)) + grid.getValue(ijk.offsetBy( 0,-3, 0)) +
+ grid.getValue(ijk.offsetBy(0,0,3)) + grid.getValue(ijk.offsetBy( 0, 0,-3)) )
+ - (3./20.)*(
+ grid.getValue(ijk.offsetBy(2,0,0)) + grid.getValue(ijk.offsetBy(-2, 0, 0)) +
+ grid.getValue(ijk.offsetBy(0,2,0)) + grid.getValue(ijk.offsetBy( 0,-2, 0)) +
+ grid.getValue(ijk.offsetBy(0,0,2)) + grid.getValue(ijk.offsetBy( 0, 0,-2)) )
+ + 1.5 *(
+ grid.getValue(ijk.offsetBy(1,0,0)) + grid.getValue(ijk.offsetBy(-1, 0, 0)) +
+ grid.getValue(ijk.offsetBy(0,1,0)) + grid.getValue(ijk.offsetBy( 0,-1, 0)) +
+ grid.getValue(ijk.offsetBy(0,0,1)) + grid.getValue(ijk.offsetBy( 0, 0,-1)) )
+ - (3*49/18.)*grid.getValue(ijk);
+ }
+
+ // stencil access version
+ template<typename StencilT>
+ static typename StencilT::ValueType result(const StencilT& stencil)
+ {
+ return (1./90.)*(
+ stencil.template getValue< 3, 0, 0>() + stencil.template getValue<-3, 0, 0>() +
+ stencil.template getValue< 0, 3, 0>() + stencil.template getValue< 0,-3, 0>() +
+ stencil.template getValue< 0, 0, 3>() + stencil.template getValue< 0, 0,-3>() )
+ - (3./20.)*(
+ stencil.template getValue< 2, 0, 0>() + stencil.template getValue<-2, 0, 0>() +
+ stencil.template getValue< 0, 2, 0>() + stencil.template getValue< 0,-2, 0>() +
+ stencil.template getValue< 0, 0, 2>() + stencil.template getValue< 0, 0,-2>() )
+ + 1.5 *(
+ stencil.template getValue< 1, 0, 0>() + stencil.template getValue<-1, 0, 0>() +
+ stencil.template getValue< 0, 1, 0>() + stencil.template getValue< 0,-1, 0>() +
+ stencil.template getValue< 0, 0, 1>() + stencil.template getValue< 0, 0,-1>() )
+ - (3*49/18.)*stencil.template getValue< 0, 0, 0>();
+ }
+};
+//@}
+
+
+//@{
+/// Divergence operator defined in index space using various first derivative schemes
+template<DScheme DiffScheme>
+struct ISDivergence
+{
+ // random access version
+ template<typename Accessor> static typename Accessor::ValueType::value_type
+ result(const Accessor& grid, const Coord& ijk)
+ {
+ return D1Vec<DiffScheme>::inX(grid, ijk, 0) +
+ D1Vec<DiffScheme>::inY(grid, ijk, 1) +
+ D1Vec<DiffScheme>::inZ(grid, ijk, 2);
+ }
+
+ // stencil access version
+ template<typename StencilT> static typename StencilT::ValueType::value_type
+ result(const StencilT& stencil)
+ {
+ return D1Vec<DiffScheme>::inX(stencil, 0) +
+ D1Vec<DiffScheme>::inY(stencil, 1) +
+ D1Vec<DiffScheme>::inZ(stencil, 2);
+ }
+};
+//@}
+
+
+//@{
+/// Curl operator defined in index space using various first derivative schemes
+template<DScheme DiffScheme>
+struct ISCurl
+{
+ // random access version
+ template<typename Accessor>
+ static typename Accessor::ValueType result(const Accessor& grid, const Coord& ijk)
+ {
+ typedef typename Accessor::ValueType Vec3Type;
+ return Vec3Type( D1Vec<DiffScheme>::inY(grid, ijk, 2) - //dw/dy - dv/dz
+ D1Vec<DiffScheme>::inZ(grid, ijk, 1),
+ D1Vec<DiffScheme>::inZ(grid, ijk, 0) - //du/dz - dw/dx
+ D1Vec<DiffScheme>::inX(grid, ijk, 2),
+ D1Vec<DiffScheme>::inX(grid, ijk, 1) - //dv/dx - du/dy
+ D1Vec<DiffScheme>::inY(grid, ijk, 0) );
+ }
+
+ // stencil access version
+ template<typename StencilT>
+ static typename StencilT::ValueType result(const StencilT& stencil)
+ {
+ typedef typename StencilT::ValueType Vec3Type;
+ return Vec3Type( D1Vec<DiffScheme>::inY(stencil, 2) - //dw/dy - dv/dz
+ D1Vec<DiffScheme>::inZ(stencil, 1),
+ D1Vec<DiffScheme>::inZ(stencil, 0) - //du/dz - dw/dx
+ D1Vec<DiffScheme>::inX(stencil, 2),
+ D1Vec<DiffScheme>::inX(stencil, 1) - //dv/dx - du/dy
+ D1Vec<DiffScheme>::inY(stencil, 0) );
+ }
+};
+//@}
+
+
+//@{
+/// Compute the mean curvature in index space
+template<DDScheme DiffScheme2, DScheme DiffScheme1>
+struct ISMeanCurvature
+{
+ // random access version
+ template<typename Accessor>
+ static void result(const Accessor& grid, const Coord& ijk,
+ typename Accessor::ValueType& alpha,
+ typename Accessor::ValueType& beta)
+ {
+ typedef typename Accessor::ValueType ValueType;
+
+ ValueType Dx = D1<DiffScheme1>::inX(grid, ijk);
+ ValueType Dy = D1<DiffScheme1>::inY(grid, ijk);
+ ValueType Dz = D1<DiffScheme1>::inZ(grid, ijk);
+
+ ValueType Dx2 = Dx*Dx;
+ ValueType Dy2 = Dy*Dy;
+ ValueType Dz2 = Dz*Dz;
+
+ ValueType Dxx = D2<DiffScheme2>::inX(grid, ijk);
+ ValueType Dyy = D2<DiffScheme2>::inY(grid, ijk);
+ ValueType Dzz = D2<DiffScheme2>::inZ(grid, ijk);
+
+ ValueType Dxy = D2<DiffScheme2>::inXandY(grid, ijk);
+ ValueType Dyz = D2<DiffScheme2>::inYandZ(grid, ijk);
+ ValueType Dxz = D2<DiffScheme2>::inXandZ(grid, ijk);
+
+ // for return
+ alpha = (Dx2*(Dyy+Dzz)+Dy2*(Dxx+Dzz)+Dz2*(Dxx+Dyy)-2*(Dx*(Dy*Dxy+Dz*Dxz)+Dy*Dz*Dyz));
+ beta = ValueType(std::sqrt(double(Dx2 + Dy2 + Dz2))); // * 1/dx
+ }
+
+ // stencil access version
+ template<typename StencilT>
+ static void result(const StencilT& stencil,
+ typename StencilT::ValueType& alpha,
+ typename StencilT::ValueType& beta)
+ {
+ typedef typename StencilT::ValueType ValueType;
+ ValueType Dx = D1<DiffScheme1>::inX(stencil);
+ ValueType Dy = D1<DiffScheme1>::inY(stencil);
+ ValueType Dz = D1<DiffScheme1>::inZ(stencil);
+
+ ValueType Dx2 = Dx*Dx;
+ ValueType Dy2 = Dy*Dy;
+ ValueType Dz2 = Dz*Dz;
+
+ ValueType Dxx = D2<DiffScheme2>::inX(stencil);
+ ValueType Dyy = D2<DiffScheme2>::inY(stencil);
+ ValueType Dzz = D2<DiffScheme2>::inZ(stencil);
+
+ ValueType Dxy = D2<DiffScheme2>::inXandY(stencil);
+ ValueType Dyz = D2<DiffScheme2>::inYandZ(stencil);
+ ValueType Dxz = D2<DiffScheme2>::inXandZ(stencil);
+
+ // for return
+ alpha = (Dx2*(Dyy+Dzz)+Dy2*(Dxx+Dzz)+Dz2*(Dxx+Dyy)-2*(Dx*(Dy*Dxy+Dz*Dxz)+Dy*Dz*Dyz));
+ beta = ValueType(std::sqrt(double(Dx2 + Dy2 + Dz2))); // * 1/dx
+ }
+};
+
+
+// --- Operators defined in the Range of a given map
+
+
+//@{
+/// @brief Center difference gradient operators, defined with respect to
+/// the range-space of the @c map
+/// @note This will need to be divided by two in the case of CD_2NDT
+template<typename MapType, DScheme DiffScheme>
+struct Gradient
+{
+ // random access version
+ template<typename Accessor>
+ static typename internal::ReturnValue<Accessor>::Vec3Type
+ result(const MapType& map, const Accessor& grid, const Coord& ijk)
+ {
+ typedef typename internal::ReturnValue<Accessor>::Vec3Type Vec3Type;
+
+ Vec3d iGradient( ISGradient<DiffScheme>::result(grid, ijk) );
+ return Vec3Type(map.applyIJT(iGradient, ijk.asVec3d()));
+ }
+
+ // stencil access version
+ template<typename StencilT>
+ static typename internal::ReturnValue<StencilT>::Vec3Type
+ result(const MapType& map, const StencilT& stencil)
+ {
+ typedef typename internal::ReturnValue<StencilT>::Vec3Type Vec3Type;
+
+ Vec3d iGradient( ISGradient<DiffScheme>::result(stencil) );
+ return Vec3Type(map.applyIJT(iGradient, stencil.getCenterCoord().asVec3d()));
+ }
+};
+
+
+// translation, any order
+template<DScheme DiffScheme>
+struct Gradient<TranslationMap, DiffScheme>
+{
+ // random access version
+ template<typename Accessor>
+ static typename internal::ReturnValue<Accessor>::Vec3Type
+ result(const TranslationMap&, const Accessor& grid, const Coord& ijk)
+ {
+ return ISGradient<DiffScheme>::result(grid, ijk);
+ }
+
+ // stencil access version
+ template<typename StencilT>
+ static typename internal::ReturnValue<StencilT>::Vec3Type
+ result(const TranslationMap&, const StencilT& stencil)
+ {
+ return ISGradient<DiffScheme>::result(stencil);
+ }
+};
+
+
+// uniform scale, 2nd order
+template<>
+struct Gradient<UniformScaleMap, CD_2ND>
+{
+ // random access version
+ template<typename Accessor>
+ static typename internal::ReturnValue<Accessor>::Vec3Type
+ result(const UniformScaleMap& map, const Accessor& grid, const Coord& ijk)
+ {
+ typedef typename internal::ReturnValue<Accessor>::ValueType ValueType;
+ typedef typename internal::ReturnValue<Accessor>::Vec3Type Vec3Type;
+
+ Vec3Type iGradient( ISGradient<CD_2NDT>::result(grid, ijk) );
+ ValueType inv2dx = ValueType(map.getInvTwiceScale()[0]);
+ return iGradient * inv2dx;
+ }
+
+ // stencil access version
+ template<typename StencilT>
+ static typename internal::ReturnValue<StencilT>::Vec3Type
+ result(const UniformScaleMap& map, const StencilT& stencil)
+ {
+ typedef typename internal::ReturnValue<StencilT>::ValueType ValueType;
+ typedef typename internal::ReturnValue<StencilT>::Vec3Type Vec3Type;
+
+ Vec3Type iGradient( ISGradient<CD_2NDT>::result(stencil) );
+ ValueType inv2dx = ValueType(map.getInvTwiceScale()[0]);
+ return iGradient * inv2dx;
+ }
+};
+
+
+// uniform scale translate, 2nd order
+template<>
+struct Gradient<UniformScaleTranslateMap, CD_2ND>
+{
+ // random access version
+ template<typename Accessor>
+ static typename internal::ReturnValue<Accessor>::Vec3Type
+ result(const UniformScaleTranslateMap& map, const Accessor& grid, const Coord& ijk)
+ {
+ typedef typename internal::ReturnValue<Accessor>::ValueType ValueType;
+ typedef typename internal::ReturnValue<Accessor>::Vec3Type Vec3Type;
+
+ Vec3Type iGradient( ISGradient<CD_2NDT>::result(grid, ijk) );
+ ValueType inv2dx = ValueType(map.getInvTwiceScale()[0]);
+ return iGradient * inv2dx;
+ }
+
+ // stencil access version
+ template<typename StencilT>
+ static typename internal::ReturnValue<StencilT>::Vec3Type
+ result(const UniformScaleTranslateMap& map, const StencilT& stencil)
+ {
+ typedef typename internal::ReturnValue<StencilT>::ValueType ValueType;
+ typedef typename internal::ReturnValue<StencilT>::Vec3Type Vec3Type;
+
+ Vec3Type iGradient( ISGradient<CD_2NDT>::result(stencil) );
+ ValueType inv2dx = ValueType(map.getInvTwiceScale()[0]);
+ return iGradient * inv2dx;
+ }
+};
+
+
+// scale, 2nd order
+template<>
+struct Gradient<ScaleMap, CD_2ND>
+{
+ // random access version
+ template<typename Accessor>
+ static typename internal::ReturnValue<Accessor>::Vec3Type
+ result(const ScaleMap& map, const Accessor& grid, const Coord& ijk)
+ {
+ typedef typename internal::ReturnValue<Accessor>::ValueType ValueType;
+ typedef typename internal::ReturnValue<Accessor>::Vec3Type Vec3Type;
+
+ Vec3Type iGradient( ISGradient<CD_2NDT>::result(grid, ijk) );
+ return Vec3Type(ValueType(iGradient[0] * map.getInvTwiceScale()[0]),
+ ValueType(iGradient[1] * map.getInvTwiceScale()[1]),
+ ValueType(iGradient[2] * map.getInvTwiceScale()[2]) );
+ }
+
+ // stencil access version
+ template<typename StencilT>
+ static typename internal::ReturnValue<StencilT>::Vec3Type
+ result(const ScaleMap& map, const StencilT& stencil)
+ {
+ typedef typename internal::ReturnValue<StencilT>::ValueType ValueType;
+ typedef typename internal::ReturnValue<StencilT>::Vec3Type Vec3Type;
+
+ Vec3Type iGradient( ISGradient<CD_2NDT>::result(stencil) );
+ return Vec3Type(ValueType(iGradient[0] * map.getInvTwiceScale()[0]),
+ ValueType(iGradient[1] * map.getInvTwiceScale()[1]),
+ ValueType(iGradient[2] * map.getInvTwiceScale()[2]) );
+ }
+};
+
+
+// scale translate, 2nd order
+template<>
+struct Gradient<ScaleTranslateMap, CD_2ND>
+{
+ // random access version
+ template<typename Accessor>
+ static typename internal::ReturnValue<Accessor>::Vec3Type
+ result(const ScaleTranslateMap& map, const Accessor& grid, const Coord& ijk)
+ {
+ typedef typename internal::ReturnValue<Accessor>::ValueType ValueType;
+ typedef typename internal::ReturnValue<Accessor>::Vec3Type Vec3Type;
+
+ Vec3Type iGradient( ISGradient<CD_2NDT>::result(grid, ijk) );
+ return Vec3Type(ValueType(iGradient[0] * map.getInvTwiceScale()[0]),
+ ValueType(iGradient[1] * map.getInvTwiceScale()[1]),
+ ValueType(iGradient[2] * map.getInvTwiceScale()[2]) );
+ }
+
+ // Stencil access version
+ template<typename StencilT>
+ static typename internal::ReturnValue<StencilT>::Vec3Type
+ result(const ScaleTranslateMap& map, const StencilT& stencil)
+ {
+ typedef typename internal::ReturnValue<StencilT>::ValueType ValueType;
+ typedef typename internal::ReturnValue<StencilT>::Vec3Type Vec3Type;
+
+ Vec3Type iGradient( ISGradient<CD_2NDT>::result(stencil) );
+ return Vec3Type(ValueType(iGradient[0] * map.getInvTwiceScale()[0]),
+ ValueType(iGradient[1] * map.getInvTwiceScale()[1]),
+ ValueType(iGradient[2] * map.getInvTwiceScale()[2]) );
+ }
+};
+//@}
+
+
+//@{
+/// @brief Biased gradient operators, defined with respect to the range-space of the map
+/// @note This will need to be divided by two in the case of CD_2NDT
+template<typename MapType, BiasedGradientScheme GradScheme>
+struct GradientBiased
+{
+ // random access version
+ template<typename Accessor> static math::Vec3<typename Accessor::ValueType>
+ result(const MapType& map, const Accessor& grid, const Coord& ijk,
+ const Vec3<typename Accessor::ValueType>& V)
+ {
+ typedef typename Accessor::ValueType ValueType;
+ typedef math::Vec3<ValueType> Vec3Type;
+
+ Vec3d iGradient( ISGradientBiased<GradScheme, Vec3Type>::result(grid, ijk, V) );
+ return Vec3Type(map.applyIJT(iGradient, ijk.asVec3d()));
+ }
+
+ // stencil access version
+ template<typename StencilT> static math::Vec3<typename StencilT::ValueType>
+ result(const MapType& map, const StencilT& stencil,
+ const Vec3<typename StencilT::ValueType>& V)
+ {
+ typedef typename StencilT::ValueType ValueType;
+ typedef math::Vec3<ValueType> Vec3Type;
+
+ Vec3d iGradient( ISGradientBiased<GradScheme, Vec3Type>::result(stencil, V) );
+ return Vec3Type(map.applyIJT(iGradient, stencil.getCenterCoord().asVec3d()));
+ }
+};
+//@}
+
+
+template<typename MapType, BiasedGradientScheme GradScheme>
+struct GradientNormSqrd
+{
+ static const DScheme FD = BIAS_SCHEME<GradScheme>::FD;
+ static const DScheme BD = BIAS_SCHEME<GradScheme>::BD;
+
+
+ // random access version
+ template<typename Accessor>
+ static typename Accessor::ValueType
+ result(const MapType& map, const Accessor& grid, const Coord& ijk)
+ {
+ typedef typename Accessor::ValueType ValueType;
+ typedef math::Vec3<ValueType> Vec3Type;
+
+ Vec3Type up = Gradient<MapType, FD>::result(map, grid, ijk);
+ Vec3Type down = Gradient<MapType, BD>::result(map, grid, ijk);
+ return math::GudonovsNormSqrd(grid.getValue(ijk)>0, down, up);
+ }
+
+ // stencil access version
+ template<typename StencilT>
+ static typename StencilT::ValueType
+ result(const MapType& map, const StencilT& stencil)
+ {
+ typedef typename StencilT::ValueType ValueType;
+ typedef math::Vec3<ValueType> Vec3Type;
+
+ Vec3Type up = Gradient<MapType, FD>::result(map, stencil);
+ Vec3Type down = Gradient<MapType, BD>::result(map, stencil);
+ return math::GudonovsNormSqrd(stencil.template getValue<0, 0, 0>()>0, down, up);
+ }
+};
+
+
+template<BiasedGradientScheme GradScheme>
+struct GradientNormSqrd<UniformScaleMap, GradScheme>
+{
+ // random access version
+ template<typename Accessor>
+ static typename Accessor::ValueType
+ result(const UniformScaleMap& map, const Accessor& grid, const Coord& ijk)
+ {
+ typedef typename Accessor::ValueType ValueType;
+
+ ValueType invdxdx = ValueType(map.getInvScaleSqr()[0]);
+ return invdxdx * ISGradientNormSqrd<GradScheme>::result(grid, ijk);
+ }
+
+ // stencil access version
+ template<typename StencilT>
+ static typename StencilT::ValueType
+ result(const UniformScaleMap& map, const StencilT& stencil)
+ {
+ typedef typename StencilT::ValueType ValueType;
+
+ ValueType invdxdx = ValueType(map.getInvScaleSqr()[0]);
+ return invdxdx * ISGradientNormSqrd<GradScheme>::result(stencil);
+ }
+};
+
+template<BiasedGradientScheme GradScheme>
+struct GradientNormSqrd<UniformScaleTranslateMap, GradScheme>
+{
+ // random access version
+ template<typename Accessor>
+ static typename Accessor::ValueType
+ result(const UniformScaleTranslateMap& map, const Accessor& grid, const Coord& ijk)
+ {
+ typedef typename Accessor::ValueType ValueType;
+
+ ValueType invdxdx = ValueType(map.getInvScaleSqr()[0]);
+ return invdxdx * ISGradientNormSqrd<GradScheme>::result(grid, ijk);
+ }
+
+ // stencil access version
+ template<typename StencilT>
+ static typename StencilT::ValueType
+ result(const UniformScaleTranslateMap& map, const StencilT& stencil)
+ {
+ typedef typename StencilT::ValueType ValueType;
+
+ ValueType invdxdx = ValueType(map.getInvScaleSqr()[0]);
+ return invdxdx * ISGradientNormSqrd<GradScheme>::result(stencil);
+ }
+};
+
+
+//@{
+/// @brief Compute the divergence of a vector-valued grid using differencing
+/// of various orders, the result defined with respect to the range-space of the map.
+template<typename MapType, DScheme DiffScheme>
+struct Divergence
+{
+ // random access version
+ template<typename Accessor> static typename Accessor::ValueType::value_type
+ result(const MapType& map, const Accessor& grid, const Coord& ijk)
+ {
+ typedef typename Accessor::ValueType Vec3Type;
+ typedef typename Accessor::ValueType::value_type ValueType;
+
+ ValueType div(0);
+ for (int i=0; i < 3; i++) {
+ Vec3d vec( D1Vec<DiffScheme>::inX(grid, ijk, i),
+ D1Vec<DiffScheme>::inY(grid, ijk, i),
+ D1Vec<DiffScheme>::inZ(grid, ijk, i) );
+ div += ValueType(map.applyIJT(vec, ijk.asVec3d())[i]);
+ }
+ return div;
+ }
+
+ // stencil access version
+ template<typename StencilT> static typename StencilT::ValueType::value_type
+ result(const MapType& map, const StencilT& stencil)
+ {
+ typedef typename StencilT::ValueType Vec3Type;
+ typedef typename StencilT::ValueType::value_type ValueType;
+
+ ValueType div(0);
+ for (int i=0; i < 3; i++) {
+ Vec3d vec( D1Vec<DiffScheme>::inX(stencil, i),
+ D1Vec<DiffScheme>::inY(stencil, i),
+ D1Vec<DiffScheme>::inZ(stencil, i) );
+ div += ValueType(map.applyIJT(vec, stencil.getCenterCoord().asVec3d())[i]);
+ }
+ return div;
+ }
+};
+
+
+// translation, any scheme
+template<DScheme DiffScheme>
+struct Divergence<TranslationMap, DiffScheme>
+{
+ // random access version
+ template<typename Accessor> static typename Accessor::ValueType::value_type
+ result(const TranslationMap&, const Accessor& grid, const Coord& ijk)
+ {
+ typedef typename Accessor::ValueType Vec3Type;
+ typedef typename Accessor::ValueType::value_type ValueType;
+
+ ValueType div(0);
+ div =ISDivergence<DiffScheme>::result(grid, ijk);
+ return div;
+ }
+
+ // stencil access version
+ template<typename StencilT> static typename StencilT::ValueType::value_type
+ result(const TranslationMap&, const StencilT& stencil)
+ {
+ typedef typename StencilT::ValueType Vec3Type;
+ typedef typename StencilT::ValueType::value_type ValueType;
+
+ ValueType div(0);
+ div =ISDivergence<DiffScheme>::result(stencil);
+ return div;
+ }
+};
+
+
+// uniform scale, any scheme
+template<DScheme DiffScheme>
+struct Divergence<UniformScaleMap, DiffScheme>
+{
+ // random access version
+ template<typename Accessor> static typename Accessor::ValueType::value_type
+ result(const UniformScaleMap& map, const Accessor& grid, const Coord& ijk)
+ {
+ typedef typename Accessor::ValueType Vec3Type;
+ typedef typename Accessor::ValueType::value_type ValueType;
+
+ ValueType div(0);
+
+ div =ISDivergence<DiffScheme>::result(grid, ijk);
+ ValueType invdx = ValueType(map.getInvScale()[0]);
+ return div * invdx;
+ }
+
+ // stencil access version
+ template<typename StencilT> static typename StencilT::ValueType::value_type
+ result(const UniformScaleMap& map, const StencilT& stencil)
+ {
+ typedef typename StencilT::ValueType Vec3Type;
+ typedef typename StencilT::ValueType::value_type ValueType;
+
+ ValueType div(0);
+
+ div =ISDivergence<DiffScheme>::result(stencil);
+ ValueType invdx = ValueType(map.getInvScale()[0]);
+ return div * invdx;
+ }
+};
+
+
+// uniform scale and translation, any scheme
+template<DScheme DiffScheme>
+struct Divergence<UniformScaleTranslateMap, DiffScheme>
+{
+ // random access version
+ template<typename Accessor> static typename Accessor::ValueType::value_type
+ result(const UniformScaleTranslateMap& map, const Accessor& grid, const Coord& ijk)
+ {
+ typedef typename Accessor::ValueType Vec3Type;
+ typedef typename Accessor::ValueType::value_type ValueType;
+
+ ValueType div(0);
+
+ div =ISDivergence<DiffScheme>::result(grid, ijk);
+ ValueType invdx = ValueType(map.getInvScale()[0]);
+ return div * invdx;
+ }
+
+ // stencil access version
+ template<typename StencilT> static typename StencilT::ValueType::value_type
+ result(const UniformScaleTranslateMap& map, const StencilT& stencil)
+ {
+ typedef typename StencilT::ValueType Vec3Type;
+ typedef typename StencilT::ValueType::value_type ValueType;
+
+ ValueType div(0);
+
+ div =ISDivergence<DiffScheme>::result(stencil);
+ ValueType invdx = ValueType(map.getInvScale()[0]);
+ return div * invdx;
+ }
+};
+
+
+// uniform scale 2nd order
+template<>
+struct Divergence<UniformScaleMap, CD_2ND>
+{
+ // random access version
+ template<typename Accessor> static typename Accessor::ValueType::value_type
+ result(const UniformScaleMap& map, const Accessor& grid, const Coord& ijk)
+ {
+ typedef typename Accessor::ValueType Vec3Type;
+ typedef typename Accessor::ValueType::value_type ValueType;
+
+ ValueType div(0);
+ div =ISDivergence<CD_2NDT>::result(grid, ijk);
+ ValueType inv2dx = ValueType(map.getInvTwiceScale()[0]);
+ return div * inv2dx;
+ }
+
+ // stencil access version
+ template<typename StencilT> static typename StencilT::ValueType::value_type
+ result(const UniformScaleMap& map, const StencilT& stencil)
+ {
+ typedef typename StencilT::ValueType Vec3Type;
+ typedef typename StencilT::ValueType::value_type ValueType;
+
+ ValueType div(0);
+ div =ISDivergence<CD_2NDT>::result(stencil);
+ ValueType inv2dx = ValueType(map.getInvTwiceScale()[0]);
+ return div * inv2dx;
+ }
+};
+
+
+// uniform scale translate 2nd order
+template<>
+struct Divergence<UniformScaleTranslateMap, CD_2ND>
+{
+ // random access version
+ template<typename Accessor> static typename Accessor::ValueType::value_type
+ result(const UniformScaleTranslateMap& map, const Accessor& grid, const Coord& ijk)
+ {
+ typedef typename Accessor::ValueType Vec3Type;
+ typedef typename Accessor::ValueType::value_type ValueType;
+
+ ValueType div(0);
+
+ div =ISDivergence<CD_2NDT>::result(grid, ijk);
+ ValueType inv2dx = ValueType(map.getInvTwiceScale()[0]);
+ return div * inv2dx;
+ }
+
+ // stencil access version
+ template<typename StencilT> static typename StencilT::ValueType::value_type
+ result(const UniformScaleTranslateMap& map, const StencilT& stencil)
+ {
+ typedef typename StencilT::ValueType Vec3Type;
+ typedef typename StencilT::ValueType::value_type ValueType;
+
+ ValueType div(0);
+
+ div =ISDivergence<CD_2NDT>::result(stencil);
+ ValueType inv2dx = ValueType(map.getInvTwiceScale()[0]);
+ return div * inv2dx;
+ }
+};
+
+
+// scale, any scheme
+template<DScheme DiffScheme>
+struct Divergence<ScaleMap, DiffScheme>
+{
+ // random access version
+ template<typename Accessor> static typename Accessor::ValueType::value_type
+ result(const ScaleMap& map, const Accessor& grid, const Coord& ijk)
+ {
+ typedef typename Accessor::ValueType Vec3Type;
+ typedef typename Accessor::ValueType::value_type ValueType;
+
+ ValueType div = ValueType(
+ D1Vec<DiffScheme>::inX(grid, ijk, 0) * (map.getInvScale()[0]) +
+ D1Vec<DiffScheme>::inY(grid, ijk, 1) * (map.getInvScale()[1]) +
+ D1Vec<DiffScheme>::inZ(grid, ijk, 2) * (map.getInvScale()[2]));
+ return div;
+ }
+
+ // stencil access version
+ template<typename StencilT> static typename StencilT::ValueType::value_type
+ result(const ScaleMap& map, const StencilT& stencil)
+ {
+ typedef typename StencilT::ValueType Vec3Type;
+ typedef typename StencilT::ValueType::value_type ValueType;
+
+ ValueType div(0);
+ div = ValueType(
+ D1Vec<DiffScheme>::inX(stencil, 0) * (map.getInvScale()[0]) +
+ D1Vec<DiffScheme>::inY(stencil, 1) * (map.getInvScale()[1]) +
+ D1Vec<DiffScheme>::inZ(stencil, 2) * (map.getInvScale()[2]) );
+ return div;
+ }
+};
+
+
+// scale translate, any scheme
+template<DScheme DiffScheme>
+struct Divergence<ScaleTranslateMap, DiffScheme>
+{
+ // random access version
+ template<typename Accessor> static typename Accessor::ValueType::value_type
+ result(const ScaleTranslateMap& map, const Accessor& grid, const Coord& ijk)
+ {
+ typedef typename Accessor::ValueType Vec3Type;
+ typedef typename Accessor::ValueType::value_type ValueType;
+
+ ValueType div = ValueType(
+ D1Vec<DiffScheme>::inX(grid, ijk, 0) * (map.getInvScale()[0]) +
+ D1Vec<DiffScheme>::inY(grid, ijk, 1) * (map.getInvScale()[1]) +
+ D1Vec<DiffScheme>::inZ(grid, ijk, 2) * (map.getInvScale()[2]));
+ return div;
+ }
+
+ // stencil access version
+ template<typename StencilT> static typename StencilT::ValueType::value_type
+ result(const ScaleTranslateMap& map, const StencilT& stencil)
+ {
+ typedef typename StencilT::ValueType Vec3Type;
+ typedef typename StencilT::ValueType::value_type ValueType;
+
+ ValueType div(0);
+ div = ValueType(
+ D1Vec<DiffScheme>::inX(stencil, 0) * (map.getInvScale()[0]) +
+ D1Vec<DiffScheme>::inY(stencil, 1) * (map.getInvScale()[1]) +
+ D1Vec<DiffScheme>::inZ(stencil, 2) * (map.getInvScale()[2]) );
+ return div;
+ }
+};
+
+
+// scale 2nd order
+template<>
+struct Divergence<ScaleMap, CD_2ND>
+{
+ // random access version
+ template<typename Accessor> static typename Accessor::ValueType::value_type
+ result(const ScaleMap& map, const Accessor& grid, const Coord& ijk)
+ {
+ typedef typename Accessor::ValueType Vec3Type;
+ typedef typename Accessor::ValueType::value_type ValueType;
+
+ ValueType div = ValueType(
+ D1Vec<CD_2NDT>::inX(grid, ijk, 0) * (map.getInvTwiceScale()[0]) +
+ D1Vec<CD_2NDT>::inY(grid, ijk, 1) * (map.getInvTwiceScale()[1]) +
+ D1Vec<CD_2NDT>::inZ(grid, ijk, 2) * (map.getInvTwiceScale()[2]) );
+ return div;
+ }
+
+ // stencil access version
+ template<typename StencilT> static typename StencilT::ValueType::value_type
+ result(const ScaleMap& map, const StencilT& stencil)
+ {
+ typedef typename StencilT::ValueType Vec3Type;
+ typedef typename StencilT::ValueType::value_type ValueType;
+
+ ValueType div = ValueType(
+ D1Vec<CD_2NDT>::inX(stencil, 0) * (map.getInvTwiceScale()[0]) +
+ D1Vec<CD_2NDT>::inY(stencil, 1) * (map.getInvTwiceScale()[1]) +
+ D1Vec<CD_2NDT>::inZ(stencil, 2) * (map.getInvTwiceScale()[2]) );
+ return div;
+ }
+};
+
+
+// scale and translate, 2nd order
+template<>
+struct Divergence<ScaleTranslateMap, CD_2ND>
+{
+ // random access version
+ template<typename Accessor> static typename Accessor::ValueType::value_type
+ result(const ScaleTranslateMap& map, const Accessor& grid, const Coord& ijk)
+ {
+ typedef typename Accessor::ValueType Vec3Type;
+ typedef typename Accessor::ValueType::value_type ValueType;
+
+ ValueType div = ValueType(
+ D1Vec<CD_2NDT>::inX(grid, ijk, 0) * (map.getInvTwiceScale()[0]) +
+ D1Vec<CD_2NDT>::inY(grid, ijk, 1) * (map.getInvTwiceScale()[1]) +
+ D1Vec<CD_2NDT>::inZ(grid, ijk, 2) * (map.getInvTwiceScale()[2]) );
+ return div;
+ }
+
+ // stencil access version
+ template<typename StencilT> static typename StencilT::ValueType::value_type
+ result(const ScaleTranslateMap& map, const StencilT& stencil)
+ {
+ typedef typename StencilT::ValueType Vec3Type;
+ typedef typename StencilT::ValueType::value_type ValueType;
+
+ ValueType div = ValueType(
+ D1Vec<CD_2NDT>::inX(stencil, 0) * (map.getInvTwiceScale()[0]) +
+ D1Vec<CD_2NDT>::inY(stencil, 1) * (map.getInvTwiceScale()[1]) +
+ D1Vec<CD_2NDT>::inZ(stencil, 2) * (map.getInvTwiceScale()[2]) );
+ return div;
+ }
+};
+//@}
+
+
+//@{
+/// @brief Compute the curl of a vector-valued grid using differencing
+/// of various orders in the space defined by the range of the map.
+template<typename MapType, DScheme DiffScheme>
+struct Curl
+{
+ // random access version
+ template<typename Accessor> static typename Accessor::ValueType
+ result(const MapType& map, const Accessor& grid, const Coord& ijk)
+ {
+ typedef typename Accessor::ValueType Vec3Type;
+ Vec3Type mat[3];
+ for (int i = 0; i < 3; i++) {
+ Vec3d vec(
+ D1Vec<DiffScheme>::inX(grid, ijk, i),
+ D1Vec<DiffScheme>::inY(grid, ijk, i),
+ D1Vec<DiffScheme>::inZ(grid, ijk, i));
+ // dF_i/dx_j (x_1 = x, x_2 = y, x_3 = z)
+ mat[i] = Vec3Type(map.applyIJT(vec, ijk.asVec3d()));
+ }
+ return Vec3Type(mat[2][1] - mat[1][2], // dF_3/dx_2 - dF_2/dx_3
+ mat[0][2] - mat[2][0], // dF_1/dx_3 - dF_3/dx_1
+ mat[1][0] - mat[0][1]); // dF_2/dx_1 - dF_1/dx_2
+ }
+
+ // stencil access version
+ template<typename StencilT> static typename StencilT::ValueType
+ result(const MapType& map, const StencilT& stencil)
+ {
+ typedef typename StencilT::ValueType Vec3Type;
+ Vec3Type mat[3];
+ for (int i = 0; i < 3; i++) {
+ Vec3d vec(
+ D1Vec<DiffScheme>::inX(stencil, i),
+ D1Vec<DiffScheme>::inY(stencil, i),
+ D1Vec<DiffScheme>::inZ(stencil, i));
+ // dF_i/dx_j (x_1 = x, x_2 = y, x_3 = z)
+ mat[i] = Vec3Type(map.applyIJT(vec, stencil.getCenterCoord().asVec3d()));
+ }
+ return Vec3Type(mat[2][1] - mat[1][2], // dF_3/dx_2 - dF_2/dx_3
+ mat[0][2] - mat[2][0], // dF_1/dx_3 - dF_3/dx_1
+ mat[1][0] - mat[0][1]); // dF_2/dx_1 - dF_1/dx_2
+ }
+};
+
+
+template<DScheme DiffScheme>
+struct Curl<UniformScaleMap, DiffScheme>
+{
+ // random access version
+ template<typename Accessor> static typename Accessor::ValueType
+ result(const UniformScaleMap& map, const Accessor& grid, const Coord& ijk)
+ {
+ typedef typename Accessor::ValueType Vec3Type;
+ typedef typename Vec3Type::value_type ValueType;
+ return ISCurl<DiffScheme>::result(grid, ijk) * ValueType(map.getInvScale()[0]);
+ }
+
+ // Stencil access version
+ template<typename StencilT> static typename StencilT::ValueType
+ result(const UniformScaleMap& map, const StencilT& stencil)
+ {
+ typedef typename StencilT::ValueType Vec3Type;
+ typedef typename Vec3Type::value_type ValueType;
+ return ISCurl<DiffScheme>::result(stencil) * ValueType(map.getInvScale()[0]);
+ }
+};
+
+
+template<DScheme DiffScheme>
+struct Curl<UniformScaleTranslateMap, DiffScheme>
+{
+ // random access version
+ template<typename Accessor> static typename Accessor::ValueType
+ result(const UniformScaleTranslateMap& map, const Accessor& grid, const Coord& ijk)
+ {
+ typedef typename Accessor::ValueType Vec3Type;
+ typedef typename Vec3Type::value_type ValueType;
+
+ return ISCurl<DiffScheme>::result(grid, ijk) * ValueType(map.getInvScale()[0]);
+ }
+
+ // stencil access version
+ template<typename StencilT> static typename StencilT::ValueType
+ result(const UniformScaleTranslateMap& map, const StencilT& stencil)
+ {
+ typedef typename StencilT::ValueType Vec3Type;
+ typedef typename Vec3Type::value_type ValueType;
+
+ return ISCurl<DiffScheme>::result(stencil) * ValueType(map.getInvScale()[0]);
+ }
+};
+
+template<>
+struct Curl<UniformScaleMap, CD_2ND>
+{
+ // random access version
+ template<typename Accessor> static typename Accessor::ValueType
+ result(const UniformScaleMap& map, const Accessor& grid, const Coord& ijk)
+ {
+ typedef typename Accessor::ValueType Vec3Type;
+ typedef typename Vec3Type::value_type ValueType;
+
+ return ISCurl<CD_2NDT>::result(grid, ijk) * ValueType(map.getInvTwiceScale()[0]);
+ }
+
+ // stencil access version
+ template<typename StencilT> static typename StencilT::ValueType
+ result(const UniformScaleMap& map, const StencilT& stencil)
+ {
+ typedef typename StencilT::ValueType Vec3Type;
+ typedef typename Vec3Type::value_type ValueType;
+
+ return ISCurl<CD_2NDT>::result(stencil) * ValueType(map.getInvTwiceScale()[0]);
+ }
+};
+
+template<>
+struct Curl<UniformScaleTranslateMap, CD_2ND>
+{
+ // random access version
+ template<typename Accessor> static typename Accessor::ValueType
+ result(const UniformScaleTranslateMap& map, const Accessor& grid, const Coord& ijk)
+ {
+ typedef typename Accessor::ValueType Vec3Type;
+ typedef typename Vec3Type::value_type ValueType;
+
+ return ISCurl<CD_2NDT>::result(grid, ijk) * ValueType(map.getInvTwiceScale()[0]);
+ }
+
+ // stencil access version
+ template<typename StencilT> static typename StencilT::ValueType
+ result(const UniformScaleTranslateMap& map, const StencilT& stencil)
+ {
+ typedef typename StencilT::ValueType Vec3Type;
+ typedef typename Vec3Type::value_type ValueType;
+
+ return ISCurl<CD_2NDT>::result(stencil) * ValueType(map.getInvTwiceScale()[0]);
+ }
+};
+//@}
+
+
+//@{
+/// @brief Compute the Laplacian at a given location in a grid using finite differencing
+/// of various orders. The result is defined in the range of the map.
+template<typename MapType, DDScheme DiffScheme>
+struct Laplacian
+{
+ // random access version
+ template<typename Accessor>
+ static typename Accessor::ValueType result(const MapType& map,
+ const Accessor& grid, const Coord& ijk)
+ {
+ typedef typename Accessor::ValueType ValueType;
+ // all the second derivatives in index space
+ ValueType iddx = D2<DiffScheme>::inX(grid, ijk);
+ ValueType iddy = D2<DiffScheme>::inY(grid, ijk);
+ ValueType iddz = D2<DiffScheme>::inZ(grid, ijk);
+
+ ValueType iddxy = D2<DiffScheme>::inXandY(grid, ijk);
+ ValueType iddyz = D2<DiffScheme>::inYandZ(grid, ijk);
+ ValueType iddxz = D2<DiffScheme>::inXandZ(grid, ijk);
+
+ // second derivatives in index space
+ Mat3d d2_is(iddx, iddxy, iddxz,
+ iddxy, iddy, iddyz,
+ iddxz, iddyz, iddz);
+
+ Mat3d d2_rs; // to hold the second derivative matrix in range space
+ if (is_linear<MapType>::value) {
+ d2_rs = map.applyIJC(d2_is);
+ } else {
+ // compute the first derivatives with 2nd order accuracy.
+ Vec3d d1_is(D1<CD_2ND>::inX(grid, ijk),
+ D1<CD_2ND>::inY(grid, ijk),
+ D1<CD_2ND>::inZ(grid, ijk) );
+
+ d2_rs = map.applyIJC(d2_is, d1_is, ijk.asVec3d());
+ }
+
+ // the trace of the second derivative (range space) matrix is laplacian
+ return ValueType(d2_rs(0,0) + d2_rs(1,1) + d2_rs(2,2));
+ }
+
+ // stencil access version
+ template<typename StencilT>
+ static typename StencilT::ValueType result(const MapType& map, const StencilT& stencil)
+ {
+ typedef typename StencilT::ValueType ValueType;
+ // all the second derivatives in index space
+ ValueType iddx = D2<DiffScheme>::inX(stencil);
+ ValueType iddy = D2<DiffScheme>::inY(stencil);
+ ValueType iddz = D2<DiffScheme>::inZ(stencil);
+
+ ValueType iddxy = D2<DiffScheme>::inXandY(stencil);
+ ValueType iddyz = D2<DiffScheme>::inYandZ(stencil);
+ ValueType iddxz = D2<DiffScheme>::inXandZ(stencil);
+
+ // second derivatives in index space
+ Mat3d d2_is(iddx, iddxy, iddxz,
+ iddxy, iddy, iddyz,
+ iddxz, iddyz, iddz);
+
+ Mat3d d2_rs; // to hold the second derivative matrix in range space
+ if (is_linear<MapType>::value) {
+ d2_rs = map.applyIJC(d2_is);
+ } else {
+ // compute the first derivatives with 2nd order accuracy.
+ Vec3d d1_is(D1<CD_2ND>::inX(stencil),
+ D1<CD_2ND>::inY(stencil),
+ D1<CD_2ND>::inZ(stencil) );
+
+ d2_rs = map.applyIJC(d2_is, d1_is, stencil.getCenterCoord().asVec3d());
+ }
+
+ // the trace of the second derivative (range space) matrix is laplacian
+ return ValueType(d2_rs(0,0) + d2_rs(1,1) + d2_rs(2,2));
+ }
+};
+
+
+template<DDScheme DiffScheme>
+struct Laplacian<TranslationMap, DiffScheme>
+{
+ // random access version
+ template<typename Accessor>
+ static typename Accessor::ValueType result(const TranslationMap&,
+ const Accessor& grid, const Coord& ijk)
+ {
+ return ISLaplacian<DiffScheme>::result(grid, ijk);
+ }
+
+ // stencil access version
+ template<typename StencilT>
+ static typename StencilT::ValueType result(const TranslationMap&, const StencilT& stencil)
+ {
+ return ISLaplacian<DiffScheme>::result(stencil);
+ }
+};
+
+
+// The Laplacian is invariant to rotation or reflection.
+template<DDScheme DiffScheme>
+struct Laplacian<UnitaryMap, DiffScheme>
+{
+ // random access version
+ template<typename Accessor>
+ static typename Accessor::ValueType result(const UnitaryMap&,
+ const Accessor& grid, const Coord& ijk)
+ {
+ return ISLaplacian<DiffScheme>::result(grid, ijk);
+ }
+
+ // stencil access version
+ template<typename StencilT>
+ static typename StencilT::ValueType result(const UnitaryMap&, const StencilT& stencil)
+ {
+ return ISLaplacian<DiffScheme>::result(stencil);
+ }
+};
+
+
+template<DDScheme DiffScheme>
+struct Laplacian<UniformScaleMap, DiffScheme>
+{
+ // random access version
+ template<typename Accessor> static typename Accessor::ValueType
+ result(const UniformScaleMap& map, const Accessor& grid, const Coord& ijk)
+ {
+ typedef typename Accessor::ValueType ValueType;
+ ValueType invdxdx = ValueType(map.getInvScaleSqr()[0]);
+ return ISLaplacian<DiffScheme>::result(grid, ijk) * invdxdx;
+ }
+
+ // stencil access version
+ template<typename StencilT> static typename StencilT::ValueType
+ result(const UniformScaleMap& map, const StencilT& stencil)
+ {
+ typedef typename StencilT::ValueType ValueType;
+ ValueType invdxdx = ValueType(map.getInvScaleSqr()[0]);
+ return ISLaplacian<DiffScheme>::result(stencil) * invdxdx;
+ }
+};
+
+
+template<DDScheme DiffScheme>
+struct Laplacian<UniformScaleTranslateMap, DiffScheme>
+{
+ // random access version
+ template<typename Accessor> static typename Accessor::ValueType
+ result(const UniformScaleTranslateMap& map, const Accessor& grid, const Coord& ijk)
+ {
+ typedef typename Accessor::ValueType ValueType;
+ ValueType invdxdx = ValueType(map.getInvScaleSqr()[0]);
+ return ISLaplacian<DiffScheme>::result(grid, ijk) * invdxdx;
+ }
+
+ // stencil access version
+ template<typename StencilT> static typename StencilT::ValueType
+ result(const UniformScaleTranslateMap& map, const StencilT& stencil)
+ {
+ typedef typename StencilT::ValueType ValueType;
+ ValueType invdxdx = ValueType(map.getInvScaleSqr()[0]);
+ return ISLaplacian<DiffScheme>::result(stencil) * invdxdx;
+ }
+};
+
+
+template<DDScheme DiffScheme>
+struct Laplacian<ScaleMap, DiffScheme>
+{
+ // random access version
+ template<typename Accessor> static typename Accessor::ValueType
+ result(const ScaleMap& map, const Accessor& grid, const Coord& ijk)
+ {
+ typedef typename Accessor::ValueType ValueType;
+
+ // compute the second derivatives in index space
+ ValueType iddx = D2<DiffScheme>::inX(grid, ijk);
+ ValueType iddy = D2<DiffScheme>::inY(grid, ijk);
+ ValueType iddz = D2<DiffScheme>::inZ(grid, ijk);
+ const Vec3d& invScaleSqr = map.getInvScaleSqr();
+ // scale them by the appropriate 1/dx^2, 1/dy^2, 1/dz^2 and sum
+ return ValueType(iddx * invScaleSqr[0] + iddy * invScaleSqr[1] + iddz * invScaleSqr[2]);
+ }
+
+ // stencil access version
+ template<typename StencilT> static typename StencilT::ValueType
+ result(const ScaleMap& map, const StencilT& stencil)
+ {
+ typedef typename StencilT::ValueType ValueType;
+
+ // compute the second derivatives in index space
+ ValueType iddx = D2<DiffScheme>::inX(stencil);
+ ValueType iddy = D2<DiffScheme>::inY(stencil);
+ ValueType iddz = D2<DiffScheme>::inZ(stencil);
+ const Vec3d& invScaleSqr = map.getInvScaleSqr();
+ // scale them by the appropriate 1/dx^2, 1/dy^2, 1/dz^2 and sum
+ return ValueType(iddx * invScaleSqr[0] + iddy * invScaleSqr[1] + iddz * invScaleSqr[2]);
+ }
+};
+
+
+template<DDScheme DiffScheme>
+struct Laplacian<ScaleTranslateMap, DiffScheme>
+{
+ // random access version
+ template<typename Accessor> static typename Accessor::ValueType
+ result(const ScaleTranslateMap& map, const Accessor& grid, const Coord& ijk)
+ {
+ typedef typename Accessor::ValueType ValueType;
+ // compute the second derivatives in index space
+ ValueType iddx = D2<DiffScheme>::inX(grid, ijk);
+ ValueType iddy = D2<DiffScheme>::inY(grid, ijk);
+ ValueType iddz = D2<DiffScheme>::inZ(grid, ijk);
+ const Vec3d& invScaleSqr = map.getInvScaleSqr();
+ // scale them by the appropriate 1/dx^2, 1/dy^2, 1/dz^2 and sum
+ return ValueType(iddx * invScaleSqr[0] + iddy * invScaleSqr[1] + iddz * invScaleSqr[2]);
+ }
+
+ // stencil access version
+ template<typename StencilT> static typename StencilT::ValueType
+ result(const ScaleTranslateMap& map, const StencilT& stencil)
+ {
+ typedef typename StencilT::ValueType ValueType;
+ // compute the second derivatives in index space
+ ValueType iddx = D2<DiffScheme>::inX(stencil);
+ ValueType iddy = D2<DiffScheme>::inY(stencil);
+ ValueType iddz = D2<DiffScheme>::inZ(stencil);
+ const Vec3d& invScaleSqr = map.getInvScaleSqr();
+ // scale them by the appropriate 1/dx^2, 1/dy^2, 1/dz^2 and sum
+ return ValueType(iddx * invScaleSqr[0] + iddy * invScaleSqr[1] + iddz * invScaleSqr[2]);
+ }
+};
+
+
+/// @brief Compute the closest-point transform to a level set.
+/// @return the closest point to the surface from which the level set was derived,
+/// in the domain space of the map (e.g., voxel space).
+template<typename MapType, DScheme DiffScheme>
+struct CPT
+{
+ // random access version
+ template<typename Accessor> static math::Vec3<typename Accessor::ValueType>
+ result(const MapType& map, const Accessor& grid, const Coord& ijk)
+ {
+ typedef typename Accessor::ValueType ValueType;
+ typedef Vec3<ValueType> Vec3Type;
+
+ // current distance
+ ValueType d = grid.getValue(ijk);
+ // compute gradient in physical space where it is a unit normal
+ // since the grid holds a distance level set.
+ Vec3d vectorFromSurface(d*Gradient<MapType,DiffScheme>::result(map, grid, ijk));
+ if (is_linear<MapType>::value) {
+ Vec3d result = ijk.asVec3d() - map.applyInverseMap(vectorFromSurface);
+ return Vec3Type(result);
+ } else {
+ Vec3d location = map.applyMap(ijk.asVec3d());
+ Vec3d result = map.applyInverseMap(location - vectorFromSurface);
+ return Vec3Type(result);
+ }
+ }
+
+ // stencil access version
+ template<typename StencilT> static math::Vec3<typename StencilT::ValueType>
+ result(const MapType& map, const StencilT& stencil)
+ {
+ typedef typename StencilT::ValueType ValueType;
+ typedef Vec3<ValueType> Vec3Type;
+
+ // current distance
+ ValueType d = stencil.template getValue<0, 0, 0>();
+ // compute gradient in physical space where it is a unit normal
+ // since the grid holds a distance level set.
+ Vec3d vectorFromSurface(d*Gradient<MapType, DiffScheme>::result(map, stencil));
+ if (is_linear<MapType>::value) {
+ Vec3d result = stencil.getCenterCoord().asVec3d()
+ - map.applyInverseMap(vectorFromSurface);
+ return Vec3Type(result);
+ } else {
+ Vec3d location = map.applyMap(stencil.getCenterCoord().asVec3d());
+ Vec3d result = map.applyInverseMap(location - vectorFromSurface);
+ return Vec3Type(result);
+ }
+ }
+};
+
+
+/// @brief Compute the closest-point transform to a level set.
+/// @return the closest point to the surface from which the level set was derived,
+/// in the range space of the map (e.g., in world space)
+template<typename MapType, DScheme DiffScheme>
+struct CPT_RANGE
+{
+ // random access version
+ template<typename Accessor> static Vec3<typename Accessor::ValueType>
+ result(const MapType& map, const Accessor& grid, const Coord& ijk)
+ {
+ typedef typename Accessor::ValueType ValueType;
+ typedef Vec3<ValueType> Vec3Type;
+ // current distance
+ ValueType d = grid.getValue(ijk);
+ // compute gradient in physical space where it is a unit normal
+ // since the grid holds a distance level set.
+ Vec3Type vectorFromSurface =
+ d*Gradient<MapType,DiffScheme>::result(map, grid, ijk);
+ Vec3d result = map.applyMap(ijk.asVec3d()) - vectorFromSurface;
+
+ return Vec3Type(result);
+ }
+
+ // stencil access version
+ template<typename StencilT> static Vec3<typename StencilT::ValueType>
+ result(const MapType& map, const StencilT& stencil)
+ {
+ typedef typename StencilT::ValueType ValueType;
+ typedef Vec3<ValueType> Vec3Type;
+ // current distance
+ ValueType d = stencil.template getValue<0, 0, 0>();
+ // compute gradient in physical space where it is a unit normal
+ // since the grid holds a distance level set.
+ Vec3Type vectorFromSurface =
+ d*Gradient<MapType, DiffScheme>::result(map, stencil);
+ Vec3d result = map.applyMap(stencil.getCenterCoord().asVec3d()) - vectorFromSurface;
+
+ return Vec3Type(result);
+ }
+};
+
+
+/// @brief Compute the mean curvature.
+/// @return the mean curvature in two parts: @c alpha is the numerator in
+/// @f$\nabla \cdot (\nabla \phi / |\nabla \phi|)@f$, and @c beta is @f$|\nabla \phi|@f$.
+template<typename MapType, DDScheme DiffScheme2, DScheme DiffScheme1>
+struct MeanCurvature
+{
+ // random access version
+ template<typename Accessor>
+ static void compute(const MapType& map, const Accessor& grid, const Coord& ijk,
+ double& alpha, double& beta)
+ {
+ typedef typename Accessor::ValueType ValueType;
+ typedef Vec3<ValueType> Vec3Type;
+
+ // compute the gradient in index space
+ Vec3d d1_is(D1<DiffScheme1>::inX(grid, ijk),
+ D1<DiffScheme1>::inY(grid, ijk),
+ D1<DiffScheme1>::inZ(grid, ijk) );
+
+ // all the second derivatives in index space
+ ValueType iddx = D2<DiffScheme2>::inX(grid, ijk);
+ ValueType iddy = D2<DiffScheme2>::inY(grid, ijk);
+ ValueType iddz = D2<DiffScheme2>::inZ(grid, ijk);
+
+ ValueType iddxy = D2<DiffScheme2>::inXandY(grid, ijk);
+ ValueType iddyz = D2<DiffScheme2>::inYandZ(grid, ijk);
+ ValueType iddxz = D2<DiffScheme2>::inXandZ(grid, ijk);
+
+ // second derivatives in index space
+ Mat3d d2_is(iddx, iddxy, iddxz,
+ iddxy, iddy, iddyz,
+ iddxz, iddyz, iddz);
+
+ // convert to range space
+ Mat3d d2_rs;
+ Vec3d d1_rs;
+ if (is_linear<MapType>::value) {
+ d2_rs = map.applyIJC(d2_is);
+ d1_rs = map.applyIJT(d1_is);
+ } else {
+ d2_rs = map.applyIJC(d2_is, d1_is, ijk.asVec3d());
+ d1_rs = map.applyIJT(d1_is, ijk.asVec3d());
+ }
+
+ // assemble the mean curvature
+ double Dx2 = d1_rs(0)*d1_rs(0);
+ double Dy2 = d1_rs(1)*d1_rs(1);
+ double Dz2 = d1_rs(2)*d1_rs(2);
+
+ // for return
+ alpha = (Dx2*(d2_rs(1,1)+d2_rs(2,2))+Dy2*(d2_rs(0,0)+d2_rs(2,2))
+ +Dz2*(d2_rs(0,0)+d2_rs(1,1))
+ -2*(d1_rs(0)*(d1_rs(1)*d2_rs(0,1)+d1_rs(2)*d2_rs(0,2))
+ +d1_rs(1)*d1_rs(2)*d2_rs(1,2)));
+ beta = std::sqrt(Dx2 + Dy2 + Dz2); // * 1/dx
+ }
+
+ template<typename Accessor>
+ static typename Accessor::ValueType result(const MapType& map,
+ const Accessor& grid, const Coord& ijk)
+ {
+ typedef typename Accessor::ValueType ValueType;
+ double alpha, beta;
+ compute(map, grid, ijk, alpha, beta);
+
+ return ValueType(alpha/(2. *math::Pow3(beta)));
+ }
+
+ template<typename Accessor>
+ static typename Accessor::ValueType normGrad(const MapType& map,
+ const Accessor& grid, const Coord& ijk)
+ {
+ typedef typename Accessor::ValueType ValueType;
+ double alpha, beta;
+ compute(map, grid, ijk, alpha, beta);
+
+ return ValueType(alpha/(2. *math::Pow2(beta)));
+ }
+
+ // stencil access version
+ template<typename StencilT>
+ static void compute(const MapType& map, const StencilT& stencil, double& alpha, double& beta)
+ {
+ typedef typename StencilT::ValueType ValueType;
+ typedef Vec3<ValueType> Vec3Type;
+
+ // compute the gradient in index space
+ Vec3d d1_is(D1<DiffScheme1>::inX(stencil),
+ D1<DiffScheme1>::inY(stencil),
+ D1<DiffScheme1>::inZ(stencil) );
+
+ // all the second derivatives in index space
+ ValueType iddx = D2<DiffScheme2>::inX(stencil);
+ ValueType iddy = D2<DiffScheme2>::inY(stencil);
+ ValueType iddz = D2<DiffScheme2>::inZ(stencil);
+
+ ValueType iddxy = D2<DiffScheme2>::inXandY(stencil);
+ ValueType iddyz = D2<DiffScheme2>::inYandZ(stencil);
+ ValueType iddxz = D2<DiffScheme2>::inXandZ(stencil);
+
+ // second derivatives in index space
+ Mat3d d2_is(iddx, iddxy, iddxz,
+ iddxy, iddy, iddyz,
+ iddxz, iddyz, iddz);
+
+ // convert to range space
+ Mat3d d2_rs;
+ Vec3d d1_rs;
+ if (is_linear<MapType>::value) {
+ d2_rs = map.applyIJC(d2_is);
+ d1_rs = map.applyIJT(d1_is);
+ } else {
+ d2_rs = map.applyIJC(d2_is, d1_is, stencil.getCenterCoord().asVec3d());
+ d1_rs = map.applyIJT(d1_is, stencil.getCenterCoord().asVec3d());
+ }
+
+ // assemble the mean curvature
+ double Dx2 = d1_rs(0)*d1_rs(0);
+ double Dy2 = d1_rs(1)*d1_rs(1);
+ double Dz2 = d1_rs(2)*d1_rs(2);
+
+ // for return
+ alpha = (Dx2*(d2_rs(1,1)+d2_rs(2,2))+Dy2*(d2_rs(0,0)+d2_rs(2,2))
+ +Dz2*(d2_rs(0,0)+d2_rs(1,1))
+ -2*(d1_rs(0)*(d1_rs(1)*d2_rs(0,1)+d1_rs(2)*d2_rs(0,2))
+ +d1_rs(1)*d1_rs(2)*d2_rs(1,2)));
+ beta = std::sqrt(Dx2 + Dy2 + Dz2); // * 1/dx
+ }
+
+ template<typename StencilT>
+ static typename StencilT::ValueType
+ result(const MapType& map, const StencilT stencil)
+ {
+ typedef typename StencilT::ValueType ValueType;
+ double alpha, beta;
+ compute(map, stencil, alpha, beta);
+ return ValueType(alpha/(2*math::Pow3(beta)));
+ }
+
+ template<typename StencilT>
+ static typename StencilT::ValueType normGrad(const MapType& map, const StencilT stencil)
+ {
+ typedef typename StencilT::ValueType ValueType;
+ double alpha, beta;
+ compute(map, stencil, alpha, beta);
+
+ return ValueType(alpha/(2*math::Pow2(beta)));
+ }
+};
+
+
+template<DDScheme DiffScheme2, DScheme DiffScheme1>
+struct MeanCurvature<TranslationMap, DiffScheme2, DiffScheme1>
+{
+ // random access version
+ template<typename Accessor>
+ static typename Accessor::ValueType result(const TranslationMap&,
+ const Accessor& grid, const Coord& ijk)
+ {
+ typedef typename Accessor::ValueType ValueType;
+
+ ValueType alpha, beta;
+ ISMeanCurvature<DiffScheme2, DiffScheme1>::result(grid, ijk, alpha, beta);
+
+ return ValueType(alpha /(2*math::Pow3(beta)));
+ }
+
+ template<typename Accessor>
+ static typename Accessor::ValueType normGrad(const TranslationMap&,
+ const Accessor& grid, const Coord& ijk)
+ {
+ typedef typename Accessor::ValueType ValueType;
+
+ ValueType alpha, beta;
+ ISMeanCurvature<DiffScheme2, DiffScheme1>::result(grid, ijk, alpha, beta);
+
+ return ValueType(alpha/(2*math::Pow2(beta)));
+ }
+
+ // stencil access version
+ template<typename StencilT>
+ static typename StencilT::ValueType result(const TranslationMap&, const StencilT& stencil)
+ {
+ typedef typename StencilT::ValueType ValueType;
+
+ ValueType alpha, beta;
+ ISMeanCurvature<DiffScheme2, DiffScheme1>::result(stencil, alpha, beta);
+
+ return ValueType(alpha /(2*math::Pow3(beta)));
+ }
+
+ template<typename StencilT>
+ static typename StencilT::ValueType normGrad(const TranslationMap&, const StencilT& stencil)
+ {
+ typedef typename StencilT::ValueType ValueType;
+
+ ValueType alpha, beta;
+ ISMeanCurvature<DiffScheme2, DiffScheme1>::result(stencil, alpha, beta);
+
+ return ValueType(alpha/(2*math::Pow2(beta)));
+ }
+};
+
+
+template<DDScheme DiffScheme2, DScheme DiffScheme1>
+struct MeanCurvature<UniformScaleMap, DiffScheme2, DiffScheme1>
+{
+ // random access version
+ template<typename Accessor>
+ static typename Accessor::ValueType result(const UniformScaleMap& map,
+ const Accessor& grid, const Coord& ijk)
+ {
+ typedef typename Accessor::ValueType ValueType;
+
+ ValueType alpha, beta;
+ ISMeanCurvature<DiffScheme2, DiffScheme1>::result(grid, ijk, alpha, beta);
+ ValueType inv2dx = ValueType(map.getInvTwiceScale()[0]);
+
+ return ValueType(alpha*inv2dx/math::Pow3(beta));
+ }
+
+ template<typename Accessor>
+ static typename Accessor::ValueType normGrad(const UniformScaleMap& map,
+ const Accessor& grid, const Coord& ijk)
+ {
+ typedef typename Accessor::ValueType ValueType;
+
+ ValueType alpha, beta;
+ ISMeanCurvature<DiffScheme2, DiffScheme1>::result(grid, ijk, alpha, beta);
+ ValueType invdxdx = ValueType(map.getInvScaleSqr()[0]);
+
+ return ValueType(alpha*invdxdx/(2*math::Pow2(beta)));
+ }
+
+ // stencil access version
+ template<typename StencilT>
+ static typename StencilT::ValueType result(const UniformScaleMap& map, const StencilT& stencil)
+ {
+ typedef typename StencilT::ValueType ValueType;
+
+ ValueType alpha, beta;
+ ISMeanCurvature<DiffScheme2, DiffScheme1>::result(stencil, alpha, beta);
+ ValueType inv2dx = ValueType(map.getInvTwiceScale()[0]);
+
+ return ValueType(alpha*inv2dx/math::Pow3(beta));
+ }
+
+ template<typename StencilT>
+ static typename StencilT::ValueType normGrad(const UniformScaleMap& map, const StencilT& stencil)
+ {
+ typedef typename StencilT::ValueType ValueType;
+
+ ValueType alpha, beta;
+ ISMeanCurvature<DiffScheme2, DiffScheme1>::result(stencil, alpha, beta);
+ ValueType invdxdx = ValueType(map.getInvScaleSqr()[0]);
+
+ return ValueType(alpha*invdxdx/(2*math::Pow2(beta)));
+ }
+};
+
+
+template<DDScheme DiffScheme2, DScheme DiffScheme1>
+struct MeanCurvature<UniformScaleTranslateMap, DiffScheme2, DiffScheme1>
+{
+ // random access version
+ template<typename Accessor> static typename Accessor::ValueType
+ result(const UniformScaleTranslateMap& map, const Accessor& grid, const Coord& ijk)
+ {
+ typedef typename Accessor::ValueType ValueType;
+
+ ValueType alpha, beta;
+ ISMeanCurvature<DiffScheme2, DiffScheme1>::result(grid, ijk, alpha, beta);
+ ValueType inv2dx = ValueType(map.getInvTwiceScale()[0]);
+
+ return ValueType(alpha*inv2dx/math::Pow3(beta));
+ }
+
+ template<typename Accessor> static typename Accessor::ValueType
+ normGrad(const UniformScaleTranslateMap& map, const Accessor& grid, const Coord& ijk)
+ {
+ typedef typename Accessor::ValueType ValueType;
+
+ ValueType alpha, beta;
+ ISMeanCurvature<DiffScheme2, DiffScheme1>::result(grid, ijk, alpha, beta);
+ ValueType invdxdx = ValueType(map.getInvScaleSqr()[0]);
+
+ return ValueType(alpha*invdxdx/(2*math::Pow2(beta)));
+ }
+
+ // stencil access version
+ template<typename StencilT> static typename StencilT::ValueType
+ result(const UniformScaleTranslateMap& map, const StencilT& stencil)
+ {
+ typedef typename StencilT::ValueType ValueType;
+
+ ValueType alpha, beta;
+ ISMeanCurvature<DiffScheme2, DiffScheme1>::result(stencil, alpha, beta);
+ ValueType inv2dx = ValueType(map.getInvTwiceScale()[0]);
+
+ return ValueType(alpha*inv2dx/math::Pow3(beta));
+ }
+
+ template<typename StencilT> static typename StencilT::ValueType
+ normGrad(const UniformScaleTranslateMap& map, const StencilT& stencil)
+ {
+ typedef typename StencilT::ValueType ValueType;
+
+ ValueType alpha, beta;
+ ISMeanCurvature<DiffScheme2, DiffScheme1>::result(stencil, alpha, beta);
+ ValueType invdxdx = ValueType(map.getInvScaleSqr()[0]);
+
+ return ValueType(alpha*invdxdx/(2*math::Pow2(beta)));
+ }
+};
+
+
+/// @brief A wrapper that holds a MapBase::ConstPtr and exposes a reduced set
+/// of functionality needed by the mathematical operators
+/// @details This may be used in some <tt>Map</tt>-templated code, when the overhead of
+/// actually resolving the @c Map type is large compared to the map work to be done.
+class GenericMap
+{
+public:
+ template<typename GridType>
+ GenericMap(const GridType& g): mMap(g.transform().baseMap()) {}
+
+ GenericMap(const Transform& t): mMap(t.baseMap()) {}
+ GenericMap(MapBase::Ptr map): mMap(boost::const_pointer_cast<const MapBase>(map)) {}
+ GenericMap(MapBase::ConstPtr map): mMap(map) {}
+ ~GenericMap() {}
+
+ Vec3d applyMap(const Vec3d& in) const { return mMap->applyMap(in); }
+ Vec3d applyInverseMap(const Vec3d& in) const { return mMap->applyInverseMap(in); }
+
+ Vec3d applyIJT(const Vec3d& in) const { return mMap->applyIJT(in); }
+ Vec3d applyIJT(const Vec3d& in, const Vec3d& pos) const { return mMap->applyIJT(in, pos); }
+ Mat3d applyIJC(const Mat3d& m) const { return mMap->applyIJC(m); }
+ Mat3d applyIJC(const Mat3d& m, const Vec3d& v, const Vec3d& pos) const
+ { return mMap->applyIJC(m,v,pos); }
+
+ double determinant() const { return mMap->determinant(); }
+ double determinant(const Vec3d& in) const { return mMap->determinant(in); }
+
+ Vec3d voxelSize() const { return mMap->voxelSize(); }
+ Vec3d voxelSize(const Vec3d&v) const { return mMap->voxelSize(v); }
+
+private:
+ MapBase::ConstPtr mMap;
+};
+
+} // end math namespace
+} // namespace OPENVDB_VERSION_NAME
+} // end openvdb namespace
+
+#endif // OPENVDB_MATH_OPERATORS_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/math/Proximity.cc b/extern/openvdb/internal/openvdb/math/Proximity.cc
new file mode 100644
index 00000000000..e5cfc278172
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/math/Proximity.cc
@@ -0,0 +1,377 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+
+#include "Proximity.h"
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace math {
+
+
+OPENVDB_API Vec3d
+closestPointOnTriangleToPoint(const Vec3d& a, const Vec3d& b, const Vec3d& c,
+ const Vec3d& p, Vec3d& uvw)
+{
+ Vec3d ab = b - a, ac = c - a, ap = p - a;
+ double d1 = ab.dot(ap), d2 = ac.dot(ap);
+ uvw.setZero();
+
+ if (d1 <= 0.0 && d2 <= 0.0) {
+ uvw[0] = 1.0;
+ return a; // barycentric coordinates (1,0,0)
+ }
+
+ // Check if P in vertex region outside B
+ Vec3d bp = p - b;
+ double d3 = ab.dot(bp), d4 = ac.dot(bp);
+ if (d3 >= 0.0 && d4 <= d3) {
+ uvw[1] = 1.0;
+ return b; // barycentric coordinates (0,1,0)
+ }
+
+ // Check if P in edge region of AB, if so return projection of P onto AB
+ double vc = d1 * d4 - d3 * d2;
+ if (vc <= 0.0 && d1 >= 0.0 && d3 <= 0.0) {
+ uvw[1] = d1 / (d1 - d3);
+ uvw[0] = 1.0 - uvw[1];
+ return a + uvw[1] * ab; // barycentric coordinates (1-v,v,0)
+ }
+
+ // Check if P in vertex region outside C
+ Vec3d cp = p - c;
+ double d5 = ab.dot(cp), d6 = ac.dot(cp);
+ if (d6 >= 0.0 && d5 <= d6) {
+ uvw[2] = 1.0;
+ return c; // barycentric coordinates (0,0,1)
+ }
+
+ // Check if P in edge region of AC, if so return projection of P onto AC
+ double vb = d5 * d2 - d1 * d6;
+ if (vb <= 0.0 && d2 >= 0.0 && d6 <= 0.0) {
+ uvw[2] = d2 / (d2 - d6);
+ uvw[0] = 1.0 - uvw[2];
+ return a + uvw[2] * ac; // barycentric coordinates (1-w,0,w)
+ }
+
+ // Check if P in edge region of BC, if so return projection of P onto BC
+ double va = d3*d6 - d5*d4;
+ if (va <= 0.0 && (d4 - d3) >= 0.0 && (d5 - d6) >= 0.0) {
+ uvw[2] = (d4 - d3) / ((d4 - d3) + (d5 - d6));
+ uvw[1] = 1.0 - uvw[2];
+ return b + uvw[2] * (c - b); // barycentric coordinates (0,1-w,w)
+ }
+
+ // P inside face region. Compute Q through its barycentric coordinates (u,v,w)
+ double denom = 1.0 / (va + vb + vc);
+ uvw[2] = vc * denom;
+ uvw[1] = vb * denom;
+ uvw[0] = 1.0 - uvw[1] - uvw[2];
+
+ return a + ab*uvw[1] + ac*uvw[2]; // = u*a + v*b + w*c , u= va*denom = 1.0-v-w
+}
+
+
+////////////////////////////////////////
+
+
+// DEPRECATED METHODS
+
+double
+sLineSeg3ToPointDistSqr(const Vec3d &p0,
+ const Vec3d &p1,
+ const Vec3d &point,
+ double &t,
+ double epsilon)
+{
+ Vec3d pDelta;
+ Vec3d tDelta;
+ double pDeltaDot;
+
+ pDelta.sub(p1, p0);
+ tDelta.sub(point, p0);
+
+ //
+ // Line is nearly a point check end points
+ //
+ pDeltaDot = pDelta.dot(pDelta);
+ if (pDeltaDot < epsilon) {
+ pDelta.sub(p1, point);
+ if (pDelta.dot(pDelta) < tDelta.dot(tDelta)) {
+ t = 1;
+ return pDelta.dot(pDelta);
+ } else {
+ t = 0;
+ return tDelta.dot(tDelta);
+ }
+ }
+ t = tDelta.dot(pDelta) / pDeltaDot;
+ if (t < 0) {
+ t = 0;
+ } else if (t > 1) {
+ t = 1;
+ tDelta.sub(point, p1);
+ } else {
+ tDelta -= t * pDelta;
+ }
+ return tDelta.dot(tDelta);
+}
+
+
+////////////////////////////////////////
+
+
+double
+sTri3ToPointDistSqr(const Vec3d &v0,
+ const Vec3d &v1,
+ const Vec3d &v2,
+ const Vec3d &point,
+ Vec2d &uv,
+ double)
+{
+ Vec3d e0, e1;
+ double distSqr;
+
+ e0.sub(v1, v0);
+ e1.sub(v2, v0);
+
+ Vec3d delta = v0 - point;
+ double a00 = e0.dot(e0);
+ double a01 = e0.dot(e1);
+ double a11 = e1.dot(e1);
+ double b0 = delta.dot(e0);
+ double b1 = delta.dot(e1);
+ double c = delta.dot(delta);
+ double det = fabs(a00*a11-a01*a01);
+ /* DEPRECATED
+ double aMax = (a00 > a11) ? a00 : a11;
+ double epsilon2 = epsilon * epsilon;
+
+ //
+ // Triangle is degenerate. Use an absolute test for the length
+ // of the edges and a relative test for area squared
+ //
+ if ((a00 <= epsilon2 && a11 <= epsilon2) || det <= epsilon * aMax * aMax) {
+
+ double t;
+ double minDistSqr;
+
+ minDistSqr = sLineSeg3ToPointDistSqr(v0, v1, point, t, epsilon);
+ uv[0] = 1.0 - t;
+ uv[1] = t;
+
+ distSqr = sLineSeg3ToPointDistSqr(v0, v2, point, t, epsilon);
+ if (distSqr < minDistSqr) {
+ minDistSqr = distSqr;
+ uv[0] = 1.0 - t;
+ uv[1] = 0;
+ }
+
+ distSqr = sLineSeg3ToPointDistSqr(v1, v2, point, t, epsilon);
+ if (distSqr < minDistSqr) {
+ minDistSqr = distSqr;
+ uv[0] = 0;
+ uv[1] = 1.0 - t;
+ }
+
+ return minDistSqr;
+ }*/
+
+ double s = a01*b1-a11*b0;
+ double t = a01*b0-a00*b1;
+
+ if (s + t <= det ) {
+ if (s < 0.0) {
+ if (t < 0.0) {
+ // region 4
+ if (b0 < 0.0) {
+ t = 0.0;
+ if (-b0 >= a00) {
+ s = 1.0;
+ distSqr = a00+2.0*b0+c;
+ } else {
+ s = -b0/a00;
+ distSqr = b0*s+c;
+ }
+ } else {
+ s = 0.0;
+ if (b1 >= 0.0) {
+ t = 0.0;
+ distSqr = c;
+ } else if (-b1 >= a11) {
+ t = 1.0;
+ distSqr = a11+2.0*b1+c;
+ } else {
+ t = -b1/a11;
+ distSqr = b1*t+c;
+ }
+ }
+ } else {
+ // region 3
+ s = 0.0;
+ if (b1 >= 0.0) {
+ t = 0.0;
+ distSqr = c;
+ }
+ else if (-b1 >= a11) {
+ t = 1.0;
+ distSqr = a11+2.0*b1+c;
+ }
+ else {
+ t = -b1/a11;
+ distSqr = b1*t+c;
+ }
+ }
+ } else if (t < 0.0) {
+ // region 5
+
+ t = 0.0;
+ if (b0 >= 0.0) {
+ s = 0.0;
+ distSqr = c;
+ } else if (-b0 >= a00) {
+ s = 1.0;
+ distSqr = a00+2.0*b0+c;
+ } else {
+ s = -b0/a00;
+ distSqr = b0*s+c;
+ }
+ } else {
+ // region 0
+
+ // minimum at interior point
+ double fInvDet = 1.0/det;
+ s *= fInvDet;
+ t *= fInvDet;
+ distSqr = s*(a00*s+a01*t+2.0*b0) +
+ t*(a01*s+a11*t+2.0*b1)+c;
+ }
+ } else {
+ double tmp0, tmp1, numer, denom;
+
+ if (s < 0.0) {
+ // region 2
+
+ tmp0 = a01 + b0;
+ tmp1 = a11 + b1;
+ if (tmp1 > tmp0) {
+ numer = tmp1 - tmp0;
+ denom = a00-2.0*a01+a11;
+ if (numer >= denom) {
+ s = 1.0;
+ t = 0.0;
+ distSqr = a00+2.0*b0+c;
+ } else {
+ s = numer/denom;
+ t = 1.0 - s;
+ distSqr = s*(a00*s+a01*t+2.0*b0) +
+ t*(a01*s+a11*t+2.0*b1)+c;
+ }
+ } else {
+ s = 0.0;
+ if (tmp1 <= 0.0) {
+ t = 1.0;
+ distSqr = a11+2.0*b1+c;
+ } else if (b1 >= 0.0) {
+ t = 0.0;
+ distSqr = c;
+ } else {
+ t = -b1/a11;
+ distSqr = b1*t+c;
+ }
+ }
+ } else if (t < 0.0) {
+ // region 6
+
+ tmp0 = a01 + b1;
+ tmp1 = a00 + b0;
+ if (tmp1 > tmp0 ) {
+ numer = tmp1 - tmp0;
+ denom = a00-2.0*a01+a11;
+ if (numer >= denom ) {
+ t = 1.0;
+ s = 0.0;
+ distSqr = a11+2.0*b1+c;
+ } else {
+ t = numer/denom;
+ s = 1.0 - t;
+ distSqr = s*(a00*s+a01*t+2.0*b0) +
+ t*(a01*s+a11*t+2.0*b1)+c;
+ }
+ } else {
+ t = 0.0;
+ if (tmp1 <= 0.0) {
+ s = 1.0;
+ distSqr = a00+2.0*b0+c;
+ } else if (b0 >= 0.0) {
+ s = 0.0;
+ distSqr = c;
+ } else {
+ s = -b0/a00;
+ distSqr = b0*s+c;
+ }
+ }
+ } else {
+ // region 1
+ numer = a11 + b1 - a01 - b0;
+ if (numer <= 0.0) {
+ s = 0.0;
+ t = 1.0;
+ distSqr = a11+2.0*b1+c;
+ } else {
+ denom = a00-2.0*a01+a11;
+ if (numer >= denom ) {
+ s = 1.0;
+ t = 0.0;
+ distSqr = a00+2.0*b0+c;
+ } else {
+ s = numer/denom;
+ t = 1.0 - s;
+ distSqr = s*(a00*s+a01*t+2.0*b0) +
+ t*(a01*s+a11*t+2.0*b1)+c;
+ }
+ }
+ }
+ }
+
+ // Convert s,t into barycentric coordinates
+ uv[0] = 1.0 - s - t;
+ uv[1] = s;
+
+ return (distSqr < 0) ? 0.0 : distSqr;
+}
+
+} // namespace math
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/math/Proximity.h b/extern/openvdb/internal/openvdb/math/Proximity.h
new file mode 100644
index 00000000000..dfa2353c0ee
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/math/Proximity.h
@@ -0,0 +1,134 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+
+#ifndef OPENVDB_MATH_PROXIMITY_HAS_BEEN_INCLUDED
+#define OPENVDB_MATH_PROXIMITY_HAS_BEEN_INCLUDED
+
+#include <openvdb/Types.h>
+//#include <openvdb/openvdb.h>
+
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace math {
+
+
+/// @brief Closest Point on Triangle to Point. Given a triangle @c abc and a
+/// point @c p, returns the point on @c abc closest to @c p and the
+/// corresponding barycentric coordinates.
+///
+/// @note Algorithm from "Real-Time Collision Detection" pg 136 to 142 by Christer Ericson.
+/// The closest point is obtained by first determining which of the triangles
+/// Voronoi feature regions @c p is in and then computing the orthogonal projection
+/// of @c p onto the corresponding feature.
+///
+/// @param a The triangle's first vertex point.
+/// @param b The triangle's second vertex point.
+/// @param c The triangle's third vertex point.
+/// @param p Point to compute the closest point on @c abc for.
+/// @param uvw Barycentric coordinates, computed and returned.
+OPENVDB_API Vec3d
+closestPointOnTriangleToPoint(const Vec3d& a, const Vec3d& b, const Vec3d& c,
+ const Vec3d& p, Vec3d& uvw);
+
+
+////////////////////////////////////////
+
+
+// DEPRECATED METHODS
+
+
+/// @brief Squared distance of a line segment p(t) = (1-t)*p0 + t*p1 to point.
+/// @return the closest point on the line segment as a function of t
+OPENVDB_API OPENVDB_DEPRECATED double
+sLineSeg3ToPointDistSqr(const Vec3d &p0,
+ const Vec3d &p1,
+ const Vec3d &point,
+ double &t,
+ double epsilon = 1e-10);
+
+
+/// @brief Slightly modified version of the algorithm described in "Geometric Tools for
+/// Computer Graphics" pg 376 to 382 by Schneider and Eberly. Extended to handle
+/// the case of a degenerate triangle. Also returns barycentric rather than
+/// (s,t) coordinates.
+///
+/// Basic Idea (See book for details):
+///
+/// Write the equation of the line as
+///
+/// T(s,t) = v0 + s*(v1-v0) + t*(v2-v0)
+///
+/// Minimize the quadratic function
+///
+/// || T(s,t) - point || ^2
+///
+/// by solving for when the gradient is 0. This can be done without any
+/// square roots.
+///
+/// If the resulting solution satisfies 0 <= s + t <= 1, then the solution lies
+/// on the interior of the triangle, and we are done (region 0). If it does
+/// not then the closest solution lies on a boundary and we have to solve for
+/// it by solving a 1D problem where we use one variable as free say "s" and
+/// set the other variable t = (1-s)
+///
+/// @return the closest point on the triangle and barycentric coordinates.
+OPENVDB_API OPENVDB_DEPRECATED double
+sTri3ToPointDistSqr(const Vec3d &v0,
+ const Vec3d &v1,
+ const Vec3d &v2,
+ const Vec3d &point,
+ Vec2d &uv,
+ double epsilon);
+
+
+/// @return the closest point on the triangle.
+static inline OPENVDB_DEPRECATED double
+triToPtnDistSqr(const Vec3d &v0,
+ const Vec3d &v1,
+ const Vec3d &v2,
+ const Vec3d &point)
+{
+ Vec3d cpt, uvw;
+ cpt = closestPointOnTriangleToPoint(v0, v1, v2, point, uvw);
+ return (cpt - point).lengthSqr();
+}
+
+
+} // namespace math
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_TOOLS_MESH_TO_VOLUME_UTIL_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/math/QuantizedUnitVec.cc b/extern/openvdb/internal/openvdb/math/QuantizedUnitVec.cc
new file mode 100644
index 00000000000..3a538bec209
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/math/QuantizedUnitVec.cc
@@ -0,0 +1,98 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+
+#include "QuantizedUnitVec.h"
+#include <openvdb/Types.h>
+#include <tbb/atomic.h>
+#include <tbb/mutex.h>
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace math {
+
+
+////////////////////////////////////////
+
+
+bool QuantizedUnitVec::sInitialized = false;
+float QuantizedUnitVec::sNormalizationWeights[MASK_SLOTS + 1];
+
+// Declare this at file scope to ensure thread-safe initialization.
+tbb::mutex sInitMutex;
+
+
+////////////////////////////////////////
+
+
+void
+QuantizedUnitVec::init()
+{
+ tbb::mutex::scoped_lock(sInitMutex);
+
+ if (!sInitialized) {
+
+ OPENVDB_START_THREADSAFE_STATIC_WRITE
+
+ sInitialized = true;
+
+ uint16_t xbits, ybits;
+ double x, y, z, w;
+
+ for (uint16_t b = 0; b < 8192; ++b) {
+
+ xbits = (b & MASK_XSLOT) >> 7;
+ ybits = b & MASK_YSLOT;
+
+ if ((xbits + ybits) > 126) {
+ xbits = 127 - xbits;
+ ybits = 127 - ybits;
+ }
+
+ x = double(xbits);
+ y = double(ybits);
+ z = double(126 - ybits - xbits);
+ w = 1.0 / std::sqrt(x*x + y*y + z*z);
+
+ sNormalizationWeights[b] = float(w);
+ }
+
+ OPENVDB_FINISH_THREADSAFE_STATIC_WRITE
+ }
+}
+
+} // namespace math
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/math/QuantizedUnitVec.h b/extern/openvdb/internal/openvdb/math/QuantizedUnitVec.h
new file mode 100644
index 00000000000..7eac3e2c198
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/math/QuantizedUnitVec.h
@@ -0,0 +1,164 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+
+#ifndef OPENVDB_MATH_QUANTIZED_UNIT_VEC_HAS_BEEN_INCLUDED
+#define OPENVDB_MATH_QUANTIZED_UNIT_VEC_HAS_BEEN_INCLUDED
+
+#include <openvdb/Platform.h>
+#include <openvdb/version.h>
+#include "Vec3.h"
+#include <tbb/atomic.h>
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace math {
+
+
+// Bit compression method that effciently represents a unit vector using
+// 2 bytes i.e. 16 bits of data by only storing two quantized components.
+// Based on "Higher Accuracy Quantized Normals" article from GameDev.Net LLC, 2000
+
+class OPENVDB_API QuantizedUnitVec
+{
+public:
+
+ template <typename T>
+ static uint16_t pack(const Vec3<T>& vec);
+ static Vec3s unpack(const uint16_t data);
+
+ static void flipSignBits(uint16_t&);
+
+private:
+ QuantizedUnitVec() {}
+
+ // threadsafe initialization function for the normalization weights.
+ static void init();
+
+ // bit masks
+ static const uint16_t MASK_SLOTS = 0x1FFF; // 0001111111111111
+ static const uint16_t MASK_XSLOT = 0x1F80; // 0001111110000000
+ static const uint16_t MASK_YSLOT = 0x007F; // 0000000001111111
+ static const uint16_t MASK_XSIGN = 0x8000; // 1000000000000000
+ static const uint16_t MASK_YSIGN = 0x4000; // 0100000000000000
+ static const uint16_t MASK_ZSIGN = 0x2000; // 0010000000000000
+
+ // initialization flag.
+ static bool sInitialized;
+
+ // normalization weights, 32 kilobytes.
+ static float sNormalizationWeights[MASK_SLOTS + 1];
+}; // class QuantizedUnitVec
+
+
+////////////////////////////////////////
+
+
+template <typename T>
+inline uint16_t
+QuantizedUnitVec::pack(const Vec3<T>& vec)
+{
+ uint16_t data = 0;
+ T x(vec[0]), y(vec[1]), z(vec[2]);
+
+ // The sign of the three components are first stored using
+ // 3-bits and can then safely be discarded.
+ if (x < T(0.0)) { data |= MASK_XSIGN; x = -x; }
+ if (y < T(0.0)) { data |= MASK_YSIGN; y = -y; }
+ if (z < T(0.0)) { data |= MASK_ZSIGN; z = -z; }
+
+ // The z component is discarded and x & y are quantized in
+ // the 0 to 126 range.
+ T w = T(126.0) / (x + y + z);
+ uint16_t xbits = uint16_t((x * w) + T(0.5));
+ uint16_t ybits = uint16_t((y * w) + T(0.5));
+
+ // The remaining 13 bits in our 16 bit word are dividied into a
+ // 6-bit x-slot and a 7-bit y-slot. Both the xbits and the ybits
+ // can still be represented using (2^7 - 1) quantization levels.
+
+ // If the xbits requre more than 6-bits, store the complement.
+ // (xbits + ybits < 127, thus if xbits > 63 => ybits <= 63)
+ if(xbits > 63) {
+ xbits = 127 - xbits;
+ ybits = 127 - ybits;
+ }
+
+ // pack components into their respetive slot
+ data |= xbits << 7;
+ data |= ybits;
+ return data;
+}
+
+
+inline Vec3s
+QuantizedUnitVec::unpack(const uint16_t data)
+{
+ if (!sInitialized) init();
+
+ const float w = sNormalizationWeights[data & MASK_SLOTS];
+
+ uint16_t xbits = (data & MASK_XSLOT) >> 7;
+ uint16_t ybits = data & MASK_YSLOT;
+
+ // Check if the complement components where stored and revert.
+ if ((xbits + ybits) > 126) {
+ xbits = 127 - xbits;
+ ybits = 127 - ybits;
+ }
+
+ Vec3s vec(float(xbits) * w, float(ybits) * w, float(126 - xbits - ybits) * w);
+
+ if(data & MASK_XSIGN) vec[0] = -vec[0];
+ if(data & MASK_YSIGN) vec[1] = -vec[1];
+ if(data & MASK_ZSIGN) vec[2] = -vec[2];
+ return vec;
+}
+
+
+////////////////////////////////////////
+
+
+inline void
+QuantizedUnitVec::flipSignBits(uint16_t& v)
+{
+ v = (v & MASK_SLOTS) | (~v & ~MASK_SLOTS);
+}
+
+
+} // namespace math
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_MATH_QUANTIZED_UNIT_VEC_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/math/Quat.h b/extern/openvdb/internal/openvdb/math/Quat.h
new file mode 100644
index 00000000000..5b95082aaf7
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/math/Quat.h
@@ -0,0 +1,658 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+
+#ifndef OPENVDB_MATH_QUAT_H_HAS_BEEN_INCLUDED
+#define OPENVDB_MATH_QUAT_H_HAS_BEEN_INCLUDED
+
+#include <iostream>
+#include <cmath>
+
+#include "Mat.h"
+#include "Mat3.h"
+#include "Math.h"
+#include "Vec3.h"
+#include <openvdb/Exceptions.h>
+
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace math {
+
+template<typename T> class Quat;
+
+/// Linear interpolation between the two quaternions
+template <typename T>
+Quat<T> slerp(const Quat<T> &q1, const Quat<T> &q2, T t, T tolerance=0.00001)
+{
+ T qdot, angle, sineAngle;
+
+ qdot = q1.dot(q2);
+
+ if (fabs(qdot) >= 1.0) {
+ angle = 0; // not necessary but suppresses compiler warning
+ sineAngle = 0;
+ } else {
+ angle = acos(qdot);
+ sineAngle = sin(angle);
+ }
+
+ //
+ // Denominator close to 0 corresponds to the case where the
+ // two quaternions are close to the same rotation. In this
+ // case linear interpolation is used but we normalize to
+ // guarantee unit length
+ //
+ if (sineAngle <= tolerance) {
+ T s = 1.0 - t;
+
+ Quat<T> qtemp(s * q1[0] + t * q2[0], s * q1[1] + t * q2[1],
+ s * q1[2] + t * q2[2], s * q1[3] + t * q2[3]);
+ //
+ // Check the case where two close to antipodal quaternions were
+ // blended resulting in a nearly zero result which can happen,
+ // for example, if t is close to 0.5. In this case it is not safe
+ // to project back onto the sphere.
+ //
+ double lengthSquared = qtemp.dot(qtemp);
+
+ if (lengthSquared <= tolerance * tolerance) {
+ qtemp = (t < 0.5) ? q1 : q2;
+ } else {
+ qtemp *= 1.0 / sqrt(lengthSquared);
+ }
+ return qtemp;
+ } else {
+
+ T sine = 1.0 / sineAngle;
+ T a = sin((1.0 - t) * angle) * sine;
+ T b = sin(t * angle) * sine;
+ return Quat<T>(a * q1[0] + b * q2[0], a * q1[1] + b * q2[1],
+ a * q1[2] + b * q2[2], a * q1[3] + b * q2[3]);
+ }
+
+}
+
+template<typename T>
+class Quat
+{
+public:
+ /// Trivial constructor, the quaternion is NOT initialized
+ Quat() {}
+
+ /// Constructor with four arguments, e.g. Quatf q(1,2,3,4);
+ Quat(T x, T y, T z, T w)
+ {
+ mm[0] = x;
+ mm[1] = y;
+ mm[2] = z;
+ mm[3] = w;
+
+ }
+
+ /// Constructor with array argument, e.g. float a[4]; Quatf q(a);
+ Quat(T *a)
+ {
+ mm[0] = a[0];
+ mm[1] = a[1];
+ mm[2] = a[2];
+ mm[3] = a[3];
+
+ }
+
+ /// Constructor given rotation as axis and angle, the axis must be
+ /// unit vector
+ Quat(const Vec3<T> &axis, T angle)
+ {
+ // assert( REL_EQ(axis.length(), 1.) );
+
+ T s = sin(angle*T(0.5));
+
+ mm[0] = axis.x() * s;
+ mm[1] = axis.y() * s;
+ mm[2] = axis.z() * s;
+
+ mm[3] = cos(angle*T(0.5));
+
+ }
+
+ /// Constructor given rotation as axis and angle
+ Quat(math::Axis axis, T angle)
+ {
+ T s = sin(angle*T(0.5));
+
+ mm[0] = (axis==math::X_AXIS) * s;
+ mm[1] = (axis==math::Y_AXIS) * s;
+ mm[2] = (axis==math::Z_AXIS) * s;
+
+ mm[3] = cos(angle*T(0.5));
+ }
+
+ /// Constructor given a rotation matrix
+ template<typename T1>
+ Quat(const Mat3<T1> &rot) {
+
+ // verify that the matrix is really a rotation
+ if(!isUnitary(rot)) { // unitary is reflection or rotation
+ OPENVDB_THROW(ArithmeticError,
+ "A non-rotation matrix can not be used to construct a quaternion");
+ }
+ if (!isApproxEqual(rot.det(), (T1)1)) { // rule out reflection
+ OPENVDB_THROW(ArithmeticError,
+ "A reflection matrix can not be used to construct a quaternion");
+ }
+
+ T trace = (T)rot.trace();
+ if (trace > 0) {
+
+ T q_w = 0.5 * std::sqrt(trace+1);
+ T factor = 0.25 / q_w;
+
+ mm[0] = factor * (rot(1,2) - rot(2,1));
+ mm[1] = factor * (rot(2,0) - rot(0,2));
+ mm[2] = factor * (rot(0,1) - rot(1,0));
+ mm[3] = q_w;
+ } else if (rot(0,0) > rot(1,1) && rot(0,0) > rot(2,2)) {
+
+ T q_x = 0.5 * sqrt(rot(0,0)- rot(1,1)-rot(2,2)+1);
+ T factor = 0.25 / q_x;
+
+ mm[0] = q_x;
+ mm[1] = factor * (rot(0,1) + rot(1,0));
+ mm[2] = factor * (rot(2,0) + rot(0,2));
+ mm[3] = factor * (rot(1,2) - rot(2,1));
+ } else if (rot(1,1) > rot(2,2)) {
+
+ T q_y = 0.5 * sqrt(rot(1,1)-rot(0,0)-rot(2,2)+1);
+ T factor = 0.25 / q_y;
+
+ mm[0] = factor * (rot(0,1) + rot(1,0));
+ mm[1] = q_y;
+ mm[2] = factor * (rot(1,2) + rot(2,1));
+ mm[3] = factor * (rot(2,0) - rot(0,2));
+ } else {
+
+ T q_z = 0.5 * sqrt(rot(2,2)-rot(0,0)-rot(1,1)+1);
+ T factor = 0.25 / q_z;
+
+ mm[0] = factor * (rot(2,0) + rot(0,2));
+ mm[1] = factor * (rot(1,2) + rot(2,1));
+ mm[2] = q_z;
+ mm[3] = factor * (rot(0,1) - rot(1,0));
+ }
+ }
+
+ /// Copy constructor
+ Quat(const Quat &q)
+ {
+ mm[0] = q.mm[0];
+ mm[1] = q.mm[1];
+ mm[2] = q.mm[2];
+ mm[3] = q.mm[3];
+
+ }
+
+ /// Reference to the component, e.g. q.x() = 4.5f;
+ T& x() { return mm[0]; }
+ T& y() { return mm[1]; }
+ T& z() { return mm[2]; }
+ T& w() { return mm[3]; }
+
+ /// Get the component, e.g. float f = q.w();
+ T x() const { return mm[0]; }
+ T y() const { return mm[1]; }
+ T z() const { return mm[2]; }
+ T w() const { return mm[3]; }
+
+ // Number of elements
+ static unsigned numElements() { return 4; }
+
+ /// Array style reference to the components, e.g. q[3] = 1.34f;
+ T& operator[](int i) { return mm[i]; }
+
+ /// Array style constant reference to the components, e.g. float f = q[1];
+ T operator[](int i) const { return mm[i]; }
+
+ /// Cast to T*
+ operator T*() { return mm; }
+ operator const T*() const { return mm; }
+
+ /// Alternative indexed reference to the elements
+ T& operator()(int i) { return mm[i]; }
+
+ /// Alternative indexed constant reference to the elements,
+ T operator()(int i) const { return mm[i]; }
+
+ /// Return angle of rotation
+ T angle() const
+ {
+ T sqrLength = mm[0]*mm[0] + mm[1]*mm[1] + mm[2]*mm[2];
+
+ if ( sqrLength > 1.0e-8 ) {
+
+ return T(2.0) * acos(mm[3]);
+
+ } else {
+
+ return T(0.0);
+ }
+ }
+
+ /// Return axis of rotation
+ Vec3<T> axis() const
+ {
+ T sqrLength = mm[0]*mm[0] + mm[1]*mm[1] + mm[2]*mm[2];
+
+ if ( sqrLength > 1.0e-8 ) {
+
+ T invLength = T(1)/sqrt(sqrLength);
+
+ return Vec3<T>( mm[0]*invLength, mm[1]*invLength, mm[2]*invLength );
+ } else {
+
+ return Vec3<T>(1,0,0);
+ }
+ }
+
+
+ /// "this" quaternion gets initialized to [x, y, z, w]
+ Quat& init(T x, T y, T z, T w)
+ {
+ mm[0] = x; mm[1] = y; mm[2] = z; mm[3] = w;
+ return *this;
+ }
+
+ /// "this" quaternion gets initialized to identity, same as setIdentity()
+ Quat& init() { return setIdentity(); }
+
+ /// Set "this" quaternion to rotation specified by axis and angle,
+ /// the axis must be unit vector
+ Quat& setAxisAngle(const Vec3<T>& axis, T angle)
+ {
+
+ T s = sin(angle*T(0.5));
+
+ mm[0] = axis.x() * s;
+ mm[1] = axis.y() * s;
+ mm[2] = axis.z() * s;
+
+ mm[3] = cos(angle*T(0.5));
+
+ return *this;
+ } // axisAngleTest
+
+ /// Set "this" vector to zero
+ Quat& setZero()
+ {
+ mm[0] = mm[1] = mm[2] = mm[3] = 0;
+ return *this;
+ }
+
+ /// Set "this" vector to identity
+ Quat& setIdentity()
+ {
+ mm[0] = mm[1] = mm[2] = 0;
+ mm[3] = 1;
+ return *this;
+ }
+
+ /// Returns vector of x,y,z rotational components
+ Vec3<T> eulerAngles(RotationOrder rotationOrder) const
+ { return math::eulerAngles(Mat3<T>(*this), rotationOrder); }
+
+ /// Assignment operator
+ Quat& operator=(const Quat &q)
+ {
+ mm[0] = q.mm[0];
+ mm[1] = q.mm[1];
+ mm[2] = q.mm[2];
+ mm[3] = q.mm[3];
+
+ return *this;
+ }
+
+ /// Equality operator, does exact floating point comparisons
+ bool operator==(const Quat &q) const
+ {
+ return (isExactlyEqual(mm[0],q.mm[0]) &&
+ isExactlyEqual(mm[1],q.mm[1]) &&
+ isExactlyEqual(mm[2],q.mm[2]) &&
+ isExactlyEqual(mm[3],q.mm[3]) );
+ }
+
+ /// Test if "this" is equivalent to q with tolerance of eps value
+ bool eq(const Quat &q, T eps=1.0e-7) const
+ {
+ return isApproxEqual(mm[0],q.mm[0],eps) && isApproxEqual(mm[1],q.mm[1],eps) &&
+ isApproxEqual(mm[2],q.mm[2],eps) && isApproxEqual(mm[3],q.mm[3],eps) ;
+ } // trivial
+
+ /// Add quaternion q to "this" quaternion, e.g. q += q1;
+ Quat& operator+=(const Quat &q)
+ {
+ mm[0] += q.mm[0];
+ mm[1] += q.mm[1];
+ mm[2] += q.mm[2];
+ mm[3] += q.mm[3];
+
+ return *this;
+ }
+
+ /// Subtract quaternion q from "this" quaternion, e.g. q -= q1;
+ Quat& operator-=(const Quat &q)
+ {
+ mm[0] -= q.mm[0];
+ mm[1] -= q.mm[1];
+ mm[2] -= q.mm[2];
+ mm[3] -= q.mm[3];
+
+ return *this;
+ }
+
+ /// Scale "this" quaternion by scalar, e.g. q *= scalar;
+ Quat& operator*=(T scalar)
+ {
+ mm[0] *= scalar;
+ mm[1] *= scalar;
+ mm[2] *= scalar;
+ mm[3] *= scalar;
+
+ return *this;
+ }
+
+ /// Return (this+q), e.g. q = q1 + q2;
+ Quat operator+(const Quat &q) const
+ {
+ return Quat<T>(mm[0]+q.mm[0], mm[1]+q.mm[1], mm[2]+q.mm[2], mm[3]+q.mm[3]);
+ }
+
+ /// Return (this-q), e.g. q = q1 - q2;
+ Quat operator-(const Quat &q) const
+ {
+ return Quat<T>(mm[0]-q.mm[0], mm[1]-q.mm[1], mm[2]-q.mm[2], mm[3]-q.mm[3]);
+ }
+
+ /// Return (this*q), e.g. q = q1 * q2;
+ Quat operator*(const Quat &q) const
+ {
+ Quat<T> prod;
+
+ prod.mm[0] = mm[3]*q.mm[0] + mm[0]*q.mm[3] + mm[1]*q.mm[2] - mm[2]*q.mm[1];
+ prod.mm[1] = mm[3]*q.mm[1] + mm[1]*q.mm[3] + mm[2]*q.mm[0] - mm[0]*q.mm[2];
+ prod.mm[2] = mm[3]*q.mm[2] + mm[2]*q.mm[3] + mm[0]*q.mm[1] - mm[1]*q.mm[0];
+ prod.mm[3] = mm[3]*q.mm[3] - mm[0]*q.mm[0] - mm[1]*q.mm[1] - mm[2]*q.mm[2];
+
+ return prod;
+
+ }
+
+ /// Assigns this to (this*q), e.g. q *= q1;
+ Quat operator*=(const Quat &q)
+ {
+ *this = *this * q;
+ return *this;
+ }
+
+ /// Return (this*scalar), e.g. q = q1 * scalar;
+ Quat operator*(T scalar) const
+ {
+ return Quat<T>(mm[0]*scalar, mm[1]*scalar, mm[2]*scalar, mm[3]*scalar);
+ }
+
+ /// Return (this/scalar), e.g. q = q1 / scalar;
+ Quat operator/(T scalar) const
+ {
+ return Quat<T>(mm[0]/scalar, mm[1]/scalar, mm[2]/scalar, mm[3]/scalar);
+ }
+
+ /// Negation operator, e.g. q = -q;
+ Quat operator-() const
+ { return Quat<T>(-mm[0], -mm[1], -mm[2], -mm[3]); }
+
+ /// this = q1 + q2
+ /// "this", q1 and q2 need not be distinct objects, e.g. q.add(q1,q);
+ Quat& add(const Quat &q1, const Quat &q2)
+ {
+ mm[0] = q1.mm[0] + q2.mm[0];
+ mm[1] = q1.mm[1] + q2.mm[1];
+ mm[2] = q1.mm[2] + q2.mm[2];
+ mm[3] = q1.mm[3] + q2.mm[3];
+
+ return *this;
+ }
+
+ /// this = q1 - q2
+ /// "this", q1 and q2 need not be distinct objects, e.g. q.sub(q1,q);
+ Quat& sub(const Quat &q1, const Quat &q2)
+ {
+ mm[0] = q1.mm[0] - q2.mm[0];
+ mm[1] = q1.mm[1] - q2.mm[1];
+ mm[2] = q1.mm[2] - q2.mm[2];
+ mm[3] = q1.mm[3] - q2.mm[3];
+
+ return *this;
+ }
+
+ /// this = q1 * q2
+ /// q1 and q2 must be distinct objects than "this", e.g. q.mult(q1,q2);
+ Quat& mult(const Quat &q1, const Quat &q2)
+ {
+ mm[0] = q1.mm[3]*q2.mm[0] + q1.mm[0]*q2.mm[3] +
+ q1.mm[1]*q2.mm[2] - q1.mm[2]*q2.mm[1];
+ mm[1] = q1.mm[3]*q2.mm[1] + q1.mm[1]*q2.mm[3] +
+ q1.mm[2]*q2.mm[0] - q1.mm[0]*q2.mm[2];
+ mm[2] = q1.mm[3]*q2.mm[2] + q1.mm[2]*q2.mm[3] +
+ q1.mm[0]*q2.mm[1] - q1.mm[1]*q2.mm[0];
+ mm[3] = q1.mm[3]*q2.mm[3] - q1.mm[0]*q2.mm[0] -
+ q1.mm[1]*q2.mm[1] - q1.mm[2]*q2.mm[2];
+
+ return *this;
+ }
+
+ /// this = scalar*q, q need not be distinct object than "this",
+ /// e.g. q.scale(1.5,q1);
+ Quat& scale(T scale, const Quat &q)
+ {
+ mm[0] = scale * q.mm[0];
+ mm[1] = scale * q.mm[1];
+ mm[2] = scale * q.mm[2];
+ mm[3] = scale * q.mm[3];
+
+ return *this;
+ }
+
+ /// Dot product
+ T dot(const Quat &q) const
+ {
+ return (mm[0]*q.mm[0] + mm[1]*q.mm[1] + mm[2]*q.mm[2] + mm[3]*q.mm[3]);
+ }
+
+ /// Return the quaternion rate corrsponding to the angular velocity omega
+ /// and "this" current rotation
+ Quat derivative(const Vec3<T>& omega) const
+ {
+ return Quat<T>( +w()*omega.x() -z()*omega.y() +y()*omega.z() ,
+ +z()*omega.x() +w()*omega.y() -x()*omega.z() ,
+ -y()*omega.x() +x()*omega.y() +w()*omega.z() ,
+ -x()*omega.x() -y()*omega.y() -z()*omega.z() );
+ }
+
+ /// this = normalized this
+ bool normalize(T eps =1.0e-8)
+ {
+ T d = sqrt(mm[0]*mm[0] + mm[1]*mm[1] + mm[2]*mm[2] + mm[3]*mm[3]);
+ if( isApproxEqual(d, T(0.0), eps) ) return false;
+ *this *= ( T(1)/d );
+ return true;
+ }
+
+ /// this = normalized this
+ Quat unit() const
+ {
+ T d = sqrt(mm[0]*mm[0] + mm[1]*mm[1] + mm[2]*mm[2] + mm[3]*mm[3]);
+ if( isExactlyEqual(d , T(0.0) ) )
+ OPENVDB_THROW(ArithmeticError,
+ "Normalizing degenerate quaternion");
+ return *this / d;
+ }
+
+ /// returns inverse of this
+ Quat inverse(T tolerance = 0)
+ {
+ T d = mm[0]*mm[0] + mm[1]*mm[1] + mm[2]*mm[2] + mm[3]*mm[3];
+ if( isApproxEqual(d, T(0.0), tolerance) )
+ OPENVDB_THROW(ArithmeticError,
+ "Cannot invert degenerate quaternion");
+ Quat result = *this/-d;
+ result.mm[3] = -result.mm[3];
+ return result;
+ }
+
+
+ /// Return the conjugate of "this", same as invert without
+ /// unit quaternion test
+ Quat conjugate() const
+ {
+ return Quat<T>(-mm[0], -mm[1], -mm[2], mm[3]);
+ }
+
+ /// Return rotated vector by "this" quaternion
+ Vec3<T> rotateVector(const Vec3<T> &v) const
+ {
+ Mat3<T> m(*this);
+ return m.transform(v);
+ }
+
+ /// Predefined constants, e.g. Quat q = Quat::identity();
+ static Quat zero() { return Quat<T>(0,0,0,0); }
+ static Quat identity() { return Quat<T>(0,0,0,1); }
+
+ /// @return string representation of Classname
+ std::string
+ str() const {
+ std::ostringstream buffer;
+
+ buffer << "[";
+
+ // For each column
+ for (unsigned j(0); j < 4; j++) {
+ if (j) buffer << ", ";
+ buffer << mm[j];
+ }
+
+ buffer << "]";
+
+ return buffer.str();
+ }
+
+ /// Output to the stream, e.g. std::cout << q << std::endl;
+ friend std::ostream& operator<<(std::ostream &stream, const Quat &q)
+ {
+ stream << q.str();
+ return stream;
+ }
+
+ friend Quat slerp<>(const Quat &q1, const Quat &q2, T t, T tolerance);
+
+
+ void write(std::ostream& os) const {
+ os.write((char*)&mm, sizeof(T)*4);
+ }
+ void read(std::istream& is) {
+ is.read((char*)&mm, sizeof(T)*4);
+ }
+
+protected:
+ T mm[4];
+};
+
+/// Returns V, where \f$V_i = v_i * scalar\f$ for \f$i \in [0, 3]\f$
+template <typename S, typename T>
+Quat<T> operator*(S scalar, const Quat<T> &q) { return q*scalar; }
+
+
+/// @brief Interpolate between m1 and m2.
+/// Converts to quaternion form and uses slerp
+/// m1 and m2 must be rotation matrices!
+template <typename T, typename T0>
+Mat3<T> slerp(const Mat3<T0> &m1, const Mat3<T0> &m2, T t)
+{
+ typedef Mat3<T> MatType;
+
+ Quat<T> q1(m1);
+ Quat<T> q2(m2);
+
+ if (q1.dot(q2) < 0) q2 *= -1;
+
+ Quat<T> qslerp = slerp<T>(q1, q2, static_cast<T>(t));
+ MatType m = rotation<MatType>(qslerp);
+ return m;
+}
+
+
+
+/// Interpolate between m1 and m4 by converting m1 ... m4 into
+/// quaternions and treating them as control points of a Bezier
+/// curve using slerp in place of lerp in the De Castlejeau evaluation
+/// algorithm. Just like a cubic Bezier curve, this will interpolate
+/// m1 at t = 0 and m4 at t = 1 but in general will not pass through
+/// m2 and m3. Unlike a standard Bezier curve this curve will not have
+/// the convex hull property.
+/// m1 ... m4 must be rotation matrices!
+template <typename T, typename T0>
+Mat3<T> bezLerp(const Mat3<T0> &m1, const Mat3<T0> &m2,
+ const Mat3<T0> &m3, const Mat3<T0> &m4,
+ T t)
+{
+ Mat3<T> m00, m01, m02, m10, m11;
+
+ m00 = slerp(m1, m2, t);
+ m01 = slerp(m2, m3, t);
+ m02 = slerp(m3, m4, t);
+
+ m10 = slerp(m00, m01, t);
+ m11 = slerp(m01, m02, t);
+
+ return slerp(m10, m11, t);
+}
+
+typedef Quat<float> Quats;
+typedef Quat<double> Quatd;
+
+} // namespace math
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif //OPENVDB_MATH_QUAT_H_HAS_BEEN_INCLUDED
+
+// ---------------------------------------------------------------------------
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/math/Stats.h b/extern/openvdb/internal/openvdb/math/Stats.h
new file mode 100644
index 00000000000..7985a22d76a
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/math/Stats.h
@@ -0,0 +1,236 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+//
+/// @file Stats.h
+///
+/// @brief Classes to compute statistics and histograms
+
+#ifndef OPENVDB_MATH_STATS_HAS_BEEN_INCLUDED
+#define OPENVDB_MATH_STATS_HAS_BEEN_INCLUDED
+
+#include <iosfwd> // for ostringstream
+
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace math {
+
+/// @brief This class computes statistics (minimum value, maximum
+/// value, mean, variance and standard deviation) of a population
+/// of floating-point values.
+///
+/// @details variance = Mean[ (X-Mean[X])^2 ] = Mean[X^2] - Mean[X]^2,
+/// standard deviation = sqrt(variance)
+///
+/// @note This class employs incremental computation and double precision.
+class Stats
+{
+public:
+ Stats(): mSize(0), mAvg(0.0), mAux(0.0),
+ mMin(std::numeric_limits<double>::max()), mMax(-mMin) {}
+
+ /// Add a single sample.
+ void add(double val)
+ {
+ mSize++;
+ mMin = std::min<double>(val, mMin);
+ mMax = std::max<double>(val, mMax);
+ const double delta = val - mAvg;
+ mAvg += delta/double(mSize);
+ mAux += delta*(val - mAvg);
+ }
+
+ /// Add @a n samples with constant value @a val.
+ void add(double val, uint64_t n)
+ {
+ mMin = std::min<double>(val, mMin);
+ mMax = std::max<double>(val, mMax);
+ const double denom = 1.0/double(mSize + n);
+ const double delta = val - mAvg;
+ mAvg += denom*delta*n;
+ mAux += denom*delta*delta*mSize*n;
+ mSize += n;
+ }
+
+ /// Add the samples from the other Stats instance.
+ void add(const Stats& other)
+ {
+ mMin = std::min<double>(mMin, other.mMin);
+ mMax = std::max<double>(mMax, other.mMax);
+ const double denom = 1.0/double(mSize + other.mSize);
+ const double delta = other.mAvg - mAvg;
+ mAvg += denom*delta*other.mSize;
+ mAux += other.mAux + denom*delta*delta*mSize*other.mSize;
+ mSize += other.mSize;
+ }
+
+ /// Return the size of the population, i.e., the total number of samples.
+ uint64_t size() const { return mSize; }
+ /// Return the minimum value.
+ double min() const { return mMin; }
+ /// Return the maximum value.
+ double max() const { return mMax; }
+ /// Return the mean value.
+ double mean() const { return mAvg; }
+ /// @brief Return the population variance.
+ /// @note The unbiased sample variance = population variance * num/(num-1)
+ double variance() const { return mSize<2 ? 0.0 : mAux/double(mSize); }
+ /// @brief Return the standard deviation (=Sqrt(variance)) as
+ /// defined from the (biased) population variance.
+ double stdDev() const { return sqrt(this->variance()); }
+
+ /// @brief Print statistics to the specified output stream.
+ void print(const std::string &name= "", std::ostream &strm=std::cout, int precision=3) const
+ {
+ // Write to a temporary string stream so as not to affect the state
+ // (precision, field width, etc.) of the output stream.
+ std::ostringstream os;
+ os << std::setprecision(precision) << std::setiosflags(std::ios::fixed);
+ os << "Statistics ";
+ if (!name.empty()) os << "for \"" << name << "\" ";
+ os << "with " << mSize << " samples:\n"
+ << " Min=" << mMin
+ << ", Max=" << mMax
+ << ", Ave=" << mAvg
+ << ", Std=" << this->stdDev()
+ << ", Var=" << this->variance() << std::endl;
+ strm << os.str();
+ }
+
+private:
+ uint64_t mSize;
+ double mAvg, mAux, mMin, mMax;
+}; // end Stats
+
+
+////////////////////////////////////////
+
+
+/// @brief This class computes a histogram, with a fixed interval width,
+/// of a population of floating-point values.
+class Histogram
+{
+public:
+ /// Construct with given minimum and maximum values and the given bin count.
+ Histogram(double min, double max, size_t numBins = 10)
+ : mSize(0), mMin(min), mMax(max+1e-10),
+ mDelta(double(numBins)/(max-min)), mBins(numBins)
+ {
+ assert(numBins > 1);
+ assert(mMax-mMin > 1e-10);
+ for (size_t i=0; i<numBins; ++i) mBins[i]=0;
+ }
+
+ /// @brief Construct with the given bin count and with minimum and maximum values
+ /// taken from a Stats object.
+ Histogram(const Stats& s, size_t numBins = 10):
+ mSize(0), mMin(s.min()), mMax(s.max()+1e-10),
+ mDelta(double(numBins)/(mMax-mMin)), mBins(numBins)
+ {
+ assert(numBins > 1);
+ assert(mMax-mMin > 1e-10);
+ for (size_t i=0; i<numBins; ++i) mBins[i]=0;
+ }
+
+ /// @brief Add @a n samples with constant value @a val, provided that the
+ /// @a val falls within this histogram's value range.
+ /// @return @c true if the sample value falls within this histogram's value range.
+ bool add(double val, uint64_t n = 1)
+ {
+ if (val<mMin || val>mMax) return false;
+ mBins[size_t(mDelta*(val-mMin))] += n;
+ mSize += n;
+ return true;
+ }
+
+ /// @brief Add all the contributions from the other histogram, provided that
+ /// it has the same configuration as this histogram.
+ bool add(const Histogram& other)
+ {
+ if (!isApproxEqual(mMin, other.mMin) || !isApproxEqual(mMax, other.mMax) ||
+ mBins.size() != other.mBins.size()) return false;
+ for (size_t i=0, e=mBins.size(); i!=e; ++i) mBins[i] += other.mBins[i];
+ mSize += other.mSize;
+ return true;
+ }
+
+ /// Return the number of bins in this histogram.
+ size_t numBins() const { return mBins.size(); }
+ /// Return the lower bound of this histogram's value range.
+ double min() const { return mMin; }
+ /// Return the upper bound of this histogram's value range.
+ double max() const { return mMax; }
+ /// Return the minimum value in the <i>n</i>th bin.
+ double min(int n) const { return mMin+n/mDelta; }
+ /// Return the maximum value in the <i>n</i>th bin.
+ double max(int n) const { return mMin+(n+1)/mDelta; }
+ /// Return the number of samples in the <i>n</i>th bin.
+ uint64_t count(int n) const { return mBins[n]; }
+ /// Return the population size, i.e., the total number of samples.
+ uint64_t size() const { return mSize; }
+
+ /// Print the histogram to the specified output stream.
+ void print(const std::string& name = "", std::ostream& strm = std::cout) const
+ {
+ // Write to a temporary string stream so as not to affect the state
+ // (precision, field width, etc.) of the output stream.
+ std::ostringstream os;
+ os << std::setprecision(6) << std::setiosflags(std::ios::fixed) << std::endl;
+ os << "Histogram ";
+ if (!name.empty()) os << "for \"" << name << "\" ";
+ os << "with " << mSize << " samples:\n";
+ os << "==============================================================\n";
+ os << "|| # | Min | Max | Frequency | % ||\n";
+ os << "==============================================================\n";
+ for (size_t i=0, e=mBins.size(); i!=e; ++i) {
+ os << "|| " << std::setw(4) << i << " | " << std::setw(14) << this->min(i) << " | "
+ << std::setw(14) << this->max(i) << " | " << std::setw(9) << mBins[i] << " | "
+ << std::setw(3) << (100*mBins[i]/mSize) << " ||\n";
+ }
+ os << "==============================================================\n";
+ strm << os.str();
+ }
+
+private:
+ uint64_t mSize;
+ double mMin, mMax, mDelta;
+ std::vector<uint64_t> mBins;
+};
+
+} // namespace math
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_MATH_STATS_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/math/Stencils.h b/extern/openvdb/internal/openvdb/math/Stencils.h
new file mode 100644
index 00000000000..bfdf18fe646
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/math/Stencils.h
@@ -0,0 +1,1466 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+//
+/// @author Ken Museth
+/// @file Stencils.h
+
+#ifndef OPENVDB_MATH_STENCILS_HAS_BEEN_INCLUDED
+#define OPENVDB_MATH_STENCILS_HAS_BEEN_INCLUDED
+
+#include <algorithm>
+#include <vector>
+#include <openvdb/math/Math.h> // for Pow2, needed by WENO and Gudonov
+#include <openvdb/Types.h> // for Real
+#include <openvdb/math/Coord.h> // for Coord
+#include <openvdb/math/FiniteDifference.h> // for WENO5 and GudonovsNormSqrd
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace math {
+
+
+////////////////////////////////////////
+
+
+template<typename _GridType, typename StencilType>
+class BaseStencil
+{
+public:
+ typedef _GridType GridType;
+ typedef typename GridType::TreeType TreeType;
+ typedef typename GridType::ValueType ValueType;
+ typedef std::vector<ValueType> BufferType;
+ typedef typename BufferType::iterator IterType;
+
+ /// Initialize the stencil buffer with the values of voxel (x, y, z)
+ /// and its neighbors.
+ inline void moveTo(const Coord& ijk)
+ {
+ mCenter = ijk;
+ mStencil[0] = mCache.getValue(ijk);
+ static_cast<StencilType&>(*this).init(mCenter);
+ }
+ /// @brief Initialize the stencil buffer with the values of voxel
+ /// (x, y, z) and its neighbors.
+ ///
+ /// @note This version is slightly faster than the one above, since
+ /// the center voxel's value is read directly from the iterator.
+ template<typename IterType>
+ inline void moveTo(const IterType& iter)
+ {
+ mCenter = iter.getCoord();
+ mStencil[0] = *iter;
+ static_cast<StencilType&>(*this).init(mCenter);
+ }
+
+ /// @brief Return the value from the stencil buffer with linear
+ /// offset pos.
+ ///
+ /// The default (@a pos = 0) corresponds to the center point of the stencil.
+ inline ValueType getValue(unsigned int pos = 0) const
+ {
+ assert(pos < mStencil.size());
+ return mStencil[pos];
+ }
+
+ /// Return the value at the specified location relative to the center of the stencil
+ template<int i, int j, int k>
+ const ValueType& getValue() const
+ {
+ return mStencil[static_cast<const StencilType&>(*this).template pos<i,j,k>()];
+ }
+
+ /// Set the value at the specified location relative to the center of the stencil
+ template<int i, int j, int k>
+ void setValue(const ValueType& value)
+ {
+ mStencil[static_cast<const StencilType&>(*this).template pos<i,j,k>()] = value;
+ }
+
+ /// Return the size of the stencil buffer.
+ inline int size() { return mStencil.size(); }
+
+ /// Return the median value of the current stencil.
+ inline ValueType median() const
+ {
+ std::vector<ValueType> tmp(mStencil);//local copy
+ assert(!tmp.empty());
+ size_t midpoint = (tmp.size() - 1) >> 1;
+ // Partially sort the vector until the median value is at the midpoint.
+ std::nth_element(tmp.begin(), tmp.begin() + midpoint, tmp.end());
+ return tmp[midpoint];
+ }
+
+ /// Return the mean value of the current stencil.
+ inline ValueType mean() const
+ {
+ double sum = 0.0;
+ for (int n=0, s=mStencil.size(); n<s; ++n) sum += mStencil[n];
+ return ValueType(sum / mStencil.size());
+ }
+
+ /// Return the smallest value in the stencil buffer.
+ inline ValueType min() const
+ {
+ IterType iter = std::min_element(mStencil.begin(), mStencil.end());
+ return *iter;
+ }
+
+ /// Return the largest value in the stencil buffer.
+ inline ValueType max() const
+ {
+ IterType iter = std::max_element(mStencil.begin(), mStencil.end());
+ return *iter;
+ }
+
+ /// Return the coordinates of the center point of the stencil.
+ inline const Coord& getCenterCoord() const { return mCenter; }
+
+ /// Return the value at the center of the stencil
+ inline const ValueType& getCenterValue() const
+ {
+ return this->getValue<0,0,0>();
+ }
+
+ /// Return true if the center of the stencil intersects the
+ /// iso-contour specified by the isoValue
+ inline bool intersects(const ValueType &isoValue = zeroVal<ValueType>()) const
+ {
+ const bool less = this->getValue< 0, 0, 0>() < isoValue;
+ return (less ^ (this->getValue<-1, 0, 0>() < isoValue)) ||
+ (less ^ (this->getValue< 1, 0, 0>() < isoValue)) ||
+ (less ^ (this->getValue< 0,-1, 0>() < isoValue)) ||
+ (less ^ (this->getValue< 0, 1, 0>() < isoValue)) ||
+ (less ^ (this->getValue< 0, 0,-1>() < isoValue)) ||
+ (less ^ (this->getValue< 0, 0, 1>() < isoValue)) ;
+ }
+
+protected:
+ // Constructor is protected to prevent direct instantiation.
+ BaseStencil(const GridType& grid, int size):
+ mCache(grid.getConstAccessor()),
+ mStencil(size)
+ {
+ }
+
+ typename GridType::ConstAccessor mCache;
+ BufferType mStencil;
+ Coord mCenter;
+
+}; // class BaseStencil
+
+
+////////////////////////////////////////
+
+
+namespace { // anonymous namespace for stencil-layout map
+
+ // the seven point stencil
+ template<int i, int j, int k> struct SevenPt {};
+ template<> struct SevenPt< 0, 0, 0> { enum { idx = 0 }; };
+ template<> struct SevenPt< 1, 0, 0> { enum { idx = 1 }; };
+ template<> struct SevenPt< 0, 1, 0> { enum { idx = 2 }; };
+ template<> struct SevenPt< 0, 0, 1> { enum { idx = 3 }; };
+ template<> struct SevenPt<-1, 0, 0> { enum { idx = 4 }; };
+ template<> struct SevenPt< 0,-1, 0> { enum { idx = 5 }; };
+ template<> struct SevenPt< 0, 0,-1> { enum { idx = 6 }; };
+
+}
+
+
+template<typename GridType>
+class SevenPointStencil: public BaseStencil<GridType, SevenPointStencil<GridType> >
+{
+public:
+ typedef BaseStencil<GridType, SevenPointStencil<GridType> > BaseType;
+ typedef typename BaseType::BufferType BufferType;
+ typedef typename GridType::ValueType ValueType;
+ typedef math::Vec3<ValueType> Vec3Type;
+ static const int SIZE = 7;
+
+ SevenPointStencil(const GridType& grid): BaseType(grid, SIZE) {}
+
+ /// Return linear offset for the specified stencil point relative to its center
+ template<int i, int j, int k>
+ unsigned int pos() const { return SevenPt<i,j,k>::idx; }
+
+private:
+ inline void init(const Coord& ijk)
+ {
+ BaseType::template setValue< 0, 0, 0>(mCache.getValue(ijk));
+
+ BaseType::template setValue<-1, 0, 0>(mCache.getValue(ijk.offsetBy(-1, 0, 0)));
+ BaseType::template setValue< 1, 0, 0>(mCache.getValue(ijk.offsetBy( 1, 0, 0)));
+
+ BaseType::template setValue< 0,-1, 0>(mCache.getValue(ijk.offsetBy( 0,-1, 0)));
+ BaseType::template setValue< 0, 1, 0>(mCache.getValue(ijk.offsetBy( 0, 1, 0)));
+
+ BaseType::template setValue< 0, 0,-1>(mCache.getValue(ijk.offsetBy( 0, 0,-1)));
+ BaseType::template setValue< 0, 0, 1>(mCache.getValue(ijk.offsetBy( 0, 0, 1)));
+ }
+
+ template<typename, typename> friend class BaseStencil; // allow base class to call init()
+ using BaseType::mCache;
+ using BaseType::mStencil;
+};
+
+
+////////////////////////////////////////
+
+
+namespace { // anonymous namespace for stencil-layout map
+
+ // the dense point stencil
+ template<int i, int j, int k> struct DensePt {};
+ template<> struct DensePt< 0, 0, 0> { enum { idx = 0 }; };
+
+ template<> struct DensePt< 1, 0, 0> { enum { idx = 1 }; };
+ template<> struct DensePt< 0, 1, 0> { enum { idx = 2 }; };
+ template<> struct DensePt< 0, 0, 1> { enum { idx = 3 }; };
+
+ template<> struct DensePt<-1, 0, 0> { enum { idx = 4 }; };
+ template<> struct DensePt< 0,-1, 0> { enum { idx = 5 }; };
+ template<> struct DensePt< 0, 0,-1> { enum { idx = 6 }; };
+
+ template<> struct DensePt<-1,-1, 0> { enum { idx = 7 }; };
+ template<> struct DensePt< 0,-1,-1> { enum { idx = 8 }; };
+ template<> struct DensePt<-1, 0,-1> { enum { idx = 9 }; };
+
+ template<> struct DensePt< 1,-1, 0> { enum { idx = 10 }; };
+ template<> struct DensePt< 0, 1,-1> { enum { idx = 11 }; };
+ template<> struct DensePt<-1, 0, 1> { enum { idx = 12 }; };
+
+ template<> struct DensePt<-1, 1, 0> { enum { idx = 13 }; };
+ template<> struct DensePt< 0,-1, 1> { enum { idx = 14 }; };
+ template<> struct DensePt< 1, 0,-1> { enum { idx = 15 }; };
+
+ template<> struct DensePt< 1, 1, 0> { enum { idx = 16 }; };
+ template<> struct DensePt< 0, 1, 1> { enum { idx = 17 }; };
+ template<> struct DensePt< 1, 0, 1> { enum { idx = 18 }; };
+
+}
+
+
+template<typename GridType>
+class SecondOrderDenseStencil: public BaseStencil<GridType, SecondOrderDenseStencil<GridType> >
+{
+public:
+ typedef BaseStencil<GridType,SecondOrderDenseStencil<GridType> > BaseType;
+ typedef typename BaseType::BufferType BufferType;
+ typedef typename GridType::ValueType ValueType;
+ typedef math::Vec3<ValueType> Vec3Type;
+
+ static const int SIZE = 19;
+
+ SecondOrderDenseStencil(const GridType& grid): BaseType(grid, SIZE) {}
+
+ /// Return linear offset for the specified stencil point relative to its center
+ template<int i, int j, int k>
+ unsigned int pos() const { return DensePt<i,j,k>::idx; }
+
+private:
+ inline void init(const Coord& ijk)
+ {
+ mStencil[DensePt< 0, 0, 0>::idx] = mCache.getValue(ijk.offsetBy( 0, 0, 0));
+
+ mStencil[DensePt< 1, 0, 0>::idx] = mCache.getValue(ijk.offsetBy( 1, 0, 0));
+ mStencil[DensePt< 0, 1, 0>::idx] = mCache.getValue(ijk.offsetBy( 0, 1, 0));
+ mStencil[DensePt< 0, 0, 1>::idx] = mCache.getValue(ijk.offsetBy( 0, 0, 1));
+
+ mStencil[DensePt<-1, 0, 0>::idx] = mCache.getValue(ijk.offsetBy(-1, 0, 0));
+ mStencil[DensePt< 0,-1, 0>::idx] = mCache.getValue(ijk.offsetBy( 0, -1, 0));
+ mStencil[DensePt< 0, 0,-1>::idx] = mCache.getValue(ijk.offsetBy( 0, 0, -1));
+
+ mStencil[DensePt<-1,-1, 0>::idx] = mCache.getValue(ijk.offsetBy(-1, -1, 0));
+ mStencil[DensePt< 1,-1, 0>::idx] = mCache.getValue(ijk.offsetBy( 1, -1, 0));
+ mStencil[DensePt<-1, 1, 0>::idx] = mCache.getValue(ijk.offsetBy(-1, 1, 0));
+ mStencil[DensePt< 1, 1, 0>::idx] = mCache.getValue(ijk.offsetBy( 1, 1, 0));
+
+ mStencil[DensePt<-1, 0,-1>::idx] = mCache.getValue(ijk.offsetBy(-1, 0, -1));
+ mStencil[DensePt< 1, 0,-1>::idx] = mCache.getValue(ijk.offsetBy( 1, 0, -1));
+ mStencil[DensePt<-1, 0, 1>::idx] = mCache.getValue(ijk.offsetBy(-1, 0, 1));
+ mStencil[DensePt< 1, 0, 1>::idx] = mCache.getValue(ijk.offsetBy( 1, 0, 1));
+
+ mStencil[DensePt< 0,-1,-1>::idx] = mCache.getValue(ijk.offsetBy( 0, -1, -1));
+ mStencil[DensePt< 0, 1,-1>::idx] = mCache.getValue(ijk.offsetBy( 0, 1, -1));
+ mStencil[DensePt< 0,-1, 1>::idx] = mCache.getValue(ijk.offsetBy( 0, -1, 1));
+ mStencil[DensePt< 0, 1, 1>::idx] = mCache.getValue(ijk.offsetBy( 0, 1, 1));
+ }
+
+ template<typename, typename> friend class BaseStencil; // allow base class to call init()
+ using BaseType::mCache;
+ using BaseType::mStencil;
+};
+
+
+////////////////////////////////////////
+
+
+namespace { // anonymous namespace for stencil-layout map
+
+ // the dense point stencil
+ template<int i, int j, int k> struct ThirteenPt {};
+ template<> struct ThirteenPt< 0, 0, 0> { enum { idx = 0 }; };
+
+ template<> struct ThirteenPt< 1, 0, 0> { enum { idx = 1 }; };
+ template<> struct ThirteenPt< 0, 1, 0> { enum { idx = 2 }; };
+ template<> struct ThirteenPt< 0, 0, 1> { enum { idx = 3 }; };
+
+ template<> struct ThirteenPt<-1, 0, 0> { enum { idx = 4 }; };
+ template<> struct ThirteenPt< 0,-1, 0> { enum { idx = 5 }; };
+ template<> struct ThirteenPt< 0, 0,-1> { enum { idx = 6 }; };
+
+ template<> struct ThirteenPt< 2, 0, 0> { enum { idx = 7 }; };
+ template<> struct ThirteenPt< 0, 2, 0> { enum { idx = 8 }; };
+ template<> struct ThirteenPt< 0, 0, 2> { enum { idx = 9 }; };
+
+ template<> struct ThirteenPt<-2, 0, 0> { enum { idx = 10 }; };
+ template<> struct ThirteenPt< 0,-2, 0> { enum { idx = 11 }; };
+ template<> struct ThirteenPt< 0, 0,-2> { enum { idx = 12 }; };
+
+}
+
+
+template<typename GridType>
+class ThirteenPointStencil: public BaseStencil<GridType, ThirteenPointStencil<GridType> >
+{
+public:
+ typedef BaseStencil<GridType, ThirteenPointStencil<GridType> > BaseType;
+ typedef typename BaseType::BufferType BufferType;
+ typedef typename GridType::ValueType ValueType;
+ typedef math::Vec3<ValueType> Vec3Type;
+
+ static const int SIZE = 13;
+
+ ThirteenPointStencil(const GridType& grid): BaseType(grid, SIZE) {}
+
+ /// Return linear offset for the specified stencil point relative to its center
+ template<int i, int j, int k>
+ unsigned int pos() const { return ThirteenPt<i,j,k>::idx; }
+
+private:
+ inline void init(const Coord& ijk)
+ {
+ mStencil[ThirteenPt< 0, 0, 0>::idx] = mCache.getValue(ijk.offsetBy( 0, 0, 0));
+
+ mStencil[ThirteenPt< 2, 0, 0>::idx] = mCache.getValue(ijk.offsetBy( 2, 0, 0));
+ mStencil[ThirteenPt< 1, 0, 0>::idx] = mCache.getValue(ijk.offsetBy( 1, 0, 0));
+ mStencil[ThirteenPt<-1, 0, 0>::idx] = mCache.getValue(ijk.offsetBy(-1, 0, 0));
+ mStencil[ThirteenPt<-2, 0, 0>::idx] = mCache.getValue(ijk.offsetBy(-2, 0, 0));
+
+ mStencil[ThirteenPt< 0, 2, 0>::idx] = mCache.getValue(ijk.offsetBy( 0, 2, 0));
+ mStencil[ThirteenPt< 0, 1, 0>::idx] = mCache.getValue(ijk.offsetBy( 0, 1, 0));
+ mStencil[ThirteenPt< 0,-1, 0>::idx] = mCache.getValue(ijk.offsetBy( 0, -1, 0));
+ mStencil[ThirteenPt< 0,-2, 0>::idx] = mCache.getValue(ijk.offsetBy( 0, -2, 0));
+
+ mStencil[ThirteenPt< 0, 0, 2>::idx] = mCache.getValue(ijk.offsetBy( 0, 0, 2));
+ mStencil[ThirteenPt< 0, 0, 1>::idx] = mCache.getValue(ijk.offsetBy( 0, 0, 1));
+ mStencil[ThirteenPt< 0, 0,-1>::idx] = mCache.getValue(ijk.offsetBy( 0, 0, -1));
+ mStencil[ThirteenPt< 0, 0,-2>::idx] = mCache.getValue(ijk.offsetBy( 0, 0, -2));
+ }
+
+ template<typename, typename> friend class BaseStencil; // allow base class to call init()
+ using BaseType::mCache;
+ using BaseType::mStencil;
+};
+
+
+////////////////////////////////////////
+
+
+namespace { // anonymous namespace for stencil-layout map
+
+ // the 4th-order dense point stencil
+ template<int i, int j, int k> struct FourthDensePt {};
+ template<> struct FourthDensePt< 0, 0, 0> { enum { idx = 0 }; };
+
+ template<> struct FourthDensePt<-2, 2, 0> { enum { idx = 1 }; };
+ template<> struct FourthDensePt<-1, 2, 0> { enum { idx = 2 }; };
+ template<> struct FourthDensePt< 0, 2, 0> { enum { idx = 3 }; };
+ template<> struct FourthDensePt< 1, 2, 0> { enum { idx = 4 }; };
+ template<> struct FourthDensePt< 2, 2, 0> { enum { idx = 5 }; };
+
+ template<> struct FourthDensePt<-2, 1, 0> { enum { idx = 6 }; };
+ template<> struct FourthDensePt<-1, 1, 0> { enum { idx = 7 }; };
+ template<> struct FourthDensePt< 0, 1, 0> { enum { idx = 8 }; };
+ template<> struct FourthDensePt< 1, 1, 0> { enum { idx = 9 }; };
+ template<> struct FourthDensePt< 2, 1, 0> { enum { idx = 10 }; };
+
+ template<> struct FourthDensePt<-2, 0, 0> { enum { idx = 11 }; };
+ template<> struct FourthDensePt<-1, 0, 0> { enum { idx = 12 }; };
+ template<> struct FourthDensePt< 1, 0, 0> { enum { idx = 13 }; };
+ template<> struct FourthDensePt< 2, 0, 0> { enum { idx = 14 }; };
+
+ template<> struct FourthDensePt<-2,-1, 0> { enum { idx = 15 }; };
+ template<> struct FourthDensePt<-1,-1, 0> { enum { idx = 16 }; };
+ template<> struct FourthDensePt< 0,-1, 0> { enum { idx = 17 }; };
+ template<> struct FourthDensePt< 1,-1, 0> { enum { idx = 18 }; };
+ template<> struct FourthDensePt< 2,-1, 0> { enum { idx = 19 }; };
+
+ template<> struct FourthDensePt<-2,-2, 0> { enum { idx = 20 }; };
+ template<> struct FourthDensePt<-1,-2, 0> { enum { idx = 21 }; };
+ template<> struct FourthDensePt< 0,-2, 0> { enum { idx = 22 }; };
+ template<> struct FourthDensePt< 1,-2, 0> { enum { idx = 23 }; };
+ template<> struct FourthDensePt< 2,-2, 0> { enum { idx = 24 }; };
+
+
+ template<> struct FourthDensePt<-2, 0, 2> { enum { idx = 25 }; };
+ template<> struct FourthDensePt<-1, 0, 2> { enum { idx = 26 }; };
+ template<> struct FourthDensePt< 0, 0, 2> { enum { idx = 27 }; };
+ template<> struct FourthDensePt< 1, 0, 2> { enum { idx = 28 }; };
+ template<> struct FourthDensePt< 2, 0, 2> { enum { idx = 29 }; };
+
+ template<> struct FourthDensePt<-2, 0, 1> { enum { idx = 30 }; };
+ template<> struct FourthDensePt<-1, 0, 1> { enum { idx = 31 }; };
+ template<> struct FourthDensePt< 0, 0, 1> { enum { idx = 32 }; };
+ template<> struct FourthDensePt< 1, 0, 1> { enum { idx = 33 }; };
+ template<> struct FourthDensePt< 2, 0, 1> { enum { idx = 34 }; };
+
+ template<> struct FourthDensePt<-2, 0,-1> { enum { idx = 35 }; };
+ template<> struct FourthDensePt<-1, 0,-1> { enum { idx = 36 }; };
+ template<> struct FourthDensePt< 0, 0,-1> { enum { idx = 37 }; };
+ template<> struct FourthDensePt< 1, 0,-1> { enum { idx = 38 }; };
+ template<> struct FourthDensePt< 2, 0,-1> { enum { idx = 39 }; };
+
+ template<> struct FourthDensePt<-2, 0,-2> { enum { idx = 40 }; };
+ template<> struct FourthDensePt<-1, 0,-2> { enum { idx = 41 }; };
+ template<> struct FourthDensePt< 0, 0,-2> { enum { idx = 42 }; };
+ template<> struct FourthDensePt< 1, 0,-2> { enum { idx = 43 }; };
+ template<> struct FourthDensePt< 2, 0,-2> { enum { idx = 44 }; };
+
+
+ template<> struct FourthDensePt< 0,-2, 2> { enum { idx = 45 }; };
+ template<> struct FourthDensePt< 0,-1, 2> { enum { idx = 46 }; };
+ template<> struct FourthDensePt< 0, 1, 2> { enum { idx = 47 }; };
+ template<> struct FourthDensePt< 0, 2, 2> { enum { idx = 48 }; };
+
+ template<> struct FourthDensePt< 0,-2, 1> { enum { idx = 49 }; };
+ template<> struct FourthDensePt< 0,-1, 1> { enum { idx = 50 }; };
+ template<> struct FourthDensePt< 0, 1, 1> { enum { idx = 51 }; };
+ template<> struct FourthDensePt< 0, 2, 1> { enum { idx = 52 }; };
+
+ template<> struct FourthDensePt< 0,-2,-1> { enum { idx = 53 }; };
+ template<> struct FourthDensePt< 0,-1,-1> { enum { idx = 54 }; };
+ template<> struct FourthDensePt< 0, 1,-1> { enum { idx = 55 }; };
+ template<> struct FourthDensePt< 0, 2,-1> { enum { idx = 56 }; };
+
+ template<> struct FourthDensePt< 0,-2,-2> { enum { idx = 57 }; };
+ template<> struct FourthDensePt< 0,-1,-2> { enum { idx = 58 }; };
+ template<> struct FourthDensePt< 0, 1,-2> { enum { idx = 59 }; };
+ template<> struct FourthDensePt< 0, 2,-2> { enum { idx = 60 }; };
+
+}
+
+
+template<typename GridType>
+class FourthOrderDenseStencil: public BaseStencil<GridType, FourthOrderDenseStencil<GridType> >
+{
+public:
+ typedef BaseStencil<GridType, FourthOrderDenseStencil<GridType> > BaseType;
+ typedef typename BaseType::BufferType BufferType;
+ typedef typename GridType::ValueType ValueType;
+ typedef math::Vec3<ValueType> Vec3Type;
+
+ static const int SIZE = 61;
+
+ FourthOrderDenseStencil(const GridType& grid): BaseType(grid, SIZE) {}
+
+ /// Return linear offset for the specified stencil point relative to its center
+ template<int i, int j, int k>
+ unsigned int pos() const { return FourthDensePt<i,j,k>::idx; }
+
+private:
+ inline void init(const Coord& ijk)
+ {
+ mStencil[FourthDensePt< 0, 0, 0>::idx] = mCache.getValue(ijk);
+
+ mStencil[FourthDensePt<-2, 2, 0>::idx] = mCache.getValue(ijk.offsetBy(-2, 2, 0));
+ mStencil[FourthDensePt<-1, 2, 0>::idx] = mCache.getValue(ijk.offsetBy(-1, 2, 0));
+ mStencil[FourthDensePt< 0, 2, 0>::idx] = mCache.getValue(ijk.offsetBy( 0, 2, 0));
+ mStencil[FourthDensePt< 1, 2, 0>::idx] = mCache.getValue(ijk.offsetBy( 1, 2, 0));
+ mStencil[FourthDensePt< 2, 2, 0>::idx] = mCache.getValue(ijk.offsetBy( 2, 2, 0));
+
+ mStencil[FourthDensePt<-2, 1, 0>::idx] = mCache.getValue(ijk.offsetBy(-2, 1, 0));
+ mStencil[FourthDensePt<-1, 1, 0>::idx] = mCache.getValue(ijk.offsetBy(-1, 1, 0));
+ mStencil[FourthDensePt< 0, 1, 0>::idx] = mCache.getValue(ijk.offsetBy( 0, 1, 0));
+ mStencil[FourthDensePt< 1, 1, 0>::idx] = mCache.getValue(ijk.offsetBy( 1, 1, 0));
+ mStencil[FourthDensePt< 2, 1, 0>::idx] = mCache.getValue(ijk.offsetBy( 2, 1, 0));
+
+ mStencil[FourthDensePt<-2, 0, 0>::idx] = mCache.getValue(ijk.offsetBy(-2, 0, 0));
+ mStencil[FourthDensePt<-1, 0, 0>::idx] = mCache.getValue(ijk.offsetBy(-1, 0, 0));
+ mStencil[FourthDensePt< 1, 0, 0>::idx] = mCache.getValue(ijk.offsetBy( 1, 0, 0));
+ mStencil[FourthDensePt< 2, 0, 0>::idx] = mCache.getValue(ijk.offsetBy( 2, 0, 0));
+
+ mStencil[FourthDensePt<-2,-1, 0>::idx] = mCache.getValue(ijk.offsetBy(-2,-1, 0));
+ mStencil[FourthDensePt<-1,-1, 0>::idx] = mCache.getValue(ijk.offsetBy(-1,-1, 0));
+ mStencil[FourthDensePt< 0,-1, 0>::idx] = mCache.getValue(ijk.offsetBy( 0,-1, 0));
+ mStencil[FourthDensePt< 1,-1, 0>::idx] = mCache.getValue(ijk.offsetBy( 1,-1, 0));
+ mStencil[FourthDensePt< 2,-1, 0>::idx] = mCache.getValue(ijk.offsetBy( 2,-1, 0));
+
+ mStencil[FourthDensePt<-2,-2, 0>::idx] = mCache.getValue(ijk.offsetBy(-2,-2, 0));
+ mStencil[FourthDensePt<-1,-2, 0>::idx] = mCache.getValue(ijk.offsetBy(-1,-2, 0));
+ mStencil[FourthDensePt< 0,-2, 0>::idx] = mCache.getValue(ijk.offsetBy( 0,-2, 0));
+ mStencil[FourthDensePt< 1,-2, 0>::idx] = mCache.getValue(ijk.offsetBy( 1,-2, 0));
+ mStencil[FourthDensePt< 2,-2, 0>::idx] = mCache.getValue(ijk.offsetBy( 2,-2, 0));
+
+ mStencil[FourthDensePt<-2, 0, 2>::idx] = mCache.getValue(ijk.offsetBy(-2, 0, 2));
+ mStencil[FourthDensePt<-1, 0, 2>::idx] = mCache.getValue(ijk.offsetBy(-1, 0, 2));
+ mStencil[FourthDensePt< 0, 0, 2>::idx] = mCache.getValue(ijk.offsetBy( 0, 0, 2));
+ mStencil[FourthDensePt< 1, 0, 2>::idx] = mCache.getValue(ijk.offsetBy( 1, 0, 2));
+ mStencil[FourthDensePt< 2, 0, 2>::idx] = mCache.getValue(ijk.offsetBy( 2, 0, 2));
+
+ mStencil[FourthDensePt<-2, 0, 1>::idx] = mCache.getValue(ijk.offsetBy(-2, 0, 1));
+ mStencil[FourthDensePt<-1, 0, 1>::idx] = mCache.getValue(ijk.offsetBy(-1, 0, 1));
+ mStencil[FourthDensePt< 0, 0, 1>::idx] = mCache.getValue(ijk.offsetBy( 0, 0, 1));
+ mStencil[FourthDensePt< 1, 0, 1>::idx] = mCache.getValue(ijk.offsetBy( 1, 0, 1));
+ mStencil[FourthDensePt< 2, 0, 1>::idx] = mCache.getValue(ijk.offsetBy( 2, 0, 1));
+
+ mStencil[FourthDensePt<-2, 0,-1>::idx] = mCache.getValue(ijk.offsetBy(-2, 0,-1));
+ mStencil[FourthDensePt<-1, 0,-1>::idx] = mCache.getValue(ijk.offsetBy(-1, 0,-1));
+ mStencil[FourthDensePt< 0, 0,-1>::idx] = mCache.getValue(ijk.offsetBy( 0, 0,-1));
+ mStencil[FourthDensePt< 1, 0,-1>::idx] = mCache.getValue(ijk.offsetBy( 1, 0,-1));
+ mStencil[FourthDensePt< 2, 0,-1>::idx] = mCache.getValue(ijk.offsetBy( 2, 0,-1));
+
+ mStencil[FourthDensePt<-2, 0,-2>::idx] = mCache.getValue(ijk.offsetBy(-2, 0,-2));
+ mStencil[FourthDensePt<-1, 0,-2>::idx] = mCache.getValue(ijk.offsetBy(-1, 0,-2));
+ mStencil[FourthDensePt< 0, 0,-2>::idx] = mCache.getValue(ijk.offsetBy( 0, 0,-2));
+ mStencil[FourthDensePt< 1, 0,-2>::idx] = mCache.getValue(ijk.offsetBy( 1, 0,-2));
+ mStencil[FourthDensePt< 2, 0,-2>::idx] = mCache.getValue(ijk.offsetBy( 2, 0,-2));
+
+
+ mStencil[FourthDensePt< 0,-2, 2>::idx] = mCache.getValue(ijk.offsetBy( 0,-2, 2));
+ mStencil[FourthDensePt< 0,-1, 2>::idx] = mCache.getValue(ijk.offsetBy( 0,-1, 2));
+ mStencil[FourthDensePt< 0, 1, 2>::idx] = mCache.getValue(ijk.offsetBy( 0, 1, 2));
+ mStencil[FourthDensePt< 0, 2, 2>::idx] = mCache.getValue(ijk.offsetBy( 0, 2, 2));
+
+ mStencil[FourthDensePt< 0,-2, 1>::idx] = mCache.getValue(ijk.offsetBy( 0,-2, 1));
+ mStencil[FourthDensePt< 0,-1, 1>::idx] = mCache.getValue(ijk.offsetBy( 0,-1, 1));
+ mStencil[FourthDensePt< 0, 1, 1>::idx] = mCache.getValue(ijk.offsetBy( 0, 1, 1));
+ mStencil[FourthDensePt< 0, 2, 1>::idx] = mCache.getValue(ijk.offsetBy( 0, 2, 1));
+
+ mStencil[FourthDensePt< 0,-2,-1>::idx] = mCache.getValue(ijk.offsetBy( 0,-2,-1));
+ mStencil[FourthDensePt< 0,-1,-1>::idx] = mCache.getValue(ijk.offsetBy( 0,-1,-1));
+ mStencil[FourthDensePt< 0, 1,-1>::idx] = mCache.getValue(ijk.offsetBy( 0, 1,-1));
+ mStencil[FourthDensePt< 0, 2,-1>::idx] = mCache.getValue(ijk.offsetBy( 0, 2,-1));
+
+ mStencil[FourthDensePt< 0,-2,-2>::idx] = mCache.getValue(ijk.offsetBy( 0,-2,-2));
+ mStencil[FourthDensePt< 0,-1,-2>::idx] = mCache.getValue(ijk.offsetBy( 0,-1,-2));
+ mStencil[FourthDensePt< 0, 1,-2>::idx] = mCache.getValue(ijk.offsetBy( 0, 1,-2));
+ mStencil[FourthDensePt< 0, 2,-2>::idx] = mCache.getValue(ijk.offsetBy( 0, 2,-2));
+ }
+
+ template<typename, typename> friend class BaseStencil; // allow base class to call init()
+ using BaseType::mCache;
+ using BaseType::mStencil;
+};
+
+
+////////////////////////////////////////
+
+
+namespace { // anonymous namespace for stencil-layout map
+
+ // the dense point stencil
+ template<int i, int j, int k> struct NineteenPt {};
+ template<> struct NineteenPt< 0, 0, 0> { enum { idx = 0 }; };
+
+ template<> struct NineteenPt< 1, 0, 0> { enum { idx = 1 }; };
+ template<> struct NineteenPt< 0, 1, 0> { enum { idx = 2 }; };
+ template<> struct NineteenPt< 0, 0, 1> { enum { idx = 3 }; };
+
+ template<> struct NineteenPt<-1, 0, 0> { enum { idx = 4 }; };
+ template<> struct NineteenPt< 0,-1, 0> { enum { idx = 5 }; };
+ template<> struct NineteenPt< 0, 0,-1> { enum { idx = 6 }; };
+
+ template<> struct NineteenPt< 2, 0, 0> { enum { idx = 7 }; };
+ template<> struct NineteenPt< 0, 2, 0> { enum { idx = 8 }; };
+ template<> struct NineteenPt< 0, 0, 2> { enum { idx = 9 }; };
+
+ template<> struct NineteenPt<-2, 0, 0> { enum { idx = 10 }; };
+ template<> struct NineteenPt< 0,-2, 0> { enum { idx = 11 }; };
+ template<> struct NineteenPt< 0, 0,-2> { enum { idx = 12 }; };
+
+ template<> struct NineteenPt< 3, 0, 0> { enum { idx = 13 }; };
+ template<> struct NineteenPt< 0, 3, 0> { enum { idx = 14 }; };
+ template<> struct NineteenPt< 0, 0, 3> { enum { idx = 15 }; };
+
+ template<> struct NineteenPt<-3, 0, 0> { enum { idx = 16 }; };
+ template<> struct NineteenPt< 0,-3, 0> { enum { idx = 17 }; };
+ template<> struct NineteenPt< 0, 0,-3> { enum { idx = 18 }; };
+
+}
+
+
+template<typename GridType>
+class NineteenPointStencil: public BaseStencil<GridType, NineteenPointStencil<GridType> >
+{
+public:
+ typedef BaseStencil<GridType, NineteenPointStencil<GridType> > BaseType;
+ typedef typename BaseType::BufferType BufferType;
+ typedef typename GridType::ValueType ValueType;
+ typedef math::Vec3<ValueType> Vec3Type;
+
+ static const int SIZE = 19;
+
+ NineteenPointStencil(const GridType& grid): BaseType(grid, SIZE) {}
+
+ /// Return linear offset for the specified stencil point relative to its center
+ template<int i, int j, int k>
+ unsigned int pos() const { return NineteenPt<i,j,k>::idx; }
+
+private:
+ inline void init(const Coord& ijk)
+ {
+ mStencil[NineteenPt< 3, 0, 0>::idx] = mCache.getValue(ijk.offsetBy( 3, 0, 0));
+ mStencil[NineteenPt< 2, 0, 0>::idx] = mCache.getValue(ijk.offsetBy( 2, 0, 0));
+ mStencil[NineteenPt< 1, 0, 0>::idx] = mCache.getValue(ijk.offsetBy( 1, 0, 0));
+ mStencil[NineteenPt<-1, 0, 0>::idx] = mCache.getValue(ijk.offsetBy(-1, 0, 0));
+ mStencil[NineteenPt<-2, 0, 0>::idx] = mCache.getValue(ijk.offsetBy(-2, 0, 0));
+ mStencil[NineteenPt<-3, 0, 0>::idx] = mCache.getValue(ijk.offsetBy(-3, 0, 0));
+
+ mStencil[NineteenPt< 0, 3, 0>::idx] = mCache.getValue(ijk.offsetBy( 0, 3, 0));
+ mStencil[NineteenPt< 0, 2, 0>::idx] = mCache.getValue(ijk.offsetBy( 0, 2, 0));
+ mStencil[NineteenPt< 0, 1, 0>::idx] = mCache.getValue(ijk.offsetBy( 0, 1, 0));
+ mStencil[NineteenPt< 0,-1, 0>::idx] = mCache.getValue(ijk.offsetBy( 0, -1, 0));
+ mStencil[NineteenPt< 0,-2, 0>::idx] = mCache.getValue(ijk.offsetBy( 0, -2, 0));
+ mStencil[NineteenPt< 0,-3, 0>::idx] = mCache.getValue(ijk.offsetBy( 0, -3, 0));
+
+
+ mStencil[NineteenPt< 0, 0, 3>::idx] = mCache.getValue(ijk.offsetBy( 0, 0, 3));
+ mStencil[NineteenPt< 0, 0, 2>::idx] = mCache.getValue(ijk.offsetBy( 0, 0, 2));
+ mStencil[NineteenPt< 0, 0, 1>::idx] = mCache.getValue(ijk.offsetBy( 0, 0, 1));
+ mStencil[NineteenPt< 0, 0,-1>::idx] = mCache.getValue(ijk.offsetBy( 0, 0, -1));
+ mStencil[NineteenPt< 0, 0,-2>::idx] = mCache.getValue(ijk.offsetBy( 0, 0, -2));
+ mStencil[NineteenPt< 0, 0,-3>::idx] = mCache.getValue(ijk.offsetBy( 0, 0, -3));
+ }
+
+ template<typename, typename> friend class BaseStencil; // allow base class to call init()
+ using BaseType::mCache;
+ using BaseType::mStencil;
+};
+
+
+////////////////////////////////////////
+
+
+namespace { // anonymous namespace for stencil-layout map
+
+ // the 4th-order dense point stencil
+ template<int i, int j, int k> struct SixthDensePt { };
+ template<> struct SixthDensePt< 0, 0, 0> { enum { idx = 0 }; };
+
+ template<> struct SixthDensePt<-3, 3, 0> { enum { idx = 1 }; };
+ template<> struct SixthDensePt<-2, 3, 0> { enum { idx = 2 }; };
+ template<> struct SixthDensePt<-1, 3, 0> { enum { idx = 3 }; };
+ template<> struct SixthDensePt< 0, 3, 0> { enum { idx = 4 }; };
+ template<> struct SixthDensePt< 1, 3, 0> { enum { idx = 5 }; };
+ template<> struct SixthDensePt< 2, 3, 0> { enum { idx = 6 }; };
+ template<> struct SixthDensePt< 3, 3, 0> { enum { idx = 7 }; };
+
+ template<> struct SixthDensePt<-3, 2, 0> { enum { idx = 8 }; };
+ template<> struct SixthDensePt<-2, 2, 0> { enum { idx = 9 }; };
+ template<> struct SixthDensePt<-1, 2, 0> { enum { idx = 10 }; };
+ template<> struct SixthDensePt< 0, 2, 0> { enum { idx = 11 }; };
+ template<> struct SixthDensePt< 1, 2, 0> { enum { idx = 12 }; };
+ template<> struct SixthDensePt< 2, 2, 0> { enum { idx = 13 }; };
+ template<> struct SixthDensePt< 3, 2, 0> { enum { idx = 14 }; };
+
+ template<> struct SixthDensePt<-3, 1, 0> { enum { idx = 15 }; };
+ template<> struct SixthDensePt<-2, 1, 0> { enum { idx = 16 }; };
+ template<> struct SixthDensePt<-1, 1, 0> { enum { idx = 17 }; };
+ template<> struct SixthDensePt< 0, 1, 0> { enum { idx = 18 }; };
+ template<> struct SixthDensePt< 1, 1, 0> { enum { idx = 19 }; };
+ template<> struct SixthDensePt< 2, 1, 0> { enum { idx = 20 }; };
+ template<> struct SixthDensePt< 3, 1, 0> { enum { idx = 21 }; };
+
+ template<> struct SixthDensePt<-3, 0, 0> { enum { idx = 22 }; };
+ template<> struct SixthDensePt<-2, 0, 0> { enum { idx = 23 }; };
+ template<> struct SixthDensePt<-1, 0, 0> { enum { idx = 24 }; };
+ template<> struct SixthDensePt< 1, 0, 0> { enum { idx = 25 }; };
+ template<> struct SixthDensePt< 2, 0, 0> { enum { idx = 26 }; };
+ template<> struct SixthDensePt< 3, 0, 0> { enum { idx = 27 }; };
+
+
+ template<> struct SixthDensePt<-3,-1, 0> { enum { idx = 28 }; };
+ template<> struct SixthDensePt<-2,-1, 0> { enum { idx = 29 }; };
+ template<> struct SixthDensePt<-1,-1, 0> { enum { idx = 30 }; };
+ template<> struct SixthDensePt< 0,-1, 0> { enum { idx = 31 }; };
+ template<> struct SixthDensePt< 1,-1, 0> { enum { idx = 32 }; };
+ template<> struct SixthDensePt< 2,-1, 0> { enum { idx = 33 }; };
+ template<> struct SixthDensePt< 3,-1, 0> { enum { idx = 34 }; };
+
+
+ template<> struct SixthDensePt<-3,-2, 0> { enum { idx = 35 }; };
+ template<> struct SixthDensePt<-2,-2, 0> { enum { idx = 36 }; };
+ template<> struct SixthDensePt<-1,-2, 0> { enum { idx = 37 }; };
+ template<> struct SixthDensePt< 0,-2, 0> { enum { idx = 38 }; };
+ template<> struct SixthDensePt< 1,-2, 0> { enum { idx = 39 }; };
+ template<> struct SixthDensePt< 2,-2, 0> { enum { idx = 40 }; };
+ template<> struct SixthDensePt< 3,-2, 0> { enum { idx = 41 }; };
+
+
+ template<> struct SixthDensePt<-3,-3, 0> { enum { idx = 42 }; };
+ template<> struct SixthDensePt<-2,-3, 0> { enum { idx = 43 }; };
+ template<> struct SixthDensePt<-1,-3, 0> { enum { idx = 44 }; };
+ template<> struct SixthDensePt< 0,-3, 0> { enum { idx = 45 }; };
+ template<> struct SixthDensePt< 1,-3, 0> { enum { idx = 46 }; };
+ template<> struct SixthDensePt< 2,-3, 0> { enum { idx = 47 }; };
+ template<> struct SixthDensePt< 3,-3, 0> { enum { idx = 48 }; };
+
+
+ template<> struct SixthDensePt<-3, 0, 3> { enum { idx = 49 }; };
+ template<> struct SixthDensePt<-2, 0, 3> { enum { idx = 50 }; };
+ template<> struct SixthDensePt<-1, 0, 3> { enum { idx = 51 }; };
+ template<> struct SixthDensePt< 0, 0, 3> { enum { idx = 52 }; };
+ template<> struct SixthDensePt< 1, 0, 3> { enum { idx = 53 }; };
+ template<> struct SixthDensePt< 2, 0, 3> { enum { idx = 54 }; };
+ template<> struct SixthDensePt< 3, 0, 3> { enum { idx = 55 }; };
+
+
+ template<> struct SixthDensePt<-3, 0, 2> { enum { idx = 56 }; };
+ template<> struct SixthDensePt<-2, 0, 2> { enum { idx = 57 }; };
+ template<> struct SixthDensePt<-1, 0, 2> { enum { idx = 58 }; };
+ template<> struct SixthDensePt< 0, 0, 2> { enum { idx = 59 }; };
+ template<> struct SixthDensePt< 1, 0, 2> { enum { idx = 60 }; };
+ template<> struct SixthDensePt< 2, 0, 2> { enum { idx = 61 }; };
+ template<> struct SixthDensePt< 3, 0, 2> { enum { idx = 62 }; };
+
+ template<> struct SixthDensePt<-3, 0, 1> { enum { idx = 63 }; };
+ template<> struct SixthDensePt<-2, 0, 1> { enum { idx = 64 }; };
+ template<> struct SixthDensePt<-1, 0, 1> { enum { idx = 65 }; };
+ template<> struct SixthDensePt< 0, 0, 1> { enum { idx = 66 }; };
+ template<> struct SixthDensePt< 1, 0, 1> { enum { idx = 67 }; };
+ template<> struct SixthDensePt< 2, 0, 1> { enum { idx = 68 }; };
+ template<> struct SixthDensePt< 3, 0, 1> { enum { idx = 69 }; };
+
+
+ template<> struct SixthDensePt<-3, 0,-1> { enum { idx = 70 }; };
+ template<> struct SixthDensePt<-2, 0,-1> { enum { idx = 71 }; };
+ template<> struct SixthDensePt<-1, 0,-1> { enum { idx = 72 }; };
+ template<> struct SixthDensePt< 0, 0,-1> { enum { idx = 73 }; };
+ template<> struct SixthDensePt< 1, 0,-1> { enum { idx = 74 }; };
+ template<> struct SixthDensePt< 2, 0,-1> { enum { idx = 75 }; };
+ template<> struct SixthDensePt< 3, 0,-1> { enum { idx = 76 }; };
+
+
+ template<> struct SixthDensePt<-3, 0,-2> { enum { idx = 77 }; };
+ template<> struct SixthDensePt<-2, 0,-2> { enum { idx = 78 }; };
+ template<> struct SixthDensePt<-1, 0,-2> { enum { idx = 79 }; };
+ template<> struct SixthDensePt< 0, 0,-2> { enum { idx = 80 }; };
+ template<> struct SixthDensePt< 1, 0,-2> { enum { idx = 81 }; };
+ template<> struct SixthDensePt< 2, 0,-2> { enum { idx = 82 }; };
+ template<> struct SixthDensePt< 3, 0,-2> { enum { idx = 83 }; };
+
+
+ template<> struct SixthDensePt<-3, 0,-3> { enum { idx = 84 }; };
+ template<> struct SixthDensePt<-2, 0,-3> { enum { idx = 85 }; };
+ template<> struct SixthDensePt<-1, 0,-3> { enum { idx = 86 }; };
+ template<> struct SixthDensePt< 0, 0,-3> { enum { idx = 87 }; };
+ template<> struct SixthDensePt< 1, 0,-3> { enum { idx = 88 }; };
+ template<> struct SixthDensePt< 2, 0,-3> { enum { idx = 89 }; };
+ template<> struct SixthDensePt< 3, 0,-3> { enum { idx = 90 }; };
+
+
+ template<> struct SixthDensePt< 0,-3, 3> { enum { idx = 91 }; };
+ template<> struct SixthDensePt< 0,-2, 3> { enum { idx = 92 }; };
+ template<> struct SixthDensePt< 0,-1, 3> { enum { idx = 93 }; };
+ template<> struct SixthDensePt< 0, 1, 3> { enum { idx = 94 }; };
+ template<> struct SixthDensePt< 0, 2, 3> { enum { idx = 95 }; };
+ template<> struct SixthDensePt< 0, 3, 3> { enum { idx = 96 }; };
+
+ template<> struct SixthDensePt< 0,-3, 2> { enum { idx = 97 }; };
+ template<> struct SixthDensePt< 0,-2, 2> { enum { idx = 98 }; };
+ template<> struct SixthDensePt< 0,-1, 2> { enum { idx = 99 }; };
+ template<> struct SixthDensePt< 0, 1, 2> { enum { idx = 100 }; };
+ template<> struct SixthDensePt< 0, 2, 2> { enum { idx = 101 }; };
+ template<> struct SixthDensePt< 0, 3, 2> { enum { idx = 102 }; };
+
+ template<> struct SixthDensePt< 0,-3, 1> { enum { idx = 103 }; };
+ template<> struct SixthDensePt< 0,-2, 1> { enum { idx = 104 }; };
+ template<> struct SixthDensePt< 0,-1, 1> { enum { idx = 105 }; };
+ template<> struct SixthDensePt< 0, 1, 1> { enum { idx = 106 }; };
+ template<> struct SixthDensePt< 0, 2, 1> { enum { idx = 107 }; };
+ template<> struct SixthDensePt< 0, 3, 1> { enum { idx = 108 }; };
+
+ template<> struct SixthDensePt< 0,-3,-1> { enum { idx = 109 }; };
+ template<> struct SixthDensePt< 0,-2,-1> { enum { idx = 110 }; };
+ template<> struct SixthDensePt< 0,-1,-1> { enum { idx = 111 }; };
+ template<> struct SixthDensePt< 0, 1,-1> { enum { idx = 112 }; };
+ template<> struct SixthDensePt< 0, 2,-1> { enum { idx = 113 }; };
+ template<> struct SixthDensePt< 0, 3,-1> { enum { idx = 114 }; };
+
+ template<> struct SixthDensePt< 0,-3,-2> { enum { idx = 115 }; };
+ template<> struct SixthDensePt< 0,-2,-2> { enum { idx = 116 }; };
+ template<> struct SixthDensePt< 0,-1,-2> { enum { idx = 117 }; };
+ template<> struct SixthDensePt< 0, 1,-2> { enum { idx = 118 }; };
+ template<> struct SixthDensePt< 0, 2,-2> { enum { idx = 119 }; };
+ template<> struct SixthDensePt< 0, 3,-2> { enum { idx = 120 }; };
+
+ template<> struct SixthDensePt< 0,-3,-3> { enum { idx = 121 }; };
+ template<> struct SixthDensePt< 0,-2,-3> { enum { idx = 122 }; };
+ template<> struct SixthDensePt< 0,-1,-3> { enum { idx = 123 }; };
+ template<> struct SixthDensePt< 0, 1,-3> { enum { idx = 124 }; };
+ template<> struct SixthDensePt< 0, 2,-3> { enum { idx = 125 }; };
+ template<> struct SixthDensePt< 0, 3,-3> { enum { idx = 126 }; };
+
+}
+
+
+template<typename GridType>
+class SixthOrderDenseStencil: public BaseStencil<GridType, SixthOrderDenseStencil<GridType> >
+{
+public:
+ typedef BaseStencil<GridType, SixthOrderDenseStencil<GridType> > BaseType;
+ typedef typename BaseType::BufferType BufferType;
+ typedef typename GridType::ValueType ValueType;
+ typedef math::Vec3<ValueType> Vec3Type;
+
+ static const int SIZE = 127;
+
+ SixthOrderDenseStencil(const GridType& grid): BaseType(grid, SIZE) {}
+
+ /// Return linear offset for the specified stencil point relative to its center
+ template<int i, int j, int k>
+ unsigned int pos() const { return SixthDensePt<i,j,k>::idx; }
+
+private:
+ inline void init(const Coord& ijk)
+ {
+ mStencil[SixthDensePt< 0, 0, 0>::idx] = mCache.getValue(ijk);
+
+ mStencil[SixthDensePt<-3, 3, 0>::idx] = mCache.getValue(ijk.offsetBy(-3, 3, 0));
+ mStencil[SixthDensePt<-2, 3, 0>::idx] = mCache.getValue(ijk.offsetBy(-2, 3, 0));
+ mStencil[SixthDensePt<-1, 3, 0>::idx] = mCache.getValue(ijk.offsetBy(-1, 3, 0));
+ mStencil[SixthDensePt< 0, 3, 0>::idx] = mCache.getValue(ijk.offsetBy( 0, 3, 0));
+ mStencil[SixthDensePt< 1, 3, 0>::idx] = mCache.getValue(ijk.offsetBy( 1, 3, 0));
+ mStencil[SixthDensePt< 2, 3, 0>::idx] = mCache.getValue(ijk.offsetBy( 2, 3, 0));
+ mStencil[SixthDensePt< 3, 3, 0>::idx] = mCache.getValue(ijk.offsetBy( 3, 3, 0));
+
+ mStencil[SixthDensePt<-3, 2, 0>::idx] = mCache.getValue(ijk.offsetBy(-3, 2, 0));
+ mStencil[SixthDensePt<-2, 2, 0>::idx] = mCache.getValue(ijk.offsetBy(-2, 2, 0));
+ mStencil[SixthDensePt<-1, 2, 0>::idx] = mCache.getValue(ijk.offsetBy(-1, 2, 0));
+ mStencil[SixthDensePt< 0, 2, 0>::idx] = mCache.getValue(ijk.offsetBy( 0, 2, 0));
+ mStencil[SixthDensePt< 1, 2, 0>::idx] = mCache.getValue(ijk.offsetBy( 1, 2, 0));
+ mStencil[SixthDensePt< 2, 2, 0>::idx] = mCache.getValue(ijk.offsetBy( 2, 2, 0));
+ mStencil[SixthDensePt< 3, 2, 0>::idx] = mCache.getValue(ijk.offsetBy( 3, 2, 0));
+
+ mStencil[SixthDensePt<-3, 1, 0>::idx] = mCache.getValue(ijk.offsetBy(-3, 1, 0));
+ mStencil[SixthDensePt<-2, 1, 0>::idx] = mCache.getValue(ijk.offsetBy(-2, 1, 0));
+ mStencil[SixthDensePt<-1, 1, 0>::idx] = mCache.getValue(ijk.offsetBy(-1, 1, 0));
+ mStencil[SixthDensePt< 0, 1, 0>::idx] = mCache.getValue(ijk.offsetBy( 0, 1, 0));
+ mStencil[SixthDensePt< 1, 1, 0>::idx] = mCache.getValue(ijk.offsetBy( 1, 1, 0));
+ mStencil[SixthDensePt< 2, 1, 0>::idx] = mCache.getValue(ijk.offsetBy( 2, 1, 0));
+ mStencil[SixthDensePt< 3, 1, 0>::idx] = mCache.getValue(ijk.offsetBy( 3, 1, 0));
+
+ mStencil[SixthDensePt<-3, 0, 0>::idx] = mCache.getValue(ijk.offsetBy(-3, 0, 0));
+ mStencil[SixthDensePt<-2, 0, 0>::idx] = mCache.getValue(ijk.offsetBy(-2, 0, 0));
+ mStencil[SixthDensePt<-1, 0, 0>::idx] = mCache.getValue(ijk.offsetBy(-1, 0, 0));
+ mStencil[SixthDensePt< 1, 0, 0>::idx] = mCache.getValue(ijk.offsetBy( 1, 0, 0));
+ mStencil[SixthDensePt< 2, 0, 0>::idx] = mCache.getValue(ijk.offsetBy( 2, 0, 0));
+ mStencil[SixthDensePt< 3, 0, 0>::idx] = mCache.getValue(ijk.offsetBy( 3, 0, 0));
+
+ mStencil[SixthDensePt<-3,-1, 0>::idx] = mCache.getValue(ijk.offsetBy(-3,-1, 0));
+ mStencil[SixthDensePt<-2,-1, 0>::idx] = mCache.getValue(ijk.offsetBy(-2,-1, 0));
+ mStencil[SixthDensePt<-1,-1, 0>::idx] = mCache.getValue(ijk.offsetBy(-1,-1, 0));
+ mStencil[SixthDensePt< 0,-1, 0>::idx] = mCache.getValue(ijk.offsetBy( 0,-1, 0));
+ mStencil[SixthDensePt< 1,-1, 0>::idx] = mCache.getValue(ijk.offsetBy( 1,-1, 0));
+ mStencil[SixthDensePt< 2,-1, 0>::idx] = mCache.getValue(ijk.offsetBy( 2,-1, 0));
+ mStencil[SixthDensePt< 3,-1, 0>::idx] = mCache.getValue(ijk.offsetBy( 3,-1, 0));
+
+ mStencil[SixthDensePt<-3,-2, 0>::idx] = mCache.getValue(ijk.offsetBy(-3,-2, 0));
+ mStencil[SixthDensePt<-2,-2, 0>::idx] = mCache.getValue(ijk.offsetBy(-2,-2, 0));
+ mStencil[SixthDensePt<-1,-2, 0>::idx] = mCache.getValue(ijk.offsetBy(-1,-2, 0));
+ mStencil[SixthDensePt< 0,-2, 0>::idx] = mCache.getValue(ijk.offsetBy( 0,-2, 0));
+ mStencil[SixthDensePt< 1,-2, 0>::idx] = mCache.getValue(ijk.offsetBy( 1,-2, 0));
+ mStencil[SixthDensePt< 2,-2, 0>::idx] = mCache.getValue(ijk.offsetBy( 2,-2, 0));
+ mStencil[SixthDensePt< 3,-2, 0>::idx] = mCache.getValue(ijk.offsetBy( 3,-2, 0));
+
+ mStencil[SixthDensePt<-3,-3, 0>::idx] = mCache.getValue(ijk.offsetBy(-3,-3, 0));
+ mStencil[SixthDensePt<-2,-3, 0>::idx] = mCache.getValue(ijk.offsetBy(-2,-3, 0));
+ mStencil[SixthDensePt<-1,-3, 0>::idx] = mCache.getValue(ijk.offsetBy(-1,-3, 0));
+ mStencil[SixthDensePt< 0,-3, 0>::idx] = mCache.getValue(ijk.offsetBy( 0,-3, 0));
+ mStencil[SixthDensePt< 1,-3, 0>::idx] = mCache.getValue(ijk.offsetBy( 1,-3, 0));
+ mStencil[SixthDensePt< 2,-3, 0>::idx] = mCache.getValue(ijk.offsetBy( 2,-3, 0));
+ mStencil[SixthDensePt< 3,-3, 0>::idx] = mCache.getValue(ijk.offsetBy( 3,-3, 0));
+
+ mStencil[SixthDensePt<-3, 0, 3>::idx] = mCache.getValue(ijk.offsetBy(-3, 0, 3));
+ mStencil[SixthDensePt<-2, 0, 3>::idx] = mCache.getValue(ijk.offsetBy(-2, 0, 3));
+ mStencil[SixthDensePt<-1, 0, 3>::idx] = mCache.getValue(ijk.offsetBy(-1, 0, 3));
+ mStencil[SixthDensePt< 0, 0, 3>::idx] = mCache.getValue(ijk.offsetBy( 0, 0, 3));
+ mStencil[SixthDensePt< 1, 0, 3>::idx] = mCache.getValue(ijk.offsetBy( 1, 0, 3));
+ mStencil[SixthDensePt< 2, 0, 3>::idx] = mCache.getValue(ijk.offsetBy( 2, 0, 3));
+ mStencil[SixthDensePt< 3, 0, 3>::idx] = mCache.getValue(ijk.offsetBy( 3, 0, 3));
+
+ mStencil[SixthDensePt<-3, 0, 2>::idx] = mCache.getValue(ijk.offsetBy(-3, 0, 2));
+ mStencil[SixthDensePt<-2, 0, 2>::idx] = mCache.getValue(ijk.offsetBy(-2, 0, 2));
+ mStencil[SixthDensePt<-1, 0, 2>::idx] = mCache.getValue(ijk.offsetBy(-1, 0, 2));
+ mStencil[SixthDensePt< 0, 0, 2>::idx] = mCache.getValue(ijk.offsetBy( 0, 0, 2));
+ mStencil[SixthDensePt< 1, 0, 2>::idx] = mCache.getValue(ijk.offsetBy( 1, 0, 2));
+ mStencil[SixthDensePt< 2, 0, 2>::idx] = mCache.getValue(ijk.offsetBy( 2, 0, 2));
+ mStencil[SixthDensePt< 3, 0, 2>::idx] = mCache.getValue(ijk.offsetBy( 3, 0, 2));
+
+ mStencil[SixthDensePt<-3, 0, 1>::idx] = mCache.getValue(ijk.offsetBy(-3, 0, 1));
+ mStencil[SixthDensePt<-2, 0, 1>::idx] = mCache.getValue(ijk.offsetBy(-2, 0, 1));
+ mStencil[SixthDensePt<-1, 0, 1>::idx] = mCache.getValue(ijk.offsetBy(-1, 0, 1));
+ mStencil[SixthDensePt< 0, 0, 1>::idx] = mCache.getValue(ijk.offsetBy( 0, 0, 1));
+ mStencil[SixthDensePt< 1, 0, 1>::idx] = mCache.getValue(ijk.offsetBy( 1, 0, 1));
+ mStencil[SixthDensePt< 2, 0, 1>::idx] = mCache.getValue(ijk.offsetBy( 2, 0, 1));
+ mStencil[SixthDensePt< 3, 0, 1>::idx] = mCache.getValue(ijk.offsetBy( 3, 0, 1));
+
+ mStencil[SixthDensePt<-3, 0,-1>::idx] = mCache.getValue(ijk.offsetBy(-3, 0,-1));
+ mStencil[SixthDensePt<-2, 0,-1>::idx] = mCache.getValue(ijk.offsetBy(-2, 0,-1));
+ mStencil[SixthDensePt<-1, 0,-1>::idx] = mCache.getValue(ijk.offsetBy(-1, 0,-1));
+ mStencil[SixthDensePt< 0, 0,-1>::idx] = mCache.getValue(ijk.offsetBy( 0, 0,-1));
+ mStencil[SixthDensePt< 1, 0,-1>::idx] = mCache.getValue(ijk.offsetBy( 1, 0,-1));
+ mStencil[SixthDensePt< 2, 0,-1>::idx] = mCache.getValue(ijk.offsetBy( 2, 0,-1));
+ mStencil[SixthDensePt< 3, 0,-1>::idx] = mCache.getValue(ijk.offsetBy( 3, 0,-1));
+
+ mStencil[SixthDensePt<-3, 0,-2>::idx] = mCache.getValue(ijk.offsetBy(-3, 0,-2));
+ mStencil[SixthDensePt<-2, 0,-2>::idx] = mCache.getValue(ijk.offsetBy(-2, 0,-2));
+ mStencil[SixthDensePt<-1, 0,-2>::idx] = mCache.getValue(ijk.offsetBy(-1, 0,-2));
+ mStencil[SixthDensePt< 0, 0,-2>::idx] = mCache.getValue(ijk.offsetBy( 0, 0,-2));
+ mStencil[SixthDensePt< 1, 0,-2>::idx] = mCache.getValue(ijk.offsetBy( 1, 0,-2));
+ mStencil[SixthDensePt< 2, 0,-2>::idx] = mCache.getValue(ijk.offsetBy( 2, 0,-2));
+ mStencil[SixthDensePt< 3, 0,-2>::idx] = mCache.getValue(ijk.offsetBy( 3, 0,-2));
+
+ mStencil[SixthDensePt<-3, 0,-3>::idx] = mCache.getValue(ijk.offsetBy(-3, 0,-3));
+ mStencil[SixthDensePt<-2, 0,-3>::idx] = mCache.getValue(ijk.offsetBy(-2, 0,-3));
+ mStencil[SixthDensePt<-1, 0,-3>::idx] = mCache.getValue(ijk.offsetBy(-1, 0,-3));
+ mStencil[SixthDensePt< 0, 0,-3>::idx] = mCache.getValue(ijk.offsetBy( 0, 0,-3));
+ mStencil[SixthDensePt< 1, 0,-3>::idx] = mCache.getValue(ijk.offsetBy( 1, 0,-3));
+ mStencil[SixthDensePt< 2, 0,-3>::idx] = mCache.getValue(ijk.offsetBy( 2, 0,-3));
+ mStencil[SixthDensePt< 3, 0,-3>::idx] = mCache.getValue(ijk.offsetBy( 3, 0,-3));
+
+ mStencil[SixthDensePt< 0,-3, 3>::idx] = mCache.getValue(ijk.offsetBy( 0,-3, 3));
+ mStencil[SixthDensePt< 0,-2, 3>::idx] = mCache.getValue(ijk.offsetBy( 0,-2, 3));
+ mStencil[SixthDensePt< 0,-1, 3>::idx] = mCache.getValue(ijk.offsetBy( 0,-1, 3));
+ mStencil[SixthDensePt< 0, 1, 3>::idx] = mCache.getValue(ijk.offsetBy( 0, 1, 3));
+ mStencil[SixthDensePt< 0, 2, 3>::idx] = mCache.getValue(ijk.offsetBy( 0, 2, 3));
+ mStencil[SixthDensePt< 0, 3, 3>::idx] = mCache.getValue(ijk.offsetBy( 0, 3, 3));
+
+ mStencil[SixthDensePt< 0,-3, 2>::idx] = mCache.getValue(ijk.offsetBy( 0,-3, 2));
+ mStencil[SixthDensePt< 0,-2, 2>::idx] = mCache.getValue(ijk.offsetBy( 0,-2, 2));
+ mStencil[SixthDensePt< 0,-1, 2>::idx] = mCache.getValue(ijk.offsetBy( 0,-1, 2));
+ mStencil[SixthDensePt< 0, 1, 2>::idx] = mCache.getValue(ijk.offsetBy( 0, 1, 2));
+ mStencil[SixthDensePt< 0, 2, 2>::idx] = mCache.getValue(ijk.offsetBy( 0, 2, 2));
+ mStencil[SixthDensePt< 0, 3, 2>::idx] = mCache.getValue(ijk.offsetBy( 0, 3, 2));
+
+ mStencil[SixthDensePt< 0,-3, 1>::idx] = mCache.getValue(ijk.offsetBy( 0,-3, 1));
+ mStencil[SixthDensePt< 0,-2, 1>::idx] = mCache.getValue(ijk.offsetBy( 0,-2, 1));
+ mStencil[SixthDensePt< 0,-1, 1>::idx] = mCache.getValue(ijk.offsetBy( 0,-1, 1));
+ mStencil[SixthDensePt< 0, 1, 1>::idx] = mCache.getValue(ijk.offsetBy( 0, 1, 1));
+ mStencil[SixthDensePt< 0, 2, 1>::idx] = mCache.getValue(ijk.offsetBy( 0, 2, 1));
+ mStencil[SixthDensePt< 0, 3, 1>::idx] = mCache.getValue(ijk.offsetBy( 0, 3, 1));
+
+ mStencil[SixthDensePt< 0,-3,-1>::idx] = mCache.getValue(ijk.offsetBy( 0,-3,-1));
+ mStencil[SixthDensePt< 0,-2,-1>::idx] = mCache.getValue(ijk.offsetBy( 0,-2,-1));
+ mStencil[SixthDensePt< 0,-1,-1>::idx] = mCache.getValue(ijk.offsetBy( 0,-1,-1));
+ mStencil[SixthDensePt< 0, 1,-1>::idx] = mCache.getValue(ijk.offsetBy( 0, 1,-1));
+ mStencil[SixthDensePt< 0, 2,-1>::idx] = mCache.getValue(ijk.offsetBy( 0, 2,-1));
+ mStencil[SixthDensePt< 0, 3,-1>::idx] = mCache.getValue(ijk.offsetBy( 0, 3,-1));
+
+ mStencil[SixthDensePt< 0,-3,-2>::idx] = mCache.getValue(ijk.offsetBy( 0,-3,-2));
+ mStencil[SixthDensePt< 0,-2,-2>::idx] = mCache.getValue(ijk.offsetBy( 0,-2,-2));
+ mStencil[SixthDensePt< 0,-1,-2>::idx] = mCache.getValue(ijk.offsetBy( 0,-1,-2));
+ mStencil[SixthDensePt< 0, 1,-2>::idx] = mCache.getValue(ijk.offsetBy( 0, 1,-2));
+ mStencil[SixthDensePt< 0, 2,-2>::idx] = mCache.getValue(ijk.offsetBy( 0, 2,-2));
+ mStencil[SixthDensePt< 0, 3,-2>::idx] = mCache.getValue(ijk.offsetBy( 0, 3,-2));
+
+ mStencil[SixthDensePt< 0,-3,-3>::idx] = mCache.getValue(ijk.offsetBy( 0,-3,-3));
+ mStencil[SixthDensePt< 0,-2,-3>::idx] = mCache.getValue(ijk.offsetBy( 0,-2,-3));
+ mStencil[SixthDensePt< 0,-1,-3>::idx] = mCache.getValue(ijk.offsetBy( 0,-1,-3));
+ mStencil[SixthDensePt< 0, 1,-3>::idx] = mCache.getValue(ijk.offsetBy( 0, 1,-3));
+ mStencil[SixthDensePt< 0, 2,-3>::idx] = mCache.getValue(ijk.offsetBy( 0, 2,-3));
+ mStencil[SixthDensePt< 0, 3,-3>::idx] = mCache.getValue(ijk.offsetBy( 0, 3,-3));
+ }
+
+ template<typename, typename> friend class BaseStencil; // allow base class to call init()
+ using BaseType::mCache;
+ using BaseType::mStencil;
+};
+
+
+//////////////////////////////////////////////////////////////////////
+
+
+/// This is a simple 7-point nearest neighbor stencil that supports
+/// gradient by second-order central differencing, first-order upwinding,
+/// Laplacian, closest-point transform and zero-crossing test.
+///
+/// @note For optimal random access performance this class
+/// includes its own grid accessor.
+template<typename GridType>
+class GradStencil: public BaseStencil<GridType, GradStencil<GridType> >
+{
+public:
+ typedef BaseStencil<GridType, GradStencil<GridType> > BaseType;
+ typedef typename BaseType::BufferType BufferType;
+ typedef typename GridType::ValueType ValueType;
+ typedef math::Vec3<ValueType> Vec3Type;
+
+ static const int SIZE = 7;
+
+ GradStencil(const GridType& grid):
+ BaseType(grid, SIZE),
+ mInv2Dx(ValueType(0.5 / grid.voxelSize()[0])),
+ mInvDx2(ValueType(4.0 * mInv2Dx * mInv2Dx))
+ {
+ }
+
+ GradStencil(const GridType& grid, Real dx):
+ BaseType(grid, SIZE),
+ mInv2Dx(ValueType(0.5 / dx)),
+ mInvDx2(ValueType(4.0 * mInv2Dx * mInv2Dx))
+ {
+ }
+
+ /// @brief Return the norm square of the single-sided upwind gradient
+ /// (computed via Gudonov's scheme) at the previously buffered location.
+ ///
+ /// @note This method should not be called until the stencil
+ /// buffer has been populated via a call to moveTo(ijk).
+ inline ValueType normSqGrad() const
+ {
+ return mInvDx2 * math::GudonovsNormSqrd(mStencil[0] > 0,
+ mStencil[0] - mStencil[1],
+ mStencil[2] - mStencil[0],
+ mStencil[0] - mStencil[3],
+ mStencil[4] - mStencil[0],
+ mStencil[0] - mStencil[5],
+ mStencil[6] - mStencil[0]);
+ }
+
+ /// @brief Return the gradient computed at the previously buffered
+ /// location by second order central differencing.
+ ///
+ /// @note This method should not be called until the stencil
+ /// buffer has been populated via a call to moveTo(ijk).
+ inline Vec3Type gradient() const
+ {
+ return Vec3Type(mStencil[2] - mStencil[1],
+ mStencil[4] - mStencil[3],
+ mStencil[6] - mStencil[5])*mInv2Dx;
+ }
+ /// @brief Return the first-order upwind gradient corresponding to the direction V.
+ ///
+ /// @note This method should not be called until the stencil
+ /// buffer has been populated via a call to moveTo(ijk).
+ inline Vec3Type gradient(const Vec3Type& V) const
+ {
+ return Vec3Type(V[0]>0 ? mStencil[0] - mStencil[1] : mStencil[2] - mStencil[0],
+ V[1]>0 ? mStencil[0] - mStencil[3] : mStencil[4] - mStencil[0],
+ V[2]>0 ? mStencil[0] - mStencil[5] : mStencil[6] - mStencil[0])*2*mInv2Dx;
+ }
+
+ /// Return the Laplacian computed at the previously buffered
+ /// location by second-order central differencing.
+ inline ValueType laplacian() const
+ {
+ return mInvDx2 * (mStencil[1] + mStencil[2] +
+ mStencil[3] + mStencil[4] +
+ mStencil[5] + mStencil[6] - 6*mStencil[0]);
+ }
+
+ /// Return @c true if the sign of the value at the center point of the stencil
+ /// is different from the signs of any of its six nearest neighbors.
+ inline bool zeroCrossing() const
+ {
+ const BufferType& v = mStencil;
+ return (v[0]>0 ? (v[1]<0 || v[2]<0 || v[3]<0 || v[4]<0 || v[5]<0 || v[6]<0)
+ : (v[1]>0 || v[2]>0 || v[3]>0 || v[4]>0 || v[5]>0 || v[6]>0));
+ }
+
+ /// @brief Compute the closest-point transform to a level set.
+ /// @return the closest point in index space to the surface
+ /// from which the level set was derived.
+ ///
+ /// @note This method assumes that the grid represents a level set
+ /// with distances in world units and a simple affine transfrom
+ /// with uniform scaling.
+ inline Vec3Type cpt()
+ {
+ const Coord& ijk = BaseType::getCenterCoord();
+ const ValueType d = ValueType(mStencil[0] * 0.5 * mInvDx2); // distance in voxels / (2dx^2)
+ return Vec3Type(ijk[0] - d*(mStencil[2] - mStencil[1]),
+ ijk[1] - d*(mStencil[4] - mStencil[3]),
+ ijk[2] - d*(mStencil[6] - mStencil[5]));
+ }
+
+private:
+ inline void init(const Coord& ijk)
+ {
+ mStencil[1] = mCache.getValue(ijk.offsetBy(-1, 0, 0));
+ mStencil[2] = mCache.getValue(ijk.offsetBy( 1, 0, 0));
+
+ mStencil[3] = mCache.getValue(ijk.offsetBy( 0, -1, 0));
+ mStencil[4] = mCache.getValue(ijk.offsetBy( 0, 1, 0));
+
+ mStencil[5] = mCache.getValue(ijk.offsetBy( 0, 0, -1));
+ mStencil[6] = mCache.getValue(ijk.offsetBy( 0, 0, 1));
+ }
+
+ template<typename, typename> friend class BaseStencil; // allow base class to call init()
+ using BaseType::mCache;
+ using BaseType::mStencil;
+ const ValueType mInv2Dx, mInvDx2;
+}; // class GradStencil
+
+
+////////////////////////////////////////
+
+
+/// @brief This is a special 19-point stencil that supports optimal fifth-order WENO
+/// upwinding, second-order central differencing, Laplacian, and zero-crossing test.
+///
+/// @note For optimal random access performance this class
+/// includes its own grid accessor.
+template<typename GridType>
+class WenoStencil: public BaseStencil<GridType, WenoStencil<GridType> >
+{
+public:
+ typedef BaseStencil<GridType, WenoStencil<GridType> > BaseType;
+ typedef typename BaseType::BufferType BufferType;
+ typedef typename GridType::ValueType ValueType;
+ typedef math::Vec3<ValueType> Vec3Type;
+
+ static const int SIZE = 19;
+
+ WenoStencil(const GridType& grid):
+ BaseType(grid, SIZE),
+ mDx2(ValueType(math::Pow2(grid.voxelSize()[0]))),
+ mInv2Dx(ValueType(0.5 / grid.voxelSize()[0])),
+ mInvDx2(ValueType(1.0 / mDx2))
+ {
+ }
+
+ WenoStencil(const GridType& grid, Real dx):
+ BaseType(grid, SIZE),
+ mDx2(ValueType(dx * dx)),
+ mInv2Dx(ValueType(0.5 / dx)),
+ mInvDx2(ValueType(1.0 / mDx2))
+ {
+ }
+
+ /// @brief Return the norm-square of the WENO upwind gradient (computed via
+ /// WENO upwinding and Gudonov's scheme) at the previously buffered location.
+ ///
+ /// @note This method should not be called until the stencil
+ /// buffer has been populated via a call to moveTo(ijk).
+ inline ValueType normSqGrad() const
+ {
+ const BufferType& v = mStencil;
+#ifdef DWA_OPENVDB
+ // SSE optimized
+ const simd::Float4
+ v1(v[2]-v[1], v[ 8]-v[ 7], v[14]-v[13], 0),
+ v2(v[3]-v[2], v[ 9]-v[ 8], v[15]-v[14], 0),
+ v3(v[0]-v[3], v[ 0]-v[ 9], v[ 0]-v[15], 0),
+ v4(v[4]-v[0], v[10]-v[ 0], v[16]-v[ 0], 0),
+ v5(v[5]-v[4], v[11]-v[10], v[17]-v[16], 0),
+ v6(v[6]-v[5], v[12]-v[11], v[18]-v[17], 0),
+ dP_m = math::WENO5(v1, v2, v3, v4, v5, mDx2),
+ dP_p = math::WENO5(v6, v5, v4, v3, v2, mDx2);
+
+ return mInvDx2 * math::GudonovsNormSqrd(mStencil[0] > 0, dP_m, dP_p);
+#else
+ const Real
+ dP_xm = math::WENO5(v[ 2]-v[ 1],v[ 3]-v[ 2],v[ 0]-v[ 3],v[ 4]-v[ 0],v[ 5]-v[ 4],mDx2),
+ dP_xp = math::WENO5(v[ 6]-v[ 5],v[ 5]-v[ 4],v[ 4]-v[ 0],v[ 0]-v[ 3],v[ 3]-v[ 2],mDx2),
+ dP_ym = math::WENO5(v[ 8]-v[ 7],v[ 9]-v[ 8],v[ 0]-v[ 9],v[10]-v[ 0],v[11]-v[10],mDx2),
+ dP_yp = math::WENO5(v[12]-v[11],v[11]-v[10],v[10]-v[ 0],v[ 0]-v[ 9],v[ 9]-v[ 8],mDx2),
+ dP_zm = math::WENO5(v[14]-v[13],v[15]-v[14],v[ 0]-v[15],v[16]-v[ 0],v[17]-v[16],mDx2),
+ dP_zp = math::WENO5(v[18]-v[17],v[17]-v[16],v[16]-v[ 0],v[ 0]-v[15],v[15]-v[14],mDx2);
+ return mInvDx2*math::GudonovsNormSqrd(v[0]>0,dP_xm,dP_xp,dP_ym,dP_yp,dP_zm,dP_zp);
+#endif
+ }
+
+ /// Return the optimal fifth-order upwind gradient corresponding to the
+ /// direction V.
+ ///
+ /// @note This method should not be called until the stencil
+ /// buffer has been populated via a call to moveTo(ijk).
+ inline Vec3Type gradient(const Vec3Type& V) const
+ {
+ const BufferType& v = mStencil;
+ return 2*mInv2Dx * Vec3Type(
+ V[0]>0 ? math::WENO5(v[ 2]-v[ 1],v[ 3]-v[ 2],v[ 0]-v[ 3], v[ 4]-v[ 0],v[ 5]-v[ 4],mDx2)
+ : math::WENO5(v[ 6]-v[ 5],v[ 5]-v[ 4],v[ 4]-v[ 0], v[ 0]-v[ 3],v[ 3]-v[ 2],mDx2),
+ V[1]>0 ? math::WENO5(v[ 8]-v[ 7],v[ 9]-v[ 8],v[ 0]-v[ 9], v[10]-v[ 0],v[11]-v[10],mDx2)
+ : math::WENO5(v[12]-v[11],v[11]-v[10],v[10]-v[ 0], v[ 0]-v[ 9],v[ 9]-v[ 8],mDx2),
+ V[2]>0 ? math::WENO5(v[14]-v[13],v[15]-v[14],v[ 0]-v[15], v[16]-v[ 0],v[17]-v[16],mDx2)
+ : math::WENO5(v[18]-v[17],v[17]-v[16],v[16]-v[ 0], v[ 0]-v[15],v[15]-v[14],mDx2));
+ }
+ /// Return the gradient computed at the previously buffered
+ /// location by second-order central differencing.
+ ///
+ /// @note This method should not be called until the stencil
+ /// buffer has been populated via a call to moveTo(ijk).
+ inline Vec3Type gradient() const
+ {
+ return mInv2Dx * Vec3Type(
+ mStencil[ 4] - mStencil[ 3],
+ mStencil[10] - mStencil[ 9],
+ mStencil[16] - mStencil[15]);
+ }
+
+ /// Return the Laplacian computed at the previously buffered
+ /// location by second-order central differencing.
+ ///
+ /// @note This method should not be called until the stencil
+ /// buffer has been populated via a call to moveTo(ijk).
+ inline ValueType laplacian() const
+ {
+ return mInvDx2 * (
+ mStencil[ 3] + mStencil[ 4] +
+ mStencil[ 9] + mStencil[10] +
+ mStencil[15] + mStencil[16] - 6*mStencil[0]);
+ }
+
+ /// Return @c true if the sign of the value at the center point of the stencil
+ /// differs from the sign of any of its six nearest neighbors
+ inline bool zeroCrossing() const
+ {
+ const BufferType& v = mStencil;
+ return (v[ 0]>0 ? (v[ 3]<0 || v[ 4]<0 || v[ 9]<0 || v[10]<0 || v[15]<0 || v[16]<0)
+ : (v[ 3]>0 || v[ 4]>0 || v[ 9]>0 || v[10]>0 || v[15]>0 || v[16]>0));
+ }
+
+private:
+ inline void init(const Coord& ijk)
+ {
+ mStencil[ 1] = mCache.getValue(ijk.offsetBy(-3, 0, 0));
+ mStencil[ 2] = mCache.getValue(ijk.offsetBy(-2, 0, 0));
+ mStencil[ 3] = mCache.getValue(ijk.offsetBy(-1, 0, 0));
+ mStencil[ 4] = mCache.getValue(ijk.offsetBy( 1, 0, 0));
+ mStencil[ 5] = mCache.getValue(ijk.offsetBy( 2, 0, 0));
+ mStencil[ 6] = mCache.getValue(ijk.offsetBy( 3, 0, 0));
+
+ mStencil[ 7] = mCache.getValue(ijk.offsetBy( 0, -3, 0));
+ mStencil[ 8] = mCache.getValue(ijk.offsetBy( 0, -2, 0));
+ mStencil[ 9] = mCache.getValue(ijk.offsetBy( 0, -1, 0));
+ mStencil[10] = mCache.getValue(ijk.offsetBy( 0, 1, 0));
+ mStencil[11] = mCache.getValue(ijk.offsetBy( 0, 2, 0));
+ mStencil[12] = mCache.getValue(ijk.offsetBy( 0, 3, 0));
+
+ mStencil[13] = mCache.getValue(ijk.offsetBy( 0, 0, -3));
+ mStencil[14] = mCache.getValue(ijk.offsetBy( 0, 0, -2));
+ mStencil[15] = mCache.getValue(ijk.offsetBy( 0, 0, -1));
+ mStencil[16] = mCache.getValue(ijk.offsetBy( 0, 0, 1));
+ mStencil[17] = mCache.getValue(ijk.offsetBy( 0, 0, 2));
+ mStencil[18] = mCache.getValue(ijk.offsetBy( 0, 0, 3));
+ }
+
+ template<typename, typename> friend class BaseStencil; // allow base class to call init()
+ using BaseType::mCache;
+ using BaseType::mStencil;
+ const ValueType mDx2, mInv2Dx, mInvDx2;
+}; // class WenoStencil
+
+
+//////////////////////////////////////////////////////////////////////
+
+
+template<typename GridType>
+class CurvatureStencil: public BaseStencil<GridType, CurvatureStencil<GridType> >
+{
+public:
+ typedef BaseStencil<GridType, CurvatureStencil<GridType> > BaseType;
+ typedef typename GridType::ValueType ValueType;
+
+ static const int SIZE = 19;
+
+ CurvatureStencil(const GridType& grid):
+ BaseType(grid, SIZE),
+ mInv2Dx(ValueType(0.5 / grid.voxelSize()[0])),
+ mInvDx2(ValueType(4.0 * mInv2Dx * mInv2Dx))
+ {
+ }
+
+ CurvatureStencil(const GridType& grid, Real dx):
+ BaseType(grid, SIZE),
+ mInv2Dx(ValueType(0.5 / dx)),
+ mInvDx2(ValueType(4.0 * mInv2Dx * mInv2Dx))
+ {
+ }
+
+ /// @brief Return the mean curvature at the previously buffered location.
+ ///
+ /// @note This method should not be called until the stencil
+ /// buffer has been populated via a call to moveTo(ijk).
+ inline ValueType meanCurvature()
+ {
+ Real alpha, beta;
+ this->meanCurvature(alpha, beta);
+ return ValueType(alpha*mInv2Dx/math::Pow3(beta));
+ }
+
+ /// Return the mean curvature multiplied by the norm of the
+ /// central-difference gradient. This method is very useful for
+ /// mean-curvature flow of level sets!
+ ///
+ /// @note This method should not be called until the stencil
+ /// buffer has been populated via a call to moveTo(ijk).
+ inline ValueType meanCurvatureNormGrad()
+ {
+ Real alpha, beta;
+ this->meanCurvature(alpha, beta);
+ return ValueType(alpha*mInvDx2/(2*math::Pow2(beta)));
+ }
+
+ /// Return the Laplacian computed at the previously buffered
+ /// location by second-order central differencing.
+ ///
+ /// @note This method should not be called until the stencil
+ /// buffer has been populated via a call to moveTo(ijk).
+ inline ValueType laplacian() const
+ {
+ return mInvDx2 * (
+ mStencil[1] + mStencil[2] +
+ mStencil[3] + mStencil[4] +
+ mStencil[5] + mStencil[6] - 6*mStencil[0]);
+ }
+
+ /// Return the gradient computed at the previously buffered
+ /// location by second-order central differencing.
+ ///
+ /// @note This method should not be called until the stencil
+ /// buffer has been populated via a call to moveTo(ijk).
+ inline math::Vec3<ValueType> gradient()
+ {
+ return math::Vec3<ValueType>(
+ mStencil[2] - mStencil[1],
+ mStencil[4] - mStencil[3],
+ mStencil[6] - mStencil[5])*mInv2Dx;
+ }
+
+private:
+ inline void init(const Coord &ijk)
+ {
+ mStencil[ 1] = mCache.getValue(ijk.offsetBy(-1, 0, 0));
+ mStencil[ 2] = mCache.getValue(ijk.offsetBy( 1, 0, 0));
+
+ mStencil[ 3] = mCache.getValue(ijk.offsetBy( 0, -1, 0));
+ mStencil[ 4] = mCache.getValue(ijk.offsetBy( 0, 1, 0));
+
+ mStencil[ 5] = mCache.getValue(ijk.offsetBy( 0, 0, -1));
+ mStencil[ 6] = mCache.getValue(ijk.offsetBy( 0, 0, 1));
+
+ mStencil[ 7] = mCache.getValue(ijk.offsetBy(-1, -1, 0));
+ mStencil[ 8] = mCache.getValue(ijk.offsetBy( 1, -1, 0));
+ mStencil[ 9] = mCache.getValue(ijk.offsetBy(-1, 1, 0));
+ mStencil[10] = mCache.getValue(ijk.offsetBy( 1, 1, 0));
+
+ mStencil[11] = mCache.getValue(ijk.offsetBy(-1, 0, -1));
+ mStencil[12] = mCache.getValue(ijk.offsetBy( 1, 0, -1));
+ mStencil[13] = mCache.getValue(ijk.offsetBy(-1, 0, 1));
+ mStencil[14] = mCache.getValue(ijk.offsetBy( 1, 0, 1));
+
+ mStencil[15] = mCache.getValue(ijk.offsetBy( 0, -1, -1));
+ mStencil[16] = mCache.getValue(ijk.offsetBy( 0, 1, -1));
+ mStencil[17] = mCache.getValue(ijk.offsetBy( 0, -1, 1));
+ mStencil[18] = mCache.getValue(ijk.offsetBy( 0, 1, 1));
+ }
+
+ inline void meanCurvature(Real& alpha, Real& beta) const
+ {
+ // For performance all finite differences are unscaled wrt dx
+ const Real
+ Half(0.5), Quarter(0.25),
+ Dx = Half * (mStencil[2] - mStencil[1]), Dx2 = Dx * Dx, // * 1/dx
+ Dy = Half * (mStencil[4] - mStencil[3]), Dy2 = Dy * Dy, // * 1/dx
+ Dz = Half * (mStencil[6] - mStencil[5]), Dz2 = Dz * Dz, // * 1/dx
+ Dxx = mStencil[2] - 2 * mStencil[0] + mStencil[1], // * 1/dx2
+ Dyy = mStencil[4] - 2 * mStencil[0] + mStencil[3], // * 1/dx2
+ Dzz = mStencil[6] - 2 * mStencil[0] + mStencil[5], // * 1/dx2
+ Dxy = Quarter * (mStencil[10] - mStencil[ 8] + mStencil[7] - mStencil[ 9]), // * 1/dx2
+ Dxz = Quarter * (mStencil[14] - mStencil[12] + mStencil[11] - mStencil[13]), // * 1/dx2
+ Dyz = Quarter * (mStencil[18] - mStencil[16] + mStencil[15] - mStencil[17]); // * 1/dx2
+ alpha = (Dx2*(Dyy+Dzz)+Dy2*(Dxx+Dzz)+Dz2*(Dxx+Dyy)-2*(Dx*(Dy*Dxy+Dz*Dxz)+Dy*Dz*Dyz));
+ beta = std::sqrt(Dx2 + Dy2 + Dz2); // * 1/dx
+ }
+
+ template<typename, typename> friend class BaseStencil; // allow base class to call init()
+ using BaseType::mCache;
+ using BaseType::mStencil;
+ const ValueType mInv2Dx, mInvDx2;
+}; // class CurvatureStencil
+
+
+//////////////////////////////////////////////////////////////////////
+
+
+/// @brief Dense stencil of a given width
+template<typename GridType>
+class DenseStencil: public BaseStencil<GridType, DenseStencil<GridType> >
+{
+public:
+ typedef BaseStencil<GridType, DenseStencil<GridType> > BaseType;
+ typedef typename GridType::ValueType ValueType;
+
+ DenseStencil(const GridType& grid, int halfWidth) :
+ BaseType(grid, /*size=*/math::Pow3(2 * halfWidth + 1)),
+ mHalfWidth(halfWidth)
+ {
+ //assert(halfWidth>0);//should this be allowed?
+ }
+
+private:
+ /// Initialize the stencil buffer centered at (x, y, z).
+ inline void init(const Coord& ijk)
+ {
+ for (int n=0, i=ijk[0]-mHalfWidth, ie = ijk[0]+mHalfWidth; i <= ie; ++i) {
+ Coord sample_ijk(i,0,0);
+ for (int j = ijk[1]-mHalfWidth, je = ijk[1]+mHalfWidth; j <= je; ++j) {
+ sample_ijk.setY(j);
+ for (int k = ijk[2]-mHalfWidth, ke = ijk[2] + mHalfWidth; k <= ke; ++k) {
+ sample_ijk.setZ(k);
+ mStencil[n++] = mCache.getValue(sample_ijk);
+ }
+ }
+ }
+ }
+
+ template<typename, typename> friend class BaseStencil; // allow base class to call init()
+ using BaseType::mCache;
+ using BaseType::mStencil;
+ const int mHalfWidth;
+};
+
+
+} // end math namespace
+} // namespace OPENVDB_VERSION_NAME
+} // end openvdb namespace
+
+#endif // OPENVDB_MATH_STENCILS_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/math/Transform.cc b/extern/openvdb/internal/openvdb/math/Transform.cc
new file mode 100644
index 00000000000..c0b45710476
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/math/Transform.cc
@@ -0,0 +1,490 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+
+#include "Transform.h"
+#include "LegacyFrustum.h"
+
+#include <openvdb/version.h>
+#include <sstream>
+#include <string>
+#include <vector>
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace math {
+
+
+////////////////////////////////////////
+
+
+Transform::Transform(const MapBase::Ptr& map):
+ mMap(boost::const_pointer_cast</*to=*/MapBase, /*from=*/const MapBase>(map))
+{
+ // auto-convert to simplest type
+ if (!mMap->isType<UniformScaleMap>() && mMap->isLinear()) {
+ AffineMap::Ptr affine = mMap->getAffineMap();
+ mMap = simplify(affine);
+ }
+}
+
+Transform::Transform(const Transform& other):
+ mMap(boost::const_pointer_cast</*to=*/MapBase, /*from=*/const MapBase>(other.baseMap()))
+{
+}
+
+
+////////////////////////////////////////
+
+
+// Factory methods
+
+Transform::Ptr
+Transform::createLinearTransform(double voxelDim)
+{
+ return Transform::Ptr(new Transform(
+ MapBase::Ptr(new UniformScaleMap(voxelDim))));
+}
+
+Transform::Ptr
+Transform::createLinearTransform(const Mat4R& m)
+{
+ return Transform::Ptr(new Transform(MapBase::Ptr(new AffineMap(m))));
+}
+
+Transform::Ptr
+Transform::createFrustumTransform(const BBoxd& bbox, double taper,
+ double depth, double voxelDim)
+{
+ return Transform::Ptr(new Transform(
+ NonlinearFrustumMap(bbox, taper, depth).preScale(Vec3d(voxelDim, voxelDim, voxelDim))));
+}
+
+
+////////////////////////////////////////
+
+
+void
+Transform::read(std::istream& is)
+{
+ // Read the type name.
+ Name type = readString(is);
+
+ if (io::getFormatVersion(is) < OPENVDB_FILE_VERSION_NEW_TRANSFORM) {
+ // Handle old-style transforms.
+
+ if(type == "LinearTransform") {
+ // First read in the old transform's base class.
+ Coord tmpMin, tmpMax;
+ is.read(reinterpret_cast<char*>(&tmpMin), sizeof(Coord::ValueType) * 3);
+ is.read(reinterpret_cast<char*>(&tmpMax), sizeof(Coord::ValueType) * 3);
+
+ // Second read in the old linear transform
+ Mat4d tmpLocalToWorld, tmpWorldToLocal, tmpVoxelToLocal, tmpLocalToVoxel;
+
+ tmpLocalToWorld.read(is);
+ tmpWorldToLocal.read(is);
+ tmpVoxelToLocal.read(is);
+ tmpLocalToVoxel.read(is);
+
+ // Convert and simplify
+ AffineMap::Ptr affineMap(new AffineMap(tmpVoxelToLocal*tmpLocalToWorld));
+ mMap = simplify(affineMap);
+
+ } else if (type == "FrustumTransform") {
+
+ internal::LegacyFrustum legacyFrustum(is);
+
+ CoordBBox bb = legacyFrustum.getBBox();
+ BBoxd bbox(bb.min().asVec3d(), bb.max().asVec3d());
+ double taper = legacyFrustum.getTaper();
+ double depth = legacyFrustum.getDepth();
+
+ double nearPlaneWidth = legacyFrustum.getNearPlaneWidth();
+ double nearPlaneDist = legacyFrustum.getNearPlaneDist();
+ Mat4d camxform = legacyFrustum.getCamXForm();
+
+ // create the new frustum with these parameters
+ Mat4d xform(Mat4d::identity());
+ xform.setToTranslation(Vec3d(0,0, -nearPlaneDist));
+ xform.preScale(Vec3d(nearPlaneWidth, nearPlaneWidth, -nearPlaneWidth));
+
+ MapBase::Ptr linearMap(simplify(AffineMap(xform * camxform).getAffineMap()));
+
+ // note that the depth is scaled on the nearPlaneSize.
+ // the linearMap will uniformly scale the frustum to the correct size
+ // and rotate to align with the camera
+ mMap = MapBase::Ptr(new NonlinearFrustumMap(
+ bbox, taper, depth/nearPlaneWidth, linearMap));
+
+
+ } else {
+ OPENVDB_THROW(IoError, "Transforms of type " + type + " are no longer supported");
+ }
+ } else {
+ // Check if the map has been registered.
+ if (!MapRegistry::instance()->isRegistered(type)) {
+ OPENVDB_THROW(KeyError, "Map " << type << " is not registered");
+ }
+
+ // Create the map of the type and then read it in.
+ mMap = math::MapRegistry::instance()->createMap(type);
+ mMap->read(is);
+ }
+}
+
+
+void
+Transform::write(std::ostream& os) const
+{
+ if (!mMap) OPENVDB_THROW(IoError, "Transform does not have a map");
+
+ // Write the type-name of the map.
+ writeString(os, mMap->type());
+
+ mMap->write(os);
+}
+
+
+////////////////////////////////////////
+
+bool
+Transform::isIdentity() const
+{
+ if (mMap->isLinear()) {
+ return mMap->getAffineMap()->isIdentity();
+ } else if ( mMap->isType<NonlinearFrustumMap>() ) {
+ NonlinearFrustumMap::Ptr frustum
+ = boost::static_pointer_cast<NonlinearFrustumMap, MapBase>(mMap);
+ return frustum->isIdentity();
+ }
+ // unknown nonlinear map type
+ return false;
+}
+
+void
+Transform::preRotate(double radians, const Axis axis)
+{
+ mMap = mMap->preRotate(radians, axis);
+}
+
+void
+Transform::preTranslate(const Vec3d& t)
+{
+ mMap = mMap->preTranslate(t);
+}
+
+void
+Transform::preScale(const Vec3d& s)
+{
+ mMap = mMap->preScale(s);
+}
+
+void
+Transform::preScale(double s)
+{
+ const Vec3d vec(s,s,s);
+ mMap = mMap->preScale(vec);
+}
+
+void
+Transform::preShear(double shear, Axis axis0, Axis axis1)
+{
+ mMap = mMap->preShear(shear, axis0, axis1);
+}
+
+void
+Transform::preMult(const Mat4d& m)
+{
+ if (mMap->isLinear()) {
+
+ const Mat4d currentMat4 = mMap->getAffineMap()->getMat4();
+ const Mat4d newMat4 = m * currentMat4;
+
+ AffineMap::Ptr affineMap( new AffineMap( newMat4) );
+ mMap = simplify(affineMap);
+
+ } else if (mMap->isType<NonlinearFrustumMap>() ) {
+
+ NonlinearFrustumMap::Ptr currentFrustum =
+ boost::static_pointer_cast<NonlinearFrustumMap, MapBase>(mMap);
+
+ const Mat4d currentMat4 = currentFrustum->secondMap().getMat4();
+ const Mat4d newMat4 = m * currentMat4;
+
+ AffineMap affine(newMat4);
+
+ NonlinearFrustumMap::Ptr frustum( new NonlinearFrustumMap( currentFrustum->getBBox(),
+ currentFrustum->getTaper(),
+ currentFrustum->getDepth(),
+ affine.copy() ) );
+ mMap = boost::static_pointer_cast<MapBase, NonlinearFrustumMap>( frustum );
+ }
+
+}
+
+void
+Transform::preMult(const Mat3d& m)
+{
+ Mat4d mat4 = Mat4d::identity();
+ mat4.setMat3(m);
+ preMult(mat4);
+}
+
+void
+Transform::postRotate(double radians, const Axis axis)
+{
+ mMap = mMap->postRotate(radians, axis);
+}
+
+void
+Transform::postTranslate(const Vec3d& t)
+{
+ mMap = mMap->postTranslate(t);
+}
+
+void
+Transform::postScale(const Vec3d& s)
+{
+ mMap = mMap->postScale(s);
+}
+
+void
+Transform::postScale(double s)
+{
+ const Vec3d vec(s,s,s);
+ mMap = mMap->postScale(vec);
+}
+
+void
+Transform::postShear(double shear, Axis axis0, Axis axis1)
+{
+ mMap = mMap->postShear(shear, axis0, axis1);
+}
+
+
+void
+Transform::postMult(const Mat4d& m)
+{
+ if (mMap->isLinear()) {
+
+ const Mat4d currentMat4 = mMap->getAffineMap()->getMat4();
+ const Mat4d newMat4 = currentMat4 * m;
+
+ AffineMap::Ptr affineMap( new AffineMap( newMat4) );
+ mMap = simplify(affineMap);
+
+ } else if (mMap->isType<NonlinearFrustumMap>() ) {
+
+ NonlinearFrustumMap::Ptr currentFrustum =
+ boost::static_pointer_cast<NonlinearFrustumMap, MapBase>(mMap);
+
+ const Mat4d currentMat4 = currentFrustum->secondMap().getMat4();
+ const Mat4d newMat4 = currentMat4 * m;
+
+ AffineMap affine(newMat4);
+
+ NonlinearFrustumMap::Ptr frustum( new NonlinearFrustumMap( currentFrustum->getBBox(),
+ currentFrustum->getTaper(),
+ currentFrustum->getDepth(),
+ affine.copy() ) );
+ mMap = boost::static_pointer_cast<MapBase, NonlinearFrustumMap>( frustum );
+ }
+
+}
+
+void
+Transform::postMult(const Mat3d& m)
+{
+ Mat4d mat4 = Mat4d::identity();
+ mat4.setMat3(m);
+ postMult(mat4);
+}
+
+
+////////////////////////////////////////
+
+// Utility methods
+
+void
+calculateBounds(const Transform& t,
+ const Vec3d& minWS,
+ const Vec3d& maxWS,
+ Vec3d& minIS,
+ Vec3d& maxIS)
+{
+ /// the pre-image of the 8 corners of the box
+ Vec3d corners[8];
+ corners[0] = minWS;
+ corners[1] = Vec3d(maxWS(0), minWS(1), minWS(2));
+ corners[2] = Vec3d(maxWS(0), maxWS(1), minWS(2));
+ corners[3] = Vec3d(minWS(0), maxWS(1), minWS(2));
+ corners[4] = Vec3d(minWS(0), minWS(1), maxWS(2));
+ corners[5] = Vec3d(maxWS(0), minWS(1), maxWS(2));
+ corners[6] = maxWS;
+ corners[7] = Vec3d(minWS(0), maxWS(1), maxWS(2));
+
+ Vec3d pre_image;
+ minIS = t.worldToIndex(corners[0]);
+ maxIS = minIS;
+ for (int i = 1; i < 8; ++i) {
+ pre_image = t.worldToIndex(corners[i]);
+ for (int j = 0; j < 3; ++j) {
+ minIS(j) = std::min(minIS(j), pre_image(j));
+ maxIS(j) = std::max(maxIS(j), pre_image(j));
+ }
+ }
+}
+
+
+////////////////////////////////////////
+
+
+bool
+Transform::operator==(const Transform& other) const
+{
+ if (!this->voxelSize().eq(other.voxelSize())) return false;
+
+ if (this->mapType() == other.mapType()) {
+ return this->baseMap()->isEqual(*other.baseMap());
+ }
+
+ if (this->isLinear() && other.isLinear()) {
+ // promote both maps to mat4 form and compare
+ return ( *(this->baseMap()->getAffineMap()) ==
+ *(other.baseMap()->getAffineMap()) );
+ }
+
+ return this->baseMap()->isEqual(*other.baseMap());
+}
+
+
+////////////////////////////////////////
+
+
+void
+Transform::print(std::ostream& os, const std::string& indent) const
+{
+ struct Local {
+ // Print a Vec4d more compactly than Vec4d::str() does.
+ static std::string rowAsString(const Vec4d& row)
+ {
+ std::ostringstream ostr;
+ ostr << "[" << std::setprecision(3) << row[0] << ", "
+ << row[1] << ", " << row[2] << ", " << row[3] << "] ";
+ return ostr.str();
+ }
+ };
+
+ // Write to a string stream so that I/O manipulators don't affect the output stream.
+ std::ostringstream ostr;
+
+ {
+ Vec3d dim = this->voxelSize();
+ if (dim.eq(Vec3d(dim[0]))) {
+ ostr << indent << std::left << "voxel size: " << std::setprecision(3) << dim[0];
+ } else {
+ ostr << indent << std::left << "voxel dimensions: [" << std::setprecision(3)
+ << dim[0] << ", " << dim[1] << ", " << dim[2] << "]";
+ }
+ ostr << "\n";
+ }
+
+ if (this->isLinear()) {
+ openvdb::Mat4R v2w = this->baseMap()->getAffineMap()->getMat4();
+
+ ostr << indent << std::left << "index to world:\n";
+ for (int row = 0; row < 4; ++row) {
+ ostr << indent << " " << std::left << Local::rowAsString(v2w[row]) << "\n";
+ }
+
+ } else if (this->mapType() == NonlinearFrustumMap::mapType()) {
+ const NonlinearFrustumMap& frustum =
+ static_cast<const NonlinearFrustumMap&>(*this->baseMap());
+ const openvdb::Mat4R linear = this->baseMap()->getAffineMap()->getMat4();
+
+ std::vector<std::string> linearRow;
+ size_t w = 0;
+ for (int row = 0; row < 4; ++row) {
+ std::string str = Local::rowAsString(linear[row]);
+ w = std::max(w, str.size());
+ linearRow.push_back(str);
+ }
+ w = std::max<size_t>(w, 30);
+
+ // Print rows of the linear component matrix side-by-side with frustum parameters.
+ ostr << indent << std::left << std::setw(w) << "linear:"
+ << " frustum:\n";
+ ostr << indent << " " << std::left << std::setw(w) << linearRow[0]
+ << " taper: " << frustum.getTaper() << "\n";
+ ostr << indent << " " << std::left << std::setw(w) << linearRow[1]
+ << " depth: " << frustum.getDepth() << "\n";
+
+ std::ostringstream ostmp;
+ ostmp << indent << " " << std::left << std::setw(w) << linearRow[2]
+ << " bounds: " << frustum.getBBox();
+ if (ostmp.str().size() < 79) {
+ ostr << ostmp.str() << "\n";
+ ostr << indent << " " << std::left << std::setw(w) << linearRow[3] << "\n";
+ } else {
+ // If the frustum bounding box doesn't fit on one line, split it into two lines.
+ ostr << indent << " " << std::left << std::setw(w) << linearRow[2]
+ << " bounds: " << frustum.getBBox().min() << " ->\n";
+ ostr << indent << " " << std::left << std::setw(w) << linearRow[3]
+ << " " << frustum.getBBox().max() << "\n";
+ }
+
+ } else {
+ /// @todo Handle other map types.
+ }
+
+ os << ostr.str();
+}
+
+
+////////////////////////////////////////
+
+
+std::ostream&
+operator<<(std::ostream& os, const Transform& t)
+{
+ os << "Transform type: " << t.baseMap()->type() << std::endl;
+ os << t.baseMap()->str() << std::endl;
+ return os;
+}
+
+
+} // namespace math
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/math/Transform.h b/extern/openvdb/internal/openvdb/math/Transform.h
new file mode 100644
index 00000000000..39794f8613b
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/math/Transform.h
@@ -0,0 +1,295 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+#pragma once
+#ifndef OPENVDB_MATH_TRANSFORM_HAS_BEEN_INCLUDED
+#define OPENVDB_MATH_TRANSFORM_HAS_BEEN_INCLUDED
+
+#include "Maps.h"
+#include <openvdb/Types.h>
+#include <iosfwd>
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace math {
+
+// Forward declaration
+class Transform;
+
+
+// Utility methods
+
+/// @brief Calculate an axis-aligned bounding box in index space from an
+/// axis-aligned bounding box in world space.
+OPENVDB_API void
+calculateBounds(const Transform& t, const Vec3d& minWS, const Vec3d& maxWS,
+ Vec3d& minIS, Vec3d& maxIS);
+
+/// @brief Calculate an axis-aligned bounding box in index space from a
+/// bounding sphere in world space.
+/// @todo void calculateBounds(const Transform& t, const Vec3d& center, const Real radius,
+/// Vec3d& minIS, Vec3d& maxIS);
+
+
+////////////////////////////////////////
+
+
+/// @class Transform
+class OPENVDB_API Transform
+{
+public:
+ typedef boost::shared_ptr<Transform> Ptr;
+ typedef boost::shared_ptr<const Transform> ConstPtr;
+
+ Transform(): mMap(MapBase::Ptr(new ScaleMap())) {}
+ Transform(const MapBase::Ptr&);
+ Transform(const Transform&);
+ ~Transform() {}
+
+ Ptr copy() const { return Ptr(new Transform(mMap->copy())); }
+
+ //@{
+ /// @brief Create and return a shared pointer to a new transform.
+ static Transform::Ptr createLinearTransform(double voxelSize = 1.0);
+ static Transform::Ptr createLinearTransform(const Mat4R&);
+ static Transform::Ptr createFrustumTransform(const BBoxd&, double taper,
+ double depth, double voxelSize = 1.0);
+ //@}
+
+ /// Return @c true if the transformation map is exclusively linear/affine.
+ bool isLinear() const { return mMap->isLinear(); }
+
+ /// Return @c true if the transform is equivalent to an idenity.
+ bool isIdentity() const ;
+ /// Return the transformation map's type-name
+ Name mapType() const { return mMap->type(); }
+
+
+ //@{
+ /// @brief Update the linear (affine) map by prepending or
+ /// postfixing the appropriate operation. In the case of
+ /// a frustum, the pre-operations apply to the linear part
+ /// of the transform and not the entire transform, while the
+ /// post-operations are allways applied last.
+ void preRotate(double radians, const Axis axis = X_AXIS);
+ void preTranslate(const Vec3d&);
+ void preScale(const Vec3d&);
+ void preScale(double);
+ void preShear(double shear, Axis axis0, Axis axis1);
+ void preMult(const Mat4d&);
+ void preMult(const Mat3d&);
+
+ void postRotate(double radians, const Axis axis = X_AXIS);
+ void postTranslate(const Vec3d&);
+ void postScale(const Vec3d&);
+ void postScale(double);
+ void postShear(double shear, Axis axis0, Axis axis1);
+ void postMult(const Mat4d&);
+ void postMult(const Mat3d&);
+ //@}
+
+ /// Return the size of a voxel using the linear component of the map.
+ Vec3d voxelSize() const { return mMap->voxelSize(); }
+ /// @brief Return the size of a voxel at position (x, y, z).
+ /// @note Maps that have a nonlinear component (e.g., perspective and frustum maps)
+ /// have position-dependent voxel sizes.
+ Vec3d voxelSize(const Vec3d& xyz) const { return mMap->voxelSize(xyz); }
+
+ /// Return the voxel volume of the linear component of the map.
+ double voxelVolume() const { return mMap->determinant(); }
+ /// Return the voxel volume at position (x, y, z).
+ double voxelVolume(const Vec3d& xyz) const { return mMap->determinant(xyz); }
+ /// Return true if the voxels in world space are uniformly sized cubes
+ bool hasUniformScale() const { return mMap->hasUniformScale(); }
+
+ //@{
+ /// @brief Apply this transformation to the given coordinates.
+ Vec3d indexToWorld(const Vec3d& xyz) const { return mMap->applyMap(xyz); }
+ Vec3d indexToWorld(const Coord& ijk) const { return mMap->applyMap(ijk.asVec3d()); }
+ Vec3d worldToIndex(const Vec3d& xyz) const { return mMap->applyInverseMap(xyz); }
+ Coord worldToIndexCellCentered(const Vec3d& xyz) const {return Coord::round(worldToIndex(xyz));}
+ Coord worldToIndexNodeCentered(const Vec3d& xyz) const {return Coord::floor(worldToIndex(xyz));}
+ //@}
+
+ //@{
+ /// Return a base pointer to the transformation map.
+ MapBase::ConstPtr baseMap() const { return mMap; }
+ MapBase::Ptr baseMap() { return mMap; }
+ //@}
+
+ //@{
+ /// @brief Return the result of downcasting the base map pointer to a
+ /// @c MapType pointer, or return a null pointer if the types are incompatible.
+ template<typename MapType> typename MapType::Ptr map();
+ template<typename MapType> typename MapType::ConstPtr map() const;
+ template<typename MapType> typename MapType::ConstPtr constMap() const;
+ //@}
+
+ /// Unserialize this transform from the given stream.
+ void read(std::istream&);
+ /// Serialize this transform to the given stream.
+ void write(std::ostream&) const;
+
+ /// @brief Print a description of this transform.
+ /// @param os a stream to which to write textual information
+ /// @param indent a string with which to prefix each line of text
+ void print(std::ostream& os = std::cout, const std::string& indent = "") const;
+
+ bool operator==(const Transform& other) const;
+ inline bool operator!=(const Transform& other) const { return !(*this == other); }
+
+private:
+ MapBase::Ptr mMap;
+}; // class Transform
+
+
+OPENVDB_API std::ostream& operator<<(std::ostream&, const Transform&);
+
+
+////////////////////////////////////////
+
+
+template<typename MapType>
+inline typename MapType::Ptr
+Transform::map()
+{
+ if (mMap->type() == MapType::mapType()) {
+ return boost::static_pointer_cast<MapType>(mMap);
+ }
+ return typename MapType::Ptr();
+}
+
+
+template<typename MapType>
+inline typename MapType::ConstPtr
+Transform::map() const
+{
+ return boost::const_pointer_cast<const MapType>(
+ const_cast<Transform*>(this)->map<MapType>());
+}
+
+
+template<typename MapType>
+inline typename MapType::ConstPtr
+Transform::constMap() const
+{
+ return map<MapType>();
+}
+
+
+////////////////////////////////////////
+
+
+/// Helper function used internally by processTypedMap()
+template<typename ResolvedMapType, typename OpType>
+inline void
+doProcessTypedMap(Transform& transform, OpType& op)
+{
+ ResolvedMapType& resolvedMap = *transform.map<ResolvedMapType>();
+#ifdef _MSC_VER
+ op.operator()<ResolvedMapType>(resolvedMap);
+#else
+ op.template operator()<ResolvedMapType>(resolvedMap);
+#endif
+}
+
+/// Helper function used internally by processTypedMap()
+template<typename ResolvedMapType, typename OpType>
+inline void
+doProcessTypedMap(const Transform& transform, OpType& op)
+{
+ const ResolvedMapType& resolvedMap = *transform.map<ResolvedMapType>();
+#ifdef _MSC_VER
+ op.operator()<ResolvedMapType>(resolvedMap);
+#else
+ op.template operator()<ResolvedMapType>(resolvedMap);
+#endif
+}
+
+
+/// @brief Utility function that, given a generic map pointer,
+/// calls a functor on the fully-resoved map
+///
+/// Usage:
+/// @code
+/// struct Foo {
+/// template<typename MapT>
+/// void operator()(const MapT& map) const { blah }
+/// };
+///
+/// processTypedMap(myMap, Foo());
+/// @endcode
+///
+/// @return @c false if the grid type is unknown or unhandled.
+template<typename TransformType, typename OpType>
+bool
+processTypedMap(TransformType& transform, OpType& op)
+{
+ using namespace openvdb;
+
+ const Name mapType = transform.mapType();
+ if (mapType == UniformScaleMap::mapType()) {
+ doProcessTypedMap<UniformScaleMap, OpType>(transform, op);
+
+ } else if (mapType == UniformScaleTranslateMap::mapType()) {
+ doProcessTypedMap<UniformScaleTranslateMap, OpType>(transform, op);
+
+ } else if (mapType == ScaleMap::mapType()) {
+ doProcessTypedMap<ScaleMap, OpType>(transform, op);
+
+ } else if (mapType == ScaleTranslateMap::mapType()) {
+ doProcessTypedMap<ScaleTranslateMap, OpType>(transform, op);
+
+ } else if (mapType == UnitaryMap::mapType()) {
+ doProcessTypedMap<UnitaryMap, OpType>(transform, op);
+
+ } else if (mapType == AffineMap::mapType()) {
+ doProcessTypedMap<AffineMap, OpType>(transform, op);
+
+ } else if (mapType == TranslationMap::mapType()) {
+ doProcessTypedMap<TranslationMap, OpType>(transform, op);
+
+ } else if (mapType == NonlinearFrustumMap::mapType()) {
+ doProcessTypedMap<NonlinearFrustumMap, OpType>(transform, op);
+ } else {
+ return false;
+ }
+ return true;
+}
+
+} // namespace math
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_MATH_TRANSFORM_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/math/Tuple.h b/extern/openvdb/internal/openvdb/math/Tuple.h
new file mode 100644
index 00000000000..579ccad8608
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/math/Tuple.h
@@ -0,0 +1,232 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+//
+/// @file Tuple.h
+/// @author Ben Kwa
+
+#ifndef OPENVDB_MATH_TUPLE_HAS_BEEN_INCLUDED
+#define OPENVDB_MATH_TUPLE_HAS_BEEN_INCLUDED
+
+#include <sstream>
+#include <boost/type_traits/is_integral.hpp>
+#include "Math.h"
+
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace math {
+
+/// @class Tuple "Tuple.h"
+/// A base class for homogenous tuple types
+template<int SIZE, typename T>
+class Tuple {
+public:
+ typedef T value_type;
+ typedef T ValueType;
+ enum SIZE_ { size = SIZE };
+
+ /// Default ctor. Does nothing. Required because declaring a copy (or
+ /// other) constructor means the default constructor gets left out.
+ Tuple() {}
+
+ /// Copy constructor. Used when the class signature matches exactly.
+ inline Tuple(Tuple const &src) {
+ for (int i = 0; i < SIZE; ++i) {
+ mm[i] = src.mm[i];
+ }
+ }
+
+ /// Conversion constructor. Tuples with different value types and
+ /// different sizes can be interconverted using this member. Converting
+ /// from a larger tuple results in truncation; converting from a smaller
+ /// tuple results in the extra data members being zeroed out. This
+ /// function assumes that the integer 0 is convertible to the tuple's
+ /// value type.
+ template <int src_size, typename src_valtype>
+ explicit Tuple(Tuple<src_size, src_valtype> const &src) {
+ static const int copyEnd = SIZE < src_size ? SIZE : src_size;
+
+ for (int i = 0; i < copyEnd; ++i) {
+ mm[i] = src[i];
+ }
+ for (int i = copyEnd; i < SIZE; ++i) {
+ mm[i] = 0;
+ }
+ }
+
+ T operator[](int i) const {
+ // we'd prefer to use size_t, but can't because gcc3.2 doesn't like
+ // it - it conflicts with child class conversion operators to
+ // pointer types.
+// assert(i >= 0 && i < SIZE);
+ return mm[i];
+ }
+
+ T& operator[](int i) {
+ // see above for size_t vs int
+// assert(i >= 0 && i < SIZE);
+ return mm[i];
+ }
+
+ /// @name Compatibility
+ /// These are mostly for backwards compability with functions that take
+ /// old-style Vs (which are just arrays).
+ //@{
+ /// Copies this tuple into an array of a compatible type
+ template <typename S>
+ void toV(S *v) const {
+ for (int i = 0; i < SIZE; ++i) {
+ v[i] = mm[i];
+ }
+ }
+
+ /// Exposes the internal array. Be careful when using this function.
+ value_type *asV() {
+ return mm;
+ }
+ /// Exposes the internal array. Be careful when using this function.
+ value_type const *asV() const {
+ return mm;
+ }
+ //@} Compatibility
+
+ /// @return string representation of Classname
+ std::string
+ str() const {
+ std::ostringstream buffer;
+
+ buffer << "[";
+
+ // For each column
+ for (unsigned j(0); j < SIZE; j++) {
+ if (j) buffer << ", ";
+ buffer << mm[j];
+ }
+
+ buffer << "]";
+
+ return buffer.str();
+ }
+
+ void write(std::ostream& os) const {
+ os.write(reinterpret_cast<const char*>(&mm), sizeof(T)*SIZE);
+ }
+ void read(std::istream& is) {
+ is.read(reinterpret_cast<char*>(&mm), sizeof(T)*SIZE);
+ }
+
+protected:
+ T mm[SIZE];
+};
+
+
+////////////////////////////////////////
+
+
+/// @return true if t0 < t1, comparing components in order of significance.
+template<int SIZE, typename T0, typename T1>
+bool
+operator<(const Tuple<SIZE, T0>& t0, const Tuple<SIZE, T1>& t1)
+{
+ for (size_t i = 0; i < SIZE-1; ++i) {
+ if (!isExactlyEqual(t0[i], t1[i])) return t0[i] < t1[i];
+ }
+ return t0[SIZE-1] < t1[SIZE-1];
+}
+
+
+/// @return true if t0 > t1, comparing components in order of significance.
+template<int SIZE, typename T0, typename T1>
+bool
+operator>(const Tuple<SIZE, T0>& t0, const Tuple<SIZE, T1>& t1)
+{
+ for (size_t i = 0; i < SIZE-1; ++i) {
+ if (!isExactlyEqual(t0[i], t1[i])) return t0[i] > t1[i];
+ }
+ return t0[SIZE-1] > t1[SIZE-1];
+}
+
+
+////////////////////////////////////////
+
+
+/// Helper class to compute the absolute value of a Tuple
+template<int SIZE, typename T, bool IsInteger>
+struct TupleAbs {
+ static inline Tuple<SIZE, T> absVal(const Tuple<SIZE, T>& t)
+ {
+ Tuple<SIZE, T> result;
+ for (size_t i = 0; i < SIZE; ++i) result[i] = ::fabs(t[i]);
+ return result;
+ }
+};
+
+// Partial specialization for integer types, using abs() instead of fabs()
+template<int SIZE, typename T>
+struct TupleAbs<SIZE, T, /*IsInteger=*/true> {
+ static inline Tuple<SIZE, T> absVal(const Tuple<SIZE, T>& t)
+ {
+ Tuple<SIZE, T> result;
+ for (size_t i = 0; i < SIZE; ++i) result[i] = ::abs(t[i]);
+ return result;
+ }
+};
+
+
+/// @return the absolute value of the given Tuple.
+template<int SIZE, typename T>
+Tuple<SIZE, T>
+Abs(const Tuple<SIZE, T>& t)
+{
+ return TupleAbs<SIZE, T, boost::is_integral<T>::value>::absVal(t);
+}
+
+
+////////////////////////////////////////
+
+
+/// Write a Tuple to an output stream
+template <int SIZE, typename T>
+std::ostream& operator<<(std::ostream& ostr, const Tuple<SIZE, T>& classname)
+{
+ ostr << classname.str();
+ return ostr;
+}
+
+} // namespace math
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_MATH_TUPLE_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/math/Vec2.h b/extern/openvdb/internal/openvdb/math/Vec2.h
new file mode 100644
index 00000000000..b1c31ee668f
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/math/Vec2.h
@@ -0,0 +1,518 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+
+#ifndef OPENVDB_MATH_VEC2_HAS_BEEN_INCLUDED
+#define OPENVDB_MATH_VEC2_HAS_BEEN_INCLUDED
+
+#include <cmath>
+#include <openvdb/Exceptions.h>
+#include "Math.h"
+#include "Tuple.h"
+
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace math {
+
+template<typename T> class Mat2;
+
+template<typename T>
+class Vec2: public Tuple<2, T>
+{
+public:
+ typedef T value_type;
+ typedef T ValueType;
+
+ /// Trivial constructor, the vector is NOT initialized
+ Vec2() {}
+
+ /// Constructor with one argument, e.g. Vec2f v(0);
+ explicit Vec2(T val) { this->mm[0] = this->mm[1] = val; }
+
+ /// Constructor with three arguments, e.g. Vec2f v(1,2,3);
+ Vec2(T x, T y)
+ {
+ this->mm[0] = x;
+ this->mm[1] = y;
+ }
+
+ /// Constructor with array argument, e.g. float a[2]; Vec2f v(a);
+ template <typename Source>
+ Vec2(Source *a)
+ {
+ this->mm[0] = a[0];
+ this->mm[1] = a[1];
+ } // trivial
+
+ /// Conversion constructor
+ template<typename Source>
+ explicit Vec2(const Tuple<2, Source> &t)
+ {
+ this->mm[0] = static_cast<T>(t[0]);
+ this->mm[1] = static_cast<T>(t[1]);
+ }
+
+ /// Reference to the component, e.g. v.x() = 4.5f;
+ T& x() {return this->mm[0];}
+ T& y() {return this->mm[1];}
+
+ /// Get the component, e.g. float f = v.y();
+ T x() const {return this->mm[0];}
+ T y() const {return this->mm[1];}
+
+ /// Alternative indexed reference to the elements
+ T& operator()(int i) {return this->mm[i];}
+
+ /// Alternative indexed constant reference to the elements,
+ T operator()(int i) const {return this->mm[i];}
+
+ T* asPointer() {return this->mm;}
+ const T* asPointer() const {return this->mm;}
+
+ /// "this" vector gets initialized to [x, y, z],
+ /// calling v.init(); has same effect as calling v = Vec2::zero();
+ const Vec2<T>& init(T x=0, T y=0)
+ {
+ this->mm[0] = x; this->mm[1] = y;
+ return *this;
+ }
+
+ /// Set "this" vector to zero
+ const Vec2<T>& setZero()
+ {
+ this->mm[0] = 0; this->mm[1] = 0;
+ return *this;
+ }
+
+ /// Assignment operator
+ template<typename Source>
+ const Vec2<T>& operator=(const Vec2<Source> &v)
+ {
+ // note: don't static_cast because that suppresses warnings
+ this->mm[0] = v[0];
+ this->mm[1] = v[1];
+
+ return *this;
+ }
+
+ /// Equality operator, does exact floating point comparisons
+ bool operator==(const Vec2<T> &v) const
+ {
+ return (isExactlyEqual(this->mm[0], v.mm[0]) && isExactlyEqual(this->mm[1], v.mm[1]));
+ }
+
+ /// Inequality operator, does exact floating point comparisons
+ bool operator!=(const Vec2<T> &v) const { return !(*this==v); }
+
+ /// Test if "this" vector is equivalent to vector v with tolerance of eps
+ bool eq(const Vec2<T> &v, T eps = static_cast<T>(1.0e-7)) const
+ {
+ return isApproxEqual(this->mm[0], v.mm[0], eps) &&
+ isApproxEqual(this->mm[1], v.mm[1], eps);
+ } // trivial
+
+ /// Negation operator, for e.g. v1 = -v2;
+ Vec2<T> operator-() const {return Vec2<T>(-this->mm[0], -this->mm[1]);}
+
+ /// this = v1 + v2
+ /// "this", v1 and v2 need not be distinct objects, e.g. v.add(v1,v);
+ template <typename T0, typename T1>
+ const Vec2<T>& add(const Vec2<T0> &v1, const Vec2<T1> &v2)
+ {
+ this->mm[0] = v1[0] + v2[0];
+ this->mm[1] = v1[1] + v2[1];
+
+ return *this;
+ }
+
+ /// this = v1 - v2
+ /// "this", v1 and v2 need not be distinct objects, e.g. v.sub(v1,v);
+ template <typename T0, typename T1>
+ const Vec2<T>& sub(const Vec2<T0> &v1, const Vec2<T1> &v2)
+ {
+ this->mm[0] = v1[0] - v2[0];
+ this->mm[1] = v1[1] - v2[1];
+
+ return *this;
+ }
+
+ /// this = scalar*v, v need not be a distinct object from "this",
+ /// e.g. v.scale(1.5,v1);
+ template <typename T0, typename T1>
+ const Vec2<T>& scale(T0 scalar, const Vec2<T1> &v)
+ {
+ this->mm[0] = scalar * v[0];
+ this->mm[1] = scalar * v[1];
+
+ return *this;
+ }
+
+ template <typename T0, typename T1>
+ const Vec2<T> &div(T0 scalar, const Vec2<T1> &v)
+ {
+ this->mm[0] = v[0] / scalar;
+ this->mm[1] = v[1] / scalar;
+
+ return *this;
+ }
+
+ /// Dot product
+ T dot(const Vec2<T> &v) const { return this->mm[0]*v[0] + this->mm[1]*v[1]; } // trivial
+
+ /// Length of the vector
+ T length() const
+ {
+ return static_cast<T>(sqrt(double(this->mm[0]*this->mm[0] + this->mm[1]*this->mm[1])));
+ }
+
+ /// Squared length of the vector, much faster than length() as it
+ /// does not involve square root
+ T lengthSqr() const { return (this->mm[0]*this->mm[0] + this->mm[1]*this->mm[1]); }
+
+ /// this = normalized this
+ bool normalize(T eps=1.0e-8)
+ {
+ T d = length();
+ if (isApproxEqual(d, T(0), eps)) {
+ return false;
+ }
+ *this *= (T(1) / d);
+ return true;
+ }
+
+ /// return normalized this, throws if null vector
+ Vec2<T> unit(T eps=0) const
+ {
+ T d;
+ return unit(eps, d);
+ }
+
+ /// return normalized this and length, throws if null vector
+ Vec2<T> unit(T eps, T& len) const
+ {
+ len = length();
+ if (isApproxEqual(len, T(0), eps)) {
+ OPENVDB_THROW(ArithmeticError, "Normalizing null 2-vector");
+ }
+ return *this / len;
+ }
+
+ /// Returns v, where \f$v_i *= scalar\f$ for \f$i \in [0, 1]\f$
+ template <typename S>
+ const Vec2<T> &operator*=(S scalar)
+ {
+ this->mm[0] *= scalar;
+ this->mm[1] *= scalar;
+ return *this;
+ }
+
+ /// Returns v0, where \f$v0_i *= v1_i\f$ for \f$i \in [0, 1]\f$
+ template <typename S>
+ const Vec2<T> &operator*=(const Vec2<S> &v1)
+ {
+ this->mm[0] *= v1[0];
+ this->mm[1] *= v1[1];
+ return *this;
+ }
+
+ /// Returns v, where \f$v_i /= scalar\f$ for \f$i \in [0, 1]\f$
+ template <typename S>
+ const Vec2<T> &operator/=(S scalar)
+ {
+ this->mm[0] /= scalar;
+ this->mm[1] /= scalar;
+ return *this;
+ }
+
+ /// Returns v0, where \f$v0_i /= v1_i\f$ for \f$i \in [0, 1]\f$
+ template <typename S>
+ const Vec2<T> &operator/=(const Vec2<S> &v1)
+ {
+ this->mm[0] /= v1[0];
+ this->mm[1] /= v1[1];
+ return *this;
+ }
+
+ /// Returns v, where \f$v_i += scalar\f$ for \f$i \in [0, 1]\f$
+ template <typename S>
+ const Vec2<T> &operator+=(S scalar)
+ {
+ this->mm[0] += scalar;
+ this->mm[1] += scalar;
+ return *this;
+ }
+
+ /// Returns v0, where \f$v0_i += v1_i\f$ for \f$i \in [0, 1]\f$
+ template <typename S>
+ const Vec2<T> &operator+=(const Vec2<S> &v1)
+ {
+ this->mm[0] += v1[0];
+ this->mm[1] += v1[1];
+ return *this;
+ }
+
+ /// Returns v, where \f$v_i += scalar\f$ for \f$i \in [0, 1]\f$
+ template <typename S>
+ const Vec2<T> &operator-=(S scalar)
+ {
+ this->mm[0] -= scalar;
+ this->mm[1] -= scalar;
+ return *this;
+ }
+
+ /// Returns v0, where \f$v0_i -= v1_i\f$ for \f$i \in [0, 1]\f$
+ template <typename S>
+ const Vec2<T> &operator-=(const Vec2<S> &v1)
+ {
+ this->mm[0] -= v1[0];
+ this->mm[1] -= v1[1];
+ return *this;
+ }
+
+ // Number of cols, rows, elements
+ static unsigned numRows() { return 1; }
+ static unsigned numColumns() { return 2; }
+ static unsigned numElements() { return 2; }
+
+ /// Returns the scalar component of v in the direction of onto, onto need
+ /// not be unit. e.g float c = Vec2f::component(v1,v2);
+ T component(const Vec2<T> &onto, T eps=1.0e-8) const
+ {
+ T l = onto.length();
+ if (isApproxEqual(l, T(0), eps)) return 0;
+
+ return dot(onto)*(T(1)/l);
+ }
+
+ /// Return the projection of v onto the vector, onto need not be unit
+ /// e.g. Vec2f v = Vec2f::projection(v,n);
+ Vec2<T> projection(const Vec2<T> &onto, T eps=1.0e-8) const
+ {
+ T l = onto.lengthSqr();
+ if (isApproxEqual(l, T(0), eps)) return Vec2::zero();
+
+ return onto*(dot(onto)*(T(1)/l));
+ }
+
+ /// Return an arbitrary unit vector perpendicular to v
+ /// Vector v must be a unit vector
+ /// e.g. v.normalize(); Vec2f n = Vec2f::getArbPerpendicular(v);
+ Vec2<T> getArbPerpendicular() const { return Vec2<T>(-this->mm[1], this->mm[0]); }
+
+ /// True if a Nan is present in vector
+ bool isNan() const { return isnan(this->mm[0]) || isnan(this->mm[1]); }
+
+ /// True if an Inf is present in vector
+ bool isInfinite() const { return isinf(this->mm[0]) || isinf(this->mm[1]); }
+
+ /// True if all no Nan or Inf values present
+ bool isFinite() const { return finite(this->mm[0]) && finite(this->mm[1]); }
+
+ /// Predefined constants, e.g. Vec2f v = Vec2f::xNegAxis();
+ static Vec2<T> zero() { return Vec2<T>(0, 0); }
+};
+
+
+/// Returns V, where \f$V_i = v_i * scalar\f$ for \f$i \in [0, 1]\f$
+template <typename S, typename T>
+inline Vec2<typename promote<S, T>::type> operator*(S scalar, const Vec2<T> &v)
+{
+ return v * scalar;
+}
+
+/// Returns V, where \f$V_i = v_i * scalar\f$ for \f$i \in [0, 1]\f$
+template <typename S, typename T>
+inline Vec2<typename promote<S, T>::type> operator*(const Vec2<T> &v, S scalar)
+{
+ Vec2<typename promote<S, T>::type> result(v);
+ result *= scalar;
+ return result;
+}
+
+/// Returns V, where \f$V_i = v0_i * v1_i\f$ for \f$i \in [0, 1]\f$
+template <typename T0, typename T1>
+inline Vec2<typename promote<T0, T1>::type> operator*(const Vec2<T0> &v0, const Vec2<T1> &v1)
+{
+ Vec2<typename promote<T0, T1>::type> result(v0[0] * v1[0], v0[1] * v1[1]);
+ return result;
+}
+
+/// Returns V, where \f$V_i = scalar / v_i\f$ for \f$i \in [0, 1]\f$
+template <typename S, typename T>
+inline Vec2<typename promote<S, T>::type> operator/(S scalar, const Vec2<T> &v)
+{
+ return Vec2<typename promote<S, T>::type>(scalar/v[0], scalar/v[1]);
+}
+
+/// Returns V, where \f$V_i = v_i / scalar\f$ for \f$i \in [0, 1]\f$
+template <typename S, typename T>
+inline Vec2<typename promote<S, T>::type> operator/(const Vec2<T> &v, S scalar)
+{
+ Vec2<typename promote<S, T>::type> result(v);
+ result /= scalar;
+ return result;
+}
+
+/// Returns V, where \f$V_i = v0_i / v1_i\f$ for \f$i \in [0, 1]\f$
+template <typename T0, typename T1>
+inline Vec2<typename promote<T0, T1>::type> operator/(const Vec2<T0> &v0, const Vec2<T1> &v1)
+{
+ Vec2<typename promote<T0, T1>::type> result(v0[0] / v1[0], v0[1] / v1[1]);
+ return result;
+}
+
+/// Returns V, where \f$V_i = v0_i + v1_i\f$ for \f$i \in [0, 1]\f$
+template <typename T0, typename T1>
+inline Vec2<typename promote<T0, T1>::type> operator+(const Vec2<T0> &v0, const Vec2<T1> &v1)
+{
+ Vec2<typename promote<T0, T1>::type> result(v0);
+ result += v1;
+ return result;
+}
+
+/// Returns V, where \f$V_i = v_i + scalar\f$ for \f$i \in [0, 1]\f$
+template <typename S, typename T>
+inline Vec2<typename promote<S, T>::type> operator+(const Vec2<T> &v, S scalar)
+{
+ Vec2<typename promote<S, T>::type> result(v);
+ result += scalar;
+ return result;
+}
+
+/// Returns V, where \f$V_i = v0_i - v1_i\f$ for \f$i \in [0, 1]\f$
+template <typename T0, typename T1>
+inline Vec2<typename promote<T0, T1>::type> operator-(const Vec2<T0> &v0, const Vec2<T1> &v1)
+{
+ Vec2<typename promote<T0, T1>::type> result(v0);
+ result -= v1;
+ return result;
+}
+
+/// Returns V, where \f$V_i = v_i - scalar\f$ for \f$i \in [0, 1]\f$
+template <typename S, typename T>
+inline Vec2<typename promote<S, T>::type> operator-(const Vec2<T> &v, S scalar)
+{
+ Vec2<typename promote<S, T>::type> result(v);
+ result -= scalar;
+ return result;
+}
+
+/// Angle between two vectors, the result is between [0, pi],
+/// e.g. float a = Vec2f::angle(v1,v2);
+template <typename T>
+inline T angle(const Vec2<T> &v1, const Vec2<T> &v2)
+{
+ T c = v1.dot(v2);
+ return acos(c);
+}
+
+template <typename T>
+inline bool
+isApproxEqual(const Vec2<T>& a, const Vec2<T>& b)
+{
+ return a.eq(b);
+}
+template <typename T>
+inline bool
+isApproxEqual(const Vec2<T>& a, const Vec2<T>& b, const Vec2<T>& eps)
+{
+ return isApproxEqual(a.x(), b.x(), eps.x()) &&
+ isApproxEqual(a.y(), b.y(), eps.y());
+}
+
+/// Orthonormalize vectors v1 and v2 and store back the resulting basis
+/// e.g. Vec2f::orthonormalize(v1,v2);
+template <typename T>
+inline void orthonormalize(Vec2<T> &v1, Vec2<T> &v2)
+{
+ // If the input vectors are v0, v1, and v2, then the Gram-Schmidt
+ // orthonormalization produces vectors u0, u1, and u2 as follows,
+ //
+ // u0 = v0/|v0|
+ // u1 = (v1-(u0*v1)u0)/|v1-(u0*v1)u0|
+ //
+ // where |A| indicates length of vector A and A*B indicates dot
+ // product of vectors A and B.
+
+ // compute u0
+ v1.normalize();
+
+ // compute u1
+ T d0 = v1.dot(v2);
+ v2 -= v1*d0;
+ v2.normalize();
+}
+
+
+/// \remark We are switching to a more explicit name because the semantics
+/// are different from std::min/max. In that case, the function returns a
+/// reference to one of the objects based on a comparator. Here, we must
+/// fabricate a new object which might not match either of the inputs.
+
+/// Return component-wise minimum of the two vectors.
+template <typename T>
+inline Vec2<T> minComponent(const Vec2<T> &v1, const Vec2<T> &v2)
+{
+ return Vec2<T>(
+ std::min(v1.x(), v2.x()),
+ std::min(v1.y(), v2.y()));
+}
+
+/// Return component-wise maximum of the two vectors.
+template <typename T>
+inline Vec2<T> maxComponent(const Vec2<T> &v1, const Vec2<T> &v2)
+{
+ return Vec2<T>(
+ std::max(v1.x(), v2.x()),
+ std::max(v1.y(), v2.y()));
+}
+
+
+typedef Vec2<float> Vec2s;
+typedef Vec2<int> Vec2i;
+typedef Vec2<unsigned int> Vec2ui;
+typedef Vec2<double> Vec2d;
+
+#if DWREAL_IS_DOUBLE == 1
+typedef Vec2d Vec2f;
+#else
+typedef Vec2s Vec2f;
+#endif // DWREAL_IS_DOUBLE
+
+} // namespace math
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_MATH_VEC2_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/math/Vec3.h b/extern/openvdb/internal/openvdb/math/Vec3.h
new file mode 100644
index 00000000000..dd0a130b7cd
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/math/Vec3.h
@@ -0,0 +1,620 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+#pragma once
+#ifndef OPENVDB_MATH_VEC3_HAS_BEEN_INCLUDED
+#define OPENVDB_MATH_VEC3_HAS_BEEN_INCLUDED
+
+#include <cmath>
+#include <openvdb/Exceptions.h>
+#include "Math.h"
+#include "Tuple.h"
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace math {
+
+template<typename T> class Mat3;
+
+template<typename T>
+class Vec3: public Tuple<3, T>
+{
+public:
+ typedef T value_type;
+ typedef T ValueType;
+
+ /// Trivial constructor, the vector is NOT initialized
+ Vec3() {}
+
+ /// Constructor with one argument, e.g. Vec3f v(0);
+ explicit Vec3(T val) { this->mm[0] = this->mm[1] = this->mm[2] = val; }
+
+ /// Constructor with three arguments, e.g. Vec3d v(1,2,3);
+ Vec3(T x, T y, T z)
+ {
+ this->mm[0] = x;
+ this->mm[1] = y;
+ this->mm[2] = z;
+ }
+
+ /// Constructor with array argument, e.g. double a[3]; Vec3d v(a);
+ template <typename Source>
+ Vec3(Source *a)
+ {
+ this->mm[0] = a[0];
+ this->mm[1] = a[1];
+ this->mm[2] = a[2];
+ }
+
+ /// Conversion constructor
+ template<typename Source>
+ explicit Vec3(const Tuple<3, Source> &v)
+ {
+ this->mm[0] = static_cast<T>(v[0]);
+ this->mm[1] = static_cast<T>(v[1]);
+ this->mm[2] = static_cast<T>(v[2]);
+ }
+
+ template<typename Other>
+ Vec3(const Vec3<Other>& v)
+ {
+ this->mm[0] = static_cast<T>(v[0]);
+ this->mm[1] = static_cast<T>(v[1]);
+ this->mm[2] = static_cast<T>(v[2]);
+ }
+
+ /// Reference to the component, e.g. v.x() = 4.5f;
+ T& x() { return this->mm[0]; }
+ T& y() { return this->mm[1]; }
+ T& z() { return this->mm[2]; }
+
+ /// Get the component, e.g. float f = v.y();
+ T x() const { return this->mm[0]; }
+ T y() const { return this->mm[1]; }
+ T z() const { return this->mm[2]; }
+
+ T* asPointer() { return this->mm; }
+ const T* asPointer() const { return this->mm; }
+
+ /// Alternative indexed reference to the elements
+ T& operator()(int i) { return this->mm[i]; }
+
+ /// Alternative indexed constant reference to the elements,
+ T operator()(int i) const { return this->mm[i]; }
+
+ /// "this" vector gets initialized to [x, y, z],
+ /// calling v.init(); has same effect as calling v = Vec3::zero();
+ const Vec3<T>& init(T x=0, T y=0, T z=0)
+ {
+ this->mm[0] = x; this->mm[1] = y; this->mm[2] = z;
+ return *this;
+ }
+
+
+ /// Set "this" vector to zero
+ const Vec3<T>& setZero()
+ {
+ this->mm[0] = 0; this->mm[1] = 0; this->mm[2] = 0;
+ return *this;
+ }
+
+ /// Assignment operator
+ template<typename Source>
+ const Vec3<T>& operator=(const Vec3<Source> &v)
+ {
+ // note: don't static_cast because that suppresses warnings
+ this->mm[0] = ValueType(v[0]);
+ this->mm[1] = ValueType(v[1]);
+ this->mm[2] = ValueType(v[2]);
+
+ return *this;
+ }
+
+ /// Test if "this" vector is equivalent to vector v with tolerance of eps
+ bool eq(const Vec3<T> &v, T eps = static_cast<T>(1.0e-7)) const
+ {
+ return isRelOrApproxEqual(this->mm[0], v.mm[0], eps, eps) &&
+ isRelOrApproxEqual(this->mm[1], v.mm[1], eps, eps) &&
+ isRelOrApproxEqual(this->mm[2], v.mm[2], eps, eps);
+ }
+
+
+ /// Negation operator, for e.g. v1 = -v2;
+ Vec3<T> operator-() const { return Vec3<T>(-this->mm[0], -this->mm[1], -this->mm[2]); }
+
+ /// this = v1 + v2
+ /// "this", v1 and v2 need not be distinct objects, e.g. v.add(v1,v);
+ template <typename T0, typename T1>
+ const Vec3<T>& add(const Vec3<T0> &v1, const Vec3<T1> &v2)
+ {
+ this->mm[0] = v1[0] + v2[0];
+ this->mm[1] = v1[1] + v2[1];
+ this->mm[2] = v1[2] + v2[2];
+
+ return *this;
+ }
+
+ /// this = v1 - v2
+ /// "this", v1 and v2 need not be distinct objects, e.g. v.sub(v1,v);
+ template <typename T0, typename T1>
+ const Vec3<T>& sub(const Vec3<T0> &v1, const Vec3<T1> &v2)
+ {
+ this->mm[0] = v1[0] - v2[0];
+ this->mm[1] = v1[1] - v2[1];
+ this->mm[2] = v1[2] - v2[2];
+
+ return *this;
+ }
+
+ /// this = scalar*v, v need not be a distinct object from "this",
+ /// e.g. v.scale(1.5,v1);
+ template <typename T0, typename T1>
+ const Vec3<T>& scale(T0 scale, const Vec3<T1> &v)
+ {
+ this->mm[0] = scale * v[0];
+ this->mm[1] = scale * v[1];
+ this->mm[2] = scale * v[2];
+
+ return *this;
+ }
+
+ template <typename T0, typename T1>
+ const Vec3<T> &div(T0 scale, const Vec3<T1> &v)
+ {
+ this->mm[0] = v[0] / scale;
+ this->mm[1] = v[1] / scale;
+ this->mm[2] = v[2] / scale;
+
+ return *this;
+ }
+
+ /// Dot product
+ T dot(const Vec3<T> &v) const
+ {
+ return
+ this->mm[0]*v.mm[0] +
+ this->mm[1]*v.mm[1] +
+ this->mm[2]*v.mm[2];
+ }
+
+ /// Length of the vector
+ T length() const
+ {
+ return static_cast<T>(sqrt(double(
+ this->mm[0]*this->mm[0] +
+ this->mm[1]*this->mm[1] +
+ this->mm[2]*this->mm[2])));
+ }
+
+
+ /// Squared length of the vector, much faster than length() as it
+ /// does not involve square root
+ T lengthSqr() const
+ {
+ return
+ this->mm[0]*this->mm[0] +
+ this->mm[1]*this->mm[1] +
+ this->mm[2]*this->mm[2];
+ }
+
+ /// Return the cross product of "this" vector and v;
+ Vec3<T> cross(const Vec3<T> &v) const
+ {
+ return Vec3<T>(this->mm[1]*v.mm[2] - this->mm[2]*v.mm[1],
+ this->mm[2]*v.mm[0] - this->mm[0]*v.mm[2],
+ this->mm[0]*v.mm[1] - this->mm[1]*v.mm[0]);
+ }
+
+
+ /// this = v1 cross v2, v1 and v2 must be distinct objects than "this"
+ const Vec3<T>& cross(const Vec3<T> &v1, const Vec3<T> &v2)
+ {
+ // assert(this!=&v1);
+ // assert(this!=&v2);
+ this->mm[0] = v1.mm[1]*v2.mm[2] - v1.mm[2]*v2.mm[1];
+ this->mm[1] = v1.mm[2]*v2.mm[0] - v1.mm[0]*v2.mm[2];
+ this->mm[2] = v1.mm[0]*v2.mm[1] - v1.mm[1]*v2.mm[0];
+ return *this;
+ }
+
+ /// Returns v, where \f$v_i *= scalar\f$ for \f$i \in [0, 2]\f$
+ template <typename S>
+ const Vec3<T> &operator*=(S scalar)
+ {
+ this->mm[0] *= scalar;
+ this->mm[1] *= scalar;
+ this->mm[2] *= scalar;
+ return *this;
+ }
+
+ /// Returns v0, where \f$v0_i *= v1_i\f$ for \f$i \in [0, 2]\f$
+ template <typename S>
+ const Vec3<T> &operator*=(const Vec3<S> &v1)
+ {
+ this->mm[0] *= v1[0];
+ this->mm[1] *= v1[1];
+ this->mm[2] *= v1[2];
+ return *this;
+ }
+
+ /// Returns v, where \f$v_i /= scalar\f$ for \f$i \in [0, 2]\f$
+ template <typename S>
+ const Vec3<T> &operator/=(S scalar)
+ {
+ this->mm[0] /= scalar;
+ this->mm[1] /= scalar;
+ this->mm[2] /= scalar;
+ return *this;
+ }
+
+ /// Returns v0, where \f$v0_i /= v1_i\f$ for \f$i \in [0, 2]\f$
+ template <typename S>
+ const Vec3<T> &operator/=(const Vec3<S> &v1)
+ {
+ this->mm[0] /= v1[0];
+ this->mm[1] /= v1[1];
+ this->mm[2] /= v1[2];
+ return *this;
+ }
+
+ /// Returns v, where \f$v_i += scalar\f$ for \f$i \in [0, 2]\f$
+ template <typename S>
+ const Vec3<T> &operator+=(S scalar)
+ {
+ this->mm[0] += scalar;
+ this->mm[1] += scalar;
+ this->mm[2] += scalar;
+ return *this;
+ }
+
+ /// Returns v0, where \f$v0_i += v1_i\f$ for \f$i \in [0, 2]\f$
+ template <typename S>
+ const Vec3<T> &operator+=(const Vec3<S> &v1)
+ {
+ this->mm[0] += v1[0];
+ this->mm[1] += v1[1];
+ this->mm[2] += v1[2];
+ return *this;
+ }
+
+ /// Returns v, where \f$v_i += scalar\f$ for \f$i \in [0, 2]\f$
+ template <typename S>
+ const Vec3<T> &operator-=(S scalar)
+ {
+ this->mm[0] -= scalar;
+ this->mm[1] -= scalar;
+ this->mm[2] -= scalar;
+ return *this;
+ }
+
+ /// Returns v0, where \f$v0_i -= v1_i\f$ for \f$i \in [0, 2]\f$
+ template <typename S>
+ const Vec3<T> &operator-=(const Vec3<S> &v1)
+ {
+ this->mm[0] -= v1[0];
+ this->mm[1] -= v1[1];
+ this->mm[2] -= v1[2];
+ return *this;
+ }
+
+ /// this = normalized this
+ bool normalize(T eps = T(1.0e-7))
+ {
+ T d = length();
+ if (isApproxEqual(d, T(0), eps)) {
+ return false;
+ }
+ *this *= (T(1) / d);
+ return true;
+ }
+
+
+ /// return normalized this, throws if null vector
+ Vec3<T> unit(T eps=0) const
+ {
+ T d;
+ return unit(eps, d);
+ }
+
+ /// return normalized this and length, throws if null vector
+ Vec3<T> unit(T eps, T& len) const
+ {
+ len = length();
+ if (isApproxEqual(len, T(0), eps)) {
+ OPENVDB_THROW(ArithmeticError, "Normalizing null 3-vector");
+ }
+ return *this / len;
+ }
+
+ // Number of cols, rows, elements
+ static unsigned numRows() { return 1; }
+ static unsigned numColumns() { return 3; }
+ static unsigned numElements() { return 3; }
+
+ /// Returns the scalar component of v in the direction of onto, onto need
+ /// not be unit. e.g double c = Vec3d::component(v1,v2);
+ T component(const Vec3<T> &onto, T eps=1.0e-7) const
+ {
+ T l = onto.length();
+ if (isApproxEqual(l, T(0), eps)) return 0;
+
+ return dot(onto)*(T(1)/l);
+ }
+
+ /// Return the projection of v onto the vector, onto need not be unit
+ /// e.g. Vec3d a = vprojection(n);
+ Vec3<T> projection(const Vec3<T> &onto, T eps=1.0e-7) const
+ {
+ T l = onto.lengthSqr();
+ if (isApproxEqual(l, T(0), eps)) return Vec3::zero();
+
+ return onto*(dot(onto)*(T(1)/l));
+ }
+
+ /// Return an arbitrary unit vector perpendicular to v
+ /// Vector this must be a unit vector
+ /// e.g. v = v.normalize(); Vec3d n = v.getArbPerpendicular();
+ Vec3<T> getArbPerpendicular() const
+ {
+ Vec3<T> u;
+ T l;
+
+ if ( fabs(this->mm[0]) >= fabs(this->mm[1]) ) {
+ // v.x or v.z is the largest magnitude component, swap them
+ l = this->mm[0]*this->mm[0] + this->mm[2]*this->mm[2];
+ l = static_cast<T>(T(1)/sqrt(double(l)));
+ u.mm[0] = -this->mm[2]*l;
+ u.mm[1] = (T)0.0;
+ u.mm[2] = +this->mm[0]*l;
+ } else {
+ // W.y or W.z is the largest magnitude component, swap them
+ l = this->mm[1]*this->mm[1] + this->mm[2]*this->mm[2];
+ l = static_cast<T>(T(1)/sqrt(double(l)));
+ u.mm[0] = (T)0.0;
+ u.mm[1] = +this->mm[2]*l;
+ u.mm[2] = -this->mm[1]*l;
+ }
+
+ return u;
+ }
+
+ /// True if a Nan is present in vector
+ bool isNan() const { return isnan(this->mm[0]) || isnan(this->mm[1]) || isnan(this->mm[2]); }
+
+ /// True if an Inf is present in vector
+ bool isInfinite() const
+ {
+ return isinf(this->mm[0]) || isinf(this->mm[1]) || isinf(this->mm[2]);
+ }
+
+ /// True if all no Nan or Inf values present
+ bool isFinite() const
+ {
+ return finite(this->mm[0]) && finite(this->mm[1]) && finite(this->mm[2]);
+ }
+
+ /// Predefined constants, e.g. Vec3d v = Vec3d::xNegAxis();
+ static Vec3<T> zero() { return Vec3<T>(0, 0, 0); }
+};
+
+
+/// Equality operator, does exact floating point comparisons
+template <typename T0, typename T1>
+inline bool operator==(const Vec3<T0> &v0, const Vec3<T1> &v1)
+{
+ return isExactlyEqual(v0[0], v1[0]) && isExactlyEqual(v0[1], v1[1])
+ && isExactlyEqual(v0[2], v1[2]);
+}
+
+/// Inequality operator, does exact floating point comparisons
+template <typename T0, typename T1>
+inline bool operator!=(const Vec3<T0> &v0, const Vec3<T1> &v1) { return !(v0==v1); }
+
+/// Returns V, where \f$V_i = v_i * scalar\f$ for \f$i \in [0, 2]\f$
+template <typename S, typename T>
+inline Vec3<typename promote<S, T>::type> operator*(S scalar, const Vec3<T> &v) { return v*scalar; }
+
+/// Returns V, where \f$V_i = v_i * scalar\f$ for \f$i \in [0, 2]\f$
+template <typename S, typename T>
+inline Vec3<typename promote<S, T>::type> operator*(const Vec3<T> &v, S scalar)
+{
+ Vec3<typename promote<S, T>::type> result(v);
+ result *= scalar;
+ return result;
+}
+
+/// Returns V, where \f$V_i = v0_i * v1_i\f$ for \f$i \in [0, 2]\f$
+template <typename T0, typename T1>
+inline Vec3<typename promote<T0, T1>::type> operator*(const Vec3<T0> &v0, const Vec3<T1> &v1)
+{
+ Vec3<typename promote<T0, T1>::type> result(v0[0] * v1[0], v0[1] * v1[1], v0[2] * v1[2]);
+ return result;
+}
+
+
+/// Returns V, where \f$V_i = scalar / v_i\f$ for \f$i \in [0, 2]\f$
+template <typename S, typename T>
+inline Vec3<typename promote<S, T>::type> operator/(S scalar, const Vec3<T> &v)
+{
+ return Vec3<typename promote<S, T>::type>(scalar/v[0], scalar/v[1], scalar/v[2]);
+}
+
+/// Returns V, where \f$V_i = v_i / scalar\f$ for \f$i \in [0, 2]\f$
+template <typename S, typename T>
+inline Vec3<typename promote<S, T>::type> operator/(const Vec3<T> &v, S scalar)
+{
+ Vec3<typename promote<S, T>::type> result(v);
+ result /= scalar;
+ return result;
+}
+
+/// Returns V, where \f$V_i = v0_i / v1_i\f$ for \f$i \in [0, 2]\f$
+template <typename T0, typename T1>
+inline Vec3<typename promote<T0, T1>::type> operator/(const Vec3<T0> &v0, const Vec3<T1> &v1)
+{
+ Vec3<typename promote<T0, T1>::type> result(v0[0] / v1[0], v0[1] / v1[1], v0[2] / v1[2]);
+ return result;
+}
+
+/// Returns V, where \f$V_i = v0_i + v1_i\f$ for \f$i \in [0, 2]\f$
+template <typename T0, typename T1>
+inline Vec3<typename promote<T0, T1>::type> operator+(const Vec3<T0> &v0, const Vec3<T1> &v1)
+{
+ Vec3<typename promote<T0, T1>::type> result(v0);
+ result += v1;
+ return result;
+}
+
+/// Returns V, where \f$V_i = v_i + scalar\f$ for \f$i \in [0, 2]\f$
+template <typename S, typename T>
+inline Vec3<typename promote<S, T>::type> operator+(const Vec3<T> &v, S scalar)
+{
+ Vec3<typename promote<S, T>::type> result(v);
+ result += scalar;
+ return result;
+}
+
+/// Returns V, where \f$V_i = v0_i - v1_i\f$ for \f$i \in [0, 2]\f$
+template <typename T0, typename T1>
+inline Vec3<typename promote<T0, T1>::type> operator-(const Vec3<T0> &v0, const Vec3<T1> &v1)
+{
+ Vec3<typename promote<T0, T1>::type> result(v0);
+ result -= v1;
+ return result;
+}
+
+/// Returns V, where \f$V_i = v_i - scalar\f$ for \f$i \in [0, 2]\f$
+template <typename S, typename T>
+inline Vec3<typename promote<S, T>::type> operator-(const Vec3<T> &v, S scalar)
+{
+ Vec3<typename promote<S, T>::type> result(v);
+ result -= scalar;
+ return result;
+}
+
+/// Angle between two vectors, the result is between [0, pi],
+/// e.g. double a = Vec3d::angle(v1,v2);
+template <typename T>
+inline T angle(const Vec3<T> &v1, const Vec3<T> &v2)
+{
+ Vec3<T> c = v1.cross(v2);
+ return static_cast<T>(atan2(c.length(), v1.dot(v2)));
+}
+
+template <typename T>
+inline bool
+isApproxEqual(const Vec3<T>& a, const Vec3<T>& b)
+{
+ return a.eq(b);
+}
+template <typename T>
+inline bool
+isApproxEqual(const Vec3<T>& a, const Vec3<T>& b, const Vec3<T>& eps)
+{
+ return isApproxEqual(a.x(), b.x(), eps.x()) &&
+ isApproxEqual(a.y(), b.y(), eps.y()) &&
+ isApproxEqual(a.z(), b.z(), eps.z());
+}
+
+/// Orthonormalize vectors v1, v2 and v3 and store back the resulting
+/// basis e.g. Vec3d::orthonormalize(v1,v2,v3);
+template <typename T>
+inline void orthonormalize(Vec3<T> &v1, Vec3<T> &v2, Vec3<T> &v3)
+{
+ // If the input vectors are v0, v1, and v2, then the Gram-Schmidt
+ // orthonormalization produces vectors u0, u1, and u2 as follows,
+ //
+ // u0 = v0/|v0|
+ // u1 = (v1-(u0*v1)u0)/|v1-(u0*v1)u0|
+ // u2 = (v2-(u0*v2)u0-(u1*v2)u1)/|v2-(u0*v2)u0-(u1*v2)u1|
+ //
+ // where |A| indicates length of vector A and A*B indicates dot
+ // product of vectors A and B.
+
+ // compute u0
+ v1.normalize();
+
+ // compute u1
+ T d0 = v1.dot(v2);
+ v2 -= v1*d0;
+ v2.normalize();
+
+ // compute u2
+ T d1 = v2.dot(v3);
+ d0 = v1.dot(v3);
+ v3 -= v1*d0 + v2*d1;
+ v3.normalize();
+}
+
+/// @remark We are switching to a more explicit name because the semantics
+/// are different from std::min/max. In that case, the function returns a
+/// reference to one of the objects based on a comparator. Here, we must
+/// fabricate a new object which might not match either of the inputs.
+
+/// Return component-wise minimum of the two vectors.
+template <typename T>
+inline Vec3<T> minComponent(const Vec3<T> &v1, const Vec3<T> &v2)
+{
+ return Vec3<T>(
+ std::min(v1.x(), v2.x()),
+ std::min(v1.y(), v2.y()),
+ std::min(v1.z(), v2.z()));
+}
+
+/// Return component-wise maximum of the two vectors.
+template <typename T>
+inline Vec3<T> maxComponent(const Vec3<T> &v1, const Vec3<T> &v2)
+{
+ return Vec3<T>(
+ std::max(v1.x(), v2.x()),
+ std::max(v1.y(), v2.y()),
+ std::max(v1.z(), v2.z()));
+}
+
+typedef Vec3<int> Vec3i;
+typedef Vec3<unsigned int> Vec3ui;
+typedef Vec3<float> Vec3s;
+typedef Vec3<double> Vec3d;
+
+#if DWREAL_IS_DOUBLE == 1
+typedef Vec3d Vec3f;
+#else
+typedef Vec3s Vec3f;
+#endif // DWREAL_IS_DOUBLE
+
+} // namespace math
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_MATH_VEC3_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/math/Vec4.h b/extern/openvdb/internal/openvdb/math/Vec4.h
new file mode 100644
index 00000000000..04fc78d0f3c
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/math/Vec4.h
@@ -0,0 +1,540 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+
+#ifndef OPENVDB_MATH_VEC4_HAS_BEEN_INCLUDED
+#define OPENVDB_MATH_VEC4_HAS_BEEN_INCLUDED
+
+#include <cmath>
+#include <openvdb/Exceptions.h>
+#include "Math.h"
+#include "Tuple.h"
+#include "Vec3.h"
+
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace math {
+
+template<typename T> class Mat3;
+
+template<typename T>
+class Vec4: public Tuple<4, T>
+{
+public:
+ typedef T value_type;
+ typedef T ValueType;
+
+ /// Trivial constructor, the vector is NOT initialized
+ Vec4() {}
+
+ /// Constructor with one argument, e.g. Vec4f v(0);
+ explicit Vec4(T val) { this->mm[0] = this->mm[1] = this->mm[2] = this->mm[3] = val; }
+
+ /// Constructor with three arguments, e.g. Vec4f v(1,2,3);
+ Vec4(T x, T y, T z, T w)
+ {
+ this->mm[0] = x;
+ this->mm[1] = y;
+ this->mm[2] = z;
+ this->mm[3] = w;
+ }
+
+ /// Constructor with array argument, e.g. float a[4]; Vec4f v(a);
+ template <typename Source>
+ Vec4(Source *a)
+ {
+ this->mm[0] = a[0];
+ this->mm[1] = a[1];
+ this->mm[2] = a[2];
+ this->mm[3] = a[3];
+ }
+
+ /// Conversion constructor
+ template<typename Source>
+ explicit Vec4(const Tuple<4, Source> &v)
+ {
+ this->mm[0] = static_cast<T>(v[0]);
+ this->mm[1] = static_cast<T>(v[1]);
+ this->mm[2] = static_cast<T>(v[2]);
+ this->mm[3] = static_cast<T>(v[3]);
+ }
+
+ /// Reference to the component, e.g. v.x() = 4.5f;
+ T& x() { return this->mm[0]; }
+ T& y() { return this->mm[1]; }
+ T& z() { return this->mm[2]; }
+ T& w() { return this->mm[3]; }
+
+ /// Get the component, e.g. float f = v.y();
+ T x() const { return this->mm[0]; }
+ T y() const { return this->mm[1]; }
+ T z() const { return this->mm[2]; }
+ T w() const { return this->mm[3]; }
+
+ T* asPointer() { return this->mm; }
+ const T* asPointer() const { return this->mm; }
+
+ /// Alternative indexed reference to the elements
+ T& operator()(int i) { return this->mm[i]; }
+
+ /// Alternative indexed constant reference to the elements,
+ T operator()(int i) const { return this->mm[i]; }
+
+ /// Returns a Vec3 with the first three elements of the Vec4.
+ Vec3<T> getVec3() const { return Vec3<T>(this->mm[0], this->mm[1], this->mm[2]); }
+
+ /// "this" vector gets initialized to [x, y, z, w],
+ /// calling v.init(); has same effect as calling v = Vec4::zero();
+ const Vec4<T>& init(T x=0, T y=0, T z=0, T w=0)
+ {
+ this->mm[0] = x; this->mm[1] = y; this->mm[2] = z; this->mm[3] = w;
+ return *this;
+ }
+
+ /// Set "this" vector to zero
+ const Vec4<T>& setZero()
+ {
+ this->mm[0] = 0; this->mm[1] = 0; this->mm[2] = 0; this->mm[3] = 0;
+ return *this;
+ }
+
+ /// Assignment operator
+ template<typename Source>
+ const Vec4<T>& operator=(const Vec4<Source> &v)
+ {
+ // note: don't static_cast because that suppresses warnings
+ this->mm[0] = v[0];
+ this->mm[1] = v[1];
+ this->mm[2] = v[2];
+ this->mm[3] = v[3];
+
+ return *this;
+ }
+
+ /// Test if "this" vector is equivalent to vector v with tolerance
+ /// of eps
+ bool eq(const Vec4<T> &v, T eps=1.0e-8) const
+ {
+ return isApproxEqual(this->mm[0], v.mm[0], eps) &&
+ isApproxEqual(this->mm[1], v.mm[1], eps) &&
+ isApproxEqual(this->mm[2], v.mm[2], eps) &&
+ isApproxEqual(this->mm[3], v.mm[3], eps);
+ }
+
+ /// Negation operator, for e.g. v1 = -v2;
+ Vec4<T> operator-() const
+ {
+ return Vec4<T>(
+ -this->mm[0],
+ -this->mm[1],
+ -this->mm[2],
+ -this->mm[3]);
+ }
+
+ /// this = v1 + v2
+ /// "this", v1 and v2 need not be distinct objects, e.g. v.add(v1,v);
+ template <typename T0, typename T1>
+ const Vec4<T>& add(const Vec4<T0> &v1, const Vec4<T1> &v2)
+ {
+ this->mm[0] = v1[0] + v2[0];
+ this->mm[1] = v1[1] + v2[1];
+ this->mm[2] = v1[2] + v2[2];
+ this->mm[3] = v1[3] + v2[3];
+
+ return *this;
+ }
+
+
+ /// this = v1 - v2
+ /// "this", v1 and v2 need not be distinct objects, e.g. v.sub(v1,v);
+ template <typename T0, typename T1>
+ const Vec4<T>& sub(const Vec4<T0> &v1, const Vec4<T1> &v2)
+ {
+ this->mm[0] = v1[0] - v2[0];
+ this->mm[1] = v1[1] - v2[1];
+ this->mm[2] = v1[2] - v2[2];
+ this->mm[3] = v1[3] - v2[3];
+
+ return *this;
+ }
+
+ /// this = scalar*v, v need not be a distinct object from "this",
+ /// e.g. v.scale(1.5,v1);
+ template <typename T0, typename T1>
+ const Vec4<T>& scale(T0 scale, const Vec4<T1> &v)
+ {
+ this->mm[0] = scale * v[0];
+ this->mm[1] = scale * v[1];
+ this->mm[2] = scale * v[2];
+ this->mm[3] = scale * v[3];
+
+ return *this;
+ }
+
+ template <typename T0, typename T1>
+ const Vec4<T> &div(T0 scalar, const Vec4<T1> &v)
+ {
+ this->mm[0] = v[0] / scalar;
+ this->mm[1] = v[1] / scalar;
+ this->mm[2] = v[2] / scalar;
+ this->mm[3] = v[3] / scalar;
+
+ return *this;
+ }
+
+ /// Dot product
+ T dot(const Vec4<T> &v) const
+ {
+ return (this->mm[0]*v.mm[0] + this->mm[1]*v.mm[1]
+ + this->mm[2]*v.mm[2] + this->mm[3]*v.mm[3]);
+ }
+
+ /// Length of the vector
+ T length() const
+ {
+ return sqrt(
+ this->mm[0]*this->mm[0] +
+ this->mm[1]*this->mm[1] +
+ this->mm[2]*this->mm[2] +
+ this->mm[3]*this->mm[3]);
+ }
+
+
+ /// Squared length of the vector, much faster than length() as it
+ /// does not involve square root
+ T lengthSqr() const
+ {
+ return (this->mm[0]*this->mm[0] + this->mm[1]*this->mm[1]
+ + this->mm[2]*this->mm[2] + this->mm[3]*this->mm[3]);
+ }
+
+ /// this = normalized this
+ bool normalize(T eps=1.0e-8)
+ {
+ T d = length();
+ if (isApproxEqual(d, T(0), eps)) {
+ return false;
+ }
+ *this *= (T(1) / d);
+ return true;
+ }
+
+ /// return normalized this, throws if null vector
+ Vec4<T> unit(T eps=0) const
+ {
+ T d;
+ return unit(eps, d);
+ }
+
+ /// return normalized this and length, throws if null vector
+ Vec4<T> unit(T eps, T& len) const
+ {
+ len = length();
+ if (isApproxEqual(len, T(0), eps)) {
+ throw ArithmeticError("Normalizing null 4-vector");
+ }
+ return *this / len;
+ }
+
+ /// Returns v, where \f$v_i *= scalar\f$ for \f$i \in [0, 3]\f$
+ template <typename S>
+ const Vec4<T> &operator*=(S scalar)
+ {
+ this->mm[0] *= scalar;
+ this->mm[1] *= scalar;
+ this->mm[2] *= scalar;
+ this->mm[3] *= scalar;
+ return *this;
+ }
+
+ /// Returns v0, where \f$v0_i *= v1_i\f$ for \f$i \in [0, 3]\f$
+ template <typename S>
+ const Vec4<T> &operator*=(const Vec4<S> &v1)
+ {
+ this->mm[0] *= v1[0];
+ this->mm[1] *= v1[1];
+ this->mm[2] *= v1[2];
+ this->mm[3] *= v1[3];
+
+ return *this;
+ }
+
+ /// Returns v, where \f$v_i /= scalar\f$ for \f$i \in [0, 3]\f$
+ template <typename S>
+ const Vec4<T> &operator/=(S scalar)
+ {
+ this->mm[0] /= scalar;
+ this->mm[1] /= scalar;
+ this->mm[2] /= scalar;
+ this->mm[3] /= scalar;
+ return *this;
+ }
+
+ /// Returns v0, where \f$v0_i /= v1_i\f$ for \f$i \in [0, 3]\f$
+ template <typename S>
+ const Vec4<T> &operator/=(const Vec4<S> &v1)
+ {
+ this->mm[0] /= v1[0];
+ this->mm[1] /= v1[1];
+ this->mm[2] /= v1[2];
+ this->mm[3] /= v1[3];
+ return *this;
+ }
+
+ /// Returns v, where \f$v_i += scalar\f$ for \f$i \in [0, 3]\f$
+ template <typename S>
+ const Vec4<T> &operator+=(S scalar)
+ {
+ this->mm[0] += scalar;
+ this->mm[1] += scalar;
+ this->mm[2] += scalar;
+ this->mm[3] += scalar;
+ return *this;
+ }
+
+ /// Returns v0, where \f$v0_i += v1_i\f$ for \f$i \in [0, 3]\f$
+ template <typename S>
+ const Vec4<T> &operator+=(const Vec4<S> &v1)
+ {
+ this->mm[0] += v1[0];
+ this->mm[1] += v1[1];
+ this->mm[2] += v1[2];
+ this->mm[3] += v1[3];
+ return *this;
+ }
+
+ /// Returns v, where \f$v_i += scalar\f$ for \f$i \in [0, 3]\f$
+ template <typename S>
+ const Vec4<T> &operator-=(S scalar)
+ {
+ this->mm[0] -= scalar;
+ this->mm[1] -= scalar;
+ this->mm[2] -= scalar;
+ this->mm[3] -= scalar;
+ return *this;
+ }
+
+ /// Returns v0, where \f$v0_i -= v1_i\f$ for \f$i \in [0, 3]\f$
+ template <typename S>
+ const Vec4<T> &operator-=(const Vec4<S> &v1)
+ {
+ this->mm[0] -= v1[0];
+ this->mm[1] -= v1[1];
+ this->mm[2] -= v1[2];
+ this->mm[3] -= v1[3];
+ return *this;
+ }
+
+ // Number of cols, rows, elements
+ static unsigned numRows() { return 1; }
+ static unsigned numColumns() { return 4; }
+ static unsigned numElements() { return 4; }
+
+ /// True if a Nan is present in vector
+ bool isNan() const
+ {
+ return isnan(this->mm[0]) || isnan(this->mm[1])
+ || isnan(this->mm[2]) || isnan(this->mm[3]);
+ }
+
+ /// True if an Inf is present in vector
+ bool isInfinite() const
+ {
+ return isinf(this->mm[0]) || isinf(this->mm[1])
+ || isinf(this->mm[2]) || isinf(this->mm[3]);
+ }
+
+ /// True if all no Nan or Inf values present
+ bool isFinite() const
+ {
+ return finite(this->mm[0]) && finite(this->mm[1])
+ && finite(this->mm[2]) && finite(this->mm[3]);
+ }
+
+ /// Predefined constants, e.g. Vec4f v = Vec4f::xNegAxis();
+ static Vec4<T> zero() { return Vec4<T>(0, 0, 0, 0); }
+ static Vec4<T> origin() { return Vec4<T>(0, 0, 0, 1); }
+};
+
+/// Equality operator, does exact floating point comparisons
+template <typename T0, typename T1>
+inline bool operator==(const Vec4<T0> &v0, const Vec4<T1> &v1)
+{
+ return
+ isExactlyEqual(v0[0], v1[0]) &&
+ isExactlyEqual(v0[1], v1[1]) &&
+ isExactlyEqual(v0[2], v1[2]) &&
+ isExactlyEqual(v0[3], v1[3]);
+}
+
+/// Inequality operator, does exact floating point comparisons
+template <typename T0, typename T1>
+inline bool operator!=(const Vec4<T0> &v0, const Vec4<T1> &v1) { return !(v0==v1); }
+
+/// Returns V, where \f$V_i = v_i * scalar\f$ for \f$i \in [0, 3]\f$
+template <typename S, typename T>
+inline Vec4<typename promote<S, T>::type> operator*(S scalar, const Vec4<T> &v)
+{ return v*scalar; }
+
+/// Returns V, where \f$V_i = v_i * scalar\f$ for \f$i \in [0, 3]\f$
+template <typename S, typename T>
+inline Vec4<typename promote<S, T>::type> operator*(const Vec4<T> &v, S scalar)
+{
+ Vec4<typename promote<S, T>::type> result(v);
+ result *= scalar;
+ return result;
+}
+
+/// Returns V, where \f$V_i = v0_i * v1_i\f$ for \f$i \in [0, 3]\f$
+template <typename T0, typename T1>
+inline Vec4<typename promote<T0, T1>::type> operator*(const Vec4<T0> &v0,
+ const Vec4<T1> &v1)
+{
+ Vec4<typename promote<T0, T1>::type> result(v0[0]*v1[0],
+ v0[1]*v1[1],
+ v0[2]*v1[2],
+ v0[3]*v1[3]);
+ return result;
+}
+
+/// Returns V, where \f$V_i = scalar / v_i\f$ for \f$i \in [0, 3]\f$
+template <typename S, typename T>
+inline Vec4<typename promote<S, T>::type> operator/(S scalar, const Vec4<T> &v)
+{
+ return Vec4<typename promote<S, T>::type>(scalar/v[0],
+ scalar/v[1],
+ scalar/v[2],
+ scalar/v[3]);
+}
+
+/// Returns V, where \f$V_i = v_i / scalar\f$ for \f$i \in [0, 3]\f$
+template <typename S, typename T>
+inline Vec4<typename promote<S, T>::type> operator/(const Vec4<T> &v, S scalar)
+{
+ Vec4<typename promote<S, T>::type> result(v);
+ result /= scalar;
+ return result;
+}
+
+/// Returns V, where \f$V_i = v0_i / v1_i\f$ for \f$i \in [0, 3]\f$
+template <typename T0, typename T1>
+inline Vec4<typename promote<T0, T1>::type> operator/(const Vec4<T0> &v0,
+ const Vec4<T1> &v1)
+{
+ Vec4<typename promote<T0, T1>::type>
+ result(v0[0]/v1[0], v0[1]/v1[1], v0[2]/v1[2], v0[3]/v1[3]);
+ return result;
+}
+
+/// Returns V, where \f$V_i = v0_i + v1_i\f$ for \f$i \in [0, 3]\f$
+template <typename T0, typename T1>
+inline Vec4<typename promote<T0, T1>::type> operator+(const Vec4<T0> &v0, const Vec4<T1> &v1)
+{
+ Vec4<typename promote<T0, T1>::type> result(v0);
+ result += v1;
+ return result;
+}
+
+/// Returns V, where \f$V_i = v_i + scalar\f$ for \f$i \in [0, 3]\f$
+template <typename S, typename T>
+inline Vec4<typename promote<S, T>::type> operator+(const Vec4<T> &v, S scalar)
+{
+ Vec4<typename promote<S, T>::type> result(v);
+ result += scalar;
+ return result;
+}
+
+/// Returns V, where \f$V_i = v0_i - v1_i\f$ for \f$i \in [0, 3]\f$
+template <typename T0, typename T1>
+inline Vec4<typename promote<T0, T1>::type> operator-(const Vec4<T0> &v0, const Vec4<T1> &v1)
+{
+ Vec4<typename promote<T0, T1>::type> result(v0);
+ result -= v1;
+ return result;
+}
+
+/// Returns V, where \f$V_i = v_i - scalar\f$ for \f$i \in [0, 3]\f$
+template <typename S, typename T>
+inline Vec4<typename promote<S, T>::type> operator-(const Vec4<T> &v, S scalar)
+{
+ Vec4<typename promote<S, T>::type> result(v);
+ result -= scalar;
+ return result;
+}
+
+/// @remark We are switching to a more explicit name because the semantics
+/// are different from std::min/max. In that case, the function returns a
+/// reference to one of the objects based on a comparator. Here, we must
+/// fabricate a new object which might not match either of the inputs.
+
+/// Return component-wise minimum of the two vectors.
+template <typename T>
+inline Vec4<T> minComponent(const Vec4<T> &v1, const Vec4<T> &v2)
+{
+ return Vec4<T>(
+ std::min(v1.x(), v2.x()),
+ std::min(v1.y(), v2.y()),
+ std::min(v1.z(), v2.z()),
+ std::min(v1.w(), v2.w()));
+}
+
+/// Return component-wise maximum of the two vectors.
+template <typename T>
+inline Vec4<T> maxComponent(const Vec4<T> &v1, const Vec4<T> &v2)
+{
+ return Vec4<T>(
+ std::max(v1.x(), v2.x()),
+ std::max(v1.y(), v2.y()),
+ std::max(v1.z(), v2.z()),
+ std::max(v1.w(), v2.w()));
+}
+
+
+typedef Vec4<int> Vec4i;
+typedef Vec4<unsigned int> Vec4ui;
+typedef Vec4<float> Vec4s;
+typedef Vec4<double> Vec4d;
+
+#if DWREAL_IS_DOUBLE == 1
+typedef Vec4d Vec4f;
+#else
+typedef Vec4s Vec4f;
+#endif // DWREAL_IS_DOUBLE
+
+} // namespace math
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_MATH_VEC4_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/metadata/MetaMap.cc b/extern/openvdb/internal/openvdb/metadata/MetaMap.cc
new file mode 100644
index 00000000000..c0ef6e3fe2d
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/metadata/MetaMap.cc
@@ -0,0 +1,204 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+
+#include <openvdb/metadata/MetaMap.h>
+#include <openvdb/util/logging.h>
+#include <sstream>
+
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+
+MetaMap::MetaMap(const MetaMap &other)
+{
+ // Insert all metadata into this map.
+ ConstMetaIterator iter = other.beginMeta();
+ for( ; iter != other.endMeta(); ++iter) {
+ this->insertMeta(iter->first, *(iter->second));
+ }
+}
+
+
+MetaMap::Ptr
+MetaMap::copyMeta() const
+{
+ MetaMap::Ptr ret(new MetaMap);
+ ret->mMeta = this->mMeta;
+ return ret;
+}
+
+
+MetaMap::Ptr
+MetaMap::deepCopyMeta() const
+{
+ return MetaMap::Ptr(new MetaMap(*this));
+}
+
+
+MetaMap&
+MetaMap::operator=(const MetaMap& other)
+{
+ if (&other != this) {
+ this->clearMetadata();
+ // Insert all metadata into this map.
+ ConstMetaIterator iter = other.beginMeta();
+ for ( ; iter != other.endMeta(); ++iter) {
+ this->insertMeta(iter->first, *(iter->second));
+ }
+ }
+ return *this;
+}
+
+
+void
+MetaMap::readMeta(std::istream &is)
+{
+ // Clear out the current metamap if need be.
+ this->clearMetadata();
+
+ // Read in the number of metadata items.
+ Index32 count = 0;
+ is.read(reinterpret_cast<char*>(&count), sizeof(Index32));
+
+ // Read in each metadata.
+ for (Index32 i = 0; i < count; ++i) {
+ // Read in the name.
+ Name name = readString(is);
+
+ // Read in the metadata typename.
+ Name typeName = readString(is);
+
+ // Create a metadata type from the typename. Make sure that the type is
+ // registered.
+ if (!Metadata::isRegisteredType(typeName)) {
+ OPENVDB_LOG_WARN("cannot read metadata \"" << name
+ << "\" of unregistered type \"" << typeName << "\"");
+ UnknownMetadata metadata;
+ metadata.read(is);
+ } else {
+ Metadata::Ptr metadata = Metadata::createMetadata(typeName);
+
+ // Read the value from the stream.
+ metadata->read(is);
+
+ // Add the name and metadata to the map.
+ insertMeta(name, *metadata);
+ }
+ }
+}
+
+void
+MetaMap::writeMeta(std::ostream &os) const
+{
+ // Write out the number of metadata items we have in the map. Note that we
+ // save as Index32 to save a 32-bit number. Using size_t would be platform
+ // dependent.
+ Index32 count = (Index32)metaCount();
+ os.write(reinterpret_cast<char*>(&count), sizeof(Index32));
+
+ // Iterate through each metadata and write it out.
+ for (ConstMetaIterator iter = beginMeta(); iter != endMeta(); ++iter) {
+ // Write the name of the metadata.
+ writeString(os, iter->first);
+
+ // Write the type name of the metadata.
+ writeString(os, iter->second->typeName());
+
+ // Write out the metadata value.
+ iter->second->write(os);
+ }
+}
+
+void
+MetaMap::insertMeta(const Name &name, const Metadata &m)
+{
+ if(name.size() == 0)
+ OPENVDB_THROW(ValueError, "Metadata name cannot be an empty string");
+
+ // See if the value already exists, if so then replace the existing one.
+ MetaIterator iter = mMeta.find(name);
+
+ if(iter == mMeta.end()) {
+ // Create a copy of hte metadata and store it in the map
+ Metadata::Ptr tmp = m.copy();
+ mMeta[name] = tmp;
+ } else {
+ if(iter->second->typeName() != m.typeName()) {
+ std::ostringstream ostr;
+ ostr << "Cannot assign value of type "
+ << m.typeName() << " to metadata attribute " << name
+ << " of " << "type " << iter->second->typeName();
+ OPENVDB_THROW(TypeError, ostr.str());
+ }
+ // else
+ Metadata::Ptr tmp = m.copy();
+ iter->second = tmp;
+ }
+}
+
+void
+MetaMap::removeMeta(const Name &name)
+{
+ // Find the required metadata
+ MetaIterator iter = mMeta.find(name);
+
+ if(iter == mMeta.end())
+ return;
+ // else, delete the metadata and remove from the map
+ mMeta.erase(iter);
+}
+
+
+std::string
+MetaMap::str() const
+{
+ std::ostringstream buffer;
+ buffer << "MetaMap:\n";
+ for(ConstMetaIterator iter = beginMeta(); iter != endMeta(); ++iter) {
+ buffer << " " << iter->first << " = " << iter->second->str();
+ buffer << std::endl;
+ }
+ return buffer.str();
+}
+
+std::ostream&
+operator<<(std::ostream& ostr, const MetaMap& metamap)
+{
+ ostr << metamap.str();
+ return ostr;
+}
+
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/metadata/MetaMap.h b/extern/openvdb/internal/openvdb/metadata/MetaMap.h
new file mode 100644
index 00000000000..71734a68563
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/metadata/MetaMap.h
@@ -0,0 +1,252 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+
+#ifndef OPENVDB_METADATA_METAMAP_HAS_BEEN_INCLUDED
+#define OPENVDB_METADATA_METAMAP_HAS_BEEN_INCLUDED
+
+#include <iosfwd>
+#include <map>
+#include <openvdb/metadata/Metadata.h>
+#include <openvdb/Types.h>
+#include <openvdb/Exceptions.h>
+
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+
+/// @brief Provides functionality storing type agnostic metadata information.
+/// Grids and other structures can inherit from this to attain metadata
+/// functionality.
+class OPENVDB_API MetaMap
+{
+public:
+ typedef boost::shared_ptr<MetaMap> Ptr;
+ typedef boost::shared_ptr<const MetaMap> ConstPtr;
+
+ typedef std::map<Name, Metadata::Ptr> MetadataMap;
+ typedef MetadataMap::iterator MetaIterator;
+ typedef MetadataMap::const_iterator ConstMetaIterator;
+ ///< @todo this should really iterate over a map of Metadata::ConstPtrs
+
+ /// Constructor
+ MetaMap() {}
+ MetaMap(const MetaMap& other);
+
+ /// Destructor
+ virtual ~MetaMap() {}
+
+ /// Return a copy of this map whose fields are shared with this map.
+ MetaMap::Ptr copyMeta() const;
+ /// Return a deep copy of this map that shares no data with this map.
+ MetaMap::Ptr deepCopyMeta() const;
+
+ /// Assign to this map a deep copy of another map.
+ MetaMap& operator=(const MetaMap&);
+
+ /// Read in all the Meta information the given stream.
+ void readMeta(std::istream&);
+
+ /// Write out all the Meta information to the given stream.
+ void writeMeta(std::ostream&) const;
+
+ /// Insert a new metadata or overwrite existing. If Metadata with given name
+ /// doesn't exist, a new Metadata field is added. If it does exist and given
+ /// metadata is of the same type, then overwrite existing with new value. If
+ /// it does exist and not of the same type, then throw an exception.
+ ///
+ /// @param name the name of the metadata.
+ /// @param metadata the actual metadata to store.
+ void insertMeta(const Name& name, const Metadata& metadata);
+
+ /// Removes an existing metadata field from the grid. If the metadata with
+ /// the given name doesn't exist, do nothing.
+ ///
+ /// @param name the name of the metadata field to remove.
+ void removeMeta(const Name &name);
+
+ //@{
+ /// @return a pointer to the metadata with the given name, NULL if no such
+ /// field exists.
+ Metadata::Ptr operator[](const Name&);
+ Metadata::ConstPtr operator[](const Name&) const;
+ //@}
+
+ //@{
+ /// @return pointer to TypedMetadata, NULL if type and name mismatch.
+ template<typename T> typename T::Ptr getMetadata(const Name &name);
+ template<typename T> typename T::ConstPtr getMetadata(const Name &name) const;
+ //@}
+
+ /// @return direct access to the underlying value stored by the given
+ /// metadata name. Here T is the type of the value stored. If there is a
+ /// mismatch, then throws an exception.
+ template<typename T> T& metaValue(const Name &name);
+ template<typename T> const T& metaValue(const Name &name) const;
+
+ /// Functions for iterating over the Metadata.
+ MetaIterator beginMeta() { return mMeta.begin(); }
+ MetaIterator endMeta() { return mMeta.end(); }
+ ConstMetaIterator beginMeta() const { return mMeta.begin(); }
+ ConstMetaIterator endMeta() const { return mMeta.end(); }
+
+ void clearMetadata() { mMeta.clear(); }
+
+ size_t metaCount() const { return mMeta.size(); }
+
+ bool empty() const { return mMeta.empty(); }
+
+ /// @return string representation of MetaMap
+ std::string str() const;
+
+private:
+ /// @return a pointer to TypedMetadata with the given template parameter.
+ /// @throw LookupError if no field with the given name is found.
+ /// @throw TypeError if the given field is not of type T.
+ template<typename T>
+ typename TypedMetadata<T>::Ptr getValidTypedMetadata(const Name&) const;
+
+ MetadataMap mMeta;
+};
+
+/// Write a MetaMap to an output stream
+std::ostream& operator<<(std::ostream&, const MetaMap&);
+
+
+////////////////////////////////////////
+
+
+inline Metadata::Ptr
+MetaMap::operator[](const Name& name)
+{
+ MetaIterator iter = mMeta.find(name);
+ return (iter == mMeta.end() ? Metadata::Ptr() : iter->second);
+}
+
+inline Metadata::ConstPtr
+MetaMap::operator[](const Name &name) const
+{
+ ConstMetaIterator iter = mMeta.find(name);
+ return (iter == mMeta.end() ? Metadata::Ptr() : iter->second);
+}
+
+
+////////////////////////////////////////
+
+
+template <typename T>
+inline typename T::Ptr
+MetaMap::getMetadata(const Name &name)
+{
+ ConstMetaIterator iter = mMeta.find(name);
+ if(iter == mMeta.end()) {
+ return typename T::Ptr();
+ }
+
+ // To ensure that we get valid conversion if the metadata pointers cross dso
+ // boundaries, we have to check the qualified typename and then do a static
+ // cast. This is slower than doing a dynamic_pointer_cast, but is safer when
+ // pointers cross dso boundaries.
+ if (iter->second->typeName() == T::staticTypeName()) {
+ return boost::static_pointer_cast<T, Metadata>(iter->second);
+ } // else
+ return typename T::Ptr();
+}
+
+template <typename T>
+inline typename T::ConstPtr
+MetaMap::getMetadata(const Name &name) const
+{
+ ConstMetaIterator iter = mMeta.find(name);
+ if(iter == mMeta.end()) {
+ return typename T::ConstPtr();
+ }
+ // To ensure that we get valid conversion if the metadata pointers cross dso
+ // boundaries, we have to check the qualified typename and then do a static
+ // cast. This is slower than doing a dynamic_pointer_cast, but is safer when
+ // pointers cross dso boundaries.
+ if (iter->second->typeName() == T::staticTypeName()) {
+ return boost::static_pointer_cast<const T, const Metadata>(iter->second);
+ } // else
+ return typename T::ConstPtr();
+}
+
+
+////////////////////////////////////////
+
+
+template <typename T>
+inline typename TypedMetadata<T>::Ptr
+MetaMap::getValidTypedMetadata(const Name &name) const
+{
+ ConstMetaIterator iter = mMeta.find(name);
+ if (iter == mMeta.end()) OPENVDB_THROW(LookupError, "Cannot find metadata " << name);
+
+ // To ensure that we get valid conversion if the metadata pointers cross dso
+ // boundaries, we have to check the qualified typename and then do a static
+ // cast. This is slower than doing a dynamic_pointer_cast, but is safer when
+ // pointers cross dso boundaries.
+ typename TypedMetadata<T>::Ptr m;
+ if (iter->second->typeName() == TypedMetadata<T>::staticTypeName()) {
+ m = boost::static_pointer_cast<TypedMetadata<T>, Metadata>(iter->second);
+ }
+ if (!m) OPENVDB_THROW(TypeError, "Invalid type for metadata " << name);
+ return m;
+}
+
+
+////////////////////////////////////////
+
+
+template <typename T>
+inline T&
+MetaMap::metaValue(const Name &name)
+{
+ typename TypedMetadata<T>::Ptr m = getValidTypedMetadata<T>(name);
+ return m->value();
+}
+
+
+template <typename T>
+inline const T&
+MetaMap::metaValue(const Name &name) const
+{
+ typename TypedMetadata<T>::Ptr m = getValidTypedMetadata<T>(name);
+ return m->value();
+}
+
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_METADATA_METAMAP_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/metadata/Metadata.cc b/extern/openvdb/internal/openvdb/metadata/Metadata.cc
new file mode 100644
index 00000000000..3901d4469fc
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/metadata/Metadata.cc
@@ -0,0 +1,168 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+
+#include "Metadata.h"
+
+#include <map>
+#include <sstream>
+#include <vector>
+#include <tbb/mutex.h>
+
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+
+typedef tbb::mutex Mutex;
+typedef Mutex::scoped_lock Lock;
+
+typedef Metadata::Ptr (*createMetadata)();
+typedef std::map<Name, createMetadata> MetadataFactoryMap;
+typedef MetadataFactoryMap::const_iterator MetadataFactoryMapCIter;
+
+struct LockedMetadataTypeRegistry {
+ LockedMetadataTypeRegistry() {}
+ ~LockedMetadataTypeRegistry() {}
+ Mutex mMutex;
+ MetadataFactoryMap mMap;
+};
+
+// Declare this at file scope to ensure thread-safe initialization
+static Mutex theInitMetadataTypeRegistryMutex;
+
+// Global function for accessing the regsitry
+static LockedMetadataTypeRegistry*
+getMetadataTypeRegistry()
+{
+ Lock lock(theInitMetadataTypeRegistryMutex);
+
+ static LockedMetadataTypeRegistry *registry = NULL;
+
+ if(registry == NULL) {
+#if defined(__ICC)
+__pragma(warning(disable:1711)) // disable ICC "assignment to static variable" warnings
+#endif
+ registry = new LockedMetadataTypeRegistry();
+#if defined(__ICC)
+__pragma(warning(default:1711))
+#endif
+ }
+
+ return registry;
+}
+
+bool
+Metadata::isRegisteredType(const Name &typeName)
+{
+ LockedMetadataTypeRegistry *registry = getMetadataTypeRegistry();
+ Lock lock(registry->mMutex);
+
+ return (registry->mMap.find(typeName) != registry->mMap.end());
+}
+
+void
+Metadata::registerType(const Name &typeName, Metadata::Ptr (*createMetadata)())
+{
+ LockedMetadataTypeRegistry *registry = getMetadataTypeRegistry();
+ Lock lock(registry->mMutex);
+
+ if (registry->mMap.find(typeName) != registry->mMap.end()) {
+ OPENVDB_THROW(KeyError,
+ "Cannot register " << typeName << ". Type is already registered");
+ }
+
+ registry->mMap[typeName] = createMetadata;
+}
+
+void
+Metadata::unregisterType(const Name &typeName)
+{
+ LockedMetadataTypeRegistry *registry = getMetadataTypeRegistry();
+ Lock lock(registry->mMutex);
+
+ registry->mMap.erase(typeName);
+}
+
+Metadata::Ptr
+Metadata::createMetadata(const Name &typeName)
+{
+ LockedMetadataTypeRegistry *registry = getMetadataTypeRegistry();
+ Lock lock(registry->mMutex);
+
+ MetadataFactoryMapCIter iter = registry->mMap.find(typeName);
+
+ if (iter == registry->mMap.end()) {
+ OPENVDB_THROW(LookupError,
+ "Cannot create metadata for unregistered type " << typeName);
+ }
+
+ return (iter->second)();
+}
+
+void
+Metadata::clearRegistry()
+{
+ LockedMetadataTypeRegistry *registry = getMetadataTypeRegistry();
+ Lock lock(registry->mMutex);
+
+ registry->mMap.clear();
+}
+
+
+////////////////////////////////////////
+
+
+void
+UnknownMetadata::readValue(std::istream& is, Index32 numBytes)
+{
+ // Read and discard the metadata (without seeking, because
+ // the stream might not be seekable).
+ const size_t BUFFER_SIZE = 1024;
+ std::vector<char> buffer(BUFFER_SIZE);
+ for (Index32 bytesRemaining = numBytes; bytesRemaining > 0; ) {
+ const Index32 bytesToSkip = std::min<Index32>(bytesRemaining, BUFFER_SIZE);
+ is.read(&buffer[0], bytesToSkip);
+ bytesRemaining -= bytesToSkip;
+ }
+}
+
+
+void
+UnknownMetadata::writeValue(std::ostream&) const
+{
+ OPENVDB_THROW(TypeError, "Metadata has unknown type");
+}
+
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/metadata/Metadata.h b/extern/openvdb/internal/openvdb/metadata/Metadata.h
new file mode 100644
index 00000000000..d579bb6735e
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/metadata/Metadata.h
@@ -0,0 +1,414 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+
+#ifndef OPENVDB_METADATA_METADATA_HAS_BEEN_INCLUDED
+#define OPENVDB_METADATA_METADATA_HAS_BEEN_INCLUDED
+
+#include <iostream>
+#include <string>
+#include <openvdb/Types.h>
+#include <openvdb/math/Math.h> // for math::isZero()
+#include <openvdb/util/Name.h>
+#include <openvdb/Exceptions.h>
+#include <boost/shared_ptr.hpp>
+#include <boost/cstdint.hpp>
+
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+
+/// @brief Base class for storing metadata information in a grid.
+class OPENVDB_API Metadata
+{
+public:
+ typedef boost::shared_ptr<Metadata> Ptr;
+ typedef boost::shared_ptr<const Metadata> ConstPtr;
+
+ /// Constructor
+ Metadata() {}
+
+ /// Destructor
+ virtual ~Metadata() {}
+
+ /// @return the type name of the metadata.
+ virtual Name typeName() const = 0;
+
+ /// @return a copy of the metadata.
+ virtual Metadata::Ptr copy() const = 0;
+
+ /// Copy value from the given metadata into the curent metadata
+ virtual void copy(const Metadata &other) = 0;
+
+ /// @return string representation of Metadata
+ virtual std::string str() const = 0;
+
+ /// Return the boolean representation of this metadata (empty strings
+ /// and zeroVals evaluate to false; most other values evaluate to true).
+ virtual bool asBool() const = 0;
+
+ /// @return the size of the attribute in bytes.
+ virtual Index32 size() const = 0;
+
+ /// Read the attribute from a stream.
+ void read(std::istream&);
+ /// Write the attribute to a stream.
+ void write(std::ostream&) const;
+
+ /// Creates a new Metadata from the metadata type registry.
+ static Metadata::Ptr createMetadata(const Name &typeName);
+
+ /// @return true if the given type is known by the metadata type registry.
+ static bool isRegisteredType(const Name &typeName);
+
+ /// Clears out the metadata registry.
+ static void clearRegistry();
+
+protected:
+ /// Read the size of the attribute from a stream.
+ static Index32 readSize(std::istream&);
+ /// Write the size of the attribute to a stream.
+ void writeSize(std::ostream&) const;
+
+ /// Read the attribute from a stream.
+ virtual void readValue(std::istream&, Index32 numBytes) = 0;
+ /// Write the attribute to a stream.
+ virtual void writeValue(std::ostream&) const = 0;
+
+ /// Register the given metadata type along with a factory function.
+ static void registerType(const Name& typeName, Metadata::Ptr (*createMetadata)());
+ static void unregisterType(const Name& typeName);
+
+private:
+ // Disallow copying of instances of this class.
+ Metadata(const Metadata&);
+ Metadata& operator=(const Metadata&);
+};
+
+
+/// @brief Subclass to read (and ignore) data of an unregistered type
+class OPENVDB_API UnknownMetadata: public Metadata
+{
+public:
+ UnknownMetadata() {}
+ virtual ~UnknownMetadata() {}
+ virtual Name typeName() const { return "<unknown>"; }
+ virtual Metadata::Ptr copy() const { OPENVDB_THROW(TypeError, "Metadata has unknown type"); }
+ virtual void copy(const Metadata&) { OPENVDB_THROW(TypeError, "Destination has unknown type"); }
+ virtual std::string str() const { return "<unknown>"; }
+ virtual bool asBool() const { return false; }
+ virtual Index32 size() const { return 0; }
+
+protected:
+ virtual void readValue(std::istream&s, Index32 numBytes);
+ virtual void writeValue(std::ostream&) const;
+};
+
+
+/// @brief Templated metadata class to hold specific types.
+template<typename T>
+class TypedMetadata: public Metadata
+{
+public:
+ typedef boost::shared_ptr<TypedMetadata<T> > Ptr;
+ typedef boost::shared_ptr<const TypedMetadata<T> > ConstPtr;
+
+ // Constructors & destructors
+ TypedMetadata();
+ TypedMetadata(const T &value);
+ TypedMetadata(const TypedMetadata<T> &other);
+ virtual ~TypedMetadata();
+
+ /// @return the type name of the metadata.
+ virtual Name typeName() const;
+
+ /// @return a copy of the metadata
+ virtual Metadata::Ptr copy() const;
+
+ /// Copy value from the given metadata into the curent metadata
+ virtual void copy(const Metadata &other);
+
+ /// @return string representation of value
+ virtual std::string str() const;
+
+ /// Return the boolean representation of this metadata (empty strings
+ /// and zeroVals evaluate to false; most other values evaluate to true).
+ virtual bool asBool() const;
+
+ /// @return the size of the attribute in bytes.
+ virtual Index32 size() const { return static_cast<Index32>(sizeof(T)); }
+
+ /// Set this metadata's value.
+ void setValue(const T&);
+ /// @return this metadata's value.
+ T& value();
+ const T& value() const;
+
+ /// Static specialized function for the type name. This function must be
+ /// template specialized for each type T.
+ static Name staticTypeName() { return typeNameAsString<T>(); }
+
+ /// Creates a new metadata of this type.
+ static Metadata::Ptr createMetadata();
+
+ /// Register the given metadata type and a function that knows how to create
+ /// the metadata type. This way the registry will know how to create certain
+ /// metadata types.
+ static void registerType();
+ static void unregisterType();
+
+ static bool isRegisteredType();
+
+protected:
+ /// Read the attribute from a stream.
+ virtual void readValue(std::istream&, Index32 numBytes);
+ /// Write the attribute to a stream.
+ virtual void writeValue(std::ostream&) const;
+
+private:
+ T mValue;
+};
+
+/// Write a Metadata to an output stream
+std::ostream& operator<<(std::ostream& ostr, const Metadata& metadata);
+
+
+////////////////////////////////////////
+
+
+inline void
+Metadata::writeSize(std::ostream& os) const
+{
+ const Index32 n = this->size();
+ os.write(reinterpret_cast<const char*>(&n), sizeof(Index32));
+}
+
+
+inline Index32
+Metadata::readSize(std::istream& is)
+{
+ Index32 n = 0;
+ is.read(reinterpret_cast<char*>(&n), sizeof(Index32));
+ return n;
+}
+
+
+inline void
+Metadata::read(std::istream& is)
+{
+ const Index32 numBytes = this->readSize(is);
+ this->readValue(is, numBytes);
+}
+
+
+inline void
+Metadata::write(std::ostream& os) const
+{
+ this->writeSize(os);
+ this->writeValue(os);
+}
+
+
+////////////////////////////////////////
+
+
+template <typename T>
+inline
+TypedMetadata<T>::TypedMetadata() : mValue(T())
+{
+}
+
+template <typename T>
+inline
+TypedMetadata<T>::TypedMetadata(const T &value) : mValue(value)
+{
+}
+
+template <typename T>
+inline
+TypedMetadata<T>::TypedMetadata(const TypedMetadata<T> &other) :
+ Metadata(),
+ mValue(other.mValue)
+{
+}
+
+template <typename T>
+inline
+TypedMetadata<T>::~TypedMetadata()
+{
+}
+
+template <typename T>
+inline Name
+TypedMetadata<T>::typeName() const
+{
+ return TypedMetadata<T>::staticTypeName();
+}
+
+template <typename T>
+inline void
+TypedMetadata<T>::setValue(const T& val)
+{
+ mValue = val;
+}
+
+template <typename T>
+inline T&
+TypedMetadata<T>::value()
+{
+ return mValue;
+}
+
+template <typename T>
+inline const T&
+TypedMetadata<T>::value() const
+{
+ return mValue;
+}
+
+template <typename T>
+inline Metadata::Ptr
+TypedMetadata<T>::copy() const
+{
+ Metadata::Ptr metadata(new TypedMetadata<T>());
+ metadata->copy(*this);
+ return metadata;
+}
+
+template <typename T>
+inline void
+TypedMetadata<T>::copy(const Metadata &other)
+{
+ const TypedMetadata<T>* t = dynamic_cast<const TypedMetadata<T>*>(&other);
+ if (t == NULL) OPENVDB_THROW(TypeError, "Incompatible type during copy");
+ mValue = t->mValue;
+}
+
+
+template<typename T>
+inline void
+TypedMetadata<T>::readValue(std::istream& is, Index32 /*numBytes*/)
+{
+ //assert(this->size() == numBytes);
+ is.read(reinterpret_cast<char*>(&mValue), this->size());
+}
+
+template<typename T>
+inline void
+TypedMetadata<T>::writeValue(std::ostream& os) const
+{
+ os.write(reinterpret_cast<const char*>(&mValue), this->size());
+}
+
+template <typename T>
+inline std::string
+TypedMetadata<T>::str() const
+{
+ std::ostringstream ostr;
+ ostr << mValue;
+ return ostr.str();
+}
+
+template<typename T>
+inline bool
+TypedMetadata<T>::asBool() const
+{
+ return !math::isZero(mValue);
+}
+
+template <typename T>
+inline Metadata::Ptr
+TypedMetadata<T>::createMetadata()
+{
+ Metadata::Ptr ret(new TypedMetadata<T>());
+ return ret;
+}
+
+template <typename T>
+inline void
+TypedMetadata<T>::registerType()
+{
+ Metadata::registerType(TypedMetadata<T>::staticTypeName(),
+ TypedMetadata<T>::createMetadata);
+}
+
+template <typename T>
+inline void
+TypedMetadata<T>::unregisterType()
+{
+ Metadata::unregisterType(TypedMetadata<T>::staticTypeName());
+}
+
+template <typename T>
+inline bool
+TypedMetadata<T>::isRegisteredType()
+{
+ return Metadata::isRegisteredType(TypedMetadata<T>::staticTypeName());
+}
+
+
+template<>
+inline std::string
+TypedMetadata<bool>::str() const
+{
+ return (mValue ? "true" : "false");
+}
+
+
+inline std::ostream&
+operator<<(std::ostream& ostr, const Metadata& metadata)
+{
+ ostr << metadata.str();
+ return ostr;
+}
+
+
+typedef TypedMetadata<bool> BoolMetadata;
+typedef TypedMetadata<double> DoubleMetadata;
+typedef TypedMetadata<float> FloatMetadata;
+typedef TypedMetadata<boost::int32_t> Int32Metadata;
+typedef TypedMetadata<boost::int64_t> Int64Metadata;
+typedef TypedMetadata<Vec2d> Vec2DMetadata;
+typedef TypedMetadata<Vec2i> Vec2IMetadata;
+typedef TypedMetadata<Vec2s> Vec2SMetadata;
+typedef TypedMetadata<Vec3d> Vec3DMetadata;
+typedef TypedMetadata<Vec3i> Vec3IMetadata;
+typedef TypedMetadata<Vec3s> Vec3SMetadata;
+typedef TypedMetadata<Mat4s> Mat4SMetadata;
+typedef TypedMetadata<Mat4d> Mat4DMetadata;
+
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_METADATA_METADATA_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/metadata/StringMetadata.h b/extern/openvdb/internal/openvdb/metadata/StringMetadata.h
new file mode 100644
index 00000000000..2b0a2bfb843
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/metadata/StringMetadata.h
@@ -0,0 +1,75 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+
+#ifndef OPENVDB_METADATA_STRINGMETADATA_HAS_BEEN_INCLUDED
+#define OPENVDB_METADATA_STRINGMETADATA_HAS_BEEN_INCLUDED
+
+#include <string>
+#include <openvdb/metadata/Metadata.h>
+
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+
+typedef TypedMetadata<std::string> StringMetadata;
+
+
+template <>
+inline Index32
+StringMetadata::size() const
+{
+ return mValue.size();
+}
+
+
+template<>
+inline void
+StringMetadata::readValue(std::istream& is, Index32 size)
+{
+ mValue.resize(size, '\0');
+ is.read(&mValue[0], size);
+}
+
+template<>
+inline void
+StringMetadata::writeValue(std::ostream &os) const
+{
+ os.write(reinterpret_cast<const char*>(&mValue[0]), this->size());
+}
+
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_METADATA_STRINGMETADATA_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/openvdb.cc b/extern/openvdb/internal/openvdb/openvdb.cc
new file mode 100644
index 00000000000..6a427569188
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/openvdb.cc
@@ -0,0 +1,134 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+
+#include "openvdb.h"
+#include <tbb/mutex.h>
+
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+
+typedef tbb::mutex Mutex;
+typedef Mutex::scoped_lock Lock;
+
+// Declare this at file scope to ensure thread-safe initialization.
+Mutex sInitMutex;
+bool sIsInitialized = false;
+
+void
+initialize()
+{
+ Lock lock(sInitMutex);
+ if (sIsInitialized) return;
+
+ // Register metadata.
+ Metadata::clearRegistry();
+ BoolMetadata::registerType();
+ DoubleMetadata::registerType();
+ FloatMetadata::registerType();
+ Int32Metadata::registerType();
+ Int64Metadata::registerType();
+ StringMetadata::registerType();
+ Vec2IMetadata::registerType();
+ Vec2SMetadata::registerType();
+ Vec2DMetadata::registerType();
+ Vec3IMetadata::registerType();
+ Vec3SMetadata::registerType();
+ Vec3DMetadata::registerType();
+ Mat4SMetadata::registerType();
+ Mat4DMetadata::registerType();
+
+ // Register maps
+ math::MapRegistry::clear();
+ math::AffineMap::registerMap();
+ math::UnitaryMap::registerMap();
+ math::ScaleMap::registerMap();
+ math::UniformScaleMap::registerMap();
+ math::TranslationMap::registerMap();
+ math::ScaleTranslateMap::registerMap();
+ math::UniformScaleTranslateMap::registerMap();
+ math::NonlinearFrustumMap::registerMap();
+
+ // Register common grid types.
+ GridBase::clearRegistry();
+ BoolGrid::registerGrid();
+ FloatGrid::registerGrid();
+ DoubleGrid::registerGrid();
+ Int32Grid::registerGrid();
+ Int64Grid::registerGrid();
+ HermiteGrid::registerGrid();
+ StringGrid::registerGrid();
+ Vec3IGrid::registerGrid();
+ Vec3SGrid::registerGrid();
+ Vec3DGrid::registerGrid();
+
+#ifdef __ICC
+// Disable ICC "assignment to statically allocated variable" warning.
+// This assignment is mutex-protected and therefore thread-safe.
+__pragma(warning(disable:1711))
+#endif
+
+ sIsInitialized = true;
+
+#ifdef __ICC
+__pragma(warning(default:1711))
+#endif
+}
+
+
+void
+uninitialize()
+{
+ Lock lock(sInitMutex);
+
+#ifdef __ICC
+// Disable ICC "assignment to statically allocated variable" warning.
+// This assignment is mutex-protected and therefore thread-safe.
+__pragma(warning(disable:1711))
+#endif
+
+ sIsInitialized = false;
+
+#ifdef __ICC
+__pragma(warning(default:1711))
+#endif
+
+ Metadata::clearRegistry();
+ GridBase::clearRegistry();
+ math::MapRegistry::clear();
+}
+
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/openvdb.h b/extern/openvdb/internal/openvdb/openvdb.h
new file mode 100644
index 00000000000..d5357c6f4cb
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/openvdb.h
@@ -0,0 +1,98 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+
+#ifndef OPENVDB_INIT_HAS_BEEN_INCLUDED
+#define OPENVDB_INIT_HAS_BEEN_INCLUDED
+
+#include "Platform.h"
+#include "Types.h"
+#include "Metadata.h"
+#include "math/Maps.h"
+#include "math/Transform.h"
+#include "Grid.h"
+#include "tree/Tree.h"
+#include "io/File.h"
+
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+
+/// Common tree types
+typedef tree::Tree4<bool, 5, 4, 3>::Type BoolTree;
+typedef tree::Tree4<float, 5, 4, 3>::Type FloatTree;
+typedef tree::Tree4<double, 5, 4, 3>::Type DoubleTree;
+typedef tree::Tree4<int32_t, 5, 4, 3>::Type Int32Tree;
+typedef tree::Tree4<uint32_t, 5, 4, 3>::Type UInt32Tree;
+typedef tree::Tree4<int64_t, 5, 4, 3>::Type Int64Tree;
+typedef tree::Tree4<Hermite, 5, 4, 3>::Type HermiteTree;
+typedef tree::Tree4<Vec2i, 5, 4, 3>::Type Vec2ITree;
+typedef tree::Tree4<Vec2s, 5, 4, 3>::Type Vec2STree;
+typedef tree::Tree4<Vec2d, 5, 4, 3>::Type Vec2DTree;
+typedef tree::Tree4<Vec3i, 5, 4, 3>::Type Vec3ITree;
+typedef tree::Tree4<Vec3f, 5, 4, 3>::Type Vec3STree;
+typedef tree::Tree4<Vec3d, 5, 4, 3>::Type Vec3DTree;
+typedef tree::Tree4<std::string, 5, 4, 3>::Type StringTree;
+typedef Vec3STree Vec3fTree;
+typedef Vec3DTree Vec3dTree;
+typedef FloatTree ScalarTree;
+typedef Vec3fTree VectorTree;
+
+/// Common grid types
+typedef Grid<BoolTree> BoolGrid;
+typedef Grid<FloatTree> FloatGrid;
+typedef Grid<DoubleTree> DoubleGrid;
+typedef Grid<Int32Tree> Int32Grid;
+typedef Grid<Int64Tree> Int64Grid;
+typedef Grid<HermiteTree> HermiteGrid;
+typedef Grid<Vec3ITree> Vec3IGrid;
+typedef Grid<Vec3STree> Vec3SGrid;
+typedef Grid<Vec3DTree> Vec3DGrid;
+typedef Grid<StringTree> StringGrid;
+typedef Vec3SGrid Vec3fGrid;
+typedef Vec3DGrid Vec3dGrid;
+typedef FloatGrid ScalarGrid;
+typedef Vec3fGrid VectorGrid;
+
+
+/// Global registration of basic types
+OPENVDB_API void initialize();
+
+/// Global deregistration of basic types
+OPENVDB_API void uninitialize();
+
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_INIT_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/tools/Composite.h b/extern/openvdb/internal/openvdb/tools/Composite.h
new file mode 100644
index 00000000000..58a3d91bc7d
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/tools/Composite.h
@@ -0,0 +1,555 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+//
+/// @file Composite.h
+///
+/// @brief Functions to efficiently perform various compositing operations on grids
+///
+/// @author Peter Cucka
+
+#ifndef OPENVDB_TOOLS_COMPOSITE_HAS_BEEN_INCLUDED
+#define OPENVDB_TOOLS_COMPOSITE_HAS_BEEN_INCLUDED
+
+#include <openvdb/Platform.h>
+#include <openvdb/Exceptions.h>
+#include <openvdb/Types.h>
+#include <openvdb/Grid.h>
+#include <openvdb/math/Math.h> // for isExactlyEqual()
+#include "ValueTransformer.h" // for transformValues()
+#include <boost/utility/enable_if.hpp>
+
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace tools {
+
+/// @brief Given two level set grids, replace the A grid with the union of A and B.
+/// @throw ValueError if the background value of either grid is not greater than zero.
+/// @note This operation always leaves the B grid empty.
+template<typename GridOrTreeT> OPENVDB_STATIC_SPECIALIZATION
+inline void csgUnion(GridOrTreeT& a, GridOrTreeT& b, bool prune = true);
+/// @brief Given two level set grids, replace the A grid with the intersection of A and B.
+/// @throw ValueError if the background value of either grid is not greater than zero.
+/// @note This operation always leaves the B grid empty.
+template<typename GridOrTreeT> OPENVDB_STATIC_SPECIALIZATION
+inline void csgIntersection(GridOrTreeT& a, GridOrTreeT& b, bool prune = true);
+/// @brief Given two level set grids, replace the A grid with the difference A / B.
+/// @throw ValueError if the background value of either grid is not greater than zero.
+/// @note This operation always leaves the B grid empty.
+template<typename GridOrTreeT> OPENVDB_STATIC_SPECIALIZATION
+inline void csgDifference(GridOrTreeT& a, GridOrTreeT& b, bool prune = true);
+
+/// @brief Given grids A and B, compute max(a, b) per voxel (using sparse traversal).
+/// Store the result in the A grid and leave the B grid empty.
+template<typename GridOrTreeT> OPENVDB_STATIC_SPECIALIZATION
+inline void compMax(GridOrTreeT& a, GridOrTreeT& b);
+/// @brief Given grids A and B, compute min(a, b) per voxel (using sparse traversal).
+/// Store the result in the A grid and leave the B grid empty.
+template<typename GridOrTreeT> OPENVDB_STATIC_SPECIALIZATION
+inline void compMin(GridOrTreeT& a, GridOrTreeT& b);
+/// @brief Given grids A and B, compute a + b per voxel (using sparse traversal).
+/// Store the result in the A grid and leave the B grid empty.
+template<typename GridOrTreeT> OPENVDB_STATIC_SPECIALIZATION
+inline void compSum(GridOrTreeT& a, GridOrTreeT& b);
+/// @brief Given grids A and B, compute a * b per voxel (using sparse traversal).
+/// Store the result in the A grid and leave the B grid empty.
+template<typename GridOrTreeT> OPENVDB_STATIC_SPECIALIZATION
+inline void compMul(GridOrTreeT& a, GridOrTreeT& b);
+
+/// Copy the active voxels of B into A.
+template<typename GridOrTreeT> OPENVDB_STATIC_SPECIALIZATION
+inline void compReplace(GridOrTreeT& a, const GridOrTreeT& b);
+
+
+////////////////////////////////////////
+
+
+namespace composite {
+
+// composite::min() and composite::max() for non-vector types compare with operator<().
+template<typename T> inline
+const typename boost::disable_if_c<VecTraits<T>::IsVec, T>::type& // = T if T is not a vector type
+min(const T& a, const T& b) { return std::min(a, b); }
+
+template<typename T> inline
+const typename boost::disable_if_c<VecTraits<T>::IsVec, T>::type&
+max(const T& a, const T& b) { return std::max(a, b); }
+
+
+// composite::min() and composite::max() for OpenVDB vector types compare by magnitude.
+template<typename T> inline
+const typename boost::enable_if_c<VecTraits<T>::IsVec, T>::type& // = T if T is a vector type
+min(const T& a, const T& b)
+{
+ const typename T::ValueType aMag = a.lengthSqr(), bMag = b.lengthSqr();
+ return (aMag < bMag ? a : (bMag < aMag ? b : std::min(a, b)));
+}
+
+template<typename T> inline
+const typename boost::enable_if_c<VecTraits<T>::IsVec, T>::type&
+max(const T& a, const T& b)
+{
+ const typename T::ValueType aMag = a.lengthSqr(), bMag = b.lengthSqr();
+ return (aMag < bMag ? b : (bMag < aMag ? a : std::max(a, b)));
+}
+
+} // namespace composite
+
+
+template<typename GridOrTreeT>
+OPENVDB_STATIC_SPECIALIZATION inline void
+compMax(GridOrTreeT& aTree, GridOrTreeT& bTree)
+{
+ typedef TreeAdapter<GridOrTreeT> Adapter;
+ typedef typename Adapter::TreeType TreeT;
+ typedef typename TreeT::ValueType ValueT;
+ struct Local {
+ static inline void op(CombineArgs<ValueT>& args) {
+ args.setResult(composite::max(args.a(), args.b()));
+ }
+ };
+ Adapter::tree(aTree).combineExtended(Adapter::tree(bTree), Local::op, /*prune=*/false);
+}
+
+
+template<typename GridOrTreeT>
+OPENVDB_STATIC_SPECIALIZATION inline void
+compMin(GridOrTreeT& aTree, GridOrTreeT& bTree)
+{
+ typedef TreeAdapter<GridOrTreeT> Adapter;
+ typedef typename Adapter::TreeType TreeT;
+ typedef typename TreeT::ValueType ValueT;
+ struct Local {
+ static inline void op(CombineArgs<ValueT>& args) {
+ args.setResult(composite::min(args.a(), args.b()));
+ }
+ };
+ Adapter::tree(aTree).combineExtended(Adapter::tree(bTree), Local::op, /*prune=*/false);
+}
+
+
+template<typename GridOrTreeT>
+OPENVDB_STATIC_SPECIALIZATION inline void
+compSum(GridOrTreeT& aTree, GridOrTreeT& bTree)
+{
+ typedef TreeAdapter<GridOrTreeT> Adapter;
+ typedef typename Adapter::TreeType TreeT;
+ struct Local {
+ static inline void op(CombineArgs<typename TreeT::ValueType>& args) {
+ args.setResult(args.a() + args.b());
+ }
+ };
+ Adapter::tree(aTree).combineExtended(Adapter::tree(bTree), Local::op, /*prune=*/false);
+}
+
+
+template<typename GridOrTreeT>
+OPENVDB_STATIC_SPECIALIZATION inline void
+compMul(GridOrTreeT& aTree, GridOrTreeT& bTree)
+{
+ typedef TreeAdapter<GridOrTreeT> Adapter;
+ typedef typename Adapter::TreeType TreeT;
+ struct Local {
+ static inline void op(CombineArgs<typename TreeT::ValueType>& args) {
+ args.setResult(args.a() * args.b());
+ }
+ };
+ Adapter::tree(aTree).combineExtended(Adapter::tree(bTree), Local::op, /*prune=*/false);
+}
+
+
+////////////////////////////////////////
+
+
+template<typename TreeT>
+struct CompReplaceOp
+{
+ TreeT* const aTree;
+
+ CompReplaceOp(TreeT& _aTree): aTree(&_aTree) {}
+
+ void operator()(const typename TreeT::ValueOnCIter& iter) const
+ {
+ CoordBBox bbox;
+ iter.getBoundingBox(bbox);
+ aTree->fill(bbox, *iter);
+ }
+
+ void operator()(const typename TreeT::LeafCIter& leafIter) const
+ {
+ tree::ValueAccessor<TreeT> acc(*aTree);
+ for (typename TreeT::LeafCIter::LeafNodeT::ValueOnCIter iter =
+ leafIter->cbeginValueOn(); iter; ++iter)
+ {
+ acc.setValue(iter.getCoord(), *iter);
+ }
+ }
+};
+
+
+template<typename GridOrTreeT>
+OPENVDB_STATIC_SPECIALIZATION inline void
+compReplace(GridOrTreeT& aTree, const GridOrTreeT& bTree)
+{
+ typedef TreeAdapter<GridOrTreeT> Adapter;
+ typedef typename Adapter::TreeType TreeT;
+ typedef typename TreeT::ValueOnCIter ValueOnCIterT;
+
+ // Copy active states (but not values) from B to A.
+ Adapter::tree(aTree).topologyUnion(Adapter::tree(bTree));
+
+ CompReplaceOp<TreeT> op(Adapter::tree(aTree));
+
+ // Copy all active tile values from B to A.
+ ValueOnCIterT iter = bTree.cbeginValueOn();
+ iter.setMaxDepth(iter.getLeafDepth() - 1); // don't descend into leaf nodes
+ foreach(iter, op);
+
+ // Copy all active voxel values from B to A.
+ foreach(Adapter::tree(bTree).cbeginLeaf(), op);
+}
+
+
+////////////////////////////////////////
+
+
+/// Base visitor class for CSG operations
+/// (not intended to be used polymorphically, so no virtual functions)
+template<typename TreeType>
+class CsgVisitorBase
+{
+public:
+ typedef TreeType TreeT;
+ typedef typename TreeT::ValueType ValueT;
+ typedef typename TreeT::LeafNodeType::ChildAllIter ChildIterT;
+
+ enum { STOP = 3 };
+
+ CsgVisitorBase(const TreeT& aTree, const TreeT& bTree):
+ mAOutside(aTree.background()),
+ mAInside(negative(mAOutside)),
+ mBOutside(bTree.background()),
+ mBInside(negative(mBOutside))
+ {
+ const ValueT zero = zeroVal<ValueT>();
+ if (!(mAOutside > zero)) {
+ OPENVDB_THROW(ValueError,
+ "expected grid A outside value > 0, got " << mAOutside);
+ }
+ if (!(mAInside < zero)) {
+ OPENVDB_THROW(ValueError,
+ "expected grid A inside value < 0, got " << mAInside);
+ }
+ if (!(mBOutside > zero)) {
+ OPENVDB_THROW(ValueError,
+ "expected grid B outside value > 0, got " << mBOutside);
+ }
+ if (!(mBInside < zero)) {
+ OPENVDB_THROW(ValueError,
+ "expected grid B outside value < 0, got " << mBOutside);
+ }
+ }
+
+protected:
+ ValueT mAOutside, mAInside, mBOutside, mBInside;
+};
+
+
+////////////////////////////////////////
+
+
+template<typename TreeType>
+struct CsgUnionVisitor: public CsgVisitorBase<TreeType>
+{
+ typedef TreeType TreeT;
+ typedef typename TreeT::ValueType ValueT;
+ typedef typename TreeT::LeafNodeType::ChildAllIter ChildIterT;
+
+ enum { STOP = CsgVisitorBase<TreeT>::STOP };
+
+ CsgUnionVisitor(const TreeT& a, const TreeT& b): CsgVisitorBase<TreeT>(a, b) {}
+
+ /// Don't process nodes that are at different tree levels.
+ template<typename AIterT, typename BIterT>
+ inline int operator()(AIterT&, BIterT&) { return 0; }
+
+ /// Process root and internal nodes.
+ template<typename IterT>
+ inline int operator()(IterT& aIter, IterT& bIter)
+ {
+ ValueT aValue = zeroVal<ValueT>();
+ typename IterT::ChildNodeType* aChild = aIter.probeChild(aValue);
+ if (!aChild && aValue < zeroVal<ValueT>()) {
+ // A is an inside tile. Leave it alone and stop traversing this branch.
+ return STOP;
+ }
+
+ ValueT bValue = zeroVal<ValueT>();
+ typename IterT::ChildNodeType* bChild = bIter.probeChild(bValue);
+ if (!bChild && bValue < zeroVal<ValueT>()) {
+ // B is an inside tile. Make A an inside tile and stop traversing this branch.
+ aIter.setValue(this->mAInside);
+ aIter.setValueOn(bIter.isValueOn());
+ delete aChild;
+ return STOP;
+ }
+
+ if (!aChild && aValue > zeroVal<ValueT>()) {
+ // A is an outside tile. If B has a child, transfer it to A,
+ // otherwise leave A alone.
+ if (bChild) {
+ bIter.setValue(this->mBOutside);
+ bIter.setValueOff();
+ bChild->resetBackground(this->mBOutside, this->mAOutside);
+ aIter.setChild(bChild); // transfer child
+ delete aChild;
+ }
+ return STOP;
+ }
+
+ // If A has a child and B is an outside tile, stop traversing this branch.
+ // Continue traversal only if A and B both have children.
+ return (aChild && bChild) ? 0 : STOP;
+ }
+
+ /// Process leaf node values.
+ inline int operator()(ChildIterT& aIter, ChildIterT& bIter)
+ {
+ ValueT aValue, bValue;
+ aIter.probeValue(aValue);
+ bIter.probeValue(bValue);
+ if (aValue > bValue) { // a = min(a, b)
+ aIter.setValue(bValue);
+ aIter.setValueOn(bIter.isValueOn());
+ }
+ return 0;
+ }
+};
+
+
+
+////////////////////////////////////////
+
+
+template<typename TreeType>
+struct CsgIntersectVisitor: public CsgVisitorBase<TreeType>
+{
+ typedef TreeType TreeT;
+ typedef typename TreeT::ValueType ValueT;
+ typedef typename TreeT::LeafNodeType::ChildAllIter ChildIterT;
+
+ enum { STOP = CsgVisitorBase<TreeT>::STOP };
+
+ CsgIntersectVisitor(const TreeT& a, const TreeT& b): CsgVisitorBase<TreeT>(a, b) {}
+
+ /// Don't process nodes that are at different tree levels.
+ template<typename AIterT, typename BIterT>
+ inline int operator()(AIterT&, BIterT&) { return 0; }
+
+ /// Process root and internal nodes.
+ template<typename IterT>
+ inline int operator()(IterT& aIter, IterT& bIter)
+ {
+ ValueT aValue = zeroVal<ValueT>();
+ typename IterT::ChildNodeType* aChild = aIter.probeChild(aValue);
+ if (!aChild && !(aValue < zeroVal<ValueT>())) {
+ // A is an outside tile. Leave it alone and stop traversing this branch.
+ return STOP;
+ }
+
+ ValueT bValue = zeroVal<ValueT>();
+ typename IterT::ChildNodeType* bChild = bIter.probeChild(bValue);
+ if (!bChild && !(bValue < zeroVal<ValueT>())) {
+ // B is an outside tile. Make A an outside tile and stop traversing this branch.
+ aIter.setValue(this->mAOutside);
+ aIter.setValueOn(bIter.isValueOn());
+ delete aChild;
+ return STOP;
+ }
+
+ if (!aChild && aValue < zeroVal<ValueT>()) {
+ // A is an inside tile. If B has a child, transfer it to A,
+ // otherwise leave A alone.
+ if (bChild) {
+ bIter.setValue(this->mBOutside);
+ bIter.setValueOff();
+ bChild->resetBackground(this->mBOutside, this->mAOutside);
+ aIter.setChild(bChild); // transfer child
+ delete aChild;
+ }
+ return STOP;
+ }
+
+ // If A has a child and B is an outside tile, stop traversing this branch.
+ // Continue traversal only if A and B both have children.
+ return (aChild && bChild) ? 0 : STOP;
+ }
+
+ /// Process leaf node values.
+ inline int operator()(ChildIterT& aIter, ChildIterT& bIter)
+ {
+ ValueT aValue, bValue;
+ aIter.probeValue(aValue);
+ bIter.probeValue(bValue);
+ if (aValue < bValue) { // a = max(a, b)
+ aIter.setValue(bValue);
+ aIter.setValueOn(bIter.isValueOn());
+ }
+ return 0;
+ }
+};
+
+
+////////////////////////////////////////
+
+
+template<typename TreeType>
+struct CsgDiffVisitor: public CsgVisitorBase<TreeType>
+{
+ typedef TreeType TreeT;
+ typedef typename TreeT::ValueType ValueT;
+ typedef typename TreeT::LeafNodeType::ChildAllIter ChildIterT;
+
+ enum { STOP = CsgVisitorBase<TreeT>::STOP };
+
+ CsgDiffVisitor(const TreeT& a, const TreeT& b): CsgVisitorBase<TreeT>(a, b) {}
+
+ /// Don't process nodes that are at different tree levels.
+ template<typename AIterT, typename BIterT>
+ inline int operator()(AIterT&, BIterT&) { return 0; }
+
+ /// Process root and internal nodes.
+ template<typename IterT>
+ inline int operator()(IterT& aIter, IterT& bIter)
+ {
+ ValueT aValue = zeroVal<ValueT>();
+ typename IterT::ChildNodeType* aChild = aIter.probeChild(aValue);
+ if (!aChild && !(aValue < zeroVal<ValueT>())) {
+ // A is an outside tile. Leave it alone and stop traversing this branch.
+ return STOP;
+ }
+
+ ValueT bValue = zeroVal<ValueT>();
+ typename IterT::ChildNodeType* bChild = bIter.probeChild(bValue);
+ if (!bChild && bValue < zeroVal<ValueT>()) {
+ // B is an inside tile. Make A an inside tile and stop traversing this branch.
+ aIter.setValue(this->mAOutside);
+ aIter.setValueOn(bIter.isValueOn());
+ delete aChild;
+ return STOP;
+ }
+
+ if (!aChild && aValue < zeroVal<ValueT>()) {
+ // A is an inside tile. If B has a child, transfer it to A,
+ // otherwise leave A alone.
+ if (bChild) {
+ bIter.setValue(this->mBOutside);
+ bIter.setValueOff();
+ bChild->resetBackground(this->mBOutside, this->mAOutside);
+ aIter.setChild(bChild); // transfer child
+ bChild->negate();
+ delete aChild;
+ }
+ return STOP;
+ }
+
+ // If A has a child and B is an outside tile, stop traversing this branch.
+ // Continue traversal only if A and B both have children.
+ return (aChild && bChild) ? 0 : STOP;
+ }
+
+ /// Process leaf node values.
+ inline int operator()(ChildIterT& aIter, ChildIterT& bIter)
+ {
+ ValueT aValue, bValue;
+ aIter.probeValue(aValue);
+ bIter.probeValue(bValue);
+ bValue = negative(bValue);
+ if (aValue < bValue) { // a = max(a, -b)
+ aIter.setValue(bValue);
+ aIter.setValueOn(bIter.isValueOn());
+ }
+ return 0;
+ }
+};
+
+
+////////////////////////////////////////
+
+
+template<typename GridOrTreeT>
+OPENVDB_STATIC_SPECIALIZATION inline void
+csgUnion(GridOrTreeT& a, GridOrTreeT& b, bool prune)
+{
+ typedef TreeAdapter<GridOrTreeT> Adapter;
+ typedef typename Adapter::TreeType TreeT;
+ TreeT &aTree = Adapter::tree(a), &bTree = Adapter::tree(b);
+ CsgUnionVisitor<TreeT> visitor(aTree, bTree);
+ aTree.visit2(bTree, visitor);
+ if (prune) aTree.pruneLevelSet();
+ //if (prune) aTree.prune();
+}
+
+template<typename GridOrTreeT>
+OPENVDB_STATIC_SPECIALIZATION inline void
+csgIntersection(GridOrTreeT& a, GridOrTreeT& b, bool prune)
+{
+ typedef TreeAdapter<GridOrTreeT> Adapter;
+ typedef typename Adapter::TreeType TreeT;
+ TreeT &aTree = Adapter::tree(a), &bTree = Adapter::tree(b);
+ CsgIntersectVisitor<TreeT> visitor(aTree, bTree);
+ aTree.visit2(bTree, visitor);
+ if (prune) aTree.pruneLevelSet();
+ //if (prune) aTree.prune();
+}
+
+template<typename GridOrTreeT>
+OPENVDB_STATIC_SPECIALIZATION inline void
+csgDifference(GridOrTreeT& a, GridOrTreeT& b, bool prune)
+{
+ typedef TreeAdapter<GridOrTreeT> Adapter;
+ typedef typename Adapter::TreeType TreeT;
+ TreeT &aTree = Adapter::tree(a), &bTree = Adapter::tree(b);
+ CsgDiffVisitor<TreeT> visitor(aTree, bTree);
+ aTree.visit2(bTree, visitor);
+ if (prune) aTree.pruneLevelSet();
+ //if (prune) aTree.prune();
+}
+
+} // namespace tools
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_TOOLS_COMPOSITE_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/tools/Dense.h b/extern/openvdb/internal/openvdb/tools/Dense.h
new file mode 100644
index 00000000000..3fbb8ace92a
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/tools/Dense.h
@@ -0,0 +1,418 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+//
+/// @file Dense.h
+///
+/// @brief This file defines a simple dense grid and efficient
+/// converters to and from VDB grids.
+
+#ifndef OPENVDB_TOOLS_DENSE_HAS_BEEN_INCLUDED
+#define OPENVDB_TOOLS_DENSE_HAS_BEEN_INCLUDED
+
+#include <openvdb/Types.h>
+#include <openvdb/Grid.h>
+#include <openvdb/Exceptions.h>
+#include <tbb/parallel_for.h>
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace tools {
+
+// Forward declaration (see definition below)
+template<typename ValueT> class Dense;
+
+
+/// @brief Populate a dense grid with the values of voxels from a sparse grid,
+/// where the sparse grid intersects the dense grid.
+/// @param sparse an OpenVDB grid or tree from which to copy values
+/// @param dense the dense grid into which to copy values
+/// @param serial if false, process voxels in parallel
+template<typename GridOrTreeT>
+void
+copyToDense(
+ const GridOrTreeT& sparse,
+ Dense<typename GridOrTreeT::ValueType>& dense,
+ bool serial = false);
+
+
+/// @brief Populate a sparse grid with the values of all of the voxels of a dense grid.
+/// @param dense the dense grid from which to copy values
+/// @param sparse an OpenVDB grid or tree into which to copy values
+/// @param tolerance values in the dense grid that are within this tolerance of the sparse
+/// grid's background value become inactive background voxels or tiles in the sparse grid
+/// @param serial if false, process voxels in parallel
+template<typename GridOrTreeT>
+void
+copyFromDense(
+ const Dense<typename GridOrTreeT::ValueType>& dense,
+ GridOrTreeT& sparse,
+ const typename GridOrTreeT::ValueType& tolerance,
+ bool serial = false);
+
+
+////////////////////////////////////////
+
+
+/// @brief Dense is a simple dense grid API used by the CopyToDense and
+/// CopyFromDense classes defined below.
+/// @details Use the Dense class to efficiently produce a dense in-memory
+/// representation of an OpenVDB grid. However, be aware that a dense grid
+/// could have a memory footprint that is orders of magnitude larger than
+/// the corresponding sparse grid from which it originates.
+///
+/// @note This class can be used as a simple wrapper for existing dense grid
+/// classes if they provide access to the raw data array.
+/// @note This implementation assumes a data layout where @e z is the
+/// fastest-changing index (because that is the layout used by OpenVDB grids).
+template<typename ValueT>
+class Dense
+{
+public:
+ /// @brief Construct a dense grid with a given range of coordinates.
+ ///
+ /// @param bbox the bounding box of the (signed) coordinate range of this grid
+ /// @throw ValueError if the bounding box is empty.
+ Dense(const CoordBBox& bbox)
+ : mBBox(bbox), mArray(new ValueT[bbox.volume()]), mData(mArray.get()),
+ mY(bbox.dim()[2]), mX(mY*bbox.dim()[1])
+ {
+ if (bbox.empty()) {
+ OPENVDB_THROW(ValueError, "can't construct a dense grid with an empty bounding box");
+ }
+ }
+
+ /// @brief Construct a dense grid that wraps an external array.
+ ///
+ /// @param bbox the bounding box of the (signed) coordinate range of this grid
+ /// @param data a raw C-style array whose size is commensurate with
+ /// the coordinate domain of @a bbox
+ ///
+ /// @note The data array is assumed to have a stride of one in the @e z direction.
+ /// @throw ValueError if the bounding box is empty.
+ Dense(const CoordBBox& bbox, ValueT* data)
+ : mBBox(bbox), mData(data), mY(mBBox.dim()[2]), mX(mY*mBBox.dim()[1])
+ {
+ if (mBBox.empty()) {
+ OPENVDB_THROW(ValueError, "can't construct a dense grid with an empty bounding box");
+ }
+ }
+
+ /// @brief Construct a dense grid with a given origin and dimensions.
+ ///
+ /// @param dim the desired dimensions of the grid
+ /// @param min the signed coordinates of the first voxel in the dense grid
+ /// @throw ValueError if any of the dimensions are zero.
+ Dense(const Coord& dim, const Coord& min = Coord(0))
+ : mBBox(min, min+dim.offsetBy(-1)), mArray(new ValueT[mBBox.volume()]),
+ mData(mArray.get()), mY(mBBox.dim()[2]), mX(mY*mBBox.dim()[1])
+ {
+ if (mBBox.empty()) {
+ OPENVDB_THROW(ValueError, "can't construct a dense grid of size zero");
+ }
+ }
+
+ /// @brief Return a raw pointer to this grid's value array.
+ ///
+ /// @note This method is required by CopyToDense.
+ ValueT* data() { return mData; }
+
+ /// @brief Return a raw pointer to this grid's value array.
+ ///
+ /// @note This method is required by CopyFromDense.
+ const ValueT* data() const { return mData; }
+
+ /// @brief Return the bounding box of the signed index domain of this grid.
+ ///
+ /// @note This method is required by both CopyToDense and CopyFromDense.
+ const CoordBBox& bbox() const { return mBBox; }
+
+ /// @brief Return the stride of the array in the x direction ( = dimY*dimZ).
+ ///
+ /// @note This method is required by both CopyToDense and CopyFromDense.
+ size_t xStride() const { return mX; }
+
+ /// @brief Return the stride of the array in the y direction ( = dimZ).
+ ///
+ /// @note This method is required by both CopyToDense and CopyFromDense.
+ size_t yStride() const { return mY; }
+
+ /// @brief Return the number of voxels contained in this grid.
+ size_t valueCount() const { return mBBox.volume(); }
+
+ /// @brief Set the value of the voxel at the given array offset.
+ void setValue(size_t offset, const ValueT& value) { mData[offset] = value; }
+
+ /// @brief Return the value of the voxel at the given array offset.
+ const ValueT& getValue(size_t offset) const { return mData[offset]; }
+
+ /// @brief Set the value of the voxel at unsigned index coordinates (i, j, k).
+ /// @note This is somewhat slower than using an array offset.
+ void setValue(size_t i, size_t j, size_t k, const ValueT& value)
+ {
+ mData[this->coordToOffset(i,j,k)] = value;
+ }
+
+ /// @brief Return the value of the voxel at unsigned index coordinates (i, j, k).
+ /// @note This is somewhat slower than using an array offset.
+ const ValueT& getValue(size_t i, size_t j, size_t k) const
+ {
+ return mData[this->coordToOffset(i,j,k)];
+ }
+
+ /// @brief Set the value of the voxel at the given signed coordinates.
+ /// @note This is slower than using either an array offset or unsigned index coordinates.
+ void setValue(const Coord& xyz, const ValueT& value)
+ {
+ mData[this->coordToOffset(xyz)] = value;
+ }
+
+ /// @brief Return the value of the voxel at the given signed coordinates.
+ /// @note This is slower than using either an array offset or unsigned index coordinates.
+ const ValueT& getValue(const Coord& xyz) const
+ {
+ return mData[this->coordToOffset(xyz)];
+ }
+
+ /// @brief Fill this grid with a constant value.
+ void fill(const ValueT& value)
+ {
+ size_t size = this->valueCount();
+ ValueT* a = mData;
+ while(size--) *a++ = value;
+ }
+
+ /// @brief Return the linear offset into this grid's value array given by
+ /// unsigned coordinates (i, j, k), i.e., coordinates relative to
+ /// the origin of this grid's bounding box.
+ ///
+ /// @note This method reflects the fact that we assume the same layout
+ /// of values as an OpenVDB grid, i.e., the fastest coordinate is @e k.
+ inline size_t coordToOffset(size_t i, size_t j, size_t k) const
+ {
+ return k + j*mY + i*mX;
+ }
+
+ /// @brief Return the linear offset into this grid's value array given by
+ /// the specified signed coordinates, i.e., coordinates in the space of
+ /// this grid's bounding box.
+ ///
+ /// @note This method reflects the fact that we assume the same
+ /// layout of values as an OpenVDB grid, i.e., the fastest coordinate is @e z.
+ inline size_t coordToOffset(Coord xyz) const
+ {
+ assert(mBBox.isInside(xyz));
+ return this->coordToOffset(size_t(xyz[0]-mBBox.min()[0]),
+ size_t(xyz[1]-mBBox.min()[1]),
+ size_t(xyz[2]-mBBox.min()[2]));
+ }
+
+private:
+ const CoordBBox mBBox;//signed coordinates of the domain represented by the grid
+ boost::shared_array<ValueT> mArray;
+ ValueT* mData;//raw c-style pointer to values
+ const size_t mY, mX;//strides in x and y (by design it's 1 in z)
+};// end of Dense
+
+
+////////////////////////////////////////
+
+
+/// @brief Copy an OpenVDB tree into an existing dense grid.
+///
+/// @note Only the voxels enclosed by the existing dense grid are copied
+/// from the OpenVDB tree.
+template<typename TreeT>
+class CopyToDense
+{
+public:
+ typedef typename TreeT::ValueType ValueT;
+
+ CopyToDense(const TreeT& tree, Dense<ValueT> &dense)
+ : mRoot(tree.root()), mDense(dense) {}
+
+ void copy(bool serial = false) const
+ {
+ if (serial) {
+ mRoot.copyToDense(mDense.bbox(), mDense);
+ } else {
+ tbb::parallel_for(mDense.bbox(), *this);
+ }
+ }
+
+ /// @brief Public method called by tbb::parallel_for
+ void operator()(const CoordBBox& bbox) const
+ {
+ mRoot.copyToDense(bbox, mDense);
+ }
+
+private:
+ const typename TreeT::RootNodeType &mRoot;
+ Dense<ValueT> &mDense;
+};// CopyToDense
+
+
+// Convenient wrapper function for the CopyToDense class
+template<typename GridOrTreeT>
+void
+copyToDense(const GridOrTreeT& sparse, Dense<typename GridOrTreeT::ValueType>& dense, bool serial)
+{
+ typedef TreeAdapter<GridOrTreeT> Adapter;
+ typedef typename Adapter::TreeType TreeT;
+
+ CopyToDense<TreeT> op(Adapter::constTree(sparse), dense);
+ op.copy(serial);
+}
+
+
+////////////////////////////////////////
+
+
+/// @brief Copy the values from a dense grid into an OpenVDB tree.
+///
+/// @details Values in the dense grid that are within a tolerance of
+/// the background value are truncated to inactive background voxels or tiles.
+/// This allows the tree to form a sparse representation of the dense grid.
+///
+/// @note Since this class allocates leaf nodes concurrently it is recommended
+/// to use a scalable implementation of @c new like the one provided by TBB,
+/// rather than the mutex-protected standard library @c new.
+template<typename TreeT>
+class CopyFromDense
+{
+public:
+ typedef typename TreeT::ValueType ValueT;
+ typedef typename TreeT::LeafNodeType LeafT;
+
+ CopyFromDense(const Dense<ValueT>& dense, TreeT& tree, const ValueT& tolerance)
+ : mDense(dense), mTree(tree), mBlocks(NULL), mTolerance(tolerance)
+ {
+ }
+
+ /// @brief Copy values from the dense grid to the sparse tree.
+ void copy(bool serial = false)
+ {
+ std::vector<Block> blocks;
+ mBlocks = &blocks;
+ const CoordBBox& bbox = mDense.bbox();
+ // Pre-process: Construct a list of blocks alligned with (potential) leaf nodes
+ for (CoordBBox sub=bbox; sub.min()[0] <= bbox.max()[0]; sub.min()[0] = sub.max()[0] + 1) {
+ for (sub.min()[1] = bbox.min()[1]; sub.min()[1] <= bbox.max()[1];
+ sub.min()[1] = sub.max()[1] + 1)
+ {
+ for (sub.min()[2] = bbox.min()[2]; sub.min()[2] <= bbox.max()[2];
+ sub.min()[2] = sub.max()[2] + 1)
+ {
+ sub.max() = Coord::minComponent(bbox.max(),
+ (sub.min()&(~(LeafT::DIM-1u))).offsetBy(LeafT::DIM-1u));
+ blocks.push_back(Block(sub));
+ }
+ }
+ }
+ // Multi-threaded process: Convert dense grid into leaf nodes and tiles
+ if (serial) {
+ (*this)(tbb::blocked_range<size_t>(0, blocks.size()));
+ } else {
+ tbb::parallel_for(tbb::blocked_range<size_t>(0, blocks.size()), *this);
+ }
+ // Post-process: Insert leaf nodes and tiles into the tree, and prune the tiles only!
+ tree::ValueAccessor<TreeT> acc(mTree);
+ for (size_t m=0, size = blocks.size(); m<size; ++m) {
+ Block& block = blocks[m];
+ if (block.leaf) {
+ acc.addLeaf(block.leaf);
+ } else if (block.tile.second) {//only background tiles are inactive
+ acc.addTile(1, block.bbox.min(), block.tile.first, true);//leaf tile
+ }
+ }
+ mTree.root().pruneTiles(mTolerance);
+ }
+
+ /// @brief Public method called by tbb::parallel_for
+ void operator()(const tbb::blocked_range<size_t> &r) const
+ {
+ LeafT* leaf = NULL;
+
+ for (size_t m=r.begin(), n=0, end = r.end(); m != end; ++m, ++n) {
+
+ if (leaf == NULL) leaf = new LeafT();
+
+ Block& block = (*mBlocks)[m];
+ const CoordBBox &bbox = block.bbox;
+
+ leaf->copyFromDense(bbox, mDense, mTree.background(), mTolerance);
+
+ if (!leaf->isConstant(block.tile.first, block.tile.second, mTolerance)) {
+ leaf->setOrigin(bbox.min());
+ block.leaf = leaf;
+ leaf = NULL;
+ }
+ }// loop over blocks
+
+ delete leaf;
+ }
+
+private:
+ struct Block {
+ CoordBBox bbox;
+ LeafT* leaf;
+ std::pair<ValueT, bool> tile;
+ Block(const CoordBBox& b) : bbox(b), leaf(NULL) {}
+ };
+
+ const Dense<ValueT>& mDense;
+ TreeT& mTree;
+ std::vector<Block>* mBlocks;
+ ValueT mTolerance;
+};// CopyFromDense
+
+
+// Convenient wrapper function for the CopyFromDense class
+template<typename GridOrTreeT>
+void
+copyFromDense(const Dense<typename GridOrTreeT::ValueType>& dense, GridOrTreeT& sparse,
+ const typename GridOrTreeT::ValueType& tolerance, bool serial)
+{
+ typedef TreeAdapter<GridOrTreeT> Adapter;
+ typedef typename Adapter::TreeType TreeT;
+
+ CopyFromDense<TreeT> op(dense, Adapter::tree(sparse), tolerance);
+ op.copy(serial);
+}
+
+} // namespace tools
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_TOOLS_DENSE_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/tools/Filter.h b/extern/openvdb/internal/openvdb/tools/Filter.h
new file mode 100644
index 00000000000..f36145946a1
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/tools/Filter.h
@@ -0,0 +1,325 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+//
+/// @author Ken Museth
+///
+/// @file Filter.h
+
+#ifndef OPENVDB_TOOLS_FILTER_HAS_BEEN_INCLUDED
+#define OPENVDB_TOOLS_FILTER_HAS_BEEN_INCLUDED
+
+#include <tbb/parallel_reduce.h>
+#include <tbb/parallel_for.h>
+#include <boost/bind.hpp>
+#include <boost/function.hpp>
+#include <boost/scoped_ptr.hpp>
+#include <openvdb/Types.h>
+#include <openvdb/math/Math.h>
+#include <openvdb/math/Stencils.h>
+#include <openvdb/math/Transform.h>
+#include <openvdb/tree/LeafManager.h>
+#include <openvdb/util/NullInterrupter.h>
+#include <openvdb/Grid.h>
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace tools {
+
+/// @brief Filtering of VDB volumes
+template<typename GridT, typename InterruptT = util::NullInterrupter>
+class Filter
+{
+public:
+ typedef GridT GridType;
+ typedef typename GridType::TreeType TreeType;
+ typedef typename TreeType::LeafNodeType LeafType;
+ typedef typename LeafType::ValueType ValueType;
+ typedef typename tree::LeafManager<TreeType> LeafManagerType;
+ typedef typename LeafManagerType::LeafRange RangeType;
+ typedef typename LeafManagerType::BufferType BufferType;
+
+ /// Constructor
+ Filter(GridT& grid, InterruptT* interrupt = NULL) :
+ mGrid(grid), mTask(0), mInterrupter(interrupt)
+ {
+ }
+
+ /// @brief One iteration of a fast separable mean-value (i.e. box) filter.
+ void mean(int width = 1, int iterations = 1, bool serial = false);
+
+ /// @brief One iteration of a fast separable gaussian filter.
+ ///
+ /// @note This is approximated as 4 iterations of a separable mean filter
+ /// which typically leads an approximation that's better than 95%!
+ void gaussian(int width = 1, int iterations = 1, bool serial = false);
+
+ /// @brief One iteration of a median-value filter
+ ///
+ /// @note This filter is not separable and is hence relatively slow!
+ void median(int width = 1, int iterations = 1, bool serial = false);
+
+ /// Offsets (i.e. adds) a constant value to all active voxels.
+ void offset(float offset, bool serial = false);
+
+ /// @brief Used internally by tbb::parallel_for()
+ ///
+ /// @note Never call this method directly!
+ void operator()(const RangeType& r) const
+ {
+ if (mTask) mTask(const_cast<Filter*>(this), r);
+ else OPENVDB_THROW(ValueError, "task is undefined - call median(), mean(), etc.");
+ }
+
+private:
+ typedef typename boost::function<void (Filter*, const RangeType&)> FuncType;
+
+ void cook(bool serial, LeafManagerType& leafs);
+
+ // Private filter methods called by tbb::parallel_for threads
+ void doBoxX(const RangeType&, Int32);
+ void doBoxY(const RangeType&, Int32);
+ void doBoxZ(const RangeType&, Int32);
+ void doMedian(const RangeType&, int);
+ void doOffset(const RangeType&, float);
+ /// @return true if the process was interrupted
+ bool wasInterrupted();
+
+ GridType& mGrid;
+ FuncType mTask;
+ InterruptT* mInterrupter;
+}; // end of Filter class
+
+////////////////////////////////////////
+
+template<typename GridT, typename InterruptT>
+inline void
+Filter<GridT, InterruptT>::mean(int width, int iterations, bool serial)
+{
+ if (mInterrupter) mInterrupter->start("Applying mean filter");
+
+ const int w = std::max(1, width);
+
+ LeafManagerType leafs(mGrid.tree(), 1, serial);
+
+ for (int i=0; i<iterations && !this->wasInterrupted(); ++i) {
+ mTask = boost::bind(&Filter::doBoxX, _1, _2, w);
+ this->cook(serial, leafs);
+
+ mTask = boost::bind(&Filter::doBoxY, _1, _2, w);
+ this->cook(serial, leafs);
+
+ mTask = boost::bind(&Filter::doBoxZ, _1, _2, w);
+ this->cook(serial, leafs);
+ }
+
+ if (mInterrupter) mInterrupter->end();
+}
+
+template<typename GridT, typename InterruptT>
+inline void
+Filter<GridT, InterruptT>::gaussian(int width, int iterations, bool serial)
+{
+ if (mInterrupter) mInterrupter->start("Applying gaussian filter");
+
+ const int w = std::max(1, width);
+
+ LeafManagerType leafs(mGrid.tree(), 1, serial);
+
+ for (int i=0; i<iterations; ++i) {
+ for (int n=0; n<4 && !this->wasInterrupted(); ++n) {
+ mTask = boost::bind(&Filter::doBoxX, _1, _2, w);
+ this->cook(serial, leafs);
+
+ mTask = boost::bind(&Filter::doBoxY, _1, _2, w);
+ this->cook(serial, leafs);
+
+ mTask = boost::bind(&Filter::doBoxZ, _1, _2, w);
+ this->cook(serial, leafs);
+ }
+ }
+
+ if (mInterrupter) mInterrupter->end();
+}
+
+
+template<typename GridT, typename InterruptT>
+inline void
+Filter<GridT, InterruptT>::median(int width, int iterations, bool serial)
+{
+ if (mInterrupter) mInterrupter->start("Applying median filter");
+
+ LeafManagerType leafs(mGrid.tree(), 1, serial);
+
+ mTask = boost::bind(&Filter::doMedian, _1, _2, std::max(1, width));
+ for (int i=0; i<iterations && !this->wasInterrupted(); ++i) this->cook(serial, leafs);
+
+ if (mInterrupter) mInterrupter->end();
+}
+
+template<typename GridT, typename InterruptT>
+inline void
+Filter<GridT, InterruptT>::offset(float value, bool serial)
+{
+ if (mInterrupter) mInterrupter->start("Applying offset");
+
+ LeafManagerType leafs(mGrid.tree(), 0, serial);
+
+ mTask = boost::bind(&Filter::doOffset, _1, _2, value);
+ this->cook(serial, leafs);
+
+ if (mInterrupter) mInterrupter->end();
+}
+
+////////////////////////////////////////
+
+
+/// Private method to perform the task (serial or threaded) and
+/// subsequently swap the leaf buffers.
+template<typename GridT, typename InterruptT>
+inline void
+Filter<GridT, InterruptT>::cook(bool serial, LeafManagerType& leafs)
+{
+ if (serial) {
+ (*this)(leafs.leafRange());
+ } else {
+ tbb::parallel_for(leafs.leafRange(), *this);
+ }
+ leafs.swapLeafBuffer(1, serial);
+}
+
+/// X convolution of a separable box filter
+template<typename GridT, typename InterruptT>
+inline void
+Filter<GridT, InterruptT>::doBoxX(const RangeType& range, Int32 w)
+{
+ this->wasInterrupted();
+ const ValueType frac = ValueType(1)/ValueType(2*w+1);
+ typename GridT::ConstAccessor acc = mGrid.getConstAccessor();
+ for (typename RangeType::Iterator lIter=range.begin(); lIter; ++lIter) {
+ BufferType& buffer = lIter.buffer(1);
+ for (typename LeafType::ValueOnCIter vIter = lIter->cbeginValueOn(); vIter; ++vIter) {
+ ValueType sum = zeroVal<ValueType>();
+ math::Coord xyz = vIter.getCoord();
+ for (Int32 x = xyz.x()-w, xLast = xyz.x()+w; x <= xLast; ++x) {
+ sum += acc.getValue(xyz.setX(x));
+ }
+ buffer.setValue(vIter.pos(), sum*frac);
+ }
+ }
+}
+
+/// Y convolution of a separable box filter
+template<typename GridT, typename InterruptT>
+inline void
+Filter<GridT, InterruptT>::doBoxY(const RangeType& range, Int32 w)
+{
+ this->wasInterrupted();
+ const ValueType frac = ValueType(1)/ValueType(2*w+1);
+ typename GridT::ConstAccessor acc = mGrid.getConstAccessor();
+ for (typename RangeType::Iterator lIter=range.begin(); lIter; ++lIter) {
+ BufferType& buffer = lIter.buffer(1);
+ for (typename LeafType::ValueOnCIter vIter = lIter->cbeginValueOn(); vIter; ++vIter) {
+ ValueType sum = zeroVal<ValueType>();
+ math::Coord xyz = vIter.getCoord();
+ for (Int32 y = xyz.y()-w, yLast = xyz.y()+w; y <= yLast; ++y) {
+ sum += acc.getValue(xyz.setY(y));
+ }
+ buffer.setValue(vIter.pos(), sum*frac);
+ }
+ }
+}
+
+/// Z convolution of a separable box filter
+template<typename GridT, typename InterruptT>
+inline void
+Filter<GridT, InterruptT>::doBoxZ(const RangeType& range, Int32 w)
+{
+ this->wasInterrupted();
+ const ValueType frac = ValueType(1)/ValueType(2*w+1);
+ typename GridT::ConstAccessor acc = mGrid.getConstAccessor();
+ for (typename RangeType::Iterator lIter=range.begin(); lIter; ++lIter) {
+ BufferType& buffer = lIter.buffer(1);
+ for (typename LeafType::ValueOnCIter vIter = lIter->cbeginValueOn(); vIter; ++vIter) {
+ ValueType sum = zeroVal<ValueType>();
+ math::Coord xyz = vIter.getCoord();
+ for (Int32 z = xyz.z()-w, zLast = xyz.z()+w; z <= zLast; ++z) {
+ sum += acc.getValue(xyz.setZ(z));
+ }
+ buffer.setValue(vIter.pos(), sum*frac);
+ }
+ }
+}
+
+/// Performs simple but slow median-value diffusion
+template<typename GridT, typename InterruptT>
+inline void
+Filter<GridT, InterruptT>::doMedian(const RangeType& range, int width)
+{
+ this->wasInterrupted();
+ typename math::DenseStencil<GridType> stencil(mGrid, width);//creates local cache!
+ for (typename RangeType::Iterator lIter=range.begin(); lIter; ++lIter) {
+ BufferType& buffer = lIter.buffer(1);
+ for (typename LeafType::ValueOnCIter vIter = lIter->cbeginValueOn(); vIter; ++vIter) {
+ stencil.moveTo(vIter);
+ buffer.setValue(vIter.pos(), stencil.median());
+ }
+ }
+}
+
+/// Offsets the values by a constant
+template<typename GridT, typename InterruptT>
+inline void
+Filter<GridT, InterruptT>::doOffset(const RangeType& range, float floatVal)
+{
+ const ValueType value = static_cast<ValueType>(floatVal);
+ for (typename RangeType::Iterator lIter=range.begin(); lIter; ++lIter) lIter->addValue(value);
+}
+
+template<typename GridT, typename InterruptT>
+inline bool
+Filter<GridT, InterruptT>::wasInterrupted()
+{
+ if (util::wasInterrupted(mInterrupter)) {
+ tbb::task::self().cancel_group_execution();
+ return true;
+ }
+ return false;
+}
+
+} // namespace tools
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_TOOLS_FILTER_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/tools/GridOperators.h b/extern/openvdb/internal/openvdb/tools/GridOperators.h
new file mode 100644
index 00000000000..ae092974ca0
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/tools/GridOperators.h
@@ -0,0 +1,738 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+//
+/// @file GridOperators.h
+///
+/// @brief Applies an operator on an input grid to produce an output
+/// grid with the same topology but potentially different value type.
+
+#ifndef OPENVDB_TOOLS_GRID_OPERATORS_HAS_BEEN_INCLUDED
+#define OPENVDB_TOOLS_GRID_OPERATORS_HAS_BEEN_INCLUDED
+
+#include <openvdb/Grid.h>
+#include <openvdb/math/Operators.h>
+#include <openvdb/util/NullInterrupter.h>
+#include <openvdb/tree/LeafManager.h>
+#include <openvdb/tree/ValueAccessor.h>
+#include <tbb/parallel_for.h>
+
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace tools {
+
+/// @brief VectorToScalarConverter<VectorGridType>::Type is the type of a grid
+/// having the same tree configuration as VectorGridType but a scalar value type, T,
+/// where T is the type of the original vector components.
+/// @details For example, VectorToScalarConverter<Vec3DGrid>::Type is equivalent to DoubleGrid.
+template<typename VectorGridType> struct VectorToScalarConverter {
+ typedef typename VectorGridType::ValueType::value_type VecComponentValueT;
+ typedef typename VectorGridType::template ValueConverter<VecComponentValueT>::Type Type;
+};
+
+/// @brief ScalarToVectorConverter<ScalarGridType>::Type is the type of a grid
+/// having the same tree configuration as ScalarGridType but value type Vec3<T>
+/// where T is ScalarGridType::ValueType.
+/// @details For example, ScalarToVectorConverter<DoubleGrid>::Type is equivalent to Vec3DGrid.
+template<typename ScalarGridType> struct ScalarToVectorConverter {
+ typedef math::Vec3<typename ScalarGridType::ValueType> VectorValueT;
+ typedef typename ScalarGridType::template ValueConverter<VectorValueT>::Type Type;
+};
+
+
+/// @brief Compute the Closest-Point Transform (CPT) from a distance field.
+/// @return a new vector-valued grid with the same numerical precision as the input grid
+/// (for example, if the input grid is a DoubleGrid, the output grid will be a Vec3DGrid)
+///
+/// @note The current implementation assumes all the input distance values
+/// are represented by leaf voxels and not tiles. This is true for all
+/// narrow-band level sets, which this class was originally developed for.
+/// In the future we will expand this class to also handle tile values.
+template<typename GridType, typename InterruptT> inline
+typename ScalarToVectorConverter<GridType>::Type::Ptr
+cpt(const GridType& grid, bool threaded, InterruptT* interrupt);
+
+template<typename GridType> inline
+typename ScalarToVectorConverter<GridType>::Type::Ptr
+cpt(const GridType& grid, bool threaded = true)
+{
+ return cpt<GridType, util::NullInterrupter>(grid, threaded, NULL);
+}
+
+
+/// @brief Compute the curl of the given vector-valued grid.
+/// @return a new vector-valued grid
+template<typename GridType, typename InterruptT> inline
+typename GridType::Ptr
+curl(const GridType& grid, bool threaded, InterruptT* interrupt);
+
+template<typename GridType> inline
+typename GridType::Ptr
+curl(const GridType& grid, bool threaded = true)
+{
+ return curl<GridType, util::NullInterrupter>(grid, threaded, NULL);
+}
+
+/// @brief Compute the divergence of the given vector-valued grid.
+/// @return a new scalar-valued grid with the same numerical precision as the input grid
+/// (for example, if the input grid is a Vec3DGrid, the output grid will be a DoubleGrid)
+template<typename GridType, typename InterruptT> inline
+typename VectorToScalarConverter<GridType>::Type::Ptr
+divergence(const GridType& grid, bool threaded, InterruptT* interrupt);
+
+template<typename GridType> inline
+typename VectorToScalarConverter<GridType>::Type::Ptr
+divergence(const GridType& grid, bool threaded = true)
+{
+ return divergence<GridType, util::NullInterrupter>(grid, threaded, NULL);
+}
+
+/// @brief Compute the gradient of the given scalar grid.
+/// @return a new vector-valued grid with the same numerical precision as the input grid
+/// (for example, if the input grid is a DoubleGrid, the output grid will be a Vec3DGrid)
+template<typename GridType, typename InterruptT> inline
+typename ScalarToVectorConverter<GridType>::Type::Ptr
+gradient(const GridType& grid, bool threaded, InterruptT* interrupt);
+
+template<typename GridType> inline
+typename ScalarToVectorConverter<GridType>::Type::Ptr
+gradient(const GridType& grid, bool threaded = true)
+{
+ return gradient<GridType, util::NullInterrupter>(grid, threaded, NULL);
+}
+
+
+/// @brief Compute the Laplacian of the given scalar grid.
+/// @return a new scalar grid
+template<typename GridType, typename InterruptT> inline
+typename GridType::Ptr
+laplacian(const GridType& grid, bool threaded, InterruptT* interrupt);
+
+template<typename GridType> inline
+typename GridType::Ptr
+laplacian(const GridType& grid, bool threaded = true)
+{
+ return laplacian<GridType, util::NullInterrupter>(grid, threaded, NULL);
+}
+
+/// @brief Compute the mean curvature of the given grid.
+/// @return a new grid
+template<typename GridType, typename InterruptT> inline
+typename GridType::Ptr
+meanCurvature(const GridType& grid, bool threaded, InterruptT* interrupt);
+
+template<typename GridType> inline
+typename GridType::Ptr
+meanCurvature(const GridType& grid, bool threaded = true)
+{
+ return meanCurvature<GridType, util::NullInterrupter>(grid, threaded, NULL);
+}
+
+/// @brief Compute the magnitudes of the vectors of the given vector-valued grid.
+/// @return a new scalar-valued grid with the same numerical precision as the input grid
+/// (for example, if the input grid is a Vec3DGrid, the output grid will be a DoubleGrid)
+template<typename GridType, typename InterruptT> inline
+typename VectorToScalarConverter<GridType>::Type::Ptr
+magnitude(const GridType& grid, bool threaded, InterruptT* interrupt);
+
+template<typename GridType> inline
+typename VectorToScalarConverter<GridType>::Type::Ptr
+magnitude(const GridType& grid, bool threaded = true)
+{
+ return magnitude<GridType, util::NullInterrupter>(grid, threaded, NULL);
+}
+
+/// @brief Normalize the vectors of the given vector-valued grid.
+/// @return a new vector-valued grid
+template<typename GridType, typename InterruptT> inline
+typename GridType::Ptr
+normalize(const GridType& grid, bool threaded, InterruptT* interrupt);
+
+template<typename GridType> inline
+typename GridType::Ptr
+normalize(const GridType& grid, bool threaded = true)
+{
+ return normalize<GridType, util::NullInterrupter>(grid, threaded, NULL);
+}
+
+////////////////////////////////////////
+
+
+namespace {
+
+/// @brief Apply an operator on an input grid to produce an output grid
+/// with the same topology but a possibly different value type.
+/// @details To facilitate inlining, this class is also templated on a Map type.
+///
+/// @note This is a helper class and should never be used directly.
+///
+/// @note The current implementation assumes all the input
+/// values are represented by leaf voxels and not tiles. In the
+/// future we will expand this class to also handle tile values.
+template<typename InGridT, typename OutGridT, typename MapT,
+ typename OperatorT, typename InterruptT = util::NullInterrupter>
+class GridOperator
+{
+public:
+ typedef typename OutGridT::TreeType OutTreeT;
+ typedef typename OutTreeT::LeafNodeType OutLeafT;
+ typedef typename tree::LeafManager<OutTreeT> LeafManagerT;
+
+ GridOperator(const InGridT& grid, const MapT& map, InterruptT* interrupt = NULL):
+ mAcc(grid.getConstAccessor()), mMap(map), mInterrupt(interrupt)
+ {
+ }
+ virtual ~GridOperator() {}
+ typename OutGridT::Ptr process(bool threaded = true)
+ {
+ if (mInterrupt) mInterrupt->start("Processing grid");
+ // Derive background value of the output grid
+ typename InGridT::TreeType tmp(mAcc.getTree()->background());
+ typename OutGridT::ValueType backg = OperatorT::result(mMap, tmp, math::Coord(0));
+ // output tree = topology copy of input tree!
+ typename OutTreeT::Ptr tree(new OutTreeT(*mAcc.getTree(), backg, TopologyCopy()));
+ // create grid with output tree and unit transform
+ typename OutGridT::Ptr result(new OutGridT(tree));
+ // transform of output grid = transform of input grid
+ result->setTransform(math::Transform::Ptr(new math::Transform( mMap.copy() )));
+
+ LeafManagerT leafManager(*tree);
+
+ if (threaded) {
+ tbb::parallel_for(leafManager.leafRange(), *this);
+ } else {
+ (*this)(leafManager.leafRange());
+ }
+
+ if (mInterrupt) mInterrupt->end();
+ return result;
+ }
+
+ /// @brief Iterate sequentially over LeafNodes and voxels in the output
+ /// grid and compute the laplacian using a valueAccessor for the
+ /// input grid.
+ ///
+ /// @note Never call this public method directly - it is called by
+ /// TBB threads only!
+ void operator()(const typename LeafManagerT::LeafRange& range) const
+ {
+ if (util::wasInterrupted(mInterrupt)) tbb::task::self().cancel_group_execution();
+
+ for (typename LeafManagerT::LeafRange::Iterator leaf=range.begin(); leaf; ++leaf) {
+ for (typename OutLeafT::ValueOnIter value=leaf->beginValueOn(); value; ++value) {
+ value.setValue(OperatorT::result(mMap, mAcc, value.getCoord()));
+ }
+ }
+ }
+
+protected:
+
+ typedef typename InGridT::ConstAccessor AccessorT;
+ mutable AccessorT mAcc;
+ const MapT& mMap;
+ InterruptT* mInterrupt;
+}; // end of GridOperator class
+
+} //end of anonymous namespace
+
+
+////////////////////////////////////////
+
+
+/// @brief Compute the closest-point transform of a scalar grid.
+template<typename InGridT, typename InterruptT = util::NullInterrupter>
+class Cpt
+{
+public:
+ typedef InGridT InGridType;
+ typedef typename ScalarToVectorConverter<InGridT>::Type OutGridType;
+
+ Cpt(const InGridType& grid, InterruptT* interrupt = NULL):
+ mInputGrid(grid), mInterrupt(interrupt)
+ {
+ }
+ typename OutGridType::Ptr process(bool threaded = true, bool useWorldTransform = true)
+ {
+ Functor functor(mInputGrid, threaded, useWorldTransform, mInterrupt);
+ processTypedMap(mInputGrid.transform(), functor);
+ return functor.mOutputGrid;
+ }
+private:
+ struct IsOpT
+ {
+ template<typename MapT, typename AccT>
+ static typename OutGridType::ValueType
+ result(const MapT& map, const AccT& acc, const Coord& xyz)
+ {
+ return math::CPT<MapT, math::CD_2ND>::result(map, acc, xyz);
+ }
+ };
+ struct WsOpT
+ {
+ template<typename MapT, typename AccT>
+ static typename OutGridType::ValueType
+ result(const MapT& map, const AccT& acc, const Coord& xyz)
+ {
+ return math::CPT_RANGE<MapT, math::CD_2ND>::result(map, acc, xyz);
+ }
+ };
+ struct Functor
+ {
+ Functor(const InGridType& grid, bool threaded, bool worldspace, InterruptT* interrupt):
+ mThreaded(threaded), mWorldSpace(worldspace), mInputGrid(grid), mInterrupt(interrupt){}
+ template<typename MapT>
+ void operator()(const MapT& map)
+ {
+ if (mWorldSpace) {
+ GridOperator<InGridType, OutGridType, MapT, WsOpT, InterruptT> op(mInputGrid, map, mInterrupt);
+ mOutputGrid = op.process(mThreaded); // cache the result
+ } else {
+ GridOperator<InGridType, OutGridType, MapT, IsOpT, InterruptT> op(mInputGrid, map, mInterrupt);
+ mOutputGrid = op.process(mThreaded); // cache the result
+ }
+ }
+ const bool mThreaded;
+ const bool mWorldSpace;
+ const InGridType& mInputGrid;
+ typename OutGridType::Ptr mOutputGrid;
+ InterruptT* mInterrupt;
+ };
+ const InGridType& mInputGrid;
+ InterruptT* mInterrupt;
+}; // end of Cpt class
+
+
+////////////////////////////////////////
+
+
+/// @brief Compute the curl of a scalar grid.
+template<typename GridT, typename InterruptT = util::NullInterrupter>
+class Curl
+{
+public:
+ typedef GridT InGridType;
+ typedef GridT OutGridType;
+ Curl(const GridT& grid, InterruptT* interrupt = NULL):
+ mInputGrid(grid), mInterrupt(interrupt)
+ {
+ }
+ typename GridT::Ptr process(bool threaded = true)
+ {
+ Functor functor(mInputGrid, threaded, mInterrupt);
+ processTypedMap(mInputGrid.transform(), functor);
+ return functor.mOutputGrid;
+ }
+
+private:
+ struct Functor
+ {
+ Functor(const GridT& grid, bool threaded, InterruptT* interrupt):
+ mThreaded(threaded), mInputGrid(grid), mInterrupt(interrupt){}
+ template<typename MapT>
+ void operator()(const MapT& map)
+ {
+ typedef math::Curl<MapT, math::CD_2ND> OpT;
+ GridOperator<GridT, GridT, MapT, OpT, InterruptT> op(mInputGrid, map, mInterrupt);
+ mOutputGrid = op.process(mThreaded); // cache the result
+ }
+ const bool mThreaded;
+ const GridT& mInputGrid;
+ typename GridT::Ptr mOutputGrid;
+ InterruptT* mInterrupt;
+ }; // Private Functor
+
+ const GridT& mInputGrid;
+ InterruptT* mInterrupt;
+}; // end of Curl class
+
+
+////////////////////////////////////////
+
+
+/// @brief Computes the Divergence of a scalar grid
+template<typename InGridT, typename InterruptT = util::NullInterrupter>
+class Divergence
+{
+public:
+ typedef InGridT InGridType;
+ typedef typename VectorToScalarConverter<InGridT>::Type OutGridType;
+
+ Divergence(const InGridT& grid, InterruptT* interrupt = NULL):
+ mInputGrid(grid), mInterrupt(interrupt)
+ {
+ }
+ typename OutGridType::Ptr process(bool threaded = true)
+ {
+ if( mInputGrid.getGridClass() == GRID_STAGGERED ) {
+ Functor<math::FD_1ST> functor(mInputGrid, threaded, mInterrupt);
+ processTypedMap(mInputGrid.transform(), functor);
+ return functor.mOutputGrid;
+ }
+ else {
+ Functor<math::CD_2ND> functor(mInputGrid, threaded, mInterrupt);
+ processTypedMap(mInputGrid.transform(), functor);
+ return functor.mOutputGrid;
+ }
+ }
+
+protected:
+ template<math::DScheme DiffScheme>
+ struct Functor
+ {
+ Functor(const InGridT& grid, bool threaded, InterruptT* interrupt):
+ mThreaded(threaded), mInputGrid(grid), mInterrupt(interrupt) {}
+ template<typename MapT>
+ void operator()(const MapT& map)
+ {
+ typedef math::Divergence<MapT, DiffScheme> OpT;
+ GridOperator<InGridType, OutGridType, MapT, OpT, InterruptT> op(mInputGrid, map, mInterrupt);
+ mOutputGrid = op.process(mThreaded); // cache the result
+ }
+ const bool mThreaded;
+ const InGridType& mInputGrid;
+ typename OutGridType::Ptr mOutputGrid;
+ InterruptT* mInterrupt;
+ }; // Private Functor
+
+ const InGridType& mInputGrid;
+ InterruptT* mInterrupt;
+}; // end of Divergence class
+
+
+////////////////////////////////////////
+
+
+/// @brief Computes the Gradient of a scalar grid
+template<typename InGridT, typename InterruptT = util::NullInterrupter>
+class Gradient
+{
+public:
+ typedef InGridT InGridType;
+ typedef typename ScalarToVectorConverter<InGridT>::Type OutGridType;
+
+ Gradient(const InGridT& grid, InterruptT* interrupt = NULL):
+ mInputGrid(grid), mInterrupt(interrupt)
+ {
+ }
+ typename OutGridType::Ptr process(bool threaded = true)
+ {
+ Functor functor(mInputGrid, threaded, mInterrupt);
+ processTypedMap(mInputGrid.transform(), functor);
+ return functor.mOutputGrid;
+ }
+
+protected:
+ struct Functor
+ {
+ Functor(const InGridT& grid, bool threaded, InterruptT* interrupt):
+ mThreaded(threaded), mInputGrid(grid), mInterrupt(interrupt) {}
+ template<typename MapT>
+ void operator()(const MapT& map)
+ {
+ typedef math::Gradient<MapT, math::CD_2ND> OpT;
+ GridOperator<InGridType, OutGridType, MapT, OpT, InterruptT> op(mInputGrid, map, mInterrupt);
+ mOutputGrid = op.process(mThreaded); // cache the result
+ }
+ const bool mThreaded;
+ const InGridT& mInputGrid;
+ typename OutGridType::Ptr mOutputGrid;
+ InterruptT* mInterrupt;
+ }; // Private Functor
+
+ const InGridT& mInputGrid;
+ InterruptT* mInterrupt;
+}; // end of Gradient class
+
+
+////////////////////////////////////////
+
+
+/// @brief Computes the Laplacian of a scalar grid
+template<typename GridT, typename InterruptT = util::NullInterrupter>
+class Laplacian
+{
+public:
+ typedef GridT InGridType;
+ typedef GridT OutGridType;
+ Laplacian(const GridT& grid, InterruptT* interrupt = NULL):
+ mInputGrid(grid), mInterrupt(interrupt)
+ {
+ }
+ typename GridT::Ptr process(bool threaded = true)
+ {
+ Functor functor(mInputGrid, threaded, mInterrupt);
+ processTypedMap(mInputGrid.transform(), functor);
+ return functor.mOutputGrid;
+ }
+
+protected:
+ struct Functor
+ {
+ Functor(const GridT& grid, bool threaded, InterruptT* interrupt):
+ mThreaded(threaded), mInputGrid(grid), mInterrupt(interrupt) {}
+ template<typename MapT>
+ void operator()(const MapT& map)
+ {
+ typedef math::Laplacian<MapT, math::CD_SECOND> OpT;
+ GridOperator<GridT, GridT, MapT, OpT, InterruptT> op(mInputGrid, map);
+ mOutputGrid = op.process(mThreaded); // cache the result
+ }
+ const bool mThreaded;
+ const GridT& mInputGrid;
+ typename GridT::Ptr mOutputGrid;
+ InterruptT* mInterrupt;
+ }; // Private Functor
+
+ const GridT& mInputGrid;
+ InterruptT* mInterrupt;
+}; // end of Laplacian class
+
+
+////////////////////////////////////////
+
+
+template<typename GridT, typename InterruptT = util::NullInterrupter>
+class MeanCurvature
+{
+public:
+ typedef GridT InGridType;
+ typedef GridT OutGridType;
+ MeanCurvature(const GridT& grid, InterruptT* interrupt = NULL):
+ mInputGrid(grid), mInterrupt(interrupt)
+ {
+ }
+ typename GridT::Ptr process(bool threaded = true)
+ {
+ Functor functor(mInputGrid, threaded, mInterrupt);
+ processTypedMap(mInputGrid.transform(), functor);
+ return functor.mOutputGrid;
+ }
+
+protected:
+ struct Functor
+ {
+ Functor(const GridT& grid, bool threaded, InterruptT* interrupt):
+ mThreaded(threaded), mInputGrid(grid), mInterrupt(interrupt) {}
+ template<typename MapT>
+ void operator()(const MapT& map)
+ {
+ typedef math::MeanCurvature<MapT, math::CD_SECOND, math::CD_2ND> OpT;
+ GridOperator<GridT, GridT, MapT, OpT, InterruptT> op(mInputGrid, map);
+ mOutputGrid = op.process(mThreaded); // cache the result
+ }
+ const bool mThreaded;
+ const GridT& mInputGrid;
+ typename GridT::Ptr mOutputGrid;
+ InterruptT* mInterrupt;
+ }; // Private Functor
+
+ const GridT& mInputGrid;
+ InterruptT* mInterrupt;
+}; // end of MeanCurvature class
+
+
+////////////////////////////////////////
+
+
+template<typename InGridT, typename InterruptT = util::NullInterrupter>
+class Magnitude
+{
+public:
+ typedef InGridT InGridType;
+ typedef typename VectorToScalarConverter<InGridT>::Type OutGridType;
+ Magnitude(const InGridType& grid, InterruptT* interrupt = NULL):
+ mInputGrid(grid), mInterrupt(interrupt)
+ {
+ }
+ typename OutGridType::Ptr process(bool threaded = true)
+ {
+ Functor functor(mInputGrid, threaded, mInterrupt);
+ processTypedMap(mInputGrid.transform(), functor);
+ return functor.mOutputGrid;
+ }
+
+protected:
+ struct OpT
+ {
+ template<typename MapT, typename AccT>
+ static typename OutGridType::ValueType
+ result(const MapT&, const AccT& acc, const Coord& xyz) { return acc.getValue(xyz).length();}
+ };
+ struct Functor
+ {
+ Functor(const InGridT& grid, bool threaded, InterruptT* interrupt):
+ mThreaded(threaded), mInputGrid(grid), mInterrupt(interrupt) {}
+ template<typename MapT>
+ void operator()(const MapT& map)
+ {
+ GridOperator<InGridType, OutGridType, MapT, OpT, InterruptT> op(mInputGrid, map);
+ mOutputGrid = op.process(mThreaded); // cache the result
+ }
+ const bool mThreaded;
+ const InGridType& mInputGrid;
+ typename OutGridType::Ptr mOutputGrid;
+ InterruptT* mInterrupt;
+ }; // Private Functor
+
+ const InGridType& mInputGrid;
+ InterruptT* mInterrupt;
+}; // end of Magnitude class
+
+
+////////////////////////////////////////
+
+
+template<typename GridT, typename InterruptT = util::NullInterrupter>
+class Normalize
+{
+public:
+ typedef GridT InGridType;
+ typedef GridT OutGridType;
+ Normalize(const GridT& grid, InterruptT* interrupt = NULL):
+ mInputGrid(grid), mInterrupt(interrupt)
+ {
+ }
+ typename GridT::Ptr process(bool threaded = true)
+ {
+ Functor functor(mInputGrid, threaded, mInterrupt);
+ processTypedMap(mInputGrid.transform(), functor);
+ return functor.mOutputGrid;
+ }
+
+protected:
+ struct OpT
+ {
+ template<typename MapT, typename AccT>
+ static typename OutGridType::ValueType
+ result(const MapT&, const AccT& acc, const Coord& xyz)
+ {
+ typename OutGridType::ValueType vec = acc.getValue(xyz);
+ if ( !vec.normalize() ) vec.setZero();
+ return vec;
+ }
+ };
+ struct Functor
+ {
+ Functor(const GridT& grid, bool threaded, InterruptT* interrupt):
+ mThreaded(threaded), mInputGrid(grid), mInterrupt(interrupt) {}
+ template<typename MapT>
+ void operator()(const MapT& map)
+ {
+ GridOperator<GridT, GridT, MapT, OpT, InterruptT> op(mInputGrid, map);
+ mOutputGrid = op.process(mThreaded); // cache the result
+ }
+ const bool mThreaded;
+ const GridT& mInputGrid;
+ typename GridT::Ptr mOutputGrid;
+ InterruptT* mInterrupt;
+ }; // Private Functor
+
+ const GridT& mInputGrid;
+ InterruptT* mInterrupt;
+}; // end of Normalize class
+
+
+////////////////////////////////////////
+
+
+template<typename GridType, typename InterruptT> inline
+typename ScalarToVectorConverter<GridType>::Type::Ptr
+cpt(const GridType& grid, bool threaded, InterruptT* interrupt)
+{
+ Cpt<GridType, InterruptT> op(grid, interrupt);
+ return op.process(threaded);
+}
+
+template<typename GridType, typename InterruptT> inline
+typename GridType::Ptr
+curl(const GridType& grid, bool threaded, InterruptT* interrupt)
+{
+ Curl<GridType, InterruptT> op(grid, interrupt);
+ return op.process(threaded);
+}
+
+template<typename GridType, typename InterruptT> inline
+typename VectorToScalarConverter<GridType>::Type::Ptr
+divergence(const GridType& grid, bool threaded, InterruptT* interrupt)
+{
+ Divergence<GridType, InterruptT> op(grid, interrupt);
+ return op.process(threaded);
+}
+
+template<typename GridType, typename InterruptT> inline
+typename ScalarToVectorConverter<GridType>::Type::Ptr
+gradient(const GridType& grid, bool threaded, InterruptT* interrupt)
+{
+ Gradient<GridType, InterruptT> op(grid, interrupt);
+ return op.process(threaded);
+}
+
+template<typename GridType, typename InterruptT> inline
+typename GridType::Ptr
+laplacian(const GridType& grid, bool threaded, InterruptT* interrupt)
+{
+ Laplacian<GridType, InterruptT> op(grid, interrupt);
+ return op.process(threaded);
+}
+
+template<typename GridType, typename InterruptT> inline
+typename GridType::Ptr
+meanCurvature(const GridType& grid, bool threaded, InterruptT* interrupt)
+{
+ MeanCurvature<GridType, InterruptT> op(grid, interrupt);
+ return op.process(threaded);
+}
+
+template<typename GridType, typename InterruptT> inline
+typename VectorToScalarConverter<GridType>::Type::Ptr
+magnitude(const GridType& grid, bool threaded, InterruptT* interrupt)
+{
+ Magnitude<GridType, InterruptT> op(grid, interrupt);
+ return op.process(threaded);
+}
+
+template<typename GridType, typename InterruptT> inline
+typename GridType::Ptr
+normalize(const GridType& grid, bool threaded, InterruptT* interrupt)
+{
+ Normalize<GridType, InterruptT> op(grid, interrupt);
+ return op.process(threaded);
+}
+
+} // namespace tools
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_TOOLS_GRID_OPERATORS_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/tools/GridTransformer.h b/extern/openvdb/internal/openvdb/tools/GridTransformer.h
new file mode 100644
index 00000000000..12586492988
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/tools/GridTransformer.h
@@ -0,0 +1,986 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+//
+/// @file GridTransformer.h
+
+#ifndef OPENVDB_TOOLS_GRIDTRANSFORMER_HAS_BEEN_INCLUDED
+#define OPENVDB_TOOLS_GRIDTRANSFORMER_HAS_BEEN_INCLUDED
+
+#include <cmath>
+#include <boost/bind.hpp>
+#include <boost/function.hpp>
+#include <boost/shared_ptr.hpp>
+#include <tbb/blocked_range.h>
+#include <tbb/parallel_reduce.h>
+#include <openvdb/Grid.h>
+#include <openvdb/Types.h>
+#include <openvdb/math/Math.h> // for isApproxEqual()
+#include <openvdb/util/NullInterrupter.h>
+#include "Interpolation.h"
+#include "LevelSetRebuild.h" // for doLevelSetRebuild()
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace tools {
+
+/// @brief Resample an input grid into an output grid of the same type such that,
+/// after resampling, the input and output grids coincide (apart from sampling
+/// artifacts), but the output grid's transform is unchanged.
+/// @details Specifically, this function resamples the input grid into the output
+/// grid's index space, using a sampling kernel like PointSampler, BoxSampler,
+/// or QuadraticSampler.
+/// @param inGrid the grid to be resampled
+/// @param outGrid the grid into which to write the resampled voxel data
+/// @param interrupter an object adhering to the util::NullInterrupter interface
+/// @par Example:
+/// @code
+/// // Create an input grid with the default identity transform
+/// // and populate it with a level-set sphere.
+/// FloatGrid::ConstPtr src = tools::makeSphere(...);
+/// // Create an output grid and give it a uniform-scale transform.
+/// FloatGrid::Ptr dest = FloatGrid::create();
+/// const float voxelSize = 0.5;
+/// dest->setTransform(math::Transform::createLinearTransform(voxelSize));
+/// // Resample the input grid into the output grid, reproducing
+/// // the level-set sphere at a smaller voxel size.
+/// MyInterrupter interrupter = ...;
+/// tools::resampleToMatch<tools::QuadraticSampler>(*src, *dest, interrupter);
+/// @endcode
+template<typename Sampler, typename Interrupter, typename GridType>
+inline void
+resampleToMatch(const GridType& inGrid, GridType& outGrid, Interrupter& interrupter);
+
+/// @brief Resample an input grid into an output grid of the same type such that,
+/// after resampling, the input and output grids coincide (apart from sampling
+/// artifacts), but the output grid's transform is unchanged.
+/// @details Specifically, this function resamples the input grid into the output
+/// grid's index space, using a sampling kernel like PointSampler, BoxSampler,
+/// or QuadraticSampler.
+/// @param inGrid the grid to be resampled
+/// @param outGrid the grid into which to write the resampled voxel data
+/// @par Example:
+/// @code
+/// // Create an input grid with the default identity transform
+/// // and populate it with a level-set sphere.
+/// FloatGrid::ConstPtr src = tools::makeSphere(...);
+/// // Create an output grid and give it a uniform-scale transform.
+/// FloatGrid::Ptr dest = FloatGrid::create();
+/// const float voxelSize = 0.5;
+/// dest->setTransform(math::Transform::createLinearTransform(voxelSize));
+/// // Resample the input grid into the output grid, reproducing
+/// // the level-set sphere at a smaller voxel size.
+/// tools::resampleToMatch<tools::QuadraticSampler>(*src, *dest);
+/// @endcode
+template<typename Sampler, typename GridType>
+inline void
+resampleToMatch(const GridType& inGrid, GridType& outGrid);
+
+
+////////////////////////////////////////
+
+
+namespace internal {
+
+/// @brief A TileSampler wraps a grid sampler of another type (BoxSampler,
+/// QuadraticSampler, etc.), and for samples that fall within a given tile
+/// of the grid, it returns a cached tile value instead of accessing the grid.
+template<typename Sampler, typename TreeT>
+class TileSampler: public Sampler
+{
+public:
+ typedef typename TreeT::ValueType ValueT;
+
+ /// @param b the index-space bounding box of a particular grid tile
+ /// @param tileVal the tile's value
+ /// @param on the tile's active state
+ TileSampler(const CoordBBox& b, const ValueT& tileVal, bool on):
+ mBBox(b.min().asVec3d(), b.max().asVec3d()), mVal(tileVal), mActive(on), mEmpty(false)
+ {
+ mBBox.expand(-this->radius()); // shrink the bounding box by the sample radius
+ mEmpty = mBBox.empty();
+ }
+
+ bool sample(const TreeT& inTree, const Vec3R& inCoord, ValueT& result) const
+ {
+ if (!mEmpty && mBBox.isInside(inCoord)) { result = mVal; return mActive; }
+ return Sampler::sample(inTree, inCoord, result);
+ }
+
+protected:
+ BBoxd mBBox;
+ ValueT mVal;
+ bool mActive, mEmpty;
+};
+
+
+/// @brief For point sampling, tree traversal is less expensive than testing
+/// bounding box membership.
+template<typename TreeT>
+struct TileSampler<PointSampler, TreeT>: public PointSampler {
+ TileSampler(const CoordBBox&, const typename TreeT::ValueType&, bool) {}
+};
+
+/// @brief For point sampling, tree traversal is less expensive than testing
+/// bounding box membership.
+template<typename TreeT>
+struct TileSampler<StaggeredPointSampler, TreeT>: public StaggeredPointSampler {
+ TileSampler(const CoordBBox&, const typename TreeT::ValueType&, bool) {}
+};
+
+} // namespace internal
+
+
+////////////////////////////////////////
+
+
+/// A GridResampler applies a geometric transformation to an
+/// input grid using one of several sampling schemes, and stores
+/// the result in an output grid.
+///
+/// Usage:
+/// @code
+/// GridResampler resampler();
+/// resampler.transformGrid<BoxSampler>(xform, inGrid, outGrid);
+/// @endcode
+/// where @c xform is a functor that implements the following methods:
+/// @code
+/// bool isAffine() const
+/// openvdb::Vec3d transform(const openvdb::Vec3d&) const
+/// openvdb::Vec3d invTransform(const openvdb::Vec3d&) const
+/// @endcode
+/// @note When the transform is affine and can be expressed as a 4 x 4 matrix,
+/// a GridTransformer is much more efficient than a GridResampler.
+class GridResampler
+{
+public:
+ typedef boost::shared_ptr<GridResampler> Ptr;
+ typedef boost::function<bool (void)> InterruptFunc;
+
+ GridResampler(): mThreaded(true), mTransformTiles(true) {}
+ virtual ~GridResampler() {}
+
+ /// Enable or disable threading. (Threading is enabled by default.)
+ void setThreaded(bool b) { mThreaded = b; }
+ /// Return @c true if threading is enabled.
+ bool threaded() const { return mThreaded; }
+ /// Enable or disable processing of tiles. (Enabled by default, except for level set grids.)
+ void setTransformTiles(bool b) { mTransformTiles = b; }
+ /// Return @c true if tile processing is enabled.
+ bool transformTiles() const { return mTransformTiles; }
+
+ /// @brief Allow processing to be aborted by providing an interrupter object.
+ /// The interrupter will be queried periodically during processing.
+ /// @see util/NullInterrupter.h for interrupter interface requirements.
+ template<typename InterrupterType> void setInterrupter(InterrupterType&);
+
+ template<typename Sampler, typename GridT, typename Transformer>
+ void transformGrid(const Transformer&,
+ const GridT& inGrid, GridT& outGrid) const;
+
+protected:
+ template<typename Sampler, typename GridT, typename Transformer>
+ void applyTransform(const Transformer&, const GridT& inGrid, GridT& outGrid) const;
+
+ bool interrupt() const { return mInterrupt && mInterrupt(); }
+
+private:
+ template<typename Sampler, typename InTreeT, typename OutTreeT, typename Transformer>
+ static void transformBBox(const Transformer&, const CoordBBox& inBBox,
+ const InTreeT& inTree, OutTreeT& outTree, const InterruptFunc&,
+ const Sampler& = Sampler());
+
+ template<typename Sampler, typename TreeT, typename Transformer>
+ class RangeProcessor;
+
+ bool mThreaded, mTransformTiles;
+ InterruptFunc mInterrupt;
+};
+
+
+////////////////////////////////////////
+
+
+/// @brief A GridTransformer applies a geometric transformation to an
+/// input grid using one of several sampling schemes, and stores
+/// the result in an output grid.
+///
+/// @note GridTransformer is optimized for affine transformations.
+///
+/// Usage:
+/// @code
+/// Mat4R xform = ...;
+/// GridTransformer transformer(xform);
+/// transformer.transformGrid<BoxSampler>(inGrid, outGrid);
+/// @endcode
+/// or
+/// @code
+/// Vec3R pivot = ..., scale = ..., rotate = ..., translate = ...;
+/// GridTransformer transformer(pivot, scale, rotate, translate);
+/// transformer.transformGrid<QuadraticSampler>(inGrid, outGrid);
+/// @endcode
+class GridTransformer: public GridResampler
+{
+public:
+ typedef boost::shared_ptr<GridTransformer> Ptr;
+
+ GridTransformer(const Mat4R& xform);
+ GridTransformer(
+ const Vec3R& pivot,
+ const Vec3R& scale,
+ const Vec3R& rotate,
+ const Vec3R& translate,
+ const std::string& xformOrder = "tsr",
+ const std::string& rotationOrder = "zyx");
+ virtual ~GridTransformer() {}
+
+ const Mat4R& getTransform() const { return mTransform; }
+
+ template<class Sampler, class GridT>
+ void transformGrid(const GridT& inGrid, GridT& outGrid) const;
+
+private:
+ struct MatrixTransform;
+
+ inline void init(const Vec3R& pivot, const Vec3R& scale,
+ const Vec3R& rotate, const Vec3R& translate,
+ const std::string& xformOrder, const std::string& rotOrder);
+
+ Vec3R mPivot;
+ Vec3i mMipLevels;
+ Mat4R mTransform, mPreScaleTransform, mPostScaleTransform;
+};
+
+
+////////////////////////////////////////
+
+
+namespace local_util {
+
+/// @brief Decompose an affine transform into scale, rotation and translation components.
+/// @return @c false if the given matrix is not affine or cannot otherwise be decomposed.
+/// @todo This is not safe for matrices with shear.
+template<typename T>
+inline bool
+decompose(const math::Mat4<T>& m, math::Vec3<T>& scale,
+ math::Vec3<T>& rotate, math::Vec3<T>& translate)
+{
+ if (!math::isAffine(m)) return false;
+
+ // this is the translation in world space
+ translate = m.getTranslation();
+ // Extract translation.
+ math::Mat3<T> temp = m.getMat3();
+
+ scale.init(
+ (math::Vec3<T>(1, 0, 0) * temp).length(),
+ (math::Vec3<T>(0, 1, 0) * temp).length(),
+ (math::Vec3<T>(0, 0, 1) * temp).length());
+ // Extract scale.
+ temp *= math::scale<math::Mat3<T> >(scale).inverse();
+
+ rotate = math::eulerAngles(temp, math::XYZ_ROTATION);
+
+ if (!rotate.eq(math::Vec3<T>::zero()) && !scale.eq(math::Vec3<T>(scale[0]))) {
+ // No unique decomposition if scale is nonuniform and rotation is nonzero.
+ return false;
+ }
+ return true;
+}
+
+} // namespace local_util
+
+
+////////////////////////////////////////
+
+
+/// This class implements the Transformer functor interface (specifically,
+/// the isAffine(), transform() and invTransform() methods) for a transform
+/// that is expressed as a 4 x 4 matrix.
+struct GridTransformer::MatrixTransform
+{
+ MatrixTransform(): mat(Mat4R::identity()), invMat(Mat4R::identity()) {}
+ MatrixTransform(const Mat4R& xform): mat(xform), invMat(xform.inverse()) {}
+
+ bool isAffine() const { return math::isAffine(mat); }
+
+ Vec3R transform(const Vec3R& pos) const { return mat.transformH(pos); }
+
+ Vec3R invTransform(const Vec3R& pos) const { return invMat.transformH(pos); }
+
+ Mat4R mat, invMat;
+};
+
+
+////////////////////////////////////////
+
+
+/// @brief This class implements the Transformer functor interface (specifically,
+/// the isAffine(), transform() and invTransform() methods) for a transform
+/// that maps an A grid into a B grid's index space such that, after resampling,
+/// A's index space and transform match B's index space and transform.
+class ABTransform
+{
+public:
+ /// @param aXform the A grid's transform
+ /// @param bXform the B grid's transform
+ ABTransform(const math::Transform& aXform, const math::Transform& bXform):
+ mAXform(aXform),
+ mBXform(bXform),
+ mIsAffine(mAXform.isLinear() && mBXform.isLinear()),
+ mIsIdentity(mIsAffine && mAXform == mBXform)
+ {}
+
+ bool isAffine() const { return mIsAffine; }
+
+ bool isIdentity() const { return mIsIdentity; }
+
+ openvdb::Vec3R transform(const openvdb::Vec3R& pos) const
+ {
+ return mBXform.worldToIndex(mAXform.indexToWorld(pos));
+ }
+
+ openvdb::Vec3R invTransform(const openvdb::Vec3R& pos) const
+ {
+ return mAXform.worldToIndex(mBXform.indexToWorld(pos));
+ }
+
+ const math::Transform& getA() const { return mAXform; }
+ const math::Transform& getB() const { return mBXform; }
+
+private:
+ const math::Transform &mAXform, &mBXform;
+ const bool mIsAffine;
+ const bool mIsIdentity;
+};
+
+
+/// The normal entry points for resampling are the resampleToMatch() functions,
+/// which correctly handle level set grids under scaling and shearing.
+/// doResampleToMatch() is mainly for internal use but is typically faster
+/// for level sets, and correct provided that no scaling or shearing is needed.
+///
+/// @warning Do not use this function to scale or shear a level set grid.
+template<typename Sampler, typename Interrupter, typename GridType>
+inline void
+doResampleToMatch(const GridType& inGrid, GridType& outGrid, Interrupter& interrupter)
+{
+ ABTransform xform(inGrid.transform(), outGrid.transform());
+
+ if (Sampler::consistent() && xform.isIdentity()) {
+ // If the transforms of the input and output are identical, the
+ // output tree is simply a deep copy of the input tree.
+ outGrid.setTree(inGrid.tree().copy());
+ } else if (xform.isAffine()) {
+ // If the input and output transforms are both affine, create an
+ // input to output transform (in:index-to-world * out:world-to-index)
+ // and use the fast GridTransformer API.
+ Mat4R mat = xform.getA().baseMap()->getAffineMap()->getMat4() *
+ ( xform.getB().baseMap()->getAffineMap()->getMat4().inverse() );
+
+ GridTransformer transformer(mat);
+ transformer.setInterrupter(interrupter);
+
+ // Transform the input grid and store the result in the output grid.
+ transformer.transformGrid<Sampler>(inGrid, outGrid);
+ } else {
+ // If either the input or the output transform is non-affine,
+ // use the slower GridResampler API.
+ GridResampler resampler;
+ resampler.setInterrupter(interrupter);
+
+ resampler.transformGrid<Sampler>(xform, inGrid, outGrid);
+ }
+}
+
+
+template<typename Sampler, typename Interrupter, typename GridType>
+inline void
+resampleToMatch(const GridType& inGrid, GridType& outGrid, Interrupter& interrupter)
+{
+ if (inGrid.getGridClass() == GRID_LEVEL_SET) {
+ // If the input grid is a level set, resample it using the level set rebuild tool.
+
+ if (inGrid.constTransform() == outGrid.constTransform()) {
+ // If the transforms of the input and output grids are identical,
+ // the output tree is simply a deep copy of the input tree.
+ outGrid.setTree(inGrid.tree().copy());
+ return;
+ }
+
+ // If the output grid is a level set, resample the input grid to have the output grid's
+ // background value. Otherwise, preserve the input grid's background value.
+ typedef typename GridType::ValueType ValueT;
+ const ValueT halfWidth = ((outGrid.getGridClass() == openvdb::GRID_LEVEL_SET)
+ ? ValueT(outGrid.background() * (1.0 / outGrid.voxelSize()[0]))
+ : ValueT(inGrid.background() * (1.0 / inGrid.voxelSize()[0])));
+
+ typename GridType::Ptr tempGrid;
+ try {
+ tempGrid = doLevelSetRebuild(inGrid, /*iso=*/zeroVal<ValueT>(),
+ /*exWidth=*/halfWidth, /*inWidth=*/halfWidth,
+ &outGrid.constTransform(), &interrupter);
+ } catch (TypeError&) {
+ // The input grid is classified as a level set, but it has a value type
+ // that is not supported by the level set rebuild tool. Fall back to
+ // using the generic resampler.
+ tempGrid.reset();
+ }
+ if (tempGrid) {
+ outGrid.setTree(tempGrid->treePtr());
+ return;
+ }
+ }
+
+ // If the input grid is not a level set, use the generic resampler.
+ doResampleToMatch<Sampler>(inGrid, outGrid, interrupter);
+}
+
+
+template<typename Sampler, typename GridType>
+inline void
+resampleToMatch(const GridType& inGrid, GridType& outGrid)
+{
+ util::NullInterrupter interrupter;
+ resampleToMatch<Sampler>(inGrid, outGrid, interrupter);
+}
+
+
+////////////////////////////////////////
+
+
+inline
+GridTransformer::GridTransformer(const Mat4R& xform):
+ mPivot(0, 0, 0),
+ mMipLevels(0, 0, 0),
+ mTransform(xform),
+ mPreScaleTransform(Mat4R::identity()),
+ mPostScaleTransform(Mat4R::identity())
+{
+ Vec3R scale, rotate, translate;
+ if (local_util::decompose(mTransform, scale, rotate, translate)) {
+ // If the transform can be decomposed into affine components,
+ // use them to set up a mipmapping-like scheme for downsampling.
+ init(mPivot, scale, rotate, translate, "srt", "zyx");
+ }
+}
+
+
+inline
+GridTransformer::GridTransformer(
+ const Vec3R& pivot, const Vec3R& scale,
+ const Vec3R& rotate, const Vec3R& translate,
+ const std::string& xformOrder, const std::string& rotOrder):
+ mPivot(0, 0, 0),
+ mMipLevels(0, 0, 0),
+ mPreScaleTransform(Mat4R::identity()),
+ mPostScaleTransform(Mat4R::identity())
+{
+ init(pivot, scale, rotate, translate, xformOrder, rotOrder);
+}
+
+
+////////////////////////////////////////
+
+
+inline void
+GridTransformer::init(
+ const Vec3R& pivot, const Vec3R& scale,
+ const Vec3R& rotate, const Vec3R& translate,
+ const std::string& xformOrder, const std::string& rotOrder)
+{
+ if (xformOrder.size() != 3) {
+ OPENVDB_THROW(ValueError, "invalid transform order (" + xformOrder + ")");
+ }
+ if (rotOrder.size() != 3) {
+ OPENVDB_THROW(ValueError, "invalid rotation order (" + rotOrder + ")");
+ }
+
+ mPivot = pivot;
+
+ // Scaling is handled via a mipmapping-like scheme of successive
+ // halvings of the tree resolution, until the remaining scale
+ // factor is greater than or equal to 1/2.
+ Vec3R scaleRemainder = scale;
+ for (int i = 0; i < 3; ++i) {
+ double s = std::fabs(scale(i));
+ if (s < 0.5) {
+ mMipLevels(i) = int(std::floor(-std::log(s)/std::log(2.0)));
+ scaleRemainder(i) = scale(i) * (1 << mMipLevels(i));
+ }
+ }
+
+ // Build pre-scale and post-scale transform matrices based on
+ // the user-specified order of operations.
+ // Note that we iterate over the transform order string in reverse order
+ // (e.g., "t", "r", "s", given "srt"). This is because math::Mat matrices
+ // postmultiply row vectors rather than premultiplying column vectors.
+ mTransform = mPreScaleTransform = mPostScaleTransform = Mat4R::identity();
+ Mat4R* remainder = &mPostScaleTransform;
+ int rpos, spos, tpos;
+ rpos = spos = tpos = 3;
+ for (int ix = 2; ix >= 0; --ix) { // reverse iteration
+ switch (xformOrder[ix]) {
+
+ case 'r':
+ rpos = ix;
+ mTransform.preTranslate(pivot);
+ remainder->preTranslate(pivot);
+
+ int xpos, ypos, zpos;
+ xpos = ypos = zpos = 3;
+ for (int ir = 2; ir >= 0; --ir) {
+ switch (rotOrder[ir]) {
+ case 'x':
+ xpos = ir;
+ mTransform.preRotate(math::X_AXIS, rotate.x());
+ remainder->preRotate(math::X_AXIS, rotate.x());
+ break;
+ case 'y':
+ ypos = ir;
+ mTransform.preRotate(math::Y_AXIS, rotate.y());
+ remainder->preRotate(math::Y_AXIS, rotate.y());
+ break;
+ case 'z':
+ zpos = ir;
+ mTransform.preRotate(math::Z_AXIS, rotate.z());
+ remainder->preRotate(math::Z_AXIS, rotate.z());
+ break;
+ }
+ }
+ // Reject rotation order strings that don't contain exactly one
+ // instance of "x", "y" and "z".
+ if (xpos > 2 || ypos > 2 || zpos > 2) {
+ OPENVDB_THROW(ValueError, "invalid rotation order (" + rotOrder + ")");
+ }
+
+ mTransform.preTranslate(-pivot);
+ remainder->preTranslate(-pivot);
+ break;
+
+ case 's':
+ spos = ix;
+ mTransform.preTranslate(pivot);
+ mTransform.preScale(scale);
+ mTransform.preTranslate(-pivot);
+
+ remainder->preTranslate(pivot);
+ remainder->preScale(scaleRemainder);
+ remainder->preTranslate(-pivot);
+ remainder = &mPreScaleTransform;
+ break;
+
+ case 't':
+ tpos = ix;
+ mTransform.preTranslate(translate);
+ remainder->preTranslate(translate);
+ break;
+ }
+ }
+ // Reject transform order strings that don't contain exactly one
+ // instance of "t", "r" and "s".
+ if (tpos > 2 || rpos > 2 || spos > 2) {
+ OPENVDB_THROW(ValueError, "invalid transform order (" + xformOrder + ")");
+ }
+}
+
+
+////////////////////////////////////////
+
+
+template<typename InterrupterType>
+void
+GridResampler::setInterrupter(InterrupterType& interrupter)
+{
+ mInterrupt = boost::bind(&InterrupterType::wasInterrupted,
+ /*this=*/&interrupter, /*percent=*/-1);
+}
+
+
+template<typename Sampler, typename GridT, typename Transformer>
+void
+GridResampler::transformGrid(const Transformer& xform,
+ const GridT& inGrid, GridT& outGrid) const
+{
+ outGrid.setBackground(inGrid.background());
+ applyTransform<Sampler>(xform, inGrid, outGrid);
+}
+
+
+template<class Sampler, class GridT>
+void
+GridTransformer::transformGrid(const GridT& inGrid, GridT& outGrid) const
+{
+ outGrid.setBackground(inGrid.background());
+
+ if (!Sampler::mipmap() || mMipLevels == Vec3i::zero()) {
+ // Skip the mipmapping step.
+ const MatrixTransform xform(mTransform);
+ applyTransform<Sampler>(xform, inGrid, outGrid);
+
+ } else {
+ bool firstPass = true;
+ const typename GridT::ValueType background = inGrid.background();
+ typename GridT::Ptr tempGrid = GridT::create(background);
+
+ if (!mPreScaleTransform.eq(Mat4R::identity())) {
+ firstPass = false;
+ // Apply the pre-scale transform to the input grid
+ // and store the result in a temporary grid.
+ const MatrixTransform xform(mPreScaleTransform);
+ applyTransform<Sampler>(xform, inGrid, *tempGrid);
+ }
+
+ // While the scale factor along one or more axes is less than 1/2,
+ // scale the grid by half along those axes.
+ Vec3i count = mMipLevels; // # of halvings remaining per axis
+ while (count != Vec3i::zero()) {
+ MatrixTransform xform;
+ xform.mat.setTranslation(mPivot);
+ xform.mat.preScale(Vec3R(
+ count.x() ? .5 : 1, count.y() ? .5 : 1, count.z() ? .5 : 1));
+ xform.mat.preTranslate(-mPivot);
+ xform.invMat = xform.mat.inverse();
+
+ if (firstPass) {
+ firstPass = false;
+ // Scale the input grid and store the result in a temporary grid.
+ applyTransform<Sampler>(xform, inGrid, *tempGrid);
+ } else {
+ // Scale the temporary grid and store the result in a transient grid,
+ // then swap the two and discard the transient grid.
+ typename GridT::Ptr destGrid = GridT::create(background);
+ applyTransform<Sampler>(xform, *tempGrid, *destGrid);
+ tempGrid.swap(destGrid);
+ }
+ // (3, 2, 1) -> (2, 1, 0) -> (1, 0, 0) -> (0, 0, 0), etc.
+ count = math::maxComponent(count - 1, Vec3i::zero());
+ }
+
+ // Apply the post-scale transform and store the result in the output grid.
+ if (!mPostScaleTransform.eq(Mat4R::identity())) {
+ const MatrixTransform xform(mPostScaleTransform);
+ applyTransform<Sampler>(xform, *tempGrid, outGrid);
+ } else {
+ outGrid.setTree(tempGrid->treePtr());
+ }
+ }
+}
+
+
+////////////////////////////////////////
+
+
+template<class Sampler, class TreeT, typename Transformer>
+class GridResampler::RangeProcessor
+{
+public:
+ typedef typename TreeT::LeafCIter LeafIterT;
+ typedef typename TreeT::ValueAllCIter TileIterT;
+ typedef typename tree::IteratorRange<LeafIterT> LeafRange;
+ typedef typename tree::IteratorRange<TileIterT> TileRange;
+ typedef typename tree::ValueAccessor<const TreeT> InTreeAccessor;
+ typedef typename tree::ValueAccessor<TreeT> OutTreeAccessor;
+
+ RangeProcessor(const Transformer& xform, const CoordBBox& b, const TreeT& inT, TreeT& outT):
+ mIsRoot(true), mXform(xform), mBBox(b),
+ mInTree(inT), mOutTree(&outT), mInAcc(mInTree), mOutAcc(*mOutTree)
+ {}
+
+ RangeProcessor(const Transformer& xform, const CoordBBox& b, const TreeT& inTree):
+ mIsRoot(false), mXform(xform), mBBox(b),
+ mInTree(inTree), mOutTree(new TreeT(inTree.background())),
+ mInAcc(mInTree), mOutAcc(*mOutTree)
+ {}
+
+ ~RangeProcessor() { if (!mIsRoot) delete mOutTree; }
+
+ /// Splitting constructor: don't copy the original processor's output tree
+ RangeProcessor(RangeProcessor& other, tbb::split):
+ mIsRoot(false),
+ mXform(other.mXform),
+ mBBox(other.mBBox),
+ mInTree(other.mInTree),
+ mOutTree(new TreeT(mInTree.background())),
+ mInAcc(mInTree),
+ mOutAcc(*mOutTree),
+ mInterrupt(other.mInterrupt)
+ {}
+
+ void setInterrupt(const InterruptFunc& f) { mInterrupt = f; }
+
+ /// Transform each leaf node in the given range.
+ void operator()(LeafRange& r)
+ {
+ for ( ; r; ++r) {
+ if (interrupt()) break;
+ LeafIterT i = r.iterator();
+ CoordBBox bbox(i->getOrigin(), i->getOrigin() + Coord(i->dim()));
+ if (!mBBox.empty()) {
+ // Intersect the leaf node's bounding box with mBBox.
+ bbox = CoordBBox(
+ Coord::maxComponent(bbox.min(), mBBox.min()),
+ Coord::minComponent(bbox.max(), mBBox.max()));
+ }
+ if (!bbox.empty()) {
+ transformBBox<Sampler>(mXform, bbox, mInAcc, mOutAcc, mInterrupt);
+ }
+ }
+ }
+
+ /// Transform each non-background tile in the given range.
+ void operator()(TileRange& r)
+ {
+ for ( ; r; ++r) {
+ if (interrupt()) break;
+
+ TileIterT i = r.iterator();
+ // Skip voxels and background tiles.
+ if (!i.isTileValue()) continue;
+ if (!i.isValueOn() && math::isApproxEqual(*i, mOutTree->background())) continue;
+
+ CoordBBox bbox;
+ i.getBoundingBox(bbox);
+ if (!mBBox.empty()) {
+ // Intersect the tile's bounding box with mBBox.
+ bbox = CoordBBox(
+ Coord::maxComponent(bbox.min(), mBBox.min()),
+ Coord::minComponent(bbox.max(), mBBox.max()));
+ }
+ if (!bbox.empty()) {
+ /// @todo This samples the tile voxel-by-voxel, which is much too slow.
+ /// Instead, compute the largest axis-aligned bounding box that is
+ /// contained in the transformed tile (adjusted for the sampler radius)
+ /// and fill it with the tile value. Then transform the remaining voxels.
+ internal::TileSampler<Sampler, InTreeAccessor>
+ sampler(bbox, i.getValue(), i.isValueOn());
+ transformBBox(mXform, bbox, mInAcc, mOutAcc, mInterrupt, sampler);
+ }
+ }
+ }
+
+ /// Merge another processor's output tree into this processor's tree.
+ void join(RangeProcessor& other)
+ {
+ if (!interrupt()) mOutTree->merge(*other.mOutTree);
+ }
+
+private:
+ bool interrupt() const { return mInterrupt && mInterrupt(); }
+
+ const bool mIsRoot; // true if mOutTree is the top-level tree
+ Transformer mXform;
+ CoordBBox mBBox;
+ const TreeT& mInTree;
+ TreeT* mOutTree;
+ InTreeAccessor mInAcc;
+ OutTreeAccessor mOutAcc;
+ InterruptFunc mInterrupt;
+};
+
+
+////////////////////////////////////////
+
+
+template<class Sampler, class GridT, typename Transformer>
+void
+GridResampler::applyTransform(const Transformer& xform,
+ const GridT& inGrid, GridT& outGrid) const
+{
+ typedef typename GridT::TreeType TreeT;
+ const TreeT& inTree = inGrid.tree();
+ TreeT& outTree = outGrid.tree();
+
+ typedef RangeProcessor<Sampler, TreeT, Transformer> RangeProc;
+
+ const GridClass gridClass = inGrid.getGridClass();
+
+ if (gridClass != GRID_LEVEL_SET && mTransformTiles) {
+ // Independently transform the tiles of the input grid.
+ // Note: Tiles in level sets can only be background tiles, and they
+ // are handled more efficiently with a signed flood fill (see below).
+
+ RangeProc proc(xform, CoordBBox(), inTree, outTree);
+ proc.setInterrupt(mInterrupt);
+
+ typename RangeProc::TileIterT tileIter = inTree.cbeginValueAll();
+ tileIter.setMaxDepth(tileIter.getLeafDepth() - 1); // skip leaf nodes
+ typename RangeProc::TileRange tileRange(tileIter);
+
+ if (mThreaded) {
+ tbb::parallel_reduce(tileRange, proc);
+ } else {
+ proc(tileRange);
+ }
+ }
+
+ CoordBBox clipBBox;
+ if (gridClass == GRID_LEVEL_SET) {
+ // Inactive voxels in level sets can only be background voxels, and they
+ // are handled more efficiently with a signed flood fill (see below).
+ clipBBox = inGrid.evalActiveVoxelBoundingBox();
+ }
+
+ // Independently transform the leaf nodes of the input grid.
+
+ RangeProc proc(xform, clipBBox, inTree, outTree);
+ proc.setInterrupt(mInterrupt);
+
+ typename RangeProc::LeafRange leafRange(inTree.cbeginLeaf());
+
+ if (mThreaded) {
+ tbb::parallel_reduce(leafRange, proc);
+ } else {
+ proc(leafRange);
+ }
+
+ // If the grid is a level set, mark inactive voxels as inside or outside.
+ if (gridClass == GRID_LEVEL_SET) {
+ outTree.pruneInactive();
+ outTree.signedFloodFill();
+ }
+}
+
+
+////////////////////////////////////////
+
+
+//static
+template<class Sampler, class InTreeT, class OutTreeT, class Transformer>
+void
+GridResampler::transformBBox(
+ const Transformer& xform,
+ const CoordBBox& bbox,
+ const InTreeT& inTree,
+ OutTreeT& outTree,
+ const InterruptFunc& interrupt,
+ const Sampler& sampler)
+{
+ typedef typename OutTreeT::ValueType ValueT;
+ typedef math::Vec4<Real> Vec4R;
+
+ // Transform the corners of the input tree's bounding box
+ // and compute the enclosing bounding box in the output tree.
+ Vec3R
+ inRMin(bbox.min().x(), bbox.min().y(), bbox.min().z()),
+ inRMax(bbox.max().x(), bbox.max().y(), bbox.max().z()),
+ outRMin = math::minComponent(xform.transform(inRMin), xform.transform(inRMax)),
+ outRMax = math::maxComponent(xform.transform(inRMin), xform.transform(inRMax));
+ for (int i = 0; i < 8; ++i) {
+ Vec3R corner(
+ i & 1 ? inRMax.x() : inRMin.x(),
+ i & 2 ? inRMax.y() : inRMin.y(),
+ i & 4 ? inRMax.z() : inRMin.z());
+ outRMin = math::minComponent(outRMin, xform.transform(corner));
+ outRMax = math::maxComponent(outRMax, xform.transform(corner));
+ }
+ Vec3i
+ outMin = local_util::floorVec3(outRMin) - Sampler::radius(),
+ outMax = local_util::ceilVec3(outRMax) + Sampler::radius();
+
+ if (!xform.isAffine()) {
+ // If the transform is not affine, back-project each output voxel
+ // into the input tree.
+ Vec3R xyz, inXYZ;
+ Coord outXYZ;
+ int &x = outXYZ.x(), &y = outXYZ.y(), &z = outXYZ.z();
+ for (x = outMin.x(); x <= outMax.x(); ++x) {
+ if (interrupt && interrupt()) break;
+ xyz.x() = x;
+ for (y = outMin.y(); y <= outMax.y(); ++y) {
+ if (interrupt && interrupt()) break;
+ xyz.y() = y;
+ for (z = outMin.z(); z <= outMax.z(); ++z) {
+ xyz.z() = z;
+ inXYZ = xform.invTransform(xyz);
+ ValueT result;
+ if (sampler.sample(inTree, inXYZ, result)) {
+ outTree.setValueOn(outXYZ, result);
+ } else {
+ // Note: Don't overwrite existing active values with inactive values.
+ if (!outTree.isValueOn(outXYZ)) {
+ outTree.setValueOff(outXYZ, result);
+ }
+ }
+ }
+ }
+ }
+ } else { // affine
+ // Compute step sizes in the input tree that correspond to
+ // unit steps in x, y and z in the output tree.
+ const Vec3R
+ translation = xform.invTransform(Vec3R(0, 0, 0)),
+ deltaX = xform.invTransform(Vec3R(1, 0, 0)) - translation,
+ deltaY = xform.invTransform(Vec3R(0, 1, 0)) - translation,
+ deltaZ = xform.invTransform(Vec3R(0, 0, 1)) - translation;
+
+#if defined(__ICC)
+ /// @todo The following line is a workaround for bad code generation
+ /// in opt-icc11.1_64 (but not debug or gcc) builds. It should be
+ /// removed once the problem has been addressed at its source.
+ const Vec3R dummy = deltaX;
+#endif
+
+ // Step by whole voxels through the output tree, sampling the
+ // corresponding fractional voxels of the input tree.
+ Vec3R inStartX = xform.invTransform(Vec3R(outMin));
+ Coord outXYZ;
+ int &x = outXYZ.x(), &y = outXYZ.y(), &z = outXYZ.z();
+ for (x = outMin.x(); x <= outMax.x(); ++x, inStartX += deltaX) {
+ if (interrupt && interrupt()) break;
+ Vec3R inStartY = inStartX;
+ for (y = outMin.y(); y <= outMax.y(); ++y, inStartY += deltaY) {
+ if (interrupt && interrupt()) break;
+ Vec3R inXYZ = inStartY;
+ for (z = outMin.z(); z <= outMax.z(); ++z, inXYZ += deltaZ) {
+ ValueT result;
+ if (sampler.sample(inTree, inXYZ, result)) {
+ outTree.setValueOn(outXYZ, result);
+ } else {
+ // Note: Don't overwrite existing active values with inactive values.
+ if (!outTree.isValueOn(outXYZ)) {
+ outTree.setValueOff(outXYZ, result);
+ }
+ }
+ }
+ }
+ }
+ }
+} // GridResampler::transformBBox()
+
+} // namespace tools
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_TOOLS_GRIDTRANSFORMER_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/tools/Interpolation.h b/extern/openvdb/internal/openvdb/tools/Interpolation.h
new file mode 100644
index 00000000000..3ddbc2dff1d
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/tools/Interpolation.h
@@ -0,0 +1,554 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+//
+/// @file Interpolation.h
+///
+/// Sampler classes such as PointSampler and BoxSampler that are intended for use
+/// with tools::GridTransformer should operate in voxel space and must adhere to
+/// the interface described in the example below:
+/// @code
+/// struct MySampler
+/// {
+/// // Return a short name that can be used to identify this sampler
+/// // in error messages and elsewhere.
+/// const char* name() { return "mysampler"; }
+///
+/// // Return the radius of the sampling kernel in voxels, not including
+/// // the center voxel. This is the number of voxels of padding that
+/// // are added to all sides of a volume as a result of resampling.
+/// int radius() { return 2; }
+///
+/// // Return true if scaling by a factor smaller than 0.5 (along any axis)
+/// // should be handled via a mipmapping-like scheme of successive halvings
+/// // of a grid's resolution, until the remaining scale factor is
+/// // greater than or equal to 1/2. Set this to false only when high-quality
+/// // scaling is not required.
+/// bool mipmap() { return true; }
+///
+/// // Specify if sampling at a location that is collocated with a grid point
+/// // is guaranteed to return the exact value at that grid point.
+/// // For most sampling kernels, this should be false.
+/// bool consistent() { return false; }
+///
+/// // Sample the tree at the given coordinates and return the result in val.
+/// // Return true if the sampled value is active.
+/// template<class TreeT>
+/// bool sample(const TreeT& tree, const Vec3R& coord, typename TreeT::ValueType& val);
+/// };
+/// @endcode
+
+#ifndef OPENVDB_TOOLS_INTERPOLATION_HAS_BEEN_INCLUDED
+#define OPENVDB_TOOLS_INTERPOLATION_HAS_BEEN_INCLUDED
+
+#include <cmath>
+#include <boost/shared_ptr.hpp>
+#include <openvdb/version.h> // for OPENVDB_VERSION_NAME
+#include <openvdb/Platform.h> // for round()
+#include <openvdb/math/Transform.h> // for Transform
+
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace tools {
+
+// The following samplers operate in voxel space.
+// When the samplers are applied to grids holding vector or other non-scalar data,
+// the data is assumed to be collocated. For example, using the BoxSampler on a grid
+// with ValueType Vec3f assumes that all three elements in a vector can be assigned
+// the same physical location.
+
+struct PointSampler
+{
+ static const char* name() { return "point"; }
+ static int radius() { return 0; }
+ static bool mipmap() { return false; }
+ static bool consistent() { return true; }
+
+ /// @brief Sample @a inTree at the nearest neighbor to @a inCoord
+ /// and store the result in @a result.
+ /// @return @c true if the sampled value is active.
+ template<class TreeT>
+ static bool sample(const TreeT& inTree, const Vec3R& inCoord,
+ typename TreeT::ValueType& result);
+};
+
+
+struct BoxSampler
+{
+ static const char* name() { return "box"; }
+ static int radius() { return 1; }
+ static bool mipmap() { return true; }
+ static bool consistent() { return true; }
+
+ /// @brief Trilinearly reconstruct @a inTree at @a inCoord
+ /// and store the result in @a result.
+ /// @return @c true if any one of the sampled values is active.
+ template<class TreeT>
+ static bool sample(const TreeT& inTree, const Vec3R& inCoord,
+ typename TreeT::ValueType& result);
+
+ /// @brief Trilinearly reconstruct @a inTree at @a inCoord.
+ /// @return the reconstructed value
+ template<class TreeT>
+ static typename TreeT::ValueType sample(const TreeT& inTree, const Vec3R& inCoord);
+
+private:
+ template<class ValueT, size_t N>
+ static inline ValueT trilinearInterpolation(ValueT (& data)[N][N][N], const Vec3R& uvw);
+};
+
+
+struct QuadraticSampler
+{
+ static const char* name() { return "quadratic"; }
+ static int radius() { return 1; }
+ static bool mipmap() { return true; }
+ static bool consistent() { return false; }
+
+ /// @brief Triquadratically reconstruct @a inTree at @a inCoord
+ /// and store the result in @a result.
+ /// @return @c true if any one of the sampled values is active.
+ template<class TreeT>
+ static bool sample(const TreeT& inTree, const Vec3R& inCoord,
+ typename TreeT::ValueType& result);
+};
+
+
+////////////////////////////////////////
+
+
+// The following samplers operate in voxel space and are designed for Vec3
+// staggered grid data (e.g., fluid simulations using the Marker-and-Cell approach
+// associate elements of the velocity vector with different physical locations:
+// the faces of a cube).
+
+struct StaggeredPointSampler
+{
+ static const char* name() { return "point"; }
+ static int radius() { return 0; }
+ static bool mipmap() { return false; }
+ static bool consistent() { return false; }
+
+ /// @brief Sample @a inTree at the nearest neighbor to @a inCoord
+ /// and store the result in @a result.
+ /// @return true if the sampled value is active.
+ template<class TreeT>
+ static bool sample(const TreeT& inTree, const Vec3R& inCoord,
+ typename TreeT::ValueType& result);
+};
+
+
+struct StaggeredBoxSampler
+{
+ static const char* name() { return "box"; }
+ static int radius() { return 1; }
+ static bool mipmap() { return true; }
+ static bool consistent() { return false; }
+
+ /// @brief Trilinearly reconstruct @a inTree at @a inCoord
+ /// and store the result in @a result.
+ /// @return true if any one of the sampled value is active.
+ template<class TreeT>
+ static bool sample(const TreeT& inTree, const Vec3R& inCoord,
+ typename TreeT::ValueType& result);
+};
+
+
+struct StaggeredQuadraticSampler
+{
+ static const char* name() { return "quadratic"; }
+ static int radius() { return 1; }
+ static bool mipmap() { return true; }
+ static bool consistent() { return false; }
+
+ /// @brief Triquadratically reconstruct @a inTree at @a inCoord
+ /// and store the result in @a result.
+ /// @return true if any one of the sampled values is active.
+ template<class TreeT>
+ static bool sample(const TreeT& inTree, const Vec3R& inCoord,
+ typename TreeT::ValueType& result);
+};
+
+
+////////////////////////////////////////
+
+
+/// @brief Base class that provides the interface for continuous sampling
+/// of values in a grid.
+/// @details Since grids support only discrete voxel sampling, GridSampler
+/// must be used to sample arbitrary continuous points in (world or index) space.
+template<typename TreeOrAccessorType, typename SamplerType>
+class GridSampler
+{
+public:
+ typedef boost::shared_ptr<GridSampler> Ptr;
+ typedef typename TreeOrAccessorType::ValueType ValueType;
+
+ /// @param tree a tree to be sampled, or a ValueAccessor for the tree
+ /// @param transform is used when sampling world space locations.
+ /// (by default an identity transform is used)
+ explicit GridSampler(const TreeOrAccessorType& tree,
+ const math::Transform& transform = math::Transform()):
+ mTree(&tree), mTransform(transform) {}
+
+ ~GridSampler() {};
+
+ /// @brief Sample a point in index space in the grid.
+ /// @param x x-coordinate of point in index-coordinates of grid
+ /// @param y y-coordinate of point in index-coordinates of grid
+ /// @param z z-coordinate of point in index-coordinates of grid
+ template<typename RealType>
+ ValueType sampleVoxel(const RealType& x, const RealType& y, const RealType& z) const
+ {
+ return isSample(Vec3d(x,y,z));
+ }
+
+ /// @brief Sample in index space
+ /// @param ispoint the location in index space
+ ValueType isSample(const Vec3d& ispoint) const
+ {
+ ValueType result = zeroVal<ValueType>();
+ SamplerType::sample(*mTree, ispoint, result);
+ return result;
+ }
+
+ /// @brief Sample in world space
+ /// @param wspoint the location in world space
+ ValueType wsSample(const Vec3d& wspoint) const
+ {
+ ValueType result = zeroVal<ValueType>();
+ SamplerType::sample(*mTree, mTransform.worldToIndex(wspoint), result);
+ return result;
+ }
+
+private:
+ const TreeOrAccessorType* mTree;
+ const math::Transform mTransform;
+};
+
+
+////////////////////////////////////////
+
+
+namespace local_util {
+
+inline Vec3i
+floorVec3(const Vec3R& v)
+{
+ return Vec3i(int(std::floor(v(0))), int(std::floor(v(1))), int(std::floor(v(2))));
+}
+
+
+inline Vec3i
+ceilVec3(const Vec3R& v)
+{
+ return Vec3i(int(std::ceil(v(0))), int(std::ceil(v(1))), int(std::ceil(v(2))));
+}
+
+
+inline Vec3i
+roundVec3(const Vec3R& v)
+{
+ return Vec3i(int(::round(v(0))), int(::round(v(1))), int(::round(v(2))));
+}
+
+} // namespace local_util
+
+
+////////////////////////////////////////
+
+
+template<class TreeT>
+inline bool
+PointSampler::sample(const TreeT& inTree, const Vec3R& inCoord,
+ typename TreeT::ValueType& result)
+{
+ Vec3i inIdx = local_util::roundVec3(inCoord);
+ return inTree.probeValue(Coord(inIdx), result);
+}
+
+
+////////////////////////////////////////
+
+
+template<class ValueT, size_t N>
+inline ValueT
+BoxSampler::trilinearInterpolation(ValueT (& data)[N][N][N], const Vec3R& uvw)
+{
+ // Trilinear interpolation:
+ // The eight surrounding latice values are used to construct the result. \n
+ // result(x,y,z) =
+ // v000 (1-x)(1-y)(1-z) + v001 (1-x)(1-y)z + v010 (1-x)y(1-z) + v011 (1-x)yz
+ // + v100 x(1-y)(1-z) + v101 x(1-y)z + v110 xy(1-z) + v111 xyz
+
+ ValueT resultA, resultB;
+
+ resultA = data[0][0][0] + ValueT((data[0][0][1] - data[0][0][0]) * uvw[2]);
+ resultB = data[0][1][0] + ValueT((data[0][1][1] - data[0][1][0]) * uvw[2]);
+ ValueT result1 = resultA + ValueT((resultB-resultA) * uvw[1]);
+
+ resultA = data[1][0][0] + ValueT((data[1][0][1] - data[1][0][0]) * uvw[2]);
+ resultB = data[1][1][0] + ValueT((data[1][1][1] - data[1][1][0]) * uvw[2]);
+ ValueT result2 = resultA + ValueT((resultB - resultA) * uvw[1]);
+
+ return result1 + ValueT(uvw[0] * (result2 - result1));
+}
+
+
+template<class TreeT>
+inline bool
+BoxSampler::sample(const TreeT& inTree, const Vec3R& inCoord,
+ typename TreeT::ValueType& result)
+{
+ typedef typename TreeT::ValueType ValueT;
+
+ Vec3i inIdx = local_util::floorVec3(inCoord);
+ Vec3R uvw = inCoord - inIdx;
+
+ // Retrieve the values of the eight voxels surrounding the
+ // fractional source coordinates.
+ ValueT data[2][2][2];
+
+ bool hasActiveValues = false;
+ Coord ijk(inIdx);
+ hasActiveValues |= inTree.probeValue(ijk, data[0][0][0]); // i, j, k
+ ijk[2] += 1;
+ hasActiveValues |= inTree.probeValue(ijk, data[0][0][1]); // i, j, k + 1
+ ijk[1] += 1;
+ hasActiveValues |= inTree.probeValue(ijk, data[0][1][1]); // i, j+1, k + 1
+ ijk[2] = inIdx[2];
+ hasActiveValues |= inTree.probeValue(ijk, data[0][1][0]); // i, j+1, k
+ ijk[0] += 1;
+ ijk[1] = inIdx[1];
+ hasActiveValues |= inTree.probeValue(ijk, data[1][0][0]); // i+1, j, k
+ ijk[2] += 1;
+ hasActiveValues |= inTree.probeValue(ijk, data[1][0][1]); // i+1, j, k + 1
+ ijk[1] += 1;
+ hasActiveValues |= inTree.probeValue(ijk, data[1][1][1]); // i+1, j+1, k + 1
+ ijk[2] = inIdx[2];
+ hasActiveValues |= inTree.probeValue(ijk, data[1][1][0]); // i+1, j+1, k
+
+ result = trilinearInterpolation(data, uvw);
+ return hasActiveValues;
+}
+
+
+template<class TreeT>
+inline typename TreeT::ValueType
+BoxSampler::sample(const TreeT& inTree, const Vec3R& inCoord)
+{
+ typedef typename TreeT::ValueType ValueT;
+
+ Vec3i inIdx = local_util::floorVec3(inCoord);
+ Vec3R uvw = inCoord - inIdx;
+
+ // Retrieve the values of the eight voxels surrounding the
+ // fractional source coordinates.
+ ValueT data[2][2][2];
+
+ Coord ijk(inIdx);
+ data[0][0][0] = inTree.getValue(ijk); // i, j, k
+ ijk[2] += 1;
+ data[0][0][1] = inTree.getValue(ijk); // i, j, k + 1
+ ijk[1] += 1;
+ data[0][1][1] = inTree.getValue(ijk); // i, j+1, k + 1
+ ijk[2] = inIdx[2];
+ data[0][1][0] = inTree.getValue(ijk); // i, j+1, k
+ ijk[0] += 1;
+ ijk[1] = inIdx[1];
+ data[1][0][0] = inTree.getValue(ijk); // i+1, j, k
+ ijk[2] += 1;
+ data[1][0][1] = inTree.getValue(ijk); // i+1, j, k + 1
+ ijk[1] += 1;
+ data[1][1][1] = inTree.getValue(ijk); // i+1, j+1, k + 1
+ ijk[2] = inIdx[2];
+ data[1][1][0] = inTree.getValue(ijk); // i+1, j+1, k
+
+ return trilinearInterpolation(data, uvw);
+}
+
+
+////////////////////////////////////////
+
+
+template<class TreeT>
+inline bool
+QuadraticSampler::sample(const TreeT& inTree, const Vec3R& inCoord,
+ typename TreeT::ValueType& result)
+{
+ typedef typename TreeT::ValueType ValueT;
+
+ Vec3i
+ inIdx = local_util::floorVec3(inCoord),
+ inLoIdx = inIdx - Vec3i(1, 1, 1);
+ Vec3R frac = inCoord - inIdx;
+
+ // Retrieve the values of the 27 voxels surrounding the
+ // fractional source coordinates.
+ bool active = false;
+ ValueT v[3][3][3];
+ for (int dx = 0, ix = inLoIdx.x(); dx < 3; ++dx, ++ix) {
+ for (int dy = 0, iy = inLoIdx.y(); dy < 3; ++dy, ++iy) {
+ for (int dz = 0, iz = inLoIdx.z(); dz < 3; ++dz, ++iz) {
+ if (inTree.probeValue(Coord(ix, iy, iz), v[dx][dy][dz])) {
+ active = true;
+ }
+ }
+ }
+ }
+
+ /// @todo For vector types, interpolate over each component independently.
+ ValueT vx[3];
+ for (int dx = 0; dx < 3; ++dx) {
+ ValueT vy[3];
+ for (int dy = 0; dy < 3; ++dy) {
+ // Fit a parabola to three contiguous samples in z
+ // (at z=-1, z=0 and z=1), then evaluate the parabola at z',
+ // where z' is the fractional part of inCoord.z, i.e.,
+ // inCoord.z - inIdx.z. The coefficients come from solving
+ //
+ // | (-1)^2 -1 1 || a | | v0 |
+ // | 0 0 1 || b | = | v1 |
+ // | 1^2 1 1 || c | | v2 |
+ //
+ // for a, b and c.
+ const ValueT* vz = &v[dx][dy][0];
+ const ValueT
+ az = static_cast<ValueT>(0.5 * (vz[0] + vz[2]) - vz[1]),
+ bz = static_cast<ValueT>(0.5 * (vz[2] - vz[0])),
+ cz = static_cast<ValueT>(vz[1]);
+ vy[dy] = static_cast<ValueT>(frac.z() * (frac.z() * az + bz) + cz);
+ }
+ // Fit a parabola to three interpolated samples in y, then
+ // evaluate the parabola at y', where y' is the fractional
+ // part of inCoord.y.
+ const ValueT
+ ay = static_cast<ValueT>(0.5 * (vy[0] + vy[2]) - vy[1]),
+ by = static_cast<ValueT>(0.5 * (vy[2] - vy[0])),
+ cy = static_cast<ValueT>(vy[1]);
+ vx[dx] = static_cast<ValueT>(frac.y() * (frac.y() * ay + by) + cy);
+ }
+ // Fit a parabola to three interpolated samples in x, then
+ // evaluate the parabola at the fractional part of inCoord.x.
+ const ValueT
+ ax = static_cast<ValueT>(0.5 * (vx[0] + vx[2]) - vx[1]),
+ bx = static_cast<ValueT>(0.5 * (vx[2] - vx[0])),
+ cx = static_cast<ValueT>(vx[1]);
+ result = static_cast<ValueT>(frac.x() * (frac.x() * ax + bx) + cx);
+
+ return active;
+}
+
+
+////////////////////////////////////////
+
+
+template<class TreeT>
+inline bool
+StaggeredPointSampler::sample(const TreeT& inTree, const Vec3R& inCoord,
+ typename TreeT::ValueType& result)
+{
+ typedef typename TreeT::ValueType ValueType;
+
+ ValueType tempX, tempY, tempZ;
+ bool active = false;
+
+ active = PointSampler::sample<TreeT>(inTree, inCoord + Vec3R(0.5, 0, 0), tempX) || active;
+ active = PointSampler::sample<TreeT>(inTree, inCoord + Vec3R(0, 0.5, 0), tempY) || active;
+ active = PointSampler::sample<TreeT>(inTree, inCoord + Vec3R(0, 0, 0.5), tempZ) || active;
+
+ result.x() = tempX.x();
+ result.y() = tempY.y();
+ result.z() = tempZ.z();
+
+ return active;
+}
+
+
+////////////////////////////////////////
+
+
+template<class TreeT>
+inline bool
+StaggeredBoxSampler::sample(const TreeT& inTree, const Vec3R& inCoord,
+ typename TreeT::ValueType& result)
+{
+ typedef typename TreeT::ValueType ValueType;
+
+ ValueType tempX, tempY, tempZ;
+ tempX = tempY = tempZ = zeroVal<ValueType>();
+ bool active = false;
+
+ active = BoxSampler::sample<TreeT>(inTree, inCoord + Vec3R(0.5, 0, 0), tempX) || active;
+ active = BoxSampler::sample<TreeT>(inTree, inCoord + Vec3R(0, 0.5, 0), tempY) || active;
+ active = BoxSampler::sample<TreeT>(inTree, inCoord + Vec3R(0, 0, 0.5), tempZ) || active;
+
+ result.x() = tempX.x();
+ result.y() = tempY.y();
+ result.z() = tempZ.z();
+
+ return active;
+}
+
+
+////////////////////////////////////////
+
+
+template<class TreeT>
+inline bool
+StaggeredQuadraticSampler::sample(const TreeT& inTree, const Vec3R& inCoord,
+ typename TreeT::ValueType& result)
+{
+ typedef typename TreeT::ValueType ValueType;
+
+ ValueType tempX, tempY, tempZ;
+ bool active = false;
+
+ active = QuadraticSampler::sample<TreeT>(inTree, inCoord + Vec3R(0.5, 0, 0), tempX) || active;
+ active = QuadraticSampler::sample<TreeT>(inTree, inCoord + Vec3R(0, 0.5, 0), tempY) || active;
+ active = QuadraticSampler::sample<TreeT>(inTree, inCoord + Vec3R(0, 0, 0.5), tempZ) || active;
+
+ result.x() = tempX.x();
+ result.y() = tempY.y();
+ result.z() = tempZ.z();
+
+ return active;
+}
+
+} // namespace tools
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_TOOLS_INTERPOLATION_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/tools/LevelSetAdvect.h b/extern/openvdb/internal/openvdb/tools/LevelSetAdvect.h
new file mode 100644
index 00000000000..0782c20ef19
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/tools/LevelSetAdvect.h
@@ -0,0 +1,712 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+//
+///////////////////////////////////////////////////////////////////////////
+//
+/// @author Ken Museth
+///
+/// @file LevelSetAdvect.h
+///
+/// @brief Hyperbolic advection of narrow-band level sets
+
+#ifndef OPENVDB_TOOLS_LEVEL_SET_ADVECT_HAS_BEEN_INCLUDED
+#define OPENVDB_TOOLS_LEVEL_SET_ADVECT_HAS_BEEN_INCLUDED
+
+#include "LevelSetTracker.h"
+#include "Interpolation.h" // for BoxSampler, etc.
+#include <openvdb/math/FiniteDifference.h>
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace tools {
+
+/// Below are two simple wrapper classes for advection velocity fields
+/// DiscreteField wraps a velocity grid and EnrightField is mostly
+/// intended for debugging (it's an analytical divergence free and
+/// periodic field). They both share the same API required by the
+/// LevelSetAdvection class defined below. Thus, any class with this
+/// API should work with LevelSetAdvection.
+
+/// Note the Field wrapper classes below always assume the velocity
+/// is represented in the world-frame of reference. For DiscreteField
+/// this implies the input grid must contain velocities in world
+/// coordinates.
+
+/// @brief Thin wrapper class for a velocity grid
+/// @note Consider replacing BoxSampler with StaggeredBoxSampler
+template <typename VelGridT, typename Interpolator = BoxSampler>
+class DiscreteField
+{
+public:
+ typedef typename VelGridT::ConstAccessor AccessorType;
+ typedef typename VelGridT::ValueType VectorType;
+ typedef typename VectorType::ValueType ScalarType;
+
+ DiscreteField(const VelGridT &vel):
+ mAccessor(vel.getConstAccessor()),
+ mTransform(vel.transform()) {}
+
+ /// @return const reference to the transfrom between world and index space
+ /// @note Use this method to determine if a client grid is
+ /// aligned with the coordinate space of the velocity grid.
+ const math::Transform& transform() const { return mTransform; }
+
+ /// @return the interpolated velocity at the world space position xyz
+ inline VectorType operator() (const Vec3d& xyz, ScalarType) const;
+
+ /// @return the velocity at the coordinate space position ijk
+ inline VectorType operator() (const Coord& ijk, ScalarType) const
+ {
+ return mAccessor.getValue(ijk);
+ }
+
+private:
+ const AccessorType mAccessor;
+ const math::Transform& mTransform;
+
+}; // end of DiscreteField
+
+/// @brief Analytical, divergence-free and periodic vecloity field
+/// @note Primarily intended for debugging!
+template <typename ScalarT = float>
+class EnrightField
+{
+public:
+ typedef ScalarT ScalarType;
+ typedef math::Vec3<ScalarT> VectorType;
+
+ EnrightField() {}
+
+ /// @return const reference to the identity transfrom between world and index space
+ /// @note Use this method to determine if a client grid is
+ /// aligned with the coordinate space of this velocity field
+ const math::Transform& transform() const { return mTransform; }
+
+ /// @return the velocity in world units, evaluated at the world
+ /// position xyz and at the specified time
+ inline VectorType operator() (const Vec3d& xyz, ScalarType time) const;
+
+ /// @return the velocity at the coordinate space position ijk
+ inline VectorType operator() (const Coord& ijk, ScalarType time) const
+ {
+ return (*this)(ijk.asVec3d(), time);
+ }
+
+private:
+ const math::Transform mTransform;//identity transform between world and index space
+
+}; // end of EnrightField
+
+/// @brief Hyperbolic advection of narrow-band level sets in an
+/// external velocity field
+///
+/// The @c FieldType template argument below refers to any functor
+/// with the following interface (see tools/VelocityFields.h
+/// for examples):
+///
+/// @code
+/// class VelocityField {
+/// ...
+/// public:
+/// openvdb::VectorType operator() (const openvdb::Coord& xyz, ScalarType time) const;
+/// ...
+/// };
+/// @endcode
+///
+/// @note The functor method returns the velocity field at coordinate
+/// position xyz of the advection grid, and for the specified
+/// time. Note that since the velocity is returned in the local
+/// coordinate space of the grid that is being advected, the functor
+/// typically depends on the transformation of that grid. This design
+/// is chosen for performance reasons.
+///
+/// The @c InterruptType template argument below refers to any class
+/// with the following interface:
+/// @code
+/// class Interrupter {
+/// ...
+/// public:
+/// void start(const char* name = NULL)// called when computations begin
+/// void end() // called when computations end
+/// bool wasInterrupted(int percent=-1)// return true to break computation
+///};
+/// @endcode
+///
+/// @note If no template argument is provided for this InterruptType
+/// the util::NullInterrupter is used which implies that all
+/// interrupter calls are no-ops (i.e. incurs no computational overhead).
+///
+
+template<typename GridT,
+ typename FieldT = EnrightField<typename GridT::ValueType>,
+ typename InterruptT = util::NullInterrupter>
+class LevelSetAdvection
+{
+public:
+ typedef GridT GridType;
+ typedef LevelSetTracker<GridT, InterruptT> TrackerT;
+ typedef typename TrackerT::RangeType RangeType;
+ typedef typename TrackerT::LeafType LeafType;
+ typedef typename TrackerT::BufferType BufferType;
+ typedef typename TrackerT::ValueType ScalarType;
+ typedef typename FieldT::VectorType VectorType;
+
+ /// Main constructor
+ LevelSetAdvection(GridT& grid, const FieldT& field, InterruptT* interrupt = NULL):
+ mTracker(grid, interrupt), mField(field),
+ mSpatialScheme(math::HJWENO5_BIAS),
+ mTemporalScheme(math::TVD_RK2) {}
+
+ virtual ~LevelSetAdvection() {};
+
+ /// @return the spatial finite difference scheme
+ math::BiasedGradientScheme getSpatialScheme() const { return mSpatialScheme; }
+ /// @brief Set the spatial finite difference scheme
+ void setSpatialScheme(math::BiasedGradientScheme scheme) { mSpatialScheme = scheme; }
+
+ /// @return the temporal integration scheme
+ math::TemporalIntegrationScheme getTemporalScheme() const { return mTemporalScheme; }
+ /// @brief Set the spatial finite difference scheme
+ void setTemporalScheme(math::TemporalIntegrationScheme scheme) { mTemporalScheme = scheme; }
+
+ /// @return the spatial finite difference scheme
+ math::BiasedGradientScheme getTrackerSpatialScheme() const { return mTracker.getSpatialScheme(); }
+ /// @brief Set the spatial finite difference scheme
+ void setTrackerSpatialScheme(math::BiasedGradientScheme scheme) { mTracker.setSpatialScheme(scheme); }
+
+ /// @return the temporal integration scheme
+ math::TemporalIntegrationScheme getTrackerTemporalScheme() const { return mTracker.getTemporalScheme(); }
+ /// @brief Set the spatial finite difference scheme
+ void setTrackerTemporalScheme(math::TemporalIntegrationScheme scheme) { mTracker.setTemporalScheme(scheme); }
+
+ /// @return The number of normalizations performed per track or
+ /// normalize call.
+ int getNormCount() const { return mTracker.getNormCount(); }
+ /// @brief Set the number of normalizations performed per track or
+ /// normalize call.
+ void setNormCount(int n) { mTracker.setNormCount(n); }
+
+ /// @return the grain-size used for multi-threading
+ int getGrainSize() const { return mTracker.getGrainSize(); }
+ /// @brief Set the grain-size used for multi-threading.
+ /// @note A grainsize of 0 or less disables multi-threading!
+ void setGrainSize(int grainsize) { mTracker.setGrainSize(grainsize); }
+
+ /// Advect the level set from it's current time, time0, to it's
+ /// final time, time1. If time0>time1 backward advection is performed.
+ ///
+ /// @return number of CFL iterations used to advect from time0 to time1
+ size_t advect(ScalarType time0, ScalarType time1);
+
+private:
+
+ // This templated private class implements all the level set magic.
+ template<typename MapT, math::BiasedGradientScheme SpatialScheme,
+ math::TemporalIntegrationScheme TemporalScheme>
+ class LevelSetAdvect
+ {
+ public:
+ /// Main constructor
+ LevelSetAdvect(LevelSetAdvection& parent);
+ /// Shallow copy constructor called by tbb::parallel_for() threads
+ LevelSetAdvect(const LevelSetAdvect& other);
+ /// Shallow copy constructor called by tbb::parallel_reduce() threads
+ LevelSetAdvect(LevelSetAdvect& other, tbb::split);
+ /// destructor
+ virtual ~LevelSetAdvect() {if (mIsMaster) this->clearField();};
+ /// Advect the level set from it's current time, time0, to it's final time, time1.
+ /// @return number of CFL iterations
+ size_t advect(ScalarType time0, ScalarType time1);
+ /// Used internally by tbb::parallel_for()
+ void operator()(const RangeType& r) const
+ {
+ if (mTask) mTask(const_cast<LevelSetAdvect*>(this), r);
+ else OPENVDB_THROW(ValueError, "task is undefined - don\'t call this method directly");
+ }
+ /// Used internally by tbb::parallel_reduce()
+ void operator()(const RangeType& r)
+ {
+ if (mTask) mTask(this, r);
+ else OPENVDB_THROW(ValueError, "task is undefined - don\'t call this method directly");
+ }
+ /// This is only called by tbb::parallel_reduce() threads
+ void join(const LevelSetAdvect& other) { mMaxAbsV = math::Max(mMaxAbsV, other.mMaxAbsV); }
+ private:
+ typedef typename boost::function<void (LevelSetAdvect*, const RangeType&)> FuncType;
+ LevelSetAdvection& mParent;
+ VectorType** mVec;
+ const ScalarType mMinAbsV;
+ ScalarType mMaxAbsV;
+ const MapT& mMap;
+ FuncType mTask;
+ const bool mIsMaster;
+ /// Enum to defeing the type of multi-threading
+ enum ThreadingMode { PARALLEL_FOR, PARALLEL_REDUCE }; // for internal use
+ // method calling tbb
+ void cook(ThreadingMode mode, size_t swapBuffer = 0);
+ /// Sample field and return the CFT time step
+ typename GridT::ValueType sampleField(ScalarType time0, ScalarType time1);
+ void clearField();
+ void sampleXformedField(const RangeType& r, ScalarType time0, ScalarType time1);
+ void sampleAlignedField(const RangeType& r, ScalarType time0, ScalarType time1);
+ // Forward Euler advection steps: Phi(result) = Phi(0) - dt * V.Grad(0);
+ void euler1(const RangeType& r, ScalarType dt, Index resultBuffer);
+ // Convex combination of Phi and a forward Euler advection steps:
+ // Phi(result) = alpha * Phi(phi) + (1-alpha) * (Phi(0) - dt * V.Grad(0));
+ void euler2(const RangeType& r, ScalarType dt, ScalarType alpha, Index phiBuffer, Index resultBuffer);
+ }; // end of private LevelSetAdvect class
+
+ template<math::BiasedGradientScheme SpatialScheme>
+ size_t advect1(ScalarType time0, ScalarType time1);
+
+ template<math::BiasedGradientScheme SpatialScheme,
+ math::TemporalIntegrationScheme TemporalScheme>
+ size_t advect2(ScalarType time0, ScalarType time1);
+
+ template<math::BiasedGradientScheme SpatialScheme,
+ math::TemporalIntegrationScheme TemporalScheme,
+ typename MapType>
+ size_t advect3(ScalarType time0, ScalarType time1);
+
+ TrackerT mTracker;
+ //each thread needs a deep copy of the field since it might contain a ValueAccessor
+ const FieldT mField;
+ math::BiasedGradientScheme mSpatialScheme;
+ math::TemporalIntegrationScheme mTemporalScheme;
+
+ // disallow copy by assignment
+ void operator=(const LevelSetAdvection& other) {}
+
+};//end of LevelSetAdvection
+
+template<typename GridT, typename FieldT, typename InterruptT>
+inline size_t
+LevelSetAdvection<GridT, FieldT, InterruptT>::advect(ScalarType time0, ScalarType time1)
+{
+ switch (mSpatialScheme) {
+ case math::FIRST_BIAS:
+ return this->advect1<math::FIRST_BIAS >(time0, time1);
+ case math::SECOND_BIAS:
+ return this->advect1<math::SECOND_BIAS >(time0, time1);
+ case math::THIRD_BIAS:
+ return this->advect1<math::THIRD_BIAS >(time0, time1);
+ case math::WENO5_BIAS:
+ return this->advect1<math::WENO5_BIAS >(time0, time1);
+ case math::HJWENO5_BIAS:
+ return this->advect1<math::HJWENO5_BIAS>(time0, time1);
+ default:
+ OPENVDB_THROW(ValueError, "Spatial difference scheme not supported!");
+ }
+ return 0;
+}
+
+template<typename GridT, typename FieldT, typename InterruptT>
+template<math::BiasedGradientScheme SpatialScheme>
+inline size_t
+LevelSetAdvection<GridT, FieldT, InterruptT>::advect1(ScalarType time0, ScalarType time1)
+{
+ switch (mTemporalScheme) {
+ case math::TVD_RK1:
+ return this->advect2<SpatialScheme, math::TVD_RK1>(time0, time1);
+ case math::TVD_RK2:
+ return this->advect2<SpatialScheme, math::TVD_RK2>(time0, time1);
+ case math::TVD_RK3:
+ return this->advect2<SpatialScheme, math::TVD_RK3>(time0, time1);
+ default:
+ OPENVDB_THROW(ValueError, "Temporal integration scheme not supported!");
+ }
+ return 0;
+}
+
+template<typename GridT, typename FieldT, typename InterruptT>
+template<math::BiasedGradientScheme SpatialScheme,
+ math::TemporalIntegrationScheme TemporalScheme>
+inline size_t
+LevelSetAdvection<GridT, FieldT, InterruptT>::advect2(ScalarType time0, ScalarType time1)
+{
+ const math::Transform& trans = mTracker.grid().transform();
+ if (trans.mapType() == math::UniformScaleMap::mapType()) {
+ return this->advect3<SpatialScheme, TemporalScheme, math::UniformScaleMap>(time0, time1);
+ } else if (trans.mapType() == math::UniformScaleTranslateMap::mapType()) {
+ return this->advect3<SpatialScheme, TemporalScheme, math::UniformScaleTranslateMap>(time0, time1);
+ } else if (trans.mapType() == math::UnitaryMap::mapType()) {
+ return this->advect3<SpatialScheme, TemporalScheme, math::UnitaryMap >(time0, time1);
+ } else if (trans.mapType() == math::TranslationMap::mapType()) {
+ return this->advect3<SpatialScheme, TemporalScheme, math::TranslationMap>(time0, time1);
+ } else {
+ OPENVDB_THROW(ValueError, "MapType not supported!");
+ }
+ return 0;
+}
+
+template<typename GridT, typename FieldT, typename InterruptT>
+template<math::BiasedGradientScheme SpatialScheme,
+ math::TemporalIntegrationScheme TemporalScheme,
+ typename MapT>
+inline size_t
+LevelSetAdvection<GridT, FieldT, InterruptT>::advect3(ScalarType time0, ScalarType time1)
+{
+ LevelSetAdvect<MapT, SpatialScheme, TemporalScheme> tmp(*this);
+ return tmp.advect(time0, time1);
+}
+
+template <typename VelGridT, typename Interpolator>
+inline typename VelGridT::ValueType
+DiscreteField<VelGridT,Interpolator>::operator() (const Vec3d& xyz, ScalarType time) const
+{
+ const VectorType coord = mTransform.worldToIndex(xyz);
+ VectorType result;
+ Interpolator::template sample<AccessorType>(mAccessor, coord, result);
+ return result;
+}
+
+template <typename ScalarT>
+inline math::Vec3<ScalarT>
+EnrightField<ScalarT>::operator() (const Vec3d& xyz, ScalarType time) const
+{
+ static const ScalarT pi = ScalarT(3.1415926535897931), phase = pi / ScalarT(3.0);
+ const ScalarT Px = pi * ScalarT(xyz[0]), Py = pi * ScalarT(xyz[1]), Pz = pi * ScalarT(xyz[2]);
+ const ScalarT tr = cos(ScalarT(time) * phase);
+ const ScalarT a = sin(ScalarT(2.0)*Py);
+ const ScalarT b = -sin(ScalarT(2.0)*Px);
+ const ScalarT c = sin(ScalarT(2.0)*Pz);
+ return math::Vec3<ScalarT>(
+ tr * ( ScalarT(2) * math::Pow2(sin(Px)) * a * c ),
+ tr * ( b * math::Pow2(sin(Py)) * c ),
+ tr * ( b * a * math::Pow2(sin(Pz)) ));
+}
+
+
+///////////////////////////////////////////////////////////////////////
+
+
+template<typename GridT, typename FieldT, typename InterruptT>
+template <typename MapT, math::BiasedGradientScheme SpatialScheme,
+ math::TemporalIntegrationScheme TemporalScheme>
+inline
+LevelSetAdvection<GridT, FieldT, InterruptT>::
+LevelSetAdvect<MapT, SpatialScheme, TemporalScheme>::
+LevelSetAdvect(LevelSetAdvection& parent):
+ mParent(parent),
+ mVec(NULL),
+ mMinAbsV(1e-6),
+ mMap(*(parent.mTracker.grid().transform().template constMap<MapT>())),
+ mTask(0),
+ mIsMaster(true)
+{
+}
+
+template<typename GridT, typename FieldT, typename InterruptT>
+template <typename MapT, math::BiasedGradientScheme SpatialScheme,
+ math::TemporalIntegrationScheme TemporalScheme>
+inline
+LevelSetAdvection<GridT, FieldT, InterruptT>::
+LevelSetAdvect<MapT, SpatialScheme, TemporalScheme>::
+LevelSetAdvect(const LevelSetAdvect& other):
+ mParent(other.mParent),
+ mVec(other.mVec),
+ mMinAbsV(other.mMinAbsV),
+ mMaxAbsV(other.mMaxAbsV),
+ mMap(other.mMap),
+ mTask(other.mTask),
+ mIsMaster(false)
+{
+}
+
+template<typename GridT, typename FieldT, typename InterruptT>
+template <typename MapT, math::BiasedGradientScheme SpatialScheme,
+ math::TemporalIntegrationScheme TemporalScheme>
+inline
+LevelSetAdvection<GridT, FieldT, InterruptT>::
+LevelSetAdvect<MapT, SpatialScheme, TemporalScheme>::
+LevelSetAdvect(LevelSetAdvect& other, tbb::split):
+ mParent(other.mParent),
+ mVec(other.mVec),
+ mMinAbsV(other.mMinAbsV),
+ mMaxAbsV(other.mMaxAbsV),
+ mMap(other.mMap),
+ mTask(other.mTask),
+ mIsMaster(false)
+{
+}
+
+template<typename GridT, typename FieldT, typename InterruptT>
+template <typename MapT, math::BiasedGradientScheme SpatialScheme,
+ math::TemporalIntegrationScheme TemporalScheme>
+inline size_t
+LevelSetAdvection<GridT, FieldT, InterruptT>::
+LevelSetAdvect<MapT, SpatialScheme, TemporalScheme>::
+advect(ScalarType time0, ScalarType time1)
+{
+ size_t countCFL = 0;
+ if ( math::isZero(time0 - time1) ) return countCFL;
+ const bool isForward = time0 < time1;
+ while ((isForward ? time0<time1 : time0>time1) && mParent.mTracker.checkInterrupter()) {
+ /// Make sure we have enough temporal auxiliary buffers
+ mParent.mTracker.mLeafs->rebuildAuxBuffers(TemporalScheme == math::TVD_RK3 ? 2 : 1);
+
+ const ScalarType dt = this->sampleField(time0, time1);
+ if ( math::isZero(dt) ) break;//V is essentially zero so terminate
+
+ switch(TemporalScheme) {//switch is resolved at compile-time
+ case math::TVD_RK1:
+ // Perform one explicit Euler step: t1 = t0 + dt
+ // Phi_t1(1) = Phi_t0(0) - dt * VdotG_t0(0)
+ mTask = boost::bind(&LevelSetAdvect::euler1, _1, _2, dt, /*result=*/1);
+ // Cook and swap buffer 0 and 1 such that Phi_t1(0) and Phi_t0(1)
+ this->cook(PARALLEL_FOR, 1);
+ break;
+ case math::TVD_RK2:
+ // Perform one explicit Euler step: t1 = t0 + dt
+ // Phi_t1(1) = Phi_t0(0) - dt * VdotG_t0(0)
+ mTask = boost::bind(&LevelSetAdvect::euler1, _1, _2, dt, /*result=*/1);
+ // Cook and swap buffer 0 and 1 such that Phi_t1(0) and Phi_t0(1)
+ this->cook(PARALLEL_FOR, 1);
+
+ // Convex combine explict Euler step: t2 = t0 + dt
+ // Phi_t2(1) = 1/2 * Phi_t0(1) + 1/2 * (Phi_t1(0) - dt * V.Grad_t1(0))
+ mTask = boost::bind(&LevelSetAdvect::euler2, _1, _2, dt, ScalarType(0.5), /*phi=*/1, /*result=*/1);
+ // Cook and swap buffer 0 and 1 such that Phi_t2(0) and Phi_t1(1)
+ this->cook(PARALLEL_FOR, 1);
+ break;
+ case math::TVD_RK3:
+ // Perform one explicit Euler step: t1 = t0 + dt
+ // Phi_t1(1) = Phi_t0(0) - dt * VdotG_t0(0)
+ mTask = boost::bind(&LevelSetAdvect::euler1, _1, _2, dt, /*result=*/1);
+ // Cook and swap buffer 0 and 1 such that Phi_t1(0) and Phi_t0(1)
+ this->cook(PARALLEL_FOR, 1);
+
+ // Convex combine explict Euler step: t2 = t0 + dt/2
+ // Phi_t2(2) = 3/4 * Phi_t0(1) + 1/4 * (Phi_t1(0) - dt * V.Grad_t1(0))
+ mTask = boost::bind(&LevelSetAdvect::euler2, _1, _2, dt, ScalarType(0.75), /*phi=*/1, /*result=*/2);
+ // Cook and swap buffer 0 and 2 such that Phi_t2(0) and Phi_t1(2)
+ this->cook(PARALLEL_FOR, 2);
+
+ // Convex combine explict Euler step: t3 = t0 + dt
+ // Phi_t3(2) = 1/3 * Phi_t0(1) + 2/3 * (Phi_t2(0) - dt * V.Grad_t2(0)
+ mTask = boost::bind(&LevelSetAdvect::euler2, _1, _2, dt, ScalarType(1.0/3.0), /*phi=*/1, /*result=*/2);
+ // Cook and swap buffer 0 and 2 such that Phi_t3(0) and Phi_t2(2)
+ this->cook(PARALLEL_FOR, 2);
+ break;
+ default:
+ OPENVDB_THROW(ValueError, "Temporal integration scheme not supported!");
+ }
+ time0 += isForward ? dt : -dt;
+ ++countCFL;
+ mParent.mTracker.mLeafs->removeAuxBuffers();
+ this->clearField();
+ /// Track the narrow band
+ mParent.mTracker.track();
+ }//end wile-loop over time
+ return countCFL;//number of CLF propagation steps
+}
+
+template<typename GridT, typename FieldT, typename InterruptT>
+template<typename MapT, math::BiasedGradientScheme SpatialScheme,
+ math::TemporalIntegrationScheme TemporalScheme>
+inline typename GridT::ValueType
+LevelSetAdvection<GridT, FieldT, InterruptT>::
+LevelSetAdvect<MapT, SpatialScheme, TemporalScheme>::
+sampleField(ScalarType time0, ScalarType time1)
+{
+ mMaxAbsV = mMinAbsV;
+ const size_t leafCount = mParent.mTracker.mLeafs->leafCount();
+ if (leafCount==0) return ScalarType(0.0);
+ mVec = new VectorType*[leafCount];
+ if (mParent.mField.transform() == mParent.mTracker.mGrid.transform()) {
+ mTask = boost::bind(&LevelSetAdvect::sampleAlignedField, _1, _2, time0, time1);
+ } else {
+ mTask = boost::bind(&LevelSetAdvect::sampleXformedField, _1, _2, time0, time1);
+ }
+ this->cook(PARALLEL_REDUCE);
+ if (math::isExactlyEqual(mMinAbsV, mMaxAbsV)) return ScalarType(0.0);//V is essentially zero
+ static const ScalarType CFL = (TemporalScheme == math::TVD_RK1 ? ScalarType(0.3) :
+ TemporalScheme == math::TVD_RK2 ? ScalarType(0.9) :
+ ScalarType(1.0))/math::Sqrt(ScalarType(3.0));
+ const ScalarType dt = math::Abs(time1 - time0), dx = mParent.mTracker.voxelSize();
+ return math::Min(dt, ScalarType(CFL*dx/math::Sqrt(mMaxAbsV)));
+}
+
+template<typename GridT, typename FieldT, typename InterruptT>
+template <typename MapT, math::BiasedGradientScheme SpatialScheme,
+ math::TemporalIntegrationScheme TemporalScheme>
+inline void
+LevelSetAdvection<GridT, FieldT, InterruptT>::
+LevelSetAdvect<MapT, SpatialScheme, TemporalScheme>::
+sampleXformedField(const RangeType& range, ScalarType time0, ScalarType time1)
+{
+ const bool isForward = time0 < time1;
+ typedef typename LeafType::ValueOnCIter VoxelIterT;
+ const MapT& map = mMap;
+ mParent.mTracker.checkInterrupter();
+ for (size_t n=range.begin(), e=range.end(); n != e; ++n) {
+ const LeafType& leaf = mParent.mTracker.mLeafs->leaf(n);
+ VectorType* vec = new VectorType[leaf.onVoxelCount()];
+ int m = 0;
+ for (VoxelIterT iter = leaf.cbeginValueOn(); iter; ++iter, ++m) {
+ const VectorType V = mParent.mField(map.applyMap(iter.getCoord().asVec3d()), time0);
+ mMaxAbsV = math::Max(mMaxAbsV, ScalarType(math::Pow2(V[0])+math::Pow2(V[1])+math::Pow2(V[2])));
+ vec[m] = isForward ? V : -V;
+ }
+ mVec[n] = vec;
+ }
+}
+
+template<typename GridT, typename FieldT, typename InterruptT>
+template <typename MapT, math::BiasedGradientScheme SpatialScheme,
+ math::TemporalIntegrationScheme TemporalScheme>
+inline void
+LevelSetAdvection<GridT, FieldT, InterruptT>::
+LevelSetAdvect<MapT, SpatialScheme, TemporalScheme>::
+sampleAlignedField(const RangeType& range, ScalarType time0, ScalarType time1)
+{
+ const bool isForward = time0 < time1;
+ typedef typename LeafType::ValueOnCIter VoxelIterT;
+ mParent.mTracker.checkInterrupter();
+ for (size_t n=range.begin(), e=range.end(); n != e; ++n) {
+ const LeafType& leaf = mParent.mTracker.mLeafs->leaf(n);
+ VectorType* vec = new VectorType[leaf.onVoxelCount()];
+ int m = 0;
+ for (VoxelIterT iter = leaf.cbeginValueOn(); iter; ++iter, ++m) {
+ const VectorType V = mParent.mField(iter.getCoord(), time0);
+ mMaxAbsV = math::Max(mMaxAbsV, ScalarType(math::Pow2(V[0])+math::Pow2(V[1])+math::Pow2(V[2])));
+ vec[m] = isForward ? V : -V;
+ }
+ mVec[n] = vec;
+ }
+}
+
+template<typename GridT, typename FieldT, typename InterruptT>
+template <typename MapT, math::BiasedGradientScheme SpatialScheme,
+ math::TemporalIntegrationScheme TemporalScheme>
+inline void
+LevelSetAdvection<GridT, FieldT, InterruptT>::
+LevelSetAdvect<MapT, SpatialScheme, TemporalScheme>::
+clearField()
+{
+ if (mVec == NULL) return;
+ for (size_t n=0, e=mParent.mTracker.mLeafs->leafCount(); n<e; ++n) delete [] mVec[n];
+ delete [] mVec;
+ mVec = NULL;
+}
+
+template<typename GridT, typename FieldT, typename InterruptT>
+template <typename MapT, math::BiasedGradientScheme SpatialScheme,
+ math::TemporalIntegrationScheme TemporalScheme>
+inline void
+LevelSetAdvection<GridT, FieldT, InterruptT>::
+LevelSetAdvect<MapT, SpatialScheme, TemporalScheme>::
+cook(ThreadingMode mode, size_t swapBuffer)
+{
+ mParent.mTracker.startInterrupter("Advecting level set");
+
+ if (mParent.mTracker.getGrainSize()==0) {
+ (*this)(mParent.mTracker.mLeafs->getRange());
+ } else if (mode == PARALLEL_FOR) {
+ tbb::parallel_for(mParent.mTracker.mLeafs->getRange(mParent.mTracker.getGrainSize()), *this);
+ } else if (mode == PARALLEL_REDUCE) {
+ tbb::parallel_reduce(mParent.mTracker.mLeafs->getRange(mParent.mTracker.getGrainSize()), *this);
+ } else {
+ throw std::runtime_error("Undefined threading mode");
+ }
+
+ mParent.mTracker.mLeafs->swapLeafBuffer(swapBuffer, mParent.mTracker.getGrainSize()==0);
+
+ mParent.mTracker.endInterrupter();
+}
+
+// Forward Euler advection steps:
+// Phi(result) = Phi(0) - dt * V.Grad(0);
+template<typename GridT, typename FieldT, typename InterruptT>
+template <typename MapT, math::BiasedGradientScheme SpatialScheme,
+ math::TemporalIntegrationScheme TemporalScheme>
+inline void
+LevelSetAdvection<GridT, FieldT, InterruptT>::
+LevelSetAdvect<MapT, SpatialScheme, TemporalScheme>::
+euler1(const RangeType& range, ScalarType dt, Index resultBuffer)
+{
+ typedef math::BIAS_SCHEME<SpatialScheme> Scheme;
+ typedef typename Scheme::template ISStencil<GridType>::StencilType Stencil;
+ typedef typename LeafType::ValueOnCIter VoxelIterT;
+ mParent.mTracker.checkInterrupter();
+ const MapT& map = mMap;
+ Stencil stencil(mParent.mTracker.mGrid);
+ for (size_t n=range.begin(), e=range.end(); n != e; ++n) {
+ BufferType& result = mParent.mTracker.mLeafs->getBuffer(n, resultBuffer);
+ const VectorType* vec = mVec[n];
+ int m=0;
+ for (VoxelIterT iter = mParent.mTracker.mLeafs->leaf(n).cbeginValueOn(); iter; ++iter, ++m) {
+ stencil.moveTo(iter);
+ const VectorType V = vec[m], G = math::GradientBiased<MapT, SpatialScheme>::result(map, stencil, V);
+ result.setValue(iter.pos(), *iter - dt * V.dot(G));
+ }
+ }
+}
+
+// Convex combination of Phi and a forward Euler advection steps:
+// Phi(result) = alpha * Phi(phi) + (1-alpha) * (Phi(0) - dt * V.Grad(0));
+template<typename GridT, typename FieldT, typename InterruptT>
+template <typename MapT, math::BiasedGradientScheme SpatialScheme,
+ math::TemporalIntegrationScheme TemporalScheme>
+inline void
+LevelSetAdvection<GridT, FieldT, InterruptT>::
+LevelSetAdvect<MapT, SpatialScheme, TemporalScheme>::
+euler2(const RangeType& range, ScalarType dt, ScalarType alpha, Index phiBuffer, Index resultBuffer)
+{
+ typedef math::BIAS_SCHEME<SpatialScheme> Scheme;
+ typedef typename Scheme::template ISStencil<GridType>::StencilType Stencil;
+ typedef typename LeafType::ValueOnCIter VoxelIterT;
+ mParent.mTracker.checkInterrupter();
+ const MapT& map = mMap;
+ const ScalarType beta = ScalarType(1.0) - alpha;
+ Stencil stencil(mParent.mTracker.mGrid);
+ for (size_t n=range.begin(), e=range.end(); n != e; ++n) {
+ const BufferType& phi = mParent.mTracker.mLeafs->getBuffer(n, phiBuffer);
+ BufferType& result = mParent.mTracker.mLeafs->getBuffer(n, resultBuffer);
+ const VectorType* vec = mVec[n];
+ int m=0;
+ for (VoxelIterT iter = mParent.mTracker.mLeafs->leaf(n).cbeginValueOn(); iter; ++iter, ++m) {
+ stencil.moveTo(iter);
+ const VectorType V = vec[m], G = math::GradientBiased<MapT, SpatialScheme>::result(map, stencil, V);
+ result.setValue(iter.pos(), alpha*phi[iter.pos()] + beta*(*iter - dt * V.dot(G)));
+ }
+ }
+}
+
+} // namespace tools
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_TOOLS_LEVEL_SET_ADVECT_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/tools/LevelSetFilter.h b/extern/openvdb/internal/openvdb/tools/LevelSetFilter.h
new file mode 100644
index 00000000000..a0e1dcb842f
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/tools/LevelSetFilter.h
@@ -0,0 +1,411 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+//
+/// @author Ken Museth
+///
+/// @file LevelSetFilter.h
+///
+/// @brief Performs various types of level set deformations with
+/// interface tracking. These unrestricted deformations include
+/// surface smoothing (e.g., Laplacian flow), filtering (e.g., mean
+/// value) and morphological operations (e.g., morphological opening).
+
+#ifndef OPENVDB_TOOLS_LEVELSETFILTER_HAS_BEEN_INCLUDED
+#define OPENVDB_TOOLS_LEVELSETFILTER_HAS_BEEN_INCLUDED
+
+#include "LevelSetTracker.h"
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace tools {
+
+/// @brief Filtering (i.e. diffusion) of narrow-band level sets
+///
+/// @note This class performs propper interface tracking which allows
+/// for unrestricted surface deformations
+template<typename GridT,
+ typename InterruptT = util::NullInterrupter>
+class LevelSetFilter : public LevelSetTracker<GridT, InterruptT>
+{
+public:
+ typedef boost::shared_ptr<LevelSetFilter> Ptr;
+ typedef LevelSetTracker<GridT, InterruptT> BaseType;
+ typedef GridT GridType;
+ typedef typename GridType::TreeType TreeType;
+ typedef typename TreeType::LeafNodeType LeafType;
+ typedef typename LeafType::ValueType ValueType;
+ typedef typename tree::LeafManager<TreeType> LeafManagerType;
+ typedef typename LeafManagerType::RangeType RangeType;
+ typedef typename LeafManagerType::BufferType BufferType;
+
+ /// Main constructor
+ LevelSetFilter(GridType& grid, InterruptT* interrupt = NULL)
+ : BaseType(grid, interrupt), mTask(0)
+ {
+ }
+ /// Shallow copy constructor called by tbb::parallel_for() threads during filtering
+ LevelSetFilter(const LevelSetFilter& other)
+ : BaseType(other), mTask(other.mTask)
+ {
+ }
+ virtual ~LevelSetFilter() {};
+
+ /// Used internally by tbb::parallel_for()
+ void operator()(const RangeType& r) const
+ {
+ if (mTask) mTask(const_cast<LevelSetFilter*>(this), r);
+ else OPENVDB_THROW(ValueError, "task is undefined - call offset(), etc");
+ }
+
+ /// @brief One iteration of mean-curvature flow of the level set
+ void meanCurvature();
+
+ /// @brief One iteration of laplacian flow of the level set
+ void laplacian();
+
+ /// @brief One iteration of a fast separable gaussian filter.
+ ///
+ /// @note This is approximated as 4 iterations of a separable mean filter
+ /// which typically leads an approximation that's better than 95%!
+ void gaussian(int width = 1);
+
+ /// @brief Offset the level set by the specified (world) distance
+ void offset(ValueType offset);
+
+ /// @brief One iteration of median-value flow of the level set
+ ///
+ /// @note This filter is not separable and is hence relatively slow!
+ void median(int width = 1);
+
+ /// @brief One iteration of mean-value flow of the level set
+ ///
+ /// @note This filter is separable so it's fast!
+ void mean(int width = 1);
+
+private:
+ typedef typename boost::function<void (LevelSetFilter*, const RangeType&)> FuncType;
+
+ FuncType mTask;
+
+ // Private cook method calling tbb::parallel_for
+ void cook(bool swap)
+ {
+ const int n = BaseType::getGrainSize();
+ if (n>0) {
+ tbb::parallel_for(BaseType::mLeafs->getRange(n), *this);
+ } else {
+ (*this)(BaseType::mLeafs->getRange());
+ }
+ if (swap) BaseType::mLeafs->swapLeafBuffer(1, n==0);
+ }
+
+ // Prive driver method for mean and gaussian filtering
+ void box(int width);
+
+ // Private methods called by tbb::parallel_for threads
+ void doBoxX(const RangeType&, Int32);
+ void doBoxY(const RangeType&, Int32);
+ void doBoxZ(const RangeType&, Int32);
+ void doMedian(const RangeType&, int);
+ void doMeanCurvature(const RangeType&);
+ void doLaplacian(const RangeType&);
+ void doOffset(const RangeType&, ValueType);
+
+}; // end of LevelSetFilter class
+
+
+////////////////////////////////////////
+
+template<typename GridT, typename InterruptT>
+inline void
+LevelSetFilter<GridT, InterruptT>::median(int width)
+{
+ BaseType::startInterrupter("Median-value flow of level set");
+
+ BaseType::mLeafs->rebuildAuxBuffers(1, BaseType::getGrainSize()==0);
+
+ mTask = boost::bind(&LevelSetFilter::doMedian, _1, _2, std::max(1, width));
+ this->cook(true);
+
+ BaseType::track();
+
+ BaseType::endInterrupter();
+}
+
+
+template<typename GridT, typename InterruptT>
+inline void
+LevelSetFilter<GridT, InterruptT>::mean(int width)
+{
+ BaseType::startInterrupter("Mean-value flow of level set");
+
+ this->box(width);
+
+ BaseType::endInterrupter();
+}
+
+template<typename GridT, typename InterruptT>
+inline void
+LevelSetFilter<GridT, InterruptT>::gaussian(int width)
+{
+ BaseType::startInterrupter("Gaussian flow of level set");
+
+ for (int n=0; n<4; ++n) this->box(width);
+
+ BaseType::endInterrupter();
+}
+
+template<typename GridT, typename InterruptT>
+inline void
+LevelSetFilter<GridT, InterruptT>::box(int width)
+{
+ BaseType::mLeafs->rebuildAuxBuffers(1, BaseType::getGrainSize()==0);
+
+ width = std::max(1, width);
+
+ mTask = boost::bind(&LevelSetFilter::doBoxX, _1, _2, width);
+ this->cook(true);
+
+ mTask = boost::bind(&LevelSetFilter::doBoxY, _1, _2, width);
+ this->cook(true);
+
+ mTask = boost::bind(&LevelSetFilter::doBoxZ, _1, _2, width);
+ this->cook(true);
+
+ BaseType::track();
+}
+
+
+template<typename GridT, typename InterruptT>
+inline void
+LevelSetFilter<GridT, InterruptT>::meanCurvature()
+{
+ BaseType::startInterrupter("Mean-curvature flow of level set");
+
+ BaseType::mLeafs->rebuildAuxBuffers(1, BaseType::getGrainSize()==0);
+
+ mTask = boost::bind(&LevelSetFilter::doMeanCurvature, _1, _2);
+ this->cook(true);
+
+ BaseType::track();
+
+ BaseType::endInterrupter();
+}
+
+template<typename GridT, typename InterruptT>
+inline void
+LevelSetFilter<GridT, InterruptT>::laplacian()
+{
+ BaseType::startInterrupter("Laplacian flow of level set");
+
+ BaseType::mLeafs->rebuildAuxBuffers(1, BaseType::getGrainSize()==0);
+
+ mTask = boost::bind(&LevelSetFilter::doLaplacian, _1, _2);
+ this->cook(true);
+
+ BaseType::track();
+
+ BaseType::endInterrupter();
+}
+
+
+template<typename GridT, typename InterruptT>
+inline void
+LevelSetFilter<GridT, InterruptT>::offset(ValueType value)
+{
+ BaseType::startInterrupter("Offsetting level set");
+
+ BaseType::mLeafs->removeAuxBuffers();// no auxiliary buffers required
+
+ const ValueType CFL = ValueType(0.5) * BaseType::voxelSize(), offset = openvdb::math::Abs(value);
+ ValueType dist = 0.0;
+ while (offset-dist > ValueType(0.001)*CFL && BaseType::checkInterrupter()) {
+ const ValueType delta = openvdb::math::Min(offset-dist, CFL);
+ dist += delta;
+
+ mTask = boost::bind(&LevelSetFilter::doOffset, _1, _2, copysign(delta,value));
+ this->cook(false);
+
+ BaseType::track();
+ }
+
+ BaseType::endInterrupter();
+}
+
+
+///////////////////////// PRIVATE METHODS //////////////////////
+
+/// Performs parabolic mean-curvature diffusion
+template<typename GridT, typename InterruptT>
+inline void
+LevelSetFilter<GridT, InterruptT>::doMeanCurvature(const RangeType& range)
+{
+ typedef typename LeafType::ValueOnCIter VoxelIterT;
+ BaseType::checkInterrupter();
+ //const float CFL = 0.9f, dt = CFL * mDx * mDx / 6.0f;
+ const ValueType dx = BaseType::voxelSize(),dt = math::Pow2(dx) / ValueType(3.0);
+ math::CurvatureStencil<GridType> stencil(BaseType::mGrid, dx);
+ for (size_t n=range.begin(), e=range.end(); n != e; ++n) {
+ BufferType& buffer = BaseType::mLeafs->getBuffer(n,1);
+ for (VoxelIterT iter = BaseType::mLeafs->leaf(n).cbeginValueOn(); iter; ++iter) {
+ stencil.moveTo(iter);
+ buffer.setValue(iter.pos(), stencil.getValue() + dt * stencil.meanCurvatureNormGrad());
+ }
+ }
+}
+
+/// Performs laplacian diffusion. Note if the grids contains a true
+/// signed distance field (e.g. a solution to the Eikonal equation)
+/// Laplacian diffusions (e.g. geometric heat equation) is actually
+/// identical to mean curvature diffusion, yet less computationally
+/// expensive! In other words if you're performing renormalization
+/// anyway (e.g. rebuilding the narrow-band) you should consider
+/// performing laplacian diffusion over mean curvature flow!
+template<typename GridT, typename InterruptT>
+inline void
+LevelSetFilter<GridT, InterruptT>::doLaplacian(const RangeType& range)
+{
+ typedef typename LeafType::ValueOnCIter VoxelIterT;
+ BaseType::checkInterrupter();
+ //const float CFL = 0.9f, half_dt = CFL * mDx * mDx / 12.0f;
+ const ValueType dx = BaseType::voxelSize(), dt = math::Pow2(dx) / ValueType(6.0);
+ math::GradStencil<GridType> stencil(BaseType::mGrid, dx);
+ for (size_t n=range.begin(), e=range.end(); n != e; ++n) {
+ BufferType& buffer = BaseType::mLeafs->getBuffer(n,1);
+ for (VoxelIterT iter = BaseType::mLeafs->leaf(n).cbeginValueOn(); iter; ++iter) {
+ stencil.moveTo(iter);
+ buffer.setValue(iter.pos(), stencil.getValue() + dt * stencil.laplacian());
+ }
+ }
+}
+
+/// Offsets the values by a constant
+template<typename GridT, typename InterruptT>
+inline void
+LevelSetFilter<GridT, InterruptT>::doOffset(const RangeType& range, ValueType value)
+{
+ BaseType::checkInterrupter();
+ for (size_t n=range.begin(), e=range.end(); n != e; ++n)
+ BaseType::mLeafs->leaf(n).addValue(value);
+}
+
+/// Performs simple but slow median-value diffusion
+template<typename GridT, typename InterruptT>
+inline void
+LevelSetFilter<GridT, InterruptT>::doMedian(const RangeType& range, int width)
+{
+ typedef typename LeafType::ValueOnCIter VoxelIterT;
+ BaseType::checkInterrupter();
+ typename math::DenseStencil<GridType> stencil(BaseType::mGrid, width);//creates local cache!
+ for (size_t n=range.begin(), e=range.end(); n != e; ++n) {
+ BufferType& buffer = BaseType::mLeafs->getBuffer(n,1);
+ for (VoxelIterT iter=BaseType::mLeafs->leaf(n).cbeginValueOn(); iter; ++iter) {
+ stencil.moveTo(iter);
+ buffer.setValue(iter.pos(), stencil.median());
+ }
+ }
+}
+
+/// X convolution of a separable box filter
+template<typename GridT, typename InterruptT>
+inline void
+LevelSetFilter<GridT, InterruptT>::doBoxX(const RangeType& range, Int32 w)
+{
+ this->checkInterrupter();
+ const ValueType frac = ValueType(1)/ValueType(2*w+1);
+ typename GridT::ConstAccessor acc = BaseType::mGrid.getConstAccessor();
+ for (size_t n=range.begin(), nLast = range.end(); n != nLast; ++n) {
+ const LeafType& leaf = BaseType::mLeafs->leaf(n);
+ BufferType& buffer = BaseType::mLeafs->getBuffer(n, 1);
+ for (typename LeafType::ValueOnCIter iter = leaf.cbeginValueOn(); iter; ++iter) {
+ ValueType sum = zeroVal<ValueType>();
+ math::Coord xyz = iter.getCoord();
+ for (Int32 x = xyz.x()-w, xLast = xyz.x()+w; x <= xLast; ++x) {
+ sum += acc.getValue(xyz.setX(x));
+ }
+ buffer.setValue(iter.pos(), sum*frac);
+ }
+ }
+}
+
+/// Y convolution of a separable box filter
+template<typename GridT, typename InterruptT>
+inline void
+LevelSetFilter<GridT, InterruptT>::doBoxY(const RangeType& range, Int32 w)
+{
+ this->checkInterrupter();
+ const ValueType frac = ValueType(1)/ValueType(2*w+1);
+ typename GridT::ConstAccessor acc = BaseType::mGrid.getConstAccessor();
+ for (size_t n=range.begin(), nLast = range.end(); n != nLast; ++n) {
+ const LeafType& leaf = BaseType::mLeafs->leaf(n);
+ BufferType& buffer = BaseType::mLeafs->getBuffer(n, 1);
+ for (typename LeafType::ValueOnCIter iter = leaf.cbeginValueOn(); iter; ++iter) {
+ ValueType sum = zeroVal<ValueType>();
+ math::Coord xyz = iter.getCoord();
+ for (Int32 y = xyz.y()-w, yLast = xyz.y()+w; y <= yLast; ++y) {
+ sum += acc.getValue(xyz.setY(y));
+ }
+ buffer.setValue(iter.pos(), sum*frac);
+ }
+ }
+}
+
+/// Z convolution of a separable box filter
+template<typename GridT, typename InterruptT>
+inline void
+LevelSetFilter<GridT, InterruptT>::doBoxZ(const RangeType& range, Int32 w)
+{
+ this->checkInterrupter();
+ const ValueType frac = ValueType(1)/ValueType(2*w+1);
+ typename GridT::ConstAccessor acc = BaseType::mGrid.getConstAccessor();
+ for (size_t n=range.begin(), nLast = range.end(); n != nLast; ++n) {
+ const LeafType& leaf = BaseType::mLeafs->leaf(n);
+ BufferType& buffer = BaseType::mLeafs->getBuffer(n, 1);
+ for (typename LeafType::ValueOnCIter iter = leaf.cbeginValueOn(); iter; ++iter) {
+ ValueType sum = zeroVal<ValueType>();
+ math::Coord xyz = iter.getCoord();
+ for (Int32 z = xyz.z()-w, zLast = xyz.z()+w; z <= zLast; ++z) {
+ sum += acc.getValue(xyz.setZ(z));
+ }
+ buffer.setValue(iter.pos(), sum*frac);
+ }
+ }
+}
+
+} // namespace tools
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_TOOLS_LEVELSETFILTER_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/tools/LevelSetFracture.h b/extern/openvdb/internal/openvdb/tools/LevelSetFracture.h
new file mode 100644
index 00000000000..586dbe7c88d
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/tools/LevelSetFracture.h
@@ -0,0 +1,360 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+//
+/// @file tools/LevelSetFracture.h
+///
+/// @brief Divide volumes represented by level set grids into multiple,
+/// disjoint pieces by intersecting them with one or more "cutter" volumes,
+/// also represented by level sets.
+
+#ifndef OPENVDB_TOOLS_LEVELSETFRACTURE_HAS_BEEN_INCLUDED
+#define OPENVDB_TOOLS_LEVELSETFRACTURE_HAS_BEEN_INCLUDED
+
+#include <openvdb/Grid.h>
+#include <openvdb/math/Quat.h>
+#include <openvdb/tree/LeafManager.h>
+#include <openvdb/util/NullInterrupter.h>
+#include "Composite.h" // for csgIntersection() and csgDifference()
+#include "GridTransformer.h" // for resampleToMatch()
+#include "LevelSetUtil.h" // for MinMaxVoxel()
+#include <list>
+#include <deque>
+
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace tools {
+
+/// @brief Level set fracturing
+template<class GridType, class InterruptType = util::NullInterrupter>
+class LevelSetFracture
+{
+public:
+ typedef std::vector<Vec3s> Vec3sList;
+ typedef std::vector<math::Quats> QuatsList;
+ typedef std::list<typename GridType::Ptr> GridPtrList;
+ typedef typename GridPtrList::iterator GridPtrListIter;
+
+
+ /// @brief Default constructor
+ ///
+ /// @param interrupter optional interrupter object
+ explicit LevelSetFracture(InterruptType* interrupter = NULL);
+
+ /// @brief Divide volumes represented by level set grids into multiple,
+ /// disjoint pieces by intersecting them with one or more "cutter" volumes,
+ /// also represented by level sets.
+ /// @details If desired, the process can be applied iteratively, so that
+ /// fragments created with one cutter are subdivided by other cutters.
+ ///
+ /// @note The incoming @a grids and the @a cutter are required to have matching
+ /// transforms and narrow band widths!
+ ///
+ /// @param grids list of grids to fracture. The residuals of the
+ /// fractured grids will remain in this list
+ /// @param cutter a level set grid to use as the cutter object
+ /// @param segment toggle to split disjoint fragments into their own grids
+ /// @param points optional list of world space points at which to instance the
+ /// cutter object (if null, use the cutter's current position only)
+ /// @param rotations optional list of custom rotations for each cutter instance
+ /// @param cutterOverlap toggle to allow consecutive cutter instances to fracture
+ /// previously generated fragments
+ void fracture(GridPtrList& grids, const GridType& cutter, bool segment = false,
+ const Vec3sList* points = NULL, const QuatsList* rotations = NULL,
+ bool cutterOverlap = true);
+
+ /// Return a list of new fragments, not including the residuals from the input grids.
+ GridPtrList& fragments() { return mFragments; }
+
+ /// Remove all elements from the fragment list.
+ void clear() { mFragments.clear(); }
+
+private:
+ // disallow copy by assignment
+ void operator=(const LevelSetFracture&) {}
+
+ bool wasInterrupted(int percent = -1) const {
+ return mInterrupter && mInterrupter->wasInterrupted(percent);
+ }
+
+ bool isValidFragment(GridType&) const;
+ void segmentFragments(GridPtrList&) const;
+ void process(GridPtrList&, const GridType& cutter);
+
+ InterruptType* mInterrupter;
+ GridPtrList mFragments;
+};
+
+
+////////////////////////////////////////
+
+
+// Internal utility objects and implementation details
+
+namespace internal {
+
+/// @brief Segmentation scheme, splits disjoint fragments into separate grids.
+/// @note This is a temporary solution and it will be replaced soon.
+template<typename GridType, typename InterruptType>
+inline std::vector<typename GridType::Ptr>
+segment(GridType& grid, InterruptType* interrupter = NULL)
+{
+ typedef typename GridType::Ptr GridPtr;
+ typedef typename GridType::TreeType TreeType;
+ typedef typename TreeType::Ptr TreePtr;
+ typedef typename TreeType::ValueType ValueType;
+
+ std::vector<GridPtr> segments;
+
+ while (grid.activeVoxelCount() > 0) {
+
+ if (interrupter && interrupter->wasInterrupted()) break;
+
+ // Deep copy the grid's metadata (tree and transform are shared)
+ GridPtr segment(new GridType(grid, ShallowCopy()));
+ // Make the transform unique and insert an empty tree
+ segment->setTransform(grid.transform().copy());
+ TreePtr tree(new TreeType(grid.background()));
+ segment->setTree(tree);
+
+ std::deque<Coord> coordList;
+ coordList.push_back(grid.tree().beginLeaf()->beginValueOn().getCoord());
+
+ Coord ijk, n_ijk;
+ ValueType value;
+
+ typename tree::ValueAccessor<TreeType> sourceAcc(grid.tree());
+ typename tree::ValueAccessor<TreeType> targetAcc(segment->tree());
+
+ while (!coordList.empty()) {
+
+ if (interrupter && interrupter->wasInterrupted()) break;
+
+ ijk = coordList.back();
+ coordList.pop_back();
+
+ if (!sourceAcc.probeValue(ijk, value)) continue;
+ if (targetAcc.isValueOn(ijk)) continue;
+
+ targetAcc.setValue(ijk, value);
+ sourceAcc.setValueOff(ijk);
+
+ for (int n = 0; n < 6; n++) {
+ n_ijk = ijk + util::COORD_OFFSETS[n];
+ if (!targetAcc.isValueOn(n_ijk) && sourceAcc.isValueOn(n_ijk)) {
+ coordList.push_back(n_ijk);
+ }
+ }
+ }
+
+ grid.tree().pruneInactive();
+ segment->tree().signedFloodFill();
+ segments.push_back(segment);
+ }
+ return segments;
+}
+
+} // namespace internal
+
+
+////////////////////////////////////////
+
+
+template<class GridType, class InterruptType>
+LevelSetFracture<GridType, InterruptType>::LevelSetFracture(InterruptType* interrupter)
+ : mInterrupter(interrupter)
+ , mFragments()
+{
+}
+
+
+template<class GridType, class InterruptType>
+void
+LevelSetFracture<GridType, InterruptType>::fracture(GridPtrList& grids, const GridType& cutter,
+ bool segmentation, const Vec3sList* points, const QuatsList* rotations, bool cutterOverlap)
+{
+ // We can process all incoming grids with the same cutter instance,
+ // this optimization is enabled by the requirement of having matching
+ // transforms between all incoming grids and the cutter object.
+ if (points && points->size() != 0) {
+
+ math::Transform::Ptr originalCutterTransform = cutter.transform().copy();
+ GridType cutterGrid(cutter, ShallowCopy());
+
+ const bool hasInstanceRotations =
+ points && rotations && points->size() == rotations->size();
+
+ // for each instance point..
+ for (size_t p = 0, P = points->size(); p < P; ++p) {
+
+ int percent = int((float(p) / float(P)) * 100.0);
+ if (wasInterrupted(percent)) break;
+
+ GridType instCutterGrid;
+ instCutterGrid.setTransform(originalCutterTransform->copy());
+ math::Transform::Ptr xform = originalCutterTransform->copy();
+
+ if (hasInstanceRotations) {
+ const Vec3s& rot = (*rotations)[p].eulerAngles(math::XYZ_ROTATION);
+ xform->preRotate(rot[0], math::X_AXIS);
+ xform->preRotate(rot[1], math::Y_AXIS);
+ xform->preRotate(rot[2], math::Z_AXIS);
+ xform->postTranslate((*points)[p]);
+ } else {
+ xform->postTranslate((*points)[p]);
+ }
+
+ cutterGrid.setTransform(xform);
+
+ if (wasInterrupted()) break;
+
+ // Since there is no scaling, use the generic resampler instead of
+ // the more expensive level set rebuild tool.
+ if (mInterrupter != NULL) {
+ doResampleToMatch<BoxSampler>(cutterGrid, instCutterGrid, *mInterrupter);
+ } else {
+ util::NullInterrupter interrupter;
+ doResampleToMatch<BoxSampler>(cutterGrid, instCutterGrid, interrupter);
+ }
+
+ if (cutterOverlap && !mFragments.empty()) process(mFragments, instCutterGrid);
+ process(grids, instCutterGrid);
+ }
+
+ } else {
+ // use cutter in place
+ if (cutterOverlap && !mFragments.empty()) process(mFragments, cutter);
+ process(grids, cutter);
+ }
+
+ if (segmentation) {
+ segmentFragments(mFragments);
+ segmentFragments(grids);
+ }
+}
+
+
+template<class GridType, class InterruptType>
+bool
+LevelSetFracture<GridType, InterruptType>::isValidFragment(GridType& grid) const
+{
+ typedef typename GridType::TreeType TreeType;
+ if (grid.activeVoxelCount() < 27) return false;
+
+ // Check if valid level-set
+ {
+ tree::LeafManager<TreeType> leafs(grid.tree());
+ MinMaxVoxel<TreeType> minmax(leafs);
+ minmax.runParallel();
+
+ if ((minmax.minVoxel() < 0) == (minmax.maxVoxel() < 0)) return false;
+ }
+
+ return true;
+}
+
+
+template<class GridType, class InterruptType>
+void
+LevelSetFracture<GridType, InterruptType>::segmentFragments(GridPtrList& grids) const
+{
+ GridPtrList newFragments;
+
+ for (GridPtrListIter it = grids.begin(); it != grids.end(); ++it) {
+
+ if (wasInterrupted()) break;
+
+ std::vector<typename GridType::Ptr> segments = internal::segment(*(*it), mInterrupter);
+ for (size_t n = 0, N = segments.size(); n < N; ++n) {
+
+ if (wasInterrupted()) break;
+
+ if (isValidFragment(*segments[n])) {
+ newFragments.push_back(segments[n]);
+ }
+ }
+ }
+
+ grids.swap(newFragments);
+}
+
+
+template<class GridType, class InterruptType>
+void
+LevelSetFracture<GridType, InterruptType>::process(
+ GridPtrList& grids, const GridType& cutter)
+{
+ typedef typename GridType::Ptr GridPtr;
+
+ GridPtrList newFragments;
+
+ for (GridPtrListIter it = grids.begin(); it != grids.end(); ++it) {
+
+ if (wasInterrupted()) break;
+
+ GridPtr grid = *it;
+
+ // gen new fragment
+ GridPtr fragment = grid->deepCopy();
+ csgIntersection(*fragment, *cutter.deepCopy());
+
+ if (wasInterrupted()) break;
+
+ if (!isValidFragment(*fragment)) continue;
+
+ // update residual
+ GridPtr residual = grid->deepCopy();
+ csgDifference(*residual, *cutter.deepCopy());
+
+ if (wasInterrupted()) break;
+
+ if (!isValidFragment(*residual)) continue;
+
+ newFragments.push_back(fragment);
+
+ grid->tree().clear();
+ grid->tree().merge(residual->tree());
+ }
+
+ if (!newFragments.empty()) {
+ mFragments.splice(mFragments.end(), newFragments);
+ }
+}
+
+} // namespace tools
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_TOOLS_LEVELSETFRACTURE_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/tools/LevelSetRebuild.h b/extern/openvdb/internal/openvdb/tools/LevelSetRebuild.h
new file mode 100644
index 00000000000..f49245e9ee8
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/tools/LevelSetRebuild.h
@@ -0,0 +1,347 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+
+#ifndef OPENVDB_TOOLS_LEVELSETREBUILD_HAS_BEEN_INCLUDED
+#define OPENVDB_TOOLS_LEVELSETREBUILD_HAS_BEEN_INCLUDED
+
+#include <openvdb/Grid.h>
+#include <openvdb/Exceptions.h>
+#include <openvdb/math/Math.h>
+#include <openvdb/math/Transform.h>
+#include <openvdb/tools/VolumeToMesh.h>
+#include <openvdb/tools/MeshToVolume.h>
+#include <openvdb/util/NullInterrupter.h>
+#include <openvdb/util/Util.h>
+#include <boost/type_traits/is_floating_point.hpp>
+#include <boost/utility/enable_if.hpp>
+#include <tbb/blocked_range.h>
+#include <tbb/parallel_for.h>
+
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace tools {
+
+
+/// @brief Return a new grid of type @c GridType that contains a narrow-band level set
+/// representation of an isosurface of a given grid.
+///
+/// @param grid a scalar, floating-point grid with one or more disjoint,
+/// closed isosurfaces at the given @a isovalue
+/// @param isovalue the isovalue that defines the implicit surface (defaults to zero,
+/// which is typical if the input grid is already a level set or a SDF).
+/// @param halfWidth half the width of the narrow band, in voxel units
+/// (defaults to 3 voxels, which is required for some level set operations)
+/// @param xform optional transform for the output grid
+/// (if not provided, the transform of the input @a grid will be matched)
+///
+/// @throw TypeError if @a grid is not scalar or not floating-point
+///
+/// @note If the input grid contains overlapping isosurfaces, interior edges will be lost.
+template<class GridType>
+inline typename GridType::Ptr
+levelSetRebuild(const GridType& grid, float isovalue = 0,
+ float halfWidth = float(LEVEL_SET_HALF_WIDTH), const math::Transform* xform = NULL);
+
+
+/// @brief Return a new grid of type @c GridType that contains a narrow-band level set
+/// representation of an isosurface of a given grid.
+///
+/// @param grid a scalar, floating-point grid with one or more disjoint,
+/// closed isosurfaces at the given @a isovalue
+/// @param isovalue the isovalue that defines the implicit surface
+/// @param exBandWidth the exterior narrow-band width in voxel units
+/// @param inBandWidth the interior narrow-band width in voxel units
+/// @param xform optional transform for the output grid
+/// (if not provided, the transform of the input @a grid will be matched)
+///
+/// @throw TypeError if @a grid is not scalar or not floating-point
+///
+/// @note If the input grid contains overlapping isosurfaces, interior edges will be lost.
+template<class GridType>
+inline typename GridType::Ptr
+levelSetRebuild(const GridType& grid, float isovalue, float exBandWidth, float inBandWidth,
+ const math::Transform* xform = NULL);
+
+
+/// @brief Return a new grid of type @c GridType that contains a narrow-band level set
+/// representation of an isosurface of a given grid.
+///
+/// @param grid a scalar, floating-point grid with one or more disjoint,
+/// closed isosurfaces at the given @a isovalue
+/// @param isovalue the isovalue that defines the implicit surface
+/// @param exBandWidth the exterior narrow-band width in voxel units
+/// @param inBandWidth the interior narrow-band width in voxel units
+/// @param xform optional transform for the output grid
+/// (if not provided, the transform of the input @a grid will be matched)
+/// @param interrupter optional interrupter object
+///
+/// @throw TypeError if @a grid is not scalar or not floating-point
+///
+/// @note If the input grid contains overlapping isosurfaces, interior edges will be lost.
+template<class GridType, typename InterruptT>
+inline typename GridType::Ptr
+levelSetRebuild(const GridType& grid, float isovalue, float exBandWidth, float inBandWidth,
+ const math::Transform* xform = NULL, InterruptT* interrupter = NULL);
+
+
+////////////////////////////////////////
+
+
+// Internal utility objects and implementation details
+
+namespace internal {
+
+class PointListTransform
+{
+public:
+ PointListTransform(const PointList& pointsIn, std::vector<Vec3s>& pointsOut,
+ const math::Transform& xform)
+ : mPointsIn(pointsIn)
+ , mPointsOut(&pointsOut)
+ , mXform(xform)
+ {
+ }
+
+ void runParallel()
+ {
+ tbb::parallel_for(tbb::blocked_range<size_t>(0, mPointsOut->size()), *this);
+ }
+
+ void runSerial()
+ {
+ (*this)(tbb::blocked_range<size_t>(0, mPointsOut->size()));
+ }
+
+ inline void operator()(const tbb::blocked_range<size_t>& range) const
+ {
+ for (size_t n = range.begin(); n < range.end(); ++n) {
+ (*mPointsOut)[n] = mXform.worldToIndex(mPointsIn[n]);
+ }
+ }
+
+private:
+ const PointList& mPointsIn;
+ std::vector<Vec3s> * const mPointsOut;
+ const math::Transform& mXform;
+};
+
+
+class PrimCpy
+{
+public:
+ PrimCpy(const PolygonPoolList& primsIn, const std::vector<size_t>& indexList,
+ std::vector<Vec4I>& primsOut)
+ : mPrimsIn(primsIn)
+ , mIndexList(indexList)
+ , mPrimsOut(&primsOut)
+ {
+ }
+
+ void runParallel()
+ {
+ tbb::parallel_for(tbb::blocked_range<size_t>(0, mIndexList.size()), *this);
+ }
+
+ void runSerial()
+ {
+ (*this)(tbb::blocked_range<size_t>(0, mIndexList.size()));
+ }
+
+ inline void operator()(const tbb::blocked_range<size_t>& range) const
+ {
+ openvdb::Vec4I quad;
+ quad[3] = openvdb::util::INVALID_IDX;
+ std::vector<Vec4I>& primsOut = *mPrimsOut;
+
+ for (size_t n = range.begin(); n < range.end(); ++n) {
+ size_t index = mIndexList[n];
+ PolygonPool& polygons = mPrimsIn[n];
+
+ // Copy quads
+ for (size_t i = 0, I = polygons.numQuads(); i < I; ++i) {
+ primsOut[index++] = polygons.quad(i);
+ }
+ polygons.clearQuads();
+
+ // Copy triangles (adaptive mesh)
+ for (size_t i = 0, I = polygons.numTriangles(); i < I; ++i) {
+ const openvdb::Vec3I& triangle = polygons.triangle(i);
+ quad[0] = triangle[0];
+ quad[1] = triangle[1];
+ quad[2] = triangle[2];
+ primsOut[index++] = quad;
+ }
+
+ polygons.clearTriangles();
+ }
+ }
+
+private:
+ const PolygonPoolList& mPrimsIn;
+ const std::vector<size_t>& mIndexList;
+ std::vector<Vec4I> * const mPrimsOut;
+};
+
+} // namespace internal
+
+
+////////////////////////////////////////
+
+
+/// The normal entry points for level set rebuild are the levelSetRebuild() functions.
+/// doLevelSetRebuild() is mainly for internal use, but when the isovalue and half band
+/// widths are given in ValueType units (for example, if they are queried from
+/// a grid), it might be more convenient to call this function directly.
+///
+/// @internal This overload is enabled only for grids with a scalar, floating-point ValueType.
+template<class GridType, typename InterruptT>
+inline typename boost::enable_if<boost::is_floating_point<typename GridType::ValueType>,
+typename GridType::Ptr>::type
+doLevelSetRebuild(const GridType& grid, typename GridType::ValueType iso,
+ typename GridType::ValueType exWidth, typename GridType::ValueType inWidth,
+ const math::Transform* xform, InterruptT* interrupter)
+{
+ const float
+ isovalue = float(iso),
+ exBandWidth = float(exWidth),
+ inBandWidth = float(inWidth);
+
+ tools::VolumeToMesh mesher(isovalue, 0.0005);
+ mesher(grid);
+
+ math::Transform::Ptr transform = (xform != NULL) ? xform->copy() : grid.transform().copy();
+
+ std::vector<Vec3s> points(mesher.pointListSize());
+
+ { // Copy and transform (required for MeshToVolume) points to grid space.
+ internal::PointListTransform ptnXForm(mesher.pointList(), points, *transform);
+ ptnXForm.runParallel();
+ mesher.pointList().reset(NULL);
+ }
+
+ std::vector<Vec4I> primitives;
+
+ { // Copy primitives.
+ PolygonPoolList& polygonPoolList = mesher.polygonPoolList();
+
+ size_t numPrimitives = 0;
+ std::vector<size_t> indexlist(mesher.polygonPoolListSize());
+
+ for (size_t n = 0, N = mesher.polygonPoolListSize(); n < N; ++n) {
+ const openvdb::tools::PolygonPool& polygons = polygonPoolList[n];
+ indexlist[n] = numPrimitives;
+ numPrimitives += polygons.numQuads();
+ numPrimitives += polygons.numTriangles();
+ }
+
+ primitives.resize(numPrimitives);
+ internal::PrimCpy primCpy(polygonPoolList, indexlist, primitives);
+ primCpy.runParallel();
+ }
+
+ MeshToVolume<GridType, InterruptT> vol(transform, 0, interrupter);
+ vol.convertToLevelSet(points, primitives, exBandWidth, inBandWidth);
+ return vol.distGridPtr();
+}
+
+
+/// @internal This overload is enabled only for grids that do not have a scalar,
+/// floating-point ValueType.
+template<class GridType, typename InterruptT>
+inline typename boost::disable_if<boost::is_floating_point<typename GridType::ValueType>,
+typename GridType::Ptr>::type
+doLevelSetRebuild(const GridType&, typename GridType::ValueType /*isovalue*/,
+ typename GridType::ValueType /*exWidth*/, typename GridType::ValueType /*inWidth*/,
+ const math::Transform*, InterruptT*)
+{
+ OPENVDB_THROW(TypeError,
+ "level set rebuild is supported only for scalar, floating-point grids");
+}
+
+
+////////////////////////////////////////
+
+
+template<class GridType, typename InterruptT>
+inline typename GridType::Ptr
+levelSetRebuild(const GridType& grid, float iso, float exWidth, float inWidth,
+ const math::Transform* xform, InterruptT* interrupter)
+{
+ typedef typename GridType::ValueType ValueT;
+ ValueT
+ isovalue(zeroVal<ValueT>() + iso),
+ exBandWidth(zeroVal<ValueT>() + exWidth),
+ inBandWidth(zeroVal<ValueT>() + inWidth);
+
+ return doLevelSetRebuild(grid, isovalue, exBandWidth, inBandWidth, xform, interrupter);
+}
+
+
+template<class GridType>
+inline typename GridType::Ptr
+levelSetRebuild(const GridType& grid, float iso, float exWidth, float inWidth,
+ const math::Transform* xform)
+{
+ typedef typename GridType::ValueType ValueT;
+ ValueT
+ isovalue(zeroVal<ValueT>() + iso),
+ exBandWidth(zeroVal<ValueT>() + exWidth),
+ inBandWidth(zeroVal<ValueT>() + inWidth);
+
+ return doLevelSetRebuild<GridType, util::NullInterrupter>(
+ grid, isovalue, exBandWidth, inBandWidth, xform, NULL);
+}
+
+
+template<class GridType>
+inline typename GridType::Ptr
+levelSetRebuild(const GridType& grid, float iso, float halfVal, const math::Transform* xform)
+{
+ typedef typename GridType::ValueType ValueT;
+ ValueT
+ isovalue(zeroVal<ValueT>() + iso),
+ halfWidth(zeroVal<ValueT>() + halfVal);
+
+ return doLevelSetRebuild<GridType, util::NullInterrupter>(
+ grid, isovalue, halfWidth, halfWidth, xform, NULL);
+}
+
+
+} // namespace tools
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_TOOLS_LEVELSETREBUILD_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/tools/LevelSetSphere.h b/extern/openvdb/internal/openvdb/tools/LevelSetSphere.h
new file mode 100644
index 00000000000..b26ef91eddc
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/tools/LevelSetSphere.h
@@ -0,0 +1,221 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+///
+/// @file LevelSetSphere.h
+///
+/// @brief Generate a narrow-band level set of sphere.
+///
+/// @note By definition a level set has a fixed narrow band width
+/// (the half width is defined by LEVEL_SET_HALF_WIDTH in Types.h),
+/// whereas an SDF can have a variable narrow band width.
+
+#ifndef OPENVDB_TOOLS_LEVELSETSPHERE_HAS_BEEN_INCLUDED
+#define OPENVDB_TOOLS_LEVELSETSPHERE_HAS_BEEN_INCLUDED
+
+#include <openvdb/Grid.h>
+#include <openvdb/Types.h>
+#include <openvdb/math/Math.h>
+#include <openvdb/util/NullInterrupter.h>
+#include <boost/utility.hpp>
+#include <boost/type_traits/is_floating_point.hpp>
+
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace tools {
+
+/// @brief Return a grid of type @c GridType containing a narrow-band level set
+/// representation of a sphere.
+///
+/// @param radius radius of the sphere in world units
+/// @param center center of the sphere in world units
+/// @param voxelSize voxel size in world units
+/// @param halfWidth half the width of the narrow band, in voxel units
+/// @param interrupt a pointer adhering to the util::NullInterrupter interface
+///
+/// @note @c GridType::ValueType must be a floating-point scalar.
+/// @note The leapfrog algorithm employed in this method is best suited
+/// for a single large sphere. For multiple small spheres consider
+/// using the faster algorithm in ParticlesToLevelSet.h
+template<typename GridType, typename InterruptT>
+typename GridType::Ptr
+createLevelSetSphere(float radius, const openvdb::Vec3f& center, float voxelSize,
+ float halfWidth = float(LEVEL_SET_HALF_WIDTH), InterruptT* interrupt = NULL);
+
+/// @brief Return a grid of type @c GridType containing a narrow-band level set
+/// representation of a sphere.
+///
+/// @param radius radius of the sphere in world units
+/// @param center center of the sphere in world units
+/// @param voxelSize voxel size in world units
+/// @param halfWidth half the width of the narrow band, in voxel units
+///
+/// @note @c GridType::ValueType must be a floating-point scalar.
+/// @note The leapfrog algorithm employed in this method is best suited
+/// for a single large sphere. For multiple small spheres consider
+/// using the faster algorithm in ParticlesToLevelSet.h
+template<typename GridType>
+typename GridType::Ptr
+createLevelSetSphere(float radius, const openvdb::Vec3f& center, float voxelSize,
+ float halfWidth = float(LEVEL_SET_HALF_WIDTH))
+{
+ return createLevelSetSphere<GridType, util::NullInterrupter>(radius,center,voxelSize,halfWidth);
+}
+
+
+////////////////////////////////////////
+
+
+/// @brief Generates a signed distance field (or narrow band level
+/// set) to a single sphere.
+///
+/// @note The leapfrog algorithm employed in this class is best
+/// suited for a single large sphere. For multiple small spheres consider
+/// using the faster algorithm in tools/ParticlesToLevelSet.h
+template<typename GridT, typename InterruptT = util::NullInterrupter>
+class LevelSetSphere
+{
+public:
+ typedef typename GridT::ValueType ValueT;
+ typedef typename math::Vec3<ValueT> Vec3T;
+ BOOST_STATIC_ASSERT(boost::is_floating_point<ValueT>::value);
+
+ /// @brief Constructor
+ ///
+ /// @param radius radius of the sphere in world units
+ /// @param center center of the sphere in world units
+ /// @param interrupt pointer to optional interrupter. Use template
+ /// argument util::NullInterrupter if no interruption is desired.
+ ///
+ /// @note If the radius of the sphere is smaller then
+ /// 1.5*voxelSize, i.e. the sphere is smaller then the Nyquist
+ /// frequency of the grid, it is ignored!
+ LevelSetSphere(ValueT radius, const Vec3T &center, InterruptT* interrupt = NULL)
+ : mRadius(radius), mCenter(center), mInterrupt(interrupt)
+ {
+ if (mRadius<=0) OPENVDB_THROW(ValueError, "radius must be positive");
+ }
+
+ /// @return a narrow-band level set of the sphere
+ ///
+ /// @param voxelSize Size of voxels in world units
+ /// @param halfWidth Half-width of narrow-band in voxel units
+ typename GridT::Ptr getLevelSet(ValueT voxelSize, ValueT halfWidth)
+ {
+ mGrid = createLevelSet<GridT>(voxelSize, halfWidth);
+ this->rasterSphere(voxelSize, halfWidth);
+ mGrid->setGridClass(GRID_LEVEL_SET);
+ return mGrid;
+ }
+
+private:
+ void rasterSphere(ValueT dx, ValueT w)
+ {
+ if (!(dx>0.0f)) OPENVDB_THROW(ValueError, "voxel size must be positive");
+ if (!(w>1)) OPENVDB_THROW(ValueError, "half-width must be larger than one");
+
+ // Define radius of sphere and narrow-band in voxel units
+ const ValueT r0 = mRadius/dx, rmax = r0 + w;
+
+ // Radius below the Nyquist frequency
+ if (r0 < 1.5f) return;
+
+ // Define center of sphere in voxel units
+ const Vec3T c(mCenter[0]/dx, mCenter[1]/dx, mCenter[2]/dx);
+
+ // Define index coordinates and their respective bounds
+ openvdb::Coord ijk;
+ int &i = ijk[0], &j = ijk[1], &k = ijk[2], m=1;
+ const int imin=math::Floor(c[0]-rmax), imax=math::Ceil(c[0]+rmax);
+ const int jmin=math::Floor(c[1]-rmax), jmax=math::Ceil(c[1]+rmax);
+ const int kmin=math::Floor(c[2]-rmax), kmax=math::Ceil(c[2]+rmax);
+
+ // Allocate an ValueAccessor for accelerated random access
+ typename GridT::Accessor accessor = mGrid->getAccessor();
+
+ if (mInterrupt) mInterrupt->start("Generating level set of sphere");
+ // Compute signed distances to sphere using leapfrogging in k
+ for ( i = imin; i <= imax; ++i ) {
+ if (util::wasInterrupted(mInterrupt)) return;
+ const float x2 = math::Pow2(i - c[0]);
+ for ( j = jmin; j <= jmax; ++j ) {
+ const float x2y2 = math::Pow2(j - c[1]) + x2;
+ for (k=kmin; k<=kmax; k += m) {
+ m = 1;
+ /// Distance in voxel units to sphere
+ const float v = math::Sqrt(x2y2 + math::Pow2(k-c[2]))-r0,
+ d = math::Abs(v);
+ if ( d < w ){ // inside narrow band
+ accessor.setValue(ijk, dx*v);// distance in world units
+ } else {// outside narrow band
+ m += math::Floor(d-w);// leapfrog
+ }
+ }//end leapfrog over k
+ }//end loop over j
+ }//end loop over i
+
+ // Define consistant signed distances outside the narrow-band
+ mGrid->signedFloodFill();
+ if (mInterrupt) mInterrupt->end();
+ }
+
+ const ValueT mRadius;
+ const Vec3T mCenter;
+ InterruptT* mInterrupt;
+ typename GridT::Ptr mGrid;
+};// LevelSetSphere
+
+
+////////////////////////////////////////
+
+
+template<typename GridType, typename InterruptT>
+typename GridType::Ptr
+createLevelSetSphere(float radius, const openvdb::Vec3f& center, float voxelSize,
+ float halfWidth, InterruptT* interrupt)
+{
+ // GridType::ValueType is required to be a floating-point scalar.
+ BOOST_STATIC_ASSERT(boost::is_floating_point<typename GridType::ValueType>::value);
+
+ typedef typename GridType::ValueType ValueT;
+ LevelSetSphere<GridType, InterruptT> factory(ValueT(radius), center, interrupt);
+ return factory.getLevelSet(ValueT(voxelSize), ValueT(halfWidth));
+}
+
+} // namespace tools
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_TOOLS_LEVELSETSPHERE_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/tools/LevelSetTracker.h b/extern/openvdb/internal/openvdb/tools/LevelSetTracker.h
new file mode 100644
index 00000000000..e6ef3ce8070
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/tools/LevelSetTracker.h
@@ -0,0 +1,516 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+//
+/// @author Ken Museth
+///
+/// @file LevelSetTracker.h
+///
+/// @brief Performs multi-threaded interface tracking of narrow band
+/// level sets. This is the building-block for most level set
+/// computations that involve dynamic topology, e.g. advection.
+
+#ifndef OPENVDB_TOOLS_LEVEL_SET_TRACKER_HAS_BEEN_INCLUDED
+#define OPENVDB_TOOLS_LEVEL_SET_TRACKER_HAS_BEEN_INCLUDED
+
+#include <tbb/parallel_reduce.h>
+#include <tbb/parallel_for.h>
+#include <boost/bind.hpp>
+#include <boost/function.hpp>
+#include <boost/type_traits/is_floating_point.hpp>
+#include <openvdb/Types.h>
+#include <openvdb/math/Math.h>
+#include <openvdb/math/FiniteDifference.h>
+#include <openvdb/math/Operators.h>
+#include <openvdb/math/Stencils.h>
+#include <openvdb/math/Transform.h>
+#include <openvdb/Grid.h>
+#include <openvdb/util/NullInterrupter.h>
+#include "Morphology.h"//for tools::dilateVoxels
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace tools {
+
+/// @brief Performs multi-threaded interface tracking of narrow band level sets
+template<typename GridT, typename InterruptT = util::NullInterrupter>
+class LevelSetTracker
+{
+public:
+ typedef GridT GridType;
+ typedef typename GridT::TreeType TreeType;
+ typedef typename TreeType::LeafNodeType LeafType;
+ typedef typename TreeType::ValueType ValueType;
+ typedef typename tree::LeafManager<TreeType> LeafManagerType; // leafs + buffers
+ typedef typename LeafManagerType::RangeType RangeType;
+ typedef typename LeafManagerType::BufferType BufferType;
+ BOOST_STATIC_ASSERT(boost::is_floating_point<ValueType>::value);
+
+ /// Main constructor
+ LevelSetTracker(GridT& grid, InterruptT* interrupt = NULL);
+
+ /// Shallow copy constructor called by tbb::parallel_for() threads during filtering
+ LevelSetTracker(const LevelSetTracker& other);
+
+ virtual ~LevelSetTracker() { if (mIsMaster) delete mLeafs; }
+
+ /// Iterative normalization, i.e. solving the Eikonal equation
+ void normalize();
+
+ /// Track the level set interface, i.e. rebuild and normalize the
+ /// narrow band of the level set.
+ void track();
+
+ /// Remove voxels that are outside the narrow band. (substep of track)
+ void prune();
+
+ /// @return the spatial finite difference scheme
+ math::BiasedGradientScheme getSpatialScheme() const { return mSpatialScheme; }
+ /// @brief Set the spatial finite difference scheme
+ void setSpatialScheme(math::BiasedGradientScheme scheme) { mSpatialScheme = scheme; }
+
+ /// @return the temporal integration scheme
+ math::TemporalIntegrationScheme getTemporalScheme() const { return mTemporalScheme; }
+ /// @brief Set the spatial finite difference scheme
+ void setTemporalScheme(math::TemporalIntegrationScheme scheme) { mTemporalScheme = scheme; }
+
+ /// @return The number of normalizations performed per track or
+ /// normalize call.
+ int getNormCount() const { return mNormCount; }
+ /// @brief Set the number of normalizations performed per track or
+ /// normalize call.
+ void setNormCount(int n) { mNormCount = n; }
+
+ /// @return the grain-size used for multi-threading
+ int getGrainSize() const { return mGrainSize; }
+ /// @brief Set the grain-size used for multi-threading.
+ /// @note A grainsize of 0 or less disables multi-threading!
+ void setGrainSize(int grainsize) { mGrainSize = grainsize; }
+
+ ValueType voxelSize() const { return mDx; }
+
+ void startInterrupter(const char* msg);
+ void endInterrupter();
+ /// @return false if the process was interrupted
+ bool checkInterrupter();
+
+ const GridType& grid() const { return mGrid; }
+
+ /// @brief Public functor called by tbb::parallel_for()
+ /// @note Never call this method directly
+ void operator()(const RangeType& r) const
+ {
+ if (mTask) mTask(const_cast<LevelSetTracker*>(this), r);
+ else OPENVDB_THROW(ValueError, "task is undefined - call track(), etc");
+ }
+
+protected:
+
+ template<math::BiasedGradientScheme SpatialScheme,
+ math::TemporalIntegrationScheme TemporalScheme>
+ struct Normalizer
+ {
+ Normalizer(LevelSetTracker& tracker): mTracker(tracker), mTask(0) {}
+ void normalize();
+ void operator()(const RangeType& r) const {mTask(const_cast<Normalizer*>(this), r);}
+ typedef typename boost::function<void (Normalizer*, const RangeType&)> FuncType;
+ LevelSetTracker& mTracker;
+ FuncType mTask;
+ void cook(int swapBuffer=0);
+ void euler1(const RangeType& range, ValueType dt, Index resultBuffer);
+ void euler2(const RangeType& range, ValueType dt, ValueType alpha,
+ Index phiBuffer, Index resultBuffer);
+ }; // end of protected Normalizer class
+
+ // required to access mLeafs
+ template<typename, typename, typename>
+ friend class LevelSetAdvection;
+
+ GridType& mGrid;
+ // Throughout the methods below mLeafs is always assumed to contain
+ // a list of the current LeafNodes! The auxiliary buffers on the
+ // other hand always have to be allocated locally, since some
+ // methods need them and others don't!
+ LeafManagerType* mLeafs;
+ InterruptT* mInterrupter;
+ const ValueType mDx;
+
+private:
+ typedef typename boost::function<void (LevelSetTracker*, const RangeType&)> FuncType;
+
+ void trim(const RangeType& r);
+
+ template<math::BiasedGradientScheme SpatialScheme>
+ void normalize1();
+
+ template<math::BiasedGradientScheme SpatialScheme,
+ math::TemporalIntegrationScheme TemporalScheme>
+ void normalize2();
+
+ math::BiasedGradientScheme mSpatialScheme;
+ math::TemporalIntegrationScheme mTemporalScheme;
+ int mNormCount;// Number of iteratations of normalization
+ int mGrainSize;
+ FuncType mTask;
+ const bool mIsMaster;
+
+ // disallow copy by assignment
+ void operator=(const LevelSetTracker& other) {}
+
+}; // end of LevelSetTracker class
+
+template<typename GridT, typename InterruptT>
+LevelSetTracker<GridT, InterruptT>::LevelSetTracker(GridT& grid, InterruptT* interrupt):
+ mGrid(grid),
+ mLeafs(new LeafManagerType(grid.tree())),
+ mInterrupter(interrupt),
+ mDx(grid.voxelSize()[0]),
+ mSpatialScheme(math::HJWENO5_BIAS),
+ mTemporalScheme(math::TVD_RK1),
+ mNormCount(static_cast<int>(LEVEL_SET_HALF_WIDTH)),
+ mGrainSize(1),
+ mTask(0),
+ mIsMaster(true)// N.B.
+{
+ if ( !grid.hasUniformVoxels() ) {
+ OPENVDB_THROW(RuntimeError,
+ "The transform must have uniform scale for the LevelSetTracker to function");
+ }
+ if ( grid.getGridClass() != GRID_LEVEL_SET) {
+ OPENVDB_THROW(RuntimeError,
+ "LevelSetTracker only supports level sets!\n"
+ "However, only level sets are guaranteed to work!\n"
+ "Hint: Grid::setGridClass(openvdb::GRID_LEVEL_SET)");
+ }
+}
+
+template<typename GridT, typename InterruptT>
+LevelSetTracker<GridT, InterruptT>::LevelSetTracker(const LevelSetTracker& other):
+ mGrid(other.mGrid),
+ mLeafs(other.mLeafs),
+ mInterrupter(other.mInterrupter),
+ mDx(other.mDx),
+ mSpatialScheme(other.mSpatialScheme),
+ mTemporalScheme(other.mTemporalScheme),
+ mNormCount(other.mNormCount),
+ mGrainSize(other.mGrainSize),
+ mTask(other.mTask),
+ mIsMaster(false)// N.B.
+{
+}
+
+template<typename GridT, typename InterruptT>
+inline void
+LevelSetTracker<GridT, InterruptT>::prune()
+{
+ this->startInterrupter("Pruning Level Set");
+ // Prune voxels that are too far away from the zero-crossing
+ mTask = boost::bind(&LevelSetTracker::trim, _1, _2);
+ if (mGrainSize>0) {
+ tbb::parallel_for(mLeafs->getRange(mGrainSize), *this);
+ } else {
+ (*this)(mLeafs->getRange());
+ }
+
+ // Remove inactive nodes from tree
+ mGrid.tree().pruneLevelSet();
+
+ // The tree topology has changes so rebuild the list of leafs
+ mLeafs->rebuildLeafArray();
+ this->endInterrupter();
+}
+
+template<typename GridT, typename InterruptT>
+inline void
+LevelSetTracker<GridT, InterruptT>::track()
+{
+ // Dilate narrow-band (this also rebuilds the leaf array!)
+ tools::dilateVoxels(*mLeafs);
+
+ // Compute signed distances in dilated narrow-band
+ this->normalize();
+
+ // Remove voxels that are outside the narrow band
+ this->prune();
+}
+
+template<typename GridT, typename InterruptT>
+inline void
+LevelSetTracker<GridT, InterruptT>::startInterrupter(const char* msg)
+{
+ if (mInterrupter) mInterrupter->start(msg);
+}
+
+template<typename GridT, typename InterruptT>
+inline void
+LevelSetTracker<GridT, InterruptT>::endInterrupter()
+{
+ if (mInterrupter) mInterrupter->end();
+}
+
+template<typename GridT, typename InterruptT>
+inline bool
+LevelSetTracker<GridT, InterruptT>::checkInterrupter()
+{
+ if (util::wasInterrupted(mInterrupter)) {
+ tbb::task::self().cancel_group_execution();
+ return false;
+ }
+ return true;
+}
+
+/// Prunes away voxels that have moved outside the narrow band
+template<typename GridT, typename InterruptT>
+inline void
+LevelSetTracker<GridT, InterruptT>::trim(const RangeType& range)
+{
+ typedef typename LeafType::ValueOnIter VoxelIterT;
+ const_cast<LevelSetTracker*>(this)->checkInterrupter();
+ const ValueType gamma = mGrid.background();
+ for (size_t n=range.begin(), e=range.end(); n != e; ++n) {
+ LeafType &leaf = mLeafs->leaf(n);
+ for (VoxelIterT iter = leaf.beginValueOn(); iter; ++iter) {
+ const ValueType val = *iter;
+ if (val < -gamma)
+ leaf.setValueOff(iter.pos(), -gamma);
+ else if (val > gamma)
+ leaf.setValueOff(iter.pos(), gamma);
+ }
+ }
+}
+
+template<typename GridT, typename InterruptT>
+inline void
+LevelSetTracker<GridT, InterruptT>::normalize()
+{
+ switch (mSpatialScheme) {
+ case math::FIRST_BIAS:
+ this->normalize1<math::FIRST_BIAS >(); break;
+ case math::SECOND_BIAS:
+ this->normalize1<math::SECOND_BIAS >(); break;
+ case math::THIRD_BIAS:
+ this->normalize1<math::THIRD_BIAS >(); break;
+ case math::WENO5_BIAS:
+ this->normalize1<math::WENO5_BIAS >(); break;
+ case math::HJWENO5_BIAS:
+ this->normalize1<math::HJWENO5_BIAS>(); break;
+ default:
+ OPENVDB_THROW(ValueError, "Spatial difference scheme not supported!");
+ }
+}
+
+template<typename GridT, typename InterruptT>
+template<math::BiasedGradientScheme SpatialScheme>
+inline void
+LevelSetTracker<GridT, InterruptT>::normalize1()
+{
+ switch (mTemporalScheme) {
+ case math::TVD_RK1:
+ this->normalize2<SpatialScheme, math::TVD_RK1>(); break;
+ case math::TVD_RK2:
+ this->normalize2<SpatialScheme, math::TVD_RK2>(); break;
+ case math::TVD_RK3:
+ this->normalize2<SpatialScheme, math::TVD_RK3>(); break;
+ default:
+ OPENVDB_THROW(ValueError, "Temporal integration scheme not supported!");
+ }
+}
+
+template<typename GridT, typename InterruptT>
+template<math::BiasedGradientScheme SpatialScheme,
+ math::TemporalIntegrationScheme TemporalScheme>
+inline void
+LevelSetTracker<GridT, InterruptT>::normalize2()
+{
+ Normalizer<SpatialScheme, TemporalScheme> tmp(*this);
+ tmp.normalize();
+}
+
+template<typename GridT, typename InterruptT>
+template<math::BiasedGradientScheme SpatialScheme,
+ math::TemporalIntegrationScheme TemporalScheme>
+inline void
+LevelSetTracker<GridT,InterruptT>::Normalizer<SpatialScheme, TemporalScheme>::
+normalize()
+{
+ /// Make sure we have enough temporal auxiliary buffers
+ mTracker.mLeafs->rebuildAuxBuffers(TemporalScheme == math::TVD_RK3 ? 2 : 1);
+
+ const ValueType dt = (TemporalScheme == math::TVD_RK1 ? ValueType(0.3) :
+ TemporalScheme == math::TVD_RK2 ? ValueType(0.9) : ValueType(1.0))
+ * ValueType(mTracker.voxelSize());
+
+ for (int n=0, e=mTracker.getNormCount(); n < e; ++n) {
+
+ switch(TemporalScheme) {//switch is resolved at compile-time
+ case math::TVD_RK1:
+ //std::cerr << "1";
+ // Perform one explicit Euler step: t1 = t0 + dt
+ // Phi_t1(0) = Phi_t0(0) - dt * VdotG_t0(1)
+ mTask = boost::bind(&Normalizer::euler1, _1, _2, dt, /*result=*/1);
+ // Cook and swap buffer 0 and 1 such that Phi_t1(0) and Phi_t0(1)
+ this->cook(1);
+ break;
+ case math::TVD_RK2:
+ //std::cerr << "2";
+ // Perform one explicit Euler step: t1 = t0 + dt
+ // Phi_t1(1) = Phi_t0(0) - dt * VdotG_t0(1)
+ mTask = boost::bind(&Normalizer::euler1, _1, _2, dt, /*result=*/1);
+ // Cook and swap buffer 0 and 1 such that Phi_t1(0) and Phi_t0(1)
+ this->cook(1);
+
+ // Convex combine explict Euler step: t2 = t0 + dt
+ // Phi_t2(1) = 1/2 * Phi_t0(1) + 1/2 * (Phi_t1(0) - dt * V.Grad_t1(0))
+ mTask = boost::bind(&Normalizer::euler2,
+ _1, _2, dt, ValueType(0.5), /*phi=*/1, /*result=*/1);
+ // Cook and swap buffer 0 and 1 such that Phi_t2(0) and Phi_t1(1)
+ this->cook(1);
+ break;
+ case math::TVD_RK3:
+ //std::cerr << "3";
+ // Perform one explicit Euler step: t1 = t0 + dt
+ // Phi_t1(1) = Phi_t0(0) - dt * VdotG_t0(1)
+ mTask = boost::bind(&Normalizer::euler1, _1, _2, dt, /*result=*/1);
+ // Cook and swap buffer 0 and 1 such that Phi_t1(0) and Phi_t0(1)
+ this->cook(1);
+
+ // Convex combine explict Euler step: t2 = t0 + dt/2
+ // Phi_t2(2) = 3/4 * Phi_t0(1) + 1/4 * (Phi_t1(0) - dt * V.Grad_t1(0))
+ mTask = boost::bind(&Normalizer::euler2,
+ _1, _2, dt, ValueType(0.75), /*phi=*/1, /*result=*/2);
+ // Cook and swap buffer 0 and 2 such that Phi_t2(0) and Phi_t1(2)
+ this->cook(2);
+
+ // Convex combine explict Euler step: t3 = t0 + dt
+ // Phi_t3(2) = 1/3 * Phi_t0(1) + 2/3 * (Phi_t2(0) - dt * V.Grad_t2(0)
+ mTask = boost::bind(&Normalizer::euler2,
+ _1, _2, dt, ValueType(1.0/3.0), /*phi=*/1, /*result=*/2);
+ // Cook and swap buffer 0 and 2 such that Phi_t3(0) and Phi_t2(2)
+ this->cook(2);
+ break;
+ default:
+ OPENVDB_THROW(ValueError, "Temporal integration scheme not supported!");
+ }
+ }
+ mTracker.mLeafs->removeAuxBuffers();
+}
+
+/// Private method to perform the task (serial or threaded) and
+/// subsequently swap the leaf buffers.
+template<typename GridT, typename InterruptT>
+template<math::BiasedGradientScheme SpatialScheme,
+ math::TemporalIntegrationScheme TemporalScheme>
+inline void
+LevelSetTracker<GridT,InterruptT>::Normalizer<SpatialScheme, TemporalScheme>::
+cook(int swapBuffer)
+{
+ mTracker.startInterrupter("Normalizing Level Set");
+
+ if (mTracker.getGrainSize()>0) {
+ tbb::parallel_for(mTracker.mLeafs->getRange(mTracker.getGrainSize()), *this);
+ } else {
+ (*this)(mTracker.mLeafs->getRange());
+ }
+
+ mTracker.mLeafs->swapLeafBuffer(swapBuffer, mTracker.getGrainSize()==0);
+
+ mTracker.endInterrupter();
+}
+
+
+/// Perform normalization using one of the upwinding schemes
+/// This currently supports only forward Euler time integration
+/// and is not expected to work well with the higher-order spactial schemes
+template<typename GridT, typename InterruptT>
+template<math::BiasedGradientScheme SpatialScheme,
+ math::TemporalIntegrationScheme TemporalScheme>
+inline void
+LevelSetTracker<GridT,InterruptT>::Normalizer<SpatialScheme, TemporalScheme>::
+euler1(const RangeType &range, ValueType dt, Index resultBuffer)
+{
+ typedef math::BIAS_SCHEME<SpatialScheme> Scheme;
+ typedef typename Scheme::template ISStencil<GridType>::StencilType Stencil;
+ typedef typename LeafType::ValueOnCIter VoxelIterT;
+ mTracker.checkInterrupter();
+ const ValueType one(1.0), invDx = one/mTracker.voxelSize();
+ Stencil stencil(mTracker.grid());
+ for (size_t n=range.begin(), e=range.end(); n != e; ++n) {
+ BufferType& result = mTracker.mLeafs->getBuffer(n, resultBuffer);
+ const LeafType& leaf = mTracker.mLeafs->leaf(n);
+ for (VoxelIterT iter = leaf.cbeginValueOn(); iter; ++iter) {
+ stencil.moveTo(iter);
+ const ValueType normSqGradPhi =
+ math::ISGradientNormSqrd<SpatialScheme>::result(stencil);
+ const ValueType phi0 = stencil.getValue();
+ const ValueType diff = math::Sqrt(normSqGradPhi)*invDx - one;
+ const ValueType S = phi0 / (math::Sqrt(math::Pow2(phi0) + normSqGradPhi));
+ result.setValue(iter.pos(), phi0 - dt * S * diff);
+ }
+ }
+}
+
+template<typename GridT, typename InterruptT>
+template<math::BiasedGradientScheme SpatialScheme,
+ math::TemporalIntegrationScheme TemporalScheme>
+inline void
+LevelSetTracker<GridT,InterruptT>::Normalizer<SpatialScheme, TemporalScheme>::
+euler2(const RangeType& range, ValueType dt, ValueType alpha, Index phiBuffer, Index resultBuffer)
+{
+ typedef math::BIAS_SCHEME<SpatialScheme> Scheme;
+ typedef typename Scheme::template ISStencil<GridType>::StencilType Stencil;
+ typedef typename LeafType::ValueOnCIter VoxelIterT;
+ mTracker.checkInterrupter();
+ const ValueType one(1.0), beta = one - alpha, invDx = one/mTracker.voxelSize();
+ Stencil stencil(mTracker.grid());
+ for (size_t n=range.begin(), e=range.end(); n != e; ++n) {
+ const BufferType& phi = mTracker.mLeafs->getBuffer(n, phiBuffer);
+ BufferType& result = mTracker.mLeafs->getBuffer(n, resultBuffer);
+ const LeafType& leaf = mTracker.mLeafs->leaf(n);
+ for (VoxelIterT iter = leaf.cbeginValueOn(); iter; ++iter) {
+ stencil.moveTo(iter);
+ const ValueType normSqGradPhi =
+ math::ISGradientNormSqrd<SpatialScheme>::result(stencil);
+ const ValueType phi0 = stencil.getValue();
+ const ValueType diff = math::Sqrt(normSqGradPhi)*invDx - one;
+ const ValueType S = phi0 / (math::Sqrt(math::Pow2(phi0) + normSqGradPhi));
+ result.setValue(iter.pos(), alpha*phi[iter.pos()] + beta*(phi0 - dt * S * diff));
+ }
+ }
+}
+
+} // namespace tools
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_TOOLS_LEVEL_SET_TRACKER_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/tools/LevelSetUtil.h b/extern/openvdb/internal/openvdb/tools/LevelSetUtil.h
new file mode 100644
index 00000000000..b77d8fff43b
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/tools/LevelSetUtil.h
@@ -0,0 +1,480 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+//
+/// @file tools/LevelSetUtil.h
+///
+/// @brief Miscellaneous utilities that operate primarily or exclusively
+/// on level set grids
+
+#ifndef OPENVDB_TOOLS_LEVELSETUTIL_HAS_BEEN_INCLUDED
+#define OPENVDB_TOOLS_LEVELSETUTIL_HAS_BEEN_INCLUDED
+
+#include <openvdb/Grid.h>
+#include <openvdb/tree/LeafManager.h>
+#include <tbb/blocked_range.h>
+#include <tbb/parallel_for.h>
+#include <tbb/parallel_reduce.h>
+#include <limits>
+
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace tools {
+
+// MS Visual C++ requires this extra level of indirection in order to compile
+// THIS MUST EXIST IN AN UNNAMED NAMESPACE IN ORDER TO COMPILE ON WINDOWS
+namespace {
+
+template<typename GridType>
+inline typename GridType::ValueType lsutilGridMax()
+{
+ return std::numeric_limits<typename GridType::ValueType>::max();
+}
+
+template<typename GridType>
+inline typename GridType::ValueType lsutilGridZero()
+{
+ return zeroVal<typename GridType::ValueType>();
+}
+
+} // unnamed namespace
+
+
+////////////////////////////////////////
+
+
+/// @brief Threaded method to convert a sparse level set/SDF into a sparse fog volume
+///
+/// @details For a level set, the active and negative-valued interior half of the
+/// narrow band becomes a linear ramp from 0 to 1; the inactive interior becomes
+/// active with a constant value of 1; and the exterior, including the background
+/// and the active exterior half of the narrow band, becomes inactive with a constant
+/// value of 0. The interior, though active, remains sparse.
+/// @details For a generic SDF, a specified cutoff distance determines the width
+/// of the ramp, but otherwise the result is the same as for a level set.
+///
+/// @param grid level set/SDF grid to transform
+/// @param cutoffDistance optional world space cutoff distance for the ramp
+/// (automatically clamped if greater than the interior
+/// narrow band width)
+template<class GridType>
+inline void
+sdfToFogVolume(
+ GridType& grid,
+ typename GridType::ValueType cutoffDistance = lsutilGridMax<GridType>());
+
+
+////////////////////////////////////////
+
+
+/// @brief Threaded method to extract an interior region mask from a level set/SDF grid
+///
+/// @return a shared pointer to a new boolean grid with the same tree configuration and
+/// transform as the incoming @c grid and whose active voxels correspond to
+/// the interior of the input SDF
+///
+/// @param grid a level set/SDF grid
+/// @param iso threshold below which values are considered to be part of the interior region
+///
+template<class GridType>
+inline typename Grid<typename GridType::TreeType::template ValueConverter<bool>::Type>::Ptr
+sdfInteriorMask(
+ const GridType& grid,
+ typename GridType::ValueType iso = lsutilGridZero<GridType>());
+
+
+////////////////////////////////////////
+
+
+/// @brief Threaded operator that finds the minimum and maximum values
+/// among the active leaf-level voxels of a grid
+/// @details This is useful primarily for level set grids, which have
+/// no active tiles (all of their active voxels are leaf-level).
+template<class TreeType>
+class MinMaxVoxel
+{
+public:
+ typedef tree::LeafManager<TreeType> LeafArray;
+ typedef typename TreeType::ValueType ValueType;
+
+ // LeafArray = openvdb::tree::LeafManager<TreeType> leafs(myTree)
+ MinMaxVoxel(LeafArray&);
+
+ void runParallel();
+ void runSerial();
+
+ const ValueType& minVoxel() const { return mMin; }
+ const ValueType& maxVoxel() const { return mMax; }
+
+ inline MinMaxVoxel(const MinMaxVoxel<TreeType>&, tbb::split);
+ inline void operator()(const tbb::blocked_range<size_t>&);
+ inline void join(const MinMaxVoxel<TreeType>&);
+
+private:
+ LeafArray& mLeafArray;
+ ValueType mMin, mMax;
+};
+
+
+////////////////////////////////////////
+
+
+/// @brief Threaded operator that applies a user-supplied functor
+/// to each leaf node in a LeafManager
+template<class TreeType, class LeafOp>
+class LeafTransformer
+{
+public:
+ typedef tree::LeafManager<TreeType> LeafArray;
+ typedef typename TreeType::ValueType ValueType;
+
+ // LeafArray = openvdb::tree::LeafManager<TreeType> leafs(myTree)
+ LeafTransformer(LeafArray&, LeafOp&);
+
+ void runParallel();
+ void runSerial();
+
+
+ inline void operator()(const tbb::blocked_range<size_t>&) const;
+ inline LeafTransformer(const LeafTransformer<TreeType, LeafOp>&);
+
+private:
+ LeafArray& mLeafArray;
+ LeafOp& mLeafOp;
+};
+
+
+////////////////////////////////////////
+
+
+// Internal utility objects and implementation details
+namespace internal {
+
+template<typename ValueType>
+class FogVolumeOp
+{
+public:
+ FogVolumeOp(ValueType cutoffDistance)
+ : mWeight(ValueType(1.0) / cutoffDistance)
+ {
+ }
+
+ // cutoff has to be < 0.0
+ template <typename LeafNodeType>
+ void operator()(LeafNodeType &leaf, size_t/*leafIndex*/) const
+ {
+ const ValueType zero = zeroVal<ValueType>();
+
+ for (typename LeafNodeType::ValueAllIter iter = leaf.beginValueAll(); iter; ++iter) {
+ ValueType& value = const_cast<ValueType&>(iter.getValue());
+ if (value > zero) {
+ value = zero;
+ iter.setValueOff();
+ } else {
+ value = std::min(ValueType(1.0), value * mWeight);
+ iter.setValueOn(value > zero);
+ }
+ }
+ }
+
+private:
+ ValueType mWeight;
+}; // class FogVolumeOp
+
+
+template<typename TreeType>
+class InteriorMaskOp
+{
+public:
+ InteriorMaskOp(const TreeType& tree, typename TreeType::ValueType iso)
+ : mTree(tree)
+ , mIso(iso)
+ {
+ }
+
+ template <typename LeafNodeType>
+ void operator()(LeafNodeType &leaf, size_t/*leafIndex*/) const
+ {
+ const Coord origin = leaf.getOrigin();
+ const typename TreeType::LeafNodeType* refLeafPt = mTree.probeConstLeaf(origin);
+
+ if (refLeafPt != NULL) {
+
+ const typename TreeType::LeafNodeType& refLeaf = *refLeafPt;
+ typename LeafNodeType::ValueAllIter iter = leaf.beginValueAll();
+
+ for (; iter; ++iter) {
+ if (refLeaf.getValue(iter.pos()) < mIso) {
+ iter.setValueOn();
+ } else {
+ iter.setValueOff();
+ }
+ }
+ }
+ }
+
+private:
+ const TreeType& mTree;
+ typename TreeType::ValueType mIso;
+}; // class InteriorMaskOp
+
+} // namespace internal
+
+
+////////////////////////////////////////
+
+
+template <class TreeType>
+MinMaxVoxel<TreeType>::MinMaxVoxel(LeafArray& leafs)
+ : mLeafArray(leafs)
+ , mMin(std::numeric_limits<ValueType>::max())
+ , mMax(-mMin)
+{
+}
+
+
+template <class TreeType>
+inline
+MinMaxVoxel<TreeType>::MinMaxVoxel(const MinMaxVoxel<TreeType>& rhs, tbb::split)
+ : mLeafArray(rhs.mLeafArray)
+ , mMin(rhs.mMin)
+ , mMax(rhs.mMax)
+{
+}
+
+
+template <class TreeType>
+void
+MinMaxVoxel<TreeType>::runParallel()
+{
+ tbb::parallel_reduce(mLeafArray.getRange(), *this);
+}
+
+
+template <class TreeType>
+void
+MinMaxVoxel<TreeType>::runSerial()
+{
+ (*this)(mLeafArray.getRange());
+}
+
+
+template <class TreeType>
+inline void
+MinMaxVoxel<TreeType>::operator()(const tbb::blocked_range<size_t>& range)
+{
+ typename TreeType::LeafNodeType::ValueOnCIter iter;
+
+ for (size_t n = range.begin(); n < range.end(); ++n) {
+ iter = mLeafArray.leaf(n).cbeginValueOn();
+ for (; iter; ++iter) {
+ const ValueType value = iter.getValue();
+ mMin = std::min(mMin, value);
+ mMax = std::max(mMax, value);
+ }
+ }
+}
+
+
+template <class TreeType>
+inline void
+MinMaxVoxel<TreeType>::join(const MinMaxVoxel<TreeType>& rhs)
+{
+ mMin = std::min(mMin, rhs.mMin);
+ mMax = std::max(mMax, rhs.mMax);
+}
+
+
+////////////////////////////////////////
+
+
+template <class TreeType, class LeafOp>
+LeafTransformer<TreeType, LeafOp>::LeafTransformer(LeafArray& leafs, LeafOp& leafOp)
+ : mLeafArray(leafs)
+ , mLeafOp(leafOp)
+{
+}
+
+
+template <class TreeType, class LeafOp>
+inline
+LeafTransformer<TreeType, LeafOp>::LeafTransformer(
+ const LeafTransformer<TreeType, LeafOp>& rhs)
+ : mLeafArray(rhs.mLeafArray)
+ , mLeafOp(rhs.mLeafOp)
+{
+}
+
+
+template <class TreeType, class LeafOp>
+void
+LeafTransformer<TreeType, LeafOp>::runParallel()
+{
+ tbb::parallel_for(mLeafArray.getRange(), *this);
+}
+
+
+template <class TreeType, class LeafOp>
+void
+LeafTransformer<TreeType, LeafOp>::runSerial()
+{
+ (*this)(mLeafArray.getRange());
+}
+
+
+template <class TreeType, class LeafOp>
+inline void
+LeafTransformer<TreeType, LeafOp>::operator()(const tbb::blocked_range<size_t>& range) const
+{
+ for (size_t n = range.begin(); n < range.end(); ++n) {
+ mLeafOp(mLeafArray.leaf(n), n);
+ }
+}
+
+
+////////////////////////////////////////
+
+
+template <class GridType>
+inline void
+sdfToFogVolume(GridType& grid, typename GridType::ValueType cutoffDistance)
+{
+ typedef typename GridType::TreeType TreeType;
+ typedef typename GridType::ValueType ValueType;
+
+ cutoffDistance = -std::abs(cutoffDistance);
+
+ TreeType& tree = const_cast<TreeType&>(grid.tree());
+
+ { // Transform all voxels (parallel, over leaf nodes)
+ tree::LeafManager<TreeType> leafs(tree);
+
+ MinMaxVoxel<TreeType> minmax(leafs);
+ minmax.runParallel();
+
+ // Clamp to the interior band width.
+ if (minmax.minVoxel() > cutoffDistance) {
+ cutoffDistance = minmax.minVoxel();
+ }
+
+ internal::FogVolumeOp<ValueType> op(cutoffDistance);
+ LeafTransformer<TreeType, internal::FogVolumeOp<ValueType> > transform(leafs, op);
+ transform.runParallel();
+ }
+
+ // Transform all tile values (serial, but the iteration
+ // is constrained from descending into leaf nodes)
+ const ValueType zero = zeroVal<ValueType>();
+ typename TreeType::ValueAllIter iter(tree);
+ iter.setMaxDepth(TreeType::ValueAllIter::LEAF_DEPTH - 1);
+
+ for ( ; iter; ++iter) {
+ ValueType& value = const_cast<ValueType&>(iter.getValue());
+
+ if (value > zero) {
+ value = zero;
+ iter.setValueOff();
+ } else {
+ value = ValueType(1.0);
+ iter.setActiveState(true);
+ }
+ }
+
+ // Update the tree background value.
+
+ typename TreeType::Ptr newTree(new TreeType(/*background=*/zero));
+ newTree->merge(tree);
+ // This is faster than calling Tree::setBackground, since we only need
+ // to update the value that is returned for coordinates that don't fall
+ // inside an allocated node. All inactive tiles and voxels have already
+ // been updated in the previous step so the Tree::setBackground method
+ // will in this case do a redundant traversal of the tree to update the
+ // inactive values once more.
+
+ //newTree->pruneInactive();
+ grid.setTree(newTree);
+
+ grid.setGridClass(GRID_FOG_VOLUME);
+}
+
+
+////////////////////////////////////////
+
+
+template <class GridType>
+inline typename Grid<typename GridType::TreeType::template ValueConverter<bool>::Type>::Ptr
+sdfInteriorMask(const GridType& grid, typename GridType::ValueType iso)
+{
+ typedef typename GridType::TreeType::template ValueConverter<bool>::Type BoolTreeType;
+ typedef Grid<BoolTreeType> BoolGridType;
+
+ typename BoolGridType::Ptr maskGrid(BoolGridType::create(false));
+ maskGrid->setTransform(grid.transform().copy());
+ BoolTreeType& maskTree = maskGrid->tree();
+
+ maskTree.topologyUnion(grid.tree());
+
+ { // Evaluate voxels (parallel, over leaf nodes)
+
+ tree::LeafManager<BoolTreeType> leafs(maskTree);
+
+ typedef internal::InteriorMaskOp<typename GridType::TreeType> InteriorMaskOp;
+ InteriorMaskOp op(grid.tree(), iso);
+
+ LeafTransformer<BoolTreeType, InteriorMaskOp> transform(leafs, op);
+ transform.runParallel();
+ }
+
+ // Evaluate tile values (serial, but the iteration
+ // is constrained from descending into leaf nodes)
+
+ tree::ValueAccessor<const typename GridType::TreeType> acc(grid.tree());
+ typename BoolTreeType::ValueAllIter iter(maskTree);
+ iter.setMaxDepth(BoolTreeType::ValueAllIter::LEAF_DEPTH - 1);
+
+ for ( ; iter; ++iter) {
+ iter.setActiveState(acc.getValue(iter.getCoord()) < iso);
+ }
+
+ maskTree.pruneInactive();
+
+ return maskGrid;
+}
+
+} // namespace tools
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_TOOLS_LEVELSETUTIL_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/tools/MeshToVolume.h b/extern/openvdb/internal/openvdb/tools/MeshToVolume.h
new file mode 100644
index 00000000000..cef6e5d4648
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/tools/MeshToVolume.h
@@ -0,0 +1,2465 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+
+#ifndef OPENVDB_TOOLS_MESH_TO_VOLUME_HAS_BEEN_INCLUDED
+#define OPENVDB_TOOLS_MESH_TO_VOLUME_HAS_BEEN_INCLUDED
+
+#include <openvdb/Types.h>
+#include <openvdb/math/FiniteDifference.h>
+#include <openvdb/math/Operators.h> // for ISGradientNormSqrd
+#include <openvdb/math/Proximity.h> // for closestPointOnTriangleToPoint()
+#include <openvdb/tools/LevelSetUtil.h> // for LeafTransformer
+#include <openvdb/tools/Morphology.h> // for dilateVoxels()
+#include <openvdb/util/NullInterrupter.h>
+#include <openvdb/util/Util.h> // for nearestCoord()
+
+#include <tbb/blocked_range.h>
+#include <tbb/parallel_for.h>
+#include <tbb/parallel_reduce.h>
+#include <deque>
+#include <limits>
+
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace tools {
+
+
+////////////////////////////////////////
+
+
+// Wrapper functions for the MeshToVolume converter
+
+
+/// @brief Convert a triangle mesh to a level set volume.
+///
+/// @return a grid of type @c GridType containing a narrow-band level set
+/// representation of the input mesh.
+///
+/// @throw TypeError if @c GridType is not scalar or not floating-point
+///
+/// @note Requires a closed surface but not necessarily a manifold surface.
+/// Supports surfaces with self intersections and degenerate faces
+/// and is independent of mesh surface normals.
+///
+/// @param xform transform for the output grid
+/// @param points list of world space point positions
+/// @param triangles triangle index list
+/// @param halfWidth half the width of the narrow band, in voxel units
+template<typename GridType>
+inline typename GridType::Ptr
+meshToLevelSet(
+ const openvdb::math::Transform& xform,
+ const std::vector<Vec3s>& points,
+ const std::vector<Vec3I>& triangles,
+ float halfWidth = float(LEVEL_SET_HALF_WIDTH));
+
+
+/// @brief Convert a quad mesh to a level set volume.
+///
+/// @return a grid of type @c GridType containing a narrow-band level set
+/// representation of the input mesh.
+///
+/// @throw TypeError if @c GridType is not scalar or not floating-point
+///
+/// @note Requires a closed surface but not necessarily a manifold surface.
+/// Supports surfaces with self intersections and degenerate faces
+/// and is independent of mesh surface normals.
+///
+/// @param xform transform for the output grid
+/// @param points list of world space point positions
+/// @param quads quad index list
+/// @param halfWidth half the width of the narrow band, in voxel units
+template<typename GridType>
+inline typename GridType::Ptr
+meshToLevelSet(
+ const openvdb::math::Transform& xform,
+ const std::vector<Vec3s>& points,
+ const std::vector<Vec4I>& quads,
+ float halfWidth = float(LEVEL_SET_HALF_WIDTH));
+
+
+/// @brief Convert a triangle and quad mesh to a level set volume.
+///
+/// @return a grid of type @c GridType containing a narrow-band level set
+/// representation of the input mesh.
+///
+/// @throw TypeError if @c GridType is not scalar or not floating-point
+///
+/// @note Requires a closed surface but not necessarily a manifold surface.
+/// Supports surfaces with self intersections and degenerate faces
+/// and is independent of mesh surface normals.
+///
+/// @param xform transform for the output grid
+/// @param points list of world space point positions
+/// @param triangles triangle index list
+/// @param quads quad index list
+/// @param halfWidth half the width of the narrow band, in voxel units
+template<typename GridType>
+inline typename GridType::Ptr
+meshToLevelSet(
+ const openvdb::math::Transform& xform,
+ const std::vector<Vec3s>& points,
+ const std::vector<Vec3I>& triangles,
+ const std::vector<Vec4I>& quads,
+ float halfWidth = float(LEVEL_SET_HALF_WIDTH));
+
+
+/// @brief Convert a triangle and quad mesh to a signed distance field
+/// with an asymmetrical narrow band.
+///
+/// @return a grid of type @c GridType containing a narrow-band signed
+/// distance field representation of the input mesh.
+///
+/// @throw TypeError if @c GridType is not scalar or not floating-point
+///
+/// @note Requires a closed surface but not necessarily a manifold surface.
+/// Supports surfaces with self intersections and degenerate faces
+/// and is independent of mesh surface normals.
+///
+/// @param xform transform for the output grid
+/// @param points list of world space point positions
+/// @param triangles triangle index list
+/// @param quads quad index list
+/// @param exBandWidth the exterior narrow-band width in voxel units
+/// @param inBandWidth the interior narrow-band width in voxel units
+template<typename GridType>
+inline typename GridType::Ptr
+meshToSignedDistanceField(
+ const openvdb::math::Transform& xform,
+ const std::vector<Vec3s>& points,
+ const std::vector<Vec3I>& triangles,
+ const std::vector<Vec4I>& quads,
+ float exBandWidth,
+ float inBandWidth);
+
+
+/// @brief Convert a triangle and quad mesh to an unsigned distance field.
+///
+/// @return a grid of type @c GridType containing a narrow-band unsigned
+/// distance field representation of the input mesh.
+///
+/// @throw TypeError if @c GridType is not scalar or not floating-point
+///
+/// @note Does not requires a closed surface.
+///
+/// @param xform transform for the output grid
+/// @param points list of world space point positions
+/// @param triangles triangle index list
+/// @param quads quad index list
+/// @param bandWidth the width of the narrow band, in voxel units
+template<typename GridType>
+inline typename GridType::Ptr
+meshToUnsignedDistanceField(
+ const openvdb::math::Transform& xform,
+ const std::vector<Vec3s>& points,
+ const std::vector<Vec3I>& triangles,
+ const std::vector<Vec4I>& quads,
+ float bandWidth);
+
+
+////////////////////////////////////////
+
+
+/// Conversion flags, used to control the MeshToVolume output
+enum { GENERATE_PRIM_INDEX_GRID = 0x1 };
+
+
+// MeshToVolume
+template<typename DistGridT, typename InterruptT = util::NullInterrupter>
+class MeshToVolume
+{
+public:
+ typedef typename DistGridT::TreeType DistTreeT;
+ typedef typename DistTreeT::ValueType DistValueT;
+ typedef typename DistTreeT::template ValueConverter<Int32>::Type IndexTreeT;
+ typedef Grid<IndexTreeT> IndexGridT;
+ typedef typename DistTreeT::template ValueConverter<bool>::Type StencilTreeT;
+ typedef Grid<StencilTreeT> StencilGridT;
+
+ MeshToVolume(openvdb::math::Transform::Ptr&, int conversionFlags = 0,
+ InterruptT *interrupter = NULL, int signSweeps = 1);
+
+ /// @brief Mesh to Level Set / Signed Distance Field conversion
+ ///
+ /// @note Requires a closed surface but not necessarily a manifold surface.
+ /// Supports surfaces with self intersections, degenerate faces and
+ /// is independent of mesh surface normals.
+ ///
+ /// @param pointList List of points in grid index space, preferably unique
+ /// and shared by different polygons.
+ /// @param polygonList List of triangles and/or quads.
+ /// @param exBandWidth The exterior narrow-band width in voxel units.
+ /// @param inBandWidth The interior narrow-band width in voxel units.
+ void convertToLevelSet(
+ const std::vector<Vec3s>& pointList,
+ const std::vector<Vec4I>& polygonList,
+ DistValueT exBandWidth = DistValueT(LEVEL_SET_HALF_WIDTH),
+ DistValueT inBandWidth = DistValueT(LEVEL_SET_HALF_WIDTH));
+
+ /// @brief Mesh to Unsigned Distance Field conversion
+ ///
+ /// @note Does not requires a closed surface.
+ ///
+ /// @param pointList List of points in grid index space, preferably unique
+ /// and shared by different polygons.
+ /// @param polygonList List of triangles and/or quads.
+ /// @param exBandWidth The narrow-band width in voxel units.
+ void convertToUnsignedDistanceField(const std::vector<Vec3s>& pointList,
+ const std::vector<Vec4I>& polygonList, DistValueT exBandWidth);
+
+ void clear();
+
+ /// Returns a narrow-band (signed) distance field / level set grid.
+ typename DistGridT::Ptr distGridPtr() const { return mDistGrid; }
+
+ /// Returns a grid containing the closest-primitive index for each
+ /// voxel in the narrow-band.
+ typename IndexGridT::Ptr indexGridPtr() const { return mIndexGrid; }
+
+private:
+ // disallow copy by assignment
+ void operator=(const MeshToVolume<DistGridT, InterruptT>&) {}
+
+ void doConvert(const std::vector<Vec3s>&, const std::vector<Vec4I>&,
+ DistValueT exBandWidth, DistValueT inBandWidth, bool unsignedDistField = false);
+
+ openvdb::math::Transform::Ptr mTransform;
+ int mConversionFlags, mSignSweeps;
+
+ typename DistGridT::Ptr mDistGrid;
+ typename IndexGridT::Ptr mIndexGrid;
+ typename StencilGridT::Ptr mIntersectingVoxelsGrid;
+
+ InterruptT *mInterrupter;
+};
+
+
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+
+
+// Internal utility objects and implementation details
+
+namespace internal {
+
+
+class PointTransform
+{
+public:
+ PointTransform(const std::vector<Vec3s>& pointsIn, std::vector<Vec3s>& pointsOut,
+ const math::Transform& xform)
+ : mPointsIn(pointsIn)
+ , mPointsOut(&pointsOut)
+ , mXform(xform)
+ {
+ }
+
+ void runParallel()
+ {
+ tbb::parallel_for(tbb::blocked_range<size_t>(0, mPointsOut->size()), *this);
+ }
+
+ void runSerial()
+ {
+ (*this)(tbb::blocked_range<size_t>(0, mPointsOut->size()));
+ }
+
+ inline void operator()(const tbb::blocked_range<size_t>& range) const
+ {
+ for (size_t n = range.begin(); n < range.end(); ++n) {
+ (*mPointsOut)[n] = mXform.worldToIndex(mPointsIn[n]);
+ }
+ }
+
+private:
+ const std::vector<Vec3s>& mPointsIn;
+ std::vector<Vec3s> * const mPointsOut;
+ const math::Transform& mXform;
+};
+
+
+template<typename ValueType>
+struct Tolerance
+{
+ static ValueType epsilon() { return ValueType(1e-7); }
+ static ValueType minNarrowBandWidth() { return ValueType(1.0 + 1e-6); }
+};
+
+
+template<typename DistTreeT, typename IndexTreeT>
+inline void
+combine(DistTreeT& lhsDist, IndexTreeT& lhsIndex, DistTreeT& rhsDist, IndexTreeT& rhsIndex)
+{
+ typedef typename DistTreeT::ValueType DistValueT;
+ typename tree::ValueAccessor<DistTreeT> lhsDistAccessor(lhsDist);
+ typename tree::ValueAccessor<IndexTreeT> lhsIndexAccessor(lhsIndex);
+ typename tree::ValueAccessor<IndexTreeT> rhsIndexAccessor(rhsIndex);
+ typename DistTreeT::LeafCIter iter = rhsDist.cbeginLeaf();
+
+ DistValueT rhsValue;
+ Coord ijk;
+
+ for ( ; iter; ++iter) {
+ typename DistTreeT::LeafNodeType::ValueOnCIter it = iter->cbeginValueOn();
+
+ for ( ; it; ++it) {
+
+ ijk = it.getCoord();
+ rhsValue = it.getValue();
+ DistValueT& lhsValue = const_cast<DistValueT&>(lhsDistAccessor.getValue(ijk));
+
+ if (-rhsValue < std::abs(lhsValue)) {
+ lhsValue = rhsValue;
+ lhsIndexAccessor.setValue(ijk, rhsIndexAccessor.getValue(ijk));
+ }
+ }
+ }
+}
+
+
+////////////////////////////////////////
+
+
+/// MeshVoxelizer
+/// @brief TBB body object to voxelize a mesh of triangles and/or quads into a collection
+/// of VDB grids, namely a squared distance grid, a closest primitive grid and an
+/// intersecting voxels grid (the voxels intersect the mesh)
+/// @note Only the leaf nodes that intersect the mesh are allocated, and only voxels in
+/// a narrow band (of two to three voxels in proximity to the mesh's surface) are activated.
+/// They are populated with distance values and primitive indices.
+template<typename DistTreeT, typename InterruptT = util::NullInterrupter>
+class MeshVoxelizer
+{
+public:
+ typedef typename DistTreeT::ValueType DistValueT;
+ typedef typename tree::ValueAccessor<DistTreeT> DistAccessorT;
+ typedef typename DistTreeT::template ValueConverter<Int32>::Type IndexTreeT;
+ typedef typename tree::ValueAccessor<IndexTreeT> IndexAccessorT;
+ typedef typename DistTreeT::template ValueConverter<bool>::Type StencilTreeT;
+ typedef typename tree::ValueAccessor<StencilTreeT> StencilAccessorT;
+
+ MeshVoxelizer(const std::vector<Vec3s>& pointList,
+ const std::vector<Vec4I>& polygonList, InterruptT *interrupter = NULL);
+
+ ~MeshVoxelizer() {}
+
+ void runParallel();
+ void runSerial();
+
+ MeshVoxelizer(MeshVoxelizer<DistTreeT, InterruptT>& rhs, tbb::split);
+ void operator() (const tbb::blocked_range<size_t> &range);
+ void join(MeshVoxelizer<DistTreeT, InterruptT>& rhs);
+
+ DistTreeT& sqrDistTree() { return mSqrDistTree; }
+ IndexTreeT& primIndexTree() { return mPrimIndexTree; }
+ StencilTreeT& intersectionTree() { return mIntersectionTree; }
+
+private:
+ // disallow copy by assignment
+ void operator=(const MeshVoxelizer<DistTreeT, InterruptT>&) {}
+
+ inline bool shortEdge(const Vec3d&, const Vec3d&, const Vec3d&) const;
+ bool evalVoxel(const Coord& ijk, const Int32 polyIdx);
+
+ std::vector<Vec3s> const * const mPointList;
+ std::vector<Vec4I> const * const mPolygonList;
+
+ DistTreeT mSqrDistTree;
+ DistAccessorT mSqrDistAccessor;
+
+ IndexTreeT mPrimIndexTree;
+ IndexAccessorT mPrimIndexAccessor;
+
+ StencilTreeT mIntersectionTree;
+ StencilAccessorT mIntersectionAccessor;
+
+ // Used internally for acceleration
+ IndexTreeT mLastPrimTree;
+ IndexAccessorT mLastPrimAccessor;
+
+ InterruptT *mInterrupter;
+};
+
+
+template<typename DistTreeT, typename InterruptT>
+void
+MeshVoxelizer<DistTreeT, InterruptT>::runParallel()
+{
+ tbb::parallel_reduce(tbb::blocked_range<size_t>(0, mPolygonList->size()), *this);
+}
+
+template<typename DistTreeT, typename InterruptT>
+void
+MeshVoxelizer<DistTreeT, InterruptT>::runSerial()
+{
+ (*this)(tbb::blocked_range<size_t>(0, mPolygonList->size()));
+}
+
+template<typename DistTreeT, typename InterruptT>
+MeshVoxelizer<DistTreeT, InterruptT>::MeshVoxelizer(
+ const std::vector<Vec3s>& pointList, const std::vector<Vec4I>& polygonList,
+ InterruptT *interrupter)
+ : mPointList(&pointList)
+ , mPolygonList(&polygonList)
+ , mSqrDistTree(std::numeric_limits<DistValueT>::max())
+ , mSqrDistAccessor(mSqrDistTree)
+ , mPrimIndexTree(Int32(util::INVALID_IDX))
+ , mPrimIndexAccessor(mPrimIndexTree)
+ , mIntersectionTree(false)
+ , mIntersectionAccessor(mIntersectionTree)
+ , mLastPrimTree(Int32(util::INVALID_IDX))
+ , mLastPrimAccessor(mLastPrimTree)
+ , mInterrupter(interrupter)
+{
+}
+
+template<typename DistTreeT, typename InterruptT>
+MeshVoxelizer<DistTreeT, InterruptT>::MeshVoxelizer(
+ MeshVoxelizer<DistTreeT, InterruptT>& rhs, tbb::split)
+ : mPointList(rhs.mPointList)
+ , mPolygonList(rhs.mPolygonList)
+ , mSqrDistTree(std::numeric_limits<DistValueT>::max())
+ , mSqrDistAccessor(mSqrDistTree)
+ , mPrimIndexTree(Int32(util::INVALID_IDX))
+ , mPrimIndexAccessor(mPrimIndexTree)
+ , mIntersectionTree(false)
+ , mIntersectionAccessor(mIntersectionTree)
+ , mLastPrimTree(Int32(util::INVALID_IDX))
+ , mLastPrimAccessor(mLastPrimTree)
+ , mInterrupter(rhs.mInterrupter)
+{
+}
+
+template<typename DistTreeT, typename InterruptT>
+inline bool
+MeshVoxelizer<DistTreeT, InterruptT>::shortEdge(
+ const Vec3d& v0, const Vec3d& v1, const Vec3d& v2) const
+{
+ double edge_max = std::abs(v1[0] - v0[0]);
+ edge_max = std::max(edge_max, std::abs(v1[1] - v0[1]));
+ edge_max = std::max(edge_max, std::abs(v1[2] - v0[2]));
+ edge_max = std::max(edge_max, std::abs(v0[0] - v2[0]));
+ edge_max = std::max(edge_max, std::abs(v0[1] - v2[1]));
+ edge_max = std::max(edge_max, std::abs(v0[2] - v2[2]));
+ return edge_max < 200.0;
+}
+
+template<typename DistTreeT, typename InterruptT>
+void
+MeshVoxelizer<DistTreeT, InterruptT>::operator()(const tbb::blocked_range<size_t> &range)
+{
+ std::deque<Coord> coordList;
+ StencilTreeT auxTree(false);
+ StencilAccessorT auxAcc(auxTree);
+ Coord ijk, n_ijk;
+
+ for (size_t n = range.begin(); n < range.end(); ++n) {
+
+ if (mInterrupter && mInterrupter->wasInterrupted()) {
+ tbb::task::self().cancel_group_execution();
+ break;
+ }
+
+ const Int32 primIdx = n;
+ const Vec4I verts = (*mPolygonList)[n];
+
+ Vec3d p0((*mPointList)[verts[0]]);
+ Vec3d p1((*mPointList)[verts[1]]);
+ Vec3d p2((*mPointList)[verts[2]]);
+
+ if (shortEdge(p0, p1, p2)) {
+ coordList.clear();
+
+
+ ijk = util::nearestCoord(p0);
+ evalVoxel(ijk, primIdx);
+ coordList.push_back(ijk);
+
+
+ ijk = util::nearestCoord(p1);
+ evalVoxel(ijk, primIdx);
+ coordList.push_back(ijk);
+
+
+ ijk = util::nearestCoord(p2);
+ evalVoxel(ijk, primIdx);
+ coordList.push_back(ijk);
+
+ if (util::INVALID_IDX != verts[3]) {
+ Vec3d p3((*mPointList)[verts[3]]);
+ ijk = util::nearestCoord(p3);
+ evalVoxel(ijk, primIdx);
+ coordList.push_back(ijk);
+ }
+
+ while (!coordList.empty()) {
+ if (mInterrupter && mInterrupter->wasInterrupted()) {
+ break;
+ }
+
+ ijk = coordList.back();
+ coordList.pop_back();
+
+ mIntersectionAccessor.setActiveState(ijk, true);
+
+ for (Int32 i = 0; i < 26; ++i) {
+ n_ijk = ijk + util::COORD_OFFSETS[i];
+
+ if (primIdx != mLastPrimAccessor.getValue(n_ijk)) {
+ mLastPrimAccessor.setValue(n_ijk, n);
+ if(evalVoxel(n_ijk, n)) coordList.push_back(n_ijk);
+ }
+ }
+ }
+
+ } else {
+
+ ijk = util::nearestCoord(p0);
+ evalVoxel(ijk, primIdx);
+
+ mLastPrimAccessor.setValue(ijk, primIdx);
+ auxAcc.setActiveState(ijk, true);
+
+ while (!auxTree.empty()) {
+
+ if (mInterrupter && mInterrupter->wasInterrupted()) {
+ break;
+ }
+
+ typename StencilTreeT::LeafIter leafIter = auxTree.beginLeaf();
+ for (; leafIter; leafIter.next()) {
+
+ if (mInterrupter && mInterrupter->wasInterrupted()) {
+ break;
+ }
+
+ typename StencilTreeT::LeafNodeType::ValueOnIter iter =
+ leafIter->beginValueOn();
+ for (; iter; iter.next()) {
+ ijk = iter.getCoord();
+ iter.setValueOff();
+
+ mIntersectionAccessor.setActiveState(ijk, true);
+
+ for (Int32 i = 0; i < 26; ++i) {
+ n_ijk = ijk + util::COORD_OFFSETS[i];
+
+ if (primIdx != mLastPrimAccessor.getValue(n_ijk)) {
+ mLastPrimAccessor.setValue(n_ijk, n);
+ if (evalVoxel(n_ijk, n)) auxAcc.setActiveState(n_ijk, true);
+ }
+ }
+ }
+ }
+
+ auxTree.pruneInactive();
+ }
+ }
+ }
+}
+
+template<typename DistTreeT, typename InterruptT>
+bool
+MeshVoxelizer<DistTreeT, InterruptT>::evalVoxel(const Coord& ijk, const Int32 polyIdx)
+{
+ Vec3d voxelCenter(ijk[0], ijk[1], ijk[2]);
+ const Vec4I& verts = (*mPolygonList)[polyIdx];
+ const std::vector<Vec3s>& points = *mPointList;
+
+ // Evaluate first triangle
+ const Vec3d a(points[verts[0]]);
+ const Vec3d b(points[verts[1]]);
+ const Vec3d c(points[verts[2]]);
+
+ Vec3d uvw;
+ double dist = (voxelCenter -
+ closestPointOnTriangleToPoint(a, c, b, voxelCenter, uvw)).lengthSqr();
+
+ // Split-up quad into a second triangle and calac distance.
+ if (util::INVALID_IDX != verts[3]) {
+ const Vec3d d(points[verts[3]]);
+
+ double secondDist = (voxelCenter -
+ closestPointOnTriangleToPoint(a, d, c, voxelCenter, uvw)).lengthSqr();
+
+ if (secondDist < dist) dist = secondDist;
+ }
+
+ const DistValueT tmp(dist);
+ if (tmp < std::abs(mSqrDistAccessor.getValue(ijk))) {
+ mSqrDistAccessor.setValue(ijk, -tmp);
+ mPrimIndexAccessor.setValue(ijk, polyIdx);
+ }
+
+ return (dist < 0.86602540378443861);
+}
+
+template<typename DistTreeT, typename InterruptT>
+void
+MeshVoxelizer<DistTreeT, InterruptT>::join(MeshVoxelizer<DistTreeT, InterruptT>& rhs)
+{
+ typename DistTreeT::LeafCIter iter = rhs.mSqrDistTree.cbeginLeaf();
+ DistValueT rhsDist;
+ Coord ijk;
+
+ for ( ; iter; ++iter) {
+ typename DistTreeT::LeafNodeType::ValueOnCIter it = iter->cbeginValueOn();
+
+ for ( ; it; ++it) {
+
+ ijk = it.getCoord();
+ rhsDist = it.getValue();
+ DistValueT lhsDist = mSqrDistAccessor.getValue(ijk);
+
+ if (-rhsDist < std::abs(lhsDist)) {
+ mSqrDistAccessor.setValue(ijk, rhsDist);
+ mPrimIndexAccessor.setValue(ijk, rhs.mPrimIndexAccessor.getValue(ijk));
+ }
+ }
+ }
+
+ mIntersectionTree.merge(rhs.mIntersectionTree);
+}
+
+
+////////////////////////////////////////
+
+
+// ContourTracer
+/// @brief TBB body object that partitions a volume into 2D slices that can be processed
+/// in parallel and marks the exterior contour of disjoint voxel sets in each slice
+template<typename DistTreeT, typename InterruptT = util::NullInterrupter>
+class ContourTracer
+{
+public:
+ typedef typename DistTreeT::ValueType DistValueT;
+ typedef typename tree::ValueAccessor<DistTreeT> DistAccessorT;
+ typedef typename DistTreeT::template ValueConverter<bool>::Type StencilTreeT;
+ typedef typename tree::ValueAccessor<const StencilTreeT> StencilAccessorT;
+
+ ContourTracer(DistTreeT&, const StencilTreeT&, InterruptT *interrupter = NULL);
+ ~ContourTracer() {}
+
+ void runParallel();
+ void runSerial();
+
+ ContourTracer(const ContourTracer<DistTreeT, InterruptT>& rhs);
+ void operator()(const tbb::blocked_range<int> &range) const;
+
+private:
+ void operator=(const ContourTracer<DistTreeT, InterruptT>&) {}
+
+ int sparseScan(int slice) const;
+
+ DistTreeT& mDistTree;
+ DistAccessorT mDistAccessor;
+
+ const StencilTreeT& mIntersectionTree;
+ StencilAccessorT mIntersectionAccessor;
+
+ CoordBBox mBBox;
+
+ /// List of value-depth dependant step sizes.
+ std::vector<Index> mStepSize;
+
+ InterruptT *mInterrupter;
+};
+
+template<typename DistTreeT, typename InterruptT>
+void
+ContourTracer<DistTreeT, InterruptT>::runParallel()
+{
+ tbb::parallel_for(tbb::blocked_range<int>(mBBox.min()[0], mBBox.max()[0]+1), *this);
+}
+
+template<typename DistTreeT, typename InterruptT>
+void
+ContourTracer<DistTreeT, InterruptT>::runSerial()
+{
+ (*this)(tbb::blocked_range<int>(mBBox.min()[0], mBBox.max()[0]+1));
+}
+
+template<typename DistTreeT, typename InterruptT>
+ContourTracer<DistTreeT, InterruptT>::ContourTracer(
+ DistTreeT& distTree, const StencilTreeT& intersectionTree, InterruptT *interrupter)
+ : mDistTree(distTree)
+ , mDistAccessor(mDistTree)
+ , mIntersectionTree(intersectionTree)
+ , mIntersectionAccessor(mIntersectionTree)
+ , mBBox(CoordBBox())
+ , mStepSize(0)
+ , mInterrupter(interrupter)
+{
+ // Build the step size table for different tree value depths.
+ std::vector<Index> dims;
+ mDistTree.getNodeLog2Dims(dims);
+
+ mStepSize.resize(dims.size()+1, 1);
+ Index exponent = 0;
+ for (int idx = static_cast<int>(dims.size()) - 1; idx > -1; --idx) {
+ exponent += dims[idx];
+ mStepSize[idx] = 1 << exponent;
+ }
+
+ mDistTree.evalLeafBoundingBox(mBBox);
+
+ // Make sure that mBBox coincides with the min and max corners of the internal nodes.
+ const int tileDim = mStepSize[0];
+
+ for (size_t i = 0; i < 3; ++i) {
+
+ int n;
+ double diff = std::abs(double(mBBox.min()[i])) / double(tileDim);
+
+ if (mBBox.min()[i] <= tileDim) {
+ n = int(std::ceil(diff));
+ mBBox.min()[i] = - n * tileDim;
+ } else {
+ n = int(std::floor(diff));
+ mBBox.min()[i] = n * tileDim;
+ }
+
+ n = int(std::ceil(std::abs(double(mBBox.max()[i] - mBBox.min()[i])) / double(tileDim)));
+ mBBox.max()[i] = mBBox.min()[i] + n * tileDim;
+ }
+}
+
+template<typename DistTreeT, typename InterruptT>
+ContourTracer<DistTreeT, InterruptT>::ContourTracer(
+ const ContourTracer<DistTreeT, InterruptT> &rhs)
+ : mDistTree(rhs.mDistTree)
+ , mDistAccessor(mDistTree)
+ , mIntersectionTree(rhs.mIntersectionTree)
+ , mIntersectionAccessor(mIntersectionTree)
+ , mBBox(rhs.mBBox)
+ , mStepSize(rhs.mStepSize)
+ , mInterrupter(rhs.mInterrupter)
+{
+}
+
+template<typename DistTreeT, typename InterruptT>
+void
+ContourTracer<DistTreeT, InterruptT>::operator()(const tbb::blocked_range<int> &range) const
+{
+ // Slice up the volume and trace contours.
+ int iStep = 1;
+ for (int n = range.begin(); n < range.end(); n += iStep) {
+
+ if (mInterrupter && mInterrupter->wasInterrupted()) {
+ tbb::task::self().cancel_group_execution();
+ break;
+ }
+
+ iStep = sparseScan(n);
+ }
+}
+
+template<typename DistTreeT, typename InterruptT>
+int
+ContourTracer<DistTreeT, InterruptT>::sparseScan(int slice) const
+{
+ bool lastVoxelWasOut = true;
+ int last_k;
+
+ Coord ijk(slice, mBBox.min()[1], mBBox.min()[2]);
+ Coord step(mStepSize[mDistAccessor.getValueDepth(ijk) + 1]);
+ Coord n_ijk;
+
+ for (ijk[1] = mBBox.min()[1]; ijk[1] <= mBBox.max()[1]; ijk[1] += step[1]) { // j
+
+ if (mInterrupter && mInterrupter->wasInterrupted()) {
+ break;
+ }
+
+ step[1] = mStepSize[mDistAccessor.getValueDepth(ijk) + 1];
+ step[0] = std::min(step[0], step[1]);
+
+ for (ijk[2] = mBBox.min()[2]; ijk[2] <= mBBox.max()[2]; ijk[2] += step[2]) { // k
+
+ step[2] = mStepSize[mDistAccessor.getValueDepth(ijk) + 1];
+ step[1] = std::min(step[1], step[2]);
+ step[0] = std::min(step[0], step[2]);
+
+ // If the current voxel is set?
+ if (mDistAccessor.isValueOn(ijk)) {
+
+ // Is this a boundary voxel?
+ if (mIntersectionAccessor.isValueOn(ijk)) {
+
+ lastVoxelWasOut = false;
+ last_k = ijk[2];
+
+ } else if (lastVoxelWasOut) {
+
+ DistValueT& val = const_cast<DistValueT&>(mDistAccessor.getValue(ijk));
+ val = -val; // flip sign
+
+ } else {
+
+ DistValueT val;
+ for (Int32 n = 3; n < 6; n += 2) {
+ n_ijk = ijk + util::COORD_OFFSETS[n];
+
+ if (mDistAccessor.probeValue(n_ijk, val) && val > 0) {
+ lastVoxelWasOut = true;
+ break;
+ }
+ }
+
+ if (lastVoxelWasOut) {
+
+ DistValueT& v = const_cast<DistValueT&>(mDistAccessor.getValue(ijk));
+ v = -v; // flip sign
+
+ const int tmp_k = ijk[2];
+
+ // backtrace
+ for (--ijk[2]; ijk[2] >= last_k; --ijk[2]) {
+ if (mIntersectionAccessor.isValueOn(ijk)) break;
+ DistValueT& vb = const_cast<DistValueT&>(mDistAccessor.getValue(ijk));
+ if(vb < DistValueT(0.0)) vb = -vb; // flip sign
+ }
+
+ last_k = tmp_k;
+ ijk[2] = tmp_k;
+
+ } else {
+ last_k = std::min(ijk[2], last_k);
+ }
+
+ }
+
+ } // end isValueOn check
+ } // end k
+ } // end j
+ return step[0];
+}
+
+
+////////////////////////////////////////
+
+
+// IntersectingVoxelSign
+/// @brief TBB body object that traversers all intersecting voxels (defined by the
+/// intersectingVoxelsGrid) and potentially flips their sign, by comparing the "closest point"
+/// directions of outside-marked and non-intersecting neighboring voxels
+template<typename DistTreeT>
+class IntersectingVoxelSign
+{
+public:
+ typedef typename DistTreeT::ValueType DistValueT;
+ typedef typename tree::ValueAccessor<DistTreeT> DistAccessorT;
+ typedef typename DistTreeT::template ValueConverter<Int32>::Type IndexTreeT;
+ typedef typename tree::ValueAccessor<IndexTreeT> IndexAccessorT;
+ typedef typename DistTreeT::template ValueConverter<bool>::Type StencilTreeT;
+ typedef typename tree::ValueAccessor<StencilTreeT> StencilAccessorT;
+ typedef tree::LeafManager<StencilTreeT> StencilArrayT;
+
+ IntersectingVoxelSign(
+ const std::vector<Vec3s>& pointList,
+ const std::vector<Vec4I>& polygonList,
+ DistTreeT& distTree,
+ IndexTreeT& indexTree,
+ StencilTreeT& intersectionTree,
+ StencilArrayT& leafs);
+
+ ~IntersectingVoxelSign() {}
+
+ void runParallel();
+ void runSerial();
+
+ IntersectingVoxelSign(const IntersectingVoxelSign<DistTreeT> &rhs);
+ void operator()(const tbb::blocked_range<size_t>&) const;
+
+private:
+ void operator=(const IntersectingVoxelSign<DistTreeT>&) {}
+
+ void evalVoxel(const Coord& ijk) const;
+ Vec3d getClosestPointDir(const Coord& ijk) const;
+
+ std::vector<Vec3s> const * const mPointList;
+ std::vector<Vec4I> const * const mPolygonList;
+
+ DistTreeT& mDistTree;
+ DistAccessorT mDistAccessor;
+
+ IndexTreeT& mIndexTree;
+ IndexAccessorT mIndexAccessor;
+
+ StencilTreeT& mIntersectionTree;
+ StencilAccessorT mIntersectionAccessor;
+ StencilArrayT& mLeafs;
+};
+
+template<typename DistTreeT>
+void
+IntersectingVoxelSign<DistTreeT>::runParallel()
+{
+ tbb::parallel_for(mLeafs.getRange(), *this);
+}
+
+template<typename DistTreeT>
+void
+IntersectingVoxelSign<DistTreeT>::runSerial()
+{
+ (*this)(mLeafs.getRange());
+}
+
+template<typename DistTreeT>
+IntersectingVoxelSign<DistTreeT>::IntersectingVoxelSign(
+ const std::vector<Vec3s>& pointList,
+ const std::vector<Vec4I>& polygonList,
+ DistTreeT& distTree,
+ IndexTreeT& indexTree,
+ StencilTreeT& intersectionTree,
+ StencilArrayT& leafs)
+ : mPointList(&pointList)
+ , mPolygonList(&polygonList)
+ , mDistTree(distTree)
+ , mDistAccessor(mDistTree)
+ , mIndexTree(indexTree)
+ , mIndexAccessor(mIndexTree)
+ , mIntersectionTree(intersectionTree)
+ , mIntersectionAccessor(mIntersectionTree)
+ , mLeafs(leafs)
+{
+}
+
+template<typename DistTreeT>
+IntersectingVoxelSign<DistTreeT>::IntersectingVoxelSign(
+ const IntersectingVoxelSign<DistTreeT> &rhs)
+ : mPointList(rhs.mPointList)
+ , mPolygonList(rhs.mPolygonList)
+ , mDistTree(rhs.mDistTree)
+ , mDistAccessor(mDistTree)
+ , mIndexTree(rhs.mIndexTree)
+ , mIndexAccessor(mIndexTree)
+ , mIntersectionTree(rhs.mIntersectionTree)
+ , mIntersectionAccessor(mIntersectionTree)
+ , mLeafs(rhs.mLeafs)
+{
+}
+
+template<typename DistTreeT>
+void
+IntersectingVoxelSign<DistTreeT>::operator()(
+ const tbb::blocked_range<size_t>& range) const
+{
+ typename StencilTreeT::LeafNodeType::ValueOnCIter iter;
+
+ for (size_t n = range.begin(); n < range.end(); ++n) {
+ iter = mLeafs.leaf(n).cbeginValueOn();
+ for (; iter; ++iter) {
+ evalVoxel(iter.getCoord());
+ }
+ }
+}
+
+template<typename DistTreeT>
+void
+IntersectingVoxelSign<DistTreeT>::evalVoxel(const Coord& ijk) const
+{
+ const DistValueT val = mDistAccessor.getValue(ijk), zeroVal(0.0);
+
+ if(!(val < zeroVal)) return;
+
+ Vec3d dir = getClosestPointDir(ijk), n_dir;
+ DistValueT n_val;
+ Coord n_ijk;
+
+ // Check voxel-face adjacent neighbors.
+ for (Int32 n = 0; n < 26; ++n) {
+ n_ijk = ijk + util::COORD_OFFSETS[n];
+
+ if (mIntersectionAccessor.isValueOn(n_ijk)) continue;
+ if (!mDistAccessor.probeValue(n_ijk, n_val)) continue;
+ if (n_val < zeroVal) continue;
+
+ n_dir = getClosestPointDir(n_ijk);
+
+ if (n_dir.dot(dir) > 0.0 ) {
+ const_cast<IntersectingVoxelSign<DistTreeT> *>(this)->
+ mDistAccessor.setValue(ijk, -val);
+ break;
+ }
+ }
+}
+
+template<typename DistTreeT>
+Vec3d
+IntersectingVoxelSign<DistTreeT>::getClosestPointDir(const Coord& ijk) const
+{
+ Vec3d voxelCenter(ijk[0], ijk[1], ijk[2]);
+ const Vec4I& prim = (*mPolygonList)[mIndexAccessor.getValue(ijk)];
+ const std::vector<Vec3s>& points = *mPointList;
+
+ // Evaluate first triangle
+ const Vec3d a(points[prim[0]]);
+ const Vec3d b(points[prim[1]]);
+ const Vec3d c(points[prim[2]]);
+
+ Vec3d uvw;
+ Vec3d cpt = closestPointOnTriangleToPoint(a, c, b, voxelCenter, uvw);
+ Vec3d diff = voxelCenter - cpt;
+
+ // Evaluate second triangle if quad.
+ if (prim[3] != util::INVALID_IDX) {
+ const Vec3d d(points[prim[3]]);
+
+ cpt = closestPointOnTriangleToPoint(a, d, c, voxelCenter, uvw);
+ Vec3d tmpDiff = voxelCenter - cpt;
+
+ if (tmpDiff.lengthSqr() < diff.lengthSqr()) {
+ diff = tmpDiff;
+ }
+ }
+
+ diff.normalize();
+ return diff;
+}
+
+
+////////////////////////////////////////
+
+
+// IntersectingVoxelCleaner
+/// @brief TBB body object that removes intersecting voxels that were set via
+/// voxelization of self-intersecting parts of a mesh
+template<typename DistTreeT>
+class IntersectingVoxelCleaner
+{
+public:
+ typedef typename DistTreeT::ValueType DistValueT;
+ typedef typename tree::ValueAccessor<DistTreeT> DistAccessorT;
+ typedef typename DistTreeT::LeafNodeType DistLeafT;
+ typedef typename DistTreeT::template ValueConverter<Int32>::Type IndexTreeT;
+ typedef typename tree::ValueAccessor<IndexTreeT> IndexAccessorT;
+ typedef typename IndexTreeT::LeafNodeType IndexLeafT;
+ typedef typename DistTreeT::template ValueConverter<bool>::Type StencilTreeT;
+ typedef typename tree::ValueAccessor<StencilTreeT> StencilAccessorT;
+ typedef typename StencilTreeT::LeafNodeType StencilLeafT;
+ typedef tree::LeafManager<StencilTreeT> StencilArrayT;
+
+ IntersectingVoxelCleaner(DistTreeT& distTree, IndexTreeT& indexTree,
+ StencilTreeT& intersectionTree, StencilArrayT& leafs);
+
+ ~IntersectingVoxelCleaner() {}
+
+ void runParallel();
+ void runSerial();
+
+ IntersectingVoxelCleaner(const IntersectingVoxelCleaner<DistTreeT> &rhs);
+ void operator()(const tbb::blocked_range<size_t>&) const;
+
+private:
+ void operator=(const IntersectingVoxelCleaner<DistTreeT>&) {}
+
+ DistTreeT& mDistTree;
+ IndexTreeT& mIndexTree;
+ StencilTreeT& mIntersectionTree;
+ StencilArrayT& mLeafs;
+};
+
+template<typename DistTreeT>
+void
+IntersectingVoxelCleaner<DistTreeT>::runParallel()
+{
+ tbb::parallel_for(mLeafs.getRange(), *this);
+ mIntersectionTree.pruneInactive();
+}
+
+template<typename DistTreeT>
+void
+IntersectingVoxelCleaner<DistTreeT>::runSerial()
+{
+ (*this)(mLeafs.getRange());
+ mIntersectionTree.pruneInactive();
+}
+
+template<typename DistTreeT>
+IntersectingVoxelCleaner<DistTreeT>::IntersectingVoxelCleaner(
+ DistTreeT& distTree,
+ IndexTreeT& indexTree,
+ StencilTreeT& intersectionTree,
+ StencilArrayT& leafs)
+ : mDistTree(distTree)
+ , mIndexTree(indexTree)
+ , mIntersectionTree(intersectionTree)
+ , mLeafs(leafs)
+{
+}
+
+template<typename DistTreeT>
+IntersectingVoxelCleaner<DistTreeT>::IntersectingVoxelCleaner(
+ const IntersectingVoxelCleaner<DistTreeT>& rhs)
+ : mDistTree(rhs.mDistTree)
+ , mIndexTree(rhs.mIndexTree)
+ , mIntersectionTree(rhs.mIntersectionTree)
+ , mLeafs(rhs.mLeafs)
+{
+}
+
+template<typename DistTreeT>
+void
+IntersectingVoxelCleaner<DistTreeT>::operator()(
+ const tbb::blocked_range<size_t>& range) const
+{
+ Coord ijk, m_ijk;
+ bool turnOff;
+ DistValueT value;
+ Index offset;
+
+ typename StencilLeafT::ValueOnCIter iter;
+
+ IndexAccessorT indexAcc(mIndexTree);
+ DistAccessorT distAcc(mDistTree);
+ StencilAccessorT maskAcc(mIntersectionTree);
+
+ for (size_t n = range.begin(); n < range.end(); ++n) {
+
+ StencilLeafT& maskLeaf = mLeafs.leaf(n);
+
+ ijk = maskLeaf.getOrigin();
+
+ DistLeafT& distLeaf = *distAcc.probeLeaf(ijk);
+
+ iter = maskLeaf.cbeginValueOn();
+ for (; iter; ++iter) {
+
+ offset = iter.pos();
+
+ if(distLeaf.getValue(offset) > 0.1) continue;
+
+ ijk = iter.getCoord();
+ turnOff = true;
+ for (Int32 m = 0; m < 26; ++m) {
+ m_ijk = ijk + util::COORD_OFFSETS[m];
+ if (distAcc.probeValue(m_ijk, value)) {
+ if (value > 0.1) {
+ turnOff = false;
+ break;
+ }
+ }
+ }
+
+ if (turnOff) {
+ maskLeaf.setValueOff(offset);
+ distLeaf.setValueOn(offset, -0.86602540378443861);
+ }
+ }
+ }
+}
+
+
+////////////////////////////////////////
+
+
+// ShellVoxelCleaner
+/// @brief TBB body object that removes non-intersecting voxels that where set by rasterizing
+/// self-intersecting parts of the mesh.
+template<typename DistTreeT>
+class ShellVoxelCleaner
+{
+public:
+ typedef typename DistTreeT::ValueType DistValueT;
+ typedef typename tree::ValueAccessor<DistTreeT> DistAccessorT;
+ typedef typename DistTreeT::LeafNodeType DistLeafT;
+ typedef tree::LeafManager<DistTreeT> DistArrayT;
+ typedef typename DistTreeT::template ValueConverter<Int32>::Type IndexTreeT;
+ typedef typename tree::ValueAccessor<IndexTreeT> IndexAccessorT;
+ typedef typename IndexTreeT::LeafNodeType IndexLeafT;
+ typedef typename DistTreeT::template ValueConverter<bool>::Type StencilTreeT;
+ typedef typename tree::ValueAccessor<StencilTreeT> StencilAccessorT;
+ typedef typename StencilTreeT::LeafNodeType StencilLeafT;
+
+ ShellVoxelCleaner(DistTreeT& distTree, DistArrayT& leafs, IndexTreeT& indexTree,
+ StencilTreeT& intersectionTree);
+
+ ~ShellVoxelCleaner() {}
+
+ void runParallel();
+ void runSerial();
+
+ ShellVoxelCleaner(const ShellVoxelCleaner<DistTreeT> &rhs);
+ void operator()(const tbb::blocked_range<size_t>&) const;
+
+private:
+ void operator=(const ShellVoxelCleaner<DistTreeT>&) {}
+
+ DistTreeT& mDistTree;
+ DistArrayT& mLeafs;
+ IndexTreeT& mIndexTree;
+ StencilTreeT& mIntersectionTree;
+};
+
+template<typename DistTreeT>
+void
+ShellVoxelCleaner<DistTreeT>::runParallel()
+{
+ tbb::parallel_for(mLeafs.getRange(), *this);
+ mDistTree.pruneInactive();
+ mIndexTree.pruneInactive();
+}
+
+template<typename DistTreeT>
+void
+ShellVoxelCleaner<DistTreeT>::runSerial()
+{
+ (*this)(mLeafs.getRange());
+ mDistTree.pruneInactive();
+ mIndexTree.pruneInactive();
+}
+
+template<typename DistTreeT>
+ShellVoxelCleaner<DistTreeT>::ShellVoxelCleaner(
+ DistTreeT& distTree,
+ DistArrayT& leafs,
+ IndexTreeT& indexTree,
+ StencilTreeT& intersectionTree)
+ : mDistTree(distTree)
+ , mLeafs(leafs)
+ , mIndexTree(indexTree)
+ , mIntersectionTree(intersectionTree)
+{
+}
+
+template<typename DistTreeT>
+ShellVoxelCleaner<DistTreeT>::ShellVoxelCleaner(
+ const ShellVoxelCleaner<DistTreeT> &rhs)
+ : mDistTree(rhs.mDistTree)
+ , mLeafs(rhs.mLeafs)
+ , mIndexTree(rhs.mIndexTree)
+ , mIntersectionTree(rhs.mIntersectionTree)
+{
+}
+
+template<typename DistTreeT>
+void
+ShellVoxelCleaner<DistTreeT>::operator()(
+ const tbb::blocked_range<size_t>& range) const
+{
+ Coord ijk, m_ijk;
+ bool turnOff;
+ DistValueT value;
+ Index offset;
+
+ typename DistLeafT::ValueOnCIter iter;
+ const DistValueT distBG = mDistTree.background();
+ const Int32 indexBG = mIntersectionTree.background();
+
+ IndexAccessorT indexAcc(mIndexTree);
+ DistAccessorT distAcc(mDistTree);
+ StencilAccessorT maskAcc(mIntersectionTree);
+
+
+ for (size_t n = range.begin(); n < range.end(); ++n) {
+
+ DistLeafT& distLeaf = mLeafs.leaf(n);
+
+ ijk = distLeaf.getOrigin();
+
+ const StencilLeafT* maskLeaf = maskAcc.probeConstLeaf(ijk);
+ IndexLeafT& indexLeaf = *indexAcc.probeLeaf(ijk);
+
+ iter = distLeaf.cbeginValueOn();
+ for (; iter; ++iter) {
+
+ value = iter.getValue();
+ if(value > 0.0) continue;
+
+ offset = iter.pos();
+ if (maskLeaf && maskLeaf->isValueOn(offset)) continue;
+
+ ijk = iter.getCoord();
+ turnOff = true;
+ for (Int32 m = 0; m < 18; ++m) {
+ m_ijk = ijk + util::COORD_OFFSETS[m];
+ if (maskAcc.isValueOn(m_ijk)) {
+ turnOff = false;
+ break;
+ }
+ }
+
+ if (turnOff) {
+ distLeaf.setValueOff(offset, distBG);
+ indexLeaf.setValueOff(offset, indexBG);
+ }
+ }
+ }
+}
+
+
+////////////////////////////////////////
+
+
+// ExpandNB
+/// @brief TBB body object to expand the level set narrow band
+/// @note The interior and exterior widths should be in world space units and squared.
+template<typename DistTreeT>
+class ExpandNB
+{
+public:
+ typedef typename DistTreeT::ValueType DistValueT;
+ typedef typename tree::ValueAccessor<DistTreeT> DistAccessorT;
+ typedef typename DistTreeT::template ValueConverter<Int32>::Type IndexTreeT;
+ typedef typename tree::ValueAccessor<IndexTreeT> IndexAccessorT;
+ typedef typename DistTreeT::template ValueConverter<bool>::Type StencilTreeT;
+ typedef typename tree::ValueAccessor<StencilTreeT> StencilAccessorT;
+ typedef tree::LeafManager<StencilTreeT> StencilArrayT;
+
+ typedef typename DistTreeT::LeafNodeType DistLeafT;
+ typedef typename IndexTreeT::LeafNodeType IndexLeafT;
+ typedef typename StencilTreeT::LeafNodeType StencilLeafT;
+
+ ExpandNB(const std::vector<Vec3s>& pointList, const std::vector<Vec4I>& polygonList,
+ DistTreeT& distTree, IndexTreeT& indexTree, StencilTreeT& maskTree, StencilArrayT& leafs,
+ DistValueT exteriorBandWidth, DistValueT interiorBandWidth, DistValueT voxelSize);
+
+ ExpandNB(const ExpandNB<DistTreeT>& rhs, tbb::split);
+
+ ~ExpandNB() {}
+
+ void runParallel();
+ void runSerial();
+
+ void operator()(const tbb::blocked_range<size_t>&) const;
+
+private:
+ void operator=(const ExpandNB<DistTreeT>&) {}
+
+ double getDist(const Coord&, DistAccessorT&, IndexAccessorT&, StencilAccessorT&,
+ Int32& primIndex) const;
+
+ double getDist(const Coord&, DistLeafT&, IndexLeafT&, StencilLeafT&,
+ Int32& primIndex) const;
+
+ double getDistToPrim(const Coord& ijk, const Int32 polyIdx) const;
+
+ std::vector<Vec3s> const * const mPointList;
+ std::vector<Vec4I> const * const mPolygonList;
+
+ DistTreeT& mDistTree;
+ IndexTreeT& mIndexTree;
+ StencilTreeT& mMaskTree;
+ StencilArrayT& mLeafs;
+
+ const DistValueT mExteriorBandWidth, mInteriorBandWidth, mVoxelSize;
+};
+
+template<typename DistTreeT>
+void
+ExpandNB<DistTreeT>::runParallel()
+{
+ tbb::parallel_for(mLeafs.getRange(), *this);
+ mMaskTree.pruneInactive();
+}
+
+template<typename DistTreeT>
+void
+ExpandNB<DistTreeT>::runSerial()
+{
+ (*this)(mLeafs.getRange());
+ mMaskTree.pruneInactive();
+}
+
+template<typename DistTreeT>
+ExpandNB<DistTreeT>::ExpandNB(
+ const std::vector<Vec3s>& pointList,
+ const std::vector<Vec4I>& polygonList,
+ DistTreeT& distTree,
+ IndexTreeT& indexTree,
+ StencilTreeT& maskTree,
+ StencilArrayT& leafs,
+ DistValueT exteriorBandWidth, DistValueT interiorBandWidth,
+ DistValueT voxelSize)
+ : mPointList(&pointList)
+ , mPolygonList(&polygonList)
+ , mDistTree(distTree)
+ , mIndexTree(indexTree)
+ , mMaskTree(maskTree)
+ , mLeafs(leafs)
+ , mExteriorBandWidth(exteriorBandWidth)
+ , mInteriorBandWidth(interiorBandWidth)
+ , mVoxelSize(voxelSize)
+{
+}
+
+template<typename DistTreeT>
+ExpandNB<DistTreeT>::ExpandNB(const ExpandNB<DistTreeT>& rhs, tbb::split)
+ : mPointList(rhs.mPointList)
+ , mPolygonList(rhs.mPolygonList)
+ , mDistTree(rhs.mDistTree)
+ , mIndexTree(rhs.mIndexTree)
+ , mMaskTree(rhs.mMaskTree)
+ , mLeafs(rhs.mLeafs)
+ , mExteriorBandWidth(rhs.mExteriorBandWidth)
+ , mInteriorBandWidth(rhs.mInteriorBandWidth)
+ , mVoxelSize(rhs.mVoxelSize)
+{
+}
+
+template<typename DistTreeT>
+void
+ExpandNB<DistTreeT>::operator()(const tbb::blocked_range<size_t>& range) const
+{
+
+
+ Coord ijk;
+ Int32 closestPrimIndex = 0;
+ Index pos;
+ DistValueT distance;
+ bool inside;
+
+ DistAccessorT distAcc(mDistTree);
+ IndexAccessorT indexAcc(mIndexTree);
+ StencilAccessorT maskAcc(mMaskTree);
+
+ CoordBBox bbox;
+
+ for (size_t n = range.begin(); n < range.end(); ++n) {
+
+ StencilLeafT& maskLeaf = mLeafs.leaf(n);
+
+ if (maskLeaf.isEmpty()) continue;
+
+ ijk = maskLeaf.getOrigin();
+
+ DistLeafT& distLeaf = *distAcc.probeLeaf(ijk);
+ IndexLeafT& indexLeaf = *indexAcc.probeLeaf(ijk);
+
+ bbox = maskLeaf.getNodeBoundingBox();
+ bbox.expand(-1);
+
+ typename StencilLeafT::ValueOnIter iter = maskLeaf.beginValueOn();
+ for (; iter; ++iter) {
+
+ ijk = iter.getCoord();
+
+ distance = Tolerance<DistValueT>::epsilon();
+
+ if (bbox.isInside(ijk)) {
+ distance += getDist(ijk, distLeaf, indexLeaf, maskLeaf, closestPrimIndex);
+ } else {
+ distance += getDist(ijk, distAcc, indexAcc, maskAcc, closestPrimIndex);
+ }
+
+ pos = iter.pos();
+
+ inside = distLeaf.getValue(pos) < DistValueT(0.0);
+
+ if (!inside && distance < mExteriorBandWidth) {
+ distLeaf.setValueOn(pos, distance);
+ indexLeaf.setValueOn(pos, closestPrimIndex);
+ } else if (inside && distance < mInteriorBandWidth) {
+ distLeaf.setValueOn(pos, -distance);
+ indexLeaf.setValueOn(pos, closestPrimIndex);
+ } else {
+ iter.setValueOff();
+ }
+ }
+ }
+}
+
+template<typename DistTreeT>
+double
+ExpandNB<DistTreeT>::getDist(
+ const Coord& ijk,
+ DistAccessorT& distAcc,
+ IndexAccessorT& indexAcc,
+ StencilAccessorT& maskAcc,
+ Int32& primIndex) const
+{
+ Vec3d voxelCenter(ijk[0], ijk[1], ijk[2]);
+ DistValueT nDist, dist = std::numeric_limits<double>::max();
+
+ // Find neighbor with closest face point
+ Coord n_ijk;
+ for (Int32 n = 0; n < 18; ++n) {
+ n_ijk = ijk + util::COORD_OFFSETS[n];
+ if (!maskAcc.isValueOn(n_ijk) && distAcc.probeValue(n_ijk, nDist)) {
+ nDist = std::abs(nDist);
+ if (nDist < dist) {
+ dist = nDist;
+ primIndex = indexAcc.getValue(n_ijk);
+ }
+ }
+ }
+
+ // Calc. this voxels distance to the closest primitive.
+ DistValueT newDist = getDistToPrim(ijk, primIndex);
+
+ // Forces the gradient to be monotonic for non-manifold
+ // polygonal models with self-intersections.
+ return newDist > dist ? newDist : dist + mVoxelSize;
+}
+
+template<typename DistTreeT>
+double
+ExpandNB<DistTreeT>::getDist(
+ const Coord& ijk,
+ DistLeafT& distLeaf,
+ IndexLeafT& indexLeaf,
+ StencilLeafT& maskLeaf,
+ Int32& primIndex) const
+{
+ Vec3d voxelCenter(ijk[0], ijk[1], ijk[2]);
+ DistValueT nDist, dist = std::numeric_limits<double>::max();
+
+ Index pos;
+ for (Int32 n = 0; n < 18; ++n) {
+ pos = DistLeafT::coord2offset(ijk + util::COORD_OFFSETS[n]);
+ if (!maskLeaf.isValueOn(pos) && distLeaf.probeValue(pos, nDist)) {
+ nDist = std::abs(nDist);
+ if (nDist < dist) {
+ dist = nDist;
+ primIndex = indexLeaf.getValue(pos);
+ }
+ }
+ }
+
+ DistValueT newDist = getDistToPrim(ijk, primIndex);
+
+ return newDist > dist ? newDist : dist + mVoxelSize;
+}
+
+
+template<typename DistTreeT>
+double
+ExpandNB<DistTreeT>::getDistToPrim(const Coord& ijk, const Int32 polyIdx) const
+{
+ Vec3d voxelCenter(ijk[0], ijk[1], ijk[2]);
+ const Vec4I& verts = (*mPolygonList)[polyIdx];
+ const std::vector<Vec3s>& points = *mPointList;
+
+ // Evaluate first triangle
+ const Vec3d a(points[verts[0]]);
+ const Vec3d b(points[verts[1]]);
+ const Vec3d c(points[verts[2]]);
+
+ Vec3d uvw;
+ double dist = (voxelCenter -
+ closestPointOnTriangleToPoint(a, c, b, voxelCenter, uvw)).lengthSqr();
+
+ // Split-up quad into a second triangle and calac distance.
+ if (util::INVALID_IDX != verts[3]) {
+ const Vec3d d(points[verts[3]]);
+
+ double secondDist = (voxelCenter -
+ closestPointOnTriangleToPoint(a, d, c, voxelCenter, uvw)).lengthSqr();
+
+ if (secondDist < dist) dist = secondDist;
+ }
+
+ return std::sqrt(dist) * double(mVoxelSize);
+}
+
+
+////////////////////////////////////////
+
+
+// Helper methods
+
+/// @brief Surface tracing method that flips the sign of interior marked voxels
+/// and will not cross the boundary defined by the intersecting voxels
+///
+/// @param seed the coordinates of a marked interior seed point
+/// @param distTree the distance field to operate on
+/// @param intersectionTree tree that defines the surface boundary
+template<typename DistTreeT>
+inline void
+surfaceTracer(const Coord &seed, DistTreeT& distTree,
+ typename DistTreeT::template ValueConverter<bool>::Type& intersectionTree)
+{
+ typedef typename DistTreeT::template ValueConverter<bool>::Type StencilTreeT;
+ typedef typename tree::ValueAccessor<StencilTreeT> StencilAccessorT;
+ typedef typename tree::ValueAccessor<DistTreeT> DistAccessorT;
+ typedef typename DistTreeT::ValueType DistValueT;
+
+ StencilAccessorT intrAccessor(intersectionTree);
+ DistAccessorT distAccessor(distTree);
+
+ std::deque<Coord> coordList;
+ coordList.push_back(seed);
+ Coord ijk, n_ijk;
+
+ while (!coordList.empty()) {
+ ijk = coordList.back();
+ coordList.pop_back();
+
+ if (!distAccessor.isValueOn(ijk)) continue;
+
+ DistValueT& dist = const_cast<DistValueT&>(distAccessor.getValue(ijk));
+ if (!(dist < 0.0)) continue;
+ dist = -dist; // flip sign
+
+ for (int n = 0; n < 6; ++n) {
+ n_ijk = ijk + util::COORD_OFFSETS[n];
+
+ if (!intrAccessor.isValueOn(n_ijk)) { // Don't cross the interface
+ if (distAccessor.isValueOn(n_ijk)) { // Is part of the narrow band
+ if (distAccessor.getValue(n_ijk) < 0.0) { // Marked as outside.
+ coordList.push_back(n_ijk);
+ }
+ }
+ }
+
+ } // END neighbor voxel loop.
+ } // END coordList loop.
+}
+
+
+/// @brief Iterate sparsely over a distance grid to find regions with inconsistent sign
+/// information and use internal::surfaceTracer to resolve those inconsistencies.
+///
+/// @param distTree signed distance field to operate on
+/// @param intersectionTree tree that defines the surface boundary for the surface tracer
+/// @param interrupter an object that implements the util::NullInterrupter interface
+template<typename DistTreeT, typename InterruptT>
+inline void
+propagateSign(DistTreeT& distTree,
+ typename DistTreeT::template ValueConverter<bool>::Type& intersectionTree,
+ InterruptT *interrupter = NULL)
+{
+ typedef typename DistTreeT::template ValueConverter<bool>::Type StencilTreeT;
+ typedef typename tree::ValueAccessor<StencilTreeT> StencilAccessorT;
+ typedef typename tree::ValueAccessor<DistTreeT> DistAccessorT;
+ typedef typename DistTreeT::ValueType DistValueT;
+
+ StencilAccessorT intrAccessor(intersectionTree);
+ DistAccessorT distAccessor(distTree);
+ Coord ijk, n_ijk;
+
+ typename DistTreeT::LeafIter leafIter = distTree.beginLeaf();
+ for (; leafIter; leafIter.next()) {
+
+ if (interrupter && interrupter->wasInterrupted()) break;
+
+ typename DistTreeT::LeafNodeType::ValueOnIter iter = leafIter->beginValueOn();
+ for (; iter; iter.next()) {
+
+ ijk = iter.getCoord();
+
+ // Ignore intersecting voxels.
+ if (intrAccessor.isValueOn(ijk)) continue;
+
+ if (iter.getValue() < 0.0) {
+ for (Int32 n = 0; n < 6; ++n) {
+ n_ijk = ijk + util::COORD_OFFSETS[n];
+
+ if (distAccessor.isValueOn(n_ijk) && distAccessor.getValue(n_ijk) > 0.0) {
+ surfaceTracer(ijk, distTree, intersectionTree);
+ break;
+ }
+ }
+ }
+
+ } // END voxel iteration
+ } // END leaf iteration
+}
+
+
+template<typename ValueType>
+struct SqrtAndScaleOp
+{
+ SqrtAndScaleOp(ValueType voxelSize, bool unsignedDist = false)
+ : mVoxelSize(voxelSize)
+ , mUnsigned(unsignedDist)
+ {
+ }
+
+ template <typename LeafNodeType>
+ void operator()(LeafNodeType &leaf, size_t/*leafIndex*/) const
+ {
+ ValueType w[2];
+ w[0] = mVoxelSize;
+ w[1] = -mVoxelSize;
+
+ typename LeafNodeType::ValueOnIter iter = leaf.beginValueOn();
+ for (; iter; ++iter) {
+ ValueType& val = const_cast<ValueType&>(iter.getValue());
+ val = w[!mUnsigned && int(val < ValueType(0.0))] * std::sqrt(std::abs(val));
+ }
+ }
+
+private:
+ ValueType mVoxelSize;
+ const bool mUnsigned;
+};
+
+
+template<typename ValueType>
+struct VoxelSignOp
+{
+ VoxelSignOp(ValueType exBandWidth, ValueType inBandWidth)
+ : mExBandWidth(exBandWidth)
+ , mInBandWidth(inBandWidth)
+ {
+ }
+
+ template <typename LeafNodeType>
+ void operator()(LeafNodeType &leaf, size_t/*leafIndex*/) const
+ {
+ ValueType bgValues[2];
+ bgValues[0] = mExBandWidth;
+ bgValues[1] = -mInBandWidth;
+
+ typename LeafNodeType::ValueOffIter iter = leaf.beginValueOff();
+
+ for (; iter; ++iter) {
+ ValueType& val = const_cast<ValueType&>(iter.getValue());
+ val = bgValues[int(val < ValueType(0.0))];
+ }
+ }
+
+private:
+ ValueType mExBandWidth, mInBandWidth;
+};
+
+
+template<typename ValueType>
+struct TrimOp
+{
+ TrimOp(ValueType exBandWidth, ValueType inBandWidth)
+ : mExBandWidth(exBandWidth)
+ , mInBandWidth(inBandWidth)
+ {
+ }
+
+ template <typename LeafNodeType>
+ void operator()(LeafNodeType &leaf, size_t/*leafIndex*/) const
+ {
+ typename LeafNodeType::ValueOnIter iter = leaf.beginValueOn();
+
+ for (; iter; ++iter) {
+ const ValueType& val = iter.getValue();
+ const bool inside = val < ValueType(0.0);
+
+ if (inside && !(val > -mInBandWidth)) {
+ iter.setValue(-mInBandWidth);
+ iter.setValueOff();
+ } else if (!inside && !(val < mExBandWidth)) {
+ iter.setValue(mExBandWidth);
+ iter.setValueOff();
+ }
+ }
+ }
+
+private:
+ ValueType mExBandWidth, mInBandWidth;
+};
+
+
+template<typename ValueType>
+struct OffsetOp
+{
+ OffsetOp(ValueType offset): mOffset(offset) {}
+
+ void resetOffset(ValueType offset) { mOffset = offset; }
+
+ template <typename LeafNodeType>
+ void operator()(LeafNodeType &leaf, size_t/*leafIndex*/) const
+ {
+ typename LeafNodeType::ValueOnIter iter = leaf.beginValueOn();
+ for (; iter; ++iter) {
+ ValueType& val = const_cast<ValueType&>(iter.getValue());
+ val += mOffset;
+ }
+ }
+
+private:
+ ValueType mOffset;
+};
+
+
+template<typename GridType, typename ValueType>
+struct RenormOp
+{
+ typedef math::BIAS_SCHEME<math::FIRST_BIAS> Scheme;
+ typedef typename Scheme::template ISStencil<GridType>::StencilType Stencil;
+ typedef tree::LeafManager<typename GridType::TreeType> LeafManagerType;
+ typedef typename LeafManagerType::BufferType BufferType;
+
+ RenormOp(GridType& grid, LeafManagerType& leafs, ValueType voxelSize, ValueType cfl = 1.0)
+ : mGrid(grid)
+ , mLeafs(leafs)
+ , mVoxelSize(voxelSize)
+ , mCFL(cfl)
+ {
+ }
+
+ void resetCFL(ValueType cfl) { mCFL = cfl; }
+
+ template <typename LeafNodeType>
+ void operator()(LeafNodeType &leaf, size_t leafIndex) const
+ {
+ const ValueType dt = mCFL * mVoxelSize, one(1.0), invDx = one / mVoxelSize;
+ Stencil stencil(mGrid);
+
+ BufferType& buffer = mLeafs.getBuffer(leafIndex, 1);
+
+ typename LeafNodeType::ValueOnIter iter = leaf.beginValueOn();
+ for (; iter; ++iter) {
+ stencil.moveTo(iter);
+
+ const ValueType normSqGradPhi =
+ math::ISGradientNormSqrd<math::FIRST_BIAS>::result(stencil);
+
+ const ValueType phi0 = stencil.getValue();
+ const ValueType diff = math::Sqrt(normSqGradPhi) * invDx - one;
+ const ValueType S = phi0 / (math::Sqrt(math::Pow2(phi0) + normSqGradPhi));
+
+ buffer.setValue(iter.pos(), phi0 - dt * S * diff);
+ }
+ }
+
+private:
+ GridType& mGrid;
+ LeafManagerType& mLeafs;
+ ValueType mVoxelSize, mCFL;
+};
+
+
+template<typename TreeType, typename ValueType>
+struct MinOp
+{
+ typedef tree::LeafManager<TreeType> LeafManagerType;
+ typedef typename LeafManagerType::BufferType BufferType;
+
+ MinOp(LeafManagerType& leafs): mLeafs(leafs) {}
+
+ template <typename LeafNodeType>
+ void operator()(LeafNodeType &leaf, size_t leafIndex) const
+ {
+ BufferType& buffer = mLeafs.getBuffer(leafIndex, 1);
+
+ typename LeafNodeType::ValueOnIter iter = leaf.beginValueOn();
+ for (; iter; ++iter) {
+ ValueType& val = const_cast<ValueType&>(iter.getValue());
+ val = std::min(val, buffer.getValue(iter.pos()));
+ }
+ }
+
+private:
+ LeafManagerType& mLeafs;
+};
+
+
+template<typename TreeType, typename ValueType>
+struct MergeBufferOp
+{
+ typedef tree::LeafManager<TreeType> LeafManagerType;
+ typedef typename LeafManagerType::BufferType BufferType;
+
+ MergeBufferOp(LeafManagerType& leafs, size_t bufferIndex = 1)
+ : mLeafs(leafs)
+ , mBufferIndex(bufferIndex)
+ {
+ }
+
+ template <typename LeafNodeType>
+ void operator()(LeafNodeType &leaf, size_t leafIndex) const
+ {
+ BufferType& buffer = mLeafs.getBuffer(leafIndex, mBufferIndex);
+
+ typename LeafNodeType::ValueOnIter iter = leaf.beginValueOn();
+ for (; iter; ++iter) {
+ leaf.setValueOnly(iter.pos(), buffer.getValue(iter.pos()));
+ }
+ }
+
+private:
+ LeafManagerType& mLeafs;
+ const size_t mBufferIndex;
+};
+
+
+template<typename TreeType>
+struct LeafTopologyDiffOp
+{
+ typedef typename tree::ValueAccessor<TreeType> AccessorT;
+ typedef typename TreeType::LeafNodeType LeafNodeT;
+
+ LeafTopologyDiffOp(TreeType& tree) : mTree(tree) { }
+
+ template <typename LeafNodeType>
+ void operator()(LeafNodeType &leaf, size_t) const
+ {
+ const LeafNodeT* rhsLeaf = mTree.probeConstLeaf(leaf.getOrigin());
+
+ if (rhsLeaf) {
+ leaf.topologyDifference(*rhsLeaf, false);
+ } else {
+ // special case required for the ExpandNB scheme.
+ leaf.setValuesOff();
+ }
+ }
+
+private:
+ TreeType& mTree;
+};
+
+
+} // internal namespace
+
+
+////////////////////////////////////////
+
+
+// MeshToVolume
+
+template<typename DistGridT, typename InterruptT>
+MeshToVolume<DistGridT, InterruptT>::MeshToVolume(
+ openvdb::math::Transform::Ptr& transform, int conversionFlags,
+ InterruptT *interrupter, int signSweeps)
+ : mTransform(transform)
+ , mConversionFlags(conversionFlags)
+ , mSignSweeps(signSweeps)
+ , mInterrupter(interrupter)
+{
+ clear();
+ mSignSweeps = std::min(mSignSweeps, 1);
+}
+
+
+template<typename DistGridT, typename InterruptT>
+void
+MeshToVolume<DistGridT, InterruptT>::clear()
+{
+ mDistGrid = DistGridT::create(std::numeric_limits<DistValueT>::max());
+ mIndexGrid = IndexGridT::create(Int32(util::INVALID_IDX));
+ mIntersectingVoxelsGrid = StencilGridT::create(false);
+}
+
+
+template<typename DistGridT, typename InterruptT>
+inline void
+MeshToVolume<DistGridT, InterruptT>::convertToLevelSet(
+ const std::vector<Vec3s>& pointList, const std::vector<Vec4I>& polygonList,
+ DistValueT exBandWidth, DistValueT inBandWidth)
+{
+ // The narrow band width is exclusive, the shortest valid distance has to be > 1 voxel
+ exBandWidth = std::max(internal::Tolerance<DistValueT>::minNarrowBandWidth(), exBandWidth);
+ inBandWidth = std::max(internal::Tolerance<DistValueT>::minNarrowBandWidth(), inBandWidth);
+ const DistValueT vs = mTransform->voxelSize()[0];
+ doConvert(pointList, polygonList, vs * exBandWidth, vs * inBandWidth);
+ mDistGrid->setGridClass(GRID_LEVEL_SET);
+}
+
+
+template<typename DistGridT, typename InterruptT>
+inline void
+MeshToVolume<DistGridT, InterruptT>::convertToUnsignedDistanceField(
+ const std::vector<Vec3s>& pointList, const std::vector<Vec4I>& polygonList,
+ DistValueT exBandWidth)
+{
+ // The narrow band width is exclusive, the shortest valid distance has to be > 1 voxel
+ exBandWidth = std::max(internal::Tolerance<DistValueT>::minNarrowBandWidth(), exBandWidth);
+ const DistValueT vs = mTransform->voxelSize()[0];
+ doConvert(pointList, polygonList, vs * exBandWidth, 0.0, true);
+ mDistGrid->setGridClass(GRID_UNKNOWN);
+}
+
+
+template<typename DistGridT, typename InterruptT>
+void
+MeshToVolume<DistGridT, InterruptT>::doConvert(
+ const std::vector<Vec3s>& pointList, const std::vector<Vec4I>& polygonList,
+ DistValueT exBandWidth, DistValueT inBandWidth, bool unsignedDistField)
+{
+ mDistGrid->setTransform(mTransform);
+ mIndexGrid->setTransform(mTransform);
+
+ if (mInterrupter && mInterrupter->wasInterrupted()) return;
+
+ // Voxelize mesh
+ {
+ internal::MeshVoxelizer<DistTreeT, InterruptT>
+ voxelizer(pointList, polygonList, mInterrupter);
+
+ voxelizer.runParallel();
+
+ if (mInterrupter && mInterrupter->wasInterrupted()) return;
+
+ mDistGrid->tree().merge(voxelizer.sqrDistTree());
+ mIndexGrid->tree().merge(voxelizer.primIndexTree());
+ mIntersectingVoxelsGrid->tree().merge(voxelizer.intersectionTree());
+ }
+
+ if (!unsignedDistField) {
+
+ // Determine the inside/outside state for the narrow band of voxels.
+ {
+ // Slices up the volume and label the exterior contour of each slice in parallel.
+ internal::ContourTracer<DistTreeT, InterruptT> trace(
+ mDistGrid->tree(), mIntersectingVoxelsGrid->tree(), mInterrupter);
+
+ for (int i = 0; i < mSignSweeps; ++i) {
+
+ if (mInterrupter && mInterrupter->wasInterrupted()) break;
+ trace.runParallel();
+
+ if (mInterrupter && mInterrupter->wasInterrupted()) break;
+
+ // Propagate sign information between the slices.
+ internal::propagateSign<DistTreeT, InterruptT>
+ (mDistGrid->tree(), mIntersectingVoxelsGrid->tree(), mInterrupter);
+ }
+ }
+
+ if (mInterrupter && mInterrupter->wasInterrupted()) return;
+
+ {
+ tree::LeafManager<StencilTreeT> leafs(mIntersectingVoxelsGrid->tree());
+
+ // Determine the sign of the mesh intersecting voxels.
+ internal::IntersectingVoxelSign<DistTreeT> sign(pointList, polygonList,
+ mDistGrid->tree(), mIndexGrid->tree(), mIntersectingVoxelsGrid->tree(), leafs);
+
+ sign.runParallel();
+
+ if (mInterrupter && mInterrupter->wasInterrupted()) return;
+
+ // Remove mesh intersecting voxels that where set by rasterizing
+ // self-intersecting portions of the mesh.
+ internal::IntersectingVoxelCleaner<DistTreeT> cleaner(mDistGrid->tree(),
+ mIndexGrid->tree(), mIntersectingVoxelsGrid->tree(), leafs);
+
+
+ cleaner.runParallel();
+ }
+
+ if (mInterrupter && mInterrupter->wasInterrupted()) return;
+
+ {
+ // Remove shell voxels that where set by rasterizing
+ // self-intersecting portions of the mesh.
+
+ tree::LeafManager<DistTreeT> leafs(mDistGrid->tree());
+
+ internal::ShellVoxelCleaner<DistTreeT> cleaner(mDistGrid->tree(),
+ leafs, mIndexGrid->tree(), mIntersectingVoxelsGrid->tree());
+
+ cleaner.runParallel();
+ }
+
+ if (mInterrupter && mInterrupter->wasInterrupted()) return;
+
+ } else { // if unsigned dist. field
+ inBandWidth = DistValueT(0.0);
+ }
+
+ if (mDistGrid->activeVoxelCount() == 0) return;
+
+ mIntersectingVoxelsGrid->clear();
+ const DistValueT voxelSize(mTransform->voxelSize()[0]);
+
+ // Transform values (world space scaling etc.)
+ {
+ typedef internal::SqrtAndScaleOp<DistValueT> XForm;
+ tree::LeafManager<DistTreeT> leafs(mDistGrid->tree());
+ XForm op(voxelSize, unsignedDistField);
+ LeafTransformer<DistTreeT, XForm> transform(leafs, op);
+
+ if (mInterrupter && mInterrupter->wasInterrupted()) return;
+
+ transform.runParallel();
+ }
+
+ if (mInterrupter && mInterrupter->wasInterrupted()) return;
+
+ if (!unsignedDistField) {
+ // Propagate sign information to inactive values.
+ mDistGrid->tree().signedFloodFill();
+
+ if (mInterrupter && mInterrupter->wasInterrupted()) return;
+
+ // Update the background value (inactive values)
+ tree::LeafManager<DistTreeT> leafs(mDistGrid->tree());
+
+ typedef internal::VoxelSignOp<DistValueT> SignXForm;
+ SignXForm op(exBandWidth, inBandWidth);
+
+ LeafTransformer<DistTreeT, SignXForm> transform(leafs, op);
+ transform.runParallel();
+
+ if (mInterrupter && mInterrupter->wasInterrupted()) return;
+
+ DistValueT bgValues[2];
+ bgValues[0] = exBandWidth;
+ bgValues[1] = -inBandWidth;
+
+ typename DistTreeT::ValueAllIter tileIt(mDistGrid->tree());
+ tileIt.setMaxDepth(DistTreeT::ValueAllIter::LEAF_DEPTH - 1);
+
+ for ( ; tileIt; ++tileIt) {
+ DistValueT& val = const_cast<DistValueT&>(tileIt.getValue());
+ val = bgValues[int(val < DistValueT(0.0))];
+ }
+
+ if (mInterrupter && mInterrupter->wasInterrupted()) return;
+
+ // fast bg value swap
+ typename DistTreeT::Ptr newTree(new DistTreeT(/*background=*/exBandWidth));
+ newTree->merge(mDistGrid->tree());
+ mDistGrid->setTree(newTree);
+ }
+
+ if (mInterrupter && mInterrupter->wasInterrupted()) return;
+
+ // Narrow-band dilation
+ const DistValueT minWidth = voxelSize * 2.0;
+ if (inBandWidth > minWidth || exBandWidth > minWidth) {
+
+ // Create the initial voxel mask.
+ StencilTreeT maskTree(false);
+ tree::ValueAccessor<StencilTreeT> acc(maskTree);
+ maskTree.topologyUnion(mDistGrid->tree());
+
+ // Preallocate leafs.
+ {
+ typedef typename DistTreeT::LeafNodeType DistLeafType;
+
+ std::vector<DistLeafType*> distLeafs;
+ distLeafs.reserve(mDistGrid->tree().leafCount());
+
+ typename DistTreeT::LeafIter iter = mDistGrid->tree().beginLeaf();
+ for ( ; iter; ++iter) distLeafs.push_back(iter.getLeaf());
+
+ tree::ValueAccessor<DistTreeT> distAcc(mDistGrid->tree());
+
+ DistValueT leafSize = DistValueT(DistLeafType::DIM - 1) * voxelSize;
+
+ const double inLeafsRatio = double(inBandWidth) / double(leafSize);
+ size_t inLeafs = std::numeric_limits<size_t>::max();
+ if (double(inLeafs) > (inLeafsRatio + 1.0)) {
+ inLeafs = size_t(std::ceil(inLeafsRatio)) + 1;
+ }
+ size_t exLeafs = size_t(std::ceil(exBandWidth / leafSize)) + 1;
+ size_t numLeafs = std::max(inLeafs, exLeafs);
+
+ for (size_t i = 0; i < numLeafs; ++i) {
+
+ if (mInterrupter && mInterrupter->wasInterrupted()) return;
+
+ std::vector<DistLeafType*> newDistLeafs;
+ newDistLeafs.reserve(2 * distLeafs.size());
+
+ for (size_t n = 0, N = distLeafs.size(); n < N; ++n) {
+
+ Coord ijk = distLeafs[n]->getOrigin();
+
+ const bool inside = distLeafs[n]->getValue(ijk) < DistValueT(0.0);
+
+ if (inside && !(i < inLeafs)) continue;
+ else if (!inside && !(i < exLeafs)) continue;
+
+ ijk[0] -= 1;
+ if (distAcc.probeLeaf(ijk) == NULL) {
+ newDistLeafs.push_back(distAcc.touchLeaf(ijk));
+ }
+
+ ijk[0] += 1;
+ ijk[1] -= 1;
+ if (distAcc.probeLeaf(ijk) == NULL) {
+ newDistLeafs.push_back(distAcc.touchLeaf(ijk));
+ }
+
+ ijk[1] += 1;
+ ijk[2] -= 1;
+ if (distAcc.probeLeaf(ijk) == NULL) {
+ newDistLeafs.push_back(distAcc.touchLeaf(ijk));
+ }
+
+ ijk[2] += 1;
+ ijk[0] += DistLeafType::DIM;
+ if (distAcc.probeLeaf(ijk) == NULL) {
+ newDistLeafs.push_back(distAcc.touchLeaf(ijk));
+ }
+
+ ijk[0] -= DistLeafType::DIM;
+ ijk[1] += DistLeafType::DIM;
+ if (distAcc.probeLeaf(ijk) == NULL) {
+ newDistLeafs.push_back(distAcc.touchLeaf(ijk));
+ }
+
+ ijk[1] -= DistLeafType::DIM;
+ ijk[2] += DistLeafType::DIM;
+ if (distAcc.probeLeaf(ijk) == NULL) {
+ newDistLeafs.push_back(distAcc.touchLeaf(ijk));
+ }
+ }
+
+ if (newDistLeafs.empty()) break;
+ distLeafs.swap(newDistLeafs);
+ }
+ }
+
+ if (mInterrupter && mInterrupter->wasInterrupted()) return;
+
+ mIndexGrid->tree().topologyUnion(mDistGrid->tree());
+
+ typedef internal::LeafTopologyDiffOp<DistTreeT> TopologyDiffOp;
+ TopologyDiffOp diffOp(mDistGrid->tree());
+
+ while (maskTree.activeVoxelCount() > 0) {
+
+ if (mInterrupter && mInterrupter->wasInterrupted()) break;
+
+ openvdb::tools::dilateVoxels(maskTree);
+ tree::LeafManager<StencilTreeT> leafs(maskTree);
+
+ LeafTransformer<StencilTreeT, TopologyDiffOp> diff(leafs, diffOp);
+ diff.runParallel();
+
+ internal::ExpandNB<DistTreeT> expand(pointList, polygonList, mDistGrid->tree(),
+ mIndexGrid->tree(), maskTree, leafs, exBandWidth, inBandWidth, voxelSize);
+
+ expand.runParallel();
+ }
+ }
+
+ if (!bool(GENERATE_PRIM_INDEX_GRID & mConversionFlags)) mIndexGrid->clear();
+
+ // Smooth out bumps caused by self-intersecting and overlapping portions
+ // of the mesh and renormalize the level set.
+ if (!unsignedDistField) {
+ typedef internal::OffsetOp<DistValueT> OffsetOp;
+ typedef internal::RenormOp<DistGridT, DistValueT> RenormOp;
+ typedef internal::MinOp<DistTreeT, DistValueT> MinOp;
+ typedef internal::MergeBufferOp<DistTreeT, DistValueT> MergeBufferOp;
+
+ tree::LeafManager<DistTreeT> leafs(mDistGrid->tree(), 1);
+
+ const DistValueT offset = 0.8 * voxelSize;
+
+ if (mInterrupter && mInterrupter->wasInterrupted()) return;
+
+ OffsetOp offsetOp(-offset);
+ LeafTransformer<DistTreeT, OffsetOp> offsetXform(leafs, offsetOp);
+ offsetXform.runParallel();
+
+ if (mInterrupter && mInterrupter->wasInterrupted()) return;
+
+ RenormOp renormOp(*mDistGrid, leafs, voxelSize);
+ LeafTransformer<DistTreeT, RenormOp> renormXform(leafs, renormOp);
+ renormXform.runParallel();
+
+ MinOp minOp(leafs);
+ LeafTransformer<DistTreeT, MinOp> minXform(leafs, minOp);
+ minXform.runParallel();
+
+ if (mInterrupter && mInterrupter->wasInterrupted()) return;
+
+ offsetOp.resetOffset(offset - internal::Tolerance<DistValueT>::epsilon());
+ offsetXform.runParallel();
+ }
+
+ const DistValueT minTrimWidth = voxelSize * 4.0;
+ if (inBandWidth < minTrimWidth || exBandWidth < minTrimWidth) {
+
+ // If the narrow band was not expanded, we might need to trim off
+ // some of the active voxels in order to respect the narrow band limits.
+ // (The mesh voxelization step generates some extra 'shell' voxels)
+
+ tree::LeafManager<DistTreeT> leafs(mDistGrid->tree());
+
+ typedef internal::TrimOp<DistValueT> TrimOp;
+
+ TrimOp op(exBandWidth, unsignedDistField ? exBandWidth : inBandWidth);
+ LeafTransformer<DistTreeT, TrimOp> transform(leafs, op);
+ transform.runParallel();
+ }
+
+ if (mInterrupter && mInterrupter->wasInterrupted()) return;
+
+ mDistGrid->tree().pruneLevelSet();
+}
+
+
+////////////////////////////////////////
+
+
+/// @internal This overload is enabled only for grids with a scalar, floating-point ValueType.
+template<typename GridType>
+inline typename boost::enable_if<boost::is_floating_point<typename GridType::ValueType>,
+typename GridType::Ptr>::type
+doMeshConversion(
+ const openvdb::math::Transform& xform,
+ const std::vector<Vec3s>& points,
+ const std::vector<Vec3I>& triangles,
+ const std::vector<Vec4I>& quads,
+ float exBandWidth,
+ float inBandWidth,
+ bool unsignedDistanceField = false)
+{
+ std::vector<Vec3s> indexSpacePoints(points.size());
+
+ { // Copy and transform (required for MeshToVolume) points to grid space.
+ internal::PointTransform ptnXForm(points, indexSpacePoints, xform);
+ ptnXForm.runParallel();
+ }
+
+ // Copy primitives
+ std::vector<Vec4I> primitives(triangles.size() + quads.size());
+
+ for (size_t n = 0, N = triangles.size(); n < N; ++n) {
+ Vec4I& prim = primitives[n];
+ const Vec3I& triangle = triangles[n];
+ prim[0] = triangle[0];
+ prim[1] = triangle[1];
+ prim[2] = triangle[2];
+ prim[3] = util::INVALID_IDX;
+ }
+
+ for (size_t n = 0, N = quads.size(); n < N; ++n) {
+ primitives[n + triangles.size()] = quads[n];
+ }
+
+ typename GridType::ValueType exWidth(exBandWidth);
+ typename GridType::ValueType inWidth(inBandWidth);
+
+
+ math::Transform::Ptr transform = xform.copy();
+ MeshToVolume<GridType> vol(transform);
+
+ if (!unsignedDistanceField) {
+ vol.convertToLevelSet(indexSpacePoints, primitives, exWidth, inWidth);
+ } else {
+ vol.convertToUnsignedDistanceField(indexSpacePoints, primitives, exWidth);
+ }
+
+ return vol.distGridPtr();
+}
+
+
+/// @internal This overload is enabled only for grids that do not have a scalar,
+/// floating-point ValueType.
+template<typename GridType>
+inline typename boost::disable_if<boost::is_floating_point<typename GridType::ValueType>,
+typename GridType::Ptr>::type
+doMeshConversion(
+ const math::Transform& /*xform*/,
+ const std::vector<Vec3s>& /*points*/,
+ const std::vector<Vec3I>& /*triangles*/,
+ const std::vector<Vec4I>& /*quads*/,
+ float /*exBandWidth*/,
+ float /*inBandWidth*/,
+ bool unsignedDistanceField = false)
+{
+ OPENVDB_THROW(TypeError,
+ "mesh to volume conversion is supported only for scalar, floating-point grids");
+}
+
+
+////////////////////////////////////////
+
+
+template<typename GridType>
+inline typename GridType::Ptr
+meshToLevelSet(
+ const openvdb::math::Transform& xform,
+ const std::vector<Vec3s>& points,
+ const std::vector<Vec3I>& triangles,
+ float halfWidth)
+{
+ std::vector<Vec4I> quads(0);
+ return doMeshConversion<GridType>(xform, points, triangles, quads,
+ halfWidth, halfWidth);
+}
+
+
+template<typename GridType>
+inline typename GridType::Ptr
+meshToLevelSet(
+ const openvdb::math::Transform& xform,
+ const std::vector<Vec3s>& points,
+ const std::vector<Vec4I>& quads,
+ float halfWidth)
+{
+ std::vector<Vec3I> triangles(0);
+ return doMeshConversion<GridType>(xform, points, triangles, quads,
+ halfWidth, halfWidth);
+}
+
+
+template<typename GridType>
+inline typename GridType::Ptr
+meshToLevelSet(
+ const openvdb::math::Transform& xform,
+ const std::vector<Vec3s>& points,
+ const std::vector<Vec3I>& triangles,
+ const std::vector<Vec4I>& quads,
+ float halfWidth)
+{
+ return doMeshConversion<GridType>(xform, points, triangles, quads,
+ halfWidth, halfWidth);
+}
+
+
+template<typename GridType>
+inline typename GridType::Ptr
+meshToSignedDistanceField(
+ const openvdb::math::Transform& xform,
+ const std::vector<Vec3s>& points,
+ const std::vector<Vec3I>& triangles,
+ const std::vector<Vec4I>& quads,
+ float exBandWidth,
+ float inBandWidth)
+{
+ return doMeshConversion<GridType>(xform, points, triangles,
+ quads, exBandWidth, inBandWidth);
+}
+
+
+template<typename GridType>
+inline typename GridType::Ptr
+meshToUnsignedDistanceField(
+ const openvdb::math::Transform& xform,
+ const std::vector<Vec3s>& points,
+ const std::vector<Vec3I>& triangles,
+ const std::vector<Vec4I>& quads,
+ float bandWidth)
+{
+ return doMeshConversion<GridType>(xform, points, triangles, quads,
+ bandWidth, bandWidth, true);
+}
+
+
+} // namespace tools
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_TOOLS_MESH_TO_VOLUME_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/tools/Morphology.h b/extern/openvdb/internal/openvdb/tools/Morphology.h
new file mode 100644
index 00000000000..f99d9934606
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/tools/Morphology.h
@@ -0,0 +1,359 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+//
+///////////////////////////////////////////////////////////////////////////
+
+#ifndef OPENVDB_TOOLS_MORPHOLOGY_HAS_BEEN_INCLUDED
+#define OPENVDB_TOOLS_MORPHOLOGY_HAS_BEEN_INCLUDED
+
+#include <openvdb/Types.h>
+#include <openvdb/tree/TreeIterator.h>
+#include <openvdb/tree/ValueAccessor.h>
+#include <openvdb/tree/LeafManager.h>
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace tools {
+
+//@{
+/// Topologically dilate all leaf-level active voxels in the given tree,
+/// i.e., expand the set of active voxels by @a count voxels in the +x, -x,
+/// +y, -y, +z and -z directions, but don't change the values of any voxels,
+/// only their active states.
+/// @todo Currently operates only on leaf voxels; need to extend to tiles.
+template<typename TreeType> OPENVDB_STATIC_SPECIALIZATION
+inline void dilateVoxels(TreeType& tree, int count=1);
+
+template<typename TreeType> OPENVDB_STATIC_SPECIALIZATION
+inline void dilateVoxels(tree::LeafManager<TreeType>& manager, int count = 1);
+//@}
+
+//@{
+/// Topologically erode all leaf-level active voxels in the given tree,
+/// i.e., shrink the set of active voxels by @a count voxels in the +x, -x,
+/// +y, -y, +z and -z directions, but don't change the values of any voxels,
+/// only their active states.
+/// @todo Currently operates only on leaf voxels; need to extend to tiles.
+template<typename TreeType> OPENVDB_STATIC_SPECIALIZATION
+inline void erodeVoxels(TreeType& tree, int count=1);
+
+template<typename TreeType> OPENVDB_STATIC_SPECIALIZATION
+inline void erodeVoxels(tree::LeafManager<TreeType>& manager, int count = 1);
+//@}
+
+////////////////////////////////////////
+
+/// Mapping from a Log2Dim to a data type of size 2^Log2Dim bits
+template<Index Log2Dim> struct DimToWord { typedef uint8_t Type[0]; };
+template<> struct DimToWord<3> { typedef uint8_t Type; };
+template<> struct DimToWord<4> { typedef uint16_t Type; };
+template<> struct DimToWord<5> { typedef uint32_t Type; };
+template<> struct DimToWord<6> { typedef uint64_t Type; };
+////////////////////////////////////////
+
+template<typename TreeType>
+class Morphology
+{
+ public:
+
+ typedef tree::LeafManager<TreeType> ManagerType;
+
+ Morphology(TreeType& tree) : mOwnsManager(true), mManager(new ManagerType(tree)), mAcc(tree), mSteps(1) {}
+ Morphology(ManagerType* mgr) : mOwnsManager(false), mManager(mgr), mAcc(mgr->tree()), mSteps(1) {}
+ virtual ~Morphology() { if (mOwnsManager) delete mManager; }
+ void dilateVoxels();
+ void dilateVoxels(int count) { for (int i=0; i<count; ++i) this->dilateVoxels(); }
+ void erodeVoxels(int count = 1) { mSteps = count; this->doErosion(); }
+
+ private:
+
+ void doErosion();
+
+ typedef typename TreeType::LeafNodeType LeafType;
+ typedef typename LeafType::NodeMaskType MaskType;
+ typedef tree::ValueAccessor<TreeType> AccessorType;
+
+ const bool mOwnsManager;
+ ManagerType* mManager;
+ AccessorType mAcc;
+ int mSteps;
+
+ static const int LEAF_DIM = LeafType::DIM;
+ static const int LEAF_LOG2DIM = LeafType::LOG2DIM;
+ typedef typename DimToWord<LEAF_LOG2DIM>::Type Word;
+
+ struct Neighbor {
+ LeafType* leaf;//null if a tile
+ bool init;//true if initialization is required
+ bool isOn;//true if an active tile
+ Neighbor() : leaf(NULL), init(true) {}
+ inline void clear() { init = true; }
+ template<int DX, int DY, int DZ>
+ void scatter(AccessorType& acc, const Coord &xyz, int indx, Word oldWord)
+ {
+ if (init) {
+ init = false;
+ Coord orig = xyz.offsetBy(DX*LEAF_DIM, DY*LEAF_DIM, DZ*LEAF_DIM);
+ leaf = acc.probeLeaf(orig);
+ if (leaf==NULL && !acc.isValueOn(orig)) leaf = acc.touchLeaf(orig);
+ }
+ static const int N = (LEAF_DIM -1 )*(DY + DX*LEAF_DIM);
+ if (leaf) leaf->getValueMask().template getWord<Word>(indx-N) |= oldWord;
+ }
+ template<int DX, int DY, int DZ>
+ Word gather(AccessorType& acc, const Coord &xyz, int indx)
+ {
+ if (init) {
+ init = false;
+ Coord orig = xyz.offsetBy(DX*LEAF_DIM, DY*LEAF_DIM, DZ*LEAF_DIM);
+ leaf = acc.probeLeaf(orig);
+ isOn = leaf ? false : acc.isValueOn(orig);
+ }
+ static const int N = (LEAF_DIM -1 )*(DY + DX*LEAF_DIM);
+ return leaf ? leaf->getValueMask().template getWord<Word>(indx-N) : isOn ? ~Word(0) : Word(0);
+ }
+ };// Neighbor
+
+
+ struct ErodeVoxelsOp {
+ ErodeVoxelsOp(std::vector<MaskType>& masks, ManagerType& manager)
+ : mSavedMasks(masks) , mManager(manager) {}
+
+ void runParallel() { tbb::parallel_for(mManager.getRange(), *this); }
+ void operator()(const tbb::blocked_range<size_t>& range) const;
+
+ private:
+ std::vector<MaskType>& mSavedMasks;
+ ManagerType& mManager;
+ };// ErodeVoxelsOp
+
+
+ struct MaskManager {
+ MaskManager(std::vector<MaskType>& masks, ManagerType& manager)
+ : mMasks(masks) , mManager(manager), mSaveMasks(true) {}
+
+ void save() { mSaveMasks = true; tbb::parallel_for(mManager.getRange(), *this); }
+ void update() { mSaveMasks = false; tbb::parallel_for(mManager.getRange(), *this); }
+ void operator()(const tbb::blocked_range<size_t>& range) const
+ {
+ if (mSaveMasks) {
+ for (size_t i = range.begin(); i < range.end(); ++i) {
+ mMasks[i] = mManager.leaf(i).getValueMask();
+ }
+ } else {
+ for (size_t i = range.begin(); i < range.end(); ++i) {
+ mManager.leaf(i).setValueMask(mMasks[i]);
+ }
+ }
+ }
+
+ private:
+ std::vector<MaskType>& mMasks;
+ ManagerType& mManager;
+ bool mSaveMasks;
+ };// MaskManager
+};
+
+template <typename TreeType>
+void Morphology<TreeType>::dilateVoxels()
+{
+ /// @todo Currently operates only on leaf voxels; need to extend to tiles.
+ const int leafCount = mManager->leafCount();
+
+ // Save the value masks of all leaf nodes.
+ std::vector<MaskType> savedMasks(leafCount);
+ MaskManager masks(savedMasks, *mManager);
+ masks.save();
+
+ Neighbor NN[6];
+ Coord origin;
+ for (int leafIdx = 0; leafIdx < leafCount; ++leafIdx) {
+ const MaskType& oldMask = savedMasks[leafIdx];//original bit-mask of current leaf node
+ LeafType& leaf = mManager->leaf(leafIdx);//current leaf node
+ leaf.getOrigin(origin);// origin of the current leaf node.
+ for (int x = 0; x < LEAF_DIM; ++x ) {
+ for (int y = 0, n = (x << LEAF_LOG2DIM); y < LEAF_DIM; ++y, ++n) {
+ // Extract the portion of the original mask that corresponds to a row in z.
+ const Word oldWord = oldMask.template getWord<Word>(n);
+ if (oldWord == 0) continue; // no active voxels
+
+ // dilate current leaf or neighbor in negative x-direction
+ if (x > 0) {
+ leaf.getValueMask().template getWord<Word>(n-LEAF_DIM) |= oldWord;
+ } else {
+ NN[0].template scatter<-1, 0, 0>(mAcc, origin, n, oldWord);
+ }
+ // dilate current leaf or neighbor in positive x-direction
+ if (x < LEAF_DIM - 1) {
+ leaf.getValueMask().template getWord<Word>(n+LEAF_DIM) |= oldWord;
+ } else {
+ NN[1].template scatter< 1, 0, 0>(mAcc, origin, n, oldWord);
+ }
+ // dilate current leaf or neighbor in negative y-direction
+ if (y > 0) {
+ leaf.getValueMask().template getWord<Word>(n-1) |= oldWord;
+ } else {
+ NN[2].template scatter< 0,-1, 0>(mAcc, origin, n, oldWord);
+ }
+ // dilate current leaf or neighbor in positive y-direction
+ if (y < LEAF_DIM - 1) {
+ leaf.getValueMask().template getWord<Word>(n+1) |= oldWord;
+ } else {
+ NN[3].template scatter< 0, 1, 0>(mAcc, origin, n, oldWord);
+ }
+ // Dilate the current leaf node in the z direction by ORing its mask
+ // with itself shifted first left and then right by one bit.
+ leaf.getValueMask().template getWord<Word>(n) |= (oldWord >> 1) | (oldWord << 1);
+ // dilate neighbor in negative z-direction
+ if (Word w = oldWord<<(LEAF_DIM-1)) {
+ NN[4].template scatter< 0, 0,-1>(mAcc, origin, n, w);
+ }
+ // dilate neighbot in positive z-direction
+ if (Word w = oldWord>>(LEAF_DIM-1)) {
+ NN[5].template scatter< 0, 0, 1>(mAcc, origin, n, w);
+ }
+ }// loop over y
+ }//loop over x
+ for (int i=0; i<6; ++i) NN[i].clear();
+ }//loop over leafs
+
+ mManager->rebuildLeafArray();
+}
+
+
+template <typename TreeType>
+void
+Morphology<TreeType>::ErodeVoxelsOp::operator()(const tbb::blocked_range<size_t>& range) const
+{
+ AccessorType acc(mManager.tree());
+ Neighbor NN[6];
+ Coord origin;
+ for (size_t leafIdx = range.begin(); leafIdx < range.end(); ++leafIdx) {
+ LeafType& leaf = mManager.leaf(leafIdx);//current leaf node
+ if (leaf.isEmpty()) continue;
+ MaskType& newMask = mSavedMasks[leafIdx];//original bit-mask of current leaf node
+ leaf.getOrigin(origin);// origin of the current leaf node.
+ for (int x = 0; x < LEAF_DIM; ++x ) {
+ for (int y = 0, n = (x << LEAF_LOG2DIM); y < LEAF_DIM; ++y, ++n) {
+ // Extract the portion of the original mask that corresponds to a row in z.
+ Word& w = newMask.template getWord<Word>(n);
+ if (w == 0) continue; // no active voxels
+
+ // Erode in two z directions (this is first since it uses the original w)
+ w &= (w<<1 | (NN[4].template gather<0,0,-1>(acc, origin, n)>>(LEAF_DIM-1))) &
+ (w>>1 | (NN[5].template gather<0,0, 1>(acc, origin, n)<<(LEAF_DIM-1)));
+
+ // dilate current leaf or neighbor in negative x-direction
+ w &= (x == 0) ? NN[0].template gather<-1, 0, 0>(acc, origin, n) :
+ leaf.getValueMask().template getWord<Word>(n-LEAF_DIM);
+
+ // dilate current leaf or neighbor in positive x-direction
+ w &= (x == LEAF_DIM-1) ? NN[1].template gather< 1, 0, 0>(acc, origin, n) :
+ leaf.getValueMask().template getWord<Word>(n+LEAF_DIM);
+
+ // dilate current leaf or neighbor in negative y-direction
+ w &= (y == 0) ? NN[2].template gather< 0,-1, 0>(acc, origin, n) :
+ leaf.getValueMask().template getWord<Word>(n-1);
+
+ // dilate current leaf or neighbor in positive y-direction
+ w &= (y == LEAF_DIM-1) ? NN[3].template gather< 0, 1, 0>(acc, origin, n) :
+ leaf.getValueMask().template getWord<Word>(n+1);
+ }// loop over y
+ }//loop over x
+ for (int i=0; i<6; ++i) NN[i].clear();
+ }//loop over leafs
+}
+
+
+template <typename TreeType>
+void Morphology<TreeType>::doErosion()
+{
+ /// @todo Currently operates only on leaf voxels; need to extend to tiles.
+ const int leafCount = mManager->leafCount();
+
+ // Save the value masks of all leaf nodes.
+ std::vector<MaskType> savedMasks(leafCount);
+ MaskManager masks(savedMasks, *mManager);
+ masks.save();
+
+ ErodeVoxelsOp erode(savedMasks, *mManager);
+ for (int i = 0; i < mSteps; ++i) {
+ erode.runParallel();
+ masks.update();
+ }
+
+ mManager->tree().pruneLevelSet();
+}
+
+
+////////////////////////////////////////
+
+template<typename TreeType>
+OPENVDB_STATIC_SPECIALIZATION inline void
+dilateVoxels(tree::LeafManager<TreeType>& manager, int count)
+{
+ Morphology<TreeType> m(&manager);
+ m.dilateVoxels(count);
+}
+
+template<typename TreeType>
+OPENVDB_STATIC_SPECIALIZATION inline void
+dilateVoxels(TreeType& tree, int count)
+{
+ Morphology<TreeType> m(tree);
+ m.dilateVoxels(count);
+}
+
+template<typename TreeType>
+OPENVDB_STATIC_SPECIALIZATION inline void
+erodeVoxels(tree::LeafManager<TreeType>& manager, int count)
+{
+ Morphology<TreeType> m(&manager);
+ m.erodeVoxels(count);
+}
+
+template<typename TreeType>
+OPENVDB_STATIC_SPECIALIZATION inline void
+erodeVoxels(TreeType& tree, int count)
+{
+ Morphology<TreeType> m(tree);
+ m.erodeVoxels(count);
+}
+
+} // namespace tools
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_TOOLS_MORPHOLOGY_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/tools/ParticlesToLevelSet.h b/extern/openvdb/internal/openvdb/tools/ParticlesToLevelSet.h
new file mode 100644
index 00000000000..a0f03262aef
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/tools/ParticlesToLevelSet.h
@@ -0,0 +1,613 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+//
+/// @author Ken Museth
+///
+/// @file ParticlesToLevelSet.h
+///
+/// @brief This tool rasterizes particles (with position, radius and velocity)
+/// into a narrow-band level set.
+///
+/// @note This fast particle to level set converter is always intended
+/// to be combined with some kind of surface post processing,
+/// i.e. tools::Filter. Without such post processing the generated
+/// surface is typically too noisy and blooby. However it serves as a
+/// great and fast starting point for subsequent level set surface
+/// processing and convolution. In the near future we will add support
+/// for anisotropic particle kernels.
+///
+/// The @c ParticleListT template argument below refers to any class
+/// with the following interface (see unittest/TestParticlesToLevelSet.cc
+/// and SOP_DW_OpenVDBParticleVoxelizer for practical examples):
+/// @code
+/// class ParticleList {
+/// ...
+/// public:
+/// openvdb::Index size() const;// number of particles in list
+/// openvdb::Vec3R pos(int n) const;// world space position of n'th particle
+/// openvdb::Vec3R vel(int n) const;// world space velocity of n'th particle
+/// openvdb::Real radius(int n) const;// world space radius of n'th particle
+/// };
+/// @endcode
+///
+/// @note All methods are assumed to be thread-safe.
+/// Also note all access methods return by value
+/// since this allows for especailly the radius and velocities to be
+/// scaled (i.e. modified) relative to the internal representations
+/// (see unittest/TestParticlesToLevelSet.cc for an example).
+///
+/// The @c InterruptT template argument below refers to any class
+/// with the following interface:
+/// @code
+/// class Interrupter {
+/// ...
+/// public:
+/// void start(const char* name = NULL)// called when computations begin
+/// void end() // called when computations end
+/// bool wasInterrupted(int percent=-1)// return true to break computation
+/// };
+/// @endcode
+///
+/// @note If no template argument is provided for this InterruptT
+/// the util::NullInterrupter is used which implies that all
+/// interrupter calls are no-ops (i.e. incurs no computational overhead).
+
+#ifndef OPENVDB_TOOLS_PARTICLES_TO_LEVELSET_HAS_BEEN_INCLUDED
+#define OPENVDB_TOOLS_PARTICLES_TO_LEVELSET_HAS_BEEN_INCLUDED
+
+#include <tbb/parallel_reduce.h>
+#include <tbb/blocked_range.h>
+#include <boost/bind.hpp>
+#include <boost/function.hpp>
+#include <openvdb/util/Util.h>
+#include <openvdb/Types.h>
+#include <openvdb/Grid.h>
+#include <openvdb/math/Math.h>
+#include <openvdb/math/Transform.h>
+#include <openvdb/util/NullInterrupter.h>
+#include "Composite.h" // for csgUnion()
+
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace tools {
+namespace local {
+/// Trait class needed to merge and split a distance and a particle id required
+/// during attribute transfer. The default implementation of merge simply
+/// ignores the particle id. A specialized implementation is given
+/// below for a Dual type that holds both a distance and a particle id.
+template <typename T>
+struct DualTrait
+{
+ static T merge(T dist, Index32) { return dist; }
+ static T split(T dist) { return dist; }
+};
+}// namespace local
+
+template<typename GridT,
+ typename ParticleListT,
+ typename InterruptT=util::NullInterrupter,
+ typename RealT = typename GridT::ValueType>
+class ParticlesToLevelSet
+{
+public:
+ /// @brief Main constructor using a default interrupter
+ ///
+ /// @param grid contains the grid in which particles are rasterized
+ /// @param interrupt callback to interrupt a long-running process
+ ///
+ /// @note The width in voxel units of the generated narrow band level set is
+ /// given by 2*background/dx, where background is the background value
+ /// stored in the grid, and dx is the voxel size derived from the
+ /// transform stored in the grid. Also note that -background
+ /// corresponds to the constant value inside the generated narrow
+ /// band level sets. Finally the default NullInterrupter should
+ /// compile out interruption checks during optimization, thus
+ /// incurring no run-time overhead.
+ ///
+ ParticlesToLevelSet(GridT& grid, InterruptT* interrupt = NULL) :
+ mGrid(&grid),
+ mPa(NULL),
+ mDx(grid.transform().voxelSize()[0]),
+ mHalfWidth(local::DualTrait<ValueT>::split(grid.background()) / mDx),
+ mRmin(1.5),// corresponds to the Nyquist grid sampling frequency
+ mRmax(100.0),// corresponds to a huge particle (probably too large!)
+ mGrainSize(1),
+ mInterrupter(interrupt),
+ mMinCount(0),
+ mMaxCount(0),
+ mIsSlave(false)
+ {
+ if ( !mGrid->hasUniformVoxels() ) {
+ OPENVDB_THROW(RuntimeError,
+ "The transform must have uniform scale for ParticlesToLevelSet to function!");
+ }
+ if (mGrid->getGridClass() != GRID_LEVEL_SET) {
+ OPENVDB_THROW(RuntimeError,
+ "ParticlesToLevelSet only supports level sets!"
+ "\nUse Grid::setGridClass(openvdb::GRID_LEVEL_SET)");
+ }
+ /// @todo create a new tree rather than CSG into an existing tree
+ //mGrid->newTree();
+ }
+ /// @brief Copy constructor called by tbb
+ ParticlesToLevelSet(ParticlesToLevelSet& other, tbb::split) :
+ mGrid(new GridT(*other.mGrid, openvdb::ShallowCopy())),
+ mPa(other.mPa),
+ mDx(other.mDx),
+ mHalfWidth(other.mHalfWidth),
+ mRmin(other.mRmin),
+ mRmax(other.mRmax),
+ mGrainSize(other.mGrainSize),
+ mTask(other.mTask),
+ mInterrupter(other.mInterrupter),
+ mMinCount(0),
+ mMaxCount(0),
+ mIsSlave(true)
+ {
+ mGrid->newTree();
+ }
+ virtual ~ParticlesToLevelSet() { if (mIsSlave) delete mGrid; }
+
+ /// @return Half-width (in voxle units) of the narrow band level set
+ RealT getHalfWidth() const { return mHalfWidth; }
+
+ /// @return Voxel size in world units
+ RealT getVoxelSize() const { return mDx; }
+
+ /// @return the smallest radius allowed in voxel units
+ RealT getRmin() const { return mRmin; }
+ /// @return the largest radius allowed in voxel units
+ RealT getRmax() const { return mRmax; }
+
+ /// @return true if any particles were ignored due to their size
+ bool ignoredParticles() const { return mMinCount>0 || mMaxCount>0; }
+ /// @return number of small particles that were ignore due to Rmin
+ size_t getMinCount() const { return mMinCount; }
+ /// @return number of large particles that were ignore due to Rmax
+ size_t getMaxCount() const { return mMaxCount; }
+
+ /// set the smallest radius allowed in voxel units
+ void setRmin(RealT Rmin) { mRmin = math::Max(RealT(0),Rmin); }
+ /// set the largest radius allowed in voxel units
+ void setRmax(RealT Rmax) { mRmax = math::Max(mRmin,Rmax); }
+
+ /// @return the grain-size used for multi-threading
+ int getGrainSize() const { return mGrainSize; }
+ /// @brief Set the grain-size used for multi-threading.
+ /// @note A grainsize of 0 or less disables multi-threading!
+ void setGrainSize(int grainSize) { mGrainSize = grainSize; }
+
+ /// @brief Rasterize a sphere per particle derived from their
+ /// position and radius. All spheres are CSG unioned.
+ /// @param pa particles with position, radius and velocity.
+ void rasterizeSpheres(const ParticleListT& pa)
+ {
+ mPa = &pa;
+ if (mInterrupter) mInterrupter->start("Rasterizing particles to level set using spheres");
+ mTask = boost::bind(&ParticlesToLevelSet::rasterSpheres, _1, _2);
+ this->cook();
+ if (mInterrupter) mInterrupter->end();
+ }
+
+ /// @brief Rasterize a trail per particle derived from their
+ /// position, radius and velocity. Each trail is generated
+ /// as CSG unions of sphere instances with decreasing radius.
+ ///
+ /// @param pa particles with position, radius and velocity.
+ /// @param delta controls distance between sphere instances
+ /// (default=1). Be careful not to use too small values since this
+ /// can lead to excessive computation per trail (which the
+ /// interrupter can't stop).
+ ///
+ /// @note The direction of a trail is inverse to the direction of
+ /// the velocity vector, and the length is given by |V|. The radius
+ /// at the head of the trail is given by the radius of the particle
+ /// and the radius at the tail of the trail is Rmin voxel units which
+ /// has a default value of 1.5 corresponding to the Nyquist frequency!
+ void rasterizeTrails(const ParticleListT& pa, Real delta=1.0)
+ {
+ mPa = &pa;
+ if (mInterrupter) mInterrupter->start("Rasterizing particles to level set using trails");
+ mTask = boost::bind(&ParticlesToLevelSet::rasterTrails, _1, _2, RealT(delta));
+ this->cook();
+ if (mInterrupter) mInterrupter->end();
+ }
+
+ // ========> DO NOT CALL ANY OF THE PUBLIC METHODS BELOW! <=============
+
+ /// @brief Non-const functor called by tbb::parallel_reduce threads
+ ///
+ /// @note Do not call this method directly!
+ void operator()(const tbb::blocked_range<size_t>& r)
+ {
+ if (!mTask) {
+ OPENVDB_THROW(ValueError, "mTask is undefined - don't call operator() directly!");
+ }
+ mTask(this, r);
+ }
+
+ /// @brief Method called by tbb::parallel_reduce threads
+ ///
+ /// @note Do not call this method directly!
+ void join(ParticlesToLevelSet& other)
+ {
+ tools::csgUnion(*mGrid, *other.mGrid, /*prune=*/true);
+ mMinCount += other.mMinCount;
+ mMaxCount += other.mMaxCount;
+ }
+
+private:
+ typedef typename GridT::ValueType ValueT;
+ typedef typename GridT::Accessor Accessor;
+ typedef typename boost::function<void (ParticlesToLevelSet*,
+ const tbb::blocked_range<size_t>&)> FuncType;
+ GridT* mGrid;
+ const ParticleListT* mPa;//list of particles
+ const RealT mDx;//size of voxel in world units
+ const RealT mHalfWidth;//half width of narrow band LS in voxels
+ RealT mRmin;//ignore particles smaller than this radius in voxels
+ RealT mRmax;//ignore particles larger than this radius in voxels
+ int mGrainSize;
+ FuncType mTask;
+ InterruptT* mInterrupter;
+ size_t mMinCount, mMaxCount;//counters for ignored particles!
+ const bool mIsSlave;
+
+ void cook()
+ {
+ if (mGrainSize>0) {
+ tbb::parallel_reduce(tbb::blocked_range<size_t>(0,mPa->size(),mGrainSize), *this);
+ } else {
+ (*this)(tbb::blocked_range<size_t>(0, mPa->size()));
+ }
+ }
+ /// @return true if the particle is too small or too large
+ inline bool ignoreParticle(RealT R)
+ {
+ if (R<mRmin) {// below the cutoff radius
+ ++mMinCount;
+ return true;
+ }
+ if (R>mRmax) {// above the cutoff radius
+ ++mMaxCount;
+ return true;
+ }
+ return false;
+ }
+ /// @brief Rasterize sphere at position P and radius R into a
+ /// narrow-band level set with half-width, mHalfWidth.
+ /// @return false if it was interrupted
+ ///
+ /// @param P coordinates of the particle position in voxel units
+ /// @param R radius of particle in voxel units
+ /// @param id
+ /// @param accessor grid accessor with a private copy of the grid
+ ///
+ /// @note For best performance all computations are performed in
+ /// voxel-space with the important exception of the final level set
+ /// value that is converted to world units (e.g. the grid stores
+ /// the closest Euclidian signed distances measured in world
+ /// units). Also note we use the convention of positive distances
+ /// outside the surface an negative distances inside the surface.
+ inline bool rasterSphere(const Vec3R &P, RealT R, Index32 id, Accessor& accessor)
+ {
+ const ValueT inside = -mGrid->background();
+ const RealT dx = mDx;
+ const RealT max = R + mHalfWidth;// maximum distance in voxel units
+ const Coord a(math::Floor(P[0]-max),math::Floor(P[1]-max),math::Floor(P[2]-max));
+ const Coord b(math::Ceil( P[0]+max),math::Ceil( P[1]+max),math::Ceil( P[2]+max));
+ const RealT max2 = math::Pow2(max);//square of maximum distance in voxel units
+ const RealT min2 = math::Pow2(math::Max(RealT(0), R - mHalfWidth));//square of minimum distance
+ ValueT v;
+ size_t count = 0;
+ for ( Coord c = a; c.x() <= b.x(); ++c.x() ) {
+ //only check interrupter every 32'th scan in x
+ if (!(count++ & (1<<5)-1) && util::wasInterrupted(mInterrupter)) {
+ tbb::task::self().cancel_group_execution();
+ return false;
+ }
+ RealT x2 = math::Pow2( c.x() - P[0] );
+ for ( c.y() = a.y(); c.y() <= b.y(); ++c.y() ) {
+ RealT x2y2 = x2 + math::Pow2( c.y() - P[1] );
+ for ( c.z() = a.z(); c.z() <= b.z(); ++c.z() ) {
+ RealT x2y2z2 = x2y2 + math::Pow2( c.z() - P[2] );//square distance from c to P
+ if ( x2y2z2 >= max2 || (!accessor.probeValue(c,v) && v<ValueT(0)) )
+ continue;//outside narrow band of the particle or inside existing ls
+ if ( x2y2z2 <= min2 ) {//inside narrowband of the particle.
+ accessor.setValueOff(c, inside);
+ continue;
+ }
+ // distance in world units
+ const ValueT d = local::DualTrait<ValueT>::merge(dx*(math::Sqrt(x2y2z2) - R), id);
+ if (d < v) accessor.setValue(c, d);//CSG union
+ }//end loop over z
+ }//end loop over y
+ }//end loop over x
+ return true;
+ }
+
+ /// @brief Rasterize particles as spheres with variable radius
+ ///
+ /// @param r tbb's default range referring to the list of particles
+ void rasterSpheres(const tbb::blocked_range<size_t> &r)
+ {
+ Accessor accessor = mGrid->getAccessor(); // local accessor
+ const RealT inv_dx = RealT(1)/mDx;
+ bool run = true;
+ for (Index32 id = r.begin(), e=r.end(); run && id != e; ++id) {
+ const RealT R = inv_dx*mPa->radius(id);// in voxel units
+ if (this->ignoreParticle(R)) continue;
+ const Vec3R P = mGrid->transform().worldToIndex(mPa->pos(id));
+ run = this->rasterSphere(P, R, id, accessor);
+ }//end loop over particles
+ }
+
+ /// @brief Rasterize particles as trails with length = |V|
+ ///
+ /// @param r tbb's default range referring to the list of particles
+ /// @param delta scale distance between the velocity blurring of
+ /// particles. Increasing it (above 1) typically results in aliasing!
+ ///
+ /// @note All computations are performed in voxle units. Also
+ /// for very small values of delta the number of instances will
+ /// increase resulting in very slow rasterization that cannot be
+ /// interrupted!
+ void rasterTrails(const tbb::blocked_range<size_t> &r,
+ RealT delta)//scale distance between instances (eg 1.0)
+ {
+ Accessor accessor = mGrid->getAccessor(); // local accessor
+ const RealT inv_dx = RealT(1)/mDx, Rmin = mRmin;
+ bool run = true;
+ for (Index32 id = r.begin(), e=r.end(); run && id != e; ++id) {
+ const RealT R0 = inv_dx*mPa->radius(id);
+ if (this->ignoreParticle(R0)) continue;
+ const Vec3R P0 = mGrid->transform().worldToIndex(mPa->pos(id)),
+ V = inv_dx*mPa->vel(id);
+ const RealT speed = V.length(), inv_speed=1.0/speed;
+ const Vec3R N = -V*inv_speed;// inverse normalized direction
+ Vec3R P = P0;// local position of instance
+ RealT R = R0, d=0;// local radius and length of trail
+ for (size_t m=0; run && d <= speed ; ++m) {
+ run = this->rasterSphere(P, R, id, accessor);
+ P += 0.5*delta*R*N;// adaptive offset along inverse velocity direction
+ d = (P-P0).length();// current length of trail
+ R = R0-(R0-Rmin)*d*inv_speed;// R = R0 -> mRmin(e.g. 1.5)
+ }//loop over sphere instances
+ }//end loop over particles
+ }
+};//end of ParticlesToLevelSet class
+
+///////////////////// YOU CAN SAFELY IGNORE THIS SECTION /////////////////////
+namespace local {
+// This is a simple type that combines a distance value and a particle
+// id. It's required for attribute transfer which is defined in the
+// ParticlesToLevelSetAndId class below.
+template <typename RealT>
+class Dual
+{
+public:
+ explicit Dual() : mId(util::INVALID_IDX) {}
+ explicit Dual(RealT d) : mDist(d), mId(util::INVALID_IDX) {}
+ explicit Dual(RealT d, Index32 id) : mDist(d), mId(id) {}
+ Dual& operator=(const Dual& rhs) { mDist = rhs.mDist; mId = rhs.mId; return *this;}
+ bool isIdValid() const { return mId != util::INVALID_IDX; }
+ Index32 id() const { return mId; }
+ RealT dist() const { return mDist; }
+ bool operator!=(const Dual& rhs) const { return mDist != rhs.mDist; }
+ bool operator==(const Dual& rhs) const { return mDist == rhs.mDist; }
+ bool operator< (const Dual& rhs) const { return mDist < rhs.mDist; };
+ bool operator<=(const Dual& rhs) const { return mDist <= rhs.mDist; };
+ bool operator> (const Dual& rhs) const { return mDist > rhs.mDist; };
+ Dual operator+ (const Dual& rhs) const { return Dual(mDist+rhs.mDist); };
+ Dual operator+ (const RealT& rhs) const { return Dual(mDist+rhs); };
+ Dual operator- (const Dual& rhs) const { return Dual(mDist-rhs.mDist); };
+ Dual operator-() const { return Dual(-mDist); }
+protected:
+ RealT mDist;
+ Index32 mId;
+};
+// Required by several of the tree nodes
+template <typename RealT>
+inline std::ostream& operator<<(std::ostream& ostr, const Dual<RealT>& rhs)
+{
+ ostr << rhs.dist();
+ return ostr;
+}
+// Required by math::Abs
+template <typename RealT>
+inline Dual<RealT> Abs(const Dual<RealT>& x)
+{
+ return Dual<RealT>(math::Abs(x.dist()),x.id());
+}
+// Specialization of trait class used to merge and split a distance and a particle id
+template <typename T>
+struct DualTrait<Dual<T> >
+{
+ static Dual<T> merge(T dist, Index32 id) { return Dual<T>(dist, id); }
+ static T split(Dual<T> dual) { return dual.dist(); }
+};
+}// local namespace
+//////////////////////////////////////////////////////////////////////////////
+
+/// @brief Use this wrapper class to convert particles into a level set and a
+/// separate index grid of closest-point particle id. The latter can
+/// be used to subsequently transfer particles attributes into
+/// separate grids.
+/// @note This class has the same API as ParticlesToLevelSet - the
+/// only exception being the raster methods that return the index grid!
+template<typename LevelSetGridT,
+ typename ParticleListT,
+ typename InterruptT = util::NullInterrupter>
+class ParticlesToLevelSetAndId
+{
+public:
+ typedef typename LevelSetGridT::ValueType RealT;
+ typedef typename local::Dual<RealT> DualT;
+ typedef typename LevelSetGridT::TreeType RealTreeT;
+ typedef typename RealTreeT::template ValueConverter<DualT>::Type DualTreeT;
+ typedef Int32Tree IndxTreeT;
+ typedef Grid<DualTreeT> DualGridT;
+ typedef Int32Grid IndxGridT;
+
+ ParticlesToLevelSetAndId(LevelSetGridT& ls, InterruptT* interrupter = NULL) :
+ mRealGrid(ls),
+ mDualGrid(DualT(ls.background()))
+ {
+ mDualGrid.setGridClass(ls.getGridClass());
+ mDualGrid.setTransform(ls.transformPtr());
+ mRaster = new RasterT(mDualGrid, interrupter);
+ }
+
+ virtual ~ParticlesToLevelSetAndId() { delete mRaster; }
+
+ /// @return Half-width (in voxle units) of the narrow band level set
+ RealT getHalfWidth() const { return mRaster->getHalfWidth(); }
+
+ /// @return Voxel size in world units
+ RealT getVoxelSize() const { return mRaster->getVoxelSize(); }
+
+ /// @return true if any particles were ignored due to their size
+ bool ignoredParticles() const { return mRaster->ignoredParticles(); }
+ /// @return number of small particles that were ignore due to Rmin
+ size_t getMinCount() const { return mRaster->getMinCount(); }
+ /// @return number of large particles that were ignore due to Rmax
+ size_t getMaxCount() const { return mRaster->getMaxCount(); }
+
+ /// @return the smallest radius allowed in voxel units
+ RealT getRmin() const { return mRaster->getRmin(); }
+ /// @return the largest radius allowed in voxel units
+ RealT getRmax() const { return mRaster->getRmax(); }
+
+ /// set the smallest radius allowed in voxel units
+ void setRmin(RealT Rmin) { mRaster->setRmin(Rmin); }
+ /// set the largest radius allowed in voxel units
+ void setRmax(RealT Rmax) { mRaster->setRmax(Rmax); }
+
+ /// @return the grain-size used for multi-threading
+ int getGrainSize() const { return mRaster->getGrainSize(); }
+ /// @brief Set the grain-size used for multi-threading.
+ /// @note A grainsize of 0 or less disables multi-threading!
+ void setGrainSize(int grainSize) { mRaster->setGrainSize(grainSize); }
+
+ /// @brief Rasterize a sphere per particle derived from their
+ /// position and radius. All spheres are CSG unioned.
+ /// @return An index grid storing the id of the closest particle.
+ /// @param pa particles with position, radius and velocity.
+ typename IndxGridT::Ptr rasterizeSpheres(const ParticleListT& pa)
+ {
+ mRaster->rasterizeSpheres(pa);
+ return this->extract();
+ }
+ /// @brief Rasterize a trail per particle derived from their
+ /// position, radius and velocity. Each trail is generated
+ /// as CSG unions of sphere instances with decreasing radius.
+ ///
+ /// @param pa particles with position, radius and velocity.
+ /// @param delta controls distance between sphere instances
+ /// (default=1). Be careful not to use too small values since this
+ /// can lead to excessive computation per trail (which the
+ /// interrupter can't stop).
+ ///
+ /// @note The direction of a trail is inverse to the direction of
+ /// the velocity vector, and the length is given by |V|. The radius
+ /// at the head of the trail is given by the radius of the particle
+ /// and the radius at the tail of the trail is Rmin voxel units which
+ /// has a default value of 1.5 corresponding to the Nyquist frequency!
+ typename IndxGridT::Ptr rasterizeTrails(const ParticleListT& pa, Real delta=1.0)
+ {
+ mRaster->rasterizeTrails(pa, delta);
+ return this->extract();
+ }
+
+private:
+
+ /// disallow copy construction
+ ParticlesToLevelSetAndId(const ParticlesToLevelSetAndId& other) {}
+ /// disallow copy assignment
+ ParticlesToLevelSetAndId& operator=(const ParticlesToLevelSetAndId& rhs)
+ {
+ return *this;
+ }
+ /// @brief Private method to extract the level set and index grid.
+ typename IndxGridT::Ptr extract()
+ {
+ // Use topology copy constructors since output grids have the
+ // same topology as mDualGrid
+ const DualTreeT& dualTree = mDualGrid.tree();
+ typename IndxTreeT::Ptr indxTree(new IndxTreeT(dualTree,util::INVALID_IDX,TopologyCopy()));
+ typename IndxGridT::Ptr indxGrid = typename IndxGridT::Ptr(new IndxGridT(indxTree));
+ indxGrid->setTransform(mDualGrid.transformPtr());
+ typename RealTreeT::Ptr realTree(new RealTreeT(dualTree,mRealGrid.background(),TopologyCopy()));
+ mRealGrid.setTree(realTree);
+
+ // Extract the level set and IDs from mDualGrid. We will
+ // explore the fact that by design active values always live
+ // at the leaf node level, i.e. no active tiles exist in level sets
+ typedef typename DualGridT::TreeType::LeafCIter LeafIterT;
+ typedef typename DualGridT::TreeType::LeafNodeType LeafT;
+ typedef typename LevelSetGridT::TreeType::LeafNodeType RealLeafT;
+ typedef typename IndxGridT::TreeType::LeafNodeType IndxLeafT;
+ RealTreeT& realTreeRef = *realTree;
+ IndxTreeT& indxTreeRef = *indxTree;
+ for (LeafIterT n = mDualGrid.tree().cbeginLeaf(); n; ++n) {
+ const LeafT& leaf = *n;
+ const Coord xyz = leaf.getOrigin();
+ // Get leafnodes that were allocated during topology contruction!
+ RealLeafT& i = *realTreeRef.probeLeaf(xyz);
+ IndxLeafT& j = *indxTreeRef.probeLeaf(xyz);
+ for (typename LeafT::ValueOnCIter m=leaf.cbeginValueOn(); m; ++m) {
+ // Use linear offset (vs coordinate) access for better performance!
+ const Index k = m.pos();
+ const DualT& v = *m;
+ i.setValueOnly(k, v.dist());
+ j.setValueOnly(k, v.id());
+ }
+ }
+ mRealGrid.signedFloodFill();//required since we only transferred active voxels!
+ return indxGrid;
+ }
+
+ typedef ParticlesToLevelSet<DualGridT, ParticleListT, InterruptT, RealT> RasterT;
+ LevelSetGridT& mRealGrid;// input level set grid
+ DualGridT mDualGrid;// grid encoding both the level set and the point id
+ RasterT* mRaster;
+};//end of ParticlesToLevelSetAndId
+
+} // namespace tools
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_TOOLS_PARTICLES_TO_LEVELSET_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/tools/PointAdvect.h b/extern/openvdb/internal/openvdb/tools/PointAdvect.h
new file mode 100644
index 00000000000..97b40d65c96
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/tools/PointAdvect.h
@@ -0,0 +1,524 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+//
+/// @author Ken Museth, D.J. Hill (openvdb port, added staggered grid support)
+/// @file PointAdvect.h
+///
+/// @brief Class PointAdvect advects points (with position) in a static velocity field
+
+#ifndef OPENVDB_TOOLS_POINT_ADVECT_HAS_BEEN_INCLUDED
+#define OPENVDB_TOOLS_POINT_ADVECT_HAS_BEEN_INCLUDED
+
+#include <openvdb/openvdb.h>
+#include <openvdb/math/Math.h> // min
+#include <openvdb/Types.h> // Vec3 types and version number
+#include <openvdb/Grid.h> // grid
+#include <openvdb/util/NullInterrupter.h>
+#include "Interpolation.h" // sampling
+
+#include <boost/static_assert.hpp>
+#include <tbb/blocked_range.h> // threading
+#include <tbb/parallel_for.h> // threading
+#include <tbb/task.h> // for cancel
+
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace tools {
+
+/// Class that holds a Vec3 grid, to be interpreted as the closest point to a constraint
+/// surface. Supports a method to allow a point to be projected onto the closest point
+/// on the constraint surface. Uses Caching.
+template<typename CptGridT = Vec3fGrid>
+class ClosestPointProjector
+{
+public:
+ typedef CptGridT CptGridType;
+ typedef typename CptGridType::ConstAccessor CptAccessor;
+ typedef typename CptGridType::ValueType CptValueType;
+
+ ClosestPointProjector():
+ mCptIterations(0)
+ {
+ }
+ ClosestPointProjector(const CptGridType& cptGrid, int n):
+ mCptGrid(&cptGrid),
+ mCptAccessor(cptGrid.getAccessor()),
+ mCptIterations(n)
+ {
+ }
+ ClosestPointProjector(const ClosestPointProjector &other):
+ mCptGrid(other.mCptGrid),
+ mCptAccessor(mCptGrid->getAccessor()),
+ mCptIterations(other.mCptIterations)
+ {
+ }
+ void setConstraintIterations(unsigned int cptIterations) { mCptIterations = cptIterations; }
+ unsigned int numIterations() { return mCptIterations;};
+
+ // point constraint
+ template <typename LocationType>
+ inline void projectToConstraintSurface(LocationType& W) const
+ {
+ /// Entries in the CPT tree are the closest point to the constraint surface.
+ /// The interpolation step in sample introduces error so that the result
+ /// of a single sample may not lie exactly on the surface. The iterations
+ /// in the loop exist to minimize this error.
+ CptValueType result(W[0], W[1],W[2]);
+ for (unsigned int i = 0; i < mCptIterations; ++i) {
+ const Vec3R location = mCptGrid->worldToIndex(Vec3R(result[0], result[1], result[2]));
+ BoxSampler::sample<CptAccessor>(mCptAccessor, location, result);
+ }
+ W[0] = result[0];
+ W[1] = result[1];
+ W[2] = result[2];
+ }
+
+private:
+ const CptGridType* mCptGrid; // Closest-Point-Transform vector field
+ CptAccessor mCptAccessor;
+ unsigned int mCptIterations;
+};// end of ClosestPointProjector class
+
+
+/// Class to hold a Vec3 field interperated as a velocity field.
+/// Primarily exists to provide a method(s) that integrate a passive
+/// point forward in the velocity field for a single time-step (dt)
+template<typename GridT = Vec3fGrid, bool StaggeredVelocity = false>
+class VelocitySampler
+{
+public:
+ typedef typename GridT::ConstAccessor VelAccessor;
+ typedef typename GridT::ValueType VelValueType;
+
+ VelocitySampler(const GridT& velGrid):
+ mVelGrid(&velGrid),
+ mVelAccessor(mVelGrid->getAccessor())
+ {
+ }
+ VelocitySampler(const VelocitySampler& other):
+ mVelGrid(other.mVelGrid),
+ mVelAccessor(mVelGrid->getAccessor())
+ {
+ }
+ ~VelocitySampler()
+ {
+ }
+ /// Samples the velocity at position W onto result. Supports both
+ /// staggered (i.e. MAC) and collocated velocity grids.
+ template <typename LocationType>
+ inline void sample(const LocationType& W, VelValueType& result) const
+ {
+ const Vec3R location = mVelGrid->worldToIndex(Vec3R(W[0], W[1], W[2]));
+ /// Note this if-branch is optimized away at compile time
+ if (StaggeredVelocity) {
+ // the velocity Grid stores data in MAC-style staggered layout
+ StaggeredBoxSampler::sample<VelAccessor>(mVelAccessor, location, result);
+ } else {
+ // the velocity Grid uses collocated data
+ BoxSampler::sample<VelAccessor>(mVelAccessor, location, result);
+ }
+ }
+
+private:
+ // holding the Grids for the transforms
+ const GridT* mVelGrid; // Velocity vector field
+ VelAccessor mVelAccessor;
+};// end of VelocitySampler class
+
+
+/// @brief Performs runge-kutta time integration of variable order in
+/// a static velocity field
+template<typename GridT = Vec3fGrid, bool StaggeredVelocity = false>
+class VelocityIntegrator
+{
+public:
+ typedef typename GridT::ValueType VecType;
+ typedef typename VecType::ValueType ElementType;
+
+ VelocityIntegrator(const GridT& velGrid):
+ mVelField(velGrid)
+ {
+ }
+ // variable order Runge-Kutta time integration for a single time step
+ template<int Order, typename LocationType>
+ void rungeKutta(const float dt, LocationType& loc) {
+ VecType P(loc[0],loc[1],loc[2]), V0, V1, V2, V3;
+
+ BOOST_STATIC_ASSERT((Order < 5) && (Order > -1));
+ /// Note the if-braching below is optimized away at compile time
+ if (Order == 0) {
+ // do nothing
+ return ;
+ } else if (Order == 1) {
+ mVelField.sample(P, V0);
+ P = dt*V0;
+
+ } else if (Order == 2) {
+ mVelField.sample(P, V0);
+ mVelField.sample(P + ElementType(0.5) * ElementType(dt) * V0, V1);
+ P = dt*V1;
+
+ } else if (Order == 3) {
+ mVelField.sample(P, V0);
+ mVelField.sample(P+ElementType(0.5)*ElementType(dt)*V0, V1);
+ mVelField.sample(P+dt*(ElementType(2.0)*V1-V0), V2);
+ P = dt*(V0 + ElementType(4.0)*V1 + V2)*ElementType(1.0/6.0);
+
+ } else if (Order == 4) {
+ mVelField.sample(P, V0);
+ mVelField.sample(P+ElementType(0.5)*ElementType(dt)*V0, V1);
+ mVelField.sample(P+ElementType(0.5)*ElementType(dt)*V1, V2);
+ mVelField.sample(P+ dt*V2, V3);
+ P = dt*(V0 + ElementType(2.0)*(V1 + V2) + V3)*ElementType(1.0/6.0);
+
+ }
+ loc += LocationType(P[0], P[1], P[2]);
+
+ }
+private:
+ VelocitySampler<GridT, StaggeredVelocity> mVelField;
+};// end of VelocityIntegrator class
+
+
+////////////////////////////////////////
+
+
+/// Performs passive or constrained advection of points in a velocity field
+/// represented by an OpenVDB grid and an optional closest-point-transform (CPT)
+/// represented in another OpenVDB grid. Note the CPT is assumed to be
+/// in world coordinates and NOT index coordinates!
+/// Supports both collocated velocity grids and staggered velocity grids
+///
+/// The @c PointListT template argument refers to any class with the following
+/// interface (e.g., std::vector<openvdb::Vec3f>):
+/// @code
+/// class PointList {
+/// ...
+/// public:
+/// typedef internal_vector3_type value_type; // must support [] component access
+/// openvdb::Index size() const; // number of points in list
+/// value_type& operator[](int n); // world space position of nth point
+/// };
+/// @endcode
+///
+/// @note All methods (except size) are assumed to be thread-safe and
+/// the positions are returned as non-const references since the
+/// advection method needs to modify them!
+template<typename GridT = Vec3fGrid,
+ typename PointListT = std::vector<typename GridT::ValueType>,
+ bool StaggeredVelocity = false,
+ typename InterrupterType = util::NullInterrupter>
+class PointAdvect
+{
+public:
+ typedef GridT GridType;
+ typedef PointListT PointListType;
+ typedef typename PointListT::value_type LocationType;
+ typedef VelocityIntegrator<GridT, StaggeredVelocity> VelocityFieldIntegrator;
+
+ PointAdvect(const GridT& velGrid, InterrupterType* interrupter=NULL) :
+ mVelGrid(&velGrid),
+ mPoints(NULL),
+ mIntegrationOrder(1),
+ mThreaded(true),
+ mInterrupter(interrupter)
+ {
+ }
+ PointAdvect(const PointAdvect &other) :
+ mVelGrid(other.mVelGrid),
+ mPoints(other.mPoints),
+ mDt(other.mDt),
+ mAdvIterations(other.mAdvIterations),
+ mIntegrationOrder(other.mIntegrationOrder),
+ mThreaded(other.mThreaded),
+ mInterrupter(other.mInterrupter)
+ {
+ }
+ virtual ~PointAdvect()
+ {
+ }
+ /// If the order of the integration is set to zero no advection is performed
+ bool earlyOut() const { return (mIntegrationOrder==0);}
+ /// get & set
+ void setThreaded(bool threaded) { mThreaded = threaded; }
+ bool getThreaded() { return mThreaded; }
+ void setIntegrationOrder(unsigned int order) {mIntegrationOrder = order;}
+
+ /// Constrained advection of a list of points over a time = dt * advIterations
+ void advect(PointListT& points, float dt, unsigned int advIterations = 1)
+ {
+ if (this->earlyOut()) return; // nothing to do!
+ mPoints = &points;
+ mDt = dt;
+ mAdvIterations = advIterations;
+
+ if (mInterrupter) mInterrupter->start("Advecting points by OpenVDB velocity field: ");
+ if (mThreaded) {
+ tbb::parallel_for(tbb::blocked_range<size_t>(0, mPoints->size()), *this);
+ } else {
+ (*this)(tbb::blocked_range<size_t>(0, mPoints->size()));
+ }
+ if (mInterrupter) mInterrupter->end();
+ }
+
+ /// Never call this method directly - it is use by TBB and has to be public!
+ void operator() (const tbb::blocked_range<size_t> &range) const
+ {
+ if (mInterrupter && mInterrupter->wasInterrupted()) {
+ tbb::task::self().cancel_group_execution();
+ }
+
+ VelocityFieldIntegrator velField(*mVelGrid);
+ switch (mIntegrationOrder) {
+ case 1:
+ {
+ for (size_t n = range.begin(); n != range.end(); ++n) {
+ LocationType& X0 = (*mPoints)[n];
+ // loop over number of time steps
+ for (unsigned int i = 0; i < mAdvIterations; ++i) {
+ velField.template rungeKutta<1>(mDt, X0);
+ }
+ }
+ }
+ break;
+ case 2:
+ {
+ for (size_t n = range.begin(); n != range.end(); ++n) {
+ LocationType& X0 = (*mPoints)[n];
+ // loop over number of time steps
+ for (unsigned int i = 0; i < mAdvIterations; ++i) {
+ velField.template rungeKutta<2>(mDt, X0);
+ }
+ }
+ }
+ break;
+ case 3:
+ {
+ for (size_t n = range.begin(); n != range.end(); ++n) {
+ LocationType& X0 = (*mPoints)[n];
+ // loop over number of time steps
+ for (unsigned int i = 0; i < mAdvIterations; ++i) {
+ velField.template rungeKutta<3>(mDt, X0);
+ }
+ }
+ }
+ break;
+ case 4:
+ {
+ for (size_t n = range.begin(); n != range.end(); ++n) {
+ LocationType& X0 = (*mPoints)[n];
+ // loop over number of time steps
+ for (unsigned int i = 0; i < mAdvIterations; ++i) {
+ velField.template rungeKutta<4>(mDt, X0);
+ }
+ }
+ }
+ break;
+ }
+ }
+
+private:
+ // the velocity field
+ const GridType* mVelGrid;
+
+ // vertex list of all the points
+ PointListT* mPoints;
+
+ // time integration parameters
+ float mDt; // time step
+ unsigned int mAdvIterations; // number of time steps
+ unsigned int mIntegrationOrder;
+
+ // operational parameters
+ bool mThreaded;
+ InterrupterType* mInterrupter;
+
+};//end of PointAdvect class
+
+
+template<typename GridT = Vec3fGrid,
+ typename PointListT = std::vector<typename GridT::ValueType>,
+ bool StaggeredVelocity = false,
+ typename CptGridType = GridT,
+ typename InterrupterType = util::NullInterrupter>
+class ConstrainedPointAdvect
+{
+public:
+ typedef GridT GridType;
+ typedef typename PointListT::value_type LocationType;
+ typedef VelocityIntegrator<GridT, StaggeredVelocity> VelocityIntegratorType;
+ typedef ClosestPointProjector<CptGridType> ClosestPointProjectorType;
+ typedef PointListT PointListType;
+
+ ConstrainedPointAdvect(const GridType& velGrid,
+ const GridType& cptGrid, int cptn, InterrupterType* interrupter = NULL):
+ mVelGrid(&velGrid),
+ mCptGrid(&cptGrid),
+ mCptIter(cptn),
+ mInterrupter(interrupter)
+ {
+ }
+ ConstrainedPointAdvect(const ConstrainedPointAdvect& other):
+ mVelGrid(other.mVelGrid),
+ mCptGrid(other.mCptGrid),
+ mCptIter(other.mCptIter),
+ mPoints(other.mPoints),
+ mDt(other.mDt),
+ mAdvIterations(other.mAdvIterations),
+ mIntegrationOrder(other.mIntegrationOrder),
+ mThreaded(other.mThreaded),
+ mInterrupter(other.mInterrupter)
+ {
+ }
+ virtual ~ConstrainedPointAdvect(){}
+
+ void setConstraintIterations(unsigned int cptIter) {mCptIter = cptIter;}
+ void setIntegrationOrder(unsigned int order) {mIntegrationOrder = order;}
+
+ void setThreaded(bool threaded) { mThreaded = threaded; }
+ bool getThreaded() { return mThreaded; }
+
+ /// Constrained Advection a list of points over a time = dt * advIterations
+ void advect(PointListT& points, float dt, unsigned int advIterations = 1)
+ {
+ mPoints = &points;
+ mDt = dt;
+
+ if (mIntegrationOrder==0 && mCptIter == 0) {
+ return; // nothing to do!
+ }
+ (mIntegrationOrder>0) ? mAdvIterations = advIterations : mAdvIterations = 1;
+
+ if (mInterrupter) mInterrupter->start("Advecting points by OpenVDB velocity field: ");
+ const int N = mPoints->size();
+
+ if (mThreaded) {
+ tbb::parallel_for(tbb::blocked_range<size_t>(0, N), *this);
+ } else {
+ (*this)(tbb::blocked_range<size_t>(0, N));
+ }
+ if (mInterrupter) mInterrupter->end();
+ }
+
+
+ /// Never call this method directly - it is use by TBB and has to be public!
+ void operator() (const tbb::blocked_range<size_t> &range) const
+ {
+ if (mInterrupter && mInterrupter->wasInterrupted()) {
+ tbb::task::self().cancel_group_execution();
+ }
+
+ VelocityIntegratorType velField(*mVelGrid);
+ ClosestPointProjectorType cptField(*mCptGrid, mCptIter);
+ switch (mIntegrationOrder) {
+ case 0://pure CPT projection
+ {
+ for (size_t n = range.begin(); n != range.end(); ++n) {
+ LocationType& X0 = (*mPoints)[n];
+ for (unsigned int i = 0; i < mAdvIterations; ++i) {
+ cptField.projectToConstraintSurface(X0);
+ }
+ }
+ }
+ break;
+ case 1://1'th order advection and CPT projection
+ {
+ for (size_t n = range.begin(); n != range.end(); ++n) {
+ LocationType& X0 = (*mPoints)[n];
+ for (unsigned int i = 0; i < mAdvIterations; ++i) {
+ velField.template rungeKutta<1>(mDt, X0);
+ cptField.projectToConstraintSurface(X0);
+ }
+ }
+ }
+ break;
+ case 2://2'nd order advection and CPT projection
+ {
+ for (size_t n = range.begin(); n != range.end(); ++n) {
+ LocationType& X0 = (*mPoints)[n];
+ for (unsigned int i = 0; i < mAdvIterations; ++i) {
+ velField.template rungeKutta<2>(mDt, X0);
+ cptField.projectToConstraintSurface(X0);
+ }
+ }
+ }
+ break;
+
+ case 3://3'rd order advection and CPT projection
+ {
+ for (size_t n = range.begin(); n != range.end(); ++n) {
+ LocationType& X0 = (*mPoints)[n];
+ for (unsigned int i = 0; i < mAdvIterations; ++i) {
+ velField.template rungeKutta<3>(mDt, X0);
+ cptField.projectToConstraintSurface(X0);
+ }
+ }
+ }
+ break;
+ case 4://4'th order advection and CPT projection
+ {
+ for (size_t n = range.begin(); n != range.end(); ++n) {
+ LocationType& X0 = (*mPoints)[n];
+ for (unsigned int i = 0; i < mAdvIterations; ++i) {
+ velField.template rungeKutta<4>(mDt, X0);
+ cptField.projectToConstraintSurface(X0);
+ }
+ }
+ }
+ break;
+ }
+ }
+
+private:
+ const GridType* mVelGrid; // the velocity field
+ const GridType* mCptGrid;
+ int mCptIter;
+ PointListT* mPoints; // vertex list of all the points
+
+ // time integration parameters
+ float mDt; // time step
+ unsigned int mAdvIterations; // number of time steps
+ unsigned int mIntegrationOrder; // order of Runge-Kutta integration
+ // operational parameters
+ bool mThreaded;
+ InterrupterType* mInterrupter;
+};// end of ConstrainedPointAdvect class
+
+} // namespace tools
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_TOOLS_POINT_ADVECT_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/tools/PointScatter.h b/extern/openvdb/internal/openvdb/tools/PointScatter.h
new file mode 100644
index 00000000000..fe3dffd66c3
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/tools/PointScatter.h
@@ -0,0 +1,328 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+//
+/// @file PointScatter.h
+
+#ifndef OPENVDB_TOOLS_POINT_SCATTER_HAS_BEEN_INCLUDED
+#define OPENVDB_TOOLS_POINT_SCATTER_HAS_BEEN_INCLUDED
+
+#include <openvdb/Types.h>
+#include <openvdb/Grid.h>
+#include <openvdb/util/NullInterrupter.h>
+#include <boost/random/uniform_01.hpp>
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace tools {
+
+/// @brief The two point scatters UniformPointScatter and
+/// NonUniformPointScatter depend on the following two classes:
+///
+/// The @c PointAccessorType template argument below refers to any class
+/// with the following interface:
+/// @code
+/// class PointAccessor {
+/// ...
+/// public:
+/// void add(const openvdb::Vec3R &pos);// appends point with world positions pos
+/// };
+/// @endcode
+///
+///
+/// The @c InterruptType template argument below refers to any class
+/// with the following interface:
+/// @code
+/// class Interrupter {
+/// ...
+/// public:
+/// void start(const char* name = NULL)// called when computations begin
+/// void end() // called when computations end
+/// bool wasInterrupted(int percent=-1)// return true to break computation
+///};
+/// @endcode
+///
+/// @note If no template argument is provided for this InterruptType
+/// the util::NullInterrupter is used which implies that all
+/// interrupter calls are no-ops (i.e. incurs no computational overhead).
+
+
+/// @brief Uniform scatters of point in the active voxels.
+/// The point count is either explicitly defined or implicitly
+/// through the specification of a global density (=points-per-volume)
+///
+/// @note This uniform scattering technique assumes that the number of
+/// points is generally smaller than the number of active voxels
+/// (including virtual active voxels in active tiles).
+template<typename PointAccessorType,
+ typename RandomGenerator,
+ typename InterruptType = openvdb::util::NullInterrupter>
+class UniformPointScatter
+{
+public:
+ UniformPointScatter(PointAccessorType& points,
+ int pointCount,
+ RandomGenerator& randGen,
+ InterruptType* interrupt = NULL):
+ mPoints(points),
+ mInterrupter(interrupt),
+ mPointCount(pointCount),
+ mPointsPerVolume(0.0f),
+ mVoxelCount(0),
+ mRandomGen(randGen)
+ {
+ }
+ UniformPointScatter(PointAccessorType& points,
+ float pointsPerVolume,
+ RandomGenerator& randGen,
+ InterruptType* interrupt = NULL):
+ mPoints(points),
+ mInterrupter(interrupt),
+ mPointCount(0),
+ mPointsPerVolume(pointsPerVolume),
+ mVoxelCount(0),
+ mRandomGen(randGen)
+ {
+ }
+
+ /// This is the main functor method implementing the actual scattering of points.
+ template<typename GridT>
+ void operator()(const GridT& grid)
+ {
+ mVoxelCount = grid.activeVoxelCount();
+ if (mVoxelCount == 0) return;//throw std::runtime_error("No voxels in which to scatter points!");
+ const openvdb::Index64 voxelId = mVoxelCount - 1;
+ const openvdb::Vec3d dim = grid.voxelSize();
+ if (mPointsPerVolume>0) {
+ if (mInterrupter) mInterrupter->start("Uniform scattering with fixed point density");
+ mPointCount = int(mPointsPerVolume * dim[0]*dim[1]*dim[2] * mVoxelCount);
+ } else if (mPointCount>0) {
+ if (mInterrupter) mInterrupter->start("Uniform scattering with fixed point count");
+ mPointsPerVolume = mPointCount/float(dim[0]*dim[1]*dim[2] * mVoxelCount);
+ } else {
+ return;
+ //throw std::runtime_error("Invalid point count and point density");
+ }
+ openvdb::CoordBBox bbox;
+ /// build sorted multi-map of random voxel-ids to contain a point
+ std::multiset<openvdb::Index64> mVoxelSet;
+ for (int i=0, chunks=100000; i<mPointCount; i += chunks) {
+ if (util::wasInterrupted(mInterrupter)) return;
+ // throw std::runtime_error("processing was interrupted");
+ //}
+ /// @todo Multi-thread the generation of mVoxelSet
+ for (int j=i, end=std::min(i+chunks, mPointCount); j<end; ++j) {
+ mVoxelSet.insert(openvdb::Index64(voxelId*getRand()));
+ }
+ }
+ std::multiset<openvdb::Index64>::iterator voxelIter =
+ mVoxelSet.begin(), voxelEnd = mVoxelSet.end();
+ typename GridT::ValueOnCIter valueIter = grid.cbeginValueOn();
+ mPointCount = 0;
+ size_t interruptCount = 0;
+ for (openvdb::Index64 i=valueIter.getVoxelCount(); voxelIter != voxelEnd; ++voxelIter) {
+ //only check interrupter for every 32'th particle
+ if (!(interruptCount++ & (1<<5)-1) && util::wasInterrupted(mInterrupter)) return;
+ while ( i <= *voxelIter ) {
+ ++valueIter;
+ i += valueIter.getVoxelCount();
+ }
+ if (valueIter.isVoxelValue()) {// a majorty is expected to be voxels
+ const openvdb::Coord min = valueIter.getCoord();
+ const openvdb::Vec3R dmin(min.x()-0.5, min.y()-0.5, min.z()-0.5);
+ this->addPoint(grid, dmin);
+ } else {// tiles contain multiple (virtual) voxels
+ valueIter.getBoundingBox(bbox);
+ const openvdb::Coord size(bbox.extents());
+ const openvdb::Vec3R dmin(bbox.min().x()-0.5,
+ bbox.min().y()-0.5,
+ bbox.min().z()-0.5);
+ this->addPoint(grid, dmin, size);
+ }
+ }
+ if (mInterrupter) mInterrupter->end();
+ }
+
+ // The following methods should only be called after the
+ // the operator() method was called
+ void print(const std::string &name, std::ostream& os = std::cout) const
+ {
+ os << "Uniformely scattered " << mPointCount << " points into " << mVoxelCount
+ << " active voxels in \"" << name << "\" corresponding to "
+ << mPointsPerVolume << " points per volume." << std::endl;
+ }
+
+ int getPointCount() const { return mPointCount; }
+ float getPointsPerVolume() const { return mPointsPerVolume; }
+ openvdb::Index64 getVoxelCount() const { return mVoxelCount; }
+
+private:
+ PointAccessorType& mPoints;
+ InterruptType* mInterrupter;
+ int mPointCount;
+ float mPointsPerVolume;
+ openvdb::Index64 mVoxelCount;
+ RandomGenerator& mRandomGen;
+ boost::uniform_01<double> mRandom;
+
+ double getRand() { return mRandom(mRandomGen); }
+
+ template <typename GridT>
+ inline void addPoint(const GridT &grid, const openvdb::Vec3R &pos, const openvdb::Vec3R &delta)
+ {
+ mPoints.add(grid.indexToWorld(pos + delta));
+ ++mPointCount;
+ }
+ template <typename GridT>
+ inline void addPoint(const GridT &grid, const openvdb::Vec3R &dmin)
+ {
+ this->addPoint(grid, dmin, openvdb::Vec3R(getRand(),getRand(),getRand()));
+ }
+ template <typename GridT>
+ inline void addPoint(const GridT &grid, const openvdb::Vec3R &dmin, const openvdb::Coord &size)
+ {
+ const openvdb::Vec3R d(size.x()*getRand(),size.y()*getRand(),size.z()*getRand());
+ this->addPoint(grid, dmin, d);
+ }
+}; // class UniformPointScatter
+
+
+/// @brief Non-uniform scatters of point in the active voxels.
+/// The local point count is implicitly defined as a product of
+/// of a global density and the local voxel (or tile) value.
+///
+/// @note This scattering technique can be significantly slower
+/// than a uniform scattering since its computational complexity
+/// is proportional to the active voxel (and tile) count.
+template<typename PointAccessorType,
+ typename RandomGenerator,
+ typename InterruptType = openvdb::util::NullInterrupter>
+class NonUniformPointScatter
+{
+public:
+ NonUniformPointScatter(PointAccessorType& points,
+ float pointsPerVolume,
+ RandomGenerator& randGen,
+ InterruptType* interrupt = NULL):
+ mPoints(points),
+ mInterrupter(interrupt),
+ mPointCount(0),
+ mPointsPerVolume(pointsPerVolume),//note this is NOT the local point density
+ mVoxelCount(0),
+ mRandomGen(randGen)
+ {
+ }
+
+ /// This is the main functor method implementing the actual scattering of points.
+ template<typename GridT>
+ void operator()(const GridT& grid)
+ {
+ mVoxelCount = grid.activeVoxelCount();
+ if (mVoxelCount == 0) return;//throw std::runtime_error("No voxels in which to scatter points!");
+ if (mInterrupter) mInterrupter->start("Non-uniform scattering with local point density");
+ const openvdb::Vec3d dim = grid.voxelSize();
+ const double volumePerVoxel = dim[0]*dim[1]*dim[2],
+ pointsPerVoxel = mPointsPerVolume * volumePerVoxel;
+ openvdb::CoordBBox bbox;
+ size_t interruptCount = 0;
+ for (typename GridT::ValueOnCIter iter = grid.cbeginValueOn(); iter; ++iter) {
+ //only check interrupter for every 32'th active value
+ if (!(interruptCount++ & (1<<5)-1) && util::wasInterrupted(mInterrupter)) return;
+ const double d = (*iter) * pointsPerVoxel * iter.getVoxelCount();
+ const int n = int(d);
+ if (iter.isVoxelValue()) { // a majorty is expected to be voxels
+ const openvdb::Coord min = iter.getCoord();
+ const openvdb::Vec3R dmin(min.x()-0.5, min.y()-0.5, min.z()-0.5);
+ for (int i = 0; i < n; ++i) this->addPoint(grid, dmin);
+ if (getRand() < (d - n)) this->addPoint(grid, dmin);
+ } else { // tiles contain multiple (virtual) voxels
+ iter.getBoundingBox(bbox);
+ const openvdb::Coord size(bbox.extents());
+ const openvdb::Vec3R dmin(bbox.min().x()-0.5,
+ bbox.min().y()-0.5,
+ bbox.min().z()-0.5);
+ for (int i = 0; i < n; ++i) this->addPoint(grid, dmin, size);
+ if (getRand() < (d - n)) this->addPoint(grid, dmin, size);
+ }
+ }//loop over the active values
+ if (mInterrupter) mInterrupter->end();
+ }
+
+ // The following methods should only be called after the
+ // the operator() method was called
+ void print(const std::string &name, std::ostream& os = std::cout) const
+ {
+ os << "Non-uniformely scattered " << mPointCount << " points into " << mVoxelCount
+ << " active voxels in \"" << name << "\"." << std::endl;
+ }
+
+ int getPointCount() const { return mPointCount; }
+ openvdb::Index64 getVoxelCount() const { return mVoxelCount; }
+
+private:
+ PointAccessorType& mPoints;
+ InterruptType* mInterrupter;
+ int mPointCount;
+ float mPointsPerVolume;
+ openvdb::Index64 mVoxelCount;
+ RandomGenerator& mRandomGen;
+ boost::uniform_01<double> mRandom;
+
+ double getRand() { return mRandom(mRandomGen); }
+
+ template <typename GridT>
+ inline void addPoint(const GridT &grid, const openvdb::Vec3R &pos, const openvdb::Vec3R &delta)
+ {
+ mPoints.add(grid.indexToWorld(pos + delta));
+ ++mPointCount;
+ }
+ template <typename GridT>
+ inline void addPoint(const GridT &grid, const openvdb::Vec3R &dmin)
+ {
+ this->addPoint(grid, dmin, openvdb::Vec3R(getRand(),getRand(),getRand()));
+ }
+ template <typename GridT>
+ inline void addPoint(const GridT &grid, const openvdb::Vec3R &dmin, const openvdb::Coord &size)
+ {
+ const openvdb::Vec3R d(size.x()*getRand(),size.y()*getRand(),size.z()*getRand());
+ this->addPoint(grid, dmin, d);
+ }
+
+}; // class NonUniformPointScatter
+
+} // namespace tools
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_TOOLS_POINT_SCATTER_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/tools/Statistics.h b/extern/openvdb/internal/openvdb/tools/Statistics.h
new file mode 100644
index 00000000000..e5d4d4ebeff
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/tools/Statistics.h
@@ -0,0 +1,370 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+//
+/// @file Statistics.h
+///
+/// @brief Functions to efficiently compute histograms and statistics
+/// (mean, variance, etc.) of grid values
+
+#ifndef OPENVDB_TOOLS_STATISTICS_HAS_BEEN_INCLUDED
+#define OPENVDB_TOOLS_STATISTICS_HAS_BEEN_INCLUDED
+
+#include <openvdb/Types.h>
+#include <openvdb/Exceptions.h>
+#include <openvdb/math/Stats.h>
+#include "ValueTransformer.h"
+
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace tools {
+
+/// @brief Iterate over a scalar grid and compute a histogram of the values
+/// of the voxels that are visited, or iterate over a vector-valued grid
+/// and compute a histogram of the magnitudes of the vectors.
+/// @param iter an iterator over the values of a grid or its tree
+/// (@c Grid::ValueOnCIter, @c Tree::ValueOffIter, etc.)
+/// @param minVal the smallest value that can be added to the histogram
+/// @param maxVal the largest value that can be added to the histogram
+/// @param numBins the number of histogram bins
+/// @param threaded if true, iterate over the grid in parallel
+template<typename IterT>
+inline math::Histogram
+histogram(const IterT& iter, double minVal, double maxVal,
+ size_t numBins = 10, bool threaded = true);
+
+
+/// @brief Iterate over a scalar grid and compute statistics (mean, variance, etc.)
+/// of the values of the voxels that are visited, or iterate over a vector-valued grid
+/// and compute statistics of the magnitudes of the vectors.
+/// @param iter an iterator over the values of a grid or its tree
+/// (@c Grid::ValueOnCIter, @c Tree::ValueOffIter, etc.)
+/// @param threaded if true, iterate over the grid in parallel
+template<typename IterT>
+inline math::Stats
+statistics(const IterT& iter, bool threaded = true);
+
+
+/// @brief Iterate over a grid and compute statistics (mean, variance, etc.) of
+/// the values produced by applying the given functor at each voxel that is visited.
+/// @param iter an iterator over the values of a grid or its tree
+/// (@c Grid::ValueOnCIter, @c Tree::ValueOffIter, etc.)
+/// @param op a functor of the form <tt>void op(const IterT&, math::Stats&)</tt>,
+/// where @c IterT is the type of @a iter, that inserts zero or more
+/// floating-point values into the provided @c math::Stats object
+/// @param threaded if true, iterate over the grid in parallel
+/// @note When @a threaded is true, each thread gets its own copy of the functor.
+///
+/// @par Example:
+/// Compute statistics of just the active and positive-valued voxels of a scalar,
+/// floating-point grid.
+/// @code
+/// struct Local {
+/// static inline
+/// void addIfPositive(const FloatGrid::ValueOnCIter& iter, math::Stats& stats)
+/// {
+/// const float f = *iter;
+/// if (f > 0.0) {
+/// if (iter.isVoxelValue()) stats.add(f);
+/// else stats.add(f, iter.getVoxelCount());
+/// }
+/// }
+/// };
+/// FloatGrid grid = ...;
+/// math::Stats stats =
+/// tools::statistics(grid.cbeginValueOn(), Local::addIfPositive, /*threaded=*/true);
+/// @endcode
+template<typename IterT, typename ValueOp>
+inline math::Stats
+statistics(const IterT& iter, const ValueOp& op, bool threaded);
+
+
+/// @brief Iterate over a grid and compute statistics (mean, variance, etc.)
+/// of the values produced by applying a given operator (see math/Operators.h)
+/// at each voxel that is visited.
+/// @param iter an iterator over the values of a grid or its tree
+/// (@c Grid::ValueOnCIter, @c Tree::ValueOffIter, etc.)
+/// @param op an operator object with a method of the form
+/// <tt>double result(Accessor&, const Coord&)</tt>
+/// @param threaded if true, iterate over the grid in parallel
+/// @note World-space operators, whose @c result() methods are of the form
+/// <tt>double result(const Map&, Accessor&, const Coord&)</tt>, must be wrapped
+/// in a math::MapAdapter.
+/// @note Vector-valued operators like math::Gradient must be wrapped in an adapter
+/// such as math::OpMagnitude.
+///
+/// @par Example:
+/// Compute statistics of the magnitude of the gradient at the active voxels of
+/// a scalar, floating-point grid. (Note the use of the math::MapAdapter and
+/// math::OpMagnitude adapters.)
+/// @code
+/// FloatGrid grid = ...;
+///
+/// // Assume that we know that the grid has a uniform scale map.
+/// typedef math::UniformScaleMap MapType;
+/// // Specify a world-space gradient operator that uses first-order differencing.
+/// typedef math::Gradient<MapType, math::FD_1ST> GradientOp;
+/// // Wrap the operator with an adapter that computes the magnitude of the gradient.
+/// typedef math::OpMagnitude<GradientOp, MapType> MagnitudeOp;
+/// // Wrap the operator with an adapter that associates a map with it.
+/// typedef math::MapAdapter<MapType, GradientOp, double> CompoundOp;
+///
+/// if (MapType::Ptr map = grid.constTransform().constMap<MapType>()) {
+/// math::Stats stats = tools::opStatistics(grid.cbeginValueOn(), CompoundOp(*map));
+/// }
+/// @endcode
+///
+/// @par Example:
+/// Compute statistics of the divergence at the active voxels of a vector-valued grid.
+/// @code
+/// Vec3SGrid grid = ...;
+///
+/// // Assume that we know that the grid has a uniform scale map.
+/// typedef math::UniformScaleMap MapType;
+/// // Specify a world-space divergence operator that uses first-order differencing.
+/// typedef math::Divergence<MapType, math::FD_1ST> DivergenceOp;
+/// // Wrap the operator with an adapter that associates a map with it.
+/// typedef math::MapAdapter<MapType, DivergenceOp, double> CompoundOp;
+///
+/// if (MapType::Ptr map = grid.constTransform().constMap<MapType>()) {
+/// math::Stats stats = tools::opStatistics(grid.cbeginValueOn(), CompoundOp(*map));
+/// }
+/// @endcode
+///
+/// @par Example:
+/// As above, but computing the divergence in index space.
+/// @code
+/// Vec3SGrid grid = ...;
+///
+/// // Specify an index-space divergence operator that uses first-order differencing.
+/// typedef math::ISDivergence<math::FD_1ST> DivergenceOp;
+///
+/// math::Stats stats = tools::opStatistics(grid.cbeginValueOn(), DivergenceOp());
+/// @endcode
+template<typename OperatorT, typename IterT>
+inline math::Stats
+opStatistics(const IterT& iter, const OperatorT& op = OperatorT(), bool threaded = true);
+
+
+////////////////////////////////////////
+
+
+namespace stats_internal {
+
+/// @todo This traits class is needed because tree::TreeValueIteratorBase uses
+/// the name ValueT for the type of the value to which the iterator points,
+/// whereas node-level iterators use the name ValueType.
+template<typename IterT, typename AuxT = void>
+struct IterTraits {
+ typedef typename IterT::ValueType ValueType;
+};
+
+template<typename TreeT, typename ValueIterT>
+struct IterTraits<tree::TreeValueIteratorBase<TreeT, ValueIterT> > {
+ typedef typename tree::TreeValueIteratorBase<TreeT, ValueIterT>::ValueT ValueType;
+};
+
+
+// Helper class to compute a scalar value from either a scalar or a vector value
+// (the latter by computing the vector's magnitude)
+template<typename T, bool IsVector> struct GetValImpl;
+
+template<typename T>
+struct GetValImpl<T, /*IsVector=*/false> {
+ static inline double get(const T& val) { return double(val); }
+};
+
+template<typename T>
+struct GetValImpl<T, /*IsVector=*/true> {
+ static inline double get(const T& val) { return val.length(); }
+};
+
+
+// Helper class to compute a scalar value from a tree or node iterator
+// that points to a value in either a scalar or a vector grid, and to
+// add that value to a math::Stats object.
+template<typename IterT, typename StatsT>
+struct GetVal
+{
+ typedef typename IterTraits<IterT>::ValueType ValueT;
+ typedef GetValImpl<ValueT, VecTraits<ValueT>::IsVec> ImplT;
+
+ inline void operator()(const IterT& iter, StatsT& stats) const {
+ if (iter.isVoxelValue()) stats.add(ImplT::get(*iter));
+ else stats.add(ImplT::get(*iter), iter.getVoxelCount());
+ }
+};
+
+
+// Helper class to accumulate scalar voxel values or vector voxel magnitudes
+// into a math::Stats object
+template<typename IterT, typename ValueOp>
+struct StatsOp
+{
+ StatsOp(const ValueOp& op): getValue(op) {}
+
+ // Accumulate voxel and tile values into this functor's Stats object.
+ inline void operator()(const IterT& iter) { getValue(iter, stats); }
+
+ // Accumulate another functor's Stats object into this functor's.
+ inline void join(StatsOp& other) { stats.add(other.stats); }
+
+ math::Stats stats;
+ ValueOp getValue;
+};
+
+
+// Helper class to accumulate scalar voxel values or vector voxel magnitudes
+// into a math::Histogram object
+template<typename IterT, typename ValueOp>
+struct HistOp
+{
+ HistOp(const ValueOp& op, double vmin, double vmax, size_t bins):
+ hist(vmin, vmax, bins), getValue(op)
+ {}
+
+ // Accumulate voxel and tile values into this functor's Histogram object.
+ inline void operator()(const IterT& iter) { getValue(iter, hist); }
+
+ // Accumulate another functor's Histogram object into this functor's.
+ inline void join(HistOp& other) { hist.add(other.hist); }
+
+ math::Histogram hist;
+ ValueOp getValue;
+};
+
+
+// Helper class to apply an operator such as math::Gradient or math::Laplacian
+// to voxels and accumulate the scalar results or the magnitudes of vector results
+// into a math::Stats object
+template<typename IterT, typename OpT>
+struct MathOp
+{
+ typedef typename IterT::TreeT TreeT;
+ typedef typename TreeT::ValueType ValueT;
+ typedef typename tree::ValueAccessor<const TreeT> ConstAccessor;
+
+ // Each thread gets its own accessor and its own copy of the operator.
+ ConstAccessor mAcc;
+ OpT mOp;
+ math::Stats mStats;
+
+ template<typename TreeT>
+ static inline TreeT* THROW_IF_NULL(TreeT* ptr) {
+ if (ptr == NULL) OPENVDB_THROW(ValueError, "iterator references a null tree");
+ return ptr;
+ }
+
+ MathOp(const IterT& iter, const OpT& op):
+ mAcc(*THROW_IF_NULL(iter.getTree())), mOp(op)
+ {}
+
+ // Accumulate voxel and tile values into this functor's Stats object.
+ void operator()(const IterT& it)
+ {
+ if (it.isVoxelValue()) {
+ // Add the magnitude of the gradient at a single voxel.
+ mStats.add(mOp.result(mAcc, it.getCoord()));
+ } else {
+ // Iterate over the voxels enclosed by a tile and add the results
+ // of applying the operator at each voxel.
+ /// @todo This could be specialized to be done more efficiently for some operators.
+ /// For example, all voxels in the interior of a tile (i.e., not on the borders)
+ /// have gradient zero, so there's no need to apply the operator to every voxel.
+ CoordBBox bbox = it.getBoundingBox();
+ Coord xyz;
+ int &x = xyz.x(), &y = xyz.y(), &z = xyz.z();
+ for (x = bbox.min().x(); x <= bbox.max().x(); ++x) {
+ for (y = bbox.min().y(); y <= bbox.max().y(); ++y) {
+ for (z = bbox.min().z(); z <= bbox.max().z(); ++z) {
+ mStats.add(mOp.result(mAcc, it.getCoord()));
+ }
+ }
+ }
+ }
+ }
+
+ // Accumulate another functor's Stats object into this functor's.
+ inline void join(MathOp& other) { mStats.add(other.mStats); }
+}; // struct MathOp
+
+} // namespace stats_internal
+
+
+template<typename IterT>
+inline math::Histogram
+histogram(const IterT& iter, double vmin, double vmax, size_t numBins, bool threaded)
+{
+ typedef stats_internal::GetVal<IterT, math::Histogram> ValueOp;
+ ValueOp valOp;
+ stats_internal::HistOp<IterT, ValueOp> op(valOp, vmin, vmax, numBins);
+ tools::accumulate(iter, op, threaded);
+ return op.hist;
+}
+
+
+template<typename IterT>
+inline math::Stats
+statistics(const IterT& iter, bool threaded)
+{
+ stats_internal::GetVal<IterT, math::Stats> valOp;
+ return statistics(iter, valOp, threaded);
+}
+
+
+template<typename IterT, typename ValueOp>
+inline math::Stats
+statistics(const IterT& iter, const ValueOp& valOp, bool threaded)
+{
+ stats_internal::StatsOp<IterT, const ValueOp> op(valOp);
+ tools::accumulate(iter, op, threaded);
+ return op.stats;
+}
+
+
+template<typename OperatorT, typename IterT>
+inline math::Stats
+opStatistics(const IterT& iter, const OperatorT& op, bool threaded)
+{
+ stats_internal::MathOp<IterT, OperatorT> func(iter, op);
+ tools::accumulate(iter, func, threaded);
+ return func.mStats;
+}
+
+} // namespace tools
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_TOOLS_STATISTICS_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/tools/ValueTransformer.h b/extern/openvdb/internal/openvdb/tools/ValueTransformer.h
new file mode 100644
index 00000000000..92f0dd085ea
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/tools/ValueTransformer.h
@@ -0,0 +1,588 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+//
+/// @file ValueTransformer.h
+///
+/// tools::foreach() and tools::transformValues() transform the values in a grid
+/// by iterating over the grid with a user-supplied iterator and applying a
+/// user-supplied functor at each step of the iteration. With tools::foreach(),
+/// the transformation is done in-place on the input grid, whereas with
+/// tools::transformValues(), transformed values are written to an output grid
+/// (which can, for example, have a different value type than the input grid).
+/// Both functions can optionally transform multiple values of the grid in parallel.
+///
+/// tools::accumulate() can be used to accumulate the results of applying a functor
+/// at each step of a grid iteration. (The functor is responsible for storing and
+/// updating intermediate results.) When the iteration is done serially the behavior is
+/// the same as with tools::foreach(), but when multiple values are processed in parallel,
+/// an additional step is performed: when any two threads finish processing,
+/// @c op.join(otherOp) is called on one thread's functor to allow it to coalesce
+/// its intermediate result with the other thread's.
+
+#ifndef OPENVDB_TOOLS_VALUETRANSFORMER_HAS_BEEN_INCLUDED
+#define OPENVDB_TOOLS_VALUETRANSFORMER_HAS_BEEN_INCLUDED
+
+#include <tbb/parallel_for.h>
+#include <tbb/parallel_reduce.h>
+#include <openvdb/Types.h>
+#include <openvdb/Grid.h>
+
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace tools {
+
+/// Iterate over a grid and at each step call @c op(iter).
+/// @param iter an iterator over a grid or its tree (@c Grid::ValueOnCIter,
+/// @c Tree::NodeIter, etc.)
+/// @param op a functor of the form <tt>void op(const IterT&)</tt>, where @c IterT is
+/// the type of @a iter
+/// @param threaded if true, transform multiple values of the grid in parallel
+/// @param shareOp if true and @a threaded is true, all threads use the same functor;
+/// otherwise, each thread gets its own copy of the @e original functor
+///
+/// @par Example:
+/// Multiply all values (both set and unset) of a scalar, floating-point grid by two.
+/// @code
+/// struct Local {
+/// static inline void op(const FloatGrid::ValueAllIter& iter) {
+/// iter.setValue(*iter * 2);
+/// }
+/// };
+/// FloatGrid grid = ...;
+/// tools::foreach(grid.beginValueAll(), Local::op);
+/// @endcode
+///
+/// @par Example:
+/// Rotate all active vectors of a vector grid by 45 degrees about the y axis.
+/// @code
+/// namespace {
+/// struct MatMul {
+/// math::Mat3s M;
+/// MatMul(const math::Mat3s& mat): M(mat) {}
+/// inline void operator()(const VectorGrid::ValueOnIter& iter) const {
+/// iter.setValue(M.transform(*iter));
+/// }
+/// };
+/// }
+/// {
+/// VectorGrid grid = ...;
+/// tools::foreach(grid.beginValueOn(),
+/// MatMul(math::rotation<math::Mat3s>(math::Y, M_PI_4)));
+/// }
+/// @endcode
+///
+/// @note For more complex operations that require finer control over threading,
+/// consider using @c tbb::parallel_for() or @c tbb::parallel_reduce() in conjunction
+/// with a tree::IteratorRange that wraps a grid or tree iterator.
+template<typename IterT, typename XformOp>
+inline void foreach(const IterT& iter, XformOp& op,
+ bool threaded = true, bool shareOp = true);
+
+template<typename IterT, typename XformOp>
+inline void foreach(const IterT& iter, const XformOp& op,
+ bool threaded = true, bool shareOp = true);
+
+
+/// Iterate over a grid and at each step call <tt>op(iter, accessor)</tt> to
+/// populate (via the accessor) the given output grid, whose @c ValueType
+/// need not be the same as the input grid's.
+/// @param inIter a non-<tt>const</tt> or (preferably) @c const iterator over an
+/// input grid or its tree (@c Grid::ValueOnCIter, @c Tree::NodeIter, etc.)
+/// @param outGrid an empty grid to be populated
+/// @param op a functor of the form
+/// <tt>void op(const InIterT&, OutGridT::ValueAccessor&)</tt>,
+/// where @c InIterT is the type of @a inIter
+/// @param threaded if true, transform multiple values of the input grid in parallel
+/// @param shareOp if true and @a threaded is true, all threads use the same functor;
+/// otherwise, each thread gets its own copy of the @e original functor
+///
+/// @par Example:
+/// Populate a scalar floating-point grid with the lengths of the vectors from all
+/// active voxels of a vector-valued input grid.
+/// @code
+/// struct Local {
+/// static void op(
+/// const Vec3fGrid::ValueOnCIter& iter,
+/// FloatGrid::ValueAccessor& accessor)
+/// {
+/// if (iter.isVoxelValue()) { // set a single voxel
+/// accessor.setValue(iter.getCoord(), iter->length());
+/// } else { // fill an entire tile
+/// CoordBBox bbox;
+/// iter.getBoundingBox(bbox);
+/// accessor.getTree()->fill(bbox, iter->length());
+/// }
+/// }
+/// };
+/// Vec3fGrid inGrid = ...;
+/// FloatGrid outGrid;
+/// tools::transformValues(inGrid.cbeginValueOn(), outGrid, Local::op);
+/// @endcode
+///
+/// @note For more complex operations that require finer control over threading,
+/// consider using @c tbb::parallel_for() or @c tbb::parallel_reduce() in conjunction
+/// with a tree::IteratorRange that wraps a grid or tree iterator.
+template<typename InIterT, typename OutGridT, typename XformOp>
+inline void transformValues(const InIterT& inIter, OutGridT& outGrid,
+ XformOp& op, bool threaded = true, bool shareOp = true);
+
+#ifndef _MSC_VER
+template<typename InIterT, typename OutGridT, typename XformOp>
+inline void transformValues(const InIterT& inIter, OutGridT& outGrid,
+ const XformOp& op, bool threaded = true, bool shareOp = true);
+#endif
+
+
+/// Iterate over a grid and at each step call @c op(iter). If threading is enabled,
+/// call @c op.join(otherOp) to accumulate intermediate results from pairs of threads.
+/// @param iter an iterator over a grid or its tree (@c Grid::ValueOnCIter,
+/// @c Tree::NodeIter, etc.)
+/// @param op a functor with a join method of the form <tt>void join(XformOp&)</tt>
+/// and a call method of the form <tt>void op(const IterT&)</tt>,
+/// where @c IterT is the type of @a iter
+/// @param threaded if true, transform multiple values of the grid in parallel
+/// @note If @a threaded is true, each thread gets its own copy of the @e original functor.
+/// The order in which threads are joined is unspecified.
+/// @note If @a threaded is false, the join method is never called.
+///
+/// @par Example:
+/// Compute the average of the active values of a scalar, floating-point grid
+/// using the math::Stats class.
+/// @code
+/// namespace {
+/// struct Average {
+/// math::Stats stats;
+///
+/// // Accumulate voxel and tile values into this functor's Stats object.
+/// inline void operator()(const FloatGrid::ValueOnCIter& iter) {
+/// if (iter.isVoxelValue()) stats.add(*iter);
+/// else stats.add(*iter, iter.getVoxelCount());
+/// }
+///
+/// // Accumulate another functor's Stats object into this functor's.
+/// inline void join(Average& other) { stats.add(other.stats); }
+///
+/// // Return the cumulative result.
+/// inline double average() const { return stats.mean(); }
+/// };
+/// }
+/// {
+/// FloatGrid grid = ...;
+/// Average op;
+/// tools::accumulate(grid.cbeginValueOn(), op);
+/// double average = op.average();
+/// }
+/// @endcode
+///
+/// @note For more complex operations that require finer control over threading,
+/// consider using @c tbb::parallel_for() or @c tbb::parallel_reduce() in conjunction
+/// with a tree::IteratorRange that wraps a grid or tree iterator.
+template<typename IterT, typename XformOp>
+inline void accumulate(const IterT& iter, XformOp& op, bool threaded = true);
+
+
+////////////////////////////////////////
+
+
+namespace valxform {
+
+template<typename IterT, typename OpT>
+class SharedOpApplier
+{
+public:
+ typedef typename tree::IteratorRange<IterT> IterRange;
+
+ SharedOpApplier(const IterT& iter, OpT& op): mIter(iter), mOp(op) {}
+
+ void process(bool threaded = true)
+ {
+ IterRange range(mIter);
+ if (threaded) {
+ tbb::parallel_for(range, *this);
+ } else {
+ (*this)(range);
+ }
+ }
+
+ void operator()(IterRange& r) const { for ( ; r; ++r) mOp(r.iterator()); }
+
+private:
+ IterT mIter;
+ OpT& mOp;
+};
+
+
+template<typename IterT, typename OpT>
+class CopyableOpApplier
+{
+public:
+ typedef typename tree::IteratorRange<IterT> IterRange;
+
+ CopyableOpApplier(const IterT& iter, const OpT& op): mIter(iter), mOp(op), mOrigOp(&op) {}
+
+ // When splitting this task, give the subtask a copy of the original functor,
+ // not of this task's functor, which might have been modified arbitrarily.
+ CopyableOpApplier(const CopyableOpApplier& other):
+ mIter(other.mIter), mOp(*other.mOrigOp), mOrigOp(other.mOrigOp) {}
+
+ void process(bool threaded = true)
+ {
+ IterRange range(mIter);
+ if (threaded) {
+ tbb::parallel_for(range, *this);
+ } else {
+ (*this)(range);
+ }
+ }
+
+ void operator()(IterRange& r) const { for ( ; r; ++r) mOp(r.iterator()); }
+
+private:
+ IterT mIter;
+ OpT mOp; // copy of original functor
+ OpT const * const mOrigOp; // pointer to original functor
+};
+
+} // namespace valxform
+
+
+template<typename IterT, typename XformOp>
+inline void
+foreach(const IterT& iter, XformOp& op, bool threaded, bool shared)
+{
+ if (shared) {
+ typename valxform::SharedOpApplier<IterT, XformOp> proc(iter, op);
+ proc.process(threaded);
+ } else {
+ typedef typename valxform::CopyableOpApplier<IterT, XformOp> Processor;
+ Processor proc(iter, op);
+ proc.process(threaded);
+ }
+}
+
+template<typename IterT, typename XformOp>
+inline void
+foreach(const IterT& iter, const XformOp& op, bool threaded, bool /*shared*/)
+{
+ // Const ops are shared across threads, not copied.
+ typename valxform::SharedOpApplier<IterT, const XformOp> proc(iter, op);
+ proc.process(threaded);
+}
+
+
+////////////////////////////////////////
+
+
+namespace valxform {
+
+template<typename InIterT, typename OutTreeT, typename OpT>
+class SharedOpTransformer
+{
+public:
+ typedef typename InIterT::TreeT InTreeT;
+ typedef typename tree::IteratorRange<InIterT> IterRange;
+ typedef typename OutTreeT::ValueType OutValueT;
+
+ SharedOpTransformer(const InIterT& inIter, OutTreeT& outTree, OpT& op):
+ mIsRoot(true),
+ mInputIter(inIter),
+ mInputTree(inIter.getTree()),
+ mOutputTree(&outTree),
+ mOp(op)
+ {
+ if (static_cast<const void*>(mInputTree) == static_cast<void*>(mOutputTree)) {
+ OPENVDB_LOG_INFO("use tools::foreach(), not transformValues(),"
+ " to transform a grid in place");
+ }
+ }
+
+ /// Splitting constructor
+ SharedOpTransformer(SharedOpTransformer& other, tbb::split):
+ mIsRoot(false),
+ mInputIter(other.mInputIter),
+ mInputTree(other.mInputTree),
+ mOutputTree(new OutTreeT(zeroVal<OutValueT>())),
+ mOp(other.mOp)
+ {}
+
+ ~SharedOpTransformer()
+ {
+ // Delete the output tree only if it was allocated locally
+ // (the top-level output tree was supplied by the caller).
+ if (!mIsRoot) {
+ delete mOutputTree;
+ mOutputTree = NULL;
+ }
+ }
+
+ void process(bool threaded = true)
+ {
+ if (!mInputTree || !mOutputTree) return;
+
+ IterRange range(mInputIter);
+
+ // Independently transform elements in the iterator range,
+ // either in parallel or serially.
+ if (threaded) {
+ tbb::parallel_reduce(range, *this);
+ } else {
+ (*this)(range);
+ }
+ }
+
+ /// Transform each element in the given range.
+ void operator()(IterRange& range) const
+ {
+ if (!mOutputTree) return;
+ typename tree::ValueAccessor<OutTreeT> outAccessor(*mOutputTree);
+ for ( ; range; ++range) {
+ mOp(range.iterator(), outAccessor);
+ }
+ }
+
+ void join(const SharedOpTransformer& other)
+ {
+ if (mOutputTree && other.mOutputTree) {
+ mOutputTree->merge(*other.mOutputTree);
+ }
+ }
+
+private:
+ bool mIsRoot;
+ InIterT mInputIter;
+ const InTreeT* mInputTree;
+ OutTreeT* mOutputTree;
+ OpT& mOp;
+}; // class SharedOpTransformer
+
+
+template<typename InIterT, typename OutTreeT, typename OpT>
+class CopyableOpTransformer
+{
+public:
+ typedef typename InIterT::TreeT InTreeT;
+ typedef typename tree::IteratorRange<InIterT> IterRange;
+ typedef typename OutTreeT::ValueType OutValueT;
+
+ CopyableOpTransformer(const InIterT& inIter, OutTreeT& outTree, const OpT& op):
+ mIsRoot(true),
+ mInputIter(inIter),
+ mInputTree(inIter.getTree()),
+ mOutputTree(&outTree),
+ mOp(op),
+ mOrigOp(&op)
+ {
+ if (static_cast<const void*>(mInputTree) == static_cast<void*>(mOutputTree)) {
+ OPENVDB_LOG_INFO("use tools::foreach(), not transformValues(),"
+ " to transform a grid in place");
+ }
+ }
+
+ // When splitting this task, give the subtask a copy of the original functor,
+ // not of this task's functor, which might have been modified arbitrarily.
+ CopyableOpTransformer(CopyableOpTransformer& other, tbb::split):
+ mIsRoot(false),
+ mInputIter(other.mInputIter),
+ mInputTree(other.mInputTree),
+ mOutputTree(new OutTreeT(zeroVal<OutValueT>())),
+ mOp(*other.mOrigOp),
+ mOrigOp(other.mOrigOp)
+ {}
+
+ ~CopyableOpTransformer()
+ {
+ // Delete the output tree only if it was allocated locally
+ // (the top-level output tree was supplied by the caller).
+ if (!mIsRoot) {
+ delete mOutputTree;
+ mOutputTree = NULL;
+ }
+ }
+
+ void process(bool threaded = true)
+ {
+ if (!mInputTree || !mOutputTree) return;
+
+ IterRange range(mInputIter);
+
+ // Independently transform elements in the iterator range,
+ // either in parallel or serially.
+ if (threaded) {
+ tbb::parallel_reduce(range, *this);
+ } else {
+ (*this)(range);
+ }
+ }
+
+ /// Transform each element in the given range.
+ void operator()(IterRange& range)
+ {
+ if (!mOutputTree) return;
+ typename tree::ValueAccessor<OutTreeT> outAccessor(*mOutputTree);
+ for ( ; range; ++range) {
+ mOp(range.iterator(), outAccessor);
+ }
+ }
+
+ void join(const CopyableOpTransformer& other)
+ {
+ if (mOutputTree && other.mOutputTree) {
+ mOutputTree->merge(*other.mOutputTree);
+ }
+ }
+
+private:
+ bool mIsRoot;
+ InIterT mInputIter;
+ const InTreeT* mInputTree;
+ OutTreeT* mOutputTree;
+ OpT mOp; // copy of original functor
+ OpT const * const mOrigOp; // pointer to original functor
+}; // class CopyableOpTransformer
+
+} // namespace valxform
+
+
+////////////////////////////////////////
+
+
+template<typename InIterT, typename OutGridT, typename XformOp>
+inline void
+transformValues(const InIterT& inIter, OutGridT& outGrid, XformOp& op,
+ bool threaded, bool shared)
+{
+ typedef TreeAdapter<OutGridT> Adapter;
+ typedef typename Adapter::TreeType OutTreeT;
+ if (shared) {
+ typedef typename valxform::SharedOpTransformer<InIterT, OutTreeT, XformOp> Processor;
+ Processor proc(inIter, Adapter::tree(outGrid), op);
+ proc.process(threaded);
+ } else {
+ typedef typename valxform::CopyableOpTransformer<InIterT, OutTreeT, XformOp> Processor;
+ Processor proc(inIter, Adapter::tree(outGrid), op);
+ proc.process(threaded);
+ }
+}
+
+#ifndef _MSC_VER
+template<typename InIterT, typename OutGridT, typename XformOp>
+inline void
+transformValues(const InIterT& inIter, OutGridT& outGrid, const XformOp& op,
+ bool threaded, bool /*share*/)
+{
+ typedef TreeAdapter<OutGridT> Adapter;
+ typedef typename Adapter::TreeType OutTreeT;
+ // Const ops are shared across threads, not copied.
+ typedef typename valxform::SharedOpTransformer<InIterT, OutTreeT, const XformOp> Processor;
+ Processor proc(inIter, Adapter::tree(outGrid), op);
+ proc.process(threaded);
+}
+#endif
+
+
+////////////////////////////////////////
+
+
+namespace valxform {
+
+template<typename IterT, typename OpT>
+class OpAccumulator
+{
+public:
+ typedef typename tree::IteratorRange<IterT> IterRange;
+
+ // The root task makes a const copy of the original functor (mOrigOp)
+ // and keeps a pointer to the original functor (mOp), which it then modifies.
+ // Each subtask keeps a const pointer to the root task's mOrigOp
+ // and makes and then modifies a non-const copy (mOp) of it.
+ OpAccumulator(const IterT& iter, OpT& op):
+ mIsRoot(true),
+ mIter(iter),
+ mOp(&op),
+ mOrigOp(new OpT(op))
+ {}
+
+ // When splitting this task, give the subtask a copy of the original functor,
+ // not of this task's functor, which might have been modified arbitrarily.
+ OpAccumulator(OpAccumulator& other, tbb::split):
+ mIsRoot(false),
+ mIter(other.mIter),
+ mOp(new OpT(*other.mOrigOp)),
+ mOrigOp(other.mOrigOp)
+ {}
+
+ ~OpAccumulator() { if (mIsRoot) delete mOrigOp; else delete mOp; }
+
+ void process(bool threaded = true)
+ {
+ IterRange range(mIter);
+ if (threaded) {
+ tbb::parallel_reduce(range, *this);
+ } else {
+ (*this)(range);
+ }
+ }
+
+ void operator()(IterRange& r) { for ( ; r; ++r) (*mOp)(r.iterator()); }
+
+ void join(OpAccumulator& other) { mOp->join(*other.mOp); }
+
+private:
+ bool mIsRoot;
+ IterT mIter;
+ OpT* mOp; // pointer to original functor, which might get modified
+ OpT const * const mOrigOp; // const copy of original functor
+}; // class OpAccumulator
+
+} // namespace valxform
+
+
+////////////////////////////////////////
+
+
+template<typename IterT, typename XformOp>
+inline void
+accumulate(const IterT& iter, XformOp& op, bool threaded)
+{
+ typename valxform::OpAccumulator<IterT, XformOp> proc(iter, op);
+ proc.process(threaded);
+}
+
+} // namespace tools
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_TOOLS_VALUETRANSFORMER_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/tools/VolumeToMesh.h b/extern/openvdb/internal/openvdb/tools/VolumeToMesh.h
new file mode 100644
index 00000000000..78b4d6c8066
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/tools/VolumeToMesh.h
@@ -0,0 +1,2710 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+
+#ifndef OPENVDB_TOOLS_VOLUME_TO_MESH_HAS_BEEN_INCLUDED
+#define OPENVDB_TOOLS_VOLUME_TO_MESH_HAS_BEEN_INCLUDED
+
+#include <openvdb/tree/ValueAccessor.h>
+#include <openvdb/util/Util.h> // for COORD_OFFSETS
+#include <openvdb/math/Operators.h> // for ISGradient
+#include <openvdb/tools/Morphology.h> // for dilateVoxels()
+
+#include <boost/scoped_array.hpp>
+#include <boost/scoped_ptr.hpp>
+
+#include <tbb/blocked_range.h>
+#include <tbb/parallel_for.h>
+#include <tbb/parallel_reduce.h>
+
+#include <vector>
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace tools {
+
+////////////////////////////////////////
+
+
+// Wrapper functions for the VolumeToMesh converter
+
+
+/// @brief Uniformly mesh any scalar grid that has a continuous isosurface.
+///
+/// @param grid a scalar grid to mesh
+/// @param points output list of world space points
+/// @param quads output quad index list
+/// @param isovalue determines which isosurface to mesh
+template<typename GridType>
+void
+volumeToMesh(
+ const GridType& grid,
+ std::vector<Vec3s>& points,
+ std::vector<Vec4I>& quads,
+ double isovalue = 0.0);
+
+
+/// @brief Adaptively mesh any scalar grid that has a continuous isosurface.
+///
+/// @param grid a scalar grid to mesh
+/// @param points output list of world space points
+/// @param triangles output quad index list
+/// @param quads output quad index list
+/// @param isovalue determines which isosurface to mesh
+/// @param adaptivity surface adaptivity threshold [0 to 1]
+template<typename GridType>
+void
+volumeToMesh(
+ const GridType& grid,
+ std::vector<Vec3s>& points,
+ std::vector<Vec3I>& triangles,
+ std::vector<Vec4I>& quads,
+ double isovalue = 0.0,
+ double adaptivity = 0.0);
+
+
+////////////////////////////////////////
+
+
+/// @brief Polygon flags, used for reference based meshing.
+enum {
+ POLYFLAG_EXTERIOR = 0x1, POLYFLAG_FRACTURE_SEAM = 0x2
+};
+
+
+/// @brief Collection of quads and triangles
+class PolygonPool
+{
+public:
+ PolygonPool()
+ : mNumQuads(0)
+ , mNumTriangles(0)
+ , mQuads(NULL)
+ , mTriangles(NULL)
+ , mQuadFlags(NULL)
+ , mTriangleFlags(NULL)
+ {
+ }
+
+ void resetQuads(size_t size)
+ {
+ mNumQuads = size;
+ mQuads.reset(new openvdb::Vec4I[mNumQuads]);
+ mQuadFlags.reset(new char[mNumQuads]);
+ }
+
+ void clearQuads()
+ {
+ mNumQuads = 0;
+ mQuads.reset(NULL);
+ mQuadFlags.reset(NULL);
+ }
+
+ void resetTriangles(size_t size)
+ {
+ mNumTriangles = size;
+ mTriangles.reset(new openvdb::Vec3I[mNumTriangles]);
+ mTriangleFlags.reset(new char[mNumTriangles]);
+ }
+
+ void clearTriangles()
+ {
+ mNumTriangles = 0;
+ mTriangles.reset(NULL);
+ mTriangleFlags.reset(NULL);
+ }
+
+ const size_t& numQuads() const { return mNumQuads; }
+ const size_t& numTriangles() const { return mNumTriangles; }
+
+ // polygon accessor methods
+ openvdb::Vec4I& quad(size_t n) { return mQuads[n]; }
+ const openvdb::Vec4I& quad(size_t n) const { return mQuads[n]; }
+
+ openvdb::Vec3I& triangle(size_t n) { return mTriangles[n]; }
+ const openvdb::Vec3I& triangle(size_t n) const { return mTriangles[n]; }
+
+ // polygon flags accessor methods
+ char& quadFlags(size_t n) { return mQuadFlags[n]; }
+ const char& quadFlags(size_t n) const { return mQuadFlags[n]; }
+
+ char& triangleFlags(size_t n) { return mTriangleFlags[n]; }
+ const char& triangleFlags(size_t n) const { return mTriangleFlags[n]; }
+
+private:
+ size_t mNumQuads, mNumTriangles;
+ boost::scoped_array<openvdb::Vec4I> mQuads;
+ boost::scoped_array<openvdb::Vec3I> mTriangles;
+ boost::scoped_array<char> mQuadFlags, mTriangleFlags;
+};
+
+
+/// @{
+/// @brief Point and primitive list types.
+typedef boost::scoped_array<openvdb::Vec3s> PointList;
+typedef boost::scoped_array<PolygonPool> PolygonPoolList;
+/// @}
+
+
+////////////////////////////////////////
+
+
+/// @brief Mesh any scalar grid that has a continuous isosurface.
+class VolumeToMesh
+{
+public:
+ /// @param isovalue Determines which isosurface to mesh.
+ /// @param adaptivity Adaptivity threshold [0 to 1]
+ VolumeToMesh(double isovalue = 0, double adaptivity = 0);
+
+ PointList& pointList();
+ const size_t& pointListSize() const;
+
+ PolygonPoolList& polygonPoolList();
+ const PolygonPoolList& polygonPoolList() const;
+ const size_t& polygonPoolListSize() const;
+
+ /// @brief main call
+ /// @note Call with scalar typed grid.
+ template<typename GridT>
+ void operator()(const GridT&);
+
+
+ /// @brief When surfacing fractured SDF fragments, the original unfractured
+ /// SDF grid can be used to eliminate seam lines and tag polygons that are
+ /// coincident with the reference surface with the @c POLYFLAG_EXTERIOR
+ /// flag and polygons that are in proximity to the seam lines with the
+ /// @c POLYFLAG_FRACTURE_SEAM flag. (The performance cost for using this
+ /// reference based scheme compared to the regular meshing scheme is
+ /// approximately 15% for the first fragment and neglect-able for
+ /// subsequent fragments.)
+ ///
+ /// @note Attributes from the original asset such as uv coordinates, normals etc.
+ /// are typically transfered to polygons that are marked with the
+ /// @c POLYFLAG_EXTERIOR flag. Polygons that are not marked with this flag
+ /// are interior to reference surface and might need projected UV coordinates
+ /// or a different material. Polygons marked as @c POLYFLAG_FRACTURE_SEAM can
+ /// be used to drive secondary elements such as debris and dust in a FX pipeline.
+ ///
+ /// @param grid reference surface grid of @c GridT type.
+ /// @param secAdaptivity Secondary adaptivity threshold [0 to 1]. Used in regions
+ /// that do not exist in the reference grid. (Parts of the
+ /// fragment surface that are not coincident with the
+ /// reference surface.)
+ /// @param smoothSeams toggle to smooth seam line edges during mesh extraction,
+ /// removes staircase artifacts.
+ void setRefGrid(const GridBase::ConstPtr& grid, double secAdaptivity = 0, bool smoothSeams = true);
+
+private:
+
+ PointList mPoints;
+ PolygonPoolList mPolygons;
+
+ size_t mPointListSize, mPolygonPoolListSize;
+ double mIsovalue, mPrimAdaptivity, mSecAdaptivity;
+
+ GridBase::ConstPtr mRefGrid;
+ TreeBase::Ptr mRefEdgeTree, mRefTopologyMaskTree, mSeamPointTree;
+ bool mSmoothSeams;
+};
+
+
+////////////////////////////////////////
+
+
+// Internal utility methods
+
+
+namespace internal {
+
+
+// Bit-flags
+enum { INSIDE = 0x1, XEDGE = 0x2, YEDGE = 0x4, ZEDGE = 0x8 };
+
+const bool sAmbiguous[256] =
+ {0,0,0,0,0,1,0,0,
+ 0,0,1,0,0,0,0,0,
+ 0,0,1,0,1,1,1,0,
+ 1,0,1,0,1,0,1,0,
+ 0,1,0,0,1,1,0,0,
+ 1,1,1,0,1,1,0,0,
+ 0,0,0,0,1,1,0,0,
+ 1,0,1,0,1,1,1,0,
+ 0,1,1,1,0,1,0,0,
+ 1,1,1,1,0,0,0,0,
+ 1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,
+ 0,1,0,0,0,1,0,0,
+ 1,1,1,1,0,1,0,0,
+ 0,0,0,0,0,1,0,0,
+ 1,1,1,1,1,1,1,0,
+ 0,1,1,1,1,1,1,1,
+ 0,0,1,0,0,0,0,0,
+ 0,0,1,0,1,1,1,1,
+ 0,0,1,0,0,0,1,0,
+ 1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,
+ 0,0,0,0,1,1,1,1,
+ 0,0,1,0,1,1,1,0,
+ 0,1,1,1,0,1,0,1,
+ 0,0,1,1,0,0,0,0,
+ 0,0,1,1,0,1,1,1,
+ 0,0,1,1,0,0,1,0,
+ 0,1,0,1,0,1,0,1,
+ 0,1,1,1,0,1,0,0,
+ 0,0,0,0,0,1,0,0,
+ 0,0,1,0,0,0,0,0};
+
+
+template<class AccessorT>
+inline bool isAmbiguous(const AccessorT& accessor, const Coord& ijk,
+ typename AccessorT::ValueType isovalue, int dim)
+{
+ unsigned signs = 0;
+ Coord coord = ijk; // i, j, k
+ if (accessor.getValue(coord) < isovalue) signs |= 1u;
+ coord[0] += dim; // i+dim, j, k
+ if (accessor.getValue(coord) < isovalue) signs |= 2u;
+ coord[2] += dim; // i+dim, j, k+dim
+ if (accessor.getValue(coord) < isovalue) signs |= 4u;
+ coord[0] = ijk[0]; // i, j, k+dim
+ if (accessor.getValue(coord) < isovalue) signs |= 8u;
+ coord[1] += dim; coord[2] = ijk[2]; // i, j+dim, k
+ if (accessor.getValue(coord) < isovalue) signs |= 16u;
+ coord[0] += dim; // i+dim, j+dim, k
+ if (accessor.getValue(coord) < isovalue) signs |= 32u;
+ coord[2] += dim; // i+dim, j+dim, k+dim
+ if (accessor.getValue(coord) < isovalue) signs |= 64u;
+ coord[0] = ijk[0]; // i, j+dim, k+dim
+ if (accessor.getValue(coord) < isovalue) signs |= 128u;
+ return sAmbiguous[signs];
+}
+
+
+template<class AccessorT>
+inline bool
+isNonManifold(const AccessorT& accessor, const Coord& ijk,
+ typename AccessorT::ValueType isovalue, const int dim)
+{
+ int hDim = dim >> 1;
+ bool m, p[8]; // Corner signs
+
+ Coord coord = ijk; // i, j, k
+ p[0] = accessor.getValue(coord) < isovalue;
+ coord[0] += dim; // i+dim, j, k
+ p[1] = accessor.getValue(coord) < isovalue;
+ coord[2] += dim; // i+dim, j, k+dim
+ p[2] = accessor.getValue(coord) < isovalue;
+ coord[0] = ijk[0]; // i, j, k+dim
+ p[3] = accessor.getValue(coord) < isovalue;
+ coord[1] += dim; coord[2] = ijk[2]; // i, j+dim, k
+ p[4] = accessor.getValue(coord) < isovalue;
+ coord[0] += dim; // i+dim, j+dim, k
+ p[5] = accessor.getValue(coord) < isovalue;
+ coord[2] += dim; // i+dim, j+dim, k+dim
+ p[6] = accessor.getValue(coord) < isovalue;
+ coord[0] = ijk[0]; // i, j+dim, k+dim
+ p[7] = accessor.getValue(coord) < isovalue;
+
+ // Check if the corner sign configuration is ambiguous
+ unsigned signs = 0;
+ if (p[0]) signs |= 1u;
+ if (p[1]) signs |= 2u;
+ if (p[2]) signs |= 4u;
+ if (p[3]) signs |= 8u;
+ if (p[4]) signs |= 16u;
+ if (p[5]) signs |= 32u;
+ if (p[6]) signs |= 64u;
+ if (p[7]) signs |= 128u;
+ if (sAmbiguous[signs]) return true;
+
+ // Manifold check
+
+ // Evaluate edges
+ int i = ijk[0], ip = ijk[0] + hDim, ipp = ijk[0] + dim;
+ int j = ijk[1], jp = ijk[1] + hDim, jpp = ijk[1] + dim;
+ int k = ijk[2], kp = ijk[2] + hDim, kpp = ijk[2] + dim;
+
+ // edge 1
+ coord.reset(ip, j, k);
+ m = accessor.getValue(coord) < isovalue;
+ if (p[0] != m && p[1] != m) return true;
+
+ // edge 2
+ coord.reset(ipp, j, kp);
+ m = accessor.getValue(coord) < isovalue;
+ if (p[1] != m && p[2] != m) return true;
+
+ // edge 3
+ coord.reset(ip, j, kpp);
+ m = accessor.getValue(coord) < isovalue;
+ if (p[2] != m && p[3] != m) return true;
+
+ // edge 4
+ coord.reset(i, j, kp);
+ m = accessor.getValue(coord) < isovalue;
+ if (p[0] != m && p[3] != m) return true;
+
+ // edge 5
+ coord.reset(ip, jpp, k);
+ m = accessor.getValue(coord) < isovalue;
+ if (p[4] != m && p[5] != m) return true;
+
+ // edge 6
+ coord.reset(ipp, jpp, kp);
+ m = accessor.getValue(coord) < isovalue;
+ if (p[5] != m && p[6] != m) return true;
+
+ // edge 7
+ coord.reset(ip, jpp, kpp);
+ m = accessor.getValue(coord) < isovalue;
+ if (p[6] != m && p[7] != m) return true;
+
+ // edge 8
+ coord.reset(i, jpp, kp);
+ m = accessor.getValue(coord) < isovalue;
+ if (p[7] != m && p[4] != m) return true;
+
+ // edge 9
+ coord.reset(i, jp, k);
+ m = accessor.getValue(coord) < isovalue;
+ if (p[0] != m && p[4] != m) return true;
+
+ // edge 10
+ coord.reset(ipp, jp, k);
+ m = accessor.getValue(coord) < isovalue;
+ if (p[1] != m && p[5] != m) return true;
+
+ // edge 11
+ coord.reset(ipp, jp, kpp);
+ m = accessor.getValue(coord) < isovalue;
+ if (p[2] != m && p[6] != m) return true;
+
+
+ // edge 12
+ coord.reset(i, jp, kpp);
+ m = accessor.getValue(coord) < isovalue;
+ if (p[3] != m && p[7] != m) return true;
+
+
+ // Evaluate faces
+
+ // face 1
+ coord.reset(ip, jp, k);
+ m = accessor.getValue(coord) < isovalue;
+ if (p[0] != m && p[1] != m && p[4] != m && p[5] != m) return true;
+
+ // face 2
+ coord.reset(ipp, jp, kp);
+ m = accessor.getValue(coord) < isovalue;
+ if (p[1] != m && p[2] != m && p[5] != m && p[6] != m) return true;
+
+ // face 3
+ coord.reset(ip, jp, kpp);
+ m = accessor.getValue(coord) < isovalue;
+ if (p[2] != m && p[3] != m && p[6] != m && p[7] != m) return true;
+
+ // face 4
+ coord.reset(i, jp, kp);
+ m = accessor.getValue(coord) < isovalue;
+ if (p[0] != m && p[3] != m && p[4] != m && p[7] != m) return true;
+
+ // face 5
+ coord.reset(ip, j, kp);
+ m = accessor.getValue(coord) < isovalue;
+ if (p[0] != m && p[1] != m && p[2] != m && p[3] != m) return true;
+
+ // face 6
+ coord.reset(ip, jpp, kp);
+ m = accessor.getValue(coord) < isovalue;
+ if (p[4] != m && p[5] != m && p[6] != m && p[7] != m) return true;
+
+ // test cube center
+ coord.reset(ip, jp, kp);
+ m = accessor.getValue(coord) < isovalue;
+ if (p[0] != m && p[1] != m && p[2] != m && p[3] != m &&
+ p[4] != m && p[5] != m && p[6] != m && p[7] != m) return true;
+
+ return false;
+}
+
+
+////////////////////////////////////////
+
+
+template <class LeafType>
+inline void
+mergeVoxels(LeafType& leaf, const Coord& start, int dim, int regionId)
+{
+ Coord ijk, end = start;
+ end[0] += dim;
+ end[1] += dim;
+ end[2] += dim;
+
+ for (ijk[0] = start[0]; ijk[0] < end[0]; ++ijk[0]) {
+ for (ijk[1] = start[1]; ijk[1] < end[1]; ++ijk[1]) {
+ for (ijk[2] = start[2]; ijk[2] < end[2]; ++ijk[2]) {
+ if(leaf.isValueOn(ijk)) leaf.setValue(ijk, regionId);
+ }
+ }
+ }
+}
+
+
+// Note that we must use ValueType::value_type or else Visual C++ gets confused
+// thinking that it is a constructor.
+template <class LeafType>
+inline bool
+isMergable(LeafType& leaf, const Coord& start, int dim,
+ typename LeafType::ValueType::value_type adaptivity)
+{
+ if (adaptivity < 1e-6) return false;
+
+ typedef typename LeafType::ValueType VecT;
+ Coord ijk, end = start;
+ end[0] += dim;
+ end[1] += dim;
+ end[2] += dim;
+
+ std::vector<VecT> norms;
+ for (ijk[0] = start[0]; ijk[0] < end[0]; ++ijk[0]) {
+ for (ijk[1] = start[1]; ijk[1] < end[1]; ++ijk[1]) {
+ for (ijk[2] = start[2]; ijk[2] < end[2]; ++ijk[2]) {
+
+ if(!leaf.isValueOn(ijk)) continue;
+ norms.push_back(leaf.getValue(ijk));
+ }
+ }
+ }
+
+ size_t N = norms.size();
+ for (size_t ni = 0; ni < N; ++ni) {
+ VecT n_i = norms[ni];
+ for (size_t nj = 0; nj < N; ++nj) {
+ VecT n_j = norms[nj];
+ if ((1.0 - n_i.dot(n_j)) > adaptivity) return false;
+ }
+ }
+ return true;
+}
+
+
+////////////////////////////////////////
+
+
+template <class TreeT>
+class LeafCPtrList
+{
+public:
+ typedef std::vector<const typename TreeT::LeafNodeType *> ListT;
+
+ LeafCPtrList(const TreeT& tree)
+ {
+ mLeafNodes.reserve(tree.leafCount());
+ typename TreeT::LeafCIter iter = tree.cbeginLeaf();
+ for ( ; iter; ++iter) mLeafNodes.push_back(iter.getLeaf());
+ }
+
+ size_t size() const { return mLeafNodes.size(); }
+
+ const typename TreeT::LeafNodeType* operator[](size_t n) const
+ { return mLeafNodes[n]; }
+
+ tbb::blocked_range<size_t> getRange() const
+ { return tbb::blocked_range<size_t>(0, mLeafNodes.size()); }
+
+ const ListT& getList() const { return mLeafNodes; }
+
+private:
+ ListT mLeafNodes;
+};
+
+
+template <class TreeT>
+class LeafPtrList
+{
+public:
+ typedef std::vector<typename TreeT::LeafNodeType *> ListT;
+
+ LeafPtrList(TreeT& tree)
+ {
+ mLeafNodes.reserve(tree.leafCount());
+ typename TreeT::LeafIter iter = tree.beginLeaf();
+ for ( ; iter; ++iter) mLeafNodes.push_back(iter.getLeaf());
+ }
+
+ size_t size() const { return mLeafNodes.size(); }
+
+ typename TreeT::LeafNodeType* operator[](size_t n) const
+ { return mLeafNodes[n]; }
+
+ tbb::blocked_range<size_t> getRange() const
+ { return tbb::blocked_range<size_t>(0, mLeafNodes.size()); }
+
+ const ListT& getList() const { return mLeafNodes; }
+
+private:
+ ListT mLeafNodes;
+};
+
+
+////////////////////////////////////////
+
+
+template<typename DistTreeT>
+struct ReferenceData
+{
+ typedef typename DistTreeT::ValueType DistValueT;
+ typedef typename DistTreeT::template ValueConverter<char>::Type CharTreeT;
+ typedef typename DistTreeT::template ValueConverter<bool>::Type BoolTreeT;
+ typedef typename DistTreeT::template ValueConverter<Vec3s>::Type Vec3sTreeT;
+
+ ReferenceData()
+ : mDistTree(NULL)
+ , mEdgeTree(NULL)
+ , mTopologyMaskTree(NULL)
+ , mSeamPointTree(NULL)
+ , mSeamMaskTree(typename BoolTreeT::Ptr())
+ , mSmoothingMaskTree(typename BoolTreeT::Ptr())
+ , mInternalAdaptivity(DistValueT(0.0))
+ {
+ }
+
+ bool isValid() const
+ {
+ return mDistTree && mEdgeTree && mTopologyMaskTree && mSeamMaskTree;
+ }
+
+ const DistTreeT* mDistTree;
+ const CharTreeT* mEdgeTree;
+ BoolTreeT* mTopologyMaskTree;
+ Vec3sTreeT* mSeamPointTree;
+ typename BoolTreeT::Ptr mSeamMaskTree, mSmoothingMaskTree;
+ DistValueT mInternalAdaptivity;
+};
+
+
+////////////////////////////////////////
+
+
+template <class DistTreeT>
+class Count
+{
+public:
+ Count(const LeafPtrList<DistTreeT>&, std::vector<size_t>&);
+ inline Count(const Count<DistTreeT>&);
+
+ void runParallel();
+ void runSerial();
+
+ inline void operator()(const tbb::blocked_range<size_t>&) const;
+private:
+ const LeafPtrList<DistTreeT>& mLeafNodes;
+ std::vector<size_t>& mLeafRegionCount;
+};
+
+
+template <class DistTreeT>
+Count<DistTreeT>::Count(
+ const LeafPtrList<DistTreeT>& leafs,
+ std::vector<size_t>& leafRegionCount)
+ : mLeafNodes(leafs)
+ , mLeafRegionCount(leafRegionCount)
+{
+}
+
+
+template <class DistTreeT>
+inline
+Count<DistTreeT>::Count(const Count<DistTreeT>& rhs)
+ : mLeafNodes(rhs.mLeafNodes)
+ , mLeafRegionCount(rhs.mLeafRegionCount)
+{
+}
+
+
+template <class DistTreeT>
+void
+Count<DistTreeT>::runParallel()
+{
+ tbb::parallel_for(mLeafNodes.getRange(), *this);
+}
+
+
+template <class DistTreeT>
+void
+Count<DistTreeT>::runSerial()
+{
+ (*this)(mLeafNodes.getRange());
+}
+
+
+template <class DistTreeT>
+inline void
+Count<DistTreeT>::operator()(const tbb::blocked_range<size_t>& range) const
+{
+ for (size_t n = range.begin(); n != range.end(); ++n) {
+ mLeafRegionCount[n] = size_t(mLeafNodes[n]->onVoxelCount());
+ }
+}
+
+
+////////////////////////////////////////
+
+
+template <class DistTreeT>
+class Merge
+{
+public:
+ typedef typename DistTreeT::ValueType DistValueT;
+ typedef typename DistTreeT::template ValueConverter<bool>::Type BoolTreeT;
+ typedef typename DistTreeT::template ValueConverter<int>::Type IntTreeT;
+
+ Merge(
+ const DistTreeT& distTree,
+ LeafPtrList<IntTreeT>& auxLeafs,
+ std::vector<size_t>& leafRegionCount,
+ const DistValueT iso,
+ const DistValueT adaptivity);
+
+ inline Merge(const Merge<DistTreeT>&);
+
+ void setRefData(const ReferenceData<DistTreeT>&);
+
+ void runParallel();
+ void runSerial();
+
+ void operator()(const tbb::blocked_range<size_t>&) const;
+
+private:
+ const DistTreeT& mDistTree;
+ LeafPtrList<IntTreeT>& mAuxLeafs;
+ std::vector<size_t>& mLeafRegionCount;
+ const DistValueT mIsovalue, mAdaptivity;
+ const ReferenceData<DistTreeT>* mRefData;
+};
+
+
+template <class DistTreeT>
+Merge<DistTreeT>::Merge(
+ const DistTreeT& distTree,
+ LeafPtrList<IntTreeT>& auxLeafs,
+ std::vector<size_t>& leafRegionCount,
+ const DistValueT iso,
+ const DistValueT adaptivity)
+ : mDistTree(distTree)
+ , mAuxLeafs(auxLeafs)
+ , mLeafRegionCount(leafRegionCount)
+ , mIsovalue(iso)
+ , mAdaptivity(adaptivity)
+ , mRefData(NULL)
+{
+}
+
+
+template <class DistTreeT>
+inline
+Merge<DistTreeT>::Merge(const Merge<DistTreeT>& rhs)
+ : mDistTree(rhs.mDistTree)
+ , mAuxLeafs(rhs.mAuxLeafs)
+ , mLeafRegionCount(rhs.mLeafRegionCount)
+ , mIsovalue(rhs.mIsovalue)
+ , mAdaptivity(rhs.mAdaptivity)
+ , mRefData(rhs.mRefData)
+{
+}
+
+
+template <class DistTreeT>
+void
+Merge<DistTreeT>::runParallel()
+{
+ tbb::parallel_for(mAuxLeafs.getRange(), *this);
+}
+
+
+template <class DistTreeT>
+void
+Merge<DistTreeT>::runSerial()
+{
+ (*this)(mAuxLeafs.getRange());
+}
+
+template <class DistTreeT>
+void
+Merge<DistTreeT>::setRefData(const ReferenceData<DistTreeT>& refData)
+{
+ mRefData = &refData;
+}
+
+template <class DistTreeT>
+void
+Merge<DistTreeT>::operator()(const tbb::blocked_range<size_t>& range) const
+{
+ typedef math::Vec3<DistValueT> Vec3T;
+ typedef typename BoolTreeT::LeafNodeType BoolLeafT;
+ typedef typename IntTreeT::LeafNodeType IntLeafT;
+ typedef typename BoolLeafT::template ValueConverter<Vec3T>::Type Vec3LeafT;
+
+ typedef typename IntLeafT::ValueOnIter IntIterT;
+ typedef typename BoolLeafT::ValueOnCIter BoolCIterT;
+
+ typedef typename tree::ValueAccessor<BoolTreeT> BoolTreeAccessorT;
+ typedef typename tree::ValueAccessor<const BoolTreeT> BoolTreeCAccessorT;
+
+ boost::scoped_ptr<BoolTreeAccessorT> seamMaskAcc;
+ boost::scoped_ptr<BoolTreeCAccessorT> topologyMaskAcc;
+ if (mRefData && mRefData->isValid()) {
+ seamMaskAcc.reset(new BoolTreeAccessorT(*mRefData->mSeamMaskTree.get()));
+ topologyMaskAcc.reset(new BoolTreeCAccessorT(*mRefData->mTopologyMaskTree));
+ }
+ const bool hasRefData = seamMaskAcc && topologyMaskAcc;
+
+ const int LeafDim = BoolLeafT::DIM;
+ tree::ValueAccessor<const DistTreeT> distAcc(mDistTree);
+
+ // Allocate reusable leaf buffers
+ BoolLeafT mask;
+ Vec3LeafT gradientBuffer;
+ Coord ijk, coord, end;
+
+ for (size_t n = range.begin(); n != range.end(); ++n) {
+
+ DistValueT adaptivity = mAdaptivity;
+ IntLeafT& auxLeaf = *mAuxLeafs[n];
+
+ const Coord& origin = auxLeaf.getOrigin();
+ end[0] = origin[0] + LeafDim;
+ end[1] = origin[1] + LeafDim;
+ end[2] = origin[2] + LeafDim;
+
+ mask.setValuesOff();
+
+ // Mask off seam line adjacent voxels
+ if (hasRefData) {
+ const BoolLeafT* seamMask = seamMaskAcc->probeConstLeaf(origin);
+ if (seamMask != NULL) {
+ for (BoolCIterT it = seamMask->cbeginValueOn(); it; ++it) {
+ ijk = it.getCoord();
+ coord[0] = ijk[0] - (ijk[0] % 2);
+ coord[1] = ijk[1] - (ijk[1] % 2);
+ coord[2] = ijk[2] - (ijk[2] % 2);
+ mask.setActiveState(coord, true);
+ }
+ }
+ if (topologyMaskAcc->probeConstLeaf(origin) == NULL) {
+ adaptivity = mRefData->mInternalAdaptivity;
+ }
+ }
+
+ // Mask off ambiguous voxels
+ for (IntIterT it = auxLeaf.beginValueOn(); it; ++it) {
+ ijk = it.getCoord();
+ coord[0] = ijk[0] - (ijk[0] % 2);
+ coord[1] = ijk[1] - (ijk[1] % 2);
+ coord[2] = ijk[2] - (ijk[2] % 2);
+ if(mask.isValueOn(coord)) continue;
+ mask.setActiveState(coord, isAmbiguous(distAcc, ijk, mIsovalue, 1));
+ }
+
+ int dim = 2;
+ // Mask off topologically ambiguous 2x2x2 voxel sub-blocks
+ for (ijk[0] = origin[0]; ijk[0] < end[0]; ijk[0] += dim) {
+ for (ijk[1] = origin[1]; ijk[1] < end[1]; ijk[1] += dim) {
+ for (ijk[2] = origin[2]; ijk[2] < end[2]; ijk[2] += dim) {
+ if (isNonManifold(distAcc, ijk, mIsovalue, dim)) {
+ mask.setActiveState(ijk, true);
+ }
+ }
+ }
+ }
+
+ // Compute the gradient for the remaining voxels
+ gradientBuffer.setValuesOff();
+
+ for (IntIterT it = auxLeaf.beginValueOn(); it; ++it) {
+
+ ijk = it.getCoord();
+ coord[0] = ijk[0] - (ijk[0] % dim);
+ coord[1] = ijk[1] - (ijk[1] % dim);
+ coord[2] = ijk[2] - (ijk[2] % dim);
+ if(mask.isValueOn(coord)) continue;
+
+ Vec3T norm(math::ISGradient<math::CD_2ND>::result(distAcc, ijk));
+ // Normalize (Vec3's normalize uses isApproxEqual, which uses abs and does more work)
+ DistValueT length = norm.length();
+ if (length > DistValueT(1.0e-7)) {
+ norm *= DistValueT(1.0) / length;
+ }
+ gradientBuffer.setValue(ijk, norm);
+ }
+
+ int regionId = 1, next_dim = dim << 1;
+
+ // Process the first adaptivity level.
+ for (ijk[0] = 0; ijk[0] < LeafDim; ijk[0] += dim) {
+ coord[0] = ijk[0] - (ijk[0] % next_dim);
+ for (ijk[1] = 0; ijk[1] < LeafDim; ijk[1] += dim) {
+ coord[1] = ijk[1] - (ijk[1] % next_dim);
+ for (ijk[2] = 0; ijk[2] < LeafDim; ijk[2] += dim) {
+ coord[2] = ijk[2] - (ijk[2] % next_dim);
+ if(mask.isValueOn(ijk) || !isMergable(gradientBuffer, ijk, dim, adaptivity)) {
+ mask.setActiveState(coord, true);
+ continue;
+ }
+ mergeVoxels(auxLeaf, ijk, dim, regionId++);
+ }
+ }
+ }
+
+
+ // Process remaining adaptivity levels
+ for (dim = 4; dim < LeafDim; dim = dim << 1) {
+ next_dim = dim << 1;
+ coord[0] = ijk[0] - (ijk[0] % next_dim);
+ for (ijk[0] = origin[0]; ijk[0] < end[0]; ijk[0] += dim) {
+ coord[1] = ijk[1] - (ijk[1] % next_dim);
+ for (ijk[1] = origin[1]; ijk[1] < end[1]; ijk[1] += dim) {
+ coord[2] = ijk[2] - (ijk[2] % next_dim);
+ for (ijk[2] = origin[2]; ijk[2] < end[2]; ijk[2] += dim) {
+
+ if (mask.isValueOn(ijk) || isNonManifold(distAcc, ijk, mIsovalue, dim) ||
+ !isMergable(gradientBuffer, ijk, dim, adaptivity)) {
+ mask.setActiveState(coord, true);
+ continue;
+ }
+ mergeVoxels(auxLeaf, ijk, dim, regionId++);
+ }
+ }
+ }
+ }
+
+
+ if (!(mask.isValueOn(origin) || isNonManifold(distAcc, origin, mIsovalue, LeafDim))
+ && isMergable(gradientBuffer, origin, LeafDim, adaptivity)) {
+ mergeVoxels(auxLeaf, origin, LeafDim, regionId++);
+ }
+
+
+ // Count unique regions
+ size_t numVoxels = 0;
+ IntLeafT tmpLeaf(auxLeaf);
+ for (IntIterT it = tmpLeaf.beginValueOn(); it; ++it) {
+ if(it.getValue() == 0) {
+ it.setValueOff();
+ ++numVoxels;
+ }
+ }
+
+ while (tmpLeaf.onVoxelCount() > 0) {
+ ++numVoxels;
+ IntIterT it = tmpLeaf.beginValueOn();
+ regionId = it.getValue();
+ for (; it; ++it) {
+ if (it.getValue() == regionId) it.setValueOff();
+ }
+ }
+
+ mLeafRegionCount[n] = numVoxels;
+ }
+}
+
+
+////////////////////////////////////////
+
+
+template <class DistTreeT>
+class PointGen
+{
+public:
+ typedef typename DistTreeT::ValueType DistValueT;
+ typedef tree::ValueAccessor<const DistTreeT> DistTreeAccessorT;
+ typedef typename DistTreeT::template ValueConverter<int>::Type IntTreeT;
+
+ PointGen(
+ const DistTreeT& distTree,
+ const LeafPtrList<IntTreeT>& auxLeafs,
+ std::vector<size_t>& leafIndices,
+ const openvdb::math::Transform& xform,
+ PointList& points,
+ double iso = 0.0);
+
+ PointGen(const PointGen<DistTreeT>&);
+
+ void setRefData(const ReferenceData<DistTreeT>&);
+
+ void runParallel();
+ void runSerial();
+
+ void operator()(const tbb::blocked_range<size_t>&) const;
+
+private:
+ const DistTreeT& mDistTree;
+ const LeafPtrList<IntTreeT>& mAuxLeafs;
+ const std::vector<size_t>& mLeafIndices;
+ const openvdb::math::Transform& mTransform;
+ const PointList& mPoints;
+ const double mIsovalue;
+ const ReferenceData<DistTreeT>* mRefData;
+
+ double root(double v0, double v1) const { return (mIsovalue - v0) / (v1 - v0); }
+ int calcAvgPoint(DistTreeAccessorT&, const Coord&, openvdb::Vec3d&) const;
+};
+
+
+template <class DistTreeT>
+PointGen<DistTreeT>::PointGen(
+ const DistTreeT& distTree,
+ const LeafPtrList<IntTreeT>& auxLeafs,
+ std::vector<size_t>& leafIndices,
+ const openvdb::math::Transform& xform,
+ PointList& points,
+ double iso)
+ : mDistTree(distTree)
+ , mAuxLeafs(auxLeafs)
+ , mLeafIndices(leafIndices)
+ , mTransform(xform)
+ , mPoints(points)
+ , mIsovalue(iso)
+ , mRefData(NULL)
+{
+}
+
+
+template <class DistTreeT>
+PointGen<DistTreeT>::PointGen(const PointGen<DistTreeT>& rhs)
+ : mDistTree(rhs.mDistTree)
+ , mAuxLeafs(rhs.mAuxLeafs)
+ , mLeafIndices(rhs.mLeafIndices)
+ , mTransform(rhs.mTransform)
+ , mPoints(rhs.mPoints)
+ , mIsovalue(rhs.mIsovalue)
+ , mRefData(rhs.mRefData)
+{
+}
+
+
+template <class DistTreeT>
+void
+PointGen<DistTreeT>::setRefData(
+ const ReferenceData<DistTreeT>& refData)
+{
+ mRefData = &refData;
+}
+
+template <class DistTreeT>
+void
+PointGen<DistTreeT>::runParallel()
+{
+ tbb::parallel_for(mAuxLeafs.getRange(), *this);
+}
+
+
+template <class DistTreeT>
+void
+PointGen<DistTreeT>::runSerial()
+{
+ (*this)(mAuxLeafs.getRange());
+}
+
+
+template <class DistTreeT>
+void
+PointGen<DistTreeT>::operator()(const tbb::blocked_range<size_t>& range) const
+{
+ typedef typename DistTreeT::template ValueConverter<bool>::Type BoolTreeT;
+ typedef typename DistTreeT::template ValueConverter<Vec3s>::Type Vec3sTreeT;
+
+ typedef tree::ValueAccessor<BoolTreeT> BoolTreeAccessorT;
+ typedef tree::ValueAccessor<Vec3sTreeT> Vec3sTreeAccessorT;
+
+ typedef typename BoolTreeT::LeafNodeType BoolLeafT;
+ typedef typename IntTreeT::LeafNodeType IntLeafT;
+ typedef typename Vec3sTreeT::LeafNodeType Vec3sLeafT;
+
+ boost::scoped_ptr<DistTreeAccessorT> refDistAcc;
+ boost::scoped_ptr<BoolTreeAccessorT> refMaskAcc, refSmoothMaskAcc;
+ boost::scoped_ptr<Vec3sTreeAccessorT> refPtnAcc;
+
+ if (mRefData && mRefData->isValid()) {
+ refDistAcc.reset(new DistTreeAccessorT(*mRefData->mDistTree));
+ refMaskAcc.reset(new BoolTreeAccessorT(*mRefData->mTopologyMaskTree));
+ refSmoothMaskAcc.reset(new BoolTreeAccessorT(*mRefData->mSmoothingMaskTree));
+ refPtnAcc.reset(new Vec3sTreeAccessorT(*mRefData->mSeamPointTree));
+ }
+
+
+ const bool hasRefData = refDistAcc && refMaskAcc;
+ typename IntTreeT::LeafNodeType::ValueOnIter auxIter;
+ DistTreeAccessorT distAcc(mDistTree);
+
+ Coord ijk;
+ openvdb::Vec3d avg, tmp;
+
+ for (size_t n = range.begin(); n != range.end(); ++n) {
+
+ size_t idx = mLeafIndices[n];
+ IntLeafT& auxLeaf = *mAuxLeafs[n];
+
+ BoolLeafT* maskLeaf = NULL;
+ BoolLeafT* smoothMaskLeaf = NULL;
+ Vec3sLeafT* ptnLeaf = NULL;
+ if (hasRefData) {
+ maskLeaf = refMaskAcc->probeLeaf(auxLeaf.getOrigin());
+ smoothMaskLeaf = refSmoothMaskAcc->probeLeaf(auxLeaf.getOrigin());
+ ptnLeaf = refPtnAcc->probeLeaf(auxLeaf.getOrigin());
+ }
+
+ for (auxIter = auxLeaf.beginValueOn(); auxIter; ++auxIter) {
+
+ if(auxIter.getValue() == 0) {
+
+ auxIter.setValue(idx);
+ auxIter.setValueOff();
+ ijk = auxIter.getCoord();
+
+ if (hasRefData && maskLeaf && maskLeaf->isValueOn(ijk)) {
+
+ if (ptnLeaf && ptnLeaf->isValueOn(ijk)) {
+ avg = ptnLeaf->getValue(ijk);
+ } else {
+ int e1 = calcAvgPoint(*refDistAcc.get(), ijk, avg);
+
+ if (e1 != (XEDGE|YEDGE|ZEDGE)) {
+ int e2 = calcAvgPoint(distAcc, ijk, tmp);
+ if((e2 & (~e1)) != 0) smoothMaskLeaf->setValueOn(ijk);
+ }
+ }
+ } else {
+ calcAvgPoint(distAcc, ijk, avg);
+ }
+
+ openvdb::Vec3s& ptn = mPoints[idx];
+ ptn[0] = float(avg[0]);
+ ptn[1] = float(avg[1]);
+ ptn[2] = float(avg[2]);
+
+ ++idx;
+ }
+ }
+
+ while(auxLeaf.onVoxelCount() > 0) {
+
+ avg[0] = 0;
+ avg[1] = 0;
+ avg[2] = 0;
+
+ auxIter = auxLeaf.beginValueOn();
+ int regionId = auxIter.getValue(), points = 0;
+
+ for (; auxIter; ++auxIter) {
+ if(auxIter.getValue() == regionId) {
+
+ auxIter.setValue(idx);
+ auxIter.setValueOff();
+ ijk = auxIter.getCoord();
+
+ if (hasRefData && maskLeaf && maskLeaf->isValueOn(ijk)) {
+ calcAvgPoint(*refDistAcc.get(), ijk, tmp);
+ } else {
+ calcAvgPoint(distAcc, ijk, tmp);
+ }
+
+ avg += tmp;
+ ++points;
+ }
+ }
+
+ if (points > 1) {
+ double w = 1.0 / double(points);
+ avg[0] *= w;
+ avg[1] *= w;
+ avg[2] *= w;
+ }
+
+ openvdb::Vec3s& ptn = mPoints[idx];
+ ptn[0] = float(avg[0]);
+ ptn[1] = float(avg[1]);
+ ptn[2] = float(avg[2]);
+ ++idx;
+ }
+ }
+}
+
+template <class DistTreeT>
+int
+PointGen<DistTreeT>::calcAvgPoint(DistTreeAccessorT& acc,
+ const Coord& ijk, openvdb::Vec3d& avg) const
+{
+ double values[8];
+ bool signMask[8];
+ Coord coord;
+
+ // Sample corner values
+ coord = ijk;
+ values[0] = double(acc.getValue(coord)); // i, j, k
+
+ coord[0] += 1;
+ values[1] = double(acc.getValue(coord)); // i+1, j, k
+
+ coord[2] += 1;
+ values[2] = double(acc.getValue(coord)); // i+i, j, k+1
+
+ coord[0] = ijk[0];
+ values[3] = double(acc.getValue(coord)); // i, j, k+1
+
+ coord[1] += 1; coord[2] = ijk[2];
+ values[4] = double(acc.getValue(coord)); // i, j+1, k
+
+ coord[0] += 1;
+ values[5] = double(acc.getValue(coord)); // i+1, j+1, k
+
+ coord[2] += 1;
+ values[6] = double(acc.getValue(coord)); // i+1, j+1, k+1
+
+ coord[0] = ijk[0];
+ values[7] = double(acc.getValue(coord)); // i, j+1, k+1
+
+ // init sign mask
+ for (int n = 0; n < 8; ++n) signMask[n] = (values[n] < mIsovalue);
+
+ int samples = 0, edgeFlags = 0;
+ avg[0] = 0.0;
+ avg[1] = 0.0;
+ avg[2] = 0.0;
+
+ if (signMask[0] != signMask[1]) { // Edged: 0 - 1
+ avg[0] += root(values[0], values[1]);
+ ++samples;
+ edgeFlags |= XEDGE;
+ }
+
+ if (signMask[1] != signMask[2]) { // Edged: 1 - 2
+ avg[0] += 1.0;
+ avg[2] += root(values[1], values[2]);
+ ++samples;
+ edgeFlags |= ZEDGE;
+ }
+
+ if (signMask[3] != signMask[2]) { // Edged: 3 - 2
+ avg[0] += root(values[3], values[2]);
+ avg[2] += 1.0;
+ ++samples;
+ edgeFlags |= XEDGE;
+ }
+
+ if (signMask[0] != signMask[3]) { // Edged: 0 - 3
+ avg[2] += root(values[0], values[3]);
+ ++samples;
+ edgeFlags |= ZEDGE;
+ }
+
+ if (signMask[4] != signMask[5]) { // Edged: 4 - 5
+ avg[0] += root(values[4], values[5]);
+ avg[1] += 1.0;
+ ++samples;
+ edgeFlags |= XEDGE;
+ }
+
+ if (signMask[5] != signMask[6]) { // Edged: 5 - 6
+ avg[0] += 1.0;
+ avg[1] += 1.0;
+ avg[2] += root(values[5], values[6]);
+ ++samples;
+ edgeFlags |= ZEDGE;
+ }
+
+ if (signMask[7] != signMask[6]) { // Edged: 7 - 6
+ avg[0] += root(values[7], values[6]);
+ avg[1] += 1.0;
+ avg[2] += 1.0;
+ ++samples;
+ edgeFlags |= XEDGE;
+ }
+
+ if (signMask[4] != signMask[7]) { // Edged: 4 - 7
+ avg[1] += 1.0;
+ avg[2] += root(values[4], values[7]);
+ ++samples;
+ edgeFlags |= ZEDGE;
+ }
+
+ if (signMask[0] != signMask[4]) { // Edged: 0 - 4
+ avg[1] += root(values[0], values[4]);
+ ++samples;
+ edgeFlags |= YEDGE;
+ }
+
+ if (signMask[1] != signMask[5]) { // Edged: 1 - 5
+ avg[0] += 1.0;
+ avg[1] += root(values[1], values[5]);
+ ++samples;
+ edgeFlags |= YEDGE;
+ }
+
+ if (signMask[2] != signMask[6]) { // Edged: 2 - 6
+ avg[0] += 1.0;
+ avg[1] += root(values[2], values[6]);
+ avg[2] += 1.0;
+ ++samples;
+ edgeFlags |= YEDGE;
+ }
+
+ if (signMask[3] != signMask[7]) { // Edged: 3 - 7
+ avg[1] += root(values[3], values[7]);
+ avg[2] += 1.0;
+ ++samples;
+ edgeFlags |= YEDGE;
+ }
+
+ if (samples > 1) {
+ double w = 1.0 / double(samples);
+ avg[0] *= w;
+ avg[1] *= w;
+ avg[2] *= w;
+ }
+
+ // offset by cell-origin
+ avg[0] += double(ijk[0]);
+ avg[1] += double(ijk[1]);
+ avg[2] += double(ijk[2]);
+
+ avg = mTransform.indexToWorld(avg);
+
+ return edgeFlags;
+}
+
+
+////////////////////////////////////////
+
+
+struct QuadMeshOp
+{
+ QuadMeshOp(): mIdx(0), mPolygonPool(NULL) {}
+
+ void init(const size_t upperBound, PolygonPool& quadPool)
+ {
+ mPolygonPool = &quadPool;
+ mPolygonPool->resetQuads(upperBound);
+ mIdx = 0;
+ }
+
+ void addPrim(const Vec4I& verts, bool reverse, char flags = 0)
+ {
+ if (!reverse) {
+ mPolygonPool->quad(mIdx) = verts;
+ } else {
+ Vec4I& quad = mPolygonPool->quad(mIdx);
+ quad[0] = verts[3];
+ quad[1] = verts[2];
+ quad[2] = verts[1];
+ quad[3] = verts[0];
+ }
+ mPolygonPool->quadFlags(mIdx) = flags;
+ ++mIdx;
+ }
+
+ void done() {}
+
+private:
+ size_t mIdx;
+ PolygonPool* mPolygonPool;
+};
+
+
+struct AdaptiveMeshOp
+{
+ AdaptiveMeshOp(): mQuadIdx(0), mTriangleIdx(0), mPolygonPool(NULL), mTmpPolygonPool() {}
+
+ void init(const size_t upperBound, PolygonPool& polygonPool)
+ {
+ mPolygonPool = &polygonPool;
+
+ mTmpPolygonPool.resetQuads(upperBound);
+ mTmpPolygonPool.resetTriangles(upperBound);
+
+ mQuadIdx = 0;
+ mTriangleIdx = 0;
+ }
+
+ void addPrim(const Vec4I& verts, bool reverse, char flags = 0)
+ {
+ if (verts[0] != verts[1] && verts[0] != verts[2] && verts[0] != verts[3]
+ && verts[1] != verts[2] && verts[1] != verts[3] && verts[2] != verts[3]) {
+ mTmpPolygonPool.quadFlags(mQuadIdx) = flags;
+ addQuad(verts, reverse);
+ } else if (
+ verts[0] == verts[3] &&
+ verts[1] != verts[2] &&
+ verts[1] != verts[0] &&
+ verts[2] != verts[0]) {
+ mTmpPolygonPool.triangleFlags(mTriangleIdx) = flags;
+ addTriangle(verts[0], verts[1], verts[2], reverse);
+ } else if (
+ verts[1] == verts[2] &&
+ verts[0] != verts[3] &&
+ verts[0] != verts[1] &&
+ verts[3] != verts[1]) {
+ mTmpPolygonPool.triangleFlags(mTriangleIdx) = flags;
+ addTriangle(verts[0], verts[1], verts[3], reverse);
+ } else if (
+ verts[0] == verts[1] &&
+ verts[2] != verts[3] &&
+ verts[2] != verts[0] &&
+ verts[3] != verts[0]) {
+ mTmpPolygonPool.triangleFlags(mTriangleIdx) = flags;
+ addTriangle(verts[0], verts[2], verts[3], reverse);
+ } else if (
+ verts[2] == verts[3] &&
+ verts[0] != verts[1] &&
+ verts[0] != verts[2] &&
+ verts[1] != verts[2]) {
+ mTmpPolygonPool.triangleFlags(mTriangleIdx) = flags;
+ addTriangle(verts[0], verts[1], verts[2], reverse);
+ }
+ }
+
+
+ void done()
+ {
+ mPolygonPool->resetQuads(mQuadIdx);
+ for (size_t i = 0; i < mQuadIdx; ++i) {
+ mPolygonPool->quad(i) = mTmpPolygonPool.quad(i);
+ mPolygonPool->quadFlags(i) = mTmpPolygonPool.quadFlags(i);
+ }
+ mTmpPolygonPool.clearQuads();
+
+ mPolygonPool->resetTriangles(mTriangleIdx);
+ for (size_t i = 0; i < mTriangleIdx; ++i) {
+ mPolygonPool->triangle(i) = mTmpPolygonPool.triangle(i);
+ mPolygonPool->triangleFlags(i) = mTmpPolygonPool.triangleFlags(i);
+ }
+ mTmpPolygonPool.clearTriangles();
+ }
+
+private:
+
+ void addQuad(const Vec4I& verts, bool reverse)
+ {
+ if (!reverse) {
+ mTmpPolygonPool.quad(mQuadIdx) = verts;
+ } else {
+ Vec4I& quad = mTmpPolygonPool.quad(mQuadIdx);
+ quad[0] = verts[3];
+ quad[1] = verts[2];
+ quad[2] = verts[1];
+ quad[3] = verts[0];
+ }
+ ++mQuadIdx;
+ }
+
+ void addTriangle(unsigned v0, unsigned v1, unsigned v2, bool reverse)
+ {
+ Vec3I& prim = mTmpPolygonPool.triangle(mTriangleIdx);
+
+ prim[1] = v1;
+
+ if (!reverse) {
+ prim[0] = v0;
+ prim[2] = v2;
+ } else {
+ prim[0] = v2;
+ prim[2] = v0;
+ }
+ ++mTriangleIdx;
+ }
+
+ size_t mQuadIdx, mTriangleIdx;
+ PolygonPool *mPolygonPool;
+ PolygonPool mTmpPolygonPool;
+};
+
+
+////////////////////////////////////////
+
+
+template<class DistTreeT, class MeshingOp>
+class MeshGen
+{
+public:
+ typedef typename DistTreeT::template ValueConverter<char>::Type CharTreeT;
+ typedef typename DistTreeT::template ValueConverter<int>::Type IntTreeT;
+
+ MeshGen(const LeafCPtrList<CharTreeT>& edgeLeafs, const IntTreeT& auxTree, PolygonPoolList&);
+ MeshGen(const MeshGen<DistTreeT, MeshingOp>&);
+
+ void setRefData(const ReferenceData<DistTreeT>&);
+
+ void runParallel();
+ void runSerial();
+
+ void operator()(const tbb::blocked_range<size_t>&) const;
+
+private:
+ const LeafCPtrList<CharTreeT>& mEdgeLeafs;
+ const IntTreeT& mAuxTree;
+ const PolygonPoolList& mPolygonPoolList;
+ size_t mID;
+ const ReferenceData<DistTreeT>* mRefData;
+};
+
+
+template<class DistTreeT, class MeshingOp>
+MeshGen<DistTreeT, MeshingOp>::MeshGen(const LeafCPtrList<CharTreeT>& edgeLeafs,
+ const IntTreeT& auxTree, PolygonPoolList& polygonPoolList)
+ : mEdgeLeafs(edgeLeafs)
+ , mAuxTree(auxTree)
+ , mPolygonPoolList(polygonPoolList)
+ , mRefData(NULL)
+{
+}
+
+
+template<class DistTreeT, class MeshingOp>
+MeshGen<DistTreeT, MeshingOp>::MeshGen(const MeshGen<DistTreeT, MeshingOp>& rhs)
+ : mEdgeLeafs(rhs.mEdgeLeafs)
+ , mAuxTree(rhs.mAuxTree)
+ , mPolygonPoolList(rhs.mPolygonPoolList)
+ , mRefData(rhs.mRefData)
+{
+}
+
+
+template<class DistTreeT, class MeshingOp>
+void
+MeshGen<DistTreeT, MeshingOp>::setRefData(
+ const ReferenceData<DistTreeT>& refData)
+{
+ mRefData = &refData;
+}
+
+
+template<class DistTreeT, class MeshingOp>
+void
+MeshGen<DistTreeT, MeshingOp>::runParallel()
+{
+ tbb::parallel_for(mEdgeLeafs.getRange(), *this);
+}
+
+
+template<class DistTreeT, class MeshingOp>
+void
+MeshGen<DistTreeT, MeshingOp>::runSerial()
+{
+ (*this)(mEdgeLeafs.getRange());
+}
+
+
+template<class DistTreeT, class MeshingOp>
+void
+MeshGen<DistTreeT, MeshingOp>::operator()(
+ const tbb::blocked_range<size_t>& range) const
+{
+ typedef typename DistTreeT::template ValueConverter<bool>::Type BoolTreeT;
+ typedef typename BoolTreeT::LeafNodeType BoolLeafT;
+ typedef typename CharTreeT::LeafNodeType CharLeafT;
+
+ typedef openvdb::tree::ValueAccessor<const CharTreeT> CharTreeAccessorT;
+ typedef openvdb::tree::ValueAccessor<const IntTreeT> IntTreeAccessorT;
+ typedef openvdb::tree::ValueAccessor<const BoolTreeT> BoolTreeAccessorT;
+
+ boost::scoped_ptr<CharTreeAccessorT> refEdgeAcc;
+ boost::scoped_ptr<BoolTreeAccessorT> refMaskAcc;
+ if (mRefData && mRefData->isValid()) {
+ refEdgeAcc.reset(new CharTreeAccessorT(*mRefData->mEdgeTree));
+ refMaskAcc.reset(new BoolTreeAccessorT(*mRefData->mSeamMaskTree.get()));
+ }
+ const bool hasRefData = refEdgeAcc && refMaskAcc;
+
+
+ typename CharTreeT::LeafNodeType::ValueOnCIter iter;
+ IntTreeAccessorT auxAcc(mAuxTree);
+
+ Coord ijk, coord;
+ char refEdgeFlags, isSemLinePoly;
+ const char isExteriorPoly[2] = {0, char(POLYFLAG_EXTERIOR)};
+ openvdb::Vec4I quad;
+ size_t edgeCount;
+
+ MeshingOp mesher;
+
+ for (size_t n = range.begin(); n != range.end(); ++n) {
+
+ const Coord origin = mEdgeLeafs[n]->getOrigin();
+
+ // Get an upper bound on the number of primitives.
+ edgeCount = 0;
+ iter = mEdgeLeafs[n]->cbeginValueOn();
+ for (; iter; ++iter) {
+ char edgeFlags = iter.getValue() >> 1;
+ edgeCount += edgeFlags & 0x1;
+
+ edgeFlags = edgeFlags >> 1;
+ edgeCount += edgeFlags & 0x1;
+
+ edgeFlags = edgeFlags >> 1;
+ edgeCount += edgeFlags & 0x1;
+ }
+
+ mesher.init(edgeCount, mPolygonPoolList[n]);
+
+ const CharLeafT* refEdgeLeaf = NULL;
+ const BoolLeafT* refMaskLeaf = NULL;
+
+ if (hasRefData) {
+ refEdgeLeaf = refEdgeAcc->probeConstLeaf(origin);
+ refMaskLeaf = refMaskAcc->probeConstLeaf(origin);
+ }
+
+ iter = mEdgeLeafs[n]->cbeginValueOn();
+ for (; iter; ++iter) {
+ ijk = iter.getCoord();
+ const char& edgeFlags = iter.getValue();
+
+ const bool isInside = edgeFlags & INSIDE;
+
+ refEdgeFlags = 0;
+ isSemLinePoly = 0;
+ if (hasRefData) {
+ if(refEdgeLeaf) refEdgeFlags = refEdgeLeaf->getValue(ijk);
+ if (refMaskLeaf && refMaskLeaf->isValueOn(ijk)) {
+ isSemLinePoly = char(POLYFLAG_FRACTURE_SEAM);
+ }
+ }
+
+
+ int v0 = auxAcc.getValue(ijk);
+
+ if (edgeFlags & XEDGE) {
+
+ quad[0] = v0;
+ coord[0] = ijk[0]; coord[1] = ijk[1]-1; coord[2] = ijk[2]; // i, j-1, k
+ quad[1] = auxAcc.getValue(coord);
+ coord[2] -= 1; // i, j-1, k-1
+ quad[2] = auxAcc.getValue(coord);
+ coord[1] = ijk[1]; // i, j, k-1
+ quad[3] = auxAcc.getValue(coord);
+
+ mesher.addPrim(quad, isInside,
+ (isSemLinePoly | isExteriorPoly[bool(refEdgeFlags & XEDGE)]));
+ }
+
+
+ if (edgeFlags & YEDGE) {
+
+ quad[0] = v0;
+ coord[0] = ijk[0]; coord[1] = ijk[1]; coord[2] = ijk[2]-1; // i, j, k-1
+ quad[1] = auxAcc.getValue(coord);
+ coord[0] -= 1; // i-1, j, k-1
+ quad[2] = auxAcc.getValue(coord);
+ coord[2] = ijk[2]; // i-1, j, k
+ quad[3] = auxAcc.getValue(coord);
+
+ mesher.addPrim(quad, isInside,
+ (isSemLinePoly | isExteriorPoly[bool(refEdgeFlags & YEDGE)]));
+ }
+
+ if (edgeFlags & ZEDGE) {
+
+ quad[0] = v0;
+ coord[0] = ijk[0]; coord[1] = ijk[1]-1; coord[2] = ijk[2]; // i, j-1, k
+ quad[1] = auxAcc.getValue(coord);
+ coord[0] -= 1; // i-1, j-1, k
+ quad[2] = auxAcc.getValue(coord);
+ coord[1] = ijk[1]; // i, j, k
+ quad[3] = auxAcc.getValue(coord);
+
+ mesher.addPrim(quad, !isInside,
+ (isSemLinePoly | isExteriorPoly[bool(refEdgeFlags & ZEDGE)]));
+ }
+ }
+
+ mesher.done();
+ }
+}
+
+
+////////////////////////////////////////
+
+
+template<class DistTreeT, class AuxDataT = int>
+class AuxiliaryData
+{
+public:
+ typedef openvdb::tree::ValueAccessor<const DistTreeT> SourceAccessorT;
+ typedef typename DistTreeT::ValueType ValueT;
+
+ typedef typename DistTreeT::template ValueConverter<char>::Type CharTreeT;
+ typedef openvdb::tree::ValueAccessor<CharTreeT> EdgeAccessorT;
+
+ typedef typename DistTreeT::template ValueConverter<AuxDataT>::Type AuxTreeT;
+ typedef openvdb::tree::ValueAccessor<AuxTreeT> AuxAccessorT;
+
+ AuxiliaryData(const DistTreeT&, const LeafCPtrList<DistTreeT>&,
+ double iso = 0.0, bool extraCheck = false);
+ AuxiliaryData(AuxiliaryData&, tbb::split);
+
+ void runParallel();
+ void runSerial();
+
+ typename CharTreeT::Ptr edgeTree() const { return mEdgeTree; }
+ typename AuxTreeT::Ptr auxTree() const { return mAuxTree; }
+
+ void operator()(const tbb::blocked_range<size_t>&);
+
+ void join(const AuxiliaryData& rhs)
+ {
+ mEdgeTree->merge(*rhs.mEdgeTree);
+ mAuxTree->merge(*rhs.mAuxTree);
+ }
+
+private:
+ const LeafCPtrList<DistTreeT>& mLeafNodes;
+ const DistTreeT& mSourceTree;
+ SourceAccessorT mSourceAccessor;
+
+ typename CharTreeT::Ptr mEdgeTree;
+ EdgeAccessorT mEdgeAccessor;
+
+ typename AuxTreeT::Ptr mAuxTree;
+ AuxAccessorT mAuxAccessor;
+
+ const double mIsovalue;
+ const bool mExtraCheck;
+
+ int edgeCheck(const Coord& ijk, const bool thisInside);
+};
+
+template<class DistTreeT, class AuxDataT>
+AuxiliaryData<DistTreeT, AuxDataT>::AuxiliaryData(const DistTreeT& tree,
+ const LeafCPtrList<DistTreeT>& leafNodes, double iso, bool extraCheck)
+ : mLeafNodes(leafNodes)
+ , mSourceTree(tree)
+ , mSourceAccessor(mSourceTree)
+ , mEdgeTree(new CharTreeT(0))
+ , mEdgeAccessor(*mEdgeTree)
+ , mAuxTree(new AuxTreeT(AuxDataT(0)))
+ , mAuxAccessor(*mAuxTree)
+ , mIsovalue(iso)
+ , mExtraCheck(extraCheck)
+{
+}
+
+template<class DistTreeT, class AuxDataT>
+AuxiliaryData<DistTreeT, AuxDataT>::AuxiliaryData(AuxiliaryData& rhs, tbb::split)
+ : mLeafNodes(rhs.mLeafNodes)
+ , mSourceTree(rhs.mSourceTree)
+ , mSourceAccessor(mSourceTree)
+ , mEdgeTree(new CharTreeT(0))
+ , mEdgeAccessor(*mEdgeTree)
+ , mAuxTree(new AuxTreeT(AuxDataT(0)))
+ , mAuxAccessor(*mAuxTree)
+ , mIsovalue(rhs.mIsovalue)
+ , mExtraCheck(rhs.mExtraCheck)
+{
+}
+
+
+
+template<class DistTreeT, typename AuxDataT>
+void
+AuxiliaryData<DistTreeT, AuxDataT>::runParallel()
+{
+ tbb::parallel_reduce(mLeafNodes.getRange(), *this);
+}
+
+template<class DistTreeT, typename AuxDataT>
+void
+AuxiliaryData<DistTreeT, AuxDataT>::runSerial()
+{
+ (*this)(mLeafNodes.getRange());
+}
+
+template<class DistTreeT, typename AuxDataT>
+void
+AuxiliaryData<DistTreeT, AuxDataT>::operator()(const tbb::blocked_range<size_t>& range)
+{
+ typename DistTreeT::LeafNodeType::ValueOnCIter iter;
+ Coord ijk;
+ bool thisInside;
+ int edgeFlags;
+ ValueT val;
+
+ if (!mExtraCheck) {
+ for (size_t n = range.begin(); n != range.end(); ++n) {
+ for (iter = mLeafNodes[n]->cbeginValueOn(); iter; ++iter) {
+ ijk = iter.getCoord();
+ thisInside = iter.getValue() < mIsovalue;
+ edgeFlags = edgeCheck(ijk, thisInside);
+
+ if (edgeFlags != 0) {
+ edgeFlags |= int(thisInside);
+ mEdgeAccessor.setValue(ijk, char(edgeFlags));
+ }
+ }
+ }
+ } else {
+ for (size_t n = range.begin(); n != range.end(); ++n) {
+ for (iter = mLeafNodes[n]->cbeginValueOn(); iter; ++iter) {
+
+ ijk = iter.getCoord();
+ thisInside = iter.getValue() < mIsovalue;
+ edgeFlags = edgeCheck(ijk, thisInside);
+
+ if (edgeFlags != 0) {
+ edgeFlags |= int(thisInside);
+ mEdgeAccessor.setValue(ijk, char(edgeFlags));
+ }
+
+ --ijk[0];
+ if (!mSourceAccessor.probeValue(ijk, val)) {
+ thisInside = val < mIsovalue;
+ edgeFlags = edgeCheck(ijk, thisInside);
+
+ if (edgeFlags != 0) {
+ edgeFlags |= int(thisInside);
+ mEdgeAccessor.setValue(ijk, char(edgeFlags));
+ }
+ }
+
+ ++ijk[0];
+ --ijk[1];
+ if (!mSourceAccessor.probeValue(ijk, val)) {
+ thisInside = val < mIsovalue;
+ edgeFlags = edgeCheck(ijk, thisInside);
+
+ if (edgeFlags != 0) {
+ edgeFlags |= int(thisInside);
+ mEdgeAccessor.setValue(ijk, char(edgeFlags));
+ }
+ }
+
+ ++ijk[1];
+ --ijk[2];
+ if (!mSourceAccessor.probeValue(ijk, val)) {
+ thisInside = val < mIsovalue;
+ edgeFlags = edgeCheck(ijk, thisInside);
+
+ if (edgeFlags != 0) {
+ edgeFlags |= int(thisInside);
+ mEdgeAccessor.setValue(ijk, char(edgeFlags));
+ }
+ }
+ }
+ }
+ }
+}
+
+template<class DistTreeT, typename AuxDataT>
+inline int
+AuxiliaryData<DistTreeT, AuxDataT>::edgeCheck(const Coord& ijk, const bool thisInside)
+{
+ int edgeFlags = 0;
+ Coord n_ijk, coord;
+
+ // Eval upwind x-edge
+ n_ijk = ijk; ++n_ijk[0];
+ bool otherInside = (mSourceAccessor.getValue(n_ijk) < mIsovalue);
+ if (otherInside != thisInside) {
+
+ edgeFlags = XEDGE;
+
+ mAuxAccessor.setActiveState(ijk, true);
+
+ coord[0] = ijk[0]; coord[1] = ijk[1]-1; coord[2] = ijk[2];
+ mAuxAccessor.setActiveState(coord, true);
+
+ coord[0] = ijk[0]; coord[1] = ijk[1]-1; coord[2] = ijk[2]-1;
+ mAuxAccessor.setActiveState(coord, true);
+
+ coord[0] = ijk[0]; coord[1] = ijk[1]; coord[2] = ijk[2]-1;
+ mAuxAccessor.setActiveState(coord, true);
+ }
+
+ // Eval upwind y-edge
+ n_ijk[0] = ijk[0]; ++n_ijk[1];
+ otherInside = (mSourceAccessor.getValue(n_ijk) < mIsovalue);
+ if (otherInside != thisInside) {
+
+ edgeFlags |= YEDGE;
+
+ mAuxAccessor.setActiveState(ijk, true);
+
+ coord[0] = ijk[0]; coord[1] = ijk[1]; coord[2] = ijk[2]-1;
+ mAuxAccessor.setActiveState(coord, true);
+
+ coord[0] = ijk[0]-1; coord[1] = ijk[1]; coord[2] = ijk[2];
+ mAuxAccessor.setActiveState(coord, true);
+
+ coord[0] = ijk[0]-1; coord[1] = ijk[1]; coord[2] = ijk[2]-1;
+ mAuxAccessor.setActiveState(coord, true);
+ }
+
+ // Eval upwind z-edge
+ n_ijk[1] = ijk[1]; ++n_ijk[2];
+ otherInside = (mSourceAccessor.getValue(n_ijk) < mIsovalue);
+ if (otherInside != thisInside) {
+
+ edgeFlags |= ZEDGE;
+
+ mAuxAccessor.setActiveState(ijk, true);
+
+ coord[0] = ijk[0]; coord[1] = ijk[1]-1; coord[2] = ijk[2];
+ mAuxAccessor.setActiveState(coord, true);
+
+ coord[0] = ijk[0]-1; coord[1] = ijk[1]; coord[2] = ijk[2];
+ mAuxAccessor.setActiveState(coord, true);
+
+ coord[0] = ijk[0]-1; coord[1] = ijk[1]-1; coord[2] = ijk[2];
+ mAuxAccessor.setActiveState(coord, true);
+ }
+ return edgeFlags;
+}
+
+
+////////////////////////////////////////
+
+
+template <class DistTreeT>
+class SeamMaskGen
+{
+public:
+ typedef typename DistTreeT::template ValueConverter<bool>::Type BoolTreeT;
+ typedef typename DistTreeT::template ValueConverter<int>::Type IntTreeT;
+ typedef tree::ValueAccessor<const BoolTreeT> BoolTreeAccessorT;
+ typedef tree::ValueAccessor<const IntTreeT> IntTreeAccessorT;
+
+ SeamMaskGen(LeafPtrList<BoolTreeT>& seamMaskLeafs,
+ const BoolTreeT& topologyMaskTree, const IntTreeT& auxTree);
+
+ SeamMaskGen(const SeamMaskGen<DistTreeT>&);
+
+ void runParallel();
+ void runSerial();
+
+ void operator()(const tbb::blocked_range<size_t>&) const;
+
+private:
+ LeafPtrList<BoolTreeT>& mSeamMaskLeafs;
+ const BoolTreeT& mTopologyMaskTree;
+ BoolTreeAccessorT mTopologyMaskAcc;
+ const IntTreeT& mAuxTree;
+ IntTreeAccessorT mAuxAcc;
+};
+
+
+template <class DistTreeT>
+SeamMaskGen<DistTreeT>::SeamMaskGen(LeafPtrList<BoolTreeT>& seamMaskLeafs,
+ const BoolTreeT& topologyMaskTree, const IntTreeT& auxTree)
+ : mSeamMaskLeafs(seamMaskLeafs)
+ , mTopologyMaskTree(topologyMaskTree)
+ , mTopologyMaskAcc(mTopologyMaskTree)
+ , mAuxTree(auxTree)
+ , mAuxAcc(mAuxTree)
+{
+}
+
+template <class DistTreeT>
+SeamMaskGen<DistTreeT>::SeamMaskGen(const SeamMaskGen<DistTreeT>& rhs)
+ : mSeamMaskLeafs(rhs.mSeamMaskLeafs)
+ , mTopologyMaskTree(rhs.mTopologyMaskTree)
+ , mTopologyMaskAcc(mTopologyMaskTree)
+ , mAuxTree(rhs.mAuxTree)
+ , mAuxAcc(mAuxTree)
+{
+}
+
+template <class DistTreeT>
+void
+SeamMaskGen<DistTreeT>::runParallel()
+{
+ tbb::parallel_for(mSeamMaskLeafs.getRange(), *this);
+}
+
+template <class DistTreeT>
+void
+SeamMaskGen<DistTreeT>::runSerial()
+{
+ (*this)(mSeamMaskLeafs.getRange());
+}
+
+template <class DistTreeT>
+void
+SeamMaskGen<DistTreeT>::operator()(const tbb::blocked_range<size_t>& range) const
+{
+ typedef typename BoolTreeT::LeafNodeType::ValueOnIter ValueOnIterT;
+ Coord ijk, n_ijk;
+ for (size_t leafIdx = range.begin(); leafIdx != range.end(); ++leafIdx) {
+ ValueOnIterT it = mSeamMaskLeafs[leafIdx]->beginValueOn();
+ for (; it; ++it) {
+ ijk = it.getCoord();
+ if (!mTopologyMaskAcc.isValueOn(ijk)) {
+ it.setValueOff();
+ } else {
+ bool turnOff = true;
+ for (size_t n = 0; n < 6; ++n) {
+ n_ijk = ijk + util::COORD_OFFSETS[n];
+ if (!mAuxTree.isValueOn(n_ijk) && mTopologyMaskAcc.isValueOn(n_ijk)) {
+ turnOff = false;
+ break;
+ }
+ }
+ if (turnOff) it.setValueOff();
+ }
+ }
+ }
+}
+
+////////////////////////////////////////
+
+
+template <class DistTreeT>
+class EdgeSmooth
+{
+public:
+ typedef typename DistTreeT::template ValueConverter<bool>::Type BoolTreeT;
+ typedef typename DistTreeT::template ValueConverter<int>::Type IntTreeT;
+ typedef tree::ValueAccessor<const BoolTreeT> BoolTreeAccessorT;
+ typedef tree::ValueAccessor<const IntTreeT> IntTreeAccessorT;
+
+ EdgeSmooth(
+ LeafPtrList<BoolTreeT>& leafs,
+ const BoolTreeT& edgeMaskTree,
+ const IntTreeT& auxTree,
+ PointList& points,
+ const math::Transform& xform);
+
+ EdgeSmooth(const EdgeSmooth<DistTreeT>&);
+
+ void runParallel(const size_t iterations);
+ void runSerial(const size_t iterations);
+
+ void operator()(const tbb::blocked_range<size_t>&) const;
+
+private:
+ LeafPtrList<BoolTreeT>& mLeafs;
+ const BoolTreeT& mEdgeMaskTree;
+ const IntTreeT& mAuxTree;
+ PointList& mPoints;
+ const math::Transform& mTransform;
+
+ bool pointInAABB(const Vec3s& p, const Vec3s& bmin, const Vec3s& bmax) const
+ {
+ for (int i = 0; i < 3; ++i) {
+ if (p[i] < bmin[i] || p[i] > bmax[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+};
+
+
+template <class DistTreeT>
+EdgeSmooth<DistTreeT>::EdgeSmooth(
+ LeafPtrList<BoolTreeT>& leafs,
+ const BoolTreeT& edgeMaskTree,
+ const IntTreeT& auxTree,
+ PointList& points,
+ const math::Transform& xform)
+ : mLeafs(leafs)
+ , mEdgeMaskTree(edgeMaskTree)
+ , mAuxTree(auxTree)
+ , mPoints(points)
+ , mTransform(xform)
+{
+}
+
+template <class DistTreeT>
+EdgeSmooth<DistTreeT>::EdgeSmooth(const EdgeSmooth<DistTreeT>& rhs)
+ : mLeafs(rhs.mLeafs)
+ , mEdgeMaskTree(rhs.mEdgeMaskTree)
+ , mAuxTree(rhs.mAuxTree)
+ , mPoints(rhs.mPoints)
+ , mTransform(rhs.mTransform)
+{
+}
+
+template <class DistTreeT>
+void
+EdgeSmooth<DistTreeT>::runParallel(const size_t iterations)
+{
+ for (size_t i = 0; i < iterations; ++i) {
+ tbb::parallel_for(mLeafs.getRange(), *this);
+ }
+}
+
+template <class DistTreeT>
+void
+EdgeSmooth<DistTreeT>::runSerial(const size_t iterations)
+{
+ for (size_t i = 0; i < iterations; ++i) {
+ (*this)(mLeafs.getRange());
+ }
+}
+
+template <class DistTreeT>
+void
+EdgeSmooth<DistTreeT>::operator()(const tbb::blocked_range<size_t>& range) const
+{
+ typedef typename BoolTreeT::LeafNodeType::ValueOnIter ValueOnIterT;
+ BoolTreeAccessorT maskAcc(mEdgeMaskTree);
+ IntTreeAccessorT auxAcc(mAuxTree);
+
+ float dx = float(mTransform.voxelSize()[0]);
+ openvdb::Vec3s avg, bmin, bmax;
+ Coord ijk, nijk;
+ int count;
+
+ for (size_t leafIdx = range.begin(); leafIdx != range.end(); ++leafIdx) {
+ typename BoolTreeT::LeafNodeType::ValueOnIter valueIt = mLeafs[leafIdx]->beginValueOn();
+ for ( ; valueIt; ++valueIt) {
+
+ ijk = valueIt.getCoord();
+ openvdb::Vec3s& ptn = mPoints[auxAcc.getValue(ijk)];
+
+ avg = ptn;
+ count = 1;
+ for (int n = 0; n < 26; ++n) {
+ nijk = ijk + util::COORD_OFFSETS[n];
+ if (maskAcc.isValueOn(nijk)) {
+ avg += mPoints[auxAcc.getValue(nijk)];
+ ++count;
+ }
+ }
+
+ if (count > 1) {
+ avg *= (1.0 / float(count));
+
+ // Constrain to current cell
+ bmin = openvdb::Vec3s(mTransform.indexToWorld(ijk));
+ bmax = bmin + dx;
+
+ bool inCell = true;
+ for (int i = 0; i < 10; ++i) {
+
+ inCell = pointInAABB(avg, bmin, bmax);
+
+ if (inCell) break;
+
+ avg += ptn;
+ avg *= 0.5;
+ }
+
+ if (inCell) ptn = avg;
+ }
+ }
+ }
+}
+
+
+////////////////////////////////////////
+
+
+template<class CharAccessorT, typename AuxAccessorT>
+class AuxDataGenerator
+{
+public:
+ AuxDataGenerator(CharAccessorT& edgeAcc, AuxAccessorT& auxAcc)
+ : mEdgeAcc(edgeAcc), mAuxAcc(auxAcc) {}
+
+
+ void setXEdge(char edgeFlags, const Coord& ijk)
+ {
+ mEdgeAcc.setValue(ijk, edgeFlags | XEDGE);
+
+ mAuxAcc.setActiveState(ijk, true);
+
+ Coord coord = ijk;
+ coord[1] = ijk[1]-1;
+ mAuxAcc.setActiveState(coord, true);
+
+ coord[0] = ijk[0]; coord[1] = ijk[1]-1; coord[2] = ijk[2]-1;
+ mAuxAcc.setActiveState(coord, true);
+
+ coord[0] = ijk[0]; coord[1] = ijk[1]; coord[2] = ijk[2]-1;
+ mAuxAcc.setActiveState(coord, true);
+ }
+
+ void setYEdge(char edgeFlags, const Coord& ijk)
+ {
+ mEdgeAcc.setValue(ijk, edgeFlags | YEDGE);
+
+ mAuxAcc.setActiveState(ijk, true);
+
+ Coord coord = ijk;
+ coord[2] = ijk[2]-1;
+ mAuxAcc.setActiveState(coord, true);
+
+ coord[0] = ijk[0]-1; coord[1] = ijk[1]; coord[2] = ijk[2];
+ mAuxAcc.setActiveState(coord, true);
+
+ coord[0] = ijk[0]-1; coord[1] = ijk[1]; coord[2] = ijk[2]-1;
+ mAuxAcc.setActiveState(coord, true);
+ }
+
+ void setZEdge(char edgeFlags, const Coord& ijk)
+ {
+ mEdgeAcc.setValue(ijk, edgeFlags | ZEDGE);
+
+ mAuxAcc.setActiveState(ijk, true);
+
+ Coord coord = ijk;
+ coord[1] = ijk[1]-1;
+ mAuxAcc.setActiveState(coord, true);
+
+ coord[0] = ijk[0]-1; coord[1] = ijk[1]; coord[2] = ijk[2];
+ mAuxAcc.setActiveState(coord, true);
+
+ coord[0] = ijk[0]-1; coord[1] = ijk[1]-1; coord[2] = ijk[2];
+ mAuxAcc.setActiveState(coord, true);
+ }
+
+private:
+ CharAccessorT& mEdgeAcc;
+ AuxAccessorT& mAuxAcc;
+};
+
+
+////////////////////////////////////////
+
+
+template<class DistTreeT, class AuxTreeT, class CharTreeT>
+inline void
+tileAuxiliaryData(
+ const DistTreeT& distTree, CharTreeT& edgeTree, AuxTreeT& auxTree,
+ double iso)
+{
+ typedef tree::ValueAccessor<const DistTreeT> DistAccessorT;
+ typedef tree::ValueAccessor<AuxTreeT> AuxTreeAccessorT;
+ typedef tree::ValueAccessor<CharTreeT> CharTreeAccessorT;
+
+ typename DistTreeT::ValueType isoValue = typename DistTreeT::ValueType(iso);
+
+ DistAccessorT distAcc(distTree);
+ CharTreeAccessorT edgeAcc(edgeTree);
+ AuxTreeAccessorT auxAcc(auxTree);
+
+ AuxDataGenerator<CharTreeAccessorT, AuxTreeAccessorT> auxData(edgeAcc, auxAcc);
+
+ Coord ijk, nijk;
+ typename DistTreeT::ValueType value;
+ CoordBBox bbox;
+ bool processTileFace;
+
+ typename DistTreeT::ValueOnCIter tileIter(distTree);
+ tileIter.setMaxDepth(DistTreeT::ValueOnCIter::LEAF_DEPTH - 1);
+
+ for ( ; tileIter; ++tileIter) {
+ tileIter.getBoundingBox(bbox);
+
+ const bool thisInside = (distAcc.getValue(bbox.min()) < isoValue);
+ const int thisDepth = distAcc.getValueDepth(bbox.min());
+
+
+ // Eval x-edges
+
+ ijk = bbox.max();
+ nijk = ijk;
+ ++nijk[0];
+
+ processTileFace = true;
+ if (thisDepth >= distAcc.getValueDepth(nijk)) {
+ processTileFace = thisInside != (distAcc.getValue(nijk) < isoValue);
+ }
+
+ if (processTileFace) {
+ for (ijk[1] = bbox.min()[1]; ijk[1] <= bbox.max()[1]; ++ijk[1]) {
+ nijk[1] = ijk[1];
+ for (ijk[2] = bbox.min()[2]; ijk[2] <= bbox.max()[2]; ++ijk[2]) {
+ nijk[2] = ijk[2];
+ if ((distAcc.getValue(nijk) < isoValue) != thisInside) {
+ auxData.setXEdge(edgeAcc.getValue(ijk) | char(thisInside), ijk);
+ }
+ }
+ }
+ }
+
+ ijk = bbox.min();
+ --ijk[0];
+
+ processTileFace = true;
+ if (thisDepth >= distAcc.getValueDepth(ijk)) {
+ processTileFace = !distAcc.probeValue(ijk, value) && thisInside != (value < isoValue);
+ }
+
+ if (processTileFace) {
+ for (ijk[1] = bbox.min()[1]; ijk[1] <= bbox.max()[1]; ++ijk[1]) {
+ for (ijk[2] = bbox.min()[2]; ijk[2] <= bbox.max()[2]; ++ijk[2]) {
+ if (!distAcc.probeValue(ijk, value) && (value < isoValue) != thisInside) {
+ auxData.setXEdge(edgeAcc.getValue(ijk) | char(!thisInside), ijk);
+ }
+ }
+ }
+ }
+
+
+ // Eval y-edges
+
+ ijk = bbox.max();
+ nijk = ijk;
+ ++nijk[1];
+
+ processTileFace = true;
+ if (thisDepth >= distAcc.getValueDepth(nijk)) {
+ processTileFace = thisInside != (distAcc.getValue(nijk) < isoValue);
+ }
+
+ if (processTileFace) {
+ for (ijk[0] = bbox.min()[0]; ijk[0] <= bbox.max()[0]; ++ijk[0]) {
+ nijk[0] = ijk[0];
+ for (ijk[2] = bbox.min()[2]; ijk[2] <= bbox.max()[2]; ++ijk[2]) {
+ nijk[2] = ijk[2];
+
+ if ((distAcc.getValue(nijk) < isoValue) != thisInside) {
+ auxData.setYEdge(edgeAcc.getValue(ijk) | char(thisInside), ijk);
+ }
+ }
+ }
+ }
+
+
+ ijk = bbox.min();
+ --ijk[1];
+
+ processTileFace = true;
+ if (thisDepth >= distAcc.getValueDepth(ijk)) {
+ processTileFace = !distAcc.probeValue(ijk, value) && thisInside != (value < isoValue);
+ }
+
+ if (processTileFace) {
+ for (ijk[0] = bbox.min()[0]; ijk[0] <= bbox.max()[0]; ++ijk[0]) {
+ for (ijk[2] = bbox.min()[2]; ijk[2] <= bbox.max()[2]; ++ijk[2]) {
+
+ if (!distAcc.probeValue(ijk, value) && (value < isoValue) != thisInside) {
+ auxData.setYEdge(edgeAcc.getValue(ijk) | char(!thisInside), ijk);
+ }
+ }
+ }
+ }
+
+
+ // Eval z-edges
+
+ ijk = bbox.max();
+ nijk = ijk;
+ ++nijk[2];
+
+ processTileFace = true;
+ if (thisDepth >= distAcc.getValueDepth(nijk)) {
+ processTileFace = thisInside != (distAcc.getValue(nijk) < isoValue);
+ }
+
+ if (processTileFace) {
+ for (ijk[0] = bbox.min()[0]; ijk[0] <= bbox.max()[0]; ++ijk[0]) {
+ nijk[0] = ijk[0];
+ for (ijk[1] = bbox.min()[1]; ijk[1] <= bbox.max()[1]; ++ijk[1]) {
+ nijk[1] = ijk[1];
+
+ if ((distAcc.getValue(nijk) < isoValue) != thisInside) {
+ auxData.setZEdge(edgeAcc.getValue(ijk) | char(thisInside), ijk);
+ }
+ }
+ }
+ }
+
+ ijk = bbox.min();
+ --ijk[2];
+
+ processTileFace = true;
+ if (thisDepth >= distAcc.getValueDepth(ijk)) {
+ processTileFace = !distAcc.probeValue(ijk, value) && thisInside != (value < isoValue);
+ }
+
+ if (processTileFace) {
+ for (ijk[0] = bbox.min()[0]; ijk[0] <= bbox.max()[0]; ++ijk[0]) {
+ for (ijk[1] = bbox.min()[1]; ijk[1] <= bbox.max()[1]; ++ijk[1]) {
+
+ if (!distAcc.probeValue(ijk, value) && (value < isoValue) != thisInside) {
+ auxData.setZEdge(edgeAcc.getValue(ijk) | char(!thisInside), ijk);
+ }
+ }
+ }
+ }
+ }
+}
+
+
+////////////////////////////////////////
+
+
+// Utility class for the volumeToMesh wrapper
+class PointListCopy
+{
+public:
+ PointListCopy(const PointList& pointsIn, std::vector<Vec3s>& pointsOut)
+ : mPointsIn(pointsIn) , mPointsOut(pointsOut)
+ {
+ }
+
+ void operator()(const tbb::blocked_range<size_t>& range) const
+ {
+ for (size_t n = range.begin(); n < range.end(); ++n) {
+ mPointsOut[n] = mPointsIn[n];
+ }
+ }
+
+private:
+ const PointList& mPointsIn;
+ std::vector<Vec3s>& mPointsOut;
+};
+
+
+} // namespace internal
+
+
+////////////////////////////////////////
+
+
+inline VolumeToMesh::VolumeToMesh(double isovalue, double adaptivity)
+ : mPointListSize(0)
+ , mPolygonPoolListSize(0)
+ , mIsovalue(isovalue)
+ , mPrimAdaptivity(adaptivity)
+ , mSecAdaptivity(0.0)
+ , mRefGrid(GridBase::ConstPtr())
+ , mRefEdgeTree(TreeBase::Ptr())
+ , mRefTopologyMaskTree(TreeBase::Ptr())
+{
+}
+
+inline PointList&
+VolumeToMesh::pointList()
+{
+ return mPoints;
+}
+
+inline const size_t&
+VolumeToMesh::pointListSize() const
+{
+ return mPointListSize;
+}
+
+
+inline PolygonPoolList&
+VolumeToMesh::polygonPoolList()
+{
+ return mPolygons;
+}
+
+
+inline const PolygonPoolList&
+VolumeToMesh::polygonPoolList() const
+{
+ return mPolygons;
+}
+
+
+inline const size_t&
+VolumeToMesh::polygonPoolListSize() const
+{
+ return mPolygonPoolListSize;
+}
+
+
+inline void
+VolumeToMesh::setRefGrid(const GridBase::ConstPtr& grid, double secAdaptivity, bool smoothSeams)
+{
+ mRefGrid = grid;
+ mSecAdaptivity = secAdaptivity;
+ // Clear out old auxiliary data
+ mRefEdgeTree = TreeBase::Ptr();
+ mRefTopologyMaskTree = TreeBase::Ptr();
+ mSeamPointTree = TreeBase::Ptr();
+ mSmoothSeams = smoothSeams;
+}
+
+
+template<typename GridT>
+inline void
+VolumeToMesh::operator()(const GridT& distGrid)
+{
+ typedef typename GridT::TreeType DistTreeT;
+ typedef typename DistTreeT::ValueType DistValueT;
+
+ typedef typename DistTreeT::template ValueConverter<char>::Type CharTreeT;
+ typedef typename DistTreeT::template ValueConverter<int>::Type IntTreeT;
+ typedef typename DistTreeT::template ValueConverter<Vec3s>::Type Vec3sTreeT;
+ typedef typename DistTreeT::template ValueConverter<bool>::Type BoolTreeT;
+
+ const bool noAdaptivity = mPrimAdaptivity < 1e-6 && mSecAdaptivity < 1e-6;
+
+ const openvdb::math::Transform& transform = distGrid.transform();
+ const DistTreeT& distTree = distGrid.tree();
+ typename CharTreeT::Ptr edgeTree; // edge flags
+ typename IntTreeT::Ptr auxTree; // auxiliary data
+
+ const bool nonLevelSetGrid = distGrid.getGridClass() != GRID_LEVEL_SET;
+
+ const bool extraSignCheck = nonLevelSetGrid ||
+ (std::abs(mIsovalue - double(distGrid.background())) < (1.5 * transform.voxelSize()[0]));
+
+
+
+ // Collect auxiliary data
+ {
+ internal::LeafCPtrList<DistTreeT> sourceLeafs(distTree);
+ internal::AuxiliaryData<DistTreeT> op(distTree, sourceLeafs, mIsovalue, extraSignCheck);
+ op.runParallel();
+ edgeTree = op.edgeTree();
+ auxTree = op.auxTree();
+
+ // Collect auxiliary data from active tiles
+ if (nonLevelSetGrid) {
+ internal::tileAuxiliaryData(distTree, *edgeTree, *auxTree, mIsovalue);
+ }
+ }
+
+
+ // Optionally collect auxiliary data from a reference surface.
+ internal::ReferenceData<DistTreeT> refData;
+ if (mRefGrid) {
+ const GridT* refGrid = static_cast<const GridT*>(mRefGrid.get());
+ if (refGrid && refGrid->activeVoxelCount() > 0) {
+
+ refData.mDistTree = &refGrid->tree();
+ refData.mInternalAdaptivity = DistValueT(mSecAdaptivity);
+
+ // Cache reference data for reuse.
+ if (!mRefEdgeTree && !mRefTopologyMaskTree) {
+ internal::LeafCPtrList<DistTreeT> leafs(*refData.mDistTree);
+ internal::AuxiliaryData<DistTreeT, bool> op(
+ *refData.mDistTree, leafs, mIsovalue, extraSignCheck);
+ op.runParallel();
+ mRefEdgeTree = op.edgeTree();
+ mRefTopologyMaskTree = op.auxTree();
+ mSeamPointTree = typename Vec3sTreeT::Ptr(new Vec3sTreeT(Vec3s(0.0)));
+ }
+
+ if (mRefEdgeTree && mRefTopologyMaskTree) {
+ refData.mEdgeTree = static_cast<CharTreeT*>(mRefEdgeTree.get());
+ refData.mTopologyMaskTree = static_cast<BoolTreeT*>(mRefTopologyMaskTree.get());
+ refData.mSeamPointTree = static_cast<Vec3sTreeT*>(mSeamPointTree.get());
+ refData.mSeamMaskTree = typename BoolTreeT::Ptr(new BoolTreeT(false));
+ refData.mSmoothingMaskTree = typename BoolTreeT::Ptr(new BoolTreeT(false));
+ }
+ }
+ }
+
+ BoolTreeT edgeMaskTree(0.0);
+
+ // Generate the seamline mask
+ if (refData.mSeamMaskTree) {
+ refData.mSeamMaskTree->topologyUnion(*auxTree.get());
+
+ internal::LeafPtrList<BoolTreeT> leafs(*refData.mSeamMaskTree.get());
+ internal::SeamMaskGen<DistTreeT> op(leafs, *refData.mTopologyMaskTree, *auxTree.get());
+ op.runParallel();
+
+ refData.mSeamMaskTree->pruneInactive();
+ edgeMaskTree.topologyUnion(*refData.mSeamMaskTree);
+ dilateVoxels(*refData.mSeamMaskTree);
+ dilateVoxels(*refData.mSeamMaskTree);
+ dilateVoxels(*refData.mSeamMaskTree);
+ }
+
+
+ // Process auxiliary data
+ {
+ internal::LeafPtrList<IntTreeT> auxLeafs(*auxTree);
+ std::vector<size_t> regions(auxLeafs.size(), 0);
+
+ {
+ if (noAdaptivity) {
+ internal::Count<IntTreeT> count(auxLeafs, regions);
+ count.runParallel();
+ } else {
+ internal::Merge<DistTreeT> merge(distTree, auxLeafs,
+ regions, DistValueT(mIsovalue), DistValueT(mPrimAdaptivity));
+ merge.setRefData(refData);
+ merge.runParallel();
+ }
+
+ mPointListSize = 0;
+ size_t tmp = 0;
+ for (size_t n = 0, N = regions.size(); n < N; ++n) {
+ tmp = regions[n];
+ regions[n] = mPointListSize;
+ mPointListSize += tmp;
+ }
+ }
+
+ if (refData.isValid()) { // match leaf topology
+ tree::ValueAccessor<BoolTreeT> acc(*refData.mSmoothingMaskTree);
+ for (size_t n = 0, N = auxLeafs.size(); n < N; ++n) {
+ acc.touchLeaf(auxLeafs[n]->getOrigin());
+ }
+ }
+
+ // Generate the unique point list
+ mPoints.reset(new openvdb::Vec3s[mPointListSize]);
+
+ internal::PointGen<DistTreeT>
+ pointGen(distTree, auxLeafs, regions, transform, mPoints, mIsovalue);
+ pointGen.setRefData(refData);
+ pointGen.runParallel();
+ }
+
+ // Smooth seam line edges
+ if (mSmoothSeams && refData.isValid()) {
+ refData.mSmoothingMaskTree->pruneInactive();
+ internal::LeafPtrList<BoolTreeT> leafs(*refData.mSmoothingMaskTree);
+ internal::EdgeSmooth<DistTreeT> op(leafs, edgeMaskTree, *auxTree, mPoints, transform);
+ op.runParallel(3);
+
+ // Cache shared points
+ tree::ValueAccessor<Vec3sTreeT> ptnAcc(*refData.mSeamPointTree);
+ tree::ValueAccessor<IntTreeT> auxAcc(*auxTree);
+ Coord ijk;
+
+ typename BoolTreeT::LeafIter leafIt = refData.mSeamMaskTree->beginLeaf();
+ for ( ; leafIt; ++leafIt) {
+ typename BoolTreeT::LeafNodeType::ValueOnIter valueIt = leafIt->beginValueOn();
+ for ( ; valueIt; ++valueIt) {
+ ijk = valueIt.getCoord();
+ const int idx = auxAcc.getValue(ijk);
+ if (idx != 0 && !ptnAcc.isValueOn(ijk)) {
+ ptnAcc.setValue(ijk, mPoints[idx]);
+ }
+ }
+ }
+ }
+
+ // Generate mesh
+ {
+ internal::LeafCPtrList<CharTreeT> edgeLeafs(*edgeTree);
+ mPolygonPoolListSize = edgeLeafs.size();
+ mPolygons.reset(new PolygonPool[mPolygonPoolListSize]);
+
+ if (noAdaptivity) {
+ internal::MeshGen<DistTreeT, internal::QuadMeshOp>
+ meshGen(edgeLeafs, *auxTree, mPolygons);
+ meshGen.setRefData(refData);
+ meshGen.runParallel();
+ } else {
+ internal::MeshGen<DistTreeT, internal::AdaptiveMeshOp>
+ meshGen(edgeLeafs, *auxTree, mPolygons);
+ meshGen.setRefData(refData);
+ meshGen.runParallel();
+ }
+ }
+}
+
+
+////////////////////////////////////////
+
+
+template<typename GridType>
+inline void
+volumeToMesh(
+ const GridType& grid,
+ std::vector<Vec3s>& points,
+ std::vector<Vec3I>& triangles,
+ std::vector<Vec4I>& quads,
+ double isovalue,
+ double adaptivity)
+{
+ VolumeToMesh mesher(isovalue, adaptivity);
+ mesher(grid);
+
+ // Preallocate the point list
+ points.clear();
+ points.resize(mesher.pointListSize());
+
+ { // Copy points
+ internal::PointListCopy ptnCpy(mesher.pointList(), points);
+ tbb::parallel_for(tbb::blocked_range<size_t>(0, points.size()), ptnCpy);
+ mesher.pointList().reset(NULL);
+ }
+
+ PolygonPoolList& polygonPoolList = mesher.polygonPoolList();
+
+ { // Preallocate primitive lists
+ size_t numQuads = 0, numTriangles = 0;
+ for (size_t n = 0, N = mesher.polygonPoolListSize(); n < N; ++n) {
+ openvdb::tools::PolygonPool& polygons = polygonPoolList[n];
+ numTriangles += polygons.numTriangles();
+ numQuads += polygons.numQuads();
+ }
+
+ triangles.clear();
+ triangles.resize(numTriangles);
+ quads.clear();
+ quads.resize(numQuads);
+ }
+
+ // Copy primitives
+ size_t qIdx = 0, tIdx = 0;
+ for (size_t n = 0, N = mesher.polygonPoolListSize(); n < N; ++n) {
+ openvdb::tools::PolygonPool& polygons = polygonPoolList[n];
+
+ for (size_t i = 0, I = polygons.numQuads(); i < I; ++i) {
+ quads[qIdx++] = polygons.quad(i);
+ }
+
+ for (size_t i = 0, I = polygons.numTriangles(); i < I; ++i) {
+ triangles[tIdx++] = polygons.triangle(i);
+ }
+ }
+}
+
+
+template<typename GridType>
+void
+volumeToMesh(
+ const GridType& grid,
+ std::vector<Vec3s>& points,
+ std::vector<Vec4I>& quads,
+ double isovalue)
+{
+ std::vector<Vec3I> triangles(0);
+ volumeToMesh(grid,points, triangles, quads, isovalue, 0.0);
+}
+
+} // namespace tools
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_TOOLS_VOLUME_TO_MESH_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/tree/InternalNode.h b/extern/openvdb/internal/openvdb/tree/InternalNode.h
new file mode 100644
index 00000000000..cb7d40c7124
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/tree/InternalNode.h
@@ -0,0 +1,2507 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+//
+/// @file InternalNode.h
+///
+/// @brief Internal table nodes for OpenVDB trees
+
+#ifndef OPENVDB_TREE_INTERNALNODE_HAS_BEEN_INCLUDED
+#define OPENVDB_TREE_INTERNALNODE_HAS_BEEN_INCLUDED
+
+#include <boost/shared_array.hpp>
+#include <boost/static_assert.hpp>
+#include <openvdb/Platform.h>
+#include <openvdb/util/NodeMasks.h>
+#include <openvdb/io/Compression.h> // for io::readData(), etc.
+#include <openvdb/math/Math.h> // for Abs(), isExactlyEqual()
+#include <openvdb/version.h>
+#include <openvdb/Types.h>
+#include "Iterator.h"
+#include "NodeUnion.h"
+#include "Util.h"
+
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace tree {
+
+template<typename _ChildNodeType, Index Log2Dim>
+class InternalNode
+{
+public:
+ typedef _ChildNodeType ChildNodeType;
+ typedef typename ChildNodeType::LeafNodeType LeafNodeType;
+ typedef typename ChildNodeType::ValueType ValueType;
+ typedef NodeUnion<ValueType, ChildNodeType> UnionType;
+ typedef util::NodeMask<Log2Dim> NodeMaskType;
+
+ static const Index
+ LOG2DIM = Log2Dim,
+ TOTAL = Log2Dim + ChildNodeType::TOTAL,
+ DIM = 1 << TOTAL,
+ NUM_VALUES = 1 << (3 * Log2Dim),
+ LEVEL = 1 + ChildNodeType::LEVEL; // level 0 = leaf
+ static const Index64
+ NUM_VOXELS = uint64_t(1) << (3 * TOTAL); // total # of voxels represented by this node
+
+ /// @brief ValueConverter<T>::Type is the type of an InternalNode having the same
+ /// child hierarchy and dimensions as this node but a different value type, T.
+ template<typename OtherValueType>
+ struct ValueConverter {
+ typedef InternalNode<typename ChildNodeType::template ValueConverter<
+ OtherValueType>::Type, Log2Dim> Type;
+ };
+
+
+ InternalNode() {}
+
+ explicit InternalNode(const ValueType& offValue);
+
+ InternalNode(const Coord&, const ValueType& value, bool active = false);
+
+ /// Deep copy constructor
+ InternalNode(const InternalNode&);
+
+ /// Topology copy constructor
+ template<typename OtherChildNodeType>
+ InternalNode(const InternalNode<OtherChildNodeType, Log2Dim>& other,
+ const ValueType& background, TopologyCopy);
+
+ /// Topology copy constructor
+ template<typename OtherChildNodeType>
+ InternalNode(const InternalNode<OtherChildNodeType, Log2Dim>& other,
+ const ValueType& offValue, const ValueType& onValue,
+ TopologyCopy);
+
+ virtual ~InternalNode();
+
+protected:
+ typedef typename NodeMaskType::OnIterator MaskOnIterator;
+ typedef typename NodeMaskType::OffIterator MaskOffIterator;
+ typedef typename NodeMaskType::DenseIterator MaskDenseIterator;
+
+ // Type tags to disambiguate template instantiations
+ struct ValueOn {}; struct ValueOff {}; struct ValueAll {};
+ struct ChildOn {}; struct ChildOff {}; struct ChildAll {};
+
+ // The following class templates implement the iterator interfaces specified in Iterator.h
+ // by providing getItem() and setItem() methods for active values and/or inactive values.
+
+ template<typename NodeT, typename ChildT, typename MaskIterT, typename TagT>
+ struct ChildIter: public SparseIteratorBase<
+ MaskIterT, ChildIter<NodeT, ChildT, MaskIterT, TagT>, NodeT, ChildT>
+ {
+ ChildIter() {}
+ ChildIter(const MaskIterT& iter, NodeT* parent): SparseIteratorBase<
+ MaskIterT, ChildIter<NodeT, ChildT, MaskIterT, TagT>, NodeT, ChildT>(iter, parent) {}
+
+ ChildT& getItem(Index pos) const { return *(this->parent().getChildNode(pos)); }
+
+ // Note: setItem() can't be called on const iterators.
+ void setItem(Index pos, const ChildT& c) const { this->parent().setChildNode(pos, &c); }
+ };// ChildIter
+
+ template<typename NodeT, typename ValueT, typename MaskIterT, typename TagT>
+ struct ValueIter: public SparseIteratorBase<
+ MaskIterT, ValueIter<NodeT, ValueT, MaskIterT, TagT>, NodeT, ValueT>
+ {
+ ValueIter() {}
+ ValueIter(const MaskIterT& iter, NodeT* parent): SparseIteratorBase<
+ MaskIterT, ValueIter<NodeT, ValueT, MaskIterT, TagT>, NodeT, ValueT>(iter, parent) {}
+
+ const ValueT& getItem(Index pos) const { return this->parent().mNodes[pos].getValue(); }
+
+ // Note: setItem() can't be called on const iterators.
+ void setItem(Index pos, const ValueT& v) const { this->parent().mNodes[pos].setValue(v); }
+ };// ValueIter
+
+ template<typename NodeT, typename ChildT, typename ValueT, typename TagT>
+ struct DenseIter: public DenseIteratorBase<
+ MaskDenseIterator, DenseIter<NodeT, ChildT, ValueT, TagT>, NodeT, ChildT, ValueT>
+ {
+ typedef DenseIteratorBase<MaskDenseIterator, DenseIter, NodeT, ChildT, ValueT> BaseT;
+ typedef typename BaseT::NonConstValueType NonConstValueT;
+
+ DenseIter() {}
+ DenseIter(const MaskDenseIterator& iter, NodeT* parent):
+ DenseIteratorBase<MaskDenseIterator, DenseIter, NodeT, ChildT, ValueT>(iter, parent) {}
+
+ bool getItem(Index pos, ChildT*& child, NonConstValueT& value) const
+ {
+ child = this->parent().getChildNode(pos);
+ if (!child) value = this->parent().mNodes[pos].getValue();
+ return (child != NULL);
+ }
+
+ // Note: setItem() can't be called on const iterators.
+ void setItem(Index pos, ChildT* child) const
+ {
+ this->parent().setChildNode(pos, child);
+ }
+
+ // Note: unsetItem() can't be called on const iterators.
+ void unsetItem(Index pos, const ValueT& value) const
+ {
+ this->parent().unsetChildNode(pos, value);
+ }
+ };// DenseIter
+
+public:
+ // Iterators (see Iterator.h for usage)
+ typedef ChildIter<InternalNode, ChildNodeType, MaskOnIterator, ChildOn> ChildOnIter;
+ typedef ChildIter<const InternalNode,const ChildNodeType,MaskOnIterator,ChildOn> ChildOnCIter;
+ typedef ValueIter<InternalNode, const ValueType, MaskOffIterator, ChildOff> ChildOffIter;
+ typedef ValueIter<const InternalNode,const ValueType,MaskOffIterator,ChildOff> ChildOffCIter;
+ typedef DenseIter<InternalNode, ChildNodeType, ValueType, ChildAll> ChildAllIter;
+ typedef DenseIter<const InternalNode,const ChildNodeType, ValueType, ChildAll> ChildAllCIter;
+
+ typedef ValueIter<InternalNode, const ValueType, MaskOnIterator, ValueOn> ValueOnIter;
+ typedef ValueIter<const InternalNode,const ValueType,MaskOnIterator,ValueOn> ValueOnCIter;
+ typedef ValueIter<InternalNode, const ValueType, MaskOffIterator, ValueOff> ValueOffIter;
+ typedef ValueIter<const InternalNode,const ValueType,MaskOffIterator,ValueOff> ValueOffCIter;
+ typedef ValueIter<InternalNode, const ValueType, MaskOffIterator, ValueAll> ValueAllIter;
+ typedef ValueIter<const InternalNode,const ValueType,MaskOffIterator,ValueAll> ValueAllCIter;
+
+ ChildOnCIter cbeginChildOn() const { return ChildOnCIter(mChildMask.beginOn(), this); }
+ ChildOffCIter cbeginChildOff() const { return ChildOffCIter(mChildMask.beginOff(), this); }
+ ChildAllCIter cbeginChildAll() const { return ChildAllCIter(mChildMask.beginDense(), this); }
+ ChildOnCIter beginChildOn() const { return cbeginChildOn(); }
+ ChildOffCIter beginChildOff() const { return cbeginChildOff(); }
+ ChildAllCIter beginChildAll() const { return cbeginChildAll(); }
+ ChildOnIter beginChildOn() { return ChildOnIter(mChildMask.beginOn(), this); }
+ ChildOffIter beginChildOff() { return ChildOffIter(mChildMask.beginOff(), this); }
+ ChildAllIter beginChildAll() { return ChildAllIter(mChildMask.beginDense(), this); }
+
+ ValueOnCIter cbeginValueOn() const { return ValueOnCIter(mValueMask.beginOn(), this); }
+ ValueOffCIter cbeginValueOff() const { return ValueOffCIter(mValueMask.beginOff(), this); }
+ ValueAllCIter cbeginValueAll() const { return ValueAllCIter(mChildMask.beginOff(), this); }
+ ValueOnCIter beginValueOn() const { return cbeginValueOn(); }
+ ValueOffCIter beginValueOff() const { return cbeginValueOff(); }
+ ValueAllCIter beginValueAll() const { return cbeginValueAll(); }
+ ValueOnIter beginValueOn() { return ValueOnIter(mValueMask.beginOn(), this); }
+ ValueOffIter beginValueOff() { return ValueOffIter(mValueMask.beginOff(), this); }
+ ValueAllIter beginValueAll() { return ValueAllIter(mChildMask.beginOff(), this); }
+
+
+ static Index dim() { return DIM; }
+ static Index getLevel() { return LEVEL; }
+ static void getNodeLog2Dims(std::vector<Index>& dims);
+ static Index getChildDim() { return ChildNodeType::DIM; }
+
+ static Index coord2offset(const Coord& xyz);
+ static void offset2coord(Index n, Coord& xyz);
+ Coord offset2globalCoord(Index n) const;
+
+ Coord getOrigin() const { return mOrigin; }
+
+ Index32 leafCount() const;
+ Index32 nonLeafCount() const;
+ Index64 onVoxelCount() const;
+ Index64 offVoxelCount() const;
+ Index64 onLeafVoxelCount() const;
+ Index64 offLeafVoxelCount() const;
+
+ /// Return the total amount of memory in bytes occupied by this node and its children.
+ Index64 memUsage() const;
+
+ /// @brief Expand the specified bounding box so that it includes the active tiles
+ /// of this internal node as well as all the active values in its child nodes.
+ void evalActiveVoxelBoundingBox(CoordBBox& bbox) const;
+
+ /// @brief Return the bounding box of this node, i.e., the full index space
+ /// spanned by the node regardless of its content.
+ CoordBBox getNodeBoundingBox() const { return CoordBBox::createCube(mOrigin, DIM); }
+
+ bool isEmpty() const { return mChildMask.isOff(); }
+
+ /// Return @c true if all of this node's table entries have the same active state
+ /// and the same constant value to within the given tolerance,
+ /// and return that value in @a constValue and the active state in @a state.
+ bool isConstant(ValueType& constValue, bool& state,
+ const ValueType& tolerance = zeroVal<ValueType>()) const;
+ /// Return @c true if this node has no children and only contains inactive values.
+ bool isInactive() const { return this->isChildMaskOff() && this->isValueMaskOff(); }
+
+ /// Return @c true if the voxel at the given coordinates is active.
+ bool isValueOn(const Coord& xyz) const;
+ /// Return @c true if the voxel at the given offset is active.
+ bool isValueOn(Index offset) const { return mValueMask.isOn(offset); }
+
+ /// Return @c true if this node or any of its child nodes have any active tiles.
+ bool hasActiveTiles() const;
+
+ const ValueType& getValue(const Coord& xyz) const;
+ bool probeValue(const Coord& xyz, ValueType& value) const;
+
+ /// @brief Return the level of the tree (0 = leaf) at which the value
+ /// at the given coordinates resides.
+ Index getValueLevel(const Coord& xyz) const;
+
+ /// @brief If the first entry in this node's table is a tile, return the tile's value.
+ /// Otherwise, return the result of calling getFirstValue() on the child.
+ const ValueType& getFirstValue() const;
+ /// @brief If the last entry in this node's table is a tile, return the tile's value.
+ /// Otherwise, return the result of calling getLastValue() on the child.
+ const ValueType& getLastValue() const;
+
+ /// Set the active state at the given coordinates, but don't change its value.
+ void setActiveState(const Coord& xyz, bool on);
+
+ /// Mark the voxel at the given coordinates as inactive, but don't change its value.
+ void setValueOff(const Coord& xyz);
+ /// Change the value of the voxel at the given coordinates and mark the voxel as inactive.
+ void setValueOff(const Coord& xyz, const ValueType& value);
+
+ void setValueOn(const Coord& xyz);
+ void setValueOn(const Coord& xyz, const ValueType& value);
+ void setValueOnly(const Coord& xyz, const ValueType& value);
+ void setValueOnMin(const Coord& xyz, const ValueType& value);
+ void setValueOnMax(const Coord& xyz, const ValueType& value);
+ void setValueOnSum(const Coord& xyz, const ValueType& value);
+
+ /// @brief Set all voxels within an axis-aligned box to a constant value.
+ /// (The min and max coordinates are inclusive.)
+ void fill(const CoordBBox& bbox, const ValueType&, bool active = true);
+
+ /// @brief Copy into a dense grid the values of the voxels that lie within
+ /// a given bounding box.
+ ///
+ /// @param bbox inclusive bounding box of the voxels to be copied into the dense grid
+ /// @param dense dense grid with a stride in @e z of one (see tools::Dense
+ /// in tools/Dense.h for the required API)
+ ///
+ /// @note @a bbox is assumed to be identical to or contained in the coordinate domains
+ /// of both the dense grid and this node, i.e., no bounds checking is performed.
+ template<typename DenseT>
+ void copyToDense(const CoordBBox& bbox, DenseT& dense) const;
+
+ /// Return the value of the voxel at the given coordinates and, if necessary, update
+ /// the accessor with pointers to the nodes along the path from the root node to
+ /// the node containing the voxel.
+ /// @note Used internally by ValueAccessor.
+ template<typename AccessorT>
+ const ValueType& getValueAndCache(const Coord& xyz, AccessorT&) const;
+
+ /// Return @c true if the voxel at the given coordinates is active and, if necessary,
+ /// update the accessor with pointers to the nodes along the path from the root node
+ /// to the node containing the voxel.
+ /// @note Used internally by ValueAccessor.
+ template<typename AccessorT>
+ bool isValueOnAndCache(const Coord& xyz, AccessorT&) const;
+
+ /// Change the value of the voxel at the given coordinates and mark it as active.
+ /// If necessary, update the accessor with pointers to the nodes along the path
+ /// from the root node to the node containing the voxel.
+ /// @note Used internally by ValueAccessor.
+ template<typename AccessorT>
+ void setValueAndCache(const Coord& xyz, const ValueType& value, AccessorT&);
+
+ /// Set the value of the voxel at the given coordinate but preserves its active state.
+ /// If necessary, update the accessor with pointers to the nodes along the path
+ /// from the root node to the node containing the voxel.
+ /// @note Used internally by ValueAccessor.
+ template<typename AccessorT>
+ void setValueOnlyAndCache(const Coord& xyz, const ValueType& value, AccessorT&);
+
+ /// Set the value of the voxel at the given coordinates to the sum of its current
+ /// value and the given value, and mark the voxel as active.
+ /// If necessary, update the accessor with pointers to the nodes along the path
+ /// from the root node to the node containing the voxel.
+ /// @note Used internally by ValueAccessor.
+ template<typename AccessorT>
+ void setValueOnSumAndCache(const Coord& xyz, const ValueType& value, AccessorT&);
+
+ /// Change the value of the voxel at the given coordinates and mark it as inactive.
+ /// If necessary, update the accessor with pointers to the nodes along the path
+ /// from the root node to the node containing the voxel.
+ /// @note Used internally by ValueAccessor.
+ template<typename AccessorT>
+ void setValueOffAndCache(const Coord& xyz, const ValueType& value, AccessorT&);
+
+ /// Set the active state of the voxel at the given coordinates without changing its value.
+ /// If necessary, update the accessor with pointers to the nodes along the path
+ /// from the root node to the node containing the voxel.
+ /// @note Used internally by ValueAccessor.
+ template<typename AccessorT>
+ void setActiveStateAndCache(const Coord& xyz, bool on, AccessorT&);
+
+ /// Return @c true if the voxel at the given coordinates is active, change the voxel's
+ /// value, and, if necessary, update the accessor with pointers to the nodes along
+ /// the path from the root node to the node containing the voxel.
+ /// @note Used internally by ValueAccessor.
+ template<typename AccessorT>
+ bool probeValueAndCache(const Coord& xyz, ValueType& value, AccessorT&) const;
+
+ /// @brief Return the level of the tree (0 = leaf) at which the value
+ /// at the given coordinates resides.
+ ///
+ /// If necessary, update the accessor with pointers to the nodes along the path
+ /// from the root node to the node containing the voxel.
+ /// @note Used internally by ValueAccessor.
+ template<typename AccessorT>
+ Index getValueLevelAndCache(const Coord& xyz, AccessorT&) const;
+
+ /// Mark all values (both tiles and voxels) as active.
+ void setValuesOn();
+
+ //
+ // I/O
+ //
+ void writeTopology(std::ostream&, bool toHalf = false) const;
+ void readTopology(std::istream&, bool fromHalf = false);
+ void writeBuffers(std::ostream&, bool toHalf = false) const;
+ void readBuffers(std::istream&, bool fromHalf = false);
+
+ /// @brief Overwrites the inactive values with a new value whos
+ /// magnitude is equal to the specified background value and sign
+ /// is consistant with the lexicographically closest active value.
+ /// The net effect is a propagation of signs from the active to the
+ /// inactive values. Note this flood-filling is also performed on
+ /// any child nodes.
+ ///
+ /// @note This method is primarily useful for propagating the sign
+ /// from the (active) voxels in a narrow-band level set to the
+ /// inactive values outside the narrow band.
+ void signedFloodFill(const ValueType& background);
+
+ /// @brief Sets the inactive values to either the outside or inside
+ /// value, depending on the sign of the closest corresponding
+ /// active value. More specefically, an inactive value is set to
+ /// the outside value if the closest active value in the
+ /// lexicographic direction is positive, else it is set to the
+ /// inside value. Note this operation is also performed on any child nodes.
+ void signedFloodFill(const ValueType& outside, const ValueType& inside);
+
+ /// Change the sign of all the values represented in this node and
+ /// its child nodes.
+ void negate();
+
+ /// Replace active tiles with dense voxels, i.e., with active leaf nodes.
+ void voxelizeActiveTiles();
+
+ /// @brief Simple merge: Nodes and values of this node are always unchanged!
+ ///
+ /// @note Nodes and values of the other node are simply merged into this
+ /// node and the other tree is cannibalized in the process!
+ void merge(InternalNode& other, const ValueType& background, const ValueType& otherBackground);
+
+ /// @brief Union this branch's set of active values with the other branch's
+ /// active values. The value type of the other branch can be different.
+ /// @details The resulting state of a value is active if the corresponding value
+ /// was already active OR if it is active in the other tree. Also, a resulting
+ /// value maps to a voxel if the corresponding value already mapped to a voxel
+ /// OR if it is a voxel in the other tree. Thus, a resulting value can only
+ /// map to a tile if the corresponding value already mapped to a tile
+ /// AND if it is a tile value in other tree.
+ ///
+ /// Specifically, active tiles and voxels in this branch are not changed, and
+ /// tiles or voxels that were inactive in this branch but active in the other branch
+ /// are marked as active in this branch but left with their original values.
+ template<typename OtherChildNodeType>
+ void topologyUnion(const InternalNode<OtherChildNodeType, Log2Dim>& other);
+
+ template<typename CombineOp>
+ void combine(InternalNode& other, CombineOp&);
+ template<typename CombineOp>
+ void combine(const ValueType& value, bool valueIsActive, CombineOp&);
+
+ template<typename CombineOp>
+ void combine2(const InternalNode& other0, const InternalNode& other1, CombineOp&);
+ template<typename CombineOp>
+ void combine2(const ValueType& value, const InternalNode& other,
+ bool valueIsActive, CombineOp&);
+ template<typename CombineOp>
+ void combine2(const InternalNode& other, const ValueType& value,
+ bool valueIsActive, CombineOp&);
+
+ /// @brief Calls the templated functor BBoxOp with bounding box
+ /// information for all active tiles and leaf nodes in this node.
+ /// An additional level argument is provided for each callback.
+ ///
+ /// @note The bounding boxes are guarenteed to be non-overlapping.
+ template<typename BBoxOp> void visitActiveBBox(BBoxOp&) const;
+
+ template<typename VisitorOp> void visit(VisitorOp&);
+ template<typename VisitorOp> void visit(VisitorOp&) const;
+
+ template<typename OtherNodeType, typename VisitorOp>
+ void visit2Node(OtherNodeType& other, VisitorOp&);
+ template<typename OtherNodeType, typename VisitorOp>
+ void visit2Node(OtherNodeType& other, VisitorOp&) const;
+ template<typename IterT, typename VisitorOp>
+ void visit2(IterT& otherIter, VisitorOp&, bool otherIsLHS = false);
+ template<typename IterT, typename VisitorOp>
+ void visit2(IterT& otherIter, VisitorOp&, bool otherIsLHS = false) const;
+
+ /// @brief Call the @c PruneOp functor for each child node and, if the functor
+ /// returns @c true, prune the node and replace it with a tile.
+ ///
+ /// This method is used to implement all of the various pruning algorithms
+ /// (prune(), pruneInactive(), etc.). It should rarely be called directly.
+ /// @see openvdb/tree/Util.h for the definition of the @c PruneOp functor
+ template<typename PruneOp> void pruneOp(PruneOp&);
+
+ /// @brief Reduce the memory footprint of this tree by replacing with tiles
+ /// any nodes whose values are all the same (optionally to within a tolerance)
+ /// and have the same active state.
+ void prune(const ValueType& tolerance = zeroVal<ValueType>());
+
+ /// @brief Reduce the memory footprint of this tree by replacing with
+ /// tiles of the given value any nodes whose values are all inactive.
+ void pruneInactive(const ValueType&);
+
+ /// @brief Reduce the memory footprint of this tree by replacing with
+ /// background tiles any nodes whose values are all inactive.
+ void pruneInactive();
+
+ /// @brief Add the specified leaf to this node, possibly creating a child branch
+ /// in the process. If the leaf node already exists, replace it.
+ void addLeaf(LeafNodeType* leaf);
+
+ /// @brief Same as addLeaf except, if necessary, it update the accessor with pointers
+ /// to the nodes along the path from the root node to the node containing the coordinate.
+ template<typename AccessorT>
+ void addLeafAndCache(LeafNodeType* leaf, AccessorT&);
+
+ /// @brief Return a pointer to the node of type @c NodeT that contains voxel (x, y, z)
+ /// and replace it with a tile of the specified value and state.
+ /// If no such node exists, leave the tree unchanged and return @c NULL.
+ ///
+ /// @note The caller takes ownership of the node and is responsible for deleting it.
+ ///
+ /// @warning Since this method potentially removes nodes and branches of the tree,
+ /// it is important to clear the caches of all ValueAccessors associated with this tree.
+ template<typename NodeT>
+ NodeT* stealNode(const Coord& xyz, const ValueType& value, bool state);
+
+ /// @brief Add a tile at the specified tree level that contains voxel (x, y, z),
+ /// possibly creating a parent branch or deleting a child branch in the process.
+ void addTile(Index level, const Coord& xyz, const ValueType& value, bool state);
+
+ /// @brief Same as addTile() except, if necessary, update the accessor with pointers
+ /// to the nodes along the path from the root node to the node containing (x, y, z).
+ template<typename AccessorT>
+ void addTileAndCache(Index level, const Coord& xyz, const ValueType& value,
+ bool state, AccessorT&);
+
+ /// @brief Return a pointer to the leaf node that contains voxel (x, y, z).
+ /// If no such node exists, return NULL.
+ LeafNodeType* probeLeaf(const Coord& xyz);
+
+ /// @brief Return a const pointer to the leaf node that contains voxel (x, y, z).
+ /// If no such node exists, return NULL.
+ const LeafNodeType* probeConstLeaf(const Coord& xyz) const;
+ const LeafNodeType* probeLeaf(const Coord& xyz) const { return this->probeConstLeaf(xyz); }
+
+ /// @brief Same as probeLeaf except, if necessary, it update the accessor with pointers
+ /// to the nodes along the path from the root node to the node containing the coordinate.
+ template<typename AccessorT>
+ LeafNodeType* probeLeafAndCache(const Coord& xyz, AccessorT&);
+
+ /// @brief Same as probeLeaf except, if necessary, it update the accessor with pointers
+ /// to the nodes along the path from the root node to the node containing the coordinate.
+ template<typename AccessorT>
+ const LeafNodeType* probeConstLeafAndCache(const Coord& xyz, AccessorT& acc) const;
+ template<typename AccessorT>
+ const LeafNodeType* probeLeafAndCache(const Coord& xyz, AccessorT& acc) const
+ {
+ return this->probeConstLeafAndCache(xyz, acc);
+ }
+
+ /// @brief Return the leaf node that contains voxel (x, y, z).
+ /// If no such node exists, create one, but preserve the values and
+ /// active states of all voxels.
+ ///
+ /// @details Use this method to preallocate a static tree topology
+ /// over which to safely perform multithreaded processing.
+ LeafNodeType* touchLeaf(const Coord& xyz);
+
+ /// @brief Same as touchLeaf except, if necessary, it update the accessor with pointers
+ /// to the nodes along the path from the root node to the node containing the coordinate.
+ template<typename AccessorT>
+ LeafNodeType* touchLeafAndCache(const Coord& xyz, AccessorT&);
+
+ /// @brief Change inactive tiles or voxels with value oldBackground to newBackground
+ /// or -oldBackground to -newBackground. Active values are unchanged.
+ void resetBackground(const ValueType& oldBackground, const ValueType& newBackground);
+
+ /// @brief Return @c true if the given tree branch has the same node and active value
+ /// topology as this tree branch (but possibly a different @c ValueType).
+ template<typename OtherChildNodeType, Index OtherLog2Dim>
+ bool hasSameTopology(const InternalNode<OtherChildNodeType, OtherLog2Dim>* other) const;
+
+protected:
+ //@{
+ /// Allow iterators to call mask accessor methods (setValueMask(), setChildMask(), etc.).
+ /// @todo Make mask accessors public?
+ friend class IteratorBase<MaskOnIterator, InternalNode>;
+ friend class IteratorBase<MaskOffIterator, InternalNode>;
+ friend class IteratorBase<MaskDenseIterator, InternalNode>;
+ //@}
+
+ /// @brief During topology-only construction, access is needed
+ /// to protected/private members of other template instances.
+ template<typename, Index> friend class InternalNode;
+
+ // Mask accessors
+public:
+ bool isValueMaskOn(Index n) const { return mValueMask.isOn(n); }
+ bool isValueMaskOn() const { return mValueMask.isOn(); }
+ bool isValueMaskOff(Index n) const { return mValueMask.isOff(n); }
+ bool isValueMaskOff() const { return mValueMask.isOff(); }
+ bool isChildMaskOn(Index n) const { return mChildMask.isOn(n); }
+ bool isChildMaskOff(Index n) const { return mChildMask.isOff(n); }
+ bool isChildMaskOff() const { return mChildMask.isOff(); }
+protected:
+ //@{
+ /// Use a mask accessor to ensure consistency between the child and value masks;
+ /// i.e., the value mask should always be off wherever the child mask is on.
+ void setValueMask(Index n, bool on) { mValueMask.set(n, mChildMask.isOn(n) ? false : on); }
+ //@}
+
+ void makeChildNodeEmpty(Index n, const ValueType& value);
+ void setChildNode(Index i, ChildNodeType* child);
+ ChildNodeType* unsetChildNode(Index i, const ValueType& value);
+
+ template<typename NodeT>
+ NodeT* doStealNode(const Coord& xyz, const ValueType& value, bool state);
+
+ template<typename NodeT, typename VisitorOp, typename ChildAllIterT>
+ static inline void doVisit(NodeT&, VisitorOp&);
+
+ template<typename NodeT, typename OtherNodeT, typename VisitorOp,
+ typename ChildAllIterT, typename OtherChildAllIterT>
+ static inline void doVisit2Node(NodeT&, OtherNodeT&, VisitorOp&);
+
+ template<typename NodeT, typename VisitorOp,
+ typename ChildAllIterT, typename OtherChildAllIterT>
+ static inline void doVisit2(NodeT&, OtherChildAllIterT&, VisitorOp&, bool otherIsLHS);
+
+ ChildNodeType* getChildNode(Index n);
+ const ChildNodeType* getChildNode(Index n) const;
+
+
+ UnionType mNodes[NUM_VALUES];
+ NodeMaskType mChildMask, mValueMask;
+ /// Global grid index coordinates (x,y,z) of the local origin of this node
+ Coord mOrigin;
+}; // class InternalNode
+
+
+////////////////////////////////////////
+
+
+template<typename ChildT, Index Log2Dim>
+inline
+InternalNode<ChildT, Log2Dim>::InternalNode(const ValueType& background)
+{
+ for (Index i = 0; i < NUM_VALUES; ++i) mNodes[i].setValue(background);
+}
+
+
+template<typename ChildT, Index Log2Dim>
+inline
+InternalNode<ChildT, Log2Dim>::InternalNode(const Coord& origin, const ValueType& val, bool active):
+ mOrigin(origin[0] & ~(DIM - 1), // zero out the low-order bits
+ origin[1] & ~(DIM - 1),
+ origin[2] & ~(DIM - 1))
+{
+ if (active) mValueMask.setOn();
+ for (Index i = 0; i < NUM_VALUES; ++i) mNodes[i].setValue(val);
+}
+
+
+template<typename ChildT, Index Log2Dim>
+inline
+InternalNode<ChildT, Log2Dim>::InternalNode(const InternalNode& other):
+ mChildMask(other.mChildMask),
+ mValueMask(other.mValueMask),
+ mOrigin(other.mOrigin)
+{
+ for (Index i = 0; i < NUM_VALUES; ++i) {
+ if (isChildMaskOn(i)) {
+ mNodes[i].setChild(new ChildNodeType(*(other.mNodes[i].getChild())));
+ } else {
+ mNodes[i].setValue(other.mNodes[i].getValue());
+ }
+ }
+}
+
+template<typename ChildT, Index Log2Dim>
+template<typename OtherChildNodeType>
+inline
+InternalNode<ChildT, Log2Dim>::InternalNode(const InternalNode<OtherChildNodeType, Log2Dim>& other,
+ const ValueType& offValue, const ValueType& onValue, TopologyCopy):
+ mChildMask(other.mChildMask),
+ mValueMask(other.mValueMask),
+ mOrigin(other.mOrigin)
+{
+ for (Index i = 0; i < NUM_VALUES; ++i) {
+ if (isChildMaskOn(i)) {
+ mNodes[i].setChild(new ChildNodeType(*(other.mNodes[i].getChild()),
+ offValue, onValue, TopologyCopy()));
+ } else {
+ mNodes[i].setValue(isValueMaskOn(i) ? onValue : offValue);
+ }
+ }
+}
+
+template<typename ChildT, Index Log2Dim>
+template<typename OtherChildNodeType>
+inline
+InternalNode<ChildT, Log2Dim>::InternalNode(const InternalNode<OtherChildNodeType, Log2Dim>& other,
+ const ValueType& background, TopologyCopy):
+ mChildMask(other.mChildMask),
+ mValueMask(other.mValueMask),
+ mOrigin(other.mOrigin)
+{
+ for (Index i = 0; i < NUM_VALUES; ++i) mNodes[i].setValue(background);
+ for (ChildOnIter iter = this->beginChildOn(); iter; ++iter) {
+ mNodes[iter.pos()].setChild(new ChildNodeType(*(other.mNodes[iter.pos()].getChild()),
+ background, TopologyCopy()));
+ }
+}
+
+
+template<typename ChildT, Index Log2Dim>
+inline
+InternalNode<ChildT, Log2Dim>::~InternalNode()
+{
+ for (ChildOnIter iter = this->beginChildOn(); iter; ++iter) {
+ delete mNodes[iter.pos()].getChild();
+ }
+}
+
+
+////////////////////////////////////////
+
+
+template<typename ChildT, Index Log2Dim>
+inline Index32
+InternalNode<ChildT, Log2Dim>::leafCount() const
+{
+ if (ChildNodeType::getLevel() == 0) return mChildMask.countOn();
+ Index32 sum = 0;
+ for (ChildOnCIter iter = this->cbeginChildOn(); iter; ++iter) {
+ sum += iter->leafCount();
+ }
+ return sum;
+}
+
+
+template<typename ChildT, Index Log2Dim>
+inline Index32
+InternalNode<ChildT, Log2Dim>::nonLeafCount() const
+{
+ Index32 sum = 1;
+ if (ChildNodeType::getLevel() == 0) return sum;
+ for (ChildOnCIter iter = this->cbeginChildOn(); iter; ++iter) {
+ sum += iter->nonLeafCount();
+ }
+ return sum;
+}
+
+
+template<typename ChildT, Index Log2Dim>
+inline Index64
+InternalNode<ChildT, Log2Dim>::onVoxelCount() const
+{
+ Index64 sum = 0;
+ for (Index i = 0; i < NUM_VALUES; ++i) {
+ if (isChildMaskOff(i)) {
+ if (isValueMaskOn(i)) sum += ChildT::NUM_VOXELS;
+ } else {
+ sum += mNodes[i].getChild()->onVoxelCount();
+ }
+ }
+ return sum;
+}
+
+
+template<typename ChildT, Index Log2Dim>
+inline Index64
+InternalNode<ChildT, Log2Dim>::offVoxelCount() const
+{
+ Index64 sum = 0;
+ for (Index i = 0; i < NUM_VALUES; ++i) {
+ if (isChildMaskOff(i)) {
+ if (isValueMaskOff(i)) sum += ChildT::NUM_VOXELS;
+ } else {
+ sum += mNodes[i].getChild()->offVoxelCount();
+ }
+ }
+ return sum;
+}
+
+
+template<typename ChildT, Index Log2Dim>
+inline Index64
+InternalNode<ChildT, Log2Dim>::onLeafVoxelCount() const
+{
+ Index64 sum = 0;
+ for (ChildOnCIter iter = this->beginChildOn(); iter; ++iter) {
+ sum += mNodes[iter.pos()].getChild()->onLeafVoxelCount();
+ }
+ return sum;
+}
+
+
+template<typename ChildT, Index Log2Dim>
+inline Index64
+InternalNode<ChildT, Log2Dim>::offLeafVoxelCount() const
+{
+ Index64 sum = 0;
+ for (ChildOnCIter iter = this->beginChildOn(); iter; ++iter) {
+ sum += mNodes[iter.pos()].getChild()->offLeafVoxelCount();
+ }
+ return sum;
+}
+
+
+template<typename ChildT, Index Log2Dim>
+inline Index64
+InternalNode<ChildT, Log2Dim>::memUsage() const
+{
+ Index64 sum = NUM_VALUES * sizeof(UnionType) + mChildMask.memUsage()
+ + mValueMask.memUsage() + sizeof(mOrigin);
+ for (ChildOnCIter iter = this->cbeginChildOn(); iter; ++iter) {
+ sum += iter->memUsage();
+ }
+ return sum;
+}
+
+
+template<typename ChildT, Index Log2Dim>
+inline void
+InternalNode<ChildT, Log2Dim>::evalActiveVoxelBoundingBox(CoordBBox& bbox) const
+{
+ if (bbox.isInside(this->getNodeBoundingBox())) return;
+
+ ValueType dummy;
+ for (ChildAllCIter iter = this->cbeginChildAll(); iter; ++iter) {
+ if (const ChildT* child = iter.probeChild(dummy)) {
+ child->evalActiveVoxelBoundingBox(bbox);
+ } else if (iter.isValueOn()) {
+ bbox.expand(iter.getCoord(), ChildT::DIM);
+ }
+ }
+}
+
+
+////////////////////////////////////////
+
+
+template<typename ChildT, Index Log2Dim>
+template<typename PruneOp>
+inline void
+InternalNode<ChildT, Log2Dim>::pruneOp(PruneOp& op)
+{
+ for (ChildOnIter iter = this->beginChildOn(); iter; ++iter) {
+ const Index i = iter.pos();
+ ChildT* child = mNodes[i].getChild();
+ if (!op(*child)) continue;
+ delete child;
+ mChildMask.setOff(i);
+ mValueMask.set(i, op.state);
+ mNodes[i].setValue(op.value);
+ }
+
+}
+
+
+template<typename ChildT, Index Log2Dim>
+inline void
+InternalNode<ChildT, Log2Dim>::prune(const ValueType& tolerance)
+{
+ TolerancePrune<ValueType> op(tolerance);
+ this->pruneOp(op);
+}
+
+
+template<typename ChildT, Index Log2Dim>
+inline void
+InternalNode<ChildT, Log2Dim>::pruneInactive(const ValueType& bg)
+{
+ InactivePrune<ValueType> op(bg);
+ this->pruneOp(op);
+}
+
+
+template<typename ChildT, Index Log2Dim>
+inline void
+InternalNode<ChildT, Log2Dim>::pruneInactive()
+{
+ this->pruneInactive(this->getBackground());
+}
+
+
+////////////////////////////////////////
+
+
+// Helper method that implements stealNode()
+template<typename ChildT, Index Log2Dim>
+template<typename NodeT>
+inline NodeT*
+InternalNode<ChildT, Log2Dim>::doStealNode(const Coord& xyz, const ValueType& value, bool state)
+{
+ const Index n = this->coord2offset(xyz);
+ if (mChildMask.isOff(n)) return NULL;
+ ChildT* child = mNodes[n].getChild();
+ if (boost::is_same<NodeT, ChildT>::value) {
+ mChildMask.setOff(n);
+ mValueMask.set(n, state);
+ mNodes[n].setValue(value);
+ }
+ return (boost::is_same<NodeT, ChildT>::value)
+ ? reinterpret_cast<NodeT*>(child)
+ : child->template stealNode<NodeT>(xyz, value, state);
+}
+
+
+template<typename ChildT, Index Log2Dim>
+template<typename NodeT>
+inline NodeT*
+InternalNode<ChildT, Log2Dim>::stealNode(const Coord& xyz, const ValueType& value, bool state)
+{
+ // The following conditional is resolved at compile time, and the ternary operator
+ // and helper method are used to avoid "unreachable code" warnings (with
+ // "if (<cond>) { <A> } else { <B> }", either <A> or <B> is unreachable if <cond>
+ // is a compile-time constant expression). Partial specialization on NodeT would be
+ // impractical because a method template can't be specialized without also
+ // specializing its class template.
+ return (NodeT::LEVEL > ChildT::LEVEL ||
+ (NodeT::LEVEL == ChildT::LEVEL && !(boost::is_same<NodeT, ChildT>::value)))
+ ? static_cast<NodeT*>(NULL)
+ : this->doStealNode<NodeT>(xyz, value, state);
+}
+
+
+////////////////////////////////////////
+
+
+template<typename ChildT, Index Log2Dim>
+inline void
+InternalNode<ChildT, Log2Dim>::addLeaf(LeafNodeType* leaf)
+{
+ assert(leaf != NULL);
+ const Coord& xyz = leaf->origin();
+ const Index n = this->coord2offset(xyz);
+ ChildT* child = NULL;
+ if (mChildMask.isOff(n)) {
+ if (ChildT::LEVEL>0) {
+ child = new ChildT(xyz, mNodes[n].getValue(), mValueMask.isOn(n));
+ } else {
+ child = reinterpret_cast<ChildT*>(leaf);
+ }
+ mNodes[n].setChild(child);
+ mChildMask.setOn(n);
+ mValueMask.setOff(n);
+ } else {
+ if (ChildT::LEVEL>0) {
+ child = mNodes[n].getChild();
+ } else {
+ delete mNodes[n].getChild();
+ child = reinterpret_cast<ChildT*>(leaf);
+ mNodes[n].setChild(child);
+ }
+ }
+ child->addLeaf(leaf);
+}
+
+
+template<typename ChildT, Index Log2Dim>
+template<typename AccessorT>
+inline void
+InternalNode<ChildT, Log2Dim>::addLeafAndCache(LeafNodeType* leaf, AccessorT& acc)
+{
+ assert(leaf != NULL);
+ const Coord& xyz = leaf->origin();
+ const Index n = this->coord2offset(xyz);
+ ChildT* child = NULL;
+ if (mChildMask.isOff(n)) {
+ if (ChildT::LEVEL>0) {
+ child = new ChildT(xyz, mNodes[n].getValue(), mValueMask.isOn(n));
+ acc.insert(xyz, child);//we only cache internal nodes
+ } else {
+ child = reinterpret_cast<ChildT*>(leaf);
+ }
+ mNodes[n].setChild(child);
+ mChildMask.setOn(n);
+ mValueMask.setOff(n);
+ } else {
+ if (ChildT::LEVEL>0) {
+ child = mNodes[n].getChild();
+ acc.insert(xyz, child);//we only cache internal nodes
+ } else {
+ delete mNodes[n].getChild();
+ child = reinterpret_cast<ChildT*>(leaf);
+ mNodes[n].setChild(child);
+ }
+ }
+ child->addLeafAndCache(leaf, acc);
+}
+
+
+////////////////////////////////////////
+
+
+template<typename ChildT, Index Log2Dim>
+inline void
+InternalNode<ChildT, Log2Dim>::addTile(Index level, const Coord& xyz,
+ const ValueType& value, bool state)
+{
+ assert(level > 0);
+ if (LEVEL >= level) {
+ const Index n = this->coord2offset(xyz);
+ if (mChildMask.isOff(n)) {// tile case
+ if (LEVEL > level) {
+ ChildT* child = new ChildT(xyz, mNodes[n].getValue(), mValueMask.isOn(n));
+ mNodes[n].setChild(child);
+ mChildMask.setOn(n);
+ mValueMask.setOff(n);
+ child->addTile(level, xyz, value, state);
+ } else {
+ mValueMask.set(n, state);
+ mNodes[n].setValue(value);
+ }
+ } else {// child branch case
+ ChildT* child = mNodes[n].getChild();
+ if (LEVEL > level) {
+ child->addTile(level, xyz, value, state);
+ } else {
+ delete child;
+ mChildMask.setOff(n);
+ mValueMask.set(n, state);
+ mNodes[n].setValue(value);
+ }
+ }
+ }
+}
+
+
+template<typename ChildT, Index Log2Dim>
+template<typename AccessorT>
+inline void
+InternalNode<ChildT, Log2Dim>::addTileAndCache(Index level, const Coord& xyz,
+ const ValueType& value, bool state, AccessorT& acc)
+{
+ assert(level > 0);
+ if (LEVEL >= level) {
+ const Index n = this->coord2offset(xyz);
+ if (mChildMask.isOff(n)) {// tile case
+ if (LEVEL > level) {
+ ChildT* child = new ChildT(xyz, mNodes[n].getValue(), mValueMask.isOn(n));
+ mNodes[n].setChild(child);
+ mChildMask.setOn(n);
+ mValueMask.setOff(n);
+ acc.insert(xyz, child);
+ child->addTileAndCache(level, xyz, value, state, acc);
+ } else {
+ mValueMask.set(n, state);
+ mNodes[n].setValue(value);
+ }
+ } else {// child branch case
+ ChildT* child = mNodes[n].getChild();
+ if (LEVEL > level) {
+ acc.insert(xyz, child);
+ child->addTileAndCache(level, xyz, value, state, acc);
+ } else {
+ delete child;
+ mChildMask.setOff(n);
+ mValueMask.set(n, state);
+ mNodes[n].setValue(value);
+ }
+ }
+ }
+}
+
+
+////////////////////////////////////////
+
+
+template<typename ChildT, Index Log2Dim>
+inline typename ChildT::LeafNodeType*
+InternalNode<ChildT, Log2Dim>::touchLeaf(const Coord& xyz)
+{
+ const Index n = this->coord2offset(xyz);
+ ChildT* child = NULL;
+ if (mChildMask.isOff(n)) {
+ child = new ChildT(xyz, mNodes[n].getValue(), mValueMask.isOn(n));
+ mNodes[n].setChild(child);
+ mChildMask.setOn(n);
+ mValueMask.setOff(n);
+ } else {
+ child = mNodes[n].getChild();
+ }
+ return child->touchLeaf(xyz);
+}
+
+
+template<typename ChildT, Index Log2Dim>
+template<typename AccessorT>
+inline typename ChildT::LeafNodeType*
+InternalNode<ChildT, Log2Dim>::touchLeafAndCache(const Coord& xyz, AccessorT& acc)
+{
+ const Index n = this->coord2offset(xyz);
+ if (mChildMask.isOff(n)) {
+ mNodes[n].setChild(new ChildNodeType(xyz, mNodes[n].getValue(), mValueMask.isOn(n)));
+ mChildMask.setOn(n);
+ mValueMask.setOff(n);
+ }
+ acc.insert(xyz, mNodes[n].getChild());
+ return mNodes[n].getChild()->touchLeafAndCache(xyz, acc);
+}
+
+
+////////////////////////////////////////
+
+
+template<typename ChildT, Index Log2Dim>
+inline typename ChildT::LeafNodeType*
+InternalNode<ChildT, Log2Dim>::probeLeaf(const Coord& xyz)
+{
+ const Index n = this->coord2offset(xyz);
+ return mChildMask.isOn(n) ? mNodes[n].getChild()->probeLeaf(xyz) : NULL;
+}
+
+
+template<typename ChildT, Index Log2Dim>
+inline const typename ChildT::LeafNodeType*
+InternalNode<ChildT, Log2Dim>::probeConstLeaf(const Coord& xyz) const
+{
+ const Index n = this->coord2offset(xyz);
+ return mChildMask.isOn(n) ? mNodes[n].getChild()->probeConstLeaf(xyz) : NULL;
+}
+
+
+template<typename ChildT, Index Log2Dim>
+template<typename AccessorT>
+inline typename ChildT::LeafNodeType*
+InternalNode<ChildT, Log2Dim>::probeLeafAndCache(const Coord& xyz, AccessorT& acc)
+{
+ const Index n = this->coord2offset(xyz);
+ if (mChildMask.isOff(n)) return NULL;
+ acc.insert(xyz, mNodes[n].getChild());
+ return mNodes[n].getChild()->probeLeafAndCache(xyz, acc);
+}
+
+
+template<typename ChildT, Index Log2Dim>
+template<typename AccessorT>
+inline const typename ChildT::LeafNodeType*
+InternalNode<ChildT, Log2Dim>::probeConstLeafAndCache(const Coord& xyz, AccessorT& acc) const
+{
+ const Index n = this->coord2offset(xyz);
+ if (mChildMask.isOff(n)) return NULL;
+ acc.insert(xyz, mNodes[n].getChild());
+ return mNodes[n].getChild()->probeConstLeafAndCache(xyz, acc);
+}
+
+
+////////////////////////////////////////
+
+
+template<typename ChildT, Index Log2Dim>
+inline bool
+InternalNode<ChildT, Log2Dim>::isConstant(ValueType& constValue, bool& state,
+ const ValueType& tolerance) const
+{
+ bool allEqual = true, firstValue = true, valueState = true;
+ ValueType value = zeroVal<ValueType>();
+ for (Index i = 0; allEqual && i < NUM_VALUES; ++i) {
+ if (this->isChildMaskOff(i)) {
+ // If entry i is a value, check if it is within tolerance
+ // and whether its active state matches the other entries.
+ if (firstValue) {
+ firstValue = false;
+ valueState = isValueMaskOn(i);
+ value = mNodes[i].getValue();
+ } else {
+ allEqual = (isValueMaskOn(i) == valueState)
+ && math::isApproxEqual(mNodes[i].getValue(), value, tolerance);
+ }
+ } else {
+ // If entry i is a child, check if the child is constant and within tolerance
+ // and whether its active state matches the other entries.
+ ValueType childValue = zeroVal<ValueType>();
+ bool isChildOn = false;
+ if (mNodes[i].getChild()->isConstant(childValue, isChildOn, tolerance)) {
+ if (firstValue) {
+ firstValue = false;
+ valueState = isChildOn;
+ value = childValue;
+ } else {
+ allEqual = (isChildOn == valueState)
+ && math::isApproxEqual(childValue, value, tolerance);
+ }
+ } else { // child is not constant
+ allEqual = false;
+ }
+ }
+ }
+ if (allEqual) {
+ constValue = value;
+ state = valueState;
+ }
+ return allEqual;
+}
+
+
+////////////////////////////////////////
+
+
+template<typename ChildT, Index Log2Dim>
+inline bool
+InternalNode<ChildT, Log2Dim>::hasActiveTiles() const
+{
+ const bool anyActiveTiles = !mValueMask.isOff();
+ if (LEVEL==1 || anyActiveTiles) return anyActiveTiles;
+ for (ChildOnCIter iter = this->cbeginChildOn(); iter; ++iter) {
+ if (iter->hasActiveTiles()) return true;
+ }
+ return false;
+}
+
+
+template<typename ChildT, Index Log2Dim>
+inline bool
+InternalNode<ChildT, Log2Dim>::isValueOn(const Coord& xyz) const
+{
+ const Index n = this->coord2offset(xyz);
+ if (this->isChildMaskOff(n)) return this->isValueMaskOn(n);
+ return mNodes[n].getChild()->isValueOn(xyz);
+}
+
+template<typename ChildT, Index Log2Dim>
+template<typename AccessorT>
+inline bool
+InternalNode<ChildT, Log2Dim>::isValueOnAndCache(const Coord& xyz, AccessorT& acc) const
+{
+ const Index n = this->coord2offset(xyz);
+ if (this->isChildMaskOff(n)) return this->isValueMaskOn(n);
+ acc.insert(xyz, mNodes[n].getChild());
+ return mNodes[n].getChild()->isValueOnAndCache(xyz, acc);
+}
+
+
+template<typename ChildT, Index Log2Dim>
+inline const typename ChildT::ValueType&
+InternalNode<ChildT, Log2Dim>::getValue(const Coord& xyz) const
+{
+ const Index n = this->coord2offset(xyz);
+ return this->isChildMaskOff(n) ? mNodes[n].getValue()
+ : mNodes[n].getChild()->getValue(xyz);
+}
+
+template<typename ChildT, Index Log2Dim>
+template<typename AccessorT>
+inline const typename ChildT::ValueType&
+InternalNode<ChildT, Log2Dim>::getValueAndCache(const Coord& xyz, AccessorT& acc) const
+{
+ const Index n = this->coord2offset(xyz);
+ if (this->isChildMaskOn(n)) {
+ acc.insert(xyz, mNodes[n].getChild());
+ return mNodes[n].getChild()->getValueAndCache(xyz, acc);
+ }
+ return mNodes[n].getValue();
+}
+
+
+template<typename ChildT, Index Log2Dim>
+inline Index
+InternalNode<ChildT, Log2Dim>::getValueLevel(const Coord& xyz) const
+{
+ const Index n = this->coord2offset(xyz);
+ return this->isChildMaskOff(n) ? LEVEL : mNodes[n].getChild()->getValueLevel(xyz);
+}
+
+template<typename ChildT, Index Log2Dim>
+template<typename AccessorT>
+inline Index
+InternalNode<ChildT, Log2Dim>::getValueLevelAndCache(const Coord& xyz, AccessorT& acc) const
+{
+ const Index n = this->coord2offset(xyz);
+ if (this->isChildMaskOn(n)) {
+ acc.insert(xyz, mNodes[n].getChild());
+ return mNodes[n].getChild()->getValueLevelAndCache(xyz, acc);
+ }
+ return LEVEL;
+}
+
+
+template<typename ChildT, Index Log2Dim>
+inline bool
+InternalNode<ChildT, Log2Dim>::probeValue(const Coord& xyz, ValueType& value) const
+{
+ const Index n = this->coord2offset(xyz);
+ if (this->isChildMaskOff(n)) {
+ value = mNodes[n].getValue();
+ return this->isValueMaskOn(n);
+ }
+ return mNodes[n].getChild()->probeValue(xyz, value);
+}
+
+template<typename ChildT, Index Log2Dim>
+template<typename AccessorT>
+inline bool
+InternalNode<ChildT, Log2Dim>::probeValueAndCache(const Coord& xyz,
+ ValueType& value, AccessorT& acc) const
+{
+ const Index n = this->coord2offset(xyz);
+ if (this->isChildMaskOn(n)) {
+ acc.insert(xyz, mNodes[n].getChild());
+ return mNodes[n].getChild()->probeValueAndCache(xyz, value, acc);
+ }
+ value = mNodes[n].getValue();
+ return this->isValueMaskOn(n);
+}
+
+
+template<typename ChildT, Index Log2Dim>
+inline void
+InternalNode<ChildT, Log2Dim>::setValueOff(const Coord& xyz)
+{
+ const Index n = this->coord2offset(xyz);
+ bool hasChild = this->isChildMaskOn(n);
+ if (!hasChild && this->isValueMaskOn(n)) {
+ // If the voxel belongs to a constant tile that is active,
+ // a child subtree must be constructed.
+ mChildMask.setOn(n); // we're adding a child node so set the mask on
+ mValueMask.setOff(n); // value mask is always off if child mask is on
+ hasChild = true;
+ mNodes[n].setChild(new ChildNodeType(xyz, mNodes[n].getValue(), /*active=*/true));
+ }
+ if (hasChild) mNodes[n].getChild()->setValueOff(xyz);
+}
+
+
+template<typename ChildT, Index Log2Dim>
+inline void
+InternalNode<ChildT, Log2Dim>::setValueOn(const Coord& xyz)
+{
+ const Index n = this->coord2offset(xyz);
+ bool hasChild = this->isChildMaskOn(n);
+ if (!hasChild && !this->isValueMaskOn(n)) {
+ // If the voxel belongs to a constant tile that is inactive,
+ // a child subtree must be constructed.
+ mChildMask.setOn(n); // we're adding a child node so set the mask on
+ mValueMask.setOff(n); // value mask is always off if child mask is on
+ hasChild = true;
+ mNodes[n].setChild(new ChildNodeType(xyz, mNodes[n].getValue(), /*active=*/false));
+ }
+ if (hasChild) mNodes[n].getChild()->setValueOn(xyz);
+}
+
+
+template<typename ChildT, Index Log2Dim>
+inline void
+InternalNode<ChildT, Log2Dim>::setValueOff(const Coord& xyz, const ValueType& value)
+{
+ const Index n = InternalNode::coord2offset(xyz);
+ bool hasChild = this->isChildMaskOn(n);
+ if (!hasChild) {
+ const bool active = this->isValueMaskOn(n);
+ if (active || !math::isExactlyEqual(mNodes[n].getValue(), value)) {
+ // If the voxel belongs to a tile that is either active or that
+ // has a constant value that is different from the one provided,
+ // a child subtree must be constructed.
+ mChildMask.setOn(n); // we're adding a child node so set the mask on
+ mValueMask.setOff(n); // value mask is always off if child mask is on
+ hasChild = true;
+ mNodes[n].setChild(new ChildNodeType(xyz, mNodes[n].getValue(), active));
+ }
+ }
+ if (hasChild) mNodes[n].getChild()->setValueOff(xyz, value);
+}
+
+template<typename ChildT, Index Log2Dim>
+template<typename AccessorT>
+inline void
+InternalNode<ChildT, Log2Dim>::setValueOffAndCache(const Coord& xyz,
+ const ValueType& value, AccessorT& acc)
+{
+ const Index n = InternalNode::coord2offset(xyz);
+ bool hasChild = this->isChildMaskOn(n);
+ if (!hasChild) {
+ const bool active = this->isValueMaskOn(n);
+ if (active || !math::isExactlyEqual(mNodes[n].getValue(), value)) {
+ // If the voxel belongs to a tile that is either active or that
+ // has a constant value that is different from the one provided,
+ // a child subtree must be constructed.
+ mChildMask.setOn(n); // we're adding a child node so set the mask on
+ mValueMask.setOff(n); // value mask is always off if child mask is on
+ hasChild = true;
+ mNodes[n].setChild(new ChildNodeType(xyz, mNodes[n].getValue(), active));
+ }
+ }
+ if (hasChild) {
+ ChildT* child = mNodes[n].getChild();
+ acc.insert(xyz, child);
+ child->setValueOffAndCache(xyz, value, acc);
+ }
+}
+
+
+template<typename ChildT, Index Log2Dim>
+inline void
+InternalNode<ChildT, Log2Dim>::setValueOn(const Coord& xyz, const ValueType& value)
+{
+ const Index n = this->coord2offset(xyz);
+ bool hasChild = this->isChildMaskOn(n);
+ if (!hasChild) {
+ const bool active = this->isValueMaskOn(n); // tile's active state
+ if (!active || !math::isExactlyEqual(mNodes[n].getValue(), value)) {
+ // If the voxel belongs to a tile that is either inactive or that
+ // has a constant value that is different from the one provided,
+ // a child subtree must be constructed.
+ mChildMask.setOn(n); // we're adding a child node so set the mask on
+ mValueMask.setOff(n); // value mask is always off if child mask is on
+ hasChild = true;
+ mNodes[n].setChild(new ChildNodeType(xyz, mNodes[n].getValue(), active));
+ }
+ }
+ if (hasChild) mNodes[n].getChild()->setValueOn(xyz, value);
+}
+
+template<typename ChildT, Index Log2Dim>
+template<typename AccessorT>
+inline void
+InternalNode<ChildT, Log2Dim>::setValueAndCache(const Coord& xyz,
+ const ValueType& value, AccessorT& acc)
+{
+ const Index n = this->coord2offset(xyz);
+ bool hasChild = this->isChildMaskOn(n);
+ if (!hasChild) {
+ const bool active = this->isValueMaskOn(n);
+ if (!active || !math::isExactlyEqual(mNodes[n].getValue(), value)) {
+ // If the voxel belongs to a tile that is either inactive or that
+ // has a constant value that is different from the one provided,
+ // a child subtree must be constructed.
+ mChildMask.setOn(n); // we're adding a child node so set the mask on
+ mValueMask.setOff(n); // value mask is always off if child mask is on
+ hasChild = true;
+ mNodes[n].setChild(new ChildNodeType(xyz, mNodes[n].getValue(), active));
+ }
+ }
+ if (hasChild) {
+ acc.insert(xyz, mNodes[n].getChild());
+ mNodes[n].getChild()->setValueAndCache(xyz, value, acc);
+ }
+}
+
+
+template<typename ChildT, Index Log2Dim>
+inline void
+InternalNode<ChildT, Log2Dim>::setValueOnly(const Coord& xyz, const ValueType& value)
+{
+ const Index n = this->coord2offset(xyz);
+ bool hasChild = this->isChildMaskOn(n);
+ if (!hasChild && !math::isExactlyEqual(mNodes[n].getValue(), value)) {
+ // If the voxel has a tile value that is different from the one provided,
+ // a child subtree must be constructed.
+ const bool active = this->isValueMaskOn(n);
+ mChildMask.setOn(n); // we're adding a child node so set the mask on
+ mValueMask.setOff(n); // value mask is always off if child mask is on
+ hasChild = true;
+ mNodes[n].setChild(new ChildNodeType(xyz, mNodes[n].getValue(), active));
+ }
+ if (hasChild) mNodes[n].getChild()->setValueOnly(xyz, value);
+}
+
+template<typename ChildT, Index Log2Dim>
+template<typename AccessorT>
+inline void
+InternalNode<ChildT, Log2Dim>::setValueOnlyAndCache(const Coord& xyz,
+ const ValueType& value, AccessorT& acc)
+{
+ const Index n = this->coord2offset(xyz);
+ bool hasChild = this->isChildMaskOn(n);
+ if (!hasChild && !math::isExactlyEqual(mNodes[n].getValue(), value)) {
+ // If the voxel has a tile value that is different from the one provided,
+ // a child subtree must be constructed.
+ const bool active = this->isValueMaskOn(n);
+ mChildMask.setOn(n); // we're adding a child node so set the mask on
+ mValueMask.setOff(n); // value mask is always off if child mask is on
+ hasChild = true;
+ mNodes[n].setChild(new ChildNodeType(xyz, mNodes[n].getValue(), active));
+ }
+ if (hasChild) {
+ acc.insert(xyz, mNodes[n].getChild());
+ mNodes[n].getChild()->setValueOnlyAndCache(xyz, value, acc);
+ }
+}
+
+
+template<typename ChildT, Index Log2Dim>
+inline void
+InternalNode<ChildT, Log2Dim>::setActiveState(const Coord& xyz, bool on)
+{
+ const Index n = this->coord2offset(xyz);
+ bool hasChild = this->isChildMaskOn(n);
+ if (!hasChild) {
+ if (on != this->isValueMaskOn(n)) {
+ // If the voxel belongs to a tile with the wrong active state,
+ // then a child subtree must be constructed.
+ mChildMask.setOn(n); // we're adding a child node so set the mask on
+ mValueMask.setOff(n); // value mask is always off if child mask is on
+ mNodes[n].setChild(new ChildNodeType(xyz, mNodes[n].getValue(), !on));
+ // 'on' is the voxel's new state, therefore '!on' is the tile's current state
+ hasChild = true;
+ }
+ }
+ if (hasChild) mNodes[n].getChild()->setActiveState(xyz, on);
+}
+
+template<typename ChildT, Index Log2Dim>
+template<typename AccessorT>
+inline void
+InternalNode<ChildT, Log2Dim>::setActiveStateAndCache(const Coord& xyz, bool on, AccessorT& acc)
+{
+ const Index n = this->coord2offset(xyz);
+ bool hasChild = this->isChildMaskOn(n);
+ if (!hasChild) {
+ if (on != this->isValueMaskOn(n)) {
+ // If the voxel belongs to a tile with the wrong active state,
+ // then a child subtree must be constructed.
+ mChildMask.setOn(n); // we're adding a child node so set the mask on
+ mValueMask.setOff(n); // value mask is always off if child mask is on
+ mNodes[n].setChild(new ChildNodeType(xyz, mNodes[n].getValue(), !on));
+ // 'on' is the voxel's new state, therefore '!on' is the tile's current state
+ hasChild = true;
+ }
+ }
+ if (hasChild) {
+ ChildT* child = mNodes[n].getChild();
+ acc.insert(xyz, child);
+ child->setActiveStateAndCache(xyz, on, acc);
+ }
+}
+
+
+template<typename ChildT, Index Log2Dim>
+inline void
+InternalNode<ChildT, Log2Dim>::setValuesOn()
+{
+ mValueMask = !mChildMask;
+ for (ChildOnIter iter = this->beginChildOn(); iter; ++iter) {
+ mNodes[iter.pos()].getChild()->setValuesOn();
+ }
+}
+
+
+template<typename ChildT, Index Log2Dim>
+inline void
+InternalNode<ChildT, Log2Dim>::setValueOnMin(const Coord& xyz, const ValueType& value)
+{
+ const Index n = InternalNode::coord2offset(xyz);
+ bool hasChild = this->isChildMaskOn(n);
+ if (!hasChild) {
+ const bool active = this->isValueMaskOn(n);
+ if (!active || (mNodes[n].getValue() > value)) {
+ // If the voxel belongs to a tile that is either inactive or that
+ // has a constant value that is greater than the one provided,
+ // a child subtree must be constructed.
+ mChildMask.setOn(n); // we're adding a child node so set the mask on
+ mValueMask.setOff(n); // value mask is always off if child mask is on
+ hasChild = true;
+ mNodes[n].setChild(new ChildNodeType(xyz, mNodes[n].getValue(), active));
+ }
+ }
+ if (hasChild) mNodes[n].getChild()->setValueOnMin(xyz, value);
+}
+
+
+template<typename ChildT, Index Log2Dim>
+inline void
+InternalNode<ChildT, Log2Dim>::setValueOnMax(const Coord& xyz, const ValueType& value)
+{
+ const Index n = InternalNode::coord2offset(xyz);
+ bool hasChild = this->isChildMaskOn(n);
+ if (!hasChild) {
+ const bool active = this->isValueMaskOn(n);
+ if (!active || (value > mNodes[n].getValue())) {
+ // If the voxel belongs to a tile that is either inactive or that
+ // has a constant value that is less than the one provided,
+ // a child subtree must be constructed.
+ mChildMask.setOn(n); // we're adding a child node so set the mask on
+ mValueMask.setOff(n); // value mask is always off if child mask is on
+ hasChild = true;
+ mNodes[n].setChild(new ChildNodeType(xyz, mNodes[n].getValue(), active));
+ }
+ }
+ if (hasChild) mNodes[n].getChild()->setValueOnMax(xyz, value);
+}
+
+
+template<typename ChildT, Index Log2Dim>
+inline void
+InternalNode<ChildT, Log2Dim>::setValueOnSum(const Coord& xyz, const ValueType& addend)
+{
+ const Index n = InternalNode::coord2offset(xyz);
+ bool hasChild = this->isChildMaskOn(n);
+ if (!hasChild) {
+ const bool active = this->isValueMaskOn(n);
+ if (!active || !math::isExactlyEqual(addend, zeroVal<ValueType>())) {
+ // If the voxel belongs to a tile that is inactive or if
+ // the addend is nonzero, a child subtree must be constructed.
+ mChildMask.setOn(n);// we're adding a child node so set the mask on
+ mValueMask.setOff(n);// value mask is always off if child mask is on
+ hasChild = true;
+ mNodes[n].setChild(new ChildNodeType(xyz, mNodes[n].getValue(), active));
+ }
+ }
+ if (hasChild) mNodes[n].getChild()->setValueOnSum(xyz, addend);
+}
+
+template<typename ChildT, Index Log2Dim>
+template<typename AccessorT>
+inline void
+InternalNode<ChildT, Log2Dim>::setValueOnSumAndCache(const Coord& xyz,
+ const ValueType& addend, AccessorT& acc)
+{
+ const Index n = this->coord2offset(xyz);
+ bool hasChild = this->isChildMaskOn(n);
+ if (!hasChild) {
+ const bool active = this->isValueMaskOn(n);
+ if (!active || !math::isExactlyEqual(addend, zeroVal<ValueType>())) {
+ // If the voxel belongs to a tile that is inactive or if
+ // the addend is nonzero, a child subtree must be constructed.
+ mChildMask.setOn(n); // we're adding a child node so set the mask on
+ mValueMask.setOff(n); // value mask is always off if child mask is on
+ hasChild = true;
+ mNodes[n].setChild(new ChildNodeType(xyz, mNodes[n].getValue(), active));
+ }
+ }
+ if (hasChild) {
+ acc.insert(xyz, mNodes[n].getChild());
+ mNodes[n].getChild()->setValueOnSumAndCache(xyz, addend, acc);
+ }
+}
+
+
+////////////////////////////////////////
+
+
+template<typename ChildT, Index Log2Dim>
+inline void
+InternalNode<ChildT, Log2Dim>::fill(const CoordBBox& bbox, const ValueType& value, bool active)
+{
+ Coord xyz, tileMin, tileMax;
+ for (int x = bbox.min().x(); x <= bbox.max().x(); x = tileMax.x() + 1) {
+ xyz.setX(x);
+ for (int y = bbox.min().y(); y <= bbox.max().y(); y = tileMax.y() + 1) {
+ xyz.setY(y);
+ for (int z = bbox.min().z(); z <= bbox.max().z(); z = tileMax.z() + 1) {
+ xyz.setZ(z);
+
+ // Get the bounds of the tile that contains voxel (x, y, z).
+ const Index n = this->coord2offset(xyz);
+ tileMin = this->offset2globalCoord(n);
+ tileMax = tileMin.offsetBy(ChildT::DIM - 1);
+
+ if (xyz != tileMin || Coord::lessThan(bbox.max(), tileMax)) {
+ // If the box defined by (xyz, bbox.max()) doesn't completely enclose
+ // the tile to which xyz belongs, create a child node (or retrieve
+ // the existing one).
+ ChildT* child = NULL;
+ if (this->isChildMaskOff(n)) {
+ // Replace the tile with a newly-created child that is initialized
+ // with the tile's value and active state.
+ child = new ChildT(xyz, mNodes[n].getValue(), this->isValueMaskOn(n));
+ mChildMask.setOn(n);
+ mValueMask.setOff(n);
+ mNodes[n].setChild(child);
+ } else {
+ child = mNodes[n].getChild();
+ }
+
+ // Forward the fill request to the child.
+ if (child) {
+ child->fill(CoordBBox(xyz, Coord::minComponent(bbox.max(), tileMax)),
+ value, active);
+ }
+
+ } else {
+ // If the box given by (xyz, bbox.max()) completely encloses
+ // the tile to which xyz belongs, create the tile (if it
+ // doesn't already exist) and give it the fill value.
+ this->makeChildNodeEmpty(n, value);
+ mValueMask.set(n, active);
+ }
+ }
+ }
+ }
+}
+
+
+////////////////////////////////////////
+
+
+template<typename ChildT, Index Log2Dim>
+template<typename DenseT>
+inline void
+InternalNode<ChildT, Log2Dim>::copyToDense(const CoordBBox& bbox, DenseT& dense) const
+{
+ const size_t xStride = dense.xStride(), yStride = dense.yStride();// zStride=1
+ const Coord& min = dense.bbox().min();
+ for (Coord xyz = bbox.min(), max; xyz[0] <= bbox.max()[0]; xyz[0] = max[0] + 1) {
+ for (xyz[1] = bbox.min()[1]; xyz[1] <= bbox.max()[1]; xyz[1] = max[1] + 1) {
+ for (xyz[2] = bbox.min()[2]; xyz[2] <= bbox.max()[2]; xyz[2] = max[2] + 1) {
+ const Index n = this->coord2offset(xyz);
+ // Get max coordinates of the child node that contains voxel xyz.
+ max = this->offset2globalCoord(n).offsetBy(ChildT::DIM-1);
+
+ // Get the bbox of the interection of bbox and the child node
+ CoordBBox sub(xyz, Coord::minComponent(bbox.max(), max));
+
+ if (this->isChildMaskOn(n)) {//is a child
+ mNodes[n].getChild()->copyToDense(sub, dense);
+ } else {//a tile value
+ const ValueType value = mNodes[n].getValue();
+ sub.translate(-min);
+ ValueType* a0 = dense.data() + sub.min()[2];
+ for (Int32 x=sub.min()[0], ex=sub.max()[0]+1; x<ex; ++x) {
+ ValueType* a1 = a0 + x*xStride;
+ for (Int32 y=sub.min()[1], ey=sub.max()[1]+1; y<ey; ++y) {
+ ValueType* a2 = a1 + y*yStride;
+ for (Int32 z=sub.min()[2], ez=sub.max()[2]+1; z<ez; ++z) *a2++ = value;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+
+////////////////////////////////////////
+
+
+template<typename ChildT, Index Log2Dim>
+inline void
+InternalNode<ChildT, Log2Dim>::writeTopology(std::ostream& os, bool toHalf) const
+{
+ mChildMask.save(os);
+ mValueMask.save(os);
+
+ {
+ // Copy all of this node's values into an array.
+ boost::shared_array<ValueType> values(new ValueType[NUM_VALUES]);
+ const ValueType zero = zeroVal<ValueType>();
+ for (Index i = 0; i < NUM_VALUES; ++i) {
+ values[i] = (mChildMask.isOff(i) ? mNodes[i].getValue() : zero);
+ }
+ // Compress (optionally) and write out the contents of the array.
+ io::writeCompressedValues(os, values.get(), NUM_VALUES, mValueMask, mChildMask, toHalf);
+ }
+ // Write out the child nodes in order.
+ for (ChildOnCIter iter = this->cbeginChildOn(); iter; ++iter) {
+ iter->writeTopology(os, toHalf);
+ }
+}
+
+
+template<typename ChildT, Index Log2Dim>
+inline void
+InternalNode<ChildT, Log2Dim>::readTopology(std::istream& is, bool fromHalf)
+{
+ mChildMask.load(is);
+ mValueMask.load(is);
+
+ if (io::getFormatVersion(is) < OPENVDB_FILE_VERSION_INTERNALNODE_COMPRESSION) {
+ for (Index i = 0; i < NUM_VALUES; ++i) {
+ if (this->isChildMaskOn(i)) {
+ ChildNodeType* child =
+ new ChildNodeType(offset2globalCoord(i), zeroVal<ValueType>());
+ mNodes[i].setChild(child);
+ child->readTopology(is);
+ } else {
+ ValueType value;
+ is.read(reinterpret_cast<char*>(&value), sizeof(ValueType));
+ mNodes[i].setValue(value);
+ }
+ }
+ } else {
+ const bool oldVersion =
+ (io::getFormatVersion(is) < OPENVDB_FILE_VERSION_NODE_MASK_COMPRESSION);
+ const Index numValues = (oldVersion ? mChildMask.countOff() : NUM_VALUES);
+ {
+ // Read in (and uncompress, if necessary) all of this node's values
+ // into a contiguous array.
+ boost::shared_array<ValueType> values(new ValueType[numValues]);
+ io::readCompressedValues(is, values.get(), numValues, mValueMask, fromHalf);
+
+ // Copy values from the array into this node's table.
+ if (oldVersion) {
+ Index n = 0;
+ for (ValueAllIter iter = this->beginValueAll(); iter; ++iter) {
+ mNodes[iter.pos()].setValue(values[n++]);
+ }
+ assert(n == numValues);
+ } else {
+ for (ValueAllIter iter = this->beginValueAll(); iter; ++iter) {
+ mNodes[iter.pos()].setValue(values[iter.pos()]);
+ }
+ }
+ }
+ // Read in all child nodes and insert them into the table at their proper locations.
+ for (ChildOnIter iter = this->beginChildOn(); iter; ++iter) {
+ ChildNodeType* child = new ChildNodeType(iter.getCoord(), zeroVal<ValueType>());
+ mNodes[iter.pos()].setChild(child);
+ child->readTopology(is, fromHalf);
+ }
+ }
+}
+
+
+////////////////////////////////////////
+
+
+template<typename ChildT, Index Log2Dim>
+inline const typename ChildT::ValueType&
+InternalNode<ChildT, Log2Dim>::getFirstValue() const
+{
+ return (this->isChildMaskOn(0) ? mNodes[0].getChild()->getFirstValue() : mNodes[0].getValue());
+}
+
+
+template<typename ChildT, Index Log2Dim>
+inline const typename ChildT::ValueType&
+InternalNode<ChildT, Log2Dim>::getLastValue() const
+{
+ const Index n = NUM_VALUES - 1;
+ return (this->isChildMaskOn(n) ? mNodes[n].getChild()->getLastValue() : mNodes[n].getValue());
+}
+
+
+////////////////////////////////////////
+
+
+template<typename ChildT, Index Log2Dim>
+inline void
+InternalNode<ChildT, Log2Dim>::signedFloodFill(const ValueType& background)
+{
+ this->signedFloodFill(background, negative(background));
+}
+
+
+template<typename ChildT, Index Log2Dim>
+inline void
+InternalNode<ChildT, Log2Dim>::signedFloodFill(const ValueType& outsideValue,
+ const ValueType& insideValue)
+{
+ // First, flood fill all child nodes.
+ for (ChildOnIter iter = this->beginChildOn(); iter; ++iter) {
+ iter->signedFloodFill(outsideValue, insideValue);
+ }
+ const Index first = mChildMask.findFirstOn();
+ if (first < NUM_VALUES) {
+ bool xInside = math::isNegative(mNodes[first].getChild()->getFirstValue()),
+ yInside = xInside, zInside = xInside;
+ for (Index x = 0; x != (1 << Log2Dim); ++x) {
+ const int x00 = x << (2 * Log2Dim); // offset for block(x, 0, 0)
+ if (isChildMaskOn(x00)) {
+ xInside = math::isNegative(mNodes[x00].getChild()->getLastValue());
+ }
+ yInside = xInside;
+ for (Index y = 0; y != (1 << Log2Dim); ++y) {
+ const Index xy0 = x00 + (y << Log2Dim); // offset for block(x, y, 0)
+ if (isChildMaskOn(xy0)) {
+ yInside = math::isNegative(mNodes[xy0].getChild()->getLastValue());
+ }
+ zInside = yInside;
+ for (Index z = 0; z != (1 << Log2Dim); ++z) {
+ const Index xyz = xy0 + z; // offset for block(x, y, z)
+ if (isChildMaskOn(xyz)) {
+ zInside = math::isNegative(mNodes[xyz].getChild()->getLastValue());
+ } else {
+ mNodes[xyz].setValue(zInside ? insideValue : outsideValue);
+ }
+ }
+ }
+ }
+ } else {//no child nodes exist simply use the sign of the first tile value.
+ const ValueType v = math::isNegative(mNodes[0].getValue()) ? insideValue : outsideValue;
+ for (Index i = 0; i < NUM_VALUES; ++i) mNodes[i].setValue(v);
+ }
+}
+
+
+template<typename ChildT, Index Log2Dim>
+inline void
+InternalNode<ChildT, Log2Dim>::negate()
+{
+ for (Index i = 0; i < NUM_VALUES; ++i) {
+ if (this->isChildMaskOn(i)) {
+ mNodes[i].getChild()->negate();
+ } else {
+ mNodes[i].setValue(negative(mNodes[i].getValue()));
+ }
+ }
+
+}
+
+
+template<typename ChildT, Index Log2Dim>
+inline void
+InternalNode<ChildT, Log2Dim>::voxelizeActiveTiles()
+{
+ for (ValueOnIter iter = this->beginValueOn(); iter; ++iter) {
+ const Index n = iter.pos();
+ ChildNodeType* child = new ChildNodeType(iter.getCoord(), iter.getValue(), true);
+ mValueMask.setOff(n);
+ mChildMask.setOn(n);
+ mNodes[n].setChild(child);
+ }
+ for (ChildOnIter iter = this->beginChildOn(); iter; ++iter) iter->voxelizeActiveTiles();
+}
+
+
+template<typename ChildT, Index Log2Dim>
+inline void
+InternalNode<ChildT, Log2Dim>::merge(InternalNode& other,
+ const ValueType& background, const ValueType& otherBackground)
+{
+ for (ChildOnIter iter = other.beginChildOn(); iter; ++iter) {
+ const Index n = iter.pos();
+ if (mChildMask.isOff(n)) { // transfer node
+ ChildNodeType* child = other.mNodes[n].getChild();
+ other.mChildMask.setOff(n);
+ // Note other's new tile value is undefined which is okay since
+ // other is assumed to be cannibalized in the process of merging!
+ child->resetBackground(otherBackground, background);
+ mChildMask.setOn(n);
+ mValueMask.setOff(n);
+ mNodes[n].setChild(child);
+ } else {
+ mNodes[n].getChild()->merge(*iter, background, otherBackground);
+ }
+ }
+
+ // Copy active tile values.
+ for (ValueOnCIter iter = other.cbeginValueOn(); iter; ++iter) {
+ const Index n = iter.pos();
+ if (mChildMask.isOff(n) && mValueMask.isOff(n)) {
+ mNodes[n].setValue(iter.getValue());
+ mValueMask.setOn(n);
+ }
+ }
+}
+
+
+template<typename ChildT, Index Log2Dim>
+template<typename OtherChildT>
+inline void
+InternalNode<ChildT, Log2Dim>::topologyUnion(const InternalNode<OtherChildT, Log2Dim>& other)
+{
+ typedef typename InternalNode<OtherChildT, Log2Dim>::ChildOnCIter OtherChildIter;
+ typedef typename InternalNode<OtherChildT, Log2Dim>::ValueOnCIter OtherValueIter;
+
+ for (OtherChildIter iter = other.cbeginChildOn(); iter; ++iter) {
+ const Index i = iter.pos();
+ if (mChildMask.isOn(i)) {//this has a child node
+ mNodes[i].getChild()->topologyUnion(*iter);
+ } else {// this is a tile so replace it with a child branch with identical topology
+ ChildNodeType* child = new ChildNodeType(*iter, mNodes[i].getValue(), TopologyCopy());
+ if (mValueMask.isOn(i)) {
+ mValueMask.isOff(i);//we're replacing the active tile with a child branch
+ child->setValuesOn();//activate all values since it was an active tile
+ }
+ mChildMask.setOn(i);
+ mNodes[i].setChild(child);
+ }
+ }
+ for (OtherValueIter iter = other.cbeginValueOn(); iter; ++iter) {
+ const Index i = iter.pos();
+ if (mChildMask.isOn(i)) {
+ mNodes[i].getChild()->setValuesOn();
+ } else if (mValueMask.isOff(i)) { //inactive tile
+ mValueMask.setOn(i);
+ }
+ }
+}
+
+
+////////////////////////////////////////
+
+
+template<typename ChildT, Index Log2Dim>
+template<typename CombineOp>
+inline void
+InternalNode<ChildT, Log2Dim>::combine(InternalNode& other, CombineOp& op)
+{
+ const ValueType zero = zeroVal<ValueType>();
+
+ CombineArgs<ValueType> args;
+
+ for (Index i = 0; i < NUM_VALUES; ++i) {
+ if (this->isChildMaskOff(i) && other.isChildMaskOff(i)) {
+ // Both this node and the other node have constant values (tiles).
+ // Combine the two values and store the result as this node's new tile value.
+ op(args.setARef(mNodes[i].getValue())
+ .setAIsActive(isValueMaskOn(i))
+ .setBRef(other.mNodes[i].getValue())
+ .setBIsActive(other.isValueMaskOn(i)));
+ mNodes[i].setValue(args.result());
+ mValueMask.set(i, args.resultIsActive());
+ } else if (this->isChildMaskOn(i) && other.isChildMaskOff(i)) {
+ // Combine this node's child with the other node's constant value.
+ ChildNodeType* child = mNodes[i].getChild();
+ assert(child);
+ if (child) {
+ child->combine(other.mNodes[i].getValue(), other.isValueMaskOn(i), op);
+ }
+ } else if (this->isChildMaskOff(i) && other.isChildMaskOn(i)) {
+ // Combine this node's constant value with the other node's child.
+ ChildNodeType* child = other.mNodes[i].getChild();
+ assert(child);
+ if (child) {
+ // Combine this node's constant value with the other node's child,
+ // but use a new functor in which the A and B values are swapped,
+ // since the constant value is the A value, not the B value.
+ SwappedCombineOp<ValueType, CombineOp> swappedOp(op);
+ child->combine(mNodes[i].getValue(), isValueMaskOn(i), swappedOp);
+
+ // Steal the other node's child.
+ other.mChildMask.setOff(i);
+ other.mNodes[i].setValue(zero);
+ mChildMask.setOn(i);
+ mValueMask.setOff(i);
+ mNodes[i].setChild(child);
+ }
+
+ } else /*if (isChildMaskOn(i) && other.isChildMaskOn(i))*/ {
+ // Combine this node's child with the other node's child.
+ ChildNodeType
+ *child = mNodes[i].getChild(),
+ *otherChild = other.mNodes[i].getChild();
+ assert(child);
+ assert(otherChild);
+ if (child && otherChild) {
+ child->combine(*otherChild, op);
+ }
+ }
+ }
+}
+
+
+template<typename ChildT, Index Log2Dim>
+template<typename CombineOp>
+inline void
+InternalNode<ChildT, Log2Dim>::combine(const ValueType& value, bool valueIsActive, CombineOp& op)
+{
+ CombineArgs<ValueType> args;
+
+ for (Index i = 0; i < NUM_VALUES; ++i) {
+ if (this->isChildMaskOff(i)) {
+ // Combine this node's constant value with the given constant value.
+ op(args.setARef(mNodes[i].getValue())
+ .setAIsActive(isValueMaskOn(i))
+ .setBRef(value)
+ .setBIsActive(valueIsActive));
+ mNodes[i].setValue(args.result());
+ mValueMask.set(i, args.resultIsActive());
+ } else /*if (isChildMaskOn(i))*/ {
+ // Combine this node's child with the given constant value.
+ ChildNodeType* child = mNodes[i].getChild();
+ assert(child);
+ if (child) child->combine(value, valueIsActive, op);
+ }
+ }
+}
+
+
+////////////////////////////////////////
+
+
+template<typename ChildT, Index Log2Dim>
+template<typename CombineOp>
+inline void
+InternalNode<ChildT, Log2Dim>::combine2(const InternalNode& other0, const InternalNode& other1,
+ CombineOp& op)
+{
+ CombineArgs<ValueType> args;
+
+ for (Index i = 0; i < NUM_VALUES; ++i) {
+ if (other0.isChildMaskOff(i) && other1.isChildMaskOff(i)) {
+ op(args.setARef(other0.mNodes[i].getValue())
+ .setAIsActive(other0.isValueMaskOn(i))
+ .setBRef(other1.mNodes[i].getValue())
+ .setBIsActive(other1.isValueMaskOn(i)));
+ // Replace child i with a constant value.
+ this->makeChildNodeEmpty(i, args.result());
+ mValueMask.set(i, args.resultIsActive());
+ } else {
+ ChildNodeType* otherChild = other0.isChildMaskOn(i)
+ ? other0.mNodes[i].getChild() : other1.mNodes[i].getChild();
+ assert(otherChild);
+ if (this->isChildMaskOff(i)) {
+ // Add a new child with the same coordinates, etc.
+ // as the other node's child.
+ mChildMask.setOn(i);
+ mValueMask.setOff(i);
+ mNodes[i].setChild(new ChildNodeType(otherChild->getOrigin(),
+ mNodes[i].getValue()));
+ }
+
+ if (other0.isChildMaskOff(i)) {
+ // Combine node1's child with node0's constant value
+ // and write the result into child i.
+ mNodes[i].getChild()->combine2(other0.mNodes[i].getValue(),
+ *other1.mNodes[i].getChild(), other0.isValueMaskOn(i), op);
+ } else if (other1.isChildMaskOff(i)) {
+ // Combine node0's child with node1's constant value
+ // and write the result into child i.
+ mNodes[i].getChild()->combine2(*other0.mNodes[i].getChild(),
+ other1.mNodes[i].getValue(), other1.isValueMaskOn(i), op);
+ } else {
+ // Combine node0's child with node1's child
+ // and write the result into child i.
+ mNodes[i].getChild()->combine2(*other0.mNodes[i].getChild(),
+ *other1.mNodes[i].getChild(), op);
+ }
+ }
+ }
+}
+
+
+template<typename ChildT, Index Log2Dim>
+template<typename CombineOp>
+inline void
+InternalNode<ChildT, Log2Dim>::combine2(const ValueType& value, const InternalNode& other,
+ bool valueIsActive, CombineOp& op)
+{
+ CombineArgs<ValueType> args;
+
+ for (Index i = 0; i < NUM_VALUES; ++i) {
+ if (other.isChildMaskOff(i)) {
+ op(args.setARef(value)
+ .setAIsActive(valueIsActive)
+ .setBRef(other.mNodes[i].getValue())
+ .setBIsActive(other.isValueMaskOn(i)));
+ // Replace child i with a constant value.
+ this->makeChildNodeEmpty(i, args.result());
+ mValueMask.set(i, args.resultIsActive());
+ } else {
+ ChildNodeType* otherChild = other.mNodes[i].getChild();
+ assert(otherChild);
+ if (this->isChildMaskOff(i)) {
+ // Add a new child with the same coordinates, etc.
+ // as the other node's child.
+ /// @todo Could the other node's ChildNodeType be different from this node's?
+ mChildMask.setOn(i);
+ mValueMask.setOff(i);
+ mNodes[i].setChild(new ChildNodeType(*otherChild));
+ }
+ // Combine the other node's child with a constant value
+ // and write the result into child i.
+ mNodes[i].getChild()->combine2(value, *otherChild, valueIsActive, op);
+ }
+ }
+}
+
+
+template<typename ChildT, Index Log2Dim>
+template<typename CombineOp>
+inline void
+InternalNode<ChildT, Log2Dim>::combine2(const InternalNode& other, const ValueType& value,
+ bool valueIsActive, CombineOp& op)
+{
+ CombineArgs<ValueType> args;
+
+ for (Index i = 0; i < NUM_VALUES; ++i) {
+ if (other.isChildMaskOff(i)) {
+ op(args.setARef(other.mNodes[i].getValue())
+ .setAIsActive(other.isValueMaskOn(i))
+ .setBRef(value)
+ .setBIsActive(valueIsActive));
+ // Replace child i with a constant value.
+ this->makeChildNodeEmpty(i, args.result());
+ mValueMask.set(i, args.resultIsActive());
+ } else {
+ ChildNodeType* otherChild = other.mNodes[i].getChild();
+ assert(otherChild);
+ if (this->isChildMaskOff(i)) {
+ // Add a new child with the same coordinates, etc.
+ // as the other node's child.
+ mChildMask.setOn(i);
+ mValueMask.setOff(i);
+ mNodes[i].setChild(new ChildNodeType(otherChild->getOrigin(),
+ mNodes[i].getValue()));
+ }
+ // Combine the other node's child with a constant value
+ // and write the result into child i.
+ mNodes[i].getChild()->combine2(*otherChild, value, valueIsActive, op);
+ }
+ }
+}
+
+
+////////////////////////////////////////
+
+
+template<typename ChildT, Index Log2Dim>
+template<typename BBoxOp>
+inline void
+InternalNode<ChildT, Log2Dim>::visitActiveBBox(BBoxOp& op) const
+{
+ for (ValueOnCIter i = this->cbeginValueOn(); i; ++i) {
+#ifdef _MSC_VER
+ op.operator()<LEVEL>(CoordBBox::createCube(i.getCoord(), ChildNodeType::DIM));
+#else
+ op.template operator()<LEVEL>(CoordBBox::createCube(i.getCoord(), ChildNodeType::DIM));
+#endif
+ }
+ if (op.template descent<LEVEL>()) {
+ for (ChildOnCIter i = this->cbeginChildOn(); i; ++i) i->visitActiveBBox(op);
+ } else {
+ for (ChildOnCIter i = this->cbeginChildOn(); i; ++i) {
+#ifdef _MSC_VER
+ op.operator()<LEVEL>(i->getNodeBoundingBox());
+#else
+ op.template operator()<LEVEL>(i->getNodeBoundingBox());
+#endif
+ }
+ }
+}
+
+
+template<typename ChildT, Index Log2Dim>
+template<typename VisitorOp>
+inline void
+InternalNode<ChildT, Log2Dim>::visit(VisitorOp& op)
+{
+ doVisit<InternalNode, VisitorOp, ChildAllIter>(*this, op);
+}
+
+
+template<typename ChildT, Index Log2Dim>
+template<typename VisitorOp>
+inline void
+InternalNode<ChildT, Log2Dim>::visit(VisitorOp& op) const
+{
+ doVisit<const InternalNode, VisitorOp, ChildAllCIter>(*this, op);
+}
+
+
+template<typename ChildT, Index Log2Dim>
+template<typename NodeT, typename VisitorOp, typename ChildAllIterT>
+inline void
+InternalNode<ChildT, Log2Dim>::doVisit(NodeT& self, VisitorOp& op)
+{
+ typename NodeT::ValueType val;
+ for (ChildAllIterT iter = self.beginChildAll(); iter; ++iter) {
+ if (op(iter)) continue;
+ if (typename ChildAllIterT::ChildNodeType* child = iter.probeChild(val)) {
+ child->visit(op);
+ }
+ }
+}
+
+
+////////////////////////////////////////
+
+
+template<typename ChildT, Index Log2Dim>
+template<typename OtherNodeType, typename VisitorOp>
+inline void
+InternalNode<ChildT, Log2Dim>::visit2Node(OtherNodeType& other, VisitorOp& op)
+{
+ doVisit2Node<InternalNode, OtherNodeType, VisitorOp, ChildAllIter,
+ typename OtherNodeType::ChildAllIter>(*this, other, op);
+}
+
+
+template<typename ChildT, Index Log2Dim>
+template<typename OtherNodeType, typename VisitorOp>
+inline void
+InternalNode<ChildT, Log2Dim>::visit2Node(OtherNodeType& other, VisitorOp& op) const
+{
+ doVisit2Node<const InternalNode, OtherNodeType, VisitorOp, ChildAllCIter,
+ typename OtherNodeType::ChildAllCIter>(*this, other, op);
+}
+
+
+template<typename ChildT, Index Log2Dim>
+template<
+ typename NodeT,
+ typename OtherNodeT,
+ typename VisitorOp,
+ typename ChildAllIterT,
+ typename OtherChildAllIterT>
+inline void
+InternalNode<ChildT, Log2Dim>::doVisit2Node(NodeT& self, OtherNodeT& other, VisitorOp& op)
+{
+ // Allow the two nodes to have different ValueTypes, but not different dimensions.
+ BOOST_STATIC_ASSERT(OtherNodeT::NUM_VALUES == NodeT::NUM_VALUES);
+ BOOST_STATIC_ASSERT(OtherNodeT::LEVEL == NodeT::LEVEL);
+
+ typename NodeT::ValueType val;
+ typename OtherNodeT::ValueType otherVal;
+
+ ChildAllIterT iter = self.beginChildAll();
+ OtherChildAllIterT otherIter = other.beginChildAll();
+
+ for ( ; iter && otherIter; ++iter, ++otherIter)
+ {
+ const size_t skipBranch = static_cast<size_t>(op(iter, otherIter));
+
+ typename ChildAllIterT::ChildNodeType* child =
+ (skipBranch & 1U) ? NULL : iter.probeChild(val);
+ typename OtherChildAllIterT::ChildNodeType* otherChild =
+ (skipBranch & 2U) ? NULL : otherIter.probeChild(otherVal);
+
+ if (child != NULL && otherChild != NULL) {
+ child->visit2Node(*otherChild, op);
+ } else if (child != NULL) {
+ child->visit2(otherIter, op);
+ } else if (otherChild != NULL) {
+ otherChild->visit2(iter, op, /*otherIsLHS=*/true);
+ }
+ }
+}
+
+
+////////////////////////////////////////
+
+
+template<typename ChildT, Index Log2Dim>
+template<typename OtherChildAllIterType, typename VisitorOp>
+inline void
+InternalNode<ChildT, Log2Dim>::visit2(OtherChildAllIterType& otherIter,
+ VisitorOp& op, bool otherIsLHS)
+{
+ doVisit2<InternalNode, VisitorOp, ChildAllIter, OtherChildAllIterType>(
+ *this, otherIter, op, otherIsLHS);
+}
+
+
+template<typename ChildT, Index Log2Dim>
+template<typename OtherChildAllIterType, typename VisitorOp>
+inline void
+InternalNode<ChildT, Log2Dim>::visit2(OtherChildAllIterType& otherIter,
+ VisitorOp& op, bool otherIsLHS) const
+{
+ doVisit2<const InternalNode, VisitorOp, ChildAllCIter, OtherChildAllIterType>(
+ *this, otherIter, op, otherIsLHS);
+}
+
+
+template<typename ChildT, Index Log2Dim>
+template<typename NodeT, typename VisitorOp, typename ChildAllIterT, typename OtherChildAllIterT>
+inline void
+InternalNode<ChildT, Log2Dim>::doVisit2(NodeT& self, OtherChildAllIterT& otherIter,
+ VisitorOp& op, bool otherIsLHS)
+{
+ if (!otherIter) return;
+
+ const size_t skipBitMask = (otherIsLHS ? 2U : 1U);
+
+ typename NodeT::ValueType val;
+ for (ChildAllIterT iter = self.beginChildAll(); iter; ++iter) {
+ const size_t skipBranch = static_cast<size_t>(
+ otherIsLHS ? op(otherIter, iter) : op(iter, otherIter));
+
+ typename ChildAllIterT::ChildNodeType* child =
+ (skipBranch & skipBitMask) ? NULL : iter.probeChild(val);
+
+ if (child != NULL) child->visit2(otherIter, op, otherIsLHS);
+ }
+}
+
+
+////////////////////////////////////////
+
+
+template<typename ChildT, Index Log2Dim>
+inline void
+InternalNode<ChildT, Log2Dim>::writeBuffers(std::ostream& os, bool toHalf) const
+{
+ for (ChildOnCIter iter = this->cbeginChildOn(); iter; ++iter) {
+ iter->writeBuffers(os, toHalf);
+ }
+}
+
+
+template<typename ChildT, Index Log2Dim>
+inline void
+InternalNode<ChildT, Log2Dim>::readBuffers(std::istream& is, bool fromHalf)
+{
+ for (ChildOnIter iter = this->beginChildOn(); iter; ++iter) {
+ iter->readBuffers(is, fromHalf);
+ }
+}
+
+
+////////////////////////////////////////
+
+
+template<typename ChildT, Index Log2Dim>
+void
+InternalNode<ChildT, Log2Dim>::getNodeLog2Dims(std::vector<Index>& dims)
+{
+ dims.push_back(Log2Dim);
+ ChildNodeType::getNodeLog2Dims(dims);
+}
+
+
+template<typename ChildT, Index Log2Dim>
+inline void
+InternalNode<ChildT, Log2Dim>::offset2coord(Index n, Coord &xyz)
+{
+ assert(n<(1<<3*Log2Dim));
+ xyz.setX(n >> 2*Log2Dim);
+ n &= ((1<<2*Log2Dim)-1);
+ xyz.setY(n >> Log2Dim);
+ xyz.setZ(n & ((1<<Log2Dim)-1));
+}
+
+
+template<typename ChildT, Index Log2Dim>
+inline Index
+InternalNode<ChildT, Log2Dim>::coord2offset(const Coord& xyz)
+{
+ return (((xyz[0]&DIM-1u)>>ChildNodeType::TOTAL)<<2*Log2Dim)
+ + (((xyz[1]&DIM-1u)>>ChildNodeType::TOTAL)<< Log2Dim)
+ + ((xyz[2]&DIM-1u)>>ChildNodeType::TOTAL);
+}
+
+
+template<typename ChildT, Index Log2Dim>
+inline Coord
+InternalNode<ChildT, Log2Dim>::offset2globalCoord(Index n) const
+{
+ Coord local;
+ this->offset2coord(n, local);
+ local <<= ChildT::TOTAL;
+ return local + this->getOrigin();
+}
+
+
+////////////////////////////////////////
+
+
+template<typename ChildT, Index Log2Dim>
+inline void
+InternalNode<ChildT, Log2Dim>::resetBackground(const ValueType& oldBackground,
+ const ValueType& newBackground)
+{
+ if (math::isExactlyEqual(oldBackground, newBackground)) return;
+ for (Index i = 0; i < NUM_VALUES; ++i) {
+ if (this->isChildMaskOn(i)) {
+ mNodes[i].getChild()->resetBackground(oldBackground, newBackground);
+ } else if (this->isValueMaskOff(i)) {
+ if (math::isApproxEqual(mNodes[i].getValue(), oldBackground)) {
+ mNodes[i].setValue(newBackground);
+ } else if (math::isApproxEqual(mNodes[i].getValue(), negative(oldBackground))) {
+ mNodes[i].setValue(negative(newBackground));
+ }
+ }
+ }
+}
+
+
+template<typename ChildT, Index Log2Dim>
+template<typename OtherChildNodeType, Index OtherLog2Dim>
+inline bool
+InternalNode<ChildT, Log2Dim>::hasSameTopology(
+ const InternalNode<OtherChildNodeType, OtherLog2Dim>* other) const
+{
+ if (Log2Dim != OtherLog2Dim || mChildMask != other->mChildMask ||
+ mValueMask != other->mValueMask) return false;
+ for (ChildOnCIter iter = this->cbeginChildOn(); iter; ++iter) {
+ if (!iter->hasSameTopology(other->mNodes[iter.pos()].getChild())) return false;
+ }
+ return true;
+}
+
+
+template<typename ChildT, Index Log2Dim>
+inline void
+InternalNode<ChildT, Log2Dim>::setChildNode(Index i, ChildNodeType* child)
+{
+ assert(child);
+ if (this->isChildMaskOn(i)) {
+ delete mNodes[i].getChild();
+ } else {
+ mChildMask.setOn(i);
+ mValueMask.setOff(i);
+ }
+ mNodes[i].setChild(child);
+}
+
+
+template<typename ChildT, Index Log2Dim>
+inline ChildT*
+InternalNode<ChildT, Log2Dim>::unsetChildNode(Index i, const ValueType& value)
+{
+ if (this->isChildMaskOff(i)) {
+ mNodes[i].setValue(value);
+ return NULL;
+ }
+ ChildNodeType* child = mNodes[i].getChild();
+ mChildMask.setOff(i);
+ mNodes[i].setValue(value);
+ return child;
+}
+
+
+template<typename ChildT, Index Log2Dim>
+inline void
+InternalNode<ChildT, Log2Dim>::makeChildNodeEmpty(Index n, const ValueType& value)
+{
+ delete this->unsetChildNode(n, value);
+}
+
+template<typename ChildT, Index Log2Dim>
+inline ChildT*
+InternalNode<ChildT, Log2Dim>::getChildNode(Index n)
+{
+ return (this->isChildMaskOn(n) ? mNodes[n].getChild() : NULL);
+}
+
+
+template<typename ChildT, Index Log2Dim>
+inline const ChildT*
+InternalNode<ChildT, Log2Dim>::getChildNode(Index n) const
+{
+ return (this->isChildMaskOn(n) ? mNodes[n].getChild() : NULL);
+}
+
+} // namespace tree
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_TREE_INTERNALNODE_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/tree/Iterator.h b/extern/openvdb/internal/openvdb/tree/Iterator.h
new file mode 100644
index 00000000000..d2bfa35abb1
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/tree/Iterator.h
@@ -0,0 +1,276 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+//
+/// @file Iterator.h
+///
+/// @author Peter Cucka and Ken Museth
+
+#ifndef OPENVDB_TREE_ITERATOR_HAS_BEEN_INCLUDED
+#define OPENVDB_TREE_ITERATOR_HAS_BEEN_INCLUDED
+
+#include <sstream>
+#include <boost/type_traits/remove_const.hpp>
+#include <openvdb/util/NodeMasks.h>
+#include <openvdb/Exceptions.h>
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace tree {
+
+/// @brief Base class for iterators over internal and leaf nodes
+///
+/// This class is typically not instantiated directly, since it doesn't provide methods
+/// to dereference the iterator. Those methods (@vdblink::tree::SparseIteratorBase::operator*()
+/// operator*()@endlink, @vdblink::tree::SparseIteratorBase::setValue() setValue()@endlink, etc.)
+/// are implemented in the @vdblink::tree::SparseIteratorBase sparse@endlink and
+/// @vdblink::tree::DenseIteratorBase dense@endlink iterator subclasses.
+template<typename MaskIterT, typename NodeT>
+class IteratorBase
+{
+public:
+ IteratorBase(): mParentNode(NULL) {}
+ IteratorBase(const MaskIterT& iter, NodeT* parent):
+ mParentNode(parent), mMaskIter(iter) {}
+
+ void operator=(const IteratorBase& other)
+ {
+ mParentNode = other.mParentNode;
+ mMaskIter = other.mMaskIter;
+ }
+
+ bool operator==(const IteratorBase& other) const
+ {
+ return (mParentNode == other.mParentNode) && (mMaskIter == other.mMaskIter);
+ }
+ bool operator!=(const IteratorBase& other) const
+ {
+ return !(*this == other);
+ }
+
+ /// Return a pointer to the node (if any) over which this iterator is iterating.
+ NodeT* getParentNode() const { return mParentNode; }
+ /// @brief Return a reference to the node over which this iterator is iterating.
+ /// @throw ValueError if there is no parent node.
+ NodeT& parent() const
+ {
+ if (!mParentNode) OPENVDB_THROW(ValueError, "iterator references a null node");
+ return *mParentNode;
+ }
+
+ /// Return this iterator's position as an index into the parent node's table.
+ Index offset() const { return mMaskIter.offset(); }
+
+ /// Identical to offset
+ Index pos() const { return mMaskIter.offset(); }
+
+ /// Return @c true if this iterator is not yet exhausted.
+ bool test() const { return mMaskIter.test(); }
+ /// Return @c true if this iterator is not yet exhausted.
+ operator bool() const { return this->test(); }
+
+ /// Advance to the next item in the parent node's table.
+ bool next() { return mMaskIter.next(); }
+ /// Advance to the next item in the parent node's table.
+ void increment() { mMaskIter.increment(); }
+ /// Advance to the next item in the parent node's table.
+ IteratorBase& operator++() { this->increment(); return *this; }
+ /// Advance @a n items in the parent node's table.
+ void increment(Index n) { mMaskIter.increment(n); }
+
+ /// @brief Return @c true if this iterator is pointing to an active value.
+ /// Return @c false if it is pointing to either an inactive value or a child node.
+ bool isValueOn() const { return parent().isValueMaskOn(this->pos()); }
+ /// @brief If this iterator is pointing to a value, set the value's active state.
+ /// Otherwise, do nothing.
+ void setValueOn(bool on = true) const { parent().setValueMask(this->pos(), on); }
+ /// @brief If this iterator is pointing to a value, mark the value as inactive.
+ /// @details If this iterator is pointing to a child node, then the current item
+ /// in the parent node's table is required to be inactive. In that case,
+ /// this method has no effect.
+ void setValueOff() const { parent().mValueMask.setOff(this->pos()); }
+
+ /// Return the coordinates of the item to which this iterator is pointing.
+ Coord getCoord() const { return parent().offset2globalCoord(this->pos()); }
+ /// Return in @a xyz the coordinates of the item to which this iterator is pointing.
+ void getCoord(Coord& xyz) const { xyz = this->getCoord(); }
+
+private:
+ /// @note This parent node pointer is mutable, because setValueOn() and
+ /// setValueOff(), though const, need to call non-const methods on the parent.
+ /// There is a distinction between a const iterator (e.g., const ValueOnIter),
+ /// which is an iterator that can't be incremented, and an iterator over
+ /// a const node (e.g., ValueOnCIter), which might be const or non-const itself
+ /// but can't call non-const methods like setValue() on the node.
+ mutable NodeT* mParentNode;
+ MaskIterT mMaskIter;
+}; // class IteratorBase
+
+
+////////////////////////////////////////
+
+
+/// @brief Base class for sparse iterators over internal and leaf nodes
+template<
+ typename MaskIterT, // mask iterator type (OnIterator, OffIterator, etc.)
+ typename IterT, // SparseIteratorBase subclass (the "Curiously Recurring Template Pattern")
+ typename NodeT, // type of node over which to iterate
+ typename ItemT> // type of value to which this iterator points
+struct SparseIteratorBase: public IteratorBase<MaskIterT, NodeT>
+{
+ typedef NodeT NodeType;
+ typedef ItemT ValueType;
+ typedef typename boost::remove_const<NodeT>::type NonConstNodeType;
+ typedef typename boost::remove_const<ItemT>::type NonConstValueType;
+ static const bool IsSparseIterator = true, IsDenseIterator = false;
+
+ SparseIteratorBase() {}
+ SparseIteratorBase(const MaskIterT& iter, NodeT* parent):
+ IteratorBase<MaskIterT, NodeT>(iter, parent) {}
+
+ /// @brief Return the item at the given index in the parent node's table.
+ /// @note All subclasses must implement this accessor.
+ ItemT& getItem(Index) const;
+ /// @brief Set the value of the item at the given index in the parent node's table.
+ /// @note All non-const iterator subclasses must implement this accessor.
+ void setItem(Index, const ItemT&) const;
+
+ /// Return a reference to the item to which this iterator is pointing.
+ ItemT& operator*() const { return this->getValue(); }
+ /// Return a pointer to the item to which this iterator is pointing.
+ ItemT* operator->() const { return &(this->operator*()); }
+
+ /// Return the item to which this iterator is pointing.
+ ItemT& getValue() const
+ {
+ return static_cast<const IterT*>(this)->getItem(this->pos()); // static polymorphism
+ }
+ /// @brief Set the value of the item to which this iterator is pointing.
+ /// (Not valid for const iterators.)
+ void setValue(const ItemT& value) const
+ {
+ static_cast<const IterT*>(this)->setItem(this->pos(), value); // static polymorphism
+ }
+}; // class SparseIteratorBase
+
+
+////////////////////////////////////////
+
+
+/// @brief Base class for dense iterators over internal and leaf nodes
+/// @note Dense iterators have no @c %operator*() or @c %operator->(),
+/// because their return type would have to vary depending on whether
+/// the iterator is pointing to a value or a child node.
+template<
+ typename MaskIterT, // mask iterator type (typically a DenseIterator)
+ typename IterT, // DenseIteratorBase subclass (the "Curiously Recurring Template Pattern")
+ typename NodeT, // type of node over which to iterate
+ typename SetItemT, // type of set value (ChildNodeType, for non-leaf nodes)
+ typename UnsetItemT> // type of unset value (ValueType, usually)
+struct DenseIteratorBase: public IteratorBase<MaskIterT, NodeT>
+{
+ typedef NodeT NodeType;
+ typedef UnsetItemT ValueType;
+ typedef SetItemT ChildNodeType;
+ typedef typename boost::remove_const<NodeT>::type NonConstNodeType;
+ typedef typename boost::remove_const<UnsetItemT>::type NonConstValueType;
+ typedef typename boost::remove_const<SetItemT>::type NonConstChildNodeType;
+ static const bool IsSparseIterator = false, IsDenseIterator = true;
+
+ DenseIteratorBase() {}
+ DenseIteratorBase(const MaskIterT& iter, NodeT* parent):
+ IteratorBase<MaskIterT, NodeT>(iter, parent) {}
+
+ /// @brief Return @c true if the item at the given index in the parent node's table
+ /// is a set value and return either the set value in @a child or the unset value
+ /// in @a value.
+ /// @note All subclasses must implement this accessor.
+ bool getItem(Index, SetItemT*& child, NonConstValueType& value) const;
+ /// @brief Set the value of the item at the given index in the parent node's table.
+ /// @note All non-const iterator subclasses must implement this accessor.
+ void setItem(Index, SetItemT*) const;
+ /// @brief "Unset" the value of the item at the given index in the parent node's table.
+ /// @note All non-const iterator subclasses must implement this accessor.
+ void unsetItem(Index, const UnsetItemT&) const;
+
+ /// Return @c true if this iterator is pointing to a child node.
+ bool isChildNode() const { return this->parent().isChildMaskOn(this->pos()); }
+
+ /// @brief If this iterator is pointing to a child node, return a pointer to the node.
+ /// Otherwise, return NULL and, in @a value, the value to which this iterator is pointing.
+ SetItemT* probeChild(NonConstValueType& value) const
+ {
+ SetItemT* child = NULL;
+ static_cast<const IterT*>(this)->getItem(this->pos(), child, value); // static polymorphism
+ return child;
+ }
+ /// @brief If this iterator is pointing to a child node, return @c true and return
+ /// a pointer to the child node in @a child. Otherwise, return @c false and return
+ /// the value to which this iterator is pointing in @a value.
+ bool probeChild(SetItemT*& child, NonConstValueType& value) const
+ {
+ child = probeChild(value);
+ return (child != NULL);
+ }
+
+ /// @brief Return @c true if this iterator is pointing to a value and return
+ /// the value in @a value. Otherwise, return @c false.
+ bool probeValue(NonConstValueType& value) const
+ {
+ SetItemT* child = NULL;
+ const bool isChild = static_cast<const IterT*>(this)-> // static polymorphism
+ getItem(this->pos(), child, value);
+ return !isChild;
+ }
+
+ /// @brief Replace with the given child node the item in the parent node's table
+ /// to which this iterator is pointing.
+ void setChild(SetItemT* child) const
+ {
+ static_cast<const IterT*>(this)->setItem(this->pos(), child); // static polymorphism
+ }
+
+ /// @brief Replace with the given value the item in the parent node's table
+ /// to which this iterator is pointing.
+ void setValue(const UnsetItemT& value) const
+ {
+ static_cast<const IterT*>(this)->unsetItem(this->pos(), value); // static polymorphism
+ }
+}; // struct DenseIteratorBase
+
+} // namespace tree
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_TREE_ITERATOR_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/tree/LeafManager.h b/extern/openvdb/internal/openvdb/tree/LeafManager.h
new file mode 100644
index 00000000000..2024951d6d4
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/tree/LeafManager.h
@@ -0,0 +1,522 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+//
+/// @file LeafManager.h
+///
+/// A LeafManager manages a linear array of pointers to a given tree's
+/// leaf nodes, as well as optional auxiliary buffers (one or more per leaf)
+/// that can be swapped with the leaf nodes' voxel data buffers.
+/// The leaf array is useful for multithreaded computations over
+/// leaf voxels in a tree with static topology but varying voxel values.
+/// The auxiliary buffers are convenient for temporal integration.
+/// Efficient methods are provided for multithreaded swapping and synching
+/// (i.e., copying the contents) of these buffers.
+
+#ifndef OPENVDB_TREE_LEAFMANAGER_HAS_BEEN_INCLUDED
+#define OPENVDB_TREE_LEAFMANAGER_HAS_BEEN_INCLUDED
+
+#include <boost/shared_ptr.hpp>
+#include <boost/bind.hpp>
+#include <boost/function.hpp>
+#include <tbb/blocked_range.h>
+#include <tbb/parallel_for.h>
+#include <openvdb/Types.h>
+#include "TreeIterator.h" // for CopyConstness
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace tree {
+
+namespace leafmgr {
+
+//@{
+/// Useful traits for Tree types
+template<typename TreeT> struct TreeTraits {
+ static const bool IsConstTree = false;
+ typedef typename TreeT::LeafIter LeafIterType;
+};
+template<typename TreeT> struct TreeTraits<const TreeT> {
+ static const bool IsConstTree = true;
+ typedef typename TreeT::LeafCIter LeafIterType;
+};
+//@}
+
+} // namespace leafmgr
+
+
+/// This helper class implements LeafManager methods that need to be
+/// specialized for const vs. non-const trees.
+template<typename ManagerT>
+struct LeafManagerImpl
+{
+ typedef typename ManagerT::RangeType RangeT;
+ typedef typename ManagerT::LeafType LeafT;
+ typedef typename ManagerT::BufferType BufT;
+
+ static inline void doSwapLeafBuffer(const RangeT& r, size_t auxBufferIdx,
+ LeafT** leafs, BufT* bufs, size_t bufsPerLeaf)
+ {
+ for (size_t n = r.begin(), m = r.end(), N = bufsPerLeaf; n != m; ++n) {
+ leafs[n]->swap(bufs[n * N + auxBufferIdx]);
+ }
+ }
+};
+
+
+/// @brief This class manages a linear array of pointers to a given tree's
+/// leaf nodes, as well as optional auxiliary buffers (one or more per leaf)
+/// that can be swapped with the leaf nodes' voxel data buffers.
+/// @details The leaf array is useful for multithreaded computations over
+/// leaf voxels in a tree with static topology but varying voxel values.
+/// The auxiliary buffers are convenient for temporal integration.
+/// Efficient methods are provided for multithreaded swapping and sync'ing
+/// (i.e., copying the contents) of these buffers.
+///
+/// @note Buffer index 0 denotes a leaf node's internal voxel data buffer.
+/// Any auxiliary buffers are indexed starting from one.
+template<typename TreeT>
+class LeafManager
+{
+public:
+ typedef TreeT TreeType;
+ typedef typename TreeType::LeafNodeType NonConstLeafType;
+ typedef typename CopyConstness<TreeType, NonConstLeafType>::Type LeafType;
+ typedef typename leafmgr::TreeTraits<TreeT>::LeafIterType LeafIterType;
+ typedef typename LeafType::Buffer NonConstBufferType;
+ typedef typename CopyConstness<TreeType, NonConstBufferType>::Type BufferType;
+ typedef tbb::blocked_range<size_t> RangeType;//leaf index range
+
+ static const bool IsConstTree = leafmgr::TreeTraits<TreeT>::IsConstTree;
+
+ class LeafRange
+ {
+ public:
+ class Iterator
+ {
+ public:
+ Iterator(const LeafRange& range, size_t pos): mRange(range), mPos(pos)
+ {
+ assert(this->isValid());
+ }
+ /// Advance to the next leaf node.
+ Iterator& operator++() { ++mPos; return *this; }
+ /// Return a reference to the leaf node to which this iterator is pointing.
+ LeafType& operator*() const { return mRange.mLeafManager.leaf(mPos); }
+ /// Return a pointer to the leaf node to which this iterator is pointing.
+ LeafType* operator->() const { return &(this->operator*()); }
+ /// @brief Return the nth buffer for the leaf node to which this iterator is pointing,
+ /// where n = @a bufferIdx and n = 0 corresponds to the leaf node's own buffer.
+ BufferType& buffer(size_t bufferIdx)
+ {
+ return mRange.mLeafManager.getBuffer(mPos, bufferIdx);
+ }
+ /// Return the index into the leaf array of the current leaf node.
+ size_t pos() const { return mPos; }
+ bool isValid() const { return mPos>=mRange.mBegin && mPos<=mRange.mEnd; }
+ /// Return @c true if this iterator is not yet exhausted.
+ bool test() const { return mPos < mRange.mEnd; }
+ /// Return @c true if this iterator is not yet exhausted.
+ operator bool() const { return this->test(); }
+ /// Return @c true if this iterator is exhausted.
+ bool empty() const { return !this->test(); }
+ //bool operator<( const Iterator& other ) const { return mPos < other.mPos; }
+ void operator=( const Iterator& other) { mRange = other.mRange; mPos = other.mPos; }
+ bool operator!=(const Iterator& other) const
+ {
+ return (mPos != other.mPos) || (&mRange != &other.mRange);
+ }
+ bool operator==(const Iterator& other) const { return !(*this != other); }
+ const LeafRange& leafRange() const { return mRange; }
+
+ private:
+ const LeafRange& mRange;
+ size_t mPos;
+ };// end Iterator
+
+ LeafRange(size_t begin, size_t end, const LeafManager& leafManager, size_t grainSize=1):
+ mEnd(end), mBegin(begin), mGrainSize(grainSize), mLeafManager(leafManager) {}
+
+ Iterator begin() const {return Iterator(*this, mBegin);}
+
+ Iterator end() const {return Iterator(*this, mEnd);}
+
+ size_t size() const { return mEnd - mBegin; }
+
+ size_t grainsize() const { return mGrainSize; }
+
+ const LeafManager& leafManager() const { return mLeafManager; }
+
+ bool empty() const {return !(mBegin < mEnd);}
+
+ bool is_divisible() const {return mGrainSize < this->size();}
+
+ LeafRange(LeafRange& r, tbb::split):
+ mEnd(r.mEnd), mBegin(doSplit(r)), mGrainSize(r.mGrainSize),
+ mLeafManager(r.mLeafManager) {}
+
+ private:
+ size_t mEnd, mBegin, mGrainSize;
+ const LeafManager& mLeafManager;
+
+ static size_t doSplit(LeafRange& r)
+ {
+ assert(r.is_divisible());
+ size_t middle = r.mBegin + (r.mEnd - r.mBegin) / 2u;
+ r.mEnd = middle;
+ return middle;
+ }
+ };// end of LeafRange
+
+ /// @brief Constructor from a tree reference and an auxiliary buffer count
+ /// (default is no auxiliary buffers)
+ LeafManager(TreeType& tree, size_t auxBuffersPerLeaf=0, bool serial=false):
+ mTree(&tree),
+ mLeafCount(0),
+ mAuxBufferCount(0),
+ mAuxBuffersPerLeaf(auxBuffersPerLeaf),
+ mLeafs(NULL),
+ mAuxBuffers(NULL),
+ mTask(0),
+ mIsMaster(true)
+ {
+ this->rebuild(serial);
+ }
+
+ /// Shallow copy constructor called by tbb::parallel_for() threads
+ ///
+ /// @note This should never get called directly
+ LeafManager(const LeafManager& other):
+ mTree(other.mTree),
+ mLeafCount(other.mLeafCount),
+ mAuxBufferCount(other.mAuxBufferCount),
+ mAuxBuffersPerLeaf(other.mAuxBuffersPerLeaf),
+ mLeafs(other.mLeafs),
+ mAuxBuffers(other.mAuxBuffers),
+ mTask(other.mTask),
+ mIsMaster(false)
+ {
+ }
+
+ virtual ~LeafManager()
+ {
+ if (mIsMaster) {
+ delete [] mLeafs;
+ delete [] mAuxBuffers;
+ }
+ }
+
+ /// @brief (Re)initialize by resizing (if necessary) and repopulating the leaf array
+ /// and by deleting existing auxiliary buffers and allocating new ones.
+ /// @details Call this method if the tree's topology, and therefore the number
+ /// of leaf nodes, changes. New auxiliary buffers are initialized with copies
+ /// of corresponding leaf node buffers.
+ void rebuild(bool serial=false)
+ {
+ this->initLeafArray();
+ this->initAuxBuffers(serial);
+ }
+ //@{
+ /// Repopulate the leaf array and delete and reallocate auxiliary buffers.
+ void rebuild(size_t auxBuffersPerLeaf, bool serial=false)
+ {
+ mAuxBuffersPerLeaf = auxBuffersPerLeaf;
+ this->rebuild(serial);
+ }
+ void rebuild(TreeType& tree, bool serial=false)
+ {
+ mTree = &tree;
+ this->rebuild(serial);
+ }
+ void rebuild(TreeType& tree, size_t auxBuffersPerLeaf, bool serial=false)
+ {
+ mTree = &tree;
+ mAuxBuffersPerLeaf = auxBuffersPerLeaf;
+ this->rebuild(serial);
+ }
+ //@}
+ /// @brief Change the number of auxiliary buffers.
+ /// @details If auxBuffersPerLeaf is 0, all existing auxiliary buffers are deleted.
+ /// New auxiliary buffers are initialized with copies of corresponding leaf node buffers.
+ /// This method does not rebuild the leaf array.
+ void rebuildAuxBuffers(size_t auxBuffersPerLeaf, bool serial=false)
+ {
+ mAuxBuffersPerLeaf = auxBuffersPerLeaf;
+ this->initAuxBuffers(serial);
+ }
+ /// @brief Remove the auxiliary buffers, but don't rebuild the leaf array.
+ void removeAuxBuffers() { this->rebuildAuxBuffers(0); }
+
+ /// @brief Remove the auxiliary buffers and rebuild the leaf array.
+ void rebuildLeafArray()
+ {
+ this->removeAuxBuffers();
+ this->initLeafArray();
+ }
+ /// @deprecated Use rebuildLeafArray() instead.
+ OPENVDB_DEPRECATED void rebuildLeafs() { this->rebuildLeafArray(); }
+
+ /// Return the total number of allocated auxiliary buffers.
+ size_t auxBufferCount() const { return mAuxBufferCount; }
+ /// Return the number of auxiliary buffers per leaf node.
+ size_t auxBuffersPerLeaf() const { return mAuxBuffersPerLeaf; }
+
+ /// Return the number of leaf nodes.
+ size_t leafCount() const { return mLeafCount; }
+
+ /// Return the tree associated with this manager.
+ TreeType& tree() { return *mTree; }
+
+ /// Return @c true if the tree associated with this manager is immutable.
+ bool isConstTree() const { return this->IsConstTree; }
+
+ /// @brief Return a pointer to the leaf node at index @a leafIdx in the array.
+ /// @note For performance reasons no range check is performed (other than an assertion)!
+ LeafType& leaf(size_t leafIdx) const { assert(leafIdx<mLeafCount); return *mLeafs[leafIdx]; }
+
+ /// @brief Return the leaf or auxiliary buffer for the leaf node at index @a leafIdx.
+ /// If @a bufferIdx is zero, return the leaf buffer, otherwise return the nth
+ /// auxiliary buffer, where n = @a bufferIdx - 1.
+ ///
+ /// @note For performance reasons no range checks are performed on the inputs
+ /// (other than assertions)! Since auxiliary buffers, unlike leaf buffers,
+ /// might not exist, be especially careful when specifying the @a bufferIdx.
+ /// @note For const trees, this method always returns a reference to a const buffer.
+ /// It is safe to @c const_cast and modify any auxiliary buffer (@a bufferIdx > 0),
+ /// but it is not safe to modify the leaf buffer (@a bufferIdx = 0).
+ BufferType& getBuffer(size_t leafIdx, size_t bufferIdx) const
+ {
+ assert(leafIdx < mLeafCount);
+ assert(bufferIdx == 0 || bufferIdx - 1 < mAuxBuffersPerLeaf);
+ return bufferIdx == 0 ? mLeafs[leafIdx]->buffer()
+ : mAuxBuffers[leafIdx * mAuxBuffersPerLeaf + bufferIdx - 1];
+ }
+
+ /// @brief Return a @c tbb::blocked_range of leaf array indices.
+ ///
+ /// @note Consider using leafRange() instead, which provides access methods
+ /// to leaf nodes and buffers.
+ RangeType getRange(size_t grainsize = 1) const { return RangeType(0, mLeafCount, grainsize); }
+
+ /// Return a TBB-compatible LeafRange.
+ LeafRange leafRange(size_t grainsize = 1) const
+ {
+ return LeafRange(0, mLeafCount, *this, grainsize);
+ }
+
+ /// @brief Swap each leaf node's buffer with the nth corresponding auxiliary buffer,
+ /// where n = @a bufferIdx.
+ /// @return @c true if the swap was successful
+ /// @param bufferIdx index of the buffer that will be swapped with
+ /// the corresponding leaf node buffer
+ /// @param serial if false, swap buffers in parallel using multiple threads.
+ /// @note Recall that the indexing of auxiliary buffers is 1-based, since
+ /// buffer index 0 denotes the leaf node buffer. So buffer index 1 denotes
+ /// the first auxiliary buffer.
+ bool swapLeafBuffer(size_t bufferIdx, bool serial = false)
+ {
+ if (bufferIdx == 0 || bufferIdx > mAuxBuffersPerLeaf || this->isConstTree()) return false;
+ mTask = boost::bind(&LeafManager::doSwapLeafBuffer, _1, _2, bufferIdx - 1);
+ this->cook(serial ? 0 : 512);
+ return true;//success
+ }
+ /// @brief Swap any two buffers for each leaf node.
+ /// @note Recall that the indexing of auxiliary buffers is 1-based, since
+ /// buffer index 0 denotes the leaf node buffer. So buffer index 1 denotes
+ /// the first auxiliary buffer.
+ bool swapBuffer(size_t bufferIdx1, size_t bufferIdx2, bool serial = false)
+ {
+ const size_t b1 = std::min(bufferIdx1, bufferIdx2);
+ const size_t b2 = std::max(bufferIdx1, bufferIdx2);
+ if (b1 == b2 || b2 > mAuxBuffersPerLeaf) return false;
+ if (b1 == 0) {
+ if (this->isConstTree()) return false;
+ mTask = boost::bind(&LeafManager::doSwapLeafBuffer, _1, _2, b2-1);
+ } else {
+ mTask = boost::bind(&LeafManager::doSwapAuxBuffer, _1, _2, b1-1, b2-1);
+ }
+ this->cook(serial ? 0 : 512);
+ return true;//success
+ }
+
+ /// @brief Sync up the specified auxiliary buffer with the corresponding leaf node buffer.
+ /// @return @c true if the sync was successful
+ /// @param bufferIdx index of the buffer that will contain a
+ /// copy of the corresponding leaf node buffer
+ /// @param serial if false, sync buffers in parallel using multiple threads.
+ /// @note Recall that the indexing of auxiliary buffers is 1-based, since
+ /// buffer index 0 denotes the leaf node buffer. So buffer index 1 denotes
+ /// the first auxiliary buffer.
+ bool syncAuxBuffer(size_t bufferIdx, bool serial = false)
+ {
+ if (bufferIdx == 0 || bufferIdx > mAuxBuffersPerLeaf) return false;
+ mTask = boost::bind(&LeafManager::doSyncAuxBuffer, _1, _2, bufferIdx - 1);
+ this->cook(serial ? 0 : 64);
+ return true;//success
+ }
+
+ /// @brief Sync up all auxiliary buffers with their corresponding leaf node buffers.
+ /// @return true if the sync was successful
+ /// @param serial if false, sync buffers in parallel using multiple threads.
+ bool syncAllBuffers(bool serial = false)
+ {
+ switch (mAuxBuffersPerLeaf) {
+ case 0: return false;//nothing to do
+ case 1: mTask = boost::bind(&LeafManager::doSyncAllBuffers1, _1, _2); break;
+ case 2: mTask = boost::bind(&LeafManager::doSyncAllBuffers2, _1, _2); break;
+ default: mTask = boost::bind(&LeafManager::doSyncAllBuffersN, _1, _2); break;
+ }
+ this->cook(serial ? 0 : 64);
+ return true;//success
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////
+ // All methods below are for internal use only and should never be called directly
+
+ /// Used internally by tbb::parallel_for() - never call it directly!
+ void operator()(const RangeType& r) const
+ {
+ if (mTask) mTask(const_cast<LeafManager*>(this), r);
+ else OPENVDB_THROW(ValueError, "task is undefined");
+ }
+
+private:
+ void initLeafArray()
+ {
+ const size_t leafCount = mTree->leafCount();
+ if (leafCount != mLeafCount) {
+ delete [] mLeafs;
+ mLeafs = (leafCount == 0) ? NULL : new LeafType*[leafCount];
+ mLeafCount = leafCount;
+ }
+ LeafIterType iter = mTree->beginLeaf();
+ for (size_t n = 0; n != leafCount; ++n, ++iter) mLeafs[n] = iter.getLeaf();
+ }
+
+ void initAuxBuffers(bool serial)
+ {
+ const size_t auxBufferCount = mLeafCount * mAuxBuffersPerLeaf;
+ if (auxBufferCount != mAuxBufferCount) {
+ delete [] mAuxBuffers;
+ mAuxBuffers = (auxBufferCount == 0) ? NULL : new NonConstBufferType[auxBufferCount];
+ mAuxBufferCount = auxBufferCount;
+ }
+ this->syncAllBuffers(serial);
+ }
+
+ void cook(size_t grainsize)
+ {
+ if (grainsize>0) {
+ tbb::parallel_for(this->getRange(grainsize), *this);
+ } else {
+ (*this)(this->getRange());
+ }
+ }
+
+ void doSwapLeafBuffer(const RangeType& r, size_t auxBufferIdx)
+ {
+ LeafManagerImpl<LeafManager>::doSwapLeafBuffer(
+ r, auxBufferIdx, mLeafs, mAuxBuffers, mAuxBuffersPerLeaf);
+ }
+
+ void doSwapAuxBuffer(const RangeType& r, size_t auxBufferIdx1, size_t auxBufferIdx2)
+ {
+ for (size_t N = mAuxBuffersPerLeaf, n = N*r.begin(), m = N*r.end(); n != m; n+=N) {
+ mAuxBuffers[n + auxBufferIdx1].swap(mAuxBuffers[n + auxBufferIdx2]);
+ }
+ }
+
+ void doSyncAuxBuffer(const RangeType& r, size_t auxBufferIdx)
+ {
+ for (size_t n = r.begin(), m = r.end(), N = mAuxBuffersPerLeaf; n != m; ++n) {
+ mAuxBuffers[n*N + auxBufferIdx] = mLeafs[n]->buffer();
+ }
+ }
+
+ void doSyncAllBuffers1(const RangeType& r)
+ {
+ for (size_t n = r.begin(), m = r.end(); n != m; ++n) {
+ mAuxBuffers[n] = mLeafs[n]->buffer();
+ }
+ }
+
+ void doSyncAllBuffers2(const RangeType& r)
+ {
+ for (size_t n = r.begin(), m = r.end(); n != m; ++n) {
+ const BufferType& leafBuffer = mLeafs[n]->buffer();
+ mAuxBuffers[2*n ] = leafBuffer;
+ mAuxBuffers[2*n+1] = leafBuffer;
+ }
+ }
+
+ void doSyncAllBuffersN(const RangeType& r)
+ {
+ for (size_t n = r.begin(), m = r.end(), N = mAuxBuffersPerLeaf; n != m; ++n) {
+ const BufferType& leafBuffer = mLeafs[n]->buffer();
+ for (size_t i=n*N, j=i+N; i!=j; ++i) mAuxBuffers[i] = leafBuffer;
+ }
+ }
+
+ typedef typename boost::function<void (LeafManager*, const RangeType&)> FuncType;
+
+ TreeType* mTree;
+ size_t mLeafCount, mAuxBufferCount, mAuxBuffersPerLeaf;
+ LeafType** mLeafs;//array of LeafNode pointers
+ NonConstBufferType* mAuxBuffers;//array of auxiliary buffers
+ FuncType mTask;
+ const bool mIsMaster;
+};//end of LeafManager class
+
+
+// Partial specializations of LeafManager methods for const trees
+template<typename TreeT>
+struct LeafManagerImpl<LeafManager<const TreeT> >
+{
+ typedef LeafManager<const TreeT> ManagerT;
+ typedef typename ManagerT::RangeType RangeT;
+ typedef typename ManagerT::LeafType LeafT;
+ typedef typename ManagerT::BufferType BufT;
+
+ static inline void doSwapLeafBuffer(const RangeT&, size_t /*auxBufferIdx*/,
+ LeafT**, BufT*, size_t /*bufsPerLeaf*/)
+ {
+ // Buffers can't be swapped into const trees.
+ }
+};
+
+} // namespace tree
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_TREE_LEAFMANAGER_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/tree/LeafNode.h b/extern/openvdb/internal/openvdb/tree/LeafNode.h
new file mode 100644
index 00000000000..5487d62d7c4
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/tree/LeafNode.h
@@ -0,0 +1,1659 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+
+#ifndef OPENVDB_TREE_LEAFNODE_HAS_BEEN_INCLUDED
+#define OPENVDB_TREE_LEAFNODE_HAS_BEEN_INCLUDED
+
+#include <iostream>
+#include <algorithm> // for std::swap
+#include <cstring> // for std::memcpy()
+#include <boost/shared_ptr.hpp>
+#include <boost/static_assert.hpp>
+#include <boost/bind.hpp>
+#include <tbb/blocked_range.h>
+#include <tbb/parallel_for.h>
+#include <openvdb/Types.h>
+#include <openvdb/util/NodeMasks.h>
+#include <openvdb/io/Compression.h> // for io::readData(), etc.
+#include "Iterator.h"
+#include "Util.h"
+
+
+class TestLeaf;
+template<typename> class TestLeafIO;
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace tree {
+
+/// @brief Templated block class to hold specific data types and a fixed
+/// number of values determined by Log2Dim. The actual coordinate
+/// dimension of the block is 2^Log2Dim, i.e. Log2Dim=3 corresponds to
+/// a LeafNode that spans a 8^3 block.
+template<typename T, Index Log2Dim>
+class LeafNode
+{
+public:
+ typedef T ValueType;
+ typedef LeafNode<ValueType, Log2Dim> LeafNodeType;
+ typedef boost::shared_ptr<LeafNode> Ptr;
+ typedef util::NodeMask<Log2Dim> NodeMaskType;
+
+ static const Index
+ LOG2DIM = Log2Dim, // needed by parent nodes
+ TOTAL = Log2Dim, // needed by parent nodes
+ DIM = 1 << TOTAL, // dimension along one coordinate direction
+ NUM_VALUES = 1 << 3 * Log2Dim,
+ NUM_VOXELS = NUM_VALUES, // total number of voxels represented by this node
+ SIZE = NUM_VALUES,
+ LEVEL = 0; // level 0 = leaf
+
+ /// @brief ValueConverter<T>::Type is the type of a LeafNode having the same
+ /// child hierarchy and dimensions as this node but a different value type, T.
+ template<typename OtherValueType>
+ struct ValueConverter {
+ typedef LeafNode<OtherValueType, Log2Dim> Type;
+ };
+
+ /// @brief Stores the actual values in the LeafNode. Its dimension
+ /// it fixed to 2^(3*Log2Dim)
+ class Buffer
+ {
+ public:
+ /// @brief Empty default constructor
+ Buffer(): mData(new ValueType[SIZE]) {}
+ /// @brief Constructs a buffer populated with the specified value
+ Buffer(const ValueType& val) : mData(new ValueType[SIZE]) { this->fill(val); }
+ /// @brief Copy constructor
+ Buffer(const Buffer& other) : mData(new ValueType[SIZE]) { *this = other; }
+ /// @brief Destructor
+ ~Buffer() { delete [] mData; }
+ /// @brief Populates the buffer with a constant value
+ void fill(const ValueType& val)
+ {
+ ValueType* target = mData;
+ Index n = SIZE;
+ while (n--) *target++ = val;
+ }
+ /// Return a const reference to the i'th element of the Buffer
+ const ValueType& getValue(Index i) const { assert(i < SIZE); return mData[i]; }
+ /// Return a const reference to the i'th element of the Buffer
+ const ValueType& operator[](Index i) const { return this->getValue(i); }
+ /// Set the i'th value of the Buffer to the specified value
+ void setValue(Index i, const ValueType& val) { assert(i < SIZE); mData[i] = val; }
+ /// Assigns the values in the other Buffer to this Buffer
+ Buffer& operator=(const Buffer& other)
+ {
+ ValueType* target = mData;
+ const ValueType* source = other.mData;
+ Index n = SIZE;
+ while (n--) *target++ = *source++;
+ return *this;
+ }
+ /// Return true if the values in the other buffer exactly
+ /// equates the values in this buffer
+ bool operator==(const Buffer& other) const
+ {
+ const ValueType* target = mData;
+ const ValueType* source = other.mData;
+ Index n = SIZE;
+ while (n && math::isExactlyEqual(*target++, *source++)) --n;
+ return n == 0;
+ }
+ /// Return true if any of the values in the other buffer do
+ /// not exactly equate the values in this buffer
+ bool operator!=(const Buffer& other) const { return !(other == *this); }
+ /// Replace the values in this Buffer with the values in the other Buffer
+ void swap(Buffer& other)
+ {
+ ValueType* tmp = mData;
+ mData = other.mData;
+ other.mData = tmp;
+ }
+ /// Return the memory-footprint of this Buffer in units of bytes
+ static Index memUsage() { return sizeof(ValueType*) + SIZE * sizeof(ValueType); }
+ /// Return the number of values represented in this Buffer
+ static Index size() { return SIZE; }
+
+ private:
+ /// This direct access method is private since it makes
+ /// assumptions about the implementations of the memory layout.
+ ValueType& operator[](Index i) { assert(i < SIZE); return mData[i]; }
+
+ friend class ::TestLeaf;
+ // Allow the parent LeafNode to access this Buffer's data pointer.
+ friend class LeafNode;
+
+ ValueType* mData;
+ }; // class Buffer
+
+
+ /// Default constructor
+ LeafNode();
+
+ /// @brief Constructor
+ /// @param coords the grid index coordinates of a voxel
+ /// @param value a value with which to fill the buffer
+ /// @param active the active state to which to initialize all voxels
+ explicit LeafNode(const Coord& coords,
+ const ValueType& value = zeroVal<ValueType>(),
+ bool active = false);
+
+ /// Deep copy constructor
+ LeafNode(const LeafNode&);
+
+ /// Topology copy constructor
+ template<typename OtherValueType>
+ LeafNode(const LeafNode<OtherValueType, Log2Dim>& other,
+ const ValueType& offValue, const ValueType& onValue, TopologyCopy);
+
+ /// Topology copy constructor
+ template<typename OtherValueType>
+ LeafNode(const LeafNode<OtherValueType, Log2Dim>& other,
+ const ValueType& background, TopologyCopy);
+
+ /// Destructor.
+ ~LeafNode();
+
+ //
+ // Statistics
+ //
+ /// Return log2 of the dimension of this LeafNode, e.g. 3 if dimensions are 8^3
+ static Index log2dim() { return Log2Dim; }
+ /// Return the number of voxels in each coordinate dimension.
+ static Index dim() { return DIM; }
+ /// Return the total number of voxels represented by this LeafNode
+ static Index size() { return SIZE; }
+ /// Return the total number of voxels represented by this LeafNode
+ static Index numValues() { return SIZE; }
+ /// Return the level of this node, which by definition is zero for LeafNodes
+ static Index getLevel() { return LEVEL; }
+ /// Append the Log2Dim of this LeafNode to the specified vector
+ static void getNodeLog2Dims(std::vector<Index>& dims) { dims.push_back(Log2Dim); }
+ /// Return the dimension of child nodes of this LeafNode, which is one for voxels.
+ static Index getChildDim() { return 1; }
+ /// Return the leaf count for this node, which is one.
+ static Index32 leafCount() { return 1; }
+ /// Return the non-leaf count for this node, which is zero.
+ static Index32 nonLeafCount() { return 0; }
+
+ /// Return the number of voxels marked On.
+ Index64 onVoxelCount() const { return mValueMask.countOn(); }
+ /// Return the number of voxels marked Off.
+ Index64 offVoxelCount() const { return mValueMask.countOff(); }
+ Index64 onLeafVoxelCount() const { return onVoxelCount(); }
+ Index64 offLeafVoxelCount() const { return offVoxelCount(); }
+ /// Return @c true if this node has no active voxels.
+ bool isEmpty() const { return mValueMask.isOff(); }
+ /// Return @c true if this node contains only active voxels.
+ bool isDense() const { return mValueMask.isOn(); }
+
+ /// Return the memory in bytes occupied by this node.
+ Index64 memUsage() const;
+
+ /// Expand the given bounding box so that it includes this leaf node's active voxels.
+ void evalActiveVoxelBoundingBox(CoordBBox&) const;
+
+ /// @brief Return the bounding box of this node, i.e., the full index space
+ /// spanned by this leaf node.
+ CoordBBox getNodeBoundingBox() const { return CoordBBox::createCube(mOrigin, DIM); }
+
+ /// Set the grid index coordinates of this node's local origin.
+ void setOrigin(const Coord& origin) { mOrigin = origin; }
+ /// Get the grid index coordinates of this node's local origin.
+ const Coord& origin() const { return mOrigin; }
+ const Coord& getOrigin() const { return mOrigin; }
+ void getOrigin(Coord& origin) const { origin = mOrigin; }
+ void getOrigin(Int32& x, Int32& y, Int32& z) const { mOrigin.asXYZ(x, y, z); }
+
+ /// Return the linear table offset of the given coordinates.
+ static Index coord2offset(const Coord& xyz);
+ /// Get the local coordinates for a linear table offset.
+ static void offset2coord(Index n, Coord &xyz);
+ /// Get the global coordinates for a linear table offset.
+ Coord offset2globalCoord(Index n) const;
+
+ /// Return a string representation of this node.
+ std::string str() const;
+
+ /// @brief Return @c true if the given node (which may have a different @c ValueType
+ /// than this node) has the same active value topology as this node.
+ template<typename OtherType, Index OtherLog2Dim>
+ bool hasSameTopology(const LeafNode<OtherType, OtherLog2Dim>* other) const;
+
+ /// Check for buffer, state and origin equivalence.
+ bool operator==(const LeafNode& other) const;
+ bool operator!=(const LeafNode& other) const { return !(other == *this); }
+
+protected:
+ typedef typename NodeMaskType::OnIterator MaskOnIterator;
+ typedef typename NodeMaskType::OffIterator MaskOffIterator;
+ typedef typename NodeMaskType::DenseIterator MaskDenseIterator;
+
+ // Type tags to disambiguate template instantiations
+ struct ValueOn {}; struct ValueOff {}; struct ValueAll {};
+ struct ChildOn {}; struct ChildOff {}; struct ChildAll {};
+
+ template<typename MaskIterT, typename NodeT, typename ValueT, typename TagT>
+ struct ValueIter:
+ // Derives from SparseIteratorBase, but can also be used as a dense iterator,
+ // if MaskIterT is a dense mask iterator type.
+ public SparseIteratorBase<
+ MaskIterT, ValueIter<MaskIterT, NodeT, ValueT, TagT>, NodeT, ValueT>
+ {
+ typedef SparseIteratorBase<MaskIterT, ValueIter, NodeT, ValueT> BaseT;
+
+ ValueIter() {}
+ ValueIter(const MaskIterT& iter, NodeT* parent): BaseT(iter, parent) {}
+
+ ValueT& getItem(Index pos) const { return this->parent().getValue(pos); }
+ ValueT& getValue() const { return this->parent().getValue(this->pos()); }
+
+ // Note: setItem() can't be called on const iterators.
+ void setItem(Index pos, const ValueT& value) const
+ {
+ this->parent().setValueOnly(pos, value);
+ }
+ // Note: setValue() can't be called on const iterators.
+ void setValue(const ValueT& value) const
+ {
+ this->parent().setValueOnly(this->pos(), value);
+ }
+ };
+
+ /// Leaf nodes have no children, so their child iterators have no get/set accessors.
+ template<typename MaskIterT, typename NodeT, typename TagT>
+ struct ChildIter:
+ public SparseIteratorBase<MaskIterT, ChildIter<MaskIterT, NodeT, TagT>, NodeT, ValueType>
+ {
+ ChildIter() {}
+ ChildIter(const MaskIterT& iter, NodeT* parent): SparseIteratorBase<
+ MaskIterT, ChildIter<MaskIterT, NodeT, TagT>, NodeT, ValueType>(iter, parent) {}
+ };
+
+ template<typename NodeT, typename ValueT, typename TagT>
+ struct DenseIter: public DenseIteratorBase<
+ MaskDenseIterator, DenseIter<NodeT, ValueT, TagT>, NodeT, /*ChildT=*/void, ValueT>
+ {
+ typedef DenseIteratorBase<MaskDenseIterator, DenseIter, NodeT, void, ValueT> BaseT;
+ typedef typename BaseT::NonConstValueType NonConstValueT;
+
+ DenseIter() {}
+ DenseIter(const MaskDenseIterator& iter, NodeT* parent): BaseT(iter, parent) {}
+
+ bool getItem(Index pos, void*& child, NonConstValueT& value) const
+ {
+ value = this->parent().getValue(pos);
+ child = NULL;
+ return false; // no child
+ }
+
+ // Note: setItem() can't be called on const iterators.
+ //void setItem(Index pos, void* child) const {}
+
+ // Note: unsetItem() can't be called on const iterators.
+ void unsetItem(Index pos, const ValueT& value) const
+ {
+ this->parent().setValueOnly(pos, value);
+ }
+ };
+
+public:
+ typedef ValueIter<MaskOnIterator, LeafNode, const ValueType, ValueOn> ValueOnIter;
+ typedef ValueIter<MaskOnIterator, const LeafNode, const ValueType, ValueOn> ValueOnCIter;
+ typedef ValueIter<MaskOffIterator, LeafNode, const ValueType, ValueOff> ValueOffIter;
+ typedef ValueIter<MaskOffIterator,const LeafNode,const ValueType,ValueOff> ValueOffCIter;
+ typedef ValueIter<MaskDenseIterator, LeafNode, const ValueType, ValueAll> ValueAllIter;
+ typedef ValueIter<MaskDenseIterator,const LeafNode,const ValueType,ValueAll> ValueAllCIter;
+ typedef ChildIter<MaskOnIterator, LeafNode, ChildOn> ChildOnIter;
+ typedef ChildIter<MaskOnIterator, const LeafNode, ChildOn> ChildOnCIter;
+ typedef ChildIter<MaskOffIterator, LeafNode, ChildOff> ChildOffIter;
+ typedef ChildIter<MaskOffIterator, const LeafNode, ChildOff> ChildOffCIter;
+ typedef DenseIter<LeafNode, ValueType, ChildAll> ChildAllIter;
+ typedef DenseIter<const LeafNode, const ValueType, ChildAll> ChildAllCIter;
+
+ ValueOnCIter cbeginValueOn() const { return ValueOnCIter(mValueMask.beginOn(), this); }
+ ValueOnCIter beginValueOn() const { return ValueOnCIter(mValueMask.beginOn(), this); }
+ ValueOnIter beginValueOn() { return ValueOnIter(mValueMask.beginOn(), this); }
+ ValueOffCIter cbeginValueOff() const { return ValueOffCIter(mValueMask.beginOff(), this); }
+ ValueOffCIter beginValueOff() const { return ValueOffCIter(mValueMask.beginOff(), this); }
+ ValueOffIter beginValueOff() { return ValueOffIter(mValueMask.beginOff(), this); }
+ ValueAllCIter cbeginValueAll() const { return ValueAllCIter(mValueMask.beginDense(), this); }
+ ValueAllCIter beginValueAll() const { return ValueAllCIter(mValueMask.beginDense(), this); }
+ ValueAllIter beginValueAll() { return ValueAllIter(mValueMask.beginDense(), this); }
+
+ ValueOnCIter cendValueOn() const { return ValueOnCIter(mValueMask.endOn(), this); }
+ ValueOnCIter endValueOn() const { return ValueOnCIter(mValueMask.endOn(), this); }
+ ValueOnIter endValueOn() { return ValueOnIter(mValueMask.endOn(), this); }
+ ValueOffCIter cendValueOff() const { return ValueOffCIter(mValueMask.endOff(), this); }
+ ValueOffCIter endValueOff() const { return ValueOffCIter(mValueMask.endOff(), this); }
+ ValueOffIter endValueOff() { return ValueOffIter(mValueMask.endOff(), this); }
+ ValueAllCIter cendValueAll() const { return ValueAllCIter(mValueMask.endDense(), this); }
+ ValueAllCIter endValueAll() const { return ValueAllCIter(mValueMask.endDense(), this); }
+ ValueAllIter endValueAll() { return ValueAllIter(mValueMask.endDense(), this); }
+
+ // Note that [c]beginChildOn() and [c]beginChildOff() actually return end iterators,
+ // because leaf nodes have no children.
+ ChildOnCIter cbeginChildOn() const { return ChildOnCIter(mValueMask.endOn(), this); }
+ ChildOnCIter beginChildOn() const { return ChildOnCIter(mValueMask.endOn(), this); }
+ ChildOnIter beginChildOn() { return ChildOnIter(mValueMask.endOn(), this); }
+ ChildOffCIter cbeginChildOff() const { return ChildOffCIter(mValueMask.endOff(), this); }
+ ChildOffCIter beginChildOff() const { return ChildOffCIter(mValueMask.endOff(), this); }
+ ChildOffIter beginChildOff() { return ChildOffIter(mValueMask.endOff(), this); }
+ ChildAllCIter cbeginChildAll() const { return ChildAllCIter(mValueMask.beginDense(), this); }
+ ChildAllCIter beginChildAll() const { return ChildAllCIter(mValueMask.beginDense(), this); }
+ ChildAllIter beginChildAll() { return ChildAllIter(mValueMask.beginDense(), this); }
+
+ ChildOnCIter cendChildOn() const { return ChildOnCIter(mValueMask.endOn(), this); }
+ ChildOnCIter endChildOn() const { return ChildOnCIter(mValueMask.endOn(), this); }
+ ChildOnIter endChildOn() { return ChildOnIter(mValueMask.endOn(), this); }
+ ChildOffCIter cendChildOff() const { return ChildOffCIter(mValueMask.endOff(), this); }
+ ChildOffCIter endChildOff() const { return ChildOffCIter(mValueMask.endOff(), this); }
+ ChildOffIter endChildOff() { return ChildOffIter(mValueMask.endOff(), this); }
+ ChildAllCIter cendChildAll() const { return ChildAllCIter(mValueMask.endDense(), this); }
+ ChildAllCIter endChildAll() const { return ChildAllCIter(mValueMask.endDense(), this); }
+ ChildAllIter endChildAll() { return ChildAllIter(mValueMask.endDense(), this); }
+
+ //
+ // Buffer management
+ //
+ /// @brief Exchange this node's data buffer with the given data buffer
+ /// without changing the active states of the values.
+ void swap(Buffer& other) { mBuffer.swap(other); }
+ const Buffer& buffer() const { return mBuffer; }
+ Buffer& buffer() { return mBuffer; }
+
+ //
+ // I/O methods
+ //
+ /// @brief Read in just the topology.
+ /// @param is the stream from which to read
+ /// @param fromHalf if true, floating-point input values are assumed to be 16-bit
+ void readTopology(std::istream& is, bool fromHalf = false);
+ /// @brief Write out just the topology.
+ /// @param os the stream to which to write
+ /// @param toHalf if true, output floating-point values as 16-bit half floats
+ void writeTopology(std::ostream& os, bool toHalf = false) const;
+
+ /// @brief Read buffers from a stream.
+ /// @param is the stream from which to read
+ /// @param fromHalf if true, floating-point input values are assumed to be 16-bit
+ void readBuffers(std::istream& is, bool fromHalf = false);
+ /// @brief Write buffers to a stream.
+ /// @param os the stream to which to write
+ /// @param toHalf if true, output floating-point values as 16-bit half floats
+ void writeBuffers(std::ostream& os, bool toHalf = false) const;
+
+ size_t streamingSize(bool toHalf = false) const;
+
+ //
+ // Accessor methods
+ //
+ /// Return the value of the voxel at the given coordinates.
+ const ValueType& getValue(const Coord& xyz) const;
+ /// Return the value of the voxel at the given linear offset.
+ const ValueType& getValue(Index offset) const;
+
+ /// @brief Return @c true if the voxel at the given coordinates is active.
+ /// @param xyz the coordinates of the voxel to be probed
+ /// @param[out] val the value of the voxel at the given coordinates
+ bool probeValue(const Coord& xyz, ValueType& val) const;
+ /// @brief Return @c true if the voxel at the given offset is active.
+ /// @param offset the linear offset of the voxel to be probed
+ /// @param[out] val the value of the voxel at the given coordinates
+ bool probeValue(Index offset, ValueType& val) const;
+
+ /// Return the level (i.e., 0) at which leaf node values reside.
+ static Index getValueLevel(const Coord&) { return LEVEL; }
+
+ /// Set the active state at the given coordinates, but don't change its value.
+ void setActiveState(const Coord& xyz, bool on);
+
+ /// Mark the voxel at the given coordinates as inactive, but don't change its value.
+ void setValueOff(const Coord& xyz) { mValueMask.setOff(LeafNode::coord2offset(xyz)); }
+ /// Mark the voxel at the given offset as inactive, but don't change its value.
+ void setValueOff(Index offset) { assert(offset < SIZE); mValueMask.setOff(offset); }
+
+ /// Change the value of the voxel at the given coordinates and mark the voxel as inactive.
+ void setValueOff(const Coord& xyz, const ValueType& val);
+ /// Change the value of the voxel at the given offset and mark the voxel as inactive.
+ void setValueOff(Index offset, const ValueType& val);
+
+ /// Mark the voxel at the given coordinates as active, but don't change its value.
+ void setValueOn(const Coord& xyz) { mValueMask.setOn(LeafNode::coord2offset(xyz)); }
+ /// Mark the voxel at the given offset as active, but don't change its value.
+ void setValueOn(Index offset) { assert(offset < SIZE); mValueMask.setOn(offset); }
+ /// Set the value of the voxel at the given coordinates and mark the voxel as active.
+ void setValueOn(const Coord& xyz, const ValueType& val) {
+ this->setValueOn(LeafNode::coord2offset(xyz), val);
+ }
+ /// Identical to setValueOn
+ void setValue(const Coord& xyz, const ValueType& val) { this->setValueOn(xyz, val); };
+ /// Change the value of the voxel at the given offset and mark the voxel as active.
+ void setValueOn(Index offset, const ValueType& val) {
+ mBuffer[offset] = val;
+ mValueMask.setOn(offset);
+ }
+
+ /// @brief Set the value of the voxel at the given coordinates to the minimum of
+ /// its current value and the given value, and mark the voxel as active.
+ void setValueOnMin(const Coord& xyz, const ValueType& val) {
+ this->setValueOnMin(LeafNode::coord2offset(xyz), val);
+ }
+ /// @brief Set the value of the voxel at the given offset to the minimum of
+ /// its current value and the given value, and mark the voxel as active.
+ void setValueOnMin(Index offset, const ValueType& val) {
+ mBuffer[offset] = std::min(val, mBuffer[offset]);
+ mValueMask.setOn(offset);
+ }
+
+ /// @brief Set the value of the voxel at the given coordinates to the maximum of
+ /// its current value and the given value, and mark the voxel as active.
+ void setValueOnMax(const Coord& xyz, const ValueType& val) {
+ this->setValueOnMax(LeafNode::coord2offset(xyz), val);
+ }
+ /// @brief Set the value of the voxel at the given offset to the maximum of
+ /// its current value and the given value, and mark the voxel as active.
+ void setValueOnMax(Index offset, const ValueType& val) {
+ mBuffer[offset] = std::max(val, mBuffer[offset]);
+ mValueMask.setOn(offset);
+ }
+
+ /// @brief Set the value of the voxel at the given coordinates to the sum of
+ /// its current value and the given value, and mark the voxel as active.
+ void setValueOnSum(const Coord& xyz, const ValueType& val) {
+ this->setValueOnSum(LeafNode::coord2offset(xyz), val);
+ }
+ /// @brief Set the value of the voxel at the given offset to the sum of
+ /// its current value and the given value, and mark the voxel as active.
+ void setValueOnSum(Index offset, const ValueType& val) {
+ mBuffer[offset] += val;
+ mValueMask.setOn(offset);
+ }
+
+ /// @brief Change the value of the voxel at the given coordinates without altering
+ /// the voxel's active state.
+ void setValueOnly(const Coord& xyz, const ValueType& val) {
+ this->setValueOnly(LeafNode::coord2offset(xyz), val);
+ }
+ /// @brief Change the value of the voxel at the given offset without altering
+ /// the voxel's active state.
+ /// @details This method is very fast since it effectively
+ /// ignores the active state of the voxel.
+ void setValueOnly(Index offset, const ValueType& val) {
+ assert(offset<SIZE); mBuffer[offset] = val;
+ }
+
+ /// Add the given value to all active voxels, leaving inactive voxels unchanged.
+ void addValue(const ValueType& val);
+ /// Multiply all active voxels by the given value, leaving inactive voxels unchanged.
+ void scaleValue(const ValueType& scale);
+
+ /// Mark all voxels as active, but don't change their values.
+ void setValuesOn() { mValueMask.setOn(); }
+ /// Mark all voxels as inactive, but don't change their values.
+ void setValuesOff() { mValueMask.setOff(); }
+
+ /// Return @c true if the voxel at the given coordinates is active.
+ bool isValueOn(const Coord& xyz) const { return this->isValueOn(LeafNode::coord2offset(xyz)); }
+ /// Return @c true if the voxel at the given offset is active.
+ bool isValueOn(Index offset) const { return mValueMask.isOn(offset); }
+
+ /// Return @c false since leaf nodes never contain tiles.
+ static bool hasActiveTiles() { return false; }
+
+ /// @brief Set all voxels within an axis-aligned box to a constant value.
+ /// (The bbox coordinates are inclusive.)
+ void fill(const CoordBBox& bbox, const ValueType&, bool active = true);
+
+ /// @brief Sets all values to the specified value. Their state is unchanged.
+ void fill(const ValueType& value);
+
+ /// @brief Sets all values to the specified value and state
+ void fill(const ValueType& value, bool active);
+
+ /// @brief Copy into a dense grid the values of the voxels that lie within
+ /// a given bounding box.
+ ///
+ /// @param bbox inclusive bounding box of the voxels to be copied into the dense grid
+ /// @param dense dense grid with a stride in @e z of one (see tools::Dense
+ /// in tools/Dense.h for the required API)
+ ///
+ /// @note @a bbox is assumed to be identical to or contained in the coordinate domains
+ /// of both the dense grid and this node, i.e., no bounds checking is performed.
+ /// @note Consider using tools::CopyToDense in tools/Dense.h
+ /// instead of calling this method directly.
+ template<typename DenseT>
+ void copyToDense(const CoordBBox& bbox, DenseT& dense) const;
+
+ /// @brief Copy from a dense grid into this node the values of the voxels
+ /// that lie within a given bounding box.
+ /// @details Only values that are different (by more than the given tolerance)
+ /// from the background value will be active. Other values are inactive
+ /// and truncated to the background value.
+ ///
+ /// @param bbox inclusive bounding box of the voxels to be copied into this node
+ /// @param dense dense grid with a stride in @e z of one (see tools::Dense
+ /// in tools/Dense.h for the required API)
+ /// @param background background value of the tree that this node belongs to
+ /// @param tolerance tolerance within which a value equals the background value
+ ///
+ /// @note @a bbox is assumed to be identical to or contained in the coordinate domains
+ /// of both the dense grid and this node, i.e., no bounds checking is performed.
+ /// @note Consider using tools::CopyFromDense in tools/Dense.h
+ /// instead of calling this method directly.
+ template<typename DenseT>
+ void copyFromDense(const CoordBBox& bbox, const DenseT& dense,
+ const ValueType& background, const ValueType& tolerance);
+
+ /// @brief Return the value of the voxel at the given coordinates.
+ /// @note Used internally by ValueAccessor (note last argument is a dummy).
+ template<typename AccessorT>
+ const ValueType& getValueAndCache(const Coord& xyz, AccessorT&) const
+ {
+ return this->getValue(xyz);
+ }
+
+ /// @brief Return @c true if the voxel at the given coordinates is active.
+ /// @note Used internally by ValueAccessor (note last argument is a dummy).
+ template<typename AccessorT>
+ bool isValueOnAndCache(const Coord& xyz, AccessorT&) const { return this->isValueOn(xyz); }
+
+ /// @brief Change the value of the voxel at the given coordinates and mark it as active.
+ /// @note Used internally by ValueAccessor (note last argument is a dummy).
+ template<typename AccessorT>
+ void setValueAndCache(const Coord& xyz, const ValueType& val, AccessorT&)
+ {
+ this->setValueOn(xyz, val);
+ }
+
+ /// @brief Change the value of the voxel at the given coordinates
+ /// but preserve its state.
+ /// @note Used internally by ValueAccessor (last argument is a dummy).
+ template<typename AccessorT>
+ void setValueOnlyAndCache(const Coord& xyz, const ValueType& val, AccessorT&)
+ {
+ this->setValueOnly(xyz, val);
+ }
+
+ /// Set the value of the voxel at the given coordinates to the sum of
+ /// its current value and the given value, and mark the voxel as
+ /// active.
+ /// @note Used internally by ValueAccessor (note last argument is a dummy).
+ template<typename AccessorT>
+ void setValueOnSumAndCache(const Coord& xyz, const ValueType& val, AccessorT&)
+ {
+ this->setValueOnSum(xyz, val);
+ }
+
+ /// @brief Change the value of the voxel at the given coordinates and mark it as inactive.
+ /// @note Used internally by ValueAccessor (note last argument is a dummy).
+ template<typename AccessorT>
+ void setValueOffAndCache(const Coord& xyz, const ValueType& value, AccessorT&)
+ {
+ this->setValueOff(xyz, value);
+ }
+
+ /// @brief Set the active state of the voxel at the given coordinates
+ /// without changing its value.
+ /// @note Used internally by ValueAccessor (note last argument is a dummy).
+ template<typename AccessorT>
+ void setActiveStateAndCache(const Coord& xyz, bool on, AccessorT&)
+ {
+ this->setActiveState(xyz, on);
+ }
+
+ /// @brief Return @c true if the voxel at the given coordinates is active
+ /// and return the voxel value in @a val.
+ /// @note Used internally by ValueAccessor (note last argument is a dummy).
+ template<typename AccessorT>
+ bool probeValueAndCache(const Coord& xyz, ValueType& val, AccessorT&) const
+ {
+ return this->probeValue(xyz, val);
+ }
+
+ /// @brief Return the value of the voxel at the given coordinates and return
+ /// its active state and level (i.e., 0) in @a state and @a level.
+ /// @note Used internally by ValueAccessor (note last argument is a dummy).
+ template<typename AccessorT>
+ const ValueType& getValue(const Coord& xyz, bool& state, int& level, AccessorT&) const
+ {
+ const Index offset = this->coord2offset(xyz);
+ state = mValueMask.isOn(offset);
+ level = LEVEL;
+ return mBuffer[offset];
+ }
+
+ /// @brief Return the LEVEL (=0) at which leaf node values reside.
+ /// @note Used internally by ValueAccessor (note last argument is a dummy).
+ template<typename AccessorT>
+ static Index getValueLevelAndCache(const Coord&, AccessorT&) { return LEVEL; }
+
+ /// @brief Return a const reference to the first value in the buffer.
+ /// @note Though it is potentially risky you can convert this
+ /// to a non-const pointer by means of const_case<ValueType*>&.
+ const ValueType& getFirstValue() const { return mBuffer[0]; }
+ /// Return a const reference to the last value in the buffer.
+ const ValueType& getLastValue() const { return mBuffer[SIZE - 1]; }
+
+ /// @brief Replace inactive occurrences of @a oldBackground with @a newBackground,
+ /// and inactive occurrences of @a -oldBackground with @a -newBackground.
+ void resetBackground(const ValueType& oldBackground, const ValueType& newBackground);
+
+ /// @brief Overwrites the inactive voxels with a new value whos
+ /// magnitude is equal to the specified background value and sign
+ /// is consistant with the lexicographically closest active voxel.
+ /// The net effect is a propagation of signs from the active
+ /// voxels to the inactive voxels.
+ ///
+ /// @note This method is primarily useful for propagating the sign
+ /// from the (active) voxels in a narrow-band level set to the
+ /// inactive values outside the narrow band.
+ void signedFloodFill(const ValueType& background);
+
+ /// @brief Sets the inactive voxels to either the outside or inside
+ /// value, depending on the sign of the closest corresponding
+ /// active voxels. More specefically, an inactive voxel is set to
+ /// the outside value if the closest active voxel in the
+ /// lexicographic direction is positive, else it is set to the
+ /// inside value.
+ void signedFloodFill(const ValueType& outside, const ValueType& inside);
+
+ void negate();
+
+ void merge(const LeafNode&);
+ void merge(const LeafNode& other, const ValueType& /*bg*/, const ValueType& /*otherBG*/)
+ {
+ LeafNode::merge(other);
+ }
+ void voxelizeActiveTiles() {};
+
+ /// @brief Union this node's set of active values with the active values
+ /// of the other node, whose @c ValueType may be different. So a
+ /// resulting voxel will be active if either of the original voxels
+ /// were active.
+ ///
+ /// @note This operation modifies only active states, not values.
+ template<typename OtherType>
+ void topologyUnion(const LeafNode<OtherType, Log2Dim>& other);
+
+ /// @brief Intersect this node's set of active values with the active values
+ /// of the other node, whose @c ValueType may be different. So a
+ /// resulting voxel will be active only if both of the original voxels
+ /// were active.
+ ///
+ /// @details The last dummy argument is required to match the signature
+ /// for InternalNode::topologyIntersection.
+ ///
+ /// @note This operation modifies only active states, not
+ /// values. Also note that this operation can result in all voxels
+ /// being inactive so consider subsequnetly calling prune.
+ template<typename OtherType>
+ void topologyIntersection(const LeafNode<OtherType, Log2Dim>& other, const ValueType&);
+
+ /// @brief Difference this node's set of active values with the active values
+ /// of the other node, whose @c ValueType may be different. So a
+ /// resulting voxel will be active only if both of the original
+ /// voxel is active in this LeafNode and inactive in the other LeafNode.
+ ///
+ /// @details The last dummy argument is required to match the signature
+ /// for InternalNode::topologyDifference.
+ ///
+ /// @note This operation modifies only active states, not
+ /// values. Also note that this operation can result in all voxels
+ /// being inactive so consider subsequnetly calling prune.
+ template<typename OtherType>
+ void topologyDifference(const LeafNode<OtherType, Log2Dim>& other, const ValueType&);
+
+ template<typename CombineOp>
+ void combine(const LeafNode& other, CombineOp& op);
+ template<typename CombineOp>
+ void combine(const ValueType& value, bool valueIsActive, CombineOp& op);
+
+ template<typename CombineOp>
+ void combine2(const LeafNode& other, const ValueType&, bool valueIsActive, CombineOp&);
+ template<typename CombineOp>
+ void combine2(const ValueType&, const LeafNode& other, bool valueIsActive, CombineOp&);
+ template<typename CombineOp>
+ void combine2(const LeafNode& b0, const LeafNode& b1, CombineOp&);
+
+ /// @brief Calls the templated functor BBoxOp with bounding box
+ /// information. An additional level argument is provided to the
+ /// callback.
+ ///
+ /// @note The bounding boxes are guarenteed to be non-overlapping.
+ template<typename BBoxOp> void visitActiveBBox(BBoxOp&) const;
+
+ template<typename VisitorOp> void visit(VisitorOp&);
+ template<typename VisitorOp> void visit(VisitorOp&) const;
+
+ template<typename OtherLeafNodeType, typename VisitorOp>
+ void visit2Node(OtherLeafNodeType& other, VisitorOp&);
+ template<typename OtherLeafNodeType, typename VisitorOp>
+ void visit2Node(OtherLeafNodeType& other, VisitorOp&) const;
+ template<typename IterT, typename VisitorOp>
+ void visit2(IterT& otherIter, VisitorOp&, bool otherIsLHS = false);
+ template<typename IterT, typename VisitorOp>
+ void visit2(IterT& otherIter, VisitorOp&, bool otherIsLHS = false) const;
+
+ //@{
+ /// This function exists only to enable template instantiation.
+ template<typename PruneOp> void pruneOp(PruneOp&) {}
+ void prune(const ValueType& /*tolerance*/ = zeroVal<ValueType>()) {}
+ void pruneInactive(const ValueType&) {}
+ void addLeaf(LeafNode*) {}
+ template<typename AccessorT>
+ void addLeafAndCache(LeafNode*, AccessorT&) {}
+ template<typename NodeT>
+ NodeT* stealNode(const Coord&, const ValueType&, bool) { return NULL; }
+ void addTile(Index, const Coord&, const ValueType&, bool) {}
+ template<typename AccessorT>
+ void addTileAndCache(Index, const Coord&, const ValueType&, bool, AccessorT&) {}
+ //@}
+ //@{
+ /// @brief Return a pointer to this node.
+ LeafNode* touchLeaf(const Coord&) { return this; }
+ template<typename AccessorT>
+ LeafNode* touchLeafAndCache(const Coord&, AccessorT&) { return this; }
+ LeafNode* probeLeaf(const Coord&) { return this; }
+ template<typename AccessorT>
+ LeafNode* probeLeafAndCache(const Coord&, AccessorT&) { return this; }
+ //@}
+ //@{
+ /// @brief Return a @const pointer to this node.
+ const LeafNode* probeConstLeaf(const Coord&) const { return this; }
+ template<typename AccessorT>
+ const LeafNode* probeConstLeafAndCache(const Coord&, AccessorT&) const { return this; }
+ template<typename AccessorT>
+ const LeafNode* probeLeafAndCache(const Coord&, AccessorT&) const { return this; }
+ const LeafNode* probeLeaf(const Coord&) const { return this; }
+ //@}
+
+ /// Return @c true if all of this node's values have the same active state
+ /// and are equal to within the given tolerance, and return the value in @a constValue
+ /// and the active state in @a state.
+ bool isConstant(ValueType& constValue, bool& state,
+ const ValueType& tolerance = zeroVal<ValueType>()) const;
+ /// Return @c true if all of this node's values are inactive.
+ bool isInactive() const { return mValueMask.isOff(); }
+
+protected:
+ friend class ::TestLeaf;
+ template<typename> friend class ::TestLeafIO;
+
+ // During topology-only construction, access is needed
+ // to protected/private members of other template instances.
+ template<typename, Index> friend class LeafNode;
+
+ friend struct ValueIter<MaskOnIterator, LeafNode, ValueType, ValueOn>;
+ friend struct ValueIter<MaskOffIterator, LeafNode, ValueType, ValueOff>;
+ friend struct ValueIter<MaskDenseIterator, LeafNode, ValueType, ValueAll>;
+ friend struct ValueIter<MaskOnIterator, const LeafNode, ValueType, ValueOn>;
+ friend struct ValueIter<MaskOffIterator, const LeafNode, ValueType, ValueOff>;
+ friend struct ValueIter<MaskDenseIterator, const LeafNode, ValueType, ValueAll>;
+
+ // Allow iterators to call mask accessor methods (see below).
+ /// @todo Make mask accessors public?
+ friend class IteratorBase<MaskOnIterator, LeafNode>;
+ friend class IteratorBase<MaskOffIterator, LeafNode>;
+ friend class IteratorBase<MaskDenseIterator, LeafNode>;
+
+ /// Buffer containing the actual data values
+ Buffer mBuffer;
+ /// Bitmask that determines which voxels are active
+ NodeMaskType mValueMask;
+ /// Global grid index coordinates (x,y,z) of the local origin of this node
+ Coord mOrigin;
+
+ // Mask accessors
+public:
+ bool isValueMaskOn(Index n) const { return mValueMask.isOn(n); }
+ bool isValueMaskOn() const { return mValueMask.isOn(); }
+ bool isValueMaskOff(Index n) const { return mValueMask.isOff(n); }
+ bool isValueMaskOff() const { return mValueMask.isOff(); }
+ const NodeMaskType& getValueMask() const { return mValueMask; }
+ NodeMaskType& getValueMask() { return mValueMask; }
+ void setValueMask(const NodeMaskType& mask) { mValueMask = mask; }
+ bool isChildMaskOn(Index) const { return false; } // leaf nodes have no children
+ bool isChildMaskOff(Index) const { return true; }
+ bool isChildMaskOff() const { return true; }
+protected:
+ void setValueMask(Index n, bool on) { mValueMask.set(n, on); }
+ void setValueMaskOn(Index n) { mValueMask.setOn(n); }
+ void setValueMaskOff(Index n) { mValueMask.setOff(n); }
+
+ /// Compute the origin of the leaf node that contains the voxel with the given coordinates.
+ static void evalNodeOrigin(Coord& xyz) { xyz &= ~(DIM - 1); }
+
+ template<typename NodeT, typename VisitorOp, typename ChildAllIterT>
+ static inline void doVisit(NodeT&, VisitorOp&);
+
+ template<typename NodeT, typename OtherNodeT, typename VisitorOp,
+ typename ChildAllIterT, typename OtherChildAllIterT>
+ static inline void doVisit2Node(NodeT& self, OtherNodeT& other, VisitorOp&);
+
+ template<typename NodeT, typename VisitorOp,
+ typename ChildAllIterT, typename OtherChildAllIterT>
+ static inline void doVisit2(NodeT& self, OtherChildAllIterT&, VisitorOp&, bool otherIsLHS);
+
+private:
+
+ // Disallow copying.
+ LeafNode& operator=(const LeafNode&);
+
+}; // end of LeafNode class
+
+
+////////////////////////////////////////
+
+template<typename T, Index Log2Dim>
+inline
+LeafNode<T, Log2Dim>::LeafNode():
+ mValueMask(),//default is off!
+ mOrigin(0, 0, 0)
+{
+}
+
+
+template<typename T, Index Log2Dim>
+inline
+LeafNode<T, Log2Dim>::LeafNode(const Coord& xyz, const ValueType& val, bool active):
+ mBuffer(val),
+ mValueMask(active),
+ mOrigin(xyz & (~(DIM - 1)))
+{
+}
+
+template<typename T, Index Log2Dim>
+template<typename OtherValueType>
+inline
+LeafNode<T, Log2Dim>::LeafNode(const LeafNode<OtherValueType, Log2Dim>& other,
+ const ValueType& background, TopologyCopy):
+ mBuffer(background),
+ mValueMask(other.mValueMask),
+ mOrigin(other.mOrigin)
+{
+}
+
+template<typename T, Index Log2Dim>
+template<typename OtherValueType>
+inline
+LeafNode<T, Log2Dim>::LeafNode(const LeafNode<OtherValueType, Log2Dim>& other,
+ const ValueType& offValue, const ValueType& onValue, TopologyCopy):
+ mValueMask(other.mValueMask),
+ mOrigin(other.mOrigin)
+{
+ for (Index i = 0; i < SIZE; ++i) {
+ mBuffer[i] = (mValueMask.isOn(i) ? onValue : offValue);
+ }
+}
+
+template<typename T, Index Log2Dim>
+inline
+LeafNode<T, Log2Dim>::LeafNode(const LeafNode &other):
+ mBuffer(other.mBuffer),
+ mValueMask(other.mValueMask),
+ mOrigin(other.mOrigin)
+{
+}
+
+
+template<typename T, Index Log2Dim>
+inline
+LeafNode<T, Log2Dim>::~LeafNode()
+{
+}
+
+
+template<typename T, Index Log2Dim>
+inline std::string
+LeafNode<T, Log2Dim>::str() const
+{
+ std::ostringstream ostr;
+ ostr << "LeafNode @" << mOrigin << ": " << mBuffer;
+ return ostr.str();
+}
+
+
+////////////////////////////////////////
+
+
+template<typename T, Index Log2Dim>
+inline Index
+LeafNode<T, Log2Dim>::coord2offset(const Coord& xyz)
+{
+ assert ((xyz[0]&DIM-1u)<DIM && (xyz[1]&DIM-1u)<DIM && (xyz[2]&DIM-1u)<DIM);
+ return ((xyz[0]&DIM-1u)<<2*Log2Dim)
+ + ((xyz[1]&DIM-1u)<< Log2Dim)
+ + (xyz[2]&DIM-1u);
+}
+
+
+template<typename T, Index Log2Dim>
+inline void
+LeafNode<T, Log2Dim>::offset2coord(Index n, Coord &xyz)
+{
+ assert(n<(1<< 3*Log2Dim));
+ xyz.setX(n >> 2*Log2Dim);
+ n &= ((1<<2*Log2Dim)-1);
+ xyz.setY(n >> Log2Dim);
+ xyz.setZ(n & ((1<<Log2Dim)-1));
+}
+
+
+template<typename T, Index Log2Dim>
+inline Coord
+LeafNode<T, Log2Dim>::offset2globalCoord(Index n) const
+{
+ Coord local;
+ this->offset2coord(n, local);
+ return Coord(local + this->getOrigin());
+}
+
+
+////////////////////////////////////////
+
+
+template<typename ValueT, Index Log2Dim>
+inline const ValueT&
+LeafNode<ValueT, Log2Dim>::getValue(const Coord& xyz) const
+{
+ return this->getValue(LeafNode::coord2offset(xyz));
+}
+
+template<typename ValueT, Index Log2Dim>
+inline const ValueT&
+LeafNode<ValueT, Log2Dim>::getValue(Index offset) const
+{
+ assert(offset < SIZE);
+ return mBuffer[offset];
+}
+
+
+template<typename T, Index Log2Dim>
+inline bool
+LeafNode<T, Log2Dim>::probeValue(const Coord& xyz, ValueType& val) const
+{
+ return this->probeValue(LeafNode::coord2offset(xyz), val);
+}
+
+template<typename T, Index Log2Dim>
+inline bool
+LeafNode<T, Log2Dim>::probeValue(Index offset, ValueType& val) const
+{
+ assert(offset < SIZE);
+ val = mBuffer[offset];
+ return mValueMask.isOn(offset);
+}
+
+
+template<typename T, Index Log2Dim>
+inline void
+LeafNode<T, Log2Dim>::setValueOff(const Coord& xyz, const ValueType& val)
+{
+ this->setValueOff(LeafNode::coord2offset(xyz), val);
+}
+
+template<typename T, Index Log2Dim>
+inline void
+LeafNode<T, Log2Dim>::setValueOff(Index offset, const ValueType& val)
+{
+ assert(offset < SIZE);
+ mBuffer[offset] = val;
+ mValueMask.setOff(offset);
+}
+
+
+template<typename T, Index Log2Dim>
+inline void
+LeafNode<T, Log2Dim>::setActiveState(const Coord& xyz, bool on)
+{
+ mValueMask.set(this->coord2offset(xyz), on);
+}
+
+
+template<typename T, Index Log2Dim>
+inline void
+LeafNode<T, Log2Dim>::addValue(const ValueType& val)
+{
+ for (typename NodeMaskType::OnIterator iter = mValueMask.beginOn(); iter; ++iter) {
+ mBuffer[iter.pos()] += val;
+ }
+}
+
+
+template<typename T, Index Log2Dim>
+inline void
+LeafNode<T, Log2Dim>::scaleValue(const ValueType& scale)
+{
+ for (typename NodeMaskType::OnIterator iter = mValueMask.beginOn(); iter; ++iter) {
+ mBuffer[iter.pos()] *= scale;
+ }
+}
+
+
+////////////////////////////////////////
+
+
+template<typename T, Index Log2Dim>
+inline void
+LeafNode<T, Log2Dim>::fill(const CoordBBox& bbox, const ValueType& value, bool active)
+{
+ for (Int32 x = bbox.min().x(); x <= bbox.max().x(); ++x) {
+ const Index offsetX = (x&DIM-1u)<<2*Log2Dim;
+ for (Int32 y = bbox.min().y(); y <= bbox.max().y(); ++y) {
+ const Index offsetXY = offsetX + ((y&DIM-1u)<< Log2Dim);
+ for (Int32 z = bbox.min().z(); z <= bbox.max().z(); ++z) {
+ const Index offset = offsetXY + (z&DIM-1u);
+ mBuffer[offset] = value;
+ mValueMask.set(offset, active);
+ }
+ }
+ }
+}
+
+template<typename T, Index Log2Dim>
+inline void
+LeafNode<T, Log2Dim>::fill(const ValueType& value)
+{
+ mBuffer.fill(value);
+}
+
+template<typename T, Index Log2Dim>
+inline void
+LeafNode<T, Log2Dim>::fill(const ValueType& value, bool active)
+{
+ mBuffer.fill(value);
+ mValueMask.set(active);
+}
+
+////////////////////////////////////////
+
+template<typename T, Index Log2Dim>
+template<typename DenseT>
+inline void
+LeafNode<T, Log2Dim>::copyToDense(const CoordBBox& bbox, DenseT& dense) const
+{
+ const size_t xStride = dense.xStride(), yStride = dense.yStride();// zStride=1
+ const Coord& min = dense.bbox().min();
+ T* t0 = dense.data() + bbox.min()[2]-min[2];//target array
+ const T* s0 = &mBuffer[bbox.min()[2]&DIM-1u];//source array
+ for (Int32 x = bbox.min()[0], ex=bbox.max()[0]+1; x<ex; ++x) {
+ T* t1 = t0 + xStride*(x-min[0]);
+ const T* s1 = s0 + ((x&DIM-1u)<<2*Log2Dim);
+ for (Int32 y = bbox.min()[1], ey=bbox.max()[1]+1; y<ey; ++y) {
+ T* t2 = t1 + yStride*(y-min[1]);
+ const T* s2 = s1 + ((y&DIM-1u)<<Log2Dim);
+ for (Int32 z = bbox.min()[2], ez=bbox.max()[2]+1; z<ez ; ++z) *t2++ = *s2++;
+ }
+ }
+}
+
+template<typename T, Index Log2Dim>
+template<typename DenseT>
+inline void
+LeafNode<T, Log2Dim>::copyFromDense(const CoordBBox& bbox, const DenseT& dense,
+ const ValueType& background, const ValueType& tolerance)
+{
+ const size_t xStride = dense.xStride(), yStride = dense.yStride();// zStride=1
+ const Coord& min = dense.bbox().min();
+
+ const ValueType* s0 = dense.data() + bbox.min()[2]-min[2];//source
+ const Int32 n0 = bbox.min()[2]&DIM-1u;
+ for (Int32 x = bbox.min()[0], ex=bbox.max()[0]+1; x<ex; ++x) {
+ const ValueType* s1 = s0 + xStride*(x-min[0]);
+ const Int32 n1 = n0 + ((x&DIM-1u)<<2*LOG2DIM);
+ for (Int32 y = bbox.min()[1], ey=bbox.max()[1]+1; y<ey; ++y) {
+ const ValueType* s2 = s1 + yStride*(y-min[1]);
+ Int32 n2 = n1 + ((y&DIM-1u)<<LOG2DIM) ;
+ for (Int32 z = bbox.min()[2], ez=bbox.max()[2]+1; z<ez ; ++z, ++n2, ++s2) {
+ if (math::isApproxEqual(background, *s2, tolerance)) {
+ mValueMask.setOff(n2);
+ mBuffer[n2] = background;
+ } else {
+ mValueMask.setOn(n2);
+ mBuffer[n2] = *s2;
+ }
+ }
+ }
+ }
+}
+
+////////////////////////////////////////
+
+
+template<typename T, Index Log2Dim>
+inline void
+LeafNode<T, Log2Dim>::readTopology(std::istream& is, bool /*fromHalf*/)
+{
+ mValueMask.load(is);
+}
+
+
+template<typename T, Index Log2Dim>
+inline void
+LeafNode<T, Log2Dim>::writeTopology(std::ostream& os, bool /*toHalf*/) const
+{
+ mValueMask.save(os);
+}
+
+
+////////////////////////////////////////
+
+
+template<typename T, Index Log2Dim>
+inline void
+LeafNode<T,Log2Dim>::readBuffers(std::istream& is, bool fromHalf)
+{
+ // Read in the value mask.
+ mValueMask.load(is);
+
+ int8_t numBuffers = 1;
+ if (io::getFormatVersion(is) < OPENVDB_FILE_VERSION_NODE_MASK_COMPRESSION) {
+ // Read in the origin.
+ is.read(reinterpret_cast<char*>(&mOrigin), sizeof(Coord::ValueType) * 3);
+
+ // Read in the number of buffers, which should now always be one.
+ is.read(reinterpret_cast<char*>(&numBuffers), sizeof(int8_t));
+ }
+
+ io::readCompressedValues(is, mBuffer.mData, SIZE, mValueMask, fromHalf);
+
+ if (numBuffers > 1) {
+ // Read in and discard auxiliary buffers that were created with earlier
+ // versions of the library. (Auxiliary buffers are not mask compressed.)
+ const bool zipped = io::getDataCompression(is) & io::COMPRESS_ZIP;
+ Buffer temp;
+ for (int i = 1; i < numBuffers; ++i) {
+ if (fromHalf) {
+ io::HalfReader<io::RealToHalf<T>::isReal, T>::read(is, temp.mData, SIZE, zipped);
+ } else {
+ io::readData<T>(is, temp.mData, SIZE, zipped);
+ }
+ }
+ }
+}
+
+
+template<typename T, Index Log2Dim>
+inline void
+LeafNode<T, Log2Dim>::writeBuffers(std::ostream& os, bool toHalf) const
+{
+ // Write out the value mask.
+ mValueMask.save(os);
+
+ io::writeCompressedValues(os, mBuffer.mData, SIZE,
+ mValueMask, /*childMask=*/NodeMaskType(), toHalf);
+}
+
+
+////////////////////////////////////////
+
+
+template<typename T, Index Log2Dim>
+inline bool
+LeafNode<T, Log2Dim>::operator==(const LeafNode& other) const
+{
+ return mOrigin == other.mOrigin &&
+ mValueMask == other.mValueMask &&
+ mBuffer == other.mBuffer;
+}
+
+
+template<typename T, Index Log2Dim>
+inline Index64
+LeafNode<T, Log2Dim>::memUsage() const
+{
+ return mBuffer.memUsage() + sizeof(mOrigin) + mValueMask.memUsage();
+}
+
+
+template<typename T, Index Log2Dim>
+inline void
+LeafNode<T, Log2Dim>::evalActiveVoxelBoundingBox(CoordBBox& bbox) const
+{
+ const CoordBBox this_bbox = this->getNodeBoundingBox();
+ if (bbox.isInside(this_bbox)) {
+ // nothing to do
+ } else if (this->isDense()) {
+ bbox.expand(this_bbox);
+ } else {
+ for (ValueOnCIter iter=this->cbeginValueOn(); iter; ++iter) bbox.expand(iter.getCoord());
+ }
+}
+
+
+template<typename T, Index Log2Dim>
+template<typename OtherType, Index OtherLog2Dim>
+inline bool
+LeafNode<T, Log2Dim>::hasSameTopology(const LeafNode<OtherType, OtherLog2Dim>* other) const
+{
+ assert(other);
+ return (Log2Dim == OtherLog2Dim && mValueMask == other->getValueMask());
+}
+
+
+template<typename T, Index Log2Dim>
+inline bool
+LeafNode<T, Log2Dim>::isConstant(ValueType& constValue,
+ bool& state, const ValueType& tolerance) const
+{
+ if (!mValueMask.isOn() && !mValueMask.isOff()) return false;
+
+ state = mValueMask.isOn();
+
+ bool allEqual = true;
+ const T value = mBuffer[0];
+ for (Index i = 1; allEqual && i < SIZE; ++i) {
+ /// @todo Alternatively, allEqual = !((maxVal - minVal) > (2 * tolerance))
+ allEqual = math::isApproxEqual(mBuffer[i], value, tolerance);
+ }
+ if (allEqual) constValue = value; ///< @todo return average/median value?
+ return allEqual;
+}
+
+template<typename T, Index Log2Dim>
+inline void
+LeafNode<T, Log2Dim>::signedFloodFill(const ValueType& background)
+{
+ this->signedFloodFill(background, negative(background));
+}
+
+template<typename T, Index Log2Dim>
+inline void
+LeafNode<T, Log2Dim>::signedFloodFill(const ValueType& outsideValue,
+ const ValueType& insideValue)
+{
+ const Index first = mValueMask.findFirstOn();
+ if (first < SIZE) {
+ bool xInside = math::isNegative(mBuffer[first]), yInside = xInside, zInside = xInside;
+ for (Index x = 0; x != (1 << Log2Dim); ++x) {
+ const Index x00 = x << (2 * Log2Dim);
+ if (mValueMask.isOn(x00)) {
+ xInside = math::isNegative(mBuffer[x00]); // element(x, 0, 0)
+ }
+ yInside = xInside;
+ for (Index y = 0; y != (1 << Log2Dim); ++y) {
+ const Index xy0 = x00 + (y << Log2Dim);
+ if (mValueMask.isOn(xy0)) {
+ yInside = math::isNegative(mBuffer[xy0]); // element(x, y, 0)
+ }
+ zInside = yInside;
+ for (Index z = 0; z != (1 << Log2Dim); ++z) {
+ const Index xyz = xy0 + z; // element(x, y, z)
+ if (mValueMask.isOn(xyz)) {
+ zInside = math::isNegative(mBuffer[xyz]);
+ } else {
+ mBuffer[xyz] = zInside ? insideValue : outsideValue;
+ }
+ }
+ }
+ }
+ } else {// if no active voxels exist simply use the sign of the first value
+ mBuffer.fill(math::isNegative(mBuffer[0]) ? insideValue : outsideValue);
+ }
+}
+
+
+template<typename T, Index Log2Dim>
+inline void
+LeafNode<T, Log2Dim>::resetBackground(const ValueType& oldBackground,
+ const ValueType& newBackground)
+{
+ typename NodeMaskType::OffIterator iter;
+ // For all inactive values...
+ for (iter = this->mValueMask.beginOff(); iter; ++iter) {
+ ValueType &inactiveValue = mBuffer[iter.pos()];
+ if (math::isApproxEqual(inactiveValue, oldBackground)) {
+ inactiveValue = newBackground;
+ } else if (math::isApproxEqual(inactiveValue, negative(oldBackground))) {
+ inactiveValue = negative(newBackground);
+ }
+ }
+}
+
+
+template<typename T, Index Log2Dim>
+inline void
+LeafNode<T, Log2Dim>::merge(const LeafNode& other)
+{
+ typename NodeMaskType::OnIterator iter = other.mValueMask.beginOn();
+ for (; iter; ++iter) {
+ const Index n = iter.pos();
+ if (mValueMask.isOn(n)) continue;
+ mBuffer[n] = other.mBuffer[n];
+ mValueMask.setOn(n);
+ }
+}
+
+
+template<typename T, Index Log2Dim>
+template<typename OtherType>
+inline void
+LeafNode<T, Log2Dim>::topologyUnion(const LeafNode<OtherType, Log2Dim>& other)
+{
+ mValueMask |= other.getValueMask();
+}
+
+template<typename T, Index Log2Dim>
+template<typename OtherType>
+inline void
+LeafNode<T, Log2Dim>::topologyIntersection(const LeafNode<OtherType, Log2Dim>& other,
+ const ValueType&)
+{
+ mValueMask &= other.getValueMask();
+}
+
+template<typename T, Index Log2Dim>
+template<typename OtherType>
+inline void
+LeafNode<T, Log2Dim>::topologyDifference(const LeafNode<OtherType, Log2Dim>& other,
+ const ValueType&)
+{
+ mValueMask &= !other.getValueMask();
+}
+
+template<typename T, Index Log2Dim>
+inline void
+LeafNode<T, Log2Dim>::negate()
+{
+ for (Index i = 0; i < SIZE; ++i) {
+ mBuffer[i] = -mBuffer[i];
+ }
+}
+
+
+////////////////////////////////////////
+
+
+template<typename T, Index Log2Dim>
+template<typename CombineOp>
+inline void
+LeafNode<T, Log2Dim>::combine(const LeafNode& other, CombineOp& op)
+{
+ CombineArgs<T> args;
+ for (Index i = 0; i < SIZE; ++i) {
+ op(args.setARef(mBuffer[i])
+ .setAIsActive(mValueMask.isOn(i))
+ .setBRef(other.mBuffer[i])
+ .setBIsActive(other.mValueMask.isOn(i))
+ .setResultRef(mBuffer[i]));
+ mValueMask.set(i, args.resultIsActive());
+ }
+}
+
+
+template<typename T, Index Log2Dim>
+template<typename CombineOp>
+inline void
+LeafNode<T, Log2Dim>::combine(const ValueType& value, bool valueIsActive, CombineOp& op)
+{
+ CombineArgs<T> args;
+ args.setBRef(value).setBIsActive(valueIsActive);
+ for (Index i = 0; i < SIZE; ++i) {
+ op(args.setARef(mBuffer[i])
+ .setAIsActive(mValueMask.isOn(i))
+ .setResultRef(mBuffer[i]));
+ mValueMask.set(i, args.resultIsActive());
+ }
+}
+
+
+////////////////////////////////////////
+
+
+template<typename T, Index Log2Dim>
+template<typename CombineOp>
+inline void
+LeafNode<T, Log2Dim>::combine2(const LeafNode& other, const ValueType& value,
+ bool valueIsActive, CombineOp& op)
+{
+ CombineArgs<T> args;
+ args.setBRef(value).setBIsActive(valueIsActive);
+ for (Index i = 0; i < SIZE; ++i) {
+ op(args.setARef(other.mBuffer[i])
+ .setAIsActive(other.mValueMask.isOn(i))
+ .setResultRef(mBuffer[i]));
+ mValueMask.set(i, args.resultIsActive());
+ }
+}
+
+
+template<typename T, Index Log2Dim>
+template<typename CombineOp>
+inline void
+LeafNode<T, Log2Dim>::combine2(const ValueType& value, const LeafNode& other,
+ bool valueIsActive, CombineOp& op)
+{
+ CombineArgs<T> args;
+ args.setARef(value).setAIsActive(valueIsActive);
+ for (Index i = 0; i < SIZE; ++i) {
+ op(args.setBRef(other.mBuffer[i])
+ .setBIsActive(other.mValueMask.isOn(i))
+ .setResultRef(mBuffer[i]));
+ mValueMask.set(i, args.resultIsActive());
+ }
+}
+
+
+template<typename T, Index Log2Dim>
+template<typename CombineOp>
+inline void
+LeafNode<T, Log2Dim>::combine2(const LeafNode& b0, const LeafNode& b1, CombineOp& op)
+{
+ CombineArgs<T> args;
+ for (Index i = 0; i < SIZE; ++i) {
+ mValueMask.set(i, b0.mValueMask.isOn(i) || b1.mValueMask.isOn(i));
+ op(args.setARef(b0.mBuffer[i])
+ .setAIsActive(b0.mValueMask.isOn(i))
+ .setBRef(b1.mBuffer[i])
+ .setBIsActive(b1.mValueMask.isOn(i))
+ .setResultRef(mBuffer[i]));
+ mValueMask.set(i, args.resultIsActive());
+ }
+}
+
+
+////////////////////////////////////////
+
+
+template<typename T, Index Log2Dim>
+template<typename BBoxOp>
+inline void
+LeafNode<T, Log2Dim>::visitActiveBBox(BBoxOp& op) const
+{
+ if (op.template descent<LEVEL>()) {
+ for (ValueOnCIter i=this->cbeginValueOn(); i; ++i) {
+#ifdef _MSC_VER
+ op.operator()<LEVEL>(CoordBBox::createCube(i.getCoord(), 1));
+#else
+ op.template operator()<LEVEL>(CoordBBox::createCube(i.getCoord(), 1));
+#endif
+ }
+ } else {
+#ifdef _MSC_VER
+ op.operator()<LEVEL>(this->getNodeBoundingBox());
+#else
+ op.template operator()<LEVEL>(this->getNodeBoundingBox());
+#endif
+ }
+}
+
+
+template<typename T, Index Log2Dim>
+template<typename VisitorOp>
+inline void
+LeafNode<T, Log2Dim>::visit(VisitorOp& op)
+{
+ doVisit<LeafNode, VisitorOp, ChildAllIter>(*this, op);
+}
+
+
+template<typename T, Index Log2Dim>
+template<typename VisitorOp>
+inline void
+LeafNode<T, Log2Dim>::visit(VisitorOp& op) const
+{
+ doVisit<const LeafNode, VisitorOp, ChildAllCIter>(*this, op);
+}
+
+
+template<typename T, Index Log2Dim>
+template<typename NodeT, typename VisitorOp, typename ChildAllIterT>
+inline void
+LeafNode<T, Log2Dim>::doVisit(NodeT& self, VisitorOp& op)
+{
+ for (ChildAllIterT iter = self.beginChildAll(); iter; ++iter) {
+ op(iter);
+ }
+}
+
+
+////////////////////////////////////////
+
+
+template<typename T, Index Log2Dim>
+template<typename OtherLeafNodeType, typename VisitorOp>
+inline void
+LeafNode<T, Log2Dim>::visit2Node(OtherLeafNodeType& other, VisitorOp& op)
+{
+ doVisit2Node<LeafNode, OtherLeafNodeType, VisitorOp, ChildAllIter,
+ typename OtherLeafNodeType::ChildAllIter>(*this, other, op);
+}
+
+
+template<typename T, Index Log2Dim>
+template<typename OtherLeafNodeType, typename VisitorOp>
+inline void
+LeafNode<T, Log2Dim>::visit2Node(OtherLeafNodeType& other, VisitorOp& op) const
+{
+ doVisit2Node<const LeafNode, OtherLeafNodeType, VisitorOp, ChildAllCIter,
+ typename OtherLeafNodeType::ChildAllCIter>(*this, other, op);
+}
+
+
+template<typename T, Index Log2Dim>
+template<
+ typename NodeT,
+ typename OtherNodeT,
+ typename VisitorOp,
+ typename ChildAllIterT,
+ typename OtherChildAllIterT>
+inline void
+LeafNode<T, Log2Dim>::doVisit2Node(NodeT& self, OtherNodeT& other, VisitorOp& op)
+{
+ // Allow the two nodes to have different ValueTypes, but not different dimensions.
+ BOOST_STATIC_ASSERT(OtherNodeT::SIZE == NodeT::SIZE);
+ BOOST_STATIC_ASSERT(OtherNodeT::LEVEL == NodeT::LEVEL);
+
+ ChildAllIterT iter = self.beginChildAll();
+ OtherChildAllIterT otherIter = other.beginChildAll();
+
+ for ( ; iter && otherIter; ++iter, ++otherIter) {
+ op(iter, otherIter);
+ }
+}
+
+
+////////////////////////////////////////
+
+
+template<typename T, Index Log2Dim>
+template<typename IterT, typename VisitorOp>
+inline void
+LeafNode<T, Log2Dim>::visit2(IterT& otherIter, VisitorOp& op, bool otherIsLHS)
+{
+ doVisit2<LeafNode, VisitorOp, ChildAllIter, IterT>(
+ *this, otherIter, op, otherIsLHS);
+}
+
+
+template<typename T, Index Log2Dim>
+template<typename IterT, typename VisitorOp>
+inline void
+LeafNode<T, Log2Dim>::visit2(IterT& otherIter, VisitorOp& op, bool otherIsLHS) const
+{
+ doVisit2<const LeafNode, VisitorOp, ChildAllCIter, IterT>(
+ *this, otherIter, op, otherIsLHS);
+}
+
+
+template<typename T, Index Log2Dim>
+template<
+ typename NodeT,
+ typename VisitorOp,
+ typename ChildAllIterT,
+ typename OtherChildAllIterT>
+inline void
+LeafNode<T, Log2Dim>::doVisit2(NodeT& self, OtherChildAllIterT& otherIter,
+ VisitorOp& op, bool otherIsLHS)
+{
+ if (!otherIter) return;
+
+ if (otherIsLHS) {
+ for (ChildAllIterT iter = self.beginChildAll(); iter; ++iter) {
+ op(otherIter, iter);
+ }
+ } else {
+ for (ChildAllIterT iter = self.beginChildAll(); iter; ++iter) {
+ op(iter, otherIter);
+ }
+ }
+}
+
+
+////////////////////////////////////////
+
+
+template<typename T, Index Log2Dim>
+inline std::ostream&
+operator<<(std::ostream& os, const typename LeafNode<T, Log2Dim>::Buffer& buf)
+{
+ for (Index32 i = 0, N = buf.size(); i < N; ++i) os << buf.mData[i] << ", ";
+ return os;
+}
+
+} // namespace tree
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+
+////////////////////////////////////////
+
+
+// Specialization for LeafNodes of type bool
+#include "LeafNodeBool.h"
+
+#endif // OPENVDB_TREE_LEAFNODE_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/tree/LeafNodeBool.h b/extern/openvdb/internal/openvdb/tree/LeafNodeBool.h
new file mode 100644
index 00000000000..ce07c2f372e
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/tree/LeafNodeBool.h
@@ -0,0 +1,1494 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+
+#ifndef OPENVDB_TREE_LEAFNODEBOOL_HAS_BEEN_INCLUDED
+#define OPENVDB_TREE_LEAFNODEBOOL_HAS_BEEN_INCLUDED
+
+#include <iostream>
+#include <boost/shared_ptr.hpp>
+#include <boost/shared_array.hpp>
+#include <boost/static_assert.hpp>
+#include <openvdb/Types.h>
+#include <openvdb/io/Compression.h> // for io::readData(), etc.
+#include <openvdb/util/NodeMasks.h>
+#include "LeafNode.h"
+#include "Iterator.h"
+#include "Util.h"
+
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace tree {
+
+/// @brief LeafNode specialization for values of type bool that stores both
+/// the active states and the values of (2^Log2Dim)^3 voxels as bit masks
+template<Index Log2Dim>
+class LeafNode<bool, Log2Dim>
+{
+public:
+ typedef LeafNode<bool, Log2Dim> LeafNodeType;
+ typedef boost::shared_ptr<LeafNodeType> Ptr;
+ typedef bool ValueType;
+ typedef util::NodeMask<Log2Dim> NodeMaskType;
+
+ // These static declarations must be on separate lines to avoid VC9 compiler errors.
+ static const Index LOG2DIM = Log2Dim; // needed by parent nodes
+ static const Index TOTAL = Log2Dim; // needed by parent nodes
+ static const Index DIM = 1 << TOTAL; // dimension along one coordinate direction
+ static const Index NUM_VALUES = 1 << 3 * Log2Dim;
+ static const Index NUM_VOXELS = NUM_VALUES; // total number of voxels represented by this node
+ static const Index SIZE = NUM_VALUES;
+ static const Index LEVEL = 0; // level 0 = leaf
+
+ /// @brief ValueConverter<T>::Type is the type of a LeafNode having the same
+ /// dimensions as this node but a different value type, T.
+ template<typename ValueType>
+ struct ValueConverter { typedef LeafNode<ValueType, Log2Dim> Type; };
+
+ class Buffer
+ {
+ public:
+ Buffer() {}
+ Buffer(bool on) : mData(on) {}
+ Buffer(const NodeMaskType& other): mData(other) {}
+ Buffer(const Buffer& other): mData(other.mData) {}
+ ~Buffer() {}
+ void fill(bool val) { mData.set(val); }
+ Buffer& operator=(const Buffer& b) { if (&b != this) { mData = b.mData; } return *this; }
+
+ const bool& getValue(Index i) const
+ {
+ assert(i < SIZE);
+ return mData.isOn(i) ? LeafNode::sOn : LeafNode::sOff;
+ }
+ const bool& operator[](Index i) const { return this->getValue(i); }
+
+ bool operator==(const Buffer& other) const { return mData == other.mData; }
+ bool operator!=(const Buffer& other) const { return mData != other.mData; }
+
+ void setValue(Index i, bool val) { assert(i < SIZE); mData.set(i, val); }
+
+ void swap(Buffer& other) { if (&other != this) std::swap(mData, other.mData); }
+
+ Index memUsage() const { return mData.memUsage(); }
+ static Index size() { return SIZE; }
+
+ private:
+ friend class ::TestLeaf;
+ // Allow the parent LeafNode to access this Buffer's bit mask.
+ friend class LeafNode;
+
+ NodeMaskType mData;
+ }; // class Buffer
+
+
+ /// Default constructor
+ LeafNode();
+
+ /// Constructor
+ /// @param xyz the coordinates of a voxel that lies within the node
+ /// @param value the initial value for all of this node's voxels
+ /// @param active the active state to which to initialize all voxels
+ explicit LeafNode(const Coord& xyz, bool value = false, bool active = false);
+
+ /// Deep copy constructor
+ LeafNode(const LeafNode&);
+
+ /// Topology copy constructor
+ template<typename ValueType>
+ LeafNode(const LeafNode<ValueType, Log2Dim>& other, TopologyCopy);
+
+ /// Topology copy constructor
+ /// @note This variant exists mainly to enable template instantiation.
+ template<typename ValueType>
+ LeafNode(const LeafNode<ValueType, Log2Dim>& other,
+ bool offValue, bool onValue, TopologyCopy);
+ template<typename ValueType>
+ LeafNode(const LeafNode<ValueType, Log2Dim>& other,
+ bool background, TopologyCopy);
+
+ /// Destructor
+ ~LeafNode();
+
+ //
+ // Statistics
+ //
+ /// Return log2 of the size of the buffer storage.
+ static Index log2dim() { return Log2Dim; }
+ /// Return the number of voxels in each dimension.
+ static Index dim() { return DIM; }
+ static Index size() { return SIZE; }
+ static Index numValues() { return SIZE; }
+ static Index getLevel() { return LEVEL; }
+ static void getNodeLog2Dims(std::vector<Index>& dims) { dims.push_back(Log2Dim); }
+ static Index getChildDim() { return 1; }
+
+ static Index32 leafCount() { return 1; }
+ static Index32 nonLeafCount() { return 0; }
+
+ /// Return the number of active voxels.
+ Index64 onVoxelCount() const { return mValueMask.countOn(); }
+ /// Return the number of inactive voxels.
+ Index64 offVoxelCount() const { return mValueMask.countOff(); }
+ Index64 onLeafVoxelCount() const { return onVoxelCount(); }
+ Index64 offLeafVoxelCount() const { return offVoxelCount(); }
+
+ /// Return @c true if this node has no active voxels.
+ bool isEmpty() const { return mValueMask.isOff(); }
+ /// Return @c true if this node only contains active voxels.
+ bool isDense() const { return mValueMask.isOn(); }
+
+ /// Return the memory in bytes occupied by this node.
+ Index64 memUsage() const;
+
+ /// Expand the specified bbox so it includes the active voxels of
+ /// this leaf node.
+ void evalActiveVoxelBoundingBox(CoordBBox& bbox) const;
+
+ /// @brief Return the bounding box of this node, i.e., the full index space
+ /// spanned by this leaf node.
+ CoordBBox getNodeBoundingBox() const { return CoordBBox::createCube(mOrigin, DIM); }
+
+ /// Set the grid index coordinates of this node's local origin.
+ void setOrigin(const Coord& origin) { mOrigin = origin; }
+ //@{
+ /// Get the grid index coordinates of this node's local origin.
+ const Coord& origin() const { return mOrigin; }
+ const Coord& getOrigin() const { return mOrigin; }
+ void getOrigin(Coord& origin) const { origin = mOrigin; }
+ void getOrigin(Int32& x, Int32& y, Int32& z) const { mOrigin.asXYZ(x, y, z); }
+ //@}
+
+ /// Return the linear table offset of the given coordinates.
+ static Index coord2offset(const Coord& xyz);
+ /// @brief Return the local coordinates for a linear table offset,
+ /// where offset 0 has coordinates (0, 0, 0).
+ static Coord offset2coord(Index n);
+ /// Return the global coordinates for a linear table offset.
+ Coord offset2globalCoord(Index n) const;
+
+ /// Return a string representation of this node.
+ std::string str() const;
+
+ /// @brief Return @c true if the given node (which may have a different @c ValueType
+ /// than this node) has the same active value topology as this node.
+ template<typename OtherType, Index OtherLog2Dim>
+ bool hasSameTopology(const LeafNode<OtherType, OtherLog2Dim>* other) const;
+
+ /// Check for buffer equivalence by value.
+ bool operator==(const LeafNode&) const;
+ bool operator!=(const LeafNode&) const;
+
+ //
+ // Buffer management
+ //
+ /// @brief Exchange this node's data buffer with the given data buffer
+ /// without changing the active states of the values.
+ void swap(Buffer& other) { mBuffer.swap(other); }
+ const Buffer& buffer() const { return mBuffer; }
+ Buffer& buffer() { return mBuffer; }
+
+ //
+ // I/O methods
+ //
+ /// Read in just the topology.
+ void readTopology(std::istream&, bool fromHalf = false);
+ /// Write out just the topology.
+ void writeTopology(std::ostream&, bool toHalf = false) const;
+
+ /// Read in the topology and the origin.
+ void readBuffers(std::istream&, bool fromHalf = false);
+ /// Write out the topology and the origin.
+ void writeBuffers(std::ostream&, bool toHalf = false) const;
+
+ //
+ // Accessor methods
+ //
+ /// Return the value of the voxel at the given coordinates.
+ const bool& getValue(const Coord& xyz) const;
+ /// Return the value of the voxel at the given offset.
+ const bool& getValue(Index offset) const;
+
+ /// @brief Return @c true if the voxel at the given coordinates is active.
+ /// @param xyz the coordinates of the voxel to be probed
+ /// @param[out] val the value of the voxel at the given coordinates
+ bool probeValue(const Coord& xyz, bool& val) const;
+
+ /// Return the level (0) at which leaf node values reside.
+ static Index getValueLevel(const Coord&) { return LEVEL; }
+
+ /// Set the active state at the given coordinates, but don't change its value.
+ void setActiveState(const Coord& xyz, bool on);
+
+ /// Mark the voxel at the given coordinates as inactive, but don't change its value.
+ void setValueOff(const Coord& xyz) { mValueMask.setOff(this->coord2offset(xyz)); }
+ /// Mark the voxel at the given offset as inactive, but don't change its value.
+ void setValueOff(Index offset) { assert(offset < SIZE); mValueMask.setOff(offset); }
+ /// Change the value of the voxel at the given coordinates and mark the voxel as inactive.
+ void setValueOff(const Coord& xyz, bool val);
+
+ /// Mark the voxel at the given coordinates as active, but don't change its value.
+ void setValueOn(const Coord& xyz) { mValueMask.setOn(this->coord2offset(xyz)); }
+ /// Mark the voxel at the given offset as active, but don't change its value.
+ void setValueOn(Index offset) { assert(offset < SIZE); mValueMask.setOn(offset); }
+ /// Change the value of the voxel at the given coordinates and mark the voxel as active.
+ void setValueOn(const Coord& xyz, bool val);
+ /// Identical to setValueOn
+ void setValue(const Coord& xyz, bool val) { this->setValueOn(xyz, val); };
+
+ /// @brief Set the value of the voxel at the given coordinates to the minimum
+ /// of its current value and the given value, and mark the voxel as active.
+ void setValueOnMin(const Coord& xyz, bool val);
+ /// @brief Set the value of the voxel at the given coordinates to the maximum
+ /// of its current value and the given value, and mark the voxel as active.
+ void setValueOnMax(const Coord& xyz, bool val);
+ /// @brief Set the value of the voxel at the given coordinates to the sum
+ /// of its current value and the given value, and mark the voxel as active.
+ void setValueOnSum(const Coord& xyz, bool val);
+
+ /// @brief Change the value of the voxel at the given coordinates without altering
+ /// the voxel's active state.
+ void setValueOnly(const Coord& xyz, bool val) {
+ this->setValueOnly(LeafNode::coord2offset(xyz), val);
+ }
+ /// @brief Change the value of the voxel at the given offset without altering
+ /// the voxel's active state.
+ void setValueOnly(Index offset, bool val) { assert(offset<SIZE); mBuffer.setValue(offset,val); }
+
+ /// Add the given value to all active voxels, leaving inactive voxels unchanged.
+ void addValue(bool val);
+ /// Multiply all active voxels by the given value, leaving inactive voxels unchanged.
+ void scaleValue(bool scale);
+
+ /// Mark all voxels as active, but don't change their values.
+ void setValuesOn() { mValueMask.setOn(); }
+ /// Mark all voxels as inactive, but don't change their values.
+ void setValuesOff() { mValueMask.setOff(); }
+
+ /// Return @c true if the voxel at the given coordinates is active.
+ bool isValueOn(const Coord& xyz) const { return mValueMask.isOn(this->coord2offset(xyz)); }
+ /// Return @c true if the voxel at the given offset is active.
+ bool isValueOn(Index offset) const { assert(offset < SIZE); return mValueMask.isOn(offset); }
+
+ /// Return @c false since leaf nodes never contain tiles.
+ static bool hasActiveTiles() { return false; }
+
+ /// @brief Set all voxels within an axis-aligned box to the given active state.
+ /// (The bbox coordinates are inclusive.)
+ void fill(const CoordBBox& bbox, bool value, bool active = true);
+
+ /// @brief Sets all values to the specified value. Their state is unchanged.
+ void fill(const bool& value);
+
+ /// @brief Sets all values to the specified value and state
+ void fill(const bool& value, bool active);
+
+ /// @brief Copy into a dense grid the values of the voxels that lie within
+ /// a given bounding box.
+ ///
+ /// @param bbox inclusive bounding box of the voxels to be copied into the dense grid
+ /// @param dense dense grid with a stride in @e z of one (see tools::Dense
+ /// in tools/Dense.h for the required API)
+ ///
+ /// @note @a bbox is assumed to be identical to or contained in the coordinate domains
+ /// of both the dense grid and this node, i.e., no bounds checking is performed.
+ /// @note Consider using tools::CopyToDense in tools/Dense.h
+ /// instead of calling this method directly.
+ template<typename DenseT>
+ void copyToDense(const CoordBBox& bbox, DenseT& dense) const;
+
+ /// @brief Copy from a dense grid into this node the values of the voxels
+ /// that lie within a given bounding box.
+ /// @details Only values that are different (by more than the given tolerance)
+ /// from the background value will be active. Other values are inactive
+ /// and truncated to the background value.
+ ///
+ /// @param bbox inclusive bounding box of the voxels to be copied into this node
+ /// @param dense dense grid with a stride in @e z of one (see tools::Dense
+ /// in tools/Dense.h for the required API)
+ /// @param background background value of the tree that this node belongs to
+ /// @param tolerance tolerance within which a value equals the background value
+ ///
+ /// @note @a bbox is assumed to be identical to or contained in the coordinate domains
+ /// of both the dense grid and this node, i.e., no bounds checking is performed.
+ /// @note Consider using tools::CopyFromDense in tools/Dense.h
+ /// instead of calling this method directly.
+ template<typename DenseT>
+ void copyFromDense(const CoordBBox& bbox, const DenseT& dense, bool background, bool tolerance);
+
+ /// @brief Return the value of the voxel at the given coordinates.
+ /// @note Used internally by ValueAccessor (note last argument is a dummy).
+ template<typename AccessorT>
+ const bool& getValueAndCache(const Coord& xyz, AccessorT&) const {return this->getValue(xyz);}
+
+ /// @brief Return @c true if the voxel at the given coordinates is active.
+ /// @note Used internally by ValueAccessor (note last argument is a dummy).
+ template<typename AccessorT>
+ bool isValueOnAndCache(const Coord& xyz, AccessorT&) const { return this->isValueOn(xyz); }
+
+ /// @brief Change the value of the voxel at the given coordinates and mark it as active.
+ /// @note Used internally by ValueAccessor (note last argument is a dummy).
+ template<typename AccessorT>
+ void setValueAndCache(const Coord& xyz, bool val, AccessorT&) { this->setValueOn(xyz, val); }
+
+ /// @brief Change the value of the voxel at the given coordinates
+ /// but preserve its state.
+ /// @note Used internally by ValueAccessor (last argument is a dummy).
+ template<typename AccessorT>
+ void setValueOnlyAndCache(const Coord& xyz, bool val, AccessorT&) {this->setValueOnly(xyz,val);}
+
+ /// @brief Change the value of the voxel at the given coordinates and mark it as inactive.
+ /// @note Used internally by ValueAccessor (note last argument is a dummy).
+ template<typename AccessorT>
+ void setValueOffAndCache(const Coord& xyz, bool value, AccessorT&)
+ {
+ this->setValueOff(xyz, value);
+ }
+
+ /// @brief Set the active state of the voxel at the given coordinates
+ /// without changing its value.
+ /// @note Used internally by ValueAccessor (note last argument is a dummy).
+ template<typename AccessorT>
+ void setActiveStateAndCache(const Coord& xyz, bool on, AccessorT&)
+ {
+ this->setActiveState(xyz, on);
+ }
+
+ /// @brief Return @c true if the voxel at the given coordinates is active
+ /// and return the voxel value in @a val.
+ /// @note Used internally by ValueAccessor (note last argument is a dummy).
+ template<typename AccessorT>
+ bool probeValueAndCache(const Coord& xyz, bool& val, AccessorT&) const
+ {
+ return this->probeValue(xyz, val);
+ }
+
+ /// @brief Return the LEVEL (=0) at which leaf node values reside.
+ /// @note Used internally by ValueAccessor (note last argument is a dummy).
+ template<typename AccessorT>
+ static Index getValueLevelAndCache(const Coord&, AccessorT&) { return LEVEL; }
+
+ /// @brief Return a const reference to the first entry in the buffer.
+ /// @note Since it's actually a reference to a static data member
+ /// it should not be converted to a non-const pointer!
+ const bool& getFirstValue() const { if (mValueMask.isOn(0)) return sOn; else return sOff; }
+ /// @brief Return a const reference to the last entry in the buffer.
+ /// @note Since it's actually a reference to a static data member
+ /// it should not be converted to a non-const pointer!
+ const bool& getLastValue() const { if (mValueMask.isOn(SIZE-1)) return sOn; else return sOff; }
+
+ /// Return @c true if all of this node's voxels have the same active state
+ /// and are equal to within the given tolerance, and return the value in
+ /// @a constValue and the active state in @a state.
+ bool isConstant(bool& constValue, bool& state, bool tolerance = 0) const;
+ /// Return @c true if all of this node's values are inactive.
+ bool isInactive() const { return mValueMask.isOff(); }
+
+ void resetBackground(bool oldBackground, bool newBackground);
+
+ void negate() { mBuffer.mData.toggle(); }
+
+ void merge(const LeafNode& other);
+
+ void voxelizeActiveTiles() {};
+
+ /// @brief Union this node's set of active values with the active values
+ /// of the other node, whose @c ValueType may be different. So a
+ /// resulting voxel will be active if either of the original voxels
+ /// were active.
+ ///
+ /// @note This operation modifies only active states, not values.
+ template<typename OtherType>
+ void topologyUnion(const LeafNode<OtherType, Log2Dim>& other);
+
+ /// @brief Intersect this node's set of active values with the active values
+ /// of the other node, whose @c ValueType may be different. So a
+ /// resulting voxel will be active only if both of the original voxels
+ /// were active.
+ ///
+ /// @details The last dummy argument is required to match the signature
+ /// for InternalNode::topologyIntersection.
+ ///
+ /// @note This operation modifies only active states, not
+ /// values. Also note that this operation can result in all voxels
+ /// being inactive so consider subsequnetly calling prune.
+ template<typename OtherType>
+ void topologyIntersection(const LeafNode<OtherType, Log2Dim>& other, const bool&);
+
+ /// @brief Difference this node's set of active values with the active values
+ /// of the other node, whose @c ValueType may be different. So a
+ /// resulting voxel will be active only if both of the original
+ /// voxel is active in this LeafNode and inactive in the other LeafNode.
+ ///
+ /// @details The last dummy argument is required to match the signature
+ /// for InternalNode::topologyDifference.
+ ///
+ /// @note This operation modifies only active states, not
+ /// values. Also note that this operation can result in all voxels
+ /// being inactive so consider subsequnetly calling prune.
+ template<typename OtherType>
+ void topologyDifference(const LeafNode<OtherType, Log2Dim>& other, const bool&);
+
+ template<typename CombineOp>
+ void combine(const LeafNode& other, CombineOp& op);
+ template<typename CombineOp>
+ void combine(bool, bool valueIsActive, CombineOp& op);
+
+ template<typename CombineOp>
+ void combine2(const LeafNode& other, bool, bool valueIsActive, CombineOp&);
+ template<typename CombineOp>
+ void combine2(bool, const LeafNode& other, bool valueIsActive, CombineOp&);
+ template<typename CombineOp>
+ void combine2(const LeafNode& b0, const LeafNode& b1, CombineOp&);
+
+ /// @brief Calls the templated functor BBoxOp with bounding box information.
+ /// An additional level argument is provided to the callback.
+ ///
+ /// @note The bounding boxes are guarenteed to be non-overlapping.
+ template<typename BBoxOp> void visitActiveBBox(BBoxOp&) const;
+
+ template<typename VisitorOp> void visit(VisitorOp&);
+ template<typename VisitorOp> void visit(VisitorOp&) const;
+
+ template<typename OtherLeafNodeType, typename VisitorOp>
+ void visit2Node(OtherLeafNodeType& other, VisitorOp&);
+ template<typename OtherLeafNodeType, typename VisitorOp>
+ void visit2Node(OtherLeafNodeType& other, VisitorOp&) const;
+ template<typename IterT, typename VisitorOp>
+ void visit2(IterT& otherIter, VisitorOp&, bool otherIsLHS = false);
+ template<typename IterT, typename VisitorOp>
+ void visit2(IterT& otherIter, VisitorOp&, bool otherIsLHS = false) const;
+
+ //@{
+ /// This function exists only to enable template instantiation.
+ void signedFloodFill(bool) {}
+ /// This function exists only to enable template instantiation.
+ void signedFloodFill(bool, bool) {}
+ template<typename PruneOp> void pruneOp(PruneOp&) {}
+ void prune(const ValueType& /*tolerance*/ = zeroVal<ValueType>()) {}
+ void pruneInactive(const ValueType&) {}
+ void addLeaf(LeafNode*) {}
+ template<typename AccessorT>
+ void addLeafAndCache(LeafNode*, AccessorT&) {}
+ LeafNode* stealLeaf(const Coord&, bool, bool) { return this; }
+ void addTile(Index, const Coord&, bool, bool) {}
+ template<typename AccessorT>
+ void addTileAndCache(Index, const Coord&, const ValueType&, bool, AccessorT&) {}
+ //@}
+
+ //@{
+ /// @brief return a pointer to itself
+ LeafNode* touchLeaf(const Coord&) { return this; }
+ template<typename AccessorT>
+ LeafNode* touchLeafAndCache(const Coord&, AccessorT&) { return this; }
+ LeafNode* probeLeaf(const Coord&) { return this; }
+ template<typename AccessorT>
+ LeafNode* probeLeafAndCache(const Coord&, AccessorT&) { return this; }
+ //@}
+ //@{
+ /// @brief return a const pointer to itself
+ const LeafNode* probeLeaf(const Coord&) const { return this; }
+ template<typename AccessorT>
+ const LeafNode* probeLeafAndCache(const Coord&, AccessorT&) const { return this; }
+ const LeafNode* probeConstLeaf(const Coord&) const { return this; }
+ template<typename AccessorT>
+ const LeafNode* probeConstLeafAndCache(const Coord&, AccessorT&) const { return this; }
+ //@}
+
+ void merge(const LeafNode& other, bool, bool) { this->merge(other); }
+
+ //
+ // Iterators
+ //
+protected:
+ typedef typename NodeMaskType::OnIterator MaskOnIter;
+ typedef typename NodeMaskType::OffIterator MaskOffIter;
+ typedef typename NodeMaskType::DenseIterator MaskDenseIter;
+
+ template<typename MaskIterT, typename NodeT, typename ValueT>
+ struct ValueIter:
+ // Derives from SparseIteratorBase, but can also be used as a dense iterator,
+ // if MaskIterT is a dense mask iterator type.
+ public SparseIteratorBase<MaskIterT, ValueIter<MaskIterT, NodeT, ValueT>, NodeT, ValueT>
+ {
+ typedef SparseIteratorBase<MaskIterT, ValueIter, NodeT, ValueT> BaseT;
+
+ ValueIter() {}
+ ValueIter(const MaskIterT& iter, NodeT* parent): BaseT(iter, parent) {}
+
+ const bool& getItem(Index pos) const { return this->parent().getValue(pos); }
+ const bool& getValue() const { return this->getItem(this->pos()); }
+
+ // Note: setItem() can't be called on const iterators.
+ void setItem(Index pos, bool value) const { this->parent().setValueOnly(pos, value); }
+ // Note: setValue() can't be called on const iterators.
+ void setValue(bool value) const { this->setItem(this->pos(), value); }
+ };
+
+ /// Leaf nodes have no children, so their child iterators have no get/set accessors.
+ template<typename MaskIterT, typename NodeT>
+ struct ChildIter:
+ public SparseIteratorBase<MaskIterT, ChildIter<MaskIterT, NodeT>, NodeT, bool>
+ {
+ ChildIter() {}
+ ChildIter(const MaskIterT& iter, NodeT* parent): SparseIteratorBase<
+ MaskIterT, ChildIter<MaskIterT, NodeT>, NodeT, bool>(iter, parent) {}
+ };
+
+ template<typename NodeT, typename ValueT>
+ struct DenseIter: public DenseIteratorBase<
+ MaskDenseIter, DenseIter<NodeT, ValueT>, NodeT, /*ChildT=*/void, ValueT>
+ {
+ typedef DenseIteratorBase<MaskDenseIter, DenseIter, NodeT, void, ValueT> BaseT;
+ typedef typename BaseT::NonConstValueType NonConstValueT;
+
+ DenseIter() {}
+ DenseIter(const MaskDenseIter& iter, NodeT* parent): BaseT(iter, parent) {}
+
+ bool getItem(Index pos, void*& child, NonConstValueT& value) const
+ {
+ value = this->parent().getValue(pos);
+ child = NULL;
+ return false; // no child
+ }
+
+ // Note: setItem() can't be called on const iterators.
+ //void setItem(Index pos, void* child) const {}
+
+ // Note: unsetItem() can't be called on const iterators.
+ void unsetItem(Index pos, const ValueT& val) const {this->parent().setValueOnly(pos, val);}
+ };
+
+public:
+ typedef ValueIter<MaskOnIter, LeafNode, bool> ValueOnIter;
+ typedef ValueIter<MaskOnIter, const LeafNode, const bool> ValueOnCIter;
+ typedef ValueIter<MaskOffIter, LeafNode, bool> ValueOffIter;
+ typedef ValueIter<MaskOffIter, const LeafNode, const bool> ValueOffCIter;
+ typedef ValueIter<MaskDenseIter, LeafNode, bool> ValueAllIter;
+ typedef ValueIter<MaskDenseIter, const LeafNode, const bool> ValueAllCIter;
+ typedef ChildIter<MaskOnIter, LeafNode> ChildOnIter;
+ typedef ChildIter<MaskOnIter, const LeafNode> ChildOnCIter;
+ typedef ChildIter<MaskOffIter, LeafNode> ChildOffIter;
+ typedef ChildIter<MaskOffIter, const LeafNode> ChildOffCIter;
+ typedef DenseIter<LeafNode, bool> ChildAllIter;
+ typedef DenseIter<const LeafNode, const bool> ChildAllCIter;
+
+ ValueOnCIter cbeginValueOn() const { return ValueOnCIter(mValueMask.beginOn(), this); }
+ ValueOnCIter beginValueOn() const { return ValueOnCIter(mValueMask.beginOn(), this); }
+ ValueOnIter beginValueOn() { return ValueOnIter(mValueMask.beginOn(), this); }
+ ValueOffCIter cbeginValueOff() const { return ValueOffCIter(mValueMask.beginOff(), this); }
+ ValueOffCIter beginValueOff() const { return ValueOffCIter(mValueMask.beginOff(), this); }
+ ValueOffIter beginValueOff() { return ValueOffIter(mValueMask.beginOff(), this); }
+ ValueAllCIter cbeginValueAll() const { return ValueAllCIter(mValueMask.beginDense(), this); }
+ ValueAllCIter beginValueAll() const { return ValueAllCIter(mValueMask.beginDense(), this); }
+ ValueAllIter beginValueAll() { return ValueAllIter(mValueMask.beginDense(), this); }
+
+ ValueOnCIter cendValueOn() const { return ValueOnCIter(mValueMask.endOn(), this); }
+ ValueOnCIter endValueOn() const { return ValueOnCIter(mValueMask.endOn(), this); }
+ ValueOnIter endValueOn() { return ValueOnIter(mValueMask.endOn(), this); }
+ ValueOffCIter cendValueOff() const { return ValueOffCIter(mValueMask.endOff(), this); }
+ ValueOffCIter endValueOff() const { return ValueOffCIter(mValueMask.endOff(), this); }
+ ValueOffIter endValueOff() { return ValueOffIter(mValueMask.endOff(), this); }
+ ValueAllCIter cendValueAll() const { return ValueAllCIter(mValueMask.endDense(), this); }
+ ValueAllCIter endValueAll() const { return ValueAllCIter(mValueMask.endDense(), this); }
+ ValueAllIter endValueAll() { return ValueAllIter(mValueMask.endDense(), this); }
+
+ // Note that [c]beginChildOn() and [c]beginChildOff() actually return end iterators,
+ // because leaf nodes have no children.
+ ChildOnCIter cbeginChildOn() const { return ChildOnCIter(mValueMask.endOn(), this); }
+ ChildOnCIter beginChildOn() const { return ChildOnCIter(mValueMask.endOn(), this); }
+ ChildOnIter beginChildOn() { return ChildOnIter(mValueMask.endOn(), this); }
+ ChildOffCIter cbeginChildOff() const { return ChildOffCIter(mValueMask.endOff(), this); }
+ ChildOffCIter beginChildOff() const { return ChildOffCIter(mValueMask.endOff(), this); }
+ ChildOffIter beginChildOff() { return ChildOffIter(mValueMask.endOff(), this); }
+ ChildAllCIter cbeginChildAll() const { return ChildAllCIter(mValueMask.beginDense(), this); }
+ ChildAllCIter beginChildAll() const { return ChildAllCIter(mValueMask.beginDense(), this); }
+ ChildAllIter beginChildAll() { return ChildAllIter(mValueMask.beginDense(), this); }
+
+ ChildOnCIter cendChildOn() const { return ChildOnCIter(mValueMask.endOn(), this); }
+ ChildOnCIter endChildOn() const { return ChildOnCIter(mValueMask.endOn(), this); }
+ ChildOnIter endChildOn() { return ChildOnIter(mValueMask.endOn(), this); }
+ ChildOffCIter cendChildOff() const { return ChildOffCIter(mValueMask.endOff(), this); }
+ ChildOffCIter endChildOff() const { return ChildOffCIter(mValueMask.endOff(), this); }
+ ChildOffIter endChildOff() { return ChildOffIter(mValueMask.endOff(), this); }
+ ChildAllCIter cendChildAll() const { return ChildAllCIter(mValueMask.endDense(), this); }
+ ChildAllCIter endChildAll() const { return ChildAllCIter(mValueMask.endDense(), this); }
+ ChildAllIter endChildAll() { return ChildAllIter(mValueMask.endDense(), this); }
+
+ //
+ // Mask accessors
+ //
+ bool isValueMaskOn(Index n) const { return mValueMask.isOn(n); }
+ bool isValueMaskOn() const { return mValueMask.isOn(); }
+ bool isValueMaskOff(Index n) const { return mValueMask.isOff(n); }
+ bool isValueMaskOff() const { return mValueMask.isOff(); }
+ const NodeMaskType& getValueMask() const { return mValueMask; }
+ NodeMaskType& getValueMask() { return mValueMask; }
+ void setValueMask(const NodeMaskType& mask) { mValueMask = mask; }
+ bool isChildMaskOn(Index) const { return false; } // leaf nodes have no children
+ bool isChildMaskOff(Index) const { return true; }
+ bool isChildMaskOff() const { return true; }
+protected:
+ void setValueMask(Index n, bool on) { mValueMask.set(n, on); }
+ void setValueMaskOn(Index n) { mValueMask.setOn(n); }
+ void setValueMaskOff(Index n) { mValueMask.setOff(n); }
+
+ /// Compute the origin of the leaf node that contains the voxel with the given coordinates.
+ static void evalNodeOrigin(Coord& xyz) { xyz &= ~(DIM - 1); }
+
+ template<typename NodeT, typename VisitorOp, typename ChildAllIterT>
+ static inline void doVisit(NodeT&, VisitorOp&);
+
+ template<typename NodeT, typename OtherNodeT, typename VisitorOp,
+ typename ChildAllIterT, typename OtherChildAllIterT>
+ static inline void doVisit2Node(NodeT& self, OtherNodeT& other, VisitorOp&);
+
+ template<typename NodeT, typename VisitorOp,
+ typename ChildAllIterT, typename OtherChildAllIterT>
+ static inline void doVisit2(NodeT& self, OtherChildAllIterT&, VisitorOp&, bool otherIsLHS);
+
+
+ /// Bitmask that determines which voxels are active
+ NodeMaskType mValueMask;
+ /// Bitmask representing the values of voxels
+ Buffer mBuffer;
+ /// Global grid index coordinates (x,y,z) of the local origin of this node
+ Coord mOrigin;
+
+ // These static declarations must be on separate lines to avoid VC9 compiler errors.
+ static const bool sOn;
+ static const bool sOff;
+
+private:
+ /// @brief During topology-only construction, access is needed
+ /// to protected/private members of other template instances.
+ template<typename, Index> friend class LeafNode;
+
+ friend struct ValueIter<MaskOnIter, LeafNode, bool>;
+ friend struct ValueIter<MaskOffIter, LeafNode, bool>;
+ friend struct ValueIter<MaskDenseIter, LeafNode, bool>;
+ friend struct ValueIter<MaskOnIter, const LeafNode, bool>;
+ friend struct ValueIter<MaskOffIter, const LeafNode, bool>;
+ friend struct ValueIter<MaskDenseIter, const LeafNode, bool>;
+
+ //@{
+ /// Allow iterators to call mask accessor methods (see below).
+ /// @todo Make mask accessors public?
+ friend class IteratorBase<MaskOnIter, LeafNode>;
+ friend class IteratorBase<MaskOffIter, LeafNode>;
+ friend class IteratorBase<MaskDenseIter, LeafNode>;
+ //@}
+
+ // Disallow copying.
+ LeafNode& operator=(const LeafNode&);
+
+}; // class LeafNode<bool>
+
+
+/// @internal For consistency with other nodes and with iterators, methods like
+/// LeafNode::getValue() return a reference to a value. Since it's not possible
+/// to return a reference to a bit in a node mask, we return a reference to one
+/// of the following static values instead.
+template<Index Log2Dim> const bool LeafNode<bool, Log2Dim>::sOn = true;
+template<Index Log2Dim> const bool LeafNode<bool, Log2Dim>::sOff = false;
+
+
+////////////////////////////////////////
+
+
+template<Index Log2Dim>
+inline
+LeafNode<bool, Log2Dim>::LeafNode(): mOrigin(0, 0, 0)
+{
+}
+
+
+template<Index Log2Dim>
+inline
+LeafNode<bool, Log2Dim>::LeafNode(const Coord& xyz, bool value, bool active):
+ mValueMask(active),
+ mBuffer(value),
+ mOrigin(xyz & (~(DIM - 1)))
+{
+}
+
+
+template<Index Log2Dim>
+template<typename ValueT>
+inline
+LeafNode<bool, Log2Dim>::LeafNode(const LeafNode<ValueT, Log2Dim>& other, TopologyCopy):
+ mValueMask(other.getValueMask()),
+ mBuffer(other.getValueMask()), // value = active state
+ mOrigin(other.getOrigin())
+{
+}
+
+
+template<Index Log2Dim>
+template<typename ValueT>
+inline
+LeafNode<bool, Log2Dim>::LeafNode(const LeafNode<ValueT, Log2Dim>& other,
+ bool offValue, bool onValue, TopologyCopy):
+ mValueMask(other.getValueMask()),
+ mBuffer(other.getValueMask()),
+ mOrigin(other.getOrigin())
+{
+ if (offValue) { if (!onValue) mBuffer.mData.toggle(); else mBuffer.mData.setOn(); }
+}
+
+
+template<Index Log2Dim>
+template<typename ValueT>
+inline
+LeafNode<bool, Log2Dim>::LeafNode(const LeafNode<ValueT, Log2Dim>& other,
+ bool background, TopologyCopy):
+ mValueMask(other.getValueMask()),
+ mBuffer(background),
+ mOrigin(other.getOrigin())
+{
+}
+
+
+template<Index Log2Dim>
+inline
+LeafNode<bool, Log2Dim>::LeafNode(const LeafNode &other):
+ mValueMask(other.mValueMask),
+ mBuffer(other.mBuffer),
+ mOrigin(other.mOrigin)
+{
+}
+
+
+template<Index Log2Dim>
+inline
+LeafNode<bool, Log2Dim>::~LeafNode()
+{
+}
+
+
+////////////////////////////////////////
+
+
+template<Index Log2Dim>
+inline Index64
+LeafNode<bool, Log2Dim>::memUsage() const
+{
+ return sizeof(mOrigin) + mValueMask.memUsage() + mBuffer.memUsage();
+}
+
+
+template<Index Log2Dim>
+inline void
+LeafNode<bool, Log2Dim>::evalActiveVoxelBoundingBox(CoordBBox& bbox) const
+{
+ const CoordBBox this_bbox = this->getNodeBoundingBox();
+ if (bbox.isInside(this_bbox)) {
+ // nothing to do
+ } else if (this->isDense()) {
+ bbox.expand(this_bbox);
+ } else {
+ for (ValueOnCIter iter=this->cbeginValueOn(); iter; ++iter) bbox.expand(iter.getCoord());
+ }
+}
+
+
+template<Index Log2Dim>
+template<typename OtherType, Index OtherLog2Dim>
+inline bool
+LeafNode<bool, Log2Dim>::hasSameTopology(const LeafNode<OtherType, OtherLog2Dim>* other) const
+{
+ assert(other);
+ return (Log2Dim == OtherLog2Dim && mValueMask == other->getValueMask());
+}
+
+
+template<Index Log2Dim>
+inline std::string
+LeafNode<bool, Log2Dim>::str() const
+{
+ std::ostringstream ostr;
+ ostr << "LeafNode @" << mOrigin << ": ";
+ for (Index32 n = 0; n < SIZE; ++n) ostr << (mValueMask.isOn(n) ? '#' : '.');
+ return ostr.str();
+}
+
+
+////////////////////////////////////////
+
+
+template<Index Log2Dim>
+inline Index
+LeafNode<bool, Log2Dim>::coord2offset(const Coord& xyz)
+{
+ assert ((xyz[0] & DIM-1u) < DIM && (xyz[1] & DIM-1u) < DIM && (xyz[2] & DIM-1u) < DIM);
+ return ((xyz[0] & DIM-1u) << 2*Log2Dim) + ((xyz[1] & DIM-1u) << Log2Dim) + (xyz[2] & DIM-1u);
+}
+
+
+template<Index Log2Dim>
+inline Coord
+LeafNode<bool, Log2Dim>::offset2coord(Index n)
+{
+ assert(n < (1 << 3*Log2Dim));
+ Coord xyz;
+ xyz.setX(n >> 2*Log2Dim);
+ n &= ((1 << 2*Log2Dim) - 1);
+ xyz.setY(n >> Log2Dim);
+ xyz.setZ(n & ((1 << Log2Dim) - 1));
+ return xyz;
+}
+
+
+template<Index Log2Dim>
+inline Coord
+LeafNode<bool, Log2Dim>::offset2globalCoord(Index n) const
+{
+ return (this->offset2coord(n) + this->getOrigin());
+}
+
+
+////////////////////////////////////////
+
+
+template<Index Log2Dim>
+inline void
+LeafNode<bool, Log2Dim>::readTopology(std::istream& is, bool /*fromHalf*/)
+{
+ mValueMask.load(is);
+}
+
+
+template<Index Log2Dim>
+inline void
+LeafNode<bool, Log2Dim>::writeTopology(std::ostream& os, bool /*toHalf*/) const
+{
+ mValueMask.save(os);
+}
+
+
+template<Index Log2Dim>
+inline void
+LeafNode<bool, Log2Dim>::readBuffers(std::istream& is, bool /*fromHalf*/)
+{
+ // Read in the value mask.
+ mValueMask.load(is);
+ // Read in the origin.
+ is.read(reinterpret_cast<char*>(&mOrigin), sizeof(Coord::ValueType) * 3);
+
+ if (io::getFormatVersion(is) >= OPENVDB_FILE_VERSION_BOOL_LEAF_OPTIMIZATION) {
+ // Read in the mask for the voxel values.
+ mBuffer.mData.load(is);
+ } else {
+ // Older files stored one or more bool arrays.
+
+ // Read in the number of buffers, which should now always be one.
+ int8_t numBuffers = 0;
+ is.read(reinterpret_cast<char*>(&numBuffers), sizeof(int8_t));
+
+ // Read in the buffer.
+ // (Note: prior to the bool leaf optimization, buffers were always compressed.)
+ boost::shared_array<bool> buf(new bool[SIZE]);
+ io::readData<bool>(is, buf.get(), SIZE, /*isCompressed=*/true);
+
+ // Transfer values to mBuffer.
+ mBuffer.mData.setOff();
+ for (Index i = 0; i < SIZE; ++i) {
+ if (buf[i]) mBuffer.mData.setOn(i);
+ }
+
+ if (numBuffers > 1) {
+ // Read in and discard auxiliary buffers that were created with
+ // earlier versions of the library.
+ for (int i = 1; i < numBuffers; ++i) {
+ io::readData<bool>(is, buf.get(), SIZE, /*isCompressed=*/true);
+ }
+ }
+ }
+}
+
+
+template<Index Log2Dim>
+inline void
+LeafNode<bool, Log2Dim>::writeBuffers(std::ostream& os, bool /*toHalf*/) const
+{
+ // Write out the value mask.
+ mValueMask.save(os);
+ // Write out the origin.
+ os.write(reinterpret_cast<const char*>(&mOrigin), sizeof(Coord::ValueType) * 3);
+ // Write out the voxel values.
+ mBuffer.mData.save(os);
+}
+
+
+////////////////////////////////////////
+
+
+template<Index Log2Dim>
+inline bool
+LeafNode<bool, Log2Dim>::operator==(const LeafNode& other) const
+{
+ return (mValueMask == other.mValueMask && mBuffer.mData == other.mBuffer.mData);
+}
+
+
+template<Index Log2Dim>
+inline bool
+LeafNode<bool, Log2Dim>::operator!=(const LeafNode& other) const
+{
+ return !(this->operator==(other));
+}
+
+
+////////////////////////////////////////
+
+
+template<Index Log2Dim>
+inline bool
+LeafNode<bool, Log2Dim>::isConstant(bool& constValue, bool& state, bool tolerance) const
+{
+ if (!(mValueMask.isOn() || mValueMask.isOff())) return false;
+
+ // Note: if tolerance is true (i.e., 1), then all boolean values compare equal.
+ if (!tolerance && !(mBuffer.mData.isOn() || mBuffer.mData.isOff())) return false;
+
+ state = mValueMask.isOn();
+ constValue = mBuffer.mData.isOn();
+ return true;
+}
+
+
+////////////////////////////////////////
+
+
+template<Index Log2Dim>
+inline const bool&
+LeafNode<bool, Log2Dim>::getValue(const Coord& xyz) const
+{
+ // This *CANNOT* use operator ? because Visual C++
+ if (mBuffer.mData.isOn(this->coord2offset(xyz))) return sOn; else return sOff;
+}
+
+
+template<Index Log2Dim>
+inline const bool&
+LeafNode<bool, Log2Dim>::getValue(Index offset) const
+{
+ assert(offset < SIZE);
+ // This *CANNOT* use operator ? for Windows
+ if (mBuffer.mData.isOn(offset)) return sOn; else return sOff;
+}
+
+
+template<Index Log2Dim>
+inline bool
+LeafNode<bool, Log2Dim>::probeValue(const Coord& xyz, bool& val) const
+{
+ const Index offset = this->coord2offset(xyz);
+ val = mBuffer.mData.isOn(offset);
+ return mValueMask.isOn(offset);
+}
+
+
+template<Index Log2Dim>
+inline void
+LeafNode<bool, Log2Dim>::setValueOn(const Coord& xyz, bool val)
+{
+ const Index offset = this->coord2offset(xyz);
+ mValueMask.setOn(offset);
+ mBuffer.mData.set(offset, val);
+}
+
+
+template<Index Log2Dim>
+inline void
+LeafNode<bool, Log2Dim>::setActiveState(const Coord& xyz, bool on)
+{
+ mValueMask.set(this->coord2offset(xyz), on);
+}
+
+
+template<Index Log2Dim>
+inline void
+LeafNode<bool, Log2Dim>::setValueOff(const Coord& xyz, bool val)
+{
+ const Index offset = this->coord2offset(xyz);
+ mValueMask.setOff(offset);
+ mBuffer.mData.set(offset, val);
+}
+
+
+template<Index Log2Dim>
+inline void
+LeafNode<bool, Log2Dim>::setValueOnMin(const Coord& xyz, bool val)
+{
+ const Index offset = this->coord2offset(xyz);
+ mValueMask.setOn(offset);
+ mBuffer.mData.set(offset, val && mBuffer.mData.isOn(offset));
+}
+
+
+template<Index Log2Dim>
+inline void
+LeafNode<bool, Log2Dim>::setValueOnMax(const Coord& xyz, bool val)
+{
+ const Index offset = this->coord2offset(xyz);
+ mValueMask.setOn(offset);
+ mBuffer.mData.set(offset, val || mBuffer.mData.isOn(offset));
+}
+
+
+template<Index Log2Dim>
+inline void
+LeafNode<bool, Log2Dim>::setValueOnSum(const Coord& xyz, bool val)
+{
+ const Index offset = this->coord2offset(xyz);
+ mValueMask.setOn(offset);
+ mBuffer.mData.set(offset, val || mBuffer.mData.isOn(offset)); // true + true = true
+}
+
+
+template<Index Log2Dim>
+inline void
+LeafNode<bool, Log2Dim>::addValue(bool val)
+{
+ if (val) mBuffer.mData |= mValueMask; // set all active voxels to true
+}
+
+
+template<Index Log2Dim>
+inline void
+LeafNode<bool, Log2Dim>::scaleValue(bool val)
+{
+ if (!val) mBuffer.mData &= !mValueMask; // set all active voxels to false
+}
+
+
+////////////////////////////////////////
+
+
+template<Index Log2Dim>
+inline void
+LeafNode<bool, Log2Dim>::resetBackground(bool oldBackground, bool newBackground)
+{
+ if (newBackground != oldBackground) {
+ // Flip mBuffer's background bits and zero its foreground bits.
+ NodeMaskType bgMask = !(mBuffer.mData | mValueMask);
+ // Overwrite mBuffer's background bits, leaving its foreground bits intact.
+ mBuffer.mData = (mBuffer.mData & mValueMask) | bgMask;
+ }
+}
+
+
+template<Index Log2Dim>
+inline void
+LeafNode<bool, Log2Dim>::merge(const LeafNode& other)
+{
+ for (typename NodeMaskType::OnIterator iter = other.mValueMask.beginOn(); iter; ++iter) {
+ const Index n = iter.pos();
+ if (mValueMask.isOn(n)) continue;
+ mBuffer.mData.set(n, other.mBuffer.mData.isOn(n));
+ mValueMask.setOn(n);
+ }
+}
+
+
+template<Index Log2Dim>
+template<typename OtherType>
+inline void
+LeafNode<bool, Log2Dim>::topologyUnion(const LeafNode<OtherType, Log2Dim>& other)
+{
+ mValueMask |= other.getValueMask();
+}
+
+template<Index Log2Dim>
+template<typename OtherType>
+inline void
+LeafNode<bool, Log2Dim>::topologyIntersection(const LeafNode<OtherType, Log2Dim>& other,
+ const bool&)
+{
+ mValueMask &= other.getValueMask();
+}
+
+template<Index Log2Dim>
+template<typename OtherType>
+inline void
+LeafNode<bool, Log2Dim>::topologyDifference(const LeafNode<OtherType, Log2Dim>& other,
+ const bool&)
+{
+ mValueMask &= !other.getValueMask();
+}
+
+template<Index Log2Dim>
+inline void
+LeafNode<bool, Log2Dim>::fill(const CoordBBox& bbox, bool value, bool active)
+{
+ for (Int32 x = bbox.min().x(); x <= bbox.max().x(); ++x) {
+ const Index offsetX = (x&DIM-1u)<<2*Log2Dim;
+ for (Int32 y = bbox.min().y(); y <= bbox.max().y(); ++y) {
+ const Index offsetXY = offsetX + ((y&DIM-1u)<< Log2Dim);
+ for (Int32 z = bbox.min().z(); z <= bbox.max().z(); ++z) {
+ const Index offset = offsetXY + (z&DIM-1u);
+ mValueMask.set(offset, active);
+ mBuffer.mData.set(offset, value);
+ }
+ }
+ }
+}
+
+template<Index Log2Dim>
+inline void
+LeafNode<bool, Log2Dim>::fill(const bool& value)
+{
+ mBuffer.fill(value);
+}
+
+template<Index Log2Dim>
+inline void
+LeafNode<bool, Log2Dim>::fill(const bool& value, bool active)
+{
+ mBuffer.fill(value);
+ mValueMask.set(active);
+}
+
+
+////////////////////////////////////////
+
+
+template<Index Log2Dim>
+template<typename DenseT>
+inline void
+LeafNode<bool, Log2Dim>::copyToDense(const CoordBBox& bbox, DenseT& dense) const
+{
+ const size_t xStride = dense.xStride(), yStride = dense.yStride();// zStride=1
+ const Coord& min = dense.bbox().min();
+ bool* t0 = dense.data() + bbox.min()[2]-min[2];//target array
+ const Int32 n0 = bbox.min()[2]&DIM-1u;
+ for (Int32 x = bbox.min()[0], ex=bbox.max()[0]+1; x<ex; ++x) {
+ bool* t1 = t0 + xStride*(x-min[0]);
+ const Int32 n1 = n0 + ((x&DIM-1u)<<2*LOG2DIM);
+ for (Int32 y = bbox.min()[1], ey=bbox.max()[1]+1; y<ey; ++y) {
+ bool* t2 = t1 + yStride*(y-min[1]);
+ Int32 n2 = n1 + ((y&DIM-1u)<<LOG2DIM) ;
+ for (Int32 z = bbox.min()[2], ez=bbox.max()[2]+1; z<ez ; ++z) {
+ *t2++ = mBuffer.mData.isOn(n2++);
+ }
+ }
+ }
+}
+
+
+template<Index Log2Dim>
+template<typename DenseT>
+inline void
+LeafNode<bool, Log2Dim>::copyFromDense(const CoordBBox& bbox, const DenseT& dense,
+ bool background, bool tolerance)
+{
+ const size_t xStride = dense.xStride(), yStride = dense.yStride();// zStride=1
+ const Coord& min = dense.bbox().min();
+ const bool* s0 = dense.data() + bbox.min()[2]-min[2];//source
+ const Int32 n0 = bbox.min()[2]&DIM-1u;
+ for (Int32 x = bbox.min()[0], ex=bbox.max()[0]+1; x<ex; ++x) {
+ const bool* s1 = s0 + xStride*(x-min[0]);
+ const Int32 n1 = n0 + ((x&DIM-1u)<<2*LOG2DIM);
+ for (Int32 y = bbox.min()[1], ey=bbox.max()[1]+1; y<ey; ++y) {
+ const bool* s2 = s1 + yStride*(y-min[1]);
+ Int32 n2 = n1 + ((y&DIM-1u)<<LOG2DIM) ;
+ for (Int32 z = bbox.min()[2], ez=bbox.max()[2]+1; z<ez ; ++z, ++n2, ++s2) {
+ // Note: if tolerance is true (i.e., 1), then all boolean values compare equal.
+ if (tolerance || background == *s2) {
+ mValueMask.setOff(n2);
+ mBuffer.mData.set(n2, background);
+ } else {
+ mValueMask.setOn(n2);
+ mBuffer.mData.set(n2, *s2);
+ }
+ }
+ }
+ }
+}
+
+
+////////////////////////////////////////
+
+
+template<Index Log2Dim>
+template<typename CombineOp>
+inline void
+LeafNode<bool, Log2Dim>::combine(const LeafNode& other, CombineOp& op)
+{
+ CombineArgs<bool> args;
+ for (Index i = 0; i < SIZE; ++i) {
+ bool result = false, aVal = mBuffer.mData.isOn(i), bVal = other.mBuffer.mData.isOn(i);
+ op(args.setARef(aVal)
+ .setAIsActive(mValueMask.isOn(i))
+ .setBRef(bVal)
+ .setBIsActive(other.mValueMask.isOn(i))
+ .setResultRef(result));
+ mValueMask.set(i, args.resultIsActive());
+ mBuffer.mData.set(i, result);
+ }
+}
+
+
+template<Index Log2Dim>
+template<typename CombineOp>
+inline void
+LeafNode<bool, Log2Dim>::combine(bool value, bool valueIsActive, CombineOp& op)
+{
+ CombineArgs<bool> args;
+ args.setBRef(value).setBIsActive(valueIsActive);
+ for (Index i = 0; i < SIZE; ++i) {
+ bool result = false, aVal = mBuffer.mData.isOn(i);
+ op(args.setARef(aVal)
+ .setAIsActive(mValueMask.isOn(i))
+ .setResultRef(result));
+ mValueMask.set(i, args.resultIsActive());
+ mBuffer.mData.set(i, result);
+ }
+}
+
+
+////////////////////////////////////////
+
+
+template<Index Log2Dim>
+template<typename CombineOp>
+inline void
+LeafNode<bool, Log2Dim>::combine2(const LeafNode& other, bool value,
+ bool valueIsActive, CombineOp& op)
+{
+ CombineArgs<bool> args;
+ args.setBRef(value).setBIsActive(valueIsActive);
+ for (Index i = 0; i < SIZE; ++i) {
+ bool result = false, aVal = other.mBuffer.mData.isOn(i);
+ op(args.setARef(aVal)
+ .setAIsActive(other.mValueMask.isOn(i))
+ .setResultRef(result));
+ mValueMask.set(i, args.resultIsActive());
+ mBuffer.mData.set(i, result);
+ }
+}
+
+
+template<Index Log2Dim>
+template<typename CombineOp>
+inline void
+LeafNode<bool, Log2Dim>::combine2(bool value, const LeafNode& other,
+ bool valueIsActive, CombineOp& op)
+{
+ CombineArgs<bool> args;
+ args.setARef(value).setAIsActive(valueIsActive);
+ for (Index i = 0; i < SIZE; ++i) {
+ bool result = false, bVal = other.mBuffer.mData.isOn(i);
+ op(args.setBRef(bVal)
+ .setBIsActive(other.mValueMask.isOn(i))
+ .setResultRef(result));
+ mValueMask.set(i, args.resultIsActive());
+ mBuffer.mData.set(i, result);
+ }
+}
+
+
+template<Index Log2Dim>
+template<typename CombineOp>
+inline void
+LeafNode<bool, Log2Dim>::combine2(const LeafNode& b0, const LeafNode& b1, CombineOp& op)
+{
+ CombineArgs<bool> args;
+ for (Index i = 0; i < SIZE; ++i) {
+ // Default behavior: output voxel is active if either input voxel is active.
+ mValueMask.set(i, b0.mValueMask.isOn(i) || b1.mValueMask.isOn(i));
+
+ bool result = false, b0Val = b0.mBuffer.mData.isOn(i), b1Val = b1.mBuffer.mData.isOn(i);
+ op(args.setARef(b0Val)
+ .setAIsActive(b0.mValueMask.isOn(i))
+ .setBRef(b1Val)
+ .setBIsActive(b1.mValueMask.isOn(i))
+ .setResultRef(result));
+ mValueMask.set(i, args.resultIsActive());
+ mBuffer.mData.set(i, result);
+ }
+}
+
+
+////////////////////////////////////////
+
+template<Index Log2Dim>
+template<typename BBoxOp>
+inline void
+LeafNode<bool, Log2Dim>::visitActiveBBox(BBoxOp& op) const
+{
+ if (op.template descent<LEVEL>()) {
+ for (ValueOnCIter i=this->cbeginValueOn(); i; ++i) {
+#ifdef _MSC_VER
+ op.operator()<LEVEL>(CoordBBox(i.getCoord(),1));
+#else
+ op.template operator()<LEVEL>(CoordBBox(i.getCoord(),1));
+#endif
+ }
+ } else {
+#ifdef _MSC_VER
+ op.operator()<LEVEL>(this->getNodeBoundingBox());
+#else
+ op.template operator()<LEVEL>(this->getNodeBoundingBox());
+#endif
+ }
+}
+
+
+template<Index Log2Dim>
+template<typename VisitorOp>
+inline void
+LeafNode<bool, Log2Dim>::visit(VisitorOp& op)
+{
+ doVisit<LeafNode, VisitorOp, ChildAllIter>(*this, op);
+}
+
+
+template<Index Log2Dim>
+template<typename VisitorOp>
+inline void
+LeafNode<bool, Log2Dim>::visit(VisitorOp& op) const
+{
+ doVisit<const LeafNode, VisitorOp, ChildAllCIter>(*this, op);
+}
+
+
+template<Index Log2Dim>
+template<typename NodeT, typename VisitorOp, typename ChildAllIterT>
+inline void
+LeafNode<bool, Log2Dim>::doVisit(NodeT& self, VisitorOp& op)
+{
+ for (ChildAllIterT iter = self.beginChildAll(); iter; ++iter) {
+ op(iter);
+ }
+}
+
+
+////////////////////////////////////////
+
+
+template<Index Log2Dim>
+template<typename OtherLeafNodeType, typename VisitorOp>
+inline void
+LeafNode<bool, Log2Dim>::visit2Node(OtherLeafNodeType& other, VisitorOp& op)
+{
+ doVisit2Node<LeafNode, OtherLeafNodeType, VisitorOp, ChildAllIter,
+ typename OtherLeafNodeType::ChildAllIter>(*this, other, op);
+}
+
+
+template<Index Log2Dim>
+template<typename OtherLeafNodeType, typename VisitorOp>
+inline void
+LeafNode<bool, Log2Dim>::visit2Node(OtherLeafNodeType& other, VisitorOp& op) const
+{
+ doVisit2Node<const LeafNode, OtherLeafNodeType, VisitorOp, ChildAllCIter,
+ typename OtherLeafNodeType::ChildAllCIter>(*this, other, op);
+}
+
+
+template<Index Log2Dim>
+template<
+ typename NodeT,
+ typename OtherNodeT,
+ typename VisitorOp,
+ typename ChildAllIterT,
+ typename OtherChildAllIterT>
+inline void
+LeafNode<bool, Log2Dim>::doVisit2Node(NodeT& self, OtherNodeT& other, VisitorOp& op)
+{
+ // Allow the two nodes to have different ValueTypes, but not different dimensions.
+ BOOST_STATIC_ASSERT(OtherNodeT::SIZE == NodeT::SIZE);
+ BOOST_STATIC_ASSERT(OtherNodeT::LEVEL == NodeT::LEVEL);
+
+ ChildAllIterT iter = self.beginChildAll();
+ OtherChildAllIterT otherIter = other.beginChildAll();
+
+ for ( ; iter && otherIter; ++iter, ++otherIter) {
+ op(iter, otherIter);
+ }
+}
+
+
+////////////////////////////////////////
+
+
+template<Index Log2Dim>
+template<typename IterT, typename VisitorOp>
+inline void
+LeafNode<bool, Log2Dim>::visit2(IterT& otherIter, VisitorOp& op, bool otherIsLHS)
+{
+ doVisit2<LeafNode, VisitorOp, ChildAllIter, IterT>(*this, otherIter, op, otherIsLHS);
+}
+
+
+template<Index Log2Dim>
+template<typename IterT, typename VisitorOp>
+inline void
+LeafNode<bool, Log2Dim>::visit2(IterT& otherIter, VisitorOp& op, bool otherIsLHS) const
+{
+ doVisit2<const LeafNode, VisitorOp, ChildAllCIter, IterT>(*this, otherIter, op, otherIsLHS);
+}
+
+
+template<Index Log2Dim>
+template<
+ typename NodeT,
+ typename VisitorOp,
+ typename ChildAllIterT,
+ typename OtherChildAllIterT>
+inline void
+LeafNode<bool, Log2Dim>::doVisit2(NodeT& self, OtherChildAllIterT& otherIter,
+ VisitorOp& op, bool otherIsLHS)
+{
+ if (!otherIter) return;
+
+ if (otherIsLHS) {
+ for (ChildAllIterT iter = self.beginChildAll(); iter; ++iter) {
+ op(otherIter, iter);
+ }
+ } else {
+ for (ChildAllIterT iter = self.beginChildAll(); iter; ++iter) {
+ op(iter, otherIter);
+ }
+ }
+}
+
+} // namespace tree
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_TREE_LEAFNODEBOOL_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/tree/NodeUnion.h b/extern/openvdb/internal/openvdb/tree/NodeUnion.h
new file mode 100644
index 00000000000..f17d32a0a60
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/tree/NodeUnion.h
@@ -0,0 +1,137 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+//
+/// @file NodeUnion.h
+///
+/// @author Peter Cucka
+///
+/// NodeUnion is a templated helper class that controls access to either
+/// the child node pointer or the fill value for a particular element
+/// of a root or internal node. For space efficiency, the child pointer
+/// and the fill value are unioned, since the two are never in use
+/// simultaneously. Template specializations of NodeUnion allow for
+/// fill values of either POD (int, float, pointer, etc.) or class
+/// (std::string, math::Vec, etc.) types. (The latter cannot be
+/// stored directly in a union.)
+
+#ifndef OPENVDB_TREE_NODEUNION_HAS_BEEN_INCLUDED
+#define OPENVDB_TREE_NODEUNION_HAS_BEEN_INCLUDED
+
+#include <boost/type_traits/is_class.hpp>
+#include <openvdb/version.h>
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace tree {
+
+// Internal implementation of a union of a child node pointer and a fill value
+template<bool ValueIsClass, class ValueT, class ChildT> class NodeUnionImpl;
+
+
+// Partial specialization for fill values of non-class types
+// (int, float, pointer, etc.) that stores elements by value
+template<typename ValueT, typename ChildT>
+class NodeUnionImpl</*ValueIsClass=*/false, ValueT, ChildT>
+{
+private:
+ union { ChildT* child; ValueT value; } mUnion;
+
+public:
+ NodeUnionImpl() { setChild(NULL); }
+
+ ChildT* getChild() const { return mUnion.child; }
+ const ValueT& getValue() const { return mUnion.value; }
+ void setChild(ChildT* child) { mUnion.child = child; }
+ void setValue(const ValueT& val) { mUnion.value = val; }
+};
+
+
+// Partial specialization for fill values of class types (std::string,
+// math::Vec, etc.) that stores elements by pointer
+template<typename ValueT, typename ChildT>
+class NodeUnionImpl</*ValueIsClass=*/true, ValueT, ChildT>
+{
+private:
+ union { ChildT* child; ValueT* value; } mUnion;
+ bool mHasChild;
+
+public:
+ NodeUnionImpl(): mHasChild(true) { setChild(NULL); }
+ NodeUnionImpl(const NodeUnionImpl& other)
+ {
+ if (other.mHasChild) setChild(other.getChild());
+ else setValue(other.getValue());
+ }
+ NodeUnionImpl& operator=(const NodeUnionImpl& other)
+ {
+ if (other.mHasChild) setChild(other.getChild());
+ else setValue(other.getValue());
+ }
+ ~NodeUnionImpl() { setChild(NULL); }
+
+ ChildT* getChild() const ///< @todo throw if !mHasChild?
+ { return mHasChild ? mUnion.child : NULL; }
+ void setChild(ChildT* child)
+ {
+ if (!mHasChild) delete mUnion.value;
+ mUnion.child = child;
+ mHasChild = true;
+ }
+
+ const ValueT& getValue() const { return *mUnion.value; }
+ ///< @todo throw if mHasChild?
+ void setValue(const ValueT& val)
+ {
+ /// @todo To minimize storage across nodes, intern and reuse
+ /// common fill values, using, e.g., boost::flyweight.
+ if (!mHasChild) delete mUnion.value;
+ mUnion.value = new ValueT(val);
+ mHasChild = false;
+ }
+};
+
+
+template<typename ValueT, typename ChildT>
+struct NodeUnion: public NodeUnionImpl<
+ boost::is_class<ValueT>::value, ValueT, ChildT>
+{
+ NodeUnion() {}
+};
+
+} // namespace tree
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_TREE_NODEUNION_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/tree/RootNode.h b/extern/openvdb/internal/openvdb/tree/RootNode.h
new file mode 100644
index 00000000000..03ecb63b01c
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/tree/RootNode.h
@@ -0,0 +1,2649 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+///
+/// @file RootNode.h
+///
+/// @brief The root node of an OpenVDB tree
+
+#ifndef OPENVDB_TREE_ROOTNODE_HAS_BEEN_INCLUDED
+#define OPENVDB_TREE_ROOTNODE_HAS_BEEN_INCLUDED
+
+#include <map>
+#include <set>
+#include <sstream>
+#include <boost/type_traits/remove_const.hpp>
+#include <openvdb/Exceptions.h>
+#include <openvdb/Types.h>
+#include <openvdb/io/Compression.h> // for truncateRealToHalf()
+#include <openvdb/math/Math.h> // for isZero(), isExactlyEqual(), etc.
+#include <openvdb/math/BBox.h>
+#include <openvdb/util/NodeMasks.h> // for backward compatibility only (see readTopology())
+#include <openvdb/version.h>
+#include "Util.h"
+
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace tree {
+
+template<typename ChildType>
+class RootNode
+{
+public:
+ typedef ChildType ChildNodeType;
+ typedef typename ChildType::LeafNodeType LeafNodeType;
+ typedef typename ChildType::ValueType ValueType;
+
+ static const Index LEVEL = 1 + ChildType::LEVEL; // level 0 = leaf
+
+ /// @brief ValueConverter<T>::Type is the type of a RootNode having the same
+ /// child hierarchy as this node but a different value type, T.
+ template<typename OtherValueType>
+ struct ValueConverter {
+ typedef RootNode<typename ChildType::template ValueConverter<OtherValueType>::Type> Type;
+ };
+
+
+ /// Construct a new tree with a background value of 0.
+ RootNode();
+
+ /// Construct a new tree with the given background value.
+ explicit RootNode(const ValueType& background);
+
+ RootNode(const RootNode& other) { *this = other; }
+
+ /// @brief Topology copy constructor that guarantees the
+ /// configuration of the constructed tree is topologically
+ /// identical to the other tree
+ ///
+ /// @details Reproduce the topology and active states of the other tree
+ /// (which may have a different ValueType), but don't copy values.
+ /// All values that are active in the other tree are set to the foreground value
+ /// and all other values to the background value.
+ /// @param other the root node of a tree having (possibly) a different ValueType
+ /// @param background the value to which inactive tiles and voxels are initialized
+ /// @param foreground the value to which active tiles and voxels are initialized
+ template<typename OtherChildType>
+ RootNode(const RootNode<OtherChildType>& other,
+ const ValueType& background, const ValueType& foreground,
+ TopologyCopy);
+
+ /// @brief Topology copy constructor that guarantees the
+ /// configuration of the constructed tree is topologically
+ /// identical to the other tree
+ ///
+ /// @note this copy constructor is generally faster then the one
+ /// above that takes both a forground and a background value. Its
+ /// main application is multithreading where the topology of
+ /// the output tree exactly matches the input tree.
+ ///
+ /// @details Reproduce the topology and active states of the other node
+ /// (which may have a different ValueType), but don't copy values.
+ /// All values in the constructed tree are set to the background value
+ /// regardless of their active states.
+ /// @param other the root node of a tree having (possibly) a different ValueType
+ /// @param background the value to which inactive tiles and voxels are initialized
+ template<typename OtherChildType>
+ RootNode(const RootNode<OtherChildType>& other, const ValueType& background,
+ TopologyCopy);
+
+ RootNode& operator=(const RootNode& other);
+
+ ~RootNode() { this->clearTable(); }
+
+private:
+ struct Tile {
+ Tile(): value(zeroVal<ValueType>()), active(false) {}
+ Tile(const ValueType& v, bool b): value(v), active(b) {}
+ ValueType value;
+ bool active;
+ };
+
+ // This lightweight struct pairs child pointers and tiles
+ struct NodeStruct {
+ ChildType* child;
+ Tile tile;
+
+ NodeStruct(): child(NULL) {}
+ NodeStruct(ChildType& c): child(&c) {}
+ NodeStruct(const Tile& t): child(NULL), tile(t) {}
+ ~NodeStruct() {} ///< @note doesn't delete child
+
+ bool isChild() const { return child != NULL; }
+ bool isTile() const { return child == NULL; }
+ bool isTileOff() const { return isTile() && !tile.active; }
+ bool isTileOn() const { return isTile() && tile.active; }
+
+ void set(ChildType& c) { delete child; child = &c; }
+ void set(const Tile& t) { delete child; child = NULL; tile = t; }
+ ChildType& steal(const Tile& t) { ChildType* c = child; child = NULL; tile = t; return *c; }
+ };
+
+ typedef std::map<Coord, NodeStruct> MapType;
+ typedef typename MapType::iterator MapIter;
+ typedef typename MapType::const_iterator MapCIter;
+
+ typedef std::set<Coord> CoordSet;
+ typedef typename CoordSet::iterator CoordSetIter;
+ typedef typename CoordSet::const_iterator CoordSetCIter;
+
+ static void setTile(const MapIter& i, const Tile& t) { i->second.set(t); }
+ static void setChild(const MapIter& i, ChildType& c) { i->second.set(c); }
+ static Tile& getTile(const MapIter& i) { return i->second.tile; }
+ static const Tile& getTile(const MapCIter& i) { return i->second.tile; }
+ static ChildType& getChild(const MapIter& i) { return *(i->second.child); }
+ static const ChildType& getChild(const MapCIter& i) { return *(i->second.child); }
+ static ChildType& stealChild(const MapIter& i, const Tile& t) {return i->second.steal(t);}
+ static const ChildType& stealChild(const MapCIter& i,const Tile& t) {return i->second.steal(t);}
+
+ static bool isChild(const MapCIter& i) { return i->second.isChild(); }
+ static bool isChild(const MapIter& i) { return i->second.isChild(); }
+ static bool isTile(const MapCIter& i) { return i->second.isTile(); }
+ static bool isTile(const MapIter& i) { return i->second.isTile(); }
+ static bool isTileOff(const MapCIter& i) { return i->second.isTileOff(); }
+ static bool isTileOff(const MapIter& i) { return i->second.isTileOff(); }
+ static bool isTileOn(const MapCIter& i) { return i->second.isTileOn(); }
+ static bool isTileOn(const MapIter& i) { return i->second.isTileOn(); }
+
+ struct NullPred {
+ static inline bool test(const MapIter&) { return true; }
+ static inline bool test(const MapCIter&) { return true; }
+ };
+ struct ValueOnPred {
+ static inline bool test(const MapIter& i) { return isTileOn(i); }
+ static inline bool test(const MapCIter& i) { return isTileOn(i); }
+ };
+ struct ValueOffPred {
+ static inline bool test(const MapIter& i) { return isTileOff(i); }
+ static inline bool test(const MapCIter& i) { return isTileOff(i); }
+ };
+ struct ValueAllPred {
+ static inline bool test(const MapIter& i) { return isTile(i); }
+ static inline bool test(const MapCIter& i) { return isTile(i); }
+ };
+ struct ChildOnPred {
+ static inline bool test(const MapIter& i) { return isChild(i); }
+ static inline bool test(const MapCIter& i) { return isChild(i); }
+ };
+ struct ChildOffPred {
+ static inline bool test(const MapIter& i) { return isTile(i); }
+ static inline bool test(const MapCIter& i) { return isTile(i); }
+ };
+
+ template<typename _RootNodeT, typename _MapIterT, typename FilterPredT>
+ class BaseIter
+ {
+ public:
+ typedef _RootNodeT RootNodeT;
+ typedef _MapIterT MapIterT; // either MapIter or MapCIter
+
+ bool operator==(const BaseIter& other) const
+ {
+ return (mParentNode == other.mParentNode) && (mIter == other.mIter);
+ }
+ bool operator!=(const BaseIter& other) const { return !(*this == other); }
+
+ RootNodeT* getParentNode() const { return mParentNode; }
+ /// Return a reference to the node over which this iterator iterates.
+ RootNodeT& parent() const
+ {
+ if (!mParentNode) OPENVDB_THROW(ValueError, "iterator references a null parent node");
+ return *mParentNode;
+ }
+
+ bool test() const { assert(mParentNode); return mIter != mParentNode->mTable.end(); }
+ operator bool() const { return this->test(); }
+
+ void increment() { ++mIter; this->skip(); }
+ bool next() { this->increment(); return this->test(); }
+ void increment(Index n) { for (int i = 0; i < n && this->next(); ++i) {} }
+
+ /// @brief Return this iterator's position as an offset from
+ /// the beginning of the parent node's map.
+ Index pos() const
+ {
+ return !mParentNode ? 0U : Index(std::distance(mParentNode->mTable.begin(), mIter));
+ }
+
+ bool isValueOn() const { return RootNodeT::isTileOn(mIter); }
+ bool isValueOff() const { return RootNodeT::isTileOff(mIter); }
+ void setValueOn(bool on = true) const { mIter->second.tile.active = on; }
+ void setValueOff() const { mIter->second.tile.active = false; }
+
+ /// Return the coordinates of the item to which this iterator is pointing.
+ Coord getCoord() const { return mIter->first; }
+ /// Return in @a xyz the coordinates of the item to which this iterator is pointing.
+ void getCoord(Coord& xyz) const { xyz = this->getCoord(); }
+
+ protected:
+ BaseIter(): mParentNode(NULL) {}
+ BaseIter(RootNodeT& parent, const MapIterT& iter): mParentNode(&parent), mIter(iter) {}
+
+ void skip() { while (this->test() && !FilterPredT::test(mIter)) ++mIter; }
+
+ RootNodeT* mParentNode;
+ MapIterT mIter;
+ }; // BaseIter
+
+ template<typename RootNodeT, typename MapIterT, typename FilterPredT, typename ChildNodeT>
+ class ChildIter: public BaseIter<RootNodeT, MapIterT, FilterPredT>
+ {
+ public:
+ typedef BaseIter<RootNodeT, MapIterT, FilterPredT> BaseT;
+ typedef RootNodeT NodeType;
+ typedef NodeType ValueType;
+ typedef ChildNodeT ChildNodeType;
+ typedef typename boost::remove_const<NodeType>::type NonConstNodeType;
+ typedef typename boost::remove_const<ValueType>::type NonConstValueType;
+ typedef typename boost::remove_const<ChildNodeType>::type NonConstChildNodeType;
+ using BaseT::mIter;
+
+ ChildIter() {}
+ ChildIter(RootNodeT& parent, const MapIterT& iter): BaseT(parent, iter) { BaseT::skip(); }
+
+ ChildIter& operator++() { BaseT::increment(); return *this; }
+
+ ChildNodeT& getValue() const { return getChild(mIter); }
+ ChildNodeT& operator*() const { return this->getValue(); }
+ ChildNodeT* operator->() const { return &this->getValue(); }
+ }; // ChildIter
+
+ template<typename RootNodeT, typename MapIterT, typename FilterPredT, typename ValueT>
+ class ValueIter: public BaseIter<RootNodeT, MapIterT, FilterPredT>
+ {
+ public:
+ typedef BaseIter<RootNodeT, MapIterT, FilterPredT> BaseT;
+ typedef RootNodeT NodeType;
+ typedef ValueT ValueType;
+ typedef typename boost::remove_const<NodeType>::type NonConstNodeType;
+ typedef typename boost::remove_const<ValueT>::type NonConstValueType;
+ using BaseT::mIter;
+
+ ValueIter() {}
+ ValueIter(RootNodeT& parent, const MapIterT& iter): BaseT(parent, iter) { BaseT::skip(); }
+
+ ValueIter& operator++() { BaseT::increment(); return *this; }
+
+ ValueT& getValue() const { return getTile(mIter).value; }
+ ValueT& operator*() const { return this->getValue(); }
+ ValueT* operator->() const { return &(this->getValue()); }
+
+ void setValue(const ValueT& v) const { assert(isTile(mIter)); getTile(mIter).value = v; }
+ }; // ValueIter
+
+ template<typename RootNodeT, typename MapIterT, typename ChildNodeT, typename ValueT>
+ class DenseIter: public BaseIter<RootNodeT, MapIterT, NullPred>
+ {
+ public:
+ typedef BaseIter<RootNodeT, MapIterT, NullPred> BaseT;
+ typedef RootNodeT NodeType;
+ typedef ValueT ValueType;
+ typedef ChildNodeT ChildNodeType;
+ typedef typename boost::remove_const<NodeType>::type NonConstNodeType;
+ typedef typename boost::remove_const<ValueT>::type NonConstValueType;
+ typedef typename boost::remove_const<ChildNodeT>::type NonConstChildNodeType;
+ using BaseT::mIter;
+
+ DenseIter() {}
+ DenseIter(RootNodeT& parent, const MapIterT& iter): BaseT(parent, iter) {}
+
+ DenseIter& operator++() { BaseT::increment(); return *this; }
+
+ bool isChildNode() const { return isChild(mIter); }
+
+ ChildNodeT* probeChild(NonConstValueType& value) const
+ {
+ if (isChild(mIter)) return &getChild(mIter);
+ value = getTile(mIter).value;
+ return NULL;
+ }
+ bool probeChild(ChildNodeT*& child, NonConstValueType& value) const
+ {
+ child = this->probeChild(value);
+ return child != NULL;
+ }
+ bool probeValue(NonConstValueType& value) const { return !this->probeChild(value); }
+
+ void setChild(ChildNodeT& c) const { RootNodeT::setChild(mIter, c); }
+ void setChild(ChildNodeT* c) const { assert(c != NULL); RootNodeT::setChild(mIter, *c); }
+ void setValue(const ValueT& v) const
+ {
+ if (isTile(mIter)) getTile(mIter).value = v;
+ /// @internal For consistency with iterators for other node types
+ /// (see, e.g., InternalNode::DenseIter::unsetItem()), we don't call
+ /// setTile() here, because that would also delete the child.
+ else stealChild(mIter, Tile(v, /*active=*/true));
+ }
+ }; // DenseIter
+
+public:
+ typedef ChildIter<RootNode, MapIter, ChildOnPred, ChildType> ChildOnIter;
+ typedef ChildIter<const RootNode, MapCIter, ChildOnPred, const ChildType> ChildOnCIter;
+ typedef ValueIter<RootNode, MapIter, ChildOffPred, const ValueType> ChildOffIter;
+ typedef ValueIter<const RootNode, MapCIter, ChildOffPred, ValueType> ChildOffCIter;
+ typedef DenseIter<RootNode, MapIter, ChildType, ValueType> ChildAllIter;
+ typedef DenseIter<const RootNode, MapCIter, const ChildType, const ValueType> ChildAllCIter;
+
+ typedef ValueIter<RootNode, MapIter, ValueOnPred, ValueType> ValueOnIter;
+ typedef ValueIter<const RootNode, MapCIter, ValueOnPred, const ValueType> ValueOnCIter;
+ typedef ValueIter<RootNode, MapIter, ValueOffPred, ValueType> ValueOffIter;
+ typedef ValueIter<const RootNode, MapCIter, ValueOffPred, const ValueType> ValueOffCIter;
+ typedef ValueIter<RootNode, MapIter, ValueAllPred, ValueType> ValueAllIter;
+ typedef ValueIter<const RootNode, MapCIter, ValueAllPred, const ValueType> ValueAllCIter;
+
+
+ ChildOnCIter cbeginChildOn() const { return ChildOnCIter(*this, mTable.begin()); }
+ ChildOffCIter cbeginChildOff() const { return ChildOffCIter(*this, mTable.begin()); }
+ ChildAllCIter cbeginChildAll() const { return ChildAllCIter(*this, mTable.begin()); }
+ ChildOnCIter beginChildOn() const { return cbeginChildOn(); }
+ ChildOffCIter beginChildOff() const { return cbeginChildOff(); }
+ ChildAllCIter beginChildAll() const { return cbeginChildAll(); }
+ ChildOnIter beginChildOn() { return ChildOnIter(*this, mTable.begin()); }
+ ChildOffIter beginChildOff() { return ChildOffIter(*this, mTable.begin()); }
+ ChildAllIter beginChildAll() { return ChildAllIter(*this, mTable.begin()); }
+
+ ValueOnCIter cbeginValueOn() const { return ValueOnCIter(*this, mTable.begin()); }
+ ValueOffCIter cbeginValueOff() const { return ValueOffCIter(*this, mTable.begin()); }
+ ValueAllCIter cbeginValueAll() const { return ValueAllCIter(*this, mTable.begin()); }
+ ValueOnCIter beginValueOn() const { return cbeginValueOn(); }
+ ValueOffCIter beginValueOff() const { return cbeginValueOff(); }
+ ValueAllCIter beginValueAll() const { return cbeginValueAll(); }
+ ValueOnIter beginValueOn() { return ValueOnIter(*this, mTable.begin()); }
+ ValueOffIter beginValueOff() { return ValueOffIter(*this, mTable.begin()); }
+ ValueAllIter beginValueAll() { return ValueAllIter(*this, mTable.begin()); }
+
+ /// Return the total amount of memory in bytes occupied by this node and its children.
+ Index64 memUsage() const;
+
+ /// @brief Expand the specified bbox so it includes the active tiles of
+ /// this root node as well as all the active values in its child nodes.
+ void evalActiveVoxelBoundingBox(CoordBBox& bbox) const;
+
+ /// Return the bounding box of this RootNode, i.e., an infinite bounding box.
+ static CoordBBox getNodeBoundingBox() { return CoordBBox::inf(); }
+
+ /// @brief Change inactive tiles or voxels with a value equal to +/- the
+ /// old background to the specified value (with the same sign). Active values
+ /// are unchanged.
+ void setBackground(const ValueType& value);
+ /// Return the background value
+ const ValueType& background() const { return mBackground; }
+ /// @deprecated Use background()
+ OPENVDB_DEPRECATED ValueType getBackground() const { return mBackground; }
+
+ /// Return @c true if the given tile is inactive and has the background value.
+ bool isBackgroundTile(const Tile&) const;
+ //@{
+ /// Return @c true if the given iterator points to an inactive tile with the background value.
+ bool isBackgroundTile(const MapIter&) const;
+ bool isBackgroundTile(const MapCIter&) const;
+ //@}
+
+ /// Return the number of background tiles.
+ size_t numBackgroundTiles() const;
+ /// @brief Remove all background tiles.
+ /// @return the number of tiles removed.
+ size_t eraseBackgroundTiles();
+ void clear() { this->clearTable(); }
+
+ /// Return @c true if this node's table is either empty or contains only background tiles.
+ bool empty() const { return mTable.size() == numBackgroundTiles(); }
+
+ /// @brief Expand this node's table so that (x, y, z) is included in the index range.
+ /// @return @c true if an expansion was performed (i.e., if (x, y, z) was not already
+ /// included in the index range).
+ bool expand(const Coord& xyz);
+
+ static Index getLevel() { return LEVEL; }
+ static void getNodeLog2Dims(std::vector<Index>& dims);
+ static Index getChildDim() { return ChildType::DIM; }
+
+ /// Return the number of entries in this node's table.
+ Index getTableSize() const { return mTable.size(); }
+
+ Index getWidth() const { return this->getMaxIndex()[0] - this->getMinIndex()[0]; }
+ Index getHeight() const { return this->getMaxIndex()[1] - this->getMinIndex()[1]; }
+ Index getDepth() const { return this->getMaxIndex()[2] - this->getMinIndex()[2]; }
+
+ /// Return the smallest index of the current tree.
+ Coord getMinIndex() const;
+ /// Return the largest index of the current tree.
+ Coord getMaxIndex() const;
+ /// Return the current index range. Both min and max are inclusive.
+ void getIndexRange(CoordBBox& bbox) const;
+
+ /// @brief Return @c true if the given tree has the same node and active value
+ /// topology as this tree (but possibly a different @c ValueType).
+ template<typename OtherChildType>
+ bool hasSameTopology(const RootNode<OtherChildType>& other) const;
+
+ /// Return @c false if the other node's dimensions don't match this node's.
+ template<typename OtherChildType>
+ static bool hasSameConfiguration(const RootNode<OtherChildType>& other);
+
+ Index32 leafCount() const;
+ Index32 nonLeafCount() const;
+ Index64 onVoxelCount() const;
+ Index64 offVoxelCount() const;
+ Index64 onLeafVoxelCount() const;
+ Index64 offLeafVoxelCount() const;
+
+ bool isValueOn(const Coord& xyz) const;
+
+ bool hasActiveTiles() const;
+
+ const ValueType& getValue(const Coord& xyz) const;
+ bool probeValue(const Coord& xyz, ValueType& value) const;
+
+ /// @brief Return the tree depth (0 = root) at which the value of voxel (x, y, z) resides.
+ /// @details If (x, y, z) isn't explicitly represented in the tree (i.e.,
+ /// it is implicitly a background voxel), return -1.
+ int getValueDepth(const Coord& xyz) const;
+
+ /// Set the active state of the voxel at the given coordinates, but don't change its value.
+ void setActiveState(const Coord& xyz, bool on);
+
+ /// Mark the voxel at the given coordinates as inactive, but don't change its value.
+ void setValueOff(const Coord& xyz);
+ /// Change the value of the voxel at the given coordinates and mark the voxel as inactive.
+ void setValueOff(const Coord& xyz, const ValueType& value);
+
+ void setValueOn(const Coord& xyz, const ValueType& value);
+ void setValueOnly(const Coord& xyz, const ValueType& value);
+ void setValueOnMin(const Coord& xyz, const ValueType& value);
+ void setValueOnMax(const Coord& xyz, const ValueType& value);
+ void setValueOnSum(const Coord& xyz, const ValueType& value);
+
+ /// @brief Set all voxels within a given box to a constant value, if necessary
+ /// subdividing tiles that intersect the box.
+ /// @param bbox inclusive coordinates of opposite corners of an axis-aligned box
+ /// @param value the value to which to set voxels within the box
+ /// @param active if true, mark voxels within the box as active,
+ /// otherwise mark them as inactive
+ void fill(const CoordBBox& bbox, const ValueType& value, bool active = true);
+
+ /// @brief Copy into a dense grid the values of all voxels, both active and inactive,
+ /// that intersect a given bounding box.
+ ///
+ /// @param bbox inclusive bounding box of the voxels to be copied into the dense grid
+ /// @param dense dense grid with a stride in @e z of one (see tools::Dense
+ /// in tools/Dense.h for the required API)
+ template<typename DenseT>
+ void copyToDense(const CoordBBox& bbox, DenseT& dense) const;
+
+ //
+ // I/O
+ //
+ bool writeTopology(std::ostream&, bool toHalf = false) const;
+ bool readTopology(std::istream&, bool fromHalf = false);
+
+ void writeBuffers(std::ostream&, bool toHalf = false) const;
+ void readBuffers(std::istream&, bool fromHalf = false);
+
+ /// Return the value of the voxel at the given coordinates and, if necessary, update
+ /// the accessor with pointers to the nodes along the path from the root node to
+ /// the node containing the voxel.
+ /// @note Used internally by ValueAccessor.
+ template<typename AccessorT>
+ const ValueType& getValueAndCache(const Coord& xyz, AccessorT&) const;
+ /// Return @c true if the voxel at the given coordinates is active and, if necessary,
+ /// update the accessor with pointers to the nodes along the path from the root node
+ /// to the node containing the voxel.
+ /// @note Used internally by ValueAccessor.
+ template<typename AccessorT>
+ bool isValueOnAndCache(const Coord& xyz, AccessorT&) const;
+
+ /// Change the value of the voxel at the given coordinates and mark it as active.
+ /// If necessary, update the accessor with pointers to the nodes along the path
+ /// from the root node to the node containing the voxel.
+ /// @note Used internally by ValueAccessor.
+ template<typename AccessorT>
+ void setValueAndCache(const Coord& xyz, const ValueType& value, AccessorT&);
+
+ /// Set the value of the voxel at the given coordinate but preserves its active state.
+ /// If necessary, update the accessor with pointers to the nodes along the path
+ /// from the root node to the node containing the voxel.
+ /// @note Used internally by ValueAccessor.
+ template<typename AccessorT>
+ void setValueOnlyAndCache(const Coord& xyz, const ValueType& value, AccessorT&);
+
+ /// Set the value of the voxel at the given coordinates to the sum of its current
+ /// value and the given value, and mark the voxel as active.
+ /// If necessary, update the accessor with pointers to the nodes along the path
+ /// from the root node to the node containing the voxel.
+ /// @note Used internally by ValueAccessor.
+ template<typename AccessorT>
+ void setValueOnSumAndCache(const Coord& xyz, const ValueType& value, AccessorT&);
+
+ /// Change the value of the voxel at the given coordinates and mark it as inactive.
+ /// If necessary, update the accessor with pointers to the nodes along the path
+ /// from the root node to the node containing the voxel.
+ /// @note Used internally by ValueAccessor.
+ template<typename AccessorT>
+ void setValueOffAndCache(const Coord& xyz, const ValueType& value, AccessorT&);
+
+ /// Set the active state of the voxel at the given coordinates without changing its value.
+ /// If necessary, update the accessor with pointers to the nodes along the path
+ /// from the root node to the node containing the voxel.
+ /// @note Used internally by ValueAccessor.
+ template<typename AccessorT>
+ void setActiveStateAndCache(const Coord& xyz, bool on, AccessorT&);
+
+ /// Return @c true if the voxel at the given coordinates is active, assigns the voxel
+ /// value, and, if necessary, update the accessor with pointers to the nodes along
+ /// the path from the root node to the node containing the voxel.
+ /// @note Used internally by ValueAccessor.
+ template<typename AccessorT>
+ bool probeValueAndCache(const Coord& xyz, ValueType& value, AccessorT&) const;
+
+ /// Return the tree depth (0 = root) at which the value of voxel (x, y, z) resides.
+ /// If (x, y, z) isn't explicitly represented in the tree (i.e., it is implicitly
+ /// a background voxel), return -1. If necessary, update the accessor with pointers
+ /// to the nodes along the path from the root node to the node containing the voxel.
+ /// @note Used internally by ValueAccessor.
+ template<typename AccessorT>
+ int getValueDepthAndCache(const Coord& xyz, AccessorT&) const;
+
+ /// Call the @c PruneOp functor for each child node and, if the functor
+ /// returns @c true, prune the node and replace it with a tile.
+ ///
+ /// This method is used to implement all of the various pruning algorithms
+ /// (prune(), pruneInactive(), etc.). It should rarely be called directly.
+ /// @see openvdb/tree/Util.h for the definition of the @c PruneOp functor
+ template<typename PruneOp> void pruneOp(PruneOp&);
+
+ /// @brief Reduce the memory footprint of this tree by replacing with tiles
+ /// any nodes whose values are all the same (optionally to within a tolerance)
+ /// and have the same active state.
+ void prune(const ValueType& tolerance = zeroVal<ValueType>());
+
+ /// @brief Reduce the memory footprint of this tree by replacing with
+ /// tiles of the given value any nodes whose values are all inactive.
+ void pruneInactive(const ValueType&);
+
+ /// @brief Reduce the memory footprint of this tree by replacing with
+ /// background tiles any nodes whose values are all inactive.
+ void pruneInactive();
+
+ void pruneTiles(const ValueType&);
+
+ /// @brief Add the specified leaf to this node, possibly creating a child branch
+ /// in the process. If the leaf node already exists, replace it.
+ void addLeaf(LeafNodeType* leaf);
+
+ /// @brief Same as addLeaf except, if necessary, it update the accessor with pointers
+ /// to the nodes along the path from the root node to the node containing the coordinate.
+ template<typename AccessorT>
+ void addLeafAndCache(LeafNodeType* leaf, AccessorT&);
+
+ /// @brief Return a pointer to the node of type @c NodeT that contains voxel (x, y, z)
+ /// and replace it with a tile of the specified value and state.
+ /// If no such node exists, leave the tree unchanged and return @c NULL.
+ ///
+ /// @note The caller takes ownership of the node and is responsible for deleting it.
+ ///
+ /// @warning Since this method potentially removes nodes and branches of the tree,
+ /// it is important to clear the caches of all ValueAccessors associated with this tree.
+ template<typename NodeT>
+ NodeT* stealNode(const Coord& xyz, const ValueType& value, bool state);
+
+ /// @brief Add a tile at the specified tree level that contains voxel (x, y, z),
+ /// possibly creating a parent branch or deleting a child branch in the process.
+ void addTile(Index level, const Coord& xyz, const ValueType& value, bool state);
+
+ /// @brief Same as addTile() except, if necessary, update the accessor with pointers
+ /// to the nodes along the path from the root node to the node containing (x, y, z).
+ template<typename AccessorT>
+ void addTileAndCache(Index level, const Coord& xyz, const ValueType& value,
+ bool state, AccessorT&);
+
+ /// @brief Return the leaf node that contains voxel (x, y, z).
+ /// If no such node exists, create one, but preserve the values and
+ /// active states of all voxels.
+ ///
+ /// @details Use this method to preallocate a static tree topology
+ /// over which to safely perform multithreaded processing.
+ LeafNodeType* touchLeaf(const Coord& xyz);
+
+ /// @brief Same as touchLeaf except, if necessary, it update the accessor with pointers
+ /// to the nodes along the path from the root node to the node containing the coordinate.
+ template<typename AccessorT>
+ LeafNodeType* touchLeafAndCache(const Coord& xyz, AccessorT& acc);
+
+ /// @brief Return a pointer to the leaf node that contains voxel (x, y, z).
+ /// If no such node exists, return NULL.
+ LeafNodeType* probeLeaf(const Coord& xyz);
+
+ /// @brief Return a const pointer to the leaf node that contains voxel (x, y, z).
+ /// If no such node exists, return NULL.
+ const LeafNodeType* probeConstLeaf(const Coord& xyz) const;
+ const LeafNodeType* probeLeaf(const Coord& xyz) const { return this->probeConstLeaf(xyz); }
+
+ /// @brief Same as probeLeaf except, if necessary, it update the accessor with pointers
+ /// to the nodes along the path from the root node to the node containing the coordinate.
+ template<typename AccessorT>
+ LeafNodeType* probeLeafAndCache(const Coord& xyz, AccessorT& acc);
+
+ /// @brief Same as probeConstLeaf except, if necessary, it update the accessor with pointers
+ /// to the nodes along the path from the root node to the node containing the coordinate.
+ template<typename AccessorT>
+ const LeafNodeType* probeConstLeafAndCache(const Coord& xyz, AccessorT& acc) const;
+ template<typename AccessorT>
+ const LeafNodeType* probeLeafAndCache(const Coord& xyz, AccessorT& acc) const
+ {
+ return this->probeConstLeafAndCache(xyz, acc);
+ }
+
+ /// @brief Set the values of all inactive voxels and tiles of a narrow-band
+ /// level set from the signs of the active voxels, setting outside values to
+ /// +background and inside values to -background.
+ ///
+ /// @note This method should only be used on closed, narrow-band level sets!
+ void signedFloodFill();
+
+ /// @brief Set the values of all inactive voxels and tiles of a narrow-band
+ /// level set from the signs of the active voxels, setting exterior values to
+ /// @a outside and interior values to @a inside. Set the background value
+ /// of this tree to @a outside.
+ ///
+ /// @note This method should only be used on closed, narrow-band level sets!
+ void signedFloodFill(const ValueType& outside, const ValueType& inside);
+
+ /// Move child nodes from the other tree into this tree wherever those nodes
+ /// correspond to constant-value tiles in this tree, and replace leaf-level
+ /// inactive voxels in this tree with corresponding voxels in the other tree
+ /// that are active.
+ /// @note This operation always empties the other tree, whether or not any nodes were moved.
+ void merge(RootNode& other);
+
+ /// Turn active tiles into dense voxels, i.e., into leaf nodes that are entirely active.
+ void voxelizeActiveTiles();
+
+ /// @brief Union this tree's set of active values with the active values
+ /// of the other tree, whose @c ValueType may be different.
+ /// @details The resulting state of a value is active if the corresponding value
+ /// was already active OR if it is active in the other tree. Also, a resulting
+ /// value maps to a voxel if the corresponding value already mapped to a voxel
+ /// OR if it is a voxel in the other tree. Thus, a resulting value can only
+ /// map to a tile if the corresponding value already mapped to a tile
+ /// AND if it is a tile value in other tree.
+ ///
+ /// @note This operation modifies only active states, not values.
+ /// Specifically, active tiles and voxels in this tree are not changed, and
+ /// tiles or voxels that were inactive in this tree but active in the other tree
+ /// are marked as active in this tree but left with their original values.
+ template<typename OtherChildType>
+ void topologyUnion(const RootNode<OtherChildType>& other);
+
+ template<typename CombineOp>
+ void combine(RootNode& other, CombineOp&, bool prune = false);
+
+ template<typename CombineOp>
+ void combine2(const RootNode& other0, const RootNode& other1,
+ CombineOp& op, bool prune = false);
+
+ /// @brief Calls the templated functor BBoxOp with bounding box
+ /// information for all active tiles and leaf nodes in the tree.
+ /// An additional level argument is provided for each callback.
+ ///
+ /// @note The bounding boxes are guarenteed to be non-overlapping.
+ template<typename BBoxOp> void visitActiveBBox(BBoxOp&) const;
+
+ template<typename VisitorOp> void visit(VisitorOp&);
+ template<typename VisitorOp> void visit(VisitorOp&) const;
+
+ template<typename OtherRootNodeType, typename VisitorOp>
+ void visit2(OtherRootNodeType& other, VisitorOp&);
+ template<typename OtherRootNodeType, typename VisitorOp>
+ void visit2(OtherRootNodeType& other, VisitorOp&) const;
+
+private:
+ /// During topology-only construction, access is needed
+ /// to protected/private members of other template instances.
+ template<typename> friend class RootNode;
+
+ /// Currently no-op, but can be used to define empty and delete keys for mTable
+ void initTable() {}
+ inline void clearTable();
+ //@{
+ /// @internal Used by doVisit2().
+ void resetTable(MapType& table) { mTable.swap(table); table.clear(); }
+ void resetTable(const MapType&) const {}
+ //@}
+
+ Index getChildCount() const;
+ Index getTileCount() const;
+ Index getActiveTileCount() const;
+ Index getInactiveTileCount() const;
+
+ /// Return a MapType key for the given coordinates.
+ static Coord coordToKey(const Coord& xyz) { return xyz & ~(ChildType::DIM - 1); }
+
+ /// Insert this node's mTable keys into the given set.
+ void insertKeys(CoordSet&) const;
+
+ /// Return @c true if this node's mTable contains the given key.
+ bool hasKey(const Coord& key) const { return mTable.find(key) != mTable.end(); }
+ //@{
+ /// @brief Look up the given key in this node's mTable.
+ /// @return an iterator pointing to the matching mTable entry or to mTable.end().
+ MapIter findKey(const Coord& key) { return mTable.find(key); }
+ MapCIter findKey(const Coord& key) const { return mTable.find(key); }
+ //@}
+ //@{
+ /// @brief Convert the given coordinates to a key and look the key up in this node's mTable.
+ /// @return an iterator pointing to the matching mTable entry or to mTable.end().
+ MapIter findCoord(const Coord& xyz) { return mTable.find(coordToKey(xyz)); }
+ MapCIter findCoord(const Coord& xyz) const { return mTable.find(coordToKey(xyz)); }
+ //@}
+ /// @brief Convert the given coordinates to a key and look the key up in this node's mTable.
+ /// @details If the key is not found, insert a background tile with that key.
+ /// @return an iterator pointing to the matching mTable entry.
+ MapIter findOrAddCoord(const Coord& xyz);
+
+ template<typename NodeT>
+ NodeT* doStealNode(const Coord&, const ValueType&, bool state);
+
+ /// @throw TypeError if the other node's dimensions don't match this node's.
+ template<typename OtherChildType>
+ static void enforceSameConfiguration(const RootNode<OtherChildType>& other);
+
+ template<typename RootNodeT, typename VisitorOp, typename ChildAllIterT>
+ static inline void doVisit(RootNodeT&, VisitorOp&);
+
+ template<typename RootNodeT, typename OtherRootNodeT, typename VisitorOp,
+ typename ChildAllIterT, typename OtherChildAllIterT>
+ static inline void doVisit2(RootNodeT&, OtherRootNodeT&, VisitorOp&);
+
+
+ MapType mTable;
+ ValueType mBackground;
+}; // end of RootNode class
+
+
+////////////////////////////////////////
+
+
+template<typename ChildT>
+inline
+RootNode<ChildT>::RootNode(): mBackground(zeroVal<ValueType>())
+{
+ this->initTable();
+}
+
+
+template<typename ChildT>
+inline
+RootNode<ChildT>::RootNode(const ValueType& background) : mBackground(background)
+{
+ this->initTable();
+}
+
+
+template<typename ChildT>
+template<typename OtherChildType>
+inline
+RootNode<ChildT>::RootNode(const RootNode<OtherChildType>& other,
+ const ValueType& backgd, const ValueType& foregd, TopologyCopy):
+ mBackground(backgd)
+{
+ typedef RootNode<OtherChildType> OtherRootT;
+
+ /// @todo Can this be avoided with partial specialization?
+ enforceSameConfiguration(other);
+
+ const Tile bgTile(backgd, /*active=*/false), fgTile(foregd, true);
+ this->initTable();
+
+ for (typename OtherRootT::MapCIter i=other.mTable.begin(), e=other.mTable.end(); i != e; ++i) {
+ mTable[i->first] = OtherRootT::isTile(i)
+ ? NodeStruct(OtherRootT::isTileOn(i) ? fgTile : bgTile)
+ : NodeStruct(*(new ChildT(OtherRootT::getChild(i), backgd, foregd, TopologyCopy())));
+ }
+}
+
+
+template<typename ChildT>
+template<typename OtherChildType>
+inline
+RootNode<ChildT>::RootNode(const RootNode<OtherChildType>& other,
+ const ValueType& backgd, TopologyCopy):
+ mBackground(backgd)
+{
+ typedef RootNode<OtherChildType> OtherRootT;
+
+ /// @todo Can this be avoided with partial specialization?
+ enforceSameConfiguration(other);
+
+ const Tile bgTile(backgd, /*active=*/false), fgTile(backgd, true);
+ this->initTable();
+ for (typename OtherRootT::MapCIter i=other.mTable.begin(), e=other.mTable.end(); i != e; ++i) {
+ mTable[i->first] = OtherRootT::isTile(i)
+ ? NodeStruct(OtherRootT::isTileOn(i) ? fgTile : bgTile)
+ : NodeStruct(*(new ChildT(OtherRootT::getChild(i), backgd, TopologyCopy())));
+ }
+}
+
+
+template<typename ChildT>
+inline RootNode<ChildT>&
+RootNode<ChildT>::operator=(const RootNode& other)
+{
+ mBackground = other.mBackground;
+
+ this->clearTable();
+ this->initTable();
+
+ for (MapCIter i = other.mTable.begin(), e = other.mTable.end(); i != e; ++i) {
+ mTable[i->first] =
+ isTile(i) ? NodeStruct(getTile(i)) : NodeStruct(*(new ChildT(getChild(i))));
+ }
+ return *this;
+}
+
+
+////////////////////////////////////////
+
+
+template<typename ChildT>
+inline void
+RootNode<ChildT>::setBackground(const ValueType& background)
+{
+ if (math::isExactlyEqual(background, mBackground)) return;
+
+ // Traverse the tree, replacing occurrences of mBackground with background
+ // and -mBackground with -background.
+ for (MapIter iter=mTable.begin(); iter!=mTable.end(); ++iter) {
+ ChildT *child = iter->second.child;
+ if (child) {
+ child->resetBackground(/*old=*/mBackground, /*new=*/background);
+ } else {
+ Tile& tile = getTile(iter);
+ if (tile.active) continue;//only change inactive tiles
+ if (math::isApproxEqual(tile.value, mBackground)) {
+ tile.value = background;
+ } else if (math::isApproxEqual(tile.value, negative(mBackground))) {
+ tile.value = negative(background);
+ }
+ }
+ }
+ mBackground = background;
+}
+
+
+template<typename ChildT>
+inline bool
+RootNode<ChildT>::isBackgroundTile(const Tile& tile) const
+{
+ return !tile.active && math::isApproxEqual(tile.value, mBackground);
+}
+
+template<typename ChildT>
+inline bool
+RootNode<ChildT>::isBackgroundTile(const MapIter& iter) const
+{
+ return isTileOff(iter) && math::isApproxEqual(getTile(iter).value, mBackground);
+}
+
+template<typename ChildT>
+inline bool
+RootNode<ChildT>::isBackgroundTile(const MapCIter& iter) const
+{
+ return isTileOff(iter) && math::isApproxEqual(getTile(iter).value, mBackground);
+}
+
+
+template<typename ChildT>
+inline size_t
+RootNode<ChildT>::numBackgroundTiles() const
+{
+ size_t count = 0;
+ for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) {
+ if (this->isBackgroundTile(i)) ++count;
+ }
+ return count;
+}
+
+
+template<typename ChildT>
+inline size_t
+RootNode<ChildT>::eraseBackgroundTiles()
+{
+ std::set<Coord> keysToErase;
+ for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) {
+ if (this->isBackgroundTile(i)) keysToErase.insert(i->first);
+ }
+ for (std::set<Coord>::iterator i = keysToErase.begin(), e = keysToErase.end(); i != e; ++i) {
+ mTable.erase(*i);
+ }
+ return keysToErase.size();
+}
+
+
+////////////////////////////////////////
+
+
+template<typename ChildT>
+inline void
+RootNode<ChildT>::insertKeys(CoordSet& keys) const
+{
+ for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) {
+ keys.insert(i->first);
+ }
+}
+
+
+template<typename ChildT>
+inline typename RootNode<ChildT>::MapIter
+RootNode<ChildT>::findOrAddCoord(const Coord& xyz)
+{
+ const Coord key = coordToKey(xyz);
+ std::pair<MapIter, bool> result = mTable.insert(
+ typename MapType::value_type(key, NodeStruct(Tile(mBackground, /*active=*/false))));
+ return result.first;
+}
+
+
+template<typename ChildT>
+inline bool
+RootNode<ChildT>::expand(const Coord& xyz)
+{
+ const Coord key = coordToKey(xyz);
+ std::pair<MapIter, bool> result = mTable.insert(
+ typename MapType::value_type(key, NodeStruct(Tile(mBackground, /*active=*/false))));
+ return result.second; // return true if the key did not already exist
+}
+
+
+////////////////////////////////////////
+
+
+template<typename ChildT>
+inline void
+RootNode<ChildT>::getNodeLog2Dims(std::vector<Index>& dims)
+{
+ dims.push_back(0); // magic number; RootNode has no Log2Dim
+ ChildT::getNodeLog2Dims(dims);
+}
+
+
+template<typename ChildT>
+inline Coord
+RootNode<ChildT>::getMinIndex() const
+{
+ return mTable.empty() ? Coord(0) : mTable.begin()->first;
+}
+
+template<typename ChildT>
+inline Coord
+RootNode<ChildT>::getMaxIndex() const
+{
+ return mTable.empty() ? Coord(0) : mTable.rbegin()->first + Coord(ChildT::DIM - 1);
+}
+
+
+template<typename ChildT>
+inline void
+RootNode<ChildT>::getIndexRange(CoordBBox& bbox) const
+{
+ bbox.min() = this->getMinIndex();
+ bbox.max() = this->getMaxIndex();
+}
+
+
+////////////////////////////////////////
+
+
+template<typename ChildT>
+template<typename OtherChildType>
+inline bool
+RootNode<ChildT>::hasSameTopology(const RootNode<OtherChildType>& other) const
+{
+ typedef RootNode<OtherChildType> OtherRootT;
+ typedef typename OtherRootT::MapType OtherMapT;
+ typedef typename OtherRootT::MapIter OtherIterT;
+ typedef typename OtherRootT::MapCIter OtherCIterT;
+
+ if (!hasSameConfiguration(other)) return false;
+
+ // Create a local copy of the other node's table.
+ OtherMapT copyOfOtherTable = other.mTable;
+
+ // For each entry in this node's table...
+ for (MapCIter thisIter = mTable.begin(); thisIter != mTable.end(); ++thisIter) {
+ if (this->isBackgroundTile(thisIter)) continue; // ignore background tiles
+
+ // Fail if there is no corresponding entry in the other node's table.
+ OtherCIterT otherIter = other.findKey(thisIter->first);
+ if (otherIter == other.mTable.end()) return false;
+
+ // Fail if this entry is a tile and the other is a child or vice-versa.
+ if (isChild(thisIter)) {//thisIter points to a child
+ if (OtherRootT::isTile(otherIter)) return false;
+ // Fail if both entries are children, but the children have different topology.
+ if (!getChild(thisIter).hasSameTopology(&OtherRootT::getChild(otherIter))) return false;
+ } else {//thisIter points to a tile
+ if (OtherRootT::isChild(otherIter)) return false;
+ if (getTile(thisIter).active != OtherRootT::getTile(otherIter).active) return false;
+ }
+
+ // Remove tiles and child nodes with matching topology from
+ // the copy of the other node's table. This is required since
+ // the two root tables can include an arbitrary number of
+ // background tiles and still have the same topology!
+ copyOfOtherTable.erase(otherIter->first);
+ }
+ // Fail if the remaining entries in copyOfOtherTable are not all background tiles.
+ for (OtherIterT i = copyOfOtherTable.begin(), e = copyOfOtherTable.end(); i != e; ++i) {
+ if (!other.isBackgroundTile(i)) return false;
+ }
+ return true;
+}
+
+
+template<typename ChildT>
+template<typename OtherChildType>
+inline bool
+RootNode<ChildT>::hasSameConfiguration(const RootNode<OtherChildType>&)
+{
+ std::vector<Index> thisDims, otherDims;
+ RootNode::getNodeLog2Dims(thisDims);
+ RootNode<OtherChildType>::getNodeLog2Dims(otherDims);
+ return (thisDims == otherDims);
+}
+
+
+template<typename ChildT>
+template<typename OtherChildType>
+inline void
+RootNode<ChildT>::enforceSameConfiguration(const RootNode<OtherChildType>&)
+{
+ std::vector<Index> thisDims, otherDims;
+ RootNode::getNodeLog2Dims(thisDims);
+ RootNode<OtherChildType>::getNodeLog2Dims(otherDims);
+ if (thisDims != otherDims) {
+ std::ostringstream ostr;
+ ostr << "grids have incompatible configurations (" << thisDims[0];
+ for (size_t i = 1, N = thisDims.size(); i < N; ++i) ostr << " x " << thisDims[i];
+ ostr << " vs. " << otherDims[0];
+ for (size_t i = 1, N = otherDims.size(); i < N; ++i) ostr << " x " << otherDims[i];
+ ostr << ")";
+ OPENVDB_THROW(TypeError, ostr.str());
+ }
+}
+
+
+////////////////////////////////////////
+
+
+template<typename ChildT>
+inline Index64
+RootNode<ChildT>::memUsage() const
+{
+ Index64 sum = sizeof(*this);
+ for (MapCIter iter=mTable.begin(); iter!=mTable.end(); ++iter) {
+ if (const ChildT *child = iter->second.child) {
+ sum += child->memUsage();
+ }
+ }
+ return sum;
+}
+
+template<typename ChildT>
+inline void
+RootNode<ChildT>::evalActiveVoxelBoundingBox(CoordBBox& bbox) const
+{
+ for (MapCIter iter=mTable.begin(); iter!=mTable.end(); ++iter) {
+ if (const ChildT *child = iter->second.child) {
+ child->evalActiveVoxelBoundingBox(bbox);
+ } else if (isTileOn(iter)) {
+ bbox.expand(iter->first, ChildT::DIM);
+ }
+ }
+}
+
+
+template<typename ChildT>
+inline void
+RootNode<ChildT>::clearTable()
+{
+ for (MapIter i = mTable.begin(), e = mTable.end(); i != e; ++i) {
+ delete i->second.child;
+ }
+ mTable.clear();
+}
+
+
+template<typename ChildT>
+inline Index
+RootNode<ChildT>::getChildCount() const {
+ Index sum = 0;
+ for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) {
+ if (isChild(i)) ++sum;
+ }
+ return sum;
+}
+
+
+template<typename ChildT>
+inline Index
+RootNode<ChildT>::getTileCount() const
+{
+ Index sum = 0;
+ for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) {
+ if (isTile(i)) ++sum;
+ }
+ return sum;
+}
+
+
+template<typename ChildT>
+inline Index
+RootNode<ChildT>::getActiveTileCount() const
+{
+ Index sum = 0;
+ for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) {
+ if (isTileOn(i)) ++sum;
+ }
+ return sum;
+}
+
+
+template<typename ChildT>
+inline Index
+RootNode<ChildT>::getInactiveTileCount() const
+{
+ Index sum = 0;
+ for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) {
+ if (isTileOff(i)) ++sum;
+ }
+ return sum;
+}
+
+
+template<typename ChildT>
+inline Index32
+RootNode<ChildT>::leafCount() const
+{
+ Index32 sum = 0;
+ for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) {
+ if (isChild(i)) sum += getChild(i).leafCount();
+ }
+ return sum;
+}
+
+
+template<typename ChildT>
+inline Index32
+RootNode<ChildT>::nonLeafCount() const
+{
+ Index32 sum = 1;
+ if (ChildT::LEVEL != 0) {
+ for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) {
+ if (isChild(i)) sum += getChild(i).nonLeafCount();
+ }
+ }
+ return sum;
+}
+
+
+template<typename ChildT>
+inline Index64
+RootNode<ChildT>::onVoxelCount() const
+{
+ Index64 sum = 0;
+ for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) {
+ if (isChild(i)) {
+ sum += getChild(i).onVoxelCount();
+ } else if (isTileOn(i)) {
+ sum += ChildT::NUM_VOXELS;
+ }
+ }
+ return sum;
+}
+
+
+template<typename ChildT>
+inline Index64
+RootNode<ChildT>::offVoxelCount() const
+{
+ Index64 sum = 0;
+ for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) {
+ if (isChild(i)) {
+ sum += getChild(i).offVoxelCount();
+ } else if (isTileOff(i) && !this->isBackgroundTile(i)) {
+ sum += ChildT::NUM_VOXELS;
+ }
+ }
+ return sum;
+}
+
+
+template<typename ChildT>
+inline Index64
+RootNode<ChildT>::onLeafVoxelCount() const
+{
+ Index64 sum = 0;
+ for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) {
+ if (isChild(i)) sum += getChild(i).onLeafVoxelCount();
+ }
+ return sum;
+}
+
+
+template<typename ChildT>
+inline Index64
+RootNode<ChildT>::offLeafVoxelCount() const
+{
+ Index64 sum = 0;
+ for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) {
+ if (isChild(i)) sum += getChild(i).offLeafVoxelCount();
+ }
+ return sum;
+}
+
+
+////////////////////////////////////////
+
+
+template<typename ChildT>
+inline bool
+RootNode<ChildT>::isValueOn(const Coord& xyz) const
+{
+ MapCIter iter = this->findCoord(xyz);
+ if (iter == mTable.end() || isTileOff(iter)) return false;
+ return isTileOn(iter) ? true : getChild(iter).isValueOn(xyz);
+}
+
+template<typename ChildT>
+inline bool
+RootNode<ChildT>::hasActiveTiles() const
+{
+ for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) {
+ if (isChild(i) ? getChild(i).hasActiveTiles() : getTile(i).active) return true;
+ }
+ return false;
+}
+
+template<typename ChildT>
+template<typename AccessorT>
+inline bool
+RootNode<ChildT>::isValueOnAndCache(const Coord& xyz, AccessorT& acc) const
+{
+ MapCIter iter = this->findCoord(xyz);
+ if (iter == mTable.end() || isTileOff(iter)) return false;
+ if (isTileOn(iter)) return true;
+ acc.insert(xyz, &getChild(iter));
+ return getChild(iter).isValueOnAndCache(xyz, acc);
+}
+
+
+template<typename ChildT>
+inline const typename ChildT::ValueType&
+RootNode<ChildT>::getValue(const Coord& xyz) const
+{
+ MapCIter iter = this->findCoord(xyz);
+ return iter == mTable.end() ? mBackground
+ : (isTile(iter) ? getTile(iter).value : getChild(iter).getValue(xyz));
+}
+
+template<typename ChildT>
+template<typename AccessorT>
+inline const typename ChildT::ValueType&
+RootNode<ChildT>::getValueAndCache(const Coord& xyz, AccessorT& acc) const
+{
+ MapCIter iter = this->findCoord(xyz);
+ if (iter == mTable.end()) return mBackground;
+ if (isChild(iter)) {
+ acc.insert(xyz, &getChild(iter));
+ return getChild(iter).getValueAndCache(xyz, acc);
+ }
+ return getTile(iter).value;
+}
+
+
+template<typename ChildT>
+inline int
+RootNode<ChildT>::getValueDepth(const Coord& xyz) const
+{
+ MapCIter iter = this->findCoord(xyz);
+ return iter == mTable.end() ? -1
+ : (isTile(iter) ? 0 : int(LEVEL) - int(getChild(iter).getValueLevel(xyz)));
+}
+
+template<typename ChildT>
+template<typename AccessorT>
+inline int
+RootNode<ChildT>::getValueDepthAndCache(const Coord& xyz, AccessorT& acc) const
+{
+ MapCIter iter = this->findCoord(xyz);
+ if (iter == mTable.end()) return -1;
+ if (isTile(iter)) return 0;
+ acc.insert(xyz, &getChild(iter));
+ return int(LEVEL) - int(getChild(iter).getValueLevelAndCache(xyz, acc));
+}
+
+
+template<typename ChildT>
+inline void
+RootNode<ChildT>::setValueOff(const Coord& xyz)
+{
+ MapIter iter = this->findCoord(xyz);
+ if (iter != mTable.end() && !isTileOff(iter)) {
+ if (isTileOn(iter)) {
+ setChild(iter, *new ChildT(xyz, getTile(iter).value, /*active=*/true));
+ }
+ getChild(iter).setValueOff(xyz);
+ }
+}
+
+
+template<typename ChildT>
+inline void
+RootNode<ChildT>::setActiveState(const Coord& xyz, bool on)
+{
+ ChildT* child = NULL;
+ MapIter iter = this->findCoord(xyz);
+ if (iter == mTable.end()) {
+ if (on) {
+ child = new ChildT(xyz, mBackground);
+ mTable[this->coordToKey(xyz)] = NodeStruct(*child);
+ } else {
+ // Nothing to do; (x, y, z) is background and therefore already inactive.
+ }
+ } else if (isChild(iter)) {
+ child = &getChild(iter);
+ } else if (on != getTile(iter).active) {
+ child = new ChildT(xyz, getTile(iter).value, !on);
+ setChild(iter, *child);
+ }
+ if (child) child->setActiveState(xyz, on);
+}
+
+template<typename ChildT>
+template<typename AccessorT>
+inline void
+RootNode<ChildT>::setActiveStateAndCache(const Coord& xyz, bool on, AccessorT& acc)
+{
+ ChildT* child = NULL;
+ MapIter iter = this->findCoord(xyz);
+ if (iter == mTable.end()) {
+ if (on) {
+ child = new ChildT(xyz, mBackground);
+ mTable[this->coordToKey(xyz)] = NodeStruct(*child);
+ } else {
+ // Nothing to do; (x, y, z) is background and therefore already inactive.
+ }
+ } else if (isChild(iter)) {
+ child = &getChild(iter);
+ } else if (on != getTile(iter).active) {
+ child = new ChildT(xyz, getTile(iter).value, !on);
+ setChild(iter, *child);
+ }
+ if (child) {
+ acc.insert(xyz, child);
+ child->setActiveStateAndCache(xyz, on, acc);
+ }
+}
+
+
+template<typename ChildT>
+inline void
+RootNode<ChildT>::setValueOff(const Coord& xyz, const ValueType& value)
+{
+ ChildT* child = NULL;
+ MapIter iter = this->findCoord(xyz);
+ if (iter == mTable.end()) {
+ if (!math::isExactlyEqual(mBackground, value)) {
+ child = new ChildT(xyz, mBackground);
+ mTable[this->coordToKey(xyz)] = NodeStruct(*child);
+ }
+ } else if (isChild(iter)) {
+ child = &getChild(iter);
+ } else if (isTileOn(iter) || !math::isExactlyEqual(getTile(iter).value, value)) {
+ child = new ChildT(xyz, getTile(iter).value, isTileOn(iter));
+ setChild(iter, *child);
+ }
+ if (child) child->setValueOff(xyz, value);
+}
+
+template<typename ChildT>
+template<typename AccessorT>
+inline void
+RootNode<ChildT>::setValueOffAndCache(const Coord& xyz, const ValueType& value, AccessorT& acc)
+{
+ ChildT* child = NULL;
+ MapIter iter = this->findCoord(xyz);
+ if (iter == mTable.end()) {
+ if (!math::isExactlyEqual(mBackground, value)) {
+ child = new ChildT(xyz, mBackground);
+ mTable[this->coordToKey(xyz)] = NodeStruct(*child);
+ }
+ } else if (isChild(iter)) {
+ child = &getChild(iter);
+ } else if (isTileOn(iter) || !math::isExactlyEqual(getTile(iter).value, value)) {
+ child = new ChildT(xyz, getTile(iter).value, isTileOn(iter));
+ setChild(iter, *child);
+ }
+ if (child) {
+ acc.insert(xyz, child);
+ child->setValueOffAndCache(xyz, value, acc);
+ }
+}
+
+
+template<typename ChildT>
+inline void
+RootNode<ChildT>::setValueOn(const Coord& xyz, const ValueType& value)
+{
+ ChildT* child = NULL;
+ MapIter iter = this->findCoord(xyz);
+ if (iter == mTable.end()) {
+ child = new ChildT(xyz, mBackground);
+ mTable[this->coordToKey(xyz)] = NodeStruct(*child);
+ } else if (isChild(iter)) {
+ child = &getChild(iter);
+ } else if (isTileOff(iter) || !math::isExactlyEqual(getTile(iter).value, value)) {
+ child = new ChildT(xyz, getTile(iter).value, isTileOn(iter));
+ setChild(iter, *child);
+ }
+ if (child) child->setValueOn(xyz, value);
+}
+
+template<typename ChildT>
+template<typename AccessorT>
+inline void
+RootNode<ChildT>::setValueAndCache(const Coord& xyz, const ValueType& value, AccessorT& acc)
+{
+ ChildT* child = NULL;
+ MapIter iter = this->findCoord(xyz);
+ if (iter == mTable.end()) {
+ child = new ChildT(xyz, mBackground);
+ mTable[this->coordToKey(xyz)] = NodeStruct(*child);
+ } else if (isChild(iter)) {
+ child = &getChild(iter);
+ } else if (isTileOff(iter) || !math::isExactlyEqual(getTile(iter).value, value)) {
+ child = new ChildT(xyz, getTile(iter).value, isTileOn(iter));
+ setChild(iter, *child);
+ }
+ if (child) {
+ acc.insert(xyz, child);
+ child->setValueAndCache(xyz, value, acc);
+ }
+}
+
+
+template<typename ChildT>
+inline void
+RootNode<ChildT>::setValueOnly(const Coord& xyz, const ValueType& value)
+{
+ ChildT* child = NULL;
+ MapIter iter = this->findCoord(xyz);
+ if (iter == mTable.end()) {
+ child = new ChildT(xyz, mBackground);
+ mTable[this->coordToKey(xyz)] = NodeStruct(*child);
+ } else if (isChild(iter)) {
+ child = &getChild(iter);
+ } else if (!math::isExactlyEqual(getTile(iter).value, value)) {
+ child = new ChildT(xyz, getTile(iter).value, isTileOn(iter));
+ setChild(iter, *child);
+ }
+ if (child) child->setValueOnly(xyz, value);
+}
+
+template<typename ChildT>
+template<typename AccessorT>
+inline void
+RootNode<ChildT>::setValueOnlyAndCache(const Coord& xyz, const ValueType& value, AccessorT& acc)
+{
+ ChildT* child = NULL;
+ MapIter iter = this->findCoord(xyz);
+ if (iter == mTable.end()) {
+ child = new ChildT(xyz, mBackground);
+ mTable[this->coordToKey(xyz)] = NodeStruct(*child);
+ } else if (isChild(iter)) {
+ child = &getChild(iter);
+ } else if (!math::isExactlyEqual(getTile(iter).value, value)) {
+ child = new ChildT(xyz, getTile(iter).value, isTileOn(iter));
+ setChild(iter, *child);
+ }
+ if (child) {
+ acc.insert(xyz, child);
+ child->setValueOnlyAndCache(xyz, value, acc);
+ }
+}
+
+
+template<typename ChildT>
+inline void
+RootNode<ChildT>::setValueOnMin(const Coord& xyz, const ValueType& value)
+{
+ ChildT* child = NULL;
+ MapIter iter = this->findCoord(xyz);
+ if (iter == mTable.end()) {
+ child = new ChildT(xyz, mBackground);
+ mTable[this->coordToKey(xyz)] = NodeStruct(*child);
+ } else if (isChild(iter)) {
+ child = &getChild(iter);
+ } else if (isTileOff(iter) || getTile(iter).value > value) {
+ child = new ChildT(xyz, getTile(iter).value, isTileOn(iter));
+ setChild(iter, *child);
+ }
+ if (child) child->setValueOnMin(xyz, value);
+}
+
+
+template<typename ChildT>
+inline void
+RootNode<ChildT>::setValueOnMax(const Coord& xyz, const ValueType& value)
+{
+ ChildT* child = NULL;
+ MapIter iter = this->findCoord(xyz);
+ if (iter == mTable.end()) {
+ child = new ChildT(xyz, mBackground);
+ mTable[this->coordToKey(xyz)] = NodeStruct(*child);
+ } else if (isChild(iter)) {
+ child = &getChild(iter);
+ } else if (isTileOff(iter) || getTile(iter).value < value) {
+ child = new ChildT(xyz, getTile(iter).value, isTileOn(iter));
+ setChild(iter, *child);
+ }
+ if (child) child->setValueOnMax(xyz, value);
+}
+
+
+template<typename ChildT>
+inline void
+RootNode<ChildT>::setValueOnSum(const Coord& xyz, const ValueType& addend)
+{
+ ChildT* child = NULL;
+ MapIter iter = this->findCoord(xyz);
+ if (iter == mTable.end()) {
+ child = new ChildT(xyz, mBackground);
+ mTable[this->coordToKey(xyz)] = NodeStruct(*child);
+ } else if (isChild(iter)) {
+ child = &getChild(iter);
+ } else if (isTileOff(iter) || !math::isZero(addend)) {
+ child = new ChildT(xyz, getTile(iter).value, isTileOn(iter));
+ setChild(iter, *child);
+ }
+ if (child) child->setValueOnSum(xyz, addend);
+}
+
+template<typename ChildT>
+template<typename AccessorT>
+inline void
+RootNode<ChildT>::setValueOnSumAndCache(const Coord& xyz,
+ const ValueType& addend, AccessorT& acc)
+{
+ ChildT* child = NULL;
+ MapIter iter = this->findCoord(xyz);
+ if (iter == mTable.end()) {
+ child = new ChildT(xyz, mBackground);
+ mTable[this->coordToKey(xyz)] = NodeStruct(*child);
+ } else if (isChild(iter)) {
+ child = &getChild(iter);
+ } else if (isTileOff(iter) || !math::isZero(addend)) {
+ child = new ChildT(xyz, getTile(iter).value, isTileOn(iter));
+ setChild(iter, *child);
+ }
+ if (child) {
+ acc.insert(xyz, child);
+ child->setValueOnSumAndCache(xyz, addend, acc);
+ }
+}
+
+
+template<typename ChildT>
+inline bool
+RootNode<ChildT>::probeValue(const Coord& xyz, ValueType& value) const
+{
+ MapCIter iter = this->findCoord(xyz);
+ if (iter == mTable.end()) {
+ value = mBackground;
+ return false;
+ } else if (isChild(iter)) {
+ return getChild(iter).probeValue(xyz, value);
+ }
+ value = getTile(iter).value;
+ return isTileOn(iter);
+}
+
+template<typename ChildT>
+template<typename AccessorT>
+inline bool
+RootNode<ChildT>::probeValueAndCache(const Coord& xyz, ValueType& value, AccessorT& acc) const
+{
+ MapCIter iter = this->findCoord(xyz);
+ if (iter == mTable.end()) {
+ value = mBackground;
+ return false;
+ } else if (isChild(iter)) {
+ acc.insert(xyz, &getChild(iter));
+ return getChild(iter).probeValueAndCache(xyz, value, acc);
+ }
+ value = getTile(iter).value;
+ return isTileOn(iter);
+}
+
+
+////////////////////////////////////////
+
+
+template<typename ChildT>
+inline void
+RootNode<ChildT>::fill(const CoordBBox& bbox, const ValueType& value, bool active)
+{
+ if (bbox.empty()) return;
+
+ Coord xyz, tileMax;
+ for (int x = bbox.min().x(); x <= bbox.max().x(); x = tileMax.x() + 1) {
+ xyz.setX(x);
+ for (int y = bbox.min().y(); y <= bbox.max().y(); y = tileMax.y() + 1) {
+ xyz.setY(y);
+ for (int z = bbox.min().z(); z <= bbox.max().z(); z = tileMax.z() + 1) {
+ xyz.setZ(z);
+
+ // Get the bounds of the tile that contains voxel (x, y, z).
+ Coord tileMin = coordToKey(xyz);
+ tileMax = tileMin.offsetBy(ChildT::DIM - 1);
+
+ if (xyz != tileMin || Coord::lessThan(bbox.max(), tileMax)) {
+ // If the box defined by (xyz, bbox.max()) doesn't completely enclose
+ // the tile to which xyz belongs, create a child node (or retrieve
+ // the existing one).
+ ChildT* child = NULL;
+ MapIter iter = this->findKey(tileMin);
+ if (iter == mTable.end()) {
+ // No child or tile exists. Create a child and initialize it
+ // with the background value.
+ child = new ChildT(xyz, mBackground);
+ mTable[tileMin] = NodeStruct(*child);
+ } else if (isTile(iter)) {
+ // Replace the tile with a newly-created child that is initialized
+ // with the tile's value and active state.
+ const Tile& tile = getTile(iter);
+ child = new ChildT(xyz, tile.value, tile.active);
+ mTable[tileMin] = NodeStruct(*child);
+ } else if (isChild(iter)) {
+ child = &getChild(iter);
+ }
+ // Forward the fill request to the child.
+ if (child) {
+ child->fill(CoordBBox(xyz, Coord::minComponent(bbox.max(), tileMax)),
+ value, active);
+ }
+ } else {
+ // If the box given by (xyz, bbox.max()) completely encloses
+ // the tile to which xyz belongs, create the tile (if it
+ // doesn't already exist) and give it the fill value.
+ MapIter iter = this->findOrAddCoord(tileMin);
+ setTile(iter, Tile(value, active));
+ }
+ }
+ }
+ }
+}
+
+template<typename ChildT>
+template<typename DenseT>
+inline void
+RootNode<ChildT>::copyToDense(const CoordBBox& bbox, DenseT& dense) const
+{
+ const size_t xStride = dense.xStride(), yStride = dense.yStride();// zStride=1
+ const Coord& min = dense.bbox().min();
+ CoordBBox nodeBBox;
+ for (Coord xyz = bbox.min(); xyz[0] <= bbox.max()[0]; xyz[0] = nodeBBox.max()[0] + 1) {
+ for (xyz[1] = bbox.min()[1]; xyz[1] <= bbox.max()[1]; xyz[1] = nodeBBox.max()[1] + 1) {
+ for (xyz[2] = bbox.min()[2]; xyz[2] <= bbox.max()[2]; xyz[2] = nodeBBox.max()[2] + 1) {
+
+ // Get the coordinate bbox of the child node that contains voxel xyz.
+ nodeBBox = CoordBBox::createCube(coordToKey(xyz), ChildT::DIM);
+
+ // Get the coordinate bbox of the interection of inBBox and nodeBBox
+ CoordBBox sub(xyz, Coord::minComponent(bbox.max(), nodeBBox.max()));
+
+ MapCIter iter = this->findKey(nodeBBox.min());
+ if (iter != mTable.end() && isChild(iter)) {//is a child
+ getChild(iter).copyToDense(sub, dense);
+ } else {//is background or a tile value
+ const ValueType value = iter==mTable.end() ? mBackground : getTile(iter).value;
+ sub.translate(-min);
+ ValueType* a0 = dense.data() + sub.min()[2];
+ for (Int32 x=sub.min()[0], ex=sub.max()[0]+1; x<ex; ++x) {
+ ValueType* a1 = a0 + x*xStride;
+ for (Int32 y=sub.min()[1], ey=sub.max()[1]+1; y<ey; ++y) {
+ ValueType* a2 = a1 + y*yStride;
+ for (Int32 z=sub.min()[2], ez=sub.max()[2]+1; z<ez; ++z) *a2++ = value;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+////////////////////////////////////////
+
+
+template<typename ChildT>
+inline bool
+RootNode<ChildT>::writeTopology(std::ostream& os, bool toHalf) const
+{
+ if (!toHalf) {
+ os.write(reinterpret_cast<const char*>(&mBackground), sizeof(ValueType));
+ } else {
+ ValueType truncatedVal = io::truncateRealToHalf(mBackground);
+ os.write(reinterpret_cast<const char*>(&truncatedVal), sizeof(ValueType));
+ }
+ io::setGridBackgroundValuePtr(os, &mBackground);
+
+ const Index numTiles = this->getTileCount(), numChildren = this->getChildCount();
+ os.write(reinterpret_cast<const char*>(&numTiles), sizeof(Index));
+ os.write(reinterpret_cast<const char*>(&numChildren), sizeof(Index));
+
+ if (numTiles == 0 && numChildren == 0) return false;
+
+ // Write tiles.
+ for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) {
+ if (isChild(i)) continue;
+ os.write(reinterpret_cast<const char*>(i->first.asPointer()), 3 * sizeof(Int32));
+ os.write(reinterpret_cast<const char*>(&getTile(i).value), sizeof(ValueType));
+ os.write(reinterpret_cast<const char*>(&getTile(i).active), sizeof(bool));
+ }
+ // Write child nodes.
+ for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) {
+ if (isTile(i)) continue;
+ os.write(reinterpret_cast<const char*>(i->first.asPointer()), 3 * sizeof(Int32));
+ getChild(i).writeTopology(os, toHalf);
+ }
+
+ return true; // not empty
+}
+
+
+template<typename ChildT>
+inline bool
+RootNode<ChildT>::readTopology(std::istream& is, bool fromHalf)
+{
+ // Delete the existing tree.
+ this->clearTable();
+
+ if (io::getFormatVersion(is) < OPENVDB_FILE_VERSION_ROOTNODE_MAP) {
+ // Read and convert an older-format RootNode.
+
+ // For backward compatibility with older file formats, read both
+ // outside and inside background values.
+ is.read(reinterpret_cast<char*>(&mBackground), sizeof(ValueType));
+ ValueType inside;
+ is.read(reinterpret_cast<char*>(&inside), sizeof(ValueType));
+
+ io::setGridBackgroundValuePtr(is, &mBackground);
+
+ // Read the index range.
+ Coord rangeMin, rangeMax;
+ is.read(reinterpret_cast<char*>(rangeMin.asPointer()), 3 * sizeof(Int32));
+ is.read(reinterpret_cast<char*>(rangeMax.asPointer()), 3 * sizeof(Int32));
+
+ this->initTable();
+ Index tableSize = 0, log2Dim[4] = { 0, 0, 0, 0 };
+ Int32 offset[3];
+ for (int i = 0; i < 3; ++i) {
+ offset[i] = rangeMin[i] >> ChildT::TOTAL;
+ rangeMin[i] = offset[i] << ChildT::TOTAL;
+ log2Dim[i] = 1 + util::FindHighestOn((rangeMax[i] >> ChildT::TOTAL) - offset[i]);
+ tableSize += log2Dim[i];
+ rangeMax[i] = (((1 << log2Dim[i]) + offset[i]) << ChildT::TOTAL) - 1;
+ }
+ log2Dim[3] = log2Dim[1] + log2Dim[2];
+ tableSize = 1U << tableSize;
+
+ // Read masks.
+ util::RootNodeMask childMask(tableSize), valueMask(tableSize);
+ childMask.load(is);
+ valueMask.load(is);
+
+ // Read child nodes/values.
+ for (Index i = 0; i < tableSize; ++i) {
+ // Compute origin = offset2coord(i).
+ Index n = i;
+ Coord origin;
+ origin[0] = (n >> log2Dim[3]) + offset[0];
+ n &= (1U << log2Dim[3]) - 1;
+ origin[1] = (n >> log2Dim[2]) + offset[1];
+ origin[2] = (n & ((1U << log2Dim[2]) - 1)) + offset[1];
+ origin <<= ChildT::TOTAL;
+
+ if (childMask.isOn(i)) {
+ // Read in and insert a child node.
+ ChildT* child = new ChildT(origin, mBackground);
+ child->readTopology(is);
+ mTable[origin] = NodeStruct(*child);
+ } else {
+ // Read in a tile value and insert a tile, but only if the value
+ // is either active or non-background.
+ ValueType value;
+ is.read(reinterpret_cast<char*>(&value), sizeof(ValueType));
+ if (valueMask.isOn(i) || (!math::isApproxEqual(value, mBackground))) {
+ mTable[origin] = NodeStruct(Tile(value, valueMask.isOn(i)));
+ }
+ }
+ }
+ return true;
+ }
+
+ // Read a RootNode that was stored in the current format.
+
+ is.read(reinterpret_cast<char*>(&mBackground), sizeof(ValueType));
+ io::setGridBackgroundValuePtr(is, &mBackground);
+
+ Index numTiles = 0, numChildren = 0;
+ is.read(reinterpret_cast<char*>(&numTiles), sizeof(Index));
+ is.read(reinterpret_cast<char*>(&numChildren), sizeof(Index));
+
+ if (numTiles == 0 && numChildren == 0) return false;
+
+ Int32 vec[3];
+ ValueType value;
+ bool active;
+
+ // Read tiles.
+ for (Index n = 0; n < numTiles; ++n) {
+ is.read(reinterpret_cast<char*>(vec), 3 * sizeof(Int32));
+ is.read(reinterpret_cast<char*>(&value), sizeof(ValueType));
+ is.read(reinterpret_cast<char*>(&active), sizeof(bool));
+ mTable[Coord(vec)] = NodeStruct(Tile(value, active));
+ }
+
+ // Read child nodes.
+ for (Index n = 0; n < numChildren; ++n) {
+ is.read(reinterpret_cast<char*>(vec), 3 * sizeof(Int32));
+ Coord origin(vec);
+ ChildT* child = new ChildT(origin, mBackground);
+ child->readTopology(is, fromHalf);
+ mTable[Coord(vec)] = NodeStruct(*child);
+ }
+
+ return true; // not empty
+}
+
+
+template<typename ChildT>
+inline void
+RootNode<ChildT>::writeBuffers(std::ostream& os, bool toHalf) const
+{
+ for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) {
+ if (isChild(i)) getChild(i).writeBuffers(os, toHalf);
+ }
+}
+
+
+template<typename ChildT>
+inline void
+RootNode<ChildT>::readBuffers(std::istream& is, bool fromHalf)
+{
+ for (MapIter i = mTable.begin(), e = mTable.end(); i != e; ++i) {
+ if (isChild(i)) getChild(i).readBuffers(is, fromHalf);
+ }
+}
+
+
+////////////////////////////////////////
+
+
+template<typename ChildT>
+template<typename PruneOp>
+inline void
+RootNode<ChildT>::pruneOp(PruneOp& op)
+{
+ for (MapIter i = mTable.begin(), e = mTable.end(); i != e; ++i) {
+ if (this->isTile(i)|| !op(this->getChild(i))) continue;
+ this->setTile(i, Tile(op.value, op.state));
+ }
+ this->eraseBackgroundTiles();
+}
+
+
+template<typename ChildT>
+inline void
+RootNode<ChildT>::prune(const ValueType& tolerance)
+{
+ TolerancePrune<ValueType> op(tolerance);
+ this->pruneOp(op);
+}
+
+
+template<typename ChildT>
+inline void
+RootNode<ChildT>::pruneInactive(const ValueType& bg)
+{
+ InactivePrune<ValueType> op(bg);
+ this->pruneOp(op);
+}
+
+
+template<typename ChildT>
+inline void
+RootNode<ChildT>::pruneInactive()
+{
+ this->pruneInactive(mBackground);
+}
+
+
+template<typename ChildT>
+inline void
+RootNode<ChildT>::pruneTiles(const ValueType& tolerance)
+{
+ TolerancePrune<ValueType, /*Terminate at level=*/1> op(tolerance);
+ this->pruneOp(op);
+}
+
+
+////////////////////////////////////////
+
+
+// Helper method that implements stealNode()
+template<typename ChildT>
+template<typename NodeT>
+inline NodeT*
+RootNode<ChildT>::doStealNode(const Coord& xyz, const ValueType& value, bool state)
+{
+ MapIter iter = this->findCoord(xyz);
+ if (iter == mTable.end() || isTile(iter)) return NULL;
+ return (boost::is_same<NodeT, ChildT>::value)
+ ? reinterpret_cast<NodeT*>(&stealChild(iter, Tile(value, state)))
+ : getChild(iter).template stealNode<NodeT>(xyz, value, state);
+}
+
+
+template<typename ChildT>
+template<typename NodeT>
+inline NodeT*
+RootNode<ChildT>::stealNode(const Coord& xyz, const ValueType& value, bool state)
+{
+ // The following conditional is resolved at compile time, and the ternary operator
+ // and helper method are used to avoid "unreachable code" warnings (with
+ // "if (<cond>) { <A> } else { <B> }", either <A> or <B> is unreachable if <cond>
+ // is a compile-time constant expression). Partial specialization on NodeT would be
+ // impractical because a method template can't be specialized without also
+ // specializing its class template.
+ return (NodeT::LEVEL > ChildT::LEVEL ||
+ (NodeT::LEVEL == ChildT::LEVEL && !(boost::is_same<NodeT, ChildT>::value)))
+ ? static_cast<NodeT*>(NULL)
+ : this->doStealNode<NodeT>(xyz, value, state);
+}
+
+
+////////////////////////////////////////
+
+
+template<typename ChildT>
+inline void
+RootNode<ChildT>::addLeaf(LeafNodeType* leaf)
+{
+ if (leaf == NULL) return;
+ ChildT* child = NULL;
+ const Coord& xyz = leaf->origin();
+ MapIter iter = this->findCoord(xyz);
+ if (iter == mTable.end()) {
+ if (ChildT::LEVEL>0) {
+ child = new ChildT(xyz, mBackground, false);
+ } else {
+ child = reinterpret_cast<ChildT*>(leaf);
+ }
+ mTable[this->coordToKey(xyz)] = NodeStruct(*child);
+ } else if (isChild(iter)) {
+ if (ChildT::LEVEL>0) {
+ child = &getChild(iter);
+ } else {
+ child = reinterpret_cast<ChildT*>(leaf);
+ setChild(iter, *child);//this also deletes the existing child node
+ }
+ } else {//tile
+ if (ChildT::LEVEL>0) {
+ child = new ChildT(xyz, getTile(iter).value, isTileOn(iter));
+ } else {
+ child = reinterpret_cast<ChildT*>(leaf);
+ }
+ setChild(iter, *child);
+ }
+ child->addLeaf(leaf);
+}
+
+
+template<typename ChildT>
+template<typename AccessorT>
+inline void
+RootNode<ChildT>::addLeafAndCache(LeafNodeType* leaf, AccessorT& acc)
+{
+ if (leaf == NULL) return;
+ ChildT* child = NULL;
+ const Coord& xyz = leaf->origin();
+ MapIter iter = this->findCoord(xyz);
+ if (iter == mTable.end()) {
+ if (ChildT::LEVEL>0) {
+ child = new ChildT(xyz, mBackground, false);
+ } else {
+ child = reinterpret_cast<ChildT*>(leaf);
+ }
+ mTable[this->coordToKey(xyz)] = NodeStruct(*child);
+ } else if (isChild(iter)) {
+ if (ChildT::LEVEL>0) {
+ child = &getChild(iter);
+ } else {
+ child = reinterpret_cast<ChildT*>(leaf);
+ setChild(iter, *child);//this also deletes the existing child node
+ }
+ } else {//tile
+ if (ChildT::LEVEL>0) {
+ child = new ChildT(xyz, getTile(iter).value, isTileOn(iter));
+ } else {
+ child = reinterpret_cast<ChildT*>(leaf);
+ }
+ setChild(iter, *child);
+ }
+ acc.insert(xyz, child);
+ child->addLeafAndCache(leaf, acc);
+}
+
+
+template<typename ChildT>
+inline void
+RootNode<ChildT>::addTile(Index level, const Coord& xyz,
+ const ValueType& value, bool state)
+{
+ if (LEVEL >= level && level > 0) {
+ MapIter iter = this->findCoord(xyz);
+ if (iter == mTable.end()) {//background
+ if (LEVEL > level) {
+ ChildT* child = new ChildT(xyz, mBackground, false);
+ mTable[this->coordToKey(xyz)] = NodeStruct(*child);
+ child->addTile(level, xyz, value, state);
+ } else {
+ mTable[this->coordToKey(xyz)] = NodeStruct(Tile(value, state));
+ }
+ } else if (isChild(iter)) {//child
+ if (LEVEL > level) {
+ getChild(iter).addTile(level, xyz, value, state);
+ } else {
+ setTile(iter, Tile(value, state));//this also deletes the existing child node
+ }
+ } else {//tile
+ if (LEVEL > level) {
+ ChildT* child = new ChildT(xyz, getTile(iter).value, isTileOn(iter));
+ setChild(iter, *child);
+ child->addTile(level, xyz, value, state);
+ } else {
+ setTile(iter, Tile(value, state));
+ }
+ }
+ }
+}
+
+
+template<typename ChildT>
+template<typename AccessorT>
+inline void
+RootNode<ChildT>::addTileAndCache(Index level, const Coord& xyz, const ValueType& value,
+ bool state, AccessorT& acc)
+{
+ if (LEVEL >= level && level > 0) {
+ MapIter iter = this->findCoord(xyz);
+ if (iter == mTable.end()) {//background
+ if (LEVEL > level) {
+ ChildT* child = new ChildT(xyz, mBackground, false);
+ acc.insert(xyz, child);
+ mTable[this->coordToKey(xyz)] = NodeStruct(*child);
+ child->addTileAndCache(level, xyz, value, state, acc);
+ } else {
+ mTable[this->coordToKey(xyz)] = NodeStruct(Tile(value, state));
+ }
+ } else if (isChild(iter)) {//child
+ if (LEVEL > level) {
+ ChildT* child = &getChild(iter);
+ acc.insert(xyz, child);
+ child->addTileAndCache(level, xyz, value, state, acc);
+ } else {
+ setTile(iter, Tile(value, state));//this also deletes the existing child node
+ }
+ } else {//tile
+ if (LEVEL > level) {
+ ChildT* child = new ChildT(xyz, getTile(iter).value, isTileOn(iter));
+ acc.insert(xyz, child);
+ setChild(iter, *child);
+ child->addTileAndCache(level, xyz, value, state, acc);
+ } else {
+ setTile(iter, Tile(value, state));
+ }
+ }
+ }
+}
+
+
+////////////////////////////////////////
+
+
+template<typename ChildT>
+inline typename ChildT::LeafNodeType*
+RootNode<ChildT>::touchLeaf(const Coord& xyz)
+{
+ ChildT* child = NULL;
+ MapIter iter = this->findCoord(xyz);
+ if (iter == mTable.end()) {
+ child = new ChildT(xyz, mBackground, false);
+ mTable[this->coordToKey(xyz)] = NodeStruct(*child);
+ } else if (isChild(iter)) {
+ child = &getChild(iter);
+ } else {
+ child = new ChildT(xyz, getTile(iter).value, isTileOn(iter));
+ setChild(iter, *child);
+ }
+ return child->touchLeaf(xyz);
+}
+
+
+template<typename ChildT>
+template<typename AccessorT>
+inline typename ChildT::LeafNodeType*
+RootNode<ChildT>::touchLeafAndCache(const Coord& xyz, AccessorT& acc)
+{
+ ChildT* child = NULL;
+ MapIter iter = this->findCoord(xyz);
+ if (iter == mTable.end()) {
+ child = new ChildT(xyz, mBackground, false);
+ mTable[this->coordToKey(xyz)] = NodeStruct(*child);
+ } else if (isChild(iter)) {
+ child = &getChild(iter);
+ } else {
+ child = new ChildT(xyz, getTile(iter).value, isTileOn(iter));
+ setChild(iter, *child);
+ }
+ acc.insert(xyz, child);
+ return child->touchLeafAndCache(xyz, acc);
+}
+
+
+////////////////////////////////////////
+
+
+template<typename ChildT>
+inline typename ChildT::LeafNodeType*
+RootNode<ChildT>::probeLeaf(const Coord& xyz)
+{
+ MapIter iter = this->findCoord(xyz);
+ if (iter == mTable.end() || isTile(iter)) return NULL;
+ return getChild(iter).probeLeaf(xyz);
+}
+
+template<typename ChildT>
+template<typename AccessorT>
+inline typename ChildT::LeafNodeType*
+RootNode<ChildT>::probeLeafAndCache(const Coord& xyz, AccessorT& acc)
+{
+ MapIter iter = this->findCoord(xyz);
+ if (iter == mTable.end() || isTile(iter)) return NULL;
+ ChildT* child = &getChild(iter);
+ acc.insert(xyz, child);
+ return child->probeLeafAndCache(xyz, acc);
+}
+
+template<typename ChildT>
+inline const typename ChildT::LeafNodeType*
+RootNode<ChildT>::probeConstLeaf(const Coord& xyz) const
+{
+ MapCIter iter = this->findCoord(xyz);
+ if (iter == mTable.end() || isTile(iter)) return NULL;
+ return getChild(iter).probeConstLeaf(xyz);
+}
+
+template<typename ChildT>
+template<typename AccessorT>
+inline const typename ChildT::LeafNodeType*
+RootNode<ChildT>::probeConstLeafAndCache(const Coord& xyz, AccessorT& acc) const
+{
+ MapCIter iter = this->findCoord(xyz);
+ if (iter == mTable.end() || isTile(iter)) return NULL;
+ const ChildT* child = &getChild(iter);
+ acc.insert(xyz, child);
+ return child->probeConstLeafAndCache(xyz, acc);
+}
+
+
+////////////////////////////////////////
+
+
+template<typename ChildT>
+inline void
+RootNode<ChildT>::signedFloodFill()
+{
+ this->signedFloodFill(mBackground, negative(mBackground));
+}
+
+
+template<typename ChildT>
+inline void
+RootNode<ChildT>::signedFloodFill(const ValueType& outside, const ValueType& inside)
+{
+ const ValueType zero = zeroVal<ValueType>();
+
+ mBackground = outside;
+
+ // First, flood fill all child nodes and put child-keys into a sorted set
+ CoordSet nodeKeys;
+ for (MapIter i = mTable.begin(), e = mTable.end(); i != e; ++i) {
+ if (this->isTile(i)) continue;
+ getChild(i).signedFloodFill(outside, inside);
+ nodeKeys.insert(i->first);//only add inactive tiles!
+ }
+
+ // We employ a simple z-scanline algorithm that inserts inactive
+ // tiles with the inside value if they are sandwiched
+ // between inside child nodes only!
+ const Tile insideTile(inside, /*on=*/false);
+ CoordSetCIter b = nodeKeys.begin(), e = nodeKeys.end();
+ if ( b == e ) return;
+ for (CoordSetCIter a = b++; b != e; ++a, ++b) {
+ Coord d = *b - *a; // delta of neighboring keys
+ if (d[0]!=0 || d[1]!=0 || d[2]==Int32(ChildT::DIM)) continue;//not z-scanline or neighbors
+ MapIter i = mTable.find(*a), j = mTable.find(*b);
+ const ValueType fill[] = { getChild(i).getLastValue(), getChild(j).getFirstValue() };
+ if (!(fill[0] < zero) || !(fill[1] < zero)) continue; // scanline isn't inside
+ for (Coord c = *a + Coord(0u,0u,ChildT::DIM); c[2] != (*b)[2]; c[2] += ChildT::DIM) {
+ mTable[c] = insideTile;
+ }
+ }
+}
+
+
+////////////////////////////////////////
+
+
+template<typename ChildT>
+inline void
+RootNode<ChildT>::voxelizeActiveTiles()
+{
+ for (MapIter i = mTable.begin(), e = mTable.end(); i != e; ++i) {
+ if (this->isTileOff(i)) continue;
+ ChildT* child = i->second.child;
+ if (child==NULL) {
+ child = new ChildT(i->first, this->getTile(i).value, true);
+ i->second.child = child;
+ }
+ child->voxelizeActiveTiles();
+ }
+}
+
+
+////////////////////////////////////////
+
+
+template<typename ChildT>
+inline void
+RootNode<ChildT>::merge(RootNode& other)
+{
+ for (MapIter i = other.mTable.begin(), e = other.mTable.end(); i != e; ++i) {
+ MapIter j = mTable.find(i->first);
+ if (other.isChild(i)) {
+ if (j == mTable.end()) { // insert other child node
+ mTable[i->first]=NodeStruct(stealChild(i, Tile(other.mBackground, /*on=*/false)));
+ } else if (isTile(j)) { // replace tile with other child node
+ setChild(j, stealChild(i, Tile(other.mBackground, /*on=*/false)));
+ } else { // merge both child nodes
+ getChild(j).merge(getChild(i),other.mBackground, mBackground);
+ }
+ } else { // other is a tile
+ if (j == mTable.end()) { // insert other tile
+ mTable[i->first] = i->second;
+ } else continue; // ignore other tile
+ }
+ }
+ // Empty the other tree so as not to leave it in a partially cannibalized state.
+ other.clear();
+}
+
+
+////////////////////////////////////////
+
+
+template<typename ChildT>
+template<typename OtherChildType>
+inline void
+RootNode<ChildT>::topologyUnion(const RootNode<OtherChildType>& other)
+{
+ typedef RootNode<OtherChildType> OtherRootT;
+ typedef typename OtherRootT::MapCIter OtherCIterT;
+
+ enforceSameConfiguration(other);
+
+ for (OtherCIterT i = other.mTable.begin(), e = other.mTable.end(); i != e; ++i) {
+ MapIter j = mTable.find(i->first);
+ if (other.isChild(i)) {
+ if (j == mTable.end()) { // create child branch with identical topology
+ mTable[i->first] = NodeStruct(
+ *(new ChildT(other.getChild(i), mBackground, TopologyCopy())));
+ } else if (this->isChild(j)) { // union with child branch
+ this->getChild(j).topologyUnion(other.getChild(i));
+ } else {// this is a tile so replace it with a child branch with identical topology
+ ChildT* child = new ChildT(
+ other.getChild(i), this->getTile(j).value, TopologyCopy());
+ if (this->isTileOn(j)) child->setValuesOn();//this is an active tile
+ this->setChild(j, *child);
+ }
+ } else if (other.isTileOn(i)) { // other is an active tile
+ if (j == mTable.end()) { // insert an active tile
+ mTable[i->first] = NodeStruct(Tile(mBackground, true));
+ } else if (this->isChild(j)) {
+ this->getChild(j).setValuesOn();
+ } else if (this->isTileOff(j)) {
+ this->setTile(j, Tile(this->getTile(j).value, true));
+ }
+ }
+ }
+}
+
+
+////////////////////////////////////////
+
+
+template<typename ChildT>
+template<typename CombineOp>
+inline void
+RootNode<ChildT>::combine(RootNode& other, CombineOp& op, bool prune)
+{
+ CombineArgs<ValueType> args;
+
+ CoordSet keys;
+ this->insertKeys(keys);
+ other.insertKeys(keys);
+
+ for (CoordSetCIter i = keys.begin(), e = keys.end(); i != e; ++i) {
+ MapIter iter = findOrAddCoord(*i), otherIter = other.findOrAddCoord(*i);
+ if (isTile(iter) && isTile(otherIter)) {
+ // Both this node and the other node have constant values (tiles).
+ // Combine the two values and store the result as this node's new tile value.
+ op(args.setARef(getTile(iter).value)
+ .setAIsActive(isTileOn(iter))
+ .setBRef(getTile(otherIter).value)
+ .setBIsActive(isTileOn(otherIter)));
+ setTile(iter, Tile(args.result(), args.resultIsActive()));
+
+ } else if (isChild(iter) && isTile(otherIter)) {
+ // Combine this node's child with the other node's constant value.
+ ChildT& child = getChild(iter);
+ child.combine(getTile(otherIter).value, isTileOn(otherIter), op);
+
+ } else if (isTile(iter) && isChild(otherIter)) {
+ // Combine this node's constant value with the other node's child,
+ // but use a new functor in which the A and B values are swapped,
+ // since the constant value is the A value, not the B value.
+ SwappedCombineOp<ValueType, CombineOp> swappedOp(op);
+ ChildT& child = getChild(otherIter);
+ child.combine(getTile(iter).value, isTileOn(iter), swappedOp);
+
+ // Steal the other node's child.
+ setChild(iter, stealChild(otherIter, Tile()));
+
+ } else /*if (isChild(iter) && isChild(otherIter))*/ {
+ // Combine this node's child with the other node's child.
+ ChildT &child = getChild(iter), &otherChild = getChild(otherIter);
+ child.combine(otherChild, op);
+ }
+ if (prune && isChild(iter)) getChild(iter).prune();
+ }
+
+ // Combine background values.
+ op(args.setARef(mBackground).setBRef(other.mBackground));
+ mBackground = args.result();
+
+ // Empty the other tree so as not to leave it in a partially cannibalized state.
+ other.clear();
+}
+
+
+////////////////////////////////////////
+
+
+template<typename ChildT>
+template<typename CombineOp>
+inline void
+RootNode<ChildT>::combine2(const RootNode& other0, const RootNode& other1,
+ CombineOp& op, bool prune)
+{
+ CombineArgs<ValueType> args;
+
+ CoordSet keys;
+ other0.insertKeys(keys);
+ other1.insertKeys(keys);
+
+ const NodeStruct
+ bg0(Tile(other0.mBackground, /*active=*/false)),
+ bg1(Tile(other1.mBackground, /*active=*/false));
+
+ for (CoordSetCIter i = keys.begin(), e = keys.end(); i != e; ++i) {
+ MapIter thisIter = this->findOrAddCoord(*i);
+ MapCIter iter0 = other0.findKey(*i), iter1 = other1.findKey(*i);
+ const NodeStruct
+ &ns0 = (iter0 != other0.mTable.end()) ? iter0->second : bg0,
+ &ns1 = (iter1 != other1.mTable.end()) ? iter1->second : bg1;
+ if (ns0.isTile() && ns1.isTile()) {
+ // Both input nodes have constant values (tiles).
+ // Combine the two values and add a new tile to this node with the result.
+ op(args.setARef(ns0.tile.value)
+ .setAIsActive(ns0.isTileOn())
+ .setBRef(ns1.tile.value)
+ .setBIsActive(ns1.isTileOn()));
+ setTile(thisIter, Tile(args.result(), args.resultIsActive()));
+ } else {
+ ChildT& otherChild = ns0.isChild() ? *ns0.child : *ns1.child;
+ if (!isChild(thisIter)) {
+ // Add a new child with the same coordinates, etc. as the other node's child.
+ setChild(thisIter,
+ *(new ChildT(otherChild.getOrigin(), getTile(thisIter).value)));
+ }
+ ChildT& child = getChild(thisIter);
+
+ if (ns0.isTile()) {
+ // Combine node1's child with node0's constant value
+ // and write the result into this node's child.
+ child.combine2(ns0.tile.value, *ns1.child, ns0.isTileOn(), op);
+ } else if (ns1.isTile()) {
+ // Combine node0's child with node1's constant value
+ // and write the result into this node's child.
+ child.combine2(*ns0.child, ns1.tile.value, ns1.isTileOn(), op);
+ } else {
+ // Combine node0's child with node1's child
+ // and write the result into this node's child.
+ child.combine2(*ns0.child, *ns1.child, op);
+ }
+ }
+ if (prune && isChild(thisIter)) getChild(thisIter).prune();
+ }
+
+ // Combine background values.
+ op(args.setARef(other0.mBackground).setBRef(other1.mBackground));
+ mBackground = args.result();
+}
+
+
+////////////////////////////////////////
+
+
+template<typename ChildT>
+template<typename BBoxOp>
+inline void
+RootNode<ChildT>::visitActiveBBox(BBoxOp& op) const
+{
+ const bool descent = op.template descent<LEVEL>();
+ for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) {
+ if (this->isTileOff(i)) continue;
+ if (this->isChild(i) && descent) {
+ this->getChild(i).visitActiveBBox(op);
+ } else {
+#ifdef _MSC_VER
+ op.operator()<LEVEL>(CoordBBox::createCube(i->first, ChildT::DIM));
+#else
+ op.template operator()<LEVEL>(CoordBBox::createCube(i->first, ChildT::DIM));
+#endif
+ }
+ }
+}
+
+
+template<typename ChildT>
+template<typename VisitorOp>
+inline void
+RootNode<ChildT>::visit(VisitorOp& op)
+{
+ doVisit<RootNode, VisitorOp, ChildAllIter>(*this, op);
+}
+
+
+template<typename ChildT>
+template<typename VisitorOp>
+inline void
+RootNode<ChildT>::visit(VisitorOp& op) const
+{
+ doVisit<const RootNode, VisitorOp, ChildAllCIter>(*this, op);
+}
+
+
+template<typename ChildT>
+template<typename RootNodeT, typename VisitorOp, typename ChildAllIterT>
+inline void
+RootNode<ChildT>::doVisit(RootNodeT& self, VisitorOp& op)
+{
+ typename RootNodeT::ValueType val;
+ for (ChildAllIterT iter = self.beginChildAll(); iter; ++iter) {
+ if (op(iter)) continue;
+ if (typename ChildAllIterT::ChildNodeType* child = iter.probeChild(val)) {
+ child->visit(op);
+ }
+ }
+}
+
+
+////////////////////////////////////////
+
+
+template<typename ChildT>
+template<typename OtherRootNodeType, typename VisitorOp>
+inline void
+RootNode<ChildT>::visit2(OtherRootNodeType& other, VisitorOp& op)
+{
+ doVisit2<RootNode, OtherRootNodeType, VisitorOp, ChildAllIter,
+ typename OtherRootNodeType::ChildAllIter>(*this, other, op);
+}
+
+
+template<typename ChildT>
+template<typename OtherRootNodeType, typename VisitorOp>
+inline void
+RootNode<ChildT>::visit2(OtherRootNodeType& other, VisitorOp& op) const
+{
+ doVisit2<const RootNode, OtherRootNodeType, VisitorOp, ChildAllCIter,
+ typename OtherRootNodeType::ChildAllCIter>(*this, other, op);
+}
+
+
+template<typename ChildT>
+template<
+ typename RootNodeT,
+ typename OtherRootNodeT,
+ typename VisitorOp,
+ typename ChildAllIterT,
+ typename OtherChildAllIterT>
+inline void
+RootNode<ChildT>::doVisit2(RootNodeT& self, OtherRootNodeT& other, VisitorOp& op)
+{
+ /// @todo Allow the two nodes to have different ValueTypes, but not
+ /// different fan-out factors or different index space bounds.
+ enforceSameConfiguration(other);
+
+ typename RootNodeT::ValueType val;
+ typename OtherRootNodeT::ValueType otherVal;
+
+ // The two nodes are required to have corresponding table entries,
+ // but since that might require background tiles to be added to one or both,
+ // and the nodes might be const, we operate on shallow copies of the nodes instead.
+ RootNodeT copyOfSelf(self.mBackground);
+ copyOfSelf.mTable = self.mTable;
+ OtherRootNodeT copyOfOther(other.mBackground);
+ copyOfOther.mTable = other.mTable;
+
+ // Add background tiles to both nodes as needed.
+ CoordSet keys;
+ self.insertKeys(keys);
+ other.insertKeys(keys);
+ for (CoordSetCIter i = keys.begin(), e = keys.end(); i != e; ++i) {
+ copyOfSelf.findOrAddCoord(*i);
+ copyOfOther.findOrAddCoord(*i);
+ }
+
+ ChildAllIterT iter = copyOfSelf.beginChildAll();
+ OtherChildAllIterT otherIter = copyOfOther.beginChildAll();
+
+ for ( ; iter && otherIter; ++iter, ++otherIter)
+ {
+ const size_t skipBranch = static_cast<size_t>(op(iter, otherIter));
+
+ typename ChildAllIterT::ChildNodeType* child =
+ (skipBranch & 1U) ? NULL : iter.probeChild(val);
+ typename OtherChildAllIterT::ChildNodeType* otherChild =
+ (skipBranch & 2U) ? NULL : otherIter.probeChild(otherVal);
+
+ if (child != NULL && otherChild != NULL) {
+ child->visit2Node(*otherChild, op);
+ } else if (child != NULL) {
+ child->visit2(otherIter, op);
+ } else if (otherChild != NULL) {
+ otherChild->visit2(iter, op, /*otherIsLHS=*/true);
+ }
+ }
+ // Remove any background tiles that were added above,
+ // as well as any that were created by the visitors.
+ copyOfSelf.eraseBackgroundTiles();
+ copyOfOther.eraseBackgroundTiles();
+
+ // If either input node is non-const, replace its table with
+ // the (possibly modified) copy.
+ self.resetTable(copyOfSelf.mTable);
+ other.resetTable(copyOfOther.mTable);
+}
+
+} // namespace tree
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_TREE_ROOTNODE_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/tree/Tree.h b/extern/openvdb/internal/openvdb/tree/Tree.h
new file mode 100644
index 00000000000..dfc0ece8ac0
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/tree/Tree.h
@@ -0,0 +1,2017 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+//
+/// @file tree/Tree.h
+
+#ifndef OPENVDB_TREE_TREE_HAS_BEEN_INCLUDED
+#define OPENVDB_TREE_TREE_HAS_BEEN_INCLUDED
+
+#include <iostream>
+#include <sstream>
+#include <vector>
+#include <boost/shared_ptr.hpp>
+#include <boost/cstdint.hpp>
+#include <tbb/atomic.h>
+#include <tbb/concurrent_hash_map.h>
+#include <openvdb/Types.h>
+#include <openvdb/metadata/Metadata.h>
+#include <openvdb/math/Math.h>
+#include <openvdb/math/BBox.h>
+#include <openvdb/util/Formats.h>
+#include <openvdb/util/logging.h>
+#include <openvdb/Platform.h>
+#include "RootNode.h"
+#include "InternalNode.h"
+#include "LeafNode.h"
+#include "TreeIterator.h"
+#include "ValueAccessor.h"
+#include "Util.h"
+
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace tree {
+
+/// @brief Base class for typed trees
+class OPENVDB_API TreeBase
+{
+public:
+ typedef boost::shared_ptr<TreeBase> Ptr;
+ typedef boost::shared_ptr<const TreeBase> ConstPtr;
+
+ TreeBase() {}
+ virtual ~TreeBase() {}
+
+ /// Return the name of this tree's type.
+ virtual const Name& type() const = 0;
+ /// Return the name of the type of a voxel's value (e.g., "float" or "vec3d").
+ virtual Name valueType() const = 0;
+
+ /// Return a pointer to a deep copy of this tree
+ virtual TreeBase::Ptr copy() const = 0;
+
+
+ //
+ // Tree methods
+ //
+ /// @brief Return this tree's background value wrapped as metadata.
+ /// @note Query the metadata object for the value's type.
+ virtual Metadata::Ptr getBackgroundValue() const { return Metadata::Ptr(); }
+
+ /// @brief Return in @a bbox the axis-aligned bounding box of all leaf nodes.
+ /// @return @c false if the bounding box is empty (in which case
+ /// the bbox is set to its default value).
+ virtual bool evalLeafBoundingBox(CoordBBox& bbox) const = 0;
+ /// @brief Return in @a dim the dimensions of the axis-aligned bounding box
+ /// of all leaf nodes.
+ /// @return @c false if the bounding box is empty.
+ virtual bool evalLeafDim(Coord& dim) const = 0;
+
+ /// @brief Return in @a bbox the axis-aligned bounding box of all
+ /// active voxels and tiles.
+ /// This is a tighter bounding box than the leaf node bounding box.
+ /// @return @c false if the bounding box is empty (in which case
+ /// the bbox is set to its default value).
+ virtual bool evalActiveVoxelBoundingBox(CoordBBox& bbox) const = 0;
+ /// @brief Return in @a dim the dimensions of the axis-aligned bounding box of all
+ /// active voxels. This is a tighter bounding box than the leaf node bounding box.
+ /// @return @c false if the bounding box is empty.
+ virtual bool evalActiveVoxelDim(Coord& dim) const = 0;
+
+ virtual void getIndexRange(CoordBBox& bbox) const = 0;
+
+
+ //
+ // Statistics
+ //
+ /// @brief Return the depth of this tree.
+ ///
+ /// A tree with only a root node and leaf nodes has depth 2, for example.
+ virtual Index treeDepth() const = 0;
+ /// Return the number of leaf nodes.
+ virtual Index32 leafCount() const = 0;
+ /// Return the number of non-leaf nodes.
+ virtual Index32 nonLeafCount() const = 0;
+ /// Return the number of active voxels stored in leaf nodes.
+ virtual Index64 activeLeafVoxelCount() const = 0;
+ /// Return the number of inactive voxels stored in leaf nodes.
+ virtual Index64 inactiveLeafVoxelCount() const = 0;
+ /// Return the total number of active voxels.
+ virtual Index64 activeVoxelCount() const = 0;
+ /// Return the number of inactive voxels within the bounding box of all active voxels.
+ virtual Index64 inactiveVoxelCount() const = 0;
+
+ /// Return the total amount of memory in bytes occupied by this tree.
+ virtual Index64 memUsage() const { return 0; }
+
+
+ //
+ // I/O methods
+ //
+ /// @brief Read the tree topology from a stream.
+ ///
+ /// This will read the tree structure and tile values, but not voxel data.
+ virtual void readTopology(std::istream&, bool saveFloatAsHalf = false);
+ /// @brief Write the tree topology to a stream.
+ ///
+ /// This will write the tree structure and tile values, but not voxel data.
+ virtual void writeTopology(std::ostream&, bool saveFloatAsHalf = false) const;
+
+ /// Read all data buffers for this tree.
+ virtual void readBuffers(std::istream&, bool saveFloatAsHalf = false) = 0;
+ /// Write out all the data buffers for this tree.
+ virtual void writeBuffers(std::ostream&, bool saveFloatAsHalf = false) const = 0;
+
+ /// @brief Print statistics, memory usage and other information about this tree.
+ /// @param os a stream to which to write textual information
+ /// @param verboseLevel 1: print tree configuration only; 2: include node and
+ /// voxel statistics; 3: include memory usage
+ virtual void print(std::ostream& os = std::cout, int verboseLevel = 1) const;
+
+private:
+ // Disallow copying of instances of this class.
+ //TreeBase(const TreeBase& other);
+ TreeBase& operator=(const TreeBase& other);
+};
+
+
+////////////////////////////////////////
+
+
+template<typename _RootNodeType>
+class Tree: public TreeBase
+{
+public:
+ typedef boost::shared_ptr<Tree> Ptr;
+ typedef boost::shared_ptr<const Tree> ConstPtr;
+
+ typedef _RootNodeType RootNodeType;
+ typedef typename RootNodeType::ValueType ValueType;
+ typedef typename RootNodeType::LeafNodeType LeafNodeType;
+
+ static const Index DEPTH = RootNodeType::LEVEL + 1;
+
+ /// @brief ValueConverter<T>::Type is the type of a tree having the same
+ /// hierarchy as this tree but a different value type, T.
+ ///
+ /// For example, FloatTree::ValueConverter<double>::Type is equivalent to DoubleTree.
+ /// @note If the source tree type is a template argument, it might be necessary
+ /// to write "typename SourceTree::template ValueConverter<T>::Type".
+ template<typename OtherValueType>
+ struct ValueConverter {
+ typedef Tree<typename RootNodeType::template ValueConverter<OtherValueType>::Type> Type;
+ };
+
+
+ Tree(){}
+
+ /// Deep copy constructor
+ Tree(const Tree& other): TreeBase(other), mRoot(other.mRoot)
+ {
+ }
+
+ /// @brief Topology copy constructor from a tree of a different type
+ ///
+ /// Copy the structure, i.e., the active states of tiles and voxels, of another
+ /// tree of a possibly different type, but don't copy any tile or voxel values.
+ /// Instead, initialize tiles and voxels with the given active and inactive values.
+ /// @param other a tree having (possibly) a different ValueType
+ /// @param inactiveValue background value for this tree, and the value to which
+ /// all inactive tiles and voxels are initialized
+ /// @param activeValue value to which active tiles and voxels are initialized
+ template<typename OtherTreeType>
+ Tree(const OtherTreeType& other,
+ const ValueType& inactiveValue,
+ const ValueType& activeValue,
+ TopologyCopy):
+ TreeBase(other),
+ mRoot(other.getRootNode(), inactiveValue, activeValue, TopologyCopy())
+ {
+ }
+
+ /// @brief Topology copy constructor from a tree of a different type
+ ///
+ /// @note This topology copy constructor is generally faster than
+ /// the one that takes both a foreground and a background value.
+ ///
+ /// Copy the structure, i.e., the active states of tiles and voxels, of another
+ /// tree of a possibly different type, but don't copy any tile or voxel values.
+ /// Instead, initialize tiles and voxels with the given background value.
+ /// @param other a tree having (possibly) a different ValueType
+ /// @param background the value to which tiles and voxels are initialized
+ template<typename OtherTreeType>
+ Tree(const OtherTreeType& other, const ValueType& background, TopologyCopy):
+ TreeBase(other),
+ mRoot(other.getRootNode(), background, TopologyCopy())
+ {
+ }
+
+ /// Empty tree constructor
+ Tree(const ValueType& background): mRoot(background) {}
+
+ virtual ~Tree() { releaseAllAccessors(); }
+
+ /// Return a pointer to a deep copy of this tree
+ virtual TreeBase::Ptr copy() const { return TreeBase::Ptr(new Tree(*this)); }
+
+ /// Return the name of the type of a voxel's value (e.g., "float" or "vec3d")
+ virtual Name valueType() const { return typeNameAsString<ValueType>(); }
+
+ /// Return the name of this type of tree.
+ static const Name& treeType();
+ /// Return the name of this type of tree.
+ virtual const Name& type() const { return treeType(); }
+
+ bool operator==(const Tree&) const { OPENVDB_THROW(NotImplementedError, ""); }
+ bool operator!=(const Tree&) const { OPENVDB_THROW(NotImplementedError, ""); }
+
+ //@{
+ /// Return this tree's root node.
+ RootNodeType& root() { return mRoot; }
+ const RootNodeType& root() const { return mRoot; }
+ // Deprecate the methods below
+ RootNodeType& getRootNode() { return mRoot; }
+ const RootNodeType& getRootNode() const { return mRoot; }
+ //@}
+
+
+ //
+ // Tree methods
+ //
+ /// @brief Return @c true if the given tree has the same node and active value
+ /// topology as this tree (but possibly a different @c ValueType).
+ template<typename OtherRootNodeType>
+ bool hasSameTopology(const Tree<OtherRootNodeType>& other) const;
+
+ virtual bool evalLeafBoundingBox(CoordBBox& bbox) const;
+ virtual bool evalActiveVoxelBoundingBox(CoordBBox& bbox) const;
+ virtual bool evalActiveVoxelDim(Coord& dim) const;
+ virtual bool evalLeafDim(Coord& dim) const;
+
+ /// @brief Traverse the type hierarchy of nodes, and return, in @a dims, a list
+ /// of the Log2Dims of nodes in order from RootNode to LeafNode.
+ /// @note Because RootNodes are resizable, the RootNode Log2Dim is 0 for all trees.
+ static void getNodeLog2Dims(std::vector<Index>& dims);
+
+
+ //
+ // I/O methods
+ //
+ /// @brief Read the tree topology from a stream.
+ ///
+ /// This will read the tree structure and tile values, but not voxel data.
+ virtual void readTopology(std::istream&, bool saveFloatAsHalf = false);
+ /// @brief Write the tree topology to a stream.
+ ///
+ /// This will write the tree structure and tile values, but not voxel data.
+ virtual void writeTopology(std::ostream&, bool saveFloatAsHalf = false) const;
+ /// Read all data buffers for this tree.
+ virtual void readBuffers(std::istream&, bool saveFloatAsHalf = false);
+ /// Write out all data buffers for this tree.
+ virtual void writeBuffers(std::ostream&, bool saveFloatAsHalf = false) const;
+
+ virtual void print(std::ostream& os = std::cout, int verboseLevel = 1) const;
+
+
+ //
+ // Statistics
+ //
+ /// @brief Return the depth of this tree.
+ ///
+ /// A tree with only a root node and leaf nodes has depth 2, for example.
+ virtual Index treeDepth() const { return DEPTH; }
+ /// Return the number of leaf nodes.
+ virtual Index32 leafCount() const { return mRoot.leafCount(); }
+ /// Return the number of non-leaf nodes.
+ virtual Index32 nonLeafCount() const { return mRoot.nonLeafCount(); }
+ /// Return the number of active voxels stored in leaf nodes.
+ virtual Index64 activeLeafVoxelCount() const { return mRoot.onLeafVoxelCount(); }
+ /// Return the number of inactive voxels stored in leaf nodes.
+ virtual Index64 inactiveLeafVoxelCount() const { return mRoot.offLeafVoxelCount(); }
+ /// Return the total number of active voxels.
+ virtual Index64 activeVoxelCount() const { return mRoot.onVoxelCount(); }
+ /// Return the number of inactive voxels within the bounding box of all active voxels.
+ virtual Index64 inactiveVoxelCount() const;
+
+ /// Return the minimum and maximum active values in this tree.
+ void evalMinMax(ValueType &min, ValueType &max) const;
+
+ virtual Index64 memUsage() const { return sizeof(*this) + mRoot.memUsage(); }
+
+
+ //
+ // Voxel access methods (using signed indexing)
+ //
+ /// Return the value of the voxel at the given coordinates.
+ const ValueType& getValue(const Coord& xyz) const;
+ /// @brief Return the value of the voxel at the given coordinates
+ /// and update the given accessor's node cache.
+ template<typename AccessT> const ValueType& getValue(const Coord& xyz, AccessT&) const;
+
+ /// @brief Return the tree depth (0 = root) at which the value of voxel (x, y, z) resides.
+ ///
+ /// If (x, y, z) isn't explicitly represented in the tree (i.e., it is implicitly
+ /// a background voxel), return -1.
+ int getValueDepth(const Coord& xyz) const;
+
+ /// Set the value of the voxel at the given coordinates and mark the voxel as active.
+ void setValue(const Coord& xyz, const ValueType& value);
+ /// Set the value of the voxel at the given coordinates but preserve it active state.
+ void setValueOnly(const Coord& xyz, const ValueType& value);
+ /// @brief Set the value of the voxel at the given coordinates, mark the voxel as active,
+ /// and update the given accessor's node cache.
+ template<typename AccessT> void setValue(const Coord& xyz, const ValueType& value, AccessT&);
+ /// Mark the voxel at the given coordinates as active, but don't change its value.
+ void setValueOn(const Coord& xyz);
+ /// Set the value of the voxel at the given coordinates and mark the voxel as active.
+ void setValueOn(const Coord& xyz, const ValueType& value);
+ /// @brief Set the value of the voxel at the given coordinates to the minimum
+ /// of its current value and the given value, and mark the voxel as active.
+ void setValueOnMin(const Coord& xyz, const ValueType& value);
+ /// @brief Set the value of the voxel at the given coordinates to the maximum
+ /// of its current value and the given value, and mark the voxel as active.
+ void setValueOnMax(const Coord& xyz, const ValueType& value);
+ /// @brief Set the value of the voxel at the given coordinates to the sum
+ /// of its current value and the given value, and mark the voxel as active.
+ void setValueOnSum(const Coord& xyz, const ValueType& value);
+
+ /// Mark the voxel at the given coordinates as inactive, but don't change its value.
+ void setValueOff(const Coord& xyz);
+ /// Change the value of the voxel at the given coordinates and mark the voxel as inactive.
+ void setValueOff(const Coord& xyz, const ValueType& value);
+
+ /// Set the active state of the voxel at the given coordinates, but don't change its value.
+ void setActiveState(const Coord& xyz, bool on);
+
+ /// @brief Get the value of the voxel at the given coordinates.
+ /// @return @c true if the value is active.
+ bool probeValue(const Coord& xyz, ValueType& value) const;
+
+ /// Return @c true if the value at the given coordinates is active.
+ bool isValueOn(const Coord& xyz) const { return mRoot.isValueOn(xyz); }
+ /// Return @c true if the value at the given coordinates is inactive.
+ bool isValueOff(const Coord& xyz) const { return !this->isValueOn(xyz); }
+ /// Return @c true if this tree has any active tiles.
+ bool hasActiveTiles() const { return mRoot.hasActiveTiles(); }
+
+ /// @brief Set all voxels within a given axis-aligned box to a constant value.
+ /// If necessary, subdivide tiles that intersect the box.
+ /// @param bbox inclusive coordinates of opposite corners of an axis-aligned box
+ /// @param value the value to which to set voxels within the box
+ /// @param active if true, mark voxels within the box as active,
+ /// otherwise mark them as inactive
+ /// @note This operation generates a sparse, but not always optimally sparse,
+ /// representation of the filled box. Follow fill operations with a prune()
+ /// operation for optimal sparseness.
+ void fill(const CoordBBox& bbox, const ValueType& value, bool active = true);
+
+ /// Call the @c PruneOp functor for each non-root node in the tree.
+ /// If the functor returns @c true, prune the node and replace it with a tile.
+ ///
+ /// This method is used to implement all of the various pruning algorithms
+ /// (prune(), pruneInactive(), etc.). It should rarely be called directly.
+ /// @see openvdb/tree/Util.h for the definition of the @c PruneOp functor
+ template<typename PruneOp> void pruneOp(PruneOp&);
+
+ /// @brief Reduce the memory footprint of this tree by replacing with tiles
+ /// any nodes whose values are all the same (optionally to within a tolerance)
+ /// and have the same active state.
+ void prune(const ValueType& tolerance = zeroVal<ValueType>());
+
+ /// @brief Reduce the memory footprint of this tree by replacing with
+ /// tiles of the given value any nodes whose values are all inactive.
+ void pruneInactive(const ValueType&);
+
+ /// @brief Reduce the memory footprint of this tree by replacing with
+ /// background tiles any nodes whose values are all inactive.
+ void pruneInactive();
+
+ /// @brief Prune any descendants whose values are all inactive and replace them
+ /// with inactive tiles having a values equal to the first value
+ /// encountered in the (inactive) child.
+ ///
+ /// @note This method is faster then tolerance based prune and
+ /// useful for narrow-band level set applications where inactive
+ /// values are limited to either an inside or outside value.
+ void pruneLevelSet();
+
+ /// @brief Add the given leaf node to this tree, creating a new branch if necessary.
+ /// If a leaf node with the same origin already exists, replace it.
+ void addLeaf(LeafNodeType& leaf) { mRoot.addLeaf(&leaf); }
+
+ /// @brief Add a tile containing voxel (x, y, z) at the specified tree level,
+ /// creating a new branch if necessary. Delete any existing lower-level nodes
+ /// that contain (x, y, z).
+ /// @note @c Level must be greater than zero (i.e., the level of leaf nodes)
+ /// and less than this tree's depth.
+ void addTile(Index level, const Coord& xyz, const ValueType& value, bool active);
+
+ /// @brief Return a pointer to the node of type @c NodeT that contains voxel (x, y, z)
+ /// and replace it with a tile of the specified value and state.
+ /// If no such node exists, leave the tree unchanged and return @c NULL.
+ /// @note The caller takes ownership of the node and is responsible for deleting it.
+ template<typename NodeT>
+ NodeT* stealNode(const Coord& xyz, const ValueType& value, bool active);
+
+ /// @brief @return the leaf node that contains voxel (x, y, z) and
+ /// if it doesn't exist, create it, but preserve the values and
+ /// active states of all voxels.
+ ///
+ /// Use this method to preallocate a static tree topology over which to
+ /// safely perform multithreaded processing.
+ LeafNodeType* touchLeaf(const Coord& xyz);
+
+ /// @brief @return a pointer to the leaf node that contains
+ /// voxel (x, y, z) and if it doesn't exist, return NULL.
+ LeafNodeType* probeLeaf(const Coord& xyz);
+
+ /// @brief @return a const pointer to the leaf node that contains
+ /// voxel (x, y, z) and if it doesn't exist, return NULL.
+ const LeafNodeType* probeConstLeaf(const Coord& xyz) const;
+ const LeafNodeType* probeLeaf(const Coord& xyz) const { return this->probeConstLeaf(xyz); }
+
+ //
+ // Aux methods
+ //
+ /// @brief Return @c true if this tree contains no nodes other than
+ /// the root node and no tiles other than background tiles.
+ bool empty() const { return mRoot.empty(); }
+
+ /// Remove all tiles from this tree and all nodes other than the root node.
+ void clear() { this->clearAllAccessors(); mRoot.clear(); }
+
+ /// Clear all registered accessors.
+ void clearAllAccessors();
+
+ //@{
+ /// @brief Register an accessor for this tree. Registered accessors are
+ /// automatically cleared whenever one of this tree's nodes is deleted.
+ void attachAccessor(ValueAccessorBase<Tree>&) const;
+ void attachAccessor(ValueAccessorBase<const Tree>&) const;
+ //@}
+ //@{
+ /// Deregister an accessor so that it is no longer automatically cleared.
+ void releaseAccessor(ValueAccessorBase<Tree>&) const;
+ void releaseAccessor(ValueAccessorBase<const Tree>&) const;
+ //@}
+
+ /// @brief Return this tree's background value wrapped as metadata.
+ /// @note Query the metadata object for the value's type.
+ virtual Metadata::Ptr getBackgroundValue() const;
+
+ /// Return this tree's background value.
+ const ValueType& background() const { return mRoot.background(); }
+ /// @deprecated Use background()
+ OPENVDB_DEPRECATED ValueType getBackground() const { return mRoot.background(); }
+ /// Replace this tree's background value.
+ void setBackground(const ValueType& background) { mRoot.setBackground(background); }
+
+ /// Min and max are both inclusive.
+ virtual void getIndexRange(CoordBBox& bbox) const { mRoot.getIndexRange(bbox); }
+
+ /// @brief Set the values of all inactive voxels and tiles of a narrow-band
+ /// level set from the signs of the active voxels, setting outside values to
+ /// +background and inside values to -background.
+ ///
+ /// @note This method should only be used on closed, narrow-band level sets!
+ void signedFloodFill() { mRoot.signedFloodFill(); }
+
+ /// @brief Set the values of all inactive voxels and tiles of a narrow-band
+ /// level set from the signs of the active voxels, setting exterior values to
+ /// @a outside and interior values to @a inside. Set the background value
+ /// of this tree to @a outside.
+ ///
+ /// @note This method should only be used on closed, narrow-band level sets!
+ void signedFloodFill(const ValueType& outside, const ValueType& inside);
+
+ /// Move child nodes from the other tree into this tree wherever those nodes
+ /// correspond to constant-value tiles in this tree, and replace leaf-level
+ /// inactive voxels in this tree with corresponding voxels in the other tree
+ /// that are active.
+ /// @note This operation always empties the other tree.
+ void merge(Tree& other);
+
+ /// Turns active tiles into dense voxels, i.e. active leaf nodes
+ void voxelizeActiveTiles();
+
+ /// @brief Union this tree's set of active values with the active values
+ /// of the other tree, whose @c ValueType may be different.
+ /// @details The resulting state of a value is active if the corresponding value
+ /// was already active OR if it is active in the other tree. Also, a resulting
+ /// value maps to a voxel if the corresponding value already mapped to a voxel
+ /// OR if it is a voxel in the other tree. Thus, a resulting value can only
+ /// map to a tile if the corresponding value already mapped to a tile
+ /// AND if it is a tile value in other tree.
+ ///
+ /// @note This operation modifies only active states, not values.
+ /// Specifically, active tiles and voxels in this tree are not changed, and
+ /// tiles or voxels that were inactive in this tree but active in the other tree
+ /// are marked as active in this tree but left with their original values.
+ template<typename OtherRootNodeType>
+ void topologyUnion(const Tree<OtherRootNodeType>& other);
+
+ /*! For a given function @c f, use sparse traversal to compute <tt>f(this, other)</tt>
+ * over all corresponding pairs of values (tile or voxel) of this tree and the other tree
+ * and store the result in this tree.
+ * This method is typically more space-efficient than the two-tree combine2(),
+ * since it moves rather than copies nodes from the other tree into this tree.
+ * @note This operation always empties the other tree.
+ * @param other a tree of the same type as this tree
+ * @param op a functor of the form <tt>void op(const T& a, const T& b, T& result)</tt>,
+ * where @c T is this tree's @c ValueType, that computes
+ * <tt>result = f(a, b)</tt>
+ * @param prune if true, prune the resulting tree one branch at a time (this is usually
+ * more space-efficient than pruning the entire tree in one pass)
+ *
+ * @par Example:
+ * Compute the per-voxel difference between two floating-point trees,
+ * @c aTree and @c bTree, and store the result in @c aTree (leaving @c bTree empty).
+ * @code
+ * {
+ * struct Local {
+ * static inline void diff(const float& a, const float& b, float& result) {
+ * result = a - b;
+ * }
+ * };
+ * aTree.combine(bTree, Local::diff);
+ * }
+ * @endcode
+ *
+ * @par Example:
+ * Compute <tt>f * a + (1 - f) * b</tt> over all voxels of two floating-point trees,
+ * @c aTree and @c bTree, and store the result in @c aTree (leaving @c bTree empty).
+ * @code
+ * namespace {
+ * struct Blend {
+ * Blend(float f): frac(f) {}
+ * inline void operator()(const float& a, const float& b, float& result) const {
+ * result = frac * a + (1.0 - frac) * b;
+ * }
+ * float frac;
+ * };
+ * }
+ * {
+ * aTree.combine(bTree, Blend(0.25)); // 0.25 * a + 0.75 * b
+ * }
+ * @endcode
+ */
+ template<typename CombineOp>
+ void combine(Tree& other, CombineOp& op, bool prune = false);
+#ifndef _MSC_VER
+ template<typename CombineOp>
+ void combine(Tree& other, const CombineOp& op, bool prune = false);
+#endif
+
+ /*! Like combine(), but with
+ * @param other a tree of the same type as this tree
+ * @param op a functor of the form <tt>void op(CombineArgs<ValueType>& args)</tt> that
+ * computes <tt>args.setResult(f(args.a(), args.b()))</tt> and, optionally,
+ * <tt>args.setResultIsActive(g(args.aIsActive(), args.bIsActive()))</tt>
+ * for some functions @c f and @c g
+ * @param prune if true, prune the resulting tree one branch at a time (this is usually
+ * more space-efficient than pruning the entire tree in one pass)
+ *
+ * This variant passes not only the @em a and @em b values but also the active states
+ * of the @em a and @em b values to the functor, which may then return, by calling
+ * @c args.setResultIsActive(), a computed active state for the result value.
+ * By default, the result is active if either the @em a or the @em b value is active.
+ *
+ * @see openvdb/Types.h for the definition of the CombineArgs struct.
+ *
+ * @par Example:
+ * Replace voxel values in floating-point @c aTree with corresponding values
+ * from floating-point @c bTree (leaving @c bTree empty) wherever the @c bTree
+ * values are larger. Also, preserve the active states of any transferred values.
+ * @code
+ * {
+ * struct Local {
+ * static inline void max(CombineArgs<float>& args) {
+ * if (args.b() > args.a()) {
+ * // Transfer the B value and its active state.
+ * args.setResult(args.b());
+ * args.setResultIsActive(args.bIsActive());
+ * } else {
+ * // Preserve the A value and its active state.
+ * args.setResult(args.a());
+ * args.setResultIsActive(args.aIsActive());
+ * }
+ * }
+ * };
+ * aTree.combineExtended(bTree, Local::max);
+ * }
+ * @endcode
+ */
+ template<typename ExtendedCombineOp>
+ void combineExtended(Tree& other, ExtendedCombineOp& op, bool prune = false);
+#ifndef _MSC_VER
+ template<typename ExtendedCombineOp>
+ void combineExtended(Tree& other, const ExtendedCombineOp& op, bool prune = false);
+#endif
+
+ /*! For a given function @c f, use sparse traversal to compute <tt>f(a, b)</tt> over all
+ * corresponding pairs of values (tile or voxel) of trees A and B and store the result
+ * in this tree.
+ * @param a,b two trees of the same type
+ * @param op a functor of the form <tt>void op(const T& a, const T& b, T& result)</tt>,
+ * where @c T is this tree's @c ValueType, that computes
+ * <tt>result = f(a, b)</tt>
+ * @param prune if true, prune the resulting tree one branch at a time (this is usually
+ * more space-efficient than pruning the entire tree in one pass)
+ *
+ * @par Example:
+ * Compute the per-voxel difference between two floating-point trees,
+ * @c aTree and @c bTree, and store the result in a third tree.
+ * @code
+ * {
+ * struct Local {
+ * static inline void diff(const float& a, const float& b, float& result) {
+ * result = a - b;
+ * }
+ * };
+ * FloatTree resultTree;
+ * resultTree.combine2(aTree, bTree, Local::diff);
+ * }
+ * @endcode
+ */
+ template<typename CombineOp>
+ void combine2(const Tree& a, const Tree& b, CombineOp& op, bool prune = false);
+#ifndef _MSC_VER
+ template<typename CombineOp>
+ void combine2(const Tree& a, const Tree& b, const CombineOp& op, bool prune = false);
+#endif
+
+ /*! Like combine2(), but with
+ * @param a,b two trees of the same type
+ * @param op a functor of the form <tt>void op(CombineArgs<ValueType>& args)</tt> that
+ * computes <tt>args.setResult(f(args.a(), args.b()))</tt> and, optionally,
+ * <tt>args.setResultIsActive(g(args.aIsActive(), args.bIsActive()))</tt>
+ * for some functions @c f and @c g
+ * @param prune if true, prune the resulting tree one branch at a time (this is usually
+ * more space-efficient than pruning the entire tree in one pass)
+ * This variant passes not only the @em a and @em b values but also the active states
+ * of the @em a and @em b values to the functor, which may then return, by calling
+ * <tt>args.setResultIsActive()</tt>, a computed active state for the result value.
+ * By default, the result is active if either the @em a or the @em b value is active.
+ *
+ * @see openvdb/Types.h for the definition of the CombineArgs struct.
+ *
+ * @par Example:
+ * Compute the per-voxel maximum values of two floating-point trees, @c aTree
+ * and @c bTree, and store the result in a third tree. Set the active state
+ * of each output value to that of the larger of the two input values.
+ * @code
+ * {
+ * struct Local {
+ * static inline void max(CombineArgs<float>& args) {
+ * if (args.b() > args.a()) {
+ * // Transfer the B value and its active state.
+ * args.setResult(args.b());
+ * args.setResultIsActive(args.bIsActive());
+ * } else {
+ * // Preserve the A value and its active state.
+ * args.setResult(args.a());
+ * args.setResultIsActive(args.aIsActive());
+ * }
+ * }
+ * };
+ * FloatTree resultTree;
+ * resultTree.combine2Extended(aTree, bTree, Local::max);
+ * }
+ * @endcode
+ */
+ template<typename ExtendedCombineOp>
+ void combine2Extended(const Tree& a, const Tree& b, ExtendedCombineOp& op,
+ bool prune = false);
+#ifndef _MSC_VER
+ template<typename ExtendedCombineOp>
+ void combine2Extended(const Tree& a, const Tree& b, const ExtendedCombineOp&,
+ bool prune = false);
+#endif
+
+ /*! For a given function use sparse traversal to call it with
+ * bounding box information for all active tiles and leaf nodes
+ * or active voxels in the tree.
+ *
+ * @note The bounding boxes are guarenteed to be non-overlapping.
+ * @param op a template functor of the form
+ * <tt>template<Index LEVEL> void op(const
+ * CoordBBox& bbox)</tt>, where <tt>bbox</tt>
+ * defines the bbox of an active tile if <tt>LEVEL>0</tt>,
+ * and else a LeafNode or active voxel. The functor
+ * must also provide a template method of the form
+ * <tt>template<Index LEVEL> bool descent()</tt>
+ * that returns false if no bboxes
+ * are to be derived below the templated tree level. In
+ * such cases of early tree termination a bbox is
+ * instead derived from each terminating child node.
+ *
+ *
+ * @par Example:
+ * Render all active tiles and leaf nodes in a tree. Note in
+ * this example descent returns false if LEVEL==0 which means
+ * the functor will never descent to the active voxels. In
+ * other words the smallest BBoxes correspond to LeafNodes or
+ * active tiles at LEVEL=1!
+ * @code
+ * {
+ * struct RenderTilesAndLeafs {
+ * template<Index LEVEL>
+ * inline bool descent() { return LEVEL>0; }//only descent to leaf nodes
+ * //inline bool descent() { return true; }//use this to decent to voxels
+ *
+ * template<Index LEVEL>
+ * inline void operator()(const CoordBBox &bbox) {
+ * if (LEVEL>0) {
+ * // code to render active tile
+ * } else {
+ * // code to render leaf node
+ * }
+ * }
+ * };
+ * RenderTilesAndLeafs op;
+ * aTree.visitActiveBBox(op);
+ * }
+ * @endcode
+ * @see openvdb/unittest/TestTree.cc for another example.
+ */
+ template<typename BBoxOp> void visitActiveBBox(BBoxOp& op) const { mRoot.visitActiveBBox(op); }
+
+ /*! Traverse this tree in depth-first order, and at each node call the given functor
+ * with a @c DenseIterator (see Iterator.h) that points to either a child node or a
+ * tile value. If the iterator points to a child node and the functor returns true,
+ * do not descend to the child node; instead, continue the traversal at the next
+ * iterator position.
+ * @param op a functor of the form <tt>template<typename IterT> bool op(IterT&)</tt>,
+ * where @c IterT is either a RootNode::ChildAllIter,
+ * an InternalNode::ChildAllIter or a LeafNode::ChildAllIter
+ *
+ * @note There is no iterator that points to a RootNode, so to visit the root node,
+ * retrieve the @c parent() of a RootNode::ChildAllIter.
+ *
+ * @par Example:
+ * Print information about the nodes and tiles of a tree, but not individual voxels.
+ * @code
+ * namespace {
+ * template<typename TreeT>
+ * struct PrintTreeVisitor
+ * {
+ * typedef typename TreeT::RootNodeType RootT;
+ * bool visitedRoot;
+ *
+ * PrintTreeVisitor(): visitedRoot(false) {}
+ *
+ * template<typename IterT>
+ * inline bool operator()(IterT& iter)
+ * {
+ * if (!visitedRoot && iter.parent().getLevel() == RootT::LEVEL) {
+ * visitedRoot = true;
+ * std::cout << "Level-" << RootT::LEVEL << " node" << std::endl;
+ * }
+ * typename IterT::NonConstValueType value;
+ * typename IterT::ChildNodeType* child = iter.probeChild(value);
+ * if (child == NULL) {
+ * std::cout << "Tile with value " << value << std::endl;
+ * return true; // no child to visit, so stop descending
+ * }
+ * std::cout << "Level-" << child->getLevel() << " node" << std::endl;
+ * return (child->getLevel() == 0); // don't visit leaf nodes
+ * }
+ *
+ * // The generic method, above, calls iter.probeChild(), which is not defined
+ * // for LeafNode::ChildAllIter. These overloads ensure that the generic
+ * // method template doesn't get instantiated for LeafNode iterators.
+ * bool operator()(typename TreeT::LeafNodeType::ChildAllIter&) { return true; }
+ * bool operator()(typename TreeT::LeafNodeType::ChildAllCIter&) { return true; }
+ * };
+ * }
+ * {
+ * PrintTreeVisitor visitor;
+ * tree.visit(visitor);
+ * }
+ * @endcode
+ */
+ template<typename VisitorOp> void visit(VisitorOp& op);
+ template<typename VisitorOp> void visit(const VisitorOp& op);
+
+ /// Like visit(), but using @c const iterators, i.e., with
+ /// @param op a functor of the form <tt>template<typename IterT> bool op(IterT&)</tt>,
+ /// where @c IterT is either a RootNode::ChildAllCIter,
+ /// an InternalNode::ChildAllCIter or a LeafNode::ChildAllCIter
+ template<typename VisitorOp> void visit(VisitorOp& op) const;
+ template<typename VisitorOp> void visit(const VisitorOp& op) const;
+
+ /*! Traverse this tree and another tree in depth-first order, and for corresponding
+ * subregions of index space call the given functor with two @c DenseIterators
+ * (see Iterator.h), each of which points to either a child node or a tile value
+ * of this tree and the other tree. If the A iterator points to a child node
+ * and the functor returns a nonzero value with bit 0 set (e.g., 1), do not descend
+ * to the child node; instead, continue the traversal at the next A iterator position.
+ * Similarly, if the B iterator points to a child node and the functor returns a value
+ * with bit 1 set (e.g., 2), continue the traversal at the next B iterator position.
+ * @note The other tree must have the same index space and fan-out factors as
+ * this tree, but it may have a different @c ValueType and a different topology.
+ * @param other a tree of the same type as this tree
+ * @param op a functor of the form
+ * <tt>template<class AIterT, class BIterT> int op(AIterT&, BIterT&)</tt>,
+ * where @c AIterT and @c BIterT are any combination of a
+ * RootNode::ChildAllIter, an InternalNode::ChildAllIter or a
+ * LeafNode::ChildAllIter with an @c OtherTreeType::RootNode::ChildAllIter,
+ * an @c OtherTreeType::InternalNode::ChildAllIter
+ * or an @c OtherTreeType::LeafNode::ChildAllIter
+ *
+ * @par Example:
+ * Given two trees of the same type, @c aTree and @c bTree, replace leaf nodes of
+ * @c aTree with corresponding leaf nodes of @c bTree, leaving @c bTree partially empty.
+ * @code
+ * namespace {
+ * template<typename AIterT, typename BIterT>
+ * inline int stealLeafNodes(AIterT& aIter, BIterT& bIter)
+ * {
+ * typename AIterT::NonConstValueType aValue;
+ * typename AIterT::ChildNodeType* aChild = aIter.probeChild(aValue);
+ * typename BIterT::NonConstValueType bValue;
+ * typename BIterT::ChildNodeType* bChild = bIter.probeChild(bValue);
+ *
+ * const Index aLevel = aChild->getLevel(), bLevel = bChild->getLevel();
+ * if (aChild && bChild && aLevel == 0 && bLevel == 0) { // both are leaf nodes
+ * aIter.setChild(bChild); // give B's child to A
+ * bIter.setValue(bValue); // replace B's child with a constant tile value
+ * }
+ * // Don't iterate over leaf node voxels of either A or B.
+ * int skipBranch = (aLevel == 0) ? 1 : 0;
+ * if (bLevel == 0) skipBranch = skipBranch | 2;
+ * return skipBranch;
+ * }
+ * }
+ * {
+ * aTree.visit2(bTree, stealLeafNodes);
+ * }
+ * @endcode
+ */
+ template<typename OtherTreeType, typename VisitorOp>
+ void visit2(OtherTreeType& other, VisitorOp& op);
+ template<typename OtherTreeType, typename VisitorOp>
+ void visit2(OtherTreeType& other, const VisitorOp& op);
+
+ /// Like visit2(), but using @c const iterators, i.e., with
+ /// @param other a tree of the same type as this tree
+ /// @param op a functor of the form
+ /// <tt>template<class AIterT, class BIterT> int op(AIterT&, BIterT&)</tt>,
+ /// where @c AIterT and @c BIterT are any combination of a
+ /// RootNode::ChildAllCIter, an InternalNode::ChildAllCIter
+ /// or a LeafNode::ChildAllCIter with an
+ /// @c OtherTreeType::RootNode::ChildAllCIter,
+ /// an @c OtherTreeType::InternalNode::ChildAllCIter
+ /// or an @c OtherTreeType::LeafNode::ChildAllCIter
+ template<typename OtherTreeType, typename VisitorOp>
+ void visit2(OtherTreeType& other, VisitorOp& op) const;
+ template<typename OtherTreeType, typename VisitorOp>
+ void visit2(OtherTreeType& other, const VisitorOp& op) const;
+
+
+ //
+ // Iteration
+ //
+ //@{
+ /// Return an iterator over children of the root node.
+ typename RootNodeType::ChildOnCIter beginRootChildren() const { return mRoot.cbeginChildOn(); }
+ typename RootNodeType::ChildOnCIter cbeginRootChildren() const { return mRoot.cbeginChildOn(); }
+ typename RootNodeType::ChildOnIter beginRootChildren() { return mRoot.beginChildOn(); }
+ //@}
+
+ //@{
+ /// Return an iterator over non-child entries of the root node's table.
+ typename RootNodeType::ChildOffCIter beginRootTiles() const { return mRoot.cbeginChildOff(); }
+ typename RootNodeType::ChildOffCIter cbeginRootTiles() const { return mRoot.cbeginChildOff(); }
+ typename RootNodeType::ChildOffIter beginRootTiles() { return mRoot.beginChildOff(); }
+ //@}
+
+ //@{
+ /// Return an iterator over all entries of the root node's table.
+ typename RootNodeType::ChildAllCIter beginRootDense() const { return mRoot.cbeginChildAll(); }
+ typename RootNodeType::ChildAllCIter cbeginRootDense() const { return mRoot.cbeginChildAll(); }
+ typename RootNodeType::ChildAllIter beginRootDense() { return mRoot.beginChildAll(); }
+ //@}
+
+
+ //@{
+ /// Iterator over all nodes in this tree
+ typedef NodeIteratorBase<Tree, typename RootNodeType::ChildOnIter> NodeIter;
+ typedef NodeIteratorBase<const Tree, typename RootNodeType::ChildOnCIter> NodeCIter;
+ //@}
+
+ //@{
+ /// Iterator over all leaf nodes in this tree
+ typedef LeafIteratorBase<Tree, typename RootNodeType::ChildOnIter> LeafIter;
+ typedef LeafIteratorBase<const Tree, typename RootNodeType::ChildOnCIter> LeafCIter;
+ //@}
+
+ //@{
+ /// Return an iterator over all nodes in this tree.
+ NodeIter beginNode() { return NodeIter(*this); }
+ NodeCIter beginNode() const { return NodeCIter(*this); }
+ NodeCIter cbeginNode() const { return NodeCIter(*this); }
+ //@}
+
+ //@{
+ /// Return an iterator over all leaf nodes in this tree.
+ LeafIter beginLeaf() { return LeafIter(*this); }
+ LeafCIter beginLeaf() const { return LeafCIter(*this); }
+ LeafCIter cbeginLeaf() const { return LeafCIter(*this); }
+ //@}
+
+ typedef TreeValueIteratorBase<Tree, typename RootNodeType::ValueAllIter> ValueAllIter;
+ typedef TreeValueIteratorBase<const Tree, typename RootNodeType::ValueAllCIter> ValueAllCIter;
+ typedef TreeValueIteratorBase<Tree, typename RootNodeType::ValueOnIter> ValueOnIter;
+ typedef TreeValueIteratorBase<const Tree, typename RootNodeType::ValueOnCIter> ValueOnCIter;
+ typedef TreeValueIteratorBase<Tree, typename RootNodeType::ValueOffIter> ValueOffIter;
+ typedef TreeValueIteratorBase<const Tree, typename RootNodeType::ValueOffCIter> ValueOffCIter;
+
+ //@{
+ /// Return an iterator over all values (tile and voxel) across all nodes.
+ ValueAllIter beginValueAll() { return ValueAllIter(*this); }
+ ValueAllCIter beginValueAll() const { return ValueAllCIter(*this); }
+ ValueAllCIter cbeginValueAll() const { return ValueAllCIter(*this); }
+ //@}
+ //@{
+ /// Return an iterator over active values (tile and voxel) across all nodes.
+ ValueOnIter beginValueOn() { return ValueOnIter(*this); }
+ ValueOnCIter beginValueOn() const { return ValueOnCIter(*this); }
+ ValueOnCIter cbeginValueOn() const { return ValueOnCIter(*this); }
+ //@}
+ //@{
+ /// Return an iterator over inactive values (tile and voxel) across all nodes.
+ ValueOffIter beginValueOff() { return ValueOffIter(*this); }
+ ValueOffCIter beginValueOff() const { return ValueOffCIter(*this); }
+ ValueOffCIter cbeginValueOff() const { return ValueOffCIter(*this); }
+ //@}
+
+ /// @brief Return an iterator of type @c IterT (for example, begin<ValueOnIter>() is
+ /// equivalent to beginValueOn()).
+ template<typename IterT> IterT begin();
+ /// @brief Return a const iterator of type CIterT (for example, cbegin<ValueOnCIter>()
+ /// is equivalent to cbeginValueOn()).
+ template<typename CIterT> CIterT cbegin() const;
+
+
+protected:
+ typedef tbb::concurrent_hash_map<ValueAccessorBase<Tree>*, bool> AccessorRegistry;
+ typedef tbb::concurrent_hash_map<ValueAccessorBase<const Tree>*, bool> ConstAccessorRegistry;
+
+ // Disallow assignment of instances of this class.
+ Tree& operator=(const Tree&);
+
+ /// @brief Notify all registered accessors, by calling ValueAccessor::release(),
+ /// that this tree is about to be deleted.
+ void releaseAllAccessors();
+
+
+ //
+ // Data members
+ //
+ RootNodeType mRoot; // root node of the tree
+ mutable AccessorRegistry mAccessorRegistry;
+ mutable ConstAccessorRegistry mConstAccessorRegistry;
+}; // end of Tree class
+
+
+/// Tree4<T, N1, N2, N3>::Type is the type of a four-level tree
+/// (Root, Internal, Internal, Leaf) with value type T and
+/// internal and leaf node log dimensions N1, N2 and N3, respectively.
+/// This is the standard tree configuration.
+template<typename T, Index N1, Index N2, Index N3>
+struct Tree4 {
+ typedef Tree<RootNode<InternalNode<InternalNode<LeafNode<T, N3>, N2>, N1> > > Type;
+};
+
+
+////////////////////////////////////////
+
+
+inline void
+TreeBase::readTopology(std::istream& is, bool /*saveFloatAsHalf*/)
+{
+ int32_t bufferCount;
+ is.read(reinterpret_cast<char*>(&bufferCount), sizeof(int32_t));
+ if (bufferCount != 1) OPENVDB_LOG_WARN("multi-buffer trees are no longer supported");
+}
+
+
+inline void
+TreeBase::writeTopology(std::ostream& os, bool /*saveFloatAsHalf*/) const
+{
+ int32_t bufferCount = 1;
+ os.write(reinterpret_cast<char*>(&bufferCount), sizeof(int32_t));
+}
+
+
+inline void
+TreeBase::print(std::ostream& os, int /*verboseLevel*/) const
+{
+ os << " Tree Type: " << type()
+ << " Active Voxel Count: " << activeVoxelCount() << std::endl
+ << " Inactive Voxel Count: " << inactiveVoxelCount() << std::endl
+ << " Leaf Node Count: " << leafCount() << std::endl
+ << " Non-leaf Node Count: " << nonLeafCount() << std::endl;
+}
+
+
+////////////////////////////////////////
+
+
+//
+// Type traits for tree iterators
+//
+
+/// @brief TreeIterTraits provides, for all tree iterators, a begin(tree) function
+/// that returns an iterator over a tree of arbitrary type.
+template<typename TreeT, typename IterT> struct TreeIterTraits;
+
+template<typename TreeT> struct TreeIterTraits<TreeT, typename TreeT::RootNodeType::ChildOnIter> {
+ static typename TreeT::RootNodeType::ChildOnIter begin(TreeT& tree) {
+ return tree.beginRootChildren();
+ }
+};
+
+template<typename TreeT> struct TreeIterTraits<TreeT, typename TreeT::RootNodeType::ChildOnCIter> {
+ static typename TreeT::RootNodeType::ChildOnCIter begin(const TreeT& tree) {
+ return tree.cbeginRootChildren();
+ }
+};
+
+template<typename TreeT> struct TreeIterTraits<TreeT, typename TreeT::RootNodeType::ChildOffIter> {
+ static typename TreeT::RootNodeType::ChildOffIter begin(TreeT& tree) {
+ return tree.beginRootTiles();
+ }
+};
+
+template<typename TreeT> struct TreeIterTraits<TreeT, typename TreeT::RootNodeType::ChildOffCIter> {
+ static typename TreeT::RootNodeType::ChildOffCIter begin(const TreeT& tree) {
+ return tree.cbeginRootTiles();
+ }
+};
+
+template<typename TreeT> struct TreeIterTraits<TreeT, typename TreeT::RootNodeType::ChildAllIter> {
+ static typename TreeT::RootNodeType::ChildAllIter begin(TreeT& tree) {
+ return tree.beginRootDense();
+ }
+};
+
+template<typename TreeT> struct TreeIterTraits<TreeT, typename TreeT::RootNodeType::ChildAllCIter> {
+ static typename TreeT::RootNodeType::ChildAllCIter begin(const TreeT& tree) {
+ return tree.cbeginRootDense();
+ }
+};
+
+template<typename TreeT> struct TreeIterTraits<TreeT, typename TreeT::NodeIter> {
+ static typename TreeT::NodeIter begin(TreeT& tree) { return tree.beginNode(); }
+};
+
+template<typename TreeT> struct TreeIterTraits<TreeT, typename TreeT::NodeCIter> {
+ static typename TreeT::NodeCIter begin(const TreeT& tree) { return tree.cbeginNode(); }
+};
+
+template<typename TreeT> struct TreeIterTraits<TreeT, typename TreeT::LeafIter> {
+ static typename TreeT::LeafIter begin(TreeT& tree) { return tree.beginLeaf(); }
+};
+
+template<typename TreeT> struct TreeIterTraits<TreeT, typename TreeT::LeafCIter> {
+ static typename TreeT::LeafCIter begin(const TreeT& tree) { return tree.cbeginLeaf(); }
+};
+
+template<typename TreeT> struct TreeIterTraits<TreeT, typename TreeT::ValueOnIter> {
+ static typename TreeT::ValueOnIter begin(TreeT& tree) { return tree.beginValueOn(); }
+};
+
+template<typename TreeT> struct TreeIterTraits<TreeT, typename TreeT::ValueOnCIter> {
+ static typename TreeT::ValueOnCIter begin(const TreeT& tree) { return tree.cbeginValueOn(); }
+};
+
+template<typename TreeT> struct TreeIterTraits<TreeT, typename TreeT::ValueOffIter> {
+ static typename TreeT::ValueOffIter begin(TreeT& tree) { return tree.beginValueOff(); }
+};
+
+template<typename TreeT> struct TreeIterTraits<TreeT, typename TreeT::ValueOffCIter> {
+ static typename TreeT::ValueOffCIter begin(const TreeT& tree) { return tree.cbeginValueOff(); }
+};
+
+template<typename TreeT> struct TreeIterTraits<TreeT, typename TreeT::ValueAllIter> {
+ static typename TreeT::ValueAllIter begin(TreeT& tree) { return tree.beginValueAll(); }
+};
+
+template<typename TreeT> struct TreeIterTraits<TreeT, typename TreeT::ValueAllCIter> {
+ static typename TreeT::ValueAllCIter begin(const TreeT& tree) { return tree.cbeginValueAll(); }
+};
+
+
+template<typename RootNodeType>
+template<typename IterT>
+inline IterT
+Tree<RootNodeType>::begin()
+{
+ return TreeIterTraits<Tree, IterT>::begin(*this);
+}
+
+
+template<typename RootNodeType>
+template<typename IterT>
+inline IterT
+Tree<RootNodeType>::cbegin() const
+{
+ return TreeIterTraits<Tree, IterT>::begin(*this);
+}
+
+
+////////////////////////////////////////
+
+
+template<typename RootNodeType>
+void
+Tree<RootNodeType>::readTopology(std::istream& is, bool saveFloatAsHalf)
+{
+ this->clearAllAccessors();
+ TreeBase::readTopology(is, saveFloatAsHalf);
+ mRoot.readTopology(is, saveFloatAsHalf);
+}
+
+
+template<typename RootNodeType>
+void
+Tree<RootNodeType>::writeTopology(std::ostream& os, bool saveFloatAsHalf) const
+{
+ TreeBase::writeTopology(os, saveFloatAsHalf);
+ mRoot.writeTopology(os, saveFloatAsHalf);
+}
+
+
+template<typename RootNodeType>
+inline void
+Tree<RootNodeType>::readBuffers(std::istream &is, bool saveFloatAsHalf)
+{
+ this->clearAllAccessors();
+ mRoot.readBuffers(is, saveFloatAsHalf);
+}
+
+
+template<typename RootNodeType>
+inline void
+Tree<RootNodeType>::writeBuffers(std::ostream &os, bool saveFloatAsHalf) const
+{
+ mRoot.writeBuffers(os, saveFloatAsHalf);
+}
+
+
+////////////////////////////////////////
+
+
+template<typename RootNodeType>
+inline void
+Tree<RootNodeType>::attachAccessor(ValueAccessorBase<Tree>& accessor) const
+{
+ typename AccessorRegistry::accessor a;
+ mAccessorRegistry.insert(a, &accessor);
+}
+
+
+template<typename RootNodeType>
+inline void
+Tree<RootNodeType>::attachAccessor(ValueAccessorBase<const Tree>& accessor) const
+{
+ typename ConstAccessorRegistry::accessor a;
+ mConstAccessorRegistry.insert(a, &accessor);
+}
+
+
+template<typename RootNodeType>
+inline void
+Tree<RootNodeType>::releaseAccessor(ValueAccessorBase<Tree>& accessor) const
+{
+ mAccessorRegistry.erase(&accessor);
+}
+
+
+template<typename RootNodeType>
+inline void
+Tree<RootNodeType>::releaseAccessor(ValueAccessorBase<const Tree>& accessor) const
+{
+ mConstAccessorRegistry.erase(&accessor);
+}
+
+
+template<typename RootNodeType>
+inline void
+Tree<RootNodeType>::clearAllAccessors()
+{
+ for (typename AccessorRegistry::iterator it = mAccessorRegistry.begin();
+ it != mAccessorRegistry.end(); ++it)
+ {
+ if (it->first) it->first->clear();
+ }
+
+ for (typename ConstAccessorRegistry::iterator it = mConstAccessorRegistry.begin();
+ it != mConstAccessorRegistry.end(); ++it)
+ {
+ if (it->first) it->first->clear();
+ }
+}
+
+
+template<typename RootNodeType>
+inline void
+Tree<RootNodeType>::releaseAllAccessors()
+{
+ mAccessorRegistry.erase(NULL);
+ for (typename AccessorRegistry::iterator it = mAccessorRegistry.begin();
+ it != mAccessorRegistry.end(); ++it)
+ {
+ it->first->release();
+ }
+ mAccessorRegistry.clear();
+
+ mAccessorRegistry.erase(NULL);
+ for (typename ConstAccessorRegistry::iterator it = mConstAccessorRegistry.begin();
+ it != mConstAccessorRegistry.end(); ++it)
+ {
+ it->first->release();
+ }
+ mConstAccessorRegistry.clear();
+}
+
+
+////////////////////////////////////////
+
+
+template<typename RootNodeType>
+inline const typename RootNodeType::ValueType&
+Tree<RootNodeType>::getValue(const Coord& xyz) const
+{
+ return mRoot.getValue(xyz);
+}
+
+
+template<typename RootNodeType>
+template<typename AccessT>
+inline const typename RootNodeType::ValueType&
+Tree<RootNodeType>::getValue(const Coord& xyz, AccessT& accessor) const
+{
+ return accessor.getValue(xyz);
+}
+
+
+template<typename RootNodeType>
+inline int
+Tree<RootNodeType>::getValueDepth(const Coord& xyz) const
+{
+ return mRoot.getValueDepth(xyz);
+}
+
+
+template<typename RootNodeType>
+inline void
+Tree<RootNodeType>::setValueOff(const Coord& xyz)
+{
+ mRoot.setValueOff(xyz);
+}
+
+
+template<typename RootNodeType>
+inline void
+Tree<RootNodeType>::setValueOff(const Coord& xyz, const ValueType& value)
+{
+ mRoot.setValueOff(xyz, value);
+}
+
+
+template<typename RootNodeType>
+inline void
+Tree<RootNodeType>::setActiveState(const Coord& xyz, bool on)
+{
+ mRoot.setActiveState(xyz, on);
+}
+
+
+template<typename RootNodeType>
+inline void
+Tree<RootNodeType>::setValue(const Coord& xyz, const ValueType& value)
+{
+ mRoot.setValueOn(xyz, value);
+}
+
+template<typename RootNodeType>
+inline void
+Tree<RootNodeType>::setValueOnly(const Coord& xyz, const ValueType& value)
+{
+ mRoot.setValueOnly(xyz, value);
+}
+
+template<typename RootNodeType>
+template<typename AccessT>
+inline void
+Tree<RootNodeType>::setValue(const Coord& xyz, const ValueType& value, AccessT& accessor)
+{
+ accessor.setValue(xyz, value);
+}
+
+
+template<typename RootNodeType>
+inline void
+Tree<RootNodeType>::setValueOn(const Coord& xyz)
+{
+ mRoot.setActiveState(xyz, true);
+}
+
+
+template<typename RootNodeType>
+inline void
+Tree<RootNodeType>::setValueOn(const Coord& xyz, const ValueType& value)
+{
+ mRoot.setValueOn(xyz, value);
+}
+
+
+template<typename RootNodeType>
+inline void
+Tree<RootNodeType>::setValueOnMin(const Coord& xyz, const ValueType& value)
+{
+ mRoot.setValueOnMin(xyz, value);
+}
+
+
+template<typename RootNodeType>
+inline void
+Tree<RootNodeType>::setValueOnMax(const Coord& xyz, const ValueType& value)
+{
+ mRoot.setValueOnMax(xyz, value);
+}
+
+
+template<typename RootNodeType>
+inline void
+Tree<RootNodeType>::setValueOnSum(const Coord& xyz, const ValueType& value)
+{
+ mRoot.setValueOnSum(xyz, value);
+}
+
+
+template<typename RootNodeType>
+inline bool
+Tree<RootNodeType>::probeValue(const Coord& xyz, ValueType& value) const
+{
+ return mRoot.probeValue(xyz, value);
+}
+
+
+////////////////////////////////////////
+
+
+template<typename RootNodeType>
+template<typename PruneOp>
+inline void
+Tree<RootNodeType>::pruneOp(PruneOp& op)
+{
+ this->clearAllAccessors();
+ mRoot.pruneOp(op);
+}
+
+
+template<typename RootNodeType>
+inline void
+Tree<RootNodeType>::prune(const ValueType& tolerance)
+{
+ TolerancePrune<ValueType> op(tolerance);
+ this->pruneOp(op);
+}
+
+
+template<typename RootNodeType>
+inline void
+Tree<RootNodeType>::pruneInactive(const ValueType& bg)
+{
+ InactivePrune<ValueType> op(bg);
+ this->pruneOp(op);
+}
+
+
+template<typename RootNodeType>
+inline void
+Tree<RootNodeType>::pruneInactive()
+{
+ this->pruneInactive(this->background());
+}
+
+
+template<typename RootNodeType>
+inline void
+Tree<RootNodeType>::pruneLevelSet()
+{
+ LevelSetPrune<ValueType> op(this->background());
+ this->pruneOp(op);
+}
+
+
+template<typename RootNodeType>
+inline void
+Tree<RootNodeType>::addTile(Index level, const Coord& xyz,
+ const ValueType& value, bool active)
+{
+ mRoot.addTile(level, xyz, value, active);
+}
+
+template<typename RootNodeType>
+template<typename NodeT>
+inline NodeT*
+Tree<RootNodeType>::stealNode(const Coord& xyz, const ValueType& value, bool active)
+{
+ this->clearAllAccessors();
+ return mRoot.template stealNode<NodeT>(xyz, value, active);
+}
+
+template<typename RootNodeType>
+inline typename RootNodeType::LeafNodeType*
+Tree<RootNodeType>::touchLeaf(const Coord& xyz)
+{
+ return mRoot.touchLeaf(xyz);
+}
+
+
+template<typename RootNodeType>
+inline typename RootNodeType::LeafNodeType*
+Tree<RootNodeType>::probeLeaf(const Coord& xyz)
+{
+ return mRoot.probeLeaf(xyz);
+}
+
+
+template<typename RootNodeType>
+inline const typename RootNodeType::LeafNodeType*
+Tree<RootNodeType>::probeConstLeaf(const Coord& xyz) const
+{
+ return mRoot.probeConstLeaf(xyz);
+}
+
+
+////////////////////////////////////////
+
+
+template<typename RootNodeType>
+inline void
+Tree<RootNodeType>::fill(const CoordBBox& bbox, const ValueType& value, bool active)
+{
+ this->clearAllAccessors();
+ return mRoot.fill(bbox, value, active);
+}
+
+
+template<typename RootNodeType>
+inline void
+Tree<RootNodeType>::signedFloodFill(const ValueType& outside, const ValueType& inside)
+{
+ mRoot.signedFloodFill(outside, inside);
+}
+
+
+template<typename RootNodeType>
+Metadata::Ptr
+Tree<RootNodeType>::getBackgroundValue() const
+{
+ Metadata::Ptr result;
+ if (Metadata::isRegisteredType(valueType())) {
+ typedef TypedMetadata<ValueType> MetadataT;
+ result = Metadata::createMetadata(valueType());
+ if (MetadataT* m = dynamic_cast<MetadataT*>(result.get())) {
+ m->value() = mRoot.background();
+ }
+ }
+ return result;
+}
+
+
+////////////////////////////////////////
+
+
+template<typename RootNodeType>
+inline void
+Tree<RootNodeType>::merge(Tree& other)
+{
+ this->clearAllAccessors();
+ other.clearAllAccessors();
+ mRoot.merge(other.mRoot);
+}
+
+
+template<typename RootNodeType>
+inline void
+Tree<RootNodeType>::voxelizeActiveTiles()
+{
+ this->clearAllAccessors();
+ mRoot.voxelizeActiveTiles();
+}
+
+
+template<typename RootNodeType>
+template<typename OtherRootNodeType>
+inline void
+Tree<RootNodeType>::topologyUnion(const Tree<OtherRootNodeType>& other)
+{
+ this->clearAllAccessors();
+ mRoot.topologyUnion(other.getRootNode());
+}
+
+
+////////////////////////////////////////
+
+
+/// @brief Helper class to adapt a three-argument (a, b, result) CombineOp functor
+/// into a single-argument functor that accepts a CombineArgs struct
+template<typename ValueT, typename CombineOp>
+struct CombineOpAdapter
+{
+ CombineOpAdapter(CombineOp& op): op(op) {}
+
+ void operator()(CombineArgs<ValueT>& args) const {
+ op(args.a(), args.b(), args.result());
+ }
+
+ CombineOp& op;
+};
+
+
+template<typename RootNodeType>
+template<typename CombineOp>
+inline void
+Tree<RootNodeType>::combine(Tree& other, CombineOp& op, bool prune)
+{
+ CombineOpAdapter<ValueType, CombineOp> extendedOp(op);
+ this->combineExtended(other, extendedOp, prune);
+}
+
+
+/// @internal This overload is needed (for ICC and GCC, but not for VC) to disambiguate
+/// code like this: <tt>aTree.combine(bTree, MyCombineOp(...))</tt>.
+#ifndef _MSC_VER
+template<typename RootNodeType>
+template<typename CombineOp>
+inline void
+Tree<RootNodeType>::combine(Tree& other, const CombineOp& op, bool prune)
+{
+ CombineOpAdapter<ValueType, const CombineOp> extendedOp(op);
+ this->combineExtended(other, extendedOp, prune);
+}
+#endif
+
+
+template<typename RootNodeType>
+template<typename ExtendedCombineOp>
+inline void
+Tree<RootNodeType>::combineExtended(Tree& other, ExtendedCombineOp& op, bool prune)
+{
+ this->clearAllAccessors();
+ mRoot.combine(other.getRootNode(), op, prune);
+}
+
+
+/// @internal This overload is needed (for ICC and GCC, but not for VC) to disambiguate
+/// code like this: <tt>aTree.combineExtended(bTree, MyCombineOp(...))</tt>.
+#ifndef _MSC_VER
+template<typename RootNodeType>
+template<typename ExtendedCombineOp>
+inline void
+Tree<RootNodeType>::combineExtended(Tree& other, const ExtendedCombineOp& op, bool prune)
+{
+ this->clearAllAccessors();
+ mRoot.template combine<const ExtendedCombineOp>(other.mRoot, op, prune);
+}
+#endif
+
+
+template<typename RootNodeType>
+template<typename CombineOp>
+inline void
+Tree<RootNodeType>::combine2(const Tree& a, const Tree& b, CombineOp& op, bool prune)
+{
+ CombineOpAdapter<ValueType, CombineOp> extendedOp(op);
+ this->combine2Extended(a, b, extendedOp, prune);
+}
+
+
+/// @internal This overload is needed (for ICC and GCC, but not for VC) to disambiguate
+/// code like this: <tt>tree.combine2(aTree, bTree, MyCombineOp(...))</tt>.
+#ifndef _MSC_VER
+template<typename RootNodeType>
+template<typename CombineOp>
+inline void
+Tree<RootNodeType>::combine2(const Tree& a, const Tree& b, const CombineOp& op, bool prune)
+{
+ CombineOpAdapter<ValueType, const CombineOp> extendedOp(op);
+ this->combine2Extended(a, b, extendedOp, prune);
+}
+#endif
+
+
+template<typename RootNodeType>
+template<typename ExtendedCombineOp>
+inline void
+Tree<RootNodeType>::combine2Extended(const Tree& a, const Tree& b,
+ ExtendedCombineOp& op, bool prune)
+{
+ this->clearAllAccessors();
+ mRoot.combine2(a.mRoot, b.mRoot, op, prune);
+}
+
+
+/// @internal This overload is needed (for ICC and GCC, but not for VC) to disambiguate
+/// code like this: <tt>tree.combine2Extended(aTree, bTree, MyCombineOp(...))</tt>.
+#ifndef _MSC_VER
+template<typename RootNodeType>
+template<typename ExtendedCombineOp>
+inline void
+Tree<RootNodeType>::combine2Extended(const Tree& a, const Tree& b,
+ const ExtendedCombineOp& op, bool prune)
+{
+ this->clearAllAccessors();
+ mRoot.template combine2<const ExtendedCombineOp>(a.mRoot, b.mRoot, op, prune);
+}
+#endif
+
+
+////////////////////////////////////////
+
+
+template<typename RootNodeType>
+template<typename VisitorOp>
+inline void
+Tree<RootNodeType>::visit(VisitorOp& op)
+{
+ this->clearAllAccessors();
+ mRoot.template visit<VisitorOp>(op);
+}
+
+
+template<typename RootNodeType>
+template<typename VisitorOp>
+inline void
+Tree<RootNodeType>::visit(VisitorOp& op) const
+{
+ mRoot.template visit<VisitorOp>(op);
+}
+
+
+/// @internal This overload is needed (for ICC and GCC, but not for VC) to disambiguate
+/// code like this: <tt>tree.visit(MyVisitorOp(...))</tt>.
+template<typename RootNodeType>
+template<typename VisitorOp>
+inline void
+Tree<RootNodeType>::visit(const VisitorOp& op)
+{
+ this->clearAllAccessors();
+ mRoot.template visit<const VisitorOp>(op);
+}
+
+
+/// @internal This overload is needed (for ICC and GCC, but not for VC) to disambiguate
+/// code like this: <tt>tree.visit(MyVisitorOp(...))</tt>.
+template<typename RootNodeType>
+template<typename VisitorOp>
+inline void
+Tree<RootNodeType>::visit(const VisitorOp& op) const
+{
+ mRoot.template visit<const VisitorOp>(op);
+}
+
+
+////////////////////////////////////////
+
+
+template<typename RootNodeType>
+template<typename OtherTreeType, typename VisitorOp>
+inline void
+Tree<RootNodeType>::visit2(OtherTreeType& other, VisitorOp& op)
+{
+ this->clearAllAccessors();
+ typedef typename OtherTreeType::RootNodeType OtherRootNodeType;
+ mRoot.template visit2<OtherRootNodeType, VisitorOp>(other.getRootNode(), op);
+}
+
+
+template<typename RootNodeType>
+template<typename OtherTreeType, typename VisitorOp>
+inline void
+Tree<RootNodeType>::visit2(OtherTreeType& other, VisitorOp& op) const
+{
+ typedef typename OtherTreeType::RootNodeType OtherRootNodeType;
+ mRoot.template visit2<OtherRootNodeType, VisitorOp>(other.getRootNode(), op);
+}
+
+
+/// @internal This overload is needed (for ICC and GCC, but not for VC) to disambiguate
+/// code like this: <tt>aTree.visit2(bTree, MyVisitorOp(...))</tt>.
+template<typename RootNodeType>
+template<typename OtherTreeType, typename VisitorOp>
+inline void
+Tree<RootNodeType>::visit2(OtherTreeType& other, const VisitorOp& op)
+{
+ this->clearAllAccessors();
+ typedef typename OtherTreeType::RootNodeType OtherRootNodeType;
+ mRoot.template visit2<OtherRootNodeType, const VisitorOp>(other.getRootNode(), op);
+}
+
+
+/// @internal This overload is needed (for ICC and GCC, but not for VC) to disambiguate
+/// code like this: <tt>aTree.visit2(bTree, MyVisitorOp(...))</tt>.
+template<typename RootNodeType>
+template<typename OtherTreeType, typename VisitorOp>
+inline void
+Tree<RootNodeType>::visit2(OtherTreeType& other, const VisitorOp& op) const
+{
+ typedef typename OtherTreeType::RootNodeType OtherRootNodeType;
+ mRoot.template visit2<OtherRootNodeType, const VisitorOp>(other.getRootNode(), op);
+}
+
+
+////////////////////////////////////////
+
+
+template<typename RootNodeType>
+inline const Name&
+Tree<RootNodeType>::treeType()
+{
+ static tbb::atomic<const Name*> sTypeName;
+ if (sTypeName == NULL) {
+ std::vector<Index> dims;
+ Tree::getNodeLog2Dims(dims);
+ std::ostringstream ostr;
+ ostr << "Tree_" << typeNameAsString<ValueType>();
+ for (size_t i = 1, N = dims.size(); i < N; ++i) { // start from 1 to skip the RootNode
+ ostr << "_" << dims[i];
+ }
+ Name* s = new Name(ostr.str());
+ sTypeName.compare_and_swap(s, NULL);
+ if (sTypeName != s) delete s;
+ }
+ return *sTypeName;
+}
+
+
+template<typename RootNodeType>
+template<typename OtherRootNodeType>
+inline bool
+Tree<RootNodeType>::hasSameTopology(const Tree<OtherRootNodeType>& other) const
+{
+ return mRoot.hasSameTopology(other.getRootNode());
+}
+
+
+template<typename RootNodeType>
+Index64
+Tree<RootNodeType>::inactiveVoxelCount() const
+{
+ Coord dim(0, 0, 0);
+ this->evalActiveVoxelDim(dim);
+ const Index64
+ totalVoxels = dim.x() * dim.y() * dim.z(),
+ activeVoxels = this->activeVoxelCount();
+ assert(totalVoxels >= activeVoxels);
+ return totalVoxels - activeVoxels;
+}
+
+
+template<typename RootNodeType>
+inline bool
+Tree<RootNodeType>::evalLeafBoundingBox(CoordBBox& bbox) const
+{
+ if (this->empty()) {
+ bbox = CoordBBox(); // return default bbox
+ return false;// empty
+ }
+
+ bbox.min() = Coord::max();
+ bbox.max() = -Coord::max();
+
+ Coord ijk;
+ for (LeafCIter bIter(*this); bIter; ++bIter) {
+ bIter->getOrigin(ijk);
+ bbox.expand(ijk);
+ }
+ bbox.max() += Coord(LeafNodeType::dim()-1);
+ return true; // not empty
+}
+
+template<typename RootNodeType>
+inline bool
+Tree<RootNodeType>::evalActiveVoxelBoundingBox(CoordBBox& bbox) const
+{
+ bbox = CoordBBox(); // default invalid bbox
+ if (this->empty()) return false; // empty
+
+ mRoot.evalActiveVoxelBoundingBox(bbox);
+
+ return true;// not empty
+}
+
+
+template<typename RootNodeType>
+inline bool
+Tree<RootNodeType>::evalActiveVoxelDim(Coord& dim) const
+{
+ CoordBBox bbox;
+ bool notEmpty = this->evalActiveVoxelBoundingBox(bbox);
+ dim = bbox.extents();
+ return notEmpty;
+}
+
+
+template<typename RootNodeType>
+inline bool
+Tree<RootNodeType>::evalLeafDim(Coord& dim) const
+{
+ CoordBBox bbox;
+ bool notEmpty = this->evalLeafBoundingBox(bbox);
+ dim = bbox.extents();
+ return notEmpty;
+}
+
+
+template<typename RootNodeType>
+inline void
+Tree<RootNodeType>::evalMinMax(ValueType& minVal, ValueType& maxVal) const
+{
+ minVal = maxVal = zeroVal<ValueType>();
+ if (ValueOnCIter iter = this->cbeginValueOn()) {
+ minVal = maxVal = *iter;
+ for (++iter; iter; ++iter) {
+ const ValueType& val = *iter;
+ if (val < minVal) minVal = val;
+ if (val > maxVal) maxVal = val;
+ }
+ }
+}
+
+
+template<typename RootNodeType>
+inline void
+Tree<RootNodeType>::getNodeLog2Dims(std::vector<Index>& dims)
+{
+ dims.clear();
+ RootNodeType::getNodeLog2Dims(dims);
+}
+
+
+template<typename RootNodeType>
+inline void
+Tree<RootNodeType>::print(std::ostream& os, int verboseLevel) const
+{
+ if (verboseLevel <= 0) return;
+
+ struct OnExit {
+ std::ostream& os;
+ std::streamsize savedPrecision;
+ OnExit(std::ostream& os): os(os), savedPrecision(os.precision()) {}
+ ~OnExit() { os.precision(savedPrecision); }
+ };
+ OnExit restorePrecision(os);
+
+ std::vector<Index> dims;
+ Tree::getNodeLog2Dims(dims);
+
+ std::vector<Index64> nodeCount;
+
+ os << "Information about Tree:\n"
+ << " Type: " << this->type() << "\n";
+
+ os << " Configuration:\n";
+ if (verboseLevel <= 1) {
+ // Print node types and sizes.
+ os << " Root(" << mRoot.getTableSize() << ")";
+ if (dims.size() > 1) {
+ for (size_t i = 1, N = dims.size() - 1; i < N; ++i) {
+ os << ", Internal(" << (1 << dims[i]) << "^3)";
+ }
+ os << ", Leaf(" << (1 << *dims.rbegin()) << "^3)\n";
+ }
+ } else {
+ // Print node types, counts and sizes.
+ nodeCount.resize(dims.size());
+ for (NodeCIter it = cbeginNode(); it; ++it) {
+ ++(nodeCount[it.getDepth()]);
+ }
+ os << " Root(1 x " << mRoot.getTableSize() << ")";
+ if (dims.size() > 1) {
+ for (size_t i = 1, N = dims.size() - 1; i < N; ++i) {
+ os << ", Internal(" << util::formattedInt(nodeCount[i]);
+ os << " x " << (1 << dims[i]) << "^3)";
+ }
+ os << ", Leaf(" << util::formattedInt(*nodeCount.rbegin());
+ os << " x " << (1 << *dims.rbegin()) << "^3)\n";
+ }
+ }
+ os << " Background value: " << mRoot.background() << "\n";
+
+ if (verboseLevel == 1) return;
+
+ // The following is tree information that is expensive to extract.
+
+ if (nodeCount.empty()) {
+ nodeCount.resize(dims.size());
+ for (NodeCIter it = cbeginNode(); it; ++it) {
+ ++(nodeCount[it.getDepth()]);
+ }
+ }
+
+ // Statistics of topology and values
+ ValueType minVal, maxVal;
+ this->evalMinMax(minVal, maxVal);
+ os << " Min value: " << minVal << "\n";
+ os << " Max value: " << maxVal << "\n";
+
+ const uint64_t
+ leafCount = *nodeCount.rbegin(),
+ numActiveVoxels = this->activeVoxelCount(),
+ numActiveLeafVoxels = this->activeLeafVoxelCount();
+
+ os << " Number of active voxels: " << util::formattedInt(numActiveVoxels) << "\n";
+
+ Coord dim(0, 0, 0);
+ uint64_t totalVoxels = 0;
+ if (numActiveVoxels) { // nonempty
+ CoordBBox bbox;
+ this->evalActiveVoxelBoundingBox(bbox);
+ dim = bbox.extents();
+ totalVoxels = dim.x() * uint64_t(dim.y()) * dim.z();
+
+ os << " Bounding box of active voxels: " << bbox << "\n";
+ os << " Dimensions of active voxels: "
+ << dim[0] << " x " << dim[1] << " x " << dim[2] << "\n";
+
+ const double activeRatio = (100.0 * numActiveVoxels) / totalVoxels;
+ os << " Percentage of active voxels: " << std::setprecision(3) << activeRatio << "%\n";
+
+ if (leafCount>0) {
+ const double fillRatio =
+ (100.0 * numActiveLeafVoxels) / (leafCount * LeafNodeType::NUM_VOXELS);
+ os << " Average leaf node fill ratio: " << fillRatio << "%\n";
+ }
+ } else {
+ os << " Tree is empty!\n";
+ }
+ os << std::flush;
+
+ if (verboseLevel == 2) return;
+
+ // Memory footprint in bytes
+ const uint64_t
+ actualMem = this->memUsage(),
+ denseMem = sizeof(ValueType) * totalVoxels,
+ voxelsMem = sizeof(ValueType) * numActiveLeafVoxels;
+ ///< @todo not accurate for BoolTree (and probably should count tile values)
+
+ os << "Memory footprint:\n";
+ util::printBytes(os, actualMem, " Actual footprint: ");
+ util::printBytes(os, voxelsMem, " Voxel footprint: ");
+
+ if (numActiveVoxels) {
+ util::printBytes(os, denseMem, " Dense* footprint: ");
+ os << " Actual footprint is " << (100.0 * actualMem / denseMem)
+ << "% of dense* footprint\n";
+ os << " Leaf voxel footprint is " << (100.0 * voxelsMem / actualMem)
+ << "% of actual footprint\n";
+ os << " *Dense refers to the smallest equivalent non-sparse volume" << std::endl;
+ }
+}
+
+} // namespace tree
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_TREE_TREE_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/tree/TreeIterator.h b/extern/openvdb/internal/openvdb/tree/TreeIterator.h
new file mode 100644
index 00000000000..7741571e972
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/tree/TreeIterator.h
@@ -0,0 +1,1360 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+//
+/// @file TreeIterator.h
+
+#ifndef OPENVDB_TREE_TREEITERATOR_HAS_BEEN_INCLUDED
+#define OPENVDB_TREE_TREEITERATOR_HAS_BEEN_INCLUDED
+
+#include <boost/mpl/front.hpp>
+#include <boost/mpl/pop_front.hpp>
+#include <boost/mpl/push_back.hpp>
+#include <boost/mpl/size.hpp>
+#include <boost/mpl/vector.hpp>
+#include <boost/static_assert.hpp>
+#include <boost/type_traits/remove_const.hpp>
+#include <tbb/blocked_range.h>
+#include <tbb/parallel_for.h>
+#include <openvdb/version.h>
+#include <openvdb/Types.h>
+
+// Prior to 0.96.1, depth-bounded value iterators always descended to the leaf level
+// and iterated past leaf nodes. Now, they never descend past the maximum depth.
+// Comment out the following line to restore the older, less-efficient behavior:
+#define ENABLE_TREE_VALUE_DEPTH_BOUND_OPTIMIZATION
+
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace tree {
+
+/// CopyConstness<T1, T2>::Type is either const T2 or T2 with no const qualifier,
+/// depending on whether T1 is const. For example,
+/// - CopyConstness<int, int>::Type is int
+/// - CopyConstness<int, const int>::Type is int
+/// - CopyConstness<const int, int>::Type is const int
+/// - CopyConstness<const int, const int>::Type is const int
+template<typename FromType, typename ToType> struct CopyConstness {
+ typedef typename boost::remove_const<ToType>::type Type;
+};
+template<typename FromType, typename ToType> struct CopyConstness<const FromType, ToType> {
+ typedef const ToType Type;
+};
+
+
+////////////////////////////////////////
+
+
+namespace iter {
+
+template<typename HeadT, int HeadLevel>
+struct InvertedTree {
+ typedef typename InvertedTree<typename HeadT::ChildNodeType, HeadLevel-1>::Type SubtreeT;
+ typedef typename boost::mpl::push_back<SubtreeT, HeadT>::type Type;
+};
+template<typename HeadT>
+struct InvertedTree<HeadT, /*HeadLevel=*/1> {
+ typedef typename boost::mpl::vector<typename HeadT::ChildNodeType, HeadT>::type Type;
+};
+
+} // namespace iter
+
+
+////////////////////////////////////////
+
+
+/// IterTraits provides the following for iterators of the standard types,
+/// i.e., for {Child,Value}{On,Off,All}{Iter,CIter}:
+/// - a NodeConverter template to convert an iterator for one type of node
+/// to an iterator of the same type for another type of node; for example,
+/// IterTraits<RootNode, RootNode::ValueOnIter>::NodeConverter<LeafNode>::Type
+/// is synonymous with LeafNode::ValueOnIter.
+/// - a begin(node) function that returns a begin iterator for a node of arbitrary type;
+/// for example, IterTraits<LeafNode, LeafNode::ValueOnIter>::begin(leaf) returns
+/// leaf.beginValueOn()
+/// - a getChild() function that returns a pointer to the child node to which the iterator
+/// is currently pointing (always NULL if the iterator is a Value iterator)
+template<typename NodeT, typename IterT>
+struct IterTraits
+{
+ template<typename ChildT> static ChildT* getChild(const IterT&) { return NULL; }
+};
+
+template<typename NodeT>
+struct IterTraits<NodeT, typename NodeT::ChildOnIter>
+{
+ typedef typename NodeT::ChildOnIter IterT;
+ static IterT begin(NodeT& node) { return node.beginChildOn(); }
+ template<typename ChildT> static ChildT* getChild(const IterT& iter) {
+ return &iter.getValue();
+ }
+ template<typename OtherNodeT> struct NodeConverter {
+ typedef typename OtherNodeT::ChildOnIter Type;
+ };
+};
+
+template<typename NodeT>
+struct IterTraits<NodeT, typename NodeT::ChildOnCIter>
+{
+ typedef typename NodeT::ChildOnCIter IterT;
+ static IterT begin(const NodeT& node) { return node.cbeginChildOn(); }
+ template<typename ChildT> static const ChildT* getChild(const IterT& iter) {
+ return &iter.getValue();
+ }
+ template<typename OtherNodeT> struct NodeConverter {
+ typedef typename OtherNodeT::ChildOnCIter Type;
+ };
+};
+
+template<typename NodeT>
+struct IterTraits<NodeT, typename NodeT::ChildOffIter>
+{
+ typedef typename NodeT::ChildOffIter IterT;
+ static IterT begin(NodeT& node) { return node.beginChildOff(); }
+ template<typename OtherNodeT> struct NodeConverter {
+ typedef typename OtherNodeT::ChildOffIter Type;
+ };
+};
+
+template<typename NodeT>
+struct IterTraits<NodeT, typename NodeT::ChildOffCIter>
+{
+ typedef typename NodeT::ChildOffCIter IterT;
+ static IterT begin(const NodeT& node) { return node.cbeginChildOff(); }
+ template<typename OtherNodeT> struct NodeConverter {
+ typedef typename OtherNodeT::ChildOffCIter Type;
+ };
+};
+
+template<typename NodeT>
+struct IterTraits<NodeT, typename NodeT::ChildAllIter>
+{
+ typedef typename NodeT::ChildAllIter IterT;
+ static IterT begin(NodeT& node) { return node.beginChildAll(); }
+ template<typename ChildT> static ChildT* getChild(const IterT& iter) {
+ typename IterT::NonConstValueType val;
+ return iter.probeChild(val);
+ }
+ template<typename OtherNodeT> struct NodeConverter {
+ typedef typename OtherNodeT::ChildAllIter Type;
+ };
+};
+
+template<typename NodeT>
+struct IterTraits<NodeT, typename NodeT::ChildAllCIter>
+{
+ typedef typename NodeT::ChildAllCIter IterT;
+ static IterT begin(const NodeT& node) { return node.cbeginChildAll(); }
+ template<typename ChildT> static ChildT* getChild(const IterT& iter) {
+ typename IterT::NonConstValueType val;
+ return iter.probeChild(val);
+ }
+ template<typename OtherNodeT> struct NodeConverter {
+ typedef typename OtherNodeT::ChildAllCIter Type;
+ };
+};
+
+template<typename NodeT>
+struct IterTraits<NodeT, typename NodeT::ValueOnIter>
+{
+ typedef typename NodeT::ValueOnIter IterT;
+ static IterT begin(NodeT& node) { return node.beginValueOn(); }
+ template<typename OtherNodeT> struct NodeConverter {
+ typedef typename OtherNodeT::ValueOnIter Type;
+ };
+};
+
+template<typename NodeT>
+struct IterTraits<NodeT, typename NodeT::ValueOnCIter>
+{
+ typedef typename NodeT::ValueOnCIter IterT;
+ static IterT begin(const NodeT& node) { return node.cbeginValueOn(); }
+ template<typename OtherNodeT> struct NodeConverter {
+ typedef typename OtherNodeT::ValueOnCIter Type;
+ };
+};
+
+template<typename NodeT>
+struct IterTraits<NodeT, typename NodeT::ValueOffIter>
+{
+ typedef typename NodeT::ValueOffIter IterT;
+ static IterT begin(NodeT& node) { return node.beginValueOff(); }
+ template<typename OtherNodeT> struct NodeConverter {
+ typedef typename OtherNodeT::ValueOffIter Type;
+ };
+};
+
+template<typename NodeT>
+struct IterTraits<NodeT, typename NodeT::ValueOffCIter>
+{
+ typedef typename NodeT::ValueOffCIter IterT;
+ static IterT begin(const NodeT& node) { return node.cbeginValueOff(); }
+ template<typename OtherNodeT> struct NodeConverter {
+ typedef typename OtherNodeT::ValueOffCIter Type;
+ };
+};
+
+template<typename NodeT>
+struct IterTraits<NodeT, typename NodeT::ValueAllIter>
+{
+ typedef typename NodeT::ValueAllIter IterT;
+ static IterT begin(NodeT& node) { return node.beginValueAll(); }
+ template<typename OtherNodeT> struct NodeConverter {
+ typedef typename OtherNodeT::ValueAllIter Type;
+ };
+};
+
+template<typename NodeT>
+struct IterTraits<NodeT, typename NodeT::ValueAllCIter>
+{
+ typedef typename NodeT::ValueAllCIter IterT;
+ static IterT begin(const NodeT& node) { return node.cbeginValueAll(); }
+ template<typename OtherNodeT> struct NodeConverter {
+ typedef typename OtherNodeT::ValueAllCIter Type;
+ };
+};
+
+
+////////////////////////////////////////
+
+
+/// @brief An IterListItem is an element of a compile-time linked list of iterators
+/// to nodes of different types.
+///
+/// The list is constructed by traversing the template hierarchy of a Tree in reverse order,
+/// so typically the elements will be a LeafNode iterator of some type (e.g., ValueOnCIter),
+/// followed by one or more InternalNode iterators of the same type, followed by a RootNode
+/// iterator of the same type.
+///
+/// The length of the list is fixed at compile time, and because it is implemented using
+/// nested, templated classes, much of the list traversal logic can be optimized away.
+template<typename PrevItemT, typename NodeVecT, size_t VecSize, Index _Level>
+class IterListItem
+{
+public:
+ /// The type of iterator stored in the previous list item
+ typedef typename PrevItemT::IterT PrevIterT;
+ /// The type of node (non-const) whose iterator is stored in this list item
+ typedef typename boost::mpl::front<NodeVecT>::type _NodeT;
+ /// The type of iterator stored in this list item (e.g., InternalNode::ValueOnCIter)
+ typedef typename IterTraits<typename PrevIterT::NonConstNodeType, PrevIterT>::template
+ NodeConverter<_NodeT>::Type IterT;
+
+ /// The type of node (const or non-const) over which IterT iterates (e.g., const RootNode<...>)
+ typedef typename IterT::NodeType NodeT;
+ /// The type of the node with const qualifiers removed ("Non-Const")
+ typedef typename IterT::NonConstNodeType NCNodeT;
+ /// The type of value (with const qualifiers removed) to which the iterator points
+ typedef typename IterT::NonConstValueType NCValueT;
+ /// NodeT's child node type, with the same constness (e.g., const InternalNode<...>)
+ typedef typename CopyConstness<NodeT, typename NodeT::ChildNodeType>::Type ChildT;
+ /// NodeT's child node type with const qualifiers removed
+ typedef typename CopyConstness<NCNodeT, typename NCNodeT::ChildNodeType>::Type NCChildT;
+ typedef IterTraits<NCNodeT, IterT> ITraits;
+ /// NodeT's level in its tree (0 = LeafNode)
+ static const Index Level = _Level;
+
+ IterListItem(PrevItemT* prev): mNext(this), mPrev(prev) {}
+
+ IterListItem(const IterListItem& other): mIter(other.mIter), mNext(other.mNext), mPrev(NULL) {}
+ IterListItem& operator=(const IterListItem& other)
+ {
+ if (&other != this) {
+ mIter = other.mIter;
+ mNext = other.mNext;
+ mPrev = NULL; ///< @note external call to updateBackPointers() required
+ }
+ return *this;
+ }
+
+ void updateBackPointers(PrevItemT* prev) { mPrev = prev; mNext.updateBackPointers(this); }
+
+ void setIter(const IterT& iter) { mIter = iter; }
+ template<typename OtherIterT>
+ void setIter(const OtherIterT& iter) { mNext.setIter(iter); }
+
+ /// Return the node over which this list element's iterator iterates.
+ void getNode(Index lvl, NodeT*& node) const
+ {
+ node = (lvl <= Level) ? mIter.getParentNode() : NULL;
+ }
+ /// Return the node over which one of the following list elements' iterator iterates.
+ template<typename OtherNodeT>
+ void getNode(Index lvl, OtherNodeT*& node) const { mNext.getNode(lvl, node); }
+
+ /// @brief Initialize the iterator for level @a lvl of the tree with the node
+ /// over which the corresponding iterator of @a otherListItem is iterating.
+ ///
+ /// For example, if @a otherListItem contains a LeafNode::ValueOnIter,
+ /// initialize this list's leaf iterator with the same LeafNode.
+ template<typename OtherIterListItemT>
+ void initLevel(Index lvl, OtherIterListItemT& otherListItem)
+ {
+ if (lvl == Level) {
+ const NodeT* node = NULL;
+ otherListItem.getNode(lvl, node);
+ mIter = (node == NULL) ? IterT() : ITraits::begin(*const_cast<NodeT*>(node));
+ } else {
+ // Forward to one of the following list elements.
+ mNext.initLevel(lvl, otherListItem);
+ }
+ }
+
+ /// Return The table offset of the iterator at level @a lvl of the tree.
+ Index pos(Index lvl) const { return (lvl == Level) ? mIter.pos() : mNext.pos(lvl); }
+
+ /// Return @c true if the iterator at level @a lvl of the tree has not yet reached its end.
+ bool test(Index lvl) const { return (lvl == Level) ? mIter.test() : mNext.test(lvl); }
+
+ /// Increment the iterator at level @a lvl of the tree.
+ bool next(Index lvl) { return (lvl == Level) ? mIter.next() : mNext.next(lvl); }
+
+ /// @brief If the iterator at level @a lvl of the tree points to a child node,
+ /// initialize the next iterator in this list with that child node.
+ bool down(Index lvl)
+ {
+ if (lvl == Level && mPrev != NULL && mIter) {
+ if (ChildT* child = ITraits::template getChild<ChildT>(mIter)) {
+ mPrev->setIter(PrevItemT::ITraits::begin(*child));
+ return true;
+ }
+ }
+ return (lvl > Level) ? mNext.down(lvl) : false;
+ }
+
+ /// @brief Return the global coordinates of the voxel or tile to which the iterator
+ /// at level @a lvl of the tree is currently pointing.
+ Coord getCoord(Index lvl) const
+ {
+ return (lvl == Level) ? mIter.getCoord() : mNext.getCoord(lvl);
+ }
+ Index getChildDim(Index lvl) const
+ {
+ return (lvl == Level) ? NodeT::getChildDim() : mNext.getChildDim(lvl);
+ }
+ /// Return the number of (virtual) voxels spanned by a tile value or child node
+ Index64 getVoxelCount(Index lvl) const
+ {
+ return (lvl == Level) ? ChildT::NUM_VOXELS : mNext.getVoxelCount(lvl);
+ }
+
+ /// Return @c true if the iterator at level @a lvl of the tree points to an active value.
+ bool isValueOn(Index lvl) const
+ {
+ return (lvl == Level) ? mIter.isValueOn() : mNext.isValueOn(lvl);
+ }
+
+ /// Return the value to which the iterator at level @a lvl of the tree points.
+ const NCValueT& getValue(Index lvl) const
+ {
+ if (lvl == Level) return mIter.getValue();
+ return mNext.getValue(lvl);
+ }
+
+ /// @brief Set the value (to @a val) to which the iterator at level @a lvl
+ /// of the tree points and mark the value as active.
+ /// @note Not valid when @c IterT is a const iterator type
+ void setValue(Index lvl, const NCValueT& val) const
+ {
+ if (lvl == Level) mIter.setValue(val); else mNext.setValue(lvl, val);
+ }
+ /// @brief Set the value (to @a val) to which the iterator at level @a lvl of the tree
+ /// points and mark the value as active if @a on is @c true, or inactive otherwise.
+ /// @note Not valid when @c IterT is a const iterator type
+ void setValueOn(Index lvl, bool on = true) const
+ {
+ if (lvl == Level) mIter.setValueOn(on); else mNext.setValueOn(lvl, on);
+ }
+ /// @brief Mark the value to which the iterator at level @a lvl of the tree points
+ /// as inactive.
+ /// @note Not valid when @c IterT is a const iterator type
+ void setValueOff(Index lvl) const
+ {
+ if (lvl == Level) mIter.setValueOff(); else mNext.setValueOff(lvl);
+ }
+
+private:
+ typedef typename boost::mpl::pop_front<NodeVecT>::type RestT; // NodeVecT minus its first item
+ typedef IterListItem<IterListItem, RestT, VecSize - 1, Level + 1> NextItem;
+
+ IterT mIter;
+ NextItem mNext;
+ PrevItemT* mPrev;
+};
+
+
+/// The initial element of a compile-time linked list of iterators to nodes of different types
+template<typename PrevItemT, typename NodeVecT, size_t VecSize>
+class IterListItem<PrevItemT, NodeVecT, VecSize, /*Level=*/0U>
+{
+public:
+ /// The type of iterator stored in the previous list item
+ typedef typename PrevItemT::IterT PrevIterT;
+ /// The type of node (non-const) whose iterator is stored in this list item
+ typedef typename boost::mpl::front<NodeVecT>::type _NodeT;
+ /// The type of iterator stored in this list item (e.g., InternalNode::ValueOnCIter)
+ typedef typename IterTraits<typename PrevIterT::NonConstNodeType, PrevIterT>::template
+ NodeConverter<_NodeT>::Type IterT;
+
+ /// The type of node (const or non-const) over which IterT iterates (e.g., const RootNode<...>)
+ typedef typename IterT::NodeType NodeT;
+ /// The type of the node with const qualifiers removed ("Non-Const")
+ typedef typename IterT::NonConstNodeType NCNodeT;
+ /// The type of value (with const qualifiers removed) to which the iterator points
+ typedef typename IterT::NonConstValueType NCValueT;
+ typedef IterTraits<NCNodeT, IterT> ITraits;
+ /// NodeT's level in its tree (0 = LeafNode)
+ static const Index Level = 0;
+
+ IterListItem(PrevItemT*): mNext(this), mPrev(NULL) {}
+
+ IterListItem(const IterListItem& other): mIter(other.mIter), mNext(other.mNext), mPrev(NULL) {}
+ IterListItem& operator=(const IterListItem& other)
+ {
+ if (&other != this) {
+ mIter = other.mIter;
+ mNext = other.mNext;
+ mPrev = NULL;
+ }
+ return *this;
+ }
+
+ void updateBackPointers(PrevItemT* = NULL) { mPrev = NULL; mNext.updateBackPointers(this); }
+
+ void setIter(const IterT& iter) { mIter = iter; }
+ template<typename OtherIterT>
+ void setIter(const OtherIterT& iter) { mNext.setIter(iter); }
+
+ void getNode(Index lvl, NodeT*& node) const
+ {
+ node = (lvl == 0) ? mIter.getParentNode() : NULL;
+ }
+ template<typename OtherNodeT>
+ void getNode(Index lvl, OtherNodeT*& node) const { mNext.getNode(lvl, node); }
+
+ template<typename OtherIterListItemT>
+ void initLevel(Index lvl, OtherIterListItemT& otherListItem)
+ {
+ if (lvl == 0) {
+ const NodeT* node = NULL;
+ otherListItem.getNode(lvl, node);
+ mIter = (node == NULL) ? IterT() : ITraits::begin(*const_cast<NodeT*>(node));
+ } else {
+ mNext.initLevel(lvl, otherListItem);
+ }
+ }
+
+ Index pos(Index lvl) const { return (lvl == 0) ? mIter.pos() : mNext.pos(lvl); }
+
+ bool test(Index lvl) const { return (lvl == 0) ? mIter.test() : mNext.test(lvl); }
+
+ bool next(Index lvl) { return (lvl == 0) ? mIter.next() : mNext.next(lvl); }
+
+ bool down(Index lvl) { return (lvl == 0) ? false : mNext.down(lvl); }
+
+ Coord getCoord(Index lvl) const
+ {
+ return (lvl == 0) ? mIter.getCoord() : mNext.getCoord(lvl);
+ }
+ Index getChildDim(Index lvl) const
+ {
+ return (lvl == 0) ? NodeT::getChildDim() : mNext.getChildDim(lvl);
+ }
+
+ Index64 getVoxelCount(Index lvl) const
+ {
+ return (lvl == 0) ? 1 : mNext.getVoxelCount(lvl);
+ }
+
+ bool isValueOn(Index lvl) const
+ {
+ return (lvl == 0) ? mIter.isValueOn() : mNext.isValueOn(lvl);
+ }
+
+ const NCValueT& getValue(Index lvl) const
+ {
+ if (lvl == 0) return mIter.getValue();
+ return mNext.getValue(lvl);
+ }
+
+ void setValue(Index lvl, const NCValueT& val) const
+ {
+ if (lvl == 0) mIter.setValue(val); else mNext.setValue(lvl, val);
+ }
+ void setValueOn(Index lvl, bool on = true) const
+ {
+ if (lvl == 0) mIter.setValueOn(on); else mNext.setValueOn(lvl, on);
+ }
+ void setValueOff(Index lvl) const
+ {
+ if (lvl == 0) mIter.setValueOff(); else mNext.setValueOff(lvl);
+ }
+
+private:
+ typedef typename boost::mpl::pop_front<NodeVecT>::type RestT; // NodeVecT minus its first item
+ typedef IterListItem<IterListItem, RestT, VecSize - 1, /*Level=*/1> NextItem;
+
+ IterT mIter;
+ NextItem mNext;
+ PrevItemT* mPrev;
+};
+
+
+/// The final element of a compile-time linked list of iterators to nodes of different types
+template<typename PrevItemT, typename NodeVecT, Index _Level>
+class IterListItem<PrevItemT, NodeVecT, /*VecSize=*/1, _Level>
+{
+public:
+ typedef typename boost::mpl::front<NodeVecT>::type _NodeT;
+ /// The type of iterator stored in the previous list item
+ typedef typename PrevItemT::IterT PrevIterT;
+ /// The type of iterator stored in this list item (e.g., RootNode::ValueOnCIter)
+ typedef typename IterTraits<typename PrevIterT::NonConstNodeType, PrevIterT>::template
+ NodeConverter<_NodeT>::Type IterT;
+
+ /// The type of node over which IterT iterates (e.g., const RootNode<...>)
+ typedef typename IterT::NodeType NodeT;
+ /// The type of the node with const qualifiers removed ("Non-Const")
+ typedef typename IterT::NonConstNodeType NCNodeT;
+ /// The type of value (with const qualifiers removed) to which the iterator points
+ typedef typename IterT::NonConstValueType NCValueT;
+ /// NodeT's child node type, with the same constness (e.g., const InternalNode<...>)
+ typedef typename CopyConstness<NodeT, typename NodeT::ChildNodeType>::Type ChildT;
+ /// NodeT's child node type with const qualifiers removed
+ typedef typename CopyConstness<NCNodeT, typename NCNodeT::ChildNodeType>::Type NCChildT;
+ typedef IterTraits<NCNodeT, IterT> ITraits;
+ /// NodeT's level in its tree (0 = LeafNode)
+ static const Index Level = _Level;
+
+ IterListItem(PrevItemT* prev): mPrev(prev) {}
+
+ IterListItem(const IterListItem& other): mIter(other.mIter), mPrev(NULL) {}
+ IterListItem& operator=(const IterListItem& other)
+ {
+ if (&other != this) {
+ mIter = other.mIter;
+ mPrev = NULL; ///< @note external call to updateBackPointers() required
+ }
+ return *this;
+ }
+
+ void updateBackPointers(PrevItemT* prev) { mPrev = prev; }
+
+ // The following method specializations differ from the default template
+ // implementations mainly in that they don't forward.
+
+ void setIter(const IterT& iter) { mIter = iter; }
+
+ void getNode(Index lvl, NodeT*& node) const
+ {
+ node = (lvl <= Level) ? mIter.getParentNode() : NULL;
+ }
+
+ template<typename OtherIterListItemT>
+ void initLevel(Index lvl, OtherIterListItemT& otherListItem)
+ {
+ if (lvl == Level) {
+ const NodeT* node = NULL;
+ otherListItem.getNode(lvl, node);
+ mIter = (node == NULL) ? IterT() : ITraits::begin(*const_cast<NodeT*>(node));
+ }
+ }
+
+ Index pos(Index lvl) const { return (lvl == Level) ? mIter.pos() : Index(-1); }
+
+ bool test(Index lvl) const { return (lvl == Level) ? mIter.test() : false; }
+
+ bool next(Index lvl) { return (lvl == Level) ? mIter.next() : false; }
+
+ bool down(Index lvl)
+ {
+ if (lvl == Level && mPrev != NULL && mIter) {
+ if (ChildT* child = ITraits::template getChild<ChildT>(mIter)) {
+ mPrev->setIter(PrevItemT::ITraits::begin(*child));
+ return true;
+ }
+ }
+ return false;
+ }
+
+ Coord getCoord(Index lvl) const { return (lvl == Level) ? mIter.getCoord() : Coord(); }
+ Index getChildDim(Index lvl) const { return (lvl == Level) ? NodeT::getChildDim() : 0; }
+ Index64 getVoxelCount(Index lvl) const { return (lvl == Level) ? ChildT::NUM_VOXELS : 0; }
+
+ bool isValueOn(Index lvl) const { return (lvl == Level) ? mIter.isValueOn() : false; }
+
+ const NCValueT& getValue(Index lvl) const
+ {
+ assert(lvl == Level);
+ (void)lvl; // avoid unused variable warning in optimized builds
+ return mIter.getValue();
+ }
+
+ void setValue(Index lvl, const NCValueT& val) const { if (lvl == Level) mIter.setValue(val); }
+ void setValueOn(Index lvl, bool on = true) const { if (lvl == Level) mIter.setValueOn(on); }
+ void setValueOff(Index lvl) const { if (lvl == Level) mIter.setValueOff(); }
+
+private:
+ IterT mIter;
+ PrevItemT* mPrev;
+};
+
+
+////////////////////////////////////////
+
+
+//#define DEBUG_TREE_VALUE_ITERATOR
+
+/// @brief Base class for tree-traversal iterators over tile and voxel values
+template<typename _TreeT, typename ValueIterT>
+class TreeValueIteratorBase
+{
+public:
+ typedef _TreeT TreeT;
+ typedef typename ValueIterT::NodeType NodeT;
+ typedef typename ValueIterT::NonConstValueType ValueT;
+ typedef typename NodeT::ChildOnCIter ChildOnIterT;
+ static const Index ROOT_LEVEL = NodeT::LEVEL;
+ BOOST_STATIC_ASSERT(ValueIterT::NodeType::LEVEL == ROOT_LEVEL);
+ static const Index LEAF_LEVEL = 0, ROOT_DEPTH = 0, LEAF_DEPTH = ROOT_LEVEL;
+
+ TreeValueIteratorBase(TreeT&);
+
+ TreeValueIteratorBase(const TreeValueIteratorBase& other);
+ TreeValueIteratorBase& operator=(const TreeValueIteratorBase& other);
+
+ /// Specify the depth of the highest level of the tree to which to ascend (depth 0 = root).
+ void setMinDepth(Index minDepth);
+ /// Return the depth of the highest level of the tree to which this iterator ascends.
+ Index getMinDepth() const { return ROOT_LEVEL - Index(mMaxLevel); }
+ /// Specify the depth of the lowest level of the tree to which to descend (depth 0 = root).
+ void setMaxDepth(Index maxDepth);
+ /// Return the depth of the lowest level of the tree to which this iterator ascends.
+ Index getMaxDepth() const { return ROOT_LEVEL - Index(mMinLevel); }
+
+ //@{
+ /// Return @c true if this iterator is not yet exhausted.
+ bool test() const { return mValueIterList.test(mLevel); }
+ operator bool() const { return this->test(); }
+ //@}
+
+ /// @brief Advance to the next tile or voxel value.
+ /// Return @c true if this iterator is not yet exhausted.
+ bool next();
+ /// Advance to the next tile or voxel value.
+ TreeValueIteratorBase& operator++() { this->next(); return *this; }
+
+ /// @brief Return the level in the tree (0 = leaf) of the node to which
+ /// this iterator is currently pointing.
+ Index getLevel() const { return mLevel; }
+ /// @brief Return the depth in the tree (0 = root) of the node to which
+ /// this iterator is currently pointing.
+ Index getDepth() const { return ROOT_LEVEL - mLevel; }
+ static Index getLeafDepth() { return LEAF_DEPTH; }
+
+ /// @brief Return in @a node a pointer to the node over which this iterator is
+ /// currently iterating or one of that node's parents, as determined by @a NodeType.
+ /// @return a null pointer if @a NodeType specifies a node at a lower level
+ /// of the tree than that given by getLevel().
+ template<typename NodeType>
+ void getNode(NodeType*& node) const { mValueIterList.getNode(mLevel, node); }
+
+ /// @brief Return the global coordinates of the voxel or tile to which
+ /// this iterator is currently pointing.
+ Coord getCoord() const { return mValueIterList.getCoord(mLevel); }
+ /// @brief Return in @a bbox the axis-aligned bounding box of
+ /// the voxel or tile to which this iterator is currently pointing.
+ /// @return false if the bounding box is empty.
+ bool getBoundingBox(CoordBBox&) const;
+ /// @brief Return the axis-aligned bounding box of the voxel or tile to which
+ /// this iterator is currently pointing.
+ CoordBBox getBoundingBox() const { CoordBBox b; this->getBoundingBox(b); return b; }
+
+ /// Return the number of (virtual) voxels corresponding to the value
+ Index64 getVoxelCount() const { return mValueIterList.getVoxelCount(mLevel);}
+
+ /// Return @c true if this iterator is currently pointing to a (non-leaf) tile value.
+ bool isTileValue() const { return mLevel != 0 && this->test(); }
+ /// Return @c true if this iterator is currently pointing to a (leaf) voxel value.
+ bool isVoxelValue() const { return mLevel == 0 && this->test(); }
+ /// Return @c true if the value to which this iterator is currently pointing is active.
+ bool isValueOn() const { return mValueIterList.isValueOn(mLevel); }
+
+ //@{
+ /// Return the tile or voxel value to which this iterator is currently pointing.
+ const ValueT& getValue() const { return mValueIterList.getValue(mLevel); }
+ const ValueT& operator*() const { return this->getValue(); }
+ const ValueT* operator->() const { return &(this->operator*()); }
+ //@}
+
+ /// @brief Change the tile or voxel value to which this iterator is currently pointing
+ /// and mark it as active.
+ void setValue(const ValueT& val) const { mValueIterList.setValue(mLevel, val); }
+ /// @brief Change the active/inactive state of the tile or voxel value to which
+ /// this iterator is currently pointing.
+ void setActiveState(bool on) const { mValueIterList.setValueOn(mLevel, on); }
+ /// Mark the tile or voxel value to which this iterator is currently pointing as inactive.
+ void setValueOff() const { mValueIterList.setValueOff(mLevel); }
+
+ /// Return a pointer to the tree over which this iterator is iterating.
+ TreeT* getTree() const { return mTree; }
+
+ /// Return a string (for debugging, mainly) describing this iterator's current state.
+ std::string summary() const;
+
+private:
+ bool advance(bool dontIncrement = false);
+
+ typedef typename iter::InvertedTree<NodeT, NodeT::LEVEL>::Type InvTreeT;
+ struct PrevChildItem { typedef ChildOnIterT IterT; };
+ struct PrevValueItem { typedef ValueIterT IterT; };
+
+ IterListItem<PrevChildItem, InvTreeT, /*VecSize=*/ROOT_LEVEL+1, /*Level=*/0> mChildIterList;
+ IterListItem<PrevValueItem, InvTreeT, /*VecSize=*/ROOT_LEVEL+1, /*Level=*/0> mValueIterList;
+ Index mLevel;
+ int mMinLevel, mMaxLevel;
+ TreeT* mTree;
+}; // class TreeValueIteratorBase
+
+
+template<typename TreeT, typename ValueIterT>
+inline
+TreeValueIteratorBase<TreeT, ValueIterT>::TreeValueIteratorBase(TreeT& tree):
+ mChildIterList(NULL),
+ mValueIterList(NULL),
+ mLevel(ROOT_LEVEL),
+ mMinLevel(int(LEAF_LEVEL)),
+ mMaxLevel(int(ROOT_LEVEL)),
+ mTree(&tree)
+{
+ mChildIterList.setIter(IterTraits<NodeT, ChildOnIterT>::begin(tree.getRootNode()));
+ mValueIterList.setIter(IterTraits<NodeT, ValueIterT>::begin(tree.getRootNode()));
+ this->advance(/*dontIncrement=*/true);
+}
+
+
+template<typename TreeT, typename ValueIterT>
+inline
+TreeValueIteratorBase<TreeT, ValueIterT>::TreeValueIteratorBase(const TreeValueIteratorBase& other):
+ mChildIterList(other.mChildIterList),
+ mValueIterList(other.mValueIterList),
+ mLevel(other.mLevel),
+ mMinLevel(other.mMinLevel),
+ mMaxLevel(other.mMaxLevel),
+ mTree(other.mTree)
+{
+ mChildIterList.updateBackPointers();
+ mValueIterList.updateBackPointers();
+}
+
+
+template<typename TreeT, typename ValueIterT>
+inline TreeValueIteratorBase<TreeT, ValueIterT>&
+TreeValueIteratorBase<TreeT, ValueIterT>::operator=(const TreeValueIteratorBase& other)
+{
+ if (&other != this) {
+ mChildIterList = other.mChildIterList;
+ mValueIterList = other.mValueIterList;
+ mLevel = other.mLevel;
+ mMinLevel = other.mMinLevel;
+ mMaxLevel = other.mMaxLevel;
+ mTree = other.mTree;
+ mChildIterList.updateBackPointers();
+ mValueIterList.updateBackPointers();
+ }
+ return *this;
+}
+
+
+template<typename TreeT, typename ValueIterT>
+inline void
+TreeValueIteratorBase<TreeT, ValueIterT>::setMinDepth(Index minDepth)
+{
+ mMaxLevel = int(ROOT_LEVEL - minDepth); // level = ROOT_LEVEL - depth
+ if (int(mLevel) > mMaxLevel) this->next();
+}
+
+
+template<typename TreeT, typename ValueIterT>
+inline void
+TreeValueIteratorBase<TreeT, ValueIterT>::setMaxDepth(Index maxDepth)
+{
+ // level = ROOT_LEVEL - depth
+ mMinLevel = int(ROOT_LEVEL - std::min(maxDepth, this->getLeafDepth()));
+ if (int(mLevel) < mMinLevel) this->next();
+}
+
+
+template<typename TreeT, typename ValueIterT>
+inline bool
+TreeValueIteratorBase<TreeT, ValueIterT>::next()
+{
+ do {
+ if (!this->advance()) return false;
+ } while (int(mLevel) < mMinLevel || int(mLevel) > mMaxLevel);
+ return true;
+}
+
+
+template<typename TreeT, typename ValueIterT>
+inline bool
+TreeValueIteratorBase<TreeT, ValueIterT>::advance(bool dontIncrement)
+{
+ Index
+ vPos = mValueIterList.pos(mLevel),
+ cPos = mChildIterList.pos(mLevel);
+ if (vPos == cPos && mChildIterList.test(mLevel)) {
+ /// @todo Once ValueOff iterators properly skip child pointers, remove this block.
+ mValueIterList.next(mLevel);
+ vPos = mValueIterList.pos(mLevel);
+ }
+ if (vPos < cPos) {
+ if (dontIncrement) return true;
+ if (mValueIterList.next(mLevel)) {
+ if (mValueIterList.pos(mLevel) == cPos && mChildIterList.test(mLevel)) {
+ /// @todo Once ValueOff iterators properly skip child pointers, remove this block.
+ mValueIterList.next(mLevel);
+ }
+ // If there is a next value and it precedes the next child, return.
+ if (mValueIterList.pos(mLevel) < cPos) return true;
+ }
+ } else {
+ // Advance to the next child, which may or may not precede the next value.
+ if (!dontIncrement) mChildIterList.next(mLevel);
+ }
+#ifdef DEBUG_TREE_VALUE_ITERATOR
+ std::cout << "\n" << this->summary() << std::flush;
+#endif
+
+ // Descend to the lowest level at which the next value precedes the next child.
+ while (mChildIterList.pos(mLevel) < mValueIterList.pos(mLevel)) {
+#ifdef ENABLE_TREE_VALUE_DEPTH_BOUND_OPTIMIZATION
+ if (int(mLevel) == mMinLevel) {
+ // If the current node lies at the lowest allowed level, none of its
+ // children can be visited, so advance its child iterator to the end.
+ /// @todo Consider adding methods to iterators to advance to the end
+ /// in one step, instead of by repeated increments.
+ while (mChildIterList.test(mLevel)) mChildIterList.next(mLevel);
+ } else
+#endif
+ if (mChildIterList.down(mLevel)) {
+ --mLevel; // descend one level
+ mValueIterList.initLevel(mLevel, mChildIterList);
+ if (mValueIterList.pos(mLevel) == mChildIterList.pos(mLevel)
+ && mChildIterList.test(mLevel))
+ {
+ /// @todo Once ValueOff iterators properly skip child pointers, remove this block.
+ mValueIterList.next(mLevel);
+ }
+ } else break;
+#ifdef DEBUG_TREE_VALUE_ITERATOR
+ std::cout << "\n" << this->summary() << std::flush;
+#endif
+ }
+ // Ascend to the nearest level at which one of the iterators is not yet exhausted.
+ while (!mChildIterList.test(mLevel) && !mValueIterList.test(mLevel)) {
+ if (mLevel == ROOT_LEVEL) return false;
+ ++mLevel;
+ mChildIterList.next(mLevel);
+ this->advance(/*dontIncrement=*/true);
+ }
+ return true;
+}
+
+
+template<typename TreeT, typename ValueIterT>
+inline bool
+TreeValueIteratorBase<TreeT, ValueIterT>::getBoundingBox(CoordBBox& bbox) const
+{
+ if (!this->test()) {
+ bbox = CoordBBox();
+ return false;
+ }
+ bbox.min() = mValueIterList.getCoord(mLevel);
+ bbox.max() = bbox.min().offsetBy(mValueIterList.getChildDim(mLevel) - 1);
+ return true;
+}
+
+
+template<typename TreeT, typename ValueIterT>
+inline std::string
+TreeValueIteratorBase<TreeT, ValueIterT>::summary() const
+{
+ std::ostringstream ostr;
+ for (int lvl = int(ROOT_LEVEL); lvl >= 0 && lvl >= int(mLevel); --lvl) {
+ if (lvl == 0) ostr << "leaf";
+ else if (lvl == int(ROOT_LEVEL)) ostr << "root";
+ else ostr << "int" << (ROOT_LEVEL - lvl);
+ ostr << " v" << mValueIterList.pos(lvl)
+ << " c" << mChildIterList.pos(lvl);
+ if (lvl > int(mLevel)) ostr << " / ";
+ }
+ if (this->test() && mValueIterList.pos(mLevel) < mChildIterList.pos(mLevel)) {
+ if (mLevel == 0) {
+ ostr << " " << this->getCoord();
+ } else {
+ ostr << " " << this->getBoundingBox();
+ }
+ }
+ return ostr.str();
+}
+
+
+////////////////////////////////////////
+
+
+/// @brief Base class for tree-traversal iterators over all nodes
+template<typename _TreeT, typename RootChildOnIterT>
+class NodeIteratorBase
+{
+public:
+ typedef _TreeT TreeT;
+ typedef RootChildOnIterT RootIterT;
+ typedef typename RootIterT::NodeType RootNodeT;
+ typedef typename RootIterT::NonConstNodeType NCRootNodeT;
+ static const Index ROOT_LEVEL = RootNodeT::LEVEL;
+ typedef typename iter::InvertedTree<NCRootNodeT, ROOT_LEVEL>::Type InvTreeT;
+ static const Index LEAF_LEVEL = 0, ROOT_DEPTH = 0, LEAF_DEPTH = ROOT_LEVEL;
+
+ typedef IterTraits<NCRootNodeT, RootIterT> RootIterTraits;
+
+ NodeIteratorBase();
+ NodeIteratorBase(TreeT&);
+
+ NodeIteratorBase(const NodeIteratorBase& other);
+ NodeIteratorBase& operator=(const NodeIteratorBase& other);
+
+ /// Specify the depth of the highest level of the tree to which to ascend (depth 0 = root).
+ void setMinDepth(Index minDepth);
+ /// Return the depth of the highest level of the tree to which this iterator ascends.
+ Index getMinDepth() const { return ROOT_LEVEL - Index(mMaxLevel); }
+ /// Specify the depth of the lowest level of the tree to which to descend (depth 0 = root).
+ void setMaxDepth(Index maxDepth);
+ /// Return the depth of the lowest level of the tree to which this iterator ascends.
+ Index getMaxDepth() const { return ROOT_LEVEL - Index(mMinLevel); }
+
+ //@{
+ /// Return @c true if this iterator is not yet exhausted.
+ bool test() const { return !mDone; }
+ operator bool() const { return this->test(); }
+ //@}
+
+ /// @brief Advance to the next tile or voxel value.
+ /// @return @c true if this iterator is not yet exhausted.
+ bool next();
+ /// Advance the iterator to the next leaf node.
+ void increment() { this->next(); }
+ NodeIteratorBase& operator++() { this->increment(); return *this; }
+ /// Increment the iterator n times.
+ void increment(Index n) { for (Index i = 0; i < n && this->next(); ++i) {} }
+
+ /// @brief Return the level in the tree (0 = leaf) of the node to which
+ /// this iterator is currently pointing.
+ Index getLevel() const { return mLevel; }
+ /// @brief Return the depth in the tree (0 = root) of the node to which
+ /// this iterator is currently pointing.
+ Index getDepth() const { return ROOT_LEVEL - mLevel; }
+ static Index getLeafDepth() { return LEAF_DEPTH; }
+
+ /// @brief Return the global coordinates of the voxel or tile to which
+ /// this iterator is currently pointing.
+ Coord getCoord() const;
+ /// @brief Return in @a bbox the axis-aligned bounding box of
+ /// the voxel or tile to which this iterator is currently pointing.
+ /// @return false if the bounding box is empty.
+ bool getBoundingBox(CoordBBox& bbox) const;
+ /// @brief Return the axis-aligned bounding box of the voxel or tile to which
+ /// this iterator is currently pointing.
+ CoordBBox getBoundingBox() const { CoordBBox b; this->getBoundingBox(b); return b; }
+
+ /// @brief Return the node to which the iterator is pointing.
+ /// @note This iterator doesn't have the usual dereference operators (* and ->),
+ /// because they would have to be overloaded by the returned node type.
+ template<typename NodeT>
+ void getNode(NodeT*& node) const { node = NULL; mIterList.getNode(mLevel, node); }
+
+ TreeT* getTree() const { return mTree; }
+
+ std::string summary() const;
+
+private:
+ struct PrevItem { typedef RootIterT IterT; };
+
+ IterListItem<PrevItem, InvTreeT, /*VecSize=*/ROOT_LEVEL+1, LEAF_LEVEL> mIterList;
+ Index mLevel;
+ int mMinLevel, mMaxLevel;
+ bool mDone;
+ TreeT* mTree;
+}; // class NodeIteratorBase
+
+
+template<typename TreeT, typename RootChildOnIterT>
+inline
+NodeIteratorBase<TreeT, RootChildOnIterT>::NodeIteratorBase():
+ mIterList(NULL),
+ mLevel(ROOT_LEVEL),
+ mMinLevel(int(LEAF_LEVEL)),
+ mMaxLevel(int(ROOT_LEVEL)),
+ mDone(true),
+ mTree(NULL)
+{
+}
+
+
+template<typename TreeT, typename RootChildOnIterT>
+inline
+NodeIteratorBase<TreeT, RootChildOnIterT>::NodeIteratorBase(TreeT& tree):
+ mIterList(NULL),
+ mLevel(ROOT_LEVEL),
+ mMinLevel(int(LEAF_LEVEL)),
+ mMaxLevel(int(ROOT_LEVEL)),
+ mDone(false),
+ mTree(&tree)
+{
+ mIterList.setIter(RootIterTraits::begin(tree.getRootNode()));
+}
+
+
+template<typename TreeT, typename RootChildOnIterT>
+inline
+NodeIteratorBase<TreeT, RootChildOnIterT>::NodeIteratorBase(const NodeIteratorBase& other):
+ mIterList(other.mIterList),
+ mLevel(other.mLevel),
+ mMinLevel(other.mMinLevel),
+ mMaxLevel(other.mMaxLevel),
+ mDone(other.mDone),
+ mTree(other.mTree)
+{
+ mIterList.updateBackPointers();
+}
+
+
+template<typename TreeT, typename RootChildOnIterT>
+inline NodeIteratorBase<TreeT, RootChildOnIterT>&
+NodeIteratorBase<TreeT, RootChildOnIterT>::operator=(const NodeIteratorBase& other)
+{
+ if (&other != this) {
+ mLevel = other.mLevel;
+ mMinLevel = other.mMinLevel;
+ mMaxLevel = other.mMaxLevel;
+ mDone = other.mDone;
+ mTree = other.mTree;
+ mIterList = other.mIterList;
+ mIterList.updateBackPointers();
+ }
+ return *this;
+}
+
+
+template<typename TreeT, typename RootChildOnIterT>
+inline void
+NodeIteratorBase<TreeT, RootChildOnIterT>::setMinDepth(Index minDepth)
+{
+ mMaxLevel = int(ROOT_LEVEL - minDepth); // level = ROOT_LEVEL - depth
+ if (int(mLevel) > mMaxLevel) this->next();
+}
+
+
+template<typename TreeT, typename RootChildOnIterT>
+inline void
+NodeIteratorBase<TreeT, RootChildOnIterT>::setMaxDepth(Index maxDepth)
+{
+ // level = ROOT_LEVEL - depth
+ mMinLevel = int(ROOT_LEVEL - std::min(maxDepth, this->getLeafDepth()));
+ if (int(mLevel) < mMinLevel) this->next();
+}
+
+
+template<typename TreeT, typename RootChildOnIterT>
+inline bool
+NodeIteratorBase<TreeT, RootChildOnIterT>::next()
+{
+ do {
+ if (mDone) return false;
+
+ // If the iterator over the current node points to a child,
+ // descend to the child (depth-first traversal).
+ if (int(mLevel) > mMinLevel && mIterList.test(mLevel)) {
+ if (!mIterList.down(mLevel)) return false;
+ --mLevel;
+ } else {
+ // Ascend to the nearest ancestor that has other children.
+ while (!mIterList.test(mLevel)) {
+ if (mLevel == ROOT_LEVEL) {
+ // Can't ascend higher than the root.
+ mDone = true;
+ return false;
+ }
+ ++mLevel; // ascend one level
+ mIterList.next(mLevel); // advance to the next child, if there is one
+ }
+ // Descend to the child.
+ if (!mIterList.down(mLevel)) return false;
+ --mLevel;
+ }
+ } while (int(mLevel) < mMinLevel || int(mLevel) > mMaxLevel);
+ return true;
+}
+
+
+template<typename TreeT, typename RootChildOnIterT>
+inline Coord
+NodeIteratorBase<TreeT, RootChildOnIterT>::getCoord() const
+{
+ if (mLevel != ROOT_LEVEL) return mIterList.getCoord(mLevel + 1);
+ RootNodeT* root = NULL;
+ this->getNode(root);
+ return root ? root->getMinIndex() : Coord::min();
+}
+
+
+template<typename TreeT, typename RootChildOnIterT>
+inline bool
+NodeIteratorBase<TreeT, RootChildOnIterT>::getBoundingBox(CoordBBox& bbox) const
+{
+ if (mLevel == ROOT_LEVEL) {
+ RootNodeT* root = NULL;
+ this->getNode(root);
+ if (root == NULL) {
+ bbox = CoordBBox();
+ return false;
+ }
+ root->getIndexRange(bbox);
+ return true;
+ }
+ bbox.min() = mIterList.getCoord(mLevel + 1);
+ bbox.max() = bbox.min().offsetBy(mIterList.getChildDim(mLevel + 1) - 1);
+ return true;
+}
+
+
+template<typename TreeT, typename RootChildOnIterT>
+inline std::string
+NodeIteratorBase<TreeT, RootChildOnIterT>::summary() const
+{
+ std::ostringstream ostr;
+ for (int lvl = int(ROOT_LEVEL); lvl >= 0 && lvl >= int(mLevel); --lvl) {
+ if (lvl == 0) ostr << "leaf";
+ else if (lvl == int(ROOT_LEVEL)) ostr << "root";
+ else ostr << "int" << (ROOT_LEVEL - lvl);
+ ostr << " c" << mIterList.pos(lvl);
+ if (lvl > int(mLevel)) ostr << " / ";
+ }
+ CoordBBox bbox;
+ this->getBoundingBox(bbox);
+ ostr << " " << bbox;
+ return ostr.str();
+}
+
+
+////////////////////////////////////////
+
+
+/// @brief Base class for tree-traversal iterators over all leaf nodes (but not leaf voxels)
+template<typename TreeT, typename RootChildOnIterT>
+class LeafIteratorBase
+{
+public:
+ typedef RootChildOnIterT RootIterT;
+ typedef typename RootIterT::NodeType RootNodeT;
+ typedef typename RootIterT::NonConstNodeType NCRootNodeT;
+ static const Index ROOT_LEVEL = RootNodeT::LEVEL;
+ typedef typename iter::InvertedTree<NCRootNodeT, ROOT_LEVEL>::Type InvTreeT;
+ typedef typename boost::mpl::front<InvTreeT>::type NCLeafNodeT;
+ typedef typename CopyConstness<RootNodeT, NCLeafNodeT>::Type LeafNodeT;
+ static const Index LEAF_LEVEL = 0, LEAF_PARENT_LEVEL = LEAF_LEVEL + 1;
+
+ typedef IterTraits<NCRootNodeT, RootIterT> RootIterTraits;
+
+ LeafIteratorBase(): mIterList(NULL), mTree(NULL) {}
+
+ LeafIteratorBase(TreeT& tree): mIterList(NULL), mTree(&tree)
+ {
+ // Initialize the iterator list with a root node iterator.
+ mIterList.setIter(RootIterTraits::begin(tree.getRootNode()));
+ // Descend along the first branch, initializing the node iterator at each level.
+ Index lvl = ROOT_LEVEL;
+ for ( ; lvl > 0 && mIterList.down(lvl); --lvl) {}
+ // If the first branch terminated above the leaf level, backtrack to the next branch.
+ if (lvl > 0) this->next();
+ }
+
+ LeafIteratorBase(const LeafIteratorBase& other): mIterList(other.mIterList), mTree(other.mTree)
+ {
+ mIterList.updateBackPointers();
+ }
+ LeafIteratorBase& operator=(const LeafIteratorBase& other)
+ {
+ if (&other != this) {
+ mTree = other.mTree;
+ mIterList = other.mIterList;
+ mIterList.updateBackPointers();
+ }
+ return *this;
+ }
+
+ //@{
+ /// Return the leaf node to which the iterator is pointing.
+ LeafNodeT* getLeaf() const { LeafNodeT* n = NULL; mIterList.getNode(LEAF_LEVEL, n); return n; }
+ LeafNodeT& operator*() const { return *this->getLeaf(); }
+ LeafNodeT* operator->() const { return this->getLeaf(); }
+ //@}
+
+ bool test() const { return mIterList.test(LEAF_PARENT_LEVEL); }
+ operator bool() const { return this->test(); }
+
+ //@{
+ /// Advance the iterator to the next leaf node.
+ bool next();
+ void increment() { this->next(); }
+ LeafIteratorBase& operator++() { this->increment(); return *this; }
+ //@}
+ /// Increment the iterator n times.
+ void increment(Index n) { for (Index i = 0; i < n && this->next(); ++i) {} }
+
+ TreeT* getTree() const { return mTree; }
+
+private:
+ struct PrevItem { typedef RootIterT IterT; };
+
+ /// @note Even though a LeafIterator doesn't iterate over leaf voxels,
+ /// the first item of this linked list of node iterators is a leaf node iterator,
+ /// whose purpose is only to provide access to its parent leaf node.
+ IterListItem<PrevItem, InvTreeT, /*VecSize=*/ROOT_LEVEL+1, LEAF_LEVEL> mIterList;
+ TreeT* mTree;
+}; // class LeafIteratorBase
+
+
+template<typename TreeT, typename RootChildOnIterT>
+inline bool
+LeafIteratorBase<TreeT, RootChildOnIterT>::next()
+{
+ // If the iterator is valid for the current node one level above the leaf level,
+ // advance the iterator to the node's next child.
+ if (mIterList.test(LEAF_PARENT_LEVEL) && mIterList.next(LEAF_PARENT_LEVEL)) {
+ mIterList.down(LEAF_PARENT_LEVEL); // initialize the leaf iterator
+ return true;
+ }
+
+ Index lvl = LEAF_PARENT_LEVEL;
+ while (!mIterList.test(LEAF_PARENT_LEVEL)) {
+ if (mIterList.test(lvl)) {
+ mIterList.next(lvl);
+ } else {
+ do {
+ // Ascend to the nearest level at which
+ // one of the iterators is not yet exhausted.
+ if (lvl == ROOT_LEVEL) return false;
+ ++lvl;
+ if (mIterList.test(lvl)) mIterList.next(lvl);
+ } while (!mIterList.test(lvl));
+ }
+ // Descend to the lowest child, but not as far as the leaf iterator.
+ while (lvl > LEAF_PARENT_LEVEL && mIterList.down(lvl)) --lvl;
+ }
+ mIterList.down(LEAF_PARENT_LEVEL); // initialize the leaf iterator
+ return true;
+}
+
+
+////////////////////////////////////////
+
+
+/// An IteratorRange wraps a tree or node iterator, giving the iterator TBB
+/// splittable range semantics.
+template<typename IterT>
+class IteratorRange
+{
+public:
+ IteratorRange(const IterT& iter, size_t grainSize = 8):
+ mIter(iter),
+ mGrainSize(grainSize),
+ mSize(0)
+ {
+ mSize = this->size();
+ }
+ IteratorRange(IteratorRange& other, tbb::split):
+ mIter(other.mIter),
+ mGrainSize(other.mGrainSize),
+ mSize(other.mSize >> 1)
+ {
+ other.increment(mSize);
+ }
+
+ /// @brief Return a reference to this range's iterator.
+ /// @note The reference is const, because the iterator should not be
+ /// incremented directly. Use this range object's increment() instead.
+ const IterT& iterator() const { return mIter; }
+
+ bool empty() const { return mSize == 0 || !mIter.test(); }
+ bool test() const { return !this->empty(); }
+ operator bool() const { return !this->empty(); }
+
+ /// @brief Return @c true if this range is splittable (i.e., if the iterator
+ /// can be advanced more than mGrainSize times).
+ bool is_divisible() const { return mSize > mGrainSize; }
+
+ /// Advance the iterator @a n times.
+ void increment(Index n = 1) { for ( ; n > 0 && mSize > 0; --n, --mSize, ++mIter) {} }
+ /// Advance the iterator to the next item.
+ IteratorRange& operator++() { this->increment(); return *this; }
+ /// @brief Advance the iterator to the next item.
+ /// @return @c true if the iterator is not yet exhausted.
+ bool next() { this->increment(); return this->test(); }
+
+private:
+ Index size() const { Index n = 0; for (IterT it(mIter); it.test(); ++n, ++it) {} return n; }
+
+ IterT mIter;
+ size_t mGrainSize;
+ /// @note mSize is only an estimate of the number of times mIter can be incremented
+ /// before it is exhausted (because the topology of the underlying tree could change
+ /// during iteration). For the purpose of range splitting, though, that should be
+ /// sufficient, since the two halves need not be of exactly equal size.
+ Index mSize;
+};
+
+
+////////////////////////////////////////
+
+
+/// @brief Base class for tree-traversal iterators over real and virtual voxel values
+/// @todo class TreeVoxelIteratorBase;
+
+} // namespace tree
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_TREE_TREEITERATOR_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/tree/Util.h b/extern/openvdb/internal/openvdb/tree/Util.h
new file mode 100644
index 00000000000..803bc163dc4
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/tree/Util.h
@@ -0,0 +1,124 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+//
+/// @file tree/Util.h
+
+#ifndef OPENVDB_TREE_UTIL_HAS_BEEN_INCLUDED
+#define OPENVDB_TREE_UTIL_HAS_BEEN_INCLUDED
+
+#include <openvdb/math/Math.h> // for zeroVal
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace tree {
+
+/// @brief Helper class for use with Tree::pruneOp() to replace constant branches
+/// (to within the provided tolerance) with more memory-efficient tiles
+template<typename ValueType, Index TerminationLevel = 0>
+struct TolerancePrune
+{
+ TolerancePrune(const ValueType& tol): tolerance(tol) {}
+
+ template<typename ChildType>
+ bool operator()(ChildType& child)
+ {
+ return (ChildType::LEVEL < TerminationLevel) ? false : this->isConstant(child);
+ }
+
+ template<typename ChildType>
+ bool isConstant(ChildType& child)
+ {
+ child.pruneOp(*this);
+ return child.isConstant(value, state, tolerance);
+ }
+
+ bool state;
+ ValueType value;
+ const ValueType tolerance;
+};
+
+
+/// @brief Helper class for use with Tree::pruneOp() to replace inactive branches
+/// with more memory-efficient inactive tiles with the provided value
+/// @details This is more specialized but faster than a TolerancePrune.
+template<typename ValueType>
+struct InactivePrune
+{
+ InactivePrune(const ValueType& val): value(val) {}
+
+ template <typename ChildType>
+ bool operator()(ChildType& child) const
+ {
+ child.pruneOp(*this);
+ return child.isInactive();
+ }
+
+ static const bool state = false;
+ const ValueType value;
+};
+
+
+/// @brief Helper class for use with Tree::pruneOp() to prune any branches
+/// whose values are all inactive and replace each with an inactive tile
+/// whose value is equal in magnitude to the background value and whose sign
+/// is equal to that of the first value encountered in the (inactive) child
+///
+/// @details This operation is faster than a TolerancePrune and useful for
+/// narrow-band level set applications where inactive values are limited
+/// to either the inside or the outside value.
+template<typename ValueType>
+struct LevelSetPrune
+{
+ LevelSetPrune(const ValueType& background): outside(background) {}
+
+ template <typename ChildType>
+ bool operator()(ChildType& child)
+ {
+ child.pruneOp(*this);
+ if (!child.isInactive()) return false;
+ value = child.getFirstValue() < zeroVal<ValueType>() ? -outside : outside;
+ return true;
+ }
+
+ static const bool state = false;
+ const ValueType outside;
+ ValueType value;
+};
+
+} // namespace tree
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_TREE_UTIL_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/tree/ValueAccessor.h b/extern/openvdb/internal/openvdb/tree/ValueAccessor.h
new file mode 100644
index 00000000000..29e605c7d4e
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/tree/ValueAccessor.h
@@ -0,0 +1,2325 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+//
+/// @file ValueAccessor.h
+///
+/// When traversing a grid in a spatially coherent pattern (e.g., iterating
+/// over neighboring voxels), request a @c ValueAccessor from the grid
+/// (with Grid::getAccessor()) and use the accessor's @c getValue() and
+/// @c setValue() methods. These will typically be significantly faster
+/// than accessing voxels directly in the grid's tree.
+///
+/// @par Example:
+///
+/// @code
+/// FloatGrid grid;
+/// FloatGrid::Accessor acc = grid.getAccessor();
+/// // First access is slow:
+/// acc.setValue(Coord(0, 0, 0), 100);
+/// // Subsequent nearby accesses are fast, since the accessor now holds pointers
+/// // to nodes that contain (0, 0, 0) along the path from the root of the grid's
+/// // tree to the leaf:
+/// acc.setValue(Coord(0, 0, 1), 100);
+/// acc.getValue(Coord(0, 2, 0), 100);
+/// // Slow, because the accessor must be repopulated:
+/// acc.getValue(Coord(-1, -1, -1));
+/// // Fast:
+/// acc.getValue(Coord(-1, -1, -2));
+/// acc.setValue(Coord(-1, -2, 0), -100);
+/// @endcode
+
+#ifndef OPENVDB_TREE_VALUEACCESSOR_HAS_BEEN_INCLUDED
+#define OPENVDB_TREE_VALUEACCESSOR_HAS_BEEN_INCLUDED
+
+#include <boost/mpl/front.hpp>
+#include <boost/mpl/pop_front.hpp>
+#include <boost/mpl/push_back.hpp>
+#include <boost/mpl/size.hpp>
+#include <boost/mpl/at.hpp>
+#include <boost/mpl/equal_to.hpp>
+#include <boost/mpl/comparison.hpp>
+#include <boost/mpl/vector.hpp>
+#include <boost/mpl/assert.hpp>
+#include <boost/mpl/erase.hpp>
+#include <boost/mpl/find.hpp>
+#include <boost/static_assert.hpp>
+#include <boost/type_traits/is_const.hpp>
+#include <tbb/null_mutex.h>
+#include <tbb/spin_mutex.h>
+#include <openvdb/version.h>
+#include <openvdb/Types.h>
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace tree {
+
+// Forward declarations of local classes that are not intended for general use
+template<typename TreeType> class ValueAccessor0;
+template<typename TreeType, Index L0 = 0> class ValueAccessor1;
+template<typename TreeType, Index L0 = 0, Index L1 = 1> class ValueAccessor2;
+template<typename TreeType, Index L0 = 0, Index L1 = 1, Index L2 = 2> class ValueAccessor3;
+template<typename HeadT, int HeadLevel> struct InvertedTree;
+template<typename TreeCacheT, typename NodeVecT, bool AtRoot> class CacheItem;
+
+
+/// @brief This base class for ValueAccessors manages registration of an accessor
+/// with a tree so that the tree can automatically clear the accessor whenever
+/// one of its nodes is deleted.
+/// @internal A base class is needed because ValueAccessor is templated on both
+/// a Tree type and a mutex type. The various instantiations of the template
+/// are distinct, unrelated types, so they can't easily be stored in a container
+/// such as the Tree's CacheRegistry. This base class, in contrast, is templated
+/// only on the Tree type, so for any given Tree, only two distinct instantiations
+/// are possible, ValueAccessorBase<Tree> and ValueAccessorBase<const Tree>.
+template<typename TreeType>
+class ValueAccessorBase
+{
+public:
+ static const bool IsConstTree = boost::is_const<TreeType>::value;
+
+ ValueAccessorBase(TreeType& tree): mTree(&tree) { tree.attachAccessor(*this); }
+
+ virtual ~ValueAccessorBase() { if (mTree) mTree->releaseAccessor(*this); }
+
+ /// @return a pointer to the tree associated by this ValueAccessor
+ TreeType* getTree() const { return mTree; }
+
+ ValueAccessorBase(const ValueAccessorBase& other): mTree(other.mTree)
+ {
+ if (mTree) mTree->attachAccessor(*this);
+ }
+
+ ValueAccessorBase& operator=(const ValueAccessorBase& other)
+ {
+ if (&other != this) {
+ if (mTree) mTree->releaseAccessor(*this);
+ mTree = other.mTree;
+ if (mTree) mTree->attachAccessor(*this);
+ }
+ return *this;
+ }
+
+ virtual void clear() = 0;
+
+protected:
+ // Allow trees to deregister themselves.
+ template<typename> friend class Tree;
+
+ virtual void release() { mTree = NULL; }
+
+ TreeType* mTree;
+}; // class ValueAccessorBase
+
+
+////////////////////////////////////////
+
+
+/// When traversing a grid in a spatially coherent pattern (e.g., iterating
+/// over neighboring voxels), request a @c ValueAccessor from the grid
+/// (with Grid::getAccessor()) and use the accessor's @c getValue() and
+/// @c setValue() methods. These will typically be significantly faster
+/// than accessing voxels directly in the grid's tree.
+/// @note If @c MutexType is a TBB-compatible mutex, then multiple threads
+/// may safely access a single, shared accessor. However, it is
+/// highly recommended that, instead, each thread be assigned its own,
+/// non-mutex-protected accessor.
+///
+/// Conceptually this ValueAccessor is a node-cache with accessor
+/// methods. Specefically the tree nodes from a previous access are
+/// cached and re-used starting with the LeafNode and moving up
+/// throug the node levels of the tree. Thus this node caching
+/// essentiall leads to acceleration of spatially coherent
+/// access by means of inverted tree traversal!
+///
+/// @param _TreeType This is the only template paramter that
+/// always has to be specified.
+/// @param CacheLevels Used to specify the number of bottom nodes
+/// that are cached. The default caches all (non-root)
+/// nodes. The maximum allowed number of CacheLevels
+/// correspond to the number of non-root nodes, i.e.
+/// CacheLevels <= DEPTH-1!
+/// @param MutexType This defines the type of mutex-lock and
+/// should almost always be left untouched (unless you're
+/// and expert!)
+template<typename _TreeType,
+ Index CacheLevels = _TreeType::DEPTH-1,
+ typename MutexType = tbb::null_mutex>
+class ValueAccessor: public ValueAccessorBase<_TreeType>
+{
+public:
+ BOOST_STATIC_ASSERT(CacheLevels <= _TreeType::DEPTH-1);
+ typedef _TreeType TreeType;
+ typedef typename TreeType::RootNodeType RootNodeT;
+ typedef typename TreeType::LeafNodeType LeafNodeT;
+ typedef typename RootNodeT::ValueType ValueType;
+ typedef ValueAccessorBase<TreeType> BaseT;
+ typedef typename MutexType::scoped_lock LockT;
+ using BaseT::IsConstTree;
+
+ ValueAccessor(TreeType& tree): BaseT(tree), mCache(*this)
+ {
+ mCache.insert(Coord(), &tree.getRootNode());
+ }
+
+ ValueAccessor(const ValueAccessor& other): BaseT(other), mCache(*this, other.mCache) {}
+
+ ValueAccessor& operator=(const ValueAccessor& other)
+ {
+ if (&other != this) {
+ this->BaseT::operator=(other);
+ mCache.copy(*this, other.mCache);
+ }
+ return *this;
+ }
+ virtual ~ValueAccessor() {}
+
+ /// Return the number of cache levels employed by this ValueAccessor
+ static Index numCacheLevels() { return CacheLevels; }
+
+ /// Return @c true if nodes along the path to the given voxel have been cached.
+ bool isCached(const Coord& xyz) const { LockT lock(mMutex); return mCache.isCached(xyz); }
+
+ /// Return the value of the voxel at the given coordinates.
+ const ValueType& getValue(const Coord& xyz) const
+ {
+ LockT lock(mMutex);
+ return mCache.getValue(xyz);
+ }
+
+ /// Return the active state of the voxel at the given coordinates.
+ bool isValueOn(const Coord& xyz) const { LockT lock(mMutex); return mCache.isValueOn(xyz); }
+
+ /// Return the active state of the voxel as well as its value
+ bool probeValue(const Coord& xyz, ValueType& value) const
+ {
+ LockT lock(mMutex);
+ return mCache.probeValue(xyz,value);
+ }
+
+ /// Return the tree depth (0 = root) at which the value of voxel (x, y, z) resides,
+ /// or -1 if (x, y, z) isn't explicitly represented in the tree (i.e., if it is
+ /// implicitly a background voxel).
+ int getValueDepth(const Coord& xyz) const
+ {
+ LockT lock(mMutex);
+ return mCache.getValueDepth(xyz);
+ }
+
+ /// Return @c true if the value of voxel (x, y, z) resides at the leaf level
+ /// of the tree, i.e., if it is not a tile value.
+ bool isVoxel(const Coord& xyz) const { LockT lock(mMutex); return mCache.isVoxel(xyz); }
+
+ //@{
+ /// Set the value of the voxel at the given coordinates and mark the voxel as active.
+ void setValue(const Coord& xyz, const ValueType& value)
+ {
+ LockT lock(mMutex);
+ mCache.setValue(xyz, value);
+ }
+ void setValueOn(const Coord& xyz, const ValueType& value) { this->setValue(xyz, value); }
+ //@}
+
+ /// Set the value of the voxel at the given coordinate but preserves its active state.
+ void setValueOnly(const Coord& xyz, const ValueType& value)
+ {
+ LockT lock(mMutex);
+ mCache.setValueOnly(xyz, value);
+ }
+
+ /// Set the value of the voxel at the given coordinates and mark the voxel
+ /// as active. [Experimental]
+ void newSetValue(const Coord& xyz, const ValueType& value)
+ {
+ LockT lock(mMutex);
+ mCache.newSetValue(xyz, value);
+ }
+
+ /// Set the value of the voxel at the given coordinates and mark the voxel as inactive.
+ void setValueOff(const Coord& xyz, const ValueType& value)
+ {
+ LockT lock(mMutex);
+ mCache.setValueOff(xyz, value);
+ }
+
+ /// Set the value of the voxel at the given coordinates to the sum of its current
+ /// value and the given value, and mark the voxel as active.
+ void setValueOnSum(const Coord& xyz, const ValueType& value)
+ {
+ LockT lock(mMutex);
+ mCache.setValueOnSum(xyz, value);
+ }
+
+ /// Set the active state of the voxel at the given coordinates without changing its value.
+ void setActiveState(const Coord& xyz, bool on = true)
+ {
+ LockT lock(mMutex);
+ mCache.setActiveState(xyz, on);
+ }
+ /// Mark the voxel at the given coordinates as active without changing its value.
+ void setValueOn(const Coord& xyz) { this->setActiveState(xyz, true); }
+ /// Mark the voxel at the given coordinates as inactive without changing its value.
+ void setValueOff(const Coord& xyz) { this->setActiveState(xyz, false); }
+
+ /// Return the cached node of type @a NodeType. [Mainly for internal use]
+ template<typename NodeType>
+ NodeType* getNode()
+ {
+ LockT lock(mMutex);
+ NodeType* node = NULL;
+ mCache.getNode(node);
+ return node;
+ }
+
+ /// Cache the given node, which should lie along the path from the root node to
+ /// the node containing voxel (x, y, z). [Mainly for internal use]
+ template<typename NodeType>
+ void insertNode(const Coord& xyz, NodeType& node)
+ {
+ LockT lock(mMutex);
+ mCache.insert(xyz, &node);
+ }
+
+ /// If a node of the given type exists in the cache, remove it, so that
+ /// isCached(xyz) returns @c false for any voxel (x, y, z) contained in
+ /// that node. [Mainly for internal use]
+ template<typename NodeType>
+ void eraseNode() { LockT lock(mMutex); NodeType* node = NULL; mCache.erase(node); }
+
+ /// @brief Add the specified leaf to this tree, possibly creating a child branch
+ /// in the process. If the leaf node already exists, replace it.
+ void addLeaf(LeafNodeT* leaf)
+ {
+ LockT lock(mMutex);
+ mCache.addLeaf(leaf);
+ }
+
+ /// @brief Add a tile at the specified tree level that contains
+ /// xyz, possibly creating a child branch in the process. If a
+ /// Node that contains xyz already exists it is replaced by a tile.
+ void addTile(Index level, const Coord& xyz, const ValueType& value, bool state)
+ {
+ LockT lock(mMutex);
+ mCache.addTile(level, xyz, value, state);
+ }
+
+ /// @brief @return the leaf node that contains voxel (x, y, z) and
+ /// if it doesn't exist, create it, but preserve the values and
+ /// active states of all voxels.
+ ///
+ /// Use this method to preallocate a static tree topology over which to
+ /// safely perform multithreaded processing.
+ LeafNodeT* touchLeaf(const Coord& xyz)
+ {
+ LockT lock(mMutex);
+ return mCache.touchLeaf(xyz);
+ }
+
+ /// @brief @return a pointer to the leaf node that contains
+ /// voxel (x, y, z) and if it doesn't exist, return NULL.
+ LeafNodeT* probeLeaf(const Coord& xyz)
+ {
+ LockT lock(mMutex);
+ return mCache.probeLeaf(xyz);
+ }
+
+ /// @brief @return a const pointer to the leaf node that contains
+ /// voxel (x, y, z) and if it doesn't exist, return NULL.
+ const LeafNodeT* probeConstLeaf(const Coord& xyz) const
+ {
+ LockT lock(mMutex);
+ return mCache.probeConstLeaf(xyz);
+ }
+ const LeafNodeT* probeLeaf(const Coord& xyz) const
+ {
+ return this->probeConstLeaf(xyz);
+ }
+
+ /// Remove all nodes from this cache, then reinsert the root node.
+ virtual void clear()
+ {
+ LockT lock(mMutex);
+ mCache.clear();
+ if (this->mTree) mCache.insert(Coord(), &(this->mTree->getRootNode()));
+ }
+
+private:
+ // Allow nodes to insert themselves into the cache.
+ template<typename> friend class RootNode;
+ template<typename, Index> friend class InternalNode;
+ template<typename, Index> friend class LeafNode;
+ // Allow trees to deregister themselves.
+ template<typename> friend class Tree;
+
+ /// Prevent this accessor from calling Tree::releaseCache() on a tree that
+ /// no longer exists. (Called by mTree when it is destroyed.)
+ virtual void release()
+ {
+ LockT lock(mMutex);
+ this->BaseT::release();
+ mCache.clear();
+ }
+ /// Cache the given node, which should lie along the path from the root node to
+ /// the node containing voxel (x, y, z).
+ /// @note This operation is not mutex-protected and is intended to be called
+ /// only by nodes and only in the context of a getValue() or setValue() call.
+ template<typename NodeType>
+ void insert(const Coord& xyz, NodeType* node) { mCache.insert(xyz, node); }
+
+ // Define a list of all tree node types from LeafNode to RootNode
+ typedef typename InvertedTree<RootNodeT, RootNodeT::LEVEL>::Type InvTreeT;
+ // Remove all tree node types that are excluded from the cache
+ typedef typename boost::mpl::begin<InvTreeT>::type BeginT;
+ typedef typename boost::mpl::advance<BeginT,boost::mpl::int_<CacheLevels> >::type FirstT;
+ typedef typename boost::mpl::find<InvTreeT, RootNodeT>::type LastT;
+ typedef typename boost::mpl::erase<InvTreeT,FirstT,LastT>::type SubtreeT;
+ typedef CacheItem<ValueAccessor, SubtreeT, boost::mpl::size<SubtreeT>::value==1> CacheItemT;
+
+ // Private member data
+ mutable CacheItemT mCache;
+ mutable MutexType mMutex;
+
+}; // class ValueAccessor
+
+
+/// Template specialization of the ValueAccessor with no mutex and no cache levels
+///
+/// @note This specialization is mosty useful for benchmark comparisions
+/// since the cached versions (above) are always expected to be faster.
+template<typename TreeType>
+struct ValueAccessor<TreeType, 0, tbb::null_mutex>: public ValueAccessor0<TreeType>
+{
+ ValueAccessor(TreeType& tree): ValueAccessor0<TreeType>(tree) {}
+ ValueAccessor(const ValueAccessor& other): ValueAccessor0<TreeType>(other) {}
+ virtual ~ValueAccessor() {}
+};
+
+
+/// Template specialization of the ValueAccessor with no mutex and 1 cache level
+template<typename TreeType>
+struct ValueAccessor<TreeType, 1, tbb::null_mutex>: public ValueAccessor1<TreeType>
+{
+ ValueAccessor(TreeType& tree): ValueAccessor1<TreeType>(tree) {}
+ ValueAccessor(const ValueAccessor& other): ValueAccessor1<TreeType>(other) {}
+ virtual ~ValueAccessor() {}
+};
+
+
+/// Template specialization of the ValueAccessor with no mutex and 2 cache levels
+template<typename TreeType>
+struct ValueAccessor<TreeType, 2, tbb::null_mutex>: public ValueAccessor2<TreeType>
+{
+ ValueAccessor(TreeType& tree): ValueAccessor2<TreeType>(tree) {}
+ ValueAccessor(const ValueAccessor& other): ValueAccessor2<TreeType>(other) {}
+ virtual ~ValueAccessor() {}
+};
+
+
+/// Template specialization of the ValueAccessor with no mutex and 3 cache levels
+template<typename TreeType>
+struct ValueAccessor<TreeType, 3, tbb::null_mutex>: public ValueAccessor3<TreeType>
+{
+ ValueAccessor(TreeType& tree): ValueAccessor3<TreeType>(tree) {}
+ ValueAccessor(const ValueAccessor& other): ValueAccessor3<TreeType>(other) {}
+ virtual ~ValueAccessor() {}
+};
+
+
+////////////////////////////////////////
+
+
+/// This accessor is thread-safe (at the cost of speed) for both reading and
+/// writing to a tree. That is, multiple threads may safely access a single,
+/// shared ValueAccessorRW. For better performance, however, it is recommended
+/// that, instead, each thread be assigned its own (non-mutex protected) accessor.
+template<typename TreeType>
+struct ValueAccessorRW: public ValueAccessor<TreeType, TreeType::DEPTH-1, tbb::spin_mutex>
+{
+ ValueAccessorRW(TreeType& tree)
+ : ValueAccessor<TreeType, TreeType::DEPTH-1, tbb::spin_mutex>(tree)
+ {
+ }
+};
+
+
+////////////////////////////////////////
+
+
+/////////////////////////////////////////////////////////////////////////////////
+/// The classes below are for experts only and should rarely be used directly ///
+/////////////////////////////////////////////////////////////////////////////////
+
+/// InvertedTree<RootNodeType, RootNodeType::LEVEL>::Type is a boost::mpl::vector
+/// that lists the types of the nodes of the tree rooted at RootNodeType
+/// in reverse order, from LeafNode to RootNode. For example, for RootNodeType
+/// RootNode<InternalNode<InternalNode<LeafNode> > >, InvertedTree::Type is
+/// boost::mpl::vector<
+/// LeafNode,
+/// InternalNode<LeafNode>,
+/// InternalNode<InternalNode<LeafNode> >,
+/// RootNode<InternalNode<InternalNode<LeafNode> > > >.
+template<typename HeadT, int HeadLevel>
+struct InvertedTree {
+ typedef typename InvertedTree<typename HeadT::ChildNodeType, HeadLevel-1>::Type SubtreeT;
+ typedef typename boost::mpl::push_back<SubtreeT, HeadT>::type Type;
+};
+/// For level one nodes (either RootNode<LeafNode> or InternalNode<LeafNode>),
+/// InvertedTree::Type is either boost::mpl::vector<LeafNode, RootNode<LeafNode> >
+/// or boost::mpl::vector<LeafNode, InternalNode<LeafNode> >.
+template<typename HeadT>
+struct InvertedTree<HeadT, /*HeadLevel=*/1> {
+ typedef typename boost::mpl::vector<typename HeadT::ChildNodeType, HeadT>::type Type;
+};
+
+
+// An element of a compile-time linked list of node pointers, ordered from LeafNode to RootNode
+template<typename TreeCacheT, typename NodeVecT, bool AtRoot>
+class CacheItem
+{
+public:
+ typedef typename boost::mpl::front<NodeVecT>::type NodeType;
+ typedef typename NodeType::ValueType ValueType;
+ typedef typename NodeType::LeafNodeType LeafNodeType;
+ typedef std::numeric_limits<Int32> CoordLimits;
+
+ CacheItem(TreeCacheT& parent):
+ mParent(&parent),
+ mHash(CoordLimits::max()),
+ mNode(NULL),
+ mNext(parent)
+ {
+ }
+
+ //@{
+ /// Copy another CacheItem's node pointers and hash keys, but not its parent pointer.
+ CacheItem(TreeCacheT& parent, const CacheItem& other):
+ mParent(&parent),
+ mHash(other.mHash),
+ mNode(other.mNode),
+ mNext(parent, other.mNext)
+ {
+ }
+
+ CacheItem& copy(TreeCacheT& parent, const CacheItem& other)
+ {
+ mParent = &parent;
+ mHash = other.mHash;
+ mNode = other.mNode;
+ mNext.copy(parent, other.mNext);
+ return *this;
+ }
+ //@}
+
+ bool isCached(const Coord& xyz) const
+ {
+ return (this->isHashed(xyz) || mNext.isCached(xyz));
+ }
+
+ /// Cache the given node at this level.
+ void insert(const Coord& xyz, const NodeType* node)
+ {
+ mHash = (node != NULL) ? xyz & ~(NodeType::DIM-1) : Coord::max();
+ mNode = node;
+ }
+ /// Forward the given node to another level of the cache.
+ template<typename OtherNodeType>
+ void insert(const Coord& xyz, const OtherNodeType* node) { mNext.insert(xyz, node); }
+
+ /// Erase the node at this level.
+ void erase(const NodeType*) { mHash = Coord::max(); mNode = NULL; }
+ /// Erase the node at another level of the cache.
+ template<typename OtherNodeType>
+ void erase(const OtherNodeType* node) { mNext.erase(node); }
+
+ /// Erase the nodes at this and lower levels of the cache.
+ void clear() { mHash = Coord::max(); mNode = NULL; mNext.clear(); }
+
+ /// Return the cached node (if any) at this level.
+ void getNode(const NodeType*& node) const { node = mNode; }
+ void getNode(const NodeType*& node) { node = mNode; }
+ void getNode(NodeType*& node)
+ {
+ // This combination of a static assertion and a const_cast might not be elegant,
+ // but it is a lot simpler than specializing TreeCache for const Trees.
+ BOOST_STATIC_ASSERT(!TreeCacheT::IsConstTree);
+ node = const_cast<NodeType*>(mNode);
+ }
+ /// Forward the request to another level of the cache.
+ template<typename OtherNodeType>
+ void getNode(OtherNodeType*& node) { mNext.getNode(node); }
+
+ /// Return the value of the voxel at the given coordinates.
+ const ValueType& getValue(const Coord& xyz)
+ {
+ if (this->isHashed(xyz)) {
+ assert(mNode);
+ return mNode->getValueAndCache(xyz, *mParent);
+ }
+ return mNext.getValue(xyz);
+ }
+
+ void addLeaf(LeafNodeType* leaf)
+ {
+ BOOST_STATIC_ASSERT(!TreeCacheT::IsConstTree);
+ if (NodeType::LEVEL == 0) return;
+ if (this->isHashed(leaf->origin())) {
+ assert(mNode);
+ return const_cast<NodeType*>(mNode)->addLeafAndCache(leaf, *mParent);
+ }
+ mNext.addLeaf(leaf);
+ }
+
+ void addTile(Index level, const Coord& xyz, const ValueType& value, bool state)
+ {
+ BOOST_STATIC_ASSERT(!TreeCacheT::IsConstTree);
+ if (NodeType::LEVEL < level) return;
+ if (this->isHashed(xyz)) {
+ assert(mNode);
+ return const_cast<NodeType*>(mNode)->addTileAndCache(level, xyz, value, state, *mParent);
+ }
+ mNext.addTile(level, xyz, value, state);
+ }
+
+ LeafNodeType* touchLeaf(const Coord& xyz)
+ {
+ BOOST_STATIC_ASSERT(!TreeCacheT::IsConstTree);
+ if (this->isHashed(xyz)) {
+ assert(mNode);
+ return const_cast<NodeType*>(mNode)->touchLeafAndCache(xyz, *mParent);
+ }
+ return mNext.touchLeaf(xyz);
+ }
+
+ LeafNodeType* probeLeaf(const Coord& xyz)
+ {
+ BOOST_STATIC_ASSERT(!TreeCacheT::IsConstTree);
+ if (this->isHashed(xyz)) {
+ assert(mNode);
+ return const_cast<NodeType*>(mNode)->probeLeafAndCache(xyz, *mParent);
+ }
+ return mNext.probeLeaf(xyz);
+ }
+
+ const LeafNodeType* probeConstLeaf(const Coord& xyz)
+ {
+ if (this->isHashed(xyz)) {
+ assert(mNode);
+ return mNode->probeConstLeafAndCache(xyz, *mParent);
+ }
+ return mNext.probeConstLeaf(xyz);
+ }
+
+ /// Return the active state of the voxel at the given coordinates.
+ bool isValueOn(const Coord& xyz)
+ {
+ if (this->isHashed(xyz)) {
+ assert(mNode);
+ return mNode->isValueOnAndCache(xyz, *mParent);
+ }
+ return mNext.isValueOn(xyz);
+ }
+
+ /// Return the active state and value of the voxel at the given coordinates.
+ bool probeValue(const Coord& xyz, ValueType& value)
+ {
+ if (this->isHashed(xyz)) {
+ assert(mNode);
+ return mNode->probeValueAndCache(xyz, value, *mParent);
+ }
+ return mNext.probeValue(xyz, value);
+ }
+
+ int getValueDepth(const Coord& xyz)
+ {
+ if (this->isHashed(xyz)) {
+ assert(mNode);
+ return static_cast<int>(TreeCacheT::RootNodeT::LEVEL) -
+ static_cast<int>(mNode->getValueLevelAndCache(xyz, *mParent));
+ } else {
+ return mNext.getValueDepth(xyz);
+ }
+ }
+
+ bool isVoxel(const Coord& xyz)
+ {
+ if (this->isHashed(xyz)) {
+ assert(mNode);
+ return mNode->getValueLevelAndCache(xyz, *mParent)==0;
+ } else {
+ return mNext.isVoxel(xyz);
+ }
+ }
+
+ /// Set the value of the voxel at the given coordinates and mark the voxel as active.
+ void setValue(const Coord& xyz, const ValueType& value)
+ {
+ if (this->isHashed(xyz)) {
+ assert(mNode);
+ BOOST_STATIC_ASSERT(!TreeCacheT::IsConstTree);
+ const_cast<NodeType*>(mNode)->setValueAndCache(xyz, value, *mParent);
+ } else {
+ mNext.setValue(xyz, value);
+ }
+ }
+ void setValueOnly(const Coord& xyz, const ValueType& value)
+ {
+ if (this->isHashed(xyz)) {
+ assert(mNode);
+ BOOST_STATIC_ASSERT(!TreeCacheT::IsConstTree);
+ const_cast<NodeType*>(mNode)->setValueOnlyAndCache(xyz, value, *mParent);
+ } else {
+ mNext.setValueOnly(xyz, value);
+ }
+ }
+ void setValueOn(const Coord& xyz, const ValueType& value) { this->setValue(xyz, value); }
+
+ /// Set the value of the voxel at the given coordinates to the sum of its current
+ /// value and the given value, and mark the voxel as active.
+ void setValueOnSum(const Coord& xyz, const ValueType& value)
+ {
+ if (this->isHashed(xyz)) {
+ assert(mNode);
+ BOOST_STATIC_ASSERT(!TreeCacheT::IsConstTree);
+ const_cast<NodeType*>(mNode)->setValueOnSumAndCache(xyz, value, *mParent);
+ } else {
+ mNext.setValueOnSum(xyz, value);
+ }
+ }
+
+ /// Set the value of the voxel at the given coordinates and mark the voxel as inactive.
+ void setValueOff(const Coord& xyz, const ValueType& value)
+ {
+ if (this->isHashed(xyz)) {
+ assert(mNode);
+ BOOST_STATIC_ASSERT(!TreeCacheT::IsConstTree);
+ const_cast<NodeType*>(mNode)->setValueOffAndCache(xyz, value, *mParent);
+ } else {
+ mNext.setValueOff(xyz, value);
+ }
+ }
+
+ /// Set the active state of the voxel at the given coordinates.
+ void setActiveState(const Coord& xyz, bool on)
+ {
+ if (this->isHashed(xyz)) {
+ assert(mNode);
+ BOOST_STATIC_ASSERT(!TreeCacheT::IsConstTree);
+ const_cast<NodeType*>(mNode)->setActiveStateAndCache(xyz, on, *mParent);
+ } else {
+ mNext.setActiveState(xyz, on);
+ }
+ }
+
+private:
+ CacheItem(const CacheItem&);
+ CacheItem& operator=(const CacheItem&);
+
+ bool isHashed(const Coord& xyz) const
+ {
+ return (xyz[0] & ~Coord::ValueType(NodeType::DIM-1)) == mHash[0]
+ && (xyz[1] & ~Coord::ValueType(NodeType::DIM-1)) == mHash[1]
+ && (xyz[2] & ~Coord::ValueType(NodeType::DIM-1)) == mHash[2];
+ }
+
+ TreeCacheT* mParent;
+ Coord mHash;
+ const NodeType* mNode;
+ typedef typename boost::mpl::pop_front<NodeVecT>::type RestT; // NodeVecT minus its first item
+ CacheItem<TreeCacheT, RestT, /*AtRoot=*/boost::mpl::size<RestT>::value == 1> mNext;
+};// end of CacheItem
+
+
+/// The tail of a compile-time list of cached node pointers, ordered from LeafNode to RootNode
+template<typename TreeCacheT, typename NodeVecT>
+class CacheItem<TreeCacheT, NodeVecT, /*AtRoot=*/true>
+{
+public:
+ typedef typename boost::mpl::front<NodeVecT>::type RootNodeType;
+ typedef typename RootNodeType::ValueType ValueType;
+ typedef typename RootNodeType::LeafNodeType LeafNodeType;
+
+ CacheItem(TreeCacheT& parent): mParent(&parent), mRoot(NULL) {}
+ CacheItem(TreeCacheT& parent, const CacheItem& other): mParent(&parent), mRoot(other.mRoot) {}
+
+ CacheItem& copy(TreeCacheT& parent, const CacheItem& other)
+ {
+ mParent = &parent;
+ mRoot = other.mRoot;
+ return *this;
+ }
+
+ bool isCached(const Coord& xyz) const { return this->isHashed(xyz); }
+
+ void insert(const Coord&, const RootNodeType* root) { mRoot = root; }
+
+ // Needed for node types that are not cached
+ template <typename OtherNodeType>
+ void insert(const Coord&, const OtherNodeType*) {}
+
+ void erase(const RootNodeType*) { mRoot = NULL; }
+
+ void clear() { mRoot = NULL; }
+
+ void getNode(RootNodeType*& node)
+ {
+ BOOST_STATIC_ASSERT(!TreeCacheT::IsConstTree);
+ node = const_cast<RootNodeType*>(mRoot);
+ }
+ void getNode(const RootNodeType*& node) const { node = mRoot; }
+
+ void addLeaf(LeafNodeType* leaf)
+ {
+ assert(mRoot);
+ BOOST_STATIC_ASSERT(!TreeCacheT::IsConstTree);
+ const_cast<RootNodeType*>(mRoot)->addLeafAndCache(leaf, *mParent);
+ }
+
+ void addTile(Index level, const Coord& xyz, const ValueType& value, bool state)
+ {
+ assert(mRoot);
+ BOOST_STATIC_ASSERT(!TreeCacheT::IsConstTree);
+ const_cast<RootNodeType*>(mRoot)->addTileAndCache(level, xyz, value, state, *mParent);
+ }
+
+ LeafNodeType* touchLeaf(const Coord& xyz)
+ {
+ assert(mRoot);
+ BOOST_STATIC_ASSERT(!TreeCacheT::IsConstTree);
+ return const_cast<RootNodeType*>(mRoot)->touchLeafAndCache(xyz, *mParent);
+ }
+
+ LeafNodeType* probeLeaf(const Coord& xyz)
+ {
+ assert(mRoot);
+ BOOST_STATIC_ASSERT(!TreeCacheT::IsConstTree);
+ return const_cast<RootNodeType*>(mRoot)->probeLeafAndCache(xyz, *mParent);
+ }
+
+ const LeafNodeType* probeConstLeaf(const Coord& xyz)
+ {
+ assert(mRoot);
+ return mRoot->probeConstLeafAndCache(xyz, *mParent);
+ }
+
+ int getValueDepth(const Coord& xyz)
+ {
+ assert(mRoot);
+ return mRoot->getValueDepthAndCache(xyz, *mParent);
+ }
+ bool isValueOn(const Coord& xyz)
+ {
+ assert(mRoot);
+ return mRoot->isValueOnAndCache(xyz, *mParent);
+ }
+
+ bool probeValue(const Coord& xyz, ValueType& value)
+ {
+ assert(mRoot);
+ return mRoot->probeValueAndCache(xyz, value, *mParent);
+ }
+ bool isVoxel(const Coord& xyz)
+ {
+ assert(mRoot);
+ return mRoot->getValueDepthAndCache(xyz, *mParent) ==
+ static_cast<int>(RootNodeType::LEVEL);
+ }
+ const ValueType& getValue(const Coord& xyz)
+ {
+ assert(mRoot);
+ return mRoot->getValueAndCache(xyz, *mParent);
+ }
+
+ void setValue(const Coord& xyz, const ValueType& value)
+ {
+ assert(mRoot);
+ BOOST_STATIC_ASSERT(!TreeCacheT::IsConstTree);
+ const_cast<RootNodeType*>(mRoot)->setValueAndCache(xyz, value, *mParent);
+ }
+ void setValueOnly(const Coord& xyz, const ValueType& value)
+ {
+ assert(mRoot);
+ BOOST_STATIC_ASSERT(!TreeCacheT::IsConstTree);
+ const_cast<RootNodeType*>(mRoot)->setValueOnlyAndCache(xyz, value, *mParent);
+ }
+ void setValueOn(const Coord& xyz, const ValueType& value) { this->setValue(xyz, value); }
+
+ void setValueOnSum(const Coord& xyz, const ValueType& value)
+ {
+ assert(mRoot);
+ BOOST_STATIC_ASSERT(!TreeCacheT::IsConstTree);
+ const_cast<RootNodeType*>(mRoot)->setValueOnSumAndCache(xyz, value, *mParent);
+ }
+
+ void setValueOff(const Coord& xyz, const ValueType& value)
+ {
+ assert(mRoot);
+ BOOST_STATIC_ASSERT(!TreeCacheT::IsConstTree);
+ const_cast<RootNodeType*>(mRoot)->setValueOffAndCache(xyz, value, *mParent);
+ }
+
+ void setActiveState(const Coord& xyz, bool on)
+ {
+ assert(mRoot);
+ BOOST_STATIC_ASSERT(!TreeCacheT::IsConstTree);
+ const_cast<RootNodeType*>(mRoot)->setActiveStateAndCache(xyz, on, *mParent);
+ }
+
+private:
+ CacheItem(const CacheItem&);
+ CacheItem& operator=(const CacheItem&);
+
+ bool isHashed(const Coord&) const { return false; }
+
+ TreeCacheT* mParent;
+ const RootNodeType* mRoot;
+};// end of CacheItem specialized for RootNode
+
+
+////////////////////////////////////////
+
+
+/// @brief ValueAccessor with no mutex and no node caching.
+///
+/// @note This specialization is mostly useful for benchmark comparisons,
+/// since the cached versions are always expected to be faster.
+template<typename _TreeType>
+class ValueAccessor0 : public ValueAccessorBase<_TreeType>
+{
+public:
+ typedef _TreeType TreeType;
+ typedef typename TreeType::ValueType ValueType;
+ typedef typename TreeType::RootNodeType RootNodeT;
+ typedef typename TreeType::LeafNodeType LeafNodeT;
+ typedef ValueAccessorBase<TreeType> BaseT;
+
+ ValueAccessor0(TreeType& tree) : BaseT(tree) {}
+
+ ValueAccessor0(const ValueAccessor0& other) : BaseT(other) {}
+
+ /// Return the number of cache levels employed by this ValueAccessor
+ static Index numCacheLevels() { return 0; }
+
+ ValueAccessor0& operator=(const ValueAccessor0& other)
+ {
+ if (&other != this) this->BaseT::operator=(other);
+ return *this;
+ }
+
+ virtual ~ValueAccessor0() {}
+
+ /// Return @c true if nodes along the path to the given voxel have been cached.
+ bool isCached(const Coord&) const { return false; }
+
+ /// Return the value of the voxel at the given coordinates.
+ const ValueType& getValue(const Coord& xyz) const
+ {
+ assert(BaseT::mTree);
+ return BaseT::mTree->getValue(xyz);
+ }
+
+ /// Return the active state of the voxel at the given coordinates.
+ bool isValueOn(const Coord& xyz) const
+ {
+ assert(BaseT::mTree);
+ return BaseT::mTree->isValueOn(xyz);
+ }
+
+ /// Return the active state of the voxel as well as its value
+ bool probeValue(const Coord& xyz, ValueType& value) const
+ {
+ assert(BaseT::mTree);
+ return BaseT::mTree->probeValue(xyz, value);
+ }
+
+ /// Return the tree depth (0 = root) at which the value of voxel (x, y, z) resides,
+ /// or -1 if (x, y, z) isn't explicitly represented in the tree (i.e., if it is
+ /// implicitly a background voxel).
+ int getValueDepth(const Coord& xyz) const
+ {
+ assert(BaseT::mTree);
+ return BaseT::mTree->getValueDepth(xyz);
+ }
+
+ /// Return @c true if the value of voxel (x, y, z) resides at the leaf level
+ /// of the tree, i.e., if it is not a tile value.
+ bool isVoxel(const Coord& xyz) const
+ {
+ assert(BaseT::mTree);
+ return BaseT::mTree->getValueDepth(xyz) == static_cast<int>(RootNodeT::LEVEL);
+ }
+
+ //@{
+ /// Set the value of the voxel at the given coordinates and mark the voxel as active.
+ void setValue(const Coord& xyz, const ValueType& value)
+ {
+ assert(BaseT::mTree);
+ BOOST_STATIC_ASSERT(!BaseT::IsConstTree);
+ BaseT::mTree->setValue(xyz, value);
+ }
+ void setValueOn(const Coord& xyz, const ValueType& value) { this->setValue(xyz, value); }
+ //@}
+
+ /// Set the value of the voxel at the given coordinate but preserves its active state.
+ void setValueOnly(const Coord& xyz, const ValueType& value)
+ {
+ assert(BaseT::mTree);
+ BOOST_STATIC_ASSERT(!BaseT::IsConstTree);
+ BaseT::mTree->setValueOnly(xyz, value);
+ }
+
+ /// Set the value of the voxel at the given coordinates and mark the voxel as inactive.
+ void setValueOff(const Coord& xyz, const ValueType& value)
+ {
+ assert(BaseT::mTree);
+ BOOST_STATIC_ASSERT(!BaseT::IsConstTree);
+ BaseT::mTree->getRootNode().setValueOff(xyz, value);
+ }
+
+ /// Set the value of the voxel at the given coordinates to the sum of its current
+ /// value and the given value, and mark the voxel as active.
+ void setValueOnSum(const Coord& xyz, const ValueType& value)
+ {
+ assert(BaseT::mTree);
+ BOOST_STATIC_ASSERT(!BaseT::IsConstTree);
+ BaseT::mTree->setValueOnSum(xyz, value);
+ }
+
+ /// Set the active state of the voxel at the given coordinates without changing its value.
+ void setActiveState(const Coord& xyz, bool on = true)
+ {
+ assert(BaseT::mTree);
+ BOOST_STATIC_ASSERT(!BaseT::IsConstTree);
+ BaseT::mTree->setActiveState(xyz, on);
+ }
+ /// Mark the voxel at the given coordinates as active without changing its value.
+ void setValueOn(const Coord& xyz) { this->setActiveState(xyz, true); }
+ /// Mark the voxel at the given coordinates as inactive without changing its value.
+ void setValueOff(const Coord& xyz) { this->setActiveState(xyz, false); }
+
+ /// Return the cached node of type @a NodeType. [Mainly for internal use]
+ template<typename NodeT> NodeT* getNode() { return NULL; }
+
+ /// Cache the given node, which should lie along the path from the root node to
+ /// the node containing voxel (x, y, z). [Mainly for internal use]
+ template<typename NodeT> void insertNode(const Coord&, NodeT&) {}
+
+ /// @brief Add the specified leaf to this tree, possibly creating a child branch
+ /// in the process. If the leaf node already exists, replace it.
+ void addLeaf(LeafNodeT* leaf)
+ {
+ assert(BaseT::mTree);
+ BOOST_STATIC_ASSERT(!BaseT::IsConstTree);
+ BaseT::mTree->root().addLeaf(leaf);
+ }
+
+ /// @brief Add a tile at the specified tree level that contains
+ /// xyz, possibly creating a child branch in the process. If a
+ /// Node that contains xyz already exists it is replaced by a tile.
+ void addTile(Index level, const Coord& xyz, const ValueType& value, bool state)
+ {
+ assert(BaseT::mTree);
+ BOOST_STATIC_ASSERT(!BaseT::IsConstTree);
+ BaseT::mTree->root().addTile(level, xyz, value, state);
+ }
+
+ /// If a node of the given type exists in the cache, remove it, so that
+ /// isCached(xyz) returns @c false for any voxel (x, y, z) contained in
+ /// that node. [Mainly for internal use]
+ template<typename NodeT> void eraseNode() {}
+
+ LeafNodeT* touchLeaf(const Coord& xyz)
+ {
+ assert(BaseT::mTree);
+ BOOST_STATIC_ASSERT(!BaseT::IsConstTree);
+ return BaseT::mTree->touchLeaf(xyz);
+ }
+
+ LeafNodeT* probeLeaf(const Coord& xyz)
+ {
+ assert(BaseT::mTree);
+ BOOST_STATIC_ASSERT(!BaseT::IsConstTree);
+ return BaseT::mTree->probeLeaf(xyz);
+ }
+
+ const LeafNodeT* probeConstLeaf(const Coord& xyz) const
+ {
+ assert(BaseT::mTree);
+ return BaseT::mTree->probeConstLeaf(xyz);
+ }
+
+ const LeafNodeT* probeLeaf(const Coord& xyz) const
+ {
+ return this->probeConstLeaf(xyz);
+ }
+
+ /// Remove all nodes from this cache, then reinsert the root node.
+ virtual void clear() {}
+
+private:
+ // Allow trees to deregister themselves.
+ template<typename> friend class Tree;
+
+ /// Prevent this accessor from calling Tree::releaseCache() on a tree that
+ /// no longer exists. (Called by mTree when it is destroyed.)
+ virtual void release() { this->BaseT::release(); }
+
+}; // ValueAccessor0
+
+
+/// @brief Value accessor with one level of node caching.
+/// @details The node cache level is specified by L0 with the default value 0
+/// (defined in the forward declaration) corresponding to a LeafNode.
+///
+/// @note This class is for experts only and should rarely be used
+/// directly. Instead use ValueAccessor with its default template arguments.
+template<typename _TreeType, Index L0>
+class ValueAccessor1 : public ValueAccessorBase<_TreeType>
+{
+public:
+ BOOST_STATIC_ASSERT(_TreeType::DEPTH >= 2);
+ BOOST_STATIC_ASSERT( L0 < _TreeType::RootNodeType::LEVEL );
+ typedef _TreeType TreeType;
+ typedef typename TreeType::ValueType ValueType;
+ typedef typename TreeType::RootNodeType RootNodeT;
+ typedef typename TreeType::LeafNodeType LeafNodeT;
+ typedef ValueAccessorBase<TreeType> BaseT;
+ typedef typename InvertedTree<RootNodeT, RootNodeT::LEVEL>::Type InvTreeT;
+ typedef typename boost::mpl::at<InvTreeT, boost::mpl::int_<L0> >::type NodeT0;
+
+ /// Constructor from a tree
+ ValueAccessor1(TreeType& tree) : BaseT(tree), mKey0(Coord::max()), mNode0(NULL)
+ {
+ }
+
+ /// Copy constructor
+ ValueAccessor1(const ValueAccessor1& other) : BaseT(other) { this->copy(other); }
+
+ /// Return the number of cache levels employed by this ValueAccessor
+ static Index numCacheLevels() { return 1; }
+
+ /// Asignment operator
+ ValueAccessor1& operator=(const ValueAccessor1& other)
+ {
+ if (&other != this) {
+ this->BaseT::operator=(other);
+ this->copy(other);
+ }
+ return *this;
+ }
+
+ /// Virtual destructor
+ virtual ~ValueAccessor1() {}
+
+ /// Return @c true if any of the nodes along the path to the given
+ /// voxel have been cached.
+ bool isCached(const Coord& xyz) const
+ {
+ assert(BaseT::mTree);
+ return this->isHashed(xyz);
+ }
+
+ /// Return the value of the voxel at the given coordinates.
+ const ValueType& getValue(const Coord& xyz) const
+ {
+ assert(BaseT::mTree);
+ if (this->isHashed(xyz)) {
+ assert(mNode0);
+ return mNode0->getValueAndCache(xyz, this->self());
+ }
+ return BaseT::mTree->getRootNode().getValueAndCache(xyz, this->self());
+ }
+
+ /// Return the active state of the voxel at the given coordinates.
+ bool isValueOn(const Coord& xyz) const
+ {
+ assert(BaseT::mTree);
+ if (this->isHashed(xyz)) {
+ assert(mNode0);
+ return mNode0->isValueOnAndCache(xyz, this->self());
+ }
+ return BaseT::mTree->getRootNode().isValueOnAndCache(xyz, this->self());
+ }
+
+ /// Return the active state of the voxel as well as its value
+ bool probeValue(const Coord& xyz, ValueType& value) const
+ {
+ assert(BaseT::mTree);
+ if (this->isHashed(xyz)) {
+ assert(mNode0);
+ return mNode0->probeValueAndCache(xyz, value, this->self());
+ }
+ return BaseT::mTree->getRootNode().probeValueAndCache(xyz, value, this->self());
+ }
+
+ /// Return the tree depth (0 = root) at which the value of voxel (x, y, z) resides,
+ /// or -1 if (x, y, z) isn't explicitly represented in the tree (i.e., if it is
+ /// implicitly a background voxel).
+ int getValueDepth(const Coord& xyz) const
+ {
+ assert(BaseT::mTree);
+ if (this->isHashed(xyz)) {
+ assert(mNode0);
+ return RootNodeT::LEVEL - mNode0->getValueLevelAndCache(xyz, this->self());
+ }
+ return BaseT::mTree->getRootNode().getValueDepthAndCache(xyz, this->self());
+ }
+
+ /// Return @c true if the value of voxel (x, y, z) resides at the leaf level
+ /// of the tree, i.e., if it is not a tile value.
+ bool isVoxel(const Coord& xyz) const
+ {
+ assert(BaseT::mTree);
+ if (this->isHashed(xyz)) {
+ assert(mNode0);
+ return mNode0->getValueLevelAndCache(xyz, this->self()) == 0;
+ }
+ return BaseT::mTree->getRootNode().getValueDepthAndCache(xyz, this->self()) ==
+ static_cast<int>(RootNodeT::LEVEL);
+ }
+
+ //@{
+ /// Set the value of the voxel at the given coordinates and mark the voxel as active.
+ void setValue(const Coord& xyz, const ValueType& value)
+ {
+ assert(BaseT::mTree);
+ BOOST_STATIC_ASSERT(!BaseT::IsConstTree);
+ if (this->isHashed(xyz)) {
+ assert(mNode0);
+ const_cast<NodeT0*>(mNode0)->setValueAndCache(xyz, value, *this);
+ } else {
+ BaseT::mTree->getRootNode().setValueAndCache(xyz, value, *this);
+ }
+ }
+ void setValueOn(const Coord& xyz, const ValueType& value) { this->setValue(xyz, value); }
+ //@}
+
+ /// Set the value of the voxel at the given coordinate but preserves its active state.
+ void setValueOnly(const Coord& xyz, const ValueType& value)
+ {
+ assert(BaseT::mTree);
+ BOOST_STATIC_ASSERT(!BaseT::IsConstTree);
+ if (this->isHashed(xyz)) {
+ assert(mNode0);
+ const_cast<NodeT0*>(mNode0)->setValueOnlyAndCache(xyz, value, *this);
+ } else {
+ BaseT::mTree->getRootNode().setValueOnlyAndCache(xyz, value, *this);
+ }
+ }
+
+ /// Set the value of the voxel at the given coordinates and mark the voxel as inactive.
+ void setValueOff(const Coord& xyz, const ValueType& value)
+ {
+ assert(BaseT::mTree);
+ BOOST_STATIC_ASSERT(!BaseT::IsConstTree);
+ if (this->isHashed(xyz)) {
+ assert(mNode0);
+ const_cast<NodeT0*>(mNode0)->setValueOffAndCache(xyz, value, *this);
+ } else {
+ BaseT::mTree->getRootNode().setValueOffAndCache(xyz, value, *this);
+ }
+ }
+
+ /// Set the value of the voxel at the given coordinates to the sum of its current
+ /// value and the given value, and mark the voxel as active.
+ void setValueOnSum(const Coord& xyz, const ValueType& value)
+ {
+ assert(BaseT::mTree);
+ BOOST_STATIC_ASSERT(!BaseT::IsConstTree);
+ if (this->isHashed(xyz)) {
+ assert(mNode0);
+ const_cast<NodeT0*>(mNode0)->setValueOnSumAndCache(xyz, value, *this);
+ } else {
+ BaseT::mTree->getRootNode().setValueOnSumAndCache(xyz, value, *this);
+ }
+ }
+
+ /// Set the active state of the voxel at the given coordinates without changing its value.
+ void setActiveState(const Coord& xyz, bool on = true)
+ {
+ assert(BaseT::mTree);
+ BOOST_STATIC_ASSERT(!BaseT::IsConstTree);
+ if (this->isHashed(xyz)) {
+ assert(mNode0);
+ const_cast<NodeT0*>(mNode0)->setActiveStateAndCache(xyz, on, *this);
+ } else {
+ BaseT::mTree->getRootNode().setActiveStateAndCache(xyz, on, *this);
+ }
+ }
+ /// Mark the voxel at the given coordinates as active without changing its value.
+ void setValueOn(const Coord& xyz) { this->setActiveState(xyz, true); }
+ /// Mark the voxel at the given coordinates as inactive without changing its value.
+ void setValueOff(const Coord& xyz) { this->setActiveState(xyz, false); }
+
+ /// Return the cached node of type @a NodeType. [Mainly for internal use]
+ template<typename NodeT>
+ NodeT* getNode()
+ {
+ const NodeT* node = NULL;
+ this->getNode(node);
+ return const_cast<NodeT*>(node);
+ }
+
+ /// Cache the given node, which should lie along the path from the root node to
+ /// the node containing voxel (x, y, z). [Mainly for internal use]
+ template<typename NodeT>
+ void insertNode(const Coord& xyz, NodeT& node) { this->insert(xyz, &node); }
+
+ /// If a node of the given type exists in the cache, remove it, so that
+ /// isCached(xyz) returns @c false for any voxel (x, y, z) contained in
+ /// that node. [Mainly for internal use]
+ template<typename NodeT>
+ void eraseNode()
+ {
+ const NodeT* node = NULL;
+ this->eraseNode(node);
+ }
+
+ /// @brief Add the specified leaf to this tree, possibly creating a child branch
+ /// in the process. If the leaf node already exists, replace it.
+ void addLeaf(LeafNodeT* leaf)
+ {
+ assert(BaseT::mTree);
+ BOOST_STATIC_ASSERT(!BaseT::IsConstTree);
+ BaseT::mTree->root().addLeaf(leaf);
+ }
+
+ /// @brief Add a tile at the specified tree level that contains
+ /// xyz, possibly creating a child branch in the process. If a
+ /// Node that contains xyz already exists it is replaced by a tile.
+ void addTile(Index level, const Coord& xyz, const ValueType& value, bool state)
+ {
+ assert(BaseT::mTree);
+ BOOST_STATIC_ASSERT(!BaseT::IsConstTree);
+ BaseT::mTree->root().addTile(level, xyz, value, state);
+ }
+
+ /// @brief @return the leaf node that contains voxel (x, y, z) and
+ /// if it doesn't exist, create it, but preserve the values and
+ /// active states of all voxels.
+ ///
+ /// Use this method to preallocate a static tree topology over which to
+ /// safely perform multithreaded processing.
+ LeafNodeT* touchLeaf(const Coord& xyz)
+ {
+ assert(BaseT::mTree);
+ BOOST_STATIC_ASSERT(!BaseT::IsConstTree);
+ if (this->isHashed(xyz)) {
+ assert(mNode0);
+ return const_cast<NodeT0*>(mNode0)->touchLeafAndCache(xyz, *this);
+ }
+ return BaseT::mTree->getRootNode().touchLeafAndCache(xyz, *this);
+ }
+
+ /// @brief @return a pointer to the leaf node that contains
+ /// voxel (x, y, z) and if it doesn't exist, return NULL.
+ LeafNodeT* probeLeaf(const Coord& xyz)
+ {
+ assert(BaseT::mTree);
+ BOOST_STATIC_ASSERT(!BaseT::IsConstTree);
+ if (this->isHashed(xyz)) {
+ assert(mNode0);
+ return const_cast<NodeT0*>(mNode0)->probeLeafAndCache(xyz, *this);
+ }
+ return BaseT::mTree->getRootNode().probeLeafAndCache(xyz, *this);
+ }
+
+ /// @brief @return a const pointer to the leaf node that contains
+ /// voxel (x, y, z) and if it doesn't exist, return NULL.
+ const LeafNodeT* probeConstLeaf(const Coord& xyz) const
+ {
+ assert(BaseT::mTree);
+ if (this->isHashed(xyz)) {
+ assert(mNode0);
+ return mNode0->probeConstLeafAndCache(xyz, this->self());
+ }
+ return BaseT::mTree->getRootNode().probeConstLeafAndCache(xyz, this->self());
+ }
+ const LeafNodeT* probeLeaf(const Coord& xyz) const { return this->probeConstLeaf(xyz); }
+
+ /// Remove all the cached nodes and invalidate the corresponding hash-keys.
+ virtual void clear()
+ {
+ mKey0 = Coord::max();
+ mNode0 = NULL;
+ }
+
+private:
+ // Allow nodes to insert themselves into the cache.
+ template<typename> friend class RootNode;
+ template<typename, Index> friend class InternalNode;
+ template<typename, Index> friend class LeafNode;
+ // Allow trees to deregister themselves.
+ template<typename> friend class Tree;
+
+ // This private method is merely for convenience.
+ inline ValueAccessor1& self() const { return const_cast<ValueAccessor1&>(*this); }
+
+ void getNode(const NodeT0*& node) { node = mNode0; }
+ void getNode(const RootNodeT*& node)
+ {
+ node = (BaseT::mTree ? &BaseT::mTree->getRootNode() : NULL);
+ }
+ template <typename OtherNodeType> void getNode(const OtherNodeType*& node) { node = NULL; }
+ void eraseNode(const NodeT0*) { mKey0 = Coord::max(); mNode0 = NULL; }
+ template <typename OtherNodeType> void eraseNode(const OtherNodeType*) {}
+
+ /// Private copy method
+ inline void copy(const ValueAccessor1& other)
+ {
+ mKey0 = other.mKey0;
+ mNode0 = other.mNode0;
+ }
+
+ /// Prevent this accessor from calling Tree::releaseCache() on a tree that
+ /// no longer exists. (Called by mTree when it is destroyed.)
+ virtual void release()
+ {
+ this->BaseT::release();
+ this->clear();
+ }
+ /// Cache the given node, which should lie along the path from the root node to
+ /// the node containing voxel (x, y, z).
+ /// @note This operation is not mutex-protected and is intended to be called
+ /// only by nodes and only in the context of a getValue() or setValue() call.
+ inline void insert(const Coord& xyz, const NodeT0* node)
+ {
+ assert(node);
+ mKey0 = xyz & ~(NodeT0::DIM-1);
+ mNode0 = node;
+ }
+
+ /// No-op in case a tree traversal attemps to insert a node that
+ /// is not cached by the ValueAccessor
+ template<typename OtherNodeType> inline void insert(const Coord&, const OtherNodeType*) {}
+
+ inline bool isHashed(const Coord& xyz) const
+ {
+ return (xyz[0] & ~Coord::ValueType(NodeT0::DIM-1)) == mKey0[0]
+ && (xyz[1] & ~Coord::ValueType(NodeT0::DIM-1)) == mKey0[1]
+ && (xyz[2] & ~Coord::ValueType(NodeT0::DIM-1)) == mKey0[2];
+ }
+ mutable Coord mKey0;
+ mutable const NodeT0* mNode0;
+}; // ValueAccessor1
+
+
+/// @brief Value accessor with two levels of node caching.
+/// @details The node cache levels are specified by L0 and L1
+/// with the default values 0 and 1 (defined in the forward declaration)
+/// corresponding to a LeafNode and its parent InternalNode.
+///
+/// @note This class is for experts only and should rarely be used directly.
+/// Instead use ValueAccessor with its default template arguments.
+template<typename _TreeType, Index L0, Index L1>
+class ValueAccessor2 : public ValueAccessorBase<_TreeType>
+{
+public:
+ BOOST_STATIC_ASSERT(_TreeType::DEPTH >= 3);
+ BOOST_STATIC_ASSERT( L0 < L1 && L1 < _TreeType::RootNodeType::LEVEL );
+ typedef _TreeType TreeType;
+ typedef typename TreeType::ValueType ValueType;
+ typedef typename TreeType::RootNodeType RootNodeT;
+ typedef typename TreeType::LeafNodeType LeafNodeT;
+ typedef ValueAccessorBase<TreeType> BaseT;
+ typedef typename InvertedTree<RootNodeT, RootNodeT::LEVEL>::Type InvTreeT;
+ typedef typename boost::mpl::at<InvTreeT, boost::mpl::int_<L0> >::type NodeT0;
+ typedef typename boost::mpl::at<InvTreeT, boost::mpl::int_<L1> >::type NodeT1;
+
+ /// Constructor from a tree
+ ValueAccessor2(TreeType& tree) : BaseT(tree),
+ mKey0(Coord::max()), mNode0(NULL),
+ mKey1(Coord::max()), mNode1(NULL) {}
+
+ /// Copy constructor
+ ValueAccessor2(const ValueAccessor2& other) : BaseT(other) { this->copy(other); }
+
+ /// Return the number of cache levels employed by this ValueAccessor
+ static Index numCacheLevels() { return 2; }
+
+ /// Asignment operator
+ ValueAccessor2& operator=(const ValueAccessor2& other)
+ {
+ if (&other != this) {
+ this->BaseT::operator=(other);
+ this->copy(other);
+ }
+ return *this;
+ }
+
+ /// Virtual destructor
+ virtual ~ValueAccessor2() {}
+
+ /// Return @c true if any of the nodes along the path to the given
+ /// voxel have been cached.
+ bool isCached(const Coord& xyz) const
+ {
+ assert(BaseT::mTree);
+ return this->isHashed1(xyz) || this->isHashed0(xyz);
+ }
+
+ /// Return the value of the voxel at the given coordinates.
+ const ValueType& getValue(const Coord& xyz) const
+ {
+ assert(BaseT::mTree);
+ if (this->isHashed0(xyz)) {
+ assert(mNode0);
+ return mNode0->getValueAndCache(xyz, this->self());
+ } else if (this->isHashed1(xyz)) {
+ assert(mNode1);
+ return mNode1->getValueAndCache(xyz, this->self());
+ }
+ return BaseT::mTree->getRootNode().getValueAndCache(xyz, this->self());
+ }
+
+ /// Return the active state of the voxel at the given coordinates.
+ bool isValueOn(const Coord& xyz) const
+ {
+ assert(BaseT::mTree);
+ if (this->isHashed0(xyz)) {
+ assert(mNode0);
+ return mNode0->isValueOnAndCache(xyz, this->self());
+ } else if (this->isHashed1(xyz)) {
+ assert(mNode1);
+ return mNode1->isValueOnAndCache(xyz, this->self());
+ }
+ return BaseT::mTree->getRootNode().isValueOnAndCache(xyz, this->self());
+ }
+
+ /// Return the active state of the voxel as well as its value
+ bool probeValue(const Coord& xyz, ValueType& value) const
+ {
+ assert(BaseT::mTree);
+ if (this->isHashed0(xyz)) {
+ assert(mNode0);
+ return mNode0->probeValueAndCache(xyz, value, this->self());
+ } else if (this->isHashed1(xyz)) {
+ assert(mNode1);
+ return mNode1->probeValueAndCache(xyz, value, this->self());
+ }
+ return BaseT::mTree->getRootNode().probeValueAndCache(xyz, value, this->self());
+ }
+
+ /// Return the tree depth (0 = root) at which the value of voxel (x, y, z) resides,
+ /// or -1 if (x, y, z) isn't explicitly represented in the tree (i.e., if it is
+ /// implicitly a background voxel).
+ int getValueDepth(const Coord& xyz) const
+ {
+ assert(BaseT::mTree);
+ if (this->isHashed0(xyz)) {
+ assert(mNode0);
+ return RootNodeT::LEVEL - mNode0->getValueLevelAndCache(xyz, this->self());
+ } else if (this->isHashed1(xyz)) {
+ assert(mNode1);
+ return RootNodeT::LEVEL - mNode1->getValueLevelAndCache(xyz, this->self());
+ }
+ return BaseT::mTree->getRootNode().getValueDepthAndCache(xyz, this->self());
+ }
+
+ /// Return @c true if the value of voxel (x, y, z) resides at the leaf level
+ /// of the tree, i.e., if it is not a tile value.
+ bool isVoxel(const Coord& xyz) const
+ {
+ assert(BaseT::mTree);
+ if (this->isHashed0(xyz)) {
+ assert(mNode0);
+ return mNode0->getValueLevelAndCache(xyz, this->self())==0;
+ } else if (this->isHashed1(xyz)) {
+ assert(mNode1);
+ return mNode1->getValueLevelAndCache(xyz, this->self())==0;
+ }
+ return BaseT::mTree->getRootNode().getValueDepthAndCache(xyz, this->self()) ==
+ static_cast<int>(RootNodeT::LEVEL);
+ }
+
+ //@{
+ /// Set the value of the voxel at the given coordinates and mark the voxel as active.
+ void setValue(const Coord& xyz, const ValueType& value)
+ {
+ assert(BaseT::mTree);
+ BOOST_STATIC_ASSERT(!BaseT::IsConstTree);
+ if (this->isHashed0(xyz)) {
+ assert(mNode0);
+ const_cast<NodeT0*>(mNode0)->setValueAndCache(xyz, value, *this);
+ } else if (this->isHashed1(xyz)) {
+ assert(mNode1);
+ const_cast<NodeT1*>(mNode1)->setValueAndCache(xyz, value, *this);
+ } else {
+ BaseT::mTree->getRootNode().setValueAndCache(xyz, value, *this);
+ }
+ }
+ void setValueOn(const Coord& xyz, const ValueType& value) { this->setValue(xyz, value); }
+ //@}
+
+ /// Set the value of the voxel at the given coordinate but preserves its active state.
+ void setValueOnly(const Coord& xyz, const ValueType& value)
+ {
+ assert(BaseT::mTree);
+ BOOST_STATIC_ASSERT(!BaseT::IsConstTree);
+ if (this->isHashed0(xyz)) {
+ assert(mNode0);
+ const_cast<NodeT0*>(mNode0)->setValueOnlyAndCache(xyz, value, *this);
+ } else if (this->isHashed1(xyz)) {
+ assert(mNode1);
+ const_cast<NodeT1*>(mNode1)->setValueOnlyAndCache(xyz, value, *this);
+ } else {
+ BaseT::mTree->getRootNode().setValueOnlyAndCache(xyz, value, *this);
+ }
+ }
+
+ /// Set the value of the voxel at the given coordinates and mark the voxel as inactive.
+ void setValueOff(const Coord& xyz, const ValueType& value)
+ {
+ assert(BaseT::mTree);
+ BOOST_STATIC_ASSERT(!BaseT::IsConstTree);
+ if (this->isHashed0(xyz)) {
+ assert(mNode0);
+ const_cast<NodeT0*>(mNode0)->setValueOffAndCache(xyz, value, *this);
+ } else if (this->isHashed1(xyz)) {
+ assert(mNode1);
+ const_cast<NodeT1*>(mNode1)->setValueOffAndCache(xyz, value, *this);
+ } else {
+ BaseT::mTree->getRootNode().setValueOffAndCache(xyz, value, *this);
+ }
+ }
+
+ /// Set the value of the voxel at the given coordinates to the sum of its current
+ /// value and the given value, and mark the voxel as active.
+ void setValueOnSum(const Coord& xyz, const ValueType& value)
+ {
+ assert(BaseT::mTree);
+ BOOST_STATIC_ASSERT(!BaseT::IsConstTree);
+ if (this->isHashed0(xyz)) {
+ assert(mNode0);
+ const_cast<NodeT0*>(mNode0)->setValueOnSumAndCache(xyz, value, *this);
+ } else if (this->isHashed1(xyz)) {
+ assert(mNode1);
+ const_cast<NodeT1*>(mNode1)->setValueOnSumAndCache(xyz, value, *this);
+ } else {
+ BaseT::mTree->getRootNode().setValueOnSumAndCache(xyz, value, *this);
+ }
+ }
+
+ /// Set the active state of the voxel at the given coordinates without changing its value.
+ void setActiveState(const Coord& xyz, bool on = true)
+ {
+ assert(BaseT::mTree);
+ BOOST_STATIC_ASSERT(!BaseT::IsConstTree);
+ if (this->isHashed0(xyz)) {
+ assert(mNode0);
+ const_cast<NodeT0*>(mNode0)->setActiveStateAndCache(xyz, on, *this);
+ } else if (this->isHashed1(xyz)) {
+ assert(mNode1);
+ const_cast<NodeT1*>(mNode1)->setActiveStateAndCache(xyz, on, *this);
+ } else {
+ BaseT::mTree->getRootNode().setActiveStateAndCache(xyz, on, *this);
+ }
+ }
+ /// Mark the voxel at the given coordinates as active without changing its value.
+ void setValueOn(const Coord& xyz) { this->setActiveState(xyz, true); }
+ /// Mark the voxel at the given coordinates as inactive without changing its value.
+ void setValueOff(const Coord& xyz) { this->setActiveState(xyz, false); }
+
+ /// Return the cached node of type @a NodeType. [Mainly for internal use]
+ template<typename NodeT>
+ NodeT* getNode()
+ {
+ const NodeT* node = NULL;
+ this->getNode(node);
+ return const_cast<NodeT*>(node);
+ }
+
+ /// Cache the given node, which should lie along the path from the root node to
+ /// the node containing voxel (x, y, z). [Mainly for internal use]
+ template<typename NodeT>
+ void insertNode(const Coord& xyz, NodeT& node) { this->insert(xyz, &node); }
+
+ /// If a node of the given type exists in the cache, remove it, so that
+ /// isCached(xyz) returns @c false for any voxel (x, y, z) contained in
+ /// that node. [Mainly for internal use]
+ template<typename NodeT>
+ void eraseNode()
+ {
+ const NodeT* node = NULL;
+ this->eraseNode(node);
+ }
+
+ /// @brief Add the specified leaf to this tree, possibly creating a child branch
+ /// in the process. If the leaf node already exists, replace it.
+ void addLeaf(LeafNodeT* leaf)
+ {
+ assert(BaseT::mTree);
+ BOOST_STATIC_ASSERT(!BaseT::IsConstTree);
+ if (this->isHashed1(leaf->origin())) {
+ assert(mNode1);
+ return const_cast<NodeT1*>(mNode1)->addLeafAndCache(leaf, *this);
+ }
+ BaseT::mTree->root().addLeafAndCache(leaf, *this);
+ }
+
+ /// @brief Add a tile at the specified tree level that contains
+ /// xyz, possibly creating a child branch in the process. If a
+ /// Node that contains xyz already exists it is replaced by a tile.
+ void addTile(Index level, const Coord& xyz, const ValueType& value, bool state)
+ {
+ assert(BaseT::mTree);
+ BOOST_STATIC_ASSERT(!BaseT::IsConstTree);
+ if (this->isHashed1(xyz)) {
+ assert(mNode1);
+ return const_cast<NodeT1*>(mNode1)->addTileAndCache(level, xyz, value, state, *this);
+ }
+ BaseT::mTree->root().addTileAndCache(level, xyz, value, state, *this);
+ }
+
+ /// @brief @return the leaf node that contains voxel (x, y, z) and
+ /// if it doesn't exist, create it, but preserve the values and
+ /// active states of all voxels.
+ ///
+ /// Use this method to preallocate a static tree topology over which to
+ /// safely perform multithreaded processing.
+ LeafNodeT* touchLeaf(const Coord& xyz)
+ {
+ assert(BaseT::mTree);
+ BOOST_STATIC_ASSERT(!BaseT::IsConstTree);
+ if (this->isHashed0(xyz)) {
+ assert(mNode0);
+ return const_cast<NodeT0*>(mNode0)->touchLeafAndCache(xyz, *this);
+ } else if (this->isHashed1(xyz)) {
+ assert(mNode1);
+ return const_cast<NodeT1*>(mNode1)->touchLeafAndCache(xyz, *this);
+ }
+ return BaseT::mTree->getRootNode().touchLeafAndCache(xyz, *this);
+ }
+
+ /// @brief @return a pointer to the leaf node that contains
+ /// voxel (x, y, z) and if it doesn't exist, return NULL.
+ LeafNodeT* probeLeaf(const Coord& xyz)
+ {
+ assert(BaseT::mTree);
+ BOOST_STATIC_ASSERT(!BaseT::IsConstTree);
+ if (this->isHashed0(xyz)) {
+ assert(mNode0);
+ return const_cast<NodeT0*>(mNode0)->probeLeafAndCache(xyz, *this);
+ } else if (this->isHashed1(xyz)) {
+ assert(mNode1);
+ return const_cast<NodeT1*>(mNode1)->probeLeafAndCache(xyz, *this);
+ }
+ return BaseT::mTree->getRootNode().probeLeafAndCache(xyz, *this);
+ }
+
+ /// @brief @return a const pointer to the leaf node that contains
+ /// voxel (x, y, z) and if it doesn't exist, return NULL.
+ const LeafNodeT* probeConstLeaf(const Coord& xyz) const
+ {
+ assert(BaseT::mTree);
+ if (this->isHashed0(xyz)) {
+ assert(mNode0);
+ return mNode0->probeConstLeafAndCache(xyz, this->self());
+ } else if (this->isHashed1(xyz)) {
+ assert(mNode1);
+ return mNode1->probeConstLeafAndCache(xyz, this->self());
+ }
+ return BaseT::mTree->getRootNode().probeConstLeafAndCache(xyz, this->self());
+ }
+ const LeafNodeT* probeLeaf(const Coord& xyz) const { return this->probeConstLeaf(xyz); }
+
+ /// Remove all the cached nodes and invalidate the corresponding hash-keys.
+ virtual void clear()
+ {
+ mKey0 = Coord::max();
+ mNode0 = NULL;
+ mKey1 = Coord::max();
+ mNode1 = NULL;
+ }
+
+private:
+ // Allow nodes to insert themselves into the cache.
+ template<typename> friend class RootNode;
+ template<typename, Index> friend class InternalNode;
+ template<typename, Index> friend class LeafNode;
+ // Allow trees to deregister themselves.
+ template<typename> friend class Tree;
+
+ // This private method is merely for convenience.
+ inline ValueAccessor2& self() const { return const_cast<ValueAccessor2&>(*this); }
+
+ void getNode(const NodeT0*& node) { node = mNode0; }
+ void getNode(const NodeT1*& node) { node = mNode1; }
+ void getNode(const RootNodeT*& node)
+ {
+ node = (BaseT::mTree ? &BaseT::mTree->getRootNode() : NULL);
+ }
+ template <typename OtherNodeType> void getNode(const OtherNodeType*& node) { node = NULL; }
+
+ void eraseNode(const NodeT0*) { mKey0 = Coord::max(); mNode0 = NULL; }
+ void eraseNode(const NodeT1*) { mKey1 = Coord::max(); mNode1 = NULL; }
+ template <typename OtherNodeType> void eraseNode(const OtherNodeType*) {}
+
+ /// Private copy method
+ inline void copy(const ValueAccessor2& other)
+ {
+ mKey0 = other.mKey0;
+ mNode0 = other.mNode0;
+ mKey1 = other.mKey1;
+ mNode1 = other.mNode1;
+ }
+
+ /// Prevent this accessor from calling Tree::releaseCache() on a tree that
+ /// no longer exists. (Called by mTree when it is destroyed.)
+ virtual void release()
+ {
+ this->BaseT::release();
+ this->clear();
+ }
+
+ /// Cache the given node, which should lie along the path from the root node to
+ /// the node containing voxel (x, y, z).
+ /// @note This operation is not mutex-protected and is intended to be called
+ /// only by nodes and only in the context of a getValue() or setValue() call.
+ inline void insert(const Coord& xyz, const NodeT0* node)
+ {
+ assert(node);
+ mKey0 = xyz & ~(NodeT0::DIM-1);
+ mNode0 = node;
+ }
+ inline void insert(const Coord& xyz, const NodeT1* node)
+ {
+ assert(node);
+ mKey1 = xyz & ~(NodeT1::DIM-1);
+ mNode1 = node;
+ }
+ /// No-op in case a tree traversal attemps to insert a node that
+ /// is not cached by the ValueAccessor
+ template<typename NodeT> inline void insert(const Coord&, const NodeT*) {}
+
+ inline bool isHashed0(const Coord& xyz) const
+ {
+ return (xyz[0] & ~Coord::ValueType(NodeT0::DIM-1)) == mKey0[0]
+ && (xyz[1] & ~Coord::ValueType(NodeT0::DIM-1)) == mKey0[1]
+ && (xyz[2] & ~Coord::ValueType(NodeT0::DIM-1)) == mKey0[2];
+ }
+ inline bool isHashed1(const Coord& xyz) const
+ {
+ return (xyz[0] & ~Coord::ValueType(NodeT1::DIM-1)) == mKey1[0]
+ && (xyz[1] & ~Coord::ValueType(NodeT1::DIM-1)) == mKey1[1]
+ && (xyz[2] & ~Coord::ValueType(NodeT1::DIM-1)) == mKey1[2];
+ }
+ mutable Coord mKey0;
+ mutable const NodeT0* mNode0;
+ mutable Coord mKey1;
+ mutable const NodeT1* mNode1;
+}; // ValueAccessor2
+
+
+/// @brief Value accessor with three levels of node caching.
+/// @details The node cache levels are specified by L0, L1, and L2
+/// with the default values 0, 1 and 2 (defined in the forward declaration)
+/// corresponding to a LeafNode, its parent InternalNode, and its parent InternalNode.
+/// Since the default configuration of all typed trees and grids, e.g.,
+/// FloatTree or FloatGrid, has a depth of four, this value accessor is the one
+/// used by default.
+///
+/// @note This class is for experts only and should rarely be used
+/// directly. Instead use ValueAccessor with its default template arguments
+template<typename _TreeType, Index L0, Index L1, Index L2>
+class ValueAccessor3 : public ValueAccessorBase<_TreeType>
+{
+public:
+ BOOST_STATIC_ASSERT(_TreeType::DEPTH >= 4);
+ BOOST_STATIC_ASSERT(L0 < L1 && L1 < L2 && L2 < _TreeType::RootNodeType::LEVEL);
+ typedef _TreeType TreeType;
+ typedef typename TreeType::ValueType ValueType;
+ typedef typename TreeType::RootNodeType RootNodeT;
+ typedef typename TreeType::LeafNodeType LeafNodeT;
+ typedef ValueAccessorBase<TreeType> BaseT;
+ typedef typename InvertedTree<RootNodeT, RootNodeT::LEVEL>::Type InvTreeT;
+ typedef typename boost::mpl::at<InvTreeT, boost::mpl::int_<L0> >::type NodeT0;
+ typedef typename boost::mpl::at<InvTreeT, boost::mpl::int_<L1> >::type NodeT1;
+ typedef typename boost::mpl::at<InvTreeT, boost::mpl::int_<L2> >::type NodeT2;
+
+ /// Constructor from a tree
+ ValueAccessor3(TreeType& tree) : BaseT(tree),
+ mKey0(Coord::max()), mNode0(NULL),
+ mKey1(Coord::max()), mNode1(NULL),
+ mKey2(Coord::max()), mNode2(NULL) {}
+
+ /// Copy constructor
+ ValueAccessor3(const ValueAccessor3& other) : BaseT(other) { this->copy(other); }
+
+ /// Asignment operator
+ ValueAccessor3& operator=(const ValueAccessor3& other)
+ {
+ if (&other != this) {
+ this->BaseT::operator=(other);
+ this->copy(other);
+ }
+ return *this;
+ }
+
+ /// Return the number of cache levels employed by this ValueAccessor
+ static Index numCacheLevels() { return 3; }
+
+ /// Virtual destructor
+ virtual ~ValueAccessor3() {}
+
+ /// Return @c true if any of the nodes along the path to the given
+ /// voxel have been cached.
+ bool isCached(const Coord& xyz) const
+ {
+ assert(BaseT::mTree);
+ return this->isHashed2(xyz) || this->isHashed1(xyz) || this->isHashed0(xyz);
+ }
+
+ /// Return the value of the voxel at the given coordinates.
+ const ValueType& getValue(const Coord& xyz) const
+ {
+ assert(BaseT::mTree);
+ if (this->isHashed0(xyz)) {
+ assert(mNode0);
+ return mNode0->getValueAndCache(xyz, this->self());
+ } else if (this->isHashed1(xyz)) {
+ assert(mNode1);
+ return mNode1->getValueAndCache(xyz, this->self());
+ } else if (this->isHashed2(xyz)) {
+ assert(mNode2);
+ return mNode2->getValueAndCache(xyz, this->self());
+ }
+ return BaseT::mTree->getRootNode().getValueAndCache(xyz, this->self());
+ }
+
+ /// Return the active state of the voxel at the given coordinates.
+ bool isValueOn(const Coord& xyz) const
+ {
+ assert(BaseT::mTree);
+ if (this->isHashed0(xyz)) {
+ assert(mNode0);
+ return mNode0->isValueOnAndCache(xyz, this->self());
+ } else if (this->isHashed1(xyz)) {
+ assert(mNode1);
+ return mNode1->isValueOnAndCache(xyz, this->self());
+ } else if (this->isHashed2(xyz)) {
+ assert(mNode2);
+ return mNode2->isValueOnAndCache(xyz, this->self());
+ }
+ return BaseT::mTree->getRootNode().isValueOnAndCache(xyz, this->self());
+ }
+
+ /// Return the active state of the voxel as well as its value
+ bool probeValue(const Coord& xyz, ValueType& value) const
+ {
+ assert(BaseT::mTree);
+ if (this->isHashed0(xyz)) {
+ assert(mNode0);
+ return mNode0->probeValueAndCache(xyz, value, this->self());
+ } else if (this->isHashed1(xyz)) {
+ assert(mNode1);
+ return mNode1->probeValueAndCache(xyz, value, this->self());
+ } else if (this->isHashed2(xyz)) {
+ assert(mNode2);
+ return mNode2->probeValueAndCache(xyz, value, this->self());
+ }
+ return BaseT::mTree->getRootNode().probeValueAndCache(xyz, value, this->self());
+ }
+
+ /// Return the tree depth (0 = root) at which the value of voxel (x, y, z) resides,
+ /// or -1 if (x, y, z) isn't explicitly represented in the tree (i.e., if it is
+ /// implicitly a background voxel).
+ int getValueDepth(const Coord& xyz) const
+ {
+ assert(BaseT::mTree);
+ if (this->isHashed0(xyz)) {
+ assert(mNode0);
+ return RootNodeT::LEVEL - mNode0->getValueLevelAndCache(xyz, this->self());
+ } else if (this->isHashed1(xyz)) {
+ assert(mNode1);
+ return RootNodeT::LEVEL - mNode1->getValueLevelAndCache(xyz, this->self());
+ } else if (this->isHashed2(xyz)) {
+ assert(mNode2);
+ return RootNodeT::LEVEL - mNode2->getValueLevelAndCache(xyz, this->self());
+ }
+ return BaseT::mTree->getRootNode().getValueDepthAndCache(xyz, this->self());
+ }
+
+ /// Return @c true if the value of voxel (x, y, z) resides at the leaf level
+ /// of the tree, i.e., if it is not a tile value.
+ bool isVoxel(const Coord& xyz) const
+ {
+ assert(BaseT::mTree);
+ if (this->isHashed0(xyz)) {
+ assert(mNode0);
+ return mNode0->getValueLevelAndCache(xyz, this->self())==0;
+ } else if (this->isHashed1(xyz)) {
+ assert(mNode1);
+ return mNode1->getValueLevelAndCache(xyz, this->self())==0;
+ } else if (this->isHashed2(xyz)) {
+ assert(mNode2);
+ return mNode2->getValueLevelAndCache(xyz, this->self())==0;
+ }
+ return BaseT::mTree->getRootNode().getValueDepthAndCache(xyz, this->self()) ==
+ static_cast<int>(RootNodeT::LEVEL);
+ }
+
+ //@{
+ /// Set the value of the voxel at the given coordinates and mark the voxel as active.
+ void setValue(const Coord& xyz, const ValueType& value)
+ {
+ assert(BaseT::mTree);
+ BOOST_STATIC_ASSERT(!BaseT::IsConstTree);
+ if (this->isHashed0(xyz)) {
+ assert(mNode0);
+ const_cast<NodeT0*>(mNode0)->setValueAndCache(xyz, value, *this);
+ } else if (this->isHashed1(xyz)) {
+ assert(mNode1);
+ const_cast<NodeT1*>(mNode1)->setValueAndCache(xyz, value, *this);
+ } else if (this->isHashed2(xyz)) {
+ assert(mNode2);
+ const_cast<NodeT2*>(mNode2)->setValueAndCache(xyz, value, *this);
+ } else {
+ BaseT::mTree->getRootNode().setValueAndCache(xyz, value, *this);
+ }
+ }
+ void setValueOn(const Coord& xyz, const ValueType& value) { this->setValue(xyz, value); }
+ //@}
+
+ /// Set the value of the voxel at the given coordinate but preserves its active state.
+ void setValueOnly(const Coord& xyz, const ValueType& value)
+ {
+ assert(BaseT::mTree);
+ BOOST_STATIC_ASSERT(!BaseT::IsConstTree);
+ if (this->isHashed0(xyz)) {
+ assert(mNode0);
+ const_cast<NodeT0*>(mNode0)->setValueOnlyAndCache(xyz, value, *this);
+ } else if (this->isHashed1(xyz)) {
+ assert(mNode1);
+ const_cast<NodeT1*>(mNode1)->setValueOnlyAndCache(xyz, value, *this);
+ } else if (this->isHashed2(xyz)) {
+ assert(mNode2);
+ const_cast<NodeT2*>(mNode2)->setValueOnlyAndCache(xyz, value, *this);
+ } else {
+ BaseT::mTree->getRootNode().setValueOnlyAndCache(xyz, value, *this);
+ }
+ }
+
+ /// Set the value of the voxel at the given coordinates and mark the voxel as inactive.
+ void setValueOff(const Coord& xyz, const ValueType& value)
+ {
+ assert(BaseT::mTree);
+ BOOST_STATIC_ASSERT(!BaseT::IsConstTree);
+ if (this->isHashed0(xyz)) {
+ assert(mNode0);
+ const_cast<NodeT0*>(mNode0)->setValueOffAndCache(xyz, value, *this);
+ } else if (this->isHashed1(xyz)) {
+ assert(mNode1);
+ const_cast<NodeT1*>(mNode1)->setValueOffAndCache(xyz, value, *this);
+ } else if (this->isHashed2(xyz)) {
+ assert(mNode2);
+ const_cast<NodeT2*>(mNode2)->setValueOffAndCache(xyz, value, *this);
+ } else {
+ BaseT::mTree->getRootNode().setValueOffAndCache(xyz, value, *this);
+ }
+ }
+
+ /// Set the value of the voxel at the given coordinates to the sum of its current
+ /// value and the given value, and mark the voxel as active.
+ void setValueOnSum(const Coord& xyz, const ValueType& value)
+ {
+ assert(BaseT::mTree);
+ BOOST_STATIC_ASSERT(!BaseT::IsConstTree);
+ if (this->isHashed0(xyz)) {
+ assert(mNode0);
+ const_cast<NodeT0*>(mNode0)->setValueOnSumAndCache(xyz, value, *this);
+ } else if (this->isHashed1(xyz)) {
+ assert(mNode1);
+ const_cast<NodeT1*>(mNode1)->setValueOnSumAndCache(xyz, value, *this);
+ } else if (this->isHashed2(xyz)) {
+ assert(mNode2);
+ const_cast<NodeT2*>(mNode2)->setValueOnSumAndCache(xyz, value, *this);
+ } else {
+ BaseT::mTree->getRootNode().setValueOnSumAndCache(xyz, value, *this);
+ }
+ }
+
+ /// Set the active state of the voxel at the given coordinates without changing its value.
+ void setActiveState(const Coord& xyz, bool on = true)
+ {
+ assert(BaseT::mTree);
+ BOOST_STATIC_ASSERT(!BaseT::IsConstTree);
+ if (this->isHashed0(xyz)) {
+ assert(mNode0);
+ const_cast<NodeT0*>(mNode0)->setActiveStateAndCache(xyz, on, *this);
+ } else if (this->isHashed1(xyz)) {
+ assert(mNode1);
+ const_cast<NodeT1*>(mNode1)->setActiveStateAndCache(xyz, on, *this);
+ } else if (this->isHashed2(xyz)) {
+ assert(mNode2);
+ const_cast<NodeT2*>(mNode2)->setActiveStateAndCache(xyz, on, *this);
+ } else {
+ BaseT::mTree->getRootNode().setActiveStateAndCache(xyz, on, *this);
+ }
+ }
+ /// Mark the voxel at the given coordinates as active without changing its value.
+ void setValueOn(const Coord& xyz) { this->setActiveState(xyz, true); }
+ /// Mark the voxel at the given coordinates as inactive without changing its value.
+ void setValueOff(const Coord& xyz) { this->setActiveState(xyz, false); }
+
+ /// Return the cached node of type @a NodeType. [Mainly for internal use]
+ template<typename NodeT>
+ NodeT* getNode()
+ {
+ const NodeT* node = NULL;
+ this->getNode(node);
+ return const_cast<NodeT*>(node);
+ }
+
+ /// Cache the given node, which should lie along the path from the root node to
+ /// the node containing voxel (x, y, z). [Mainly for internal use]
+ template<typename NodeT>
+ void insertNode(const Coord& xyz, NodeT& node) { this->insert(xyz, &node); }
+
+ /// If a node of the given type exists in the cache, remove it, so that
+ /// isCached(xyz) returns @c false for any voxel (x, y, z) contained in
+ /// that node. [Mainly for internal use]
+ template<typename NodeT>
+ void eraseNode()
+ {
+ const NodeT* node = NULL;
+ this->eraseNode(node);
+ }
+
+ /// @brief Add the specified leaf to this tree, possibly creating a child branch
+ /// in the process. If the leaf node already exists, replace it.
+ void addLeaf(LeafNodeT* leaf)
+ {
+ assert(BaseT::mTree);
+ BOOST_STATIC_ASSERT(!BaseT::IsConstTree);
+ if (this->isHashed1(leaf->origin())) {
+ assert(mNode1);
+ return const_cast<NodeT1*>(mNode1)->addLeafAndCache(leaf, *this);
+ } else if (this->isHashed2(leaf->origin())) {
+ assert(mNode2);
+ return const_cast<NodeT2*>(mNode2)->addLeafAndCache(leaf, *this);
+ }
+ BaseT::mTree->root().addLeafAndCache(leaf, *this);
+ }
+
+ /// @brief Add a tile at the specified tree level that contains
+ /// xyz, possibly creating a child branch in the process. If a
+ /// Node that contains xyz already exists it is replaced by a tile.
+ void addTile(Index level, const Coord& xyz, const ValueType& value, bool state)
+ {
+ assert(BaseT::mTree);
+ BOOST_STATIC_ASSERT(!BaseT::IsConstTree);
+ if (this->isHashed1(xyz)) {
+ assert(mNode1);
+ return const_cast<NodeT1*>(mNode1)->addTileAndCache(level, xyz, value, state, *this);
+ } if (this->isHashed2(xyz)) {
+ assert(mNode2);
+ return const_cast<NodeT2*>(mNode2)->addTileAndCache(level, xyz, value, state, *this);
+ }
+ BaseT::mTree->root().addTileAndCache(level, xyz, value, state, *this);
+ }
+
+ /// @brief @return the leaf node that contains voxel (x, y, z) and
+ /// if it doesn't exist, create it, but preserve the values and
+ /// active states of all voxels.
+ ///
+ /// Use this method to preallocate a static tree topology over which to
+ /// safely perform multithreaded processing.
+ LeafNodeT* touchLeaf(const Coord& xyz)
+ {
+ assert(BaseT::mTree);
+ BOOST_STATIC_ASSERT(!BaseT::IsConstTree);
+ if (this->isHashed0(xyz)) {
+ assert(mNode0);
+ return const_cast<NodeT0*>(mNode0)->touchLeafAndCache(xyz, *this);
+ } else if (this->isHashed1(xyz)) {
+ assert(mNode1);
+ return const_cast<NodeT1*>(mNode1)->touchLeafAndCache(xyz, *this);
+ } else if (this->isHashed2(xyz)) {
+ assert(mNode2);
+ return const_cast<NodeT2*>(mNode2)->touchLeafAndCache(xyz, *this);
+ }
+ return BaseT::mTree->getRootNode().touchLeafAndCache(xyz, *this);
+ }
+
+ /// @brief @return a pointer to the leaf node that contains
+ /// voxel (x, y, z) and if it doesn't exist, return NULL.
+ LeafNodeT* probeLeaf(const Coord& xyz)
+ {
+ assert(BaseT::mTree);
+ BOOST_STATIC_ASSERT(!BaseT::IsConstTree);
+ if (this->isHashed0(xyz)) {
+ assert(mNode0);
+ return const_cast<NodeT0*>(mNode0)->probeLeafAndCache(xyz, *this);
+ } else if (this->isHashed1(xyz)) {
+ assert(mNode1);
+ return const_cast<NodeT1*>(mNode1)->probeLeafAndCache(xyz, *this);
+ } else if (this->isHashed2(xyz)) {
+ assert(mNode2);
+ return const_cast<NodeT2*>(mNode2)->probeLeafAndCache(xyz, *this);
+ }
+ return BaseT::mTree->getRootNode().probeLeafAndCache(xyz, *this);
+ }
+
+ /// @brief @return a const pointer to the leaf node that contains
+ /// voxel (x, y, z) and if it doesn't exist, return NULL.
+ const LeafNodeT* probeConstLeaf(const Coord& xyz) const
+ {
+ assert(BaseT::mTree);
+ if (this->isHashed0(xyz)) {
+ assert(mNode0);
+ return mNode0->probeConstLeafAndCache(xyz, this->self());
+ } else if (this->isHashed1(xyz)) {
+ assert(mNode1);
+ return mNode1->probeConstLeafAndCache(xyz, this->self());
+ } else if (this->isHashed2(xyz)) {
+ assert(mNode2);
+ return mNode2->probeConstLeafAndCache(xyz, this->self());
+ }
+ return BaseT::mTree->getRootNode().probeConstLeafAndCache(xyz, this->self());
+ }
+ const LeafNodeT* probeLeaf(const Coord& xyz) const { return this->probeConstLeaf(xyz); }
+
+ /// Remove all the cached nodes and invalidate the corresponding hash-keys.
+ virtual void clear()
+ {
+ mKey0 = Coord::max();
+ mNode0 = NULL;
+ mKey1 = Coord::max();
+ mNode1 = NULL;
+ mKey2 = Coord::max();
+ mNode2 = NULL;
+ }
+
+private:
+ // Allow nodes to insert themselves into the cache.
+ template<typename> friend class RootNode;
+ template<typename, Index> friend class InternalNode;
+ template<typename, Index> friend class LeafNode;
+ // Allow trees to deregister themselves.
+ template<typename> friend class Tree;
+
+ // This private method is merely for convenience.
+ inline ValueAccessor3& self() const { return const_cast<ValueAccessor3&>(*this); }
+
+ /// Private copy method
+ inline void copy(const ValueAccessor3& other)
+ {
+ mKey0 = other.mKey0;
+ mNode0 = other.mNode0;
+ mKey1 = other.mKey1;
+ mNode1 = other.mNode1;
+ mKey2 = other.mKey2;
+ mNode2 = other.mNode2;
+ }
+
+ /// Prevent this accessor from calling Tree::releaseCache() on a tree that
+ /// no longer exists. (Called by mTree when it is destroyed.)
+ virtual void release()
+ {
+ this->BaseT::release();
+ this->clear();
+ }
+ void getNode(const NodeT0*& node) { node = mNode0; }
+ void getNode(const NodeT1*& node) { node = mNode1; }
+ void getNode(const NodeT2*& node) { node = mNode2; }
+ void getNode(const RootNodeT*& node)
+ {
+ node = (BaseT::mTree ? &BaseT::mTree->getRootNode() : NULL);
+ }
+ template <typename OtherNodeType> void getNode(const OtherNodeType*& node) { node = NULL; }
+
+ void eraseNode(const NodeT0*) { mKey0 = Coord::max(); mNode0 = NULL; }
+ void eraseNode(const NodeT1*) { mKey1 = Coord::max(); mNode1 = NULL; }
+ void eraseNode(const NodeT2*) { mKey2 = Coord::max(); mNode2 = NULL; }
+ template <typename OtherNodeType> void eraseNode(const OtherNodeType*) {}
+
+ /// Cache the given node, which should lie along the path from the root node to
+ /// the node containing voxel (x, y, z).
+ /// @note This operation is not mutex-protected and is intended to be called
+ /// only by nodes and only in the context of a getValue() or setValue() call.
+ inline void insert(const Coord& xyz, const NodeT0* node)
+ {
+ assert(node);
+ mKey0 = xyz & ~(NodeT0::DIM-1);
+ mNode0 = node;
+ }
+ inline void insert(const Coord& xyz, const NodeT1* node)
+ {
+ assert(node);
+ mKey1 = xyz & ~(NodeT1::DIM-1);
+ mNode1 = node;
+ }
+ inline void insert(const Coord& xyz, const NodeT2* node)
+ {
+ assert(node);
+ mKey2 = xyz & ~(NodeT2::DIM-1);
+ mNode2 = node;
+ }
+ /// No-op in case a tree traversal attemps to insert a node that
+ /// is not cached by the ValueAccessor
+ template<typename OtherNodeType>
+ inline void insert(const Coord&, const OtherNodeType*)
+ {
+ }
+ inline bool isHashed0(const Coord& xyz) const
+ {
+ return (xyz[0] & ~Coord::ValueType(NodeT0::DIM-1)) == mKey0[0]
+ && (xyz[1] & ~Coord::ValueType(NodeT0::DIM-1)) == mKey0[1]
+ && (xyz[2] & ~Coord::ValueType(NodeT0::DIM-1)) == mKey0[2];
+ }
+ inline bool isHashed1(const Coord& xyz) const
+ {
+ return (xyz[0] & ~Coord::ValueType(NodeT1::DIM-1)) == mKey1[0]
+ && (xyz[1] & ~Coord::ValueType(NodeT1::DIM-1)) == mKey1[1]
+ && (xyz[2] & ~Coord::ValueType(NodeT1::DIM-1)) == mKey1[2];
+ }
+ inline bool isHashed2(const Coord& xyz) const
+ {
+ return (xyz[0] & ~Coord::ValueType(NodeT2::DIM-1)) == mKey2[0]
+ && (xyz[1] & ~Coord::ValueType(NodeT2::DIM-1)) == mKey2[1]
+ && (xyz[2] & ~Coord::ValueType(NodeT2::DIM-1)) == mKey2[2];
+ }
+ mutable Coord mKey0;
+ mutable const NodeT0* mNode0;
+ mutable Coord mKey1;
+ mutable const NodeT1* mNode1;
+ mutable Coord mKey2;
+ mutable const NodeT2* mNode2;
+}; // ValueAccessor3
+
+} // namespace tree
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_TREE_VALUEACCESSOR_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/util/Formats.cc b/extern/openvdb/internal/openvdb/util/Formats.cc
new file mode 100644
index 00000000000..3d7b4cb765b
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/util/Formats.cc
@@ -0,0 +1,121 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+
+#include "Formats.h"
+
+#include <openvdb/Platform.h>
+#include <iostream>
+#include <iomanip>
+
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace util {
+
+int
+printBytes(std::ostream& os, uint64_t bytes,
+ const std::string& head, const std::string& tail,
+ bool exact, int width, int precision)
+{
+ const uint64_t one = 1;
+ int group = 0;
+
+ // Write to a string stream so that I/O manipulators like
+ // std::setprecision() don't alter the output stream.
+ std::ostringstream ostr;
+ ostr << head;
+ ostr << std::setprecision(precision) << std::setiosflags(std::ios::fixed);
+ if (bytes >> 40) {
+ ostr << std::setw(width) << (bytes / double(one << 40)) << " TB";
+ group = 4;
+ } else if (bytes >> 30) {
+ ostr << std::setw(width) << (bytes / double(one << 30)) << " GB";
+ group = 3;
+ } else if (bytes >> 20) {
+ ostr << std::setw(width) << (bytes / double(one << 20)) << " MB";
+ group = 2;
+ } else if (bytes >> 10) {
+ ostr << std::setw(width) << (bytes / double(one << 10)) << " KB";
+ group = 1;
+ } else {
+ ostr << std::setw(width) << bytes << " Bytes";
+ }
+ if (exact && group) ostr << " (" << bytes << " Bytes)";
+ ostr << tail;
+
+ os << ostr.str();
+
+ return group;
+}
+
+
+int
+printNumber(std::ostream& os, uint64_t number,
+ const std::string& head, const std::string& tail,
+ bool exact, int width, int precision)
+{
+ int group = 0;
+
+ // Write to a string stream so that I/O manipulators like
+ // std::setprecision() don't alter the output stream.
+ std::ostringstream ostr;
+ ostr << head;
+ ostr << std::setprecision(precision) << std::setiosflags(std::ios::fixed);
+ if (number / UINT64_C(1000000000000)) {
+ ostr << std::setw(width) << (number / 1000000000000.0) << " trillion";
+ group = 4;
+ } else if (number / UINT64_C(1000000000)) {
+ ostr << std::setw(width) << (number / 1000000000.0) << " billion";
+ group = 3;
+ } else if (number / UINT64_C(1000000)) {
+ ostr << std::setw(width) << (number / 1000000.0) << " million";
+ group = 2;
+ } else if (number / UINT64_C(1000)) {
+ ostr << std::setw(width) << (number / 1000.0) << " thousand";
+ group = 1;
+ } else {
+ ostr << std::setw(width) << number;
+ }
+ if (exact && group) ostr << " (" << number << ")";
+ ostr << tail;
+
+ os << ostr.str();
+
+ return group;
+}
+
+} // namespace util
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/util/Formats.h b/extern/openvdb/internal/openvdb/util/Formats.h
new file mode 100644
index 00000000000..9a685c37d90
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/util/Formats.h
@@ -0,0 +1,140 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+//
+/// @author Ken Museth
+///
+/// @file Formats.h
+///
+/// @brief Utility routines to output nicely-formatted numeric values
+
+
+#ifndef OPENVDB_UTIL_FORMATS_HAS_BEEN_INCLUDED
+#define OPENVDB_UTIL_FORMATS_HAS_BEEN_INCLUDED
+
+#include <iosfwd>
+#include <sstream>
+#include <string>
+#include <openvdb/version.h>
+#include <openvdb/Platform.h>
+
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace util {
+
+/// Output a byte count with the correct binary suffix (KB, MB, GB or TB).
+/// @param os the output stream
+/// @param bytes the byte count to be output
+/// @param head a string to be output before the numeric text
+/// @param tail a string to be output after the numeric text
+/// @param exact if true, also output the unmodified count, e.g., "4.6 KB (4620 Bytes)"
+/// @param width a fixed width for the numeric text
+/// @param precision the number of digits after the decimal point
+/// @return 0, 1, 2, 3 or 4, denoting the order of magnitude of the count.
+OPENVDB_API int
+printBytes(std::ostream& os, uint64_t bytes,
+ const std::string& head = "",
+ const std::string& tail = "\n",
+ bool exact = false, int width = 8, int precision = 3);
+
+/// Output a number with the correct SI suffix (thousand, million, billion or trillion)
+/// @param os the output stream
+/// @param number the number to be output
+/// @param head a string to be output before the numeric text
+/// @param tail a string to be output after the numeric text
+/// @param exact if true, also output the unmodified count, e.g., "4.6 Thousand (4620)"
+/// @param width a fixed width for the numeric text
+/// @param precision the number of digits after the decimal point
+/// @return 0, 1, 2, 3 or 4, denoting the order of magnitude of the number.
+OPENVDB_API int
+printNumber(std::ostream& os, uint64_t number,
+ const std::string& head = "",
+ const std::string& tail = "\n",
+ bool exact = true, int width = 8, int precision = 3);
+
+
+////////////////////////////////////////
+
+
+/// @brief I/O manipulator that formats integer values with thousands separators
+template<typename IntT>
+class FormattedInt
+{
+public:
+ static char sep() { return ','; }
+
+ FormattedInt(IntT n): mInt(n) {}
+
+ std::ostream& put(std::ostream& os) const
+ {
+ // Convert the integer to a string.
+ std::ostringstream ostr;
+ ostr << mInt;
+ std::string s = ostr.str();
+ // Prefix the string with spaces if its length is not a multiple of three.
+ size_t padding = (s.size() % 3) ? 3 - (s.size() % 3) : 0;
+ s = std::string(padding, ' ') + s;
+ // Construct a new string in which groups of three digits are followed
+ // by a separator character.
+ ostr.str("");
+ for (size_t i = 0, N = s.size(); i < N; ) {
+ ostr << s[i];
+ ++i;
+ if (i >= padding && i % 3 == 0 && i < s.size()) {
+ ostr << sep();
+ }
+ }
+ // Remove any padding that was added and output the string.
+ s = ostr.str();
+ os << s.substr(padding, s.size());
+ return os;
+ }
+
+private:
+ IntT mInt;
+};
+
+template<typename IntT>
+std::ostream& operator<<(std::ostream& os, const FormattedInt<IntT>& n) { return n.put(os); }
+
+/// @return an I/O manipulator that formats the given integer value for output to a stream.
+template<typename IntT>
+FormattedInt<IntT> formattedInt(IntT n) { return FormattedInt<IntT>(n); }
+
+} // namespace util
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_UTIL_FORMATS_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/util/MapsUtil.h b/extern/openvdb/internal/openvdb/util/MapsUtil.h
new file mode 100644
index 00000000000..8865169f1d4
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/util/MapsUtil.h
@@ -0,0 +1,329 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+//
+/// @file MapsUtil.h
+
+#ifndef OPENVDB_UTIL_MAPSUTIL_HAS_BEEN_INCLUDED
+#define OPENVDB_UTIL_MAPSUTIL_HAS_BEEN_INCLUDED
+
+#include <openvdb/math/Maps.h>
+
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace util {
+
+// Utility methods for calculating bounding boxes
+
+/// @brief Calculate an axis-aligned bounding box in the given map's domain
+/// (e.g., index space) from an axis-aligned bounding box in its range
+/// (e.g., world space)
+template<typename MapType>
+inline void
+calculateBounds(const MapType& map, const BBoxd& in, BBoxd& out)
+{
+ const Vec3d& min = in.min();
+ const Vec3d& max = in.max();
+
+ // the pre-image of the 8 corners of the box
+ Vec3d corners[8];
+ corners[0] = in.min();;
+ corners[1] = Vec3d(min(0), min(1), min(2));
+ corners[2] = Vec3d(max(0), max(1), min(2));
+ corners[3] = Vec3d(min(0), max(1), min(2));
+ corners[4] = Vec3d(min(0), min(1), max(2));
+ corners[5] = Vec3d(max(0), min(1), max(2));
+ corners[6] = max;
+ corners[7] = Vec3d(min(0), max(1), max(2));
+
+ Vec3d pre_image;
+ Vec3d& out_min = out.min();
+ Vec3d& out_max = out.max();
+ out_min = map.applyInverseMap(corners[0]);
+ out_max = min;
+ for (int i = 1; i < 8; ++i) {
+ pre_image = map.applyInverseMap(corners[i]);
+ for (int j = 0; j < 3; ++j) {
+ out_min(j) = std::min( out_min(j), pre_image(j));
+ out_max(j) = std::max( out_max(j), pre_image(j));
+ }
+ }
+}
+
+
+/// @brief Calculate an axis-aligned bounding box in the given map's domain
+/// from a spherical bounding box in its range.
+template<typename MapType>
+inline void
+calculateBounds(const MapType& map, const Vec3d& center, const Real radius, BBoxd& out)
+{
+ // On return, out gives a bounding box in continuous index space
+ // that encloses the sphere.
+ //
+ // the image of a sphere under the inverse of the linearMap will be an ellipsoid.
+
+ if (math::is_linear<MapType>::value) {
+ // I want to find extrema for three functions f(x', y', z') = x', or = y', or = z'
+ // with the constraint that g = (x-xo)^2 + (y-yo)^2 + (z-zo)^2 = r^2.
+ // Where the point x,y,z is the image of x',y',z'
+ // Solve: \lambda Grad(g) = Grad(f) and g = r^2.
+ // Note: here (x,y,z) is the image of (x',y',z'), and the gradient
+ // is w.r.t the (') space.
+ //
+ // This can be solved exactly: e_a^T (x' -xo') =\pm r\sqrt(e_a^T J^(-1)J^(-T)e_a)
+ // where e_a is one of the three unit vectors. - djh.
+
+ /// find the image of the center of the sphere
+ Vec3d center_pre_image = map.applyInverseMap(center);
+
+ std::vector<Vec3d> coordinate_units;
+ coordinate_units.push_back(Vec3d(1,0,0));
+ coordinate_units.push_back(Vec3d(0,1,0));
+ coordinate_units.push_back(Vec3d(0,0,1));
+
+ Vec3d& out_min = out.min();
+ Vec3d& out_max = out.max();
+ for (int direction = 0; direction < 3; ++direction) {
+ Vec3d temp = map.applyIJT(coordinate_units[direction]);
+ double offset =
+ radius * sqrt(temp.x()*temp.x() + temp.y()*temp.y() + temp.z()*temp.z());
+ out_min(direction) = center_pre_image(direction) - offset;
+ out_max(direction) = center_pre_image(direction) + offset;
+ }
+
+ } else {
+ // This is some unknown map type. In this case, we form an axis-aligned
+ // bounding box for the sphere in world space and find the pre-images of
+ // the corners in index space. From these corners we compute an axis-aligned
+ // bounding box in index space.
+ BBoxd bounding_box(center - radius*Vec3d(1,1,1), center + radius*Vec3d(1,1,1));
+ calculateBounds<MapType>(map, bounding_box, out);
+ }
+}
+
+
+namespace { // anonymous namespace for this helper function
+
+/// @brief Find the intersection of a line passing through the point
+/// \f$ (x=0, z=-1/g)\f$ with the circle \f$ (x-xo)^2 + (z-zo)^2 = r^2 \f$
+/// at a point tangent to the circle.
+/// @return 0 if the focal point (0, -1/g) is inside the circle,
+/// 1 if the focal point touches the circle, or 2 when both points are found.
+inline int
+findTangentPoints(const double g, const double xo, const double zo,
+ const double r, double& xp, double& zp, double& xm, double& zm)
+{
+ double x2 = xo * xo;
+ double r2 = r * r;
+ double xd = g * xo;
+ double xd2 = xd*xd;
+ double zd = g * zo + 1.;
+ double zd2 = zd*zd;
+ double rd2 = r2*g*g;
+
+ double distA = xd2 + zd2;
+ double distB = distA - rd2;
+
+ if (distB > 0) {
+ double discriminate = sqrt(distB);
+
+ xp = xo - xo*rd2/distA + r * zd *discriminate / distA;
+ xm = xo - xo*rd2/distA - r * zd *discriminate / distA;
+
+ zp = (zo*zd2 + zd*g*(x2 - r2) - xo*xo*g - r*xd*discriminate) / distA;
+ zm = (zo*zd2 + zd*g*(x2 - r2) - xo*xo*g + r*xd*discriminate) / distA;
+
+ return 2;
+
+ } if (0 >= distB && distB >= -1e-9) {
+ // the circle touches the focal point (x=0, z = -1/g)
+ xp = 0; xm = 0;
+ zp = -1/g; zm = -1/g;
+
+ return 1;
+ }
+
+ return 0;
+}
+
+} // end anonymous namespace
+
+
+/// @brief Calculate an axis-aligned bounding box in index space
+/// from a spherical bounding box in world space.
+/// @note This specialization is optimized for a frustum map
+template<>
+inline void
+calculateBounds<math::NonlinearFrustumMap>(const math::NonlinearFrustumMap& frustum,
+ const Vec3d& center, const Real radius, BBoxd& out)
+{
+ // The frustum is a nonlinear map followed by a uniform scale, rotation, translation.
+ // First we invert the translation, rotation and scale to find the spherical pre-image
+ // of the sphere in "local" coordinates where the frustum is aligned with the near plane
+ // on the z=0 plane and the "camera" is located at (x=0, y=0, z=-1/g).
+
+ // check that the internal map has no shear.
+ const math::AffineMap& secondMap = frustum.secondMap();
+ // test if the linear part has shear or non-uniform scaling
+ if (!frustum.hasSimpleAffine()) {
+
+ // In this case, we form an axis-aligned bounding box for sphere in world space
+ // and find the pre_images of the corners in voxel space. From these corners we
+ // compute an axis-algined bounding box in voxel-spae
+ BBoxd bounding_box(center - radius*Vec3d(1,1,1), center + radius*Vec3d(1,1,1));
+ calculateBounds<math::NonlinearFrustumMap>(frustum, bounding_box, out);
+ return;
+ }
+
+ // for convenience
+ Vec3d& out_min = out.min();
+ Vec3d& out_max = out.max();
+
+ Vec3d centerLS = secondMap.applyInverseMap(center);
+ Vec3d voxelSize = secondMap.voxelSize();
+
+ // all the voxels have the same size since we know this is a simple affine map
+ double radiusLS = radius / voxelSize(0);
+
+ double gamma = frustum.getGamma();
+ double xp;
+ double zp;
+ double xm;
+ double zm;
+ int soln_number;
+
+ // the bounding box in index space for the points in the frustum
+ const BBoxd& bbox = frustum.getBBox();
+ // initialize min and max
+ const double x_min = bbox.min().x();
+ const double y_min = bbox.min().y();
+ const double z_min = bbox.min().z();
+
+ const double x_max = bbox.max().x();
+ const double y_max = bbox.max().y();
+ const double z_max = bbox.max().z();
+
+ out_min.x() = x_min;
+ out_max.x() = x_max;
+ out_min.y() = y_min;
+ out_max.y() = y_max;
+
+ Vec3d extreme;
+ Vec3d extreme2;
+ Vec3d pre_image;
+ // find the x-range
+ soln_number = findTangentPoints(gamma, centerLS.x(), centerLS.z(), radiusLS, xp, zp, xm, zm);
+ if (soln_number == 2) {
+ extreme.x() = xp;
+ extreme.y() = centerLS.y();
+ extreme.z() = zp;
+
+ // location in world space of the tangent point
+ extreme2 = secondMap.applyMap(extreme);
+ // convert back to voxel space
+ pre_image = frustum.applyInverseMap(extreme2);
+ out_max.x() = std::max(x_min, std::min(x_max, pre_image.x()));
+
+ const Vec3d tmpPlus = extreme2;
+
+ extreme.x() = xm;
+ extreme.y() = centerLS.y();
+ extreme.z() = zm;
+ // location in world space of the tangent point
+ extreme2 = secondMap.applyMap(extreme);
+
+ const Vec3d tmpMinus = extreme2;
+
+ // convert back to voxel space
+ pre_image = frustum.applyInverseMap(extreme2);
+ out_min.x() = std::max(x_min, std::min(x_max, pre_image.x()));
+
+ } else if (soln_number == 1) {
+ // the circle was tangent at the focal point
+ } else if (soln_number == 0) {
+ // the focal point was inside the circle
+ }
+
+ // find the y-range
+ soln_number = findTangentPoints(gamma, centerLS.y(), centerLS.z(), radiusLS, xp, zp, xm, zm);
+ if (soln_number == 2) {
+ extreme.x() = centerLS.x();
+ extreme.y() = xp;
+ extreme.z() = zp;
+
+ // location in world space of the tangent point
+ extreme2 = secondMap.applyMap(extreme);
+ // convert back to voxel space
+ pre_image = frustum.applyInverseMap(extreme2);
+ out_max.y() = std::max(y_min, std::min(y_max, pre_image.y()));
+
+ const Vec3d tmpPlus = extreme2;
+
+ extreme.x() = centerLS.x();
+ extreme.y() = xm;
+ extreme.z() = zm;
+ extreme2 = secondMap.applyMap(extreme);
+
+ const Vec3d tmpMinus = extreme2;
+
+ // convert back to voxel space
+ pre_image = frustum.applyInverseMap(extreme2);
+ out_min.y() = std::max(y_min, std::min(y_max, pre_image.y()));
+
+ } else if (soln_number == 1) {
+ // the circle was tangent at the focal point
+ } else if (soln_number == 0) {
+ // the focal point was inside the circle
+ }
+
+ // the near and far
+ // the closest point. The front of the frustum is at 0 in index space
+ double near_dist = std::max(centerLS.z() - radiusLS, 0.);
+ // the farthest point. The back of the frustum is at mDepth in index space
+ double far_dist = std::min(centerLS.z() + radiusLS, frustum.getDepth() );
+
+ Vec3d near_point(0.f, 0.f, near_dist);
+ Vec3d far_point(0.f, 0.f, far_dist);
+
+ out_min.z() = std::max(z_min, frustum.applyInverseMap(secondMap.applyMap(near_point)).z());
+ out_max.z() = std::min(z_max, frustum.applyInverseMap(secondMap.applyMap(far_point)).z());
+
+}
+
+} // namespace util
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_UTIL_MAPSUTIL_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/util/Name.h b/extern/openvdb/internal/openvdb/util/Name.h
new file mode 100644
index 00000000000..51cf919d258
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/util/Name.h
@@ -0,0 +1,72 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+
+#ifndef OPENVDB_UTIL_NAME_HAS_BEEN_INCLUDED
+#define OPENVDB_UTIL_NAME_HAS_BEEN_INCLUDED
+
+#include <openvdb/Platform.h>
+#include <openvdb/version.h>
+#include <string>
+#include <iostream>
+#include <vector>
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+
+typedef std::string Name;
+
+inline Name
+readString(std::istream& is)
+{
+ uint32_t size;
+ is.read(reinterpret_cast<char*>(&size), sizeof(uint32_t));
+ std::string buffer(size, ' ');
+ is.read(&buffer[0], size);
+ return buffer;
+}
+
+
+inline void
+writeString(std::ostream& os, const Name& name)
+{
+ uint32_t size = uint32_t(name.size());
+ os.write(reinterpret_cast<char*>(&size), sizeof(uint32_t));
+ os.write(&name[0], size);
+}
+
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_UTIL_NAME_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/util/NodeMasks.h b/extern/openvdb/internal/openvdb/util/NodeMasks.h
new file mode 100644
index 00000000000..a38f0c651f8
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/util/NodeMasks.h
@@ -0,0 +1,1282 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+//
+/// @author Ken Museth
+///
+/// @file NodeMasks.h
+
+#ifndef OPENVDB_UTIL_NODEMASKS_HAS_BEEN_INCLUDED
+#define OPENVDB_UTIL_NODEMASKS_HAS_BEEN_INCLUDED
+
+#include <cassert>
+#include <cstring>
+#include <iostream>// for cout
+#include <openvdb/Types.h>
+//#include <boost/mpl/if.hpp>
+//#include <strings.h> // for ffs
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace util {
+
+/// Return the number of on bits in the given 8-bit value.
+inline Index32
+CountOn(Byte v)
+{
+ // Simple LUT:
+ static const Byte numBits[256] = {
+# define B2(n) n, n+1, n+1, n+2
+# define B4(n) B2(n), B2(n+1), B2(n+1), B2(n+2)
+# define B6(n) B4(n), B4(n+1), B4(n+1), B4(n+2)
+ B6(0), B6(1), B6(1), B6(2)
+ };
+ return numBits[v];
+
+ // Sequentially clear least significant bits
+ //Index32 c;
+ //for (c = 0; v; c++) v &= v - 0x01U;
+ //return c;
+
+ // This version is only fast on CPUs with fast "%" and "*" operations
+ //return (v * UINT64_C(0x200040008001) & UINT64_C(0x111111111111111)) % 0xF;
+}
+/// Return the number of off bits in the given 8-bit value.
+inline Index32 CountOff(Byte v) { return CountOn(~v); }
+
+/// Return the number of on bits in the given 32-bit value.
+inline Index32
+CountOn(Index32 v)
+{
+ v = v - ((v >> 1) & 0x55555555U);
+ v = (v & 0x33333333U) + ((v >> 2) & 0x33333333U);
+ return ((v + (v >> 4) & 0xF0F0F0FU) * 0x1010101U) >> 24;
+}
+
+/// Return the number of off bits in the given 32-bit value.
+inline Index32 CountOff(Index32 v) { return CountOn(~v); }
+
+/// Return the number of on bits in the given 64-bit value.
+inline Index32
+CountOn(Index64 v)
+{
+ v = v - ((v >> 1) & UINT64_C(0x5555555555555555));
+ v = (v & UINT64_C(0x3333333333333333)) + ((v >> 2) & UINT64_C(0x3333333333333333));
+ return ((v + (v >> 4) & UINT64_C(0xF0F0F0F0F0F0F0F)) * UINT64_C(0x101010101010101)) >> 56;
+}
+
+/// Return the number of off bits in the given 64-bit value.
+inline Index32 CountOff(Index64 v) { return CountOn(~v); }
+
+/// Return the least significant on bit of the given 8-bit value.
+inline Index32
+FindLowestOn(Byte v)
+{
+ assert(v);
+ static const Byte DeBruijn[8] = {0, 1, 6, 2, 7, 5, 4, 3};
+ return DeBruijn[Byte((v & -v) * 0x1DU) >> 5];
+}
+
+/// Return the least significant on bit of the given 32-bit value.
+inline Index32
+FindLowestOn(Index32 v)
+{
+ assert(v);
+ //return ffs(v);
+ static const Byte DeBruijn[32] = {
+ 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
+ 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9
+ };
+ return DeBruijn[Index32((v & -v) * 0x077CB531U) >> 27];
+}
+
+/// Return the least significant on bit of the given 64-bit value.
+inline Index32
+FindLowestOn(Index64 v)
+{
+ assert(v);
+ //return ffsll(v);
+ static const Byte DeBruijn[64] = {
+ 0, 1, 2, 53, 3, 7, 54, 27, 4, 38, 41, 8, 34, 55, 48, 28,
+ 62, 5, 39, 46, 44, 42, 22, 9, 24, 35, 59, 56, 49, 18, 29, 11,
+ 63, 52, 6, 26, 37, 40, 33, 47, 61, 45, 43, 21, 23, 58, 17, 10,
+ 51, 25, 36, 32, 60, 20, 57, 16, 50, 31, 19, 15, 30, 14, 13, 12,
+ };
+ return DeBruijn[Index64((v & -v) * UINT64_C(0x022FDD63CC95386D)) >> 58];
+}
+
+/// Return the most significant on bit of the given 32-bit value.
+inline Index32
+FindHighestOn(Index32 v)
+{
+ static const Byte DeBruijn[32] = {
+ 0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30,
+ 8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31
+ };
+ v |= v >> 1; // first round down to one less than a power of 2
+ v |= v >> 2;
+ v |= v >> 4;
+ v |= v >> 8;
+ v |= v >> 16;
+ return DeBruijn[Index32(v * 0x07C4ACDDU) >> 27];
+}
+
+
+////////////////////////////////////////
+
+
+/// Base class for the bit mask iterators
+template <typename NodeMask>
+class BaseMaskIterator
+{
+protected:
+ Index32 mPos;//bit position
+ const NodeMask* mParent;//this iterator can't change the parent_mask!
+public:
+ BaseMaskIterator() : mPos(NodeMask::SIZE), mParent(NULL) {}
+ BaseMaskIterator(Index32 pos,const NodeMask *parent) : mPos(pos), mParent(parent)
+ {
+ assert( (parent==NULL && pos==0 ) || (parent!=NULL && pos<=NodeMask::SIZE) );
+ }
+ bool operator==(const BaseMaskIterator &iter) const {return mPos == iter.mPos;}
+ bool operator!=(const BaseMaskIterator &iter) const {return mPos != iter.mPos;}
+ bool operator< (const BaseMaskIterator &iter) const {return mPos < iter.mPos;}
+ void operator= (const BaseMaskIterator &iter)
+ {
+ mPos = iter.mPos;
+ mParent = iter.mParent;
+ }
+ Index32 offset() const {return mPos;}
+ Index32 pos() const {return mPos;}
+ bool test() const
+ {
+ assert(mPos <= NodeMask::SIZE);
+ return (mPos != NodeMask::SIZE);
+ }
+ operator bool() const {return this->test();}
+}; // class BaseMaskIterator
+
+
+/// @note This happens to be a const-iterator!
+template <typename NodeMask>
+class OnMaskIterator: public BaseMaskIterator<NodeMask>
+{
+private:
+ typedef BaseMaskIterator<NodeMask> BaseType;
+ using BaseType::mPos;//bit position;
+ using BaseType::mParent;//this iterator can't change the parent_mask!
+public:
+ OnMaskIterator() : BaseType() {}
+ OnMaskIterator(Index32 pos,const NodeMask *parent) : BaseType(pos,parent) {}
+ void increment()
+ {
+ assert(mParent != NULL);
+ mPos = mParent->findNextOn(mPos+1);
+ assert(mPos <= NodeMask::SIZE);
+ }
+ void increment(Index n) { while(n-- && this->next()) ; }
+ bool next()
+ {
+ this->increment();
+ return this->test();
+ }
+ bool operator*() const {return true;}
+ OnMaskIterator& operator++()
+ {
+ this->increment();
+ return *this;
+ }
+}; // class OnMaskIterator
+
+
+template <typename NodeMask>
+class OffMaskIterator: public BaseMaskIterator<NodeMask>
+{
+private:
+ typedef BaseMaskIterator<NodeMask> BaseType;
+ using BaseType::mPos;//bit position;
+ using BaseType::mParent;//this iterator can't change the parent_mask!
+public:
+ OffMaskIterator() : BaseType() {}
+ OffMaskIterator(Index32 pos,const NodeMask *parent) : BaseType(pos,parent) {}
+ void increment()
+ {
+ assert(mParent != NULL);
+ mPos=mParent->findNextOff(mPos+1);
+ assert(mPos <= NodeMask::SIZE);
+ }
+ void increment(Index n) { while(n-- && this->next()) ; }
+ bool next()
+ {
+ this->increment();
+ return this->test();
+ }
+ bool operator*() const {return false;}
+ OffMaskIterator& operator++()
+ {
+ this->increment();
+ return *this;
+ }
+}; // class OffMaskIterator
+
+
+template <typename NodeMask>
+class DenseMaskIterator: public BaseMaskIterator<NodeMask>
+{
+private:
+ typedef BaseMaskIterator<NodeMask> BaseType;
+ using BaseType::mPos;//bit position;
+ using BaseType::mParent;//this iterator can't change the parent_mask!
+
+public:
+ DenseMaskIterator() : BaseType() {}
+ DenseMaskIterator(Index32 pos,const NodeMask *parent) : BaseType(pos,parent) {}
+ void increment()
+ {
+ assert(mParent != NULL);
+ mPos += 1;//careful - the increment might go beyond the end
+ assert(mPos<= NodeMask::SIZE);
+ }
+ void increment(Index n) { while(n-- && this->next()) ; }
+ bool next()
+ {
+ this->increment();
+ return this->test();
+ }
+ bool operator*() const {return mParent->isOn(mPos);}
+ DenseMaskIterator& operator++()
+ {
+ this->increment();
+ return *this;
+ }
+}; // class DenseMaskIterator
+
+
+/// @brief Bit mask for the internal and leaf nodes of VDB. This
+/// is a 64-bit implementation.
+///
+/// @note A template specialization for Log2Dim=1 and Log2Dim=2 are
+/// given below.
+template<Index Log2Dim>
+class NodeMask
+{
+public:
+ BOOST_STATIC_ASSERT( Log2Dim>2 );
+
+ static const Index32 LOG2DIM = Log2Dim;
+ static const Index32 DIM = 1<<Log2Dim;
+ static const Index32 SIZE = 1<<3*Log2Dim;
+ static const Index32 WORD_COUNT = SIZE >> 6;// 2^6=64
+ typedef Index64 Word;
+
+private:
+
+ // The bits are represented as a linear array of Words, and the
+ // size of a Word is 32 or 64 bits depending on the platform.
+ // The BIT_MASK is defined as the number of bits in a Word - 1
+ //static const Index32 BIT_MASK = sizeof(void*) == 8 ? 63 : 31;
+ //static const Index32 LOG2WORD = BIT_MASK == 63 ? 6 : 5;
+ //static const Index32 WORD_COUNT = SIZE >> LOG2WORD;
+ //typedef boost::mpl::if_c<BIT_MASK == 63, Index64, Index32>::type Word;
+
+ Word mWords[WORD_COUNT];//only member data!
+
+public:
+ /// Default constructor sets all bits off
+ NodeMask() { this->setOff(); }
+ /// All bits are set to the specified state
+ NodeMask(bool on) { this->set(on); }
+ /// Copy constructor
+ NodeMask(const NodeMask &other) { *this = other; }
+ /// Destructor
+ ~NodeMask() {}
+ /// Assignment operator
+ void operator = (const NodeMask &other)
+ {
+ Index32 n = WORD_COUNT;
+ const Word* w2 = other.mWords;
+ for ( Word* w1 = mWords; n--; ++w1, ++w2) *w1 = *w2;
+ }
+
+ typedef OnMaskIterator<NodeMask> OnIterator;
+ typedef OffMaskIterator<NodeMask> OffIterator;
+ typedef DenseMaskIterator<NodeMask> DenseIterator;
+
+ OnIterator beginOn() const { return OnIterator(this->findFirstOn(),this); }
+ OnIterator endOn() const { return OnIterator(SIZE,this); }
+ OffIterator beginOff() const { return OffIterator(this->findFirstOff(),this); }
+ OffIterator endOff() const { return OffIterator(SIZE,this); }
+ DenseIterator beginDense() const { return DenseIterator(0,this); }
+ DenseIterator endDense() const { return DenseIterator(SIZE,this); }
+
+ bool operator == (const NodeMask &other) const
+ {
+ int n = WORD_COUNT;
+ for (const Word *w1=mWords, *w2=other.mWords; n-- && *w1++ == *w2++;) ;
+ return n == -1;
+ }
+
+ bool operator != (const NodeMask &other) const { return !(*this == other); }
+
+ //
+ // Bitwise logical operations
+ //
+ NodeMask operator!() const { NodeMask m(*this); m.toggle(); return m; }
+ const NodeMask& operator&=(const NodeMask& other)
+ {
+ Index32 n = WORD_COUNT;
+ const Word* w2 = other.mWords;
+ for ( Word* w1 = mWords; n--; ++w1, ++w2) *w1 &= *w2;
+ return *this;
+ }
+ const NodeMask& operator|=(const NodeMask& other)
+ {
+ Index32 n = WORD_COUNT;
+ const Word* w2 = other.mWords;
+ for ( Word* w1 = mWords; n--; ++w1, ++w2) *w1 |= *w2;
+ return *this;
+ }
+ const NodeMask& operator^=(const NodeMask& other)
+ {
+ Index32 n = WORD_COUNT;
+ const Word* w2 = other.mWords;
+ for ( Word* w1 = mWords; n--; ++w1, ++w2) *w1 ^= *w2;
+ return *this;
+ }
+ NodeMask operator&(const NodeMask& other) const { NodeMask m(*this); m &= other; return m; }
+ NodeMask operator|(const NodeMask& other) const { NodeMask m(*this); m |= other; return m; }
+ NodeMask operator^(const NodeMask& other) const { NodeMask m(*this); m ^= other; return m; }
+ /// Return the byte size of this NodeMask
+ static Index32 memUsage() { return WORD_COUNT*sizeof(Word); }
+ /// Return the byte size of this NodeMask
+ OPENVDB_DEPRECATED Index32 getMemUsage() const {return sizeof(*this);}
+ /// Return the total number of on bits
+ Index32 countOn() const
+ {
+ Index32 sum = 0, n = WORD_COUNT;
+ for (const Word* w = mWords; n--; ++w) sum += CountOn(*w);
+ return sum;
+ }
+ /// Return the total number of on bits
+ Index32 countOff() const { return SIZE-this->countOn(); }
+ /// Set the <i>n</i>th bit on
+ void setOn(Index32 n) {
+ assert( (n >> 6) < WORD_COUNT );
+ mWords[n >> 6] |= Word(1) << (n & 63);
+ }
+ /// Set the <i>n</i>th bit off
+ void setOff(Index32 n) {
+ assert( (n >> 6) < WORD_COUNT );
+ mWords[n >> 6] &= ~(Word(1) << (n & 63));
+ }
+ /// Set the <i>n</i>th bit to the specified state
+ void set(Index32 n, bool On) { On ? this->setOn(n) : this->setOff(n); }
+ /// Set all bits to the specified state
+ void set(bool on)
+ {
+ const Word state = on ? ~Word(0) : Word(0);
+ Index32 n = WORD_COUNT;
+ for (Word* w = mWords; n--; ++w) *w = state;
+ }
+ /// Set all bits on
+ void setOn()
+ {
+ Index32 n = WORD_COUNT;
+ for (Word* w = mWords; n--; ++w) *w = ~Word(0);
+ }
+ /// Set all bits off
+ void setOff()
+ {
+ Index32 n = WORD_COUNT;
+ for (Word* w = mWords; n--; ++w) *w = Word(0);
+ }
+ /// Toggle the state of the <i>n</i>th bit
+ void toggle(Index32 n) {
+ assert( (n >> 6) < WORD_COUNT );
+ mWords[n >> 6] ^= 1 << (n & 63);
+ }
+ /// Toggle the state of all bits in the mask
+ void toggle()
+ {
+ Index32 n = WORD_COUNT;
+ for (Word* w = mWords; n--; ++w) *w = ~*w;
+ }
+ /// Set the first bit on
+ void setFirstOn() { this->setOn(0); }
+ /// Set the last bit on
+ void setLastOn() { this->setOn(SIZE-1); }
+ /// Set the first bit off
+ void setFirstOff() { this->setOff(0); }
+ /// Set the last bit off
+ void setLastOff() { this->setOff(SIZE-1); }
+ /// Return @c true if the <i>n</i>th bit is on
+ bool isOn(Index32 n) const
+ {
+ assert( (n >> 6) < WORD_COUNT );
+ return 0 != (mWords[n >> 6] & (Word(1) << (n & 63)));
+ }
+ /// Return @c true if the <i>n</i>th bit is off
+ bool isOff(Index32 n) const {return !this->isOn(n); }
+ /// Return @c true if all the bits are on
+ bool isOn() const
+ {
+ int n = WORD_COUNT;
+ for (const Word *w = mWords; n-- && *w++ == ~Word(0);) ;
+ return n == -1;
+ }
+ /// Return @c true if all the bits are off
+ bool isOff() const
+ {
+ int n = WORD_COUNT;
+ for (const Word *w = mWords; n-- && *w++ == Word(0);) ;
+ return n == -1;
+ }
+ Index32 findFirstOn() const
+ {
+ Index32 n = 0;
+ const Word* w = mWords;
+ for (; n<WORD_COUNT && !*w; ++w, ++n) ;
+ return n==WORD_COUNT ? SIZE : (n << 6) + FindLowestOn(*w);
+ }
+ Index32 findFirstOff() const
+ {
+ Index32 n = 0;
+ const Word* w = mWords;
+ for (; n<WORD_COUNT && !~*w; ++w, ++n) ;
+ return n==WORD_COUNT ? SIZE : (n << 6) + FindLowestOn(~*w);
+ }
+
+ //@{
+ /// Return the <i>n</i>th word of the bit mask, for a word of arbitrary size.
+ template<typename WordT>
+ WordT getWord(Index n) const
+ {
+ assert(n*8*sizeof(WordT) < SIZE);
+ return reinterpret_cast<const WordT*>(mWords)[n];
+ }
+ template<typename WordT>
+ WordT& getWord(Index n)
+ {
+ assert(n*8*sizeof(WordT) < SIZE);
+ return reinterpret_cast<WordT*>(mWords)[n];
+ }
+ //@}
+
+ void save(std::ostream& os) const
+ {
+ os.write(reinterpret_cast<const char*>(mWords), this->memUsage());
+ }
+ void load(std::istream& is) {
+ is.read(reinterpret_cast<char*>(mWords), this->memUsage());
+ }
+ /// @brief simple print method for debugging
+ void printInfo(std::ostream& os=std::cout) const
+ {
+ os << "NodeMask: Dim=" << DIM << " Log2Dim=" << Log2Dim
+ << " Bit count=" << SIZE << " word count=" << WORD_COUNT << std::endl;
+ }
+ void printBits(std::ostream& os=std::cout, Index32 max_out=80u) const
+ {
+ const Index32 n=(SIZE>max_out ? max_out : SIZE);
+ for (Index32 i=0; i < n; ++i) {
+ if ( !(i & 63) )
+ os << "||";
+ else if ( !(i%8) )
+ os << "|";
+ os << this->isOn(i);
+ }
+ os << "|" << std::endl;
+ }
+ void printAll(std::ostream& os=std::cout, Index32 max_out=80u) const
+ {
+ this->printInfo(os);
+ this->printBits(os, max_out);
+ }
+
+ Index32 findNextOn(Index32 start) const
+ {
+ Index32 n = start >> 6;//initiate
+ if (n >= WORD_COUNT) return SIZE; // check for out of bounds
+ Index32 m = start & 63;
+ Word b = mWords[n];
+ if (b & (Word(1) << m)) return start;//simpel case: start is on
+ b &= ~Word(0) << m;// mask out lower bits
+ while(!b && ++n<WORD_COUNT) b = mWords[n];// find next none-zero word
+ return (!b ? SIZE : (n << 6) + FindLowestOn(b));//catch last word=0
+ }
+
+ Index32 findNextOff(Index32 start) const
+ {
+ Index32 n = start >> 6;//initiate
+ if (n >= WORD_COUNT) return SIZE; // check for out of bounds
+ Index32 m = start & 63;
+ Word b = ~mWords[n];
+ if (b & (Word(1) << m)) return start;//simpel case: start is on
+ b &= ~Word(0) << m;// mask out lower bits
+ while(!b && ++n<WORD_COUNT) b = ~mWords[n];// find next none-zero word
+ return (!b ? SIZE : (n << 6) + FindLowestOn(b));//catch last word=0
+ }
+};// NodeMask
+
+
+/// @brief Template specialization of NodeMask for Log2Dim=1, i.e. 2^3 nodes
+template<>
+class NodeMask<1>
+{
+public:
+
+ static const Index32 LOG2DIM = 1;
+ static const Index32 DIM = 2;
+ static const Index32 SIZE = 8;
+ static const Index32 WORD_COUNT = 1;
+ typedef Byte Word;
+
+private:
+
+ Byte mByte;//only member data!
+
+public:
+ /// Default constructor sets all bits off
+ NodeMask() : mByte(0x00U) {}
+ /// All bits are set to the specified state
+ NodeMask(bool on) : mByte(on ? 0xFFU : 0x00U) {}
+ /// Copy constructor
+ NodeMask(const NodeMask &other) : mByte(other.mByte) {}
+ /// Destructor
+ ~NodeMask() {}
+ /// Assignment operator
+ void operator = (const NodeMask &other) { mByte = other.mByte; }
+
+ typedef OnMaskIterator<NodeMask> OnIterator;
+ typedef OffMaskIterator<NodeMask> OffIterator;
+ typedef DenseMaskIterator<NodeMask> DenseIterator;
+
+ OnIterator beginOn() const { return OnIterator(this->findFirstOn(),this); }
+ OnIterator endOn() const { return OnIterator(SIZE,this); }
+ OffIterator beginOff() const { return OffIterator(this->findFirstOff(),this); }
+ OffIterator endOff() const { return OffIterator(SIZE,this); }
+ DenseIterator beginDense() const { return DenseIterator(0,this); }
+ DenseIterator endDense() const { return DenseIterator(SIZE,this); }
+
+ bool operator == (const NodeMask &other) const { return mByte == other.mByte; }
+
+ bool operator != (const NodeMask &other) const {return mByte != other.mByte; }
+
+ //
+ // Bitwise logical operations
+ //
+ NodeMask operator!() const { NodeMask m(*this); m.toggle(); return m; }
+ const NodeMask& operator&=(const NodeMask& other)
+ {
+ mByte &= other.mByte;
+ return *this;
+ }
+ const NodeMask& operator|=(const NodeMask& other)
+ {
+ mByte |= other.mByte;
+ return *this;
+ }
+ const NodeMask& operator^=(const NodeMask& other)
+ {
+ mByte ^= other.mByte;
+ return *this;
+ }
+ NodeMask operator&(const NodeMask& other) const { NodeMask m(*this); m &= other; return m; }
+ NodeMask operator|(const NodeMask& other) const { NodeMask m(*this); m |= other; return m; }
+ NodeMask operator^(const NodeMask& other) const { NodeMask m(*this); m ^= other; return m; }
+ /// Return the byte size of this NodeMask
+ static Index32 memUsage() { return 1; }
+ /// Return the byte size of this NodeMask
+ OPENVDB_DEPRECATED Index32 getMemUsage() const {return sizeof(*this);}
+ /// Return the total number of on bits
+ Index32 countOn() const { return CountOn(mByte); }
+ /// Return the total number of on bits
+ Index32 countOff() const { return CountOff(mByte); }
+ /// Set the <i>n</i>th bit on
+ void setOn(Index32 n) {
+ assert( n < 8 );
+ mByte |= 0x01U << (n & 7);
+ }
+ /// Set the <i>n</i>th bit off
+ void setOff(Index32 n) {
+ assert( n < 8 );
+ mByte &= ~(0x01U << (n & 7));
+ }
+ /// Set the <i>n</i>th bit to the specified state
+ void set(Index32 n, bool On) { On ? this->setOn(n) : this->setOff(n); }
+ /// Set all bits to the specified state
+ void set(bool on) { mByte = on ? 0xFFU : 0x00U; }
+ /// Set all bits on
+ void setOn() { mByte = 0xFFU; }
+ /// Set all bits off
+ void setOff() { mByte = 0x00U; }
+ /// Toggle the state of the <i>n</i>th bit
+ void toggle(Index32 n) {
+ assert( n < 8 );
+ mByte ^= 0x01U << (n & 7);
+ }
+ /// Toggle the state of all bits in the mask
+ void toggle() { mByte = ~mByte; }
+ /// Set the first bit on
+ void setFirstOn() { this->setOn(0); }
+ /// Set the last bit on
+ void setLastOn() { this->setOn(7); }
+ /// Set the first bit off
+ void setFirstOff() { this->setOff(0); }
+ /// Set the last bit off
+ void setLastOff() { this->setOff(7); }
+ /// Return true if the <i>n</i>th bit is on
+ bool isOn(Index32 n) const
+ {
+ assert( n < 8 );
+ return mByte & (0x01U << (n & 7));
+ }
+ /// Return true if the <i>n</i>th bit is off
+ bool isOff(Index32 n) const {return !this->isOn(n); }
+ /// Return true if all the bits are on
+ bool isOn() const { return mByte == 0xFFU; }
+ /// Return true if all the bits are off
+ bool isOff() const { return mByte == 0; }
+ Index32 findFirstOn() const { return mByte ? FindLowestOn(mByte) : 8; }
+ Index32 findFirstOff() const
+ {
+ const Byte b = ~mByte;
+ return b ? FindLowestOn(b) : 8;
+ }
+ /*
+ //@{
+ /// Return the <i>n</i>th word of the bit mask, for a word of arbitrary size.
+ /// @note This version assumes WordT=Byte and n=0!
+ template<typename WordT>
+ WordT getWord(Index n) const
+ {
+ BOOST_STATIC_ASSERT(sizeof(WordT) == sizeof(Byte));
+ assert(n == 0);
+ return reinterpret_cast<WordT>(mByte);
+ }
+ template<typename WordT>
+ WordT& getWord(Index n)
+ {
+ BOOST_STATIC_ASSERT(sizeof(WordT) == sizeof(Byte));
+ assert(n == 0);
+ return reinterpret_cast<WordT&>(mByte);
+ }
+ //@}
+ */
+ void save(std::ostream& os) const
+ {
+ os.write(reinterpret_cast<const char*>(&mByte), 1);
+ }
+ void load(std::istream& is) { is.read(reinterpret_cast<char*>(&mByte), 1); }
+ /// @brief simple print method for debugging
+ void printInfo(std::ostream& os=std::cout) const
+ {
+ os << "NodeMask: Dim=2, Log2Dim=1, Bit count=8, Word count=1"<<std::endl;
+ }
+ void printBits(std::ostream& os=std::cout) const
+ {
+ os << "||";
+ for (Index32 i=0; i < 8; ++i) os << this->isOn(i);
+ os << "||" << std::endl;
+ }
+ void printAll(std::ostream& os=std::cout) const
+ {
+ this->printInfo(os);
+ this->printBits(os);
+ }
+
+ Index32 findNextOn(Index32 start) const
+ {
+ if (start>=8) return 8;
+ const Byte b = mByte & (0xFFU << start);
+ return b ? FindLowestOn(b) : 8;
+ }
+
+ Index32 findNextOff(Index32 start) const
+ {
+ if (start>=8) return 8;
+ const Byte b = ~mByte & (0xFFU << start);
+ return b ? FindLowestOn(b) : 8;
+ }
+
+};// NodeMask<1>
+
+
+/// @brief Template specialization of NodeMask for Log2Dim=2, i.e. 4^3 nodes
+template<>
+class NodeMask<2>
+{
+public:
+
+ static const Index32 LOG2DIM = 2;
+ static const Index32 DIM = 4;
+ static const Index32 SIZE = 64;
+ static const Index32 WORD_COUNT = 1;
+ typedef Index64 Word;
+
+private:
+
+ Word mWord;//only member data!
+
+public:
+ /// Default constructor sets all bits off
+ NodeMask() : mWord(UINT64_C(0x00)) {}
+ /// All bits are set to the specified state
+ NodeMask(bool on) : mWord(on ? UINT64_C(0xFFFFFFFFFFFFFFFF) : UINT64_C(0x00)) {}
+ /// Copy constructor
+ NodeMask(const NodeMask &other) : mWord(other.mWord) {}
+ /// Destructor
+ ~NodeMask() {}
+ /// Assignment operator
+ void operator = (const NodeMask &other) { mWord = other.mWord; }
+
+ typedef OnMaskIterator<NodeMask> OnIterator;
+ typedef OffMaskIterator<NodeMask> OffIterator;
+ typedef DenseMaskIterator<NodeMask> DenseIterator;
+
+ OnIterator beginOn() const { return OnIterator(this->findFirstOn(),this); }
+ OnIterator endOn() const { return OnIterator(SIZE,this); }
+ OffIterator beginOff() const { return OffIterator(this->findFirstOff(),this); }
+ OffIterator endOff() const { return OffIterator(SIZE,this); }
+ DenseIterator beginDense() const { return DenseIterator(0,this); }
+ DenseIterator endDense() const { return DenseIterator(SIZE,this); }
+
+ bool operator == (const NodeMask &other) const { return mWord == other.mWord; }
+
+ bool operator != (const NodeMask &other) const {return mWord != other.mWord; }
+
+ //
+ // Bitwise logical operations
+ //
+ NodeMask operator!() const { NodeMask m(*this); m.toggle(); return m; }
+ const NodeMask& operator&=(const NodeMask& other)
+ {
+ mWord &= other.mWord;
+ return *this;
+ }
+ const NodeMask& operator|=(const NodeMask& other)
+ {
+ mWord |= other.mWord;
+ return *this;
+ }
+ const NodeMask& operator^=(const NodeMask& other)
+ {
+ mWord ^= other.mWord;
+ return *this;
+ }
+ NodeMask operator&(const NodeMask& other) const { NodeMask m(*this); m &= other; return m; }
+ NodeMask operator|(const NodeMask& other) const { NodeMask m(*this); m |= other; return m; }
+ NodeMask operator^(const NodeMask& other) const { NodeMask m(*this); m ^= other; return m; }
+ /// Return the byte size of this NodeMask
+ static Index32 memUsage() { return 8; }
+ /// Return the byte size of this NodeMask
+ OPENVDB_DEPRECATED Index32 getMemUsage() const {return sizeof(*this);}
+ /// Return the total number of on bits
+ Index32 countOn() const { return CountOn(mWord); }
+ /// Return the total number of on bits
+ Index32 countOff() const { return CountOff(mWord); }
+ /// Set the <i>n</i>th bit on
+ void setOn(Index32 n) {
+ assert( n < 64 );
+ mWord |= UINT64_C(0x01) << (n & 63);
+ }
+ /// Set the <i>n</i>th bit off
+ void setOff(Index32 n) {
+ assert( n < 64 );
+ mWord &= ~(UINT64_C(0x01) << (n & 63));
+ }
+ /// Set the <i>n</i>th bit to the specified state
+ void set(Index32 n, bool On) { On ? this->setOn(n) : this->setOff(n); }
+ /// Set all bits to the specified state
+ void set(bool on) { mWord = on ? UINT64_C(0xFFFFFFFFFFFFFFFF) : UINT64_C(0x00); }
+ /// Set all bits on
+ void setOn() { mWord = UINT64_C(0xFFFFFFFFFFFFFFFF); }
+ /// Set all bits off
+ void setOff() { mWord = UINT64_C(0x00); }
+ /// Toggle the state of the <i>n</i>th bit
+ void toggle(Index32 n) {
+ assert( n < 64 );
+ mWord ^= UINT64_C(0x01) << (n & 63);
+ }
+ /// Toggle the state of all bits in the mask
+ void toggle() { mWord = ~mWord; }
+ /// Set the first bit on
+ void setFirstOn() { this->setOn(0); }
+ /// Set the last bit on
+ void setLastOn() { this->setOn(63); }
+ /// Set the first bit off
+ void setFirstOff() { this->setOff(0); }
+ /// Set the last bit off
+ void setLastOff() { this->setOff(63); }
+ /// Return true if the <i>n</i>th bit is on
+ bool isOn(Index32 n) const
+ {
+ assert( n < 64 );
+ return 0 != (mWord & (UINT64_C(0x01) << (n & 63)));
+ }
+ /// Return true if the <i>n</i>th bit is off
+ bool isOff(Index32 n) const {return !this->isOn(n); }
+ /// Return true if all the bits are on
+ bool isOn() const { return mWord == UINT64_C(0xFFFFFFFFFFFFFFFF); }
+ /// Return true if all the bits are off
+ bool isOff() const { return mWord == 0; }
+ Index32 findFirstOn() const { return mWord ? FindLowestOn(mWord) : 64; }
+ Index32 findFirstOff() const
+ {
+ const Word w = ~mWord;
+ return w ? FindLowestOn(w) : 64;
+ }
+ //@{
+ /// Return the <i>n</i>th word of the bit mask, for a word of arbitrary size.
+ template<typename WordT>
+ WordT getWord(Index n) const
+ {
+ assert(n*8*sizeof(WordT) < SIZE);
+ return reinterpret_cast<const WordT*>(&mWord)[n];
+ }
+ template<typename WordT>
+ WordT& getWord(Index n)
+ {
+ assert(n*8*sizeof(WordT) < SIZE);
+ return reinterpret_cast<WordT*>(mWord)[n];
+ }
+ //@}
+ void save(std::ostream& os) const
+ {
+ os.write(reinterpret_cast<const char*>(&mWord), 8);
+ }
+ void load(std::istream& is) { is.read(reinterpret_cast<char*>(&mWord), 8); }
+ /// @brief simple print method for debugging
+ void printInfo(std::ostream& os=std::cout) const
+ {
+ os << "NodeMask: Dim=4, Log2Dim=2, Bit count=64, Word count=1"<<std::endl;
+ }
+ void printBits(std::ostream& os=std::cout) const
+ {
+ os << "|";
+ for (Index32 i=0; i < 64; ++i) {
+ if ( !(i%8) ) os << "|";
+ os << this->isOn(i);
+ }
+ os << "||" << std::endl;
+ }
+ void printAll(std::ostream& os=std::cout) const
+ {
+ this->printInfo(os);
+ this->printBits(os);
+ }
+
+ Index32 findNextOn(Index32 start) const
+ {
+ if (start>=64) return 64;
+ const Word w = mWord & (UINT64_C(0xFFFFFFFFFFFFFFFF) << start);
+ return w ? FindLowestOn(w) : 64;
+ }
+
+ Index32 findNextOff(Index32 start) const
+ {
+ if (start>=64) return 64;
+ const Word w = ~mWord & (UINT64_C(0xFFFFFFFFFFFFFFFF) << start);
+ return w ? FindLowestOn(w) : 64;
+ }
+
+};// NodeMask<2>
+
+
+// Unlike NodeMask above this RootNodeMask has a run-time defined size.
+// It is only included for backward compatibility and will likely be
+// deprecated in the future!
+// This class is 32-bit specefic, hence the use if Index32 vs Index!
+class RootNodeMask
+{
+protected:
+ Index32 mBitSize, mIntSize;
+ Index32 *mBits;
+
+public:
+ RootNodeMask(): mBitSize(0), mIntSize(0), mBits(NULL) {}
+ RootNodeMask(Index32 bit_size):
+ mBitSize(bit_size), mIntSize(((bit_size-1)>>5)+1), mBits(new Index32[mIntSize])
+ {
+ for (Index32 i=0; i<mIntSize; ++i) mBits[i]=0x00000000;
+ }
+ RootNodeMask(const RootNodeMask& B):
+ mBitSize(B.mBitSize), mIntSize(B.mIntSize), mBits(new Index32[mIntSize])
+ {
+ for (Index32 i=0; i<mIntSize; ++i) mBits[i]=B.mBits[i];
+ }
+ ~RootNodeMask() {delete [] mBits;}
+
+ void init(Index32 bit_size) {
+ mBitSize = bit_size;
+ mIntSize =((bit_size-1)>>5)+1;
+ delete [] mBits;
+ mBits = new Index32[mIntSize];
+ for (Index32 i=0; i<mIntSize; ++i) mBits[i]=0x00000000;
+ }
+
+ Index getBitSize() const {return mBitSize;}
+
+ Index getIntSize() const {return mIntSize;}
+
+ void operator = (const RootNodeMask &B) {
+ if (mBitSize!=B.mBitSize) {
+ mBitSize=B.mBitSize;
+ mIntSize=B.mIntSize;
+ delete [] mBits;
+ mBits = new Index32[mIntSize];
+ }
+ for (Index32 i=0; i<mIntSize; ++i) mBits[i]=B.mBits[i];
+ }
+
+ class BaseIterator
+ {
+ protected:
+ Index32 mPos;//bit position
+ Index32 mBitSize;
+ const RootNodeMask* mParent;//this iterator can't change the parent_mask!
+ public:
+ BaseIterator() : mPos(0), mBitSize(0), mParent(NULL) {}
+ BaseIterator(Index32 pos,const RootNodeMask *parent)
+ : mPos(pos), mBitSize(parent->getBitSize()), mParent(parent) {
+ assert( pos<=mBitSize );
+ }
+ bool operator==(const BaseIterator &iter) const {return mPos == iter.mPos;}
+ bool operator!=(const BaseIterator &iter) const {return mPos != iter.mPos;}
+ bool operator< (const BaseIterator &iter) const {return mPos < iter.mPos;}
+ void operator=(const BaseIterator &iter) {
+ mPos = iter.mPos;
+ mBitSize = iter.mBitSize;
+ mParent = iter.mParent;
+ }
+
+ Index32 offset() const {return mPos;}
+
+ Index32 pos() const {return mPos;}
+
+ bool test() const {
+ assert(mPos <= mBitSize);
+ return (mPos != mBitSize);
+ }
+
+ operator bool() const {return this->test();}
+ }; // class BaseIterator
+
+ /// @note This happens to be a const-iterator!
+ class OnIterator: public BaseIterator
+ {
+ protected:
+ using BaseIterator::mPos;//bit position;
+ using BaseIterator::mBitSize;//bit size;
+ using BaseIterator::mParent;//this iterator can't change the parent_mask!
+ public:
+ OnIterator() : BaseIterator() {}
+ OnIterator(Index32 pos,const RootNodeMask *parent) : BaseIterator(pos,parent) {}
+ void increment() {
+ assert(mParent!=NULL);
+ mPos=mParent->findNextOn(mPos+1);
+ assert(mPos <= mBitSize);
+ }
+ void increment(Index n) {
+ for (Index i=0; i<n && this->next(); ++i) {}
+ }
+ bool next() {
+ this->increment();
+ return this->test();
+ }
+ bool operator*() const {return true;}
+ OnIterator& operator++() {
+ this->increment();
+ return *this;
+ }
+ }; // class OnIterator
+
+ class OffIterator: public BaseIterator
+ {
+ protected:
+ using BaseIterator::mPos;//bit position;
+ using BaseIterator::mBitSize;//bit size;
+ using BaseIterator::mParent;//this iterator can't change the parent_mask!
+ public:
+ OffIterator() : BaseIterator() {}
+ OffIterator(Index32 pos,const RootNodeMask *parent) : BaseIterator(pos,parent) {}
+ void increment() {
+ assert(mParent!=NULL);
+ mPos=mParent->findNextOff(mPos+1);
+ assert(mPos <= mBitSize);
+ }
+ void increment(Index n) {
+ for (Index i=0; i<n && this->next(); ++i) {}
+ }
+ bool next() {
+ this->increment();
+ return this->test();
+ }
+ bool operator*() const {return true;}
+ OffIterator& operator++() {
+ this->increment();
+ return *this;
+ }
+ }; // class OffIterator
+
+ class DenseIterator: public BaseIterator
+ {
+ protected:
+ using BaseIterator::mPos;//bit position;
+ using BaseIterator::mBitSize;//bit size;
+ using BaseIterator::mParent;//this iterator can't change the parent_mask!
+ public:
+ DenseIterator() : BaseIterator() {}
+ DenseIterator(Index32 pos,const RootNodeMask *parent) : BaseIterator(pos,parent) {}
+ void increment() {
+ assert(mParent!=NULL);
+ mPos += 1;//carefull - the increament might go beyond the end
+ assert(mPos<= mBitSize);
+ }
+ void increment(Index n) {
+ for (Index i=0; i<n && this->next(); ++i) {}
+ }
+ bool next() {
+ this->increment();
+ return this->test();
+ }
+ bool operator*() const {return mParent->isOn(mPos);}
+ DenseIterator& operator++() {
+ this->increment();
+ return *this;
+ }
+ }; // class DenseIterator
+
+ OnIterator beginOn() const { return OnIterator(this->findFirstOn(),this); }
+ OnIterator endOn() const { return OnIterator(mBitSize,this); }
+ OffIterator beginOff() const { return OffIterator(this->findFirstOff(),this); }
+ OffIterator endOff() const { return OffIterator(mBitSize,this); }
+ DenseIterator beginDense() const { return DenseIterator(0,this); }
+ DenseIterator endDense() const { return DenseIterator(mBitSize,this); }
+
+ bool operator == (const RootNodeMask &B) const {
+ if (mBitSize != B.mBitSize) return false;
+ for (Index32 i=0; i<mIntSize; ++i) if (mBits[i] != B.mBits[i]) return false;
+ return true;
+ }
+
+ bool operator != (const RootNodeMask &B) const {
+ if (mBitSize != B.mBitSize) return true;
+ for (Index32 i=0; i<mIntSize; ++i) if (mBits[i] != B.mBits[i]) return true;
+ return false;
+ }
+
+ //
+ // Bitwise logical operations
+ //
+ RootNodeMask operator!() const { RootNodeMask m = *this; m.toggle(); return m; }
+ const RootNodeMask& operator&=(const RootNodeMask& other) {
+ assert(mIntSize == other.mIntSize);
+ for (Index32 i = 0, N = std::min(mIntSize, other.mIntSize); i < N; ++i) {
+ mBits[i] &= other.mBits[i];
+ }
+ for (Index32 i = other.mIntSize; i < mIntSize; ++i) mBits[i] = 0x00000000;
+ return *this;
+ }
+ const RootNodeMask& operator|=(const RootNodeMask& other) {
+ assert(mIntSize == other.mIntSize);
+ for (Index32 i = 0, N = std::min(mIntSize, other.mIntSize); i < N; ++i) {
+ mBits[i] |= other.mBits[i];
+ }
+ return *this;
+ }
+ const RootNodeMask& operator^=(const RootNodeMask& other) {
+ assert(mIntSize == other.mIntSize);
+ for (Index32 i = 0, N = std::min(mIntSize, other.mIntSize); i < N; ++i) {
+ mBits[i] ^= other.mBits[i];
+ }
+ return *this;
+ }
+ RootNodeMask operator&(const RootNodeMask& other) const {
+ RootNodeMask m(*this); m &= other; return m;
+ }
+ RootNodeMask operator|(const RootNodeMask& other) const {
+ RootNodeMask m(*this); m |= other; return m;
+ }
+ RootNodeMask operator^(const RootNodeMask& other) const {
+ RootNodeMask m(*this); m ^= other; return m;
+ }
+
+
+ Index32 getMemUsage() const {return mIntSize*sizeof(Index32) + sizeof(*this);}
+
+ Index32 countOn() const {
+ assert(mBits);
+ Index32 n=0;
+ for (Index32 i=0; i< mIntSize; ++i) n += CountOn(mBits[i]);
+ return n;
+ }
+
+ Index32 countOff() const { return mBitSize-this->countOn(); }
+
+ void setOn(Index32 i) {
+ assert(mBits);
+ assert( (i>>5) < mIntSize);
+ mBits[i>>5] |= 1<<(i&31);
+ }
+
+ void setOff(Index32 i) {
+ assert(mBits);
+ assert( (i>>5) < mIntSize);
+ mBits[i>>5] &= ~(1<<(i&31));
+ }
+
+ void set(Index32 i, bool On) { On ? this->setOn(i) : this->setOff(i); }
+
+ void setOn() {
+ assert(mBits);
+ for (Index32 i=0; i<mIntSize; ++i) mBits[i]=0xFFFFFFFF;
+ }
+ void setOff() {
+ assert(mBits);
+ for (Index32 i=0; i<mIntSize; ++i) mBits[i]=0x00000000;
+ }
+ void toggle(Index32 i) {
+ assert(mBits);
+ assert( (i>>5) < mIntSize);
+ mBits[i>>5] ^= 1<<(i&31);
+ }
+ void toggle() {
+ assert(mBits);
+ for (Index32 i=0; i<mIntSize; ++i) mBits[i]=~mBits[i];
+ }
+ void setFirstOn() { this->setOn(0); }
+ void setLastOn() { this->setOn(mBitSize-1); }
+ void setFirstOff() { this->setOff(0); }
+ void setLastOff() { this->setOff(mBitSize-1); }
+ bool isOn(Index32 i) const {
+ assert(mBits);
+ assert( (i>>5) < mIntSize);
+ return ( mBits[i >> 5] & (1<<(i&31)) );
+ }
+ bool isOff(Index32 i) const {
+ assert(mBits);
+ assert( (i>>5) < mIntSize);
+ return ( ~mBits[i >> 5] & (1<<(i&31)) );
+ }
+
+ bool isOn() const {
+ if (!mBits) return false;//undefined is off
+ for (Index32 i=0; i<mIntSize; ++i) if (mBits[i] != 0xFFFFFFFF) return false;
+ return true;
+ }
+
+ bool isOff() const {
+ if (!mBits) return true;//undefined is off
+ for (Index32 i=0; i<mIntSize; ++i) if (mBits[i] != 0) return false;
+ return true;
+ }
+
+ Index32 findFirstOn() const {
+ assert(mBits);
+ Index32 i=0;
+ while(!mBits[i]) if (++i == mIntSize) return mBitSize;//reached end
+ return 32*i + FindLowestOn(mBits[i]);
+ }
+
+ Index32 findFirstOff() const {
+ assert(mBits);
+ Index32 i=0;
+ while(!(~mBits[i])) if (++i == mIntSize) return mBitSize;//reached end
+ return 32*i + FindLowestOn(~mBits[i]);
+ }
+
+ void save(std::ostream& os) const {
+ assert(mBits);
+ os.write((const char *)mBits,mIntSize*sizeof(Index32));
+ }
+ void load(std::istream& is) {
+ assert(mBits);
+ is.read((char *)mBits,mIntSize*sizeof(Index32));
+ }
+ /// @brief simple print method for debugging
+ void printInfo(std::ostream& os=std::cout) const {
+ os << "RootNodeMask: Bit-size="<<mBitSize<<" Int-size="<<mIntSize<<std::endl;
+ }
+
+ void printBits(std::ostream& os=std::cout, Index32 max_out=80u) const {
+ const Index32 n=(mBitSize>max_out?max_out:mBitSize);
+ for (Index32 i=0; i < n; ++i) {
+ if ( !(i&31) )
+ os << "||";
+ else if ( !(i%8) )
+ os << "|";
+ os << this->isOn(i);
+ }
+ os << "|" << std::endl;
+ }
+
+ void printAll(std::ostream& os=std::cout, Index32 max_out=80u) const {
+ this->printInfo(os);
+ this->printBits(os,max_out);
+ }
+
+ Index32 findNextOn(Index32 start) const {
+ assert(mBits);
+ Index32 n = start >> 5, m = start & 31;//initiate
+ if (n>=mIntSize) return mBitSize; // check for out of bounds
+ Index32 b = mBits[n];
+ if (b & (1<<m)) return start;//simple case
+ b &= 0xFFFFFFFF << m;// mask lower bits
+ while(!b && ++n<mIntSize) b = mBits[n];// find next nonzero int
+ return (!b ? mBitSize : 32*n + FindLowestOn(b));//catch last-int=0
+ }
+
+ Index32 findNextOff(Index32 start) const {
+ assert(mBits);
+ Index32 n = start >> 5, m = start & 31;//initiate
+ if (n>=mIntSize) return mBitSize; // check for out of bounds
+ Index32 b = ~mBits[n];
+ if (b & (1<<m)) return start;//simple case
+ b &= 0xFFFFFFFF<<m;// mask lower bits
+ while(!b && ++n<mIntSize) b = ~mBits[n];// find next nonzero int
+ return (!b ? mBitSize : 32*n + FindLowestOn(b));//catch last-int=0
+ }
+
+ Index32 memUsage() const {
+ assert(mBits);
+ return sizeof(Index32*)+(2+mIntSize)*sizeof(Index32);//in bytes
+ }
+}; // class RootNodeMask
+
+} // namespace util
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_UTIL_NODEMASKS_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/util/NullInterrupter.h b/extern/openvdb/internal/openvdb/util/NullInterrupter.h
new file mode 100644
index 00000000000..713e68df32e
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/util/NullInterrupter.h
@@ -0,0 +1,90 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+//
+/// @file NullInterrupter.h
+
+#ifndef OPENVDB_UTIL_NULL_INTERRUPTER_HAS_BEEN_INCLUDED
+#define OPENVDB_UTIL_NULL_INTERRUPTER_HAS_BEEN_INCLUDED
+
+#include <openvdb/version.h>
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace util {
+
+/// @brief Dummy NOOP interrupter class defining interface
+///
+/// This shows the required interface for the @c InterrupterType template argument
+/// using by several threaded applications (e.g. tools/PointAdvect.h). The host
+/// application calls start() at the beginning of an interruptible operation, end()
+/// at the end of the operation, and wasInterrupted() periodically during the operation.
+/// If any call to wasInterrupted() returns @c true, the operation will be aborted.
+/// @note This Dummy interrupter will NEVER interrupt since wasInterrupted() always
+/// returns false!
+struct NullInterrupter
+{
+ /// Default constructor
+ NullInterrupter () {}
+ /// Signal the start of an interruptible operation.
+ /// @param name an optional descriptive name for the operation
+ void start(const char* name = NULL) { (void)name; }
+ /// Signal the end of an interruptible operation.
+ void end() {}
+ /// Check if an interruptible operation should be aborted.
+ /// @param percent an optional (when >= 0) percentage indicating
+ /// the fraction of the operation that has been completed
+ /// @note this method is assumed to be thread-safe. The current
+ /// implementation is clearly a NOOP and should compile out during
+ /// optimization!
+ inline bool wasInterrupted(int percent = -1) { (void)percent; return false; }
+};
+
+/// This method allows NullInterrupter::wasInterrupted to be compiled
+/// out when client code only has a pointer (vs reference) to the interrupter.
+///
+/// @note This is a free-standing function since C++ doesn't allow for
+/// partial template specialization (in client code of the interrupter).
+template <typename T>
+inline bool wasInterrupted(T* i, int percent = -1) { return i && i->wasInterrupted(percent); }
+
+/// Specialization for NullInterrupter
+template<>
+inline bool wasInterrupted<util::NullInterrupter>(util::NullInterrupter*, int) { return false; }
+
+} // namespace util
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_UTIL_NULL_INTERRUPTER_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/util/Util.cc b/extern/openvdb/internal/openvdb/util/Util.cc
new file mode 100644
index 00000000000..58267279783
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/util/Util.cc
@@ -0,0 +1,77 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+
+#include "Util.h"
+#include <limits>
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace util {
+
+const Index32 INVALID_IDX = std::numeric_limits<Index32>::max();
+
+const Coord COORD_OFFSETS[26] =
+{
+ Coord( 1, 0, 0), /// Voxel-face adjacent neghbours
+ Coord(-1, 0, 0), /// 0 to 5
+ Coord( 0, 1, 0),
+ Coord( 0, -1, 0),
+ Coord( 0, 0, 1),
+ Coord( 0, 0, -1),
+ Coord( 1, 0, -1), /// Voxel-edge adjacent neghbours
+ Coord(-1, 0, -1), /// 6 to 17
+ Coord( 1, 0, 1),
+ Coord(-1, 0, 1),
+ Coord( 1, 1, 0),
+ Coord(-1, 1, 0),
+ Coord( 1, -1, 0),
+ Coord(-1, -1, 0),
+ Coord( 0, -1, 1),
+ Coord( 0, -1, -1),
+ Coord( 0, 1, 1),
+ Coord( 0, 1, -1),
+ Coord(-1, -1, -1), /// Voxel-corner adjacent neghbours
+ Coord(-1, -1, 1), /// 18 to 25
+ Coord( 1, -1, 1),
+ Coord( 1, -1, -1),
+ Coord(-1, 1, -1),
+ Coord(-1, 1, 1),
+ Coord( 1, 1, 1),
+ Coord( 1, 1, -1)
+};
+
+} // namespace util
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/util/Util.h b/extern/openvdb/internal/openvdb/util/Util.h
new file mode 100644
index 00000000000..a70a28a295b
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/util/Util.h
@@ -0,0 +1,165 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+
+#ifndef OPENVDB_UTIL_UTIL_HAS_BEEN_INCLUDED
+#define OPENVDB_UTIL_UTIL_HAS_BEEN_INCLUDED
+
+#include <openvdb/Types.h>
+#include <openvdb/tree/Tree.h>
+#include <openvdb/tools/ValueTransformer.h>
+
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+namespace util {
+
+OPENVDB_API extern const Index32 INVALID_IDX;
+
+/// @brief coordinate offset table for neighboring voxels
+OPENVDB_API extern const Coord COORD_OFFSETS[26];
+
+
+////////////////////////////////////////
+
+
+/// Return @a voxelCoord rounded to the closest integer coordinates.
+inline Coord
+nearestCoord(const Vec3d& voxelCoord)
+{
+ Coord ijk;
+ ijk[0] = int(std::floor(voxelCoord[0]));
+ ijk[1] = int(std::floor(voxelCoord[1]));
+ ijk[2] = int(std::floor(voxelCoord[2]));
+ return ijk;
+}
+
+
+////////////////////////////////////////
+
+
+/// @brief Functor for use with tools::foreach() to compute the boolean intersection
+/// between the value masks of corresponding leaf nodes in two trees
+template<class TreeType1, class TreeType2>
+class LeafTopologyIntOp
+{
+public:
+ LeafTopologyIntOp(const TreeType2& tree): mOtherTree(&tree) {}
+
+ inline void operator()(const typename TreeType1::LeafIter& lIter) const
+ {
+ const Coord xyz = lIter->getOrigin();
+ const typename TreeType2::LeafNodeType* leaf = mOtherTree->probeConstLeaf(xyz);
+ if (leaf) {//leaf node
+ lIter->topologyIntersection(*leaf, zeroVal<typename TreeType1::ValueType>());
+ } else if (!mOtherTree->isValueOn(xyz)) {//inactive tile
+ lIter->setValuesOff();
+ }
+ }
+
+private:
+ const TreeType2* mOtherTree;
+};
+
+
+/// @brief Functor for use with tools::foreach() to compute the boolean difference
+/// between the value masks of corresponding leaf nodes in two trees
+template<class TreeType1, class TreeType2>
+class LeafTopologyDiffOp
+{
+public:
+ LeafTopologyDiffOp(const TreeType2& tree): mOtherTree(&tree) {}
+
+ inline void operator()(const typename TreeType1::LeafIter& lIter) const
+ {
+ const Coord xyz = lIter->getOrigin();
+ const typename TreeType2::LeafNodeType* leaf = mOtherTree->probeConstLeaf(xyz);
+ if (leaf) {//leaf node
+ lIter->topologyDifference(*leaf, zeroVal<typename TreeType1::ValueType>());
+ } else if (mOtherTree->isValueOn(xyz)) {//active tile
+ lIter->setValuesOff();
+ }
+ }
+
+private:
+ const TreeType2* mOtherTree;
+};
+
+
+////////////////////////////////////////
+
+
+/// @brief Perform a boolean intersection between two leaf nodes' topology masks.
+/// @return a pointer to a new, boolean-valued tree containing the overlapping voxels.
+template<class TreeType1, class TreeType2>
+inline typename TreeType1::template ValueConverter<bool>::Type::Ptr
+leafTopologyIntersection(const TreeType1& lhs, const TreeType2& rhs, bool threaded = true)
+{
+ typedef typename TreeType1::template ValueConverter<bool>::Type BoolTreeType;
+
+ typename BoolTreeType::Ptr topologyTree(new BoolTreeType(
+ lhs, /*inactiveValue=*/false, /*activeValue=*/true, TopologyCopy()));
+
+ tools::foreach(topologyTree->beginLeaf(),
+ LeafTopologyIntOp<BoolTreeType, TreeType2>(rhs), threaded);
+
+ topologyTree->pruneInactive();
+ return topologyTree;
+}
+
+
+/// @brief Perform a boolean difference between two leaf nodes' topology masks.
+/// @return a pointer to a new, boolean-valued tree containing the non-overlapping
+/// voxels from the lhs.
+template<class TreeType1, class TreeType2>
+inline typename TreeType1::template ValueConverter<bool>::Type::Ptr
+leafTopologyDifference(const TreeType1& lhs, const TreeType2& rhs, bool threaded = true)
+{
+ typedef typename TreeType1::template ValueConverter<bool>::Type BoolTreeType;
+
+ typename BoolTreeType::Ptr topologyTree(new BoolTreeType(
+ lhs, /*inactiveValue=*/false, /*activeValue=*/true, TopologyCopy()));
+
+ tools::foreach(topologyTree->beginLeaf(),
+ LeafTopologyDiffOp<BoolTreeType, TreeType2>(rhs), threaded);
+
+ topologyTree->pruneInactive();
+ return topologyTree;
+}
+
+} // namespace util
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_UTIL_UTIL_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/util/logging.h b/extern/openvdb/internal/openvdb/util/logging.h
new file mode 100644
index 00000000000..c8bcb79fe27
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/util/logging.h
@@ -0,0 +1,69 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+
+#ifndef OPENVDB_UTIL_LOGGING_HAS_BEEN_INCLUDED
+#define OPENVDB_UTIL_LOGGING_HAS_BEEN_INCLUDED
+
+#ifndef OPENVDB_USE_LOG4CPLUS
+
+/// Macros to log messages of various severity levels
+/// The message is of the form 'someVar << "some text" << ...'.
+#define OPENVDB_LOG_INFO(message)
+#define OPENVDB_LOG_WARN(message) do { std::cerr << message << std::endl; } while (0);
+#define OPENVDB_LOG_ERROR(message) do { std::cerr << message << std::endl; } while (0);
+#define OPENVDB_LOG_FATAL(message) do { std::cerr << message << std::endl; } while (0);
+#define OPENVDB_LOG_DEBUG(message)
+#define OPENVDB_LOG_DEBUG_RUNTIME(message)
+
+#else // ifdef OPENVDB_USE_LOG4CPLUS
+
+#include <logging_base/logging.h>
+
+#define OPENVDB_LOG_INFO(message) LOG_INFO(message)
+#define OPENVDB_LOG_WARN(message) LOG_WARN(message)
+#define OPENVDB_LOG_ERROR(message) LOG_ERROR(message)
+#define OPENVDB_LOG_FATAL(message) LOG_FATAL(message)
+#ifdef DEBUG
+/// Log debugging messages in debug builds only.
+#define OPENVDB_LOG_DEBUG(message) LOG_DEBUG(message)
+#else
+#define OPENVDB_LOG_DEBUG(message)
+#endif
+/// Log debugging messages even in non-debug builds.
+/// Don't use this in performance-critical code.
+#define OPENVDB_LOG_DEBUG_RUNTIME(message) LOG_DEBUG_RUNTIME(message)
+
+#endif // OPENVDB_USE_LOG4CPLUS
+
+#endif // OPENVDB_UTIL_LOGGING_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
diff --git a/extern/openvdb/internal/openvdb/version.h b/extern/openvdb/internal/openvdb/version.h
new file mode 100644
index 00000000000..f4287a81797
--- /dev/null
+++ b/extern/openvdb/internal/openvdb/version.h
@@ -0,0 +1,139 @@
+///////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+//
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
+//
+// Redistributions of source code must retain the above copyright
+// and license notice and the following restrictions and disclaimer.
+//
+// * Neither the name of DreamWorks Animation nor the names of
+// its contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
+// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
+//
+///////////////////////////////////////////////////////////////////////////
+
+#ifndef OPENVDB_VERSION_HAS_BEEN_INCLUDED
+#define OPENVDB_VERSION_HAS_BEEN_INCLUDED
+
+#include "Platform.h"
+#include <iosfwd> // for std::istream
+#include <string>
+
+
+/// The version namespace name for this library version
+///
+/// Fully-namespace-qualified symbols are named as follows:
+/// vdb::vX_Y_Z::Vec3i, vdb::vX_Y_Z::io::File, vdb::vX_Y_Z::tree::Tree, etc.,
+/// where X, Y and Z are OPENVDB_LIBRARY_MAJOR_VERSION, OPENVDB_LIBRARY_MINOR_VERSION
+/// and OPENVDB_LIBRARY_PATCH_VERSION, respectively (defined below).
+#define OPENVDB_VERSION_NAME v1_1_1
+
+/// If OPENVDB_REQUIRE_VERSION_NAME is undefined, symbols from the version
+/// namespace are promoted to the top-level namespace (e.g., vdb::v1_0_0::io::File
+/// can be referred to simply as vdb::io::File). Otherwise, symbols must be fully
+/// namespace-qualified.
+#ifdef OPENVDB_REQUIRE_VERSION_NAME
+#define OPENVDB_USE_VERSION_NAMESPACE
+#else
+/// @note The empty namespace clause below ensures that
+/// OPENVDB_VERSION_NAME is recognized as a namespace name.
+#define OPENVDB_USE_VERSION_NAMESPACE \
+ namespace OPENVDB_VERSION_NAME {} \
+ using namespace OPENVDB_VERSION_NAME;
+#endif
+
+
+namespace openvdb {
+OPENVDB_USE_VERSION_NAMESPACE
+namespace OPENVDB_VERSION_NAME {
+
+/// @brief The magic number is stored in the first four bytes of every VDB file.
+/// @details This can be used to quickly test whether we have a valid file or not.
+const int32_t OPENVDB_MAGIC = 0x56444220;
+
+const uint32_t
+ OPENVDB_LIBRARY_MAJOR_VERSION = 1,
+ OPENVDB_LIBRARY_MINOR_VERSION = 1,
+ OPENVDB_LIBRARY_PATCH_VERSION = 1;
+
+/// @brief The current version number of the VDB file format
+/// @details This can be used to enable various backwards compatability switches
+/// or to reject files that cannot be read.
+const uint32_t OPENVDB_FILE_VERSION = 222;
+
+/// Notable file format version numbers
+enum {
+ OPENVDB_FILE_VERSION_ROOTNODE_MAP = 213,
+ OPENVDB_FILE_VERSION_INTERNALNODE_COMPRESSION = 214,
+ OPENVDB_FILE_VERSION_SIMPLIFIED_GRID_TYPENAME = 215,
+ OPENVDB_FILE_VERSION_GRID_INSTANCING = 216,
+ OPENVDB_FILE_VERSION_BOOL_LEAF_OPTIMIZATION = 217,
+ OPENVDB_FILE_VERSION_BOOST_UUID = 218,
+ OPENVDB_FILE_VERSION_NO_GRIDMAP = 219,
+ OPENVDB_FILE_VERSION_NEW_TRANSFORM = 219,
+ OPENVDB_FILE_VERSION_SELECTIVE_COMPRESSION = 220,
+ OPENVDB_FILE_VERSION_FLOAT_FRUSTUM_BBOX = 221,
+ OPENVDB_FILE_VERSION_NODE_MASK_COMPRESSION = 222,
+};
+
+
+struct VersionId { uint32_t first, second; VersionId(): first(0), second(0) {} };
+
+namespace io {
+/// @brief Return the file format version number associated with the given input stream.
+OPENVDB_API uint32_t getFormatVersion(std::istream&);
+/// @brief Return the (major, minor) library version number associated with the given input stream.
+OPENVDB_API VersionId getLibraryVersion(std::istream&);
+/// @brief Return a string of the form "<major>.<minor>/<format>", giving the library
+/// and file format version numbers associated with the given input stream.
+OPENVDB_API std::string getVersion(std::istream&);
+// Associate the current file format and library version numbers with the given input stream.
+OPENVDB_API void setCurrentVersion(std::istream&);
+// Associate specific file format and library version numbers with the given stream.
+OPENVDB_API void setVersion(std::ios_base&, const VersionId& libraryVersion, uint32_t fileVersion);
+// Return a bitwise OR of compression option flags (COMPRESS_ZIP, COMPRESS_ACTIVE_MASK, etc.)
+// specifying whether and how input data is compressed or output data should be compressed.
+OPENVDB_API uint32_t getDataCompression(std::ios_base&);
+// Associate with the given stream a bitwise OR of compression option flags (COMPRESS_ZIP,
+// COMPRESS_ACTIVE_MASK, etc.) specifying whether and how input data is compressed
+// or output data should be compressed.
+OPENVDB_API void setDataCompression(std::ios_base&, uint32_t compressionFlags);
+// Return the class (GRID_LEVEL_SET, GRID_UNKNOWN, etc.) of the grid
+// currently being read from or written to the given stream.
+OPENVDB_API uint32_t getGridClass(std::ios_base&);
+// brief Associate with the given stream the class (GRID_LEVEL_SET, GRID_UNKNOWN, etc.)
+// of the grid currently being read or written.
+OPENVDB_API void setGridClass(std::ios_base&, uint32_t);
+// Return a pointer to the background value of the grid currently being
+// read from or written to the given stream.
+OPENVDB_API const void* getGridBackgroundValuePtr(std::ios_base&);
+// Specify (a pointer to) the background value of the grid currently being
+// read from or written to the given stream.
+// The pointer must remain valid until the entire grid has been read or written.
+OPENVDB_API void setGridBackgroundValuePtr(std::ios_base&, const void* background);
+} // namespace io
+
+} // namespace OPENVDB_VERSION_NAME
+} // namespace openvdb
+
+#endif // OPENVDB_VERSION_HAS_BEEN_INCLUDED
+
+// Copyright (c) 2012-2013 DreamWorks Animation LLC
+// All rights reserved. This software is distributed under the
+// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )