diff options
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 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’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’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’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—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 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 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">“VDB: High-Resolution Sparse Volumes with Dynamic Topology”</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é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—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 “Hello, World” 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 “<tt>-o vdb_print</tt>” with, for example, +“<tt>-o helloworld</tt>” +and “<tt>cmd/openvdb_print/main.cc</tt>” +with “<tt>helloworld.cc</tt>”. + + + +@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.—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 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—and therefore slower, but thread-safe—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 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 (1, 2, 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 “first” 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’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’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’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 ¢er, 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/ ) |